33import org .junit .jupiter .api .Test ;
44import org .junit .jupiter .params .ParameterizedTest ;
55import org .junit .jupiter .params .provider .ValueSource ;
6- import software .coley .llzip .part .ZipPart ;
6+ import software .coley .llzip .format .compression .ZipCompressions ;
7+ import software .coley .llzip .format .model .LocalFileHeader ;
8+ import software .coley .llzip .format .model .ZipArchive ;
9+ import software .coley .llzip .format .model .ZipPart ;
10+ import software .coley .llzip .format .read .JvmZipReaderStrategy ;
11+ import software .coley .llzip .util .ByteDataUtil ;
712
813import java .io .IOException ;
14+ import java .nio .file .Path ;
915import java .nio .file .Paths ;
1016
1117import static org .junit .jupiter .api .Assertions .*;
1925public class PartParseTests {
2026 @ ParameterizedTest
2127 @ ValueSource (strings = {
22- "src/test/resources/code-windows .zip" ,
23- "src/test/resources/code-7z .zip" ,
28+ "src/test/resources/sample- code-7z .zip" , // ZIP made from 7z
29+ "src/test/resources/sample- code-windows .zip" , // ZIP made from windows built in 'send to zip'
2430 })
2531 public void testStandardCodeZip (String path ) {
2632 try {
2733 ZipArchive zip = ZipIO .readStandard (Paths .get (path ));
2834 assertNotNull (zip );
35+
2936 // Each code zip contains these files
3037 assertTrue (hasFile (zip , "ClassFile.java" ));
3138 assertTrue (hasFile (zip , "ClassMember.java" ));
@@ -38,53 +45,100 @@ public void testStandardCodeZip(String path) {
3845 }
3946 }
4047
41- @ Test
42- public void testStandardJar () {
48+ @ ParameterizedTest
49+ @ ValueSource (strings = {
50+ "hello.jar" ,
51+ "hello-secret.jar" ,
52+ "hello-secret-0-length-locals.jar" ,
53+ "hello-secret-junkheader.jar" ,
54+ })
55+ public void testHello (String name ) {
4356 try {
44- ZipArchive zip = ZipIO .readStandard (Paths .get ("src/test/resources/hello.jar" ));
45- assertNotNull (zip );
46- // The 'hello' jar has a manifest and single class to run itself when invoked via 'java -jar'
47- assertTrue (hasFile (zip , "META-INF/MANIFEST.MF" ));
48- assertTrue (hasFile (zip , "Hello.class" ));
57+ Path data = Paths .get ("src/test/resources/" + name );
58+ ZipArchive zipStd = ZipIO .readStandard (data );
59+ ZipArchive zipJvm = ZipIO .readJvm (data );
60+ assertNotNull (zipStd );
61+ assertNotNull (zipJvm );
62+ assertEquals (zipJvm , zipJvm );
63+
64+ // The 'hello' jars has a manifest and single class to run itself when invoked via 'java -jar'
65+ assertTrue (hasFile (zipStd , "META-INF/MANIFEST.MF" ));
66+ assertTrue (hasFile (zipStd , "Hello.class" ));
67+ } catch (IOException ex ) {
68+ fail (ex );
69+ }
70+ }
71+
72+ @ ParameterizedTest
73+ @ ValueSource (strings = {
74+ "hello-concat.jar" ,
75+ "hello-concat-junkheader.jar" ,
76+ "hello-merged.jar" ,
77+ "hello-merged-junkheader.jar" ,
78+ })
79+ public void testConcatAndMerged (String name ) {
80+ try {
81+ Path path = Paths .get ("src/test/resources/" + name );
82+ ZipArchive zipStd = ZipIO .readStandard (path );
83+ ZipArchive zipJvm = ZipIO .readJvm (path );
84+ assertNotNull (zipStd );
85+ assertNotNull (zipJvm );
86+ assertNotEquals (zipJvm , zipStd );
87+ assertNotEquals (zipJvm .getEnd (), zipStd .getEnd ());
88+ assertTrue (hasFile (zipJvm , "META-INF/MANIFEST.MF" ));
89+ assertTrue (hasFile (zipJvm , "Hello.class" ));
90+ LocalFileHeader stdHello = zipStd .getLocalFileByName ("Hello.class" );
91+ LocalFileHeader jvmHello = zipJvm .getLocalFileByName ("Hello.class" );
92+ String stdHelloRaw = ByteDataUtil .toString (ZipCompressions .decompress (stdHello ));
93+ String jvmHelloRaw = ByteDataUtil .toString (ZipCompressions .decompress (jvmHello ));
94+ assertTrue (stdHelloRaw .contains ("Hello world" ));
95+ assertTrue (jvmHelloRaw .contains ("The secret code is: ROSE" ));
4996 } catch (IOException ex ) {
5097 fail (ex );
5198 }
5299 }
53100
54101 @ Test
55- public void testJvmStandardJar () {
102+ public void testLocalHeaderDetectMismatch () {
103+ Path path = Paths .get ("src/test/resources/hello-secret-0-length-locals.jar" );
104+
56105 try {
57- // Even with edge case parsing being the focus, the JVM reader should be able to handle this jar fine.
58- ZipArchive zip = ZipIO .readJvm (Paths .get ("src/test/resources/hello.jar" ));
59- assertNotNull (zip );
60- // The 'hello' jar has a manifest and single class to run itself when invoked via 'java -jar'
61- assertTrue (hasFile (zip , "META-INF/MANIFEST.MF" ));
62- assertTrue (hasFile (zip , "Hello.class" ));
106+ ZipArchive zipJvm = ZipIO .readJvm (path );
107+ assertNotNull (zipJvm );
108+
109+ LocalFileHeader hello = zipJvm .getLocalFileByName ("Hello.class" );
110+ assertNotNull (hello );
111+
112+ // The local file header says the contents are 0 bytes, but the central header has the real length
113+ assertTrue (hello .hasDifferentValuesThanCentralDirectoryHeader ());
114+
115+ // The solution to differing values is to adopt values in the reader strategy
116+ zipJvm = ZipIO .read (path , new JvmZipReaderStrategy () {
117+ @ Override
118+ public void postProcessLocalFileHeader (LocalFileHeader file ) {
119+ file .adoptLinkedCentralDirectoryValues ();
120+ }
121+ });
122+ hello = zipJvm .getLocalFileByName ("Hello.class" );
123+ assertFalse (hello .hasDifferentValuesThanCentralDirectoryHeader ());
63124 } catch (IOException ex ) {
64125 fail (ex );
65126 }
66127 }
67128
68129 @ Test
69- public void testJvmTrickJar () {
130+ public void testMergedFakeEmpty () {
70131 try {
71- ZipArchive zip = ZipIO .readJvm (Paths .get ("src/test/resources/hello-trick.jar" ));
72- assertNotNull (zip );
73- // The 'hello' jar has a manifest and single class to run itself when invoked via 'java -jar'
74- assertTrue (hasFile (zip , "META-INF/MANIFEST.MF" ));
75- // There are two classes with deceiving names in the trick jar
76- // - The central directory names are authoritative in Java.
77- // - The local file names are ignored, so they can be anything, even `\0`
78- assertTrue (hasFile (zip , "Hello.class/" ));
79- assertTrue (hasFile (zip , "Hello.class\1 " ));
132+ ZipArchive zipJvm = ZipIO .readJvm (Paths .get ("src/test/resources/hello-merged-fake-empty.jar" ));
133+ assertNotNull (zipJvm );
134+ assertTrue (hasFile (zipJvm , "META-INF/MANIFEST.MF" ));
135+ assertTrue (hasFile (zipJvm , "Hello.class/" )); // has trailing slash in class name
80136 } catch (IOException ex ) {
81137 fail (ex );
82138 }
83139 }
84140
85141 private static boolean hasFile (ZipArchive zip , String name ) {
86- return zip .getCentralDirectories ().stream ()
87- .anyMatch (cdfh -> cdfh .getLinkedFileHeader () != null &&
88- cdfh .getFileNameAsString ().equals (name ));
142+ return !zip .getNameFilteredLocalFiles (name ::equals ).isEmpty ();
89143 }
90144}
0 commit comments