/* * Copyright 2009-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.eclipse.jdt.groovy.core.tests.basic; import static org.eclipse.jdt.core.tests.util.GroovyUtils.isAtLeastGroovy; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.control.CompilationUnit; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; public final class TraitsTests extends GroovyCompilerTestSuite { public TraitsTests(long level) { super(level); } @Before public void setUp() { assumeTrue(isAtLeastJava(JDK6)); assumeTrue(isAtLeastGroovy(23)); } @Test public void testTraits1() { String[] sources = { "Test.groovy", "class Person implements Greetable {\n" + " String name() { 'Bob' }\n" + "}\n" + "public class Test {\n" + " public static void main(String[] argv) {\n" + " def p = new Person()\n" + " print p.greeting()\n" + " }\n" + "}", "Greetable.groovy", "trait Greetable {\n" + " abstract String name()\n" + " String greeting() { \"Hello, ${name()}!\" }\n" + "}" }; runConformTest(sources, "Hello, Bob!"); ClassNode classNode = getCUDeclFor("Greetable.groovy").getCompilationUnit().getClassNode("Greetable"); assertTrue(classNode.isInterface()); } @Test public void testTraits1a() { String[] sources = { "Test.groovy", "class Person implements Greetable {\n" + " String name() { 'Bob' }\n" + "}\n" + "public class Test {\n" + " public static void main(String[] argv) {\n" + " def p = new Person()\n" + " print p.greeting()\n" + " }\n" + "}", "Greetable.groovy", "import groovy.transform.Trait;\n" + "@Trait\n" + "class Greetable {\n" + " abstract String name()\n" + " String greeting() { \"Hello, ${name()}!\" }\n" + "}" }; runConformTest(sources, "Hello, Bob!"); ClassNode classNode = getCUDeclFor("Greetable.groovy").getCompilationUnit().getClassNode("Greetable"); assertTrue(classNode.isInterface()); } @Test // Abstract Methods public void testTraits2() { String[] sources = { "A.groovy", "trait Greetable {\n" + " abstract String name()\n" + " String greeting() { \"Hello, ${name()}!\" }\n" + "}\n" + "class Person implements Greetable {\n" + " String name() { 'Bob' }\n" + "}\n" + "def p = new Person()\n" + "print p.greeting()" }; runConformTest(sources, "Hello, Bob!"); } @Test // Private Methods - positive test public void testTraits3() { String[] sources = { "A.groovy", "trait Greeter {\n" + " private String greetingMessage() {\n" + " 'Hello from a private method!'\n" + " }\n" + " String greet() {\n" + " def m = greetingMessage()\n" + " println m\n" + " m\n" + " }\n" + "}\n" + "class GreetingMachine implements Greeter {}\n" + "def g = new GreetingMachine()\n" + "g.greet()" }; runConformTest(sources, "Hello from a private method!"); } @Test // Private Methods - negative test public void testTraits4() { String[] sources = { "A.groovy", "trait Greeter {\n" + " private String greetingMessage() {\n" + " 'Hello from a private method!'\n" + " }\n" + " String greet() {\n" + " def m = greetingMessage()\n" + " println m\n" + " m\n" + " }\n" + "}\n" + "class GreetingMachine implements Greeter {}\n" + "def g = new GreetingMachine()\n" + "try {\n" + " g.greetingMessage()\n" + "} catch (MissingMethodException e) {\n" + "}" }; runConformTest(sources); } @Test // Meaning of this public void testTraits5() { String[] sources = { "A.groovy", "trait Introspector {\n" + " def whoAmI() { this.getClass() }\n" + "}\n" + "class Foo implements Introspector {}\n" + "def foo = new Foo()\n" + "print foo.whoAmI()" }; runConformTest(sources, "class Foo"); } @Test // Interfaces public void testTraits6() { String[] sources = { "A.groovy", "interface Named {\n" + " String name()\n" + "}\n" + "trait Greetable implements Named {\n" + " String greeting() { \"Hello, ${name()}!\" }\n" + "}\n" + "class Person implements Greetable {\n" + " String name() { 'Bob' }\n" + "}\n" + "def p = new Person()\n" + "print p.greeting()" }; runConformTest(sources, "Hello, Bob!"); CompilationUnit unit = getCUDeclFor("A.groovy").getCompilationUnit(); ClassNode classNode = unit.getClassNode("Person"); ClassNode type = unit.getClassNode("Greetable"); assertTrue(classNode.implementsInterface(type)); type = unit.getClassNode("Named"); assertTrue(classNode.implementsInterface(type)); } @Test // Properties public void testTraits7() { String[] sources = { "A.groovy", "trait Named {\n" + " String name\n" + "}\n" + "class Person implements Named {}\n" + "def p = new Person(name: 'Bob')\n" + "print p.name == 'Bob'\n" + "print p.getName()" }; runConformTest(sources, "trueBob"); } @Test // Private fields public void testTraits8() { String[] sources = { "A.groovy", "trait Counter {\n" + " private int count = 0\n" + " int count() { count += 1; count }\n" + "}\n" + "class Foo implements Counter {}\n" + "def f = new Foo()\n" + "print f.count()" }; runConformTest(sources, "1"); } @Test // Public fields public void testTraits9() { String[] sources = { "A.groovy", "trait Named {\n" + " public String name\n" + "}\n" + "class Person implements Named {}\n" + "def p = new Person()\n" + "p.Named__name = 'Bob'\n" + "print p.Named__name" }; runConformTest(sources, "Bob"); } @Test // Composition of Behaviors public void testTraits10() { String[] sources = { "A.groovy", "trait FlyingAbility {\n" + " String fly() { \"I'm flying!\" }\n" + "}\n" + "trait SpeakingAbility {\n" + " String speak() { \"I'm speaking!\" }\n" + "}\n" + "class Duck implements FlyingAbility, SpeakingAbility {}\n" + "def d = new Duck()\n" + "print d.fly()\n" + "print d.speak()" }; runConformTest(sources, "I'm flying!I'm speaking!"); } @Test // Overriding default methods public void testTraits11() { String[] sources = { "A.groovy", "trait FlyingAbility {\n" + " String fly() { \"I'm flying!\" }\n" + "}\n" + "trait SpeakingAbility {\n" + " String speak() { \"I'm speaking!\" }\n" + "}\n" + "class Duck implements FlyingAbility, SpeakingAbility {\n" + " String quack() { \"Quack!\" }\n" + " String speak() { quack() }\n" + "}\n" + "def d = new Duck()\n" + "print d.fly()\n" + "print d.quack()\n" + "print d.speak()" }; runConformTest(sources, "I'm flying!Quack!Quack!"); } @Test // Simple Inheritance public void testTraits12() { String[] sources = { "A.groovy", "trait Named {\n" + " String name\n" + "}\n" + "trait Polite extends Named {\n" + " String introduce() { \"Hello, I am $name\" }\n" + "}\n" + "class Person implements Polite {}\n" + "def p = new Person(name: 'Alice')\n" + "print p.introduce()" }; runConformTest(sources, "Hello, I am Alice"); } @Test // Multiple Inheritance public void testTraits13() { String[] sources = { "A.groovy", "trait WithId {\n" + " Long id\n" + "}\n" + "trait WithName {\n" + " String name\n" + "}\n" + "trait Identified implements WithId, WithName {\n" + "}" }; runConformTest(sources); } @Test // Dynamic code public void testTraits14() { String[] sources = { "A.groovy", "trait SpeakingDuck {\n" + " String speak() { quack() }\n" + "}\n" + "class Duck implements SpeakingDuck {\n" + " String methodMissing(String name, args) {\n" + " \"${name.capitalize()}!\"\n" + " }\n" + "}\n" + "def d = new Duck()\n" + "print d.speak()" }; runConformTest(sources, "Quack!"); } @Test // Dynamic methods in trait public void testTraits15() { String[] sources = { "A.groovy", "trait DynamicObject {\n" + " private Map props = [:]\n" + " def methodMissing(String name, args) {\n" + " name.toUpperCase()\n" + " }\n" + " def propertyMissing(String prop) {\n" + " props['prop']\n" + " }\n" + " void setProperty(String prop, Object value) {\n" + " props['prop'] = value\n" + " }\n" + "}\n" + "class Dynamic implements DynamicObject {\n" + " String existingProperty = 'ok'\n" + " String existingMethod() { 'ok' }\n" + "}\n" + "def d = new Dynamic()\n" + "print d.existingProperty\n" + "print d.foo\n" + "d.foo = 'bar'\n" + "print d.foo\n" + "print d.existingMethod()\n" + "print d.someMethod()" }; runConformTest(sources, "oknullbarokSOMEMETHOD"); } @Test // Multiple inheritance conflicts - Default conflict resolution public void testTraits16() { String[] sources = { "Sample.groovy", "trait A {\n" + " String exec() { 'A' }\n" + "}\n" + "trait B {\n" + " String exec() { 'B' }\n" + "}\n" + "class C implements A, B {}\n" + "def c = new C()\n" + "print c.exec()" }; runConformTest(sources, "B"); } @Test // Multiple inheritance conflicts - Default conflict resolution public void testTraits17() { String[] sources = { "Sample.groovy", "trait A {\n" + " String exec() { 'A' }\n" + "}\n" + "trait B {\n" + " String exec() { 'B' }\n" + "}\n" + "class C implements B, A {}\n" + "def c = new C()\n" + "print c.exec()" }; runConformTest(sources, "A"); } @Test // Multiple inheritance conflicts - User conflict resolution public void testTraits18() { String[] sources = { "Sample.groovy", "trait A {\n" + " String exec() { 'A' }\n" + "}\n" + "trait B {\n" + " String exec() { 'B' }\n" + "}\n" + "class C implements A, B {\n" + " String exec() { A.super.exec() }\n" + "}\n" + "def c = new C()\n" + "print c.exec()" }; runConformTest(sources, "A"); } @Test // Implementing a trait at runtime public void testTraits19() { String[] sources = { "Sample.groovy", "trait Extra {\n" + " String extra() { 'Extra' }\n" + "}\n" + "class Something {\n" + " String doSomething() { 'Something' }\n" + "}\n" + "def s = new Something() as Extra\n" + "print s.extra()\n" + "print s.doSomething()" }; runConformTest(sources, "ExtraSomething"); } @Test // Implementing multiple traits at once - negative public void testTraits20() { String[] sources = { "Sample.groovy", "trait A { String methodFromA() { 'A' } }\n" + "trait B { String methodFromB() { 'B' } }\n" + "class C {}\n" + "def c = new C()\n" + "print c.methodFromA()\n" + "print c.methodFromB()" }; runConformTest(sources, "", "groovy.lang.MissingMethodException: No signature of method: C.methodFromA() is applicable for argument types: () values: []"); } @Test // Implementing multiple traits at once - positive public void testTraits21() { String[] sources = { "Sample.groovy", "trait A { String methodFromA() { 'A' } }\n" + "trait B { String methodFromB() { 'B' } }\n" + "class C {}\n" + "def c = new C()\n" + "def d = c.withTraits A, B\n" + "print d.methodFromA()\n" + "print d.methodFromB()" }; runConformTest(sources, "AB"); } @Test // Chaining behavior public void testTraits22() { String[] sources = { "Sample.groovy", "interface MessageHandler {\n" + " void on(String message, Map payload)\n" + "}\n" + "trait DefaultHandler implements MessageHandler {\n" + " void on(String message, Map payload) {\n" + " println \"Received $message with payload $payload\"\n" + " }\n" + "}\n" + "class SimpleHandler implements DefaultHandler {}\n" + "def handler = new SimpleHandler()\n" + "handler.on('test logging', [:])" }; runConformTest(sources, "Received test logging with payload [:]"); } @Test // Chaining behavior public void testTraits23() { String[] sources = { "Sample.groovy", "interface MessageHandler {\n" + " void on(String message, Map payload)\n" + "}\n" + "trait DefaultHandler implements MessageHandler {\n" + " void on(String message, Map payload) {\n" + " println \"Received $message with payload $payload\"\n" + " }\n" + "}\n" + "class SimpleHandlerWithLogging implements DefaultHandler {\n" + " void on(String message, Map payload) {\n" + " println \"Seeing $message with payload $payload\"\n" + " DefaultHandler.super.on(message, payload)\n" + " }\n" + "}\n" + "def handler = new SimpleHandlerWithLogging()\n" + "handler.on('test logging', [:])" }; runConformTest(sources, "Seeing test logging with payload [:]\nReceived test logging with payload [:]"); } @Test // Chaining behavior public void testTraits24() { String[] sources = { "Sample.groovy", "interface MessageHandler {\n" + " void on(String message, Map payload)\n" + "}\n" + "trait DefaultHandler implements MessageHandler {\n" + " void on(String message, Map payload) {\n" + " println \"Received $message with payload $payload\"\n" + " }\n" + "}\n" + "trait SayHandler implements MessageHandler {\n" + " void on(String message, Map payload) {\n" + " if (message.startsWith('say')) {\n" + " println \"I say ${message - 'say'}!\"\n" + " } else {\n" + " super.on(message, payload)\n" + " }\n" + " }\n" + "}\n" + "trait LoggingHandler implements MessageHandler {\n" + " void on(String message, Map payload) {\n" + " println \"Seeing $message with payload $payload\"\n" + " super.on(message, payload)\n" + " }\n" + "}\n" + "class Handler implements DefaultHandler, SayHandler, LoggingHandler {}\n" + "def handler = new Handler()\n" + "handler.on('foo', [:])\n" + "handler.on('sayHello', [:])" }; runConformTest(sources, "Seeing foo with payload [:]\nReceived foo with payload [:]\nSeeing sayHello with payload [:]\nI say Hello!"); } @Test // Chaining behavior public void testTraits25() { String[] sources = { "Sample.groovy", "interface MessageHandler {\n" + " void on(String message, Map payload)\n" + "}\n" + "trait DefaultHandler implements MessageHandler {\n" + " void on(String message, Map payload) {\n" + " println \"Received $message with payload $payload\"\n" + " }\n" + "}\n" + "trait SayHandler implements MessageHandler {\n" + " void on(String message, Map payload) {\n" + " if (message.startsWith('say')) {\n" + " println \"I say ${message - 'say'}!\"\n" + " } else {\n" + " super.on(message, payload)\n" + " }\n" + " }\n" + "}\n" + "trait LoggingHandler implements MessageHandler {\n" + " void on(String message, Map payload) {\n" + " println \"Seeing $message with payload $payload\"\n" + " super.on(message, payload)\n" + " }\n" + "}\n" + "class AlternateHandler implements DefaultHandler, LoggingHandler, SayHandler {}\n" + "def handler = new AlternateHandler()\n" + "handler.on('foo', [:])\n" + "handler.on('sayHello', [:])" }; runConformTest(sources, "Seeing foo with payload [:]\nReceived foo with payload [:]\nI say Hello!"); } @Test // Chaining behavior - Semantics of super inside a trait public void testTraits26() { String[] sources = { "Sample.groovy", "trait Filtering {\n" + " StringBuilder append(String str) {\n" + " def subst = str.replace('o', '')\n" + " super.append(subst)\n" + " }\n" + " String toString() { super.toString() }\n" + "}\n" + "def sb = new StringBuilder().withTraits Filtering\n" + "sb.append('Groovy')\n" + "print sb.toString()" }; runConformTest(sources, "Grvy"); } @Test // SAM type coercion public void testTraits27() { String[] sources = { "Sample.groovy", "trait Greeter {\n" + " String greet() { \"Hello $name\" }\n" + " abstract String getName()\n" + "}\n" + "Greeter greeter = { 'Alice' }\n" + "print greeter.getName()" }; runConformTest(sources, "Alice"); } @Test // SAM type coercion public void testTraits28() { String[] sources = { "Sample.groovy", "trait Greeter {\n" + " String greet() { \"Hello $name\" }\n" + " abstract String getName()\n" + "}\n" + "void greet(Greeter g) { println g.greet() }\n" + "greet { 'Alice' }" }; runConformTest(sources, "Hello Alice"); } @Test // Differences with Java 8 default methods public void testTraits29() { String[] sources = { "Sample.groovy", "class Person {\n" + " String name\n" + "}\n" + "trait Bob {\n" + " String getName() { 'Bob' }\n" + "}\n" + "def p = new Person(name: 'Alice')\n" + "print p.name\n" + "def p2 = p as Bob\n" + "print p2.name" }; runConformTest(sources, "AliceBob"); } @Test // Differences with mixins public void testTraits30() { String[] sources = { "Sample.groovy", "class A { String methodFromA() { 'A' } }\n" + "class B { String methodFromB() { 'B' } }\n" + "A.metaClass.mixin B\n" + "def o = new A()\n" + "print o.methodFromA()\n" + "print o.methodFromB()\n" + "print(o instanceof A)\n" + "print(o instanceof B)" }; runConformTest(sources, "ABtruefalse"); } @Test // Static methods, properties and fields public void testTraits31() { String[] sources = { "Sample.groovy", "trait TestHelper {\n" + " public static boolean called = false\n" + " static void init() {\n" + " called = true\n" + " }\n" + "}\n" + "class Foo implements TestHelper {}\n" + "Foo.init()\n" + "print Foo.TestHelper__called" }; runConformTest(sources, "true"); } @Test // Static methods, properties and fields public void testTraits32() { String[] sources = { "Sample.groovy", "trait TestHelper {\n" + " public static boolean called = false\n" + " static void init() {\n" + " called = true\n" + " }\n" + "}\n" + "class Bar implements TestHelper {}\n" + "class Baz implements TestHelper {}\n" + "Bar.init()\n" + "print Bar.TestHelper__called\n" + "print Baz.TestHelper__called" }; runConformTest(sources, "truefalse"); } @Test // Inheritance of state gotchas public void testTraits33() { String[] sources = { "Sample.groovy", "trait IntCouple {\n" + " int x = 1\n" + " int y = 2\n" + " int sum() { x+y }\n" + "}\n" + "class BaseElem implements IntCouple {\n" + " int f() { sum() }\n" + "}\n" + "def base = new BaseElem()\n" + "print base.f()" }; runConformTest(sources, "3"); } @Test // Inheritance of state gotchas public void testTraits34() { String[] sources = { "Sample.groovy", "trait IntCouple {\n" + " int x = 1\n" + " int y = 2\n" + " int sum() { x+y }\n" + "}\n" + "class Elem implements IntCouple {\n" + " int x = 3\n" + " int y = 4\n" + " int f() { sum() }\n" + "}\n" + "def elem = new Elem()\n" + "print elem.f()" }; runConformTest(sources, "3"); } @Test // Inheritance of state gotchas public void testTraits35() { String[] sources = { "Sample.groovy", "trait IntCouple {\n" + " int x = 1\n" + " int y = 2\n" + " int sum() { getX() + getY() }\n" + "}\n" + "class Elem implements IntCouple {\n" + " int x = 3\n" + " int y = 4\n" + " int f() { sum() }\n" + "}\n" + "def elem = new Elem()\n" + "print elem.f()" }; runConformTest(sources, "7"); } @Test // Limitations - Prefix and postfix operations public void testTraits36() { String[] sources = { "Sample.groovy", "trait Counting {\n" + " int x\n" + " void inc() {\n" + " x++\n" + " }\n" + " void dec() {\n" + " --x\n" + " }\n" + "}\n" + "class Counter implements Counting {}\n" + "def c = new Counter()\n" + "c.inc()" }; runNegativeTest(sources, "----------\n" + "1. ERROR in Sample.groovy (at line 4)\n" + "\tx++\n" + "\t ^\n" + "Groovy:Postfix expressions on trait fields/properties are not supported in traits. @ line 4, column 6.\n" + "----------\n" + "2. ERROR in Sample.groovy (at line 7)\n" + "\t--x\n" + "\t^\n" + "Groovy:Prefix expressions on trait fields/properties are not supported in traits. @ line 7, column 5.\n" + "----------\n"); } @Test // Test @Trait annotation public void testTraits37() { String[] sources = { "Sample.groovy", "@groovy.transform.Trait\n" + "class MyTrait {\n" + " def m() { 'a' }\n" + "}\n" + "class MyClass implements MyTrait {\n" + "}\n" + "def myClass = new MyClass()\n" + "print myClass.m()" }; runConformTest(sources, "a"); } @Test // Test @Trait annotation public void testTraits38() { String[] sources = { "Sample.groovy", "import groovy.transform.Trait\n" + "@Trait\n" + "class MyTrait {\n" + " def m() { 'a' }\n" + "}\n" + "class MyClass implements MyTrait {\n" + "}\n" + "def myClass = new MyClass()\n" + "print myClass.m()" }; runConformTest(sources, "a"); } @Test // Test @Trait annotation public void testTraits39() { String[] sources = { "Sample.groovy", "import groovy.transform.*\n" + "@Trait\n" + "class MyTrait {\n" + " def m() { 'a' }\n" + "}\n" + "class MyClass implements MyTrait {\n" + "}\n" + "def myClass = new MyClass()\n" + "print myClass.m()" }; runConformTest(sources, "a"); } @Test // Negative test for @Trait annotation public void testTraits40() { String[] sources = { "Sample.groovy", "@interface Trait{}\n" + "@Trait\n" + "class MyTrait {\n" + " def m() { 'a' }\n" + "}\n" + "class MyClass implements MyTrait {\n" + "}\n" + "def myClass = new MyClass()\n" + "print myClass.m()" }; runNegativeTest(sources, "----------\n" + "1. ERROR in Sample.groovy (at line 6)\n" + "\tclass MyClass implements MyTrait {\n" + "\t ^^^^^^^\n" + "Groovy:You are not allowed to implement the class 'MyTrait', use extends instead.\n" + "----------\n" + "2. ERROR in Sample.groovy (at line 6)\n" + "\tclass MyClass implements MyTrait {\n" + "\t ^^^^^^^^\n" + "The type MyTrait cannot be a superinterface of MyClass; a superinterface must be an interface\n" + "----------\n"); } @Test // Negative test for @Trait annotation public void testTraits41() { String[] sources = { "Trait.groovy", "package a\n" + "@interface Trait {}", "Sample.groovy", "package b\n" + "import a.Trait\n" + "@Trait\n" + "class MyTrait {\n" + " def m() { 'a' }\n" + "}\n" + "class MyClass implements MyTrait {\n" + "}\n" + "def myClass = new MyClass()\n" + "print myClass.m()" }; runNegativeTest(sources, "----------\n" + "1. ERROR in Sample.groovy (at line 7)\n" + "\tclass MyClass implements MyTrait {\n" + "\t ^^^^^^^\n" + "Groovy:You are not allowed to implement the class 'b.MyTrait', use extends instead.\n" + "----------\n" + "2. ERROR in Sample.groovy (at line 7)\n" + "\tclass MyClass implements MyTrait {\n" + "\t ^^^^^^^^\n" + "The type MyTrait cannot be a superinterface of MyClass; a superinterface must be an interface\n" + "----------\n"); } @Test // Negative test for @Trait annotation public void testTraits42() { String[] sources = { "Trait.groovy", "package a\n" + "@interface Trait {}", "Sample.groovy", "package b\n" + "@a.Trait\n" + "class MyTrait {\n" + " def m() { 'a' }\n" + "}\n" + "class MyClass implements MyTrait {\n" + "}\n" + "def myClass = new MyClass()\n" + "print myClass.m()" }; runNegativeTest(sources, "----------\n" + "1. ERROR in Sample.groovy (at line 6)\n" + "\tclass MyClass implements MyTrait {\n" + "\t ^^^^^^^\n" + "Groovy:You are not allowed to implement the class 'b.MyTrait', use extends instead.\n" + "----------\n" + "2. ERROR in Sample.groovy (at line 6)\n" + "\tclass MyClass implements MyTrait {\n" + "\t ^^^^^^^^\n" + "The type MyTrait cannot be a superinterface of MyClass; a superinterface must be an interface\n" + "----------\n"); } @Test // Negative test for @Trait annotation public void testTraits43() { String[] sources = { "Trait.groovy", "package a\n" + "@interface Trait {}", "Sample.groovy", "package b\n" + "import a.Trait\n" + "import groovy.transform.*\n" + "@Trait\n" + "class MyTrait {\n" + " def m() { 'a' }\n" + "}\n" + "class MyClass implements MyTrait {\n" + "}\n" + "def myClass = new MyClass()\n" + "print myClass.m()" }; runNegativeTest(sources, "----------\n" + "1. ERROR in Sample.groovy (at line 8)\n" + "\tclass MyClass implements MyTrait {\n" + "\t ^^^^^^^\n" + "Groovy:You are not allowed to implement the class 'b.MyTrait', use extends instead.\n" + "----------\n" + "2. ERROR in Sample.groovy (at line 8)\n" + "\tclass MyClass implements MyTrait {\n" + "\t ^^^^^^^^\n" + "The type MyTrait cannot be a superinterface of MyClass; a superinterface must be an interface\n" + "----------\n"); } @Test // Test protected method of superclass overriding by trait method - default package public void testTraits44() { String[] sources = { "Sample.groovy", "trait MyTrait {\n" + " def m() { 'a' }\n" + "}\n" + "class MySuperClass {\n" + " protected def m() { 'b' }\n" + "}\n" + "class MyClass extends MySuperClass implements MyTrait {}\n" + "def myClass = new MyClass()\n" + "print myClass.m()" }; runConformTest(sources, "a"); } @Test // Test protected method of superclass overriding by trait method - the same package public void testTraits45() { String[] sources = { "Sample.groovy", "def myClass = new a.MyClass()\n" + "print myClass.m()", "Stuff.groovy", "package a\n" + "trait MyTrait {\n" + " def m() { 'a' }\n" + "}\n" + "class MySuperClass {\n" + " protected def m() { 'b' }\n" + "}\n" + "class MyClass extends MySuperClass implements MyTrait {}" }; runConformTest(sources, "a"); } @Test // Test protected method of superclass overriding by trait method - different packages public void testTraits46() { String[] sources = { "Sample.groovy", "def myClass = new c.MyClass()\n" + "print myClass.m()", "MyTrait.groovy", "package a\n" + "trait MyTrait {\n" + " def m() { 'a' }\n" + "}", "MySuperClass.groovy", "package b\n" + "class MySuperClass {\n" + " protected def m() { 'b' }\n" + "}", "MyClass.groovy", "package c\n" + "class MyClass extends b.MySuperClass implements a.MyTrait {}" }; runConformTest(sources, "a"); } @Test // Test protected method of superclass overriding by trait method - different packages public void testTraits47() { String[] sources = { "Sample.groovy", "def myClass = new c.MyClass()\n" + "print myClass.m()", "MyTrait.groovy", "package a\n" + "trait MyTrait {\n" + " def m() { 'a' }\n" + "}", "MySuperClass.groovy", "package b\n" + "class MySuperClass {\n" + " protected def m() { 'b' }\n" + "}", "MyClass.groovy", "package c\n" + "import a.MyTrait\n" + "import b.MySuperClass\n" + "class MyClass extends MySuperClass implements MyTrait {}" }; runConformTest(sources, "a"); } @Test // Test protected method of superclass and traits method overriding by class public void testTraits48() { String[] sources = { "Sample.groovy", "trait MyTrait {\n" + " def m() { 'a' }\n" + "}\n" + "class MySuperClass {\n" + " protected def m() { 'b' }\n" + "}\n" + "class MyClass extends MySuperClass implements MyTrait {\n" + " def m() { 'c' }\n" + "}\n" + "def myClass = new MyClass()\n" + "print myClass.m()" }; runConformTest(sources, "c"); } @Test // Test protected method of superclass and traits method overriding by class - negative test public void testTraits49() { String[] sources = { "Sample.groovy", "trait MyTrait {\n" + " abstract def m()\n" + "}\n" + "class MySuperClass {\n" + " protected def m() { 'b' }\n" + "}\n" + "class MyClass extends MySuperClass implements MyTrait {}\n" + "def myClass = new MyClass()\n" + "print myClass.m()" }; runNegativeTest(sources, "----------\n" + "1. ERROR in Sample.groovy (at line 7)\n" + "\tclass MyClass extends MySuperClass implements MyTrait {}\n" + "\t ^^^^^^^\n" + "The inherited method MySuperClass.m() cannot hide the public abstract method in MyTrait\n" + "----------\n"); } @Test // Test protected method of superclass and traits method overriding by class - positive test public void testTraits50() { String[] sources = { "Sample.groovy", "trait MyTrait {\n" + " abstract def m()\n" + "}\n" + "class MySuperClass {\n" + " protected def m() { 'b' }\n" + "}\n" + "class MyClass extends MySuperClass implements MyTrait {\n" + " def m() { 'c' }\n" + "}\n" + "def myClass = new MyClass()\n" + "print myClass.m()" }; runConformTest(sources, "c"); } @Test @Ignore("Java classes should be able to implement traits as well -- this doesn't work in groovyc either as of Groovy 2.4.8") public void testTraitsInteroperability() { String[] sources = { "Sample.java", "public class Sample implements Valuable {\n" + " public String showMeTheMoney() {\n" + " return \"$\" + getValue() + \"$\";\n" + " }\n" + "}", "Valuable.groovy", "trait Valuable {\n" + " String value\n" + "}" }; runConformTest(sources); } }