|  | // 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. | 
|  |  | 
|  | import static com.google.common.collect.ImmutableList.toImmutableList; | 
|  | import static com.google.common.truth.Truth.assertThat; | 
|  | import static java.nio.charset.StandardCharsets.UTF_8; | 
|  | import static org.junit.Assert.assertFalse; | 
|  | import static org.junit.Assert.fail; | 
|  |  | 
|  | import com.google.common.collect.ImmutableList; | 
|  | import com.google.common.collect.ImmutableMap; | 
|  | import com.google.common.io.ByteStreams; | 
|  | import org.objectweb.asm.Opcodes; | 
|  | import java.io.File; | 
|  | import java.io.IOException; | 
|  | import java.io.InputStream; | 
|  | import java.nio.file.Files; | 
|  | import java.nio.file.Paths; | 
|  | import java.time.LocalDateTime; | 
|  | import java.time.ZoneOffset; | 
|  | import java.util.Arrays; | 
|  | import java.util.Collections; | 
|  | import java.util.Enumeration; | 
|  | import java.util.HashMap; | 
|  | import java.util.Locale; | 
|  | import java.util.Map; | 
|  | import java.util.Set; | 
|  | import java.util.jar.Attributes; | 
|  | import java.util.jar.JarEntry; | 
|  | import java.util.jar.JarFile; | 
|  | import java.util.jar.Manifest; | 
|  | import java.util.zip.ZipEntry; | 
|  | import java.util.zip.ZipFile; | 
|  | import javax.annotation.processing.AbstractProcessor; | 
|  | import javax.annotation.processing.RoundEnvironment; | 
|  | import javax.lang.model.SourceVersion; | 
|  | import javax.lang.model.element.TypeElement; | 
|  | import javax.tools.Diagnostic; | 
|  | import javax.tools.DiagnosticCollector; | 
|  | import javax.tools.JavaCompiler; | 
|  | import javax.tools.JavaFileObject; | 
|  | import javax.tools.StandardJavaFileManager; | 
|  | import javax.tools.StandardLocation; | 
|  | import javax.tools.ToolProvider; | 
|  | import org.junit.Test; | 
|  | import org.junit.runner.RunWith; | 
|  | import org.junit.runners.JUnit4; | 
|  | import org.objectweb.asm.ClassReader; | 
|  | import org.objectweb.asm.ClassVisitor; | 
|  |  | 
|  | /** JUnit tests for ijar tool. */ | 
|  | @RunWith(JUnit4.class) | 
|  | public class IjarTests { | 
|  |  | 
|  | private static File getTmpDir() { | 
|  | String tmpdir = System.getenv("TEST_TMPDIR"); | 
|  | if (tmpdir == null) { | 
|  | // Fall back on the system temporary directory | 
|  | tmpdir = System.getProperty("java.io.tmpdir"); | 
|  | } | 
|  | if (tmpdir == null) { | 
|  | fail("TEST_TMPDIR environment variable is not set!"); | 
|  | } | 
|  | return new File(tmpdir); | 
|  | } | 
|  |  | 
|  | DiagnosticCollector<JavaFileObject> diagnostics; | 
|  |  | 
|  | private JavaCompiler.CompilationTask makeCompilationTask(String... files) throws IOException { | 
|  | JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); | 
|  | StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); | 
|  | fileManager.setLocation( | 
|  | StandardLocation.CLASS_PATH, | 
|  | Arrays.asList(new File("third_party/ijar/test/interface_ijar_testlib.jar"))); | 
|  | fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(getTmpDir())); | 
|  | diagnostics = new DiagnosticCollector<JavaFileObject>(); | 
|  | return compiler.getTask( | 
|  | null, | 
|  | fileManager, | 
|  | diagnostics, | 
|  | Arrays.asList("-Xlint:deprecation"), // used for deprecation tests | 
|  | null, | 
|  | fileManager.getJavaFileObjects(files)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Test that the ijar tool preserves private nested classes as they may be exposed through public | 
|  | * API. This test relies on an interface jar provided through the build rule | 
|  | * :interface_ijar_testlib and the Java source file PrivateNestedClass.java. | 
|  | */ | 
|  | @Test | 
|  | public void testPrivateNestedClass() throws IOException { | 
|  | if (!makeCompilationTask("third_party/ijar/test/PrivateNestedClass.java").call()) { | 
|  | fail(getFailedCompilationMessage()); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Test that the ijar tool preserves annotations, especially @Target meta-annotation. */ | 
|  | @Test | 
|  | public void testRestrictedAnnotations() throws IOException { | 
|  | assertFalse(makeCompilationTask("third_party/ijar/test/UseRestrictedAnnotation.java").call()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Test that the ijar tool preserves private nested classes as they may be exposed through public | 
|  | * API. This test relies on an interface jar provided through the build rule | 
|  | * :interface_ijar_testlib and the Java source file PrivateNestedClass.java. | 
|  | */ | 
|  | @Test | 
|  | public void testDeprecatedParts() throws IOException { | 
|  | if (!makeCompilationTask("third_party/ijar/test/UseDeprecatedParts.java").call()) { | 
|  | fail(getFailedCompilationMessage()); | 
|  | } | 
|  | int deprecatedWarningCount = 0; | 
|  | for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) { | 
|  | if ((diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING) | 
|  | && | 
|  | // Java 6: | 
|  | (diagnostic.getMessage(Locale.ENGLISH).startsWith("[deprecation]") | 
|  | || | 
|  | // Java 7: | 
|  | diagnostic.getMessage(Locale.ENGLISH).contains("has been deprecated"))) { | 
|  | deprecatedWarningCount++; | 
|  | } | 
|  | } | 
|  | assertThat(deprecatedWarningCount).isAtLeast(16); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Test that the ijar tool preserves EnclosingMethod attributes and doesn't prevent annotation | 
|  | * processors from accessing all the elements in a package. | 
|  | */ | 
|  | @Test | 
|  | public void testEnclosingMethod() throws IOException { | 
|  | JavaCompiler.CompilationTask task = | 
|  | makeCompilationTask("third_party/ijar/test/package-info.java"); | 
|  | task.setProcessors( | 
|  | Arrays.asList( | 
|  | new AbstractProcessor() { | 
|  |  | 
|  | @Override | 
|  | public SourceVersion getSupportedSourceVersion() { | 
|  | return SourceVersion.latestSupported(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Set<String> getSupportedAnnotationTypes() { | 
|  | return Collections.singleton("*"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean process( | 
|  | Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { | 
|  | roundEnv.getElementsAnnotatedWith(java.lang.Override.class); | 
|  | return false; | 
|  | } | 
|  | })); | 
|  | if (!task.call()) { | 
|  | fail(getFailedCompilationMessage()); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void testVerifyStripping() throws Exception { | 
|  | ZipFile zip = new ZipFile("third_party/ijar/test/interface_ijar_testlib.jar"); | 
|  | Enumeration<? extends ZipEntry> entries = zip.entries(); | 
|  | while (entries.hasMoreElements()) { | 
|  | ZipEntry entry = entries.nextElement(); | 
|  | ClassReader reader = new ClassReader(zip.getInputStream(entry)); | 
|  | StripVerifyingVisitor verifier = new StripVerifyingVisitor(); | 
|  |  | 
|  | reader.accept(verifier, 0); | 
|  |  | 
|  | if (verifier.errors.size() > 0) { | 
|  | StringBuilder builder = new StringBuilder(); | 
|  | builder.append("Verification of "); | 
|  | builder.append(entry.getName()); | 
|  | builder.append(" failed: "); | 
|  | for (String msg : verifier.errors) { | 
|  | builder.append(msg); | 
|  | builder.append("\t"); | 
|  | } | 
|  | fail(builder.toString()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private String getFailedCompilationMessage() { | 
|  | StringBuilder builder = new StringBuilder(); | 
|  | builder.append("Build failed unexpectedly"); | 
|  | for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) { | 
|  | builder.append( | 
|  | String.format( | 
|  | "\t%s line %d column %d: %s", | 
|  | diagnostic.getKind().toString(), | 
|  | diagnostic.getLineNumber(), | 
|  | diagnostic.getColumnNumber(), | 
|  | diagnostic.getMessage(Locale.ENGLISH))); | 
|  | } | 
|  | return builder.toString(); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void localAndAnonymous() throws Exception { | 
|  | Map<String, byte[]> lib = readJar("third_party/ijar/test/liblocal_and_anonymous_lib.jar"); | 
|  | Map<String, byte[]> ijar = readJar("third_party/ijar/test/local_and_anonymous-interface.jar"); | 
|  |  | 
|  | assertThat(lib.keySet()) | 
|  | .containsExactly( | 
|  | "LocalAndAnonymous$1.class", | 
|  | "LocalAndAnonymous$2.class", | 
|  | "LocalAndAnonymous$1LocalClass.class", | 
|  | "LocalAndAnonymous.class", | 
|  | "LocalAndAnonymous$NestedClass.class", | 
|  | "LocalAndAnonymous$InnerClass.class", | 
|  | "LocalAndAnonymous$PrivateInnerClass.class"); | 
|  | assertThat(ijar.keySet()) | 
|  | .containsExactly( | 
|  | "LocalAndAnonymous.class", | 
|  | "LocalAndAnonymous$NestedClass.class", | 
|  | "LocalAndAnonymous$InnerClass.class", | 
|  | "LocalAndAnonymous$PrivateInnerClass.class"); | 
|  |  | 
|  | assertThat(innerClasses(lib.get("LocalAndAnonymous.class"))) | 
|  | .isEqualTo( | 
|  | ImmutableMap.<String, String>builder() | 
|  | .put("LocalAndAnonymous$1", "null") | 
|  | .put("LocalAndAnonymous$2", "null") | 
|  | .put("LocalAndAnonymous$1LocalClass", "null") | 
|  | .put("LocalAndAnonymous$InnerClass", "LocalAndAnonymous") | 
|  | .put("LocalAndAnonymous$NestedClass", "LocalAndAnonymous") | 
|  | .put("LocalAndAnonymous$PrivateInnerClass", "LocalAndAnonymous") | 
|  | .buildOrThrow()); | 
|  | assertThat(innerClasses(ijar.get("LocalAndAnonymous.class"))) | 
|  | .containsExactly( | 
|  | "LocalAndAnonymous$InnerClass", "LocalAndAnonymous", | 
|  | "LocalAndAnonymous$NestedClass", "LocalAndAnonymous", | 
|  | "LocalAndAnonymous$PrivateInnerClass", "LocalAndAnonymous"); | 
|  | } | 
|  |  | 
|  | static Map<String, byte[]> readJar(String path) throws IOException { | 
|  | Map<String, byte[]> classes = new HashMap<>(); | 
|  | try (JarFile jf = new JarFile(path)) { | 
|  | Enumeration<JarEntry> entries = jf.entries(); | 
|  | while (entries.hasMoreElements()) { | 
|  | JarEntry je = entries.nextElement(); | 
|  | if (!je.getName().endsWith(".class") | 
|  | && !je.getName().endsWith(".kotlin_builtins") | 
|  | && !je.getName().endsWith(".kotlin_module") | 
|  | && !je.getName().endsWith(".tasty")) { | 
|  | continue; | 
|  | } | 
|  | classes.put(je.getName(), ByteStreams.toByteArray(jf.getInputStream(je))); | 
|  | } | 
|  | } | 
|  | return classes; | 
|  | } | 
|  |  | 
|  | static Map<String, String> innerClasses(byte[] bytes) { | 
|  | final Map<String, String> innerClasses = new HashMap<>(); | 
|  | new ClassReader(bytes) | 
|  | .accept( | 
|  | new ClassVisitor(Opcodes.ASM9) { | 
|  | @Override | 
|  | public void visitInnerClass( | 
|  | String name, String outerName, String innerName, int access) { | 
|  | innerClasses.put(name, String.valueOf(outerName)); | 
|  | } | 
|  | }, | 
|  | /* parsingOptions= */ 0); | 
|  | return innerClasses; | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void moduleInfo() throws Exception { | 
|  | Map<String, byte[]> lib = readJar("third_party/ijar/test/module_info-interface.jar"); | 
|  | assertThat(lib.keySet()) | 
|  | .containsExactly("java/lang/String.class", "module-info.class", "foo/module-info.class"); | 
|  | // ijar passes module-infos through unmodified, so it doesn't care that these ones are bogus | 
|  | assertThat(new String(lib.get("module-info.class"), UTF_8)).isEqualTo("hello"); | 
|  | assertThat(new String(lib.get("foo/module-info.class"), UTF_8)).isEqualTo("goodbye"); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void kotlinModule() throws Exception { | 
|  | Map<String, byte[]> lib = readJar("third_party/ijar/test/kotlin_module-interface.jar"); | 
|  | assertThat(lib.keySet()) | 
|  | .containsExactly( | 
|  | "java/lang/String.class", | 
|  | "kotlin/kotlin.kotlin_builtins", | 
|  | "META-INF/bar.kotlin_module"); | 
|  | // ijar passes kotlin modules and builtins through unmodified | 
|  | assertThat(new String(lib.get("META-INF/bar.kotlin_module"), UTF_8)).isEqualTo("hello"); | 
|  | assertThat(new String(lib.get("kotlin/kotlin.kotlin_builtins"), UTF_8)).isEqualTo("goodbye"); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void scalaTasty() throws Exception { | 
|  | Map<String, byte[]> lib = readJar("third_party/ijar/test/scala_tasty-interface.jar"); | 
|  | assertThat(lib.keySet()).containsExactly("java/lang/String.class", "Bar.tasty"); | 
|  | // ijar passes scala tasty files through unmodified | 
|  | assertThat(new String(lib.get("Bar.tasty"), UTF_8)).isEqualTo("hello"); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void testTargetLabel() throws Exception { | 
|  | try (JarFile jf = | 
|  | new JarFile("third_party/ijar/test/interface_ijar_testlib_with_target_label.jar")) { | 
|  | ImmutableList<String> entries = jf.stream().map(JarEntry::getName).collect(toImmutableList()); | 
|  | assertThat(entries.get(0)).isEqualTo("META-INF/"); | 
|  | assertThat(entries.get(1)).isEqualTo("META-INF/MANIFEST.MF"); | 
|  | Manifest manifest = jf.getManifest(); | 
|  | Attributes attributes = manifest.getMainAttributes(); | 
|  | assertThat(attributes.getValue("Target-Label")).isEqualTo("//foo:foo"); | 
|  | assertThat(attributes.getValue("Injecting-Rule-Kind")).isEqualTo("foo_library"); | 
|  | assertThat(jf.getEntry(JarFile.MANIFEST_NAME).getLastModifiedTime().toInstant()) | 
|  | .isEqualTo( | 
|  | LocalDateTime.of(2010, 1, 1, 0, 0, 0).atZone(ZoneOffset.systemDefault()).toInstant()); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void testEmptyWithTargetLabel() throws Exception { | 
|  | try (JarFile jf = new JarFile("third_party/ijar/test/empty_with_target_label.jar")) { | 
|  | Manifest manifest = jf.getManifest(); | 
|  | Attributes attributes = manifest.getMainAttributes(); | 
|  | assertThat(attributes.getValue("Target-Label")).isEqualTo("//empty"); | 
|  | assertThat(jf.getEntry(JarFile.MANIFEST_NAME).getLastModifiedTime().toInstant()) | 
|  | .isEqualTo( | 
|  | LocalDateTime.of(2010, 1, 1, 0, 0, 0).atZone(ZoneOffset.systemDefault()).toInstant()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Tests --nostrip_jar with a jar that already has a manifest, but no target label | 
|  | @Test | 
|  | public void testNoStripJarWithManifest() throws Exception { | 
|  | JarFile original = new JarFile("third_party/ijar/test/jar-with-manifest.jar"); | 
|  | JarFile stripped = new JarFile("third_party/ijar/test/jar-with-manifest-nostrip.jar"); | 
|  | try { | 
|  | ImmutableList<String> strippedEntries = | 
|  | stripped.stream().map(JarEntry::getName).collect(toImmutableList()); | 
|  | assertThat(strippedEntries.get(0)).isEqualTo("META-INF/"); | 
|  | assertThat(strippedEntries.get(1)).isEqualTo("META-INF/MANIFEST.MF"); | 
|  | Manifest manifest = stripped.getManifest(); | 
|  | Attributes attributes = manifest.getMainAttributes(); | 
|  | assertThat(attributes.getValue("Manifest-Version")).isEqualTo("1.0"); | 
|  | // Created-By was already in manifest, doesn't get overwritten | 
|  | assertThat(attributes.getValue("Created-By")).isEqualTo("test-code"); | 
|  | assertThat(attributes.getValue("Target-Label")).isEqualTo("//foo:foo"); | 
|  | assertNonManifestFilesBitIdentical(original, stripped); | 
|  | } finally { | 
|  | original.close(); | 
|  | stripped.close(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Tests --nostrip_jar with a jar that already has a manifest with a target label | 
|  | @Test | 
|  | public void testNoStripJarWithManifestAndTargetLabel() throws Exception { | 
|  | JarFile original = new JarFile("third_party/ijar/test/jar-with-manifest-and-target-label.jar"); | 
|  | JarFile stripped = | 
|  | new JarFile("third_party/ijar/test/jar-with-manifest-and-target-label-nostrip.jar"); | 
|  | try { | 
|  | ImmutableList<String> strippedEntries = | 
|  | stripped.stream().map(JarEntry::getName).collect(toImmutableList()); | 
|  | assertThat(strippedEntries.get(0)).isEqualTo("META-INF/"); | 
|  | assertThat(strippedEntries.get(1)).isEqualTo("META-INF/MANIFEST.MF"); | 
|  | Manifest manifest = stripped.getManifest(); | 
|  | Attributes attributes = manifest.getMainAttributes(); | 
|  | assertThat(attributes.getValue("Manifest-Version")).isEqualTo("1.0"); | 
|  | // Created-By was already in manifest, doesn't get overwritten | 
|  | assertThat(attributes.getValue("Created-By")).isEqualTo("test-code"); | 
|  | assertThat(attributes.getValue("Target-Label")).isEqualTo("//foo:foo"); | 
|  | assertNonManifestFilesBitIdentical(original, stripped); | 
|  | } finally { | 
|  | original.close(); | 
|  | stripped.close(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Tests --nostrip_jar with a jar that didn't already have a manifest | 
|  | @Test | 
|  | public void testNoStripJarWithoutManifest() throws Exception { | 
|  | JarFile original = new JarFile("third_party/ijar/test/jar-without-manifest.jar"); | 
|  | JarFile stripped = new JarFile("third_party/ijar/test/jar-without-manifest-nostrip.jar"); | 
|  | try { | 
|  | ImmutableList<String> strippedEntries = | 
|  | stripped.stream().map(JarEntry::getName).collect(toImmutableList()); | 
|  | assertThat(strippedEntries.get(0)).isEqualTo("META-INF/"); | 
|  | assertThat(strippedEntries.get(1)).isEqualTo("META-INF/MANIFEST.MF"); | 
|  | Manifest manifest = stripped.getManifest(); | 
|  | Attributes attributes = manifest.getMainAttributes(); | 
|  | assertThat(attributes.getValue("Manifest-Version")).isEqualTo("1.0"); | 
|  | assertThat(attributes.getValue("Created-By")).isEqualTo("bazel"); | 
|  | assertThat(attributes.getValue("Target-Label")).isEqualTo("//foo:foo"); | 
|  | assertNonManifestFilesBitIdentical(original, stripped); | 
|  | } finally { | 
|  | original.close(); | 
|  | stripped.close(); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void testPreserveManifestSections() throws Exception { | 
|  | try (JarFile original = | 
|  | new JarFile( | 
|  | "third_party/ijar/test/jar-with-target-label-and-manifest-sections-nostrip.jar"); | 
|  | JarFile stripped = | 
|  | new JarFile("third_party/ijar/test/jar-with-manifest-sections-nostrip.jar")) { | 
|  | ImmutableList<String> strippedEntries = | 
|  | stripped.stream().map(JarEntry::getName).collect(toImmutableList()); | 
|  |  | 
|  | assertThat(strippedEntries.get(0)).isEqualTo("META-INF/"); | 
|  | assertThat(strippedEntries.get(1)).isEqualTo("META-INF/MANIFEST.MF"); | 
|  | Manifest manifest = stripped.getManifest(); | 
|  | Attributes attributes = manifest.getMainAttributes(); | 
|  | assertThat(attributes.getValue("Target-Label")).isEqualTo("//foo:foo"); | 
|  | assertNonManifestFilesBitIdentical(original, stripped); | 
|  |  | 
|  | Attributes sectionAttributes1 = manifest.getAttributes("foo"); | 
|  | assertThat(sectionAttributes1.getValue("Foo")).isEqualTo("bar"); | 
|  |  | 
|  | Attributes sectionAttributes2 = manifest.getAttributes("baz"); | 
|  | assertThat(sectionAttributes2.getValue("Another")).isEqualTo("bar"); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void testPreserveManifestSectionsAndUpdateExistingTargetLabel() throws Exception { | 
|  | try (JarFile original = | 
|  | new JarFile( | 
|  | "third_party/ijar/test/jar-with-target-label-and-manifest-sections-nostrip.jar"); | 
|  | JarFile stripped = | 
|  | new JarFile( | 
|  | "third_party/ijar/test/jar-with-target-label-and-manifest-sections-nostrip.jar")) { | 
|  | ImmutableList<String> strippedEntries = | 
|  | stripped.stream().map(JarEntry::getName).collect(toImmutableList()); | 
|  |  | 
|  | assertThat(strippedEntries.get(0)).isEqualTo("META-INF/"); | 
|  | assertThat(strippedEntries.get(1)).isEqualTo("META-INF/MANIFEST.MF"); | 
|  | Manifest manifest = stripped.getManifest(); | 
|  | Attributes attributes = manifest.getMainAttributes(); | 
|  | assertThat(attributes.getValue("Target-Label")).isEqualTo("//foo:foo"); | 
|  | assertNonManifestFilesBitIdentical(original, stripped); | 
|  |  | 
|  | Attributes sectionAttributes1 = manifest.getAttributes("foo"); | 
|  | assertThat(sectionAttributes1.getValue("Foo")).isEqualTo("bar"); | 
|  |  | 
|  | Attributes sectionAttributes2 = manifest.getAttributes("baz"); | 
|  | assertThat(sectionAttributes2.getValue("Another")).isEqualTo("bar"); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Tests idempotence of --nostrip | 
|  | @Test | 
|  | public void testNoStripIdempotence() throws Exception { | 
|  | byte[] original = | 
|  | Files.readAllBytes(Paths.get("third_party/ijar/test/jar-without-manifest-nostrip.jar")); | 
|  | byte[] stripped = | 
|  | Files.readAllBytes( | 
|  | Paths.get("third_party/ijar/test/jar-without-manifest-nostrip-idempotence.jar")); | 
|  | assertThat(original).isEqualTo(stripped); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void metaInfTtransitive() throws Exception { | 
|  | Map<String, byte[]> lib = readJar("third_party/ijar/test/meta_inf_transitive-interface.jar"); | 
|  | assertThat(lib.keySet()).containsExactly("java/lang/String.class"); | 
|  | } | 
|  |  | 
|  | private static void assertNonManifestFilesBitIdentical(JarFile original, JarFile stripped) | 
|  | throws IOException { | 
|  | // Make sure that all other files came across bitwise equal | 
|  | for (String classEntry : | 
|  | original.stream() | 
|  | .map(JarEntry::getName) | 
|  | .filter(name -> !name.equals("META-INF/MANIFEST.MF")) | 
|  | .collect(toImmutableList())) { | 
|  | ZipEntry originalEntry = original.getEntry(classEntry); | 
|  | ZipEntry strippedEntry = stripped.getEntry(classEntry); | 
|  | InputStream originalStream = original.getInputStream(originalEntry); | 
|  | InputStream strippedStream = stripped.getInputStream(strippedEntry); | 
|  | assertThat(ByteStreams.toByteArray(strippedStream)) | 
|  | .isEqualTo(ByteStreams.toByteArray(originalStream)); | 
|  | } | 
|  | } | 
|  | } |