// Copyright 2016 The Bazel Authors. 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.build.android; import static com.google.common.truth.Truth.assertThat; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileTime; import java.util.Collections; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for {@link RClassGeneratorAction}. */ @RunWith(JUnit4.class) public class RClassGeneratorActionTest { private Path tempDir; @Before public void setUp() throws IOException { tempDir = Files.createTempDirectory(toString()); } /** * TODO(jvoung): use {@link AndroidDataBuilder} instead, once that's moved to this source tree. * This is a slimmed down version used to avoid dependencies. */ private static class ManifestBuilder { private final Path root; private ManifestBuilder(Path root) { this.root = root; } public static ManifestBuilder of(Path root) { return new ManifestBuilder(root); } public Path createManifest(String path, String manifestPackage, String... lines) throws IOException { Path manifest = root.resolve(path); Files.createDirectories(root); Files.write(manifest, String.format( "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" " + " package=\"%s\">" + "%s</manifest>", manifestPackage, Joiner.on("\n").join(lines)).getBytes(StandardCharsets.UTF_8)); return manifest; } } @Test public void withBinaryAndLibraries() throws Exception { Path binaryManifest = ManifestBuilder.of(tempDir.resolve("binary")) .createManifest("AndroidManifest.xml", "com.google.app", "<application android:name=\"com.google.app\">", "<activity android:name=\"com.google.bar.activityFoo\" />", "</application>"); Path libFooManifest = ManifestBuilder.of(tempDir.resolve("libFoo")) .createManifest("AndroidManifest.xml", "com.google.foo", ""); Path libBarManifest = ManifestBuilder.of(tempDir.resolve("libBar")) .createManifest("AndroidManifest.xml", "com.google.bar", ""); Path binarySymbols = createFile("R.txt", "int attr agility 0x7f010000", "int attr dexterity 0x7f010001", "int drawable heart 0x7f020000", "int id someTextView 0x7f080000", "int integer maxNotifications 0x7f090000", "int string alphabet 0x7f100000", "int string ok 0x7f100001"); Path libFooSymbols = createFile("libFoo.R.txt", "int attr agility 0x1", "int id someTextView 0x1", "int string ok 0x1"); Path libBarSymbols = createFile("libBar.R.txt", "int attr dexterity 0x1", "int drawable heart 0x1"); Path jarPath = tempDir.resolve("app_resources.jar"); RClassGeneratorAction.main( ImmutableList.<String>of( "--primaryRTxt", binarySymbols.toString(), "--primaryManifest", binaryManifest.toString(), "--libraries", libFooSymbols + File.pathSeparator + libFooManifest + "," + libBarSymbols + File.pathSeparator + libBarManifest, "--classJarOutput", jarPath.toString()) .toArray(new String[0])); assertThat(Files.exists(jarPath)).isTrue(); assertThat(Files.getLastModifiedTime(jarPath)).isEqualTo(FileTime.fromMillis(0)); try (ZipFile zip = new ZipFile(jarPath.toFile())) { List<? extends ZipEntry> zipEntries = Collections.list(zip.entries()); Iterable<String> entries = getZipFilenames(zipEntries); assertThat(entries) .containsExactly( Paths.get("com/google/foo/R$attr.class").toString(), Paths.get("com/google/foo/R$id.class").toString(), Paths.get("com/google/foo/R$string.class").toString(), Paths.get("com/google/foo/R.class").toString(), Paths.get("com/google/bar/R$attr.class").toString(), Paths.get("com/google/bar/R$drawable.class").toString(), Paths.get("com/google/bar/R.class").toString(), Paths.get("com/google/app/R$attr.class").toString(), Paths.get("com/google/app/R$drawable.class").toString(), Paths.get("com/google/app/R$id.class").toString(), Paths.get("com/google/app/R$integer.class").toString(), Paths.get("com/google/app/R$string.class").toString(), Paths.get("com/google/app/R.class").toString(), Paths.get("META-INF/MANIFEST.MF").toString()); } } @Test public void withNoBinaryAndLibraries() throws Exception { Path libFooManifest = ManifestBuilder.of(tempDir.resolve("libFoo")) .createManifest("AndroidManifest.xml", "com.google.foo", ""); Path libBarManifest = ManifestBuilder.of(tempDir.resolve("libBar")) .createManifest("AndroidManifest.xml", "com.google.bar", ""); Path libFooSymbols = createFile( "libFoo.R.txt", "int attr agility 0x1", "int id someTextView 0x1", "int string ok 0x1"); Path libBarSymbols = createFile("libBar.R.txt", "int attr dexterity 0x1", "int drawable heart 0x1"); Path jarPath = tempDir.resolve("app_resources.jar"); RClassGeneratorAction.main( ImmutableList.<String>of( "--libraries", libFooSymbols + File.pathSeparator + libFooManifest + "," + libBarSymbols + File.pathSeparator + libBarManifest, "--classJarOutput", jarPath.toString()) .toArray(new String[0])); assertThat(Files.exists(jarPath)).isTrue(); assertThat(Files.getLastModifiedTime(jarPath)).isEqualTo(FileTime.fromMillis(0)); try (ZipFile zip = new ZipFile(jarPath.toFile())) { List<? extends ZipEntry> zipEntries = Collections.list(zip.entries()); Iterable<String> entries = getZipFilenames(zipEntries); assertThat(entries) .containsExactly( Paths.get("com/google/foo/R$attr.class").toString(), Paths.get("com/google/foo/R$id.class").toString(), Paths.get("com/google/foo/R$string.class").toString(), Paths.get("com/google/foo/R.class").toString(), Paths.get("com/google/bar/R$attr.class").toString(), Paths.get("com/google/bar/R$drawable.class").toString(), Paths.get("com/google/bar/R.class").toString(), Paths.get("META-INF/MANIFEST.MF").toString()); } } @Test public void withBinaryNoLibraries() throws Exception { Path binaryManifest = ManifestBuilder.of(tempDir.resolve("binary")) .createManifest("AndroidManifest.xml", "com.google.app", "<application android:name=\"com.google.app\">", "<activity android:name=\"com.google.bar.activityFoo\" />", "</application>"); Path binarySymbols = createFile("R.txt", "int attr agility 0x7f010000", "int attr dexterity 0x7f010001", "int drawable heart 0x7f020000", "int id someTextView 0x7f080000", "int integer maxNotifications 0x7f090000", "int string alphabet 0x7f100000", "int string ok 0x7f100001"); Path jarPath = tempDir.resolve("app_resources.jar"); RClassGeneratorAction.main(ImmutableList.<String>of( "--primaryRTxt", binarySymbols.toString(), "--primaryManifest", binaryManifest.toString(), "--classJarOutput", jarPath.toString() ).toArray(new String[0])); assertThat(Files.exists(jarPath)).isTrue(); assertThat(Files.getLastModifiedTime(jarPath)).isEqualTo(FileTime.fromMillis(0)); try (ZipFile zip = new ZipFile(jarPath.toFile())) { List<? extends ZipEntry> zipEntries = Collections.list(zip.entries()); Iterable<String> entries = getZipFilenames(zipEntries); assertThat(entries) .containsExactly( Paths.get("com/google/app/R$attr.class").toString(), Paths.get("com/google/app/R$drawable.class").toString(), Paths.get("com/google/app/R$id.class").toString(), Paths.get("com/google/app/R$integer.class").toString(), Paths.get("com/google/app/R$string.class").toString(), Paths.get("com/google/app/R.class").toString(), Paths.get("META-INF/MANIFEST.MF").toString()); } } @Test public void noBinary() throws Exception { Path jarPath = tempDir.resolve("app_resources.jar"); RClassGeneratorAction.main(ImmutableList.<String>of( "--classJarOutput", jarPath.toString() ).toArray(new String[0])); assertThat(Files.exists(jarPath)).isTrue(); assertThat(Files.getLastModifiedTime(jarPath)).isEqualTo(FileTime.fromMillis(0)); try (ZipFile zip = new ZipFile(jarPath.toFile())) { List<? extends ZipEntry> zipEntries = Collections.list(zip.entries()); Iterable<String> entries = getZipFilenames(zipEntries); assertThat(entries).containsExactly(Paths.get("META-INF/MANIFEST.MF").toString()); } } @Test public void customPackageForR() throws Exception { Path binaryManifest = ManifestBuilder.of(tempDir.resolve("binary")) .createManifest("AndroidManifest.xml", "com.google.app", "<application android:name=\"com.google.app\">", "<activity android:name=\"com.google.foo.activityFoo\" />", "</application>"); Path libFooManifest = ManifestBuilder.of(tempDir.resolve("libFoo")) .createManifest("AndroidManifest.xml", "com.google.foo", ""); Path binarySymbols = createFile("R.txt", "int attr agility 0x7f010000", "int integer maxNotifications 0x7f090000", "int string ok 0x7f100001"); Path libFooSymbols = createFile("libFoo.R.txt", "int string ok 0x1"); Path jarPath = tempDir.resolve("app_resources.jar"); RClassGeneratorAction.main( ImmutableList.<String>of( "--primaryRTxt", binarySymbols.toString(), "--primaryManifest", binaryManifest.toString(), "--packageForR", "com.custom.er", "--libraries", libFooSymbols + File.pathSeparator + libFooManifest, "--classJarOutput", jarPath.toString()) .toArray(new String[0])); assertThat(Files.exists(jarPath)).isTrue(); assertThat(Files.getLastModifiedTime(jarPath)).isEqualTo(FileTime.fromMillis(0)); try (ZipFile zip = new ZipFile(jarPath.toFile())) { List<? extends ZipEntry> zipEntries = Collections.list(zip.entries()); Iterable<String> entries = getZipFilenames(zipEntries); assertThat(entries) .containsExactly( Paths.get("com/google/foo/R$string.class").toString(), Paths.get("com/google/foo/R.class").toString(), Paths.get("com/custom/er/R$attr.class").toString(), Paths.get("com/custom/er/R$integer.class").toString(), Paths.get("com/custom/er/R$string.class").toString(), Paths.get("com/custom/er/R.class").toString(), Paths.get("META-INF/MANIFEST.MF").toString()); } } @Test public void noSymbolsNoRClass() throws Exception { Path binaryManifest = ManifestBuilder.of(tempDir.resolve("binary")) .createManifest("AndroidManifest.xml", "com.google.app", "<application android:name=\"com.google.app\">", "<activity android:name=\"com.google.foo.activityFoo\" />", "</application>"); Path binarySymbols = createFile("R.txt", ""); Path jarPath = tempDir.resolve("app_resources.jar"); RClassGeneratorAction.main( ImmutableList.<String>of( "--primaryRTxt", binarySymbols.toString(), "--primaryManifest", binaryManifest.toString(), "--classJarOutput", jarPath.toString()) .toArray(new String[0])); assertThat(Files.exists(jarPath)).isTrue(); assertThat(Files.getLastModifiedTime(jarPath)).isEqualTo(FileTime.fromMillis(0)); try (ZipFile zip = new ZipFile(jarPath.toFile())) { List<? extends ZipEntry> zipEntries = Collections.list(zip.entries()); Iterable<String> entries = getZipFilenames(zipEntries); assertThat(entries) .containsExactly( Paths.get("META-INF/MANIFEST.MF").toString()); } } private Path createFile(String name, String... contents) throws IOException { Path path = tempDir.resolve(name); Files.createDirectories(path.getParent()); Files.newOutputStream(path).write( Joiner.on("\n").join(contents).getBytes(StandardCharsets.UTF_8)); return path; } private Iterable<String> getZipFilenames(Iterable<? extends ZipEntry> entries) { return Iterables.transform(entries, new Function<ZipEntry, String>() { @Override public String apply(ZipEntry input) { return input.getName(); } }); } }