Completed
Push — namespace-template ( 7967f2...367a36 )
by Sam
10:48
created

ErrorControlChainTest_Chain::getDisplayErrors()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
rs 9.4285
c 1
b 1
f 0
1
<?php
2
3
/**
4
 * An extension of ErrorControlChain that runs the chain in a subprocess.
5
 *
6
 * We need this because ErrorControlChain only suppresses uncaught fatal errors, and
7
 * that would kill PHPUnit execution
8
 */
9
class ErrorControlChainTest_Chain extends ErrorControlChain {
10
11
	protected $displayErrors = 'STDERR';
12
13
	/**
14
	 * Modify method visibility to public for testing
15
	 *
16
	 * @return string
17
	 */
18
	public function getDisplayErrors()
19
	{
20
		// Protect manipulation of underlying php_ini values
21
		return $this->displayErrors;
22
	}
23
24
	/**
25
	 * Modify method visibility to public for testing
26
	 *
27
	 * @param mixed $errors
28
	 */
29
	public function setDisplayErrors($errors)
30
	{
31
		// Protect manipulation of underlying php_ini values
32
		$this->displayErrors = $errors;
33
	}
34
35
	// Change function visibility to be testable directly
36
	public function translateMemstring($memstring) {
37
		return parent::translateMemstring($memstring);
38
	}
39
40
	function executeInSubprocess($includeStderr = false) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
41
		// Get the path to the ErrorControlChain class
42
		$classpath = SS_ClassLoader::instance()->getItemPath('ErrorControlChain');
43
		$suppression = $this->suppression ? 'true' : 'false';
44
45
		// Start building a PHP file that will execute the chain
46
		$src = '<'."?php
47
require_once '$classpath';
48
49
\$chain = new ErrorControlChain();
50
51
\$chain->setSuppression($suppression);
52
53
\$chain
54
";
55
56
		// For each step, use reflection to pull out the call, stick in the the PHP source we're building
57
		foreach ($this->steps as $step) {
58
			$func = new ReflectionFunction($step['callback']);
59
			$source = file($func->getFileName());
60
61
			$start_line = $func->getStartLine() - 1;
62
			$end_line = $func->getEndLine();
63
			$length = $end_line - $start_line;
64
65
			$src .= implode("", array_slice($source, $start_line, $length)) . "\n";
66
		}
67
68
		// Finally add a line to execute the chain
69
		$src .= "->execute();";
70
71
		// Now stick it in a temporary file & run it
72
		$codepath = TEMP_FOLDER.'/ErrorControlChainTest_'.sha1($src).'.php';
73
74
		if($includeStderr) {
75
			$null = '&1';
76
		} else {
77
			$null = is_writeable('/dev/null') ? '/dev/null' : 'NUL';
78
		}
79
80
		file_put_contents($codepath, $src);
81
		exec("php $codepath 2>$null", $stdout, $errcode);
82
		unlink($codepath);
83
84
		return array(implode("\n", $stdout), $errcode);
85
	}
86
}
87
88
class ErrorControlChainTest extends SapphireTest {
89
90
	function setUp() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
91
92
		// Check we can run PHP at all
93
		$null = is_writeable('/dev/null') ? '/dev/null' : 'NUL';
94
		exec("php -v 2> $null", $out, $rv);
95
96
		if ($rv != 0) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $rv of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
97
			$this->markTestSkipped("Can't run PHP from the command line - is it in your path?");
98
		}
99
100
		parent::setUp();
101
	}
102
103
	function testErrorSuppression() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
104
105
		// Errors disabled by default
106
		$chain = new ErrorControlChainTest_Chain();
107
		$chain->setDisplayErrors('Off'); // mocks display_errors: Off
108
		$initialValue = null;
109
		$whenNotSuppressed = null;
110
		$whenSuppressed = null;
111
		$chain->then(
112 View Code Duplication
			function(ErrorControlChainTest_Chain $chain)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
113
				use(&$initialValue, &$whenNotSuppressed, &$whenSuppressed) {
114
					$initialValue = $chain->getDisplayErrors();
115
					$chain->setSuppression(false);
116
					$whenNotSuppressed = $chain->getDisplayErrors();
117
					$chain->setSuppression(true);
118
					$whenSuppressed = $chain->getDisplayErrors();
119
				}
120
		)->execute();
121
122
		// Disabled errors never un-disable
123
		$this->assertEquals(0, $initialValue); // Chain starts suppressed
124
		$this->assertEquals(0, $whenSuppressed); // false value used internally when suppressed
125
		$this->assertEquals('Off', $whenNotSuppressed); // false value set by php ini when suppression lifted
