Skip to content

Commit 5fe6f25

Browse files
Initial work on #6404
1 parent 043a636 commit 5fe6f25

12 files changed

Lines changed: 118 additions & 12 deletions

src/Framework/Attributes/DataProvider.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@
2424
*/
2525
private string $methodName;
2626
private bool $validateArgumentCount;
27+
private bool $skipWhenEmpty;
2728

2829
/**
2930
* @param non-empty-string $methodName
3031
*/
31-
public function __construct(string $methodName, bool $validateArgumentCount = true)
32+
public function __construct(string $methodName, bool $validateArgumentCount = true, bool $skipWhenEmpty = false)
3233
{
3334
$this->methodName = $methodName;
3435
$this->validateArgumentCount = $validateArgumentCount;
36+
$this->skipWhenEmpty = $skipWhenEmpty;
3537
}
3638

3739
/**
@@ -46,4 +48,9 @@ public function validateArgumentCount(): bool
4648
{
4749
return $this->validateArgumentCount;
4850
}
51+
52+
public function skipWhenEmpty(): bool
53+
{
54+
return $this->skipWhenEmpty;
55+
}
4956
}

src/Framework/Attributes/DataProviderExternal.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,18 @@
2929
*/
3030
private string $methodName;
3131
private bool $validateArgumentCount;
32+
private bool $skipWhenEmpty;
3233

3334
/**
3435
* @param class-string $className
3536
* @param non-empty-string $methodName
3637
*/
37-
public function __construct(string $className, string $methodName, bool $validateArgumentCount = true)
38+
public function __construct(string $className, string $methodName, bool $validateArgumentCount = true, bool $skipWhenEmpty = false)
3839
{
3940
$this->className = $className;
4041
$this->methodName = $methodName;
4142
$this->validateArgumentCount = $validateArgumentCount;
43+
$this->skipWhenEmpty = $skipWhenEmpty;
4244
}
4345

4446
/**
@@ -61,4 +63,9 @@ public function validateArgumentCount(): bool
6163
{
6264
return $this->validateArgumentCount;
6365
}
66+
67+
public function skipWhenEmpty(): bool
68+
{
69+
return $this->skipWhenEmpty;
70+
}
6471
}

src/Framework/TestBuilder.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public function build(ReflectionClass $theClass, string $methodName, array $grou
5555
}
5656
}
5757

58-
if ($data !== null) {
58+
if ($data !== null && $data !== []) {
5959
return $this->buildDataProviderTestSuite(
6060
$methodName,
6161
$className,
@@ -69,6 +69,12 @@ public function build(ReflectionClass $theClass, string $methodName, array $grou
6969

7070
$test = new $className($methodName);
7171

72+
if ($data === []) {
73+
$test->setEmptyDataProviderSkipMessage(
74+
'The data provider for this test provided no data, which is explicitly permitted',
75+
);
76+
}
77+
7278
$this->configureTestCase(
7379
$test,
7480
$this->shouldTestMethodBeRunInSeparateProcess($className, $methodName),

src/Framework/TestCase.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,9 @@ abstract class TestCase extends Assert implements Reorderable, SelfDescribing, T
229229
/**
230230
* @var false|resource
231231
*/
232-
private mixed $errorLogCapture = false;
233-
private false|string $previousErrorLogTarget = false;
232+
private mixed $errorLogCapture = false;
233+
private false|string $previousErrorLogTarget = false;
234+
private ?string $emptyDataProviderSkipMessage = null;
234235

