| // Copyright 2015 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.ziputils; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.Truth.assertWithMessage; |
| import static org.junit.Assert.assertThrows; |
| import static org.junit.Assert.fail; |
| |
| import com.google.common.base.Predicates; |
| import com.google.common.collect.ImmutableSet; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.Arrays; |
| import java.util.Date; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Unit tests for {@link SplitZip}. */ |
| @RunWith(JUnit4.class) |
| public class SplitZipTest { |
| private FakeFileSystem fileSystem; |
| |
| @Before |
| public void setUp() { |
| fileSystem = new FakeFileSystem(); |
| } |
| |
| @Test |
| public void test() { |
| SplitZip instance = new SplitZip(); |
| assertThat(instance.getMainClassListFile()).isNull(); |
| assertThat(instance.isVerbose()).isFalse(); |
| assertThat(instance.getEntryDate()).isNull(); |
| assertThat(instance.getResourceFile()).isNull(); |
| } |
| |
| @Test |
| public void testSetOutput_null() { |
| SplitZip instance = new SplitZip(); |
| Exception ex = assertThrows(Exception.class, () -> instance.addOutput((String) null)); |
| assertWithMessage("NullPointerException expected") |
| .that(ex instanceof NullPointerException) |
| .isTrue(); |
| } |
| |
| @Test |
| public void testSetOutput() throws IOException { |
| SplitZip instance = new SplitZip(); |
| SplitZip result = instance |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), |
| "out/shard1.jar")) |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard2.jar", false), |
| "out/shard2.jar")); |
| assertThat(result).isSameInstanceAs(instance); |
| } |
| |
| @Test |
| public void testSetResourceFile() { |
| SplitZip instance = new SplitZip(); |
| String res = "res"; |
| SplitZip result = instance.setResourceFile(res); |
| assertThat(result).isSameInstanceAs(instance); |
| } |
| |
| @Test |
| public void testGetResourceFile() { |
| SplitZip instance = new SplitZip(); |
| String res = "res"; |
| assertThat(instance.setResourceFile(res).getResourceFile()).isEqualTo(res); |
| assertThat(instance.setResourceFile((String) null).getResourceFile()).isNull(); |
| } |
| |
| @Test |
| public void testSetMainClassListFile() { |
| SplitZip instance = new SplitZip(); |
| SplitZip result = instance.setMainClassListFile((String) null); |
| assertThat(result).isSameInstanceAs(instance); |
| result = instance.setMainClassListFile("no format checks"); |
| assertThat(result).isSameInstanceAs(instance); |
| } |
| |
| @Test |
| public void testGetMainClassListFile() { |
| SplitZip instance = new SplitZip(); |
| String file = "list.txt"; |
| instance.setMainClassListFile(file); |
| String result = instance.getMainClassListFile(); |
| assertThat(result).isEqualTo(file); |
| } |
| |
| // Instance date test. Implementation has little constraints today. |
| // This should be improved. |
| @Test |
| public void testSetEntryDate() { |
| SplitZip instance = new SplitZip(); |
| SplitZip result = instance.setEntryDate(null); |
| assertThat(result).isSameInstanceAs(instance); |
| } |
| |
| @Test |
| public void testGetEntryDate() { |
| SplitZip instance = new SplitZip(); |
| Date now = new Date(); |
| instance.setEntryDate(now); |
| Date result = instance.getEntryDate(); |
| assertThat(now).isSameInstanceAs(result); |
| instance.setEntryDate(null); |
| assertThat(instance.getEntryDate()).isNull(); |
| } |
| |
| @Test |
| public void testUseDefaultEntryDate() { |
| SplitZip instance = new SplitZip(); |
| SplitZip result = instance.useDefaultEntryDate(); |
| assertThat(result).isSameInstanceAs(instance); |
| Date date = instance.getEntryDate(); |
| assertThat(date).isEqualTo(DosTime.DOS_EPOCHISH); |
| } |
| |
| @Test |
| public void testAddInput() { |
| SplitZip instance = new SplitZip(); |
| String noexists = "noexists.zip"; |
| IOException ex = |
| assertThrows( |
| "should not be able to add non existing file: " + noexists, |
| IOException.class, |
| () -> instance.addInput(noexists)); |
| assertWithMessage("FileNotFoundException expected") |
| .that(ex instanceof FileNotFoundException) |
| .isTrue(); |
| } |
| |
| @Test |
| public void testAddInputs() { |
| SplitZip instance = new SplitZip(); |
| String noexists = "noexists.zip"; |
| IOException ex = |
| assertThrows( |
| "should not be able to add non existing file: " + noexists, |
| IOException.class, |
| () -> instance.addInputs(Arrays.asList(noexists))); |
| assertWithMessage("FileNotFoundException expected") |
| .that(ex instanceof FileNotFoundException) |
| .isTrue(); |
| } |
| |
| @Test |
| public void testCopyOneDir() { |
| try { |
| new ZipFileBuilder() |
| .add("pkg/test.txt", "hello world") |
| .create("input.zip"); |
| byte[] inputBytes = fileSystem.toByteArray("input.zip"); |
| |
| new SplitZip() |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), |
| "out/shard1.jar")) |
| .setVerbose(true) |
| .addInput(new ZipIn(fileSystem.getInputChannel("input.zip"), "input.zip")) |
| .run() |
| .close(); |
| |
| byte[] outputBytes = fileSystem.toByteArray("out/shard1.jar"); |
| assertThat(inputBytes).isEqualTo(outputBytes); |
| } catch (IOException e) { |
| fail("Exception: " + e); |
| } |
| } |
| |
| @Test |
| public void testSetDate() { |
| try { |
| Date now = new Date(); |
| new ZipFileBuilder() |
| .add(new ZipFileBuilder.FileInfo("pkg/test.txt", new DosTime(now).time, "hello world")) |
| .create("input.zip"); |
| |
| new ZipFileBuilder() |
| .add(new ZipFileBuilder.FileInfo("pkg/test.txt", DosTime.EPOCHISH.time, "hello world")) |
| .create("expect.zip"); |
| byte[] expectBytes = fileSystem.toByteArray("expect.zip"); |
| |
| new SplitZip() |
| .addOutput( |
| new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), "out/shard1.jar")) |
| .setVerbose(true) |
| .setEntryDate(DosTime.DOS_EPOCHISH) |
| .addInput(new ZipIn(fileSystem.getInputChannel("input.zip"), "input.zip")) |
| .run() |
| .close(); |
| |
| byte[] outputBytes = fileSystem.toByteArray("out/shard1.jar"); |
| assertThat(expectBytes).isEqualTo(outputBytes); |
| } catch (IOException e) { |
| fail("Exception: " + e); |
| } |
| } |
| |
| @Test |
| public void testDuplicatedInput() { |
| try { |
| new ZipFileBuilder() |
| .add("pkg/test.txt", "hello world") |
| .create("input1.zip"); |
| |
| new ZipFileBuilder() |
| .add("pkg/test.txt", "Goodbye world") |
| .create("input2.zip"); |
| |
| new SplitZip() |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), |
| "out/shard1.jar")) |
| .setVerbose(true) |
| .addInput(new ZipIn(fileSystem.getInputChannel("input1.zip"), "input1.zip")) |
| .addInput(new ZipIn(fileSystem.getInputChannel("input2.zip"), "input2.zip")) |
| .run() |
| .close(); |
| |
| new ZipFileBuilder() |
| .add("pkg/test.txt", "hello world") |
| .create("expect.zip"); |
| byte[] expectBytes = fileSystem.toByteArray("expect.zip"); |
| byte[] outputBytes = fileSystem.toByteArray("out/shard1.jar"); |
| assertThat(expectBytes).isEqualTo(outputBytes); |
| } catch (IOException e) { |
| fail("Exception: " + e); |
| } |
| } |
| |
| @Test |
| public void testCopyThreeDir() { |
| try { |
| new ZipFileBuilder() |
| .add("pkg/hello.txt", "hello world") |
| .add("pkg/greet.txt", "how are you") |
| .add("pkg/bye.txt", "bye bye") |
| .create("input.zip"); |
| byte[] inputBytes = fileSystem.toByteArray("input.zip"); |
| |
| new SplitZip() |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), |
| "out/shard1.jar")) |
| .setVerbose(true) |
| .addInput(new ZipIn(fileSystem.getInputChannel("input.zip"), "input.zip")) |
| .run() |
| .close(); |
| |
| byte[] outputBytes = fileSystem.toByteArray("out/shard1.jar"); |
| assertThat(inputBytes).isEqualTo(outputBytes); |
| } catch (IOException e) { |
| fail("Exception: " + e); |
| } |
| } |
| |
| @Test |
| public void testSplitOnPackageBoundary() throws IOException { |
| new ZipFileBuilder() |
| .add("pkg1/test1.class", "hello world") |
| .add("pkg2/test1.class", "hello world") |
| .add("pkg1/test2.class", "how are you") |
| .add("pkg2/test2.class", "how are you") |
| // no third file in pkg1 to test splitting early on package boundary |
| .add("pkg2/test3.class", "bye bye") |
| .create("input.jar"); |
| |
| new SplitZip() |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), |
| "out/shard1.jar")) |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard2.jar", false), |
| "out/shard2.jar")) |
| .setVerbose(true) |
| .addInput(new ZipIn(fileSystem.getInputChannel("input.jar"), "input.jar")) |
| .run() |
| .close(); |
| |
| new ZipFileBuilder() |
| .add("pkg1/test1.class", "hello world") |
| .add("pkg1/test2.class", "how are you") |
| .create("expected/shard1.jar"); |
| new ZipFileBuilder() |
| .add("pkg2/test1.class", "hello world") |
| .add("pkg2/test2.class", "how are you") |
| .add("pkg2/test3.class", "bye bye") |
| .create("expected/shard2.jar"); |
| |
| assertWithMessage("shard1") |
| .that(fileSystem.toByteArray("out/shard1.jar")) |
| .isEqualTo(fileSystem.toByteArray("expected/shard1.jar")); |
| |
| assertWithMessage("shard2") |
| .that(fileSystem.toByteArray("out/shard2.jar")) |
| .isEqualTo(fileSystem.toByteArray("expected/shard2.jar")); |
| } |
| |
| @Test |
| public void testSplitSinglePackageInTwo() throws IOException { |
| new ZipFileBuilder() |
| .add("a.class", "hello world") |
| .add("b.class", "how are you") |
| .add("c.class", "bye bye") |
| .add("d.class", "good night") |
| .create("input.jar"); |
| |
| new SplitZip() |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), |
| "out/shard1.jar")) |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard2.jar", false), |
| "out/shard2.jar")) |
| .setVerbose(true) |
| .addInput(new ZipIn(fileSystem.getInputChannel("input.jar"), "input.jar")) |
| .run() |
| .close(); |
| |
| new ZipFileBuilder() |
| .add("a.class", "hello world") |
| .add("b.class", "how are you") |
| .create("expected/shard1.jar"); |
| new ZipFileBuilder() |
| .add("c.class", "bye bye") |
| .add("d.class", "good night") |
| .create("expected/shard2.jar"); |
| |
| assertWithMessage("shard1") |
| .that(fileSystem.toByteArray("out/shard1.jar")) |
| .isEqualTo(fileSystem.toByteArray("expected/shard1.jar")); |
| |
| assertWithMessage("shard2") |
| .that(fileSystem.toByteArray("out/shard2.jar")) |
| .isEqualTo(fileSystem.toByteArray("expected/shard2.jar")); |
| } |
| |
| @Test |
| public void testSeparateResources() { |
| try { |
| new ZipFileBuilder() |
| .add("resources/oil.xml", "oil") |
| .add("pkg1/test1.class", "hello world") |
| .add("pkg2/test1.class", "hello world") |
| .add("pkg1/test2.class", "how are you") |
| .add("pkg2/test2.class", "how are you") |
| .add("pkg1/test3.class", "bye bye") |
| .add("pkg2/test3.class", "bye bye") |
| .create("input.jar"); |
| ZipIn input = new ZipIn(fileSystem.getInputChannel("input.jar"), "input.jar"); |
| |
| String resources = "out/resources.zip"; |
| ZipOut resourceOut = new ZipOut(fileSystem.getOutputChannel(resources, false), resources); |
| new SplitZip() |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), |
| "out/shard1.jar")) |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard2.jar", false), |
| "out/shard2.jar")) |
| .setResourceFile(resourceOut) |
| .setVerbose(true) |
| .addInput(input) |
| .run() |
| .close(); |
| |
| new ZipFileBuilder() |
| .add("pkg1/test1.class", "hello world") |
| .add("pkg1/test2.class", "how are you") |
| .add("pkg1/test3.class", "bye bye") |
| .create("expected/shard1.jar"); |
| new ZipFileBuilder() |
| .add("pkg2/test1.class", "hello world") |
| .add("pkg2/test2.class", "how are you") |
| .add("pkg2/test3.class", "bye bye") |
| .create("expected/shard2.jar"); |
| new ZipFileBuilder() |
| .add("resources/oil.xml", "oil") |
| .create("expected/resources.zip"); |
| |
| |
| assertThat(fileSystem.toByteArray("out/shard1.jar")) |
| .isEqualTo(fileSystem.toByteArray("expected/shard1.jar")); |
| |
| assertThat(fileSystem.toByteArray("out/shard2.jar")) |
| .isEqualTo(fileSystem.toByteArray("expected/shard2.jar")); |
| |
| assertThat(fileSystem.toByteArray("out/resources.zip")) |
| .isEqualTo(fileSystem.toByteArray("expected/resources.zip")); |
| |
| } catch (IOException e) { |
| e.printStackTrace(); |
| fail("Exception: " + e); |
| } |
| } |
| |
| @Test |
| public void testMainClassListFile() { |
| SplitZip instance = new SplitZip(); |
| String filename = "x/y/z/foo.txt"; |
| instance.setMainClassListFile(filename); |
| String out = instance.getMainClassListFile(); |
| assertThat(out).isEqualTo(filename); |
| |
| instance.setMainClassListFile((String) null); |
| assertThat(instance.getMainClassListFile()).isNull(); |
| |
| try { |
| new ZipFileBuilder() |
| .add("pkg1/test1.class", "hello world") |
| .add("pkg2/test1.class", "hello world") |
| .add("pkg1/test2.class", "how are you") |
| .add("pkg2/test2.class", "how are you") |
| .add("pkg1/test3.class", "bye bye") |
| .add("pkg2/test3.class", "bye bye") |
| .create("input.jar"); |
| |
| String classFileList = "pkg1/test1.class\npkg2/test2.class\n"; |
| fileSystem.addFile("main_dex_list.txt", classFileList); |
| |
| try (InputStream mainDex = fileSystem.getInputStream("main_dex_list.txt")) { |
| new SplitZip() |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), |
| "out/shard1.jar")) |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard2.jar", false), |
| "out/shard2.jar")) |
| .setMainClassListStreamForTesting(mainDex) |
| .addInput(new ZipIn(fileSystem.getInputChannel("input.jar"), "input.jar")) |
| .run() |
| .close(); |
| } |
| |
| new ZipFileBuilder() |
| .add("pkg1/test1.class", "hello world") |
| .add("pkg2/test2.class", "how are you") |
| .create("expected/shard1.jar"); |
| |
| // Sorting is used for split calculation, but classes assigned to the same shard are expected |
| // to be output in the order they appear in input. |
| new ZipFileBuilder() |
| .add("pkg2/test1.class", "hello world") |
| .add("pkg1/test2.class", "how are you") |
| .add("pkg1/test3.class", "bye bye") |
| .add("pkg2/test3.class", "bye bye") |
| .create("expected/shard2.jar"); |
| |
| assertThat(fileSystem.toByteArray("out/shard1.jar")) |
| .isEqualTo(fileSystem.toByteArray("expected/shard1.jar")); |
| |
| assertThat(fileSystem.toByteArray("out/shard2.jar")) |
| .isEqualTo(fileSystem.toByteArray("expected/shard2.jar")); |
| |
| } catch (IOException e) { |
| fail("Exception: " + e); |
| } |
| } |
| |
| @Test |
| public void testInputFilter() throws Exception { |
| new ZipFileBuilder() |
| .add("pkg/test.txt", "hello world") |
| .add("pkg/test2.txt", "how are you") |
| .add("pkg/test.class", "hello world") |
| .add("pkg/test2.class", "how are you") |
| .add("pkg/R$attr.class", "bye bye") |
| .create("input.zip"); |
| |
| new ZipFileBuilder() |
| .add("pkg/test.txt", "hello world") |
| .add("pkg/test2.class", "how are you") |
| .create("expected.zip"); |
| byte[] expectedBytes = fileSystem.toByteArray("expected.zip"); |
| |
| new SplitZip() |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), |
| "out/shard1.jar")) |
| .setVerbose(true) |
| .addInput(new ZipIn(fileSystem.getInputChannel("input.zip"), "input.zip")) |
| .setInputFilter( |
| Predicates.in(ImmutableSet.of("pkg/test.txt", "pkg/test2.class", "pkg2/test.class"))) |
| .run() |
| .close(); |
| |
| byte[] outputBytes = fileSystem.toByteArray("out/shard1.jar"); |
| assertThat(outputBytes).isEqualTo(expectedBytes); |
| } |
| |
| @Test |
| public void testInputFilter_splitDexedClasses() throws Exception { |
| new ZipFileBuilder() |
| .add("pkg/test.class.dex", "hello world") |
| .add("pkg/test2.class", "how are you") |
| .add("pkg/R$attr.class", "bye bye") |
| .create("input.zip"); |
| |
| new ZipFileBuilder() |
| .add("pkg/test.class.dex", "hello world") |
| .add("pkg/R$attr.class", "bye bye") |
| .create("expected.zip"); |
| byte[] expectedBytes = fileSystem.toByteArray("expected.zip"); |
| |
| new SplitZip() |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), |
| "out/shard1.jar")) |
| .setVerbose(true) |
| .addInput(new ZipIn(fileSystem.getInputChannel("input.zip"), "input.zip")) |
| .setInputFilter( |
| Predicates.in(ImmutableSet.of("pkg/test.class", "pkg/R$attr.class"))) |
| .setSplitDexedClasses(true) |
| .run() |
| .close(); |
| |
| byte[] outputBytes = fileSystem.toByteArray("out/shard1.jar"); |
| assertThat(outputBytes).isEqualTo(expectedBytes); |
| } |
| |
| @Test |
| public void testInputFilter_mainDexFilter() throws Exception { |
| new ZipFileBuilder() |
| .add("pkg1/test1.class", "hello world") |
| .add("pkg2/test1.class", "how are you") |
| .add("pkg1/test2.class", "hello world") |
| .add("pkg2/test2.class", "how are you") |
| .add("pkg1/test3.class", "bye bye") |
| .add("pkg2/test3.class", "bye bye") |
| .create("input.jar"); |
| |
| String classFileList = "pkg1/test1.class\npkg2/test2.class\n"; |
| fileSystem.addFile("main_dex_list.txt", classFileList); |
| |
| try (InputStream mainDex = fileSystem.getInputStream("main_dex_list.txt")) { |
| new SplitZip() |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), |
| "out/shard1.jar")) |
| .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard2.jar", false), |
| "out/shard2.jar")) |
| .setVerbose(true) |
| .setMainClassListStreamForTesting(mainDex) |
| .addInput(new ZipIn(fileSystem.getInputChannel("input.jar"), "input.jar")) |
| .setInputFilter( |
| Predicates.in( |
| ImmutableSet.of("pkg1/test1.class", "pkg2/test1.class", "pkg3/test1.class"))) |
| .setSplitDexedClasses(true) |
| .run() |
| .close(); |
| } |
| |
| // 1st shard contains only main dex list classes also in the filter |
| new ZipFileBuilder() |
| .add("pkg1/test1.class", "hello world") |
| .create("expected/shard1.jar"); |
| |
| new ZipFileBuilder() |
| .add("pkg2/test1.class", "how are you") |
| .create("expected/shard2.jar"); |
| |
| assertThat(fileSystem.toByteArray("out/shard1.jar")) |
| .isEqualTo(fileSystem.toByteArray("expected/shard1.jar")); |
| |
| assertThat(fileSystem.toByteArray("out/shard2.jar")) |
| .isEqualTo(fileSystem.toByteArray("expected/shard2.jar")); |
| } |
| |
| @Test |
| public void testVerbose() { |
| SplitZip instance = new SplitZip(); |
| instance.setVerbose(true); |
| assertThat(instance.isVerbose()).isTrue(); |
| instance.setVerbose(false); |
| assertThat(instance.isVerbose()).isFalse(); |
| } |
| } |