126
		$this->assertEquals('Off', $chain->getDisplayErrors()); // Correctly restored after run
127
128
		// Errors enabled by default
129
		$chain = new ErrorControlChainTest_Chain();
130
		$chain->setDisplayErrors('Yes'); // non-falsey ini value
131
		$initialValue = null;
132
		$whenNotSuppressed = null;
133
		$whenSuppressed = null;
134
		$chain->then(
135 View Code Duplication
			function(ErrorControlChainTest_Chain $chain)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
136
				use(&$initialValue, &$whenNotSuppressed, &$whenSuppressed) {
137
					$initialValue = $chain->getDisplayErrors();
138
					$chain->setSuppression(true);
139
					$whenSuppressed = $chain->getDisplayErrors();
140
					$chain->setSuppression(false);
141
					$whenNotSuppressed = $chain->getDisplayErrors();
142
				}
143
		)->execute();
144
145
		// Errors can be suppressed an un-suppressed when initially enabled
146
		$this->assertEquals(0, $initialValue); // Chain starts suppressed
147
		$this->assertEquals(0, $whenSuppressed); // false value used internally when suppressed
148
		$this->assertEquals('Yes', $whenNotSuppressed); // false value set by php ini when suppression lifted
149
		$this->assertEquals('Yes', $chain->getDisplayErrors()); // Correctly restored after run
150
151
		// Fatal error
152
		$chain = new ErrorControlChainTest_Chain();
153
154
		list($out, $code) = $chain
0 ignored issues
show
Unused Code introduced by
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
155
			->then(function(){
156
				Foo::bar(); // Non-existant class causes fatal error
157
			})
158
			->thenIfErrored(function(){
159
				echo "Done";
160
			})
161
			->executeInSubprocess();
162
163
		$this->assertEquals('Done', $out);
164
165
		// User error
166
167
		$chain = new ErrorControlChainTest_Chain();
168
169
		list($out, $code) = $chain
0 ignored issues
show
Unused Code introduced by
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
170
			->then(function(){
171
				user_error('Error', E_USER_ERROR);
172
			})
173
			->thenIfErrored(function(){
174
				echo "Done";
175
			})
176
			->executeInSubprocess();
177
178
		$this->assertEquals('Done', $out);
179
180
		// Recoverable error
181
182
		$chain = new ErrorControlChainTest_Chain();
183
184
		list($out, $code) = $chain
0 ignored issues
show
Unused Code introduced by
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
185
			->then(function(){
186
				$x = function(ErrorControlChain $foo){ };
0 ignored issues
show
Unused Code introduced by
The parameter $foo is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
187
				$x(1); // Calling against type
188
			})
189
			->thenIfErrored(function(){
190
				echo "Done";
191
			})
192
			->executeInSubprocess();
193
194
		$this->assertEquals('Done', $out);
195
196
		// Memory exhaustion
197
198
		$chain = new ErrorControlChainTest_Chain();
199
200
		list($out, $code) = $chain
0 ignored issues
show
Unused Code introduced by
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
201
			->then(function(){
202
				ini_set('memory_limit', '10M');
203
				$a = array();
204
				while(1) $a[] = 1;
205
			})
206
			->thenIfErrored(function(){
207
				echo "Done";
208
			})
209
			->executeInSubprocess();
210
211
		$this->assertEquals('Done', $out);
212
213
		// Exceptions
214
215
		$chain = new ErrorControlChainTest_Chain();
216
217
		list($out, $code) = $chain
0 ignored issues
show
Unused Code introduced by
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
218
			->then(function(){
219
				throw new Exception("bob");
220
			})
221
			->thenIfErrored(function(){
222
				echo "Done";
223
			})
224
			->executeInSubprocess();
225
226
		$this->assertEquals('Done', $out);
227
	}
228
229 View Code Duplication
	function testExceptionSuppression() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
230
		$chain = new ErrorControlChainTest_Chain();
231
232
		list($out, $code) = $chain
0 ignored issues
show
Unused Code introduced by
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
233
			->then(function(){
234
				throw new Exception('This exception should be suppressed');
235
			})
236
			->thenIfErrored(function(){
237
				echo "Done";
238
			})
239
			->executeInSubprocess();
240
241
		$this->assertEquals('Done', $out);
242
	}
243
244
	function testErrorControl() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
245
		$chain = new ErrorControlChainTest_Chain();
246
247
		list($out, $code) = $chain
0 ignored issues
show
Unused Code introduced by
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
248
			->then(function() { echo 'preThen,'; })
