ConfigurationResolverTest.php 38 KB

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