235236
/**
236237
* @param non-empty-string $name
@@ -500,6 +501,10 @@ final public function runBare(): void
500501
$this->checkRequirements();
501502
$hasMetRequirements = true;
502503

504+
if ($this->emptyDataProviderSkipMessage !== null) {
505+
$this->markTestSkipped($this->emptyDataProviderSkipMessage);
506+
}
507+
503508
if ($this->inIsolation) {
504509
// @codeCoverageIgnoreStart
505510
$this->invokeBeforeClassHookMethods($hookMethods, $emitter);
@@ -788,6 +793,14 @@ final public function setInIsolation(bool $inIsolation): void
788793
$this->inIsolation = $inIsolation;
789794
}
790795

796+
/**
797+
* @internal This method is not covered by the backward compatibility promise for PHPUnit
798+
*/
799+
final public function setEmptyDataProviderSkipMessage(string $message): void
800+
{
801+
$this->emptyDataProviderSkipMessage = $message;
802+
}
803+
791804
/**
792805
* @internal This method is not covered by the backward compatibility promise for PHPUnit
793806
*

src/Metadata/Api/DataProvider.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ private function dataProvidedByMethods(string $testClassName, ReflectionMethod $
8686

8787
$methodsCalled = [];
8888
$result = [];
89+
$skipWhenEmpty = false;
8990
$testMethodNumberOfParameters = $testMethod->getNumberOfParameters();
9091
$testMethodIsNonVariadic = !$testMethod->isVariadic();
9192

@@ -96,6 +97,10 @@ private function dataProvidedByMethods(string $testClassName, ReflectionMethod $
9697
$dataProviderMethod = new Event\Code\ClassMethod($_dataProvider->className(), $_dataProvider->methodName());
9798
$validateArgumentCount = $testMethodIsNonVariadic && $_dataProvider->validateArgumentCount();
9899

100+
if ($_dataProvider->skipWhenEmpty()) {
101+
$skipWhenEmpty = true;
102+
}
103+
99104
Event\Facade::emitter()->dataProviderMethodCalled(
100105
$testMethodValueObject,
101106
$dataProviderMethod,
@@ -331,6 +336,10 @@ private function dataProvidedByMethods(string $testClassName, ReflectionMethod $
331336
);
332337

333338
if ($result === []) {
339+
if ($skipWhenEmpty) {
340+
return [];
341+
}
342+
334343
throw new InvalidDataProviderException(
335344
'Empty data set provided by data provider',
336345
);

src/Metadata/DataProvider.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,20 @@
2626
*/
2727
private string $methodName;
2828
private bool $validateArgumentCount;
29+
private bool $skipWhenEmpty;
2930

3031
/**
3132
* @param class-string $className
3233
* @param non-empty-string $methodName
3334
*/
34-
protected function __construct(Level $level, string $className, string $methodName, bool $validateArgumentCount)
35+
protected function __construct(Level $level, string $className, string $methodName, bool $validateArgumentCount, bool $skipWhenEmpty)
3536
{
3637
parent::__construct($level);
3738

3839
$this->className = $className;
3940
$this->methodName = $methodName;
4041
$this->validateArgumentCount = $validateArgumentCount;
42+
$this->skipWhenEmpty = $skipWhenEmpty;
4143
}
4244

4345
public function isDataProvider(): true
@@ -65,4 +67,9 @@ public function validateArgumentCount(): bool
6567
{
6668
return $this->validateArgumentCount;
6769
}
70+
71+
public function skipWhenEmpty(): bool
72+
{
73+
return $this->skipWhenEmpty;
74+
}
6875
}

src/Metadata/Metadata.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,9 @@ public static function coversNothingOnMethod(): CoversNothing
143143
* @param class-string $className
144144
* @param non-empty-string $methodName
145145
*/
146-
public static function dataProvider(string $className, string $methodName, bool $validateArgumentCount): DataProvider
146+
public static function dataProvider(string $className, string $methodName, bool $validateArgumentCount, bool $skipWhenEmpty): DataProvider
147147
{
148-
return new DataProvider(Level::METHOD_LEVEL, $className, $methodName, $validateArgumentCount);
148+
return new DataProvider(Level::METHOD_LEVEL, $className, $methodName, $validateArgumentCount, $skipWhenEmpty);
149149
}
150150

