1<?php
2/*
3 * This file is part of the php-code-coverage package.
4 *
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11namespace SebastianBergmann\CodeCoverage;
12
13use SebastianBergmann\CodeCoverage\Driver\PHPDBG;
14use SebastianBergmann\CodeCoverage\Driver\Xdebug;
15
16/**
17 * @covers SebastianBergmann\CodeCoverage\CodeCoverage
18 */
19class CodeCoverageTest extends TestCase
20{
21    /**
22     * @var CodeCoverage
23     */
24    private $coverage;
25
26    protected function setUp()
27    {
28        $this->coverage = new CodeCoverage;
29    }
30
31    public function testCanBeConstructedForXdebugWithoutGivenFilterObject()
32    {
33        if (PHP_SAPI == 'phpdbg') {
34            $this->markTestSkipped('Requires PHP CLI and Xdebug');
35        }
36
37        $this->assertAttributeInstanceOf(
38            Xdebug::class,
39            'driver',
40            $this->coverage
41        );
42
43        $this->assertAttributeInstanceOf(
44            Filter::class,
45            'filter',
46            $this->coverage
47        );
48    }
49
50    public function testCanBeConstructedForXdebugWithGivenFilterObject()
51    {
52        if (PHP_SAPI == 'phpdbg') {
53            $this->markTestSkipped('Requires PHP CLI and Xdebug');
54        }
55
56        $filter   = new Filter;
57        $coverage = new CodeCoverage(null, $filter);
58
59        $this->assertAttributeInstanceOf(
60            Xdebug::class,
61            'driver',
62            $coverage
63        );
64
65        $this->assertSame($filter, $coverage->filter());
66    }
67
68    public function testCanBeConstructedForPhpdbgWithoutGivenFilterObject()
69    {
70        if (PHP_SAPI != 'phpdbg') {
71            $this->markTestSkipped('Requires PHPDBG');
72        }
73
74        $this->assertAttributeInstanceOf(
75            PHPDBG::class,
76            'driver',
77            $this->coverage
78        );
79
80        $this->assertAttributeInstanceOf(
81            Filter::class,
82            'filter',
83            $this->coverage
84        );
85    }
86
87    public function testCanBeConstructedForPhpdbgWithGivenFilterObject()
88    {
89        if (PHP_SAPI != 'phpdbg') {
90            $this->markTestSkipped('Requires PHPDBG');
91        }
92
93        $filter   = new Filter;
94        $coverage = new CodeCoverage(null, $filter);
95
96        $this->assertAttributeInstanceOf(
97            PHPDBG::class,
98            'driver',
99            $coverage
100        );
101
102        $this->assertSame($filter, $coverage->filter());
103    }
104
105    /**
106     * @expectedException SebastianBergmann\CodeCoverage\Exception
107     */
108    public function testCannotStartWithInvalidArgument()
109    {
110        $this->coverage->start(null, null);
111    }
112
113    /**
114     * @expectedException SebastianBergmann\CodeCoverage\Exception
115     */
116    public function testCannotStopWithInvalidFirstArgument()
117    {
118        $this->coverage->stop(null);
119    }
120
121    /**
122     * @expectedException SebastianBergmann\CodeCoverage\Exception
123     */
124    public function testCannotStopWithInvalidSecondArgument()
125    {
126        $this->coverage->stop(true, null);
127    }
128
129    /**
130     * @expectedException SebastianBergmann\CodeCoverage\Exception
131     */
132    public function testCannotAppendWithInvalidArgument()
133    {
134        $this->coverage->append([], null);
135    }
136
137    /**
138     * @expectedException SebastianBergmann\CodeCoverage\Exception
139     */
140    public function testSetCacheTokensThrowsExceptionForInvalidArgument()
141    {
142        $this->coverage->setCacheTokens(null);
143    }
144
145    public function testSetCacheTokens()
146    {
147        $this->coverage->setCacheTokens(true);
148        $this->assertAttributeEquals(true, 'cacheTokens', $this->coverage);
149    }
150
151    /**
152     * @expectedException SebastianBergmann\CodeCoverage\Exception
153     */
154    public function testSetCheckForUnintentionallyCoveredCodeThrowsExceptionForInvalidArgument()
155    {
156        $this->coverage->setCheckForUnintentionallyCoveredCode(null);
157    }
158
159    public function testSetCheckForUnintentionallyCoveredCode()
160    {
161        $this->coverage->setCheckForUnintentionallyCoveredCode(true);
162        $this->assertAttributeEquals(
163            true,
164            'checkForUnintentionallyCoveredCode',
165            $this->coverage
166        );
167    }
168
169    /**
170     * @expectedException SebastianBergmann\CodeCoverage\Exception
171     */
172    public function testSetForceCoversAnnotationThrowsExceptionForInvalidArgument()
173    {
174        $this->coverage->setForceCoversAnnotation(null);
175    }
176
177    public function testSetCheckForMissingCoversAnnotation()
178    {
179        $this->coverage->setCheckForMissingCoversAnnotation(true);
180        $this->assertAttributeEquals(
181            true,
182            'checkForMissingCoversAnnotation',
183            $this->coverage
184        );
185    }
186
187    /**
188     * @expectedException SebastianBergmann\CodeCoverage\Exception
189     */
190    public function testSetCheckForMissingCoversAnnotationThrowsExceptionForInvalidArgument()
191    {
192        $this->coverage->setCheckForMissingCoversAnnotation(null);
193    }
194
195    public function testSetForceCoversAnnotation()
196    {
197        $this->coverage->setForceCoversAnnotation(true);
198        $this->assertAttributeEquals(
199            true,
200            'forceCoversAnnotation',
201            $this->coverage
202        );
203    }
204
205    /**
206     * @expectedException SebastianBergmann\CodeCoverage\Exception
207     */
208    public function testSetCheckForUnexecutedCoveredCodeThrowsExceptionForInvalidArgument()
209    {
210        $this->coverage->setCheckForUnexecutedCoveredCode(null);
211    }
212
213    public function testSetCheckForUnexecutedCoveredCode()
214    {
215        $this->coverage->setCheckForUnexecutedCoveredCode(true);
216        $this->assertAttributeEquals(
217            true,
218            'checkForUnexecutedCoveredCode',
219            $this->coverage
220        );
221    }
222
223    /**
224     * @expectedException SebastianBergmann\CodeCoverage\Exception
225     */
226    public function testSetAddUncoveredFilesFromWhitelistThrowsExceptionForInvalidArgument()
227    {
228        $this->coverage->setAddUncoveredFilesFromWhitelist(null);
229    }
230
231    public function testSetAddUncoveredFilesFromWhitelist()
232    {
233        $this->coverage->setAddUncoveredFilesFromWhitelist(true);
234        $this->assertAttributeEquals(
235            true,
236            'addUncoveredFilesFromWhitelist',
237            $this->coverage
238        );
239    }
240
241    /**
242     * @expectedException SebastianBergmann\CodeCoverage\Exception
243     */
244    public function testSetProcessUncoveredFilesFromWhitelistThrowsExceptionForInvalidArgument()
245    {
246        $this->coverage->setProcessUncoveredFilesFromWhitelist(null);
247    }
248
249    public function testSetProcessUncoveredFilesFromWhitelist()
250    {
251        $this->coverage->setProcessUncoveredFilesFromWhitelist(true);
252        $this->assertAttributeEquals(
253            true,
254            'processUncoveredFilesFromWhitelist',
255            $this->coverage
256        );
257    }
258
259    public function testSetIgnoreDeprecatedCode()
260    {
261        $this->coverage->setIgnoreDeprecatedCode(true);
262        $this->assertAttributeEquals(
263            true,
264            'ignoreDeprecatedCode',
265            $this->coverage
266        );
267    }
268
269    /**
270     * @expectedException SebastianBergmann\CodeCoverage\Exception
271     */
272    public function testSetIgnoreDeprecatedCodeThrowsExceptionForInvalidArgument()
273    {
274        $this->coverage->setIgnoreDeprecatedCode(null);
275    }
276
277    public function testClear()
278    {
279        $this->coverage->clear();
280
281        $this->assertAttributeEquals(null, 'currentId', $this->coverage);
282        $this->assertAttributeEquals([], 'data', $this->coverage);
283        $this->assertAttributeEquals([], 'tests', $this->coverage);
284    }
285
286    public function testCollect()
287    {
288        $coverage = $this->getCoverageForBankAccount();
289
290        $this->assertEquals(
291            $this->getExpectedDataArrayForBankAccount(),
292            $coverage->getData()
293        );
294
295        if (version_compare(\PHPUnit_Runner_Version::id(), '4.7', '>=')) {
296            $size = 'unknown';
297        } else {
298            $size = 'small';
299        }
300
301        $this->assertEquals(
302            [
303                'BankAccountTest::testBalanceIsInitiallyZero'       => ['size' => $size, 'status' => null],
304                'BankAccountTest::testBalanceCannotBecomeNegative'  => ['size' => $size, 'status' => null],
305                'BankAccountTest::testBalanceCannotBecomeNegative2' => ['size' => $size, 'status' => null],
306                'BankAccountTest::testDepositWithdrawMoney'         => ['size' => $size, 'status' => null]
307            ],
308            $coverage->getTests()
309        );
310    }
311
312    public function testMerge()
313    {
314        $coverage = $this->getCoverageForBankAccountForFirstTwoTests();
315        $coverage->merge($this->getCoverageForBankAccountForLastTwoTests());
316
317        $this->assertEquals(
318            $this->getExpectedDataArrayForBankAccount(),
319            $coverage->getData()
320        );
321    }
322
323    public function testMerge2()
324    {
325        $coverage = new CodeCoverage(
326            $this->createMock(Xdebug::class),
327            new Filter
328        );
329
330        $coverage->merge($this->getCoverageForBankAccount());
331
332        $this->assertEquals(
333            $this->getExpectedDataArrayForBankAccount(),
334            $coverage->getData()
335        );
336    }
337
338    public function testGetLinesToBeIgnored()
339    {
340        $this->assertEquals(
341            [
342                1,
343                3,
344                4,
345                5,
346                7,
347                8,
348                9,
349                10,
350                11,
351                12,
352                13,
353                14,
354                15,
355                16,
356                17,
357                18,
358                19,
359                20,
360                21,
361                22,
362                23,
363                24,
364                25,
365                26,
366                27,
367                28,
368                30,
369                32,
370                33,
371                34,
372                35,
373                36,
374                37,
375                38
376            ],
377            $this->getLinesToBeIgnored()->invoke(
378                $this->coverage,
379                TEST_FILES_PATH . 'source_with_ignore.php'
380            )
381        );
382    }
383
384    public function testGetLinesToBeIgnored2()
385    {
386        $this->assertEquals(
387            [1, 5],
388            $this->getLinesToBeIgnored()->invoke(
389                $this->coverage,
390                TEST_FILES_PATH . 'source_without_ignore.php'
391            )
392        );
393    }
394
395    public function testGetLinesToBeIgnored3()
396    {
397        $this->assertEquals(
398            [
399                1,
400                2,
401                3,
402                4,
403                5,
404                8,
405                11,
406                15,
407                16,
408                19,
409                20
410            ],
411            $this->getLinesToBeIgnored()->invoke(
412                $this->coverage,
413                TEST_FILES_PATH . 'source_with_class_and_anonymous_function.php'
414            )
415        );
416    }
417
418    public function testGetLinesToBeIgnoredOneLineAnnotations()
419    {
420        $this->assertEquals(
421            [
422                1,
423                2,
424                3,
425                4,
426                5,
427                6,
428                7,
429                8,
430                9,
431                10,
432                11,
433                12,
434                13,
435                14,
436                15,
437                16,
438                18,
439                20,
440                21,
441                23,
442                24,
443                25,
444                27,
445                28,
446                29,
447                30,
448                31,
449                32,
450                33,
451                34,
452                37
453            ],
454            $this->getLinesToBeIgnored()->invoke(
455                $this->coverage,
456                TEST_FILES_PATH . 'source_with_oneline_annotations.php'
457            )
458        );
459    }
460
461    /**
462     * @return \ReflectionMethod
463     */
464    private function getLinesToBeIgnored()
465    {
466        $getLinesToBeIgnored = new \ReflectionMethod(
467            'SebastianBergmann\CodeCoverage\CodeCoverage',
468            'getLinesToBeIgnored'
469        );
470
471        $getLinesToBeIgnored->setAccessible(true);
472
473        return $getLinesToBeIgnored;
474    }
475
476    public function testGetLinesToBeIgnoredWhenIgnoreIsDisabled()
477    {
478        $this->coverage->setDisableIgnoredLines(true);
479
480        $this->assertEquals(
481            [],
482            $this->getLinesToBeIgnored()->invoke(
483                $this->coverage,
484                TEST_FILES_PATH . 'source_with_ignore.php'
485            )
486        );
487    }
488
489    /**
490     * @expectedException SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException
491     */
492    public function testAppendThrowsExceptionIfCoveredCodeWasNotExecuted()
493    {
494        $this->coverage->filter()->addDirectoryToWhitelist(TEST_FILES_PATH);
495        $this->coverage->setCheckForUnexecutedCoveredCode(true);
496
497        $data = [
498            TEST_FILES_PATH . 'BankAccount.php' => [
499                29 => -1,
500                31 => -1
501            ]
502        ];
503
504        $linesToBeCovered = [
505            TEST_FILES_PATH . 'BankAccount.php' => [
506                22,
507                24
508            ]
509        ];
510
511        $linesToBeUsed = [];
512
513        $this->coverage->append($data, 'File1.php', true, $linesToBeCovered, $linesToBeUsed);
514    }
515
516    /**
517     * @expectedException SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException
518     */
519    public function testAppendThrowsExceptionIfUsedCodeWasNotExecuted()
520    {
521        $this->coverage->filter()->addDirectoryToWhitelist(TEST_FILES_PATH);
522        $this->coverage->setCheckForUnexecutedCoveredCode(true);
523
524        $data = [
525            TEST_FILES_PATH . 'BankAccount.php' => [
526                29 => -1,
527                31 => -1
528            ]
529        ];
530
531        $linesToBeCovered = [
532            TEST_FILES_PATH . 'BankAccount.php' => [
533                29,
534                31
535            ]
536        ];
537
538        $linesToBeUsed = [
539            TEST_FILES_PATH . 'BankAccount.php' => [
540                22,
541                24
542            ]
543        ];
544
545        $this->coverage->append($data, 'File1.php', true, $linesToBeCovered, $linesToBeUsed);
546    }
547}
548