├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── autoload.php ├── behat.yml.dist ├── build.php ├── composer.json ├── doc └── index.rst ├── init.php ├── phpunit.xml.dist ├── src └── VIPSoft │ └── CodeCoverageExtension │ ├── Compiler │ ├── DriverPass.php │ ├── FactoryPass.php │ └── FilterPass.php │ ├── Driver │ ├── Proxy.php │ └── RemoteXdebug.php │ ├── Extension.php │ ├── Listener │ └── EventListener.php │ ├── Resources │ └── config │ │ └── services.xml │ └── Service │ └── ReportService.php └── tests └── VIPSoft ├── CodeCoverageExtension ├── Compiler │ ├── DriverPassTest.php │ ├── FactoryPassTest.php │ └── FilterPassTest.php ├── Driver │ ├── ProxyTest.php │ └── RemoteXdebugTest.php ├── ExtensionTest.php ├── Listener │ └── EventListenerTest.php └── Service │ └── ReportServiceTest.php └── TestCase.php /.gitignore: -------------------------------------------------------------------------------- 1 | nbproject/ 2 | vendor/ 3 | composer.phar 4 | **/*.dbf 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | 8 | before_script: 9 | - wget http://getcomposer.org/composer.phar 10 | - php composer.phar install --dev 11 | 12 | script: ./vendor/bin/phpunit 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Anthon Pang 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeCoverage 2 | 3 | Code coverage (Behat) extension. 4 | 5 | ## Documentation 6 | 7 | [Official documentation](https://github.com/vipsoft/code-coverage-extension/blob/master/doc/index.rst) 8 | 9 | ## Source 10 | 11 | [Github](https://github.com/vipsoft/code-coverage-extension) 12 | 13 | [![Build Status](https://travis-ci.org/vipsoft/code-coverage-extension.png?branch=master)](https://travis-ci.org/vipsoft/code-coverage-extension) 14 | 15 | ## Copyright 16 | 17 | Copyright (c) 2013 Anthon Pang. See LICENSE for details. 18 | 19 | ## Credits 20 | 21 | * Anthon Pang [robocoder](http://github.com/robocoder) 22 | * Konstantin Kudryashov [everzet](http://github.com/everzet) - init.php and build.php 23 | * [Others](https://github.com/vipsoft/code-coverage-extension/graphs/contributors) 24 | -------------------------------------------------------------------------------- /autoload.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class Bootstrap 18 | { 19 | /** 20 | * Load class 21 | * 22 | * @param string $class Class name 23 | */ 24 | public static function autoload($class) 25 | { 26 | $file = str_replace(array('\\', '_'), '/', $class); 27 | $path = __DIR__ . '/src/' . $file . '.php'; 28 | 29 | if (file_exists($path)) { 30 | include_once $path; 31 | } 32 | } 33 | } 34 | 35 | spl_autoload_register('VIPSoft\Bootstrap::autoload'); 36 | -------------------------------------------------------------------------------- /behat.yml.dist: -------------------------------------------------------------------------------- 1 | default: 2 | extensions: 3 | VIPSoft\CodeCoverageExtension\Extension: 4 | # optional authentication 5 | auth: ~ 6 | # auth: 7 | # user: user 8 | # password: password 9 | create: 10 | method: POST 11 | path: / 12 | read: 13 | method: GET 14 | path: / 15 | delete: 16 | method: DELETE 17 | path: / 18 | drivers: 19 | - remote 20 | - local 21 | filter: ~ 22 | # filter: 23 | # forceCoversAnnotation: false 24 | # mapTestClassNameToCoveredClassName: false 25 | # whitelist: 26 | # addUncoveredFilesFromWhitelist: true 27 | # processUncoveredFilesFromWhitelist: false 28 | # include: 29 | # directories: 30 | # 'some_path': 31 | # suffix: 'some_suffix' 32 | # prefix: 'some_prefix' 33 | # files: 34 | # - 'some_file' 35 | # exclude: 36 | # directories: 37 | # ... 38 | # files: 39 | # ... 40 | # blacklist: 41 | # include: 42 | # directories: 43 | # ... 44 | # files: 45 | # ... 46 | # exclude: 47 | # directories: 48 | # ... 49 | # files: 50 | # ... 51 | report: 52 | format: html 53 | options: 54 | target:: /tmp/report 55 | -------------------------------------------------------------------------------- /build.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | chdir(__DIR__); 13 | 14 | $filename = 'code_coverage_extension.phar'; 15 | 16 | if (file_exists($filename)) { 17 | unlink($filename); 18 | } 19 | 20 | $phar = new \Phar($filename, 0, 'extension.phar'); 21 | $phar->setSignatureAlgorithm(\Phar::SHA1); 22 | $phar->startBuffering(); 23 | 24 | foreach (findFiles('src') as $path) { 25 | $phar->addFromString($path, file_get_contents(__DIR__.'/'.$path)); 26 | } 27 | 28 | $phar->addFromString('init.php', file_get_contents(__DIR__.'/init.php')); 29 | 30 | $phar->setStub(<< 37 | * 38 | * This source file is subject to the MIT license that is bundled 39 | * with this source code in the file LICENSE. 40 | */ 41 | 42 | Phar::mapPhar('extension.phar'); 43 | 44 | return require 'phar://extension.phar/init.php'; 45 | 46 | __HALT_COMPILER(); 47 | STUB 48 | ); 49 | $phar->stopBuffering(); 50 | 51 | /** 52 | * Find files 53 | * 54 | * @param string $dir Directory 55 | * 56 | * @return array 57 | */ 58 | function findFiles($dir) 59 | { 60 | $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), 61 | RecursiveIteratorIterator::CHILD_FIRST); 62 | 63 | $files = array(); 64 | foreach ($iterator as $path) { 65 | if ($path->isFile()) { 66 | $files[] = $path->getPath().DIRECTORY_SEPARATOR.$path->getFilename(); 67 | } 68 | } 69 | 70 | return $files; 71 | } 72 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vipsoft/code-coverage-extension", 3 | "description": "Behat code coverage collector", 4 | "license": "BSD-2-Clause", 5 | "authors": [ 6 | { 7 | "name": "Anthon Pang", 8 | "email": "apang@softwaredevelopment.ca" 9 | } 10 | ], 11 | "require": { 12 | "php": ">=5.3.10", 13 | "behat/behat": "~3.0", 14 | "guzzle/http": "~3.0", 15 | "symfony/config": "~2.2", 16 | "symfony/dependency-injection": "~2.2", 17 | "symfony/http-kernel": "~2.2", 18 | "symfony/http-foundation": "~2.2", 19 | "vipsoft/code-coverage-common": "2.5.*" 20 | }, 21 | "require-dev": { 22 | "phpunit/phpunit": "~3.7", 23 | "mikey179/vfsstream": "~1.2" 24 | }, 25 | "autoload": { 26 | "psr-0": { 27 | "VIPSoft\\CodeCoverageExtension": "src/" 28 | } 29 | }, 30 | "keywords": [ 31 | "bdd", "behat", "phpunit", "code", "coverage", "testing" 32 | ], 33 | "extra": { 34 | "branch-alias": { 35 | "dev-master": "3.0.x-dev" 36 | } 37 | }, 38 | "minimum-stability": "dev" 39 | } 40 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | CodeCoverageExtension 3 | ===================== 4 | 5 | The Code Coverage extension allows you to collect local and/or remote code 6 | coverage. 7 | 8 | Use this extension to find dead code in your application, or unused/obsolete 9 | contexts or step definitions. 10 | 11 | Installation 12 | ============ 13 | This extension requires: 14 | 15 | * Behat 2.4+ 16 | * Mink 1.4+ 17 | 18 | Through Composer 19 | ---------------- 20 | 1. Set dependencies in your **composer.json**: 21 | 22 | .. code-block:: js 23 | 24 | { 25 | "require": { 26 | ... 27 | "vipsoft/code-coverage-extension": "*" 28 | } 29 | } 30 | 31 | 2. Install/update your vendors: 32 | 33 | .. code-block:: bash 34 | 35 | $ curl http://getcomposer.org/installer | php 36 | $ php composer.phar install 37 | 38 | Through PHAR 39 | ------------ 40 | Download the .phar archive: 41 | 42 | * `code_coverage_extension.phar `_ 43 | 44 | Configuration 45 | ============= 46 | Activate extension in your **behat.yml** and define your routes for remote code coverage collection (relative to Mink's base URL): 47 | 48 | .. code-block:: yaml 49 | 50 | # behat.yml 51 | default: 52 | # ... 53 | extensions: 54 | VIPSoft\CodeCoverageExtension\Extension: 55 | auth: ~ 56 | create: 57 | method: POST 58 | path: / 59 | read: 60 | method: GET 61 | path: / 62 | delete: 63 | method: DELETE 64 | path: / 65 | drivers: 66 | - remote 67 | - local 68 | filter: ~ 69 | report: 70 | format: html 71 | options: 72 | target: /tmp/report 73 | 74 | Settings 75 | -------- 76 | If HTTP Authentication is required, use: 77 | 78 | .. code-block:: yaml 79 | 80 | # behat.yml 81 | default: 82 | # ... 83 | extensions: 84 | VIPSoft\CodeCoverageExtension\Extension: 85 | auth: 86 | user: your_user 87 | password: your_password 88 | ... 89 | 90 | There are two code coverage driver services. The "local" driver will 91 | collect code coverage from the PHP instance running Behat. The "remote" 92 | driver will collect code coverage for the Symfony 2 application under test 93 | on a remote web server. 94 | 95 | The report "directory" determines where the extension will write the code 96 | coverage report. 97 | 98 | Other choices for report "format" include "clover", "crap4j", "php", "text", 99 | and "xml". The "options" vary and correspond to the __construct() and process() 100 | arguments of the underlying PHP_CodeCoverage report class. 101 | 102 | The default "filter" includes everything / excludes nothing. Using a 103 | PHPUnit configuration as an example: 104 | 105 | .. code-block:: xml 106 | 107 | 108 | 109 | src 110 | 111 | src/*/*/Tests 112 | 113 | 114 | 115 | 116 | would be configured in YAML as: 117 | 118 | .. code-block:: yaml 119 | 120 | filter: 121 | whitelist: 122 | addUncoveredFilesFromWhitelist: true 123 | include: 124 | directories: 125 | 'src': 126 | suffix: .php 127 | exclude: 128 | directories: 129 | 'src/*/*/Tests': ~ 130 | 131 | Limitations 132 | ----------- 133 | Multiple web servers (e.g., clusters or distributed testing environments) are not 134 | currently supported because the Code Coverage Bundle uses a SQLite database. 135 | 136 | Source 137 | ====== 138 | `Github `_ 139 | 140 | Copyright 141 | ========= 142 | Copyright (c) 2013 Anthon Pang. See **LICENSE** for details. 143 | 144 | Contributors 145 | ============ 146 | * Anthon Pang `(robocoder) `_ 147 | * `Others `_ 148 | -------------------------------------------------------------------------------- /init.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | spl_autoload_register(function($class) { 13 | if (false !== strpos($class, 'VIPSoft\\CodeCoverageExtension')) { 14 | require_once(__DIR__.'/src/'.str_replace('\\', '/', $class).'.php'); 15 | 16 | return true; 17 | } 18 | }, true, false); 19 | 20 | return new VIPSoft\CodeCoverageExtension\Extension; 21 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | 17 | tests 18 | 19 | 20 | 21 | 22 | 23 | src 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/VIPSoft/CodeCoverageExtension/Compiler/DriverPass.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class DriverPass implements CompilerPassInterface 21 | { 22 | /** 23 | * {@inheritdoc} 24 | */ 25 | public function process(ContainerBuilder $container) 26 | { 27 | if (! $container->hasDefinition('behat.code_coverage.driver.proxy')) { 28 | return; 29 | } 30 | 31 | $proxy = $container->getDefinition('behat.code_coverage.driver.proxy'); 32 | $enabled = $container->getParameter('behat.code_coverage.config.drivers'); 33 | 34 | foreach ($container->findTaggedServiceIds('behat.code_coverage.driver') as $id => $tagAttributes) { 35 | foreach ($tagAttributes as $attributes) { 36 | if (isset($attributes['alias']) 37 | && in_array($attributes['alias'], $enabled) 38 | ) { 39 | $proxy->addMethodCall('addDriver', array(new Reference($id))); 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/VIPSoft/CodeCoverageExtension/Compiler/FactoryPass.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class FactoryPass implements CompilerPassInterface 21 | { 22 | /** 23 | * {@inheritdoc} 24 | */ 25 | public function process(ContainerBuilder $container) 26 | { 27 | if (! $container->hasDefinition('vipsoft.code_coverage.driver.factory')) { 28 | return; 29 | } 30 | 31 | $factory = $container->getDefinition('vipsoft.code_coverage.driver.factory'); 32 | $drivers = array(); 33 | $ids = $container->findTaggedServiceIds('vipsoft.code_coverage.driver'); 34 | 35 | foreach ($ids as $id => $attributes) { 36 | $drivers[] = $container->getDefinition($id)->getClass(); 37 | } 38 | 39 | $factory->setArguments(array($drivers)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/VIPSoft/CodeCoverageExtension/Compiler/FilterPass.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class FilterPass implements CompilerPassInterface 21 | { 22 | /** 23 | * {@inheritdoc} 24 | */ 25 | public function process(ContainerBuilder $container) 26 | { 27 | $this->processCodeCoverage($container); 28 | $this->processCodeCoverageFilter($container); 29 | } 30 | 31 | private function processCodeCoverage(ContainerBuilder $container) 32 | { 33 | if (! $container->hasDefinition('behat.code_coverage.php_code_coverage')) { 34 | return; 35 | } 36 | 37 | $coverage = $container->getDefinition('behat.code_coverage.php_code_coverage'); 38 | $config = $container->getParameter('behat.code_coverage.config.filter'); 39 | 40 | $coverage->addMethodCall( 41 | 'setAddUncoveredFilesFromWhitelist', 42 | array($config['whitelist']['addUncoveredFilesFromWhitelist']) 43 | ); 44 | $coverage->addMethodCall( 45 | 'setProcessUncoveredFilesFromWhiteList', 46 | array($config['whitelist']['processUncoveredFilesFromWhitelist']) 47 | ); 48 | $coverage->addMethodCall( 49 | 'setForceCoversAnnotation', 50 | array($config['forceCoversAnnotation']) 51 | ); 52 | $coverage->addMethodCall( 53 | 'setMapTestClassNameToCoveredClassName', 54 | array($config['mapTestClassNameToCoveredClassName']) 55 | ); 56 | } 57 | 58 | private function processCodeCoverageFilter(ContainerBuilder $container) 59 | { 60 | if (! $container->hasDefinition('behat.code_coverage.php_code_coverage_filter')) { 61 | return; 62 | } 63 | 64 | $filter = $container->getDefinition('behat.code_coverage.php_code_coverage_filter'); 65 | $config = $container->getParameter('behat.code_coverage.config.filter'); 66 | 67 | $dirs = array( 68 | 'addDirectoryToBlackList' => array('blacklist', 'include', 'directories'), 69 | 'removeDirectoryFromBlackList' => array('blacklist', 'exclude', 'directories'), 70 | 'addDirectoryToWhiteList' => array('whitelist', 'include', 'directories'), 71 | 'removeDirectoryFromWhiteList' => array('whitelist', 'exclude', 'directories'), 72 | ); 73 | 74 | foreach ($dirs as $method => $hiera) { 75 | foreach ($config[$hiera[0]][$hiera[1]][$hiera[2]] as $path => $dir) { 76 | $filter->addMethodCall($method, array($path, $dir['suffix'], $dir['prefix'])); 77 | } 78 | } 79 | 80 | $files = array( 81 | 'addFileToBlackList' => array('blacklist', 'include', 'files'), 82 | 'removeFileFromBlackList' => array('blacklist', 'exclude', 'files'), 83 | 'addFileToWhiteList' => array('whitelist', 'include', 'files'), 84 | 'removeFileFromWhiteList' => array('whitelist', 'exclude', 'files'), 85 | ); 86 | 87 | foreach ($files as $method => $hiera) { 88 | foreach ($config[$hiera[0]][$hiera[1]][$hiera[2]] as $file) { 89 | $filter->addMethodCall($method, array($file)); 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/VIPSoft/CodeCoverageExtension/Driver/Proxy.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class Proxy implements DriverInterface 21 | { 22 | /** 23 | * @var array 24 | */ 25 | private $drivers = array(); 26 | 27 | /** 28 | * Register driver 29 | * 30 | * @param DriverInterface|null $driver 31 | */ 32 | public function addDriver(DriverInterface $driver = null) 33 | { 34 | if ($driver) { 35 | $this->drivers[] = $driver; 36 | } 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public function start() 43 | { 44 | foreach ($this->drivers as $driver) { 45 | $driver->start(); 46 | } 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function stop() 53 | { 54 | $aggregate = new Aggregate; 55 | 56 | foreach ($this->drivers as $driver) { 57 | $coverage = $driver->stop(); 58 | 59 | if (! $coverage) { 60 | continue; 61 | } 62 | 63 | foreach ($coverage as $class => $counts) { 64 | $aggregate->update($class, $counts); 65 | } 66 | } 67 | 68 | return $aggregate->getCoverage(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/VIPSoft/CodeCoverageExtension/Driver/RemoteXdebug.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class RemoteXdebug implements DriverInterface 20 | { 21 | /** 22 | * @var array 23 | */ 24 | private $config; 25 | 26 | /** 27 | * @var \Guzzle\Http\Client 28 | */ 29 | private $client; 30 | 31 | /** 32 | * Constructor 33 | * 34 | * [ 35 | * 'base_url' => 'http://api.example.com/1.0/coverage', 36 | * 'auth' => [ 37 | * 'user' => 'user name', 38 | * 'password' => 'password', 39 | * ], 40 | * 'create' => [ 41 | * 'method' => 'POST', 42 | * 'path' => '/', 43 | * ], 44 | * 'read' => [ 45 | * 'method' => 'GET', 46 | * 'path' => '/', 47 | * ], 48 | * 'delete' => [ 49 | * 'method' => 'DELETE', 50 | * 'path' => '/', 51 | * ], 52 | * ] 53 | * 54 | * @param array $config Configuration 55 | * @param \Guzzle\Http\Client $client HTTP client 56 | */ 57 | public function __construct(array $config, Client $client) 58 | { 59 | $this->config = $config; 60 | 61 | $this->client = $client; 62 | $this->client->setBaseUrl($config['base_url']); 63 | } 64 | 65 | /** 66 | * {@inheritdoc} 67 | */ 68 | public function start() 69 | { 70 | $request = $this->buildRequest('create'); 71 | 72 | $response = $request->send(); 73 | 74 | if ($response->getStatusCode() !== 200) { 75 | throw new \Exception('remote driver start failed: ' . $response->getReasonPhrase()); 76 | } 77 | } 78 | 79 | /** 80 | * {@inheritdoc} 81 | */ 82 | public function stop() 83 | { 84 | $request = $this->buildRequest('read'); 85 | $request->setHeader('Accept', 'application/json'); 86 | 87 | $response = $request->send(); 88 | 89 | if ($response->getStatusCode() !== 200) { 90 | throw new \Exception('remote driver fetch failed: ' . $response->getReasonPhrase()); 91 | } 92 | 93 | $request = $this->buildRequest('delete'); 94 | $request->send(); 95 | 96 | return json_decode($response->getBody(true), true); 97 | } 98 | 99 | /** 100 | * Construct request 101 | * 102 | * @param string $endpoint 103 | * 104 | * @return \Guzzle\Http\Message\Request 105 | */ 106 | private function buildRequest($endpoint) 107 | { 108 | $method = strtolower($this->config[$endpoint]['method']); 109 | 110 | if (! in_array($method, array('get', 'post', 'put', 'delete'))) { 111 | throw new \Exception($endpoint . ' method must be GET, POST, PUT, or DELETE'); 112 | } 113 | 114 | $request = $this->client->$method($this->config[$endpoint]['path']); 115 | 116 | if (isset($this->config['auth'])) { 117 | $request->setAuth($this->config['auth']['user'], $this->config['auth']['password']); 118 | } 119 | 120 | return $request; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/VIPSoft/CodeCoverageExtension/Extension.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | class Extension implements ExtensionInterface 25 | { 26 | /** 27 | * @var string 28 | */ 29 | private $configFolder; 30 | 31 | /** 32 | * Constructor 33 | * 34 | * @param string $configFolder 35 | */ 36 | public function __construct($configFolder = null) 37 | { 38 | $this->configFolder = $configFolder ?: __DIR__ . '/Resources/config'; 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function initialize(ExtensionManager $extensionManager) 45 | { 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function load(ContainerBuilder $container, array $config) 52 | { 53 | $loader = new XmlFileLoader($container, new FileLocator($this->configFolder)); 54 | $loader->load('services.xml'); 55 | 56 | if (! isset($config['auth']['user']) || ! isset($config['auth']['password'])) { 57 | $config['auth'] = null; 58 | } 59 | 60 | if (! count($config['drivers'])) { 61 | $config['drivers'] = array('remote', 'local'); 62 | } 63 | 64 | if (! count($config['report']['options'])) { 65 | $config['report']['options'] = array( 66 | 'target' => '/tmp' 67 | ); 68 | } 69 | 70 | if (! $container->hasParameter('mink.base_url')) { 71 | $container->setParameter('mink.base_url', null); 72 | } 73 | 74 | $container->setParameter('behat.code_coverage.config.auth', $config['auth']); 75 | $container->setParameter('behat.code_coverage.config.create', $config['create']); 76 | $container->setParameter('behat.code_coverage.config.read', $config['read']); 77 | $container->setParameter('behat.code_coverage.config.delete', $config['delete']); 78 | $container->setParameter('behat.code_coverage.config.drivers', $config['drivers']); 79 | $container->setParameter('behat.code_coverage.config.filter', $config['filter']); 80 | $container->setParameter('behat.code_coverage.config.report', $config['report']); 81 | } 82 | 83 | /** 84 | * {@inheritdoc} 85 | */ 86 | public function configure(ArrayNodeDefinition $builder) 87 | { 88 | $builder 89 | ->addDefaultsIfNotSet() 90 | ->children() 91 | ->arrayNode('auth') 92 | ->children() 93 | ->scalarNode('user')->end() 94 | ->scalarNode('password')->end() 95 | ->end() 96 | ->end() 97 | ->arrayNode('create') 98 | ->addDefaultsIfNotSet() 99 | ->children() 100 | ->scalarNode('method')->defaultValue('POST')->end() 101 | ->scalarNode('path')->defaultValue('/')->end() 102 | ->end() 103 | ->end() 104 | ->arrayNode('read') 105 | ->addDefaultsIfNotSet() 106 | ->children() 107 | ->scalarNode('method')->defaultValue('GET')->end() 108 | ->scalarNode('path')->defaultValue('/')->end() 109 | ->end() 110 | ->end() 111 | ->arrayNode('delete') 112 | ->addDefaultsIfNotSet() 113 | ->children() 114 | ->scalarNode('method')->defaultValue('DELETE')->end() 115 | ->scalarNode('path')->defaultValue('/')->end() 116 | ->end() 117 | ->end() 118 | ->arrayNode('drivers') 119 | ->prototype('scalar')->end() 120 | ->end() 121 | ->arrayNode('filter') 122 | ->addDefaultsIfNotSet() 123 | ->children() 124 | ->scalarNode('forceCoversAnnotation') 125 | ->defaultFalse() 126 | ->end() 127 | ->scalarNode('mapTestClassNameToCoveredClassName') 128 | ->defaultFalse() 129 | ->end() 130 | ->arrayNode('whitelist') 131 | ->addDefaultsIfNotSet() 132 | ->children() 133 | ->scalarNode('addUncoveredFilesFromWhitelist') 134 | ->defaultTrue() 135 | ->end() 136 | ->scalarNode('processUncoveredFilesFromWhitelist') 137 | ->defaultFalse() 138 | ->end() 139 | ->arrayNode('include') 140 | ->addDefaultsIfNotSet() 141 | ->children() 142 | ->arrayNode('directories') 143 | ->useAttributeAsKey('name') 144 | ->prototype('array') 145 | ->children() 146 | ->scalarNode('prefix')->defaultValue('')->end() 147 | ->scalarNode('suffix')->defaultValue('.php')->end() 148 | ->end() 149 | ->end() 150 | ->end() 151 | ->arrayNode('files') 152 | ->prototype('scalar')->end() 153 | ->end() 154 | ->end() 155 | ->end() 156 | ->arrayNode('exclude') 157 | ->addDefaultsIfNotSet() 158 | ->children() 159 | ->arrayNode('directories') 160 | ->useAttributeAsKey('name') 161 | ->prototype('array') 162 | ->children() 163 | ->scalarNode('prefix')->defaultValue('')->end() 164 | ->scalarNode('suffix')->defaultValue('.php')->end() 165 | ->end() 166 | ->end() 167 | ->end() 168 | ->arrayNode('files') 169 | ->prototype('scalar')->end() 170 | ->end() 171 | ->end() 172 | ->end() 173 | ->end() 174 | ->end() 175 | ->arrayNode('blacklist') 176 | ->addDefaultsIfNotSet() 177 | ->children() 178 | ->arrayNode('include') 179 | ->addDefaultsIfNotSet() 180 | ->children() 181 | ->arrayNode('directories') 182 | ->useAttributeAsKey('name') 183 | ->prototype('array') 184 | ->children() 185 | ->scalarNode('prefix')->defaultValue('')->end() 186 | ->scalarNode('suffix')->defaultValue('.php')->end() 187 | ->end() 188 | ->end() 189 | ->end() 190 | ->arrayNode('files') 191 | ->prototype('scalar')->end() 192 | ->end() 193 | ->end() 194 | ->end() 195 | ->arrayNode('exclude') 196 | ->addDefaultsIfNotSet() 197 | ->children() 198 | ->arrayNode('directories') 199 | ->useAttributeAsKey('name') 200 | ->prototype('array') 201 | ->children() 202 | ->scalarNode('prefix')->defaultValue('')->end() 203 | ->scalarNode('suffix')->defaultValue('.php')->end() 204 | ->end() 205 | ->end() 206 | ->end() 207 | ->arrayNode('files') 208 | ->prototype('scalar')->end() 209 | ->end() 210 | ->end() 211 | ->end() 212 | ->end() 213 | ->end() 214 | ->end() 215 | ->end() 216 | ->arrayNode('report') 217 | ->children() 218 | ->scalarNode('format')->defaultValue('html')->end() 219 | ->arrayNode('options') 220 | ->useAttributeAsKey('name') 221 | ->prototype('scalar')->end() 222 | ->end() 223 | ->end() 224 | ->end() 225 | ->end() 226 | ->end(); 227 | } 228 | 229 | /** 230 | * {@inheritdoc} 231 | */ 232 | public function getConfigKey() 233 | { 234 | return 'code_coverage'; 235 | } 236 | 237 | /** 238 | * {@inheritdoc} 239 | */ 240 | public function process(ContainerBuilder $container) 241 | { 242 | $passes = $this->getCompilerPasses(); 243 | 244 | foreach ($passes as $pass) { 245 | $pass->process($container); 246 | } 247 | } 248 | 249 | private function getCompilerPasses() 250 | { 251 | return array( 252 | new Compiler\DriverPass(), 253 | new Compiler\FactoryPass(), 254 | new Compiler\FilterPass(), 255 | ); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/VIPSoft/CodeCoverageExtension/Listener/EventListener.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class EventListener implements EventSubscriberInterface 24 | { 25 | /** 26 | * @var \PHP_CodeCoverage 27 | */ 28 | private $coverage; 29 | 30 | /** 31 | * @var \VIPSoft\CodeCoverageExtension\Service\ReportService 32 | */ 33 | private $reportService; 34 | 35 | /** 36 | * Constructor 37 | * 38 | * @param \PHP_CodeCoverage $coverage 39 | * @param \VIPSoft\CodeCoverageExtension\Service\ReportService $reportService 40 | */ 41 | public function __construct(\PHP_CodeCoverage $coverage, ReportService $reportService) 42 | { 43 | $this->coverage = $coverage; 44 | $this->reportService = $reportService; 45 | } 46 | 47 | /** 48 | * {@inheritdoc} 49 | */ 50 | public static function getSubscribedEvents() 51 | { 52 | return array( 53 | ExerciseCompleted::BEFORE => 'beforeExercise', 54 | ScenarioTested::BEFORE => 'beforeScenario', 55 | ExampleTested::BEFORE => 'beforeScenario', 56 | ScenarioTested::AFTER => 'afterScenario', 57 | ExampleTested::AFTER => 'afterScenario', 58 | ExerciseCompleted::AFTER => 'afterExercise', 59 | ); 60 | } 61 | 62 | /** 63 | * Before Exercise hook 64 | * 65 | * @param \Behat\Testwork\EventDispatcher\Event\ExerciseCompleted $event 66 | */ 67 | public function beforeExercise(ExerciseCompleted $event) 68 | { 69 | $this->coverage->clear(); 70 | } 71 | 72 | /** 73 | * Before Scenario/Outline Example hook 74 | * 75 | * @param \Behat\Behat\EventDispatcher\Event\ScenarioTested $event 76 | */ 77 | public function beforeScenario(ScenarioTested $event) 78 | { 79 | $node = $event->getScenario(); 80 | $id = $event->getFeature()->getFile() . ':' . $node->getLine(); 81 | 82 | $this->coverage->start($id); 83 | } 84 | 85 | /** 86 | * After Scenario/Outline Example hook 87 | * 88 | * @param \Behat\Behat\EventDispatcher\Event\ScenarioTested $event 89 | */ 90 | public function afterScenario(ScenarioTested $event) 91 | { 92 | $this->coverage->stop(); 93 | } 94 | 95 | /** 96 | * After Exercise hook 97 | * 98 | * @param \Behat\Testwork\Tester\Event\ExerciseCompleted $event 99 | */ 100 | public function afterExercise(ExerciseCompleted $event) 101 | { 102 | $this->reportService->generateReport($this->coverage); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/VIPSoft/CodeCoverageExtension/Resources/config/services.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | VIPSoft\CodeCoverageExtension\Driver\Proxy 8 | VIPSoft\CodeCoverageExtension\Driver\RemoteXdebug 9 | VIPSoft\CodeCoverageExtension\Listener\EventListener 10 | VIPSoft\CodeCoverageExtension\Service\ReportService 11 | VIPSoft\CodeCoverageCommon\Report\Factory 12 | 13 | 14 | %mink.base_url% 15 | %behat.code_coverage.config.auth% 16 | %behat.code_coverage.config.create% 17 | %behat.code_coverage.config.read% 18 | %behat.code_coverage.config.delete% 19 | %behat.code_coverage.config.drivers% 20 | %behat.code_coverage.config.filter% 21 | %behat.code_coverage.config.report% 22 | 23 | 24 | VIPSoft\CodeCoverageCommon\Driver\HHVM 25 | VIPSoft\CodeCoverageCommon\Driver\XCache 26 | PHP_CodeCoverage_Driver_Xdebug 27 | VIPSoft\CodeCoverageCommon\Driver\Factory 28 | 29 | 30 | 31 | 34 | 35 | 36 | 39 | 40 | 41 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 67 | 68 | 69 | 70 | 71 | %behat.code_coverage.config% 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | %behat.code_coverage.config% 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/VIPSoft/CodeCoverageExtension/Service/ReportService.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class ReportService 19 | { 20 | /** 21 | * @var array 22 | */ 23 | private $config; 24 | 25 | /** 26 | * @var \VIPSoft\CodeCoverageCommon\Report\Factory 27 | */ 28 | private $factory; 29 | 30 | /** 31 | * Constructor 32 | * 33 | * @param array $config 34 | * @param \VIPSoft\CodeCoverageCommon\Report\Factory $factory 35 | */ 36 | public function __construct(array $config, Factory $factory) 37 | { 38 | $this->config = $config; 39 | $this->factory = $factory; 40 | } 41 | 42 | /** 43 | * Generate report 44 | * 45 | * @param \PHP_CodeCoverage $coverage 46 | */ 47 | public function generateReport(\PHP_CodeCoverage $coverage) 48 | { 49 | $format = $this->config['report']['format']; 50 | $options = $this->config['report']['options']; 51 | 52 | $report = $this->factory->create($format, $options); 53 | $report->process($coverage); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/VIPSoft/CodeCoverageExtension/Compiler/DriverPassTest.php: -------------------------------------------------------------------------------- 1 | getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); 24 | $container->expects($this->once()) 25 | ->method('hasDefinition') 26 | ->will($this->returnValue(false)); 27 | 28 | $pass = new DriverPass(); 29 | $pass->process($container); 30 | } 31 | 32 | public function testProcess() 33 | { 34 | $proxy = $this->getMock('Symfony\Component\DependencyInjection\Definition'); 35 | $proxy->expects($this->exactly(2)) 36 | ->method('addMethodCall'); 37 | 38 | $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); 39 | $container->expects($this->once()) 40 | ->method('hasDefinition') 41 | ->with('behat.code_coverage.driver.proxy') 42 | ->will($this->returnValue(true)); 43 | 44 | $container->expects($this->once()) 45 | ->method('getDefinition') 46 | ->with('behat.code_coverage.driver.proxy') 47 | ->will($this->returnValue($proxy)); 48 | 49 | $container->expects($this->once()) 50 | ->method('getParameter') 51 | ->with('behat.code_coverage.config.drivers') 52 | ->will($this->returnValue(array('local', 'remote'))); 53 | 54 | $container->expects($this->once()) 55 | ->method('findTaggedServiceIds') 56 | ->with('behat.code_coverage.driver') 57 | ->will($this->returnValue(array( 58 | 'behat.code_coverage.driver.local' => array(array( 59 | 'alias' => 'local' 60 | )), 61 | 'behat.code_coverage.driver.remote' => array(array( 62 | 'alias' => 'remote' 63 | )), 64 | ))); 65 | 66 | $pass = new DriverPass(); 67 | $pass->process($container); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/VIPSoft/CodeCoverageExtension/Compiler/FactoryPassTest.php: -------------------------------------------------------------------------------- 1 | getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); 24 | $container->expects($this->once()) 25 | ->method('hasDefinition') 26 | ->will($this->returnValue(false)); 27 | 28 | $pass = new FactoryPass(); 29 | $pass->process($container); 30 | } 31 | 32 | public function testProcess() 33 | { 34 | $factory = $this->getMock('Symfony\Component\DependencyInjection\Definition'); 35 | $factory->expects($this->once()) 36 | ->method('setArguments'); 37 | 38 | $xcache = $this->getMock('Symfony\Component\DependencyInjection\Definition'); 39 | $xcache->expects($this->once()) 40 | ->method('getClass') 41 | ->will($this->returnValue('VIPSoft\CodeCoverageCommon\Driver\XCache')); 42 | 43 | $xdebug = $this->getMock('Symfony\Component\DependencyInjection\Definition'); 44 | $xdebug->expects($this->once()) 45 | ->method('getClass') 46 | ->will($this->returnValue('PHP_CodeCoverage_Driver_Xdebug')); 47 | 48 | $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); 49 | 50 | $container->expects($this->at(0)) 51 | ->method('hasDefinition') 52 | ->with('vipsoft.code_coverage.driver.factory') 53 | ->will($this->returnValue(true)); 54 | 55 | $container->expects($this->at(1)) 56 | ->method('getDefinition') 57 | ->with('vipsoft.code_coverage.driver.factory') 58 | ->will($this->returnValue($factory)); 59 | 60 | $container->expects($this->at(2)) 61 | ->method('findTaggedServiceIds') 62 | ->with('vipsoft.code_coverage.driver') 63 | ->will($this->returnValue(array( 64 | 'vipsoft.code_coverage.driver.xcache' => array(), 65 | 'vipsoft.code_coverage.driver.xdebug' => array(), 66 | ))); 67 | 68 | $container->expects($this->at(3)) 69 | ->method('getDefinition') 70 | ->with('vipsoft.code_coverage.driver.xcache') 71 | ->will($this->returnValue($xcache)); 72 | 73 | $container->expects($this->at(4)) 74 | ->method('getDefinition') 75 | ->with('vipsoft.code_coverage.driver.xdebug') 76 | ->will($this->returnValue($xdebug)); 77 | 78 | $pass = new FactoryPass(); 79 | $pass->process($container); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /tests/VIPSoft/CodeCoverageExtension/Compiler/FilterPassTest.php: -------------------------------------------------------------------------------- 1 | getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); 24 | $container->expects($this->exactly(2)) 25 | ->method('hasDefinition') 26 | ->will($this->returnValue(false)); 27 | 28 | $pass = new FilterPass(); 29 | $pass->process($container); 30 | } 31 | 32 | public function testProcessCodeCoverage() 33 | { 34 | $coverage = $this->getMock('Symfony\Component\DependencyInjection\Definition'); 35 | $coverage->expects($this->exactly(4)) 36 | ->method('addMethodCall'); 37 | 38 | $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); 39 | $container->expects($this->exactly(2)) 40 | ->method('hasDefinition') 41 | ->will($this->onConsecutiveCalls(true, false)); 42 | 43 | $container->expects($this->once()) 44 | ->method('getDefinition') 45 | ->with('behat.code_coverage.php_code_coverage') 46 | ->will($this->returnValue($coverage)); 47 | 48 | $container->expects($this->once()) 49 | ->method('getParameter') 50 | ->with('behat.code_coverage.config.filter') 51 | ->will($this->returnValue(array( 52 | 'whitelist' => array( 53 | 'addUncoveredFilesFromWhitelist' => false, 54 | 'processUncoveredFilesFromWhitelist' => true 55 | ), 56 | 'forceCoversAnnotation' => false, 57 | 'mapTestClassNameToCoveredClassName' => true 58 | ))); 59 | 60 | $pass = new FilterPass(); 61 | $pass->process($container); 62 | } 63 | 64 | public function testProcessCodeCoverageFilter() 65 | { 66 | $filter = $this->getMock('Symfony\Component\DependencyInjection\Definition'); 67 | $filter->expects($this->exactly(8)) 68 | ->method('addMethodCall'); 69 | 70 | $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); 71 | $container->expects($this->exactly(2)) 72 | ->method('hasDefinition') 73 | ->will($this->onConsecutiveCalls(false, true)); 74 | 75 | $container->expects($this->once()) 76 | ->method('getDefinition') 77 | ->with('behat.code_coverage.php_code_coverage_filter') 78 | ->will($this->returnValue($filter)); 79 | 80 | $container->expects($this->once()) 81 | ->method('getParameter') 82 | ->with('behat.code_coverage.config.filter') 83 | ->will($this->returnValue(array( 84 | 'whitelist' => array( 85 | 'addUncoveredFilesFromWhitelist' => false, 86 | 'processUncoveredFilesFromWhitelist' => true, 87 | 'include' => array( 88 | 'directories' => array( 89 | 'directory1' => array( 90 | 'prefix' => 'Secure', 91 | 'suffix' => '.php', 92 | ) 93 | ), 94 | 'files' => array( 95 | 'file1' 96 | ), 97 | ), 98 | 'exclude' => array( 99 | 'directories' => array( 100 | 'directory2' => array( 101 | 'prefix' => 'Insecure', 102 | 'suffix' => '.inc', 103 | ) 104 | ), 105 | 'files' => array( 106 | 'file2' 107 | ), 108 | ), 109 | ), 110 | 'blacklist' => array( 111 | 'include' => array( 112 | 'directories' => array( 113 | 'directory3' => array( 114 | 'prefix' => 'Public', 115 | 'suffix' => '.php', 116 | ) 117 | ), 118 | 'files' => array( 119 | 'file3' 120 | ), 121 | ), 122 | 'exclude' => array( 123 | 'directories' => array( 124 | 'directory4' => array( 125 | 'prefix' => 'Private', 126 | 'suffix' => '.inc', 127 | ) 128 | ), 129 | 'files' => array( 130 | 'file4' 131 | ), 132 | ), 133 | ), 134 | ))); 135 | 136 | $pass = new FilterPass(); 137 | $pass->process($container); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /tests/VIPSoft/CodeCoverageExtension/Driver/ProxyTest.php: -------------------------------------------------------------------------------- 1 | localDriver = $this->getMock('VIPSoft\CodeCoverageCommon\Driver\Stub'); 30 | 31 | $this->remoteDriver = $this->getMockBuilder('VIPSoft\CodeCoverageExtension\Driver\RemoteXdebug') 32 | ->disableOriginalConstructor() 33 | ->getMock(); 34 | 35 | $this->driver = new Proxy(); 36 | $this->driver->addDriver($this->localDriver); 37 | $this->driver->addDriver($this->remoteDriver); 38 | } 39 | 40 | public function testStart() 41 | { 42 | $this->localDriver->expects($this->once()) 43 | ->method('start'); 44 | 45 | $this->remoteDriver->expects($this->once()) 46 | ->method('start'); 47 | 48 | $this->driver->start(); 49 | } 50 | 51 | public function testStop() 52 | { 53 | $coverage = array( 54 | 'SomeClass' => array( 55 | 1 => 1, 56 | 2 => -1, 57 | 3 => -2, 58 | ) 59 | ); 60 | 61 | $this->localDriver->expects($this->once()) 62 | ->method('stop') 63 | ->will($this->returnValue($coverage)); 64 | 65 | $this->remoteDriver->expects($this->once()) 66 | ->method('stop') 67 | ->will($this->returnValue(null)); 68 | 69 | $coverage = $this->driver->stop(); 70 | 71 | $this->assertEquals( 72 | array( 73 | 'SomeClass' => array( 74 | 1 => 1, 75 | 2 => -1, 76 | 3 => -2, 77 | ) 78 | ), 79 | $coverage 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/VIPSoft/CodeCoverageExtension/Driver/RemoteXdebugTest.php: -------------------------------------------------------------------------------- 1 | config = array( 32 | 'base_url' => 'http://localhost', 33 | 'auth' => array( 34 | 'user' => 'user name', 35 | 'password' => 'password', 36 | ), 37 | 'create' => array( 38 | 'method' => 'POST', 39 | 'path' => '/', 40 | ), 41 | 'read' => array( 42 | 'method' => 'GET', 43 | 'path' => '/', 44 | ), 45 | 'delete' => array( 46 | 'method' => 'DELETE', 47 | 'path' => '/', 48 | ), 49 | ); 50 | 51 | $this->response = $this->getMockBuilder('Guzzle\Http\Message\Response') 52 | ->disableOriginalConstructor() 53 | ->getMock(); 54 | 55 | $request = $this->getMockBuilder('Guzzle\Http\Message\Request') 56 | ->disableOriginalConstructor() 57 | ->getMock(); 58 | 59 | $request->expects($this->any()) 60 | ->method('send') 61 | ->will($this->returnValue($this->response)); 62 | 63 | $this->client = $this->getMock('Guzzle\Http\Client'); 64 | $this->client->expects($this->any()) 65 | ->method('post') 66 | ->will($this->returnValue($request)); 67 | $this->client->expects($this->any()) 68 | ->method('put') 69 | ->will($this->returnValue($request)); 70 | $this->client->expects($this->any()) 71 | ->method('get') 72 | ->will($this->returnValue($request)); 73 | $this->client->expects($this->any()) 74 | ->method('delete') 75 | ->will($this->returnValue($request)); 76 | } 77 | 78 | public function testInvalidMethodException() 79 | { 80 | try { 81 | $this->config['create']['method'] = 'TRACE'; 82 | 83 | $driver = new RemoteXdebug($this->config, $this->client); 84 | $driver->start(); 85 | 86 | $this->fail(); 87 | } catch (\Exception $e) { 88 | $this->assertTrue(strpos($e->getMessage(), 'method must be GET, POST, PUT, or DELETE') !== false); 89 | } 90 | } 91 | 92 | public function testStart() 93 | { 94 | $this->response->expects($this->once()) 95 | ->method('getStatusCode') 96 | ->will($this->returnValue(200)); 97 | 98 | $driver = new RemoteXdebug($this->config, $this->client); 99 | $driver->start(); 100 | } 101 | 102 | public function testStartException() 103 | { 104 | try { 105 | $driver = new RemoteXdebug($this->config, $this->client); 106 | $driver->start(); 107 | 108 | $this->fail(); 109 | } catch (\Exception $e) { 110 | $this->assertTrue(strpos($e->getMessage(), 'remote driver start failed: ') === 0); 111 | } 112 | } 113 | 114 | public function testStop() 115 | { 116 | $this->response->expects($this->once()) 117 | ->method('getStatusCode') 118 | ->will($this->returnValue(200)); 119 | 120 | $driver = new RemoteXdebug($this->config, $this->client); 121 | $coverage = $driver->stop(); 122 | } 123 | 124 | public function testStopException() 125 | { 126 | try { 127 | $driver = new RemoteXdebug($this->config, $this->client); 128 | 129 | $coverage = $driver->stop(); 130 | 131 | $this->fail(); 132 | } catch (\Exception $e) { 133 | $this->assertTrue(strpos($e->getMessage(), 'remote driver fetch failed: ') === 0); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tests/VIPSoft/CodeCoverageExtension/ExtensionTest.php: -------------------------------------------------------------------------------- 1 | 34 | 37 | 38 | 39 | VIPSoft\CodeCoverageExtension\Service\ReportService 40 | 41 | 42 | 43 | 44 | 45 | 46 | END_OF_CONFIG 47 | ); 48 | 49 | $container = new ContainerBuilder; 50 | 51 | $extension = new Extension($configDir); 52 | $extension->load($container, $config); 53 | 54 | foreach ($expected as $key => $value) { 55 | $this->assertEquals($value, $container->getParameter($key)); 56 | } 57 | } 58 | 59 | /** 60 | * @return array 61 | */ 62 | public function loadProvider() 63 | { 64 | return array( 65 | array( 66 | array( 67 | 'behat.code_coverage.config.auth' => array( 68 | 'user' => 'test_user', 69 | 'password' => 'test_password', 70 | ), 71 | 'behat.code_coverage.config.create' => array( 72 | 'method' => 'CREATE', 73 | 'path' => 'create_path', 74 | ), 75 | 'behat.code_coverage.config.read' => array( 76 | 'method' => 'READ', 77 | 'path' => 'read_path', 78 | ), 79 | 'behat.code_coverage.config.delete' => array( 80 | 'method' => 'DELETE', 81 | 'path' => 'delete_path', 82 | ), 83 | 'behat.code_coverage.config.drivers' => array('remote'), 84 | 'behat.code_coverage.config.filter' => array( 85 | 'whitelist' => array( 86 | 'addUncoveredFilesFromWhitelist' => false, 87 | 'processUncoveredFilesFromWhitelist' => true, 88 | 'include' => array( 89 | 'directories' => array( 90 | 'directory1' => array( 91 | 'prefix' => 'Secure', 92 | 'suffix' => '.php', 93 | ) 94 | ), 95 | 'files' => array( 96 | 'file1' 97 | ), 98 | ), 99 | 'exclude' => array( 100 | 'directories' => array( 101 | 'directory2' => array( 102 | 'prefix' => 'Insecure', 103 | 'suffix' => '.inc', 104 | ) 105 | ), 106 | 'files' => array( 107 | 'file2' 108 | ), 109 | ), 110 | ), 111 | 'blacklist' => array( 112 | 'include' => array( 113 | 'directories' => array( 114 | 'directory3' => array( 115 | 'prefix' => 'Public', 116 | 'suffix' => '.php', 117 | ) 118 | ), 119 | 'files' => array( 120 | 'file3' 121 | ), 122 | ), 123 | 'exclude' => array( 124 | 'directories' => array( 125 | 'directory4' => array( 126 | 'prefix' => 'Private', 127 | 'suffix' => '.inc', 128 | ) 129 | ), 130 | 'files' => array( 131 | 'file4' 132 | ), 133 | ), 134 | ), 135 | 'forceCoversAnnotation' => true, 136 | 'mapTestClassNameToCoveredClassName' => true 137 | ), 138 | 'behat.code_coverage.config.report' => array( 139 | 'format' => 'fmt', 140 | 'options' => array( 141 | 'target' => '/tmp' 142 | ) 143 | ) 144 | ), 145 | array( 146 | 'auth' => array( 147 | 'user' => 'test_user', 148 | 'password' => 'test_password', 149 | ), 150 | 'create' => array( 151 | 'method' => 'CREATE', 152 | 'path' => 'create_path', 153 | ), 154 | 'read' => array( 155 | 'method' => 'READ', 156 | 'path' => 'read_path', 157 | ), 158 | 'delete' => array( 159 | 'method' => 'DELETE', 160 | 'path' => 'delete_path', 161 | ), 162 | 'drivers' => array('remote'), 163 | 'filter' => array( 164 | 'whitelist' => array( 165 | 'addUncoveredFilesFromWhitelist' => false, 166 | 'processUncoveredFilesFromWhitelist' => true, 167 | 'include' => array( 168 | 'directories' => array( 169 | 'directory1' => array( 170 | 'prefix' => 'Secure', 171 | 'suffix' => '.php', 172 | ) 173 | ), 174 | 'files' => array( 175 | 'file1' 176 | ), 177 | ), 178 | 'exclude' => array( 179 | 'directories' => array( 180 | 'directory2' => array( 181 | 'prefix' => 'Insecure', 182 | 'suffix' => '.inc', 183 | ) 184 | ), 185 | 'files' => array( 186 | 'file2' 187 | ), 188 | ), 189 | ), 190 | 'blacklist' => array( 191 | 'include' => array( 192 | 'directories' => array( 193 | 'directory3' => array( 194 | 'prefix' => 'Public', 195 | 'suffix' => '.php', 196 | ) 197 | ), 198 | 'files' => array( 199 | 'file3' 200 | ), 201 | ), 202 | 'exclude' => array( 203 | 'directories' => array( 204 | 'directory4' => array( 205 | 'prefix' => 'Private', 206 | 'suffix' => '.inc', 207 | ) 208 | ), 209 | 'files' => array( 210 | 'file4' 211 | ), 212 | ), 213 | ), 214 | 'forceCoversAnnotation' => true, 215 | 'mapTestClassNameToCoveredClassName' => true 216 | ), 217 | 'report' => array( 218 | 'format' => 'fmt', 219 | 'options' => array( 220 | 'target' => '/tmp' 221 | ) 222 | ) 223 | ), 224 | ), 225 | array( 226 | array( 227 | 'behat.code_coverage.config.auth' => null, 228 | 'behat.code_coverage.config.create' => array( 229 | 'method' => 'POST', 230 | 'path' => '/', 231 | ), 232 | 'behat.code_coverage.config.read' => array( 233 | 'method' => 'GET', 234 | 'path' => '/', 235 | ), 236 | 'behat.code_coverage.config.delete' => array( 237 | 'method' => 'DELETE', 238 | 'path' => '/', 239 | ), 240 | 'behat.code_coverage.config.drivers' => array('remote', 'local'), 241 | 'behat.code_coverage.config.filter' => array( 242 | 'whitelist' => array( 243 | 'addUncoveredFilesFromWhitelist' => true, 244 | 'processUncoveredFilesFromWhitelist' => false, 245 | 'include' => array( 246 | 'directories' => array(), 247 | 'files' => array(), 248 | ), 249 | 'exclude' => array( 250 | 'directories' => array(), 251 | 'files' => array(), 252 | ), 253 | ), 254 | 'blacklist' => array( 255 | 'include' => array( 256 | 'directories' => array(), 257 | 'files' => array(), 258 | ), 259 | 'exclude' => array( 260 | 'directories' => array(), 261 | 'files' => array(), 262 | ), 263 | ), 264 | 'forceCoversAnnotation' => false, 265 | 'mapTestClassNameToCoveredClassName' => false 266 | ) 267 | ), 268 | array( 269 | 'create' => array( 270 | 'method' => 'POST', 271 | 'path' => '/', 272 | ), 273 | 'read' => array( 274 | 'method' => 'GET', 275 | 'path' => '/', 276 | ), 277 | 'delete' => array( 278 | 'method' => 'DELETE', 279 | 'path' => '/', 280 | ), 281 | 'drivers' => array(), 282 | 'filter' => array( 283 | 'whitelist' => array( 284 | 'addUncoveredFilesFromWhitelist' => true, 285 | 'processUncoveredFilesFromWhitelist' => false, 286 | 'include' => array( 287 | 'directories' => array(), 288 | 'files' => array(), 289 | ), 290 | 'exclude' => array( 291 | 'directories' => array(), 292 | 'files' => array(), 293 | ), 294 | ), 295 | 'blacklist' => array( 296 | 'include' => array( 297 | 'directories' => array(), 298 | 'files' => array(), 299 | ), 300 | 'exclude' => array( 301 | 'directories' => array(), 302 | 'files' => array(), 303 | ), 304 | ), 305 | 'forceCoversAnnotation' => false, 306 | 'mapTestClassNameToCoveredClassName' => false 307 | ), 308 | 'report' => array( 309 | 'format' => 'html', 310 | 'options' => array(), 311 | ) 312 | ), 313 | ), 314 | ); 315 | } 316 | 317 | public function testConfigure() 318 | { 319 | $builder = new ArrayNodeDefinition('test'); 320 | 321 | $extension = new Extension(); 322 | $extension->configure($builder); 323 | 324 | $children = $this->getPropertyOnObject($builder, 'children'); 325 | 326 | $this->assertCount(7, $children); 327 | $this->assertTrue(isset($children['auth'])); 328 | $this->assertTrue(isset($children['create'])); 329 | $this->assertTrue(isset($children['read'])); 330 | $this->assertTrue(isset($children['delete'])); 331 | $this->assertTrue(isset($children['drivers'])); 332 | $this->assertTrue(isset($children['filter'])); 333 | $this->assertTrue(isset($children['report'])); 334 | 335 | $auth = $this->getPropertyOnObject($children['auth'], 'children'); 336 | 337 | $this->assertCount(2, $auth); 338 | $this->assertTrue(isset($auth['user'])); 339 | $this->assertTrue(isset($auth['password'])); 340 | 341 | $create = $this->getPropertyOnObject($children['create'], 'children'); 342 | 343 | $this->assertCount(2, $create); 344 | $this->assertTrue(isset($create['method'])); 345 | $this->assertTrue(isset($create['path'])); 346 | 347 | $read = $this->getPropertyOnObject($children['read'], 'children'); 348 | 349 | $this->assertCount(2, $read); 350 | $this->assertTrue(isset($read['method'])); 351 | $this->assertTrue(isset($read['path'])); 352 | 353 | $delete = $this->getPropertyOnObject($children['delete'], 'children'); 354 | 355 | $this->assertCount(2, $delete); 356 | $this->assertTrue(isset($delete['method'])); 357 | $this->assertTrue(isset($delete['path'])); 358 | 359 | $report = $this->getPropertyOnObject($children['report'], 'children'); 360 | 361 | $this->assertCount(2, $report); 362 | $this->assertTrue(isset($report['format'])); 363 | $this->assertTrue(isset($report['options'])); 364 | } 365 | 366 | public function testProcess() 367 | { 368 | $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); 369 | $container->expects($this->exactly(4)) 370 | ->method('hasDefinition'); 371 | 372 | $extension = new Extension(); 373 | 374 | $compilerPasses = $extension->process($container); 375 | } 376 | 377 | /** 378 | * Gets the given property of an object 379 | * 380 | * @param mixed $object Object 381 | * @param string $name Property name 382 | * 383 | * @return mixed 384 | */ 385 | private function getPropertyOnObject($object, $name) 386 | { 387 | $property = new \ReflectionProperty($object, $name); 388 | $property->setAccessible(true); 389 | 390 | return $property->getValue($object); 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /tests/VIPSoft/CodeCoverageExtension/Listener/EventListenerTest.php: -------------------------------------------------------------------------------- 1 | coverage = $this->getMock('PHP_CodeCoverage'); 35 | 36 | $this->service = $this->getMockBuilder('VIPSoft\CodeCoverageExtension\Service\ReportService') 37 | ->disableOriginalConstructor() 38 | ->getMock(); 39 | } 40 | 41 | public function testGetSubscribedEvents() 42 | { 43 | $listener = new EventListener($this->coverage, $this->service); 44 | $events = $listener->getSubscribedEvents(); 45 | 46 | $this->assertTrue(is_array($events)); 47 | $this->assertCount(6, $events); 48 | } 49 | 50 | public function testBeforeExercise() 51 | { 52 | $this->coverage->expects($this->once()) 53 | ->method('clear'); 54 | 55 | $event = $this->getMockBuilder('Behat\Testwork\EventDispatcher\Event\ExerciseCompleted') 56 | ->disableOriginalConstructor() 57 | ->getMock(); 58 | 59 | $listener = new EventListener($this->coverage, $this->service); 60 | $listener->beforeExercise($event); 61 | } 62 | 63 | public function testAfterExercise() 64 | { 65 | $this->service->expects($this->once()) 66 | ->method('generateReport'); 67 | 68 | $event = $this->getMockBuilder('Behat\Testwork\EventDispatcher\Event\ExerciseCompleted') 69 | ->disableOriginalConstructor() 70 | ->getMock(); 71 | 72 | $listener = new EventListener($this->coverage, $this->service); 73 | $listener->afterExercise($event); 74 | } 75 | 76 | public function testBeforeScenario() 77 | { 78 | $this->coverage->expects($this->once()) 79 | ->method('start') 80 | ->with('MyFile.feature:1'); 81 | 82 | $node = new ScenarioNode('scenarioNode', array(), array(), 'Scenario', 1); 83 | 84 | $feature = new FeatureNode('featureNode', 'A Feature', array(), null, array(), 'Feature', 'en', 'MyFile.feature', 0); 85 | 86 | $event = $this->getMockBuilder('Behat\Behat\EventDispatcher\Event\ScenarioTested') 87 | ->disableOriginalConstructor() 88 | ->getMock(); 89 | 90 | $event->expects($this->once()) 91 | ->method('getScenario') 92 | ->will($this->returnValue($node)); 93 | 94 | $event->expects($this->once()) 95 | ->method('getFeature') 96 | ->will($this->returnValue($feature)); 97 | 98 | $listener = new EventListener($this->coverage, $this->service); 99 | $listener->beforeScenario($event); 100 | } 101 | 102 | public function testBeforeExample() 103 | { 104 | $this->coverage->expects($this->once()) 105 | ->method('start') 106 | ->with('MyFile.feature:2'); 107 | 108 | $node = new OutlineNode('outlineNode', array(), array(), new ExampleTableNode(array(), 'Example'), 'Outline', 2); 109 | 110 | $feature = new FeatureNode('featureNode', 'A Feature', array(), null, array(), 'Feature', 'en', 'MyFile.feature', 0); 111 | 112 | $event = $this->getMockBuilder('Behat\Behat\EventDispatcher\Event\ScenarioTested') 113 | ->disableOriginalConstructor() 114 | ->getMock(); 115 | 116 | $event->expects($this->once()) 117 | ->method('getScenario') 118 | ->will($this->returnValue($node)); 119 | 120 | $event->expects($this->once()) 121 | ->method('getFeature') 122 | ->will($this->returnValue($feature)); 123 | 124 | $listener = new EventListener($this->coverage, $this->service); 125 | $listener->beforeScenario($event); 126 | } 127 | 128 | public function testAfterScenario() 129 | { 130 | $this->coverage->expects($this->once()) 131 | ->method('stop'); 132 | 133 | $event = $this->getMockBuilder('Behat\Behat\EventDispatcher\Event\ScenarioTested') 134 | ->disableOriginalConstructor() 135 | ->getMock(); 136 | 137 | $listener = new EventListener($this->coverage, $this->service); 138 | $listener->afterScenario($event); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /tests/VIPSoft/CodeCoverageExtension/Service/ReportServiceTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder('VIPSoft\CodeCoverageCommon\Report\Html') 45 | ->disableOriginalConstructor() 46 | ->getMock(); 47 | 48 | $factory = $this->getMock('VIPSoft\CodeCoverageCommon\Report\Factory'); 49 | $factory->expects($this->once()) 50 | ->method('create') 51 | ->will($this->returnValue($report)); 52 | 53 | $coverage = $this->getMock('PHP_CodeCoverage'); 54 | 55 | $service = new ReportService(array('report' => array('format' => 'html', 'options' => array())), $factory); 56 | $service->generateReport($coverage); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/VIPSoft/TestCase.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class TestCase extends \PHPUnit_Framework_TestCase 17 | { 18 | /** 19 | * @var array 20 | */ 21 | static public $proxiedFunctions; 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | protected function setUp() 27 | { 28 | parent::setUp(); 29 | 30 | if ( ! class_exists('\\VIPSoft\\Test\\FunctionProxy')) { 31 | eval(<<getCaller(); 67 | 68 | if ( ! $caller || ! isset($caller[0]) || ($pos = strrpos($caller[0], '\\')) === false) { 69 | throw new \Exception('Unable to mock functions in the root namespace'); 70 | } 71 | 72 | $namespace = str_replace(array('\\Test\\', '\\Tests\\'), '\\', substr($caller[0], 0, $pos)); 73 | } 74 | 75 | if ( ! function_exists('\\' . $namespace . '\\' . $functionName)) { 76 | eval(<<