Skip to content

Commit f2ca728

Browse files
MartinMystikJonasdg
authored andcommitted
Capture stderr of test via temp file and output it in test results [Closes #420] (#438)
1 parent 0480187 commit f2ca728

25 files changed

+195
-53
lines changed

src/Runner/CliTester.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public function run(): ?int
5757

5858
if ($this->options['--info']) {
5959
$job = new Job(new Test(__DIR__ . '/info.php'), $this->interpreter);
60+
$job->setTempDirectory($this->options['--temp']);
6061
$job->run();
6162
echo $job->getTest()->stdout;
6263
return null;

src/Runner/Job.php

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ class Job
2727
/** waiting time between process activity check in microseconds */
2828
public const RUN_USLEEP = 10000;
2929

30-
public const
31-
RUN_ASYNC = 1,
32-
RUN_COLLECT_ERRORS = 2;
30+
public const RUN_ASYNC = 1;
3331

3432
/** @var Test */
3533
private $test;
@@ -46,8 +44,8 @@ class Job
4644
/** @var resource|null */
4745
private $stdout;
4846

49-
/** @var resource|null */
50-
private $stderr;
47+
/** @var string|null */
48+
private $stderrFile;
5149

5250
/** @var int */
5351
private $exitCode = self::CODE_NONE;
@@ -74,6 +72,14 @@ public function __construct(Test $test, PhpInterpreter $interpreter, ?array $env
7472
}
7573

7674

75+
public function setTempDirectory(?string $path): void
76+
{
77+
$this->stderrFile = $path === null
78+
? null
79+
: $path . DIRECTORY_SEPARATOR . 'Job.pid-' . getmypid() . '.' . uniqid() . '.stderr';
80+
}
81+
82+
7783
public function setEnvironmentVariable(string $name, string $value): void
7884
{
7985
$this->envVars[$name] = $value;
@@ -88,7 +94,7 @@ public function getEnvironmentVariable(string $name): string
8894

8995
/**
9096
* Runs single test.
91-
* @param int $flags self::RUN_ASYNC | self::RUN_COLLECT_ERRORS
97+
* @param int $flags self::RUN_ASYNC
9298
*/
9399
public function run(int $flags = 0): void
94100
{
@@ -110,7 +116,7 @@ public function run(int $flags = 0): void
110116
[
111117
['pipe', 'r'],
112118
['pipe', 'w'],
113-
['pipe', 'w'],
119+
$this->stderrFile ? ['file', $this->stderrFile, 'w'] : ['pipe', 'w'],
114120
],
115121
$pipes,
116122
dirname($this->test->getFile()),
@@ -122,19 +128,15 @@ public function run(int $flags = 0): void
122128
putenv($name);
123129
}
124130

125-
[$stdin, $this->stdout, $stderr] = $pipes;
131+
[$stdin, $this->stdout] = $pipes;
126132
fclose($stdin);
127-
if ($flags & self::RUN_COLLECT_ERRORS) {
128-
$this->stderr = $stderr;
129-
} else {
130-
fclose($stderr);
133+
134+
if (isset($pipes[2])) {
135+
fclose($pipes[2]);
131136
}
132137

133138
if ($flags & self::RUN_ASYNC) {
134139
stream_set_blocking($this->stdout, false); // on Windows does not work with proc_open()
135-
if ($this->stderr) {
136-
stream_set_blocking($this->stderr, false);
137-
}
138140
} else {
139141
while ($this->isRunning()) {
140142
usleep(self::RUN_USLEEP); // stream_select() doesn't work with proc_open()
@@ -153,9 +155,6 @@ public function isRunning(): bool
153155
}
154156

155157
$this->test->stdout .= stream_get_contents($this->stdout);
156-
if ($this->stderr) {
157-
$this->test->stderr .= stream_get_contents($this->stderr);
158-
}
159158

160159
$status = proc_get_status($this->proc);
161160
if ($status['running']) {
@@ -165,8 +164,9 @@ public function isRunning(): bool
165164
$this->duration += microtime(true);
166165

167166
fclose($this->stdout);
168-
if ($this->stderr) {
169-
fclose($this->stderr);
167+
if ($this->stderrFile) {
168+
$this->test->stderr .= file_get_contents($this->stderrFile);
169+
unlink($this->stderrFile);
170170
}
171171

172172
$code = proc_close($this->proc);

src/Runner/Output/ConsolePrinter.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ public function finish(Test $test): void
112112

113113
$title = ($test->title ? "$test->title | " : '') . substr($test->getSignature(), strlen($this->baseDir));
114114
$message = ' ' . str_replace("\n", "\n ", trim((string) $test->message)) . "\n\n";
115+
$message = preg_replace('/^ $/m', '', $message);
115116
if ($test->getResult() === Test::FAILED) {
116117
$this->buffer .= Dumper::color('red', "-- FAILED: $title") . "\n$message";
117118
} elseif ($test->getResult() === Test::SKIPPED && $this->displaySkipped) {

src/Runner/Test.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ public function getDuration(): ?float
9999
}
100100

101101

102+
/**
103+
* Full output (stdout + stderr)
104+
*/
105+
public function getOutput(): string
106+
{
107+
return $this->stdout . ($this->stderr ? "\nSTDERR:\n" . $this->stderr : '');
108+
}
109+
110+
102111
/**
103112
* @return static
104113
*/

src/Runner/TestHandler.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ public function initiate(string $file): void
7878

7979
foreach ($tests as $test) {
8080
$this->runner->prepareTest($test);
81-
$this->runner->addJob(new Job($test, $php, $this->runner->getEnvironmentVariables()));
81+
$job = new Job($test, $php, $this->runner->getEnvironmentVariables());
82+
$job->setTempDirectory($this->tempDir);
83+
$this->runner->addJob($job);
8284
}
8385
}
8486

@@ -196,10 +198,11 @@ private function initiateTestCase(Test $test, $foo, PhpInterpreter $interpreter)
196198

197199
if ($methods === null) {
198200
$job = new Job($test->withArguments(['method' => TestCase::ListMethods]), $interpreter, $this->runner->getEnvironmentVariables());
201+
$job->setTempDirectory($this->tempDir);
199202
$job->run();
200203

201204
if (in_array($job->getExitCode(), [Job::CODE_ERROR, Job::CODE_FAIL, Job::CODE_SKIP], true)) {
202-
return $test->withResult($job->getExitCode() === Job::CODE_SKIP ? Test::SKIPPED : Test::FAILED, $job->getTest()->stdout);
205+
return $test->withResult($job->getExitCode() === Job::CODE_SKIP ? Test::SKIPPED : Test::FAILED, $job->getTest()->getOutput());
203206
}
204207

205208
$stdout = $job->getTest()->stdout;
@@ -245,7 +248,7 @@ private function assessExitCode(Job $job, $code): ?Test
245248
$message = $job->getExitCode() !== Job::CODE_FAIL
246249
? "Exited with error code {$job->getExitCode()} (expected $code)"
247250
: '';
248-
return $job->getTest()->withResult(Test::FAILED, trim($message . "\n" . $job->getTest()->stdout));
251+
return $job->getTest()->withResult(Test::FAILED, trim($message . "\n" . $job->getTest()->getOutput()));
249252
}
250253

251254
return null;

tests/Runner/Job.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ require __DIR__ . '/../bootstrap.php';
1313
test(function () {
1414
$test = (new Test('Job.test.phptx'))->withArguments(['one', 'two' => 1])->withArguments(['three', 'two' => 2]);
1515
$job = new Job($test, createInterpreter());
16-
$job->run($job::RUN_COLLECT_ERRORS);
16+
$job->setTempDirectory(Tester\Helpers::prepareTempDir(sys_get_temp_dir()));
17+
$job->run();
1718

1819
Assert::false($job->isRunning());
1920
Assert::same($test, $job->getTest());

tests/Runner/Runner.multiple-fails.phpt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ $interpreter = createInterpreter()
4747
$runner = new Runner($interpreter);
4848
$runner->paths[] = __DIR__ . '/multiple-fails/*.phptx';
4949
$runner->outputHandlers[] = $logger = new Logger;
50+
$runner->setTempDirectory(Tester\Helpers::prepareTempDir(sys_get_temp_dir()));
5051
$runner->run();
5152

5253
Assert::match(
@@ -86,8 +87,8 @@ Assert::same(Test::FAILED, $logger->results['testcase-pre-fail.phptx'][0]);
8687

8788
Assert::match(
8889
defined('PHPDBG_VERSION')
89-
? '%A%Parse error: %a% in %a%testcase-syntax-error.phptx on line %d%'
90-
: 'Parse error: %a% in %a%testcase-syntax-error.phptx on line %d%',
90+
? '%A%Parse error: %a% in %a%testcase-syntax-error.phptx on line %d%%A?%'
91+
: 'Parse error: %a% in %a%testcase-syntax-error.phptx on line %d%%A?%',
9192
trim($logger->results['testcase-syntax-error.phptx'][1])
9293
);
9394
Assert::same(Test::FAILED, $logger->results['testcase-syntax-error.phptx'][0]);

tests/Runner/Test.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ test(function () {
2020
Assert::null($test->message);
2121
Assert::same('', $test->stdout);
2222
Assert::same('', $test->stderr);
23+
Assert::same('', $test->getOutput());
2324
Assert::same('some/Test.phpt', $test->getFile());
2425
Assert::same([], $test->getArguments());
2526
Assert::same('some/Test.phpt', $test->getSignature());

tests/RunnerOutput/OutputHandlers.expect.console.txt

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,24 @@ F.sF.sFsF.s
77
line
88
stdout.Failed:
99

10-
in %a%01-basic.fail.phptx(7) Tester\Assert::fail('');
10+
in %a%01-basic.fail.phptx(%d%) Tester\Assert::fail('');
11+
12+
STDERR:
13+
Multi
14+
line
15+
stderr.
1116

1217
-- FAILED: Title for output handlers | 02-title.fail.phptx
1318
Multi
1419
line
1520
stdout.Failed:
1621

17-
in %a%02-title.fail.phptx(11) Tester\Assert::fail('');
22+
in %a%02-title.fail.phptx(%d%) Tester\Assert::fail('');
23+
24+
STDERR:
25+
Multi
26+
line
27+
stderr.
1828

1929
-- FAILED: 03-message.fail.phptx
2030
Multi
@@ -23,14 +33,24 @@ F.sF.sFsF.s
2333
line
2434
message.
2535

26-
in %a%03-message.fail.phptx(7) Tester\Assert::fail("Multi\nline\nmessage.");
36+
in %a%03-message.fail.phptx(%d%) Tester\Assert::fail("Multi\nline\nmessage.");
37+
38+
STDERR:
39+
Multi
40+
line
41+
stderr.
2742

2843
-- FAILED: 04-args.fail.phptx dataprovider=thisIsAVeryVeryVeryLongArgumentNameToTestHowOutputHandlersDealWithItsLengthInTheirOutputFormatting|%a%provider.ini
2944
Multi
3045
line
3146
stdout.Failed:
3247

33-
in %a%04-args.fail.phptx(11) Tester\Assert::fail('');
48+
in %a%04-args.fail.phptx(%d%) Tester\Assert::fail('');
49+
50+
STDERR:
51+
Multi
52+
line
53+
stderr.
3454

3555

3656
FAILURES! (11 tests, 4 failures, 4 skipped, %a% seconds)

tests/RunnerOutput/OutputHandlers.expect.consoleWithSkip.txt

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,63 @@ F.sF.sFsF.s
77
line
88
stdout.Failed:
99

10-
in %a%01-basic.fail.phptx(7) Tester\Assert::fail('');
10+
in %a%01-basic.fail.phptx(%d%) Tester\Assert::fail('');
1111

12-
-- Skipped: %a?%01-basic.skip.phptx
12+
STDERR:
13+
Multi
14+
line
15+
stderr.
16+
17+
-- Skipped: 01-basic.skip.phptx
1318

1419

15-
-- FAILED: Title for output handlers | 02-title.fail.phptx
20+
-- FAILED: Title for output handlers | %a?%02-title.fail.phptx
1621
Multi
1722
line
1823
stdout.Failed:
1924

20-
in %a%02-title.fail.phptx(11) Tester\Assert::fail('');
25+
in %a%02-title.fail.phptx(%d%) Tester\Assert::fail('');
26+
27+
STDERR:
28+
Multi
29+
line
30+
stderr.
2131

22-
-- Skipped: Title for output handlers | 02-title.skip.phptx
32+
-- Skipped: Title for output handlers | %a?%02-title.skip.phptx
2333

2434

25-
-- FAILED: 03-message.fail.phptx
35+
-- FAILED: %a?%03-message.fail.phptx
2636
Multi
2737
line
2838
stdout.Failed: Multi
2939
line
3040
message.
3141

32-
in %a%03-message.fail.phptx(7) Tester\Assert::fail("Multi\nline\nmessage.");
42+
in %a%03-message.fail.phptx(%d%) Tester\Assert::fail("Multi\nline\nmessage.");
3343

34-
-- Skipped: 03-message.skip.phptx
44+
STDERR:
45+
Multi
46+
line
47+
stderr.
48+
49+
-- Skipped: %a?%03-message.skip.phptx
3550
Multi
3651
line
3752
message.
3853

39-
-- FAILED: 04-args.fail.phptx dataprovider=thisIsAVeryVeryVeryLongArgumentNameToTestHowOutputHandlersDealWithItsLengthInTheirOutputFormatting|%a%provider.ini
54+
-- FAILED: %a?%04-args.fail.phptx dataprovider=thisIsAVeryVeryVeryLongArgumentNameToTestHowOutputHandlersDealWithItsLengthInTheirOutputFormatting|%a%provider.ini
4055
Multi
4156
line
4257
stdout.Failed:
4358

44-
in %a%04-args.fail.phptx(11) Tester\Assert::fail('');
59+
in %a%04-args.fail.phptx(%d%) Tester\Assert::fail('');
60+
61+
STDERR:
62+
Multi
63+
line
64+
stderr.
4565

46-
-- Skipped: 04-args.skip.phptx dataprovider=thisIsAVeryVeryVeryLongArgumentNameToTestHowOutputHandlersDealWithItsLengthInTheirOutputFormatting|%a%provider.ini
66+
-- Skipped: %a?%04-args.skip.phptx dataprovider=thisIsAVeryVeryVeryLongArgumentNameToTestHowOutputHandlersDealWithItsLengthInTheirOutputFormatting|%a%provider.ini
4767
Multi
4868
line
4969
message.

tests/RunnerOutput/OutputHandlers.expect.jUnit.xml

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
line
77
stdout.Failed:
88
9-
in %a%01-basic.fail.phptx(7) Tester\Assert::fail('');"/>
9+
in %a%01-basic.fail.phptx(%d%) Tester\Assert::fail('');
10+
11+
STDERR:
12+
Multi
13+
line
14+
stderr."/>
1015
</testcase>
1116
<testcase classname="%a%01-basic.pass.phptx" name="%a%01-basic.pass.phptx"/>
1217
<testcase classname="%a%01-basic.skip.phptx" name="%a%01-basic.skip.phptx">
@@ -17,7 +22,12 @@ in %a%01-basic.fail.phptx(7) Tester\Assert::fail('');"/>
1722
line
1823
stdout.Failed:
1924
20-
in %a%02-title.fail.phptx(11) Tester\Assert::fail('');"/>
25+
in %a%02-title.fail.phptx(%d%) Tester\Assert::fail('');
26+
27+
STDERR:
28+
Multi
29+
line
30+
stderr."/>
2131
</testcase>
2232
<testcase classname="%a%02-title.pass.phptx" name="%a%02-title.pass.phptx"/>
2333
<testcase classname="%a%02-title.skip.phptx" name="%a%02-title.skip.phptx">
@@ -30,7 +40,12 @@ stdout.Failed: Multi
3040
line
3141
message.
3242
33-
in %a%03-message.fail.phptx(7) Tester\Assert::fail(&quot;Multi\nline\nmessage.&quot;);"/>
43+
in %a%03-message.fail.phptx(%d%) Tester\Assert::fail(&quot;Multi\nline\nmessage.&quot;);
44+
45+
STDERR:
46+
Multi
47+
line
48+
stderr."/>
3449
</testcase>
3550
<testcase classname="%a%03-message.skip.phptx" name="%a%03-message.skip.phptx">
3651
<skipped/>
@@ -40,7 +55,12 @@ in %a%03-message.fail.phptx(7) Tester\Assert::fail(&quot;Multi\nline\nmessage.&q
4055
line
4156
stdout.Failed:
4257
43-
in %a%04-args.fail.phptx(11) Tester\Assert::fail('');"/>
58+
in %a%04-args.fail.phptx(%d%) Tester\Assert::fail('');
59+
60+
STDERR:
61+
Multi
62+
line
63+
stderr."/>
4464
</testcase>
4565
<testcase classname="%a%04-args.pass.phptx dataprovider=thisIsAVeryVeryVeryLongArgumentNameToTestHowOutputHandlersDealWithItsLengthInTheirOutputFormatting|%a%provider.ini" name="%a%04-args.pass.phptx dataprovider=thisIsAVeryVeryVeryLongArgumentNameToTestHowOutputHandlersDealWithItsLengthInTheirOutputFormatting|%a%provider.ini"/>
4666
<testcase classname="%a%04-args.skip.phptx dataprovider=thisIsAVeryVeryVeryLongArgumentNameToTestHowOutputHandlersDealWithItsLengthInTheirOutputFormatting|%a%provider.ini" name="%a%04-args.skip.phptx dataprovider=thisIsAVeryVeryVeryLongArgumentNameToTestHowOutputHandlersDealWithItsLengthInTheirOutputFormatting|%a%provider.ini">

0 commit comments

Comments
 (0)