You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ArchiveTest.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /*
  2. * Copyright (C) 2012 Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.pgm;
  44. import static org.junit.Assert.assertArrayEquals;
  45. import static org.junit.Assert.fail;
  46. import static org.junit.Assume.assumeNoException;
  47. import java.io.BufferedReader;
  48. import java.io.ByteArrayInputStream;
  49. import java.io.File;
  50. import java.io.FileOutputStream;
  51. import java.io.InputStreamReader;
  52. import java.io.IOException;
  53. import java.io.OutputStream;
  54. import java.lang.Object;
  55. import java.lang.String;
  56. import java.util.ArrayList;
  57. import java.util.Arrays;
  58. import java.util.List;
  59. import java.util.concurrent.Callable;
  60. import java.util.concurrent.Executors;
  61. import java.util.concurrent.ExecutorService;
  62. import java.util.concurrent.Future;
  63. import java.util.zip.ZipEntry;
  64. import java.util.zip.ZipInputStream;
  65. import org.eclipse.jgit.api.Git;
  66. import org.eclipse.jgit.dircache.DirCache;
  67. import org.eclipse.jgit.lib.CLIRepositoryTestCase;
  68. import org.eclipse.jgit.lib.FileMode;
  69. import org.eclipse.jgit.pgm.CLIGitCommand;
  70. import org.junit.Before;
  71. import org.junit.Ignore;
  72. import org.junit.Test;
  73. public class ArchiveTest extends CLIRepositoryTestCase {
  74. private Git git;
  75. private String emptyTree;
  76. @Override
  77. @Before
  78. public void setUp() throws Exception {
  79. super.setUp();
  80. git = new Git(db);
  81. git.commit().setMessage("initial commit").call();
  82. emptyTree = db.resolve("HEAD^{tree}").abbreviate(12).name();
  83. }
  84. @Ignore("Some versions of java.util.zip refuse to write an empty ZIP")
  85. @Test
  86. public void testEmptyArchive() throws Exception {
  87. final byte[] result = CLIGitCommand.rawExecute( //
  88. "git archive " + emptyTree, db);
  89. assertArrayEquals(new String[0], listZipEntries(result));
  90. }
  91. @Test
  92. public void testEmptyTar() throws Exception {
  93. final byte[] result = CLIGitCommand.rawExecute( //
  94. "git archive --format=tar " + emptyTree, db);
  95. assertArrayEquals(new String[0], listTarEntries(result));
  96. }
  97. @Test
  98. public void testUnrecognizedFormat() throws Exception {
  99. final String[] expect = new String[] { "fatal: Unknown archive format 'nonsense'" };
  100. final String[] actual = execute("git archive --format=nonsense " + emptyTree);
  101. assertArrayEquals(expect, actual);
  102. }
  103. @Test
  104. public void testArchiveWithFiles() throws Exception {
  105. writeTrashFile("a", "a file with content!");
  106. writeTrashFile("c", ""); // empty file
  107. writeTrashFile("unrelated", "another file, just for kicks");
  108. git.add().addFilepattern("a").call();
  109. git.add().addFilepattern("c").call();
  110. git.commit().setMessage("populate toplevel").call();
  111. final byte[] result = CLIGitCommand.rawExecute( //
  112. "git archive HEAD", db);
  113. assertArrayEquals(new String[] { "a", "c" }, //
  114. listZipEntries(result));
  115. }
  116. @Test
  117. public void testArchiveWithSubdir() throws Exception {
  118. writeTrashFile("a", "a file with content!");
  119. writeTrashFile("b.c", "before subdir in git sort order");
  120. writeTrashFile("b0c", "after subdir in git sort order");
  121. writeTrashFile("c", "");
  122. git.add().addFilepattern("a").call();
  123. git.add().addFilepattern("b.c").call();
  124. git.add().addFilepattern("b0c").call();
  125. git.add().addFilepattern("c").call();
  126. git.commit().setMessage("populate toplevel").call();
  127. writeTrashFile("b/b", "file in subdirectory");
  128. writeTrashFile("b/a", "another file in subdirectory");
  129. git.add().addFilepattern("b").call();
  130. git.commit().setMessage("add subdir").call();
  131. final byte[] result = CLIGitCommand.rawExecute( //
  132. "git archive master", db);
  133. String[] expect = { "a", "b.c", "b0c", "b/a", "b/b", "c" };
  134. String[] actual = listZipEntries(result);
  135. Arrays.sort(expect);
  136. Arrays.sort(actual);
  137. assertArrayEquals(expect, actual);
  138. }
  139. @Test
  140. public void testTarWithSubdir() throws Exception {
  141. writeTrashFile("a", "a file with content!");
  142. writeTrashFile("b.c", "before subdir in git sort order");
  143. writeTrashFile("b0c", "after subdir in git sort order");
  144. writeTrashFile("c", "");
  145. git.add().addFilepattern("a").call();
  146. git.add().addFilepattern("b.c").call();
  147. git.add().addFilepattern("b0c").call();
  148. git.add().addFilepattern("c").call();
  149. git.commit().setMessage("populate toplevel").call();
  150. writeTrashFile("b/b", "file in subdirectory");
  151. writeTrashFile("b/a", "another file in subdirectory");
  152. git.add().addFilepattern("b").call();
  153. git.commit().setMessage("add subdir").call();
  154. final byte[] result = CLIGitCommand.rawExecute( //
  155. "git archive --format=tar master", db);
  156. String[] expect = { "a", "b.c", "b0c", "b/a", "b/b", "c" };
  157. String[] actual = listTarEntries(result);
  158. Arrays.sort(expect);
  159. Arrays.sort(actual);
  160. assertArrayEquals(expect, actual);
  161. }
  162. @Test
  163. public void testArchivePreservesMode() throws Exception {
  164. writeTrashFile("plain", "a file with content");
  165. writeTrashFile("executable", "an executable file");
  166. writeTrashFile("symlink", "plain");
  167. git.add().addFilepattern("plain").call();
  168. git.add().addFilepattern("executable").call();
  169. git.add().addFilepattern("symlink").call();
  170. DirCache cache = db.lockDirCache();
  171. cache.getEntry("executable").setFileMode(FileMode.EXECUTABLE_FILE);
  172. cache.getEntry("symlink").setFileMode(FileMode.SYMLINK);
  173. cache.write();
  174. cache.commit();
  175. cache.unlock();
  176. git.commit().setMessage("three files with different modes").call();
  177. final byte[] zipData = CLIGitCommand.rawExecute( //
  178. "git archive master", db);
  179. writeRaw("zip-with-modes.zip", zipData);
  180. assertContainsEntryWithMode("zip-with-modes.zip", "-rw-", "plain");
  181. assertContainsEntryWithMode("zip-with-modes.zip", "-rwx", "executable");
  182. assertContainsEntryWithMode("zip-with-modes.zip", "l", "symlink");
  183. }
  184. @Test
  185. public void testTarPreservesMode() throws Exception {
  186. writeTrashFile("plain", "a file with content");
  187. writeTrashFile("executable", "an executable file");
  188. writeTrashFile("symlink", "plain");
  189. git.add().addFilepattern("plain").call();
  190. git.add().addFilepattern("executable").call();
  191. git.add().addFilepattern("symlink").call();
  192. DirCache cache = db.lockDirCache();
  193. cache.getEntry("executable").setFileMode(FileMode.EXECUTABLE_FILE);
  194. cache.getEntry("symlink").setFileMode(FileMode.SYMLINK);
  195. cache.write();
  196. cache.commit();
  197. cache.unlock();
  198. git.commit().setMessage("three files with different modes").call();
  199. final byte[] archive = CLIGitCommand.rawExecute( //
  200. "git archive --format=tar master", db);
  201. writeRaw("with-modes.tar", archive);
  202. assertTarContainsEntry("with-modes.tar", "-rw-r--r--", "plain");
  203. assertTarContainsEntry("with-modes.tar", "-rwxr-xr-x", "executable");
  204. assertTarContainsEntry("with-modes.tar", "l", "symlink -> plain");
  205. }
  206. @Test
  207. public void testArchivePreservesContent() throws Exception {
  208. final String payload = "“The quick brown fox jumps over the lazy dog!”";
  209. writeTrashFile("xyzzy", payload);
  210. git.add().addFilepattern("xyzzy").call();
  211. git.commit().setMessage("add file with content").call();
  212. final byte[] result = CLIGitCommand.rawExecute( //
  213. "git archive HEAD", db);
  214. assertArrayEquals(new String[] { payload }, //
  215. zipEntryContent(result, "xyzzy"));
  216. }
  217. @Test
  218. public void testTarPreservesContent() throws Exception {
  219. final String payload = "“The quick brown fox jumps over the lazy dog!”";
  220. writeTrashFile("xyzzy", payload);
  221. git.add().addFilepattern("xyzzy").call();
  222. git.commit().setMessage("add file with content").call();
  223. final byte[] result = CLIGitCommand.rawExecute( //
  224. "git archive --format=tar HEAD", db);
  225. assertArrayEquals(new String[] { payload }, //
  226. tarEntryContent(result, "xyzzy"));
  227. }
  228. private Process spawnAssumingCommandPresent(String... cmdline) {
  229. final File cwd = db.getWorkTree();
  230. final ProcessBuilder procBuilder = new ProcessBuilder(cmdline) //
  231. .directory(cwd) //
  232. .redirectErrorStream(true);
  233. Process proc = null;
  234. try {
  235. proc = procBuilder.start();
  236. } catch (IOException e) {
  237. // On machines without `cmdline[0]`, let the test pass.
  238. assumeNoException(e);
  239. }
  240. return proc;
  241. }
  242. private BufferedReader readFromProcess(Process proc) throws Exception {
  243. return new BufferedReader( //
  244. new InputStreamReader(proc.getInputStream(), "UTF-8"));
  245. }
  246. private void grepForEntry(String name, String mode, String... cmdline) //
  247. throws Exception {
  248. final Process proc = spawnAssumingCommandPresent(cmdline);
  249. proc.getOutputStream().close();
  250. final BufferedReader reader = readFromProcess(proc);
  251. try {
  252. String line;
  253. while ((line = reader.readLine()) != null)
  254. if (line.startsWith(mode) && line.endsWith(name))
  255. // found it!
  256. return;
  257. fail("expected entry " + name + " with mode " + mode + " but found none");
  258. } finally {
  259. proc.getOutputStream().close();
  260. proc.destroy();
  261. }
  262. }
  263. private void assertContainsEntryWithMode(String zipFilename, String mode, String name) //
  264. throws Exception {
  265. grepForEntry(name, mode, "zipinfo", zipFilename);
  266. }
  267. private void assertTarContainsEntry(String tarfile, String mode, String name) //
  268. throws Exception {
  269. grepForEntry(name, mode, "tar", "tvf", tarfile);
  270. }
  271. private void writeRaw(String filename, byte[] data) //
  272. throws IOException {
  273. final File path = new File(db.getWorkTree(), filename);
  274. final OutputStream out = new FileOutputStream(path);
  275. try {
  276. out.write(data);
  277. } finally {
  278. out.close();
  279. }
  280. }
  281. private static String[] listZipEntries(byte[] zipData) throws IOException {
  282. final List<String> l = new ArrayList<String>();
  283. final ZipInputStream in = new ZipInputStream( //
  284. new ByteArrayInputStream(zipData));
  285. ZipEntry e;
  286. while ((e = in.getNextEntry()) != null)
  287. l.add(e.getName());
  288. in.close();
  289. return l.toArray(new String[l.size()]);
  290. }
  291. private static Future<Object> writeAsync(final OutputStream stream, final byte[] data) {
  292. final ExecutorService executor = Executors.newSingleThreadExecutor();
  293. return executor.submit(new Callable<Object>() { //
  294. public Object call() throws IOException {
  295. try {
  296. stream.write(data);
  297. return null;
  298. } finally {
  299. stream.close();
  300. }
  301. }
  302. });
  303. }
  304. private String[] listTarEntries(byte[] tarData) throws Exception {
  305. final List<String> l = new ArrayList<String>();
  306. final Process proc = spawnAssumingCommandPresent("tar", "tf", "-");
  307. final BufferedReader reader = readFromProcess(proc);
  308. final OutputStream out = proc.getOutputStream();
  309. // Dump tarball to tar stdin in background
  310. final Future<?> writing = writeAsync(out, tarData);
  311. try {
  312. String line;
  313. while ((line = reader.readLine()) != null)
  314. l.add(line);
  315. return l.toArray(new String[l.size()]);
  316. } finally {
  317. writing.get();
  318. reader.close();
  319. proc.destroy();
  320. }
  321. }
  322. private static String[] zipEntryContent(byte[] zipData, String path) //
  323. throws IOException {
  324. final ZipInputStream in = new ZipInputStream( //
  325. new ByteArrayInputStream(zipData));
  326. ZipEntry e;
  327. while ((e = in.getNextEntry()) != null) {
  328. if (!e.getName().equals(path))
  329. continue;
  330. // found!
  331. final List<String> l = new ArrayList<String>();
  332. final BufferedReader reader = new BufferedReader( //
  333. new InputStreamReader(in, "UTF-8"));
  334. String line;
  335. while ((line = reader.readLine()) != null)
  336. l.add(line);
  337. return l.toArray(new String[l.size()]);
  338. }
  339. // not found
  340. return null;
  341. }
  342. private String[] tarEntryContent(byte[] tarData, String path) //
  343. throws Exception {
  344. final List<String> l = new ArrayList<String>();
  345. final Process proc = spawnAssumingCommandPresent("tar", "Oxf", "-", path);
  346. final BufferedReader reader = readFromProcess(proc);
  347. final OutputStream out = proc.getOutputStream();
  348. final Future<?> writing = writeAsync(out, tarData);
  349. try {
  350. String line;
  351. while ((line = reader.readLine()) != null)
  352. l.add(line);
  353. return l.toArray(new String[l.size()]);
  354. } finally {
  355. writing.get();
  356. reader.close();
  357. proc.destroy();
  358. }
  359. }
  360. }