EventDispatcherTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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 testDispatcherCanPutEnv()
  196. {
  197. $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  198. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  199. ->setConstructorArgs(array(
  200. $this->createComposerInstance(),
  201. $io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE),
  202. $process,
  203. ))
  204. ->setMethods(array(
  205. 'getListeners',
  206. ))
  207. ->getMock();
  208. $listeners = array(
  209. '@putenv ABC=123',
  210. 'Composer\\Test\\EventDispatcher\\EventDispatcherTest::getTestEnv',
  211. );
  212. $dispatcher->expects($this->atLeastOnce())
  213. ->method('getListeners')
  214. ->will($this->returnValue($listeners));
  215. $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
  216. $expected = '> post-install-cmd: @putenv ABC=123'.PHP_EOL.
  217. '> post-install-cmd: Composer\Test\EventDispatcher\EventDispatcherTest::getTestEnv'.PHP_EOL;
  218. $this->assertEquals($expected, $io->getOutput());
  219. }
  220. static public function getTestEnv() {
  221. $val = getenv('ABC');
  222. if ($val !== '123') {
  223. throw new \Exception('getenv() did not return the expected value. expected 123 got '. var_export($val, true));
  224. }
  225. }
  226. public function testDispatcherCanExecuteComposerScriptGroups()
  227. {
  228. $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  229. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  230. ->setConstructorArgs(array(
  231. $composer = $this->createComposerInstance(),
  232. $io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE),
  233. $process,
  234. ))
  235. ->setMethods(array(
  236. 'getListeners',
  237. ))
  238. ->getMock();
  239. $process->expects($this->exactly(3))
  240. ->method('execute')
  241. ->will($this->returnValue(0));
  242. $dispatcher->expects($this->atLeastOnce())
  243. ->method('getListeners')
  244. ->will($this->returnCallback(function (Event $event) {
  245. if ($event->getName() === 'root') {
  246. return array('@group');
  247. }
  248. if ($event->getName() === 'group') {
  249. return array('echo -n foo', '@subgroup', 'echo -n bar');
  250. }
  251. if ($event->getName() === 'subgroup') {
  252. return array('echo -n baz');
  253. }
  254. return array();
  255. }));
  256. $dispatcher->dispatch('root', new CommandEvent('root', $composer, $io));
  257. $expected = '> root: @group'.PHP_EOL.
  258. '> group: echo -n foo'.PHP_EOL.
  259. '> group: @subgroup'.PHP_EOL.
  260. '> subgroup: echo -n baz'.PHP_EOL.
  261. '> group: echo -n bar'.PHP_EOL;
  262. $this->assertEquals($expected, $io->getOutput());
  263. }
  264. public function testRecursionInScriptsNames()
  265. {
  266. $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  267. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  268. ->setConstructorArgs(array(
  269. $composer = $this->createComposerInstance(),
  270. $io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE),
  271. $process
  272. ))
  273. ->setMethods(array(
  274. 'getListeners'
  275. ))
  276. ->getMock();
  277. $process->expects($this->exactly(1))
  278. ->method('execute')
  279. ->will($this->returnValue(0));
  280. $dispatcher->expects($this->atLeastOnce())
  281. ->method('getListeners')
  282. ->will($this->returnCallback(function (Event $event) {
  283. if($event->getName() === 'hello') {
  284. return array('echo Hello');
  285. }
  286. if($event->getName() === 'helloWorld') {
  287. return array('@hello World');
  288. }
  289. return array();
  290. }));
  291. $dispatcher->dispatch('helloWorld', new CommandEvent('helloWorld', $composer, $io));
  292. $expected = "> helloWorld: @hello World".PHP_EOL.
  293. "> hello: echo Hello " .escapeshellarg('World').PHP_EOL;
  294. $this->assertEquals($expected, $io->getOutput());
  295. }
  296. /**
  297. * @expectedException RuntimeException
  298. */
  299. public function testDispatcherDetectInfiniteRecursion()
  300. {
  301. $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  302. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  303. ->setConstructorArgs(array(
  304. $composer = $this->createComposerInstance(),
  305. $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
  306. $process,
  307. ))
  308. ->setMethods(array(
  309. 'getListeners',
  310. ))
  311. ->getMock();
  312. $dispatcher->expects($this->atLeastOnce())
  313. ->method('getListeners')
  314. ->will($this->returnCallback(function (Event $event) {
  315. if ($event->getName() === 'root') {
  316. return array('@recurse');
  317. }
  318. if ($event->getName() === 'recurse') {
  319. return array('@root');
  320. }
  321. return array();
  322. }));
  323. $dispatcher->dispatch('root', new CommandEvent('root', $composer, $io));
  324. }
  325. private function getDispatcherStubForListenersTest($listeners, $io)
  326. {
  327. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  328. ->setConstructorArgs(array(
  329. $this->createComposerInstance(),
  330. $io,
  331. ))
  332. ->setMethods(array('getListeners'))
  333. ->getMock();
  334. $dispatcher->expects($this->atLeastOnce())
  335. ->method('getListeners')
  336. ->will($this->returnValue($listeners));
  337. return $dispatcher;
  338. }
  339. public function getValidCommands()
  340. {
  341. return array(
  342. array('phpunit'),
  343. array('echo foo'),
  344. array('echo -n foo'),
  345. );
  346. }
  347. public function testDispatcherOutputsCommand()
  348. {
  349. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  350. ->setConstructorArgs(array(
  351. $this->createComposerInstance(),
  352. $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
  353. new ProcessExecutor($io),
  354. ))
  355. ->setMethods(array('getListeners'))
  356. ->getMock();
  357. $listener = array('echo foo');
  358. $dispatcher->expects($this->atLeastOnce())
  359. ->method('getListeners')
  360. ->will($this->returnValue($listener));
  361. $io->expects($this->once())
  362. ->method('writeError')
  363. ->with($this->equalTo('> echo foo'));
  364. $io->expects($this->once())
  365. ->method('write')
  366. ->with($this->equalTo('foo'.PHP_EOL), false);
  367. $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
  368. }
  369. public function testDispatcherOutputsErrorOnFailedCommand()
  370. {
  371. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  372. ->setConstructorArgs(array(
  373. $this->createComposerInstance(),
  374. $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
  375. new ProcessExecutor,
  376. ))
  377. ->setMethods(array('getListeners'))
  378. ->getMock();
  379. $code = 'exit 1';
  380. $listener = array($code);
  381. $dispatcher->expects($this->atLeastOnce())
  382. ->method('getListeners')
  383. ->will($this->returnValue($listener));
  384. $io->expects($this->at(0))
  385. ->method('isVerbose')
  386. ->willReturn(0);
  387. $io->expects($this->at(1))
  388. ->method('writeError')
  389. ->willReturn('> exit 1');
  390. $io->expects($this->at(2))
  391. ->method('writeError')
  392. ->with($this->equalTo('<error>Script '.$code.' handling the post-install-cmd event returned with error code 1</error>'));
  393. $this->setExpectedException('RuntimeException');
  394. $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
  395. }
  396. public function testDispatcherInstallerEvents()
  397. {
  398. $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
  399. $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
  400. ->setConstructorArgs(array(
  401. $this->createComposerInstance(),
  402. $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
  403. $process,
  404. ))
  405. ->setMethods(array('getListeners'))
  406. ->getMock();
  407. $dispatcher->expects($this->atLeastOnce())
  408. ->method('getListeners')
  409. ->will($this->returnValue(array()));
  410. $policy = $this->getMockBuilder('Composer\DependencyResolver\PolicyInterface')->getMock();
  411. $pool = $this->getMockBuilder('Composer\DependencyResolver\Pool')->disableOriginalConstructor()->getMock();
  412. $installedRepo = $this->getMockBuilder('Composer\Repository\CompositeRepository')->disableOriginalConstructor()->getMock();
  413. $request = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock();
  414. $dispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, true, $policy, $pool, $installedRepo, $request);
  415. $dispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, true, $policy, $pool, $installedRepo, $request, array());
  416. }
  417. public static function call()
  418. {
  419. throw new \RuntimeException();
  420. }
  421. public static function expectsCommandEvent(CommandEvent $event)
  422. {
  423. return false;
  424. }
  425. public static function expectsVariableEvent($event)
  426. {
  427. return false;
  428. }
  429. public static function someMethod()
  430. {
  431. return true;
  432. }
  433. private function createComposerInstance()
  434. {
  435. $composer = new Composer;
  436. $config = new Config;
  437. $composer->setConfig($config);
  438. $package = $this->getMockBuilder('Composer\Package\RootPackageInterface')->getMock();
  439. $composer->setPackage($package);
  440. return $composer;
  441. }
  442. }