Skip to content

Commit 7da49b7

Browse files
committed
Make check for removing 'phpunit' from generated backtrace more strict, to prevent false positives.
1 parent 9d2e56d commit 7da49b7

16 files changed

+133
-50
lines changed

CHANGELOG.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# SoftMocks ChangeLog
22

3+
## 4.0.2
4+
5+
Changes:
6+
7+
- Make check for removing 'phpunit' from generated backtrace more strict, to prevent false positives.
8+
39
## 4.0.1
410

511
Changes:
@@ -10,12 +16,12 @@ Changes:
1016
## 4.0.0
1117

1218
Changes:
19+
1320
- Minimum PHP version is now `7.4`.
1421
- Updated `nikic/php-parser` from version `^4.19.1` to `^5.3.1`.
1522
- Soft Mocks now automatically uses PHP Parser for currently installed PHP version. This should work well for most
16-
use cases, but can still be overwritten by passing a parameter to `\Badoo\SoftMocks::init()`
17-
- Fixed an error where heredoc or nowdoc comments would cause the rewritten file to have code at incorrect lines.
18-
23+
use cases, but can still be overwritten by passing a parameter to `\Badoo\SoftMocks::init()`
24+
- Fixed an error where heredoc or nowdoc comments would cause the rewritten file to have code at incorrect lines.
1925

2026
## 3.7
2127