249
			->thenIfErrored(function() { echo 'preThenIfErrored,'; })
250
			->thenAlways(function() { echo 'preThenAlways,'; })
251
252
			->then(function(){ user_error('An error', E_USER_ERROR); })
253
254
			->then(function() { echo 'postThen,'; })
255
			->thenIfErrored(function() { echo 'postThenIfErrored,'; })
256
			->thenAlways(function() { echo 'postThenAlways,'; })
257
258
			->executeInSubprocess();
259
260
		$this->assertEquals(
261
			"preThen,preThenAlways,postThenIfErrored,postThenAlways,",
262
			$out
263
		);
264
	}
265
266
	function testSuppressionControl() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
267
		// Turning off suppression before execution
268
269
		$chain = new ErrorControlChainTest_Chain();
270
		$chain->setSuppression(false);
271
272
		list($out, $code) = $chain
0 ignored issues
show
Unused Code introduced by
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
273
			->then(function($chain){
0 ignored issues
show
Unused Code introduced by
The parameter $chain is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
274
				Foo::bar(); // Non-existant class causes fatal error
275
			})
276
			->executeInSubprocess(true);
277
278
		$this->assertContains('Fatal error', $out);
279
		$this->assertContains('Foo', $out);
280
281
		// Turning off suppression during execution
282
283
		$chain = new ErrorControlChainTest_Chain();
284
285
		list($out, $code) = $chain
0 ignored issues
show
Unused Code introduced by
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
286
			->then(function($chain){
287
				$chain->setSuppression(false);
288
				Foo::bar(); // Non-existent class causes fatal error
289
			})
290
			->executeInSubprocess(true);
291
292
		$this->assertContains('Fatal error', $out);
293
		$this->assertContains('Foo', $out);
294
	}
295
296
	function testDoesntAffectNonFatalErrors() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
297
		$chain = new ErrorControlChainTest_Chain();
298
299
		list($out, $code) = $chain
0 ignored issues
show
Unused Code introduced by
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
300
			->then(function(){
301
				$array = null;
302
				if (@$array['key'] !== null) user_error('Error', E_USER_ERROR);
303
			})
304
			->then(function(){
305
				echo "Good";
306
			})
307
			->thenIfErrored(function(){
308
				echo "Bad";
309
			})
310
			->executeInSubprocess();
311
312
		$this->assertContains("Good", $out);
313
	}
314
315 View Code Duplication
	function testDoesntAffectCaughtExceptions() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
316
		$chain = new ErrorControlChainTest_Chain();
317
318
		list($out, $code) = $chain
0 ignored issues
show
Unused Code introduced by
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
319
			->then(function(){
320
				try {
321
					throw new Exception('Error');
322
				}
323
				catch (Exception $e) {
324
					echo "Good";
325
				}
326
			})
327
			->thenIfErrored(function(){
328
				echo "Bad";
329
			})
330
			->executeInSubprocess();
331
332
		$this->assertContains("Good", $out);
333
	}
334
335
	function testDoesntAffectHandledErrors() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
336
		$chain = new ErrorControlChainTest_Chain();
337
338
		list($out, $code) = $chain
0 ignored issues
show
Unused Code introduced by
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
339
			->then(function(){
340
				set_error_handler(function(){ /* NOP */ });
341
				user_error('Error', E_USER_ERROR);
342
			})
343
			->then(function(){
344
				echo "Good";
345
			})
346
			->thenIfErrored(function(){
347
				echo "Bad";
348
			})
349
			->executeInSubprocess();
350
351
		$this->assertContains("Good", $out);
352
	}
353
354
	function testMemoryConversion() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
355
		$chain = new ErrorControlChainTest_Chain();
356
357
		$this->assertEquals(200, $chain->translateMemstring('200'));
358
		$this->assertEquals(300, $chain->translateMemstring('300'));
359
360
		$this->assertEquals(2 * 1024, $chain->translateMemstring('2k'));
361
		$this->assertEquals(3 * 1024, $chain->translateMemstring('3K'));
362
363
		$this->assertEquals(2 * 1024 * 1024, $chain->translateMemstring('2m'));
364
		$this->assertEquals(3 * 1024 * 1024, $chain->translateMemstring('3M'));
365
366
		$this->assertEquals(2 * 1024 * 1024 * 1024, $chain->translateMemstring('2g'));
367
		$this->assertEquals(3 * 1024 * 1024 * 1024, $chain->translateMemstring('3G'));
368
369
		$this->assertEquals(200, $chain->translateMemstring('200foo'));
370
		$this->assertEquals(300, $chain->translateMemstring('300foo'));
371
	}
372
}
373