Merge pull request #24 from weirdan/variadic-tests

Support variadics in test methods
This commit is contained in:
Bruce Weirdan 2019-03-02 21:44:52 +02:00 committed by GitHub
commit e33ae73dc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 131 additions and 22 deletions

View File

@ -231,10 +231,14 @@ class TestCaseHandler implements
} }
$checkParam = $checkParam =
/** @return void */ /**
* @param null|Type\Union $param_default_type
* @return void
*/
function ( function (
Type\Union $potential_argument_type, Type\Union $potential_argument_type,
FunctionLikeParameter $param, Type\Union $param_type,
$param_default_type,
int $param_offset int $param_offset
) use ( ) use (
$codebase, $codebase,
@ -243,9 +247,8 @@ class TestCaseHandler implements
$provider_return_type_string, $provider_return_type_string,
$provider_docblock_location $provider_docblock_location
) { ) {
assert(null !== $param->type); $param_type = clone $param_type;
$param_type = clone $param->type; if ($param_default_type) {
if ($param->default_type) {
$param_type->possibly_undefined = true; $param_type->possibly_undefined = true;
} }
if (self::isTypeContainedByType($codebase, $potential_argument_type, $param_type)) { if (self::isTypeContainedByType($codebase, $potential_argument_type, $param_type)) {
@ -258,7 +261,7 @@ class TestCaseHandler implements
. ' by ' . $provider_method_id . '():(' . $provider_return_type_string . ')', . ' by ' . $provider_method_id . '():(' . $provider_return_type_string . ')',
$provider_docblock_location $provider_docblock_location
)); ));
} elseif ($potential_argument_type->possibly_undefined && !$param->default_type) { } elseif ($potential_argument_type->possibly_undefined && !$param_default_type) {
IssueBuffer::accepts(new Issue\InvalidArgument( IssueBuffer::accepts(new Issue\InvalidArgument(
'Argument ' . ($param_offset + 1) . ' of ' . $method_name 'Argument ' . ($param_offset + 1) . ' of ' . $method_name
. ' has no default value, but possibly undefined ' . ' has no default value, but possibly undefined '
@ -284,32 +287,60 @@ class TestCaseHandler implements
// check that all of the required (?) params accept value type // check that all of the required (?) params accept value type
$potential_argument_type = $dataset_type->type_params[1]; $potential_argument_type = $dataset_type->type_params[1];
foreach ($method_storage->params as $param_offset => $param) { foreach ($method_storage->params as $param_offset => $param) {
$checkParam($potential_argument_type, $param, $param_offset); assert(null !== $param->type);
$checkParam($potential_argument_type, $param->type, $param->default_type, $param_offset);
} }
} else { } else {
// iterate over all params checking if corresponding value type is acceptable // iterate over all params checking if corresponding value type is acceptable
// let's hope properties are sorted in array order // let's hope properties are sorted in array order
$potential_argument_types = array_values($dataset_type->properties); $potential_argument_types = array_values($dataset_type->properties);
if (count($potential_argument_types) < $method_storage->required_param_count) {
IssueBuffer::accepts(new Issue\TooFewArguments(
'Too few arguments for ' . $method_name
. ' - expecting ' . $method_storage->required_param_count
. ' but saw ' . count($potential_argument_types)
. ' provided by ' . $provider_method_id . '()'
. ':(' . $provider_return_type_string . ')',
$provider_docblock_location,
$method_name
));
}
foreach ($method_storage->params as $param_offset => $param) { foreach ($method_storage->params as $param_offset => $param) {
if (!isset($potential_argument_types[$param_offset])) { if (!isset($potential_argument_types[$param_offset])) {
// variadics are never required
// and they always come last
if ($param->is_variadic) {
break;
}
// reached default params, so it's fine, but let's continue
// because MisplacedRequiredParam could be suppressed
if ($param->default_type) {
continue;
}
IssueBuffer::accepts(new Issue\TooFewArguments(
'Too few arguments for ' . $method_name
. ' - expecting at least ' . ($param_offset + 1)
. ', but saw ' . count($potential_argument_types)
. ' provided by ' . $provider_method_id . '()'
. ':(' . $provider_return_type_string . ')',
$provider_docblock_location,
$method_name
));
break; break;
} }
$potential_argument_type = $potential_argument_types[$param_offset]; $potential_argument_type = $potential_argument_types[$param_offset];
$checkParam($potential_argument_type, $param, $param_offset); assert(null !== $param->type);
if ($param->is_variadic) {
/** @var Type\Atomic\TArray $variadic_type */
$variadic_type = $param->type->getTypes()['array'];
$variadic_param_type = $variadic_type->type_params[1] ?? Type::getMixed();
// check remaining argument types
for (; $param_offset < count($potential_argument_types); $param_offset++) {
$potential_argument_type = $potential_argument_types[$param_offset];
$checkParam(
$potential_argument_type,
$variadic_param_type,
$variadic_param_type,
$param_offset
);
}
break;
}
$checkParam($potential_argument_type, $param->type, $param->default_type, $param_offset);
} }
} }
} }

View File

@ -469,8 +469,8 @@ Feature: TestCase
""" """
When I run Psalm When I run Psalm
Then I see these errors Then I see these errors
| Type | Message | | Type | Message |
| TooFewArguments | Too few arguments for NS\MyTestCase::testSomething - expecting 2 but saw 1 provided by NS\MyTestCase::provide():(iterable<string, array{0:int}>) | | TooFewArguments | Too few arguments for NS\MyTestCase::testSomething - expecting at least 2, but saw 1 provided by NS\MyTestCase::provide():(iterable<string, array{0:int}>) |
And I see no other errors And I see no other errors
Scenario: Referenced providers are not marked as unused Scenario: Referenced providers are not marked as unused
@ -857,3 +857,81 @@ Feature: TestCase
| Type | Message | | Type | Message |
| InvalidArgument | Argument 1 of NS\MyTestTrait::testSomething expects string, int provided by NS\MyTestTrait::provide():(iterable<int, array<array-key, int>>) | | InvalidArgument | Argument 1 of NS\MyTestTrait::testSomething expects string, int provided by NS\MyTestTrait::provide():(iterable<int, array<array-key, int>>) |
And I see no other errors And I see no other errors
Scenario: Providers may omit variadic part for variadic tests
Given I have the following code
"""
class MyTestCase extends TestCase {
/** @return iterable<string,array{int}> */
public function provide() {
yield "data set" => [1];
}
/**
* @dataProvider provide
* @return void
*/
public function testSomething(int $i, ...$rest) {}
}
"""
When I run Psalm
Then I see no errors
Scenario: Providers may omit non-varidic params with default for variadic tests
Given I have the following code
"""
class MyTestCase extends TestCase {
/** @return iterable<string,array{int}> */
public function provide() {
yield "data set" => [1];
}
/**
* @dataProvider provide
* @return void
*/
public function testSomething(int $i, string $s = "", ...$rest) {}
}
"""
When I run Psalm
Then I see no errors
Scenario: Providers may not omit non-varidic params with no default for variadic tests
Given I have the following code
"""
class MyTestCase extends TestCase {
/** @return iterable<string,array{int}> */
public function provide() {
yield "data set" => [1];
}
/**
* @dataProvider provide
* @return void
*/
public function testSomething(int $i, string $s, ...$rest) {}
}
"""
When I run Psalm
Then I see these errors
| Type | Message |
| TooFewArguments | Too few arguments for NS\MyTestCase::testSomething - expecting at least 2, but saw 1 provided by NS\MyTestCase::provide():(iterable<string, array{0:int}>) |
And I see no other errors
Scenario: Providers generating incompatible datasets for variadic tests are reported
Given I have the following code
"""
class MyTestCase extends TestCase {
/** @return iterable<string,array{float,1?:string}> */
public function provide() {
yield "data set" => [1., "a"];
}
/**
* @dataProvider provide
* @return void
*/
public function testSomething(float ...$rest) {}
}
"""
When I run Psalm
Then I see these errors
| Type | Message |
| InvalidArgument | Argument 2 of NS\MyTestCase::testSomething expects float, string provided by NS\MyTestCase::provide():(iterable<string, array{0:float, 1?:string}>) |
And I see no other errors