/*
* 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.translate;
import com.google.devtools.j2objc.GenerationTest;
import java.io.IOException;
/**
* Unit tests for {@link InitializationNormalizer} phase.
*
* @author Tom Ball
*/
public class InitializationNormalizerTest extends GenerationTest {
// TODO(tball): update bug id in comments to public issue numbers when
// issue tracking is sync'd.
/**
* Verify that for a constructor that calls another constructor and has
* other statements, the "this-constructor" statement is used to
* initialize self, rather than a super constructor call.
*/
public void testThisConstructorCallInlined() throws IOException {
String source = "class Test {"
+ "boolean b1; boolean b2;"
+ "Test() { this(true); b2 = true; }"
+ "Test(boolean b) { b1 = b; }}";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslatedLines(translation,
"void Test_init(Test *self) {",
" Test_initWithBoolean_(self, true);",
" self->b2_ = true;",
"}");
}
/**
* Regression test (b/5822974): translation fails with an
* ArrayIndexOutOfBoundsException in JDT, due to a syntax error in the
* vertices initializer after initialization normalization.
* @throws IOException
*/
public void testFieldArrayInitializer() throws IOException {
String source = "public class Distance {"
+ "private class SimplexVertex {}"
+ "private class Simplex {"
+ " public final SimplexVertex vertices[] = {"
+ " new SimplexVertex() "
+ " }; }}";
String translation = translateSourceFile(source, "Distance", "Distance.m");
assertTranslation(translation,
"[IOSObjectArray newArrayWithObjects:(id[]){ "
+ "create_Distance_SimplexVertex_initWithDistance_(outer$) } "
+ "count:1 type:Distance_SimplexVertex_class_()]");
}
public void testStaticVarInitialization() throws IOException {
String translation = translateSourceFile(
"class Test { static java.util.Date date = new java.util.Date(); }", "Test", "Test.m");
// test that initializer was stripped from the declaration
assertTranslation(translation, "JavaUtilDate *Test_date;");
// test that initializer was moved to new initialize method
assertTranslatedLines(translation,
"+ (void)initialize {",
"if (self == [Test class]) {",
"JreStrongAssignAndConsume(&Test_date, new_JavaUtilDate_init());");
}
public void testFieldInitializer() throws IOException {
String translation = translateSourceFile(
"class Test { java.util.Date date = new java.util.Date(); }", "Test", "Test.m");
// Test that a default constructor was created and the initializer statement
// moved to the constructor.
assertTranslatedLines(translation,
"void Test_init(Test *self) {",
" NSObject_init(self);",
" JreStrongAssignAndConsume(&self->date_, new_JavaUtilDate_init());",
"}");
}
public void testInitializationBlock() throws IOException {
String translation = translateSourceFile(
"class Test { java.util.Date date; { date = new java.util.Date(); } }", "Test", "Test.m");
// Test that a default constructor was created and the initializer statement
// moved to the constructor.
assertTranslatedLines(translation,
"void Test_init(Test *self) {",
" NSObject_init(self);",
" {",
" JreStrongAssignAndConsume(&self->date_, new_JavaUtilDate_init());",
" }",
"}");
}
public void testStaticInitializerBlock() throws IOException {
String translation = translateSourceFile(
"class Test { static { System.out.println(\"foo\"); } }", "Test", "Test.m");
// test that a static initialize() method was created and that it contains
// the block's statement.
assertTranslatedLines(translation,
"+ (void)initialize {",
"if (self == [Test class]) {",
"{",
"[((JavaIoPrintStream *) nil_chk(JreLoadStatic(JavaLangSystem, out))) "
+ "printlnWithNSString:@\"foo\"];");
}
public void testInitializerMovedToDesignatedConstructor() throws IOException {
String translation = translateSourceFile(
"class Test { java.util.Date date; { date = new java.util.Date(); } "
+ "public Test() { this(2); } public Test(int i) { System.out.println(i); } }",
"Test", "Test.m");
// test that default constructor was untouched, since it calls self()
assertTranslatedLines(translation,
"void Test_init(Test *self) {",
" Test_initWithInt_(self, 2);",
"}");
// test that initializer statement was added to second constructor
assertTranslatedLines(translation,
"void Test_initWithInt_(Test *self, jint i) {",
" NSObject_init(self);",
" {",
" JreStrongAssignAndConsume(&self->date_, new_JavaUtilDate_init());",
" }",
" [((JavaIoPrintStream *) nil_chk(JreLoadStatic(JavaLangSystem, out))) "
+ "printlnWithInt:i];",
"}");
}
public void testInitializerMovedToEmptyConstructor() throws IOException {
String translation = translateSourceFile(
"class Test { java.util.Date date = new java.util.Date(); public Test() {} }",
"Test", "Test.m");
assertTranslatedLines(translation,
"void Test_init(Test *self) {",
" NSObject_init(self);",
" JreStrongAssignAndConsume(&self->date_, new_JavaUtilDate_init());",
"}");
}
/**
* Regression test (b/5861660): translation fails with an NPE when
* an interface has a constant defined.
*/
public void testInterfaceConstantsIgnored() throws IOException {
String source = "public interface Mouse { int BUTTON_LEFT = 0; }";
String translation = translateSourceFile(source, "Mouse", "Mouse.h");
assertTranslation(translation, "#define Mouse_BUTTON_LEFT 0");
}
public void testStringWithInvalidCppCharacters() throws IOException {
String source = "class Test { static final String foo = \"\\udfff\"; }";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation, "NSString *Test_foo;");
assertTranslation(translation,
"JreStrongAssign(&Test_foo, [NSString stringWithCharacters:(jchar[]) { "
+ "(int) 0xdfff } length:1]);");
}
public void testStringConcatWithInvalidCppCharacters() throws IOException {
// Include a 1 between the strings so javac's parser doesn't combine them.
String source = "class Test { static final String foo = \"hello\" + 1 + \"\\udfff\"; }";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation, "NSString *Test_foo;");
assertTranslation(translation,
"JreStrongAssign(&Test_foo, JreStrcat(\"$$\", @\"hello1\", "
+ "[NSString stringWithCharacters:(jchar[]) { (int) 0xdfff } length:1]));");
}
public void testInitializersPlacedAfterOuterAssignments() throws IOException {
String source = "class Test { "
+ " int outerVar = 1; "
+ " class Inner { int innerVar = outerVar; void test() { outerVar++; } } }";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation, "JreStrongAssign(&self->this$0_, outer$);");
assertTranslation(translation, "innerVar_ = outer$->outerVar_;");
assertTrue(translation.indexOf("JreStrongAssign(&self->this$0_, outer$);")
< translation.indexOf("innerVar_ = outer$->outerVar_;"));
}
public void testStaticInitializersKeptInOrder() throws IOException {
String source =
"public class Test { "
+ " public static final int I = 1; "
+ " public static final java.util.Set<Integer> iSet = new java.util.HashSet<Integer>(); "
+ " static { iSet.add(I); } "
+ " public static final int iSetSize = iSet.size(); }";
String translation = translateSourceFile(source, "Test", "Test.m");
String setInit = "JreStrongAssignAndConsume(&Test_iSet, new_JavaUtilHashSet_init())";
String setAdd = "[Test_iSet addWithId:JavaLangInteger_valueOfWithInt_(Test_I)]";
String setSize = "Test_iSetSize = [Test_iSet size]";
assertTranslation(translation, setInit);
assertTranslation(translation, setAdd);
assertTranslation(translation, setSize);
assertTrue(translation.indexOf(setInit) < translation.indexOf(setAdd));
assertTrue(translation.indexOf(setAdd) < translation.indexOf(setSize));
}
public void testStaticFinalStringAssignedToStaticFinalString() throws IOException {
String translation = translateSourceFile(
"class Test { static final String FOO = Inner.BAR; "
+ "class Inner { static final String BAR = \"bar\"; } }", "Test", "Test.m");
assertTranslation(translation, "NSString *Test_FOO = @\"bar\";");
}
public void testVarargConstructorCallFromSubclass() throws IOException {
String translation = translateSourceFile(
"class A { A(Object ... bars) {} static class B extends A {}}",
"A", "A.m");
assertNotInTranslation(translation, "A_init(self);");
assertTranslation(translation, "A_initWithNSObjectArray_(self, "
+ "[IOSObjectArray arrayWithLength:0 type:NSObject_class_()]);");
}
/**
* Verify that the java/lang/Object.java stub file can be translated.
* InitializationNormalizer.normalizeMethod() threw an NPE because
* Object's supertype is null.
*/
public void testTranslateObject() throws IOException {
String source = "package java.lang;"
+ "public class Object {"
+ " public Object() {}}";
String translation = translateSourceFile(source, "Object", "java/lang/Object.h");
assertTranslation(translation, "@interface NSObject");
}
// Regression test for Issue #809.
public void testConstantInitializedByConditionalOnConstants() throws IOException {
String translation = translateSourceFile(
"class Test { private static final int A = 1; private static final int B = 2; "
+ "private static final int C = A < B ? A : B; }", "Test", "Test.m");
assertTranslation(translation, "#define Test_C 1");
}
// Verify that code in a double-brace is added to initialize method.
public void testDoubleBraceInitialization() throws IOException {
String translation = translateSourceFile(
"import java.util.*; class Test { static final Map<String, String> test = "
+ "new HashMap<String, String>() {{ put(\"123\", \"123\"); }}; }", "Test", "Test.m");
assertTranslatedLines(translation,
"void Test_1_init(Test_1 *self) {",
"JavaUtilHashMap_init(self);",
"{",
"[self putWithId:@\"123\" withId:@\"123\"];",
"}",
"}");
}
public void testConstantStringInitializationOrder() throws IOException {
String translation = translateSourceFile(
"class Test { private static final int CODE_POINT = getCodePoint(); "
+ "private static final String FOO = \"\\uda6e\"; "
+ "static int getCodePoint() { return FOO.codePointAt(0); } }", "Test", "Test.m");
assertTranslatedLines(translation,
"+ (void)initialize {",
" if (self == [Test class]) {",
// Initialization of the constant FOO must come before initialization of non-constants.
" JreStrongAssign(&Test_FOO, [NSString stringWithCharacters:(jchar[]) { "
+ "(int) 0xda6e } length:1]);",
" Test_CODE_POINT = Test_getCodePoint();");
}
}