151151
public static function dataProviderClosure(Closure $callable, bool $validateArgumentCount): DataProviderClosure

src/Metadata/Parser/AttributeParser.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -608,14 +608,14 @@ public function forMethod(string $className, string $methodName): MetadataCollec
608608
case DataProvider::class:
609609
assert($attributeInstance instanceof DataProvider);
610610

611-
$result[] = Metadata::dataProvider($className, $attributeInstance->methodName(), $attributeInstance->validateArgumentCount());
611+
$result[] = Metadata::dataProvider($className, $attributeInstance->methodName(), $attributeInstance->validateArgumentCount(), $attributeInstance->skipWhenEmpty());
612612

613613
break;
614614

615615
case DataProviderExternal::class:
616616
assert($attributeInstance instanceof DataProviderExternal);
617617

618-
$result[] = Metadata::dataProvider($attributeInstance->className(), $attributeInstance->methodName(), $attributeInstance->validateArgumentCount());
618+
$result[] = Metadata::dataProvider($attributeInstance->className(), $attributeInstance->methodName(), $attributeInstance->validateArgumentCount(), $attributeInstance->skipWhenEmpty());
619619

620620
break;
621621

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\TestFixture\Event;
11+
12+
use PHPUnit\Framework\Attributes\DataProvider;
13+
use PHPUnit\Framework\TestCase;
14+
15+
final class EmptyDataProviderSkipWhenEmptyTest extends TestCase
16+
{
17+
public static function providerMethod(): array
18+
{
19+
return [];
20+
}
21+
22+
#[DataProvider('providerMethod', skipWhenEmpty: true)]
23+
public function testCase(): void
24+
{
25+
}
26+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
The right events are emitted in the right order for a test that uses an empty data provider with skipWhenEmpty
3+
--FILE--
4+
<?php declare(strict_types=1);
5+
$_SERVER['argv'][] = '--do-not-cache-result';
6+
$_SERVER['argv'][] = '--no-configuration';
7+
$_SERVER['argv'][] = '--debug';
8+
$_SERVER['argv'][] = __DIR__ . '/_files/EmptyDataProviderSkipWhenEmptyTest.php';
9+
10+
require __DIR__ . '/../../bootstrap.php';
11+
12+
(new PHPUnit\TextUI\Application)->run($_SERVER['argv']);
13+
--EXPECTF--
14+
PHPUnit Started (PHPUnit %s using %s)
15+
Test Runner Configured
16+
Event Facade Sealed
17+
Data Provider Method Called (PHPUnit\TestFixture\Event\EmptyDataProviderSkipWhenEmptyTest::providerMethod for test method PHPUnit\TestFixture\Event\EmptyDataProviderSkipWhenEmptyTest::testCase)
18+
Data Provider Method Finished for PHPUnit\TestFixture\Event\EmptyDataProviderSkipWhenEmptyTest::testCase:
19+
- PHPUnit\TestFixture\Event\EmptyDataProviderSkipWhenEmptyTest::providerMethod
20+
Test Suite Loaded (1 test)
21+
Test Runner Started
22+
Test Suite Sorted
23+
Test Runner Execution Started (1 test)
24+
Test Suite Started (PHPUnit\TestFixture\Event\EmptyDataProviderSkipWhenEmptyTest, 1 test)
25+
Test Preparation Started (PHPUnit\TestFixture\Event\EmptyDataProviderSkipWhenEmptyTest::testCase)
26+
Test Skipped (PHPUnit\TestFixture\Event\EmptyDataProviderSkipWhenEmptyTest::testCase)
27+
The data provider for this test provided no data, which is explicitly permitted
28+
Test Suite Finished (PHPUnit\TestFixture\Event\EmptyDataProviderSkipWhenEmptyTest, 1 test)
29+
Test Runner Execution Finished
30+
Test Runner Finished
31+
PHPUnit Finished (Shell Exit Code: 0)

0 commit comments

Comments
 (0)