PageRenderTime 88ms CodeModel.GetById 21ms RepoModel.GetById 3ms app.codeStats 0ms

/tests/Composer/Test/Downloader/GitDownloaderTest.php

http://github.com/composer/composer
PHP | 744 lines | 620 code | 95 blank | 29 comment | 54 complexity | c806d48de9b576375e3123a877f082df MD5 | raw file
  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\Downloader;
  12. use Composer\Downloader\GitDownloader;
  13. use Composer\Config;
  14. use Composer\Test\TestCase;
  15. use Composer\Util\Filesystem;
  16. use Composer\Util\Platform;
  17. use Prophecy\Argument;
  18. class GitDownloaderTest extends TestCase
  19. {
  20. /** @var Filesystem */
  21. private $fs;
  22. /** @var string */
  23. private $workingDir;
  24. protected function setUp()
  25. {
  26. $this->skipIfNotExecutable('git');
  27. $this->fs = new Filesystem;
  28. $this->workingDir = $this->getUniqueTmpDirectory();
  29. }
  30. protected function tearDown()
  31. {
  32. if (is_dir($this->workingDir)) {
  33. $this->fs->removeDirectory($this->workingDir);
  34. }
  35. // reset the static version cache
  36. $refl = new \ReflectionProperty('Composer\Util\Git', 'version');
  37. $refl->setAccessible(true);
  38. $refl->setValue(null, null);
  39. }
  40. protected function setupConfig($config = null)
  41. {
  42. if (!$config) {
  43. $config = new Config();
  44. }
  45. if (!$config->has('home')) {
  46. $tmpDir = realpath(sys_get_temp_dir()).DIRECTORY_SEPARATOR.'cmptest-'.md5(uniqid('', true));
  47. $config->merge(array('config' => array('home' => $tmpDir)));
  48. }
  49. return $config;
  50. }
  51. protected function getDownloaderMock($io = null, $config = null, $executor = null, $filesystem = null)
  52. {
  53. $io = $io ?: $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
  54. $executor = $executor ?: $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  55. $filesystem = $filesystem ?: $this->getMockBuilder('Composer\Util\Filesystem')->getMock();
  56. $config = $this->setupConfig($config);
  57. return new GitDownloader($io, $config, $executor, $filesystem);
  58. }
  59. /**
  60. * @expectedException \InvalidArgumentException
  61. */
  62. public function testDownloadForPackageWithoutSourceReference()
  63. {
  64. $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  65. $packageMock->expects($this->once())
  66. ->method('getSourceReference')
  67. ->will($this->returnValue(null));
  68. $downloader = $this->getDownloaderMock();
  69. $downloader->download($packageMock, '/path');
  70. $downloader->prepare('install', $packageMock, '/path');
  71. $downloader->install($packageMock, '/path');
  72. $downloader->cleanup('install', $packageMock, '/path');
  73. }
  74. public function testDownload()
  75. {
  76. $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  77. $packageMock->expects($this->any())
  78. ->method('getSourceReference')
  79. ->will($this->returnValue('1234567890123456789012345678901234567890'));
  80. $packageMock->expects($this->any())
  81. ->method('getSourceUrls')
  82. ->will($this->returnValue(array('https://example.com/composer/composer')));
  83. $packageMock->expects($this->any())
  84. ->method('getSourceUrl')
  85. ->will($this->returnValue('https://example.com/composer/composer'));
  86. $packageMock->expects($this->any())
  87. ->method('getPrettyVersion')
  88. ->will($this->returnValue('dev-master'));
  89. $processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  90. $processExecutor->expects($this->at(0))
  91. ->method('execute')
  92. ->with($this->equalTo($this->winCompat('git --version')))
  93. ->will($this->returnCallback(function ($command, &$output = null) {
  94. $output = 'git version 1.0.0';
  95. return 0;
  96. }));
  97. $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://example.com/composer/composer' && git fetch composer && git remote set-url origin 'https://example.com/composer/composer' && git remote set-url composer 'https://example.com/composer/composer'");
  98. $processExecutor->expects($this->at(1))
  99. ->method('execute')
  100. ->with($this->equalTo($expectedGitCommand))
  101. ->will($this->returnValue(0));
  102. $processExecutor->expects($this->at(2))
  103. ->method('execute')
  104. ->with($this->equalTo($this->winCompat("git branch -r")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
  105. ->will($this->returnValue(0));
  106. $processExecutor->expects($this->at(3))
  107. ->method('execute')
  108. ->with($this->equalTo($this->winCompat("git checkout 'master' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
  109. ->will($this->returnValue(0));
  110. $processExecutor->expects($this->at(4))
  111. ->method('execute')
  112. ->with($this->equalTo($this->winCompat("git reset --hard '1234567890123456789012345678901234567890' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
  113. ->will($this->returnValue(0));
  114. $downloader = $this->getDownloaderMock(null, null, $processExecutor);
  115. $downloader->download($packageMock, 'composerPath');
  116. $downloader->prepare('install', $packageMock, 'composerPath');
  117. $downloader->install($packageMock, 'composerPath');
  118. $downloader->cleanup('install', $packageMock, 'composerPath');
  119. }
  120. public function testDownloadWithCache()
  121. {
  122. $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  123. $packageMock->expects($this->any())
  124. ->method('getSourceReference')
  125. ->will($this->returnValue('1234567890123456789012345678901234567890'));
  126. $packageMock->expects($this->any())
  127. ->method('getSourceUrls')
  128. ->will($this->returnValue(array('https://example.com/composer/composer')));
  129. $packageMock->expects($this->any())
  130. ->method('getSourceUrl')
  131. ->will($this->returnValue('https://example.com/composer/composer'));
  132. $packageMock->expects($this->any())
  133. ->method('getPrettyVersion')
  134. ->will($this->returnValue('dev-master'));
  135. $processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  136. $processExecutor->expects($this->at(0))
  137. ->method('execute')
  138. ->with($this->equalTo($this->winCompat('git --version')))
  139. ->will($this->returnCallback(function ($command, &$output = null) {
  140. $output = 'git version 2.3.1';
  141. return 0;
  142. }));
  143. $config = new Config;
  144. $this->setupConfig($config);
  145. $cachePath = $config->get('cache-vcs-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', 'https://example.com/composer/composer').'/';
  146. $filesystem = new \Composer\Util\Filesystem;
  147. $filesystem->removeDirectory($cachePath);
  148. $expectedGitCommand = $this->winCompat(sprintf("git clone --mirror 'https://example.com/composer/composer' '%s'", $cachePath));
  149. $processExecutor->expects($this->at(1))
  150. ->method('execute')
  151. ->with($this->equalTo($expectedGitCommand))
  152. ->will($this->returnCallback(function () use ($cachePath) {
  153. @mkdir($cachePath, 0777, true);
  154. return 0;
  155. }));
  156. $processExecutor->expects($this->at(2))
  157. ->method('execute')
  158. ->with($this->equalTo('git rev-parse --git-dir'), $this->anything(), $this->equalTo($this->winCompat($cachePath)))
  159. ->will($this->returnCallback(function ($command, &$output = null) {
  160. $output = '.';
  161. return 0;
  162. }));
  163. $processExecutor->expects($this->at(3))
  164. ->method('execute')
  165. ->with($this->equalTo($this->winCompat('git rev-parse --quiet --verify \'1234567890123456789012345678901234567890^{commit}\'')), $this->equalTo(null), $this->equalTo($this->winCompat($cachePath)))
  166. ->will($this->returnValue(0));
  167. $expectedGitCommand = $this->winCompat(sprintf("git clone --no-checkout '%1\$s' 'composerPath' --dissociate --reference '%1\$s' && cd 'composerPath' && git remote set-url origin 'https://example.com/composer/composer' && git remote add composer 'https://example.com/composer/composer'", $cachePath));
  168. $processExecutor->expects($this->at(4))
  169. ->method('execute')
  170. ->with($this->equalTo($expectedGitCommand))
  171. ->will($this->returnValue(0));
  172. $processExecutor->expects($this->at(5))
  173. ->method('execute')
  174. ->with($this->equalTo($this->winCompat("git branch -r")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
  175. ->will($this->returnValue(0));
  176. $processExecutor->expects($this->at(6))
  177. ->method('execute')
  178. ->with($this->equalTo($this->winCompat("git checkout 'master' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
  179. ->will($this->returnValue(0));
  180. $processExecutor->expects($this->at(7))
  181. ->method('execute')
  182. ->with($this->equalTo($this->winCompat("git reset --hard '1234567890123456789012345678901234567890' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
  183. ->will($this->returnValue(0));
  184. $downloader = $this->getDownloaderMock(null, $config, $processExecutor);
  185. $downloader->download($packageMock, 'composerPath');
  186. $downloader->prepare('install', $packageMock, 'composerPath');
  187. $downloader->install($packageMock, 'composerPath');
  188. $downloader->cleanup('install', $packageMock, 'composerPath');
  189. @rmdir($cachePath);
  190. }
  191. public function testDownloadUsesVariousProtocolsAndSetsPushUrlForGithub()
  192. {
  193. $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  194. $packageMock->expects($this->any())
  195. ->method('getSourceReference')
  196. ->will($this->returnValue('ref'));
  197. $packageMock->expects($this->any())
  198. ->method('getSourceUrls')
  199. ->will($this->returnValue(array('https://github.com/mirrors/composer', 'https://github.com/composer/composer')));
  200. $packageMock->expects($this->any())
  201. ->method('getSourceUrl')
  202. ->will($this->returnValue('https://github.com/composer/composer'));
  203. $packageMock->expects($this->any())
  204. ->method('getPrettyVersion')
  205. ->will($this->returnValue('1.0.0'));
  206. $processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  207. $processExecutor->expects($this->at(0))
  208. ->method('execute')
  209. ->with($this->equalTo($this->winCompat('git --version')))
  210. ->will($this->returnCallback(function ($command, &$output = null) {
  211. $output = 'git version 1.0.0';
  212. return 0;
  213. }));
  214. $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://github.com/mirrors/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://github.com/mirrors/composer' && git fetch composer && git remote set-url origin 'https://github.com/mirrors/composer' && git remote set-url composer 'https://github.com/mirrors/composer'");
  215. $processExecutor->expects($this->at(1))
  216. ->method('execute')
  217. ->with($this->equalTo($expectedGitCommand))
  218. ->will($this->returnValue(1));
  219. $processExecutor->expects($this->at(2))
  220. ->method('getErrorOutput')
  221. ->with()
  222. ->will($this->returnValue('Error1'));
  223. $expectedGitCommand = $this->winCompat("git clone --no-checkout 'git@github.com:mirrors/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'git@github.com:mirrors/composer' && git fetch composer && git remote set-url origin 'git@github.com:mirrors/composer' && git remote set-url composer 'git@github.com:mirrors/composer'");
  224. $processExecutor->expects($this->at(3))
  225. ->method('execute')
  226. ->with($this->equalTo($expectedGitCommand))
  227. ->will($this->returnValue(0));
  228. $expectedGitCommand = $this->winCompat("git remote set-url origin 'https://github.com/composer/composer'");
  229. $processExecutor->expects($this->at(4))
  230. ->method('execute')
  231. ->with($this->equalTo($expectedGitCommand), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
  232. ->will($this->returnValue(0));
  233. $expectedGitCommand = $this->winCompat("git remote set-url --push origin 'git@github.com:composer/composer.git'");
  234. $processExecutor->expects($this->at(5))
  235. ->method('execute')
  236. ->with($this->equalTo($expectedGitCommand), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
  237. ->will($this->returnValue(0));
  238. $processExecutor->expects($this->at(6))
  239. ->method('execute')
  240. ->with($this->equalTo('git branch -r'))
  241. ->will($this->returnValue(0));
  242. $processExecutor->expects($this->at(7))
  243. ->method('execute')
  244. ->with($this->equalTo($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
  245. ->will($this->returnValue(0));
  246. $downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
  247. $downloader->download($packageMock, 'composerPath');
  248. $downloader->prepare('install', $packageMock, 'composerPath');
  249. $downloader->install($packageMock, 'composerPath');
  250. $downloader->cleanup('install', $packageMock, 'composerPath');
  251. }
  252. public function pushUrlProvider()
  253. {
  254. return array(
  255. // ssh proto should use git@ all along
  256. array(array('ssh'), 'git@github.com:composer/composer', 'git@github.com:composer/composer.git'),
  257. // auto-proto uses git@ by default for push url, but not fetch
  258. array(array('https', 'ssh', 'git'), 'https://github.com/composer/composer', 'git@github.com:composer/composer.git'),
  259. // if restricted to https then push url is not overwritten to git@
  260. array(array('https'), 'https://github.com/composer/composer', 'https://github.com/composer/composer.git'),
  261. );
  262. }
  263. /**
  264. * @dataProvider pushUrlProvider
  265. */
  266. public function testDownloadAndSetPushUrlUseCustomVariousProtocolsForGithub($protocols, $url, $pushUrl)
  267. {
  268. $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  269. $packageMock->expects($this->any())
  270. ->method('getSourceReference')
  271. ->will($this->returnValue('ref'));
  272. $packageMock->expects($this->any())
  273. ->method('getSourceUrls')
  274. ->will($this->returnValue(array('https://github.com/composer/composer')));
  275. $packageMock->expects($this->any())
  276. ->method('getSourceUrl')
  277. ->will($this->returnValue('https://github.com/composer/composer'));
  278. $packageMock->expects($this->any())
  279. ->method('getPrettyVersion')
  280. ->will($this->returnValue('1.0.0'));
  281. $processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  282. $processExecutor->expects($this->at(0))
  283. ->method('execute')
  284. ->with($this->equalTo($this->winCompat('git --version')))
  285. ->will($this->returnCallback(function ($command, &$output = null) {
  286. $output = 'git version 1.0.0';
  287. return 0;
  288. }));
  289. $expectedGitCommand = $this->winCompat("git clone --no-checkout '{$url}' 'composerPath' && cd 'composerPath' && git remote add composer '{$url}' && git fetch composer && git remote set-url origin '{$url}' && git remote set-url composer '{$url}'");
  290. $processExecutor->expects($this->at(1))
  291. ->method('execute')
  292. ->with($this->equalTo($expectedGitCommand))
  293. ->will($this->returnValue(0));
  294. $expectedGitCommand = $this->winCompat("git remote set-url --push origin '{$pushUrl}'");
  295. $processExecutor->expects($this->at(2))
  296. ->method('execute')
  297. ->with($this->equalTo($expectedGitCommand), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
  298. ->will($this->returnValue(0));
  299. $processExecutor->expects($this->exactly(5))
  300. ->method('execute')
  301. ->will($this->returnValue(0));
  302. $config = new Config();
  303. $config->merge(array('config' => array('github-protocols' => $protocols)));
  304. $downloader = $this->getDownloaderMock(null, $config, $processExecutor);
  305. $downloader->download($packageMock, 'composerPath');
  306. $downloader->prepare('install', $packageMock, 'composerPath');
  307. $downloader->install($packageMock, 'composerPath');
  308. $downloader->cleanup('install', $packageMock, 'composerPath');
  309. }
  310. public function testDownloadThrowsRuntimeExceptionIfGitCommandFails()
  311. {
  312. $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://example.com/composer/composer' && git fetch composer && git remote set-url origin 'https://example.com/composer/composer' && git remote set-url composer 'https://example.com/composer/composer'");
  313. $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  314. $packageMock->expects($this->any())
  315. ->method('getSourceReference')
  316. ->will($this->returnValue('ref'));
  317. $packageMock->expects($this->any())
  318. ->method('getSourceUrls')
  319. ->will($this->returnValue(array('https://example.com/composer/composer')));
  320. $processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  321. $processExecutor->expects($this->at(0))
  322. ->method('execute')
  323. ->with($this->equalTo($this->winCompat('git --version')))
  324. ->will($this->returnCallback(function ($command, &$output = null) {
  325. $output = 'git version 1.0.0';
  326. return 0;
  327. }));
  328. $processExecutor->expects($this->at(1))
  329. ->method('execute')
  330. ->with($this->equalTo($expectedGitCommand))
  331. ->will($this->returnValue(1));
  332. // not using PHPUnit's expected exception because Prophecy exceptions extend from RuntimeException too so it is not safe
  333. try {
  334. $downloader = $this->getDownloaderMock(null, null, $processExecutor);
  335. $downloader->download($packageMock, 'composerPath');
  336. $downloader->prepare('install', $packageMock, 'composerPath');
  337. $downloader->install($packageMock, 'composerPath');
  338. $downloader->cleanup('install', $packageMock, 'composerPath');
  339. $this->fail('This test should throw');
  340. } catch (\RuntimeException $e) {
  341. if ('RuntimeException' !== get_class($e)) {
  342. throw $e;
  343. }
  344. $this->assertEquals('RuntimeException', get_class($e));
  345. }
  346. }
  347. /**
  348. * @expectedException \InvalidArgumentException
  349. */
  350. public function testUpdateforPackageWithoutSourceReference()
  351. {
  352. $initialPackageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  353. $sourcePackageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  354. $sourcePackageMock->expects($this->once())
  355. ->method('getSourceReference')
  356. ->will($this->returnValue(null));
  357. $downloader = $this->getDownloaderMock();
  358. $downloader->download($sourcePackageMock, '/path', $initialPackageMock);
  359. $downloader->prepare('update', $sourcePackageMock, '/path', $initialPackageMock);
  360. $downloader->update($initialPackageMock, $sourcePackageMock, '/path');
  361. $downloader->cleanup('update', $sourcePackageMock, '/path', $initialPackageMock);
  362. }
  363. public function testUpdate()
  364. {
  365. $expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'https://github.com/composer/composer'");
  366. $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  367. $packageMock->expects($this->any())
  368. ->method('getSourceReference')
  369. ->will($this->returnValue('ref'));
  370. $packageMock->expects($this->any())
  371. ->method('getSourceUrls')
  372. ->will($this->returnValue(array('https://github.com/composer/composer')));
  373. $packageMock->expects($this->any())
  374. ->method('getVersion')
  375. ->will($this->returnValue('1.0.0.0'));
  376. $process = $this->prophesize('Composer\Util\ProcessExecutor');
  377. $process->execute($this->winCompat('git --version'), Argument::cetera())->willReturn(0);
  378. $process->execute($this->winCompat('git show-ref --head -d'), Argument::cetera())->willReturn(0);
  379. $process->execute($this->winCompat('git status --porcelain --untracked-files=no'), Argument::cetera())->willReturn(0);
  380. $process->execute($this->winCompat('git remote -v'), Argument::cetera())->willReturn(0);
  381. $process->execute($this->winCompat('git branch -r'), Argument::cetera())->willReturn(0);
  382. $process->execute($expectedGitUpdateCommand, null, $this->winCompat($this->workingDir))->willReturn(0)->shouldBeCalled();
  383. $process->execute($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --"), null, $this->winCompat($this->workingDir))->willReturn(0)->shouldBeCalled();
  384. $this->fs->ensureDirectoryExists($this->workingDir.'/.git');
  385. $downloader = $this->getDownloaderMock(null, new Config(), $process->reveal());
  386. $downloader->download($packageMock, $this->workingDir, $packageMock);
  387. $downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
  388. $downloader->update($packageMock, $packageMock, $this->workingDir);
  389. $downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
  390. }
  391. public function testUpdateWithNewRepoUrl()
  392. {
  393. $expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'https://github.com/composer/composer'");
  394. $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  395. $packageMock->expects($this->any())
  396. ->method('getSourceReference')
  397. ->will($this->returnValue('ref'));
  398. $packageMock->expects($this->any())
  399. ->method('getSourceUrls')
  400. ->will($this->returnValue(array('https://github.com/composer/composer')));
  401. $packageMock->expects($this->any())
  402. ->method('getSourceUrl')
  403. ->will($this->returnValue('https://github.com/composer/composer'));
  404. $packageMock->expects($this->any())
  405. ->method('getVersion')
  406. ->will($this->returnValue('1.0.0.0'));
  407. $processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  408. $processExecutor->expects($this->at(0))
  409. ->method('execute')
  410. ->with($this->equalTo($this->winCompat("git --version")))
  411. ->will($this->returnValue(0));
  412. $processExecutor->expects($this->at(1))
  413. ->method('execute')
  414. ->with($this->equalTo($this->winCompat("git show-ref --head -d")))
  415. ->will($this->returnValue(0));
  416. $processExecutor->expects($this->at(2))
  417. ->method('execute')
  418. ->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
  419. ->will($this->returnValue(0));
  420. $processExecutor->expects($this->at(3))
  421. ->method('execute')
  422. ->with($this->equalTo($this->winCompat("git remote -v")))
  423. ->will($this->returnValue(0));
  424. $processExecutor->expects($this->at(4))
  425. ->method('execute')
  426. ->with($this->equalTo($this->winCompat($expectedGitUpdateCommand)), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
  427. ->will($this->returnValue(0));
  428. $processExecutor->expects($this->at(5))
  429. ->method('execute')
  430. ->with($this->equalTo('git branch -r'))
  431. ->will($this->returnValue(0));
  432. $processExecutor->expects($this->at(6))
  433. ->method('execute')
  434. ->with($this->equalTo($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --")), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
  435. ->will($this->returnValue(0));
  436. $processExecutor->expects($this->at(7))
  437. ->method('execute')
  438. ->with($this->equalTo($this->winCompat("git remote -v")))
  439. ->will($this->returnCallback(function ($cmd, &$output, $cwd) {
  440. $output = 'origin https://github.com/old/url (fetch)
  441. origin https://github.com/old/url (push)
  442. composer https://github.com/old/url (fetch)
  443. composer https://github.com/old/url (push)
  444. ';
  445. return 0;
  446. }));
  447. $processExecutor->expects($this->at(8))
  448. ->method('execute')
  449. ->with($this->equalTo($this->winCompat("git remote set-url origin 'https://github.com/composer/composer'")), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
  450. ->will($this->returnValue(0));
  451. $processExecutor->expects($this->at(9))
  452. ->method('execute')
  453. ->with($this->equalTo($this->winCompat("git remote set-url --push origin 'git@github.com:composer/composer.git'")), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
  454. ->will($this->returnValue(0));
  455. $this->fs->ensureDirectoryExists($this->workingDir.'/.git');
  456. $downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
  457. $downloader->download($packageMock, $this->workingDir, $packageMock);
  458. $downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
  459. $downloader->update($packageMock, $packageMock, $this->workingDir);
  460. $downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
  461. }
  462. /**
  463. * @group failing
  464. */
  465. public function testUpdateThrowsRuntimeExceptionIfGitCommandFails()
  466. {
  467. $expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'https://github.com/composer/composer'");
  468. $expectedGitUpdateCommand2 = $this->winCompat("git remote set-url composer 'git@github.com:composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'git@github.com:composer/composer'");
  469. $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  470. $packageMock->expects($this->any())
  471. ->method('getSourceReference')
  472. ->will($this->returnValue('ref'));
  473. $packageMock->expects($this->any())
  474. ->method('getSourceUrls')
  475. ->will($this->returnValue(array('https://github.com/composer/composer')));
  476. $packageMock->expects($this->any())
  477. ->method('getVersion')
  478. ->will($this->returnValue('1.0.0.0'));
  479. $process = $this->prophesize('Composer\Util\ProcessExecutor');
  480. $process->execute($this->winCompat('git --version'), Argument::cetera())->willReturn(0);
  481. $process->execute($this->winCompat('git show-ref --head -d'), Argument::cetera())->willReturn(0);
  482. $process->execute($this->winCompat('git status --porcelain --untracked-files=no'), Argument::cetera())->willReturn(0);
  483. $process->execute($this->winCompat('git remote -v'), Argument::cetera())->willReturn(0);
  484. $process->execute($this->winCompat('git branch -r'), Argument::cetera())->willReturn(0);
  485. $process->execute($expectedGitUpdateCommand, null, $this->winCompat($this->workingDir))->willReturn(1)->shouldBeCalled();
  486. $process->execute($expectedGitUpdateCommand2, null, $this->winCompat($this->workingDir))->willReturn(1)->shouldBeCalled();
  487. $process->getErrorOutput()->willReturn('');
  488. $this->fs->ensureDirectoryExists($this->workingDir.'/.git');
  489. // not using PHPUnit's expected exception because Prophecy exceptions extend from RuntimeException too so it is not safe
  490. try {
  491. $downloader = $this->getDownloaderMock(null, new Config(), $process->reveal());
  492. $downloader->download($packageMock, $this->workingDir, $packageMock);
  493. $downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
  494. $downloader->update($packageMock, $packageMock, $this->workingDir);
  495. $downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
  496. $this->fail('This test should throw');
  497. } catch (\RuntimeException $e) {
  498. if ('RuntimeException' !== get_class($e)) {
  499. throw $e;
  500. }
  501. $this->assertEquals('RuntimeException', get_class($e));
  502. }
  503. }
  504. public function testUpdateDoesntThrowsRuntimeExceptionIfGitCommandFailsAtFirstButIsAbleToRecover()
  505. {
  506. $expectedFirstGitUpdateCommand = $this->winCompat("git remote set-url composer '".(Platform::isWindows() ? 'C:\\\\' : '/')."' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer '".(Platform::isWindows() ? 'C:\\\\' : '/')."'");
  507. $expectedSecondGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'https://github.com/composer/composer'");
  508. $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  509. $packageMock->expects($this->any())
  510. ->method('getSourceReference')
  511. ->will($this->returnValue('ref'));
  512. $packageMock->expects($this->any())
  513. ->method('getVersion')
  514. ->will($this->returnValue('1.0.0.0'));
  515. $packageMock->expects($this->any())
  516. ->method('getSourceUrls')
  517. ->will($this->returnValue(array(Platform::isWindows() ? 'C:\\' : '/', 'https://github.com/composer/composer')));
  518. $process = $this->prophesize('Composer\Util\ProcessExecutor');
  519. $process->execute($this->winCompat('git --version'), Argument::cetera())->willReturn(0);
  520. $process->execute($this->winCompat('git show-ref --head -d'), Argument::cetera())->willReturn(0);
  521. $process->execute($this->winCompat('git status --porcelain --untracked-files=no'), Argument::cetera())->willReturn(0);
  522. $process->execute($this->winCompat('git remote -v'), Argument::cetera())->willReturn(0);
  523. $process->execute($this->winCompat('git branch -r'), Argument::cetera())->willReturn(0);
  524. $process->execute($expectedFirstGitUpdateCommand, Argument::cetera())->willReturn(1)->shouldBeCalled();
  525. $process->execute($expectedSecondGitUpdateCommand, Argument::cetera())->willReturn(0)->shouldBeCalled();
  526. $process->execute($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --"), null, $this->winCompat($this->workingDir))->willReturn(0)->shouldBeCalled();
  527. $process->getErrorOutput()->willReturn('');
  528. $this->fs->ensureDirectoryExists($this->workingDir.'/.git');
  529. $downloader = $this->getDownloaderMock(null, new Config(), $process->reveal());
  530. $downloader->download($packageMock, $this->workingDir, $packageMock);
  531. $downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
  532. $downloader->update($packageMock, $packageMock, $this->workingDir);
  533. $downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
  534. }
  535. public function testDowngradeShowsAppropriateMessage()
  536. {
  537. $oldPackage = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  538. $oldPackage->expects($this->any())
  539. ->method('getVersion')
  540. ->will($this->returnValue('1.2.0.0'));
  541. $oldPackage->expects($this->any())
  542. ->method('getFullPrettyVersion')
  543. ->will($this->returnValue('1.2.0'));
  544. $oldPackage->expects($this->any())
  545. ->method('getSourceReference')
  546. ->will($this->returnValue('ref'));
  547. $oldPackage->expects($this->any())
  548. ->method('getSourceUrls')
  549. ->will($this->returnValue(array('/foo/bar', 'https://github.com/composer/composer')));
  550. $newPackage = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  551. $newPackage->expects($this->any())
  552. ->method('getSourceReference')
  553. ->will($this->returnValue('ref'));
  554. $newPackage->expects($this->any())
  555. ->method('getSourceUrls')
  556. ->will($this->returnValue(array('https://github.com/composer/composer')));
  557. $newPackage->expects($this->any())
  558. ->method('getVersion')
  559. ->will($this->returnValue('1.0.0.0'));
  560. $newPackage->expects($this->any())
  561. ->method('getFullPrettyVersion')
  562. ->will($this->returnValue('1.0.0'));
  563. $processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  564. $processExecutor->expects($this->any())
  565. ->method('execute')
  566. ->will($this->returnValue(0));
  567. $ioMock = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
  568. $ioMock->expects($this->at(0))
  569. ->method('writeError')
  570. ->with($this->stringContains('Downgrading'));
  571. $this->fs->ensureDirectoryExists($this->workingDir.'/.git');
  572. $downloader = $this->getDownloaderMock($ioMock, null, $processExecutor);
  573. $downloader->download($newPackage, $this->workingDir, $oldPackage);
  574. $downloader->prepare('update', $newPackage, $this->workingDir, $oldPackage);
  575. $downloader->update($oldPackage, $newPackage, $this->workingDir);
  576. $downloader->cleanup('update', $newPackage, $this->workingDir, $oldPackage);
  577. }
  578. public function testNotUsingDowngradingWithReferences()
  579. {
  580. $oldPackage = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  581. $oldPackage->expects($this->any())
  582. ->method('getVersion')
  583. ->will($this->returnValue('dev-ref'));
  584. $oldPackage->expects($this->any())
  585. ->method('getSourceReference')
  586. ->will($this->returnValue('ref'));
  587. $oldPackage->expects($this->any())
  588. ->method('getSourceUrls')
  589. ->will($this->returnValue(array('/foo/bar', 'https://github.com/composer/composer')));
  590. $newPackage = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  591. $newPackage->expects($this->any())
  592. ->method('getSourceReference')
  593. ->will($this->returnValue('ref'));
  594. $newPackage->expects($this->any())
  595. ->method('getSourceUrls')
  596. ->will($this->returnValue(array('https://github.com/composer/composer')));
  597. $newPackage->expects($this->any())
  598. ->method('getVersion')
  599. ->will($this->returnValue('dev-ref2'));
  600. $processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  601. $processExecutor->expects($this->any())
  602. ->method('execute')
  603. ->will($this->returnValue(0));
  604. $ioMock = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
  605. $ioMock->expects($this->at(0))
  606. ->method('writeError')
  607. ->with($this->stringContains('Upgrading'));
  608. $this->fs->ensureDirectoryExists($this->workingDir.'/.git');
  609. $downloader = $this->getDownloaderMock($ioMock, null, $processExecutor);
  610. $downloader->download($newPackage, $this->workingDir, $oldPackage);
  611. $downloader->prepare('update', $newPackage, $this->workingDir, $oldPackage);
  612. $downloader->update($oldPackage, $newPackage, $this->workingDir);
  613. $downloader->cleanup('update', $newPackage, $this->workingDir, $oldPackage);
  614. }
  615. public function testRemove()
  616. {
  617. $expectedGitResetCommand = $this->winCompat("cd 'composerPath' && git status --porcelain --untracked-files=no");
  618. $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
  619. $processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  620. $processExecutor->expects($this->any())
  621. ->method('execute')
  622. ->with($this->equalTo($expectedGitResetCommand))
  623. ->will($this->returnValue(0));
  624. $filesystem = $this->getMockBuilder('Composer\Util\Filesystem')->getMock();
  625. $filesystem->expects($this->once())
  626. ->method('removeDirectory')
  627. ->with($this->equalTo('composerPath'))
  628. ->will($this->returnValue(true));
  629. $downloader = $this->getDownloaderMock(null, null, $processExecutor, $filesystem);
  630. $downloader->prepare('uninstall', $packageMock, 'composerPath');
  631. $downloader->remove($packageMock, 'composerPath');
  632. $downloader->cleanup('uninstall', $packageMock, 'composerPath');
  633. }
  634. public function testGetInstallationSource()
  635. {
  636. $downloader = $this->getDownloaderMock();
  637. $this->assertEquals('source', $downloader->getInstallationSource());
  638. }
  639. private function winCompat($cmd)
  640. {
  641. if (Platform::isWindows()) {
  642. $cmd = str_replace('cd ', 'cd /D ', $cmd);
  643. $cmd = str_replace('composerPath', getcwd().'/composerPath', $cmd);
  644. return strtr($cmd, "'", '"');
  645. }
  646. return $cmd;
  647. }
  648. }