blob: 22ce6339b3641ddd30b0a141273269061e0c6bff [file] [log] [blame]
// Copyright 2017 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 static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimap;
import com.google.devtools.build.android.ZipFilterAction.HashMismatchCheckMode;
import com.google.devtools.build.singlejar.ZipEntryFilter.CustomMergeStrategy;
import com.google.devtools.build.singlejar.ZipEntryFilter.StrategyCallback;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@link ZipFilterAction}. */
@RunWith(JUnit4.class)
public class ZipFilterActionTest {
private static final class Entry {
private final String name;
private final String contents;
public Entry(String name, String contents) {
this.name = name;
this.contents = contents;
}
public String getName() {
return name;
}
public String getContents() {
return contents;
}
}
private enum FilterOperation {
SKIP,
RENAME,
CUSTOM_MERGE,
COPY
}
private static final class TestingStrategyCallback implements StrategyCallback {
private FilterOperation operation;
public void assertOp(FilterOperation operation) {
assertThat(this.operation).isEqualTo(operation);
}
@Override
public void skip() throws IOException {
operation = FilterOperation.SKIP;
}
@Override
public void rename(String filename, Date date) throws IOException {
operation = FilterOperation.RENAME;
}
@Override
public void customMerge(Date date, CustomMergeStrategy strategy) throws IOException {
operation = FilterOperation.CUSTOM_MERGE;
}
@Override
public void copy(Date date) throws IOException {
operation = FilterOperation.COPY;
}
}
@Rule public ExpectedException thrown = ExpectedException.none();
@Rule public TemporaryFolder tmp = new TemporaryFolder();
private int fileCount;
private TestingStrategyCallback callback;
private Path createZip(String... filenames) throws IOException {
Entry[] entries = new Entry[filenames.length];
int i = 0;
for (String filename : filenames) {
entries[i++] = new Entry(filename, "" + fileCount++);
}
return createZip(entries);
}
private Path createZip(Entry... entries) throws IOException {
File zip = tmp.newFile();
try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(zip))) {
for (Entry entry : entries) {
ZipEntry e = new ZipEntry(entry.getName());
zout.putNextEntry(e);
zout.write(entry.getContents().getBytes(UTF_8));
zout.closeEntry();
}
}
return zip.toPath();
}
private List<String> outputEntriesWithArgs(ImmutableList<String> args, File output)
throws IOException {
ZipFilterAction.run(args.toArray(new String[0]));
List<String> filteredEntries = new ArrayList<>();
try (ZipFile zip = new ZipFile(output)) {
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
filteredEntries.add(entries.nextElement().getName());
}
}
return filteredEntries;
}
@Before public void setup() {
callback = new TestingStrategyCallback();
}
@Test public void testCreateFilter() throws IOException {
ImmutableSet<Path> filters = ImmutableSet.of(
createZip("foo.class", "bar.java"),
createZip("foo.java", "bar.java", "baz.class"));
ImmutableSet<String> types = ImmutableSet.of(".class");
Multimap<String, Long> filterFiles = ZipFilterAction.getEntriesToOmit(filters, types);
assertThat(filterFiles.keySet()).containsExactly("foo.class", "baz.class");
assertThat(filterFiles).valuesForKey("foo.class").hasSize(1);
assertThat(filterFiles).valuesForKey("baz.class").hasSize(1);
}
@Test public void testCreateFilter_NoZips() throws IOException {
ImmutableSet<Path> filters = ImmutableSet.of();
ImmutableSet<String> types = ImmutableSet.of(".class");
Multimap<String, Long> filterFiles = ZipFilterAction.getEntriesToOmit(filters, types);
assertThat(filterFiles).isEmpty();
}
@Test public void testCreateFilter_NoTypes() throws IOException {
ImmutableSet<Path> filters = ImmutableSet.of(
createZip("foo.class", "bar.java"),
createZip("foo.java", "bar.java", "baz.class"));
ImmutableSet<String> types = ImmutableSet.of();
Multimap<String, Long> filterFiles = ZipFilterAction.getEntriesToOmit(filters, types);
assertThat(filterFiles.keySet())
.containsExactly("foo.class", "bar.java", "foo.java", "baz.class");
}
@Test public void testCreateFilter_MultipleTypes() throws IOException {
ImmutableSet<Path> filters = ImmutableSet.of(
createZip("foo.class", "bar.java"),
createZip("foo.java", "bar.java", "baz.class"));
ImmutableSet<String> types = ImmutableSet.of(".class", "bar.java");
Multimap<String, Long> filterFiles = ZipFilterAction.getEntriesToOmit(filters, types);
assertThat(filterFiles.keySet()).containsExactly("foo.class", "baz.class", "bar.java");
assertThat(filterFiles).valuesForKey("foo.class").hasSize(1);
assertThat(filterFiles).valuesForKey("bar.java").hasSize(2);
}
@Test public void testZipEntryFilter() throws Exception {
ZipFilterEntryFilter filter =
new ZipFilterEntryFilter(
".*R.class.*",
ImmutableSetMultimap.of("foo.class", 1L, "baz.class", 2L),
ImmutableMap.of("foo.class", 1L, "bar.class", 2L, "baz.class", 3L, "res/R.class", 4L),
HashMismatchCheckMode.WARN);
filter.accept("foo.class", callback);
callback.assertOp(FilterOperation.SKIP);
filter.accept("bar.class", callback);
callback.assertOp(FilterOperation.COPY);
filter.accept("baz.class", callback);
callback.assertOp(FilterOperation.COPY);
filter.accept("res/R.class", callback);
callback.assertOp(FilterOperation.SKIP);
}
@Test public void testZipEntryFilter_ErrorOnMismatch() throws Exception {
ZipFilterEntryFilter filter =
new ZipFilterEntryFilter(
".*R.class.*",
ImmutableSetMultimap.of("foo.class", 1L, "baz.class", 2L),
ImmutableMap.of("foo.class", 1L, "bar.class", 2L, "baz.class", 3L, "res/R.class", 4L),
HashMismatchCheckMode.ERROR);
filter.accept("foo.class", callback);
callback.assertOp(FilterOperation.SKIP);
filter.accept("bar.class", callback);
callback.assertOp(FilterOperation.COPY);
filter.accept("res/R.class", callback);
callback.assertOp(FilterOperation.SKIP);
filter.accept("baz.class", callback);
assertThat(filter.sawErrors()).isTrue();
}
@Test public void testFlags() throws Exception {
File input = tmp.newFile("input");
File output = tmp.newFile("output");
output.delete();
File filter1 = tmp.newFile("filter1");
File filter2 = tmp.newFile("filter2");
ImmutableList<String> args =
ImmutableList.of(
"--inputZip", input.getPath(),
"--outputZip", output.getPath(),
"--filterZips",
Joiner.on(",").join(filter1.getPath(), filter2.getPath(), filter1.getPath()),
"--filterTypes", Joiner.on(",").join(".class", ".class", ".java"),
"--explicitFilters", Joiner.on(",").join("R\\.class", "R\\$.*\\.class"),
"--outputMode", "DONT_CARE",
"--checkHashMismatch", "IGNORE");
thrown.expect(ZipException.class);
thrown.expectMessage("Zip file 'filter1' is malformed");
ZipFilterAction.run(args.toArray(new String[0]));
}
@Test public void testFullIntegration() throws IOException {
Path input = createZip(new Entry("foo.java", "foo"), new Entry("bar.class", "bar"),
new Entry("baz.class", "baz"), new Entry("1.class", "1"), new Entry("2.class", "2"),
new Entry("R.class", "r"), new Entry("Read.class", "read"));
File output = tmp.newFile();
output.delete();
Path filter1 = createZip(new Entry("1.class", "1"), new Entry("b.class", "b"));
Path filter2 = createZip(new Entry("1.class", "2"), new Entry("2.class", "1"),
new Entry("foo.java", "foo"), new Entry("bar.class", "bar"));
ImmutableList<String> args = ImmutableList.of(
"--inputZip", input.toFile().getPath(),
"--outputZip", output.getPath(),
"--filterZips", Joiner.on(",").join(filter1.toFile().getPath(), filter2.toFile().getPath(),
filter1.toFile().getPath()),
"--filterTypes", ".class",
"--explicitFilters", Joiner.on(",").join("R\\.class", "R\\$.*\\.class"),
"--outputMode", "DONT_CARE");
assertThat(outputEntriesWithArgs(args, output))
.containsExactly("foo.java", "baz.class", "2.class", "Read.class");
}
@Test public void testFullIntegrationErrorsOnHash() throws IOException {
Path input = createZip("foo.java", "bar.class", "baz.class");
File output = tmp.newFile();
output.delete();
Path filter = createZip("foo.java", "bar.class");
ImmutableList<String> args =
ImmutableList.of(
"--inputZip",
input.toFile().getPath(),
"--outputZip",
output.getPath(),
"--filterZips",
filter.toFile().getPath(),
"--filterTypes",
".class",
"--checkHashMismatch",
"ERROR",
"--outputMode",
"DONT_CARE");
int exitCode = ZipFilterAction.run(args.toArray(new String[0]));
assertThat(exitCode).isEqualTo(1);
}
@Test
public void testSkipHashMismatchCheck() throws IOException {
Path input =
createZip(
new Entry("foo.java", "foo"),
new Entry("bar.class", "bar1"),
new Entry("baz.class", "baz"));
File output = tmp.newFile();
output.delete();
Path filter = createZip(new Entry("foo.java", "foo"), new Entry("bar.class", "bar2"));
ImmutableList<String> args =
ImmutableList.of(
"--inputZip",
input.toFile().getPath(),
"--outputZip",
output.getPath(),
"--filterZips",
filter.toFile().getPath(),
"--filterTypes",
".class",
"--checkHashMismatch",
"IGNORE",
"--outputMode",
"DONT_CARE");
assertThat(outputEntriesWithArgs(args, output)).containsExactly("foo.java", "baz.class");
}
@Test public void testFullIntegrationErrorsOnHash_WithExplicitOverride()
throws IOException {
Path input = createZip("foo.java", "bar.class", "baz.class");
File output = tmp.newFile();
output.delete();
Path filter = createZip("foo.java", "bar.class");
ImmutableList<String> args = ImmutableList.of(
"--inputZip", input.toFile().getPath(),
"--outputZip", output.getPath(),
"--filterZips", filter.toFile().getPath(),
"--filterTypes", ".class",
"--explicitFilters", "bar\\.class",
"--outputMode", "DONT_CARE",
"--errorOnHashMismatch");
assertThat(outputEntriesWithArgs(args, output)).containsExactly("foo.java", "baz.class");
}
}