/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* 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 com.google.devtools.j2objc.gen;
import com.google.common.collect.Lists;
import com.google.devtools.j2objc.GenerationTest;
import com.google.devtools.j2objc.Options.MemoryManagementOption;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;
/**
* Tests for {@link ObjectiveCImplementationGenerator}.
*
* @author Tom Ball
*/
public class ObjectiveCImplementationGeneratorTest extends GenerationTest {
public void testOuterVariableAccess() throws IOException {
String translation = translateSourceFile(
"public class Example { int foo; class Inner { int test() { return foo; }}}",
"Example", "Example.m");
assertTranslation(translation, "return this$0_->foo_;");
}
public void testTypeNameTranslation() throws IOException {
String translation = translateSourceFile(
"public class Example {}", "Example", "Example.m");
assertTranslation(translation, "#include \"Example.h\"");
}
public void testPackageTypeNameTranslation() throws IOException {
String translation = translateSourceFile(
"package unit.test; public class Example {}", "Example", "unit/test/Example.m");
assertTranslation(translation, "#include \"unit/test/Example.h\"");
}
public void testHeaderFileMapping() throws IOException {
options.getHeaderMap().setMappingFiles("testMappings.j2objc");
loadHeaderMappings();
addSourceFile("package unit.mapping.custom; public class Test { }",
"unit/mapping/custom/Test.java");
String translation = translateSourceFile(
"import unit.mapping.custom.Test; "
+ "public class MyTest { MyTest(Test u) {}}",
"MyTest", "MyTest.m");
assertTranslation(translation, "#include \"my/mapping/custom/Test.h\"");
}
public void testPackageTypeNameTranslationWithInnerClass() throws IOException {
String translation = translateSourceFile(
"package unit.test; public class Example { class Inner {}}",
"Example", "unit/test/Example.m");
assertTranslation(translation, "#include \"unit/test/Example.h\"");
assertFalse(translation.contains("#include \"unit/test/Example_Inner.h\""));
}
public void testSameClassMethodInvocation() throws IOException {
String translation = translateSourceFile(
"package unit.test; public class Example { void foo() {} void test() { foo(); }}",
"Example", "unit/test/Example.m");
assertTranslation(translation, "[self foo];");
}
public void testSameClassStaticMethodInvocation() throws IOException {
String translation = translateSourceFile(
"public class Example { static void foo() {} void test() { foo(); }}",
"Example", "Example.m");
assertTranslation(translation, "Example_foo();");
}
public void testSameClassStaticMethodPackageInvocation() throws IOException {
String translation = translateSourceFile(
"package unit.test; public class Example { static void foo() {} void test() { foo(); }}",
"Example", "unit/test/Example.m");
assertTranslation(translation, "UnitTestExample_foo();");
}
public void testConstStaticIntTranslation() throws IOException {
String translation = translateSourceFile(
"public class Example { public static final int FOO=1; int test() { return FOO; }}",
"Example", "Example.h");
assertTranslation(translation, "#define Example_FOO 1");
translation = getTranslatedFile("Example.m");
assertFalse(translation.contains("initialize"));
}
public void testConstVariableInOtherClassTranslation() throws IOException {
String translation = translateSourceFile("public class Example { "
+ "int test() { return Bool.FOO; } } class Bool { public static final int FOO=1; }",
"Example", "Example.m");
assertTranslation(translation, "return Bool_FOO;");
}
public void testStaticVariableInitialization() throws IOException {
String translation = translateSourceFile(
"public class Example { public static java.util.Date today; }",
"Example", "Example.m");
assertTranslation(translation, "JavaUtilDate *Example_today;");
assertFalse(translation.contains("initialize"));
}
public void testStaticVariableWithInitInitialization() throws IOException {
String translation = translateSourceFile(
"public class Example { public static java.util.Date today = new java.util.Date();}",
"Example", "Example.m");
assertTranslation(translation, "JavaUtilDate *Example_today;");
assertTranslation(translation, "+ (void)initialize {");
assertTranslation(translation,
"JreStrongAssignAndConsume(&Example_today, new_JavaUtilDate_init());");
}
public void testStaticVariableWithNonInitInitialization() throws IOException {
String translation = translateSourceFile(
"public class Example { "
+ " public static java.util.logging.Logger logger ="
+ " java.util.logging.Logger.getLogger(\"Test\");}",
"Example", "Example.m");
assertTranslation(translation,
"JreStrongAssign(&Example_logger, "
+ "JavaUtilLoggingLogger_getLoggerWithNSString_(@\"Test\"));");
}
public void testStaticVariableInGenericInnerClass() throws IOException {
String translation = translateSourceFile(
"public class Example { "
+ " static class Inner<T> { "
+ " static int foo = 1; "
+ " final int myFoo = foo++; }}",
"Example", "Example.m");
assertTranslation(translation, "int Example_Inner_foo = 1");
assertTranslation(translation, "myFoo_ = Example_Inner_foo++");
}
public void testStaticVariableWithGenericTypeCast() throws IOException {
String translation = translateSourceFile(
"public class Example<B> { "
+ " public <B> Example<B> foo() { return (Example<B>) FOO; } "
+ " public static final Example<?> FOO = new Example(); }",
"Example", "Example.m");
// The erasure of FOO matches the erasure of the return type of foo() so no cast necessary.
assertTranslation(translation, "return Example_FOO");
}
public void testStaticVariableInOtherVariable() throws IOException {
String translation = translateSourceFile("public class Example { "
+ "void test() { Bar.FOO=2; } } class Bar { public static int FOO=1; }",
"Example", "Example.m");
assertTranslation(translation, "jint Bar_FOO = 1;");
assertTranslation(translation, "*JreLoadStaticRef(Bar, FOO) = 2;");
}
public void testNSObjectMessageRename() throws IOException {
String translation = translateSourceFile(
"public class Example { int load() { return 1; } int test() { return load(); }}",
"Example", "Example.m");
assertTranslation(translation, "- (jint)load__ {");
assertTranslation(translation, "return [self load__];");
}
public void testNSObjectMessageQualifiedNameRename() throws IOException {
String translation = translateSourceFile(
"public class Example { int load() { return 1; } int test() { return this.load(); }}",
"Example", "Example.m");
assertTranslation(translation, "return [self load__];");
}
public void testNSObjectMessageSuperRename() throws IOException {
String translation = translateSourceFile(
"public class Example { int load() { return 1; }} "
+ "class SubClass extends Example { int load() { return super.load(); }}",
"Example", "Example.m");
assertTranslation(translation, "return [super load__];");
}
public void testNSObjectMessageStaticRename() throws IOException {
String translation = translateSourceFile(
"public class Example { static int load() { return 1; }} "
+ "class Other { int test() { return Example.load(); }}",
"Example", "Example.m");
assertTranslation(translation, "return Example_load__();");
}
public void testToStringRenaming() throws IOException {
String translation = translateSourceFile(
"public class Example { public String toString() { return super.toString(); } }",
"Example", "Example.m");
assertTranslation(translation, "- (NSString *)description {");
}
public void testInnerClassAccessToOuterMethods() throws IOException {
String translation = translateSourceFile(
"public class Example {"
+ "public int size() { return 0; } public void add(int n) {} "
+ "class Inner {} "
+ "class Innermost { void test() { Example.this.add(size()); }}}",
"Example", "Example.m");
assertTranslation(translation, "[this$0_ addWithInt:[this$0_ size]];");
}
public void testEnum() throws IOException {
String translation = translateSourceFile(
"public enum Color { RED, WHITE, BLUE }",
"Color", "Color.m");
assertTranslation(translation, "Color *Color_values_[3];");
assertTranslation(translation, "@implementation Color");
assertTranslation(translation, "@\"RED\", @\"WHITE\", @\"BLUE\",");
assertTranslation(translation, "for (int i = 0; i < 3; i++) {");
assertTranslation(translation, "Color *e = Color_values_[i];");
}
public void testEnumWithParameters() throws IOException {
String sourceContent =
"public enum Color { RED(0xff0000), WHITE(0xffffff), BLUE(0x0000ff); "
+ "private int rgb; private int newValue;"
+ "private Color(int rgb) { this.rgb = rgb; } "
+ "public int getRgb() { return rgb; }}";
String translation = translateSourceFile(sourceContent, "Color", "Color.m");
assertTranslation(translation, "@implementation Color");
assertTranslation(translation,
"Color_initWithInt_withNSString_withInt_(e, (jint) 0xff0000, @\"RED\", 0);");
assertTranslation(translation,
"Color_initWithInt_withNSString_withInt_(e, (jint) 0xffffff, @\"WHITE\", 1);");
assertTranslation(translation,
"Color_initWithInt_withNSString_withInt_(e, (jint) 0x0000ff, @\"BLUE\", 2);");
assertTranslation(translation, "- (jint)getRgb {");
assertTranslation(translation, "return rgb_;");
assertTranslation(translation, "jint newValue_;");
}
public void testClassField() throws IOException {
String sourceContent =
"import com.google.j2objc.annotations.Weak;"
+ "public class FooBar {"
+ "private static int fieldPhi;"
+ "private Object fieldFoo;"
+ "@Weak private Object fieldJar;"
+ "private int newFieldBar;"
+ "}";
String translation = translateSourceFile(sourceContent, "FooBar", "FooBar.m");
assertTranslation(translation, "int FooBar_fieldPhi;");
assertTranslation(translation, "RELEASE_(fieldFoo_);");
assertTranslation(translation, "id fieldFoo_;");
assertTranslation(translation, "id fieldJar_;");
assertTranslation(translation, "int newFieldBar_;");
assertTranslation(translation, "id fieldFoo_;");
assertTranslation(translation, "__unsafe_unretained id fieldJar_;");
assertTranslation(translation, "int newFieldBar_;");
assertTranslation(translation, "J2OBJC_STATIC_FIELD_PRIMITIVE(FooBar, fieldPhi, jint)");
}
public void testEmptyInterfaceGenerationNoMetadata() throws IOException {
options.setStripReflection(true);
String translation = translateSourceFile(
"package foo; public interface Compatible {}",
"Compatible", "foo/Compatible.m");
assertNotInTranslation(translation, "@interface");
}
public void testEmptyInterfaceGeneration() throws IOException {
String translation = translateSourceFile(
"package foo; public interface Compatible {}",
"Compatible", "foo/Compatible.m");
assertTranslation(translation, "@interface FooCompatible : NSObject");
assertTranslation(translation, "@implementation FooCompatible");
assertTranslation(translation, "+ (const J2ObjcClassInfo *)__metadata");
}
public void testInterfaceConstantGeneration() throws IOException {
String translation = translateSourceFile(
"package foo; public interface Compatible { "
+ "public static final Object FOO = new Object(); }",
"Compatible", "foo/Compatible.m");
assertTranslation(translation, "id FooCompatible_FOO;");
assertTranslation(translation,
"JreStrongAssignAndConsume(&FooCompatible_FOO, new_NSObject_init());");
}
public void testAnnotationGeneration() throws IOException {
String translation = translateSourceFile(
"package foo; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) "
+ "public @interface Compatible { boolean fooable() default false; }",
"Compatible", "foo/Compatible.m");
assertTranslation(translation, "@implementation FooCompatible");
assertTranslation(translation, "@synthesize fooable = fooable_;");
// Verify constructor generated.
assertTranslation(translation, "id<FooCompatible> create_FooCompatible(jboolean fooable) {");
assertTranslation(translation, "fooable_ = fooable;");
// Verify default value accessor.
assertTranslatedLines(translation,
"+ (jboolean)fooableDefault {",
"return false;");
assertTranslatedLines(translation,
"- (IOSClass *)annotationType {",
"return FooCompatible_class_();");
}
public void testMethodsWithTypeParameters() throws IOException {
String translation = translateSourceFile(
"public class Example implements Foo<String> { public void doFoo(String foo) {}} "
+ "interface Foo<T> { void doFoo(T foo); }",
"Example", "Example.m");
assertTranslation(translation, "- (void)doFooWithId:(NSString *)foo {");
}
public void testTopLevelClassesNotImported() throws IOException {
String translation = translateSourceFile(
"public class Example { Bar bar; } class Bar {}",
"Example", "Example.m");
assertNotInTranslation(translation, "#include \"Bar.h\"");
}
public void testEnumWithStaticVar() throws IOException {
String translation = translateSourceFile(
"public enum Example { ONE, TWO; public static int foo = 42; }",
"Example", "Example.m");
// Verify that there's only one initialize() method.
String initializeSignature = "+ (void)initialize";
int initializeOffset = translation.indexOf(initializeSignature);
assertTrue(initializeOffset != -1);
initializeOffset = translation.indexOf(initializeSignature,
initializeOffset + initializeSignature.length());
assertTrue(initializeOffset == -1);
assertTranslation(translation, "int Example_foo = 42;");
}
public void testNativeCodeBlock() throws IOException {
String translation = translateSourceFile(
"public class Example { native void test() /*-[ good native code block ]-*/; }",
"Example", "Example.m");
assertTranslation(translation, "good native code block");
}
public void testImportDerivedTypeInMethodParams() throws IOException {
addSourceFile("abstract class Foo implements java.util.List { }", "Foo.java");
addSourceFile("class Bar { Foo foo() { return null; } }", "Bar.java");
String translation = translateSourceFile(
"class Test { "
+ " void baz(java.util.List list) { }"
+ " void foobar() { baz(new Bar().foo()); } }",
"Test", "Test.m");
assertTranslation(translation, "#include \"Foo.h\"");
}
public void testImportDerivedTypeInConstructorParams() throws IOException {
addSourceFile("abstract class Foo implements java.util.List { }", "Foo.java");
addSourceFile("class Bar { Foo foo() { return null; } }", "Bar.java");
addSourceFile("class Baz { Baz(java.util.List l) { } }", "Baz.java");
String translation = translateSourceFile(
"class Test { "
+ " void foobar() { new Baz(new Bar().foo()); } }",
"Test", "Test.m");
assertTranslation(translation, "#include \"Foo.h\"");
}
public void testEnumWithEnumField() throws IOException {
String header = translateSourceFile(
"public class Test { "
+ "enum Type { BOOLEAN(false), INT(0), STRING(\"\"); "
+ "Type(Object value) { this.value = value; } private Object value; } "
+ "enum Field { BOOL(Type.BOOLEAN), INT32(Type.INT), "
+ "STRING(Type.STRING) { public boolean isPackable() { return false; }}; "
+ "Field(Type type) { this.type = type; } private Type type;"
+ "public boolean isPackable() { return true; }}}",
"Test", "Test.h");
String impl = getTranslatedFile("Test.m");
assertFalse(header.contains("isPackableWithTest_TypeEnum"));
assertFalse(impl.contains("\n return NO;\n [super initWithTest_TypeEnum:arg$0]}"));
assertTranslation(impl,
"Test_Field_1_initWithTest_Type_withNSString_withInt_("
+ "e, JreLoadEnum(Test_Type, STRING), @\"STRING\", 2);");
}
public void testAutoreleasePoolMethod() throws IOException {
String translation = translateSourceFile(
"import com.google.j2objc.annotations.AutoreleasePool;"
+ "public class Test {"
+ " @AutoreleasePool\n"
+ " public void foo() { }"
+ "}",
"Test", "Test.m");
assertTranslation(translation, "- (void)foo {\n"
+ " @autoreleasepool {\n"
+ " }\n"
+ "}");
}
public void testARCAutoreleasePoolMethod() throws IOException {
options.setMemoryManagementOption(MemoryManagementOption.ARC);
String translation = translateSourceFile(
"import com.google.j2objc.annotations.AutoreleasePool;"
+ "public class Test {"
+ " @AutoreleasePool\n"
+ " public void foo() { }"
+ "}",
"Test", "Test.m");
assertTranslation(translation, "- (void)foo {\n"
+ " @autoreleasepool {\n"
+ " }\n"
+ "}");
}
public void testAutoreleasePoolAnonymousClassMethod() throws IOException {
String translation = translateSourceFile(
"import com.google.j2objc.annotations.AutoreleasePool;"
+ "public class Test {"
+ " interface Foo {"
+ " void apply();"
+ " }"
+ " Foo foo() {"
+ " return new Foo() {"
+ " @AutoreleasePool\n"
+ " public void apply() { }"
+ " };"
+ " }"
+ "}",
"Test", "Test.m");
assertTranslation(translation, "- (void)apply {\n"
+ " @autoreleasepool {\n }\n"
+ "}");
}
public void testInnerConstructorGenerated() throws IOException {
String translation = translateSourceFile(
"public class Test {"
+ " public Test() { this(42); }"
+ " public Test(int i) {} }",
"Test", "Test.m");
assertTranslatedLines(translation,
"- (instancetype)init {",
" Test_init(self);",
" return self;",
"}");
assertTranslatedLines(translation,
"- (instancetype)initWithInt:(jint)i {",
" Test_initWithInt_(self, i);",
" return self;",
"}");
assertTranslatedLines(translation,
"void Test_init(Test *self) {",
" Test_initWithInt_(self, 42);",
"}");
assertTranslatedLines(translation,
"void Test_initWithInt_(Test *self, jint i) {",
" NSObject_init(self);",
"}");
}
public void testInnerConstructorGeneratedForNonStaticInnerClass() throws IOException {
String translation = translateSourceFile(
"public class Test {"
+ " class Inner {"
+ " public Inner() { this(42); }"
+ " public Inner(int i) {} } }",
"Test", "Test.m");
assertTranslatedLines(translation,
"- (instancetype)initWithTest:(Test *)outer$ {",
" Test_Inner_initWithTest_(self, outer$);",
" return self;",
"}");
assertTranslatedLines(translation,
"- (instancetype)initWithTest:(Test *)outer$",
" withInt:(jint)i {",
" Test_Inner_initWithTest_withInt_(self, outer$, i);",
" return self;",
"}");
assertTranslatedLines(translation,
"void Test_Inner_initWithTest_(Test_Inner *self, Test *outer$) {",
" Test_Inner_initWithTest_withInt_(self, outer$, 42);",
"}");
assertTranslatedLines(translation,
"void Test_Inner_initWithTest_withInt_(Test_Inner *self, Test *outer$, jint i) {",
" NSObject_init(self);",
"}");
}
public void testSynchronizedMethod() throws IOException {
String translation = translateSourceFile(
"public class Test {"
+ " public synchronized void foo() {} }",
"Test", "Test.m");
assertTranslation(translation, "- (void)foo {\n"
+ " @synchronized(self) {");
}
public void testStaticSynchronizedMethod() throws IOException {
String translation = translateSourceFile(
"public class Test {"
+ " public static synchronized void foo() {} }",
"Test", "Test.m");
assertTranslation(translation, "void Test_foo() {\n"
+ " Test_initialize();\n"
+ " @synchronized(Test_class_()) {");
}
// Verify that an interface that has a generated implementation file and an Object method
// like toString() doesn't print a description method.
public void testNoInterfaceToString() throws IOException {
String translation = translateSourceFile(
"interface Test { public static final Object FOO = new Object(); String toString(); }",
"Test", "Test.m");
assertNotInTranslation(translation, "- (NSString *)description {");
}
public void testAddIgnoreDeprecationWarningsPragmaIfDeprecatedDeclarationsIsEnabled()
throws IOException {
options.enableDeprecatedDeclarations();
String translation = translateSourceFile(
"class Test { public static String foo; }", "Test", "Test.m");
assertTranslation(translation, "#pragma clang diagnostic push");
assertTranslation(translation, "#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"");
assertTranslation(translation, "#pragma clang diagnostic pop");
}
public void testDoNotAddIgnoreDeprecationWarningsPragmaIfDeprecatedDeclarationsIsDisabled()
throws IOException {
String translation = translateSourceFile(
"class Test { public static String foo; }", "Test", "Test.m");
assertNotInTranslation(translation,
"#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"");
}
public void testFreeFormNativeCode() throws IOException {
String translation = translateSourceFile(
"class Test { void method1() {} /*-[ OCNI1 ]-*/ "
+ "enum Inner { A, B; /*-[ OCNI2 ]-*/ void method2() {}} "
+ " native void method3() /*-[ OCNI3 ]-*/; }", "Test", "Test.m");
assertOccurrences(translation, "OCNI1", 1);
assertOccurrences(translation, "OCNI2", 1);
assertOccurrences(translation, "OCNI3", 1);
String testType = "@implementation Test\n";
String innerType = "@implementation Test_Inner";
String method1 = "- (void)method1";
String method2 = "- (void)method2";
String method3 = "- (void)method3";
assertOccurrences(translation, testType, 1);
assertOccurrences(translation, innerType, 1);
assertOccurrences(translation, method1, 1);
assertOccurrences(translation, method2, 1);
assertOccurrences(translation, method3, 1);
assertTrue(translation.indexOf(testType) < translation.indexOf(method1));
assertTrue(translation.indexOf(method1) < translation.indexOf("OCNI1"));
assertTrue(translation.indexOf("OCNI1") < translation.indexOf(method3));
assertTrue(translation.indexOf(method3) < translation.indexOf("OCNI3"));
assertTrue(translation.indexOf("OCNI3") < translation.indexOf(innerType));
assertTrue(translation.indexOf(innerType) < translation.indexOf("OCNI2"));
assertTrue(translation.indexOf("OCNI2") < translation.indexOf(method2));
}
public void testPrintsCountByEnumeratingWithState() throws IOException {
String translation = translateSourceFile(
"import java.util.Iterator; "
+ "abstract class Test implements Iterable { abstract public Iterator iterator(); }",
"Test", "Test.m");
assertTranslatedLines(translation,
"- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state "
+ "objects:(__unsafe_unretained id *)stackbuf count:(NSUInteger)len {",
"return JreDefaultFastEnumeration(self, state, stackbuf, len);",
"}");
}
public void testNoDuplicateCountByEnumeratingWithState() throws IOException {
String translation = translateSourceFile(
"import java.util.Iterator; "
+ "abstract class Test implements Iterable { abstract public Iterator iterator(); /*-[ "
+ "-(NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state "
+ "objects:(id *)stackbuf count:(NSUInteger)len { return 0; } ]-*/}",
"Test", "Test.m");
assertOccurrences(translation, "countByEnumeratingWithState", 1);
}
public void testSynchronizedNativeMethod() throws IOException {
String translation = translateSourceFile(
"class Test { public synchronized native void exit() /*-[ exit(0); ]-*/; }",
"Test", "Test.m");
assertTranslation(translation, "@synchronized(self)");
}
public void testAnnotationWithField() throws IOException {
String translation = translateSourceFile(
"@interface Test { String FOO = \"foo\"; int I = 5; }", "Test", "Test.h");
assertTranslation(translation, "#define Test_I 5");
assertTranslation(translation, "FOUNDATION_EXPORT NSString *Test_FOO;");
assertTranslation(translation, "J2OBJC_STATIC_FIELD_OBJ_FINAL(Test, FOO, NSString *)");
translation = getTranslatedFile("Test.m");
assertTranslation(translation, "NSString *Test_FOO = @\"foo\";");
assertTranslation(translation, "@interface Test : NSObject");
}
public void testCombinedGeneration() throws IOException {
addSourceFile("package unit; public class Test { }", "unit/Test.java");
addSourceFile("package unit; public class AnotherTest extends Test { }",
"unit/AnotherTest.java");
String translation = translateCombinedFiles(
"unit/Foo", ".m", "unit/Test.java", "unit/AnotherTest.java");
assertTranslation(translation, "source: unit/Foo.testfile");
assertTranslation(translation, "#include \"unit/Foo.h\"");
assertTranslation(translation, "#include \"J2ObjC_source.h\"");
assertTranslation(translation, "@implementation UnitTest");
assertTranslation(translation, "@implementation UnitAnotherTest");
}
public void testPackageInfoAnnotationAndDoc() throws IOException {
addSourcesToSourcepaths();
options.setDocCommentsEnabled(true);
addSourceFile("package foo.annotations;\n"
+ "import java.lang.annotation.*;\n"
+ "@Retention(RetentionPolicy.RUNTIME)\n"
+ "@Target(ElementType.PACKAGE)\n"
+ "public @interface Test {}",
"foo/annotations/Test.java");
String translation = translateSourceFile(
"/** A package doc-comment. */\n"
+ "@Test\n"
+ "package foo.bar.mumble;\n"
+ "import foo.annotations.Test;",
"package-info", "foo/bar/mumble/package-info.h");
assertTranslatedLines(translation, "/*!", "@brief A package doc-comment.", "*/");
translation = getTranslatedFile("foo/bar/mumble/package-info.m");
assertTranslation(translation, "@implementation FooBarMumblepackage_info");
assertTranslation(translation, "IOSObjectArray *FooBarMumblepackage_info__Annotations$0() {");
assertTranslation(translation, "create_FooAnnotationsTest()");
}
public void testPackageInfoAnnotationNoDoc() throws IOException {
addSourcesToSourcepaths();
addSourceFile("package foo.annotations;\n"
+ "import java.lang.annotation.*;\n"
+ "@Retention(RetentionPolicy.RUNTIME)\n"
+ "@Target(ElementType.PACKAGE)\n"
+ "public @interface Test {}",
"foo/annotations/Test.java");
String translation = translateSourceFile(
"@Test\n"
+ "package foo.bar.mumble;\n"
+ "import foo.annotations.Test;",
"package-info", "foo/bar/mumble/package-info.h");
assertNotInTranslation(translation, "/**");
translation = getTranslatedFile("foo/bar/mumble/package-info.m");
assertTranslation(translation, "@implementation FooBarMumblepackage_info");
assertTranslation(translation, "IOSObjectArray *FooBarMumblepackage_info__Annotations$0() {");
assertTranslation(translation, "create_FooAnnotationsTest()");
}
public void testPackageInfoDocNoAnnotation() throws IOException {
addSourcesToSourcepaths();
options.setDocCommentsEnabled(true);
String translation = translateSourceFile(
"/** A package doc-comment. */\n"
+ "package foo.bar.mumble;",
"package-info", "foo/bar/mumble/package-info.h");
assertTranslatedLines(translation, "/*!", "@brief A package doc-comment.", "*/");
translation = getTranslatedFile("foo/bar/mumble/package-info.m");
assertNotInTranslation(translation, "@implementation FooBarMumblepackage_info");
assertNotInTranslation(translation, "+ (IOSObjectArray *)__annotations");
}
public void testPackageInfoPrefixAnnotation() throws IOException {
addSourcesToSourcepaths();
addSourceFile(
"@ObjectiveCName(\"FBM\")\n"
+ "package foo.bar.mumble;\n"
+ "import com.google.j2objc.annotations.ObjectiveCName;",
"foo/bar/mumble/package-info.java");
String translation = translateSourceFile("package foo.bar.mumble;\n"
+ "public class Test {}",
"foo.bar.mumble.Test", "foo/bar/mumble/Test.h");
assertTranslation(translation, "@interface FBMTest");
assertTranslation(translation, "@compatibility_alias FooBarMumbleTest FBMTest;");
translation = getTranslatedFile("foo/bar/mumble/Test.m");
assertTranslation(translation, "@implementation FBMTest");
assertNotInTranslation(translation, "FooBarMumbleTest");
}
public void testPackageInfoPreprocessing() throws IOException {
addSourceFile(
"@ObjectiveCName(\"FBM\")\n"
+ "package foo.bar.mumble;\n"
+ "import com.google.j2objc.annotations.ObjectiveCName;",
"foo/bar/mumble/package-info.java");
preprocessFiles("foo/bar/mumble/package-info.java");
String translation = translateSourceFile("package foo.bar.mumble;\n"
+ "public class Test {}",
"foo.bar.mumble.Test", "foo/bar/mumble/Test.h");
assertTranslation(translation, "@interface FBMTest");
assertTranslation(translation, "@compatibility_alias FooBarMumbleTest FBMTest;");
translation = getTranslatedFile("foo/bar/mumble/Test.m");
assertTranslation(translation, "@implementation FBMTest");
assertNotInTranslation(translation, "FooBarMumbleTest");
}
public void testPackageInfoOnClasspath() throws IOException {
addSourceFile(
"@ObjectiveCName(\"FBM\")\n"
+ "package foo.bar.mumble;\n"
+ "import com.google.j2objc.annotations.ObjectiveCName;",
"src/foo/bar/mumble/package-info.java");
List<String> compileArgs = Lists.newArrayList();
compileArgs.add("-classpath");
compileArgs.add(System.getProperty("java.class.path"));
compileArgs.add("-encoding");
compileArgs.add(options.fileUtil().getCharset().name());
compileArgs.add("-source");
compileArgs.add("1.7");
compileArgs.add(tempDir.getAbsolutePath() + "/src/foo/bar/mumble/package-info.java");
org.eclipse.jdt.internal.compiler.batch.Main batchCompiler =
new org.eclipse.jdt.internal.compiler.batch.Main(
new PrintWriter(System.out), new PrintWriter(System.err),
false, Collections.emptyMap(), null);
batchCompiler.compile(compileArgs.toArray(new String[0]));
options.fileUtil().getClassPathEntries().add(tempDir.getAbsolutePath() + "/src/");
String translation = translateSourceFile("package foo.bar.mumble;\n"
+ "public class Test {}",
"foo.bar.mumble.Test", "foo/bar/mumble/Test.h");
assertTranslation(translation, "@interface FBMTest");
assertTranslation(translation, "@compatibility_alias FooBarMumbleTest FBMTest;");
translation = getTranslatedFile("foo/bar/mumble/Test.m");
assertTranslation(translation, "@implementation FBMTest");
assertNotInTranslation(translation, "FooBarMumbleTest");
}
public void testPackageInfoPrefixMethod() throws IOException {
String translation = translateSourceFile(
"@ObjectiveCName(\"FBM\")\npackage foo.bar.mumble;\n"
+ "import com.google.j2objc.annotations.ObjectiveCName;",
"foo.bar.mumble.package-info", "foo/bar/mumble/package-info.m");
assertTranslatedLines(translation,
"+ (NSString *)__prefix {",
" return @\"FBM\";",
"}");
}
public void testInitializeNotInClassExtension() throws IOException {
String translation = translateSourceFile(
"class Test { static Integer i = new Integer(5); }", "Test", "Test.m");
assertNotInTranslation(translation, "+ (void)initialize OBJC_METHOD_FAMILY_NONE;");
assertOccurrences(translation, "+ (void)initialize", 1);
}
public void testInterfaceTypeLiteralAsAnnotationValue() throws IOException {
addSourceFile(
"import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME)"
+ " @interface Foo { Class<?> value(); }", "Foo.java");
String translation = translateSourceFile(
"@Foo(CharSequence.class) class Test {}", "Test", "Test.m");
assertTranslation(translation, "JavaLangCharSequence_class_()");
}
public void testReservedWordAsAnnotationPropertyName() throws IOException {
String translation = translateSourceFile(
"package foo; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) "
+ "public @interface Bar { String namespace() default \"\"; } "
+ "class Test { Bar ann; String namespace() { return ann.namespace(); }}",
"Bar", "foo/Bar.m");
assertTranslation(translation, "@synthesize namespace__ = namespace___;");
assertTranslation(translation, "id<FooBar> create_FooBar(NSString *namespace__) {");
assertTranslation(translation, "self->namespace___ = RETAIN_(namespace__);");
assertTranslation(translation, "+ (NSString *)namespace__Default {");
}
public void testAnnotationWithDefaultAnnotation() throws IOException {
String translation = translateSourceFile(
"import java.lang.annotation.*; public class A { "
+ "@Retention(RetentionPolicy.RUNTIME) "
+ "public @interface InnerAnn {} "
+ "@Retention(RetentionPolicy.RUNTIME) "
+ "public @interface OuterAnn { InnerAnn test() default @InnerAnn(); }}",
"A", "A.m");
assertTranslatedLines(translation,
"+ (id<A_InnerAnn>)testDefault {", "return create_A_InnerAnn();", "}");
}
public void testAnnotationWithDefaultAnnotationWithArguments() throws IOException {
String translation = translateSourceFile(
"import java.lang.annotation.*; public class A { "
+ "@Retention(RetentionPolicy.RUNTIME) "
+ "public @interface InnerAnn { String foo(); int num(); } "
+ "@Retention(RetentionPolicy.RUNTIME) "
+ "public @interface OuterAnn { InnerAnn test() default @InnerAnn(foo=\"bar\", num=5); }}",
"A", "A.m");
assertTranslatedLines(translation,
"+ (id<A_InnerAnn>)testDefault {",
"return create_A_InnerAnn(@\"bar\", 5);",
"}");
}
public void testAnnotationsAsAnnotationValues() throws IOException {
String translation = translateSourceFile(
"import java.lang.annotation.*; "
+ "public class A {"
+ "@Retention(RetentionPolicy.RUNTIME) @interface Outer { Inner innerAnnotation(); }"
+ "@Retention(RetentionPolicy.RUNTIME) @interface Inner { String name(); }"
+ "@Outer(innerAnnotation=@Inner(name=\"Bar\")) class Foo {}}",
"A", "A.m");
assertTranslation(translation, "create_A_Outer(create_A_Inner(@\"Bar\"))");
}
public void testForwradDeclarationForPrivateAbstractDeclaration() throws IOException {
// We need a forward declaration of JavaLangInteger for the type narrowing declaration of get()
// in the private class B.
String translation = translateSourceFile(
"class Test { static class A <T> { T get() { return null; } }"
+ "private static class B extends A<Integer> { } }", "Test", "Test.m");
assertTranslation(translation, "@class JavaLangInteger;");
}
}