| /* |
| * Copyright 2012-present Facebook, Inc. |
| * |
| * 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.facebook.buck.dalvik; |
| |
| import static org.junit.Assert.*; |
| |
| import com.google.common.base.Functions; |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Sets; |
| |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.TemporaryFolder; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.InputStream; |
| import java.net.URI; |
| import java.util.Set; |
| |
| import javax.tools.JavaCompiler; |
| import javax.tools.SimpleJavaFileObject; |
| import javax.tools.ToolProvider; |
| |
| /** |
| * Tests for {@link DalvikStatsTool}. |
| */ |
| public class DalvikStatsToolTest { |
| |
| private static final String TEST_CLASS = createSource( |
| "package test;", |
| "", |
| "import java.lang.StringBuilder;", |
| "", |
| "public class TestClass {", |
| "", |
| " public String get() {", |
| " StringBuilder sb = new StringBuilder();", |
| " sb.append(\"foo\");", |
| " return sb.toString();", |
| " }", |
| "}"); |
| |
| private static final String TEST_CLASS_WITH_INNER = createSource( |
| "package test;", |
| "", |
| "public class TestClassWithInner {", |
| "", |
| " private long startTime;", |
| "", |
| " public TestClassWithInner() {", |
| " startTime = System.currentTimeMillis();", |
| " }", |
| "", |
| " public Object get() {", |
| " return new Object() {", |
| " public String toString() {", |
| " return Long.toString(startTime);", |
| " }", |
| " };", |
| " }", |
| "}"); |
| |
| @Rule |
| public TemporaryFolder tmpDir = new TemporaryFolder(); |
| |
| private File outputDir; |
| |
| @Before |
| public void setUp() throws Exception { |
| ImmutableList.Builder<String> builder = ImmutableList.builder(); |
| outputDir = tmpDir.newFolder("output"); |
| builder.add("-d", outputDir.toString()); |
| |
| ImmutableList<JavaSourceFromString> inputs = ImmutableList.of( |
| new JavaSourceFromString("TestClass", TEST_CLASS), |
| new JavaSourceFromString("TestClassWithInner", TEST_CLASS_WITH_INNER)); |
| JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); |
| JavaCompiler.CompilationTask task = |
| compiler.getTask(null, null, null, builder.build(), null, inputs); |
| assertTrue(task.call()); |
| } |
| |
| @Test |
| public void testSimpleClass() throws Exception { |
| File classFile = new File(outputDir, "test/TestClass.class"); |
| InputStream inputStream = new FileInputStream(classFile); |
| DalvikStatsTool.Stats stats = DalvikStatsTool.getEstimate(inputStream); |
| assertMethodReferences( |
| stats.methodReferences, |
| "test/TestClass.<init>:()V", |
| "java/lang/StringBuilder.toString:()Ljava/lang/String;", |
| "test/TestClass.get:()Ljava/lang/String;", |
| "java/lang/StringBuilder.<init>:()V", |
| "java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;", |
| "java/lang/Object.<init>:()V" |
| ); |
| assertEquals(156, stats.estimatedLinearAllocSize); |
| } |
| |
| @Test |
| public void testClassWithInner() throws Exception { |
| File classFileOuter = new File(outputDir, "test/TestClassWithInner.class"); |
| InputStream inputStreamOuter = new FileInputStream(classFileOuter); |
| DalvikStatsTool.Stats statsOuter = DalvikStatsTool.getEstimate(inputStreamOuter); |
| assertMethodReferences( |
| statsOuter.methodReferences, |
| "test/TestClassWithInner.<init>:()V", |
| "java/lang/Object.<init>:()V", |
| "test/TestClassWithInner.get:()Ljava/lang/Object;", |
| "test/TestClassWithInner$1.<init>:(Ltest/TestClassWithInner;)V", |
| "test/TestClassWithInner.access$000:(Ltest/TestClassWithInner;)J", |
| "java/lang/System.currentTimeMillis:()J" |
| ); |
| assertEquals(224, statsOuter.estimatedLinearAllocSize); |
| |
| File classFileInner = new File(outputDir, "test/TestClassWithInner$1.class"); |
| InputStream inputStreamInner = new FileInputStream(classFileInner); |
| DalvikStatsTool.Stats statsInner = DalvikStatsTool.getEstimate(inputStreamInner); |
| assertMethodReferences( |
| statsInner.methodReferences, |
| "test/TestClassWithInner$1.toString:()Ljava/lang/String;", |
| "test/TestClassWithInner$1.<init>:(Ltest/TestClassWithInner;)V", |
| "test/TestClassWithInner$1.get:()Ljava/lang/Object;", |
| "java/lang/Object.<init>:()V", |
| "java/lang/Long.toString:(J)Ljava/lang/String;", |
| "test/TestClassWithInner.access$000:(Ltest/TestClassWithInner;)J" // visitOuterClass |
| |
| ); |
| assertEquals(172, statsInner.estimatedLinearAllocSize); |
| } |
| |
| /** |
| * A file object used to represent source coming from a string. |
| */ |
| public class JavaSourceFromString extends SimpleJavaFileObject { |
| /** |
| * The source code of this "file". |
| */ |
| final String code; |
| |
| /** |
| * Constructs a new JavaSourceFromString. |
| * @param name the name of the compilation unit represented by this file object |
| * @param code the source code for the compilation unit represented by this file object |
| */ |
| JavaSourceFromString(String name, String code) { |
| super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); |
| this.code = code; |
| } |
| |
| @Override |
| public CharSequence getCharContent(boolean ignoreEncodingErrors) { |
| return code; |
| } |
| } |
| |
| private static String createSource(String... args) { |
| return Joiner.on("\n").join(args); |
| } |
| |
| private static void assertMethodReferences( |
| Set<DalvikStatsTool.MethodReference> references, |
| String... methods) { |
| Set<String> actual = Sets.newHashSet( |
| Iterables.transform(references, Functions.toStringFunction())); |
| Set<String> expected = Sets.newHashSet(methods); |
| |
| Set<String> onlyInActual = Sets.difference(actual, expected); |
| Set<String> onlyInExpected = Sets.difference(expected, actual); |
| if (onlyInActual.isEmpty() && onlyInExpected.isEmpty()) { |
| return; |
| } |
| StringBuilder sbError = new StringBuilder(); |
| if (!onlyInExpected.isEmpty()) { |
| sbError.append("Missing method references:\n"); |
| for (String s : onlyInExpected) { |
| sbError.append(" ").append(s).append("\n"); |
| } |
| } |
| if (!onlyInActual.isEmpty()) { |
| sbError.append("Unexpected method references:\n"); |
| for (String s : onlyInActual) { |
| sbError.append(" ").append(s).append("\n"); |
| } |
| } |
| fail(sbError.toString()); |
| } |
| } |