ArchivableFilesFinderTest.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\Test\Package\Archiver;
  12. use Composer\Package\Archiver\ArchivableFilesFinder;
  13. use Composer\Test\TestCase;
  14. use Composer\Util\Filesystem;
  15. use Symfony\Component\Process\Process;
  16. class ArchivableFilesFinderTest extends TestCase
  17. {
  18. protected $sources;
  19. protected $finder;
  20. protected $fs;
  21. protected function setUp()
  22. {
  23. $fs = new Filesystem;
  24. $this->fs = $fs;
  25. $this->sources = $fs->normalizePath(
  26. $this->getUniqueTmpDirectory()
  27. );
  28. $fileTree = array(
  29. 'A/prefixA.foo',
  30. 'A/prefixB.foo',
  31. 'A/prefixC.foo',
  32. 'A/prefixD.foo',
  33. 'A/prefixE.foo',
  34. 'A/prefixF.foo',
  35. 'B/sub/prefixA.foo',
  36. 'B/sub/prefixB.foo',
  37. 'B/sub/prefixC.foo',
  38. 'B/sub/prefixD.foo',
  39. 'B/sub/prefixE.foo',
  40. 'B/sub/prefixF.foo',
  41. 'C/prefixA.foo',
  42. 'C/prefixB.foo',
  43. 'C/prefixC.foo',
  44. 'C/prefixD.foo',
  45. 'C/prefixE.foo',
  46. 'C/prefixF.foo',
  47. 'D/prefixA',
  48. 'D/prefixB',
  49. 'D/prefixC',
  50. 'D/prefixD',
  51. 'D/prefixE',
  52. 'D/prefixF',
  53. 'E/subtestA.foo',
  54. 'F/subtestA.foo',
  55. 'G/subtestA.foo',
  56. 'H/subtestA.foo',
  57. 'I/J/subtestA.foo',
  58. 'K/dirJ/subtestA.foo',
  59. 'toplevelA.foo',
  60. 'toplevelB.foo',
  61. 'prefixA.foo',
  62. 'prefixB.foo',
  63. 'prefixC.foo',
  64. 'prefixD.foo',
  65. 'prefixE.foo',
  66. 'prefixF.foo',
  67. 'parameters.yml',
  68. 'parameters.yml.dist',
  69. '!important!.txt',
  70. '!important_too!.txt',
  71. '#weirdfile',
  72. );
  73. foreach ($fileTree as $relativePath) {
  74. $path = $this->sources.'/'.$relativePath;
  75. $fs->ensureDirectoryExists(dirname($path));
  76. file_put_contents($path, '');
  77. }
  78. }
  79. protected function tearDown()
  80. {
  81. $fs = new Filesystem;
  82. $fs->removeDirectory($this->sources);
  83. }
  84. public function testManualExcludes()
  85. {
  86. $excludes = array(
  87. 'prefixB.foo',
  88. '!/prefixB.foo',
  89. '/prefixA.foo',
  90. 'prefixC.*',
  91. '!*/*/*/prefixC.foo',
  92. );
  93. $this->finder = new ArchivableFilesFinder($this->sources, $excludes);
  94. $this->assertArchivableFiles(array(
  95. '/!important!.txt',
  96. '/!important_too!.txt',
  97. '/#weirdfile',
  98. '/A/prefixA.foo',
  99. '/A/prefixD.foo',
  100. '/A/prefixE.foo',
  101. '/A/prefixF.foo',
  102. '/B/sub/prefixA.foo',
  103. '/B/sub/prefixC.foo',
  104. '/B/sub/prefixD.foo',
  105. '/B/sub/prefixE.foo',
  106. '/B/sub/prefixF.foo',
  107. '/C/prefixA.foo',
  108. '/C/prefixD.foo',
  109. '/C/prefixE.foo',
  110. '/C/prefixF.foo',
  111. '/D/prefixA',
  112. '/D/prefixB',
  113. '/D/prefixC',
  114. '/D/prefixD',
  115. '/D/prefixE',
  116. '/D/prefixF',
  117. '/E/subtestA.foo',
  118. '/F/subtestA.foo',
  119. '/G/subtestA.foo',
  120. '/H/subtestA.foo',
  121. '/I/J/subtestA.foo',
  122. '/K/dirJ/subtestA.foo',
  123. '/parameters.yml',
  124. '/parameters.yml.dist',
  125. '/prefixB.foo',
  126. '/prefixD.foo',
  127. '/prefixE.foo',
  128. '/prefixF.foo',
  129. '/toplevelA.foo',
  130. '/toplevelB.foo',
  131. ));
  132. }
  133. public function testGitExcludes()
  134. {
  135. $this->skipIfNotExecutable('git');
  136. file_put_contents($this->sources.'/.gitignore', implode("\n", array(
  137. '# gitignore rules with comments and blank lines',
  138. '',
  139. 'prefixE.foo',
  140. '# and more',
  141. '# comments',
  142. '',
  143. '!/prefixE.foo',
  144. '/prefixD.foo',
  145. 'prefixF.*',
  146. '!/*/*/prefixF.foo',
  147. '',
  148. 'refixD.foo',
  149. '/C',
  150. 'D/prefixA',
  151. 'E',
  152. 'F/',
  153. 'G/*',
  154. 'H/**',
  155. 'J/',
  156. 'parameters.yml',
  157. '\!important!.txt',
  158. '\#*',
  159. )));
  160. // git does not currently support negative git attributes
  161. file_put_contents($this->sources.'/.gitattributes', implode("\n", array(
  162. '',
  163. '# gitattributes rules with comments and blank lines',
  164. 'prefixB.foo export-ignore',
  165. //'!/prefixB.foo export-ignore',
  166. '/prefixA.foo export-ignore',
  167. 'prefixC.* export-ignore',
  168. //'!/*/*/prefixC.foo export-ignore',
  169. )));
  170. $this->finder = new ArchivableFilesFinder($this->sources, array());
  171. $this->assertArchivableFiles($this->getArchivedFiles(
  172. 'git init && '.
  173. 'git config user.email "you@example.com" && '.
  174. 'git config user.name "Your Name" && '.
  175. 'git config commit.gpgsign false && '.
  176. 'git add .git* && '.
  177. 'git commit -m "ignore rules" && '.
  178. 'git add . && '.
  179. 'git commit -m "init" && '.
  180. 'git archive --format=zip --prefix=archive/ -o archive.zip HEAD'
  181. ));
  182. }
  183. public function testHgExcludes()
  184. {
  185. $this->skipIfNotExecutable('hg');
  186. file_put_contents($this->sources.'/.hgignore', implode("\n", array(
  187. '# hgignore rules with comments, blank lines and syntax changes',
  188. '',
  189. 'pre*A.foo',
  190. 'prefixE.foo',
  191. '# and more',
  192. '# comments',
  193. '',
  194. '^prefixD.foo',
  195. 'D/prefixA',
  196. 'parameters.yml',
  197. '\!important!.txt',
  198. 'E',
  199. 'F/',
  200. 'syntax: glob',
  201. 'prefixF.*',
  202. 'B/*',
  203. 'H/**',
  204. )));
  205. $this->finder = new ArchivableFilesFinder($this->sources, array());
  206. $expectedFiles = $this->getArchivedFiles(
  207. 'hg init && '.
  208. 'hg add && '.
  209. 'hg commit -m "init" && '.
  210. 'hg archive archive.zip'
  211. );
  212. // Remove .hg_archival.txt from the expectedFiles
  213. $archiveKey = array_search('/.hg_archival.txt', $expectedFiles);
  214. array_splice($expectedFiles, $archiveKey, 1);
  215. $this->assertArchivableFiles($expectedFiles);
  216. }
  217. public function testSkipExcludes()
  218. {
  219. $excludes = array(
  220. 'prefixB.foo',
  221. );
  222. $this->finder = new ArchivableFilesFinder($this->sources, $excludes, true);
  223. $this->assertArchivableFiles(array(
  224. '/!important!.txt',
  225. '/!important_too!.txt',
  226. '/#weirdfile',
  227. '/A/prefixA.foo',
  228. '/A/prefixB.foo',
  229. '/A/prefixC.foo',
  230. '/A/prefixD.foo',
  231. '/A/prefixE.foo',
  232. '/A/prefixF.foo',
  233. '/B/sub/prefixA.foo',
  234. '/B/sub/prefixB.foo',
  235. '/B/sub/prefixC.foo',
  236. '/B/sub/prefixD.foo',
  237. '/B/sub/prefixE.foo',
  238. '/B/sub/prefixF.foo',
  239. '/C/prefixA.foo',
  240. '/C/prefixB.foo',
  241. '/C/prefixC.foo',
  242. '/C/prefixD.foo',
  243. '/C/prefixE.foo',
  244. '/C/prefixF.foo',
  245. '/D/prefixA',
  246. '/D/prefixB',
  247. '/D/prefixC',
  248. '/D/prefixD',
  249. '/D/prefixE',
  250. '/D/prefixF',
  251. '/E/subtestA.foo',
  252. '/F/subtestA.foo',
  253. '/G/subtestA.foo',
  254. '/H/subtestA.foo',
  255. '/I/J/subtestA.foo',
  256. '/K/dirJ/subtestA.foo',
  257. '/parameters.yml',
  258. '/parameters.yml.dist',
  259. '/prefixA.foo',
  260. '/prefixB.foo',
  261. '/prefixC.foo',
  262. '/prefixD.foo',
  263. '/prefixE.foo',
  264. '/prefixF.foo',
  265. '/toplevelA.foo',
  266. '/toplevelB.foo',
  267. ));
  268. }
  269. protected function getArchivableFiles()
  270. {
  271. $files = array();
  272. foreach ($this->finder as $file) {
  273. if (!$file->isDir()) {
  274. $files[] = preg_replace('#^'.preg_quote($this->sources, '#').'#', '', $this->fs->normalizePath($file->getRealPath()));
  275. }
  276. }
  277. sort($files);
  278. return $files;
  279. }
  280. protected function getArchivedFiles($command)
  281. {
  282. if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) {
  283. $process = Process::fromShellCommandline($command, $this->sources);
  284. } else {
  285. $process = new Process($command, $this->sources);
  286. }
  287. $process->run();
  288. $archive = new \PharData($this->sources.'/archive.zip');
  289. $iterator = new \RecursiveIteratorIterator($archive);
  290. $files = array();
  291. foreach ($iterator as $file) {
  292. $files[] = preg_replace('#^phar://'.preg_quote($this->sources, '#').'/archive\.zip/archive#', '', $this->fs->normalizePath($file));
  293. }
  294. unset($archive, $iterator, $file);
  295. unlink($this->sources.'/archive.zip');
  296. return $files;
  297. }
  298. protected function assertArchivableFiles($expectedFiles)
  299. {
  300. $actualFiles = $this->getArchivableFiles();
  301. $this->assertEquals($expectedFiles, $actualFiles);
  302. }
  303. }