Skip to content

Commit 71ce82e

Browse files
authored
[12.x] Only exclude Command ending with Test isn't an instance of Command (#58147)
* [12.x] Only exclude Command ending with `Test` when other than Command Signed-off-by: Mior Muhammad Zaki <[email protected]> * wip Signed-off-by: Mior Muhammad Zaki <[email protected]> * wip Signed-off-by: Mior Muhammad Zaki <[email protected]> --------- Signed-off-by: Mior Muhammad Zaki <[email protected]>
1 parent 5829202 commit 71ce82e

File tree

2 files changed

+73
-31
lines changed

2 files changed

+73
-31
lines changed

src/Illuminate/Foundation/Console/Kernel.php

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Symfony\Component\EventDispatcher\EventDispatcher;
3030
use Symfony\Component\Finder\Finder;
3131
use Throwable;
32+
use WeakMap;
3233

3334
class Kernel implements KernelContract
3435
{
@@ -366,15 +367,24 @@ protected function load($paths)
366367

367368
$namespace = $this->app->getNamespace();
368369

369-
foreach ($this->findCommands($paths) as $file) {
370-
$command = $this->commandClassFromFile($file, $namespace);
370+
$possibleCommands = new WeakMap;
371371

372-
if (is_subclass_of($command, Command::class) &&
373-
! (new ReflectionClass($command))->isAbstract()) {
374-
Artisan::starting(function ($artisan) use ($command) {
375-
$artisan->resolve($command);
376-
});
377-
}
372+
$filterCommands = function (SplFileInfo $file) use ($namespace, &$possibleCommands) {
373+
$commandClassName = $this->commandClassFromFile($file, $namespace);
374+
375+
$possibleCommands[$file] = $commandClassName;
376+
377+
$command = rescue(fn () => new ReflectionClass($commandClassName), null, false);
378+
379+
return $command instanceof ReflectionClass
380+
&& $command->isSubClassOf(Command::class)
381+
&& ! $command->isAbstract();
382+
};
383+
384+
foreach ($this->findCommands($paths)->filter($filterCommands) as $file) {
385+
Artisan::starting(function ($artisan) use ($file, $possibleCommands) {
386+
$artisan->resolve($possibleCommands[$file]);
387+
});
378388
}
379389
}
380390

@@ -386,7 +396,7 @@ protected function load($paths)
386396
*/
387397
protected function findCommands(array $paths)
388398
{
389-
return Finder::create()->in($paths)->notName('*Test.php')->files();
399+
return Finder::create()->in($paths)->name('*.php')->files();
390400
}
391401

392402
/**

tests/Console/ConsoleApplicationTest.php

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,37 @@
22

33
namespace Illuminate\Tests\Console;
44

5+
use Composer\Autoload\ClassLoader;
56
use Illuminate\Console\Application;
67
use Illuminate\Console\Command;
78
use Illuminate\Contracts\Events\Dispatcher;
89
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
910
use Illuminate\Events\Dispatcher as EventsDispatcher;
11+
use Illuminate\Filesystem\Filesystem;
1012
use Illuminate\Foundation\Application as FoundationApplication;
1113
use Illuminate\Foundation\Console\Kernel;
1214
use Illuminate\Tests\Console\Fixtures\FakeCommandWithArrayInputPrompting;
1315
use Illuminate\Tests\Console\Fixtures\FakeCommandWithInputPrompting;
1416
use Mockery as m;
17+
use Orchestra\Testbench\Concerns\InteractsWithMockery;
18+
use Orchestra\Testbench\Foundation\Application as Testbench;
19+
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
1520
use PHPUnit\Framework\TestCase;
1621
use Symfony\Component\Console\Attribute\AsCommand;
1722
use Symfony\Component\Console\Command\Command as SymfonyCommand;
1823
use Symfony\Component\Console\Exception\CommandNotFoundException;
1924
use Throwable;
2025

26+
use function Illuminate\Filesystem\join_paths;
27+
use function Orchestra\Testbench\default_skeleton_path;
28+
2129
class ConsoleApplicationTest extends TestCase
2230
{
31+
use InteractsWithMockery;
32+
2333
protected function tearDown(): void
2434
{
25-
m::close();
35+
$this->tearDownTheTestEnvironmentUsingMockery();
2636
}
2737

2838
public function testAddSetsLaravelInstance()
@@ -150,7 +160,7 @@ public function testCallFullyStringCommandLine()
150160
public function testCommandInputPromptsWhenRequiredArgumentIsMissing()
151161
{
152162
$artisan = new Application(
153-
$laravel = new \Illuminate\Foundation\Application(__DIR__),
163+
$laravel = new FoundationApplication(__DIR__),
154164
m::mock(Dispatcher::class, ['dispatch' => null]),
155165
'testing'
156166
);
@@ -169,7 +179,7 @@ public function testCommandInputPromptsWhenRequiredArgumentIsMissing()
169179
public function testCommandInputDoesntPromptWhenRequiredArgumentIsPassed()
170180
{
171181
$artisan = new Application(
172-
new \Illuminate\Foundation\Application(__DIR__),
182+
new FoundationApplication(__DIR__),
173183
m::mock(Dispatcher::class, ['dispatch' => null]),
174184
'testing'
175185
);
@@ -188,7 +198,7 @@ public function testCommandInputDoesntPromptWhenRequiredArgumentIsPassed()
188198
public function testCommandInputPromptsWhenRequiredArgumentsAreMissing()
189199
{
190200
$artisan = new Application(
191-
$laravel = new \Illuminate\Foundation\Application(__DIR__),
201+
$laravel = new FoundationApplication(__DIR__),
192202
m::mock(Dispatcher::class, ['dispatch' => null]),
193203
'testing'
194204
);
@@ -207,7 +217,7 @@ public function testCommandInputPromptsWhenRequiredArgumentsAreMissing()
207217
public function testCommandInputDoesntPromptWhenRequiredArgumentsArePassed()
208218
{
209219
$artisan = new Application(
210-
new \Illuminate\Foundation\Application(__DIR__),
220+
new FoundationApplication(__DIR__),
211221
m::mock(Dispatcher::class, ['dispatch' => null]),
212222
'testing'
213223
);
@@ -226,7 +236,7 @@ public function testCommandInputDoesntPromptWhenRequiredArgumentsArePassed()
226236
public function testCallMethodCanCallArtisanCommandUsingCommandClassObject()
227237
{
228238
$artisan = new Application(
229-
$laravel = new \Illuminate\Foundation\Application(__DIR__),
239+
$laravel = new FoundationApplication(__DIR__),
230240
m::mock(Dispatcher::class, ['dispatch' => null]),
231241
'testing'
232242
);
@@ -242,32 +252,48 @@ public function testCallMethodCanCallArtisanCommandUsingCommandClassObject()
242252
$this->assertSame(0, $exitCode);
243253
}
244254

255+
#[RunInSeparateProcess]
245256
public function testLoadIgnoresTestFiles()
246257
{
247-
$dir = __DIR__.'/laravel';
248-
@mkdir($dir.'/app/Console/Commands', 0755, true);
249-
file_put_contents($dir.'/composer.json', json_encode(['autoload' => ['psr-4' => ['App\\' => 'app/']]]));
250-
file_put_contents($dir.'/app/Console/Commands/ExampleCommand.php', '<?php namespace App\Console\Commands; class ExampleCommand extends \Illuminate\Console\Command { protected $signature = "example"; public function handle() {} }');
251-
file_put_contents($dir.'/app/Console/Commands/ExampleCommandTest.php', '<?php namespace App\Console\Commands; class ExampleCommandTest extends \Illuminate\Console\Command { protected $signature = "example-test"; public function handle() {} }');
258+
$files = new Filesystem;
259+
260+
$files->ensureDirectoryExists(join_paths(default_skeleton_path(), 'app', 'Console', 'Commands'), 0755, true);
252261

253262
try {
254-
$app = new FoundationApplication($dir);
263+
$files->put(
264+
join_paths(default_skeleton_path(), 'app', 'Console', 'Commands', 'ExampleCommand.php'),
265+
'<?php namespace App\Console\Commands; class ExampleCommand extends \Illuminate\Console\Command { protected $signature = "example"; public function handle() {} }'
266+
);
267+
268+
$files->put(
269+
join_paths(default_skeleton_path(), 'app', 'Console', 'Commands', 'ExampleCommandTest.php'),
270+
'<?php namespace App\Console\Commands; class ExampleCommandTest extends \Illuminate\Console\Command { protected $signature = "example-test"; public function handle() {} }'
271+
);
272+
273+
$files->put(
274+
join_paths(default_skeleton_path(), 'app', 'Console', 'Commands', 'ExampleCommandUnitTest.php'),
275+
'<?php namespace App\Console\Commands; class ExampleCommandUnitTest extends \PHPUnit\Framework\TestCase { public function test_command() { $this->assertTrue(true); } }'
276+
);
277+
278+
foreach (ClassLoader::getRegisteredLoaders() as $loader) {
279+
$loader->addPsr4('App\\', [default_skeleton_path('app')]);
280+
}
281+
282+
$app = Testbench::create(default_skeleton_path());
255283
$events = new EventsDispatcher($app);
256284
$app->instance('events', $events);
257285

258286
$kernel = new TestKernel($app, $events);
259-
$kernel->loadFrom([$dir.'/app/Console/Commands']);
260287

261-
$this->assertContains('App\Console\Commands\ExampleCommand', $kernel->loadedCommands);
262-
$this->assertNotContains('App\Console\Commands\ExampleCommandTest', $kernel->loadedCommands);
288+
$commands = $kernel->getRegisteredCommands();
289+
290+
$this->assertContains('App\Console\Commands\ExampleCommand', $commands);
291+
$this->assertContains('App\Console\Commands\ExampleCommandTest', $commands);
292+
$this->assertNotContains('App\Console\Commands\ExampleCommandUnitTest', $commands);
293+
294+
Testbench::flushState($this);
263295
} finally {
264-
@unlink($dir.'/app/Console/Commands/ExampleCommand.php');
265-
@unlink($dir.'/app/Console/Commands/ExampleCommandTest.php');
266-
@unlink($dir.'/composer.json');
267-
@rmdir($dir.'/app/Console/Commands');
268-
@rmdir($dir.'/app/Console');
269-
@rmdir($dir.'/app');
270-
@rmdir($dir);
296+
$files->cleanDirectory(default_skeleton_path('app', 'Console', 'Commands'));
271297
}
272298
}
273299

@@ -313,8 +339,14 @@ public function loadFrom($paths)
313339
$this->load($paths);
314340
}
315341

342+
#[\Override]
316343
protected function commandClassFromFile(\SplFileInfo $file, string $namespace): string
317344
{
318345
return tap(parent::commandClassFromFile($file, $namespace), fn ($command) => $this->loadedCommands[] = $command);
319346
}
347+
348+
public function getRegisteredCommands(): array
349+
{
350+
return collect($this->getArtisan()->all())->values()->transform(fn ($command) => $command::class)->all();
351+
}
320352
}

0 commit comments

Comments
 (0)