EventDispatcherTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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\EventDispatcher;
  12. use Composer\EventDispatcher\Event;
  13. use Composer\EventDispatcher\EventDispatcher;
  14. use Composer\Installer\InstallerEvents;
  15. use Composer\Config;
  16. use Composer\Composer;
  17. use Composer\TestCase;
  18. use Composer\IO\BufferIO;
  19. use Composer\Script\ScriptEvents;
  20. use Composer\Script\Event as ScriptEvent;
  21. use Composer\Util\ProcessExecutor;
  22. use Symfony\Component\Console\Output\OutputInterface;
  23. class EventDispatcherTest extends TestCase
  24. {
  25. /**
  26. * @expectedException RuntimeException
  27. */
  28. public function testListenerExceptionsAreCaught()
  29. {
  30. $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
  31. $dispatcher = $this->getDispatcherStubForListenersTest(array(
  32. 'Composer\Test\EventDispatcher\EventDispatcherTest::call',
  33. ), $io);
  34. $io->expects($this->at(0))
  35. ->method('isVerbose')
  36. ->willReturn(0);
  37. $io->expects($this->at(1))
  38. ->method('writeError')
  39. ->with('> Composer\Test\EventDispatcher\EventDispatcherTest::call');
  40. $io->expects($this->at(2))
  41. ->method('writeError')
  42. ->with('<error>Script Composer\Test\EventDispatcher\EventDispatcherTest::call handling the post-install-cmd event terminated with an exception</error>');
  43. $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
  44. }
  45. /**
  46. * @dataProvider getValidCommands
  47. * @param string $command
  48. */
  49. public function testDispatcherCanExecuteSingleCommandLineScript($command)
  50. {
  51. $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  52. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  53. ->setConstructorArgs(array(
  54. $this->createComposerInstance(),
  55. $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
  56. $process,
  57. ))
  58. ->setMethods(array('getListeners'))
  59. ->getMock();
  60. $listener = array($command);
  61. $dispatcher->expects($this->atLeastOnce())
  62. ->method('getListeners')
  63. ->will($this->returnValue($listener));
  64. $process->expects($this->once())
  65. ->method('execute')
  66. ->with($command)
  67. ->will($this->returnValue(0));
  68. $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
  69. }
  70. /**
  71. * @dataProvider getDevModes
  72. * @param bool $devMode
  73. */
  74. public function testDispatcherPassDevModeToAutoloadGeneratorForScriptEvents($devMode)
  75. {
  76. $composer = $this->createComposerInstance();
  77. $generator = $this->getGeneratorMockForDevModePassingTest();
  78. $generator->expects($this->atLeastOnce())
  79. ->method('setDevMode')
  80. ->with($devMode);
  81. $composer->setAutoloadGenerator($generator);
  82. $package = $this->getMockBuilder('Composer\Package\RootPackageInterface')->getMock();
  83. $package->method('getScripts')->will($this->returnValue(array('scriptName' => array('scriptName'))));
  84. $composer->setPackage($package);
  85. $composer->setRepositoryManager($this->getRepositoryManagerMockForDevModePassingTest());
  86. $composer->setInstallationManager($this->getMockBuilder('Composer\Installer\InstallationManager')->getMock());
  87. $dispatcher = new EventDispatcher(
  88. $composer,
  89. $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
  90. $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock()
  91. );
  92. $event = $this->getMockBuilder('Composer\Script\Event')
  93. ->disableOriginalConstructor()
  94. ->getMock();
  95. $event->method('getName')->will($this->returnValue('scriptName'));
  96. $event->expects($this->atLeastOnce())
  97. ->method('isDevMode')
  98. ->will($this->returnValue($devMode));
  99. $dispatcher->hasEventListeners($event);
  100. }
  101. public function getDevModes()
  102. {
  103. return array(
  104. array(true),
  105. array(false),
  106. );
  107. }
  108. private function getGeneratorMockForDevModePassingTest()
  109. {
  110. $generator = $this->getMockBuilder('Composer\Autoload\AutoloadGenerator')
  111. ->disableOriginalConstructor()
  112. ->setMethods(array(
  113. 'buildPackageMap',
  114. 'parseAutoloads',
  115. 'createLoader',
  116. 'setDevMode',
  117. ))
  118. ->getMock();
  119. $generator
  120. ->method('buildPackageMap')
  121. ->will($this->returnValue(array()));
  122. $generator
  123. ->method('parseAutoloads')
  124. ->will($this->returnValue(array()));
  125. $generator
  126. ->method('createLoader')
  127. ->will($this->returnValue($this->getMockBuilder('Composer\Autoload\ClassLoader')->getMock()));
  128. return $generator;
  129. }
  130. private function getRepositoryManagerMockForDevModePassingTest()
  131. {
  132. $rm = $this->getMockBuilder('Composer\Repository\RepositoryManager')
  133. ->disableOriginalConstructor()
  134. ->setMethods(array('getLocalRepository'))
  135. ->getMock();
  136. $repo = $this->getMockBuilder('Composer\Repository\InstalledRepositoryInterface')->getMock();
  137. $repo
  138. ->method('getCanonicalPackages')
  139. ->will($this->returnValue(array()));
  140. $rm
  141. ->method('getLocalRepository')
  142. ->will($this->returnValue($repo));
  143. return $rm;
  144. }
  145. public function testDispatcherCanExecuteCliAndPhpInSameEventScriptStack()
  146. {
  147. $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  148. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  149. ->setConstructorArgs(array(
  150. $this->createComposerInstance(),
  151. $io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE),
  152. $process,
  153. ))
  154. ->setMethods(array(
  155. 'getListeners',
  156. ))
  157. ->getMock();
  158. $process->expects($this->exactly(2))
  159. ->method('execute')
  160. ->will($this->returnValue(0));
  161. $listeners = array(
  162. 'echo -n foo',
  163. 'Composer\\Test\\EventDispatcher\\EventDispatcherTest::someMethod',
  164. 'echo -n bar',
  165. );
  166. $dispatcher->expects($this->atLeastOnce())
  167. ->method('getListeners')
  168. ->will($this->returnValue($listeners));
  169. $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
  170. $expected = '> post-install-cmd: echo -n foo'.PHP_EOL.
  171. '> post-install-cmd: Composer\Test\EventDispatcher\EventDispatcherTest::someMethod'.PHP_EOL.
  172. '> post-install-cmd: echo -n bar'.PHP_EOL;
  173. $this->assertEquals($expected, $io->getOutput());
  174. }
  175. public function testDispatcherCanExecuteComposerScriptGroups()
  176. {
  177. $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  178. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  179. ->setConstructorArgs(array(
  180. $composer = $this->createComposerInstance(),
  181. $io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE),
  182. $process,
  183. ))
  184. ->setMethods(array(
  185. 'getListeners',
  186. ))
  187. ->getMock();
  188. $process->expects($this->exactly(3))
  189. ->method('execute')
  190. ->will($this->returnValue(0));
  191. $dispatcher->expects($this->atLeastOnce())
  192. ->method('getListeners')
  193. ->will($this->returnCallback(function (Event $event) {
  194. if ($event->getName() === 'root') {
  195. return array('@group');
  196. }
  197. if ($event->getName() === 'group') {
  198. return array('echo -n foo', '@subgroup', 'echo -n bar');
  199. }
  200. if ($event->getName() === 'subgroup') {
  201. return array('echo -n baz');
  202. }
  203. return array();
  204. }));
  205. $dispatcher->dispatch('root', new ScriptEvent('root', $composer, $io));
  206. $expected = '> root: @group'.PHP_EOL.
  207. '> group: echo -n foo'.PHP_EOL.
  208. '> group: @subgroup'.PHP_EOL.
  209. '> subgroup: echo -n baz'.PHP_EOL.
  210. '> group: echo -n bar'.PHP_EOL;
  211. $this->assertEquals($expected, $io->getOutput());
  212. }
  213. /**
  214. * @expectedException RuntimeException
  215. */
  216. public function testDispatcherDetectInfiniteRecursion()
  217. {
  218. $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  219. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  220. ->setConstructorArgs(array(
  221. $composer = $this->createComposerInstance(),
  222. $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
  223. $process,
  224. ))
  225. ->setMethods(array(
  226. 'getListeners',
  227. ))
  228. ->getMock();
  229. $dispatcher->expects($this->atLeastOnce())
  230. ->method('getListeners')
  231. ->will($this->returnCallback(function (Event $event) {
  232. if ($event->getName() === 'root') {
  233. return array('@recurse');
  234. }
  235. if ($event->getName() === 'recurse') {
  236. return array('@root');
  237. }
  238. return array();
  239. }));
  240. $dispatcher->dispatch('root', new ScriptEvent('root', $composer, $io));
  241. }
  242. private function getDispatcherStubForListenersTest($listeners, $io)
  243. {
  244. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  245. ->setConstructorArgs(array(
  246. $this->createComposerInstance(),
  247. $io,
  248. ))
  249. ->setMethods(array('getListeners'))
  250. ->getMock();
  251. $dispatcher->expects($this->atLeastOnce())
  252. ->method('getListeners')
  253. ->will($this->returnValue($listeners));
  254. return $dispatcher;
  255. }
  256. public function getValidCommands()
  257. {
  258. return array(
  259. array('phpunit'),
  260. array('echo foo'),
  261. array('echo -n foo'),
  262. );
  263. }
  264. public function testDispatcherOutputsCommand()
  265. {
  266. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  267. ->setConstructorArgs(array(
  268. $this->createComposerInstance(),
  269. $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
  270. new ProcessExecutor($io),
  271. ))
  272. ->setMethods(array('getListeners'))
  273. ->getMock();
  274. $listener = array('echo foo');
  275. $dispatcher->expects($this->atLeastOnce())
  276. ->method('getListeners')
  277. ->will($this->returnValue($listener));
  278. $io->expects($this->once())
  279. ->method('writeError')
  280. ->with($this->equalTo('> echo foo'));
  281. $io->expects($this->once())
  282. ->method('write')
  283. ->with($this->equalTo('foo'.PHP_EOL), false);
  284. $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
  285. }
  286. public function testDispatcherOutputsErrorOnFailedCommand()
  287. {
  288. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  289. ->setConstructorArgs(array(
  290. $this->createComposerInstance(),
  291. $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
  292. new ProcessExecutor,
  293. ))
  294. ->setMethods(array('getListeners'))
  295. ->getMock();
  296. $code = 'exit 1';
  297. $listener = array($code);
  298. $dispatcher->expects($this->atLeastOnce())
  299. ->method('getListeners')
  300. ->will($this->returnValue($listener));
  301. $io->expects($this->at(0))
  302. ->method('isVerbose')
  303. ->willReturn(0);
  304. $io->expects($this->at(1))
  305. ->method('writeError')
  306. ->willReturn('> exit 1');
  307. $io->expects($this->at(2))
  308. ->method('writeError')
  309. ->with($this->equalTo('<error>Script '.$code.' handling the post-install-cmd event returned with error code 1</error>'));
  310. $this->setExpectedException('RuntimeException');
  311. $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
  312. }
  313. public function testDispatcherInstallerEvents()
  314. {
  315. $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  316. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  317. ->setConstructorArgs(array(
  318. $this->createComposerInstance(),
  319. $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
  320. $process,
  321. ))
  322. ->setMethods(array('getListeners'))
  323. ->getMock();
  324. $dispatcher->expects($this->atLeastOnce())
  325. ->method('getListeners')
  326. ->will($this->returnValue(array()));
  327. $policy = $this->getMockBuilder('Composer\DependencyResolver\PolicyInterface')->getMock();
  328. $repositorySet = $this->getMockBuilder('Composer\Repository\RepositorySet')->disableOriginalConstructor()->getMock();
  329. $installedRepo = $this->getMockBuilder('Composer\Repository\CompositeRepository')->disableOriginalConstructor()->getMock();
  330. $request = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock();
  331. $dispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, true, $policy, $repositorySet, $installedRepo, $request);
  332. $dispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, true, $policy, $repositorySet, $installedRepo, $request, array());
  333. }
  334. public static function call()
  335. {
  336. throw new \RuntimeException();
  337. }
  338. public static function someMethod()
  339. {
  340. return true;
  341. }
  342. private function createComposerInstance()
  343. {
  344. $composer = new Composer;
  345. $config = new Config;
  346. $composer->setConfig($config);
  347. $package = $this->getMockBuilder('Composer\Package\RootPackageInterface')->getMock();
  348. $composer->setPackage($package);
  349. return $composer;
  350. }
  351. }