Skip to content
This repository was archived by the owner on May 13, 2024. It is now read-only.

Commit a1c4075

Browse files
authored
Merge pull request #3 from Potherca/feature/v0.7.0/fix-argument-count-error-inconsistency
Fixes bug caused by inconsistent behaviour in PHP70.
2 parents c96ca7b + 99b59d5 commit a1c4075

File tree

5 files changed

+202
-34
lines changed

5 files changed

+202
-34
lines changed

example/GetCompatibleExceptionNameExample.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
namespace Potherca\PHPUnit\Example\GetCompatibleExceptionName;
44

5+
use Potherca\PhpUnit\Shim\GetCompatibleExceptionName;
6+
57
class Example
68
{
79
public function __construct(array $value) {}
10+
public function example($value) {}
811
}
912

1013
abstract class AbstractTestCase extends \PHPUnit\Framework\TestCase {}
@@ -31,10 +34,10 @@ public function testArithmeticError()
3134
1 >> -1;
3235
}
3336

34-
public function testArgumentCountError()
37+
public function testArgumentCountErrorWithTypeHint()
3538
{
3639
// Please note that `\ArgumentCountError::class` is NOT used, as this will cause an error if `ArgumentCountError` does not exist.
37-
$exceptionName = $this->getCompatibleExceptionName('\\ArgumentCountError');
40+
$exceptionName = $this->getCompatibleExceptionName('\\ArgumentCountError', GetCompatibleExceptionName::ARGUMENT_COUNT_ERROR_WITH_TYPE_HINT);
3841

3942
if (method_exists($this, 'expectExceptionMessageRegExp')) {
4043
/* PHPUnit ^5.2 | ^6.0 */
@@ -49,6 +52,26 @@ public function testArgumentCountError()
4952
new Example();
5053
}
5154

55+
public function testArgumentCountErrorWithoutTypeHint()
56+
{
57+
// Please note that `\ArgumentCountError::class` is NOT used, as this will cause an error if `ArgumentCountError` does not exist.
58+
$exceptionName = $this->getCompatibleExceptionName('\\ArgumentCountError', GetCompatibleExceptionName::ARGUMENT_COUNT_ERROR_WITHOUT_TYPE_HINT);
59+
60+
if (method_exists($this, 'expectExceptionMessageRegExp')) {
61+
/* PHPUnit ^5.2 | ^6.0 */
62+
$this->expectException($exceptionName);
63+
$this->expectExceptionMessageRegExp('/Too few arguments to function|Missing argument 1/');
64+
} else {
65+
/* PHPUnit ^4.3 | =< 5.6 */
66+
$this->setExpectedExceptionRegExp($exceptionName, '/Too few arguments to function|Missing argument 1/');
67+
}
68+
69+
$example = new Example(array());
70+
71+
/** @noinspection PhpParamsInspection */
72+
$example->example();
73+
}
74+
5275
public function testDivisionByZeroError()
5376
{
5477
// Please note that `\DivisionByZeroError::class` is NOT used, as this will cause an error if `DivisionByZeroError` does not exist.

example/PHP5.3/GetCompatibleExceptionNameExample.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
namespace Potherca\PHPUnit\Example\GetCompatibleExceptionName;
44

5+
use Potherca\PhpUnit\Shim\GetCompatibleExceptionName;
6+
57
class Example
68
{
79
public function __construct(array $value) {}
10+
public function example($value) {}
811
}
912

1013
abstract class AbstractTestCase extends \PHPUnit\Framework\TestCase {}
@@ -44,10 +47,10 @@ public function testArithmeticError()
4447
1 >> -1;
4548
}
4649

47-
public function testArgumentCountError()
50+
public function testArgumentCountErrorWithTypeHint()
4851
{
4952
// Please note that `\ArgumentCountError::class` is NOT used, as this will cause an error if `ArgumentCountError` does not exist.
50-
$exceptionName = $this->getCompatibleExceptionName('\\ArgumentCountError');
53+
$exceptionName = $this->getCompatibleExceptionName('\\ArgumentCountError', GetCompatibleExceptionName::ARGUMENT_COUNT_ERROR_WITH_TYPE_HINT);
5154

5255
if (method_exists($this, 'expectExceptionMessageRegExp')) {
5356
/* PHPUnit ^5.2 | ^6.0 */
@@ -62,6 +65,26 @@ public function testArgumentCountError()
6265
new Example();
6366
}
6467

68+
public function testArgumentCountErrorWithoutTypeHint()
69+
{
70+
// Please note that `\ArgumentCountError::class` is NOT used, as this will cause an error if `ArgumentCountError` does not exist.
71+
$exceptionName = $this->getCompatibleExceptionName('\\ArgumentCountError', GetCompatibleExceptionName::ARGUMENT_COUNT_ERROR_WITHOUT_TYPE_HINT);
72+
73+
if (method_exists($this, 'expectExceptionMessageRegExp')) {
74+
/* PHPUnit ^5.2 | ^6.0 */
75+
$this->expectException($exceptionName);
76+
$this->expectExceptionMessageRegExp('/Too few arguments to function|Missing argument 1/');
77+
} else {
78+
/* PHPUnit ^4.3 | =< 5.6 */
79+
$this->setExpectedExceptionRegExp($exceptionName, '/Too few arguments to function|Missing argument 1/');
80+
}
81+
82+
$example = new Example(array());
83+
84+
/** @noinspection PhpParamsInspection */
85+
$example->example();
86+
}
87+
6588
public function testDivisionByZeroError()
6689
{
6790
// Please note that `\DivisionByZeroError::class` is NOT used, as this will cause an error if `DivisionByZeroError` does not exist.

src/Shim/GetCompatibleExceptionName.php

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@
1212
class GetCompatibleExceptionName extends AbstractTraitShim
1313
{
1414
//////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
15+
const ARGUMENT_COUNT_ERROR_WITH_TYPE_HINT = 'type-hint';
16+
const ARGUMENT_COUNT_ERROR_WITHOUT_TYPE_HINT = 'no-type-hint';
17+
const ERROR_CONTEXT_NEEDED = '%s needs a context to decide which exception name to provide. Available options are: "%s"';
1518

1619
/**
1720
* @param string $exceptionName
21+
* @param string $context
1822
*
1923
* @return string
2024
*
@@ -24,7 +28,7 @@ class GetCompatibleExceptionName extends AbstractTraitShim
2428
* @throws \PHPUnit_Framework_AssertionFailedError|\PHPUnit\Framework\AssertionFailedError
2529
* @throws \PHPUnit_Framework_SkippedTestError|\PHPUnit\Framework\SkippedTestError
2630
*/
27-
final public function getCompatibleExceptionName($exceptionName)
31+
final public function getCompatibleExceptionName($exceptionName, $context = '')
2832
{
2933
$matchingExceptionName = '';
3034
$alternative = '';
@@ -48,25 +52,46 @@ final public function getCompatibleExceptionName($exceptionName)
4852
if ($exceptionName === 'ParseError') {
4953
$this->getTestcase()->markTestSkipped('Parse errors can not be caught in PHP5');
5054
} elseif ($exceptionName === 'ArithmeticError') {
51-
/* PHP 7.0 thrown when an error occurs while performing
52-
* mathematical operations. As I have not been able to find the
53-
* PHP5 equivalent, marking as skipped until a working example
54-
* is available
55+
/* PHP 7.0 thrown when an error occurs while performing mathematical operations.
56+
* As I have not been able to find the PHP5 equivalent, marking as skipped until
57+
* a working example is available.
5558
*/
5659
$this->getTestcase()->markTestSkipped('There are no equivalent for Arithmetic errors in PHP5 ');
5760
} elseif ($exceptionName === 'ArgumentCountError') {
58-
// PHP 7.1 thrown when too few arguments are passed to a user-defined function or method.
59-
// PHP 7.0 throws a TypeError with message 'none given'
60-
$matchingExceptionName = $this->getCompatibleExceptionName('\\TypeError');
61+
/* PHP 7.1 thrown when too few arguments are passed to a user-defined function or method.
62+
* PHP 7.0 throws a TypeError if a type-hint is present or a warning if no
63+
* type-hint is present. The only way to know which one is needed is to ask the
64+
* calling side.
65+
*/
66+
$candidates = array(
67+
self::ARGUMENT_COUNT_ERROR_WITH_TYPE_HINT => $this->getCompatibleExceptionName('\\TypeError'),
68+
self::ARGUMENT_COUNT_ERROR_WITHOUT_TYPE_HINT => '\\PHPUnit_Framework_Error',
69+
);
70+
71+
$candidate = self::ARGUMENT_COUNT_ERROR_WITHOUT_TYPE_HINT;
72+
73+
if (PHP_MAJOR_VERSION.PHP_MINOR_VERSION === '70') {
74+
if (array_key_exists((string) $context, $candidates) === false) {
75+
$exception = $this->getExistingClassName('\\PHPUnit\\Framework\\Exception');
76+
$error = vsprintf(self::ERROR_CONTEXT_NEEDED, array(
77+
'function' => __FUNCTION__,
78+
'candidates' => implode('", "', array_keys($candidates))
79+
));
80+
throw new $exception($error);
81+
}
82+
$candidate = $context;
83+
}
84+
$matchingExceptionName = $candidates[$candidate];
6185
} else {
6286
$matchingExceptionName = $this->getMatchingExceptionName($exceptionName);
6387

64-
if ($matchingExceptionName === '\\PHPUnit_Framework_Error') {
65-
$alternative = '\\PHPUnit\\Framework\\Error\\Error';
66-
}
6788
}
6889
}
6990

91+
if ($matchingExceptionName === '\\PHPUnit_Framework_Error') {
92+
$alternative = '\\PHPUnit_Framework_Error_Error';
93+
}
94+
7095
return $this->getExistingClassName($matchingExceptionName, $alternative);
7196
}
7297

src/Traits/GetCompatibleExceptionNameTrait.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,14 @@ trait GetCompatibleExceptionNameTrait
6868

6969
/**
7070
* @param string $exceptionName
71+
* @param string $context
7172
*
7273
* @return string
7374
*
7475
* @throws \PHPUnit_Framework_AssertionFailedError|\PHPUnit\Framework\AssertionFailedError
7576
* @throws \PHPUnit_Framework_SkippedTestError|\PHPUnit\Framework\SkippedTestError
7677
*/
77-
final public function getCompatibleExceptionName($exceptionName)
78+
final public function getCompatibleExceptionName($exceptionName, $context = '')
7879
{
7980
return call_user_func_array(
8081
\Potherca\PhpUnit\Shim\Util::createShimForTrait($this, __FUNCTION__, __TRAIT__),

tests/unit/Shim/GetCompatibleExceptionNameTest.php

Lines changed: 114 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,82 @@ class GetCompatibleExceptionNameTest extends AbstractTraitShimTest
1010

1111
/** @noinspection PhpDocMissingThrowsInspection
1212
*
13-
* @dataProvider provideExpectedExceptions
13+
* @dataProvider provideExpectedExceptionsWithoutContext
1414
*
1515
* @param string $exceptionName
1616
* @param string|array $expected
1717
*/
18-
final public function testShimShouldGetCompatibleExceptionNameWhenGivenExceptionName($exceptionName, $expected)
18+
final public function testShimShouldGetCompatibleExceptionNameWhenGivenExceptionNameWithoutContext($exceptionName, $expected)
1919
{
20-
if (is_array($expected) === true) {
21-
/* Grab version specific value */
22-
$key = PHP_MAJOR_VERSION;
23-
if (array_key_exists(PHP_MAJOR_VERSION . PHP_MINOR_VERSION, $expected) === true) {
24-
$key = PHP_MAJOR_VERSION . PHP_MINOR_VERSION;
25-
}
26-
$expected = $expected[$key];
20+
$expected = $this->getExpectedExceptionFromData($expected);
21+
22+
$mockTestCase = $this->getMockTestCase();
23+
24+
$shim = new GetCompatibleExceptionName($mockTestCase);
25+
26+
/** @noinspection PhpUnhandledExceptionInspection */
27+
$actual = $shim->getCompatibleExceptionName($exceptionName);
28+
29+
$this->assertSame($expected, $actual);
30+
}
31+
32+
/** @noinspection PhpDocMissingThrowsInspection
33+
*
34+
* @dataProvider provideExpectedExceptionsWithContext
35+
*
36+
* @param string $exceptionName
37+
* @param string|array $expected
38+
*/
39+
final public function testShimShouldComplainWhenGivenExceptionNameWithContext($exceptionName, $expected)
40+
{
41+
$mockTestCase = $this->getMockTestCase();
42+
43+
$shim = new GetCompatibleExceptionName($mockTestCase);
44+
45+
$currentVersion = PHP_MAJOR_VERSION . PHP_MINOR_VERSION;
46+
47+
if (array_key_exists($currentVersion, $expected) === false) {
48+
$this->markTestSkipped('Context not needed for PHP'.$currentVersion);
2749
}
2850

29-
if (class_exists($expected) === false) {
30-
$expected = str_replace('_', '\\', $expected);
51+
$exception = '\\PHPUnit_Framework_Exception';
52+
53+
if (class_exists($exception) === false) {
54+
$exception = str_replace('_', '\\', $exception);
3155
}
3256

57+
$regex = '/' . vsprintf(GetCompatibleExceptionName::ERROR_CONTEXT_NEEDED, array('.*', '.*')) . '/';
58+
if (method_exists($this, 'expectExceptionMessageRegExp')) {
59+
/* PHPUnit ^5.2 | ^6.0 */
60+
$this->expectExceptionMessageRegExp($regex);
61+
$this->expectException($exception);
62+
} else {
63+
/* PHPUnit ^4.3 | =< 5.6 */
64+
$this->setExpectedExceptionRegExp($exception, $regex);
65+
}
66+
67+
/** @noinspection PhpUnhandledExceptionInspection */
68+
$actual = $shim->getCompatibleExceptionName($exceptionName);
69+
}
70+
71+
/** @noinspection PhpDocMissingThrowsInspection
72+
*
73+
* @dataProvider provideExpectedExceptionsWithContext
74+
*
75+
* @param string $exceptionName
76+
* @param string|array $expected
77+
* @param string $context
78+
*/
79+
final public function testShimShouldGetCompatibleExceptionNameWhenGivenExceptionNameWithContext($exceptionName, $expected, $context)
80+
{
81+
$expected = $this->getExpectedExceptionFromData($expected);
82+
3383
$mockTestCase = $this->getMockTestCase();
3484

3585
$shim = new GetCompatibleExceptionName($mockTestCase);
3686

3787
/** @noinspection PhpUnhandledExceptionInspection */
38-
$actual = $shim->getCompatibleExceptionName($exceptionName);
88+
$actual = $shim->getCompatibleExceptionName($exceptionName, $context);
3989

4090
$this->assertSame($expected, $actual);
4191
}
@@ -44,22 +94,68 @@ final public function testShimShouldGetCompatibleExceptionNameWhenGivenException
4494

4595
/////////////////////////////// DATAPROVIDERS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
4696

47-
public function provideExpectedExceptions()
97+
public function provideExpectedExceptionsWithContext()
98+
{
99+
return array(
100+
'ArgumentCountError:with type-hint' => array(
101+
'\\ArgumentCountError',
102+
array(
103+
'5' => '\\PHPUnit_Framework_Error',
104+
'7' => '\\ArgumentCountError',
105+
'70' => '\\TypeError',
106+
),
107+
GetCompatibleExceptionName::ARGUMENT_COUNT_ERROR_WITH_TYPE_HINT,
108+
),
109+
'ArgumentCountError:without type-hint' => array(
110+
'\\ArgumentCountError',
111+
array(
112+
'5' => '\\PHPUnit_Framework_Error',
113+
'7' => '\\ArgumentCountError',
114+
'70' => '\\PHPUnit_Framework_Error',
115+
),
116+
GetCompatibleExceptionName::ARGUMENT_COUNT_ERROR_WITHOUT_TYPE_HINT,
117+
),
118+
);
119+
}
120+
121+
public function provideExpectedExceptionsWithoutContext()
48122
{
49123
return array(
50-
'ArgumentCountError' => array('\\ArgumentCountError', array(
51-
'5' => '\\PHPUnit_Framework_Error',
52-
'7' => '\\ArgumentCountError',
53-
'70' => '\\TypeError',
54-
)),
55124
'ArithmeticError' => array('\\ArithmeticError', array(
56125
'5' => '\\PHPUnit_Framework_Error',
57126
'7' => '\\ArithmeticError',
58127
)),
59128
'DivisionByZeroError' => array('\\DivisionByZeroError', '\\PHPUnit_Framework_Error_Warning'),
60129
'Exception' => array('\\Exception', '\\Exception'),
130+
'ParseError' => array('\\ParseError', '\\ParseError'),
61131
);
62132
}
63133

134+
/**
135+
* @param $expected
136+
*
137+
* @return mixed
138+
*/
139+
private function getExpectedExceptionFromData($expected)
140+
{
141+
if (is_array($expected) === true) {
142+
/* Grab version specific value */
143+
$key = PHP_MAJOR_VERSION;
144+
if (array_key_exists(PHP_MAJOR_VERSION . PHP_MINOR_VERSION, $expected) === true) {
145+
$key = PHP_MAJOR_VERSION . PHP_MINOR_VERSION;
146+
}
147+
$expected = $expected[$key];
148+
}
149+
150+
if (class_exists($expected) === false) {
151+
$expected = str_replace('_', '\\', $expected);
152+
153+
if ($expected === '\\PHPUnit\\Framework\\Error') {
154+
$expected = '\\PHPUnit\\Framework\\Error\\Error';
155+
}
156+
}
157+
158+
return $expected;
159+
}
64160
}
65161
/*EOF*/

0 commit comments

Comments
 (0)