Ejemplo n.º 1
0
 def setUp(self):
     unittest.TestCase.setUp(self)
     # Get an interpreter object for convenience
     self._code_gen = CodeGenerator()
     # Get the directory to get test files from
     this_file_path = os.path.dirname(__file__)
     self._file_dir = os.path.join(this_file_path, 'test_files')
 def setUp(self):
     unittest.TestCase.setUp(self)
     # Get an interpreter object for convenience
     self._code_gen = CodeGenerator()
     # Get the directory to get test files from
     this_file_path = os.path.dirname(__file__)
     self._file_dir = os.path.join(this_file_path, 'test_files')
Ejemplo n.º 3
0
class TestCodeGenerator(unittest.TestCase):
    """Test class where tests to be run are the methods."""
    def setUp(self):
        unittest.TestCase.setUp(self)
        # Get an interpreter object for convenience
        self._code_gen = CodeGenerator()
        # Get the directory to get test files from
        this_file_path = os.path.dirname(__file__)
        self._file_dir = os.path.join(this_file_path, 'test_files')

    def test_extends_field(self):
        """Test a subclass can use a field of a superclass."""
        self._check_output_file('test_extends_field.jml', '10')

    def test_extends_method(self):
        """Test a subclass can use a method of a superclass."""
        self._check_output_file('test_extends_method.jml', '10')

    def test_invoke_implemented_method(self):
        """Test the case where a class invokes a method of an interface which
        has been instantiated by a class.
        """
        self._check_output_file('test_invoke_implemented_method.jml', '10')

    def test_cons_param(self):
        """Test that constructor parameters can be correctly used."""
        p = ('class X { X(String x) {' + self._wrap_print('x') +
             '} static void main(String[] args) {X inst = new X("pass");}}')
        self._check_output(p, 'X', 'pass')

    def test_method_param(self):
        """Test that method parameters can be correctly used."""
        p = ('class X { void meth(String x){' + self._wrap_print('x') +
             '} static void main(String[] args) {' +
             'X inst = new X();inst.meth("pass");}}')
        self._check_output(p, 'X', 'pass')

    def test_super_cons_implicitly_invoked(self):
        """Test that the super constructor is implicitly invoked."""
        self._check_output_file('test_super_cons_implicitly_invoked.jml',
                                'pass')

    def test_super_cons_explicitly_invoked(self):
        """Test that the super classes constructor can be explicitly
        invoked correctly.
        """
        self._check_output_file('test_super_cons_explicitly_invoked.jml',
                                'pass')

    def test_method_call(self):
        """Test a simple method call to a method in the current class."""
        p = ('class X { void meth() { int x = meth2();' +
             self._wrap_print('x') + '} byte meth2() {return 5;} ' +
             'static void main(String[] args){X inst = new X();inst.meth();}}')
        self._check_output(p, 'X', '5')

    def test_method_return_array(self):
        """Test a method in the same class correctly returns an array."""
        p = ('class X { void meth() { byte[][] x = meth2();' +
             'int y = x[0][0];' + self._wrap_print('y') +
             '} byte[][] meth2() { byte[][] arr = new byte[5][5];'
             'arr[0][0] = 5; return arr;} static void main(String[] args) {' +
             'X inst = new X();inst.meth();}}')
        self._check_output(p, 'X', '5')

    def test_method_call_external(self):
        """Test that a external method call is correctly compiled."""
        self._check_output_file('test_method_call_external.jml', '10.1')

    def test_method_call_static(self):
        """Test a static external method call."""
        self._check_output_file('test_method_call_static.jml', '10.0')

    def test_private_method_call(self):
        """Test an invocation of a private method in the current class.
        """
        p = ('class X { private void meth() {' + self._wrap_print('10') +
             '} static void main(String[] args) {' +
             'X inst = new X();inst.meth();}}')
        self._check_output(p, 'X', '10')

    def test_lib_method_call(self):
        """Test a call to a library class."""
        p = ('class X{static void main(String[] args) ' +
             '{Integer i = new Integer(5);' +
             self._wrap_print('i.intValue()') + '}}')
        self._check_output(p, 'X', '5')

    def test_lib_method_call_params(self):
        """Test a call to a library class with parameters."""
        p = ('class X {static void main(String[] args)' +
             '{String s = new String("pass");' +
             self._wrap_print('s.endsWith("s")') + '}}')
        self._check_output(p, 'X', 'true')

    def test_lib_method_in_super_call(self):
        """Test a method call to a library object where the method is defined
        in its super class.
        """
        p = ('class X{static void main(String[] args){' +
             self._wrap_print('Double.isNaN(1.1)') + '}}')
        self._check_output(p, 'X', 'false')

    def test_method_call_explicitly_super(self):
        """Test that the method in the super class is called, even when there
        is one with the same name in the current class.
        """
        p = ('class X {  X() {' +
             self._wrap_print('super.equals(new Object())') +
             '}static void main(String[] args){X x = new X();} ' +
             'boolean equals(Object x) {return true;}}')
        self._check_output(p, 'X', 'false')

    def test_lib_method_call_static(self):
        """Test a static method call. """
        p = ('class X {static void main(String[] args)' +
             '{GregorianCalendar x = new GregorianCalendar();' +
             self._wrap_print('x.isLenient()') + '}}')
        self._check_output(p, 'X', 'true')

    def test_field_access(self):
        """Test a simple field accessing in the same class."""
        p = ('class X { int f; void meth() { f = 5;' + self._wrap_print('f') +
             '} static void main(' +
             'String[] args) { X inst = new X();inst.meth();}}')
        self._check_output(p, 'X', '5')

    def test_field_inc(self):
        """Test incrementing a field in the current class."""
        p = ('class X { int f; void meth() {f = 5;f++;' +
             self._wrap_print('f') + '} static void main(' +
             'String[] args) {X inst = new X();inst.meth();}}')
        self._check_output(p, 'X', '6')

    def test_field_array(self):
        """Test a field of array type correctly compiles."""
        p = ('class X { int[] f; void meth() { f = new int[1];' + 'f[0] = 5;' +
             self._wrap_print('f[0]') + '} ' +
             'static void main(String[] args) ' +
             '{ X inst = new X();inst.meth();}}')
        self._check_output(p, 'X', '5')

    def test_field_ref_external(self):
        """Test access to an external field."""
        self._check_output_file('test_field_ref_external.jml', 'pass')

    def test_field_ref_static(self):
        """Test a reference to a static final field."""
        self._check_output_file('test_field_ref_static.jml', '10.5')

    def test_field_ref_super(self):
        """Test a reference to a field in the super class."""
        self._check_output_file('test_field_ref_super.jml', '10')

    def test_field_ref_private(self):
        """Test a reference to a private field in the current class."""
        p = ('class X { private int x; X() {x = 10;' + self._wrap_print('x') +
             '} static void main ' + '(String[] args) { new X();}}')
        self._check_output(p, 'X', '10')

    def test_lib_field_ref(self):
        """Test that a public field can be accessed that is in a library class.
        """
        p = ('class X {static void main(String[] args) { ' +
             'int x = Short.MAX_VALUE;' + self._wrap_print('x') + '}}')
        self._check_output(p, 'X', '32767')

    def test_var_dcl(self):
        """Test variable declarations."""
        p = self._wrap_stmts('int x = 1;' + self._wrap_print('x'))
        self._check_output(p, 'X', '1')

    def test_if(self):
        """Test a simple if statement."""
        p = self._wrap_stmts('int x = 2; if (x > 2) {' +
                             self._wrap_print('"fail"') + '} else {' +
                             self._wrap_print('"pass"') + '}')
        self._check_output(p, 'X', "pass")

    def test_nested_if(self):
        """Test nested if statements."""
        p = self._wrap_stmts('String s = "hello"; int x = 2; if (x > = 3) {' +
                             self._wrap_print('"fail"') +
                             '} else if (s == "hi") {' +
                             self._wrap_print('"fail2"') + '} else {' +
                             self._wrap_print('"pass"') + '}')
        self._check_output(p, 'X', 'pass')

    def test_while(self):
        """Test a while statement that increments an int and prints out it's
        value.
        """
        p = self._wrap_stmts('int i = 1; while (i <= 10) {' +
                             self._wrap_print('i') + 'i = i + 1;}')
        # The new lines are needed because each new print statement has
        # a new line character after it
        nl = os.linesep
        self._check_output(
            p, 'X', '1' + nl + '2' + nl + '3' + nl + '4' + nl + '5' + nl +
            '6' + nl + '7' + nl + '8' + nl + '9' + nl + '10')

    def test_for(self):
        """Test a for loop which loops five times, each time printing out "s".
        """
        p = self._wrap_stmts('String s = "s"; for (int i = 0; i <5; i++) {' +
                             self._wrap_print('s') + '}')
        nl = os.linesep
        self._check_output(p, 'X',
                           's' + nl + 's' + nl + 's' + nl + 's' + nl + 's')

    def test_or(self):
        """Test the boolean or operator by including it in an if statement
        where only one side is true.
        """
        p = self._wrap_stmts('int x = 1; if (x != 1 || x == 1) {' +
                             self._wrap_print('"pass"') + '}')
        self._check_output(p, 'X', 'pass')

    def test_and(self):
        """Test the boolean and operator by including it in an if statement
        where both sides are true.
        """
        p = self._wrap_stmts('int x = 1; if (x != 1 && x == 1) {' +
                             self._wrap_print('"fail"') + '} else {' +
                             self._wrap_print('"pass"') + '}')
        self._check_output(p, 'X', 'pass')

    # Equality and relational operators already tested in previous tests.

    def test_add(self):
        """Test simple addition in a print statement."""
        p = self._wrap_stmts(self._wrap_print('1 + 1'))
        self._check_output(p, 'X', '2')

    def test_concat(self):
        """Test concatination of two strings."""
        p = self._wrap_stmts(self._wrap_print('"fst" + "snd"'))
        self._check_output(p, 'X', 'fstsnd')

    def test_sub(self):
        """Test simple subtraction in a print statement."""
        p = self._wrap_stmts(self._wrap_print('1 - 1'))
        self._check_output(p, 'X', '0')

    def test_mul(self):
        """Test simple multiplication in a print statement."""
        p = self._wrap_stmts(self._wrap_print('2*2'))
        self._check_output(p, 'X', '4')

    def test_div(self):
        """Test simple division in a print statement."""
        p = self._wrap_stmts(self._wrap_print('4/2'))
        self._check_output(p, 'X', '2')

    def test_not(self):
        """Test that the not operator inverts the boolean expression: true."""
        p = self._wrap_stmts(self._wrap_print('!true'))
        self._check_output(p, 'X', 'false')

    def test_neg(self):
        """Test that the negative operator makes the expression negative."""
        p = self._wrap_stmts(self._wrap_print('-(2+2)'))
        self._check_output(p, 'X', '-4')

    def test_pos(self):
        """Test that the positive operator has no effect on the expression's
        sign.
        """
        p = self._wrap_stmts(self._wrap_print('+(-2)'))
        self._check_output(p, 'X', '-2')

    def test_inc(self):
        """Test that the increment operator increments by 1."""
        p = self._wrap_stmts('int x = 1; x++;' + self._wrap_print('x'))
        self._check_output(p, 'X', '2')

    def test_dec(self):
        """Test that the decrement operator decrements by 1."""
        p = self._wrap_stmts('int x = 1; --x;' + self._wrap_print('x'))
        self._check_output(p, 'X', '0')

    def test_brackets(self):
        """Test precedence rules are followed when parentheses are used."""
        p = self._wrap_stmts(self._wrap_print('(1+1)/1'))
        self._check_output(p, 'X', '2')

    def test_array(self):
        """Test a simple array creation and access."""
        p = self._wrap_stmts('String[] arr = new String[5];' +
                             'arr[0] = "Hello";' + self._wrap_print('arr[0]'))
        self._check_output(p, 'X', 'Hello')

    def test_multi_array(self):
        """Test creation and access of a multi-dimensional array."""
        p = self._wrap_stmts('int[][][] a = new int[5][10][1];' +
                             'a[2][1][0] = 10;' +
                             self._wrap_print('a[2][1][0]'))
        self._check_output(p, 'X', '10')

    def test_array_init_loop(self):
        """Test an array being initialised in a for loop and printed
        in a for loop.
        """
        p = self._wrap_stmts('long[] a = new long[5];' +
                             'for (int i = 0; i < 5; i++) { a[i] = i;}' +
                             'for (int j = 4; j >= 0; j--) {' +
                             self._wrap_print('a[j]') + '}')
        nl = os.linesep
        self._check_output(p, 'X',
                           '4' + nl + '3' + nl + '2' + nl + '1' + nl + '0')

    def test_matrix(self):
        """Test a simple matrix creation and access."""
        p = self._wrap_stmts('matrix m = |1,1|;' + 'm|0,0| = 10.5;' +
                             self._wrap_print('m|0,0|'))
        self._check_output(p, 'X', '10.5')

    def test_matrix_mult(self):
        """Test a matrix multiplication."""
        p = self._wrap_stmts('matrix a1 = |2, 2|;' + 'a1|0,0| = 1;' +
                             'a1|0,1| = 2;' + 'a1|1,0| = 3;' + 'a1|1,0| = 4;' +
                             'matrix a2 = |2, 1|;' + 'a2|0,0| = 1;' +
                             'a2|1,0| = 2;' + 'matrix a3;' + 'a3 = a1 * a2;' +
                             'PrintStream ps = System.out;' +
                             'for (int n = 0; n<a1.rowLength; n++) {' +
                             'for (int o=0; o<a2.colLength;o++) {' +
                             'ps.println(a3|n,o|); }}')
        self._check_output(p, 'X', '5.0' + os.linesep + '4.0')

    def test_matrix_mult_dimension_error(self):
        """Test an exception is thrown when the inner dimensions do not agree.
        """
        p = self._wrap_stmts('matrix a1 = |2, 2|;' + 'a1|0,0| = 1;' +
                             'a1|0,1| = 2;' + 'a1|1,0| = 3;' + 'a1|1,0| = 4;' +
                             'matrix a2 = |1, 1|;' + 'a2|0,0| = 1;' +
                             'matrix a3;' + 'a3 = a1 * a2;')
        self._check_output(
            p,
            'X',
            'Exception in thread "main" ' + 'java.lang.ArithmeticException: ' +
            'Inner matrix dimensions must match for ' + 'multiplication!' +
            os.linesep + '\tat X.main(X.j)',
            check_error=True)

    def test_matrix_add(self):
        """Test a matrix addition."""
        p = self._wrap_stmts('matrix a1 = |2, 2|;' + 'a1|0,0| = 1;' +
                             'a1|0,1| = 2;' + 'a1|1,0| = 3;' + 'a1|1,1| = 4;' +
                             'matrix a2 = |2, 2|;' + 'a2|0,0| = 1;' +
                             'a2|0,1| = 2;' + 'a2|1,0| = 3;' + 'a2|1,1| = 4;' +
                             'matrix a3;' + 'a3 = a1 + a2;' +
                             'PrintStream ps = System.out;' +
                             'for (int n = 0; n<a1.rowLength; n++) {' +
                             'for (int o=0; o<a2.colLength;o++) {' +
                             'ps.println(a3|n,o|); }}')
        nl = os.linesep
        self._check_output(p, 'X',
                           '2.0' + nl + '4.0' + nl + '6.0' + nl + '8.0')

    def test_matrix_add_dimension_error(self):
        """Test an exception is thrown when the dimensions are not the same
        for two matrices when subtracted.
        """
        p = self._wrap_stmts('matrix a1 = |2, 2|;' + 'a1|0,0| = 1;' +
                             'a1|0,1| = 2;' + 'a1|1,0| = 3;' + 'a1|1,0| = 4;' +
                             'matrix a2 = |1, 1|;' + 'a2|0,0| = 1;' +
                             'matrix a3;' + 'a3 = a1 - a2;')
        self._check_output(
            p,
            'X',
            'Exception in thread "main" ' + 'java.lang.ArithmeticException: ' +
            'Matrix dimensions must be equal for ' + 'addition/subtraction!' +
            os.linesep + '\tat X.main(X.j)',
            check_error=True)

    def test_command_line_args(self):
        """Test that command line arguments are correctly read."""
        p = ('class X { static void main(String[] args) {' +
             self._wrap_print('args[0]') + '}}')
        self._check_output(p, 'X', 'arg', args=['arg'])

    def _wrap_stmts(self, stmts):
        """Helper method used so lines of code can be tested
        without having to create a class and method for it to go in.
        """
        return ("""
                class X {
                    static void main(String[] args) {
                        """ + stmts + """
                    }
               }
               """)

    def _wrap_print(self, stmt):
        return ("""
                PrintStream ps = System.out;
                ps.println(""" + stmt + """);
                """)

    def _check_output(self,
                      program,
                      class_name,
                      exptd_result,
                      args=[],
                      check_error=False):
        """Checks the program was compiled correctly.
        Given a program to run the compiler on, this method checks the result
        printed when the JVM is run with the code generator's output.
        """
        output_dir = os.path.join(os.path.dirname(__file__), 'bin_test_files')
        # Remove any old asm and binary files
        self._cleanup(output_dir)
        # Run the compiler with the program
        self._code_gen.compile_(program, output_dir)
        # Run the JVM on the compiled file
        cmd = ['java', '-cp', output_dir, class_name] + args
        process = subprocess.Popen(cmd,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
        output = ''
        if not check_error:
            output = process.communicate()[0]
        else:
            output = process.communicate()[1]
        output = output.rstrip(os.linesep)

        #Check the printed results match the expected result
        self.assertEqual(output, exptd_result)

    def _check_output_file(self, file_name, exptd_result):
        """Helper method to allow the code generator to be run on a
        particular file, and the output checked
        """
        path = os.path.join(self._file_dir, file_name)
        # Remove the .jml extension
        class_name = file_name[:-4]
        return self._check_output(path, class_name, exptd_result)

    def _cleanup(self, dir_):
        """Removes all files in the directory."""
        for file_ in os.listdir(dir_):
            file_path = os.path.join(dir_, file_)
            try:
                os.remove(file_path)
            except OSError:
                # It was a Folder
                pass
class TestCodeGenerator(unittest.TestCase):
    """Test class where tests to be run are the methods."""
    def setUp(self):
        unittest.TestCase.setUp(self)
        # Get an interpreter object for convenience
        self._code_gen = CodeGenerator()
        # Get the directory to get test files from
        this_file_path = os.path.dirname(__file__)
        self._file_dir = os.path.join(this_file_path, 'test_files')

    def test_extends_field(self):
        """Test a subclass can use a field of a superclass."""
        self._check_output_file('test_extends_field.jml', '10')

    def test_extends_method(self):
        """Test a subclass can use a method of a superclass."""
        self._check_output_file('test_extends_method.jml', '10')

    def test_invoke_implemented_method(self):
        """Test the case where a class invokes a method of an interface which
        has been instantiated by a class.
        """
        self._check_output_file('test_invoke_implemented_method.jml', '10')

    def test_cons_param(self):
        """Test that constructor parameters can be correctly used."""
        p = ('class X { X(String x) {' + self._wrap_print('x') +
             '} static void main(String[] args) {X inst = new X("pass");}}')
        self._check_output(p, 'X', 'pass')

    def test_method_param(self):
        """Test that method parameters can be correctly used."""
        p = ('class X { void meth(String x){' + self._wrap_print('x') +
             '} static void main(String[] args) {' +
             'X inst = new X();inst.meth("pass");}}')
        self._check_output(p, 'X', 'pass')

    def test_super_cons_implicitly_invoked(self):
        """Test that the super constructor is implicitly invoked."""
        self._check_output_file('test_super_cons_implicitly_invoked.jml',
                                'pass')

    def test_super_cons_explicitly_invoked(self):
        """Test that the super classes constructor can be explicitly
        invoked correctly.
        """
        self._check_output_file('test_super_cons_explicitly_invoked.jml',
                                'pass')

    def test_method_call(self):
        """Test a simple method call to a method in the current class."""
        p = ('class X { void meth() { int x = meth2();' +
             self._wrap_print('x') + '} byte meth2() {return 5;} ' +
             'static void main(String[] args){X inst = new X();inst.meth();}}')
        self._check_output(p, 'X', '5')

    def test_method_return_array(self):
        """Test a method in the same class correctly returns an array."""
        p = ('class X { void meth() { byte[][] x = meth2();' +
             'int y = x[0][0];' + self._wrap_print('y') +
             '} byte[][] meth2() { byte[][] arr = new byte[5][5];'
             'arr[0][0] = 5; return arr;} static void main(String[] args) {' +
             'X inst = new X();inst.meth();}}')
        self._check_output(p, 'X', '5')

    def test_method_call_external(self):
        """Test that a external method call is correctly compiled."""
        self._check_output_file('test_method_call_external.jml','10.1')

    def test_method_call_static(self):
        """Test a static external method call."""
        self._check_output_file('test_method_call_static.jml', '10.0')

    def test_private_method_call(self):
        """Test an invocation of a private method in the current class.
        """
        p = ('class X { private void meth() {' + self._wrap_print('10') +
             '} static void main(String[] args) {' +
             'X inst = new X();inst.meth();}}')
        self._check_output(p, 'X', '10')

    def test_lib_method_call(self):
        """Test a call to a library class."""
        p = ('class X{static void main(String[] args) ' +
             '{Integer i = new Integer(5);' +
             self._wrap_print('i.intValue()') + '}}')
        self._check_output(p, 'X', '5')

    def test_lib_method_call_params(self):
        """Test a call to a library class with parameters."""
        p = ('class X {static void main(String[] args)' +
             '{String s = new String("pass");' +
             self._wrap_print('s.endsWith("s")') + '}}')
        self._check_output(p, 'X', 'true')

    def test_lib_method_in_super_call(self):
        """Test a method call to a library object where the method is defined
        in its super class.
        """
        p = ('class X{static void main(String[] args){' +
             self._wrap_print('Double.isNaN(1.1)') + '}}')
        self._check_output(p, 'X', 'false')

    def test_method_call_explicitly_super(self):
        """Test that the method in the super class is called, even when there
        is one with the same name in the current class.
        """
        p = ('class X {  X() {' +
             self._wrap_print('super.equals(new Object())') +
             '}static void main(String[] args){X x = new X();} ' +
             'boolean equals(Object x) {return true;}}')
        self._check_output(p, 'X', 'false')

    def test_lib_method_call_static(self):
        """Test a static method call. """
        p = ('class X {static void main(String[] args)' +
             '{GregorianCalendar x = new GregorianCalendar();' +
             self._wrap_print('x.isLenient()') + '}}')
        self._check_output(p, 'X', 'true')

    def test_field_access(self):
        """Test a simple field accessing in the same class."""
        p = ('class X { int f; void meth() { f = 5;' +
             self._wrap_print('f') + '} static void main(' +
             'String[] args) { X inst = new X();inst.meth();}}')
        self._check_output(p, 'X', '5')

    def test_field_inc(self):
        """Test incrementing a field in the current class."""
        p = ('class X { int f; void meth() {f = 5;f++;' +
             self._wrap_print('f') + '} static void main(' +
             'String[] args) {X inst = new X();inst.meth();}}')
        self._check_output(p, 'X', '6')

    def test_field_array(self):
        """Test a field of array type correctly compiles."""
        p = ('class X { int[] f; void meth() { f = new int[1];' +
             'f[0] = 5;' + self._wrap_print('f[0]') + '} ' +
             'static void main(String[] args) ' +
             '{ X inst = new X();inst.meth();}}')
        self._check_output(p, 'X', '5')

    def test_field_ref_external(self):
        """Test access to an external field."""
        self._check_output_file('test_field_ref_external.jml', 'pass')

    def test_field_ref_static(self):
        """Test a reference to a static final field."""
        self._check_output_file('test_field_ref_static.jml', '10.5')

    def test_field_ref_super(self):
        """Test a reference to a field in the super class."""
        self._check_output_file('test_field_ref_super.jml', '10')

    def test_field_ref_private(self):
        """Test a reference to a private field in the current class."""
        p = ('class X { private int x; X() {x = 10;' +
             self._wrap_print('x') + '} static void main ' +
             '(String[] args) { new X();}}')
        self._check_output(p, 'X', '10')

    def test_lib_field_ref(self):
        """Test that a public field can be accessed that is in a library class.
        """
        p = ('class X {static void main(String[] args) { '+
             'int x = Short.MAX_VALUE;'+ self._wrap_print('x') + '}}')
        self._check_output(p, 'X', '32767')

    def test_var_dcl(self):
        """Test variable declarations."""
        p = self._wrap_stmts('int x = 1;' + self._wrap_print('x'))
        self._check_output(p, 'X', '1')

    def test_if(self):
        """Test a simple if statement."""
        p = self._wrap_stmts('int x = 2; if (x > 2) {' +
                             self._wrap_print('"fail"') +
                             '} else {' + self._wrap_print('"pass"') + '}')
        self._check_output(p, 'X', "pass")

    def test_nested_if(self):
        """Test nested if statements."""
        p = self._wrap_stmts('String s = "hello"; int x = 2; if (x > = 3) {' +
                             self._wrap_print('"fail"') +
                             '} else if (s == "hi") {' +
                             self._wrap_print('"fail2"') +
                             '} else {' + self._wrap_print('"pass"') + '}')
        self._check_output(p, 'X', 'pass')

    def  test_while(self):
        """Test a while statement that increments an int and prints out it's
        value.
        """
        p = self._wrap_stmts('int i = 1; while (i <= 10) {' +
                             self._wrap_print('i') + 'i = i + 1;}')
        # The new lines are needed because each new print statement has
        # a new line character after it
        nl = os.linesep
        self._check_output(p, 'X', '1' + nl + '2' + nl + '3' + nl +
                           '4' + nl + '5' + nl + '6' + nl + '7' + nl +
                           '8' + nl + '9' + nl + '10')

    def  test_for(self):
        """Test a for loop which loops five times, each time printing out "s".
        """
        p = self._wrap_stmts('String s = "s"; for (int i = 0; i <5; i++) {' +
                             self._wrap_print('s') + '}')
        nl = os.linesep
        self._check_output(p, 'X', 's' + nl + 's' + nl + 's' + nl + 's' + nl +
                           's')

    def test_or(self):
        """Test the boolean or operator by including it in an if statement
        where only one side is true.
        """
        p = self._wrap_stmts('int x = 1; if (x != 1 || x == 1) {' +
                             self._wrap_print('"pass"') + '}')
        self._check_output(p, 'X', 'pass')

    def test_and(self):
        """Test the boolean and operator by including it in an if statement
        where both sides are true.
        """
        p = self._wrap_stmts('int x = 1; if (x != 1 && x == 1) {' +
                             self._wrap_print('"fail"') +
                             '} else {' + self._wrap_print('"pass"') + '}')
        self._check_output(p, 'X', 'pass')

    # Equality and relational operators already tested in previous tests.

    def test_add(self):
        """Test simple addition in a print statement."""
        p = self._wrap_stmts(self._wrap_print('1 + 1'))
        self._check_output(p, 'X', '2')

    def test_concat(self):
        """Test concatination of two strings."""
        p = self._wrap_stmts(self._wrap_print('"fst" + "snd"'))
        self._check_output(p, 'X', 'fstsnd')

    def test_sub(self):
        """Test simple subtraction in a print statement."""
        p = self._wrap_stmts(self._wrap_print('1 - 1'))
        self._check_output(p, 'X', '0')

    def test_mul(self):
        """Test simple multiplication in a print statement."""
        p = self._wrap_stmts(self._wrap_print('2*2'))
        self._check_output(p, 'X', '4')

    def test_div(self):
        """Test simple division in a print statement."""
        p = self._wrap_stmts(self._wrap_print('4/2'))
        self._check_output(p, 'X', '2')

    def test_not(self):
        """Test that the not operator inverts the boolean expression: true."""
        p = self._wrap_stmts(self._wrap_print('!true'))
        self._check_output(p, 'X', 'false')

    def test_neg(self):
        """Test that the negative operator makes the expression negative."""
        p = self._wrap_stmts(self._wrap_print('-(2+2)'))
        self._check_output(p, 'X', '-4')

    def test_pos(self):
        """Test that the positive operator has no effect on the expression's
        sign.
        """
        p = self._wrap_stmts(self._wrap_print('+(-2)'))
        self._check_output(p, 'X', '-2')

    def test_inc(self):
        """Test that the increment operator increments by 1."""
        p = self._wrap_stmts('int x = 1; x++;' + self._wrap_print('x'))
        self._check_output(p, 'X', '2')

    def test_dec(self):
        """Test that the decrement operator decrements by 1."""
        p = self._wrap_stmts('int x = 1; --x;' + self._wrap_print('x'))
        self._check_output(p, 'X', '0')

    def test_brackets(self):
        """Test precedence rules are followed when parentheses are used."""
        p = self._wrap_stmts(self._wrap_print('(1+1)/1'))
        self._check_output(p, 'X', '2')

    def test_array(self):
        """Test a simple array creation and access."""
        p = self._wrap_stmts('String[] arr = new String[5];' +
                             'arr[0] = "Hello";' + self._wrap_print('arr[0]'))
        self._check_output(p, 'X', 'Hello')

    def test_multi_array(self):
        """Test creation and access of a multi-dimensional array."""
        p = self._wrap_stmts('int[][][] a = new int[5][10][1];' +
                             'a[2][1][0] = 10;' +
                             self._wrap_print('a[2][1][0]'))
        self._check_output(p, 'X', '10')

    def test_array_init_loop(self):
        """Test an array being initialised in a for loop and printed
        in a for loop.
        """
        p = self._wrap_stmts('long[] a = new long[5];' +
                             'for (int i = 0; i < 5; i++) { a[i] = i;}' +
                             'for (int j = 4; j >= 0; j--) {' +
                             self._wrap_print('a[j]') + '}')
        nl = os.linesep
        self._check_output(p, 'X', '4' + nl + '3' + nl + '2' + nl + '1' + nl +
                           '0')

    def test_matrix(self):
        """Test a simple matrix creation and access."""
        p = self._wrap_stmts('matrix m = |1,1|;' +
                     'm|0,0| = 10.5;' +
                     self._wrap_print('m|0,0|'))
        self._check_output(p, 'X', '10.5')

    def test_matrix_mult(self):
        """Test a matrix multiplication."""
        p = self._wrap_stmts('matrix a1 = |2, 2|;' +
                             'a1|0,0| = 1;' +
                             'a1|0,1| = 2;' +
                             'a1|1,0| = 3;' +
                             'a1|1,0| = 4;' +
                             'matrix a2 = |2, 1|;' +
                             'a2|0,0| = 1;' +
                             'a2|1,0| = 2;' +
                             'matrix a3;' +
                             'a3 = a1 * a2;' +
                             'PrintStream ps = System.out;' +
                             'for (int n = 0; n<a1.rowLength; n++) {' +
                             'for (int o=0; o<a2.colLength;o++) {' +
                             'ps.println(a3|n,o|); }}')
        self._check_output(p, 'X', '5.0' + os.linesep + '4.0')

    def test_matrix_mult_dimension_error(self):
        """Test an exception is thrown when the inner dimensions do not agree.
        """
        p = self._wrap_stmts('matrix a1 = |2, 2|;' +
                             'a1|0,0| = 1;' +
                             'a1|0,1| = 2;' +
                             'a1|1,0| = 3;' +
                             'a1|1,0| = 4;' +
                             'matrix a2 = |1, 1|;' +
                             'a2|0,0| = 1;' +
                             'matrix a3;' +
                             'a3 = a1 * a2;')
        self._check_output(p, 'X', 'Exception in thread "main" ' +
                           'java.lang.ArithmeticException: ' +
                           'Inner matrix dimensions must match for ' +
                           'multiplication!' + os.linesep +
                           '\tat X.main(X.j)', check_error = True)

    def test_matrix_add(self):
        """Test a matrix addition."""
        p = self._wrap_stmts('matrix a1 = |2, 2|;' +
                             'a1|0,0| = 1;' +
                             'a1|0,1| = 2;' +
                             'a1|1,0| = 3;' +
                             'a1|1,1| = 4;' +
                             'matrix a2 = |2, 2|;' +
                             'a2|0,0| = 1;' +
                             'a2|0,1| = 2;' +
                             'a2|1,0| = 3;' +
                             'a2|1,1| = 4;' +
                             'matrix a3;' +
                             'a3 = a1 + a2;' +
                             'PrintStream ps = System.out;' +
                             'for (int n = 0; n<a1.rowLength; n++) {' +
                             'for (int o=0; o<a2.colLength;o++) {' +
                             'ps.println(a3|n,o|); }}')
        nl = os.linesep
        self._check_output(p, 'X', '2.0' + nl + '4.0' + nl + '6.0' +
                   nl + '8.0')

    def test_matrix_add_dimension_error(self):
        """Test an exception is thrown when the dimensions are not the same
        for two matrices when subtracted.
        """
        p = self._wrap_stmts('matrix a1 = |2, 2|;' +
                             'a1|0,0| = 1;' +
                             'a1|0,1| = 2;' +
                             'a1|1,0| = 3;' +
                             'a1|1,0| = 4;' +
                             'matrix a2 = |1, 1|;' +
                             'a2|0,0| = 1;' +
                             'matrix a3;' +
                             'a3 = a1 - a2;')
        self._check_output(p, 'X', 'Exception in thread "main" ' +
                           'java.lang.ArithmeticException: ' +
                           'Matrix dimensions must be equal for ' +
                           'addition/subtraction!' + os.linesep +
                           '\tat X.main(X.j)', check_error = True)

    def test_command_line_args(self):
        """Test that command line arguments are correctly read."""
        p = ('class X { static void main(String[] args) {' +
             self._wrap_print('args[0]') + '}}')
        self._check_output(p, 'X', 'arg', args = ['arg'])

    def _wrap_stmts(self, stmts):
        """Helper method used so lines of code can be tested
        without having to create a class and method for it to go in.
        """
        return ("""
                class X {
                    static void main(String[] args) {
                        """ +
                        stmts +
                        """
                    }
               }
               """)

    def _wrap_print(self, stmt):
        return ("""
                PrintStream ps = System.out;
                ps.println(""" + stmt + """);
                """)

    def _check_output(self, program, class_name, exptd_result, args = [],
                      check_error = False):
        """Checks the program was compiled correctly.
        Given a program to run the compiler on, this method checks the result
        printed when the JVM is run with the code generator's output.
        """
        output_dir = os.path.join(os.path.dirname(__file__), 'bin_test_files')
        # Remove any old asm and binary files
        self._cleanup(output_dir)
        # Run the compiler with the program
        self._code_gen.compile_(program, output_dir)
        # Run the JVM on the compiled file
        cmd = ['java', '-cp', output_dir, class_name] + args
        process = subprocess.Popen(cmd, stdout = subprocess.PIPE,
                                   stderr = subprocess.PIPE)
        output = ''
        if not check_error:
            output = process.communicate()[0]
        else:
            output = process.communicate()[1]
        output = output.rstrip(os.linesep)

        #Check the printed results match the expected result
        self.assertEqual(output, exptd_result)

    def _check_output_file(self, file_name, exptd_result):
        """Helper method to allow the code generator to be run on a
        particular file, and the output checked
        """
        path = os.path.join(self._file_dir, file_name)
        # Remove the .jml extension
        class_name = file_name[:-4]
        return self._check_output(path, class_name, exptd_result)

    def _cleanup(self, dir_):
        """Removes all files in the directory."""
        for file_ in os.listdir(dir_):
            file_path = os.path.join(dir_, file_)
            try:
                os.remove(file_path)
            except OSError:
                # It was a Folder
                pass
Ejemplo n.º 5
0
"""This allows the program to be run.  It will either compile the file, or
print error information.

Usage: jamlcomp <file> [<output directory>]
"""
import sys
from code_generation.code_generator import CodeGenerator

if __name__ == '__main__':
    try:
        if len(sys.argv) == 2:
            CodeGenerator().compile_(sys.argv[1])
        elif len(sys.argv) == 3:
            CodeGenerator().compile_(sys.argv[1], sys.argv[2])
        else:
            print 'Usage: jamlcomp <file> [<output directory>]'
    except Exception as error:
        print 'Compilation error! Message: "' + str(error) + '"'