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.wrapper;
20  
21  import java.io.BufferedWriter;
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.OutputStream;
25  import java.net.URI;
26  import java.net.URISyntaxException;
27  import java.net.URL;
28  import java.nio.charset.StandardCharsets;
29  import java.nio.file.FileVisitResult;
30  import java.nio.file.Files;
31  import java.nio.file.Path;
32  import java.nio.file.Paths;
33  import java.nio.file.SimpleFileVisitor;
34  import java.nio.file.attribute.BasicFileAttributes;
35  import java.nio.file.attribute.FileTime;
36  import java.util.jar.JarOutputStream;
37  import java.util.jar.Manifest;
38  import java.util.zip.ZipEntry;
39  import java.util.zip.ZipException;
40  import java.util.zip.ZipOutputStream;
41  
42  import org.junit.jupiter.api.BeforeEach;
43  import org.junit.jupiter.api.Test;
44  import org.junit.jupiter.api.io.TempDir;
45  
46  import static org.junit.jupiter.api.Assertions.assertEquals;
47  import static org.junit.jupiter.api.Assertions.assertFalse;
48  import static org.junit.jupiter.api.Assertions.assertTrue;
49  import static org.junit.jupiter.api.Assertions.fail;
50  import static org.mockito.Mockito.mock;
51  import static org.mockito.Mockito.when;
52  
53  /**
54   * @author Hans Dockter
55   */
56  public class InstallerTest {
57      @TempDir
58      private File tempFolder;
59  
60      private Path testDir;
61  
62      private Installer install;
63  
64      private Path distributionDir;
65  
66      private Path zipStore;
67  
68      private Path mavenHomeDir;
69  
70      private Path zipDestination;
71  
72      private WrapperConfiguration configuration = new WrapperConfiguration();
73  
74      private Downloader download;
75  
76      private Verifier verifier;
77  
78      private PathAssembler pathAssembler;
79  
80      private PathAssembler.LocalDistribution localDistribution;
81  
82      @BeforeEach
83      void setup() throws Exception {
84          testDir = tempFolder.toPath();
85  
86          configuration.setZipBase(PathAssembler.PROJECT_STRING);
87          configuration.setZipPath(Paths.get("someZipPath"));
88          configuration.setDistributionBase(PathAssembler.MAVEN_USER_HOME_STRING);
89          configuration.setDistributionPath(Paths.get("someDistPath"));
90          configuration.setDistribution(new URI("http://server/maven-0.9.zip"));
91          configuration.setAlwaysDownload(false);
92          configuration.setAlwaysUnpack(false);
93          configuration.setDistributionSha256Sum("");
94          distributionDir = testDir.resolve("someDistPath");
95          mavenHomeDir = distributionDir.resolve("maven-0.9");
96          zipStore = testDir.resolve("zips");
97          zipDestination = zipStore.resolve("maven-0.9.zip");
98  
99          download = mock(Downloader.class);
100         verifier = mock(Verifier.class);
101         pathAssembler = mock(PathAssembler.class);
102         localDistribution = mock(PathAssembler.LocalDistribution.class);
103 
104         when(localDistribution.getZipFile()).thenReturn(zipDestination);
105         when(localDistribution.getDistributionDir()).thenReturn(distributionDir);
106         when(pathAssembler.getDistribution(configuration)).thenReturn(localDistribution);
107 
108         install = new Installer(download, verifier, pathAssembler);
109     }
110 
111     private void createTestZip(Path zipDestination) throws Exception {
112         Path explodedZipDir = testDir.resolve("explodedZip");
113         Files.createDirectories(explodedZipDir);
114         Files.createDirectories(zipDestination.getParent());
115         Path mavenScript = explodedZipDir.resolve("maven-0.9/bin/mvn");
116         Path mavenLib = explodedZipDir.resolve("maven-0.9/lib/maven-core-0.9.jar");
117         Files.createDirectories(mavenScript.getParent());
118         Files.createDirectories(mavenLib.getParent());
119         try (BufferedWriter writer = Files.newBufferedWriter(mavenScript, StandardCharsets.UTF_8)) {
120             writer.write("something");
121         }
122         try (OutputStream os = Files.newOutputStream(mavenLib);
123                 JarOutputStream jar = new JarOutputStream(os, new Manifest())) {
124             jar.putNextEntry(new ZipEntry("test"));
125             jar.closeEntry();
126         }
127 
128         zipTo(explodedZipDir, zipDestination);
129     }
130 
131     public void testCreateDist() throws Exception {
132         Path homeDir = install.createDist(configuration);
133 
134         assertEquals(mavenHomeDir, homeDir);
135         assertTrue(Files.isDirectory(homeDir));
136         assertTrue(Files.exists(homeDir.resolve("bin/mvn")));
137         assertTrue(Files.exists(zipDestination));
138 
139         assertEquals(localDistribution, pathAssembler.getDistribution(configuration));
140         assertEquals(distributionDir, localDistribution.getDistributionDir());
141         assertEquals(zipDestination, localDistribution.getZipFile());
142 
143         // download.download(new URI("http://some/test"), distributionDir);
144         // verify(download).download(new URI("http://some/test"), distributionDir);
145     }
146 
147     @Test
148     void testCreateDistWithExistingDistribution() throws Exception {
149 
150         createTestZip(zipDestination);
151         Files.createDirectories(mavenHomeDir);
152         Path someFile = mavenHomeDir.resolve("some-file");
153         touchFile(someFile);
154 
155         Path homeDir = install.createDist(configuration);
156 
157         assertEquals(mavenHomeDir, homeDir);
158         assertTrue(Files.isDirectory(mavenHomeDir));
159         assertTrue(Files.exists(homeDir.resolve("some-file")));
160         assertTrue(Files.exists(zipDestination));
161 
162         assertEquals(localDistribution, pathAssembler.getDistribution(configuration));
163         assertEquals(distributionDir, localDistribution.getDistributionDir());
164         assertEquals(zipDestination, localDistribution.getZipFile());
165     }
166 
167     @Test
168     void testCreateDistWithExistingDistAndZipAndAlwaysUnpackTrue() throws Exception {
169 
170         createTestZip(zipDestination);
171         Files.createDirectories(mavenHomeDir);
172         Path garbage = mavenHomeDir.resolve("garbage");
173         touchFile(garbage);
174         configuration.setAlwaysUnpack(true);
175 
176         Path homeDir = install.createDist(configuration);
177 
178         assertEquals(mavenHomeDir, homeDir);
179         assertTrue(Files.isDirectory(mavenHomeDir));
180         assertFalse(Files.exists(homeDir.resolve("garbage")));
181         assertTrue(Files.exists(zipDestination));
182 
183         assertEquals(localDistribution, pathAssembler.getDistribution(configuration));
184         assertEquals(distributionDir, localDistribution.getDistributionDir());
185         assertEquals(zipDestination, localDistribution.getZipFile());
186     }
187 
188     @Test
189     void testCreateDistWithExistingZipAndDistAndAlwaysDownloadTrue() throws Exception {
190 
191         createTestZip(zipDestination);
192         Path garbage = mavenHomeDir.resolve("garbage");
193         touchFile(garbage);
194         configuration.setAlwaysUnpack(true);
195 
196         Path homeDir = install.createDist(configuration);
197 
198         assertEquals(mavenHomeDir, homeDir);
199         assertTrue(Files.isDirectory(mavenHomeDir));
200         assertTrue(Files.exists(homeDir.resolve("bin/mvn")));
201         assertFalse(Files.exists(homeDir.resolve("garbage")));
202         assertTrue(Files.exists(zipDestination));
203 
204         assertEquals(localDistribution, pathAssembler.getDistribution(configuration));
205         assertEquals(distributionDir, localDistribution.getDistributionDir());
206         assertEquals(zipDestination, localDistribution.getZipFile());
207 
208         // download.download(new URI("http://some/test"), distributionDir);
209         // verify(download).download(new URI("http://some/test"), distributionDir);
210     }
211 
212     @Test
213     void testZipSlip() throws URISyntaxException {
214         URL resource = getClass().getClassLoader().getResource("zip-slip.zip");
215         Path zipSlip = Paths.get(resource.toURI());
216         when(localDistribution.getZipFile()).thenReturn(zipSlip);
217         configuration.setAlwaysUnpack(true);
218 
219         try {
220             install.createDist(configuration);
221             fail("Should fail as it contains a zip slip.");
222         } catch (Exception ex) {
223             assertTrue(ex instanceof ZipException);
224         }
225     }
226 
227     public void zipTo(final Path directoryToZip, final Path zipFile) throws IOException {
228         // Creating a ZipOutputStream by wrapping a OutputStream
229         try (OutputStream fos = Files.newOutputStream(zipFile);
230                 ZipOutputStream zos = new ZipOutputStream(fos)) {
231             // Walk the tree structure using WalkFileTree method
232             Files.walkFileTree(directoryToZip, new SimpleFileVisitor<Path>() {
233                 @Override
234                 // Before visiting the directory create the directory in zip archive
235                 public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs)
236                         throws IOException {
237                     // Don't create dir for root folder as it is already created with .zip name
238                     if (!directoryToZip.equals(dir)) {
239                         zos.putNextEntry(
240                                 new ZipEntry(directoryToZip.relativize(dir).toString() + "/"));
241                         zos.closeEntry();
242                     }
243                     return FileVisitResult.CONTINUE;
244                 }
245 
246                 @Override
247                 // For each visited file add it to zip entry
248                 public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
249                     zos.putNextEntry(
250                             new ZipEntry(directoryToZip.relativize(file).toString()));
251                     Files.copy(file, zos);
252                     zos.closeEntry();
253                     return FileVisitResult.CONTINUE;
254                 }
255             });
256         }
257     }
258 
259     private void touchFile(Path file) throws IOException {
260         if (Files.notExists(file)) {
261             Files.createDirectories(file.getParent());
262             Files.createFile(file);
263         }
264         Files.setLastModifiedTime(file, FileTime.fromMillis(System.currentTimeMillis()));
265     }
266 }