/* * 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 com.google.devtools.j2objc.ast.Statement; import java.io.IOException; import java.util.List; /** * Unit tests for {@link JavaToIOSMethodTranslatorTest}. * * @author Tom Ball */ public class JavaToIOSMethodTranslatorTest extends GenerationTest { public void testCopyCloneRenaming() throws IOException { String translation = translateSourceFile( "public class Example implements Cloneable { " + "int i;" + "public Example copy() { return (Example) clone(); }" + "public Object clone() { " + " try { Example e = (Example) super.clone(); e.i = i; return e; } " + " catch (CloneNotSupportedException e) { return null; }}}", "Example", "Example.h"); assertTranslation(translation, "- (Example *)copy__ OBJC_METHOD_FAMILY_NONE;"); translation = getTranslatedFile("Example.m"); assertTranslation(translation, "return (Example *) cast_chk([self java_clone], [Example class]);"); assertTranslation(translation, "- (id)copyWithZone:(NSZone *)zone {"); assertTranslation(translation, "Example *e = (Example *) cast_chk([super java_clone], [Example class]);"); assertTranslation(translation, "((Example *) nil_chk(e))->i_ = i_"); } public void testStringValueOfBoolean() throws IOException { String source = "String trueString = String.valueOf(true); String falseString = String.valueOf(false);"; List<Statement> stmts = translateStatements(source); assertEquals(2, stmts.size()); String result = generateStatement(stmts.get(0)); assertEquals("NSString *trueString = NSString_java_valueOfBool_(true);", result); result = generateStatement(stmts.get(1)); assertEquals("NSString *falseString = NSString_java_valueOfBool_(false);", result); } /** * Verify that statements within a translated method get translated, too. */ public void testMethodAndStatementTranslation() throws IOException { String translation = translateSourceFile( "class Test { public String toString(boolean value) { return String.valueOf(value); } }", "Test", "Test.m"); assertTranslatedLines(translation, "- (NSString *)toStringWithBoolean:(jboolean)value {", "return NSString_java_valueOfBool_(value);"); } public void testStringDefaultConstructor() throws IOException { String source = "String s = new String();"; List<Statement> stmts = translateStatements(source); assertEquals(1, stmts.size()); String result = generateStatement(stmts.get(0)); assertEquals("NSString *s = [NSString string];", result); } public void testStringConstructorWithString() throws IOException { String source = "String s = new String(\"test\");"; List<Statement> stmts = translateStatements(source); assertEquals(1, stmts.size()); String result = generateStatement(stmts.get(0)); assertEquals("NSString *s = @\"test\";", result); } public void testStringHashCode() throws IOException { String source = "int test = \"foo\".hashCode();"; List<Statement> stmts = translateStatements(source); assertEquals(1, stmts.size()); String result = generateStatement(stmts.get(0)); assertEquals("jint test = ((jint) [@\"foo\" hash]);", result); } public void testClassGetSuperclass() throws IOException { String source = "Class cls = getClass(); Class superClass = cls.getSuperclass();"; List<Statement> stmts = translateStatements(source); assertEquals(2, stmts.size()); String result = generateStatement(stmts.get(0)); assertEquals("IOSClass *cls = [self java_getClass];", result); result = generateStatement(stmts.get(1)); assertEquals("IOSClass *superClass = [cls getSuperclass];", result); } /** * Verify that Class.getName() and Class.getSimpleName() return the * Obj-C class name. They are the same because Obj-C has a flat class * namespace. */ public void testClassGetName() throws IOException { String source = "Class cls = getClass(); String s1 = cls.getName();" + "String s2 = cls.getSimpleName(); String s3 = cls.getCanonicalName();"; List<Statement> stmts = translateStatements(source); assertEquals(4, stmts.size()); String result = generateStatement(stmts.get(1)); assertEquals("NSString *s1 = [cls getName];", result); result = generateStatement(stmts.get(2)); assertEquals("NSString *s2 = [cls getSimpleName];", result); result = generateStatement(stmts.get(3)); assertEquals("NSString *s3 = [cls getCanonicalName];", result); } public void testStringSubstring() throws IOException { String source = "String s1 = \"123456\"; String s2 = s1.substring(2); " + "String s3 = s1.substring(2, 4);"; List<Statement> stmts = translateStatements(source); assertEquals(3, stmts.size()); String result = generateStatement(stmts.get(1)); assertEquals("NSString *s2 = [s1 java_substring:2];", result); result = generateStatement(stmts.get(2)); assertEquals("NSString *s3 = [s1 java_substring:2 endIndex:4];", result); } public void testStringIndexOf() throws IOException { String source = "String s = \"'twas brillig, and the slithy toves\";" + "int idx = s.indexOf('g'); idx = s.indexOf(\"brillig\");" + "idx = s.lastIndexOf('v'); idx = s.lastIndexOf(\"the\");" + "idx = s.indexOf('g', 1); idx = s.indexOf(\"brillig\", 2);" + "idx = s.lastIndexOf('v', 3); idx = s.lastIndexOf(\"the\", 4);"; List<Statement> stmts = translateStatements(source); assertEquals(9, stmts.size()); String result = generateStatement(stmts.get(1)); assertEquals("jint idx = [s java_indexOf:'g'];", result); result = generateStatement(stmts.get(2)); assertEquals("idx = [s java_indexOfString:@\"brillig\"];", result); result = generateStatement(stmts.get(3)); assertEquals("idx = [s java_lastIndexOf:'v'];", result); result = generateStatement(stmts.get(4)); assertEquals("idx = [s java_lastIndexOfString:@\"the\"];", result); result = generateStatement(stmts.get(5)); assertEquals("idx = [s java_indexOf:'g' fromIndex:1];", result); result = generateStatement(stmts.get(6)); assertEquals("idx = [s java_indexOfString:@\"brillig\" fromIndex:2];", result); result = generateStatement(stmts.get(7)); assertEquals("idx = [s java_lastIndexOf:'v' fromIndex:3];", result); result = generateStatement(stmts.get(8)); assertEquals("idx = [s java_lastIndexOfString:@\"the\" fromIndex:4];", result); } public void testStringToCharArray() throws IOException { String source = "String s = \"123456\"; char[] array = s.toCharArray();"; List<Statement> stmts = translateStatements(source); assertEquals(2, stmts.size()); String result = generateStatement(stmts.get(1)); assertEquals("IOSCharArray *array = [s java_toCharArray];", result); } public void testNewInstanceMapping() throws IOException { String source = "try { Class<?> clazz = Object.class; " + "Object o = clazz.newInstance(); } catch (Exception e) {}"; List<Statement> stmts = translateStatements(source); assertEquals(1, stmts.size()); String result = generateStatement(stmts.get(0)); assertTranslation(result, "[clazz newInstance]"); } // Verify that a method named cloned in a class that doesn't // implement Cloneable is unchanged. public void testNonCloneableClone() throws IOException { String translation = translateSourceFile( "public class Example { " + "String s; " + "public Object clone() { " + " Example e = new Example();" + " e.s = s;" + " return e;" + "}}", "Example", "Example.h"); assertTranslation(translation, "- (id)java_clone;"); translation = getTranslatedFile("Example.m"); assertTranslation(translation, "- (id)java_clone {"); } // Verify that if a Cloneable class doesn't have a clone method, public void testCloneMethodAddedToCloneable() throws IOException { String translation = translateSourceFile( "public class Example implements Cloneable { int i; }", "Example", "Example.m"); assertTranslation(translation, "- (id)copyWithZone:(NSZone *)zone {"); assertTranslation(translation, "return [[self java_clone] retain];"); } public void testCloneRenamingWithSuperClone() throws IOException { String translation = translateSourceFile( "public class Example implements Cloneable { " + "class Inner extends Example {" + " int i;" + " public Example copy() { return (Example) clone(); }" + " public Object clone() { " + " try { Inner inner = (Inner) super.clone(); inner.i = i; return inner; } " + " catch (CloneNotSupportedException e) { return null; }}}}", "Example", "Example.h"); assertTranslation(translation, "- (Example *)copy__ OBJC_METHOD_FAMILY_NONE;"); translation = getTranslatedFile("Example.m"); assertTranslation(translation, "return (Example *) cast_chk([self java_clone], [Example class]);"); assertTranslation(translation, "- (id)copyWithZone:(NSZone *)zone {"); assertTranslation(translation, "Example_Inner *inner = " + "(Example_Inner *) cast_chk([super java_clone], [Example_Inner class]);"); assertTranslation(translation, "((Example_Inner *) nil_chk(inner))->i_ = i_;"); } // Ensure using the builder pattern does not invoke O(2^N) running time. // The test below would have taken about 100 days to run before this was fixed. public void testHugeBuilderExpression() throws IOException { translateSourceFile( "public class Example { " + " static class Builder { " + " Example build() { return new Example(); } " + " Builder add(int i) { return this; } " + " } " + " static Example create() { " + " return new Builder()" + " .add(10).add(11).add(12).add(13).add(14).add(15).add(16).add(17).add(18).add(19)" + " .add(20).add(21).add(22).add(23).add(24).add(25).add(26).add(27).add(28).add(29)" + " .add(30).add(31).add(32).add(33).add(34).add(35).add(36).add(37).add(38).add(39)" + " .add(40).add(41).add(42).add(43).add(44).add(45).add(46).add(47).add(48).add(49)" + " .build(); " + "} }", "Example", "Example.m"); } }