diff --git a/composer.json b/composer.json
index c66f40706..b27459b20 100644
--- a/composer.json
+++ b/composer.json
@@ -47,7 +47,7 @@
"ext-json": "*",
"ext-libxml": "*",
"composer/xdebug-handler": "^2.0",
- "infection/abstract-testframework-adapter": "^0.3.1",
+ "infection/abstract-testframework-adapter": "^0.5.0",
"infection/extension-installer": "^0.1.0",
"infection/include-interceptor": "^0.2.4",
"justinrainbow/json-schema": "^5.2.10",
diff --git a/composer.lock b/composer.lock
index 89b035c5a..5fce70f41 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "e386c963bbc78549523114db79bcb94c",
+ "content-hash": "d9cfb23acce60abd6aa6cc78193cd142",
"packages": [
{
"name": "composer/xdebug-handler",
@@ -72,25 +72,25 @@
},
{
"name": "infection/abstract-testframework-adapter",
- "version": "0.3.1",
+ "version": "0.5.0",
"source": {
"type": "git",
"url": "https://github.com/infection/abstract-testframework-adapter.git",
- "reference": "c52539339f28d6b67625ff24496289b3e6d66025"
+ "reference": "18925e20d15d1a5995bb85c9dc09e8751e1e069b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/infection/abstract-testframework-adapter/zipball/c52539339f28d6b67625ff24496289b3e6d66025",
- "reference": "c52539339f28d6b67625ff24496289b3e6d66025",
+ "url": "https://api.github.com/repos/infection/abstract-testframework-adapter/zipball/18925e20d15d1a5995bb85c9dc09e8751e1e069b",
+ "reference": "18925e20d15d1a5995bb85c9dc09e8751e1e069b",
"shasum": ""
},
"require": {
- "php": "^7.3 || ^8.0"
+ "php": "^7.4 || ^8.0"
},
"require-dev": {
"ergebnis/composer-normalize": "^2.8",
- "friendsofphp/php-cs-fixer": "^2.16",
- "phpunit/phpunit": "^9.0"
+ "friendsofphp/php-cs-fixer": "^2.17",
+ "phpunit/phpunit": "^9.5"
},
"type": "library",
"autoload": {
@@ -111,9 +111,19 @@
"description": "Abstract Test Framework Adapter for Infection",
"support": {
"issues": "https://github.com/infection/abstract-testframework-adapter/issues",
- "source": "https://github.com/infection/abstract-testframework-adapter/tree/0.3"
+ "source": "https://github.com/infection/abstract-testframework-adapter/tree/0.5.0"
},
- "time": "2020-08-30T13:50:12+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/infection",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/infection",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2021-08-17T18:49:12+00:00"
},
{
"name": "infection/extension-installer",
diff --git a/resources/schema.json b/resources/schema.json
index a274eebe3..a17f81b67 100644
--- a/resources/schema.json
+++ b/resources/schema.json
@@ -339,6 +339,7 @@
"SpreadOneItem": { "$ref": "#/definitions/default-mutator-config" },
"SpreadAssignment": { "$ref": "#/definitions/default-mutator-config" },
"SpreadRemoval": { "$ref": "#/definitions/default-mutator-config" },
+ "SyntaxError": { "$ref": "#/definitions/default-mutator-config" },
"Foreach_": { "$ref": "#/definitions/default-mutator-config" },
"For_": { "$ref": "#/definitions/default-mutator-config" },
"DoWhile": { "$ref": "#/definitions/default-mutator-config" },
diff --git a/src/Console/OutputFormatter/DotFormatter.php b/src/Console/OutputFormatter/DotFormatter.php
index 87920f9a1..204964c35 100644
--- a/src/Console/OutputFormatter/DotFormatter.php
+++ b/src/Console/OutputFormatter/DotFormatter.php
@@ -66,6 +66,7 @@ public function start(int $mutationCount): void
. 'M: escaped, '
. 'U: uncovered, '
. 'E: fatal error, '
+ . 'X: syntax error, '
. 'T: timed out, '
. 'S: skipped',
'',
@@ -100,6 +101,10 @@ public function advance(MutantExecutionResult $executionResult, int $mutationCou
case DetectionStatus::ERROR:
$this->output->write('E');
+ break;
+ case DetectionStatus::SYNTAX_ERROR:
+ $this->output->write('X');
+
break;
}
diff --git a/src/Console/OutputFormatterStyleConfigurator.php b/src/Console/OutputFormatterStyleConfigurator.php
index 55e62eb63..66e3bccbc 100644
--- a/src/Console/OutputFormatterStyleConfigurator.php
+++ b/src/Console/OutputFormatterStyleConfigurator.php
@@ -59,6 +59,7 @@ public static function configure(OutputInterface $output): void
private static function configureMutantStyle(OutputFormatterInterface $formatter): void
{
$formatter->setStyle('with-error', new OutputFormatterStyle('green'));
+ $formatter->setStyle('with-syntax-error', new OutputFormatterStyle('red', null, ['bold']));
$formatter->setStyle(
'uncovered',
new OutputFormatterStyle('blue', null, ['bold'])
diff --git a/src/Event/Subscriber/MutationTestingConsoleLoggerSubscriber.php b/src/Event/Subscriber/MutationTestingConsoleLoggerSubscriber.php
index 7fb1eb3dc..f18146864 100644
--- a/src/Event/Subscriber/MutationTestingConsoleLoggerSubscriber.php
+++ b/src/Event/Subscriber/MutationTestingConsoleLoggerSubscriber.php
@@ -150,6 +150,7 @@ private function showMetrics(): void
$this->output->writeln('' . $this->getPadded($this->metricsCalculator->getNotTestedCount()) . ' mutants were not covered by tests');
$this->output->writeln('' . $this->getPadded($this->metricsCalculator->getEscapedCount()) . ' covered mutants were not detected');
$this->output->writeln('' . $this->getPadded($this->metricsCalculator->getErrorCount()) . ' errors were encountered');
+ $this->output->writeln('' . $this->getPadded($this->metricsCalculator->getSyntaxErrorCount()) . ' syntax errors were encountered');
$this->output->writeln('' . $this->getPadded($this->metricsCalculator->getTimedOutCount()) . ' time outs were encountered');
$this->output->writeln('' . $this->getPadded($this->metricsCalculator->getSkippedCount()) . ' mutants required more time than configured');
diff --git a/src/Logger/DebugFileLogger.php b/src/Logger/DebugFileLogger.php
index ea7f7b3bb..9e6bbc3b0 100644
--- a/src/Logger/DebugFileLogger.php
+++ b/src/Logger/DebugFileLogger.php
@@ -84,6 +84,11 @@ public function getLogLines(): array
'Errors',
$separateSections
);
+ $logs[] = $this->getResultsLine(
+ $this->resultsCollector->getSyntaxErrorExecutionResults(),
+ 'Syntax Errors',
+ $separateSections
+ );
$logs[] = $this->getResultsLine(
$this->resultsCollector->getEscapedExecutionResults(),
'Escaped',
diff --git a/src/Logger/FederatedLogger.php b/src/Logger/FederatedLogger.php
index 3d90363e1..fe369abb4 100644
--- a/src/Logger/FederatedLogger.php
+++ b/src/Logger/FederatedLogger.php
@@ -43,7 +43,7 @@ final class FederatedLogger implements MutationTestingResultsLogger
/**
* @var MutationTestingResultsLogger[]
*/
- private $loggers;
+ private array $loggers;
public function __construct(MutationTestingResultsLogger ...$loggers)
{
diff --git a/src/Logger/JsonLogger.php b/src/Logger/JsonLogger.php
index 8f9849efd..de363cbd1 100644
--- a/src/Logger/JsonLogger.php
+++ b/src/Logger/JsonLogger.php
@@ -74,6 +74,7 @@ public function getLogLines(): array
'notCoveredCount' => $this->metricsCalculator->getNotTestedCount(),
'escapedCount' => $this->metricsCalculator->getEscapedCount(),
'errorCount' => $this->metricsCalculator->getErrorCount(),
+ 'syntaxErrorCount' => $this->metricsCalculator->getSyntaxErrorCount(),
'skippedCount' => $this->metricsCalculator->getSkippedCount(),
'timeOutCount' => $this->metricsCalculator->getTimedOutCount(),
'msi' => $this->metricsCalculator->getMutationScoreIndicator(),
@@ -84,6 +85,7 @@ public function getLogLines(): array
'timeouted' => $this->getResultsLine($this->resultsCollector->getTimedOutExecutionResults()),
'killed' => $this->getResultsLine($this->resultsCollector->getKilledExecutionResults()),
'errored' => $this->getResultsLine($this->resultsCollector->getErrorExecutionResults()),
+ 'syntaxErrors' => $this->getResultsLine($this->resultsCollector->getSyntaxErrorExecutionResults()),
'uncovered' => $this->onlyCoveredMode ? [] : $this->getResultsLine($this->resultsCollector->getNotCoveredExecutionResults()),
];
diff --git a/src/Logger/PerMutatorLogger.php b/src/Logger/PerMutatorLogger.php
index d9cf6c9ee..a6f2bda17 100644
--- a/src/Logger/PerMutatorLogger.php
+++ b/src/Logger/PerMutatorLogger.php
@@ -73,7 +73,7 @@ public function getLogLines(): array
$calculatorPerMutator = $this->createMetricsPerMutators();
$table = [
- ['Mutator', 'Mutations', 'Killed', 'Escaped', 'Errors', 'Timed Out', 'Skipped', 'MSI (%s)', 'Covered MSI (%s)'],
+ ['Mutator', 'Mutations', 'Killed', 'Escaped', 'Errors', 'Syntax Errors', 'Timed Out', 'Skipped', 'MSI (%s)', 'Covered MSI (%s)'],
];
foreach ($calculatorPerMutator as $mutatorName => $calculator) {
@@ -85,6 +85,7 @@ public function getLogLines(): array
(string) $calculator->getKilledCount(),
(string) $calculator->getEscapedCount(),
(string) $calculator->getErrorCount(),
+ (string) $calculator->getSyntaxErrorCount(),
(string) $calculator->getTimedOutCount(),
(string) $calculator->getSkippedCount(),
self::formatScore($calculator->getMutationScoreIndicator()),
diff --git a/src/Logger/SummaryFileLogger.php b/src/Logger/SummaryFileLogger.php
index 0e3480c14..e9044b756 100644
--- a/src/Logger/SummaryFileLogger.php
+++ b/src/Logger/SummaryFileLogger.php
@@ -59,6 +59,7 @@ public function getLogLines(): array
'',
'Killed: ' . $this->metricsCalculator->getKilledCount(),
'Errored: ' . $this->metricsCalculator->getErrorCount(),
+ 'Syntax Errors: ' . $this->metricsCalculator->getSyntaxErrorCount(),
'Escaped: ' . $this->metricsCalculator->getEscapedCount(),
'Timed Out: ' . $this->metricsCalculator->getTimedOutCount(),
'Skipped: ' . $this->metricsCalculator->getSkippedCount(),
diff --git a/src/Logger/TextFileLogger.php b/src/Logger/TextFileLogger.php
index 1de18b03e..2c1630311 100644
--- a/src/Logger/TextFileLogger.php
+++ b/src/Logger/TextFileLogger.php
@@ -102,6 +102,12 @@ public function getLogLines(): array
'Errors',
$separateSections
);
+
+ $logs[] = $this->getResultsLine(
+ $this->resultsCollector->getSyntaxErrorExecutionResults(),
+ 'Syntax Errors',
+ $separateSections
+ );
}
if (!$this->onlyCoveredMode) {
diff --git a/src/Metrics/MetricsCalculator.php b/src/Metrics/MetricsCalculator.php
index 859c5faf9..527c99092 100644
--- a/src/Metrics/MetricsCalculator.php
+++ b/src/Metrics/MetricsCalculator.php
@@ -103,6 +103,11 @@ public function getErrorCount(): int
return $this->countByStatus[DetectionStatus::ERROR];
}
+ public function getSyntaxErrorCount(): int
+ {
+ return $this->countByStatus[DetectionStatus::SYNTAX_ERROR];
+ }
+
public function getSkippedCount(): int
{
return $this->countByStatus[DetectionStatus::SKIPPED];
diff --git a/src/Metrics/ResultsCollector.php b/src/Metrics/ResultsCollector.php
index e27da417f..7078c560c 100644
--- a/src/Metrics/ResultsCollector.php
+++ b/src/Metrics/ResultsCollector.php
@@ -105,6 +105,14 @@ public function getErrorExecutionResults(): array
return $this->getResultListForStatus(DetectionStatus::ERROR)->getSortedExecutionResults();
}
+ /**
+ * @return MutantExecutionResult[]
+ */
+ public function getSyntaxErrorExecutionResults(): array
+ {
+ return $this->getResultListForStatus(DetectionStatus::SYNTAX_ERROR)->getSortedExecutionResults();
+ }
+
/**
* @return MutantExecutionResult[]
*/
diff --git a/src/Metrics/TargetDetectionStatusesProvider.php b/src/Metrics/TargetDetectionStatusesProvider.php
index be141bc8d..6915bc2b9 100644
--- a/src/Metrics/TargetDetectionStatusesProvider.php
+++ b/src/Metrics/TargetDetectionStatusesProvider.php
@@ -123,6 +123,8 @@ private function findRequired(): Generator
yield DetectionStatus::ERROR;
+ yield DetectionStatus::SYNTAX_ERROR;
+
yield DetectionStatus::TIMED_OUT;
if (!$this->onlyCoveredMode) {
@@ -138,6 +140,8 @@ private function findRequired(): Generator
yield DetectionStatus::SKIPPED;
+ yield DetectionStatus::SYNTAX_ERROR;
+
if ($this->logVerbosity === LogVerbosity::DEBUG) {
yield DetectionStatus::KILLED;
diff --git a/src/Mutant/DetectionStatus.php b/src/Mutant/DetectionStatus.php
index 9b8fa1263..b3fcc4879 100644
--- a/src/Mutant/DetectionStatus.php
+++ b/src/Mutant/DetectionStatus.php
@@ -49,6 +49,7 @@ final class DetectionStatus
public const ERROR = 'error';
public const TIMED_OUT = 'timed out';
public const SKIPPED = 'skipped';
+ public const SYNTAX_ERROR = 'syntax error';
public const NOT_COVERED = 'not covered';
public const ALL = [
@@ -57,6 +58,7 @@ final class DetectionStatus
self::ERROR,
self::TIMED_OUT,
self::SKIPPED,
+ self::SYNTAX_ERROR,
self::NOT_COVERED,
];
}
diff --git a/src/Mutant/MutantExecutionResultFactory.php b/src/Mutant/MutantExecutionResultFactory.php
index cd487c511..c17731dd5 100644
--- a/src/Mutant/MutantExecutionResultFactory.php
+++ b/src/Mutant/MutantExecutionResultFactory.php
@@ -35,6 +35,7 @@
namespace Infection\Mutant;
+use Infection\AbstractTestFramework\SyntaxErrorAware;
use Infection\AbstractTestFramework\TestFrameworkAdapter;
use Infection\Process\MutantProcess;
use function Safe\sprintf;
@@ -103,10 +104,16 @@ private function retrieveDetectionStatus(MutantProcess $mutantProcess): string
return DetectionStatus::ERROR;
}
- if ($this->testFrameworkAdapter->testsPass($this->retrieveProcessOutput($process))) {
+ $output = $this->retrieveProcessOutput($process);
+
+ if ($this->testFrameworkAdapter->testsPass($output)) {
return DetectionStatus::ESCAPED;
}
+ if ($this->testFrameworkAdapter instanceof SyntaxErrorAware && $this->testFrameworkAdapter->isSyntaxError($output)) {
+ return DetectionStatus::SYNTAX_ERROR;
+ }
+
return DetectionStatus::KILLED;
}
}
diff --git a/src/Mutator/ProfileList.php b/src/Mutator/ProfileList.php
index cb224528c..17bdf5407 100644
--- a/src/Mutator/ProfileList.php
+++ b/src/Mutator/ProfileList.php
@@ -464,6 +464,9 @@ final class ProfileList
// Extensions
'BCMath' => Mutator\Extensions\BCMath::class,
'MBString' => Mutator\Extensions\MBString::class,
+
+ // Internal only usage
+ 'SyntaxError' => Mutator\SyntaxError::class,
];
/** @var array|null */
diff --git a/src/Mutator/SyntaxError.php b/src/Mutator/SyntaxError.php
new file mode 100644
index 000000000..1094b0150
--- /dev/null
+++ b/src/Mutator/SyntaxError.php
@@ -0,0 +1,81 @@
+
+ */
+final class SyntaxError implements Mutator
+{
+ use GetMutatorName;
+
+ public static function getDefinition(): ?Definition
+ {
+ return new Definition(
+ 'Replaces a `$this` with `false` to produce a syntax error. Internal usage only.',
+ MutatorCategory::ORTHOGONAL_REPLACEMENT,
+ null,
+ <<<'DIFF'
+class X {
+ function foo()
+ {
+- $this->method();
++ $->method();
+ }
+}
+DIFF
+ );
+ }
+
+ /**
+ * @psalm-mutation-free
+ *
+ * @return iterable
+ */
+ public function mutate(Node $node): iterable
+ {
+ yield new Node\Expr\ConstFetch(new Node\Name('$'));
+ }
+
+ public function canMutate(Node $node): bool
+ {
+ return $node instanceof Node\Expr\Variable && $node->name === 'this';
+ }
+}
diff --git a/src/TestFramework/PhpUnit/Adapter/PestAdapter.php b/src/TestFramework/PhpUnit/Adapter/PestAdapter.php
index 3fb1dc642..fb829034e 100644
--- a/src/TestFramework/PhpUnit/Adapter/PestAdapter.php
+++ b/src/TestFramework/PhpUnit/Adapter/PestAdapter.php
@@ -36,6 +36,7 @@
namespace Infection\TestFramework\PhpUnit\Adapter;
use Infection\AbstractTestFramework\MemoryUsageAware;
+use Infection\AbstractTestFramework\SyntaxErrorAware;
use Infection\AbstractTestFramework\TestFrameworkAdapter;
use Infection\TestFramework\ProvidesInitialRunOnlyOptions;
use function Safe\preg_match;
@@ -44,7 +45,7 @@
/**
* @internal
*/
-final class PestAdapter implements MemoryUsageAware, ProvidesInitialRunOnlyOptions, TestFrameworkAdapter
+final class PestAdapter implements MemoryUsageAware, ProvidesInitialRunOnlyOptions, SyntaxErrorAware, TestFrameworkAdapter
{
private const NAME = 'Pest';
@@ -76,6 +77,11 @@ public function testsPass(string $output): bool
return $isOk || $isOkRisked;
}
+ public function isSyntaxError(string $output): bool
+ {
+ return preg_match('/ParseError\s*syntax error/i', $output) === 1;
+ }
+
public function hasJUnitReport(): bool
{
return $this->phpUnitAdapter->hasJUnitReport();
diff --git a/src/TestFramework/PhpUnit/Adapter/PhpUnitAdapter.php b/src/TestFramework/PhpUnit/Adapter/PhpUnitAdapter.php
index 5258633b1..3b3b3e0c6 100644
--- a/src/TestFramework/PhpUnit/Adapter/PhpUnitAdapter.php
+++ b/src/TestFramework/PhpUnit/Adapter/PhpUnitAdapter.php
@@ -37,6 +37,7 @@
use function escapeshellarg;
use Infection\AbstractTestFramework\MemoryUsageAware;
+use Infection\AbstractTestFramework\SyntaxErrorAware;
use Infection\Config\ValueProvider\PCOVDirectoryProvider;
use Infection\TestFramework\AbstractTestFrameworkAdapter;
use Infection\TestFramework\CommandLineArgumentsAndOptionsBuilder;
@@ -54,7 +55,7 @@
* @internal
* @final
*/
-class PhpUnitAdapter extends AbstractTestFrameworkAdapter implements MemoryUsageAware, ProvidesInitialRunOnlyOptions
+class PhpUnitAdapter extends AbstractTestFrameworkAdapter implements MemoryUsageAware, ProvidesInitialRunOnlyOptions, SyntaxErrorAware
{
public const COVERAGE_DIR = 'coverage-xml';
@@ -142,6 +143,11 @@ public function testsPass(string $output): bool
return $isOk || $isOkWithInfo || $isWarning | $isNoTestsExecuted;
}
+ public function isSyntaxError(string $output): bool
+ {
+ return preg_match('/ParseError: syntax error/i', $output) === 1;
+ }
+
public function getMemoryUsed(string $output): float
{
if (preg_match('/Memory: (\d+(?:\.\d+))\s*MB/', $output, $match)) {
diff --git a/tests/e2e/Config_Bootstrap/expected-output.txt b/tests/e2e/Config_Bootstrap/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/Config_Bootstrap/expected-output.txt
+++ b/tests/e2e/Config_Bootstrap/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Custom_tmp_dir/expected-output.txt b/tests/e2e/Custom_tmp_dir/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/Custom_tmp_dir/expected-output.txt
+++ b/tests/e2e/Custom_tmp_dir/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Empty_Path/expected-output.txt b/tests/e2e/Empty_Path/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/Empty_Path/expected-output.txt
+++ b/tests/e2e/Empty_Path/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Example_Test/expected-output.txt b/tests/e2e/Example_Test/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/Example_Test/expected-output.txt
+++ b/tests/e2e/Example_Test/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Example_Test/infection.json b/tests/e2e/Example_Test/infection.json
index 7029746fb..a5d873d67 100644
--- a/tests/e2e/Example_Test/infection.json
+++ b/tests/e2e/Example_Test/infection.json
@@ -1,4 +1,5 @@
{
+ "$schema": "../../../resources/schema.json",
"timeout": 25,
"source": {
"directories": [
diff --git a/tests/e2e/Exclude_Mutations_By_Regex/expected-output.txt b/tests/e2e/Exclude_Mutations_By_Regex/expected-output.txt
index aa6c03830..95e73f9ca 100644
--- a/tests/e2e/Exclude_Mutations_By_Regex/expected-output.txt
+++ b/tests/e2e/Exclude_Mutations_By_Regex/expected-output.txt
@@ -2,6 +2,7 @@ Total: 0
Killed: 0
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Exclude_by_file_name/expected-output.txt b/tests/e2e/Exclude_by_file_name/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/Exclude_by_file_name/expected-output.txt
+++ b/tests/e2e/Exclude_by_file_name/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Exec_Path/expected-output.txt b/tests/e2e/Exec_Path/expected-output.txt
index afaccc04a..c654acc50 100644
--- a/tests/e2e/Exec_Path/expected-output.txt
+++ b/tests/e2e/Exec_Path/expected-output.txt
@@ -2,6 +2,7 @@ Total: 6
Killed: 5
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Ignore_All_Mutations/expected-output.txt b/tests/e2e/Ignore_All_Mutations/expected-output.txt
index 76a558afd..b53522bf8 100644
--- a/tests/e2e/Ignore_All_Mutations/expected-output.txt
+++ b/tests/e2e/Ignore_All_Mutations/expected-output.txt
@@ -2,6 +2,7 @@ Total: 2
Killed: 2
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Ignore_MSI_Zero_Mutations/expected-output.txt b/tests/e2e/Ignore_MSI_Zero_Mutations/expected-output.txt
index aa6c03830..95e73f9ca 100644
--- a/tests/e2e/Ignore_MSI_Zero_Mutations/expected-output.txt
+++ b/tests/e2e/Ignore_MSI_Zero_Mutations/expected-output.txt
@@ -2,6 +2,7 @@ Total: 0
Killed: 0
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Memory_Limit/expected-output.txt b/tests/e2e/Memory_Limit/expected-output.txt
index 5aec86877..003c4078e 100644
--- a/tests/e2e/Memory_Limit/expected-output.txt
+++ b/tests/e2e/Memory_Limit/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 0
Errored: 1
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Multiline_Statement/expected-output.txt b/tests/e2e/Multiline_Statement/expected-output.txt
index af47cacd2..b975cd448 100644
--- a/tests/e2e/Multiline_Statement/expected-output.txt
+++ b/tests/e2e/Multiline_Statement/expected-output.txt
@@ -49,6 +49,9 @@ Line 22
Errors mutants:
===============
+Syntax Errors mutants:
+======================
+
Escaped mutants:
================
diff --git a/tests/e2e/Output_Stream/expected-output.txt b/tests/e2e/Output_Stream/expected-output.txt
index 4cc565248..e887c84cd 100644
--- a/tests/e2e/Output_Stream/expected-output.txt
+++ b/tests/e2e/Output_Stream/expected-output.txt
@@ -13,6 +13,9 @@ Line 9
Errors mutants:
===============
+Syntax Errors mutants:
+======================
+
Escaped mutants:
================
diff --git a/tests/e2e/PCOV_Directory/expected-output.txt b/tests/e2e/PCOV_Directory/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/PCOV_Directory/expected-output.txt
+++ b/tests/e2e/PCOV_Directory/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/PCOV_PHPUnit8/expected-output.txt b/tests/e2e/PCOV_PHPUnit8/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/PCOV_PHPUnit8/expected-output.txt
+++ b/tests/e2e/PCOV_PHPUnit8/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/PHPUnit93/expected-output.txt b/tests/e2e/PHPUnit93/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/PHPUnit93/expected-output.txt
+++ b/tests/e2e/PHPUnit93/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/PHPUnit_Custom_Config_Dir/expected-output.txt b/tests/e2e/PHPUnit_Custom_Config_Dir/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/PHPUnit_Custom_Config_Dir/expected-output.txt
+++ b/tests/e2e/PHPUnit_Custom_Config_Dir/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/PHPUnit_Hidden_Dependency/expected-output.txt b/tests/e2e/PHPUnit_Hidden_Dependency/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/PHPUnit_Hidden_Dependency/expected-output.txt
+++ b/tests/e2e/PHPUnit_Hidden_Dependency/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/PHPUnit_XSD_Validation/expected-output.txt b/tests/e2e/PHPUnit_XSD_Validation/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/PHPUnit_XSD_Validation/expected-output.txt
+++ b/tests/e2e/PHPUnit_XSD_Validation/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/PSR_0_Autoloader/expected-output.txt b/tests/e2e/PSR_0_Autoloader/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/PSR_0_Autoloader/expected-output.txt
+++ b/tests/e2e/PSR_0_Autoloader/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/PestTestFramework/expected-output.txt b/tests/e2e/PestTestFramework/expected-output.txt
index 0a408ead3..00e5e6f54 100644
--- a/tests/e2e/PestTestFramework/expected-output.txt
+++ b/tests/e2e/PestTestFramework/expected-output.txt
@@ -2,6 +2,7 @@ Total: 11
Killed: 9
Errored: 0
+Syntax Errors: 0
Escaped: 2
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/PhpUnit_Depends_Order/expected-output.txt b/tests/e2e/PhpUnit_Depends_Order/expected-output.txt
index aa6c03830..95e73f9ca 100644
--- a/tests/e2e/PhpUnit_Depends_Order/expected-output.txt
+++ b/tests/e2e/PhpUnit_Depends_Order/expected-output.txt
@@ -2,6 +2,7 @@ Total: 0
Killed: 0
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Phpunit_Bat_Wrapper/expected-output.txt b/tests/e2e/Phpunit_Bat_Wrapper/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/Phpunit_Bat_Wrapper/expected-output.txt
+++ b/tests/e2e/Phpunit_Bat_Wrapper/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Profiles_Ignore_Combination/expected-output.txt b/tests/e2e/Profiles_Ignore_Combination/expected-output.txt
index 01d3ef456..c0e51b061 100644
--- a/tests/e2e/Profiles_Ignore_Combination/expected-output.txt
+++ b/tests/e2e/Profiles_Ignore_Combination/expected-output.txt
@@ -2,6 +2,7 @@ Total: 4
Killed: 0
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Provide_Existing_Coverage/expected-output_phpunit.txt b/tests/e2e/Provide_Existing_Coverage/expected-output_phpunit.txt
index e0322f347..e2f0f5d54 100644
--- a/tests/e2e/Provide_Existing_Coverage/expected-output_phpunit.txt
+++ b/tests/e2e/Provide_Existing_Coverage/expected-output_phpunit.txt
@@ -28,6 +28,9 @@ Line 15
Errors mutants:
===============
+Syntax Errors mutants:
+======================
+
Escaped mutants:
================
diff --git a/tests/e2e/Save_PHPUnit_Bootstrap_File/expected-output.txt b/tests/e2e/Save_PHPUnit_Bootstrap_File/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/Save_PHPUnit_Bootstrap_File/expected-output.txt
+++ b/tests/e2e/Save_PHPUnit_Bootstrap_File/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Skip_Initial_Tests/expected-output_phpunit.txt b/tests/e2e/Skip_Initial_Tests/expected-output_phpunit.txt
index c73cd3f1f..9323b3835 100644
--- a/tests/e2e/Skip_Initial_Tests/expected-output_phpunit.txt
+++ b/tests/e2e/Skip_Initial_Tests/expected-output_phpunit.txt
@@ -28,6 +28,9 @@ Line 15
Errors mutants:
===============
+Syntax Errors mutants:
+======================
+
Escaped mutants:
================
diff --git a/tests/e2e/Source_Directories_Config/expected-output.txt b/tests/e2e/Source_Directories_Config/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/Source_Directories_Config/expected-output.txt
+++ b/tests/e2e/Source_Directories_Config/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/SymfonyBridge/expected-output.txt b/tests/e2e/SymfonyBridge/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/SymfonyBridge/expected-output.txt
+++ b/tests/e2e/SymfonyBridge/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/SymfonyFlex/expected-output.txt b/tests/e2e/SymfonyFlex/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/SymfonyFlex/expected-output.txt
+++ b/tests/e2e/SymfonyFlex/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Syntax_Error_PHPUnit/README.md b/tests/e2e/Syntax_Error_PHPUnit/README.md
new file mode 100644
index 000000000..e5f27ff79
--- /dev/null
+++ b/tests/e2e/Syntax_Error_PHPUnit/README.md
@@ -0,0 +1,7 @@
+# Title
+
+* This test ensures that the results produced by Mutators that generate invalid syntax (with "syntax error") are treated separately
+
+## Summary
+
+Related to https://github.com/infection/infection/issues/262
diff --git a/tests/e2e/Syntax_Error_PHPUnit/composer.json b/tests/e2e/Syntax_Error_PHPUnit/composer.json
new file mode 100644
index 000000000..8f49b60f7
--- /dev/null
+++ b/tests/e2e/Syntax_Error_PHPUnit/composer.json
@@ -0,0 +1,15 @@
+{
+ "require-dev": {
+ "phpunit/phpunit": "^9.5.8"
+ },
+ "autoload": {
+ "psr-4": {
+ "Syntax_Error_PHPUnit\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Syntax_Error_PHPUnit\\Test\\": "tests/"
+ }
+ }
+}
diff --git a/tests/e2e/Syntax_Error_PHPUnit/expected-output.txt b/tests/e2e/Syntax_Error_PHPUnit/expected-output.txt
new file mode 100644
index 000000000..b3a0bb786
--- /dev/null
+++ b/tests/e2e/Syntax_Error_PHPUnit/expected-output.txt
@@ -0,0 +1,9 @@
+Total: 1
+
+Killed: 0
+Errored: 0
+Syntax Errors: 1
+Escaped: 0
+Timed Out: 0
+Skipped: 0
+Not Covered: 0
diff --git a/tests/e2e/Syntax_Error_PHPUnit/infection.json b/tests/e2e/Syntax_Error_PHPUnit/infection.json
new file mode 100644
index 000000000..3164ffe15
--- /dev/null
+++ b/tests/e2e/Syntax_Error_PHPUnit/infection.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "../../../resources/schema.json",
+ "timeout": 25,
+ "source": {
+ "directories": [
+ "src"
+ ]
+ },
+ "logs": {
+ "summary": "infection.log"
+ },
+ "tmpDir": ".",
+ "mutators": {
+ "@default": false,
+ "SyntaxError": true
+ }
+}
diff --git a/tests/e2e/Syntax_Error_PHPUnit/phpunit.xml b/tests/e2e/Syntax_Error_PHPUnit/phpunit.xml
new file mode 100644
index 000000000..df138fe3f
--- /dev/null
+++ b/tests/e2e/Syntax_Error_PHPUnit/phpunit.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ ./tests/
+
+
+
+
+
+ ./src/
+
+
+
diff --git a/tests/e2e/Syntax_Error_PHPUnit/src/SourceClass.php b/tests/e2e/Syntax_Error_PHPUnit/src/SourceClass.php
new file mode 100644
index 000000000..f92913337
--- /dev/null
+++ b/tests/e2e/Syntax_Error_PHPUnit/src/SourceClass.php
@@ -0,0 +1,16 @@
+foo();
+ }
+}
diff --git a/tests/e2e/Syntax_Error_PHPUnit/tests/SourceClassTest.php b/tests/e2e/Syntax_Error_PHPUnit/tests/SourceClassTest.php
new file mode 100644
index 000000000..ba2ac274d
--- /dev/null
+++ b/tests/e2e/Syntax_Error_PHPUnit/tests/SourceClassTest.php
@@ -0,0 +1,15 @@
+assertSame('hello', $sourceClass->bar());
+ }
+}
diff --git a/tests/e2e/Syntax_Error_Pest/README.md b/tests/e2e/Syntax_Error_Pest/README.md
new file mode 100644
index 000000000..e5f27ff79
--- /dev/null
+++ b/tests/e2e/Syntax_Error_Pest/README.md
@@ -0,0 +1,7 @@
+# Title
+
+* This test ensures that the results produced by Mutators that generate invalid syntax (with "syntax error") are treated separately
+
+## Summary
+
+Related to https://github.com/infection/infection/issues/262
diff --git a/tests/e2e/Syntax_Error_Pest/composer.json b/tests/e2e/Syntax_Error_Pest/composer.json
new file mode 100644
index 000000000..a41b55ed7
--- /dev/null
+++ b/tests/e2e/Syntax_Error_Pest/composer.json
@@ -0,0 +1,15 @@
+{
+ "require-dev": {
+ "pestphp/pest": "^1.2.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Syntax_Error_Pest\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Syntax_Error_Pest\\Test\\": "tests/"
+ }
+ }
+}
diff --git a/tests/e2e/Syntax_Error_Pest/expected-output.txt b/tests/e2e/Syntax_Error_Pest/expected-output.txt
new file mode 100644
index 000000000..b3a0bb786
--- /dev/null
+++ b/tests/e2e/Syntax_Error_Pest/expected-output.txt
@@ -0,0 +1,9 @@
+Total: 1
+
+Killed: 0
+Errored: 0
+Syntax Errors: 1
+Escaped: 0
+Timed Out: 0
+Skipped: 0
+Not Covered: 0
diff --git a/tests/e2e/Syntax_Error_Pest/infection.json b/tests/e2e/Syntax_Error_Pest/infection.json
new file mode 100644
index 000000000..3164ffe15
--- /dev/null
+++ b/tests/e2e/Syntax_Error_Pest/infection.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "../../../resources/schema.json",
+ "timeout": 25,
+ "source": {
+ "directories": [
+ "src"
+ ]
+ },
+ "logs": {
+ "summary": "infection.log"
+ },
+ "tmpDir": ".",
+ "mutators": {
+ "@default": false,
+ "SyntaxError": true
+ }
+}
diff --git a/tests/e2e/Syntax_Error_Pest/phpunit.xml b/tests/e2e/Syntax_Error_Pest/phpunit.xml
new file mode 100644
index 000000000..df138fe3f
--- /dev/null
+++ b/tests/e2e/Syntax_Error_Pest/phpunit.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ ./tests/
+
+
+
+
+
+ ./src/
+
+
+
diff --git a/tests/e2e/Syntax_Error_Pest/run_tests.bash b/tests/e2e/Syntax_Error_Pest/run_tests.bash
new file mode 100755
index 000000000..949873a44
--- /dev/null
+++ b/tests/e2e/Syntax_Error_Pest/run_tests.bash
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+readonly INFECTION="../../../bin/infection --test-framework=pest"
+
+set -e pipefail
+
+if [ "$DRIVER" = "phpdbg" ]
+then
+ phpdbg -qrr $INFECTION
+else
+ php $INFECTION
+fi
+
+diff -w expected-output.txt infection.log
diff --git a/tests/e2e/Syntax_Error_Pest/src/ForPest.php b/tests/e2e/Syntax_Error_Pest/src/ForPest.php
new file mode 100644
index 000000000..6692b610f
--- /dev/null
+++ b/tests/e2e/Syntax_Error_Pest/src/ForPest.php
@@ -0,0 +1,16 @@
+foo();
+ }
+}
diff --git a/tests/e2e/Syntax_Error_Pest/tests/ForPestTest.php b/tests/e2e/Syntax_Error_Pest/tests/ForPestTest.php
new file mode 100644
index 000000000..e12e6c83f
--- /dev/null
+++ b/tests/e2e/Syntax_Error_Pest/tests/ForPestTest.php
@@ -0,0 +1,11 @@
+hello())->toBe('hello');
+});
diff --git a/tests/e2e/Test_Framework_Options_Config/expected-output.txt b/tests/e2e/Test_Framework_Options_Config/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/Test_Framework_Options_Config/expected-output.txt
+++ b/tests/e2e/Test_Framework_Options_Config/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/Test_PHP_Options_Config/expected-output.txt b/tests/e2e/Test_PHP_Options_Config/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/Test_PHP_Options_Config/expected-output.txt
+++ b/tests/e2e/Test_PHP_Options_Config/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/TimeoutSkipped/expected-output.txt b/tests/e2e/TimeoutSkipped/expected-output.txt
index 4c78b02b4..129bf771f 100644
--- a/tests/e2e/TimeoutSkipped/expected-output.txt
+++ b/tests/e2e/TimeoutSkipped/expected-output.txt
@@ -2,6 +2,7 @@ Total: 5
Killed: 2
Errored: 0
+Syntax Errors: 0
Escaped: 2
Timed Out: 1
Skipped: 0
diff --git a/tests/e2e/Variables_Order_EGPCS/expected-output.txt b/tests/e2e/Variables_Order_EGPCS/expected-output.txt
index 7bcb80de3..a31fc060f 100644
--- a/tests/e2e/Variables_Order_EGPCS/expected-output.txt
+++ b/tests/e2e/Variables_Order_EGPCS/expected-output.txt
@@ -2,6 +2,7 @@ Total: 1
Killed: 1
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
diff --git a/tests/e2e/YieldValue/expected-output.txt b/tests/e2e/YieldValue/expected-output.txt
index 3c92154a1..1ce4f94ab 100644
--- a/tests/e2e/YieldValue/expected-output.txt
+++ b/tests/e2e/YieldValue/expected-output.txt
@@ -1,6 +1,6 @@
# Effects per Mutator
-| Mutator | Mutations | Killed | Escaped | Errors | Timed Out | Skipped | MSI (%s) | Covered MSI (%s) |
-| ---------- | --------- | ------ | ------- | ------ | --------- | ------- | -------- | ---------------- |
-| YieldValue | 2 | 1 | 0 | 0 | 0 | 0 | 50.00 | 100.00 |
-| Yield_ | 2 | 1 | 0 | 0 | 0 | 0 | 50.00 | 100.00 |
+| Mutator | Mutations | Killed | Escaped | Errors | Syntax Errors | Timed Out | Skipped | MSI (%s) | Covered MSI (%s) |
+| ---------- | --------- | ------ | ------- | ------ | ------------- | --------- | ------- | -------- | ---------------- |
+| YieldValue | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 50.00 | 100.00 |
+| Yield_ | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 50.00 | 100.00 |
diff --git a/tests/phpunit/Console/OutputFormatter/DotFormatterTest.php b/tests/phpunit/Console/OutputFormatter/DotFormatterTest.php
index 60f440f6f..fd6768c6f 100644
--- a/tests/phpunit/Console/OutputFormatter/DotFormatterTest.php
+++ b/tests/phpunit/Console/OutputFormatter/DotFormatterTest.php
@@ -58,6 +58,7 @@ public function test_start_logs_initial_starting_text(): void
. 'M: escaped, '
. 'U: uncovered, '
. 'E: fatal error, '
+ . 'X: syntax error, '
. 'T: timed out, '
. 'S: skipped',
'',
@@ -184,7 +185,7 @@ public function test_it_prints_total_number_of_mutations(): void
$this->assertSame(str_replace("\n", PHP_EOL,
<<<'TXT'
-.: killed, M: escaped, U: uncovered, E: fatal error, T: timed out, S: skipped
+.: killed, M: escaped, U: uncovered, E: fatal error, X: syntax error, T: timed out, S: skipped
.................................................. ( 50 / 127)
.................................................. (100 / 127)
@@ -213,7 +214,7 @@ public function test_it_prints_current_number_of_pending_mutations(): void
$this->assertSame(str_replace("\n", PHP_EOL,
<<<'TXT'
-.: killed, M: escaped, U: uncovered, E: fatal error, T: timed out, S: skipped
+.: killed, M: escaped, U: uncovered, E: fatal error, X: syntax error, T: timed out, S: skipped
.................................................. ( 50)
.................................................. ( 100)
@@ -252,6 +253,7 @@ private function getStartOutputFormatter()
. 'M: escaped, '
. 'U: uncovered, '
. 'E: fatal error, '
+ . 'X: syntax error, '
. 'T: timed out, '
. 'S: skipped',
'',
diff --git a/tests/phpunit/Console/OutputFormatterStyleConfiguratorTest.php b/tests/phpunit/Console/OutputFormatterStyleConfiguratorTest.php
index ad0d7ad90..6c1214269 100644
--- a/tests/phpunit/Console/OutputFormatterStyleConfiguratorTest.php
+++ b/tests/phpunit/Console/OutputFormatterStyleConfiguratorTest.php
@@ -46,7 +46,7 @@ public function test_it_adds_styles(): void
{
$formatter = $this->createMock(OutputFormatterInterface::class);
$formatter
- ->expects($this->exactly(12))
+ ->expects($this->exactly(13))
->method('setStyle')
;
diff --git a/tests/phpunit/Fixtures/TestFramework/DummyTestFrameworkAdapter.php b/tests/phpunit/Fixtures/TestFramework/DummyTestFrameworkAdapter.php
index 7f2d982a7..c18dcf6bf 100644
--- a/tests/phpunit/Fixtures/TestFramework/DummyTestFrameworkAdapter.php
+++ b/tests/phpunit/Fixtures/TestFramework/DummyTestFrameworkAdapter.php
@@ -5,6 +5,7 @@
namespace Infection\Tests\Fixtures\TestFramework;
use Infection\AbstractTestFramework\TestFrameworkAdapter;
+use Infection\AbstractTestFramework\UnsupportedTestFrameworkVersion;
final class DummyTestFrameworkAdapter implements TestFrameworkAdapter
{
diff --git a/tests/phpunit/Logger/CreateMetricsCalculator.php b/tests/phpunit/Logger/CreateMetricsCalculator.php
index 7d3dc21fa..70eabdda4 100644
--- a/tests/phpunit/Logger/CreateMetricsCalculator.php
+++ b/tests/phpunit/Logger/CreateMetricsCalculator.php
@@ -93,6 +93,18 @@ private function feedCollector(Collector $collector): void
DetectionStatus::ERROR,
'error#1'
),
+ $this->createMutantExecutionResult(
+ 0,
+ For_::class,
+ DetectionStatus::SYNTAX_ERROR,
+ 'syntaxError#0'
+ ),
+ $this->createMutantExecutionResult(
+ 1,
+ PregQuote::class,
+ DetectionStatus::SYNTAX_ERROR,
+ 'syntaxError#1'
+ ),
$this->createMutantExecutionResult(
0,
For_::class,
diff --git a/tests/phpunit/Logger/DebugFileLoggerTest.php b/tests/phpunit/Logger/DebugFileLoggerTest.php
index cbf9f63ef..bbe286361 100644
--- a/tests/phpunit/Logger/DebugFileLoggerTest.php
+++ b/tests/phpunit/Logger/DebugFileLoggerTest.php
@@ -74,6 +74,9 @@ public function metricsProvider(): iterable
Errors mutants:
===============
+Syntax Errors mutants:
+======================
+
Escaped mutants:
================
@@ -94,7 +97,7 @@ public function metricsProvider(): iterable
$this->createCompleteResultsCollector(),
false,
<<<'TXT'
-Total: 12
+Total: 14
Killed mutants:
===============
@@ -116,6 +119,16 @@ public function metricsProvider(): iterable
Line 10
+Syntax Errors mutants:
+======================
+
+Mutator: PregQuote
+Line 9
+
+Mutator: For_
+Line 10
+
+
Escaped mutants:
================
@@ -163,7 +176,7 @@ public function metricsProvider(): iterable
$this->createCompleteResultsCollector(),
true,
<<<'TXT'
-Total: 12
+Total: 14
Killed mutants:
===============
@@ -185,6 +198,16 @@ public function metricsProvider(): iterable
Line 10
+Syntax Errors mutants:
+======================
+
+Mutator: PregQuote
+Line 9
+
+Mutator: For_
+Line 10
+
+
Escaped mutants:
================
diff --git a/tests/phpunit/Logger/JsonLoggerTest.php b/tests/phpunit/Logger/JsonLoggerTest.php
index a4d1f933e..f3fc9c0db 100644
--- a/tests/phpunit/Logger/JsonLoggerTest.php
+++ b/tests/phpunit/Logger/JsonLoggerTest.php
@@ -82,6 +82,7 @@ public function metricsProvider(): iterable
'notCoveredCount' => 0,
'escapedCount' => 0,
'errorCount' => 0,
+ 'syntaxErrorCount' => 0,
'skippedCount' => 0,
'timeOutCount' => 0,
'msi' => 0,
@@ -92,6 +93,7 @@ public function metricsProvider(): iterable
'timeouted' => [],
'killed' => [],
'errored' => [],
+ 'syntaxErrors' => [],
'uncovered' => [],
],
];
@@ -102,16 +104,17 @@ public function metricsProvider(): iterable
$this->createCompleteResultsCollector(),
[
'stats' => [
- 'totalMutantsCount' => 12,
+ 'totalMutantsCount' => 14,
'killedCount' => 2,
'notCoveredCount' => 2,
'escapedCount' => 2,
'errorCount' => 2,
+ 'syntaxErrorCount' => 2,
'skippedCount' => 2,
'timeOutCount' => 2,
- 'msi' => 60,
- 'mutationCodeCoverage' => 80,
- 'coveredCodeMsi' => 75,
+ 'msi' => 50,
+ 'mutationCodeCoverage' => 83.33,
+ 'coveredCodeMsi' => 60,
],
'escaped' => [
[
@@ -209,6 +212,30 @@ public function metricsProvider(): iterable
'processOutput' => 'process output',
],
],
+ 'syntaxErrors' => [
+ [
+ 'mutator' => [
+ 'mutatorName' => 'PregQuote',
+ 'originalSourceCode' => ' ' 'foo/bar',
+ 'originalStartLine' => 9,
+ ],
+ 'diff' => str_replace("\n", PHP_EOL, "--- Original\n+++ New\n@@ @@\n\n- echo 'original';\n+ echo 'syntaxError#1';"),
+ 'processOutput' => 'process output',
+ ],
+ [
+ 'mutator' => [
+ 'mutatorName' => 'For_',
+ 'originalSourceCode' => ' ' 'foo/bar',
+ 'originalStartLine' => 10,
+ ],
+ 'diff' => str_replace("\n", PHP_EOL, "--- Original\n+++ New\n@@ @@\n\n- echo 'original';\n+ echo 'syntaxError#0';"),
+ 'processOutput' => 'process output',
+ ],
+ ],
'uncovered' => [],
],
];
@@ -224,6 +251,7 @@ public function metricsProvider(): iterable
'notCoveredCount' => 1,
'escapedCount' => 0,
'errorCount' => 0,
+ 'syntaxErrorCount' => 0,
'skippedCount' => 0,
'timeOutCount' => 0,
'msi' => 0,
@@ -234,6 +262,7 @@ public function metricsProvider(): iterable
'timeouted' => [],
'killed' => [],
'errored' => [],
+ 'syntaxErrors' => [],
'uncovered' => [
[
'mutator' => [
@@ -261,6 +290,7 @@ public function metricsProvider(): iterable
'notCoveredCount' => 0,
'escapedCount' => 0,
'errorCount' => 0,
+ 'syntaxErrorCount' => 0,
'skippedCount' => 0,
'timeOutCount' => 0,
'msi' => 0,
@@ -271,6 +301,7 @@ public function metricsProvider(): iterable
'timeouted' => [],
'killed' => [],
'errored' => [],
+ 'syntaxErrors' => [],
'uncovered' => [
[
'mutator' => [
diff --git a/tests/phpunit/Logger/PerMutatorLoggerTest.php b/tests/phpunit/Logger/PerMutatorLoggerTest.php
index 22e484004..d9a9262ba 100644
--- a/tests/phpunit/Logger/PerMutatorLoggerTest.php
+++ b/tests/phpunit/Logger/PerMutatorLoggerTest.php
@@ -66,8 +66,8 @@ public function metricsProvider(): iterable
<<<'TXT'
# Effects per Mutator
-| Mutator | Mutations | Killed | Escaped | Errors | Timed Out | Skipped | MSI (%s) | Covered MSI (%s) |
-| ------- | --------- | ------ | ------- | ------ | --------- | ------- | -------- | ---------------- |
+| Mutator | Mutations | Killed | Escaped | Errors | Syntax Errors | Timed Out | Skipped | MSI (%s) | Covered MSI (%s) |
+| ------- | --------- | ------ | ------- | ------ | ------------- | --------- | ------- | -------- | ---------------- |
TXT
];
@@ -78,10 +78,10 @@ public function metricsProvider(): iterable
<<<'TXT'
# Effects per Mutator
-| Mutator | Mutations | Killed | Escaped | Errors | Timed Out | Skipped | MSI (%s) | Covered MSI (%s) |
-| --------- | --------- | ------ | ------- | ------ | --------- | ------- | -------- | ---------------- |
-| For_ | 6 | 1 | 1 | 1 | 1 | 1 | 60.00 | 75.00 |
-| PregQuote | 6 | 1 | 1 | 1 | 1 | 1 | 60.00 | 75.00 |
+| Mutator | Mutations | Killed | Escaped | Errors | Syntax Errors | Timed Out | Skipped | MSI (%s) | Covered MSI (%s) |
+| --------- | --------- | ------ | ------- | ------ | ------------- | --------- | ------- | -------- | ---------------- |
+| For_ | 7 | 1 | 1 | 1 | 1 | 1 | 1 | 50.00 | 60.00 |
+| PregQuote | 7 | 1 | 1 | 1 | 1 | 1 | 1 | 50.00 | 60.00 |
TXT
];
diff --git a/tests/phpunit/Logger/SummaryFileLoggerTest.php b/tests/phpunit/Logger/SummaryFileLoggerTest.php
index 9cf38cb51..3abb453e5 100644
--- a/tests/phpunit/Logger/SummaryFileLoggerTest.php
+++ b/tests/phpunit/Logger/SummaryFileLoggerTest.php
@@ -65,6 +65,7 @@ public function metricsProvider(): iterable
Killed: 0
Errored: 0
+Syntax Errors: 0
Escaped: 0
Timed Out: 0
Skipped: 0
@@ -76,10 +77,11 @@ public function metricsProvider(): iterable
yield 'all mutations' => [
$this->createCompleteMetricsCalculator(),
<<<'TXT'
-Total: 12
+Total: 14
Killed: 2
Errored: 2
+Syntax Errors: 2
Escaped: 2
Timed Out: 2
Skipped: 2
diff --git a/tests/phpunit/Logger/TextFileLoggerTest.php b/tests/phpunit/Logger/TextFileLoggerTest.php
index 1f51a393a..34bb866aa 100644
--- a/tests/phpunit/Logger/TextFileLoggerTest.php
+++ b/tests/phpunit/Logger/TextFileLoggerTest.php
@@ -124,6 +124,9 @@ public function emptyMetricsProvider(): iterable
Errors mutants:
===============
+Syntax Errors mutants:
+======================
+
Not Covered mutants:
====================
@@ -170,6 +173,9 @@ public function emptyMetricsProvider(): iterable
Errors mutants:
===============
+Syntax Errors mutants:
+======================
+
Not Covered mutants:
====================
@@ -213,6 +219,9 @@ public function emptyMetricsProvider(): iterable
Errors mutants:
===============
+Syntax Errors mutants:
+======================
+
TXT
];
@@ -253,6 +262,9 @@ public function emptyMetricsProvider(): iterable
Errors mutants:
===============
+Syntax Errors mutants:
+======================
+
TXT
];
}
@@ -498,6 +510,33 @@ public function completeMetricsProvider(): iterable
process output
+Syntax Errors mutants:
+======================
+
+1) foo/bar:9 [M] PregQuote
+
+--- Original
++++ New
+@@ @@
+
+- echo 'original';
++ echo 'syntaxError#1';
+
+ process output
+
+
+2) foo/bar:10 [M] For_
+
+--- Original
++++ New
+@@ @@
+
+- echo 'original';
++ echo 'syntaxError#0';
+
+ process output
+
+
Not Covered mutants:
====================
@@ -792,6 +831,35 @@ public function completeMetricsProvider(): iterable
process output
+Syntax Errors mutants:
+======================
+
+1) foo/bar:9 [M] PregQuote
+
+--- Original
++++ New
+@@ @@
+
+- echo 'original';
++ echo 'syntaxError#1';
+
+$ bin/phpunit --configuration infection-tmp-phpunit.xml --filter "tests/Acme/FooTest.php"
+ process output
+
+
+2) foo/bar:10 [M] For_
+
+--- Original
++++ New
+@@ @@
+
+- echo 'original';
++ echo 'syntaxError#0';
+
+$ bin/phpunit --configuration infection-tmp-phpunit.xml --filter "tests/Acme/FooTest.php"
+ process output
+
+
Not Covered mutants:
====================
@@ -1038,6 +1106,33 @@ public function completeMetricsProvider(): iterable
process output
+
+Syntax Errors mutants:
+======================
+
+1) foo/bar:9 [M] PregQuote
+
+--- Original
++++ New
+@@ @@
+
+- echo 'original';
++ echo 'syntaxError#1';
+
+ process output
+
+
+2) foo/bar:10 [M] For_
+
+--- Original
++++ New
+@@ @@
+
+- echo 'original';
++ echo 'syntaxError#0';
+
+ process output
+
TXT
];
@@ -1275,6 +1370,35 @@ public function completeMetricsProvider(): iterable
- echo 'original';
+ echo 'error#0';
+$ bin/phpunit --configuration infection-tmp-phpunit.xml --filter "tests/Acme/FooTest.php"
+ process output
+
+
+Syntax Errors mutants:
+======================
+
+1) foo/bar:9 [M] PregQuote
+
+--- Original
++++ New
+@@ @@
+
+- echo 'original';
++ echo 'syntaxError#1';
+
+$ bin/phpunit --configuration infection-tmp-phpunit.xml --filter "tests/Acme/FooTest.php"
+ process output
+
+
+2) foo/bar:10 [M] For_
+
+--- Original
++++ New
+@@ @@
+
+- echo 'original';
++ echo 'syntaxError#0';
+
$ bin/phpunit --configuration infection-tmp-phpunit.xml --filter "tests/Acme/FooTest.php"
process output
diff --git a/tests/phpunit/Metrics/CalculatorTest.php b/tests/phpunit/Metrics/CalculatorTest.php
index 7919f7cd0..cfd726cfa 100644
--- a/tests/phpunit/Metrics/CalculatorTest.php
+++ b/tests/phpunit/Metrics/CalculatorTest.php
@@ -174,9 +174,9 @@ public function metricsCalculatorProvider(): iterable
yield 'nominal' => [
$this->createCompleteMetricsCalculator(),
- 60.,
- 80.0,
- 75.0,
+ 50., // 14 total mutations; 2 skipped; 6 of 12 are killed => 50%
+ 83.33, // 14 total mutations; 2 skipped & 2 not covered; => 10 of 12 => 83.33%
+ 60.0, // 14 total mutations; 2 skipped & 2 not covered; 6 of 10 are killed => 60%
];
}
}
diff --git a/tests/phpunit/Mutator/BaseMutatorTestCase.php b/tests/phpunit/Mutator/BaseMutatorTestCase.php
index 27a85b3e9..54cc9e150 100644
--- a/tests/phpunit/Mutator/BaseMutatorTestCase.php
+++ b/tests/phpunit/Mutator/BaseMutatorTestCase.php
@@ -70,7 +70,7 @@ protected function setUp(): void
* @var string|string[]
* @var mixed[] $settings
*/
- final public function doTest(string $inputCode, $expectedCode = [], array $settings = []): void
+ final public function doTest(string $inputCode, $expectedCode = [], array $settings = [], bool $allowInvalidCode = false): void
{
$expectedCodeSamples = (array) $expectedCode;
@@ -106,7 +106,10 @@ final public function doTest(string $inputCode, $expectedCode = [], array $setti
StringNormalizer::normalizeString($expectedCodeSample),
StringNormalizer::normalizeString($realMutatedCode)
);
- $this->assertSyntaxIsValid($realMutatedCode);
+
+ if (!$allowInvalidCode) {
+ $this->assertSyntaxIsValid($realMutatedCode);
+ }
}
}
diff --git a/tests/phpunit/Mutator/ProfileListProvider.php b/tests/phpunit/Mutator/ProfileListProvider.php
index ada99f0ae..9dbc81850 100644
--- a/tests/phpunit/Mutator/ProfileListProvider.php
+++ b/tests/phpunit/Mutator/ProfileListProvider.php
@@ -44,6 +44,7 @@
use Infection\Mutator\Mutator;
use Infection\Mutator\NoopMutator;
use Infection\Mutator\ProfileList;
+use Infection\Mutator\SyntaxError;
use ReflectionClass;
use function Safe\ksort;
use function Safe\realpath;
@@ -98,7 +99,7 @@ public static function implementedMutatorProvider(): iterable
$shortClassName = substr($file->getFilename(), 0, -4);
$className = self::getMutatorClassNameFromPath($file->getPathname());
- if (in_array($className, [IgnoreMutator::class, NoopMutator::class], true)) {
+ if (in_array($className, [IgnoreMutator::class, NoopMutator::class, SyntaxError::class], true)) {
continue;
}
diff --git a/tests/phpunit/Mutator/SyntaxErrorTest.php b/tests/phpunit/Mutator/SyntaxErrorTest.php
new file mode 100644
index 000000000..5916197c4
--- /dev/null
+++ b/tests/phpunit/Mutator/SyntaxErrorTest.php
@@ -0,0 +1,66 @@
+doTest($input, $expected, [], true);
+ }
+
+ public function mutationsProvider(): iterable
+ {
+ yield 'It mutates method call to invalid syntax' => [
+ <<<'PHP'
+methodCall();
+PHP
+ ,
+ <<<'PHP'
+methodCall();
+PHP
+ ];
+ }
+}
diff --git a/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterTest.php b/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterTest.php
index bc5c85802..69ccfb90d 100644
--- a/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterTest.php
+++ b/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterTest.php
@@ -91,9 +91,9 @@ public function test_it_supports_junit_reports(): void
}
/**
- * @dataProvider outputProvider
+ * @dataProvider passOutputProvider
*/
- public function test_it_can_tell_the_outcome_of_the_tests_from_the_output(
+ public function test_it_can_tell_if_tests_pass_from_the_output(
string $output,
bool $expected
): void {
@@ -102,6 +102,18 @@ public function test_it_can_tell_the_outcome_of_the_tests_from_the_output(
$this->assertSame($expected, $actual);
}
+ /**
+ * @dataProvider syntaxErrorOutputProvider
+ */
+ public function test_it_can_tell_if_there_is_a_syntax_error_from_the_output(
+ string $output,
+ bool $expected
+ ): void {
+ $actual = $this->adapter->isSyntaxError($output);
+
+ $this->assertSame($expected, $actual);
+ }
+
/**
* @dataProvider memoryReportProvider
*/
@@ -266,7 +278,7 @@ public function test_it_provides_initial_test_run_command_line_when_coverage_rep
);
}
- public function outputProvider(): iterable
+ public function passOutputProvider(): iterable
{
yield ['Tests: 1 risked', true];
@@ -275,6 +287,21 @@ public function outputProvider(): iterable
yield ['Tests: 1 failed, 8 passed', false];
}
+ public function syntaxErrorOutputProvider(): iterable
+ {
+ yield ['Tests: 1 risked', false];
+
+ yield [
+ <<"'
+TXT
+ ,
+ true,
+ ];
+ }
+
public function memoryReportProvider(): iterable
{
yield ['Memory: 8.00MB', 8.0];
diff --git a/tests/phpunit/TestFramework/PhpUnit/Adapter/PhpUnitAdapterTest.php b/tests/phpunit/TestFramework/PhpUnit/Adapter/PhpUnitAdapterTest.php
index eb9d9b56e..ce718c2d0 100644
--- a/tests/phpunit/TestFramework/PhpUnit/Adapter/PhpUnitAdapterTest.php
+++ b/tests/phpunit/TestFramework/PhpUnit/Adapter/PhpUnitAdapterTest.php
@@ -91,9 +91,9 @@ public function test_it_supports_junit_reports(): void
}
/**
- * @dataProvider outputProvider
+ * @dataProvider passOutputProvider
*/
- public function test_it_can_tell_the_outcome_of_the_tests_from_the_output(
+ public function test_it_can_tell_if_tests_pass_from_the_output(
string $output,
bool $expected
): void {
@@ -102,6 +102,18 @@ public function test_it_can_tell_the_outcome_of_the_tests_from_the_output(
$this->assertSame($expected, $actual);
}
+ /**
+ * @dataProvider syntaxErrorOutputProvider
+ */
+ public function test_it_can_tell_if_there_is_a_syntax_error_from_the_output(
+ string $output,
+ bool $expected
+ ): void {
+ $actual = $this->adapter->isSyntaxError($output);
+
+ $this->assertSame($expected, $actual);
+ }
+
/**
* @dataProvider memoryReportProvider
*/
@@ -266,7 +278,7 @@ public function test_it_provides_initial_test_run_command_line_when_coverage_rep
);
}
- public function outputProvider(): iterable
+ public function passOutputProvider(): iterable
{
yield ['OK, but incomplete, skipped, or risky tests!', true];
@@ -279,6 +291,13 @@ public function outputProvider(): iterable
yield ['No tests executed!', true];
}
+ public function syntaxErrorOutputProvider(): iterable
+ {
+ yield ['OK, but incomplete, skipped, or risky tests!', false];
+
+ yield ['ParseError: syntax error, unexpected ">"', true];
+ }
+
public function memoryReportProvider(): iterable
{
yield ['Memory: 8.00MB', 8.0];