/* * Copyright Red Hat Inc. and/or its affiliates and other contributors * as indicated by the authors tag. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License version 2. * * This particular file is subject to the "Classpath" exception as provided in the * LICENSE file that accompanied this code. * * This program is distributed in the hope that it will be useful, but WITHOUT A * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License, * along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package com.redhat.ceylon.compiler.java.test.nativecode; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import com.redhat.ceylon.compiler.java.test.CompilerError; import com.redhat.ceylon.compiler.java.test.CompilerTests; public class NativeTests extends CompilerTests { private void testNative(String test) { testNative("simple", test); } private void testNative(String dir, String test) { boolean ok = false; try { compileAndRun("com.redhat.ceylon.compiler.java.test.nativecode." + dir + ".test" + test, dir + "/" + test + ".ceylon"); } catch (RuntimeException ex) { assert(("ceylon.language.Exception \"" + test + "-JVM\"").equals(ex.getMessage())); ok = true; } if (!ok) { Assert.fail("Test terminated incorrectly"); } } private void testNativeErrors(String test, CompilerError... expectedErrors) { testNativeErrors("simple", test, expectedErrors); } private void testNativeErrors(String dir, String test, CompilerError... expectedErrors) { assertErrors(dir + "/" + test, expectedErrors); } private ModuleWithArtifact testNativeModule(String dir) { return testNativeModule(dir, null, null, "test.ceylon", "package.ceylon", "module.ceylon"); } private ModuleWithArtifact testNativeModule(String dir, ModuleWithArtifact extraModule) { return testNativeModule(dir, extraModule, null, "test.ceylon", "package.ceylon", "module.ceylon"); } private ModuleWithArtifact testNativeModule(String dir, ModuleWithArtifact extraModule1, ModuleWithArtifact extraModule2) { return testNativeModule(dir, extraModule1, extraModule2, "test.ceylon", "package.ceylon", "module.ceylon"); } private ModuleWithArtifact testNativeModule(String dir, ModuleWithArtifact extraModule1, ModuleWithArtifact extraModule2, String... files) { ModuleWithArtifact mainModule = null; boolean ok = false; try { String[] paths = new String[files.length]; for (int i=0; i < files.length; i++) { paths[i] = dir + "/" + files[i]; } compile(paths); String main = "com.redhat.ceylon.compiler.java.test.nativecode." + dir + ".test"; mainModule = new ModuleWithArtifact("com.redhat.ceylon.compiler.java.test.nativecode." + dir, "1.0"); if (extraModule1 != null) { if (extraModule2 != null) { run(main, mainModule, extraModule1, extraModule2); } else { run(main, mainModule, extraModule1); } } else { run(main, mainModule); } } catch (RuntimeException ex) { assert(("ceylon.language.Exception \"" + dir + "-JVM\"").equals(ex.getMessage())); ok = true; } if (!ok) { Assert.fail("Test terminated incorrectly"); } return mainModule; } private void testNativeModuleErrors(String dir, CompilerError... expectedErrors) { assertErrors(new String[] {dir + "/test.ceylon", dir + "/module.ceylon"}, defaultOptions, null, expectedErrors); } private void testNativeModuleErrors(String dir, String extraFile, CompilerError... expectedErrors) { assertErrors(new String[] {dir + "/test.ceylon", dir + "/module.ceylon", dir + "/" + extraFile}, defaultOptions, null, expectedErrors); } // Methods @Test public void testNativeMethodPrivate() { testNative("NativeMethodPrivate"); } @Test public void testNativeMethodPrivateAbstraction() { testNative("NativeMethodPrivateAbstraction"); } @Test public void testNativeMethodShared() { testNative("NativeMethodShared"); } @Test public void testNativeMethodHeaderImpl() { testNative("NativeMethodHeaderImpl"); testNative("NativeMethodHeaderImpl"); } @Test public void testNativeMethodLocal() { testNative("NativeMethodLocal"); } @Test public void testNativeMethodSharedInvalid() { testNativeErrors("NativeMethodSharedInvalid", new CompilerError(20, "shared native implementation must have a header: 'nativeMethodSharedInvalid'"), new CompilerError(23, "shared native implementation must have a header: 'nativeMethodSharedInvalid'")); } @Test public void testNativeMethodMismatch() { testNativeErrors("NativeMethodMismatch", new CompilerError(25, "native header is not shared: 'nativeMethodMismatch1'"), new CompilerError(30, "native implementation must have the same return type as native header: 'nativeMethodMismatch2' must have the type 'Anything'"), new CompilerError(34, "native implementation must have the same return type as native header: 'nativeMethodMismatch2' must have the type 'Anything'"), new CompilerError(40, "member does not have the same number of parameters as native header: 'nativeMethodMismatch3'"), new CompilerError(44, "parameter does not have the same name as its header: 's' is not 'i' for 'nativeMethodMismatch3'"), new CompilerError(44, "type of parameter 's' of 'nativeMethodMismatch3' is different to type of corresponding parameter 'i' of native header 'nativeMethodMismatch3': 'String' is not exactly 'Integer'"), new CompilerError(51, "no native implementation for backend: native 'nativeMethodMismatch4js' is not implemented for the 'jvm' backend"), new CompilerError(54, "no native implementation for backend: native 'nativeMethodMismatch4js' is not implemented for the 'jvm' backend"), new CompilerError(72, "native header does not have the same number of type parameters as native implementation: 'nativeMethodMismatch5'"), new CompilerError(75, "native header does not have the same number of type parameters as native implementation: 'nativeMethodMismatch5'"), new CompilerError(80, "type parameter does not have the same bounds as its header: 'T' for 'nativeMethodMismatch6'"), new CompilerError(83, "type parameter does not have the same bounds as its header: 'T' for 'nativeMethodMismatch6'") ); } // Attributes @Test public void testNativeAttributePrivate() { testNative("NativeAttributePrivate"); } @Test public void testNativeAttributePrivateAbstraction() { testNative("NativeAttributePrivateAbstraction"); } @Test public void testNativeAttributeShared() { testNative("NativeAttributeShared"); } @Test public void testNativeSetterShared() { testNative("NativeSetterShared"); } @Test public void testNativeVariableSetter() { testNative("NativeVariableSetter"); } @Test public void testNativeSetterVariable() { testNative("NativeSetterVariable"); } @Test public void testNativeAttributeHeaderImpl() { testNative("NativeAttributeHeaderImpl"); testNative("NativeAttributeHeaderImpl"); } @Test public void testNativeAttributeLocal() { testNative("NativeAttributeLocal"); } @Test public void testNativeSetterInvalid() { testNativeErrors("NativeSetterInvalid", new CompilerError(21, "setter must be marked native: 'nativeSetterInvalid'"), new CompilerError(24, "setter must be marked native: 'nativeSetterInvalid'"), new CompilerError(27, "setter must be marked native: 'nativeSetterInvalid'"), new CompilerError(30, "native setter for non-native getter: 'nativeSetterInvalid2'") ); } @Test public void testNativeAttributeSharedInvalid() { testNativeErrors("NativeAttributeSharedInvalid", new CompilerError(20, "shared native implementation must have a header: 'nativeAttributeSharedInvalid'"), new CompilerError(22, "shared native implementation must have a header: 'nativeAttributeSharedInvalid'")); } @Test public void testNativeAttributeMismatch() { testNativeErrors("NativeAttributeMismatch", new CompilerError(24, "native header is not shared: 'nativeAttributeMismatch1'"), new CompilerError(28, "native implementation must have the same type as native header: 'nativeAttributeMismatch2' must have the type 'Integer'")); } // Classes @Test public void testNativeClassPrivate() { testNative("NativeClassPrivate"); } @Test public void testNativeClassPrivateAbstraction() { testNative("NativeClassPrivateAbstraction"); } @Test public void testNativeClassShared() { testNative("NativeClassShared"); } @Test public void testNativeClassExtends() { testNative("NativeClassExtends"); } @Test public void testNativeClassSatisfies() { testNative("NativeClassSatisfies"); } @Test public void testNativeClassWithImpl() { testNative("NativeClassWithImpl"); } @Test public void testNativeClassSharedMembers() { testNative("NativeClassSharedMembers"); } @Test public void testNativeClassMembersWithImpl() { testNative("ClassNativeMembersWithImpl"); } @Test public void testClassNativeMembers() { testNative("ClassNativeMembers"); } @Test public void testNativeClassLocal() { testNative("NativeClassLocal"); } @Test public void testNativeClassWithConstructors() { testNative("NativeClassWithConstructors"); } @Test public void testNativeClassSharedInvalid() { testNativeErrors("NativeClassSharedInvalid", new CompilerError(20, "shared native implementation must have a header: 'NativeClassSharedInvalid'"), new CompilerError(22, "shared native implementation must have a header: 'NativeClassSharedInvalid'")); } @Test public void testNativeClassMismatch() { testNativeErrors("NativeClassMismatch", new CompilerError(35, "native header is not shared: 'NativeClassMismatch1'"), new CompilerError(40, "member does not have the same number of parameters as native header: 'NativeClassMismatch2'"), new CompilerError(42, "parameter does not have the same name as its header: 's' is not 'i' for 'NativeClassMismatch2'"), new CompilerError(42, "type of parameter 's' of 'NativeClassMismatch2' is different to type of corresponding parameter 'i' of native header 'NativeClassMismatch2': 'String' is not exactly 'Integer'"), new CompilerError(57, "native classes do not satisfy the same interfaces: 'NativeClassMismatch4'"), new CompilerError(75, "native classes do not satisfy the same interfaces: 'NativeClassMismatch5'"), new CompilerError(82, "native header for non-native declaration: 'NativeClassMismatch6'"), new CompilerError(84, "native implementation for non-native header: 'NativeClassMismatch6'"), new CompilerError(91, "formal member 'test1' of 'NativeClassMismatchSuper1' not implemented in class hierarchy"), new CompilerError(92, "native backend name on declaration conflicts with its scope: 'test1'"), new CompilerError(102, "no native implementation for backend: native 'NativeClassMismatch8js' is not implemented for the 'jvm' backend"), new CompilerError(106, "no native implementation for backend: native 'NativeClassMismatch8js' is not implemented for the 'jvm' backend"), new CompilerError(134, "native header 'test5' of 'NativeClassMismatch9' has no native implementation"), new CompilerError(136, "parameter does not have the same name as its header: 's' is not 'i' for 'test2' in 'NativeClassMismatch9'"), new CompilerError(136, "type of parameter 's' of 'test2' is different to type of corresponding parameter 'i' of native header 'test2': 'String' is not exactly 'Integer'"), new CompilerError(137, "native implementation must have the same return type as native header: 'test3' in 'NativeClassMismatch9' must have the type 'Anything'"), new CompilerError(138, "member does not have the same number of parameters as native header: 'test4'"), new CompilerError(139, "native member does not implement any header member: 'testX' in 'NativeClassMismatch9'"), new CompilerError(140, "non-native shared members not allowed in native implementations: 'testY' in 'NativeClassMismatch9'"), new CompilerError(143, "native header 'test5' of 'NativeClassMismatch9' has no native implementation"), new CompilerError(145, "parameter does not have the same name as its header: 's' is not 'i' for 'test2' in 'NativeClassMismatch9'"), new CompilerError(145, "type of parameter 's' of 'test2' is different to type of corresponding parameter 'i' of native header 'test2': 'String' is not exactly 'Integer'"), new CompilerError(146, "native implementation must have the same return type as native header: 'test3' in 'NativeClassMismatch9' must have the type 'Anything'"), new CompilerError(147, "member does not have the same number of parameters as native header: 'test4'"), new CompilerError(148, "native member does not implement any header member: 'testX' in 'NativeClassMismatch9'"), new CompilerError(149, "non-native shared members not allowed in native implementations: 'testY' in 'NativeClassMismatch9'"), new CompilerError(160, "native header 'privattr' of 'NativeClassMismatch10' has no native implementation"), new CompilerError(160, "native header 'privmeth' of 'NativeClassMismatch10' has no native implementation"), new CompilerError(167, "native header 'privattr' of 'NativeClassMismatch10' has no native implementation"), new CompilerError(167, "native header 'privmeth' of 'NativeClassMismatch10' has no native implementation"), new CompilerError(177, "native header does not have the same number of type parameters as native implementation: 'NativeClassMismatch11'"), new CompilerError(179, "native header does not have the same number of type parameters as native implementation: 'NativeClassMismatch11'"), new CompilerError(184, "type parameter does not have the same bounds as its header: 'B' for 'NativeClassMismatch12'"), new CompilerError(184, "type parameter does not have the same name as its header: 'B' is not 'A' for 'NativeClassMismatch12'"), new CompilerError(186, "type parameter does not have the same bounds as its header: 'A' for 'NativeClassMismatch12'"), new CompilerError(195, "native header 'test3' of 'NativeClassMismatch13' has no native implementation"), new CompilerError(196, "member implementing a native header member must be marked native: 'test1' in 'NativeClassMismatch13'"), new CompilerError(197, "member implementing a native header member must be marked native: 'test2' in 'NativeClassMismatch13'"), new CompilerError(198, "member implementing a native header member must be marked native: 'test3' in 'NativeClassMismatch13'"), new CompilerError(201, "native header 'test3' of 'NativeClassMismatch13' has no native implementation"), new CompilerError(202, "member implementing a native header member must be marked native: 'test1' in 'NativeClassMismatch13'"), new CompilerError(203, "member implementing a native header member must be marked native: 'test2' in 'NativeClassMismatch13'"), new CompilerError(204, "member implementing a native header member must be marked native: 'test3' in 'NativeClassMismatch13'") ); } // Objects @Test public void testNativeObjectPrivate() { testNative("NativeObjectPrivate"); } @Test public void testNativeObjectShared() { testNative("NativeObjectShared"); } @Test public void testNativeObjectWithImpl() { testNative("NativeObjectWithImpl"); } @Test public void testObjectNativeMembers() { testNative("ObjectNativeMembers"); } @Test public void testObjectNativeMembersWithImpl() { testNative("ObjectNativeMembersWithImpl"); } @Test public void testNativeObjectLocal() { testNative("NativeObjectLocal"); } // Modules @Test public void testNativeModule() { testNativeModule("modok"); } @Test public void testNativeModuleImport() { ModuleWithArtifact sampleMod = testNativeModule("modsample"); testNativeModule("modimport", sampleMod); } @Test public void testNativeModuleMissing() { testNativeModuleErrors("modmissing", new CompilerError(21, "missing backend argument for native annotation on module: com.redhat.ceylon.compiler.java.test.nativecode.modmissing") ); } @Test public void testNativeModuleWrong() { testNativeModuleErrors("modwrong", new CompilerError(21, "module not meant for this backend: com.redhat.ceylon.compiler.java.test.nativecode.modwrong") ); } @Test public void testNativeOtherRef() { testNativeModule("otherref"); } @Test @Ignore("see https://github.com/ceylon/ceylon-compiler/issues/2196") public void testNativeWithJava() { testNativeModule("withjava", null, null, "NativeClass.java", "test.ceylon", "module.ceylon"); } @Test public void testNativeConflict() { testNativeModuleErrors("modconflict", new CompilerError(20, "no native implementation for backend: native 'conflicting' is not implemented for the 'jvm' backend"), new CompilerError(22, "native backend name on declaration conflicts with module descriptor: '\"js\"' is not '\"jvm\"' for 'conflicting'") ); } @Test @Ignore("We do away with incremental compilation for now") public void testNativeIncremental() { compile("modincremental/test.ceylon", "modincremental/testheader.ceylon", "modincremental/module.ceylon"); testNativeModule("modincremental"); } @Test public void testNativeLocalJava() { testNativeModuleErrors("localjava", "JavaTest.java", new CompilerError(31, "illlegal reference to native declaration 'JavaTest': native declaration 'x' has a different backend"), new CompilerError(32, "illlegal reference to native declaration 'jvmonly': native declaration 'test' has a different backend"), new CompilerError(36, "illlegal reference to native declaration 'JavaTest': declaration 'x' is not native (mark it or the module native)"), new CompilerError(37, "illlegal reference to native declaration 'jvmonly': declaration 'testjs' is not native (mark it or the module native)"), new CompilerError(40, "illlegal reference to native declaration 'JavaTest': native declaration 'Test' has a different backend"), new CompilerError(45, "illlegal reference to native declaration 'JavaTest': native declaration 'Test' has a different backend"), new CompilerError(48, "illlegal reference to native declaration 'JavaTest': declaration 'Testjs' is not native (mark it or the module native)") ); } // Misc @Test public void testNativeInvalidTypes() { testNativeErrors("NativeInvalidTypes", new CompilerError(22, "native declarations not of same type: 'nativeInvalidTypes'"), new CompilerError(24, "native declarations not of same type: 'nativeInvalidTypes'"), new CompilerError(27, "illegal native backend name: '\"foo\"' (must be either '\"jvm\"' or '\"js\"')") ); } @Test public void testNativeNonNativeMixed() { testNativeErrors("NativeNonNativeMixed", new CompilerError(21, "native implementation for non-native header: 'NativeNonNativeMixed1'"), new CompilerError(22, "native implementation for non-native header: 'NativeNonNativeMixed1'"), new CompilerError(25, "native header for non-native declaration: 'nativeNonNativeMixed2'"), new CompilerError(25, "no native implementation for backend: native 'nativeNonNativeMixed2' is not implemented for the 'jvm' backend"), new CompilerError(26, "native implementation for non-native header: 'nativeNonNativeMixed2'"), new CompilerError(27, "native implementation for non-native header: 'nativeNonNativeMixed2'"), new CompilerError(30, "duplicate declaration name: 'nativeNonNativeMixed3'") ); } @Test public void testNativeDuplicates() { testNativeErrors("NativeDuplicates", new CompilerError(20, "no native implementation for backend: native 'nativeDuplicates1' is not implemented for the 'jvm' backend"), new CompilerError(22, "duplicate native header: 'nativeDuplicates1'"), new CompilerError(22, "no native implementation for backend: native 'nativeDuplicates1' is not implemented for the 'jvm' backend"), new CompilerError(28, "duplicate native implementation: 'nativeDuplicates2'"), new CompilerError(30, "no native implementation for backend: native 'nativeDuplicates3' is not implemented for the 'jvm' backend"), new CompilerError(34, "duplicate native implementation: 'nativeDuplicates3'"), new CompilerError(40, "duplicate native implementation: 'nativeDuplicates4'"), new CompilerError(42, "no native implementation for backend: native 'nativeDuplicates5' is not implemented for the 'jvm' backend"), new CompilerError(46, "duplicate native implementation: 'nativeDuplicates5'"), new CompilerError(50, "duplicate native implementation: 'foo'"), new CompilerError(52, "duplicate native implementation: 'foo'"), new CompilerError(57, "duplicate native implementation: 'foo'"), new CompilerError(58, "duplicate native implementation: 'foo'"), new CompilerError(60, "duplicate native implementation: 'foo'"), new CompilerError(61, "duplicate native implementation: 'foo'") ); } @Test public void testNativeMissing() { testNativeErrors("NativeMissing", new CompilerError(20, "no native implementation for backend: native 'nativeMissingMethod' is not implemented for the 'jvm' backend"), new CompilerError(21, "no native implementation for backend: native 'NativeMissingClass' is not implemented for the 'jvm' backend"), new CompilerError(25, "no native implementation for backend: native 'nativeMissingMethod2' is not implemented for the 'jvm' backend"), new CompilerError(29, "no native implementation for backend: native 'nativeMissingMethod' is not implemented for the 'jvm' backend"), new CompilerError(30, "no native implementation for backend: native 'nativeMissingMethod2' is not implemented for the 'jvm' backend"), new CompilerError(31, "no native implementation for backend: native 'NativeMissingClass' is not implemented for the 'jvm' backend"), new CompilerError(34, "no native implementation for backend: native 'NativeMissingClass' is not implemented for the 'jvm' backend") ); } @Test public void testNativeDelegate() { testNative("NativeDelegate"); } @Test public void testBugSpec1372() { testNative("BugSpec1372"); } @Test public void testNativeRefOk() { ModuleWithArtifact sampleMod = testNativeModule("modsample"); ModuleWithArtifact okMod = testNativeModule("modok"); testNativeModule("nativerefok", sampleMod, okMod); } @Test public void testNativeRefWrong() { assertErrors(new String[] {"nativerefwrong/test.ceylon", "nativerefwrong/module.ceylon", "modsample/test.ceylon", "modsample/package.ceylon", "modsample/module.ceylon", "modok/test.ceylon", "modok/package.ceylon", "modok/module.ceylon"}, defaultOptions, null, new CompilerError(30, "illlegal reference to native declaration 'nativeMethodJvm': declaration 'x' is not native (mark it or the module native)"), new CompilerError(32, "illlegal reference to native declaration 'nativeMethodJvm': declaration 'x' is not native (mark it or the module native)"), new CompilerError(37, "illlegal reference to native declaration 'nativeMethodJs': declaration 'x' is not native (mark it or the module native)"), new CompilerError(39, "illlegal reference to native declaration 'nativeMethodJs': declaration 'x' is not native (mark it or the module native)"), new CompilerError(44, "illlegal reference to native declaration 'testMethod': declaration 'x' is not native (mark it or the module native)"), new CompilerError(45, "illlegal reference to native declaration 'TestClass': declaration 'y' is not native (mark it or the module native)"), new CompilerError(47, "illlegal reference to native declaration 'testMethod': declaration 'x' is not native (mark it or the module native)"), new CompilerError(48, "illlegal reference to native declaration 'TestClass': declaration 'y' is not native (mark it or the module native)"), new CompilerError(53, "illlegal reference to native declaration 'foo': declaration 'x' is not native (mark it or the module native)"), new CompilerError(54, "illlegal reference to native declaration 'Bar': declaration 'y' is not native (mark it or the module native)"), new CompilerError(56, "illlegal reference to native declaration 'foo': declaration 'x' is not native (mark it or the module native)"), new CompilerError(57, "illlegal reference to native declaration 'Bar': declaration 'y' is not native (mark it or the module native)"), new CompilerError(63, "illlegal reference to native declaration 'nativeMethodJvm': native declaration 'x' has a different backend"), new CompilerError(65, "illlegal reference to native declaration 'nativeMethodJvm': native declaration 'x' has a different backend"), new CompilerError(71, "illlegal reference to native declaration 'nativeMethodJs': native declaration 'x' has a different backend"), new CompilerError(73, "illlegal reference to native declaration 'nativeMethodJs': native declaration 'x' has a different backend") ); } @Test public void testNativeScopes() { testNative("NativeScopes"); } @Test public void testNativeScopesWrong() { testNativeErrors("NativeScopesWrong", new CompilerError(27, "native backend name on declaration conflicts with its scope: 'test3'"), new CompilerError(32, "native backend name on declaration conflicts with its scope: 'test'"), new CompilerError(42, "native backend name on declaration conflicts with its scope: 'test3'"), new CompilerError(47, "native backend name on declaration conflicts with its scope: 'test'") ); } @Test public void testNativeErrorHandling() { testNativeErrors("NativeErrorHandling", new CompilerError(22, "function or value does not exist: 'foo'") ); } @Test public void testNativeCorrectOrder() { testNative("NativeCorrectOrder"); } @Test public void testNativeWrongOrder() { testNativeErrors("NativeWrongOrder", new CompilerError(26, "native header must be defined before its implementations: 'NativeWrongOrder'"), new CompilerError(32, "native header must be defined before its implementations: 'test'") ); } @Test public void testBug2369() { testNative("Bug2369"); } }