/** Test Exprs. * See http://www.radford.edu/itec380/2008fall/Hw06/hw06.html * * Meant to cover most syntax situations for L0-L3. * 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). * * @author Ian Barland * @version 2008.Dec.06 */ 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 L0 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 L0 Expr, and what that expression evaluates to. * Use a Double, for Num values. */ allTests = java.util.Arrays.asList( /* L0 tests: */ new Pair( "7.0", 7 ) ,new Pair( "{plus 3.0 4.0}", 7 ) ,new Pair( "{times 3.0 4.0}", 12 ) ,new Pair( "{plus {plus 3.0 4.0} {times 3.0 4.0}}", 19) ,new Pair( "{ifZero 0.0 1.0 2.0}", 1 ) ,new Pair( "{ifZero 1.0 1.0 2.0}", 2 ) ,new Pair( "{ifZero {plus 3.0 -3.0} 1.0 2.0}", 1 ) ,new Pair( "{ifZero {plus {ifZero {ifZero 0.0 1.0 2.0} 3.0 4.0} -3.0} 1.0 2.0}", 2 ) /**** L1 tests: ****/ // /**** ,new Pair( "{mod 3.0 4.0}", 3) ,new Pair( "{mod {plus 5.0 6.0} 3.0}", 2) ,new Pair( "{mod 8.1 3.0}", 2.1) ,new Pair( "{mod 8.0 3.1}", 1.8) ,new Pair( "{mod -8.1 3.0}", 0.9) ,new Pair( "{mod -8.0 3.1}", 1.3) ,new Pair( "{mod 8.1 -3}", -0.9) ,new Pair( "{mod 8.0 -3.1}", -1.3) ,new Pair( "{mod -8.1 -3.0}", -2.1) ,new Pair( "{mod -8.0 -3.1}", -1.8) ,new Pair( "{mod 8.0 2.0}", 0) ,new Pair( "{mod -8.0 2.0}", 0) ,new Pair( "{mod 8.0 -2.0}", 0) ,new Pair( "{mod -8.0 -2.0}", 0) ,new Pair( "{mod 8.0 3.0}", 2) ,new Pair( "{mod -8.0 3.0}", 1) ,new Pair( "{mod 8.0 -3.0}", -1) ,new Pair( "{mod -8.0 -3.0}", -2) ,new Pair( "{ifNeg 0.0 1.0 2.0}", 2) ,new Pair( "{ifNeg -2.0 -3.0 -4.0}", -3) ,new Pair( "{ifNeg {mod 8.0 -2.0} 3.0 {plus 3.0 {times 4.0 5.0}}}", 23) ,new Pair( "{ifNeg {mod 9.0 -2.0} 3.0 {plus 3.0 {times 4.0 5.0}}}", 3) // ****/ /**** L2 tests: ****/ // /**** ,new Pair( "{with x 5.0 9.0}", 9 ) ,new Pair( "{with x 5.0 x}", 5 ) ,new Pair( "{with x {plus 2.0 3.0} x}", 5 ) ,new Pair( "{with x 5.0 {plus 4.0 3.0}}", 7 ) ,new Pair( "{with x 5.0 {plus x 3.0}}", 8 ) ,new Pair( "{with y 3.0 {with x 5.0 {plus x y}}}", 8 ) ,new Pair( "{with y 3.0 {with x y {plus x y}}}", 6 ) ,new Pair( "{with y {with z 4.0 {with y 99.0 z}} {with z 5.0 {plus {with z 10.0 y} {plus y z}}}}", 13 ) // Check that we don't substitute Expr0: ,new Pair( "{with x {with x 5.0 x} {plus x 4.0}}", 9 ) ,new Pair( "{with x {with x 5.0 {plus x 1.0}} {plus x -4.0}}", 2 ) ,new Pair( "{with x {with y 5.0 {plus y 1.0}} {plus x -4.0}}", 2 ) ,new Pair( "{with x {with x 5.0 x} x}", 5 ) ,new Pair( "{with x {with x 5.0 {plus x 1.0}} x}", 6 ) ,new Pair( "{with x {with y 5.0 {plus y 1.0}} x}", 6 ) // ****/ /**** L3 tests: ****/ // /**** ,new Pair( "{fun x 5.0}" , "How to test this?" ) ,new Pair( "{fun x x}" , "How to test this?" ) ,new Pair( "{fun x {plus x 1.0}}" , "How to test this?" ) ,new Pair( "{fun x {ifNeg x {times -1.0 x} x}}" , "How to test this?" ) ,new Pair( "{fun x y}" , "How to test this?" ) ,new Pair( "{fun x {fun y {plus x y}}}", "How to test this?" ) ,new Pair( "{{fun x 5.0} 3.0}", 5 ) ,new Pair( "{{fun x x} 3.0}", 3 ) ,new Pair( "{{fun x {plus x 1.0}} 3.0}", 4 ) ,new Pair( "{{fun x {fun y {plus x y}}} 3.0}", "How to test this?" ) ,new Pair( "{{fun x {ifNeg x {times -1.0 x} x}} -5.0}", 5 ) ,new Pair( "{{fun x {ifNeg x {times -1.0 x} x}} +5.0}", 5 ) ,new Pair( "{with abs {fun x {ifNeg x {times -1.0 x} x}}\n {abs -5.0}}", 5 ) ,new Pair( "{with abs {fun x {ifNeg x {times -1.0 x} x}}\n {abs +5.0}}", 5 ) // Check that, when eval'ing a function-application, // you evaluate the function being applied, since it might be // a fuction-appliation-which-will-return-a-function: ,new Pair( "{{{fun x {fun y {plus x y}}} 3} 4}", 7 ) // ****/ ); } /** 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)) { // 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 L2, 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?" ); } } } /** * Tears down the test fixture. * * Called after every test case method. */ protected void tearDown() { } }