blob: 89d0115dc9a61accdc5e3facb7f15e326c3bd429 [file]
// 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.bootstrap;
import static com.android.tools.r8.utils.DescriptorUtils.JAVA_PACKAGE_SEPARATOR;
import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.Version;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.CfUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.StreamUtils;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.Set;
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 SanityCheck extends TestBase {
private static final String SRV_PREFIX = "META-INF/services/";
private static final String METADATA_EXTENSION =
"com.android.tools.r8.jetbrains.kotlin.metadata.internal.extensions.MetadataExtensions";
private static final String EXT_IN_SRV = SRV_PREFIX + METADATA_EXTENSION;
private static final String THREADING_MODULE_SERVICE_FILE =
"META-INF/services/com.android.tools.r8.threading.ThreadingModuleProvider";
@Parameters
public static TestParametersCollection data() {
return TestParameters.builder().withNoneRuntime().build();
}
public SanityCheck(TestParameters parameters) {
parameters.assertNoneRuntime();
}
private void checkJarContent(
Path jar, boolean allowDirectories, ClassNameMapper mapping, Predicate<String> entryTester)
throws Exception {
BooleanBox licenseSeen = new BooleanBox();
Set<String> apiDatabaseFiles = Sets.newHashSet("resources/new_api_database.ser");
Set<String> r8AssistantRuntime =
ImmutableSet.of(
"ReflectiveEventType.java",
"ReflectiveOperationJsonLogger.java",
"ReflectiveOracle.java",
"EmptyReflectiveOperationReceiver.java",
"ReflectiveOperationReceiver.java",
"ReflectiveOperationLogger.java");
try {
ZipUtils.iter(
jar,
(entry, input) -> {
String name = entry.getName();
if (ZipUtils.isClassFile(name) || FileUtils.isKotlinBuiltinsFile(name)) {
assertThat(name, startsWith("com/android/tools/r8/"));
if (ZipUtils.isClassFile(name)) {
byte[] classFileBytes = StreamUtils.streamToByteArrayClose(input);
String sourceFile = extractSourceFile(classFileBytes);
if (!r8AssistantRuntime.contains(sourceFile)) {
if (mapping != null) {
assertNotNull(sourceFile);
if (Version.isMainVersion()) {
assertTrue(sourceFile, sourceFile.startsWith("R8_"));
assertEquals(
sourceFile.contains("+excldeps") ? 117 : 108, sourceFile.length());
} else {
assertTrue(sourceFile, sourceFile.startsWith("R8_" + Version.LABEL));
assertEquals(
68 + Version.LABEL.length() + (sourceFile.contains("+excldeps") ? 9 : 0),
sourceFile.length());
}
} else {
// Some class files from third party libraries does not have a SourceFile
// attribute.
if (sourceFile != null) {
assertTrue(
sourceFile, sourceFile.endsWith(".java") || sourceFile.endsWith(".kt"));
}
}
} else {
String className = CfUtils.extractClassName(classFileBytes);
int lastPackageSeparator = className.lastIndexOf(JAVA_PACKAGE_SEPARATOR);
String expectedSourceFile = className.substring(lastPackageSeparator + 1);
int firstInnerclassSeparator = expectedSourceFile.indexOf('$');
if (firstInnerclassSeparator >= 0) {
expectedSourceFile = expectedSourceFile.substring(0, firstInnerclassSeparator);
}
expectedSourceFile += JAVA_EXTENSION;
assertEquals(expectedSourceFile, sourceFile);
}
}
} else if (name.equals("META-INF/MANIFEST.MF")) {
// Allow.
} else if (name.equals("LICENSE")) {
licenseSeen.set();
} else if (name.equals(THREADING_MODULE_SERVICE_FILE)) {
// Allow.
} else if (entryTester.test(name)) {
// Allow.
} else if (apiDatabaseFiles.contains(name)) {
// Allow all api database files.
apiDatabaseFiles.remove(name);
} else if (name.endsWith("/")) {
assertTrue("Unexpected directory entry in" + jar, allowDirectories);
} else {
fail("Unexpected entry '" + name + "' in " + jar);
}
});
assertTrue(apiDatabaseFiles.isEmpty());
assertTrue(
"No LICENSE entry found in " + jar, licenseSeen.isAssigned() && licenseSeen.isTrue());
} catch (IOException e) {
if (!Files.exists(jar)) {
throw new NoSuchFileException(jar.toString());
} else {
throw e;
}
}
}
private void checkLibJarContent(Path jar, Path map) throws Exception {
if (!Files.exists(jar)) {
return;
}
assertTrue(Files.exists(map));
ClassNameMapper mapping = ClassNameMapper.mapperFromFile(map);
checkJarContent(jar, false, mapping, name -> metadataExtensionTester(name, mapping));
}
private void checkJarContent(Path jar) throws Exception {
if (!Files.exists(jar)) {
return;
}
checkJarContent(jar, true, null, name -> metadataExtensionTester(name, null));
}
private boolean metadataExtensionTester(String name, ClassNameMapper mapping) {
if (name.equals(EXT_IN_SRV)) {
assertNull(mapping);
return true;
}
if (mapping != null && name.startsWith(SRV_PREFIX)) {
String obfuscatedName = name.substring(SRV_PREFIX.length());
String originalName =
mapping.getObfuscatedToOriginalMapping().original
.getOrDefault(obfuscatedName, obfuscatedName);
if (originalName.equals(METADATA_EXTENSION)) {
return true;
}
}
return false;
}
@Test
public void testLibJarsContent() throws Exception {
assumeTrue(ToolHelper.isTestingR8Lib());
checkLibJarContent(ToolHelper.R8LIB_JAR, ToolHelper.R8LIB_MAP);
checkLibJarContent(ToolHelper.R8LIB_EXCLUDE_DEPS_JAR, ToolHelper.R8LIB_EXCLUDE_DEPS_MAP);
}
@Test
public void testJarsContent() throws Exception {
checkJarContent(ToolHelper.getR8WithRelocatedDeps());
}
}