/** Test Exprs. * See http://www.radford.edu/itec380/2013fall/hw06.html * * Meant to cover most syntax situations for O0-O3. * HOWEVER, the testing approach has some systemic flaws -- * we never test against the expected internal representation. * (For example, testParseToString just checks that parse and * 'toString' are inverses of each other; if they are both * the identity function, it would pass all tests.) * * Compiling this function will generate a warning * ('unchecked generic array creation', due to using Arrays.asList * and varargs). * * For compiling with junit (if your IDE doesn't automatically recognize it), * see the note at end of this file. * * @author Ian Barland * @version 2013.Nov.08 */ public class ExprTest extends junit.framework.TestCase { static final double TOLERANCE = 0.000001; java.util.List> allTests; /* allTests should be a list of pairs: * an O0 Expr, and what that expression evaluates to. * Use a Double, for Num values. */ /** * Sets up the test fixture. * * Called before every test case method. */ protected void setUp() { /* allTests should be a list of pairs: * an O0 Expr, and what that expression evaluates to. * Use a Double, for Num values. */ allTests = java.util.Arrays.asList( /* O0 tests: */ new Pair( "7", 7 ) //,new Pair( "7.0", 7 ) // parseString("7.0").toString() return "7", not "7.0". ,new Pair( "(3 add 4)", 7 ) ,new Pair( "(3 mul 4)", 12 ) ,new Pair( "((3 add 4) add( 3 mul 4 ))", 19) ,new Pair( "if 0 is0 then 1 else 2;", 1 ) ,new Pair( "if 1 is0 then 1 else 2;", 2 ) ,new Pair( "if (3 add -3) is0 then 1 else 2;", 1 ) ,new Pair( "if (if if -1 is0 then 1 else 2; is0 then 3 else 4 ; add -3) is0 then 1 else 2;", 2 ) /* *** O1 tests: *** */ /* Uncomment these tests, once `mod` is implemented: ,new Pair( "(3 mod 4)", 3) ,new Pair( "((5 add 6) mod 3)", 2) ,new Pair( "(8.1 mod 3)", 2.1) ,new Pair( "(8 mod 3.1)", 1.8) ,new Pair( "(-8.1 mod 3)", 0.9) ,new Pair( "(-8 mod 3.1)", 1.3) ,new Pair( "(8.1 mod -3)", -0.9) ,new Pair( "(8 mod -3.1)", -1.3) ,new Pair( "(-8.1 mod -3)", -2.1) ,new Pair( "(-8 mod -3.1)", -1.8) ,new Pair( "(8 mod 2)", 0) ,new Pair( "(-8 mod 2)", 0) ,new Pair( "(8 mod -2)", 0) ,new Pair( "(-8 mod -2)", 0) ,new Pair( "(8 mod 3)", 2) ,new Pair( "(-8 mod 3)", 1) ,new Pair( "(8 mod -3)", -1) ,new Pair( "(-8 mod -3)", -2) YOU MUST CREATE SOME TESTS FOR isNegExprs *** */ ); } /** For every element in allTests, * parse the string, and then call toString on the result, * checking that we get back exactly the input string * (up to whitespace). */ public void testParseToString() { for ( Pair t : allTests ) { String expected = t.getFirst(); String actual = Expr.parse( t.getFirst() ).toString(); if (! UtilIan.equalsIgnoreWhitespace(expected, actual, Expr.PUNCTUATION)) { // This assert will fail; just present it to the user: assertEquals( expected, actual ); } } } /** For every element in allTests, * parse the string and eval the result, * checking that we get back the second item in the pair. * If the second item is a number, we check within {@value TOLERANCE}. * If it's not a number (as in O2, once function-values are introduced), * this code doesn't actually test anything, and asserts an error saying so. */ public void testEval() { for ( Pair t : allTests ) { if (Number.class.isInstance( t.getSecond() )) { assertEquals( ((Number)t.getSecond()).doubleValue(), ((Num)(Expr.parse( t.getFirst() ).eval())).doubleValue(), TOLERANCE ); } else { // I guess Java expects us to implement .equals for every single Expr. // Should we do that, or hack some other way of doing our tests? assertEquals( t.getFirst() + " didn't evaluate to a double", "How to express the expected Fun value?" ); } } } public void testMyStuff() { assertEquals( Expr.parse("2").eval(), new Num(2) ); // N.B. This test *fails*, if you don't override Num#equals(Object). // // Option 1: Be correct, and override `equals`. // See Pair#equals(Object), for an example of how to properly override `equals`. // You need to override `equals for *any* Java class where equality isn't `==`. // Then, remember: If you ever override `equals`, you must also override `hashCode`. // Again, see Pair#hashCode() for an example. // // Option 2: Be sloppy, and never test `parse` directly. // Instead, check whether parse(programText).toString() is the same as `programText` // (for any String `programText` that happens to be a legal program). // As I discussed in class, that's what I did in the methods above: // You can just add items to that list `allTests`, since I provided two // tests that just loop over that list (which testing parse+toString, and parse+eval. // If a test fails, we don't know whether it's in parse, or toString, or eval // (that's the price of our laziness). // // Option 3: Alternately: see ObjectImmutable.java, in // http://www.radford.edu/~itec380/2013fall-ibarland/Lectures/lect27-Anc-Tree-reflected/ // If you extend that class, you'll inherit a default `equals`, `hashCode`, // a constructor(almost), and a `toString`. // They all do the expected things, for immutable objects. // (However, for ITEC380 Project, you must still write toString yourself.) } /** * Tears down the test fixture. * * Called after every test case method. */ protected void tearDown() { } } /* In the O0-java code, a couple of classes extend junit.framework.TestCase. In order to compile these, you'll need to have that class (and others) in your CLASSPATH. I know that BlueJ comes with the junit-classes automatically, and I imagine that Eclipse does, as well. If you're getting an error "package junit.framework does not exist", you'll want to download 'junit.jar' (I got it from junit.org), and extract it somewhere in your classpath (e.g., in the same dir as your project). To run the tests [again, BlueJ and Eclipse have buttons to do this] from the command line, you'd type: java org.junit.runner.JUnitCore TestExpr */