EventDispatcherTest.php 17 KB

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