33
44use PetrKotek \NaughtyTestDetector \MetricFetcher ;
55use PHPUnit_Framework_BaseTestListener as BaseTestListener ;
6+ use PHPUnit_Framework_Test as Test ;
7+ use PHPUnit_Framework_TestCase as TestCase ;
68use PHPUnit_Framework_TestSuite as TestSuite ;
79
810/**
911 * NaughtyTestListener is PHPUnit TestListener, which identifies tests, which don't clean up after themselves.
1012 */
1113class 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