ConfigurationResolverTest.php 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167
  1. <?php
  2. /*
  3. * This file is part of PHP CS Fixer.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  7. *
  8. * This source file is subject to the MIT license that is bundled
  9. * with this source code in the file LICENSE.
  10. */
  11. namespace PhpCsFixer\Tests\Console;
  12. use PhpCsFixer\Config;
  13. use PhpCsFixer\ConfigurationException\InvalidConfigurationException;
  14. use PhpCsFixer\Console\Command\FixCommand;
  15. use PhpCsFixer\Console\ConfigurationResolver;
  16. use PhpCsFixer\Finder;
  17. use PHPUnit\Framework\TestCase;
  18. use Symfony\Component\Console\Output\OutputInterface;
  19. /**
  20. * @author Katsuhiro Ogawa <ko.fivestar@gmail.com>
  21. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  22. *
  23. * @internal
  24. *
  25. * @covers \PhpCsFixer\Console\ConfigurationResolver
  26. */
  27. final class ConfigurationResolverTest extends TestCase
  28. {
  29. /**
  30. * @var Config
  31. */
  32. private $config;
  33. protected function setUp()
  34. {
  35. parent::setUp();
  36. $this->config = new Config();
  37. }
  38. protected function tearDown()
  39. {
  40. parent::tearDown();
  41. unset($this->config);
  42. }
  43. public function testSetOptionWithUndefinedOption()
  44. {
  45. $this->expectException(InvalidConfigurationException::class);
  46. $this->expectExceptionMessageRegExp('/^Unknown option name: "foo"\.$/');
  47. new ConfigurationResolver(
  48. $this->config,
  49. ['foo' => 'bar'],
  50. ''
  51. );
  52. }
  53. public function testResolveProgressWithPositiveConfigAndPositiveOption()
  54. {
  55. $this->config->setHideProgress(true);
  56. $resolver = new ConfigurationResolver(
  57. $this->config,
  58. [
  59. 'format' => 'txt',
  60. 'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
  61. ],
  62. ''
  63. );
  64. $this->assertSame('none', $resolver->getProgress());
  65. }
  66. public function testResolveProgressWithPositiveConfigAndNegativeOption()
  67. {
  68. $this->config->setHideProgress(true);
  69. $resolver = new ConfigurationResolver(
  70. $this->config,
  71. [
  72. 'format' => 'txt',
  73. 'verbosity' => OutputInterface::VERBOSITY_NORMAL,
  74. ],
  75. ''
  76. );
  77. $this->assertSame('none', $resolver->getProgress());
  78. }
  79. public function testResolveProgressWithNegativeConfigAndPositiveOption()
  80. {
  81. $this->config->setHideProgress(false);
  82. $resolver = new ConfigurationResolver(
  83. $this->config,
  84. [
  85. 'format' => 'txt',
  86. 'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
  87. ],
  88. ''
  89. );
  90. $this->assertSame('run-in', $resolver->getProgress());
  91. }
  92. public function testResolveProgressWithNegativeConfigAndNegativeOption()
  93. {
  94. $this->config->setHideProgress(false);
  95. $resolver = new ConfigurationResolver(
  96. $this->config,
  97. [
  98. 'format' => 'txt',
  99. 'verbosity' => OutputInterface::VERBOSITY_NORMAL,
  100. ],
  101. ''
  102. );
  103. $this->assertSame('none', $resolver->getProgress());
  104. }
  105. /**
  106. * @param string $progressType
  107. *
  108. * @dataProvider provideProgressTypeCases
  109. */
  110. public function testResolveProgressWithPositiveConfigAndExplicitProgress($progressType)
  111. {
  112. $this->config->setHideProgress(true);
  113. $resolver = new ConfigurationResolver(
  114. $this->config,
  115. [
  116. 'format' => 'txt',
  117. 'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
  118. 'show-progress' => $progressType,
  119. ],
  120. ''
  121. );
  122. $this->assertSame($progressType, $resolver->getProgress());
  123. }
  124. /**
  125. * @param string $progressType
  126. *
  127. * @dataProvider provideProgressTypeCases
  128. */
  129. public function testResolveProgressWithNegativeConfigAndExplicitProgress($progressType)
  130. {
  131. $this->config->setHideProgress(false);
  132. $resolver = new ConfigurationResolver(
  133. $this->config,
  134. [
  135. 'format' => 'txt',
  136. 'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
  137. 'show-progress' => $progressType,
  138. ],
  139. ''
  140. );
  141. $this->assertSame($progressType, $resolver->getProgress());
  142. }
  143. public function provideProgressTypeCases()
  144. {
  145. return [
  146. ['none'],
  147. ['run-in'],
  148. ['estimating'],
  149. ['estimating-max'],
  150. ];
  151. }
  152. public function testResolveProgressWithInvalidExplicitProgress()
  153. {
  154. $resolver = new ConfigurationResolver(
  155. $this->config,
  156. [
  157. 'format' => 'txt',
  158. 'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
  159. 'show-progress' => 'foo',
  160. ],
  161. ''
  162. );
  163. $this->expectException(InvalidConfigurationException::class);
  164. $this->expectExceptionMessage('The progress type "foo" is not defined, supported are "none", "run-in", "estimating", "estimating-max".');
  165. $resolver->getProgress();
  166. }
  167. public function testResolveConfigFileDefault()
  168. {
  169. $resolver = new ConfigurationResolver(
  170. $this->config,
  171. [],
  172. ''
  173. );
  174. $this->assertNull($resolver->getConfigFile());
  175. $this->assertInstanceOf(\PhpCsFixer\ConfigInterface::class, $resolver->getConfig());
  176. }
  177. public function testResolveConfigFileByPathOfFile()
  178. {
  179. $dir = __DIR__.'/../Fixtures/ConfigurationResolverConfigFile/case_1';
  180. $resolver = new ConfigurationResolver(
  181. $this->config,
  182. ['path' => [$dir.DIRECTORY_SEPARATOR.'foo.php']],
  183. ''
  184. );
  185. $this->assertSame($dir.DIRECTORY_SEPARATOR.'.php_cs.dist', $resolver->getConfigFile());
  186. $this->assertInstanceOf('Test1Config', $resolver->getConfig());
  187. }
  188. public function testResolveConfigFileSpecified()
  189. {
  190. $file = __DIR__.'/../Fixtures/ConfigurationResolverConfigFile/case_4/my.php_cs';
  191. $resolver = new ConfigurationResolver(
  192. $this->config,
  193. ['config' => $file],
  194. ''
  195. );
  196. $this->assertSame($file, $resolver->getConfigFile());
  197. $this->assertInstanceOf('Test4Config', $resolver->getConfig());
  198. }
  199. /**
  200. * @param string $expectedFile
  201. * @param string $expectedClass
  202. * @param string $path
  203. * @param null|string $cwdPath
  204. *
  205. * @dataProvider provideResolveConfigFileDefaultCases
  206. */
  207. public function testResolveConfigFileChooseFile($expectedFile, $expectedClass, $path, $cwdPath = null)
  208. {
  209. $resolver = new ConfigurationResolver(
  210. $this->config,
  211. ['path' => [$path]],
  212. $cwdPath
  213. );
  214. $this->assertSame($expectedFile, $resolver->getConfigFile());
  215. $this->assertInstanceOf($expectedClass, $resolver->getConfig());
  216. }
  217. public function provideResolveConfigFileDefaultCases()
  218. {
  219. $dirBase = $this->getFixtureDir();
  220. return [
  221. [
  222. $dirBase.'case_1'.DIRECTORY_SEPARATOR.'.php_cs.dist',
  223. 'Test1Config',
  224. $dirBase.'case_1',
  225. ],
  226. [
  227. $dirBase.'case_2'.DIRECTORY_SEPARATOR.'.php_cs',
  228. 'Test2Config',
  229. $dirBase.'case_2',
  230. ],
  231. [
  232. $dirBase.'case_3'.DIRECTORY_SEPARATOR.'.php_cs',
  233. 'Test3Config',
  234. $dirBase.'case_3',
  235. ],
  236. [
  237. $dirBase.'case_6'.DIRECTORY_SEPARATOR.'.php_cs.dist',
  238. 'Test6Config',
  239. $dirBase.'case_6'.DIRECTORY_SEPARATOR.'subdir',
  240. $dirBase.'case_6',
  241. ],
  242. [
  243. $dirBase.'case_6'.DIRECTORY_SEPARATOR.'.php_cs.dist',
  244. 'Test6Config',
  245. $dirBase.'case_6'.DIRECTORY_SEPARATOR.'subdir/empty_file.php',
  246. $dirBase.'case_6',
  247. ],
  248. ];
  249. }
  250. public function testResolveConfigFileChooseFileWithInvalidFile()
  251. {
  252. $this->expectException(InvalidConfigurationException::class);
  253. $this->expectExceptionMessageRegExp(
  254. '#^The config file: ".+[\/\\\]Fixtures[\/\\\]ConfigurationResolverConfigFile[\/\\\]case_5[\/\\\]\.php_cs\.dist" does not return a "PhpCsFixer\\\ConfigInterface" instance\. Got: "string"\.$#'
  255. );
  256. $dirBase = $this->getFixtureDir();
  257. $resolver = new ConfigurationResolver(
  258. $this->config,
  259. ['path' => [$dirBase.'case_5']],
  260. ''
  261. );
  262. $resolver->getConfig();
  263. }
  264. public function testResolveConfigFileChooseFileWithInvalidFormat()
  265. {
  266. $this->expectException(InvalidConfigurationException::class);
  267. $this->expectExceptionMessageRegExp('/^The format "xls" is not defined, supported are "json", "junit", "txt", "xml"\.$/');
  268. $dirBase = $this->getFixtureDir();
  269. $resolver = new ConfigurationResolver(
  270. $this->config,
  271. ['path' => [$dirBase.'case_7']],
  272. ''
  273. );
  274. $resolver->getReporter();
  275. }
  276. public function testResolveConfigFileChooseFileWithPathArrayWithoutConfig()
  277. {
  278. $this->expectException(InvalidConfigurationException::class);
  279. $this->expectExceptionMessageRegExp('/^For multiple paths config parameter is required\.$/');
  280. $dirBase = $this->getFixtureDir();
  281. $resolver = new ConfigurationResolver(
  282. $this->config,
  283. ['path' => [$dirBase.'case_1/.php_cs.dist', $dirBase.'case_1/foo.php']],
  284. ''
  285. );
  286. $resolver->getConfig();
  287. }
  288. public function testResolveConfigFileChooseFileWithPathArrayAndConfig()
  289. {
  290. $dirBase = $this->getFixtureDir();
  291. $resolver = new ConfigurationResolver(
  292. $this->config,
  293. [
  294. 'config' => $dirBase.'case_1/.php_cs.dist',
  295. 'path' => [$dirBase.'case_1/.php_cs.dist', $dirBase.'case_1/foo.php'],
  296. ],
  297. ''
  298. );
  299. $this->assertInstanceOf(\PhpCsFixer\Console\ConfigurationResolver::class, $resolver);
  300. }
  301. public function testResolvePathRelativeA()
  302. {
  303. $resolver = new ConfigurationResolver(
  304. $this->config,
  305. ['path' => ['Command']],
  306. __DIR__
  307. );
  308. $this->assertSame([__DIR__.DIRECTORY_SEPARATOR.'Command'], $resolver->getPath());
  309. }
  310. public function testResolvePathRelativeB()
  311. {
  312. $resolver = new ConfigurationResolver(
  313. $this->config,
  314. ['path' => [basename(__DIR__)]],
  315. dirname(__DIR__)
  316. );
  317. $this->assertSame([__DIR__], $resolver->getPath());
  318. }
  319. public function testResolvePathWithFileThatIsExcludedDirectlyOverridePathMode()
  320. {
  321. $this->config->getFinder()
  322. ->in(__DIR__)
  323. ->notPath(basename(__FILE__));
  324. $resolver = new ConfigurationResolver(
  325. $this->config,
  326. ['path' => [__FILE__]],
  327. ''
  328. );
  329. $this->assertCount(1, $resolver->getFinder());
  330. }
  331. public function testResolvePathWithFileThatIsExcludedDirectlyIntersectionPathMode()
  332. {
  333. $this->config->getFinder()
  334. ->in(__DIR__)
  335. ->notPath(basename(__FILE__));
  336. $resolver = new ConfigurationResolver(
  337. $this->config,
  338. [
  339. 'path' => [__FILE__],
  340. 'path-mode' => 'intersection',
  341. ],
  342. ''
  343. );
  344. $this->assertCount(0, $resolver->getFinder());
  345. }
  346. public function testResolvePathWithFileThatIsExcludedByDirOverridePathMode()
  347. {
  348. $dir = dirname(__DIR__);
  349. $this->config->getFinder()
  350. ->in($dir)
  351. ->exclude(basename(__DIR__));
  352. $resolver = new ConfigurationResolver(
  353. $this->config,
  354. ['path' => [__FILE__]],
  355. ''
  356. );
  357. $this->assertCount(1, $resolver->getFinder());
  358. }
  359. public function testResolvePathWithFileThatIsExcludedByDirIntersectionPathMode()
  360. {
  361. $dir = dirname(__DIR__);
  362. $this->config->getFinder()
  363. ->in($dir)
  364. ->exclude(basename(__DIR__));
  365. $resolver = new ConfigurationResolver(
  366. $this->config,
  367. [
  368. 'path-mode' => 'intersection',
  369. 'path' => [__FILE__],
  370. ],
  371. ''
  372. );
  373. $this->assertCount(0, $resolver->getFinder());
  374. }
  375. public function testResolvePathWithFileThatIsNotExcluded()
  376. {
  377. $dir = __DIR__;
  378. $this->config->getFinder()
  379. ->in($dir)
  380. ->notPath('foo-'.basename(__FILE__));
  381. $resolver = new ConfigurationResolver(
  382. $this->config,
  383. ['path' => [__FILE__]],
  384. ''
  385. );
  386. $this->assertCount(1, $resolver->getFinder());
  387. }
  388. /**
  389. * @param array|\Exception $expected
  390. * @param null|Finder $configFinder
  391. * @param string $pathMode
  392. * @param null|string $config
  393. *
  394. * @dataProvider provideResolveIntersectionOfPathsCases
  395. */
  396. public function testResolveIntersectionOfPaths($expected, $configFinder, array $path, $pathMode, $config = null)
  397. {
  398. if ($expected instanceof \Exception) {
  399. $this->expectException(get_class($expected));
  400. }
  401. if (null !== $configFinder) {
  402. $this->config->setFinder($configFinder);
  403. }
  404. $resolver = new ConfigurationResolver(
  405. $this->config,
  406. [
  407. 'config' => $config,
  408. 'path' => $path,
  409. 'path-mode' => $pathMode,
  410. ],
  411. ''
  412. );
  413. $intersectionItems = array_map(
  414. function (\SplFileInfo $file) {
  415. return $file->getRealPath();
  416. },
  417. iterator_to_array($resolver->getFinder(), false)
  418. );
  419. sort($expected);
  420. sort($intersectionItems);
  421. $this->assertSame($expected, $intersectionItems);
  422. }
  423. public function provideResolveIntersectionOfPathsCases()
  424. {
  425. $dir = __DIR__.'/../Fixtures/ConfigurationResolverPathsIntersection';
  426. $cb = function (array $items) use ($dir) {
  427. return array_map(
  428. function ($item) use ($dir) {
  429. return realpath($dir.'/'.$item);
  430. },
  431. $items
  432. );
  433. };
  434. return [
  435. 'no path at all' => [
  436. new \LogicException(),
  437. Finder::create(),
  438. [],
  439. 'override',
  440. ],
  441. 'configured only by finder' => [
  442. // don't override if the argument is empty
  443. $cb(['a1.php', 'a2.php', 'b/b1.php', 'b/b2.php', 'b_b/b_b1.php', 'c/c1.php', 'c/d/cd1.php', 'd/d1.php', 'd/d2.php', 'd/e/de1.php', 'd/f/df1.php']),
  444. Finder::create()
  445. ->in($dir),
  446. [],
  447. 'override',
  448. ],
  449. 'configured only by argument' => [
  450. $cb(['a1.php', 'a2.php', 'b/b1.php', 'b/b2.php', 'b_b/b_b1.php', 'c/c1.php', 'c/d/cd1.php', 'd/d1.php', 'd/d2.php', 'd/e/de1.php', 'd/f/df1.php']),
  451. Finder::create(),
  452. [$dir],
  453. 'override',
  454. ],
  455. 'configured by finder, intersected with empty argument' => [
  456. [],
  457. Finder::create()
  458. ->in($dir),
  459. [],
  460. 'intersection',
  461. ],
  462. 'configured by finder, intersected with dir' => [
  463. $cb(['c/c1.php', 'c/d/cd1.php']),
  464. Finder::create()
  465. ->in($dir),
  466. [$dir.'/c'],
  467. 'intersection',
  468. ],
  469. 'configured by finder, intersected with file' => [
  470. $cb(['c/c1.php']),
  471. Finder::create()
  472. ->in($dir),
  473. [$dir.'/c/c1.php'],
  474. 'intersection',
  475. ],
  476. 'finder points to one dir while argument to another, not connected' => [
  477. [],
  478. Finder::create()
  479. ->in($dir.'/b'),
  480. [$dir.'/c'],
  481. 'intersection',
  482. ],
  483. 'finder with excluded dir, intersected with excluded file' => [
  484. [],
  485. Finder::create()
  486. ->in($dir)
  487. ->exclude('c'),
  488. [$dir.'/c/d/cd1.php'],
  489. 'intersection',
  490. ],
  491. 'finder with excluded dir, intersected with dir containing excluded one' => [
  492. $cb(['c/c1.php']),
  493. Finder::create()
  494. ->in($dir)
  495. ->exclude('c/d'),
  496. [$dir.'/c'],
  497. 'intersection',
  498. ],
  499. 'finder with excluded file, intersected with dir containing excluded one' => [
  500. $cb(['c/d/cd1.php']),
  501. Finder::create()
  502. ->in($dir)
  503. ->notPath('c/c1.php'),
  504. [$dir.'/c'],
  505. 'intersection',
  506. ],
  507. 'configured by finder, intersected with non-existing path' => [
  508. new \LogicException(),
  509. Finder::create()
  510. ->in($dir),
  511. ['non_existing_dir'],
  512. 'intersection',
  513. ],
  514. 'configured by config file, overriden by multiple files' => [
  515. $cb(['d/d1.php', 'd/d2.php']),
  516. null,
  517. [$dir.'/d/d1.php', $dir.'/d/d2.php'],
  518. 'override',
  519. $dir.'/d/.php_cs',
  520. ],
  521. 'configured by config file, intersected with multiple files' => [
  522. $cb(['d/d1.php', 'd/d2.php']),
  523. null,
  524. [$dir.'/d/d1.php', $dir.'/d/d2.php'],
  525. 'intersection',
  526. $dir.'/d/.php_cs',
  527. ],
  528. 'configured by config file, overriden by non-existing dir' => [
  529. new \LogicException(),
  530. null,
  531. [$dir.'/d/fff'],
  532. 'override',
  533. $dir.'/d/.php_cs',
  534. ],
  535. 'configured by config file, intersected with non-existing dir' => [
  536. new \LogicException(),
  537. null,
  538. [$dir.'/d/fff'],
  539. 'intersection',
  540. $dir.'/d/.php_cs',
  541. ],
  542. 'configured by config file, overriden by non-existing file' => [
  543. new \LogicException(),
  544. null,
  545. [$dir.'/d/fff.php'],
  546. 'override',
  547. $dir.'/d/.php_cs',
  548. ],
  549. 'configured by config file, intersected with non-existing file' => [
  550. new \LogicException(),
  551. null,
  552. [$dir.'/d/fff.php'],
  553. 'intersection',
  554. $dir.'/d/.php_cs',
  555. ],
  556. 'configured by config file, overriden by multiple files and dirs' => [
  557. $cb(['d/d1.php', 'd/e/de1.php', 'd/f/df1.php']),
  558. null,
  559. [$dir.'/d/d1.php', $dir.'/d/e', $dir.'/d/f/'],
  560. 'override',
  561. $dir.'/d/.php_cs',
  562. ],
  563. 'configured by config file, intersected with multiple files and dirs' => [
  564. $cb(['d/d1.php', 'd/e/de1.php', 'd/f/df1.php']),
  565. null,
  566. [$dir.'/d/d1.php', $dir.'/d/e', $dir.'/d/f/'],
  567. 'intersection',
  568. $dir.'/d/.php_cs',
  569. ],
  570. ];
  571. }
  572. /**
  573. * @param array $options
  574. * @param bool $expectedResult
  575. *
  576. * @dataProvider provideConfigFinderIsOverriddenCases
  577. */
  578. public function testConfigFinderIsOverridden(array $options, $expectedResult)
  579. {
  580. $resolver = new ConfigurationResolver($this->config, $options, '');
  581. $this->assertSame($expectedResult, $resolver->configFinderIsOverridden());
  582. $resolver = new ConfigurationResolver($this->config, $options, '');
  583. $resolver->getFinder();
  584. $this->assertSame($expectedResult, $resolver->configFinderIsOverridden());
  585. }
  586. public function provideConfigFinderIsOverriddenCases()
  587. {
  588. $root = __DIR__.'/../..';
  589. return [
  590. [
  591. [
  592. 'config' => $root.'/.php_cs.dist',
  593. ],
  594. false,
  595. ],
  596. [
  597. [
  598. 'config' => $root.'/.php_cs.dist',
  599. 'path' => [$root.'/src'],
  600. ],
  601. true,
  602. ],
  603. [
  604. [],
  605. false,
  606. ],
  607. [
  608. [
  609. 'path' => [$root.'/src'],
  610. ],
  611. false,
  612. ],
  613. [
  614. [
  615. 'config' => $root.'/.php_cs.dist',
  616. 'path' => [$root.'/src'],
  617. 'path-mode' => ConfigurationResolver::PATH_MODE_INTERSECTION,
  618. ],
  619. false,
  620. ],
  621. // scenario when loaded config is not setting custom finder
  622. [
  623. [
  624. 'config' => $root.'/tests/Fixtures/ConfigurationResolverConfigFile/case_3/.php_cs.dist',
  625. 'path' => [$root.'/src'],
  626. ],
  627. false,
  628. ],
  629. // scenario when loaded config contains not fully defined finder
  630. [
  631. [
  632. 'config' => $root.'/tests/Fixtures/ConfigurationResolverConfigFile/case_9/.php_cs',
  633. 'path' => [$root.'/src'],
  634. ],
  635. false,
  636. ],
  637. ];
  638. }
  639. public function testResolveIsDryRunViaStdIn()
  640. {
  641. $resolver = new ConfigurationResolver(
  642. $this->config,
  643. [
  644. 'dry-run' => false,
  645. 'path' => ['-'],
  646. ],
  647. ''
  648. );
  649. $this->assertTrue($resolver->isDryRun());
  650. }
  651. public function testResolveIsDryRunViaNegativeOption()
  652. {
  653. $resolver = new ConfigurationResolver(
  654. $this->config,
  655. ['dry-run' => false],
  656. ''
  657. );
  658. $this->assertFalse($resolver->isDryRun());
  659. }
  660. public function testResolveIsDryRunViaPositiveOption()
  661. {
  662. $resolver = new ConfigurationResolver(
  663. $this->config,
  664. ['dry-run' => true],
  665. ''
  666. );
  667. $this->assertTrue($resolver->isDryRun());
  668. }
  669. /**
  670. * @param bool $expected
  671. * @param bool $configValue
  672. * @param null|bool|string $passed
  673. *
  674. * @dataProvider provideResolveBooleanOptionCases
  675. */
  676. public function testResolveUsingCacheWithConfigOption($expected, $configValue, $passed)
  677. {
  678. $this->config->setUsingCache($configValue);
  679. $resolver = new ConfigurationResolver(
  680. $this->config,
  681. ['using-cache' => $passed],
  682. ''
  683. );
  684. $this->assertSame($expected, $resolver->getUsingCache());
  685. }
  686. public function testResolveUsingCacheWithPositiveConfigAndNoOption()
  687. {
  688. $this->config->setUsingCache(true);
  689. $resolver = new ConfigurationResolver(
  690. $this->config,
  691. [],
  692. ''
  693. );
  694. $this->assertTrue($resolver->getUsingCache());
  695. }
  696. public function testResolveUsingCacheWithNegativeConfigAndNoOption()
  697. {
  698. $this->config->setUsingCache(false);
  699. $resolver = new ConfigurationResolver(
  700. $this->config,
  701. [],
  702. ''
  703. );
  704. $this->assertFalse($resolver->getUsingCache());
  705. }
  706. public function testResolveCacheFileWithoutConfigAndOption()
  707. {
  708. $default = $this->config->getCacheFile();
  709. $resolver = new ConfigurationResolver(
  710. $this->config,
  711. [],
  712. ''
  713. );
  714. $this->assertSame($default, $resolver->getCacheFile());
  715. }
  716. public function testResolveCacheFileWithConfig()
  717. {
  718. $cacheFile = 'foo/bar.baz';
  719. $this->config->setCacheFile($cacheFile);
  720. $resolver = new ConfigurationResolver(
  721. $this->config,
  722. [],
  723. ''
  724. );
  725. $this->assertSame($cacheFile, $resolver->getCacheFile());
  726. }
  727. public function testResolveCacheFileWithOption()
  728. {
  729. $cacheFile = 'bar.baz';
  730. $this->config->setCacheFile($cacheFile);
  731. $resolver = new ConfigurationResolver(
  732. $this->config,
  733. ['cache-file' => $cacheFile],
  734. ''
  735. );
  736. $this->assertSame($cacheFile, $resolver->getCacheFile());
  737. }
  738. public function testResolveCacheFileWithConfigAndOption()
  739. {
  740. $configCacheFile = 'foo/bar.baz';
  741. $optionCacheFile = 'bar.baz';
  742. $this->config->setCacheFile($configCacheFile);
  743. $resolver = new ConfigurationResolver(
  744. $this->config,
  745. ['cache-file' => $optionCacheFile],
  746. ''
  747. );
  748. $this->assertSame($optionCacheFile, $resolver->getCacheFile());
  749. }
  750. /**
  751. * @param bool $expected
  752. * @param bool $configValue
  753. * @param null|bool|string $passed
  754. *
  755. * @dataProvider provideResolveBooleanOptionCases
  756. */
  757. public function testResolveAllowRiskyWithConfigOption($expected, $configValue, $passed)
  758. {
  759. $this->config->setRiskyAllowed($configValue);
  760. $resolver = new ConfigurationResolver(
  761. $this->config,
  762. ['allow-risky' => $passed],
  763. ''
  764. );
  765. $this->assertSame($expected, $resolver->getRiskyAllowed());
  766. }
  767. public function testResolveAllowRiskyWithNegativeConfigAndPositiveOption()
  768. {
  769. $this->config->setRiskyAllowed(false);
  770. $resolver = new ConfigurationResolver(
  771. $this->config,
  772. ['allow-risky' => 'yes'],
  773. ''
  774. );
  775. $this->assertTrue($resolver->getRiskyAllowed());
  776. }
  777. public function testResolveAllowRiskyWithNegativeConfigAndNegativeOption()
  778. {
  779. $this->config->setRiskyAllowed(false);
  780. $resolver = new ConfigurationResolver(
  781. $this->config,
  782. ['allow-risky' => 'no'],
  783. ''
  784. );
  785. $this->assertFalse($resolver->getRiskyAllowed());
  786. }
  787. public function testResolveAllowRiskyWithPositiveConfigAndNoOption()
  788. {
  789. $this->config->setRiskyAllowed(true);
  790. $resolver = new ConfigurationResolver(
  791. $this->config,
  792. [],
  793. ''
  794. );
  795. $this->assertTrue($resolver->getRiskyAllowed());
  796. }
  797. public function testResolveAllowRiskyWithNegativeConfigAndNoOption()
  798. {
  799. $this->config->setRiskyAllowed(false);
  800. $resolver = new ConfigurationResolver(
  801. $this->config,
  802. [],
  803. ''
  804. );
  805. $this->assertFalse($resolver->getRiskyAllowed());
  806. }
  807. public function testResolveRulesWithConfig()
  808. {
  809. $this->config->setRules([
  810. 'braces' => true,
  811. 'strict_comparison' => false,
  812. ]);
  813. $resolver = new ConfigurationResolver(
  814. $this->config,
  815. [],
  816. ''
  817. );
  818. $this->assertSameRules(
  819. [
  820. 'braces' => true,
  821. ],
  822. $resolver->getRules()
  823. );
  824. }
  825. public function testResolveRulesWithOption()
  826. {
  827. $resolver = new ConfigurationResolver(
  828. $this->config,
  829. ['rules' => 'braces,-strict_comparison'],
  830. ''
  831. );
  832. $this->assertSameRules(
  833. [
  834. 'braces' => true,
  835. ],
  836. $resolver->getRules()
  837. );
  838. }
  839. public function testResolveRulesWithUnknownRules()
  840. {
  841. $this->expectException(
  842. \PhpCsFixer\ConfigurationException\InvalidConfigurationException::class
  843. );
  844. $this->expectExceptionMessage(
  845. 'The rules contain unknown fixers: "bar", "binary_operator_space" (did you mean "binary_operator_spaces"?).'
  846. );
  847. $resolver = new ConfigurationResolver(
  848. $this->config,
  849. ['rules' => 'braces,-bar,binary_operator_space'],
  850. ''
  851. );
  852. $resolver->getRules();
  853. }
  854. public function testResolveRulesWithConfigAndOption()
  855. {
  856. $this->config->setRules([
  857. 'braces' => true,
  858. 'strict_comparison' => false,
  859. ]);
  860. $resolver = new ConfigurationResolver(
  861. $this->config,
  862. ['rules' => 'blank_line_before_statement'],
  863. ''
  864. );
  865. $this->assertSameRules(
  866. [
  867. 'blank_line_before_statement' => true,
  868. ],
  869. $resolver->getRules()
  870. );
  871. }
  872. public function testResolveCommandLineInputOverridesDefault()
  873. {
  874. $command = new FixCommand();
  875. $definition = $command->getDefinition();
  876. $arguments = $definition->getArguments();
  877. $this->assertCount(1, $arguments, 'Expected one argument, possibly test needs updating.');
  878. $this->assertArrayHasKey('path', $arguments);
  879. $options = $definition->getOptions();
  880. $this->assertSame(
  881. ['path-mode', 'allow-risky', 'config', 'dry-run', 'rules', 'using-cache', 'cache-file', 'diff', 'format', 'stop-on-violation', 'show-progress'],
  882. array_keys($options),
  883. 'Expected options mismatch, possibly test needs updating.'
  884. );
  885. $resolver = new ConfigurationResolver(
  886. $this->config,
  887. [
  888. 'path-mode' => 'intersection',
  889. 'allow-risky' => 'yes',
  890. 'config' => null,
  891. 'dry-run' => true,
  892. 'rules' => 'php_unit_construct',
  893. 'using-cache' => false,
  894. 'diff' => true,
  895. 'format' => 'json',
  896. 'stop-on-violation' => true,
  897. ],
  898. ''
  899. );
  900. $this->assertTrue($resolver->shouldStopOnViolation());
  901. $this->assertTrue($resolver->getRiskyAllowed());
  902. $this->assertTrue($resolver->isDryRun());
  903. $this->assertSame(['php_unit_construct' => true], $resolver->getRules());
  904. $this->assertFalse($resolver->getUsingCache());
  905. $this->assertNull($resolver->getCacheFile());
  906. $this->assertInstanceOf(\PhpCsFixer\Differ\SebastianBergmannDiffer::class, $resolver->getDiffer());
  907. $this->assertSame('json', $resolver->getReporter()->getFormat());
  908. }
  909. /**
  910. * @param string $expected
  911. * @param bool|string $differConfig
  912. *
  913. * @dataProvider provideDifferCases
  914. */
  915. public function testResolveDiffer($expected, $differConfig)
  916. {
  917. $resolver = new ConfigurationResolver(
  918. $this->config,
  919. ['diff' => $differConfig],
  920. ''
  921. );
  922. $this->assertInstanceOf($expected, $resolver->getDiffer());
  923. }
  924. public function provideDifferCases()
  925. {
  926. return [
  927. [
  928. \PhpCsFixer\Differ\NullDiffer::class,
  929. false,
  930. ],
  931. [
  932. \PhpCsFixer\Differ\SebastianBergmannDiffer::class,
  933. true,
  934. ],
  935. ];
  936. }
  937. public function testResolveConfigFileOverridesDefault()
  938. {
  939. $dir = __DIR__.'/../Fixtures/ConfigurationResolverConfigFile/case_8';
  940. $resolver = new ConfigurationResolver(
  941. $this->config,
  942. ['path' => [$dir.DIRECTORY_SEPARATOR.'.php_cs']],
  943. ''
  944. );
  945. $this->assertTrue($resolver->getRiskyAllowed());
  946. $this->assertSame(['php_unit_construct' => true], $resolver->getRules());
  947. $this->assertFalse($resolver->getUsingCache());
  948. $this->assertNull($resolver->getCacheFile());
  949. $this->assertSame('xml', $resolver->getReporter()->getFormat());
  950. }
  951. /**
  952. * @group legacy
  953. * @expectedDeprecation Expected "yes" or "no" for option "allow-risky", other values are deprecated and support will be removed in 3.0. Got "yes please", this implicitly set the option to "false".
  954. */
  955. public function testDeprecationOfPassingOtherThanNoOrYes()
  956. {
  957. $resolver = new ConfigurationResolver(
  958. $this->config,
  959. ['allow-risky' => 'yes please'],
  960. ''
  961. );
  962. $this->assertFalse($resolver->getRiskyAllowed());
  963. }
  964. public function provideResolveBooleanOptionCases()
  965. {
  966. return [
  967. [true, true, 'yes'],
  968. [true, true, true],
  969. [true, false, 'yes'],
  970. [true, false, true],
  971. [false, true, 'no'],
  972. [false, true, false],
  973. [false, false, 'no'],
  974. [false, false, false],
  975. [true, true, null],
  976. [false, false, null],
  977. ];
  978. }
  979. public function testWithEmptyRules()
  980. {
  981. $resolver = new ConfigurationResolver(
  982. $this->config,
  983. ['rules' => ''],
  984. ''
  985. );
  986. $this->expectException(InvalidConfigurationException::class);
  987. $this->expectExceptionMessageRegExp('/^Empty rules value is not allowed\.$/');
  988. $resolver->getRules();
  989. }
  990. private function assertSameRules(array $expected, array $actual, $message = '')
  991. {
  992. ksort($expected);
  993. ksort($actual);
  994. $this->assertSame($expected, $actual, $message);
  995. }
  996. private function getFixtureDir()
  997. {
  998. return realpath(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'ConfigurationResolverConfigFile'.DIRECTORY_SEPARATOR).'/';
  999. }
  1000. }