refactor: susun semula struktur folder — Laravel source ke src/
This commit is contained in:
71
vendor/laravel/pao/src/Autoload.php
vendored
Normal file
71
vendor/laravel/pao/src/Autoload.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/** @codeCoverageIgnoreStart */
|
||||
|
||||
namespace Laravel\Pao;
|
||||
|
||||
use Laravel\AgentDetector\AgentDetector;
|
||||
|
||||
/** @var array<int, string>|null $argv */
|
||||
$argv = $_SERVER['argv'] ?? null;
|
||||
|
||||
if (! is_array($argv) || $argv === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($_SERVER['PAO_DISABLE'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$agent = AgentDetector::detect();
|
||||
|
||||
if (! $agent->isAgent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (array_intersect($argv, ['--version', '--help', '-h', 'worker'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
unset($_SERVER['COLLISION_PRINTER']);
|
||||
$_SERVER['PEST_PARALLEL_NO_OUTPUT'] = '1';
|
||||
|
||||
register_shutdown_function(function (): void {
|
||||
if (! Execution::running()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$execution = Execution::current();
|
||||
|
||||
$result = $execution->driver->parse() ?: [];
|
||||
|
||||
$captured = trim(UserFilters\CaptureFilter::output());
|
||||
|
||||
$execution->restoreStdout();
|
||||
|
||||
if ($captured !== '') {
|
||||
$captured = OutputCleaner::clean($captured);
|
||||
|
||||
$lines = array_values(array_filter(
|
||||
array_map(trim(...), explode("\n", $captured)),
|
||||
fn (string $line): bool => $line !== ''
|
||||
&& ! preg_match('/^[.st!]+$/', $line)
|
||||
&& ! preg_match('/^(Tests:|Duration:|Parallel:|Time:|Generating code coverage)\s/', $line)
|
||||
&& ! str_ends_with($line, 'by Sebastian Bergmann and contributors.'),
|
||||
));
|
||||
|
||||
if ($lines !== []) {
|
||||
$result['raw'] = $lines;
|
||||
}
|
||||
}
|
||||
|
||||
if ($result !== []) {
|
||||
$result = ['tool' => $execution->driver->name()] + $result;
|
||||
|
||||
fwrite(STDOUT, json_encode($result, JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE | JSON_THROW_ON_ERROR).PHP_EOL);
|
||||
}
|
||||
});
|
||||
|
||||
Execution::start($agent, $argv);
|
||||
20
vendor/laravel/pao/src/Contracts/Driver.php
vendored
Normal file
20
vendor/laravel/pao/src/Contracts/Driver.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Contracts;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface Driver
|
||||
{
|
||||
public function start(): void;
|
||||
|
||||
public function name(): string;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function parse(): ?array;
|
||||
}
|
||||
94
vendor/laravel/pao/src/Drivers/Concerns/ProfileCollector.php
vendored
Normal file
94
vendor/laravel/pao/src/Drivers/Concerns/ProfileCollector.php
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Drivers\Concerns;
|
||||
|
||||
use PHPUnit\Event\Code\TestMethod;
|
||||
use PHPUnit\Event\Telemetry\HRTime;
|
||||
use PHPUnit\Event\Test\Finished;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class ProfileCollector
|
||||
{
|
||||
private static bool $executionStarted = false;
|
||||
|
||||
private static ?HRTime $startTime = null;
|
||||
|
||||
private static float $preparedAt = 0.0;
|
||||
|
||||
/** @var list<array{test: string, file: string, duration_ms: int}> */
|
||||
private static array $entries = [];
|
||||
|
||||
public static function executionStarted(): void
|
||||
{
|
||||
self::$executionStarted = true;
|
||||
}
|
||||
|
||||
public static function hasExecutionStarted(): bool
|
||||
{
|
||||
return self::$executionStarted;
|
||||
}
|
||||
|
||||
public static function startTimer(HRTime $time): void
|
||||
{
|
||||
self::$startTime = $time;
|
||||
}
|
||||
|
||||
public static function startTimerFromNanoseconds(float $nanoseconds): void
|
||||
{
|
||||
$seconds = (int) ($nanoseconds / 1_000_000_000);
|
||||
$nanos = (int) ($nanoseconds - ($seconds * 1_000_000_000));
|
||||
|
||||
self::$startTime = HRTime::fromSecondsAndNanoseconds($seconds, $nanos);
|
||||
}
|
||||
|
||||
public static function durationMs(): int
|
||||
{
|
||||
if (! self::$startTime instanceof HRTime) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$startNs = (self::$startTime->seconds() * 1_000_000_000) + self::$startTime->nanoseconds();
|
||||
|
||||
return (int) round((hrtime(true) - $startNs) / 1_000_000);
|
||||
}
|
||||
|
||||
public static function prepared(): void
|
||||
{
|
||||
self::$preparedAt = hrtime(true);
|
||||
}
|
||||
|
||||
public static function finished(Finished $event): void
|
||||
{
|
||||
$test = $event->test();
|
||||
|
||||
$file = $test->file();
|
||||
$doubleColonPos = strpos($file, '::');
|
||||
if ($doubleColonPos !== false) {
|
||||
$file = substr($file, 0, $doubleColonPos);
|
||||
}
|
||||
|
||||
self::$entries[] = [
|
||||
'test' => $test instanceof TestMethod ? $test->nameWithClass() : $test->id(),
|
||||
'file' => $file,
|
||||
'duration_ms' => self::$preparedAt > 0
|
||||
? (int) round((hrtime(true) - self::$preparedAt) / 1_000_000)
|
||||
: (int) round($event->telemetryInfo()->durationSincePrevious()->asFloat() * 1000),
|
||||
];
|
||||
|
||||
self::$preparedAt = 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{test: string, file: string, duration_ms: int}>
|
||||
*/
|
||||
public static function entries(): array
|
||||
{
|
||||
return self::$entries;
|
||||
}
|
||||
}
|
||||
288
vendor/laravel/pao/src/Drivers/Concerns/TestResultParsable.php
vendored
Normal file
288
vendor/laravel/pao/src/Drivers/Concerns/TestResultParsable.php
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Drivers\Concerns;
|
||||
|
||||
use Pest\Plugins\Parallel\Paratest\WrapperRunner;
|
||||
use PHPUnit\Event\Code\TestMethod;
|
||||
use PHPUnit\Event\Code\Throwable;
|
||||
use PHPUnit\Event\Facade as EventFacade;
|
||||
use PHPUnit\Event\Test\Errored;
|
||||
use PHPUnit\Event\Test\Finished;
|
||||
use PHPUnit\Event\Test\FinishedSubscriber;
|
||||
use PHPUnit\Event\Test\Prepared;
|
||||
use PHPUnit\Event\Test\PreparedSubscriber;
|
||||
use PHPUnit\Event\TestRunner\ExecutionStarted;
|
||||
use PHPUnit\Event\TestRunner\ExecutionStartedSubscriber;
|
||||
use PHPUnit\TestRunner\TestResult\Facade as TestResultFacade;
|
||||
use PHPUnit\TestRunner\TestResult\Issues\Issue;
|
||||
use PHPUnit\TestRunner\TestResult\TestResult;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
trait TestResultParsable
|
||||
{
|
||||
public ?TestResult $testResult = null;
|
||||
|
||||
protected function startTimer(): void
|
||||
{
|
||||
try {
|
||||
EventFacade::instance()->registerSubscriber(
|
||||
new class implements ExecutionStartedSubscriber
|
||||
{
|
||||
public function notify(ExecutionStarted $event): void
|
||||
{
|
||||
ProfileCollector::executionStarted();
|
||||
ProfileCollector::startTimer($event->telemetryInfo()->time());
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (\Throwable) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
protected function registerProfileSubscriber(): void
|
||||
{
|
||||
/** @var list<string> $argv */
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
|
||||
if (! in_array('--profile', $argv, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
EventFacade::instance()->registerSubscribers(
|
||||
new class implements PreparedSubscriber
|
||||
{
|
||||
public function notify(Prepared $event): void
|
||||
{
|
||||
ProfileCollector::prepared();
|
||||
}
|
||||
},
|
||||
new class implements FinishedSubscriber
|
||||
{
|
||||
public function notify(Finished $event): void
|
||||
{
|
||||
ProfileCollector::finished($event);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
$testResult = $this->resolveTestResult();
|
||||
|
||||
if (! $testResult instanceof TestResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($testResult->numberOfTestsRun() > 0 || ProfileCollector::hasExecutionStarted()) {
|
||||
return $this->parseTestResult($testResult);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function resolveTestResult(): ?TestResult
|
||||
{
|
||||
if ($this->testResult instanceof TestResult) {
|
||||
return $this->testResult;
|
||||
}
|
||||
|
||||
if (class_exists(WrapperRunner::class, false)
|
||||
&& WrapperRunner::$result instanceof TestResult) {
|
||||
return WrapperRunner::$result;
|
||||
}
|
||||
|
||||
try {
|
||||
return TestResultFacade::result();
|
||||
} catch (\Throwable) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function parseTestResult(TestResult $testResult): array
|
||||
{
|
||||
$failedCount = $testResult->numberOfTestFailedEvents();
|
||||
$erroredCount = $testResult->numberOfTestErroredEvents();
|
||||
$skipped = $testResult->numberOfTestSkippedEvents() + $testResult->numberOfTestSkippedByTestSuiteSkippedEvents();
|
||||
$incomplete = $testResult->numberOfTestMarkedIncompleteEvents();
|
||||
$tests = $testResult->numberOfTestsRun();
|
||||
$assertions = $testResult->numberOfAssertions();
|
||||
$deprecations = $testResult->numberOfPhpOrUserDeprecations();
|
||||
$warnings = $testResult->numberOfWarnings();
|
||||
$notices = $testResult->numberOfNotices();
|
||||
$risky = $testResult->numberOfTestsWithTestConsideredRiskyEvents();
|
||||
$ignoredByBaseline = $testResult->numberOfIssuesIgnoredByBaseline();
|
||||
|
||||
$durationMs = ProfileCollector::durationMs();
|
||||
|
||||
/** @var list<array{test: string, file: string, line: int, message: string}> $failureDetails */
|
||||
$failureDetails = [];
|
||||
|
||||
foreach ($testResult->testFailedEvents() as $event) {
|
||||
$test = $event->test();
|
||||
$throwable = $event->throwable();
|
||||
$message = trim($throwable->description());
|
||||
$file = $test->file();
|
||||
$line = $test instanceof TestMethod ? $test->line() : 0;
|
||||
|
||||
[$file, $line] = $this->resolveTestLocation($file, $line, $throwable);
|
||||
|
||||
$failureDetails[] = [
|
||||
'test' => $test instanceof TestMethod ? $test->nameWithClass() : $test->id(),
|
||||
'file' => $file,
|
||||
'line' => $line,
|
||||
'message' => $message,
|
||||
];
|
||||
}
|
||||
|
||||
/** @var list<array{test: string, file: string, line: int, message: string}> $errorDetails */
|
||||
$errorDetails = [];
|
||||
|
||||
foreach ($testResult->testErroredEvents() as $event) {
|
||||
if ($event instanceof Errored) {
|
||||
$test = $event->test();
|
||||
$throwable = $event->throwable();
|
||||
$message = trim($throwable->message());
|
||||
$file = $test->file();
|
||||
$line = $test instanceof TestMethod ? $test->line() : 0;
|
||||
|
||||
[$file, $line] = $this->resolveTestLocation($file, $line, $throwable);
|
||||
|
||||
$errorDetails[] = [
|
||||
'test' => $test instanceof TestMethod ? $test->nameWithClass() : $test->id(),
|
||||
'file' => $file,
|
||||
'line' => $line,
|
||||
'message' => $message,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/** @var array<string, mixed> $result */
|
||||
$result = [
|
||||
'result' => $testResult->wasSuccessful() ? 'passed' : 'failed',
|
||||
'tests' => $tests,
|
||||
'passed' => $tests - $failedCount - $erroredCount - $skipped,
|
||||
'assertions' => $assertions,
|
||||
'duration_ms' => $durationMs,
|
||||
];
|
||||
|
||||
if ($failedCount > 0) {
|
||||
$result['failed'] = $failedCount;
|
||||
$result['failures'] = $failureDetails;
|
||||
}
|
||||
|
||||
if ($erroredCount > 0) {
|
||||
$result['errors'] = $erroredCount;
|
||||
$result['error_details'] = $errorDetails;
|
||||
}
|
||||
|
||||
if ($skipped > 0) {
|
||||
$result['skipped'] = $skipped;
|
||||
}
|
||||
|
||||
if ($incomplete > 0) {
|
||||
$result['incomplete'] = $incomplete;
|
||||
}
|
||||
|
||||
if ($deprecations > 0) {
|
||||
$result['deprecations'] = $deprecations;
|
||||
$result['deprecation_details'] = $this->extractIssueDetails(
|
||||
[...$testResult->deprecations(), ...$testResult->phpDeprecations()],
|
||||
);
|
||||
}
|
||||
|
||||
if ($warnings > 0) {
|
||||
$result['warnings'] = $warnings;
|
||||
$result['warning_details'] = $this->extractIssueDetails(
|
||||
[...$testResult->warnings(), ...$testResult->phpWarnings()],
|
||||
);
|
||||
}
|
||||
|
||||
if ($notices > 0) {
|
||||
$result['notices'] = $notices;
|
||||
$result['notice_details'] = $this->extractIssueDetails(
|
||||
[...$testResult->notices(), ...$testResult->phpNotices()],
|
||||
);
|
||||
}
|
||||
|
||||
$phpErrors = $testResult->errors();
|
||||
|
||||
if ($phpErrors !== []) {
|
||||
$result['php_errors'] = count($phpErrors);
|
||||
$result['php_error_details'] = $this->extractIssueDetails($phpErrors);
|
||||
}
|
||||
|
||||
if ($risky > 0) {
|
||||
$result['risky'] = $risky;
|
||||
}
|
||||
|
||||
if ($ignoredByBaseline > 0) {
|
||||
$result['ignored_by_baseline'] = $ignoredByBaseline;
|
||||
}
|
||||
|
||||
$profileEntries = ProfileCollector::entries();
|
||||
|
||||
if ($profileEntries !== []) {
|
||||
usort($profileEntries, fn (array $a, array $b): int => $b['duration_ms'] <=> $a['duration_ms']);
|
||||
$result['profile'] = array_slice($profileEntries, 0, 10);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Issue> $issues
|
||||
* @return list<array{file: string, line: int, message: string}>
|
||||
*/
|
||||
private function extractIssueDetails(array $issues): array
|
||||
{
|
||||
$details = [];
|
||||
|
||||
foreach ($issues as $issue) {
|
||||
$details[] = [
|
||||
'file' => $issue->file(),
|
||||
'line' => $issue->line(),
|
||||
'message' => $issue->description(),
|
||||
];
|
||||
}
|
||||
|
||||
return $details;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{string, int}
|
||||
*/
|
||||
private function resolveTestLocation(string $file, int $line, Throwable $throwable): array
|
||||
{
|
||||
$isReal = $line > 0 && ! str_contains($file, "eval()'d code");
|
||||
|
||||
if ($isReal) {
|
||||
return [$file, $line];
|
||||
}
|
||||
|
||||
$text = $throwable->description()."\n".$throwable->stackTrace();
|
||||
|
||||
if (preg_match('/\bat\s+(.+\.php):(\d+)/', $text, $matches) === 1) {
|
||||
return [$matches[1], (int) $matches[2]];
|
||||
}
|
||||
|
||||
if (preg_match('#([\w/\\\\._-]+\.php):(\d+)#', $throwable->stackTrace(), $matches) === 1) {
|
||||
return [$matches[1], (int) $matches[2]];
|
||||
}
|
||||
|
||||
return [$file, $line];
|
||||
}
|
||||
}
|
||||
40
vendor/laravel/pao/src/Drivers/Paratest/Starter.php
vendored
Normal file
40
vendor/laravel/pao/src/Drivers/Paratest/Starter.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Drivers\Paratest;
|
||||
|
||||
use Laravel\Pao\Drivers\Concerns\TestResultParsable;
|
||||
use Laravel\Pao\Drivers\Starter as BaseStarter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class Starter extends BaseStarter
|
||||
{
|
||||
use TestResultParsable;
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'paratest';
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
$this->registerNullFilter();
|
||||
$this->startTimer();
|
||||
$this->silenceStdout();
|
||||
|
||||
/** @var list<string> $serverArgv */
|
||||
$serverArgv = $_SERVER['argv'];
|
||||
|
||||
$argv = $serverArgv;
|
||||
|
||||
$argv[] = '--runner';
|
||||
$argv[] = WrapperRunner::class;
|
||||
|
||||
$_SERVER['argv'] = $argv;
|
||||
}
|
||||
}
|
||||
146
vendor/laravel/pao/src/Drivers/Paratest/WrapperRunner.php
vendored
Normal file
146
vendor/laravel/pao/src/Drivers/Paratest/WrapperRunner.php
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Drivers\Paratest;
|
||||
|
||||
use Laravel\Pao\Drivers\Concerns\ProfileCollector;
|
||||
use Laravel\Pao\Execution;
|
||||
use ParaTest\Options;
|
||||
use ParaTest\RunnerInterface;
|
||||
use ParaTest\WrapperRunner\ResultPrinter;
|
||||
use ParaTest\WrapperRunner\SuiteLoader;
|
||||
use ParaTest\WrapperRunner\WrapperRunner as ParatestWrapperRunner;
|
||||
use PHPUnit\TestRunner\TestResult\Facade as TestResultFacade;
|
||||
use PHPUnit\TestRunner\TestResult\TestResult;
|
||||
use PHPUnit\TextUI\Configuration\CodeCoverageFilterRegistry;
|
||||
use PHPUnit\Util\ExcludeList;
|
||||
use ReflectionObject;
|
||||
use SplFileInfo;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final readonly class WrapperRunner implements RunnerInterface
|
||||
{
|
||||
private ParatestWrapperRunner $runner;
|
||||
|
||||
public function __construct(
|
||||
Options $options,
|
||||
) {
|
||||
$this->runner = new ParatestWrapperRunner($options, new NullOutput);
|
||||
}
|
||||
|
||||
public function run(): int
|
||||
{
|
||||
$runner = $this->runner;
|
||||
$r = new ReflectionObject($runner);
|
||||
|
||||
/** @var non-empty-string $directory */
|
||||
$directory = dirname((string) $r->getFileName(), 2);
|
||||
ExcludeList::addDirectory($directory);
|
||||
|
||||
/** @var Options $options */
|
||||
$options = $r->getProperty('options')->getValue($runner);
|
||||
|
||||
/** @var OutputInterface $output */
|
||||
$output = $r->getProperty('output')->getValue($runner);
|
||||
|
||||
/** @var CodeCoverageFilterRegistry $filterRegistry */
|
||||
$filterRegistry = $r->getProperty('codeCoverageFilterRegistry')->getValue($runner);
|
||||
|
||||
$suiteLoader = new SuiteLoader($options, $output, $filterRegistry);
|
||||
|
||||
$result = TestResultFacade::result();
|
||||
|
||||
$r->getProperty('pending')->setValue($runner, $suiteLoader->tests);
|
||||
|
||||
/** @var ResultPrinter $printer */
|
||||
$printer = $r->getProperty('printer')->getValue($runner);
|
||||
$printer->setTestCount($suiteLoader->testCount);
|
||||
$printer->start();
|
||||
|
||||
$startTime = hrtime(true);
|
||||
|
||||
$r->getMethod('startWorkers')->invoke($runner);
|
||||
$r->getMethod('assignAllPendingTests')->invoke($runner);
|
||||
$r->getMethod('waitForAllToFinish')->invoke($runner);
|
||||
|
||||
ProfileCollector::startTimerFromNanoseconds($startTime);
|
||||
|
||||
/** @var list<SplFileInfo> $testResultFiles */
|
||||
$testResultFiles = $r->getProperty('testResultFiles')->getValue($runner);
|
||||
|
||||
$mergedResult = $this->mergeTestResults($result, $testResultFiles);
|
||||
|
||||
if (Execution::running()) {
|
||||
$driver = Execution::current()->driver;
|
||||
|
||||
if ($driver instanceof Starter) {
|
||||
$driver->testResult = $mergedResult;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var int $exitCode */
|
||||
$exitCode = $r->getMethod('complete')->invoke($runner, $result);
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<SplFileInfo> $testResultFiles
|
||||
*/
|
||||
private function mergeTestResults(TestResult $sum, array $testResultFiles): TestResult
|
||||
{
|
||||
foreach ($testResultFiles as $testResultFile) {
|
||||
if (! $testResultFile->isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$contents = file_get_contents($testResultFile->getPathname());
|
||||
|
||||
if ($contents === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$testResult = unserialize($contents);
|
||||
|
||||
if (! $testResult instanceof TestResult) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sum = new TestResult(
|
||||
(int) $sum->hasTests() + (int) $testResult->hasTests(),
|
||||
$sum->numberOfTestsRun() + $testResult->numberOfTestsRun(),
|
||||
$sum->numberOfAssertions() + $testResult->numberOfAssertions(),
|
||||
[...$sum->testErroredEvents(), ...$testResult->testErroredEvents()],
|
||||
[...$sum->testFailedEvents(), ...$testResult->testFailedEvents()],
|
||||
array_merge_recursive($sum->testConsideredRiskyEvents(), $testResult->testConsideredRiskyEvents()), // @phpstan-ignore argument.type
|
||||
[...$sum->testSuiteSkippedEvents(), ...$testResult->testSuiteSkippedEvents()],
|
||||
[...$sum->testSkippedEvents(), ...$testResult->testSkippedEvents()],
|
||||
[...$sum->testMarkedIncompleteEvents(), ...$testResult->testMarkedIncompleteEvents()],
|
||||
array_merge_recursive($sum->testTriggeredPhpunitDeprecationEvents(), $testResult->testTriggeredPhpunitDeprecationEvents()), // @phpstan-ignore argument.type
|
||||
array_merge_recursive($sum->testTriggeredPhpunitErrorEvents(), $testResult->testTriggeredPhpunitErrorEvents()), // @phpstan-ignore argument.type
|
||||
array_merge_recursive($sum->testTriggeredPhpunitNoticeEvents(), $testResult->testTriggeredPhpunitNoticeEvents()), // @phpstan-ignore argument.type
|
||||
array_merge_recursive($sum->testTriggeredPhpunitWarningEvents(), $testResult->testTriggeredPhpunitWarningEvents()), // @phpstan-ignore argument.type
|
||||
[...$sum->testRunnerTriggeredDeprecationEvents(), ...$testResult->testRunnerTriggeredDeprecationEvents()],
|
||||
[...$sum->testRunnerTriggeredNoticeEvents(), ...$testResult->testRunnerTriggeredNoticeEvents()],
|
||||
[...$sum->testRunnerTriggeredWarningEvents(), ...$testResult->testRunnerTriggeredWarningEvents()],
|
||||
[...$sum->errors(), ...$testResult->errors()],
|
||||
[...$sum->deprecations(), ...$testResult->deprecations()],
|
||||
[...$sum->notices(), ...$testResult->notices()],
|
||||
[...$sum->warnings(), ...$testResult->warnings()],
|
||||
[...$sum->phpDeprecations(), ...$testResult->phpDeprecations()],
|
||||
[...$sum->phpNotices(), ...$testResult->phpNotices()],
|
||||
[...$sum->phpWarnings(), ...$testResult->phpWarnings()],
|
||||
$sum->numberOfIssuesIgnoredByBaseline() + $testResult->numberOfIssuesIgnoredByBaseline(),
|
||||
);
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
}
|
||||
37
vendor/laravel/pao/src/Drivers/Pest/Plugin.php
vendored
Normal file
37
vendor/laravel/pao/src/Drivers/Pest/Plugin.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Drivers\Pest;
|
||||
|
||||
use Laravel\Pao\Execution;
|
||||
use Pest\Contracts\Plugins\HandlesArguments;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class Plugin implements HandlesArguments
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $arguments
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function handleArguments(array $arguments): array
|
||||
{
|
||||
if (! Execution::running()) {
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
$arguments[] = '--no-output';
|
||||
$arguments[] = '--no-progress';
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
}
|
||||
41
vendor/laravel/pao/src/Drivers/Pest/Starter.php
vendored
Normal file
41
vendor/laravel/pao/src/Drivers/Pest/Starter.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Drivers\Pest;
|
||||
|
||||
use Laravel\Pao\Drivers\Concerns\ProfileCollector;
|
||||
use Laravel\Pao\Drivers\Concerns\TestResultParsable;
|
||||
use Laravel\Pao\Drivers\Starter as BaseStarter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class Starter extends BaseStarter
|
||||
{
|
||||
use TestResultParsable;
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'pest';
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
$this->registerNullFilter();
|
||||
$this->startTimer();
|
||||
$this->saveStdout();
|
||||
$this->silenceStdout();
|
||||
|
||||
/** @var list<string> $argv */
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
|
||||
if (in_array('--parallel', $argv, true)) {
|
||||
ProfileCollector::startTimerFromNanoseconds(hrtime(true));
|
||||
} else {
|
||||
$this->registerProfileSubscriber();
|
||||
}
|
||||
}
|
||||
}
|
||||
208
vendor/laravel/pao/src/Drivers/Phpstan/Starter.php
vendored
Normal file
208
vendor/laravel/pao/src/Drivers/Phpstan/Starter.php
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Drivers\Phpstan;
|
||||
|
||||
use Laravel\Pao\Drivers\Starter as BaseStarter;
|
||||
use Laravel\Pao\UserFilters\CaptureFilter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class Starter extends BaseStarter
|
||||
{
|
||||
public function name(): string
|
||||
{
|
||||
return 'phpstan';
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
$this->registerNullFilter();
|
||||
$this->silenceStderr();
|
||||
|
||||
/** @var array<int, string> $argv */
|
||||
$argv = $_SERVER['argv'];
|
||||
$argv = $this->ensureErrorFormatJson($argv);
|
||||
$argv = $this->ensureNoProgress($argv);
|
||||
$_SERVER['argv'] = $argv;
|
||||
|
||||
$this->silenceStdout();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
$captured = trim(CaptureFilter::output());
|
||||
|
||||
CaptureFilter::reset();
|
||||
|
||||
if ($captured === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$start = strpos($captured, '{');
|
||||
|
||||
if ($start !== false && $start > 0) {
|
||||
$captured = substr($captured, $start);
|
||||
}
|
||||
|
||||
/** @var array<string, mixed>|null $data */
|
||||
$data = json_decode($captured, associative: true);
|
||||
|
||||
if (! is_array($data) || ! isset($data['totals'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var array<string, list<array{line: int, message: string, identifier: string, ignorable?: bool, tip?: string}>> $errorDetails */
|
||||
$errorDetails = [];
|
||||
$totalFileErrors = 0;
|
||||
|
||||
/** @var array<string, array{errors: int, messages: list<array{message: string, line: int, identifier?: string, ignorable?: bool, tip?: string}>}> $files */
|
||||
$files = is_array($data['files'] ?? null) ? $data['files'] : [];
|
||||
|
||||
foreach ($files as $file => $fileData) {
|
||||
foreach ($fileData['messages'] as $message) {
|
||||
$totalFileErrors++;
|
||||
|
||||
$detail = [
|
||||
'line' => $message['line'],
|
||||
'message' => $message['message'],
|
||||
'identifier' => $message['identifier'] ?? 'unknown',
|
||||
];
|
||||
|
||||
if (isset($message['ignorable']) && $message['ignorable'] === false) {
|
||||
$detail['ignorable'] = false;
|
||||
}
|
||||
|
||||
if (isset($message['tip']) && $message['tip'] !== '') {
|
||||
$detail['tip'] = $message['tip'];
|
||||
}
|
||||
|
||||
$errorDetails[$file][] = $detail;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var list<string> $errors */
|
||||
$errors = is_array($data['errors'] ?? null) ? $data['errors'] : [];
|
||||
|
||||
/** @var list<string> $generalErrors */
|
||||
$generalErrors = array_values(array_filter($errors, static fn (string $error): bool => $error !== ''));
|
||||
|
||||
$totalErrors = $totalFileErrors + count($generalErrors);
|
||||
|
||||
/** @var array<string, mixed> $result */
|
||||
$result = [
|
||||
'result' => $totalErrors > 0 ? 'failed' : 'passed',
|
||||
'errors' => $totalErrors,
|
||||
];
|
||||
|
||||
if ($errorDetails !== []) {
|
||||
$verbose = $this->isVerbose();
|
||||
$limit = 30;
|
||||
|
||||
if (! $verbose && $totalFileErrors > $limit) {
|
||||
$result['error_details'] = $this->truncateGrouped($errorDetails, $limit);
|
||||
$result['truncated'] = true;
|
||||
$result['hint'] = 'Pass -v to see all errors.';
|
||||
} else {
|
||||
$result['error_details'] = $errorDetails;
|
||||
}
|
||||
}
|
||||
|
||||
if ($generalErrors !== []) {
|
||||
$result['general_errors'] = $generalErrors;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, list<array{line: int, message: string, identifier: string, ignorable?: bool, tip?: string}>> $grouped
|
||||
* @return array<string, list<array{line: int, message: string, identifier: string, ignorable?: bool, tip?: string}>>
|
||||
*/
|
||||
private function truncateGrouped(array $grouped, int $limit): array
|
||||
{
|
||||
$result = [];
|
||||
$count = 0;
|
||||
|
||||
foreach ($grouped as $file => $errors) {
|
||||
foreach ($errors as $error) {
|
||||
if ($count >= $limit) {
|
||||
break 2;
|
||||
}
|
||||
|
||||
$result[$file][] = $error;
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function isVerbose(): bool
|
||||
{
|
||||
/** @var array<int, string> $argv */
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
|
||||
foreach ($argv as $arg) {
|
||||
if (in_array($arg, ['-v', '-vv', '-vvv', '--verbose'], true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $argv
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function ensureErrorFormatJson(array $argv): array
|
||||
{
|
||||
$filtered = [];
|
||||
$skipNext = false;
|
||||
|
||||
foreach ($argv as $arg) {
|
||||
if ($skipNext) {
|
||||
$skipNext = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($arg, '--error-format=')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($arg === '--error-format') {
|
||||
$skipNext = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$filtered[] = $arg;
|
||||
}
|
||||
|
||||
$filtered[] = '--error-format=json';
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $argv
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function ensureNoProgress(array $argv): array
|
||||
{
|
||||
if (! in_array('--no-progress', $argv, true)) {
|
||||
$argv[] = '--no-progress';
|
||||
}
|
||||
|
||||
return $argv;
|
||||
}
|
||||
}
|
||||
41
vendor/laravel/pao/src/Drivers/Phpunit/Starter.php
vendored
Normal file
41
vendor/laravel/pao/src/Drivers/Phpunit/Starter.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Drivers\Phpunit;
|
||||
|
||||
use Laravel\Pao\Drivers\Concerns\TestResultParsable;
|
||||
use Laravel\Pao\Drivers\Starter as BaseStarter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class Starter extends BaseStarter
|
||||
{
|
||||
use TestResultParsable;
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'phpunit';
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
$this->registerNullFilter();
|
||||
$this->startTimer();
|
||||
$this->registerProfileSubscriber();
|
||||
|
||||
/** @var list<string> $serverArgv */
|
||||
$serverArgv = $_SERVER['argv'];
|
||||
|
||||
$argv = $serverArgv;
|
||||
|
||||
if (! in_array('--no-output', $argv, true)) {
|
||||
$argv[] = '--no-output';
|
||||
}
|
||||
|
||||
$_SERVER['argv'] = $argv;
|
||||
}
|
||||
}
|
||||
50
vendor/laravel/pao/src/Drivers/Starter.php
vendored
Normal file
50
vendor/laravel/pao/src/Drivers/Starter.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Drivers;
|
||||
|
||||
use Laravel\Pao\Contracts\Driver;
|
||||
use Laravel\Pao\Execution;
|
||||
use Laravel\Pao\UserFilters\CaptureFilter;
|
||||
use Laravel\Pao\UserFilters\NullFilter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
abstract class Starter implements Driver
|
||||
{
|
||||
protected function registerNullFilter(): void
|
||||
{
|
||||
if (! in_array('agent_output_null', stream_get_filters(), true)) {
|
||||
stream_filter_register('agent_output_null', NullFilter::class);
|
||||
}
|
||||
}
|
||||
|
||||
protected function silenceStdout(): void
|
||||
{
|
||||
if (! in_array('agent_output_capture', stream_get_filters(), true)) {
|
||||
stream_filter_register('agent_output_capture', CaptureFilter::class);
|
||||
}
|
||||
|
||||
CaptureFilter::reset();
|
||||
|
||||
$execution = Execution::current();
|
||||
|
||||
$execution->filter = stream_filter_append(STDOUT, 'agent_output_capture', STREAM_FILTER_WRITE) ?: null;
|
||||
}
|
||||
|
||||
protected function silenceStderr(): void
|
||||
{
|
||||
stream_filter_append(STDERR, 'agent_output_null', STREAM_FILTER_WRITE);
|
||||
}
|
||||
|
||||
protected function saveStdout(): void
|
||||
{
|
||||
$execution = Execution::current();
|
||||
|
||||
$execution->stdout = fopen('php://stdout', 'w') ?: STDOUT;
|
||||
}
|
||||
}
|
||||
20
vendor/laravel/pao/src/Exceptions/ShouldNotHappenException.php
vendored
Normal file
20
vendor/laravel/pao/src/Exceptions/ShouldNotHappenException.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class ShouldNotHappenException extends RuntimeException
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('This should not have happened. Please report this issue at [https://github.com/laravel/pao/issues/new].');
|
||||
}
|
||||
}
|
||||
100
vendor/laravel/pao/src/Execution.php
vendored
Normal file
100
vendor/laravel/pao/src/Execution.php
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao;
|
||||
|
||||
use Laravel\AgentDetector\AgentResult;
|
||||
use Laravel\Pao\Contracts\Driver;
|
||||
use Laravel\Pao\Exceptions\ShouldNotHappenException;
|
||||
use Laravel\Pao\UserFilters\CaptureFilter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @phpstan-type TestDetail array{test: string, file: string, line: int, message: string}
|
||||
* @phpstan-type ProfileEntry array{test: string, file: string, duration_ms: int}
|
||||
* @phpstan-type Result array{result: 'passed'|'failed', tests: int, passed: int, duration_ms: int, failed?: int, failures?: list<TestDetail>, errors?: int, error_details?: list<TestDetail>, skipped?: int, profile?: list<ProfileEntry>, raw?: list<string>}
|
||||
*/
|
||||
final class Execution
|
||||
{
|
||||
private static ?self $instance = null;
|
||||
|
||||
/**
|
||||
* @param resource|null $stdout
|
||||
* @param resource|null $filter
|
||||
*/
|
||||
private function __construct(
|
||||
public readonly AgentResult $agent,
|
||||
public readonly Driver $driver,
|
||||
public mixed $stdout = null,
|
||||
public mixed $filter = null,
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $argv
|
||||
*/
|
||||
public static function start(AgentResult $agent, array $argv): void
|
||||
{
|
||||
if (self::running()) {
|
||||
throw new ShouldNotHappenException;
|
||||
}
|
||||
|
||||
$binary = basename($argv[0] ?? '');
|
||||
|
||||
$starter = match ($binary) {
|
||||
'paratest' => new Drivers\Paratest\Starter,
|
||||
'pest' => new Drivers\Pest\Starter,
|
||||
'phpstan', 'phpstan.phar' => new Drivers\Phpstan\Starter,
|
||||
'phpunit' => new Drivers\Phpunit\Starter,
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($starter instanceof Driver) {
|
||||
self::$instance = new self(
|
||||
$agent,
|
||||
$starter,
|
||||
);
|
||||
|
||||
$starter->start();
|
||||
}
|
||||
}
|
||||
|
||||
public static function running(): bool
|
||||
{
|
||||
return self::$instance instanceof Execution;
|
||||
}
|
||||
|
||||
public static function current(): self
|
||||
{
|
||||
return self::$instance ?? throw new ShouldNotHappenException;
|
||||
}
|
||||
|
||||
public function restoreStdout(): void
|
||||
{
|
||||
if (is_resource($this->filter)) {
|
||||
stream_filter_remove($this->filter);
|
||||
|
||||
$this->filter = null;
|
||||
}
|
||||
}
|
||||
|
||||
public function flushStdout(): void
|
||||
{
|
||||
if (! is_resource($this->filter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$captured = CaptureFilter::output();
|
||||
|
||||
$this->restoreStdout();
|
||||
|
||||
if ($captured !== '') {
|
||||
fwrite(STDOUT, $captured);
|
||||
}
|
||||
}
|
||||
}
|
||||
62
vendor/laravel/pao/src/Laravel/PaoOutputStyle.php
vendored
Normal file
62
vendor/laravel/pao/src/Laravel/PaoOutputStyle.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Laravel;
|
||||
|
||||
use Illuminate\Console\OutputStyle;
|
||||
use Laravel\Pao\OutputCleaner;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class PaoOutputStyle extends OutputStyle
|
||||
{
|
||||
private static ?OutputFormatter $formatter = null;
|
||||
|
||||
public function __construct(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$output->setDecorated(false);
|
||||
|
||||
parent::__construct($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|iterable<string> $messages
|
||||
*/
|
||||
#[\Override]
|
||||
public function write(string|iterable $messages, bool $newline = false, int $options = 0): void
|
||||
{
|
||||
parent::write($this->clean($messages), $newline, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|iterable<string> $messages
|
||||
*/
|
||||
#[\Override]
|
||||
public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL): void
|
||||
{
|
||||
parent::writeln($this->clean($messages), $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|iterable<string> $messages
|
||||
* @return string|list<string>
|
||||
*/
|
||||
private function clean(string|iterable $messages): string|array
|
||||
{
|
||||
$formatter = self::$formatter ??= new OutputFormatter(false);
|
||||
$strip = fn (string $m): string => OutputCleaner::clean((string) $formatter->format($m));
|
||||
|
||||
if (is_string($messages)) {
|
||||
return $strip($messages);
|
||||
}
|
||||
|
||||
return array_values(array_map($strip, [...$messages]));
|
||||
}
|
||||
}
|
||||
46
vendor/laravel/pao/src/Laravel/ServiceProvider.php
vendored
Normal file
46
vendor/laravel/pao/src/Laravel/ServiceProvider.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\Laravel;
|
||||
|
||||
use Illuminate\Console\Events\CommandStarting;
|
||||
use Illuminate\Console\OutputStyle;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
|
||||
use Laravel\AgentDetector\AgentDetector;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class ServiceProvider extends LaravelServiceProvider
|
||||
{
|
||||
public function boot(): void
|
||||
{
|
||||
if (isset($_SERVER['PAO_DISABLE'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->app->runningInConsole()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->app->runningUnitTests()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! AgentDetector::detect()->isAgent) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->app->bind(OutputStyle::class, PaoOutputStyle::class);
|
||||
|
||||
/** @var Dispatcher $events */
|
||||
$events = $this->app->make(Dispatcher::class);
|
||||
$events->listen(CommandStarting::class, function (CommandStarting $event): void {
|
||||
$event->output->setDecorated(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
25
vendor/laravel/pao/src/OutputCleaner.php
vendored
Normal file
25
vendor/laravel/pao/src/OutputCleaner.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class OutputCleaner
|
||||
{
|
||||
public static function clean(string $output): string
|
||||
{
|
||||
$output = (string) preg_replace('/\e\[[0-9;]*[A-Za-z]/', '', $output);
|
||||
$output = (string) preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/', '', $output);
|
||||
$output = (string) preg_replace('/\x{FFFD}/u', '', $output);
|
||||
$output = (string) preg_replace('/[─━│┌┐└┘├┤┬┴┼▓░▒═║╔╗╚╝╠╣╦╩╬➜▶►⚠✖✔●◆■▪→←↑↓▕⨯✕]+/u', '', $output);
|
||||
$output = (string) preg_replace('/\.{3,}/', '..', $output);
|
||||
$output = (string) preg_replace('/[ \t]+/', ' ', $output);
|
||||
|
||||
return (string) preg_replace('/\n\s*\n/', "\n", $output);
|
||||
}
|
||||
}
|
||||
50
vendor/laravel/pao/src/UserFilters/CaptureFilter.php
vendored
Normal file
50
vendor/laravel/pao/src/UserFilters/CaptureFilter.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\UserFilters;
|
||||
|
||||
use php_user_filter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class CaptureFilter extends php_user_filter
|
||||
{
|
||||
private static string $captured = '';
|
||||
|
||||
/**
|
||||
* @param resource $in
|
||||
* @param resource $out
|
||||
* @param int $consumed
|
||||
*/
|
||||
public function filter($in, $out, &$consumed, bool $closing): int // @pest-ignore-type
|
||||
{
|
||||
while ($bucket = stream_bucket_make_writeable($in)) {
|
||||
/** @var int $datalen */
|
||||
$datalen = $bucket->datalen;
|
||||
$consumed += $datalen;
|
||||
|
||||
/** @var string $data */
|
||||
$data = $bucket->data;
|
||||
self::$captured .= $data;
|
||||
|
||||
$bucket->data = '';
|
||||
stream_bucket_append($out, $bucket);
|
||||
}
|
||||
|
||||
return PSFS_PASS_ON;
|
||||
}
|
||||
|
||||
public static function output(): string
|
||||
{
|
||||
return self::$captured;
|
||||
}
|
||||
|
||||
public static function reset(): void
|
||||
{
|
||||
self::$captured = '';
|
||||
}
|
||||
}
|
||||
28
vendor/laravel/pao/src/UserFilters/NullFilter.php
vendored
Normal file
28
vendor/laravel/pao/src/UserFilters/NullFilter.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laravel\Pao\UserFilters;
|
||||
|
||||
use php_user_filter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class NullFilter extends php_user_filter
|
||||
{
|
||||
public function filter($in, $out, &$consumed, bool $closing): int // @pest-ignore-type
|
||||
{
|
||||
while ($bucket = stream_bucket_make_writeable($in)) {
|
||||
/** @var int $datalen */
|
||||
$datalen = $bucket->datalen;
|
||||
$consumed += $datalen;
|
||||
$bucket->data = '';
|
||||
stream_bucket_append($out, $bucket);
|
||||
}
|
||||
|
||||
return PSFS_PASS_ON;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user