src/Badoo/SoftMocks.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,7 @@ function ($str) {
976976
$parts = explode('(', trim($str), 2);
977977
if (count($parts) > 1) {
978978
$filename = $parts[0];
979-
if (stripos($filename, 'PHPUnit') !== false) {
979+
if (stripos(dirname($filename), 'phpunit/phpunit') !== false) {
980980
return false;
981981
}
982982
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Badoo\SoftMock\Tests;
5+
6+
class ClassContainsPhpunitName
7+
{
8+
public static function getBacktrace(): string
9+
{
10+
ob_start();
11+
\Badoo\SoftMocks::printBackTrace();
12+
return ob_get_clean();
13+
}
14+
}

tests/unit/Badoo/SoftMocksTest.php

Lines changed: 72 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
* @author Kirill Abrosimov <[email protected]>
55
* @author Oleg Efimov <[email protected]>
66
* @author Rinat Akhmadeev <[email protected]>
7+
*
8+
* @noinspection PhpExpressionResultUnusedInspection
9+
* @noinspection PhpUndefinedMethodInspection
10+
* @noinspection PhpMissingReturnTypeInspection
11+
* @noinspection PhpUnitMisorderedAssertEqualsArgumentsInspection
712
*/
813
namespace Badoo\SoftMock\Tests;
914

@@ -46,7 +51,7 @@ public function testExitMock()
4651
\Badoo\SoftMocks::redefineExit(
4752
'',
4853
function ($code) {
49-
throw new \Exception("exit called: {$code}");
54+
throw new \Exception("exit called: $code");
5055
}
5156
);
5257
try {
@@ -653,7 +658,7 @@ public function testFromBaseToForthClassConstantRedefined()
653658
static::assertSame(50, ConstantRedeclareForthTestClass::getForthStaticValue());
654659
}
655660

656-
public function testRedifineBaseClassConstantAndRemoveRestoreOtherClassConstants()
661+
public function testRedefineBaseClassConstantAndRemoveRestoreOtherClassConstants()
657662
{
658663
\Badoo\SoftMocks::redefineConstant(
659664
ConstantRedeclareBaseTestClass::class . '::VALUE',
@@ -1435,6 +1440,7 @@ public function dataProviderResolveFile()
14351440
* @dataProvider dataProviderResolveFile
14361441
* @param $file
14371442
* @param $expected_result
1443+
* @throws \ReflectionException
14381444
*/
14391445
public function testResolveFile($file, $expected_result)
14401446
{
@@ -1449,6 +1455,9 @@ public function testResolveFile($file, $expected_result)
14491455
static::assertSame($expected_result, $result);
14501456
}
14511457

1458+
/**
1459+
* @throws \ReflectionException
1460+
*/
14521461
public function testResolveFileFileShouldNotBeEmptyException()
14531462
{
14541463
if (\method_exists($this, 'expectException')) {
@@ -1461,10 +1470,13 @@ public function testResolveFileFileShouldNotBeEmptyException()
14611470
$this->getPrepareFilePathToRewriteMethod()->invoke(null, '');
14621471
}
14631472

1473+
/**
1474+
* @throws \ReflectionException
1475+
*/
14641476
public function testResolveAbsoluteFilePathNotResolvedException()
14651477
{
14661478
$file = __DIR__ . '/fixtures/original/__unknown.php';
1467-
$exception_message = "Can't resolve file '{$file}'";
1479+
$exception_message = "Can't resolve file '$file'";
14681480
if (\method_exists($this, 'expectExceptionMessage')) {
14691481
$this->expectException(\RuntimeException::class);
14701482
$this->expectExceptionMessage($exception_message);
@@ -1475,10 +1487,13 @@ public function testResolveAbsoluteFilePathNotResolvedException()
14751487
$this->getPrepareFilePathToRewriteMethod()->invoke(null, $file);
14761488
}
14771489

1490+
/**
1491+
* @throws \ReflectionException
1492+
*/
14781493
public function testResolveRelativeFilePathNotResolvedException()
14791494
{
14801495
$file = 'unit/Badoo/fixtures/original/php7.php';
1481-
$exception_message = "Can't resolve file '{$file}'";
1496+
$exception_message = "Can't resolve file '$file'";
14821497
if (\method_exists($this, 'expectExceptionMessage')) {
14831498
$this->expectException(\RuntimeException::class);
14841499
$this->expectExceptionMessage($exception_message);
@@ -1608,10 +1623,8 @@ public function testWithPrivateConstantPHP71()
16081623

16091624
/**
16101625
* @dataProvider providerWithOrWithoutMock
1611-
*
1612-
* @param bool $set_mock
16131626
*/
1614-
public function testWithWrongPrivateConstantAccessPHP71($set_mock)
1627+
public function testWithWrongPrivateConstantAccessPHP71(bool $set_mock)
16151628
{
16161629
static::markTestSkippedForPHPVersionBelow('7.1.0');
16171630

@@ -1629,10 +1642,8 @@ public function testWithWrongPrivateConstantAccessPHP71($set_mock)
16291642

16301643
/**
16311644
* @dataProvider providerWithOrWithoutMock
1632-
*
1633-
* @param bool $set_mock
16341645
*/
1635-
public function testWithWrongPrivateConstantAccessFromFunctionPHP71($set_mock)
1646+
public function testWithWrongPrivateConstantAccessFromFunctionPHP71(bool $set_mock)
16361647
{
16371648
static::markTestSkippedForPHPVersionBelow('7.1.0');
16381649

@@ -1650,10 +1661,8 @@ public function testWithWrongPrivateConstantAccessFromFunctionPHP71($set_mock)
16501661

16511662
/**
16521663
* @dataProvider providerWithOrWithoutMock
1653-
*
1654-
* @param bool $set_mock
16551664
*/
1656-
public function testWithWrongParentPrivateConstantAccessPHP71($set_mock)
1665+
public function testWithWrongParentPrivateConstantAccessPHP71(bool $set_mock)
16571666
{
16581667
static::markTestSkippedForPHPVersionBelow('7.1.0');
16591668

@@ -1738,10 +1747,8 @@ public function testWithThisProtectedConstantFromChildPHP71()
17381747

17391748
/**
17401749
* @dataProvider providerWithOrWithoutMock
1741-
*
1742-
* @param bool $set_mock
17431750
*/
1744-
public function testWithWrongProtectedConstantAccessPHP71($set_mock)
1751+
public function testWithWrongProtectedConstantAccessPHP71(bool $set_mock)
17451752
{
17461753
static::markTestSkippedForPHPVersionBelow('7.1.0');
17471754

@@ -1759,10 +1766,8 @@ public function testWithWrongProtectedConstantAccessPHP71($set_mock)
17591766

17601767
/**
17611768
* @dataProvider providerWithOrWithoutMock
1762-
*
1763-
* @param bool $set_mock
17641769
*/
1765-
public function testWithWrongProtectedConstantAccessFromFunctionPHP71($set_mock)
1770+
public function testWithWrongProtectedConstantAccessFromFunctionPHP71(bool $set_mock)
17661771
{
17671772
static::markTestSkippedForPHPVersionBelow('7.1.0');
17681773

@@ -2197,7 +2202,7 @@ public function testFromBaseToForthClassConstantRedefinedPHP71()
21972202
static::assertSame(50, ConstantRedeclareForthPHP71TestClass::getForthStaticValue());
21982203
}
21992204

2200-
public function testRedifineBaseClassConstantAndRemoveRestoreOtherClassConstantsPHP71()
2205+
public function testRedefineBaseClassConstantAndRemoveRestoreOtherClassConstantsPHP71()
22012206
{
22022207
static::markTestSkippedForPHPVersionBelow('7.1.0');
22032208

@@ -2652,10 +2657,8 @@ public function testRemoveConstantFromHierarchyPHP71()
26522657

26532658
/**
26542659
* @dataProvider providerWithOrWithoutMock
2655-
*
2656-
* @param bool $set_mock
26572660
*/
2658-
public function testArrayDestructingPHP71($set_mock)
2661+
public function testArrayDestructingPHP71(bool $set_mock)
26592662
{
26602663
static::markTestSkippedForPHPVersionBelow('7.1.0');
26612664

@@ -2720,6 +2723,10 @@ public function testRewrite($filename)
27202723
static::markTestSkippedForPHPVersionBelow('8.2.0');
27212724
}
27222725

2726+
if ($filename === 'array_with_functions_inside_function.php') {
2727+
static::markTestSkipped('This fails to generate correct lines due to design flaw in Soft Mocks.');
2728+
}
2729+
27232730
$result = \Badoo\SoftMocks::rewrite(__DIR__ . '/fixtures/original/' . $filename);
27242731
$this->assertNotFalse($result, "Rewrite failed");
27252732

@@ -2774,6 +2781,9 @@ public function testGetOriginalFilePath()
27742781
);
27752782
}
27762783

2784+
/**
2785+
* @throws \ReflectionException
2786+
*/
27772787
public function testGetDeclaringTrait()
27782788
{
27792789
$soft_mocks = new \ReflectionClass(\Badoo\SoftMocks::class);
@@ -2805,11 +2815,8 @@ public function providerClassWithIsCallable()
28052815

28062816
/**
28072817
* @dataProvider providerClassWithIsCallable
2808-
*
2809-
* @param callable $callable
2810-
* @param bool $expected_result
28112818
*/
2812-
public function testClassWithIsCallable($callable, $expected_result)
2819+
public function testClassWithIsCallable($callable, bool $expected_result)
28132820
{
28142821
$this->assertSame($expected_result, ClassWithIsCallable::check($callable));
28152822
}
@@ -2848,7 +2855,7 @@ public function testParseErrors($filename)
28482855
{
28492856
$filename = __DIR__ . '/fixtures/invalid_fatal/' . $filename;
28502857

2851-
$exception_message = "File: {$filename}, message: File: {$filename}";
2858+
$exception_message = "File: $filename, message: File: $filename";
28522859
if (\method_exists($this, 'expectExceptionMessage')) {
28532860
$this->expectException(\Badoo\SoftMocksParseError::class);
28542861
$this->expectExceptionMessage($exception_message);
@@ -2860,6 +2867,9 @@ public function testParseErrors($filename)
28602867
\Badoo\SoftMocks::rewrite($filename);
28612868
}
28622869

2870+
/**
2871+
* @throws \Throwable
2872+
*/
28632873
public function testInvalidRedefine()
28642874
{
28652875
self::markTestSkippedForPHPVersionAbove('8.0');
@@ -2871,7 +2881,7 @@ public function testInvalidRedefine()
28712881
} else {
28722882
$warning_class = \PHPUnit_Framework_Error_Warning::class;
28732883
}
2874-
2884+
28752885
if (\method_exists($this, 'expectExceptionMessage')) {
28762886
$this->expectException($warning_class);
28772887
$this->expectExceptionMessage($exception_message);
@@ -2888,6 +2898,9 @@ public function testInvalidRedefine()
28882898
);
28892899
}
28902900

2901+
/**
2902+
* @throws \Throwable
2903+
*/
28912904
public function testExceptionInBody()
28922905
{
28932906
$exception_message = "real exception";
@@ -2935,7 +2948,7 @@ public function testPauseResumeConstants()
29352948
\Badoo\SoftMocks::redefineConstant(ClassToTestPauseResume::class . '::A_inside_pause', $new_result_A);
29362949
\Badoo\SoftMocks::redefineConstant(ClassToTestPauseResume::class . '::B_inside_pause', $new_result_B);
29372950
\Badoo\SoftMocks::removeConstant(ClassToTestPauseResume::class . '::C_inside_pause');
2938-
2951+
29392952
$this->assertEquals($old_result_A, ClassToTestPauseResume::A_inside_pause);
29402953
$this->assertTrue(!defined(ClassToTestPauseResume::class . '::B_inside_pause'));
29412954
$this->assertTrue(defined(ClassToTestPauseResume::class . '::C_inside_pause'));
@@ -2946,13 +2959,15 @@ public function testPauseResumeConstants()
29462959
$this->assertEquals($new_result_A, ClassToTestPauseResume::A);
29472960
$this->assertTrue(defined(ClassToTestPauseResume::class . '::B'));
29482961
if (defined('ClassToTestPauseResume::B')) {
2962+
/** @noinspection PhpUndefinedClassConstantInspection */
29492963
$this->assertEquals($new_result_B, ClassToTestPauseResume::B);
29502964
}
29512965
$this->assertTrue(!defined(ClassToTestPauseResume::class . '::C'));
2952-
2966+
29532967
$this->assertEquals($new_result_A, ClassToTestPauseResume::A_inside_pause);
29542968
$this->assertTrue(defined(ClassToTestPauseResume::class . '::B_inside_pause'));
29552969
if (defined('ClassToTestPauseResume::B_inside_pause')) {
2970+
/** @noinspection PhpUndefinedClassConstantInspection */
29562971
$this->assertEquals($new_result_B, ClassToTestPauseResume::B_inside_pause);
29572972
}
29582973
$this->assertTrue(!defined(ClassToTestPauseResume::class . '::C_inside_pause'));
@@ -2976,7 +2991,7 @@ public function testPauseResumeFunctions()
29762991
\Badoo\SoftMocks::resume();
29772992

29782993
$this->assertEquals($new_result, functionToTestPauseResume());
2979-
2994+
29802995
$this->assertEquals($new_result, functionToTestPauseResumeInsidePause());
29812996
}
29822997

@@ -2998,7 +3013,7 @@ public function testPauseResumeMethods()
29983013
\Badoo\SoftMocks::resume();
29993014

30003015
$this->assertEquals($new_result, $class->method());
3001-
3016+
30023017
$this->assertEquals($new_result, $class->methodInsidePause());
30033018
}
30043019

@@ -3016,11 +3031,11 @@ public function testPauseResumeGenerators()
30163031
$class = new ClassToTestPauseResume();
30173032
// pause environment
30183033
\Badoo\SoftMocks::pause();
3019-
3034+
30203035
$values = [];
30213036
foreach ($class->generator() as $item) $values[] = $item;
30223037
$this->assertEquals($values, [$old_result]);
3023-
3038+
30243039
// do redefine inside pause
30253040
\Badoo\SoftMocks::redefineGenerator(ClassToTestPauseResume::class, 'generatorInsidePause', [$this, 'generatorForReplace']);
30263041
$values_inside_pause = [];
@@ -3032,7 +3047,7 @@ public function testPauseResumeGenerators()
30323047
$values = [];
30333048
foreach ($class->generator() as $item) $values[] = $item;
30343049
$this->assertEquals($values, [$new_result]);
3035-
3050+
30363051
$values_inside_pause = [];
30373052
foreach ($class->generatorInsidePause() as $item) $values_inside_pause[] = $item;
30383053
$this->assertEquals($values_inside_pause, [$new_result]);
@@ -3151,4 +3166,25 @@ public function testEnumMocksCannotRedefineCaseAsConstant(): void
31513166

31523167
$this->fail("Redefining enum cases using redefineConstant() should be blocked.");
31533168
}
3169+
3170+
public function testDoNotStripBacktraceItemsThatContainPhpunitInNameOutsideOfTheFramework(): void
3171+
{
3172+
require_once __DIR__ . '/ClassContainsPhpunitName.php';
3173+
$backtrace = ClassContainsPhpunitName::getBacktrace();
3174+
$this->assertStringContainsString(
3175+
'SoftMocksTest.php',
3176+
$backtrace,
3177+
'Entry for this test class is not present.'
3178+
);
3179+
$this->assertStringContainsString(
3180+
'ClassContainsPhpunitName.php',
3181+
$backtrace,
3182+
'Entry for the class containing "phpunit" in the name is not present.'
3183+
);
3184+
$this->assertStringNotContainsString(
3185+
'phpunit/phpunit',
3186+
$backtrace,
3187+
'Entries for PHPUnit internal classes are present.'
3188+
);
3189+
}
31543190
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
$dispatcherArgumentsJson = \Badoo\SoftMocks::callRewrittenOrOriginalFunction('', 'json_encode', [[
4+
5+
'first element',
6+
(isset(\Badoo\SoftMocks::$func_mocks_by_name['getcwd']) ? \Badoo\SoftMocks::callFunction('', 'getcwd', []) : \getcwd()) ?: 'no cwd',
7+
'third element',
8+
isset(\Badoo\SoftMocks::$func_mocks_by_name['trim']) ? \Badoo\SoftMocks::callFunction('', 'trim', [isset(\Badoo\SoftMocks::$func_mocks_by_name['str_repeat']) ? \Badoo\SoftMocks::callFunction('', 'str_repeat', ['hey! ', 10]) : \str_repeat('hey! ', 10)]) : \trim(isset(\Badoo\SoftMocks::$func_mocks_by_name['str_repeat']) ? \Badoo\SoftMocks::callFunction('', 'str_repeat', ['hey! ', 10]) : \str_repeat('hey! ', 10)),
9+
'last element',
10+
]]);
11+
12+
13+
14+
isset(\Badoo\SoftMocks::$func_mocks_by_name['file_exists']) ? \Badoo\SoftMocks::callFunction('', 'file_exists', ['x']) : \file_exists('x');

tests/unit/Badoo/fixtures/expected/enums.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,4 @@ class MainTest{
6666

6767
public function __construct(){if (isset(\Badoo\SoftMocks::$mocks_by_name[__FUNCTION__]) && false !== $__softmocksvariableforcode = \Badoo\SoftMocks::isMocked(MainTest::class, static::class, __FUNCTION__)) {$mm_func_args = func_get_args();$params = [];$variadic_params_idx = '';return eval($__softmocksvariableforcode);/** @codeCoverageIgnore */}
6868

69-
$this->blogPost = new \BlogPost(isset(\Badoo\SoftMocks::$class_const_mocks_by_name['DRAFT']) ? \Badoo\SoftMocks::getClassConst(\Status::class, 'DRAFT', self::class) : \Status::DRAFT);}}
69+
$this->blogPost = new \BlogPost(isset(\Badoo\SoftMocks::$class_const_mocks_by_name['DRAFT']) ? \Badoo\SoftMocks::getClassConst(\Status::class, 'DRAFT', self::class) : \Status::DRAFT);}}

0 commit comments

Comments
 (0)