| // Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| package com.android.tools.r8.dexsplitter; | |
| import static org.junit.Assert.assertEquals; | |
| import static org.junit.Assert.assertNotEquals; | |
| import static org.junit.Assert.assertTrue; | |
| import static org.junit.Assume.assumeTrue; | |
| import com.android.tools.r8.CompilationFailedException; | |
| import com.android.tools.r8.FeatureSplit; | |
| import com.android.tools.r8.NeverInline; | |
| import com.android.tools.r8.NeverMerge; | |
| import com.android.tools.r8.R8FullTestBuilder; | |
| import com.android.tools.r8.R8TestCompileResult; | |
| import com.android.tools.r8.TestParameters; | |
| import com.android.tools.r8.TestParametersCollection; | |
| import com.android.tools.r8.ToolHelper.ProcessResult; | |
| import com.android.tools.r8.utils.StringUtils; | |
| import com.android.tools.r8.utils.codeinspector.ClassSubject; | |
| import com.google.common.collect.ImmutableSet; | |
| import java.io.IOException; | |
| import java.util.concurrent.ExecutionException; | |
| import java.util.function.Consumer; | |
| import java.util.function.Predicate; | |
| import org.junit.Test; | |
| import org.junit.runner.RunWith; | |
| import org.junit.runners.Parameterized; | |
| import org.junit.runners.Parameterized.Parameters; | |
| @RunWith(Parameterized.class) | |
| public class DexSplitterMergeRegression extends SplitterTestBase { | |
| public static final String EXPECTED = StringUtils.lines("42", "foobar"); | |
| @Parameters(name = "{0}") | |
| public static TestParametersCollection params() { | |
| return getTestParameters().withAllRuntimes().build(); | |
| } | |
| private final TestParameters parameters; | |
| public DexSplitterMergeRegression(TestParameters parameters) { | |
| this.parameters = parameters; | |
| } | |
| @Test | |
| public void testInliningFromFeature() throws Exception { | |
| // Static merging is based on sorting order, we assert that we merged to the feature. | |
| Predicate<R8TestCompileResult> ensureMergingToFeature = | |
| r8TestCompileResult -> { | |
| try { | |
| ClassSubject clazz = r8TestCompileResult.inspector().clazz(AFeatureWithStatic.class); | |
| return clazz.allMethods().size() == 2 | |
| && clazz.uniqueMethodWithName("getBase42").isPresent() | |
| && clazz.uniqueMethodWithName("getFoobar").isPresent(); | |
| } catch (IOException | ExecutionException ex) { | |
| throw new RuntimeException("Failed lookup up AFeatureWithStatic"); | |
| } | |
| }; | |
| Consumer<R8FullTestBuilder> configurator = | |
| r8FullTestBuilder -> | |
| r8FullTestBuilder | |
| .enableMergeAnnotations() | |
| .enableInliningAnnotations() | |
| .noMinification() | |
| .addOptionsModification(o -> o.testing.deterministicSortingBasedOnDexType = true); | |
| ProcessResult processResult = | |
| testDexSplitter( | |
| parameters, | |
| ImmutableSet.of(BaseClass.class, BaseWithStatic.class), | |
| ImmutableSet.of(FeatureClass.class, AFeatureWithStatic.class), | |
| FeatureClass.class, | |
| EXPECTED, | |
| ensureMergingToFeature, | |
| configurator); | |
| // We expect art to fail on this with the dex splitter. | |
| assertNotEquals(processResult.exitCode, 0); | |
| assertTrue(processResult.stderr.contains("NoClassDefFoundError")); | |
| } | |
| @Test | |
| public void testOnR8Splitter() throws IOException, CompilationFailedException, | |
| ExecutionException { | |
| assumeTrue(parameters.isDexRuntime()); | |
| Consumer<R8FullTestBuilder> configurator = | |
| r8FullTestBuilder -> r8FullTestBuilder.enableMergeAnnotations().noMinification(); | |
| ProcessResult processResult = | |
| testR8Splitter( | |
| parameters, | |
| ImmutableSet.of(BaseClass.class, BaseWithStatic.class), | |
| ImmutableSet.of(FeatureClass.class, AFeatureWithStatic.class), | |
| FeatureClass.class, | |
| EXPECTED, | |
| a -> true, | |
| configurator); | |
| assertEquals(processResult.exitCode, 0); | |
| assertTrue(processResult.stdout.equals(StringUtils.lines("42", "foobar"))); | |
| } | |
| @NeverMerge | |
| public static class BaseClass implements RunInterface { | |
| @NeverInline | |
| public void run() { | |
| System.out.println(BaseWithStatic.getBase42()); | |
| } | |
| } | |
| public static class BaseWithStatic { | |
| @NeverInline | |
| public static int getBase42() { | |
| if (System.currentTimeMillis() < 2) { | |
| return 43; | |
| } else { | |
| return 42; | |
| } | |
| } | |
| } | |
| public static class FeatureClass extends BaseClass { | |
| public void run() { | |
| super.run(); | |
| System.out.println(AFeatureWithStatic.getFoobar()); | |
| } | |
| } | |
| // Name is important, see predicate in tests/ | |
| public static class AFeatureWithStatic { | |
| @NeverInline | |
| public static String getFoobar() { | |
| if (System.currentTimeMillis() < 2) { | |
| return "barFoo"; | |
| } else { | |
| return "foobar"; | |
| } | |
| } | |
| } | |
| } |