Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d914c66

Browse files
committed
Add remote coverage by c3
1 parent 958162d commit d914c66

File tree

4 files changed

+379
-0
lines changed

4 files changed

+379
-0
lines changed

‎c3.php

Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
<?php
2+
// @codingStandardsIgnoreFile
3+
// @codeCoverageIgnoreStart
4+
5+
/**
6+
* C3 - Codeception Code Coverage
7+
*
8+
* @author tiger
9+
*/
10+
11+
// $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG'] = 1;
12+
13+
if (isset($_COOKIE['CODECEPTION_CODECOVERAGE'])) {
14+
$cookie = json_decode($_COOKIE['CODECEPTION_CODECOVERAGE'], true);
15+
16+
// fix for improperly encoded JSON in Code Coverage cookie with WebDriver.
17+
// @see https://github.com/Codeception/Codeception/issues/874
18+
if (!is_array($cookie)) {
19+
$cookie = json_decode($cookie, true);
20+
}
21+
22+
if ($cookie) {
23+
foreach ($cookie as $key => $value) {
24+
$_SERVER["HTTP_X_CODECEPTION_" . strtoupper($key)] = $value;
25+
}
26+
}
27+
}
28+
29+
if (!array_key_exists('HTTP_X_CODECEPTION_CODECOVERAGE', $_SERVER)) {
30+
return;
31+
}
32+
33+
if (!function_exists('__c3_error')) {
34+
function __c3_error($message)
35+
{
36+
$errorLogFile = defined('C3_CODECOVERAGE_ERROR_LOG_FILE') ?
37+
C3_CODECOVERAGE_ERROR_LOG_FILE :
38+
C3_CODECOVERAGE_MEDIATE_STORAGE . DIRECTORY_SEPARATOR . 'error.txt';
39+
if (is_writable($errorLogFile)) {
40+
file_put_contents($errorLogFile, $message);
41+
} else {
42+
$message = "Could not write error to log file ($errorLogFile), original message: $message";
43+
}
44+
if (!headers_sent()) {
45+
header('X-Codeception-CodeCoverage-Error: ' . str_replace("\n", '', $message), true, 500);
46+
}
47+
setcookie('CODECEPTION_CODECOVERAGE_ERROR', $message);
48+
}
49+
}
50+
51+
// phpunit codecoverage shimming
52+
if (!class_exists('PHP_CodeCoverage') and class_exists('SebastianBergmann\CodeCoverage\CodeCoverage')) {
53+
class_alias('SebastianBergmann\CodeCoverage\CodeCoverage', 'PHP_CodeCoverage');
54+
class_alias('SebastianBergmann\CodeCoverage\Report\Text', 'PHP_CodeCoverage_Report_Text');
55+
class_alias('SebastianBergmann\CodeCoverage\Report\PHP', 'PHP_CodeCoverage_Report_PHP');
56+
class_alias('SebastianBergmann\CodeCoverage\Report\Clover', 'PHP_CodeCoverage_Report_Clover');
57+
class_alias('SebastianBergmann\CodeCoverage\Report\Crap4j', 'PHP_CodeCoverage_Report_Crap4j');
58+
class_alias('SebastianBergmann\CodeCoverage\Report\Html\Facade', 'PHP_CodeCoverage_Report_HTML');
59+
class_alias('SebastianBergmann\CodeCoverage\Report\Xml\Facade', 'PHP_CodeCoverage_Report_XML');
60+
class_alias('SebastianBergmann\CodeCoverage\Exception', 'PHP_CodeCoverage_Exception');
61+
}
62+
63+
// Autoload Codeception classes
64+
if (!class_exists('\\Codeception\\Codecept')) {
65+
if (file_exists(__DIR__ . '/codecept.phar')) {
66+
require_once 'phar://' . __DIR__ . '/codecept.phar/autoload.php';
67+
} elseif (stream_resolve_include_path(__DIR__ . '/vendor/autoload.php')) {
68+
require_once __DIR__ . '/vendor/autoload.php';
69+
// Required to load some methods only available at codeception/autoload.php
70+
if (stream_resolve_include_path(__DIR__ . '/vendor/codeception/codeception/autoload.php')) {
71+
require_once __DIR__ . '/vendor/codeception/codeception/autoload.php';
72+
}
73+
} elseif (stream_resolve_include_path('Codeception/autoload.php')) {
74+
require_once 'Codeception/autoload.php';
75+
} else {
76+
__c3_error('Codeception is not loaded. Please check that either PHAR or Composer package can be used');
77+
}
78+
}
79+
80+
// Load Codeception Config
81+
$config_dist_file = realpath(__DIR__) . DIRECTORY_SEPARATOR . 'codeception.dist.yml';
82+
$config_file = realpath(__DIR__) . DIRECTORY_SEPARATOR . 'codeception.yml';
83+
84+
if (isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_CONFIG'])) {
85+
$config_file = realpath(__DIR__) . DIRECTORY_SEPARATOR . $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_CONFIG'];
86+
}
87+
if (file_exists($config_file)) {
88+
// Use codeception.yml for configuration.
89+
} elseif (file_exists($config_dist_file)) {
90+
// Use codeception.dist.yml for configuration.
91+
$config_file = $config_dist_file;
92+
} else {
93+
__c3_error(sprintf("Codeception config file '%s' not found", $config_file));
94+
}
95+
try {
96+
\Codeception\Configuration::config($config_file);
97+
} catch (\Exception $e) {
98+
__c3_error($e->getMessage());
99+
}
100+
101+
if (!defined('C3_CODECOVERAGE_MEDIATE_STORAGE')) {
102+
103+
// workaround for 'zend_mm_heap corrupted' problem
104+
gc_disable();
105+
106+
$memoryLimit = ini_get('memory_limit');
107+
$requiredMemory = '384M';
108+
if ((substr($memoryLimit, -1) === 'M' && (int)$memoryLimit < (int)$requiredMemory)
109+
|| (substr($memoryLimit, -1) === 'K' && (int)$memoryLimit < (int)$requiredMemory * 1024)
110+
|| (ctype_digit($memoryLimit) && (int)$memoryLimit < (int)$requiredMemory * 1024 * 1024)
111+
) {
112+
ini_set('memory_limit', $requiredMemory);
113+
}
114+
115+
define('C3_CODECOVERAGE_MEDIATE_STORAGE', Codeception\Configuration::logDir() . 'c3tmp');
116+
define('C3_CODECOVERAGE_PROJECT_ROOT', Codeception\Configuration::projectDir());
117+
define('C3_CODECOVERAGE_TESTNAME', $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE']);
118+
119+
function __c3_build_html_report(PHP_CodeCoverage $codeCoverage, $path)
120+
{
121+
$writer = new PHP_CodeCoverage_Report_HTML();
122+
$writer->process($codeCoverage, $path . 'html');
123+
124+
if (file_exists($path . '.tar')) {
125+
unlink($path . '.tar');
126+
}
127+
128+
$phar = new PharData($path . '.tar');
129+
$phar->setSignatureAlgorithm(Phar::SHA1);
130+
$files = $phar->buildFromDirectory($path . 'html');
131+
array_map('unlink', $files);
132+
133+
if (in_array('GZ', Phar::getSupportedCompression())) {
134+
if (file_exists($path . '.tar.gz')) {
135+
unlink($path . '.tar.gz');
136+
}
137+
138+
$phar->compress(\Phar::GZ);
139+
140+
// close the file so that we can rename it
141+
unset($phar);
142+
143+
unlink($path . '.tar');
144+
rename($path . '.tar.gz', $path . '.tar');
145+
}
146+
147+
return $path . '.tar';
148+
}
149+
150+
function __c3_build_clover_report(PHP_CodeCoverage $codeCoverage, $path)
151+
{
152+
$writer = new PHP_CodeCoverage_Report_Clover();
153+
$writer->process($codeCoverage, $path . '.clover.xml');
154+
155+
return $path . '.clover.xml';
156+
}
157+
158+
function __c3_build_crap4j_report(PHP_CodeCoverage $codeCoverage, $path)
159+
{
160+
$writer = new PHP_CodeCoverage_Report_Crap4j();
161+
$writer->process($codeCoverage, $path . '.crap4j.xml');
162+
163+
return $path . '.crap4j.xml';
164+
}
165+
166+
function __c3_build_phpunit_report(PHP_CodeCoverage $codeCoverage, $path)
167+
{
168+
$writer = new PHP_CodeCoverage_Report_XML(\PHPUnit_Runner_Version::id());
169+
$writer->process($codeCoverage, $path . 'phpunit');
170+
171+
if (file_exists($path . '.tar')) {
172+
unlink($path . '.tar');
173+
}
174+
175+
$phar = new PharData($path . '.tar');
176+
$phar->setSignatureAlgorithm(Phar::SHA1);
177+
$files = $phar->buildFromDirectory($path . 'phpunit');
178+
array_map('unlink', $files);
179+
180+
if (in_array('GZ', Phar::getSupportedCompression())) {
181+
if (file_exists($path . '.tar.gz')) {
182+
unlink($path . '.tar.gz');
183+
}
184+
185+
$phar->compress(\Phar::GZ);
186+
187+
// close the file so that we can rename it
188+
unset($phar);
189+
190+
unlink($path . '.tar');
191+
rename($path . '.tar.gz', $path . '.tar');
192+
}
193+
194+
return $path . '.tar';
195+
}
196+
197+
function __c3_send_file($filename)
198+
{
199+
if (!headers_sent()) {
200+
readfile($filename);
201+
}
202+
203+
return __c3_exit();
204+
}
205+
206+
/**
207+
* @param $filename
208+
* @param bool $lock Lock the file for writing?
209+
* @return [null|PHP_CodeCoverage|\SebastianBergmann\CodeCoverage\CodeCoverage, resource]
210+
*/
211+
function __c3_factory($filename, $lock=false)
212+
{
213+
$file = null;
214+
if ($filename !== null && is_readable($filename)) {
215+
if ($lock) {
216+
$file = fopen($filename, 'r+');
217+
if (flock($file, LOCK_EX)) {
218+
$phpCoverage = unserialize(stream_get_contents($file));
219+
} else {
220+
__c3_error("Failed to acquire write-lock for $filename");
221+
}
222+
} else {
223+
$phpCoverage = unserialize(file_get_contents($filename));
224+
}
225+
226+
return array($phpCoverage, $file);
227+
} else {
228+
$phpCoverage = new PHP_CodeCoverage();
229+
}
230+
231+
if (isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_SUITE'])) {
232+
$suite = $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_SUITE'];
233+
try {
234+
$settings = \Codeception\Configuration::suiteSettings($suite, \Codeception\Configuration::config());
235+
} catch (Exception $e) {
236+
__c3_error($e->getMessage());
237+
}
238+
} else {
239+
$settings = \Codeception\Configuration::config();
240+
}
241+
242+
try {
243+
\Codeception\Coverage\Filter::setup($phpCoverage)
244+
->whiteList($settings)
245+
->blackList($settings);
246+
} catch (Exception $e) {
247+
__c3_error($e->getMessage());
248+
}
249+
250+
return array($phpCoverage, $file);
251+
}
252+
253+
function __c3_exit()
254+
{
255+
if (!isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG'])) {
256+
exit;
257+
}
258+
return null;
259+
}
260+
261+
function __c3_clear()
262+
{
263+
\Codeception\Util\FileSystem::doEmptyDir(C3_CODECOVERAGE_MEDIATE_STORAGE);
264+
}
265+
}
266+
267+
if (!is_dir(C3_CODECOVERAGE_MEDIATE_STORAGE)) {
268+
if (mkdir(C3_CODECOVERAGE_MEDIATE_STORAGE, 0777, true) === false) {
269+
__c3_error('Failed to create directory "' . C3_CODECOVERAGE_MEDIATE_STORAGE . '"');
270+
}
271+
}
272+
273+
// evaluate base path for c3-related files
274+
$path = realpath(C3_CODECOVERAGE_MEDIATE_STORAGE) . DIRECTORY_SEPARATOR . 'codecoverage';
275+
276+
$requested_c3_report = (strpos($_SERVER['REQUEST_URI'], 'c3/report') !== false);
277+
278+
$complete_report = $current_report = $path . '.serialized';
279+
if ($requested_c3_report) {
280+
set_time_limit(0);
281+
282+
$route = ltrim(strrchr($_SERVER['REQUEST_URI'], '/'), '/');
283+
284+
if ($route === 'clear') {
285+
__c3_clear();
286+
return __c3_exit();
287+
}
288+
289+
list($codeCoverage, ) = __c3_factory($complete_report);
290+
291+
switch ($route) {
292+
case 'html':
293+
try {
294+
__c3_send_file(__c3_build_html_report($codeCoverage, $path));
295+
} catch (Exception $e) {
296+
__c3_error($e->getMessage());
297+
}
298+
return __c3_exit();
299+
case 'clover':
300+
try {
301+
__c3_send_file(__c3_build_clover_report($codeCoverage, $path));
302+
} catch (Exception $e) {
303+
__c3_error($e->getMessage());
304+
}
305+
return __c3_exit();
306+
case 'crap4j':
307+
try {
308+
__c3_send_file(__c3_build_crap4j_report($codeCoverage, $path));
309+
} catch (Exception $e) {
310+
__c3_error($e->getMessage());
311+
}
312+
return __c3_exit();
313+
case 'serialized':
314+
try {
315+
__c3_send_file($complete_report);
316+
} catch (Exception $e) {
317+
__c3_error($e->getMessage());
318+
}
319+
return __c3_exit();
320+
case 'phpunit':
321+
try {
322+
__c3_send_file(__c3_build_phpunit_report($codeCoverage, $path));
323+
} catch (Exception $e) {
324+
__c3_error($e->getMessage());
325+
}
326+
return __c3_exit();
327+
}
328+
329+
} else {
330+
list($codeCoverage, ) = __c3_factory(null);
331+
$codeCoverage->start(C3_CODECOVERAGE_TESTNAME);
332+
if (!array_key_exists('HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG', $_SERVER)) {
333+
register_shutdown_function(
334+
function () use ($codeCoverage, $current_report) {
335+
336+
$codeCoverage->stop();
337+
if (!file_exists(dirname($current_report))) { // verify directory exists
338+
if (!mkdir(dirname($current_report), 0777, true)) {
339+
__c3_error("Can't write CodeCoverage report into $current_report");
340+
}
341+
}
342+
343+
// This will either lock the existing report for writing and return it along with a file pointer,
344+
// or return a fresh PHP_CodeCoverage object without a file pointer. We'll merge the current request
345+
// into that coverage object, write it to disk, and release the lock. By doing this in the end of
346+
// the request, we avoid this scenario, where Request 2 overwrites the changes from Request 1:
347+
//
348+
// Time ->
349+
// Request 1 [ <read> <write> ]
350+
// Request 2 [ <read> <write> ]
351+
//
352+
// In addition, by locking the file for exclusive writing, we make sure no other request try to
353+
// read/write to the file at the same time as this request (leading to a corrupt file). flock() is a
354+
// blocking call, so it waits until an exclusive lock can be acquired before continuing.
355+
356+
list($existingCodeCoverage, $file) = __c3_factory($current_report, true);
357+
$existingCodeCoverage->merge($codeCoverage);
358+
359+
if ($file === null) {
360+
file_put_contents($current_report, serialize($existingCodeCoverage), LOCK_EX);
361+
} else {
362+
fseek($file, 0);
363+
fwrite($file, serialize($existingCodeCoverage));
364+
fflush($file);
365+
flock($file, LOCK_UN);
366+
fclose($file);
367+
}
368+
}
369+
);
370+
}
371+
}
372+
373+
// @codeCoverageIgnoreEnd

‎codeception.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
actor: Tester
22
coverage:
33
enabled: true
4+
remote: true
45
include:
56
- app/Http/*
67
- app/Post.php

‎composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
},
1212
"require-dev": {
1313
"codeception/codeception": "2.3.x-dev",
14+
"codeception/c3": "2.*",
1415
"codeception/mockery-module": "0.2.3",
1516
"barryvdh/laravel-ide-helper": "2.4.1",
1617
"symfony/dom-crawler": "~3.0",

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /