EventDispatcherTest.php 16 KB

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