/** * */ package it.xsemantics.example.fj.tests; import it.xsemantics.example.fj.typing.FJTypeChecker; import java.io.IOException; import it.xsemantics.example.fj.fj.Class; import it.xsemantics.example.fj.fj.Expression; import it.xsemantics.example.fj.fj.Field; import it.xsemantics.example.fj.fj.Method; import it.xsemantics.example.fj.fj.Program; import it.xsemantics.example.fj.lookup.FjAuxiliaryFunctions; /** * @author bettini * * Tests for validation (type checking) */ public class TypeCheckerTest extends TestWithLoader { FJTypeChecker fixture; public TypeCheckerTest(String name) { super(name); } protected void setUp() throws Exception { super.setUp(); fixture = getTypeChecker(); } protected void tearDown() throws Exception { super.tearDown(); } /** * Checks method body * * @throws IOException */ public void testMethod() throws IOException { Program program = getProgramFromString("class A { Object f; Object m() {return this.f;} }"); Method method = FjAuxiliaryFunctions.selectMethods(program.getClasses().get(0)).get(0); String errors = fixture.typeCheck(method); assertEquals("", errors); } /** * Checks method body where body is a subtype of return type * * @throws IOException */ public void testMethodWithSubtyping() throws IOException { Program program = getProgramFromString("class B {} class A extends B { A a; B m() {return this.a;} }"); Method method = FjAuxiliaryFunctions.selectMethods(program.getClasses().get(1)).get(0); String errors = fixture.typeCheck(method); assertTrue(errors.length() == 0); } /** * body returns a basic type which is not subtype of class type * * @throws IOException */ public void testMethodFail() throws IOException { Program program = getProgramFromString("class A { int f; Object m() {return this.f;} }"); Method method = FjAuxiliaryFunctions.selectMethods(program.getClasses().get(0)).get(0); String errors = fixture.typeCheck(method); System.out.println("error: " + errors); assertTrue(errors.length() != 0); assertEquals("rule TMethodOk cannot type: |- Method 'm' : OK", errors); } /** * incorrect method override (different return types) * * @throws IOException */ public void testOverriddenFail() throws IOException { Program program = getProgramFromString("class B { B m() {return new B();} } class A extends B { Object f; Object m() {return this.f;} }"); Method method = FjAuxiliaryFunctions.selectMethods(program.getClasses().get(1)).get(0); String errors = fixture.typeCheck(method); System.out.println("error: " + errors); assertTrue(errors.length() != 0); assertEquals("rule TMethodOk cannot type: |- Method 'm' : OK", errors); } /** * incorrect method override (different parameters) * * @throws IOException */ public void testOverriddenFail2() throws IOException { Program program = getProgramFromString("class B { B m(Object a, int i) {return new B();} } class A extends B { Object f; B m(Object a, String s) {return this.f;} }"); Method method = FjAuxiliaryFunctions.selectMethods(program.getClasses().get(1)).get(0); String errors = fixture.typeCheck(method); System.out.println("error: " + errors); assertTrue(errors.length() != 0); assertEquals("rule TMethodOk cannot type: |- Method 'm' : OK", errors); } /** * correct method override * * @throws IOException */ public void testOverridden() throws IOException { Program program = getProgramFromString("class B { B m(Object a, int i) {return new B();} } class A extends B { Object f; B m(Object c, int s) {return new B();} }"); Method method = FjAuxiliaryFunctions.selectMethods(program.getClasses().get(1)).get(0); String errors = fixture.typeCheck(method); System.out.println("error: " + errors); assertTrue(errors.length() == 0); } /** * passing the wrong number of arguments to a method * * @throws IOException */ public void testWrongNumberOfArgs() throws IOException { Program program = getProgramFromString("class A { int f; Object m() {return this.m(new A());} }"); Expression body = FjAuxiliaryFunctions.selectMethods(program.getClasses().get(0)).get(0) .getBody().getExpression(); String errors = fixture.typeCheck(body); System.out.println("error: " + errors); assertTrue(errors.length() != 0); assertEquals( "rule TSelection cannot type: [this -> ClassType 'Class 'A''] |- Selection 'This 'this'' : Type", errors); } /** * passing the argument with wrong type * * @throws IOException */ public void testWrongArguments() throws IOException { Program program = getProgramFromString("class A { int f; Object m(Object a) {return this.m(this.f);} }"); Expression body = FjAuxiliaryFunctions.selectMethods(program.getClasses().get(0)).get(0) .getBody().getExpression(); String errors = fixture.typeCheck(body); System.out.println("error: " + errors); assertTrue(errors.length() != 0); assertEquals( "rule TSelection cannot type: [this -> ClassType 'Class 'A''] |- Selection 'This 'this'' : Type", errors); } /** * passing the arguments with right types * * @throws IOException */ public void testCorrectArguments() throws IOException { Program program = getProgramFromString("class A { int f; Object m(Object a, int i) {return this.m(new A(10), this.f);} }"); Expression body = FjAuxiliaryFunctions.selectMethods(program.getClasses().get(0)).get(0) .getBody().getExpression(); String errors = fixture.typeCheck(body); System.out.println("error: " + errors); assertTrue(errors.length() == 0); } /** * Test for duplicate fields in a class and in a hierarchy * * @throws IOException */ public void testDuplicateFields() throws IOException { Program program = getProgramFromString("class B { int h; } class A extends B { int f; int g; int f; Object h; }"); Class classA = program.getClasses().get(1); Field f1 = FjAuxiliaryFunctions.selectFields(classA).get(0); String errors = fixture.typeCheck(f1); System.out.println("error: " + errors); assertTrue(errors.length() > 0); assertEquals("rule TFieldOk cannot type: [this -> ClassType 'Class 'A''] |- Field 'f' : OK", errors); f1 = FjAuxiliaryFunctions.selectFields(classA).get(2); errors = fixture.typeCheck(f1); System.out.println("error: " + errors); assertTrue(errors.length() > 0); assertEquals("rule TFieldOk cannot type: [this -> ClassType 'Class 'A''] |- Field 'f' : OK", errors); f1 = FjAuxiliaryFunctions.selectFields(classA).get(1); errors = fixture.typeCheck(f1); System.out.println("error: " + errors); assertTrue(errors.length() == 0); f1 = FjAuxiliaryFunctions.selectFields(classA).get(3); errors = fixture.typeCheck(f1); System.out.println("error: " + errors); assertTrue(errors.length() > 0); assertEquals("rule TFieldOk cannot type: [this -> ClassType 'Class 'A''] |- Field 'h' : OK", errors); } public void testDuplicateMethods() throws IOException { Program program = getProgramFromString("class A { A m() { return this; } Object m() { return this; } Object n() { return this; } }"); Class classA = program.getClasses().get(0); Method m = FjAuxiliaryFunctions.selectMethods(classA).get(0); String errors = fixture.typeCheck(m); System.out.println("error: " + errors); assertTrue(errors.length() > 0); assertEquals("rule TMethodOk cannot type: |- Method 'm' : OK", errors); m = FjAuxiliaryFunctions.selectMethods(classA).get(1); errors = fixture.typeCheck(m); System.out.println("error: " + errors); assertTrue(errors.length() > 0); assertEquals("rule TMethodOk cannot type: |- Method 'm' : OK", errors); m = FjAuxiliaryFunctions.selectMethods(classA).get(2); errors = fixture.typeCheck(m); System.out.println("error: " + errors); assertTrue(errors.length() == 0); } public void testNew() throws IOException { Program program = getProgramFromString("class A { int f; Object a; A m() { return new A(this.f, this.a); } }"); Class classA = program.getClasses().get(0); Method m = FjAuxiliaryFunctions.selectMethods(classA).get(0); Expression n = m.getBody().getExpression(); String errors = fixture.typeCheck(n); System.out.println("errors: " + errors); assertTrue(errors.length() == 0); } public void testNewInheritance() throws IOException { Program program = getProgramFromString("class B { Object c; } class A extends B { int f; Object a; A m() { return new A(this.a, this.f, this.a); } }"); Class classA = program.getClasses().get(1); Method m = FjAuxiliaryFunctions.selectMethods(classA).get(0); Expression n = m.getBody().getExpression(); String errors = fixture.typeCheck(n); assertEquals("", errors); } /** * wrong new invocation missing one argument * * @throws IOException */ public void testNewFail() throws IOException { Program program = getProgramFromString("class A { int f; Object a; A m() { return new A(this.f); } }"); Class classA = program.getClasses().get(0); Method m = FjAuxiliaryFunctions.selectMethods(classA).get(0); Expression n = m.getBody().getExpression(); String errors = fixture.typeCheck(n); System.out.println("errors: " + errors); assertTrue(errors.length() > 0); assertEquals("rule TNew cannot type: [this -> ClassType 'Class 'A''] |- New 'ClassType 'Class 'A''' : Type", errors); } /** * wrong new invocation, type mismatch, (with inherited fields) * * @throws IOException */ public void testNewInheritanceFail() throws IOException { Program program = getProgramFromString("class B { Object g; } class A extends B { int f; Object a; A m() { return new A(this.a, this.f, this.f); } }"); Class classA = program.getClasses().get(1); Method m = FjAuxiliaryFunctions.selectMethods(classA).get(0); Expression n = m.getBody().getExpression(); String errors = fixture.typeCheck(n); System.out.println("errors: " + errors); assertTrue(errors.length() > 0); assertEquals("rule TNew cannot type: [this -> ClassType 'Class 'A''] |- New 'ClassType 'Class 'A''' : Type", errors); } /** * test for correct cast * * @throws IOException */ public void testCast() throws IOException { Program program = getProgramFromString("class B { } class A extends B { } (A) new B()"); Expression main = program.getMain(); String errors = fixture.typeCheck(main); System.out.println("errors: " + errors); assertTrue(errors.length() == 0); } /** * test for correct cast * * @throws IOException */ public void testCast2() throws IOException { Program program = getProgramFromString("class B { } class A extends B { } (B) new A()"); Expression main = program.getMain(); String errors = fixture.typeCheck(main); System.out.println("errors: " + errors); assertTrue(errors.length() == 0); } /** * test for incorrect cast * * @throws IOException */ public void testCastFail() throws IOException { Program program = getProgramFromString("class B { } class A { } (B) new A()"); Expression main = program.getMain(); String errors = fixture.typeCheck(main); System.out.println("errors: " + errors); assertTrue(errors.length() > 0); assertEquals("invalid cast", errors); } /** * Detects simple cycle in the hierarchy * * <pre> * class A extends A {} new A() * </pre> * * @throws IOException */ public void testSimpleCycleAndNew() throws IOException { Program program = loadProgramFromString("class A extends A {} new A()"); String errors = getTypeChecker().typeCheck(program.getClasses().get(0)); assertEquals("class hierarchy is not acyclic for Class 'A'", errors); } /** * Detects duplicate classes in a program * * @throws IOException */ public void testDuplicateClasses() throws IOException { Program program = loadProgramFromString("class A {} class B {} class C {} class A{}"); String errors = getTypeChecker().typeCheck(program); // duplicate classes is now handeld by the validator directly assertEquals("", errors); } /** * body returns a basic type which is not subtype of the method return type * * @throws IOException */ public void testWrongConstant() throws IOException { Program program = getProgramFromString("class A { int m() {return true;} }"); Method method = FjAuxiliaryFunctions.selectMethods(program.getClasses().get(0)).get(0); String errors = fixture.typeCheck(method); System.out.println("error: " + errors); assertTrue(errors.length() != 0); assertEquals("rule TMethodOk cannot type: |- Method 'm' : OK", errors); } }