ArchivableFilesFinderTest.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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\Util\Filesystem;
  14. use Symfony\Component\Process\Process;
  15. use Symfony\Component\Process\ExecutableFinder;
  16. class ArchivableFilesFinderTest extends \PHPUnit_Framework_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. realpath(sys_get_temp_dir()).'/composer_archiver_test'.uniqid(mt_rand(), true)
  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. 'toplevelA.foo',
  58. 'toplevelB.foo',
  59. 'prefixA.foo',
  60. 'prefixB.foo',
  61. 'prefixC.foo',
  62. 'prefixD.foo',
  63. 'prefixE.foo',
  64. 'prefixF.foo',
  65. 'parameters.yml',
  66. 'parameters.yml.dist',
  67. '!important!.txt',
  68. '!important_too!.txt'
  69. );
  70. foreach ($fileTree as $relativePath) {
  71. $path = $this->sources.'/'.$relativePath;
  72. $fs->ensureDirectoryExists(dirname($path));
  73. file_put_contents($path, '');
  74. }
  75. }
  76. protected function tearDown()
  77. {
  78. $fs = new Filesystem;
  79. $fs->removeDirectory($this->sources);
  80. }
  81. public function testManualExcludes()
  82. {
  83. $excludes = array(
  84. 'prefixB.foo',
  85. '!/prefixB.foo',
  86. '/prefixA.foo',
  87. 'prefixC.*',
  88. '!*/*/*/prefixC.foo'
  89. );
  90. $this->finder = new ArchivableFilesFinder($this->sources, $excludes);
  91. $this->assertArchivableFiles(array(
  92. '/!important!.txt',
  93. '/!important_too!.txt',
  94. '/A/prefixA.foo',
  95. '/A/prefixD.foo',
  96. '/A/prefixE.foo',
  97. '/A/prefixF.foo',
  98. '/B/sub/prefixA.foo',
  99. '/B/sub/prefixC.foo',
  100. '/B/sub/prefixD.foo',
  101. '/B/sub/prefixE.foo',
  102. '/B/sub/prefixF.foo',
  103. '/C/prefixA.foo',
  104. '/C/prefixD.foo',
  105. '/C/prefixE.foo',
  106. '/C/prefixF.foo',
  107. '/D/prefixA',
  108. '/D/prefixB',
  109. '/D/prefixC',
  110. '/D/prefixD',
  111. '/D/prefixE',
  112. '/D/prefixF',
  113. '/E/subtestA.foo',
  114. '/F/subtestA.foo',
  115. '/G/subtestA.foo',
  116. '/H/subtestA.foo',
  117. '/parameters.yml',
  118. '/parameters.yml.dist',
  119. '/prefixB.foo',
  120. '/prefixD.foo',
  121. '/prefixE.foo',
  122. '/prefixF.foo',
  123. '/toplevelA.foo',
  124. '/toplevelB.foo',
  125. ));
  126. }
  127. public function testGitExcludes()
  128. {
  129. // Ensure that git is available for testing.
  130. if (!$this->isProcessAvailable('git')) {
  131. return $this->markTestSkipped('git is not available.');
  132. }
  133. file_put_contents($this->sources.'/.gitignore', implode("\n", array(
  134. '# gitignore rules with comments and blank lines',
  135. '',
  136. 'prefixE.foo',
  137. '# and more',
  138. '# comments',
  139. '',
  140. '!/prefixE.foo',
  141. '/prefixD.foo',
  142. 'prefixF.*',
  143. '!/*/*/prefixF.foo',
  144. '',
  145. 'refixD.foo',
  146. '/C',
  147. 'D/prefixA',
  148. 'E',
  149. 'F/',
  150. 'G/*',
  151. 'H/**',
  152. 'parameters.yml',
  153. '\!important!.txt'
  154. )));
  155. // git does not currently support negative git attributes
  156. file_put_contents($this->sources.'/.gitattributes', implode("\n", array(
  157. '',
  158. '# gitattributes rules with comments and blank lines',
  159. 'prefixB.foo export-ignore',
  160. //'!/prefixB.foo export-ignore',
  161. '/prefixA.foo export-ignore',
  162. 'prefixC.* export-ignore',
  163. //'!/*/*/prefixC.foo export-ignore'
  164. )));
  165. $this->finder = new ArchivableFilesFinder($this->sources, array());
  166. $this->assertArchivableFiles($this->getArchivedFiles('git init && '.
  167. 'git add .git* && '.
  168. 'git commit -m "ignore rules" && '.
  169. 'git add . && '.
  170. 'git commit -m "init" && '.
  171. 'git archive --format=zip --prefix=archive/ -o archive.zip HEAD'
  172. ));
  173. }
  174. public function testHgExcludes()
  175. {
  176. // Ensure that Mercurial is available for testing.
  177. if (!$this->isProcessAvailable('hg')) {
  178. return $this->markTestSkipped('Mercurial is not available.');
  179. }
  180. file_put_contents($this->sources.'/.hgignore', implode("\n", array(
  181. '# hgignore rules with comments, blank lines and syntax changes',
  182. '',
  183. 'pre*A.foo',
  184. 'prefixE.foo',
  185. '# and more',
  186. '# comments',
  187. '',
  188. '^prefixD.foo',
  189. 'D/prefixA',
  190. 'parameters.yml',
  191. '\!important!.txt',
  192. 'E',
  193. 'F/',
  194. 'syntax: glob',
  195. 'prefixF.*',
  196. 'B/*',
  197. 'H/**',
  198. )));
  199. $this->finder = new ArchivableFilesFinder($this->sources, array());
  200. $expectedFiles = $this->getArchivedFiles('hg init && '.
  201. 'hg add && '.
  202. 'hg commit -m "init" && '.
  203. 'hg archive archive.zip'
  204. );
  205. // Remove .hg_archival.txt from the expectedFiles
  206. $archiveKey = array_search('/.hg_archival.txt', $expectedFiles);
  207. array_splice($expectedFiles, $archiveKey, 1);
  208. $this->assertArchivableFiles($expectedFiles);
  209. }
  210. protected function getArchivableFiles()
  211. {
  212. $files = array();
  213. foreach ($this->finder as $file) {
  214. if (!$file->isDir()) {
  215. $files[] = preg_replace('#^'.preg_quote($this->sources, '#').'#', '', $this->fs->normalizePath($file->getRealPath()));
  216. }
  217. }
  218. sort($files);
  219. return $files;
  220. }
  221. protected function getArchivedFiles($command)
  222. {
  223. $process = new Process($command, $this->sources);
  224. $process->run();
  225. $archive = new \PharData($this->sources.'/archive.zip');
  226. $iterator = new \RecursiveIteratorIterator($archive);
  227. $files = array();
  228. foreach ($iterator as $file) {
  229. $files[] = preg_replace('#^phar://'.preg_quote($this->sources, '#').'/archive\.zip/archive#', '', $this->fs->normalizePath($file));
  230. }
  231. unset($archive, $iterator, $file);
  232. unlink($this->sources.'/archive.zip');
  233. return $files;
  234. }
  235. protected function assertArchivableFiles($expectedFiles)
  236. {
  237. $actualFiles = $this->getArchivableFiles();
  238. $this->assertEquals($expectedFiles, $actualFiles);
  239. }
  240. /**
  241. * Check whether or not the given process is available.
  242. *
  243. * @param string $process The name of the binary to test.
  244. *
  245. * @return boolean True if the process is available, false otherwise.
  246. */
  247. protected function isProcessAvailable($process)
  248. {
  249. $finder = new ExecutableFinder();
  250. return (bool) $finder->find($process);
  251. }
  252. }