Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support using @depends to depend on classes #3936

Merged
merged 28 commits into from
Jun 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
fd839c8
Explore #3519 using @depends for depending on whole TestCase classes
epdenouden Mar 5, 2019
b0447bb
Update MVP #3519
epdenouden Sep 7, 2019
908dd5a
Track pass-fail state for each individual TestSuite
epdenouden Nov 8, 2019
d727df0
Housekeeping
epdenouden Nov 8, 2019
69dd6a4
Fix directory separators in end-to-end test
epdenouden Nov 8, 2019
82ea061
Restore unit test for test size requirements
epdenouden Nov 8, 2019
36efa22
Restore refactoring
epdenouden Nov 8, 2019
bbb2e4a
WIP stashing on Github before start of new implementation
epdenouden Feb 25, 2020
73fd64c
Refactor logic for normalizing all @depends to class::method
epdenouden Jun 11, 2020
46d9817
Groundwork for new dependency resolver
epdenouden Jun 13, 2020
21aa4da
Playing Super Meat Boy with PHPUnit internals
epdenouden Jun 13, 2020
bb4eea3
Refactoring
epdenouden Jun 13, 2020
d29fc42
MVP implementation of new resolver
epdenouden Jun 13, 2020
29fbcec
Check to satisfy static analysis without BC-breaking interface changes
epdenouden Jun 13, 2020
0c45748
Clean up based on coverage report
epdenouden Jun 13, 2020
1a0ab63
Add coverage for test size dependency check
epdenouden Jun 14, 2020
c4ef44a
Remove obsolete test count and retrieval mechanisms
epdenouden Jun 14, 2020
a18b8c6
Improve code coverage
epdenouden Jun 14, 2020
7f28ced
Add coverage for legacy Test::getGroups helper
epdenouden Jun 14, 2020
4d3c1c5
Improve test coverage of PHPT
epdenouden Jun 14, 2020
b6abe2d
Add test coverage for inheritance of @ticket annotation
epdenouden Jun 14, 2020
4728700
Remove obsolete getters and setters
epdenouden Jun 14, 2020
15ee576
Remove obsolote test scaffolding
epdenouden Jun 14, 2020
9640a0a
Fix typo in self-test annotation
epdenouden Jun 15, 2020
e48432f
Code review clean up
epdenouden Jun 15, 2020
e9a7679
Code review clean up
epdenouden Jun 15, 2020
d06b02f
Code changes to fix static analysis warnings for callable-string
epdenouden Jun 15, 2020
0fc833e
Automatic CS fix
epdenouden Jun 15, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 30 additions & 8 deletions src/Framework/DataProviderTestSuite.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,56 @@
final class DataProviderTestSuite extends TestSuite
{
/**
* @var string[]
* @var array<string>
*/
private $dependencies = [];

/**
* @param string[] $dependencies
* @param array<string> $dependencies
*/
public function setDependencies(array $dependencies): void
{
$this->dependencies = $dependencies;
$this->dependencies = $dependencies;
$this->requiredTests = TestUtil::trimDependencyOptions($dependencies);

foreach ($this->tests as $test) {
if (!$test instanceof TestCase) {
// @codeCoverageIgnoreStart
continue;
// @codeCoverageIgnoreStart
}

$test->setDependencies($dependencies);
}
}

public function getDependencies(): array
/**
* @return array<callable-string>
*/
public function provides(): array
{
return $this->dependencies;
if ($this->providedTests !== null) {
return $this->providedTests;
}

$callableName = $this->getName();

if (\is_callable($callableName, true)) {
$this->providedTests = [$callableName];
} else {
$this->providedTests = [];
}

return $this->providedTests;
}

public function hasDependencies(): bool
/**
* @return array<callable-string>
*/
public function requires(): array
{
return \count($this->dependencies) > 0;
// A DataProviderTestSuite does not have to traverse its child tests
// as these are inherited and cannot reference dataProvider rows directly
return $this->requiredTests ?? [];
}

/**
Expand Down
216 changes: 118 additions & 98 deletions src/Framework/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ abstract class TestCase extends Assert implements SelfDescribing, Test
*/
protected $preserveGlobalState = true;

/**
* @var array<callable-string>
*/
protected $providedTests = [];

/**
* @var array<callable-string>
*/
protected $requiredTests = [];

/**
* @var bool
*/
Expand Down Expand Up @@ -150,7 +160,7 @@ abstract class TestCase extends Assert implements SelfDescribing, Test
private $name = '';

/**
* @var string[]
* @var array<string>
*/
private $dependencies = [];

Expand Down Expand Up @@ -1157,32 +1167,24 @@ public function runBare(): void
public function setName(string $name): void
{
$this->name = $name;
}

/**
* @param string[] $dependencies
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setDependencies(array $dependencies): void
{
$this->dependencies = $dependencies;
}
// Normalized name for dependency resolver
$callableName = \get_class($this) . '::' . $name;

/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getDependencies(): array
{
return $this->dependencies;
if (\is_callable($callableName, true)) {
$this->providedTests = [$callableName];
}
}

/**
* @param array<string> $dependencies
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function hasDependencies(): bool
public function setDependencies(array $dependencies): void
{
return \count($this->dependencies) > 0;
$this->dependencies = $dependencies;
$this->requiredTests = TestUtil::trimDependencyOptions($dependencies);
}

/**
Expand All @@ -1193,14 +1195,6 @@ public function setDependencyInput(array $dependencyInput): void
$this->dependencyInput = $dependencyInput;
}

/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getDependencyInput(): array
{
return $this->dependencyInput;
}

/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
Expand Down Expand Up @@ -1347,14 +1341,6 @@ public function usesDataProvider(): bool
return !empty($this->data);
}

/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function dataDescription(): string
{
return \is_string($this->dataName) ? $this->dataName : '';
}

/**
* @return int|string
*
Expand Down Expand Up @@ -1407,6 +1393,30 @@ public function addWarning(string $warning): void
$this->warnings[] = $warning;
}

/**
* Returns the normalized test name as class::method.
*
* @return array<callable-string>
*/
public function provides(): array
{
return $this->providedTests;
}

/**
* Returns a list of normalized dependency names, class::method.
*
* This list can differ from the raw dependencies as the resolver has
* no need for the [!][shallow]clone prefix that is filtered out
* during normalization.
*
* @return array<callable-string>
*/
public function requires(): array
{
return $this->requiredTests;
}

/**
* Override to run the test and assert its state.
*
Expand Down Expand Up @@ -1942,89 +1952,99 @@ private function checkRequirements(): void

private function handleDependencies(): bool
{
if (!empty($this->dependencies) && !$this->inIsolation) {
$className = \get_class($this);
$passed = $this->result->passed();
$passedKeys = \array_keys($passed);
if ([] === $this->dependencies || $this->inIsolation) {
return true;
}

foreach ($passedKeys as $key => $value) {
$pos = \strpos($value, ' with data set');
$passed = $this->result->passed();
$passedKeys = \array_keys($passed);
$numKeys = \count($passedKeys);

if ($pos !== false) {
$passedKeys[$key] = \substr($value, 0, $pos);
}
for ($i = 0; $i < $numKeys; $i++) {
$pos = \strpos($passedKeys[$i], ' with data set');

if ($pos !== false) {
$passedKeys[$i] = \substr($passedKeys[$i], 0, $pos);
}
}

$passedKeys = \array_flip(\array_unique($passedKeys));
$passedKeys = \array_flip(\array_unique($passedKeys));

foreach ($this->dependencies as $dependency) {
$deepClone = false;
$shallowClone = false;
foreach ($this->dependencies as $dependency) {
if ($dependency === '') {
$this->markSkippedForNotSpecifyingDependency();

return false;
}

if (\substr($dependency, -7) === '::class') {
$dependencyClassName = \substr($dependency, 0, -7);

if (empty($dependency)) {
$this->markSkippedForNotSpecifyingDependency();
if (\array_search($dependencyClassName, $this->result->passedClasses()) === false) {
$this->markSkippedForMissingDependency($dependency);

return false;
}

if (\strpos($dependency, 'clone ') === 0) {
$deepClone = true;
$dependency = \substr($dependency, \strlen('clone '));
} elseif (\strpos($dependency, '!clone ') === 0) {
$deepClone = false;
$dependency = \substr($dependency, \strlen('!clone '));
}
continue;
}

if (\strpos($dependency, 'shallowClone ') === 0) {
$shallowClone = true;
$dependency = \substr($dependency, \strlen('shallowClone '));
} elseif (\strpos($dependency, '!shallowClone ') === 0) {
$shallowClone = false;
$dependency = \substr($dependency, \strlen('!shallowClone '));
}
$deepClone = false;
$shallowClone = false;

if (\strpos($dependency, 'clone ') === 0) {
$deepClone = true;
$dependency = \substr($dependency, \strlen('clone '));
} elseif (\strpos($dependency, '!clone ') === 0) {
$deepClone = false;
$dependency = \substr($dependency, \strlen('!clone '));
}

if (\strpos($dependency, '::') === false) {
$dependency = $className . '::' . $dependency;
if (\strpos($dependency, 'shallowClone ') === 0) {
$shallowClone = true;
$dependency = \substr($dependency, \strlen('shallowClone '));
} elseif (\strpos($dependency, '!shallowClone ') === 0) {
$shallowClone = false;
$dependency = \substr($dependency, \strlen('!shallowClone '));
}

if (!isset($passedKeys[$dependency])) {
if (!$this->isCallableTestMethod($dependency)) {
$this->markWarningForUncallableDependency($dependency);
} else {
$this->markSkippedForMissingDependency($dependency);
}

if (!isset($passedKeys[$dependency])) {
if (!$this->isCallableTestMethod($dependency)) {
$this->warnAboutDependencyThatDoesNotExist($dependency);
} else {
$this->markSkippedForMissingDependency($dependency);
}
return false;
}

if (isset($passed[$dependency])) {
if ($passed[$dependency]['size'] != \PHPUnit\Util\Test::UNKNOWN &&
$this->getSize() != \PHPUnit\Util\Test::UNKNOWN &&
$passed[$dependency]['size'] > $this->getSize()) {
$this->result->addError(
$this,
new SkippedTestError(
'This test depends on a test that is larger than itself.'
),
0
);

return false;
}

if (isset($passed[$dependency])) {
if ($passed[$dependency]['size'] !== TestUtil::UNKNOWN &&
$this->getSize() !== TestUtil::UNKNOWN &&
$passed[$dependency]['size'] > $this->getSize()) {
$this->result->addError(
$this,
new SkippedTestError(
'This test depends on a test that is larger than itself.'
),
0
);

return false;
}

if ($deepClone) {
$deepCopy = new DeepCopy;
$deepCopy->skipUncloneable(false);
if ($deepClone) {
$deepCopy = new DeepCopy;
$deepCopy->skipUncloneable(false);

$this->dependencyInput[$dependency] = $deepCopy->copy($passed[$dependency]['result']);
} elseif ($shallowClone) {
$this->dependencyInput[$dependency] = clone $passed[$dependency]['result'];
} else {
$this->dependencyInput[$dependency] = $passed[$dependency]['result'];
}
$this->dependencyInput[$dependency] = $deepCopy->copy($passed[$dependency]['result']);
} elseif ($shallowClone) {
$this->dependencyInput[$dependency] = clone $passed[$dependency]['result'];
} else {
$this->dependencyInput[$dependency] = null;
$this->dependencyInput[$dependency] = $passed[$dependency]['result'];
}
} else {
$this->dependencyInput[$dependency] = null;
}
}

Expand Down Expand Up @@ -2068,7 +2088,7 @@ private function markSkippedForMissingDependency(string $dependency): void
$this->result->endTest($this, 0);
}

private function warnAboutDependencyThatDoesNotExist(string $dependency): void
private function markWarningForUncallableDependency(string $dependency): void
{
$this->status = BaseTestRunner::STATUS_WARNING;

Expand Down