View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.archiver;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.net.URI;
25  import java.net.URL;
26  import java.nio.file.Files;
27  import java.nio.file.Path;
28  import java.nio.file.Paths;
29  import java.nio.file.attribute.FileTime;
30  import java.time.Instant;
31  import java.time.format.DateTimeParseException;
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.Collections;
35  import java.util.Comparator;
36  import java.util.HashMap;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Properties;
40  import java.util.Set;
41  import java.util.TreeSet;
42  import java.util.jar.Attributes;
43  import java.util.jar.JarFile;
44  import java.util.jar.Manifest;
45  import java.util.stream.Stream;
46  import java.util.zip.ZipEntry;
47  import java.util.zip.ZipFile;
48  
49  import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
50  import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
51  import org.apache.maven.artifact.Artifact;
52  import org.apache.maven.artifact.handler.ArtifactHandler;
53  import org.apache.maven.artifact.handler.DefaultArtifactHandler;
54  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
55  import org.apache.maven.execution.MavenSession;
56  import org.apache.maven.model.Build;
57  import org.apache.maven.model.Model;
58  import org.apache.maven.model.Organization;
59  import org.apache.maven.project.MavenProject;
60  import org.codehaus.plexus.archiver.jar.JarArchiver;
61  import org.codehaus.plexus.archiver.jar.ManifestException;
62  import org.junit.jupiter.api.Test;
63  import org.junit.jupiter.api.condition.EnabledForJreRange;
64  import org.junit.jupiter.api.condition.JRE;
65  import org.junit.jupiter.params.ParameterizedTest;
66  import org.junit.jupiter.params.provider.CsvSource;
67  import org.junit.jupiter.params.provider.EmptySource;
68  import org.junit.jupiter.params.provider.NullAndEmptySource;
69  import org.junit.jupiter.params.provider.ValueSource;
70  
71  import static org.apache.maven.archiver.MavenArchiver.parseBuildOutputTimestamp;
72  import static org.assertj.core.api.Assertions.assertThat;
73  import static org.assertj.core.api.Assertions.assertThatCode;
74  import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
75  import static org.codehaus.plexus.archiver.util.Streams.bufferedOutputStream;
76  import static org.codehaus.plexus.archiver.util.Streams.fileOutputStream;
77  import static org.junit.jupiter.api.Assertions.assertTrue;
78  import static org.mockito.Mockito.mock;
79  import static org.mockito.Mockito.when;
80  
81  class MavenArchiverTest {
82      static class ArtifactComparator implements Comparator<Artifact> {
83          public int compare(Artifact o1, Artifact o2) {
84              return o1.getArtifactId().compareTo(o2.getArtifactId());
85          }
86      }
87  
88      @ParameterizedTest
89      @EmptySource
90      @ValueSource(
91              strings = {
92                  ".",
93                  "dash-is-invalid",
94                  "plus+is+invalid",
95                  "colon:is:invalid",
96                  "new.class",
97                  "123.at.start.is.invalid",
98                  "digit.at.123start.is.invalid"
99              })
100     void testInvalidModuleNames(String value) {
101         assertThat(MavenArchiver.isValidModuleName(value)).isFalse();
102     }
103 
104     @ParameterizedTest
105     @ValueSource(strings = {"a", "a.b", "a_b", "trailing0.digits123.are456.ok789", "UTF8.chars.are.okay.äëïöüẍ", "ℤ€ℕ"})
106     void testValidModuleNames(String value) {
107         assertThat(MavenArchiver.isValidModuleName(value)).isTrue();
108     }
109 
110     @Test
111     void testGetManifestExtensionList() throws Exception {
112         MavenArchiver archiver = new MavenArchiver();
113 
114         MavenSession session = getDummySession();
115 
116         Model model = new Model();
117         model.setArtifactId("dummy");
118 
119         MavenProject project = new MavenProject(model);
120         // we need to sort the artifacts for test purposes
121         Set<Artifact> artifacts = new TreeSet<>(new ArtifactComparator());
122         project.setArtifacts(artifacts);
123 
124         // there should be a mock or a setter for this field.
125         ManifestConfiguration config = new ManifestConfiguration() {
126             public boolean isAddExtensions() {
127                 return true;
128             }
129         };
130 
131         Manifest manifest = archiver.getManifest(session, project, config);
132 
133         assertThat(manifest.getMainAttributes()).isNotNull();
134 
135         assertThat(manifest.getMainAttributes().getValue("Extension-List")).isNull();
136 
137         Artifact artifact1 = mock(Artifact.class);
138         when(artifact1.getGroupId()).thenReturn("org.apache.dummy");
139         when(artifact1.getArtifactId()).thenReturn("dummy1");
140         when(artifact1.getVersion()).thenReturn("1.0");
141         when(artifact1.getType()).thenReturn("dll");
142         when(artifact1.getScope()).thenReturn("compile");
143 
144         artifacts.add(artifact1);
145 
146         manifest = archiver.getManifest(session, project, config);
147 
148         assertThat(manifest.getMainAttributes().getValue("Extension-List")).isNull();
149 
150         Artifact artifact2 = mock(Artifact.class);
151         when(artifact2.getGroupId()).thenReturn("org.apache.dummy");
152         when(artifact2.getArtifactId()).thenReturn("dummy2");
153         when(artifact2.getVersion()).thenReturn("1.0");
154         when(artifact2.getType()).thenReturn("jar");
155         when(artifact2.getScope()).thenReturn("compile");
156 
157         artifacts.add(artifact2);
158 
159         manifest = archiver.getManifest(session, project, config);
160 
161         assertThat(manifest.getMainAttributes().getValue("Extension-List")).isEqualTo("dummy2");
162 
163         Artifact artifact3 = mock(Artifact.class);
164         when(artifact3.getGroupId()).thenReturn("org.apache.dummy");
165         when(artifact3.getArtifactId()).thenReturn("dummy3");
166         when(artifact3.getVersion()).thenReturn("1.0");
167         when(artifact3.getType()).thenReturn("jar");
168         when(artifact3.getScope()).thenReturn("test");
169 
170         artifacts.add(artifact3);
171 
172         manifest = archiver.getManifest(session, project, config);
173 
174         assertThat(manifest.getMainAttributes().getValue("Extension-List")).isEqualTo("dummy2");
175 
176         Artifact artifact4 = mock(Artifact.class);
177         when(artifact4.getGroupId()).thenReturn("org.apache.dummy");
178         when(artifact4.getArtifactId()).thenReturn("dummy4");
179         when(artifact4.getVersion()).thenReturn("1.0");
180         when(artifact4.getType()).thenReturn("jar");
181         when(artifact4.getScope()).thenReturn("compile");
182 
183         artifacts.add(artifact4);
184 
185         manifest = archiver.getManifest(session, project, config);
186 
187         assertThat(manifest.getMainAttributes().getValue("Extension-List")).isEqualTo("dummy2 dummy4");
188     }
189 
190     @Test
191     void testMultiClassPath() throws Exception {
192         final File tempFile = File.createTempFile("maven-archiver-test-", ".jar");
193 
194         try {
195             MavenArchiver archiver = new MavenArchiver();
196 
197             MavenSession session = getDummySession();
198 
199             Model model = new Model();
200             model.setArtifactId("dummy");
201 
202             MavenProject project = new MavenProject(model) {
203                 public List<String> getRuntimeClasspathElements() {
204                     return Collections.singletonList(tempFile.getAbsolutePath());
205                 }
206             };
207 
208             // there should be a mock or a setter for this field.
209             ManifestConfiguration manifestConfig = new ManifestConfiguration() {
210                 public boolean isAddClasspath() {
211                     return true;
212                 }
213             };
214 
215             MavenArchiveConfiguration archiveConfiguration = new MavenArchiveConfiguration();
216             archiveConfiguration.setManifest(manifestConfig);
217             archiveConfiguration.addManifestEntry("Class-Path", "help/");
218 
219             Manifest manifest = archiver.getManifest(session, project, archiveConfiguration);
220             String classPath = manifest.getMainAttributes().getValue("Class-Path");
221             assertThat(classPath)
222                     .as("User specified Class-Path entry was not prepended to manifest")
223                     .startsWith("help/")
224                     .as("Class-Path generated by addClasspath was not appended to manifest")
225                     .endsWith(tempFile.getName());
226         } finally {
227             // noinspection ResultOfMethodCallIgnored
228             tempFile.delete();
229         }
230     }
231 
232     @Test
233     void testRecreation() throws Exception {
234         File jarFile = new File("target/test/dummy.jar");
235         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
236 
237         MavenArchiver archiver = getMavenArchiver(jarArchiver);
238 
239         MavenSession session = getDummySession();
240         MavenProject project = getDummyProject();
241 
242         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
243         config.setForced(false);
244 
245         Path directory = Paths.get("target", "maven-archiver");
246         if (Files.exists(directory)) {
247             try (Stream<Path> paths = Files.walk(directory)) {
248                 paths.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
249             }
250         }
251 
252         archiver.createArchive(session, project, config);
253         assertThat(jarFile).exists();
254 
255         long history = System.currentTimeMillis() - 60000L;
256         jarFile.setLastModified(history);
257         long time = jarFile.lastModified();
258 
259         try (Stream<Path> paths = Files.walk(directory)) {
260             FileTime fileTime = FileTime.fromMillis(time);
261             paths.forEach(path -> assertThatCode(() -> Files.setLastModifiedTime(path, fileTime))
262                     .doesNotThrowAnyException());
263         }
264 
265         archiver.createArchive(session, project, config);
266 
267         config.setForced(true);
268         archiver.createArchive(session, project, config);
269         // I'm not sure if it could only be greater than time or if it is sufficient to be greater or equal..
270         assertThat(jarFile.lastModified()).isGreaterThanOrEqualTo(time);
271     }
272 
273     @Test
274     void testNotGenerateImplementationVersionForMANIFESTMF() throws Exception {
275         File jarFile = new File("target/test/dummy.jar");
276         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
277 
278         MavenArchiver archiver = getMavenArchiver(jarArchiver);
279 
280         MavenSession session = getDummySession();
281         MavenProject project = getDummyProject();
282 
283         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
284         config.setForced(true);
285         config.getManifest().setAddDefaultImplementationEntries(false);
286         archiver.createArchive(session, project, config);
287         assertThat(jarFile).exists();
288 
289         try (JarFile jar = new JarFile(jarFile)) {
290             assertThat(jar.getManifest().getMainAttributes())
291                     .doesNotContainKey(Attributes.Name.IMPLEMENTATION_VERSION); // "Implementation-Version"
292         }
293     }
294 
295     @Test
296     void testGenerateImplementationVersionForMANIFESTMF() throws Exception {
297         File jarFile = new File("target/test/dummy.jar");
298         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
299 
300         MavenArchiver archiver = getMavenArchiver(jarArchiver);
301 
302         MavenSession session = getDummySession();
303         MavenProject project = getDummyProject();
304 
305         String ls = System.getProperty("line.separator");
306         project.setDescription("foo " + ls + " bar ");
307         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
308         config.setForced(true);
309         config.getManifest().setAddDefaultImplementationEntries(true);
310         config.addManifestEntry("Description", project.getDescription());
311         archiver.createArchive(session, project, config);
312         assertThat(jarFile).exists();
313 
314         try (JarFile jar = new JarFile(jarFile)) {
315             assertThat(jar.getManifest().getMainAttributes())
316                     .containsKey(Attributes.Name.IMPLEMENTATION_VERSION)
317                     .containsEntry(Attributes.Name.IMPLEMENTATION_VERSION, "0.1.1");
318         }
319     }
320 
321     private MavenArchiver getMavenArchiver(JarArchiver jarArchiver) {
322         MavenArchiver archiver = new MavenArchiver();
323         archiver.setArchiver(jarArchiver);
324         archiver.setOutputFile(jarArchiver.getDestFile());
325         return archiver;
326     }
327 
328     @Test
329     void testDashesInClassPathMSHARED134() throws Exception {
330         File jarFile = new File("target/test/dummyWithDashes.jar");
331         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
332 
333         MavenArchiver archiver = getMavenArchiver(jarArchiver);
334 
335         MavenSession session = getDummySession();
336         MavenProject project = getDummyProject();
337 
338         Set<Artifact> artifacts =
339                 getArtifacts(getMockArtifact1(), getArtifactWithDot(), getMockArtifact2(), getMockArtifact3());
340 
341         project.setArtifacts(artifacts);
342 
343         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
344         config.setForced(false);
345 
346         final ManifestConfiguration mftConfig = config.getManifest();
347         mftConfig.setMainClass("org.apache.maven.Foo");
348         mftConfig.setAddClasspath(true);
349         mftConfig.setAddExtensions(true);
350         mftConfig.setClasspathPrefix("./lib/");
351 
352         archiver.createArchive(session, project, config);
353         assertThat(jarFile).exists();
354     }
355 
356     @Test
357     void testDashesInClassPathMSHARED182() throws Exception {
358         File jarFile = new File("target/test/dummy.jar");
359         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
360         MavenArchiver archiver = getMavenArchiver(jarArchiver);
361 
362         MavenSession session = getDummySession();
363         MavenProject project = getDummyProject();
364 
365         Set<Artifact> artifacts =
366                 getArtifacts(getMockArtifact1(), getArtifactWithDot(), getMockArtifact2(), getMockArtifact3());
367 
368         project.setArtifacts(artifacts);
369 
370         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
371         config.setForced(false);
372 
373         final ManifestConfiguration mftConfig = config.getManifest();
374         mftConfig.setMainClass("org.apache.maven.Foo");
375         mftConfig.setAddClasspath(true);
376         mftConfig.setAddExtensions(true);
377         mftConfig.setClasspathPrefix("./lib/");
378         config.addManifestEntry("Key1", "value1");
379         config.addManifestEntry("key2", "value2");
380 
381         archiver.createArchive(session, project, config);
382         assertThat(jarFile).exists();
383         final Attributes mainAttributes = getJarFileManifest(jarFile).getMainAttributes();
384         assertThat(mainAttributes.getValue("Key1")).isEqualTo("value1");
385         assertThat(mainAttributes.getValue("Key2")).isEqualTo("value2");
386     }
387 
388     @Test
389     void testCarriageReturnInManifestEntry() throws Exception {
390         File jarFile = new File("target/test/dummy.jar");
391         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
392 
393         MavenArchiver archiver = getMavenArchiver(jarArchiver);
394 
395         MavenSession session = getDummySession();
396         MavenProject project = getDummyProject();
397 
398         String ls = System.getProperty("line.separator");
399         project.setDescription("foo " + ls + " bar ");
400         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
401         config.setForced(true);
402         config.getManifest().setAddDefaultImplementationEntries(true);
403         config.addManifestEntry("Description", project.getDescription());
404         // config.addManifestEntry( "EntryWithTab", " foo tab " + ( '\u0009' ) + ( '\u0009' ) // + " bar tab" + ( //
405         // '\u0009' // ) );
406         archiver.createArchive(session, project, config);
407         assertThat(jarFile).exists();
408 
409         final Manifest manifest = getJarFileManifest(jarFile);
410         Attributes attributes = manifest.getMainAttributes();
411         assertThat(project.getDescription().indexOf(ls)).isGreaterThan(0);
412         Attributes.Name description = new Attributes.Name("Description");
413         String value = attributes.getValue(description);
414         assertThat(value).isNotNull();
415         assertThat(value.indexOf(ls)).isLessThanOrEqualTo(0);
416     }
417 
418     @Test
419     void testDeprecatedCreateArchiveAPI() throws Exception {
420         File jarFile = new File("target/test/dummy.jar");
421         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
422 
423         MavenArchiver archiver = getMavenArchiver(jarArchiver);
424 
425         MavenProject project = getDummyProject();
426         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
427         config.setForced(true);
428         config.getManifest().setAddDefaultImplementationEntries(true);
429         config.getManifest().setAddDefaultSpecificationEntries(true);
430 
431         MavenSession session = getDummySessionWithoutMavenVersion();
432         archiver.createArchive(session, project, config);
433         assertThat(jarFile).exists();
434         Attributes manifest = getJarFileManifest(jarFile).getMainAttributes();
435 
436         // no version number
437         assertThat(manifest)
438                 .containsEntry(new Attributes.Name("Created-By"), "Maven Archiver")
439                 .containsEntry(Attributes.Name.SPECIFICATION_TITLE, "archiver test")
440                 .containsEntry(Attributes.Name.SPECIFICATION_VERSION, "0.1")
441                 .containsEntry(Attributes.Name.SPECIFICATION_VENDOR, "Apache")
442                 .containsEntry(Attributes.Name.IMPLEMENTATION_TITLE, "archiver test")
443                 .containsEntry(Attributes.Name.IMPLEMENTATION_VERSION, "0.1.1")
444                 .containsEntry(Attributes.Name.IMPLEMENTATION_VENDOR, "Apache")
445                 .containsEntry(new Attributes.Name("Build-Jdk-Spec"), System.getProperty("java.specification.version"));
446     }
447 
448     @Test
449     void testMinimalManifestEntries() throws Exception {
450         File jarFile = new File("target/test/dummy.jar");
451         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
452 
453         MavenArchiver archiver = getMavenArchiver(jarArchiver);
454 
455         MavenSession session = getDummySession();
456         MavenProject project = getDummyProject();
457         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
458         config.setForced(true);
459         config.getManifest().setAddDefaultEntries(false);
460 
461         archiver.createArchive(session, project, config);
462         assertThat(jarFile).exists();
463 
464         final Manifest jarFileManifest = getJarFileManifest(jarFile);
465         Attributes manifest = jarFileManifest.getMainAttributes();
466 
467         assertThat(manifest).hasSize(1).containsOnlyKeys(new Attributes.Name("Manifest-Version"));
468         assertThat(manifest.getValue("Manifest-Version")).isEqualTo("1.0");
469     }
470 
471     @Test
472     void testManifestEntries() throws Exception {
473         File jarFile = new File("target/test/dummy.jar");
474         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
475 
476         MavenArchiver archiver = getMavenArchiver(jarArchiver);
477 
478         MavenSession session = getDummySession();
479         MavenProject project = getDummyProject();
480         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
481         config.setForced(true);
482         config.getManifest().setAddDefaultImplementationEntries(true);
483         config.getManifest().setAddDefaultSpecificationEntries(true);
484         config.getManifest().setAddBuildEnvironmentEntries(true);
485 
486         Map<String, String> manifestEntries = new HashMap<>();
487         manifestEntries.put("foo", "bar");
488         manifestEntries.put("first-name", "olivier");
489         manifestEntries.put("Automatic-Module-Name", "org.apache.maven.archiver");
490         manifestEntries.put("keyWithEmptyValue", null);
491         config.setManifestEntries(manifestEntries);
492 
493         ManifestSection manifestSection = new ManifestSection();
494         manifestSection.setName("UserSection");
495         manifestSection.addManifestEntry("key", "value");
496         List<ManifestSection> manifestSections = new ArrayList<>();
497         manifestSections.add(manifestSection);
498         config.setManifestSections(manifestSections);
499         config.getManifest().setMainClass("org.apache.maven.Foo");
500         archiver.createArchive(session, project, config);
501         assertThat(jarFile).exists();
502 
503         final Manifest jarFileManifest = getJarFileManifest(jarFile);
504         Attributes manifest = jarFileManifest.getMainAttributes();
505 
506         // no version number
507         assertThat(manifest)
508                 .containsEntry(new Attributes.Name("Created-By"), "Maven Archiver")
509                 .containsEntry(
510                         new Attributes.Name("Build-Tool"),
511                         session.getSystemProperties().get("maven.build.version"))
512                 .containsEntry(
513                         new Attributes.Name("Build-Jdk"),
514                         String.format("%s (%s)", System.getProperty("java.version"), System.getProperty("java.vendor")))
515                 .containsEntry(
516                         new Attributes.Name("Build-Os"),
517                         String.format(
518                                 "%s (%s; %s)",
519                                 System.getProperty("os.name"),
520                                 System.getProperty("os.version"),
521                                 System.getProperty("os.arch")))
522                 .containsEntry(Attributes.Name.SPECIFICATION_TITLE, "archiver test")
523                 .containsEntry(Attributes.Name.SPECIFICATION_VERSION, "0.1")
524                 .containsEntry(Attributes.Name.SPECIFICATION_VENDOR, "Apache")
525                 .containsEntry(Attributes.Name.IMPLEMENTATION_TITLE, "archiver test")
526                 .containsEntry(Attributes.Name.IMPLEMENTATION_VERSION, "0.1.1")
527                 .containsEntry(Attributes.Name.IMPLEMENTATION_VENDOR, "Apache")
528                 .containsEntry(Attributes.Name.MAIN_CLASS, "org.apache.maven.Foo")
529                 .containsEntry(new Attributes.Name("foo"), "bar")
530                 .containsEntry(new Attributes.Name("first-name"), "olivier");
531 
532         assertThat(manifest.getValue("Automatic-Module-Name")).isEqualTo("org.apache.maven.archiver");
533 
534         assertThat(manifest)
535                 .containsEntry(new Attributes.Name("Build-Jdk-Spec"), System.getProperty("java.specification.version"));
536 
537         assertThat(manifest.getValue(new Attributes.Name("keyWithEmptyValue"))).isEmpty();
538         assertThat(manifest).containsKey(new Attributes.Name("keyWithEmptyValue"));
539 
540         manifest = jarFileManifest.getAttributes("UserSection");
541 
542         assertThat(manifest).containsEntry(new Attributes.Name("key"), "value");
543     }
544 
545     @Test
546     void testManifestWithInvalidAutomaticModuleNameThrowsOnCreateArchive() throws Exception {
547         File jarFile = new File("target/test/dummy.jar");
548         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
549 
550         MavenArchiver archiver = getMavenArchiver(jarArchiver);
551 
552         MavenSession session = getDummySession();
553         MavenProject project = getDummyProject();
554         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
555 
556         Map<String, String> manifestEntries = new HashMap<>();
557         manifestEntries.put("Automatic-Module-Name", "123.in-valid.new.name");
558         config.setManifestEntries(manifestEntries);
559 
560         try {
561             archiver.createArchive(session, project, config);
562         } catch (ManifestException e) {
563             assertThat(e.getMessage()).isEqualTo("Invalid automatic module name: '123.in-valid.new.name'");
564         }
565     }
566 
567     /*
568      * Test to make sure that empty Automatic-Module-Name will result in no
569      * Automatic-Module-Name attribute at all, but that the archive will be created.
570      */
571     @Test
572     void testManifestWithEmptyAutomaticModuleName() throws Exception {
573         File jarFile = new File("target/test/dummy.jar");
574         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
575 
576         MavenArchiver archiver = getMavenArchiver(jarArchiver);
577 
578         MavenSession session = getDummySession();
579         MavenProject project = getDummyProject();
580         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
581 
582         Map<String, String> manifestEntries = new HashMap<>();
583         manifestEntries.put("Automatic-Module-Name", "");
584         config.setManifestEntries(manifestEntries);
585 
586         archiver.createArchive(session, project, config);
587         assertThat(jarFile).exists();
588 
589         final Manifest jarFileManifest = getJarFileManifest(jarFile);
590         Attributes manifest = jarFileManifest.getMainAttributes();
591 
592         assertThat(manifest).doesNotContainKey(new Attributes.Name("Automatic-Module-Name"));
593     }
594 
595     //
596     // Test to make sure that manifest sections are present in the manifest prior to the archive has been created.
597     //
598     @Test
599     void testManifestSections() throws Exception {
600         MavenArchiver archiver = new MavenArchiver();
601 
602         MavenSession session = getDummySession();
603 
604         MavenProject project = getDummyProject();
605         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
606 
607         ManifestSection manifestSection = new ManifestSection();
608         manifestSection.setName("SectionOne");
609         manifestSection.addManifestEntry("key", "value");
610         List<ManifestSection> manifestSections = new ArrayList<>();
611         manifestSections.add(manifestSection);
612         config.setManifestSections(manifestSections);
613 
614         Manifest manifest = archiver.getManifest(session, project, config);
615 
616         Attributes section = manifest.getAttributes("SectionOne");
617         assertThat(section)
618                 .as("The section is not present in the manifest as it should be.")
619                 .isNotNull();
620 
621         String attribute = section.getValue("key");
622         assertThat(attribute)
623                 .as("The attribute we are looking for is not present in the section.")
624                 .isNotNull()
625                 .as("The value of the attribute is wrong.")
626                 .isEqualTo("value");
627     }
628 
629     @Test
630     void testDefaultClassPathValue() throws Exception {
631         MavenSession session = getDummySession();
632         MavenProject project = getDummyProject();
633         File jarFile = new File("target/test/dummy.jar");
634         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
635 
636         MavenArchiver archiver = getMavenArchiver(jarArchiver);
637 
638         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
639         config.setForced(true);
640         config.getManifest().setAddDefaultImplementationEntries(true);
641         config.getManifest().setAddDefaultSpecificationEntries(true);
642         config.getManifest().setMainClass("org.apache.maven.Foo");
643         config.getManifest().setAddClasspath(true);
644         config.getManifest().setClasspathLayoutType(ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM);
645         config.getManifest()
646                 .setCustomClasspathLayout(
647                         "${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}");
648         archiver.createArchive(session, project, config);
649         assertThat(jarFile).exists();
650         final Manifest manifest = getJarFileManifest(jarFile);
651         String classPath = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
652         assertThat(classPath).isNotNull();
653         assertThat(classPath.split(" "))
654                 .containsExactly("dummy1-1.0.jar", "dummy2-1.5.jar", "dummy3-2.0-classifier.jar");
655     }
656 
657     private void deleteAndAssertNotPresent(File jarFile) {
658         jarFile.delete();
659         assertThat(jarFile).doesNotExist();
660     }
661 
662     @Test
663     void testDefaultClassPathValueWithSnapshot() throws Exception {
664         MavenSession session = getDummySession();
665         MavenProject project = getDummyProjectWithSnapshot();
666         File jarFile = new File("target/test/dummy.jar");
667         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
668 
669         MavenArchiver archiver = getMavenArchiver(jarArchiver);
670 
671         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
672         config.setForced(true);
673         config.getManifest().setAddDefaultImplementationEntries(true);
674         config.getManifest().setAddDefaultSpecificationEntries(true);
675         config.getManifest().setMainClass("org.apache.maven.Foo");
676         config.getManifest().setAddClasspath(true);
677         config.getManifest().setClasspathLayoutType(ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM);
678         config.getManifest()
679                 .setCustomClasspathLayout(
680                         "${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}");
681         archiver.createArchive(session, project, config);
682         assertThat(jarFile).exists();
683 
684         final Manifest manifest = getJarFileManifest(jarFile);
685         String classPath = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
686         assertThat(classPath).isNotNull();
687         assertThat(classPath.split(" "))
688                 .containsExactly("dummy1-1.1-20081022.112233-1.jar", "dummy2-1.5.jar", "dummy3-2.0-classifier.jar");
689     }
690 
691     @Test
692     void testMavenRepoClassPathValue() throws Exception {
693         MavenSession session = getDummySession();
694         MavenProject project = getDummyProject();
695         File jarFile = new File("target/test/dummy.jar");
696         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
697 
698         MavenArchiver archiver = getMavenArchiver(jarArchiver);
699 
700         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
701         config.setForced(true);
702         config.getManifest().setAddDefaultImplementationEntries(true);
703         config.getManifest().setAddDefaultSpecificationEntries(true);
704         config.getManifest().setMainClass("org.apache.maven.Foo");
705         config.getManifest().setAddClasspath(true);
706         config.getManifest().setUseUniqueVersions(true);
707         config.getManifest().setClasspathLayoutType(ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_REPOSITORY);
708         archiver.createArchive(session, project, config);
709         assertThat(jarFile).exists();
710         Manifest manifest = archiver.getManifest(session, project, config);
711         String[] classPathEntries =
712                 new String(manifest.getMainAttributes().getValue("Class-Path").getBytes()).split(" ");
713         assertThat(classPathEntries)
714                 .containsExactly(
715                         "org/apache/dummy/dummy1/1.0.1/dummy1-1.0.jar",
716                         "org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
717                         "org/apache/dummy/bar/dummy3/2.0/dummy3-2.0-classifier.jar");
718 
719         String classPath = getJarFileManifest(jarFile).getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
720         assertThat(classPath).isNotNull();
721         assertThat(classPath.split(" "))
722                 .containsExactly(
723                         "org/apache/dummy/dummy1/1.0.1/dummy1-1.0.jar",
724                         "org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
725                         "org/apache/dummy/bar/dummy3/2.0/dummy3-2.0-classifier.jar");
726     }
727 
728     @Test
729     void shouldCreateArchiveWithSimpleClassPathLayoutWhileSettingSimpleLayoutExplicit() throws Exception {
730         MavenSession session = getDummySession();
731         MavenProject project = getDummyProject();
732         File jarFile = new File("target/test/dummy-explicit-simple.jar");
733         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
734 
735         MavenArchiver archiver = getMavenArchiver(jarArchiver);
736 
737         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
738         config.setForced(true);
739         config.getManifest().setAddDefaultImplementationEntries(true);
740         config.getManifest().setAddDefaultSpecificationEntries(true);
741         config.getManifest().setMainClass("org.apache.maven.Foo");
742         config.getManifest().setAddClasspath(true);
743         config.getManifest().setClasspathPrefix("lib");
744         config.getManifest().setClasspathLayoutType(ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_SIMPLE);
745 
746         archiver.createArchive(session, project, config);
747         assertThat(jarFile).exists();
748         Manifest manifest = archiver.getManifest(session, project, config);
749         String[] classPathEntries =
750                 new String(manifest.getMainAttributes().getValue("Class-Path").getBytes()).split(" ");
751         assertThat(classPathEntries)
752                 .containsExactly("lib/dummy1-1.0.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0-classifier.jar");
753 
754         String classPath = getJarFileManifest(jarFile).getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
755 
756         assertThat(classPath).isNotNull();
757         assertThat(classPath.split(" "))
758                 .containsExactly("lib/dummy1-1.0.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0-classifier.jar");
759     }
760 
761     @Test
762     void shouldCreateArchiveCustomerLayoutSimple() throws Exception {
763         MavenSession session = getDummySession();
764         MavenProject project = getDummyProject();
765         File jarFile = new File("target/test/dummy-custom-layout-simple.jar");
766         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
767 
768         MavenArchiver archiver = getMavenArchiver(jarArchiver);
769 
770         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
771         config.setForced(true);
772         config.getManifest().setAddDefaultImplementationEntries(true);
773         config.getManifest().setAddDefaultSpecificationEntries(true);
774         config.getManifest().setMainClass("org.apache.maven.Foo");
775         config.getManifest().setAddClasspath(true);
776         config.getManifest().setClasspathPrefix("lib");
777         config.getManifest().setClasspathLayoutType(ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM);
778         config.getManifest().setCustomClasspathLayout(MavenArchiver.SIMPLE_LAYOUT);
779 
780         archiver.createArchive(session, project, config);
781         assertThat(jarFile).exists();
782         Manifest manifest = archiver.getManifest(session, project, config);
783         String[] classPathEntries =
784                 new String(manifest.getMainAttributes().getValue("Class-Path").getBytes()).split(" ");
785         assertThat(classPathEntries)
786                 .containsExactly("lib/dummy1-1.0.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0-classifier.jar");
787 
788         String classPath = getJarFileManifest(jarFile).getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
789 
790         assertThat(classPath).isNotNull();
791         assertThat(classPath.split(" "))
792                 .containsExactly("lib/dummy1-1.0.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0-classifier.jar");
793     }
794 
795     @Test
796     void shouldCreateArchiveCustomLayoutSimpleNonUnique() throws Exception {
797         MavenSession session = getDummySession();
798         MavenProject project = getDummyProject();
799         File jarFile = new File("target/test/dummy-custom-layout-simple-non-unique.jar");
800         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
801 
802         MavenArchiver archiver = getMavenArchiver(jarArchiver);
803 
804         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
805         config.setForced(true);
806         config.getManifest().setAddDefaultImplementationEntries(true);
807         config.getManifest().setAddDefaultSpecificationEntries(true);
808         config.getManifest().setMainClass("org.apache.maven.Foo");
809         config.getManifest().setAddClasspath(true);
810         config.getManifest().setClasspathPrefix("lib");
811         config.getManifest().setClasspathLayoutType(ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM);
812         config.getManifest().setCustomClasspathLayout(MavenArchiver.SIMPLE_LAYOUT_NONUNIQUE);
813 
814         archiver.createArchive(session, project, config);
815         assertThat(jarFile).exists();
816         Manifest manifest = archiver.getManifest(session, project, config);
817         String[] classPathEntries =
818                 new String(manifest.getMainAttributes().getValue("Class-Path").getBytes()).split(" ");
819         assertThat(classPathEntries)
820                 .containsExactly("lib/dummy1-1.0.1.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0-classifier.jar");
821 
822         String classPath = getJarFileManifest(jarFile).getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
823 
824         assertThat(classPath).isNotNull();
825         assertThat(classPath.split(" "))
826                 .containsExactly("lib/dummy1-1.0.1.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0-classifier.jar");
827     }
828 
829     @Test
830     void shouldCreateArchiveCustomLayoutRepository() throws Exception {
831         MavenSession session = getDummySession();
832         MavenProject project = getDummyProject();
833         File jarFile = new File("target/test/dummy-custom-layout-repo.jar");
834         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
835 
836         MavenArchiver archiver = getMavenArchiver(jarArchiver);
837 
838         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
839         config.setForced(true);
840         config.getManifest().setAddDefaultImplementationEntries(true);
841         config.getManifest().setAddDefaultSpecificationEntries(true);
842         config.getManifest().setMainClass("org.apache.maven.Foo");
843         config.getManifest().setAddClasspath(true);
844         config.getManifest().setClasspathPrefix("lib");
845         config.getManifest().setClasspathLayoutType(ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM);
846         config.getManifest().setCustomClasspathLayout(MavenArchiver.REPOSITORY_LAYOUT);
847 
848         archiver.createArchive(session, project, config);
849         assertThat(jarFile).exists();
850         Manifest manifest = archiver.getManifest(session, project, config);
851         String[] classPathEntries =
852                 new String(manifest.getMainAttributes().getValue("Class-Path").getBytes()).split(" ");
853         assertThat(classPathEntries)
854                 .containsExactly(
855                         "lib/org/apache/dummy/dummy1/1.0.1/dummy1-1.0.jar",
856                         "lib/org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
857                         "lib/org/apache/dummy/bar/dummy3/2.0/dummy3-2.0-classifier.jar");
858 
859         String classPath = getJarFileManifest(jarFile).getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
860 
861         assertThat(classPath).isNotNull();
862         assertThat(classPath.split(" "))
863                 .containsExactly(
864                         "lib/org/apache/dummy/dummy1/1.0.1/dummy1-1.0.jar",
865                         "lib/org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
866                         "lib/org/apache/dummy/bar/dummy3/2.0/dummy3-2.0-classifier.jar");
867     }
868 
869     @Test
870     void shouldCreateArchiveCustomLayoutRepositoryNonUnique() throws Exception {
871         MavenSession session = getDummySession();
872         MavenProject project = getDummyProject();
873         File jarFile = new File("target/test/dummy-custom-layout-repo-non-unique.jar");
874         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
875 
876         MavenArchiver archiver = getMavenArchiver(jarArchiver);
877 
878         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
879         config.setForced(true);
880         config.getManifest().setAddDefaultImplementationEntries(true);
881         config.getManifest().setAddDefaultSpecificationEntries(true);
882         config.getManifest().setMainClass("org.apache.maven.Foo");
883         config.getManifest().setAddClasspath(true);
884         config.getManifest().setClasspathPrefix("lib");
885         config.getManifest().setClasspathLayoutType(ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM);
886         config.getManifest().setCustomClasspathLayout(MavenArchiver.REPOSITORY_LAYOUT_NONUNIQUE);
887 
888         archiver.createArchive(session, project, config);
889         assertThat(jarFile).exists();
890         Manifest manifest = archiver.getManifest(session, project, config);
891         String[] classPathEntries =
892                 new String(manifest.getMainAttributes().getValue("Class-Path").getBytes()).split(" ");
893         assertThat(classPathEntries)
894                 .containsExactly(
895                         "lib/org/apache/dummy/dummy1/1.0.1/dummy1-1.0.1.jar",
896                         "lib/org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
897                         "lib/org/apache/dummy/bar/dummy3/2.0/dummy3-2.0-classifier.jar");
898 
899         String classPath = getJarFileManifest(jarFile).getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
900 
901         assertThat(classPath).isNotNull();
902         assertThat(classPath.split(" "))
903                 .containsExactly(
904                         "lib/org/apache/dummy/dummy1/1.0.1/dummy1-1.0.1.jar",
905                         "lib/org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
906                         "lib/org/apache/dummy/bar/dummy3/2.0/dummy3-2.0-classifier.jar");
907     }
908 
909     @Test
910     void shouldCreateArchiveWithSimpleClassPathLayoutUsingDefaults() throws Exception {
911         MavenSession session = getDummySession();
912         MavenProject project = getDummyProject();
913         File jarFile = new File("target/test/dummy-defaults.jar");
914         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
915 
916         MavenArchiver archiver = getMavenArchiver(jarArchiver);
917 
918         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
919         config.setForced(true);
920         config.getManifest().setAddDefaultImplementationEntries(true);
921         config.getManifest().setAddDefaultSpecificationEntries(true);
922         config.getManifest().setMainClass("org.apache.maven.Foo");
923         config.getManifest().setAddClasspath(true);
924         config.getManifest().setClasspathPrefix("lib");
925 
926         archiver.createArchive(session, project, config);
927         assertThat(jarFile).exists();
928         Manifest manifest = archiver.getManifest(session, project, config);
929         String[] classPathEntries =
930                 new String(manifest.getMainAttributes().getValue("Class-Path").getBytes()).split(" ");
931         assertThat(classPathEntries)
932                 .containsExactly("lib/dummy1-1.0.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0-classifier.jar");
933 
934         String classPath = getJarFileManifest(jarFile).getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
935         assertThat(classPath).isNotNull();
936         assertThat(classPath.split(" "))
937                 .containsExactly("lib/dummy1-1.0.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0-classifier.jar");
938     }
939 
940     @Test
941     void testMavenRepoClassPathValueWithSnapshot() throws Exception {
942         MavenSession session = getDummySession();
943         MavenProject project = getDummyProjectWithSnapshot();
944         File jarFile = new File("target/test/dummy.jar");
945         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
946 
947         MavenArchiver archiver = getMavenArchiver(jarArchiver);
948 
949         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
950         config.setForced(true);
951         config.getManifest().setAddDefaultImplementationEntries(true);
952         config.getManifest().setAddDefaultSpecificationEntries(true);
953         config.getManifest().setMainClass("org.apache.maven.Foo");
954         config.getManifest().setAddClasspath(true);
955         config.getManifest().setClasspathLayoutType(ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_REPOSITORY);
956         archiver.createArchive(session, project, config);
957         assertThat(jarFile).exists();
958 
959         Manifest manifest = archiver.getManifest(session, project, config);
960         String[] classPathEntries =
961                 new String(manifest.getMainAttributes().getValue("Class-Path").getBytes()).split(" ");
962         assertThat(classPathEntries)
963                 .containsExactly(
964                         "org/apache/dummy/dummy1/1.1-SNAPSHOT/dummy1-1.1-20081022.112233-1.jar",
965                         "org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
966                         "org/apache/dummy/bar/dummy3/2.0/dummy3-2.0-classifier.jar");
967 
968         String classPath = getJarFileManifest(jarFile).getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
969         assertThat(classPath).isNotNull();
970         assertThat(classPath.split(" "))
971                 .containsExactly(
972                         "org/apache/dummy/dummy1/1.1-SNAPSHOT/dummy1-1.1-20081022.112233-1.jar",
973                         "org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
974                         "org/apache/dummy/bar/dummy3/2.0/dummy3-2.0-classifier.jar");
975     }
976 
977     @Test
978     void testCustomClassPathValue() throws Exception {
979         MavenSession session = getDummySession();
980         MavenProject project = getDummyProject();
981         File jarFile = new File("target/test/dummy.jar");
982         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
983 
984         MavenArchiver archiver = getMavenArchiver(jarArchiver);
985 
986         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
987         config.setForced(true);
988         config.getManifest().setAddDefaultImplementationEntries(true);
989         config.getManifest().setAddDefaultSpecificationEntries(true);
990         config.getManifest().setMainClass("org.apache.maven.Foo");
991         config.getManifest().setAddClasspath(true);
992         config.getManifest().setClasspathLayoutType(ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM);
993         config.getManifest()
994                 .setCustomClasspathLayout(
995                         "${artifact.groupIdPath}/${artifact.artifactId}/${artifact.version}/TEST-${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}");
996         archiver.createArchive(session, project, config);
997         assertThat(jarFile).exists();
998         Manifest manifest = archiver.getManifest(session, project, config);
999         String[] classPathEntries =
1000                 new String(manifest.getMainAttributes().getValue("Class-Path").getBytes()).split(" ");
1001         assertThat(classPathEntries)
1002                 .containsExactly(
1003                         "org/apache/dummy/dummy1/1.0/TEST-dummy1-1.0.jar",
1004                         "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar",
1005                         "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0-classifier.jar");
1006 
1007         final Manifest manifest1 = getJarFileManifest(jarFile);
1008         String classPath = manifest1.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
1009         assertThat(classPath).isNotNull();
1010         assertThat(classPath.split(" "))
1011                 .containsExactly(
1012                         "org/apache/dummy/dummy1/1.0/TEST-dummy1-1.0.jar",
1013                         "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar",
1014                         "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0-classifier.jar");
1015     }
1016 
1017     @Test
1018     void testCustomClassPathValueWithSnapshotResolvedVersion() throws Exception {
1019         MavenSession session = getDummySession();
1020         MavenProject project = getDummyProjectWithSnapshot();
1021         File jarFile = new File("target/test/dummy.jar");
1022         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
1023         MavenArchiver archiver = getMavenArchiver(jarArchiver);
1024 
1025         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
1026         config.setForced(true);
1027         config.getManifest().setAddDefaultImplementationEntries(true);
1028         config.getManifest().setAddDefaultSpecificationEntries(true);
1029         config.getManifest().setMainClass("org.apache.maven.Foo");
1030         config.getManifest().setAddClasspath(true);
1031         config.getManifest().setClasspathLayoutType(ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM);
1032         config.getManifest()
1033                 .setCustomClasspathLayout(
1034                         "${artifact.groupIdPath}/${artifact.artifactId}/${artifact.baseVersion}/TEST-${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}");
1035         archiver.createArchive(session, project, config);
1036         assertThat(jarFile).exists();
1037 
1038         Manifest manifest = archiver.getManifest(session, project, config);
1039         String[] classPathEntries =
1040                 new String(manifest.getMainAttributes().getValue("Class-Path").getBytes()).split(" ");
1041         assertThat(classPathEntries)
1042                 .containsExactly(
1043                         "org/apache/dummy/dummy1/1.1-SNAPSHOT/TEST-dummy1-1.1-20081022.112233-1.jar",
1044                         "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar",
1045                         "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0-classifier.jar");
1046 
1047         String classPath = getJarFileManifest(jarFile).getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
1048         assertThat(classPath).isNotNull();
1049         assertThat(classPath.split(" "))
1050                 .containsExactly(
1051                         "org/apache/dummy/dummy1/1.1-SNAPSHOT/TEST-dummy1-1.1-20081022.112233-1.jar",
1052                         "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar",
1053                         "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0-classifier.jar");
1054     }
1055 
1056     @Test
1057     void testCustomClassPathValueWithSnapshotForcingBaseVersion() throws Exception {
1058         MavenSession session = getDummySession();
1059         MavenProject project = getDummyProjectWithSnapshot();
1060         File jarFile = new File("target/test/dummy.jar");
1061         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
1062 
1063         MavenArchiver archiver = getMavenArchiver(jarArchiver);
1064 
1065         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
1066         config.setForced(true);
1067         config.getManifest().setAddDefaultImplementationEntries(true);
1068         config.getManifest().setAddDefaultSpecificationEntries(true);
1069         config.getManifest().setMainClass("org.apache.maven.Foo");
1070         config.getManifest().setAddClasspath(true);
1071         config.getManifest().setClasspathLayoutType(ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM);
1072         config.getManifest()
1073                 .setCustomClasspathLayout(
1074                         "${artifact.groupIdPath}/${artifact.artifactId}/${artifact.baseVersion}/TEST-${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}");
1075         archiver.createArchive(session, project, config);
1076         assertThat(jarFile).exists();
1077         Manifest manifest = archiver.getManifest(session, project, config);
1078         String[] classPathEntries =
1079                 new String(manifest.getMainAttributes().getValue("Class-Path").getBytes()).split(" ");
1080         assertThat(classPathEntries[0]).isEqualTo("org/apache/dummy/dummy1/1.1-SNAPSHOT/TEST-dummy1-1.1-SNAPSHOT.jar");
1081         assertThat(classPathEntries[1]).isEqualTo("org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar");
1082         assertThat(classPathEntries[2]).isEqualTo("org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0-classifier.jar");
1083 
1084         String classPath = getJarFileManifest(jarFile).getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
1085         assertThat(classPath).isNotNull();
1086         assertThat(classPath.split(" "))
1087                 .containsExactly(
1088                         "org/apache/dummy/dummy1/1.1-SNAPSHOT/TEST-dummy1-1.1-SNAPSHOT.jar",
1089                         "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar",
1090                         "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0-classifier.jar");
1091     }
1092 
1093     @Test
1094     void testDefaultPomProperties() throws Exception {
1095         MavenSession session = getDummySession();
1096         MavenProject project = getDummyProject();
1097         File jarFile = new File("target/test/dummy.jar");
1098         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
1099 
1100         MavenArchiver archiver = getMavenArchiver(jarArchiver);
1101 
1102         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
1103         config.setForced(true);
1104         archiver.createArchive(session, project, config);
1105         assertThat(jarFile).exists();
1106 
1107         final String groupId = project.getGroupId();
1108         final String artifactId = project.getArtifactId();
1109         final String version = project.getVersion();
1110 
1111         JarFile virtJarFile = new JarFile(jarFile);
1112         ZipEntry pomPropertiesEntry =
1113                 virtJarFile.getEntry("META-INF/maven/" + groupId + "/" + artifactId + "/pom.properties");
1114         assertThat(pomPropertiesEntry).isNotNull();
1115 
1116         try (InputStream is = virtJarFile.getInputStream(pomPropertiesEntry)) {
1117             Properties p = new Properties();
1118             p.load(is);
1119 
1120             assertThat(p.getProperty("groupId")).isEqualTo(groupId);
1121             assertThat(p.getProperty("artifactId")).isEqualTo(artifactId);
1122             assertThat(p.getProperty("version")).isEqualTo(version);
1123         }
1124         virtJarFile.close();
1125     }
1126 
1127     @Test
1128     void testCustomPomProperties() throws Exception {
1129         MavenSession session = getDummySession();
1130         MavenProject project = getDummyProject();
1131         File jarFile = new File("target/test/dummy.jar");
1132         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
1133 
1134         MavenArchiver archiver = getMavenArchiver(jarArchiver);
1135 
1136         File customPomPropertiesFile = new File("src/test/resources/custom-pom.properties");
1137         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
1138         config.setForced(true);
1139         config.setPomPropertiesFile(customPomPropertiesFile);
1140         archiver.createArchive(session, project, config);
1141         assertThat(jarFile).exists();
1142 
1143         final String groupId = project.getGroupId();
1144         final String artifactId = project.getArtifactId();
1145         final String version = project.getVersion();
1146 
1147         try (JarFile virtJarFile = new JarFile(jarFile)) {
1148             ZipEntry pomPropertiesEntry =
1149                     virtJarFile.getEntry("META-INF/maven/" + groupId + "/" + artifactId + "/pom.properties");
1150             assertThat(pomPropertiesEntry).isNotNull();
1151 
1152             try (InputStream is = virtJarFile.getInputStream(pomPropertiesEntry)) {
1153                 Properties p = new Properties();
1154                 p.load(is);
1155 
1156                 assertThat(p.getProperty("groupId")).isEqualTo(groupId);
1157                 assertThat(p.getProperty("artifactId")).isEqualTo(artifactId);
1158                 assertThat(p.getProperty("version")).isEqualTo(version);
1159                 assertThat(p.getProperty("build.revision")).isEqualTo("1337");
1160                 assertThat(p.getProperty("build.branch")).isEqualTo("tags/0.1.1");
1161             }
1162         }
1163     }
1164 
1165     private JarArchiver getCleanJarArchiver(File jarFile) {
1166         deleteAndAssertNotPresent(jarFile);
1167         JarArchiver jarArchiver = new JarArchiver();
1168         jarArchiver.setDestFile(jarFile);
1169         return jarArchiver;
1170     }
1171 
1172     // ----------------------------------------
1173     // common methods for testing
1174     // ----------------------------------------
1175 
1176     private MavenProject getDummyProject() throws Exception {
1177         MavenProject project = getMavenProject();
1178 
1179         Artifact artifact = mock(Artifact.class);
1180         when(artifact.getGroupId()).thenReturn("org.apache.dummy");
1181         when(artifact.getArtifactId()).thenReturn("dummy");
1182         when(artifact.getVersion()).thenReturn("0.1.1");
1183         when(artifact.getBaseVersion()).thenReturn("0.1.2");
1184         when(artifact.getSelectedVersion()).thenReturn(new DefaultArtifactVersion("0.1.1"));
1185         when(artifact.getType()).thenReturn("jar");
1186         when(artifact.getArtifactHandler()).thenReturn(new DefaultArtifactHandler("jar"));
1187         project.setArtifact(artifact);
1188 
1189         Set<Artifact> artifacts = getArtifacts(getMockArtifact1Release(), getMockArtifact2(), getMockArtifact3());
1190         project.setArtifacts(artifacts);
1191 
1192         return project;
1193     }
1194 
1195     private MavenProject getMavenProject() {
1196         Model model = new Model();
1197         model.setGroupId("org.apache.dummy");
1198         model.setArtifactId("dummy");
1199         model.setVersion("0.1.1");
1200 
1201         final MavenProject project = new MavenProject(model);
1202         project.setRemoteArtifactRepositories(Collections.emptyList());
1203         project.setPluginArtifactRepositories(Collections.emptyList());
1204         project.setName("archiver test");
1205 
1206         File pomFile = new File("src/test/resources/pom.xml");
1207         project.setFile(pomFile);
1208 
1209         Build build = new Build();
1210         build.setDirectory("target");
1211         build.setOutputDirectory("target");
1212         project.setBuild(build);
1213 
1214         Organization organization = new Organization();
1215         organization.setName("Apache");
1216         project.setOrganization(organization);
1217         return project;
1218     }
1219 
1220     private Artifact getMockArtifact3() {
1221         Artifact artifact = mock(Artifact.class);
1222         when(artifact.getGroupId()).thenReturn("org.apache.dummy.bar");
1223         when(artifact.getArtifactId()).thenReturn("dummy3");
1224         when(artifact.getVersion()).thenReturn("2.0");
1225         when(artifact.getType()).thenReturn("jar");
1226         when(artifact.getScope()).thenReturn("runtime");
1227         when(artifact.getClassifier()).thenReturn("classifier");
1228         File file = getClasspathFile(artifact.getArtifactId() + "-" + artifact.getVersion() + ".jar");
1229         when(artifact.getFile()).thenReturn(file);
1230         ArtifactHandler artifactHandler = mock(ArtifactHandler.class);
1231         when(artifactHandler.isAddedToClasspath()).thenReturn(true);
1232         when(artifactHandler.getExtension()).thenReturn("jar");
1233         when(artifact.getArtifactHandler()).thenReturn(artifactHandler);
1234         return artifact;
1235     }
1236 
1237     private MavenProject getDummyProjectWithSnapshot() throws Exception {
1238         MavenProject project = getMavenProject();
1239 
1240         Artifact artifact = mock(Artifact.class);
1241         when(artifact.getGroupId()).thenReturn("org.apache.dummy");
1242         when(artifact.getArtifactId()).thenReturn("dummy");
1243         when(artifact.getVersion()).thenReturn("0.1.1");
1244         when(artifact.getBaseVersion()).thenReturn("0.1.1");
1245         when(artifact.getSelectedVersion()).thenReturn(new DefaultArtifactVersion("0.1.1"));
1246         when(artifact.getType()).thenReturn("jar");
1247         when(artifact.getScope()).thenReturn("compile");
1248         when(artifact.getArtifactHandler()).thenReturn(new DefaultArtifactHandler("jar"));
1249         project.setArtifact(artifact);
1250 
1251         Set<Artifact> artifacts = getArtifacts(getMockArtifact1(), getMockArtifact2(), getMockArtifact3());
1252         project.setArtifacts(artifacts);
1253 
1254         return project;
1255     }
1256 
1257     private Artifact getMockArtifact2() {
1258         Artifact artifact = mock(Artifact.class);
1259         when(artifact.getGroupId()).thenReturn("org.apache.dummy.foo");
1260         when(artifact.getArtifactId()).thenReturn("dummy2");
1261         when(artifact.getVersion()).thenReturn("1.5");
1262         when(artifact.getType()).thenReturn("jar");
1263         when(artifact.getScope()).thenReturn("runtime");
1264         File file = getClasspathFile(artifact.getArtifactId() + "-" + artifact.getVersion() + ".jar");
1265         when(artifact.getFile()).thenReturn(file);
1266         ArtifactHandler artifactHandler = mock(ArtifactHandler.class);
1267         when(artifactHandler.isAddedToClasspath()).thenReturn(true);
1268         when(artifactHandler.getExtension()).thenReturn("jar");
1269         when(artifact.getArtifactHandler()).thenReturn(artifactHandler);
1270         return artifact;
1271     }
1272 
1273     private Artifact getArtifactWithDot() {
1274         Artifact artifact = mock(Artifact.class);
1275         when(artifact.getGroupId()).thenReturn("org.apache.dummy.foo");
1276         when(artifact.getArtifactId()).thenReturn("dummy.dot");
1277         when(artifact.getVersion()).thenReturn("1.5");
1278         when(artifact.getScope()).thenReturn("runtime");
1279         when(artifact.getArtifactHandler()).thenReturn(new DefaultArtifactHandler("jar"));
1280         return artifact;
1281     }
1282 
1283     private Artifact getMockArtifact1() {
1284         Artifact artifact = mock(Artifact.class);
1285         when(artifact.getGroupId()).thenReturn("org.apache.dummy");
1286         when(artifact.getArtifactId()).thenReturn("dummy1");
1287         when(artifact.getVersion()).thenReturn("1.1-20081022.112233-1");
1288         when(artifact.getBaseVersion()).thenReturn("1.1-SNAPSHOT");
1289         when(artifact.getType()).thenReturn("jar");
1290         when(artifact.getScope()).thenReturn("runtime");
1291         File file = getClasspathFile(artifact.getArtifactId() + "-" + artifact.getVersion() + ".jar");
1292         when(artifact.getFile()).thenReturn(file);
1293         ArtifactHandler artifactHandler = mock(ArtifactHandler.class);
1294         when(artifactHandler.isAddedToClasspath()).thenReturn(true);
1295         when(artifactHandler.getExtension()).thenReturn("jar");
1296         when(artifact.getArtifactHandler()).thenReturn(artifactHandler);
1297         return artifact;
1298     }
1299 
1300     private Artifact getMockArtifact1Release() {
1301         Artifact artifact = mock(Artifact.class);
1302         when(artifact.getGroupId()).thenReturn("org.apache.dummy");
1303         when(artifact.getArtifactId()).thenReturn("dummy1");
1304         when(artifact.getVersion()).thenReturn("1.0");
1305         when(artifact.getBaseVersion()).thenReturn("1.0.1");
1306         when(artifact.getType()).thenReturn("jar");
1307         when(artifact.getScope()).thenReturn("runtime");
1308         File file = getClasspathFile(artifact.getArtifactId() + "-" + artifact.getVersion() + ".jar");
1309         when(artifact.getFile()).thenReturn(file);
1310         ArtifactHandler artifactHandler = mock(ArtifactHandler.class);
1311         when(artifactHandler.isAddedToClasspath()).thenReturn(true);
1312         when(artifactHandler.getExtension()).thenReturn("jar");
1313         when(artifact.getArtifactHandler()).thenReturn(artifactHandler);
1314         return artifact;
1315     }
1316 
1317     private File getClasspathFile(String file) {
1318         URL resource = Thread.currentThread().getContextClassLoader().getResource(file);
1319         if (resource == null) {
1320             throw new IllegalStateException(
1321                     "Cannot retrieve java.net.URL for file: " + file + " on the current test classpath.");
1322         }
1323 
1324         URI uri = new File(resource.getPath()).toURI().normalize();
1325 
1326         return new File(uri.getPath().replaceAll("%20", " "));
1327     }
1328 
1329     private MavenSession getDummySession() {
1330         Properties systemProperties = new Properties();
1331         systemProperties.put("maven.version", "3.1.1");
1332         systemProperties.put(
1333                 "maven.build.version",
1334                 "Apache Maven 3.1.1 (0728685237757ffbf44136acec0402957f723d9a; 2013-09-17 17:22:22+0200)");
1335 
1336         return getDummySession(systemProperties);
1337     }
1338 
1339     private MavenSession getDummySessionWithoutMavenVersion() {
1340         return getDummySession(new Properties());
1341     }
1342 
1343     private MavenSession getDummySession(Properties systemProperties) {
1344         MavenSession session = mock(MavenSession.class);
1345         when(session.getSystemProperties()).thenReturn(systemProperties);
1346         return session;
1347     }
1348 
1349     private Set<Artifact> getArtifacts(Artifact... artifacts) {
1350         Set<Artifact> result = new TreeSet<>(new ArtifactComparator());
1351         result.addAll(Arrays.asList(artifacts));
1352         return result;
1353     }
1354 
1355     public Manifest getJarFileManifest(File jarFile) throws IOException {
1356         try (JarFile jar = new JarFile(jarFile)) {
1357             return jar.getManifest();
1358         }
1359     }
1360 
1361     @Test
1362     void testParseOutputTimestamp() {
1363         assertThat(parseBuildOutputTimestamp(null)).isEmpty();
1364         assertThat(parseBuildOutputTimestamp("")).isEmpty();
1365         assertThat(parseBuildOutputTimestamp(".")).isEmpty();
1366         assertThat(parseBuildOutputTimestamp(" ")).isEmpty();
1367         assertThat(parseBuildOutputTimestamp("_")).isEmpty();
1368         assertThat(parseBuildOutputTimestamp("-")).isEmpty();
1369         assertThat(parseBuildOutputTimestamp("/")).isEmpty();
1370         assertThat(parseBuildOutputTimestamp("!")).isEmpty();
1371         assertThat(parseBuildOutputTimestamp("*")).isEmpty();
1372 
1373         assertThat(parseBuildOutputTimestamp("1570300662").get().getEpochSecond())
1374                 .isEqualTo(1570300662L);
1375         assertThat(parseBuildOutputTimestamp("2019-10-05T18:37:42Z").get().getEpochSecond())
1376                 .isEqualTo(1570300662L);
1377         assertThat(parseBuildOutputTimestamp("2019-10-05T20:37:42+02:00").get().getEpochSecond())
1378                 .isEqualTo(1570300662L);
1379         assertThat(parseBuildOutputTimestamp("2019-10-05T16:37:42-02:00").get().getEpochSecond())
1380                 .isEqualTo(1570300662L);
1381 
1382         // These must result in IAE because we expect extended ISO format only (ie with - separator for date and
1383         // : separator for timezone), hence the XXX SimpleDateFormat for tz offset
1384         // X SimpleDateFormat accepts timezone without separator while date has separator, which is a mix between
1385         // basic (no separators, both for date and timezone) and extended (separator for both)
1386         assertThatExceptionOfType(IllegalArgumentException.class)
1387                 .isThrownBy(() -> parseBuildOutputTimestamp("2019-10-05T20:37:42+0200"));
1388         assertThatExceptionOfType(IllegalArgumentException.class)
1389                 .isThrownBy(() -> parseBuildOutputTimestamp("2019-10-05T20:37:42-0200"));
1390     }
1391 
1392     @ParameterizedTest
1393     @NullAndEmptySource
1394     @ValueSource(strings = {".", " ", "_", "-", "T", "/", "!", "!", "*", "ñ"})
1395     void testEmptyParseOutputTimestampInstant(String value) {
1396         // Empty optional if null or 1 char
1397         assertThat(parseBuildOutputTimestamp(value)).isEmpty();
1398     }
1399 
1400     @ParameterizedTest
1401     @CsvSource({
1402         "1570300662,1570300662",
1403         "2147483648,2147483648",
1404         "2019-10-05T18:37:42Z,1570300662",
1405         "2019-10-05T20:37:42+02:00,1570300662",
1406         "2019-10-05T16:37:42-02:00,1570300662",
1407         "1988-02-22T15:23:47.76598Z,572541827",
1408         "2011-12-03T10:15:30+01:00,1322903730",
1409         "1980-01-01T00:00:02Z,315532802",
1410         "2099-12-31T23:59:59Z,4102444799"
1411     })
1412     void testParseOutputTimestampInstant(String value, long expected) {
1413         assertThat(parseBuildOutputTimestamp(value)).contains(Instant.ofEpochSecond(expected));
1414     }
1415 
1416     @ParameterizedTest
1417     @ValueSource(
1418             strings = {
1419                 "2019-10-05T20:37:42+0200",
1420                 "2019-10-05T20:37:42-0200",
1421                 "2019-10-05T25:00:00Z",
1422                 "2019-10-05",
1423                 "XYZ",
1424                 "Tue, 3 Jun 2008 11:05:30 GMT",
1425                 "2011-12-03T10:15:30+01:00[Europe/Paris]"
1426             })
1427     void testThrownParseOutputTimestampInstant(String outputTimestamp) {
1428         // Invalid parsing
1429         assertThatExceptionOfType(IllegalArgumentException.class)
1430                 .isThrownBy(() -> parseBuildOutputTimestamp(outputTimestamp))
1431                 .withCauseInstanceOf(DateTimeParseException.class);
1432     }
1433 
1434     @ParameterizedTest
1435     @CsvSource({
1436         "2011-12-03T10:15:30+01,1322903730",
1437         "2019-10-05T20:37:42+02,1570300662",
1438         "2011-12-03T10:15:30+06,1322885730",
1439         "1988-02-22T20:37:42+06,572539062"
1440     })
1441     @EnabledForJreRange(min = JRE.JAVA_9)
1442     void testShortOffset(String value, long expected) {
1443         assertThat(parseBuildOutputTimestamp(value)).contains(Instant.ofEpochSecond(expected));
1444     }
1445 
1446     private long testReproducibleJarEntryTime(String name, String timestamp) throws Exception {
1447         File jarFile = new File("target/test/dummy-" + name + ".jar");
1448 
1449         MavenArchiver archiver = getMavenArchiver(getCleanJarArchiver(jarFile));
1450         archiver.configureReproducibleBuild(timestamp);
1451         archiver.createArchive(getDummySession(), getDummyProject(), new MavenArchiveConfiguration());
1452 
1453         assertThat(jarFile).exists();
1454         ZipFile zf = new ZipFile(jarFile);
1455         ZipEntry ze = zf.getEntry("META-INF/MANIFEST.MF");
1456         return ze.getTime();
1457     }
1458 
1459     /**
1460      * before upgrading plexus archiver to 4.10.2 to benefit from https://github.com/codehaus-plexus/plexus-archiver/pull/388
1461      * $ zipdetails target/test/dummy-1970.jar
1462      * gives negative Extended Timestamp in Java, that is seen as some point in time in 2106 (zip spec is unsigned)
1463      * 0027 Extra ID #0001        5455 'UT: Extended Timestamp'
1464      * 0029   Length              0005
1465      * 002B   Flags               '01 mod'
1466      * 002C   Mod Time            FFFFF1FA 'Sun Feb  7 06:28:26 2106'
1467      * @throws Exception
1468      */
1469     @Test
1470     void testReproducibleJar19700101() throws Exception {
1471         long entryTime = testReproducibleJarEntryTime("1970", "10");
1472         assertThat(entryTime).isGreaterThanOrEqualTo(0);
1473     }
1474 
1475     private void checkJar(String name, String timestamp) throws Exception {
1476         File jarFile = new File("target/test/dummy-" + name + ".jar");
1477         JarArchiver jarArchiver = getCleanJarArchiver(jarFile);
1478 
1479         MavenArchiver archiver = getMavenArchiver(jarArchiver);
1480         archiver.configureReproducibleBuild(timestamp);
1481 
1482         MavenSession session = getDummySession();
1483         MavenProject project = getDummyProject();
1484 
1485         archiver.createArchive(session, project, new MavenArchiveConfiguration());
1486         assertThat(jarFile).exists();
1487     }
1488 
1489     @Test
1490     void checkJar() throws Exception {
1491         checkJar("1970", "10");
1492         checkJar("1970-0h0m10", "1970-01-01T00:00:10Z");
1493         checkJar("1970-2h", "1970-01-01T02:00:00Z");
1494         checkJar("1970-1h59", "1970-01-01T01:59:00Z");
1495         checkJar("1970-1h", "1970-01-01T01:00:00Z");
1496         checkJar("1970-0h59", "1970-01-01T00:59:00Z");
1497         checkJar("2000", "2000-01-01T00:00:00Z");
1498     }
1499 
1500     @Test
1501     void testCompress() throws Exception {
1502         File zipFile = new File("target/test/dummy.zip");
1503         ZipArchiveOutputStream zipArchiveOutputStream =
1504                 new ZipArchiveOutputStream(bufferedOutputStream(fileOutputStream(zipFile, "zip")));
1505         zipArchiveOutputStream.setEncoding("UTF-8");
1506         zipArchiveOutputStream.setCreateUnicodeExtraFields(ZipArchiveOutputStream.UnicodeExtraFieldPolicy.NEVER);
1507         zipArchiveOutputStream.setMethod(ZipArchiveOutputStream.DEFLATED);
1508 
1509         ZipArchiveEntry ze = new ZipArchiveEntry("f.txt");
1510         ze.setTime(0);
1511 
1512         zipArchiveOutputStream.putArchiveEntry(ze);
1513         zipArchiveOutputStream.write(1);
1514         zipArchiveOutputStream.closeArchiveEntry();
1515 
1516         zipArchiveOutputStream.close();
1517 
1518         assertTrue(zipFile.delete());
1519     }
1520 }