CiIntegrationTest.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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\Smoke;
  12. use Keradus\CliExecutor\CommandExecutor;
  13. use Keradus\CliExecutor\ScriptExecutor;
  14. use PhpCsFixer\Console\Application;
  15. /**
  16. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  17. *
  18. * @internal
  19. *
  20. * @requires OS Linux|Darwin
  21. * @coversNothing
  22. * @group covers-nothing
  23. * @large
  24. */
  25. final class CiIntegrationTest extends AbstractSmokeTest
  26. {
  27. public static $fixtureDir;
  28. public static function doSetUpBeforeClass()
  29. {
  30. parent::doSetUpBeforeClass();
  31. self::$fixtureDir = __DIR__.'/../Fixtures/ci-integration';
  32. try {
  33. CommandExecutor::create('composer --version', __DIR__)->getResult();
  34. } catch (\RuntimeException $e) {
  35. static::markTestSkippedOrFail('Missing `composer` env script. Details:'."\n".$e->getMessage());
  36. }
  37. try {
  38. CommandExecutor::create('composer check', __DIR__.'/../..')->getResult();
  39. } catch (\RuntimeException $e) {
  40. static::markTestSkippedOrFail('Composer check failed. Details:'."\n".$e->getMessage());
  41. }
  42. try {
  43. self::executeScript([
  44. 'rm -rf .git',
  45. 'git init -q',
  46. 'git config user.name test',
  47. 'git config user.email test',
  48. 'git add .',
  49. 'git commit -m "init" -q',
  50. ]);
  51. } catch (\RuntimeException $e) {
  52. static::markTestSkippedOrFail($e->getMessage());
  53. }
  54. }
  55. public static function doTearDownAfterClass()
  56. {
  57. parent::doTearDownAfterClass();
  58. self::executeCommand('rm -rf .git');
  59. }
  60. protected function doTearDown()
  61. {
  62. parent::doTearDown();
  63. self::executeScript([
  64. 'git reset . -q',
  65. 'git checkout . -q',
  66. 'git clean -fdq',
  67. 'git checkout master -q',
  68. ]);
  69. }
  70. /**
  71. * @param string $branchName
  72. * @param string[] $caseCommands
  73. * @param string[] $expectedResult1Lines
  74. * @param string[] $expectedResult2Lines
  75. * @param string $expectedResult3Files
  76. *
  77. * @dataProvider provideIntegrationCases
  78. */
  79. public function testIntegration(
  80. $branchName,
  81. array $caseCommands,
  82. array $expectedResult1Lines,
  83. array $expectedResult2Lines,
  84. $expectedResult3Files
  85. ) {
  86. self::executeScript(array_merge(
  87. [
  88. "git checkout -b {$branchName} -q",
  89. ],
  90. $caseCommands
  91. ));
  92. $integrationScript = explode("\n", str_replace('vendor/bin/', './../../../', file_get_contents(__DIR__.'/../../ci-integration.sh')));
  93. $steps = [
  94. "COMMIT_RANGE=\"master..{$branchName}\"",
  95. "{$integrationScript[3]}\n{$integrationScript[4]}",
  96. $integrationScript[5],
  97. $integrationScript[6],
  98. $integrationScript[7],
  99. ];
  100. $result1 = self::executeScript([
  101. $steps[0],
  102. $steps[1],
  103. $steps[2],
  104. 'echo "$CHANGED_FILES"',
  105. ]);
  106. static::assertSame(implode("\n", $expectedResult1Lines)."\n", $result1->getOutput());
  107. $result2 = self::executeScript([
  108. $steps[0],
  109. $steps[1],
  110. $steps[2],
  111. $steps[3],
  112. 'echo "${EXTRA_ARGS}"',
  113. ]);
  114. static::assertSame(implode("\n", $expectedResult2Lines), $result2->getOutput());
  115. $result3 = self::executeScript([
  116. $steps[0],
  117. $steps[1],
  118. $steps[2],
  119. $steps[3],
  120. $steps[4],
  121. ]);
  122. $optionalDeprecatedVersionWarning = 'You are running PHP CS Fixer v2, which is not maintained anymore. Please update to v3.
  123. You may find an UPGRADE guide at https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/v3.3.0/UPGRADE-v3.md .
  124. ';
  125. $optionalIncompatibilityWarning = 'PHP needs to be a minimum version of PHP 5.6.0 and maximum version of PHP 7.4.*.
  126. Current PHP version: '.PHP_VERSION.'.
  127. Ignoring environment requirements because `PHP_CS_FIXER_IGNORE_ENV` is set. Execution may be unstable.
  128. ';
  129. $optionalXdebugWarning = 'You are running PHP CS Fixer with xdebug enabled. This has a major impact on runtime performance.
  130. ';
  131. $optionalWarningsHelp = 'If you need help while solving warnings, ask at https://gitter.im/PHP-CS-Fixer, we will help you!
  132. ';
  133. /** @phpstan-ignore-next-line to avoid `Ternary operator condition is always true|false.` */
  134. $aboutSubpattern = Application::VERSION_CODENAME
  135. ? 'PHP CS Fixer '.preg_quote(Application::VERSION, '/').' '.preg_quote(Application::VERSION_CODENAME, '/').' by Fabien Potencier and Dariusz Ruminski'
  136. : 'PHP CS Fixer '.preg_quote(Application::VERSION, '/').' by Fabien Potencier and Dariusz Ruminski';
  137. $pattern = sprintf(
  138. '/^(?:%s)?(?:%s)?(?:%s)?(?:%s)?%s\n%s\n%s\n([\.S]{%d})\n%s$/',
  139. preg_quote($optionalDeprecatedVersionWarning, '/'),
  140. preg_quote($optionalIncompatibilityWarning, '/'),
  141. preg_quote($optionalXdebugWarning, '/'),
  142. preg_quote($optionalWarningsHelp, '/'),
  143. $aboutSubpattern,
  144. preg_quote(sprintf('Runtime: PHP %s', PHP_VERSION), '/'),
  145. preg_quote('Loaded config default from ".php-cs-fixer.dist.php".', '/'),
  146. \strlen($expectedResult3Files),
  147. preg_quote('Legend: ?-unknown, I-invalid file syntax (file ignored), S-skipped (cached or empty file), .-no changes, F-fixed, E-error', '/')
  148. );
  149. static::assertMatchesRegularExpression($pattern, $result3->getError());
  150. preg_match($pattern, $result3->getError(), $matches);
  151. static::assertArrayHasKey(1, $matches);
  152. static::assertSame(substr_count($expectedResult3Files, '.'), substr_count($matches[1], '.'));
  153. static::assertSame(substr_count($expectedResult3Files, 'S'), substr_count($matches[1], 'S'));
  154. static::assertMatchesRegularExpression(
  155. '/^\s*Checked all files in \d+\.\d+ seconds, \d+\.\d+ MB memory used\s*$/',
  156. $result3->getOutput()
  157. );
  158. }
  159. public function provideIntegrationCases()
  160. {
  161. return [
  162. 'random-changes' => [
  163. 'random-changes',
  164. [
  165. 'touch dir\ a/file.php',
  166. 'rm -r dir\ c',
  167. 'echo "" >> dir\ b/file\ b.php',
  168. 'echo "echo 1;" >> dir\ b/file\ b.php',
  169. 'git add .',
  170. 'git commit -m "Random changes" -q',
  171. ],
  172. [
  173. 'dir a/file.php',
  174. 'dir b/file b.php',
  175. ],
  176. [
  177. '--path-mode=intersection',
  178. '--',
  179. 'dir a/file.php',
  180. 'dir b/file b.php',
  181. '',
  182. ],
  183. 'S.',
  184. ],
  185. 'changes-including-dist-config-file' => [
  186. 'changes-including-dist-config-file',
  187. [
  188. 'echo "" >> dir\ b/file\ b.php',
  189. 'echo "echo 1;" >> dir\ b/file\ b.php',
  190. // `sed -i ...` is not handled the same on Linux and macOS
  191. 'sed -e \'s/@Symfony/@PSR2/\' .php-cs-fixer.dist.php > .php-cs-fixer.dist.php.new',
  192. 'mv .php-cs-fixer.dist.php.new .php-cs-fixer.dist.php',
  193. 'git add .',
  194. 'git commit -m "Random changes including config file" -q',
  195. ],
  196. [
  197. '.php-cs-fixer.dist.php',
  198. 'dir b/file b.php',
  199. ],
  200. [
  201. '',
  202. '',
  203. ],
  204. '...',
  205. ],
  206. 'changes-including-custom-config-file-creation' => [
  207. 'changes-including-custom-config-file-creation',
  208. [
  209. 'echo "" >> dir\ b/file\ b.php',
  210. 'echo "echo 1;" >> dir\ b/file\ b.php',
  211. 'sed -e \'s/@Symfony/@PSR2/\' .php-cs-fixer.dist.php > .php-cs-fixer.php',
  212. 'git add .',
  213. 'git commit -m "Random changes including custom config file creation" -q',
  214. ],
  215. [
  216. '.php-cs-fixer.php',
  217. 'dir b/file b.php',
  218. ],
  219. [
  220. '',
  221. '',
  222. ],
  223. '...',
  224. ],
  225. 'changes-including-composer-lock' => [
  226. 'changes-including-composer-lock',
  227. [
  228. 'echo "" >> dir\ b/file\ b.php',
  229. 'echo "echo 1;" >> dir\ b/file\ b.php',
  230. 'touch composer.lock',
  231. 'git add .',
  232. 'git commit -m "Random changes including composer.lock" -q',
  233. ],
  234. [
  235. 'composer.lock',
  236. 'dir b/file b.php',
  237. ],
  238. [
  239. '',
  240. '',
  241. ],
  242. '...',
  243. ],
  244. ];
  245. }
  246. private static function executeCommand($command)
  247. {
  248. return CommandExecutor::create($command, self::$fixtureDir)->getResult();
  249. }
  250. private static function executeScript(array $scriptParts)
  251. {
  252. return ScriptExecutor::create($scriptParts, self::$fixtureDir)->getResult();
  253. }
  254. }