Skip to content

Commit 622260b

Browse files
authored
Merge pull request #4 from petrkotek/support-run-levels
Support run levels & php-cs-fixer
2 parents c94ae28 + ccbc430 commit 622260b

5 files changed

Lines changed: 387 additions & 65 deletions

File tree

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ before_script:
1212

1313
script:
1414
- ./vendor/bin/phpunit --coverage-clover build/logs/clover.xml
15+
- ./phpcs.sh --dry-run --diff
1516

1617
after_success:
1718
- ./vendor/bin/coveralls

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,19 @@ NaughtyTestDetector is installable via [Composer](http://getcomposer.org) and sh
3636
<string>hello world</string>
3737
</element>
3838
</array>
39+
<!-- Optionally specify levels on which MetricFetcher should be executed -->
40+
<!-- Note: values below are the default ones -->
41+
<array>
42+
<element key="executeOnTestLevel">
43+
<boolean>false</boolean>
44+
</element>
45+
<element key="executeOnTestSuiteLevel">
46+
<boolean>true</boolean>
47+
</element>
48+
<element key="executeOnGlobalLevel">
49+
<boolean>false</boolean>
50+
</element>
51+
</array>
3952
</arguments>
4053
</listener>
4154
</listeners>

phpcs.sh

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env bash
2+
3+
export PHP_CS_FIXER=~/.composer/vendor/bin/php-cs-fixer
4+
5+
if ! [ -x "$(command -v ${PHP_CS_FIXER})" ]; then
6+
echo 'Command `php-cs-fixer` not found. Installing...'
7+
composer global require friendsofphp/php-cs-fixer
8+
fi
9+
10+
${PHP_CS_FIXER} fix --fixers="\
11+
array_element_no_space_before_comma,\
12+
array_element_white_space_after_comma,\
13+
braces,\
14+
concat_with_spaces,\
15+
duplicate_semicolon,\
16+
elseif,\
17+
encoding,\
18+
eof_ending,\
19+
extra_empty_lines,\
20+
function_call_space,\
21+
function_declaration,\
22+
indentation,\
23+
join_function,\
24+
line_after_namespace,\
25+
linefeed,\
26+
list_commas,\
27+
lowercase_constants,\
28+
lowercase_keywords,\
29+
method_argument_space,\
30+
multiline_array_trailing_comma,\
31+
multiple_use,\
32+
new_with_braces,\
33+
no_blank_lines_after_class_opening,\
34+
no_empty_lines_after_phpdocs,\
35+
object_operator,\
36+
operators_spaces,\
37+
ordered_use,\
38+
parenthesis,\
39+
php4_constructor,\
40+
php_closing_tag,\
41+
php_unit_construct,\
42+
phpdoc_indent,\
43+
phpdoc_no_access,\
44+
phpdoc_no_package,\
45+
phpdoc_order,\
46+
phpdoc_scalar,\
47+
phpdoc_separation,\
48+
phpdoc_to_comment,\
49+
phpdoc_trim,\
50+
phpdoc_type_to_var,\
51+
phpdoc_types,\
52+
phpdoc_var_without_name,\
53+
print_to_echo,\
54+
remove_leading_slash_use,\
55+
remove_lines_between_uses,\
56+
return,\
57+
short_array_syntax,\
58+
short_bool_cast,\
59+
short_tag,\
60+
single_blank_line_before_namespace,\
61+
single_line_after_imports,\
62+
single_quote,\
63+
spaces_before_semicolon,\
64+
standardize_not_equal,\
65+
ternary_spaces,\
66+
trailing_spaces,\
67+
trim_array_spaces,\
68+
unalign_double_arrow,\
69+
unalign_equals,\
70+
unneeded_control_parentheses,\
71+
unused_use,\
72+
visibility,\
73+
whitespacy_lines"\
74+
$@ .

src/PHPUnit/Listeners/NaughtyTestListener.php

Lines changed: 137 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,80 +3,147 @@
33

44
use PetrKotek\NaughtyTestDetector\MetricFetcher;
55
use PHPUnit_Framework_BaseTestListener as BaseTestListener;
6+
use PHPUnit_Framework_Test as Test;
7+
use PHPUnit_Framework_TestCase as TestCase;
68
use PHPUnit_Framework_TestSuite as TestSuite;
79

810
/**
911
* NaughtyTestListener is PHPUnit TestListener, which identifies tests, which don't clean up after themselves.
1012
*/
1113
class NaughtyTestListener extends BaseTestListener
1214
{
13-
/** @var int[] Key is a metric name, value is a metric value before the run */
14-
private $metricsBeforeRun = [];
15+
/** @var string Config key enabling execution of MetricFetcher on test-level (bool) */
16+
const CONFIG_KEY_LEVEL_TEST = 'executeOnTestLevel';
1517

16-
/** @var bool Flag indicate if this is the very first executed test suite */
17-
private $firstTestSuite = true;
18+
/** @var string Config key enabling execution of MetricFetcher on test suite-level (bool) */
19+
const CONFIG_KEY_LEVEL_SUITE = 'executeOnTestSuiteLevel';
20+
21+
/** @var string Config key enabling execution of MetricFetcher before & after running all tests (bool) */
22+
const CONFIG_KEY_LEVEL_GLOBAL = 'executeOnGlobalLevel';
23+
24+
private $defaultOptions = [
25+
self::CONFIG_KEY_LEVEL_TEST => false,
26+
self::CONFIG_KEY_LEVEL_SUITE => true,
27+
self::CONFIG_KEY_LEVEL_GLOBAL => false,
28+
];
29+
30+
/** @var mixed[] Key is a metric name, value is a metric value before the globals test suite execution */
31+
private $metricsBeforeGlobalTestSuite;
32+
33+
/** @var mixed[] Key is a metric name, value is a metric value before the test suite execution */
34+
private $metricsBeforeTestSuite;
35+
36+
/** @var mixed[] Key is a metric name, value is a metric value before the test execution */
37+
private $metricsBeforeTest;
38+
39+
/**
40+
* Counts test suites. Since there is one "overall" test suite encapsulating all test suits, once this counter is 0,
41+
* it's over or the beginning.
42+
*
43+
* @var int
44+
*/
45+
private $testSuiteCounter = 0;
1846

1947
/** @var MetricFetcher|null */
2048
private $metricFetcher;
2149

50+
/** @var array */
51+
private $flags;
52+
2253
/**
2354
* @param string $metricFetcherClass
2455
* @param array $constructorArgs
56+
* @param array $options Array with self::CONFIG_KEY_* as keys and value as noted in constant's comment
2557
*/
26-
public function __construct($metricFetcherClass = null, array $constructorArgs = [])
27-
{
58+
public function __construct(
59+
$metricFetcherClass = null,
60+
array $constructorArgs = [],
61+
array $options = []
62+
) {
2863
if ($metricFetcherClass !== null) {
2964
$this->metricFetcher = new $metricFetcherClass(...$constructorArgs);
3065
}
66+
$this->flags = array_merge($this->defaultOptions, $options);
3167
}
3268

3369
/**
3470
* @param TestSuite $suite
3571
*/
3672
public function startTestSuite(TestSuite $suite)
3773
{
38-
// We need to fetch counts before the very first test suite.
39-
// Next test suites will store counts in endTestSuite() method call
40-
if ($this->firstTestSuite) {
41-
$this->metricsBeforeRun = $this->fetchMetrics();
42-
$this->firstTestSuite = false;
74+
if ($this->testSuiteCounter === 0 &&
75+
($this->isLevelEnabled(self::CONFIG_KEY_LEVEL_SUITE) || $this->isLevelEnabled(self::CONFIG_KEY_LEVEL_GLOBAL))
76+
) {
77+
$currentMetrics = $this->fetchMetrics();
78+
$this->metricsBeforeGlobalTestSuite = $currentMetrics;
79+
$this->metricsBeforeTestSuite = $currentMetrics;
80+
$this->metricsBeforeTest = $currentMetrics;
4381
}
82+
83+
$this->testSuiteCounter++;
4484
}
4585

4686
/**
4787
* @param TestSuite $suite
4888
*/
4989
public function endTestSuite(TestSuite $suite)
5090
{
51-
$currentMetrics = $this->fetchMetrics();
91+
$this->testSuiteCounter--;
92+
$currentMetrics = $this->isLevelEnabled(self::CONFIG_KEY_LEVEL_TEST) ? $this->metricsBeforeTest : null;
5293

53-
$modifications = [];
94+
if ($suite->getName() !== '' && $this->isLevelEnabled(self::CONFIG_KEY_LEVEL_SUITE)) {
95+
// finished test suite
96+
$currentMetrics = $currentMetrics ?: $this->fetchMetrics();
97+
$modifications = $this->evaluateModifications($this->metricsBeforeTestSuite, $currentMetrics);
5498

55-
// evaluate metrics, which we had before the run
56-
foreach ($this->metricsBeforeRun as $metric => $previousValue) {
57-
$currentValue = array_key_exists($metric, $currentMetrics) ? $currentMetrics[$metric] : null;
58-
if ($currentValue !== $previousValue) {
59-
$diffString = '';
60-
$currentValue = $this->formatValue($currentValue);
61-
if (is_numeric($previousValue) && is_numeric($currentValue)) {
62-
$diffNum = $currentValue - $previousValue;
63-
$diffString = sprintf(' (%s%d)', $diffNum > 0 ? '+' : '-', abs($diffNum));
64-
}
65-
$modifications[$metric] = $this->formatValue($previousValue) . ' -> ' . $this->formatValue($currentValue) . $diffString;
99+
if (count($modifications) > 0) {
100+
$this->printNaughtyTest('TestSuite ' . $suite->getName(), $modifications);
66101
}
102+
103+
$this->metricsBeforeTestSuite = $currentMetrics;
67104
}
68105

69-
// evaluate metrics, which we didn't have before the run
70-
$newMetrics = array_diff_key($currentMetrics, $this->metricsBeforeRun);
71-
foreach ($newMetrics as $metric => $currentValue) {
72-
$modifications[$metric] = $this->formatValue(null) . ' -> ' . $this->formatValue($currentValue);
106+
if ($this->testSuiteCounter === 0 && $this->isLevelEnabled(self::CONFIG_KEY_LEVEL_GLOBAL)) {
107+
// finished the global test suite
108+
$currentMetrics = $currentMetrics ?: $this->fetchMetrics();
109+
$modifications = $this->evaluateModifications($this->metricsBeforeGlobalTestSuite, $currentMetrics);
110+
111+
if (count($modifications) > 0) {
112+
$this->printNaughtyTest('Global TestSuite', $modifications);
113+
}
73114
}
115+
}
116+
117+
/**
118+
* @param Test $test
119+
*/
120+
public function startTest(Test $test)
121+
{
122+
if ($this->metricsBeforeTest === null && $this->isLevelEnabled(self::CONFIG_KEY_LEVEL_TEST)) {
123+
$this->metricsBeforeTest = $this->fetchMetrics();
124+
}
125+
}
74126

75-
if (count($modifications) > 0) {
76-
$this->printNaughtyTest($suite->getName(), $modifications);
127+
/**
128+
* @param Test $test
129+
* @param float $time
130+
*/
131+
public function endTest(Test $test, $time)
132+
{
133+
if (!($test instanceof TestCase)) {
134+
return;
77135
}
136+
if ($this->isLevelEnabled(self::CONFIG_KEY_LEVEL_TEST)) {
137+
$currentMetrics = $this->fetchMetrics();
138+
139+
$modifications = $this->evaluateModifications($this->metricsBeforeTest, $currentMetrics);
140+
141+
if (count($modifications) > 0) {
142+
$this->printNaughtyTest('Test ' . $test->getName(), $modifications);
143+
}
78144

79-
$this->metricsBeforeRun = $currentMetrics;
145+
$this->metricsBeforeTest = $currentMetrics;
146+
}
80147
}
81148

82149
/**
@@ -100,11 +167,50 @@ private function fetchMetrics()
100167
if ($this->metricFetcher === null) {
101168
return [];
102169
}
170+
103171
return $this->metricFetcher->fetchMetrics();
104172
}
105173

106174
private function formatValue($value)
107175
{
108176
return $value !== null ? $value : 'n/a';
109177
}
178+
179+
/**
180+
* @param array $metricsBefore
181+
* @param array $metricsAfter
182+
*
183+
* @return array
184+
*/
185+
private function evaluateModifications(array $metricsBefore, array $metricsAfter)
186+
{
187+
$modifications = [];
188+
189+
// evaluate metrics, which we had before the run
190+
foreach ($metricsBefore as $metric => $previousValue) {
191+
$currentValue = array_key_exists($metric, $metricsAfter) ? $metricsAfter[$metric] : null;
192+
if ($currentValue !== $previousValue) {
193+
$diffString = '';
194+
$currentValue = $this->formatValue($currentValue);
195+
if (is_numeric($previousValue) && is_numeric($currentValue)) {
196+
$diffNum = $currentValue - $previousValue;
197+
$diffString = sprintf(' (%s%d)', $diffNum > 0 ? '+' : '-', abs($diffNum));
198+
}
199+
$modifications[$metric] = $this->formatValue($previousValue) . ' -> ' . $this->formatValue($currentValue) . $diffString;
200+
}
201+
}
202+
203+
// evaluate metrics, which we didn't have before the run
204+
$newMetrics = array_diff_key($metricsAfter, $metricsBefore);
205+
foreach ($newMetrics as $metric => $currentValue) {
206+
$modifications[$metric] = $this->formatValue(null) . ' -> ' . $this->formatValue($currentValue);
207+
}
208+
209+
return $modifications;
210+
}
211+
212+
private function isLevelEnabled($level)
213+
{
214+
return array_key_exists($level, $this->flags) && $this->flags[$level];
215+
}
110216
}

0 commit comments

Comments
 (0)