1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.eclipse.jgit.api;
44
45 import static org.junit.Assert.assertEquals;
46 import static org.junit.Assert.assertNull;
47
48 import java.beans.Statement;
49 import java.io.BufferedInputStream;
50 import java.io.File;
51 import java.io.FileNotFoundException;
52 import java.io.FileOutputStream;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.io.OutputStream;
56 import java.nio.file.Files;
57 import java.util.Arrays;
58 import java.util.Collections;
59 import java.util.HashMap;
60 import java.util.List;
61 import java.util.Map;
62
63 import org.apache.commons.compress.archivers.ArchiveEntry;
64 import org.apache.commons.compress.archivers.ArchiveInputStream;
65 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
66 import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
67 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
68 import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
69 import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
70 import org.eclipse.jgit.api.errors.AbortedByHookException;
71 import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
72 import org.eclipse.jgit.api.errors.GitAPIException;
73 import org.eclipse.jgit.api.errors.NoFilepatternException;
74 import org.eclipse.jgit.api.errors.NoHeadException;
75 import org.eclipse.jgit.api.errors.NoMessageException;
76 import org.eclipse.jgit.api.errors.UnmergedPathsException;
77 import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
78 import org.eclipse.jgit.archive.ArchiveFormats;
79 import org.eclipse.jgit.errors.AmbiguousObjectException;
80 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
81 import org.eclipse.jgit.junit.RepositoryTestCase;
82 import org.eclipse.jgit.lib.FileMode;
83 import org.eclipse.jgit.lib.ObjectId;
84 import org.eclipse.jgit.lib.ObjectLoader;
85 import org.eclipse.jgit.revwalk.RevCommit;
86 import org.eclipse.jgit.util.IO;
87 import org.eclipse.jgit.util.StringUtils;
88 import org.junit.After;
89 import org.junit.Before;
90 import org.junit.Test;
91
92 public class ArchiveCommandTest extends RepositoryTestCase {
93
94
95 private static final int WAIT = 2000;
96 private static final String UNEXPECTED_ARCHIVE_SIZE = "Unexpected archive size";
97 private static final String UNEXPECTED_FILE_CONTENTS = "Unexpected file contents";
98 private static final String UNEXPECTED_TREE_CONTENTS = "Unexpected tree contents";
99 private static final String UNEXPECTED_LAST_MODIFIED =
100 "Unexpected lastModified mocked by MockSystemReader, truncated to 1 second";
101 private static final String UNEXPECTED_DIFFERENT_HASH = "Unexpected different hash";
102
103 private MockFormat format = null;
104
105 @Before
106 public void setup() {
107 format = new MockFormat();
108 ArchiveCommand.registerFormat(format.SUFFIXES.get(0), format);
109 ArchiveFormats.registerAll();
110 }
111
112 @Override
113 @After
114 public void tearDown() {
115 ArchiveCommand.unregisterFormat(format.SUFFIXES.get(0));
116 ArchiveFormats.unregisterAll();
117 }
118
119 @Test
120 public void archiveHeadAllFiles() throws IOException, GitAPIException {
121 try (Git git = new Git(db)) {
122 createTestContent(git);
123
124 git.archive().setOutputStream(new MockOutputStream())
125 .setFormat(format.SUFFIXES.get(0))
126 .setTree(git.getRepository().resolve("HEAD")).call();
127
128 assertEquals(UNEXPECTED_ARCHIVE_SIZE, 2, format.size());
129 assertEquals(UNEXPECTED_FILE_CONTENTS, "content_1_2", format.getByPath("file_1.txt"));
130 assertEquals(UNEXPECTED_FILE_CONTENTS, "content_2_2", format.getByPath("file_2.txt"));
131 }
132 }
133
134 @Test
135 public void archiveHeadSpecificPath() throws IOException, GitAPIException {
136 try (Git git = new Git(db)) {
137 writeTrashFile("file_1.txt", "content_1_1");
138 git.add().addFilepattern("file_1.txt").call();
139 git.commit().setMessage("create file").call();
140
141 writeTrashFile("file_1.txt", "content_1_2");
142 String expectedFilePath = "some_directory/file_2.txt";
143 writeTrashFile(expectedFilePath, "content_2_2");
144 git.add().addFilepattern(".").call();
145 git.commit().setMessage("updated file").call();
146
147 git.archive().setOutputStream(new MockOutputStream())
148 .setFormat(format.SUFFIXES.get(0))
149 .setTree(git.getRepository().resolve("HEAD"))
150 .setPaths(expectedFilePath).call();
151
152 assertEquals(UNEXPECTED_ARCHIVE_SIZE, 2, format.size());
153 assertEquals(UNEXPECTED_FILE_CONTENTS, "content_2_2", format.getByPath(expectedFilePath));
154 assertNull(UNEXPECTED_TREE_CONTENTS, format.getByPath("some_directory"));
155 }
156 }
157
158 @Test
159 public void archiveByIdSpecificFile() throws IOException, GitAPIException {
160 try (Git git = new Git(db)) {
161 writeTrashFile("file_1.txt", "content_1_1");
162 git.add().addFilepattern("file_1.txt").call();
163 RevCommit first = git.commit().setMessage("create file").call();
164
165 writeTrashFile("file_1.txt", "content_1_2");
166 String expectedFilePath = "some_directory/file_2.txt";
167 writeTrashFile(expectedFilePath, "content_2_2");
168 git.add().addFilepattern(".").call();
169 git.commit().setMessage("updated file").call();
170
171 Map<String, Object> options = new HashMap<>();
172 Integer opt = Integer.valueOf(42);
173 options.put("foo", opt);
174 MockOutputStream out = new MockOutputStream();
175 git.archive().setOutputStream(out)
176 .setFormat(format.SUFFIXES.get(0))
177 .setFormatOptions(options)
178 .setTree(first)
179 .setPaths("file_1.txt").call();
180
181 assertEquals(opt.intValue(), out.getFoo());
182 assertEquals(UNEXPECTED_ARCHIVE_SIZE, 1, format.size());
183 assertEquals(UNEXPECTED_FILE_CONTENTS, "content_1_1", format.getByPath("file_1.txt"));
184 }
185 }
186
187 @Test
188 public void archiveByDirectoryPath() throws GitAPIException, IOException {
189 try (Git git = new Git(db)) {
190 writeTrashFile("file_0.txt", "content_0_1");
191 git.add().addFilepattern("file_0.txt").call();
192 git.commit().setMessage("commit_1").call();
193
194 writeTrashFile("file_0.txt", "content_0_2");
195 String expectedFilePath1 = "some_directory/file_1.txt";
196 writeTrashFile(expectedFilePath1, "content_1_2");
197 String expectedFilePath2 = "some_directory/file_2.txt";
198 writeTrashFile(expectedFilePath2, "content_2_2");
199 String expectedFilePath3 = "some_directory/nested_directory/file_3.txt";
200 writeTrashFile(expectedFilePath3, "content_3_2");
201 git.add().addFilepattern(".").call();
202 git.commit().setMessage("commit_2").call();
203 git.archive().setOutputStream(new MockOutputStream())
204 .setFormat(format.SUFFIXES.get(0))
205 .setTree(git.getRepository().resolve("HEAD"))
206 .setPaths("some_directory/").call();
207
208 assertEquals(UNEXPECTED_ARCHIVE_SIZE, 5, format.size());
209 assertEquals(UNEXPECTED_FILE_CONTENTS, "content_1_2", format.getByPath(expectedFilePath1));
210 assertEquals(UNEXPECTED_FILE_CONTENTS, "content_2_2", format.getByPath(expectedFilePath2));
211 assertEquals(UNEXPECTED_FILE_CONTENTS, "content_3_2", format.getByPath(expectedFilePath3));
212 assertNull(UNEXPECTED_TREE_CONTENTS, format.getByPath("some_directory"));
213 assertNull(UNEXPECTED_TREE_CONTENTS, format.getByPath("some_directory/nested_directory"));
214 }
215 }
216
217 @Test
218 public void archiveHeadAllFilesTarTimestamps() throws Exception {
219 try (Git git = new Git(db)) {
220 createTestContent(git);
221 String fmt = "tar";
222 File archive = new File(getTemporaryDirectory(),
223 "archive." + format);
224 archive(git, archive, fmt);
225 ObjectId hash1 = ObjectId.fromRaw(IO.readFully(archive));
226
227 try (InputStream fi = Files.newInputStream(archive.toPath());
228 InputStream bi = new BufferedInputStream(fi);
229 ArchiveInputStream o = new TarArchiveInputStream(bi)) {
230 assertEntries(o);
231 }
232
233 Thread.sleep(WAIT);
234 archive(git, archive, fmt);
235 assertEquals(UNEXPECTED_DIFFERENT_HASH, hash1,
236 ObjectId.fromRaw(IO.readFully(archive)));
237 }
238 }
239
240 @Test
241 public void archiveHeadAllFilesTgzTimestamps() throws Exception {
242 try (Git git = new Git(db)) {
243 createTestContent(git);
244 String fmt = "tgz";
245 File archive = new File(getTemporaryDirectory(),
246 "archive." + fmt);
247 archive(git, archive, fmt);
248 ObjectId hash1 = ObjectId.fromRaw(IO.readFully(archive));
249
250 try (InputStream fi = Files.newInputStream(archive.toPath());
251 InputStream bi = new BufferedInputStream(fi);
252 InputStream gzi = new GzipCompressorInputStream(bi);
253 ArchiveInputStream o = new TarArchiveInputStream(gzi)) {
254 assertEntries(o);
255 }
256
257 Thread.sleep(WAIT);
258 archive(git, archive, fmt);
259 assertEquals(UNEXPECTED_DIFFERENT_HASH, hash1,
260 ObjectId.fromRaw(IO.readFully(archive)));
261 }
262 }
263
264 @Test
265 public void archiveHeadAllFilesTbz2Timestamps() throws Exception {
266 try (Git git = new Git(db)) {
267 createTestContent(git);
268 String fmt = "tbz2";
269 File archive = new File(getTemporaryDirectory(),
270 "archive." + fmt);
271 archive(git, archive, fmt);
272 ObjectId hash1 = ObjectId.fromRaw(IO.readFully(archive));
273
274 try (InputStream fi = Files.newInputStream(archive.toPath());
275 InputStream bi = new BufferedInputStream(fi);
276 InputStream gzi = new BZip2CompressorInputStream(bi);
277 ArchiveInputStream o = new TarArchiveInputStream(gzi)) {
278 assertEntries(o);
279 }
280
281 Thread.sleep(WAIT);
282 archive(git, archive, fmt);
283 assertEquals(UNEXPECTED_DIFFERENT_HASH, hash1,
284 ObjectId.fromRaw(IO.readFully(archive)));
285 }
286 }
287
288 @Test
289 public void archiveHeadAllFilesTxzTimestamps() throws Exception {
290 try (Git git = new Git(db)) {
291 createTestContent(git);
292 String fmt = "txz";
293 File archive = new File(getTemporaryDirectory(), "archive." + fmt);
294 archive(git, archive, fmt);
295 ObjectId hash1 = ObjectId.fromRaw(IO.readFully(archive));
296
297 try (InputStream fi = Files.newInputStream(archive.toPath());
298 InputStream bi = new BufferedInputStream(fi);
299 InputStream gzi = new XZCompressorInputStream(bi);
300 ArchiveInputStream o = new TarArchiveInputStream(gzi)) {
301 assertEntries(o);
302 }
303
304 Thread.sleep(WAIT);
305 archive(git, archive, fmt);
306 assertEquals(UNEXPECTED_DIFFERENT_HASH, hash1,
307 ObjectId.fromRaw(IO.readFully(archive)));
308 }
309 }
310
311 @Test
312 public void archiveHeadAllFilesZipTimestamps() throws Exception {
313 try (Git git = new Git(db)) {
314 createTestContent(git);
315 String fmt = "zip";
316 File archive = new File(getTemporaryDirectory(), "archive." + fmt);
317 archive(git, archive, fmt);
318 ObjectId hash1 = ObjectId.fromRaw(IO.readFully(archive));
319
320 try (InputStream fi = Files.newInputStream(archive.toPath());
321 InputStream bi = new BufferedInputStream(fi);
322 ArchiveInputStream o = new ZipArchiveInputStream(bi)) {
323 assertEntries(o);
324 }
325
326 Thread.sleep(WAIT);
327 archive(git, archive, fmt);
328 assertEquals(UNEXPECTED_DIFFERENT_HASH, hash1,
329 ObjectId.fromRaw(IO.readFully(archive)));
330 }
331 }
332
333 private void createTestContent(Git git) throws IOException, GitAPIException,
334 NoFilepatternException, NoHeadException, NoMessageException,
335 UnmergedPathsException, ConcurrentRefUpdateException,
336 WrongRepositoryStateException, AbortedByHookException {
337 writeTrashFile("file_1.txt", "content_1_1");
338 git.add().addFilepattern("file_1.txt").call();
339 git.commit().setMessage("create file").call();
340
341 writeTrashFile("file_1.txt", "content_1_2");
342 writeTrashFile("file_2.txt", "content_2_2");
343 git.add().addFilepattern(".").call();
344 git.commit().setMessage("updated file").call();
345 }
346
347 private static void archive(Git git, File archive, String fmt)
348 throws GitAPIException,
349 FileNotFoundException, AmbiguousObjectException,
350 IncorrectObjectTypeException, IOException {
351 git.archive().setOutputStream(new FileOutputStream(archive))
352 .setFormat(fmt)
353 .setTree(git.getRepository().resolve("HEAD")).call();
354 }
355
356 private static void assertEntries(ArchiveInputStream o) throws IOException {
357 ArchiveEntry e;
358 int n = 0;
359 while ((e = o.getNextEntry()) != null) {
360 n++;
361 assertEquals(UNEXPECTED_LAST_MODIFIED,
362 (1250379778668L / 1000L) * 1000L,
363 e.getLastModifiedDate().getTime());
364 }
365 assertEquals(UNEXPECTED_ARCHIVE_SIZE, 2, n);
366 }
367
368 private static class MockFormat
369 implements ArchiveCommand.Format<MockOutputStream> {
370
371 private Map<String, String> entries = new HashMap<>();
372
373 private int size() {
374 return entries.size();
375 }
376
377 private String getByPath(String path) {
378 return entries.get(path);
379 }
380
381 private final List<String> SUFFIXES = Collections
382 .unmodifiableList(Arrays.asList(".mck"));
383
384 @Override
385 public MockOutputStream createArchiveOutputStream(OutputStream s)
386 throws IOException {
387 return createArchiveOutputStream(s,
388 Collections.<String, Object> emptyMap());
389 }
390
391 @Override
392 public MockOutputStream createArchiveOutputStream(OutputStream s,
393 Map<String, Object> o) throws IOException {
394 for (Map.Entry<String, Object> p : o.entrySet()) {
395 try {
396 String methodName = "set"
397 + StringUtils.capitalize(p.getKey());
398 new Statement(s, methodName, new Object[] { p.getValue() })
399 .execute();
400 } catch (Exception e) {
401 throw new IOException("cannot set option: " + p.getKey(), e);
402 }
403 }
404 return new MockOutputStream();
405 }
406
407 @Override
408 public void putEntry(MockOutputStream out, ObjectId tree, String path, FileMode mode, ObjectLoader loader) {
409 String content = mode != FileMode.TREE ? new String(loader.getBytes()) : null;
410 entries.put(path, content);
411 }
412
413 @Override
414 public Iterable<String> suffixes() {
415 return SUFFIXES;
416 }
417 }
418
419 public static class MockOutputStream extends OutputStream {
420
421 private int foo;
422
423 public void setFoo(int foo) {
424 this.foo = foo;
425 }
426
427 public int getFoo() {
428 return foo;
429 }
430
431 @Override
432 public void write(int b) throws IOException {
433
434 }
435 }
436 }