| // 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 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(); | |
| } | |
| }); | |
| } | |
| } |