Coder Social home page Coder Social logo

humbug / humbug Goto Github PK

View Code? Open in Web Editor NEW
1.1K 58.0 73.0 991 KB

Humbug is a Mutation Testing framework for PHP to measure the real effectiveness of your test suites and assist in their improvement. It eats Code Coverage for breakfast.

License: BSD 3-Clause "New" or "Revised" License

Shell 0.01% PHP 92.62% Gherkin 7.36%
mutation-testing php coverage testing mutation-analysis

humbug's Introduction

Humbug: Mutation Testing for PHP

๐Ÿšจ This package is deprecated, check out Infection instead.

Humbug is a Mutation Testing framework for PHP. It is currently in development and so, while it does actually work quite well, it will have rough edges that a team of minions are working hard to hammer out. If it falls out of the gate, you have been warned ;).

Build Status Build status Scrutinizer Code Quality StyleCI Total Downloads Slack

โš ๏ธ๏ธ Update your remotes! Humbug has transferred to a new location. While your existing repositories will redirect transparently for any operations, take some time to transition to the new URL.

$ git remote set-url upstream https://github.com/humbug/humbug.git

Replace upstream with the name of the remote you use locally; upstream is commonly used but you may be using something else. You may also using a different URL (e.g. [email protected]:mockery/mockery.git). Run git remote -v to see what you're actually using.

Table of Contents

Introduction

Mutation Testing is, in a nutshell, giving your unit tests a run for their money. It involves injecting small defects into source code and then checking if the unit tests noticed. If they do, then your unit tests have "killed" the mutation. If not, the mutation has escaped detection. As unit tests are intended to prevent regressions, having a real regression pass unnoticed would be a bad thing!

Whereas Code Coverage can tell you what code your tests are executing, Mutation Testing is intended to help you judge how well your unit tests actually perform and where they could be improved.

I've written in more detail about why Mutation Testing is worth having: Lies, Damned Lies and Code Coverage: Towards Mutation Testing

Contributing

Humbug is an open source project that welcomes pull requests and issues from anyone. Before opening pull requests, please read our short Contribution Guide.

Installation

Git

You can clone and install Humbug's dependencies using Composer:

git clone https://github.com/humbug/humbug.git
cd humbug
/path/to/composer.phar install

The humbug command is now at bin/humbug.

Phar

If you don't want to track the master branch directly, you can install the Humbug phar as follows:

wget https://padraic.github.io/humbug/downloads/humbug.phar
wget https://padraic.github.io/humbug/downloads/humbug.phar.pubkey
# If you wish to make humbug.phar directly executable
chmod +x humbug.phar

On Windows, you can just download using a browser or from Powershell v3 using the following commands where wget is an alias for Invoke-WebRequest:

wget https://padraic.github.io/humbug/downloads/humbug.phar -OutFile humbug.phar
wget https://padraic.github.io/humbug/downloads/humbug.phar.pubkey -OutFile humbug.phar.pubkey

If you're stuck with Powershell v2:

$client = new-object System.Net.WebClient
$client.DownloadFile("https://padraic.github.io/humbug/downloads/humbug.phar", "humbug.phar")
$client.DownloadFile("https://padraic.github.io/humbug/downloads/humbug.phar.pubkey", "humbug.phar.pubkey")
PHAR Updates

The phar is signed with an openssl private key. You will need the pubkey file to be stored beside the phar file at all times in order to use it. If you rename humbug.phar to humbug, for example, then also rename the key from humbug.phar.pubkey to humbug.pubkey.

The phar releases are currently done manually so they will not be updated with the same frequency as git master. To update your current phar, just run:

./humbug.phar self-update

Note: Using a phar means that fixes may take longer to reach your version, but there's more assurance of having a stable development version. The public key is downloaded only once. It is re-used by self-update to verify future phar releases.

Once releases commence towards stable, there will be an alpha, beta, RC and a final release. Your development track phar file will self-update automatically until it reaches a stable release. If you wish to continue tracking the development level phars, you will need to indicate this using one of the stability flags:

./humbug.phar self-update --dev
Self-Update Request Debugging

If you experience any issues self-updating with unexpected openssl or SSL errors, please ensure that you have enabled the openssl extension. On Windows, you can do this by adding or uncommenting the following line in the php.ini file for PHP on the command line (if different than the file for your http server):

extension=php_openssl.dll

Certain other SSL errors may arise due missing certificates. You can rectify this by finding their location on your system (e.g. C:/xampp/php/ext/cacert.pem), or alternatively downloading a copy from http://curl.haxx.se/ca/cacert.pem. Then ensure the following option is correctly pointing to this file:

openssl.cafile=C:/path/to/cacert.pem

Composer

Due to Humbug's dependencies being pegged to recent versions, adding Humbug to composer.json may give rise to conflicts. The above two methods of installation are preferred where this occurs. You can however install it globally as any other general purpose tool:

composer global require 'humbug/humbug=~1.0@dev'

And if you haven't done so previously...add this to ~/.bash_profile (or ~/.bashrc):

export PATH=~/.composer/vendor/bin:$PATH

Humbug currently works on PHP 5.4 or greater.

Usage

Configuration

Humbug is still under development so, to repeat, beware of rough edges.

Configure command

To configure humbug in your project you may run:

humbug configure

This tool will ask some questions required to create the Humbug configuration file (humbug.json.dist).

Manual Configuration

In the base directory of your project create a humbug.json.dist file:

{
    "timeout": 10,
    "source": {
        "directories": [
            "src"
        ]
    },
    "logs": {
        "text": "humbuglog.txt",
        "json": "humbuglog.json"
    }
}

You can commit the humbug.json.dist to your VCS and override it locally with a humbug.json file.

Edit as appropriate. If you do not define at least one log, detailed information about escaped mutants will not be available. The Text log is human readable. If source files exist in the base directory, or files in the source directories must be excluded, you can add exclude patterns (here's one for files in base directory where composer vendor and Tests directories are excluded):

{
    "timeout": 10,
    "source": {
        "directories": [
            "."
        ],
        "excludes": [
            "vendor",
            "Tests"
        ]
    },
    "logs": {
        "text": "humbuglog.txt"
    }
}

If, from your project's base directory, you must run tests from another directory then you can signal this also. You should not need to run Humbug from any directory other than your project's base directory.

{
    "chdir": "tests",
    "timeout": 10,
    "source": {
        "directories": [
            "src"
        ],
    }
}

Running Humbug

Ensure that your tests are all in a passing state (incomplete and skipped tests are allowed). Humbug will quit if any of your tests are failing.

The magic command, while in your project's base directory (using the PHAR download) is:

./humbug.phar

or if you just cloned Humbug:

../humbug/bin/humbug

or if you added Humbug as a composer dependency to your project:

./vendor/bin/humbug

Instead of php with the xdebug extension you may also run Humbug via phpdbg:

phpdbg -qrr humbug.phar

If all went well, you will get something similar to:

 _  _            _
| || |_  _ _ __ | |__ _  _ __ _
| __ | || | '  \| '_ \ || / _` |
|_||_|\_,_|_|_|_|_.__/\_,_\__, |
                          |___/ 
Humbug version 1.0-dev

Humbug running test suite to generate logs and code coverage data...

  361 [==========================================================] 28 secs

Humbug has completed the initial test run successfully.
Tests: 361 Line Coverage: 64.86%

Humbug is analysing source files...

Mutation Testing is commencing on 78 files...
(.: killed, M: escaped, S: uncovered, E: fatal error, T: timed out)

.....M.M..EMMMMMSSSSMMMMMSMMMMMSSSE.ESSSSSSSSSSSSSSSSSM..M.. |   60 ( 7/78)
...MM.ES..SSSSSSSSSS...MMM.MEMME.SSSS.............SSMMSSSSM. |  120 (12/78)
M.M.M...TT.M...T.MM....S.....SSS..M..SMMSM...........M...... |  180 (17/78)
MM...M...ESSSEM..MMM.M.MM...SSS.SS.M.SMMMMMMM..SMMMMS....... |  240 (24/78)
.........SMMMSMMMM.MM..M.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  300 (26/78)
SSSSSSSSM..E....S......SS......M.SS..S..M...SSSSSSSS....MMM. |  360 (37/78)
.M....MM..SM..S..SSSSSSSS.EM.S.E.M............M.....M.SM.M.M |  420 (45/78)
..M....MMS...MMSSS................M.....EME....SEMS...SSSSSS |  480 (52/78)
SSSSS.EMSSSSM..M.MMMM...SSE.....MMM.M..MM..MSSSSSSSSSSSSSSSS |  540 (60/78)
SSS....SSSSSSSSMM.SSS..........S..M..MSSMS.SSSSSSSSSSSSSSSSS |  600 (68/78)
......E...M..........SM.....M..MMMMM.MMMMMSSSSSSSM.SS

653 mutations were generated:
     284 mutants were killed
     218 mutants were not covered by tests
     131 covered mutants were not detected
      17 fatal errors were encountered
       3 time outs were encountered

Metrics:
    Mutation Score Indicator (MSI): 47%
    Mutation Code Coverage: 67%
    Covered Code MSI: 70%

Remember that some mutants will inevitably be harmless (i.e. false positives).

Humbug results are being logged as JSON to: log.json
Humbug results are being logged as TEXT to: log.txt

To explain the perhaps cryptic progress output:

  • Killed Mutation (.): A mutation that caused unit tests to fail which is a positive outcome.
  • Escaped Mutation (M): A mutation where the unit tests still passed which is not what we want! Our unit tests should detect any behaviour changes.
  • Uncovered Mutation (S): A mutation which occurs on a line not covered by any unit test. Since there are no unit tests, this is another undesireable result.
  • Fatal Error (E): A mutation created a fatal error. Usually a positive result since it's obviously going to be noticed. In some cases, however, it might be a Humbug problem that needs fixing.
  • Timeout (T): This is where unit tests exceed the allowed timeout configured for Humbug. Likely a positive result if your timeout is appropriate, and often occurs when a mutation ends up creating an infinite loop.

Kills, errors and timeouts are all counted as detected mutations. We report errors in the logs on the off chance that Humbug itself encountered an internal error, i.e. a bug to be reported as an issue here!

The Metrics

The example summary results reported a number of metric scores:

  • A Mutation Score Indicator (MSI) of 47%. This means that 47% of all generated mutations were detected (i.e. kills, timeouts or fatal errors). The MSI is the primary Mutation Testing metric. Given the code coverage of 65%, there is a 18% discrepancy so Code Coverage was a terrible quality measurement in this example.
  • Mutation Code Coverage is 67%. On average it should be within the same ballpark as your normal code coverage, but code coverage ignores mutation frequency.
  • The Mutation Score Indicator for code that is actually covered by tests was 70% (i.e. ignoring code not even tested). This gives you some idea of how effective the tests that do exist really are.

If you examine these metrics, the standout issue is that the MSI of 47% is 18 points lower than the reported Code Coverage at 65%. These unit tests are far less effective than Code Coverage alone could detect.

Interpreting these results requires some context. The logs will list all undetected mutations as diffs against the original source code. Examining these will provide further insight as to what specific mutations went undetected.

Command Line Options

Humbug has a few command line options of note, other than those normally associated with any Symfony Console application.

Overriding The Configured Timeout

You can manually set the timeout threshold for any single test:

humbug --timeout=10

Restricting Files To Mutate

If you're only interested in mutating a subset of your files, you can pass any number of --file options containing simple file names, globs or regular expressions. Basically, these are all passed to the Symfony Finder's name() method.

humbug --file=NewClass.php --file=*Driver.php

This in no way restricts the initial Humbug check on the overall test suite which is still executed in full to ensure all tests are passing correctly before proceeding.

Mutate specific files

If you want to mutate only a few specific files, you can pass any number of --path options containing full path file names. This option will be passed to a filter \Closure that will intersect files found using the config and/or --file option with the files provided by you using the --path option.

humbug --path=src/Data/NewClass.php --path=src/Driver/Driver.php

Note: This in no way restricts the initial Humbug check on the overall test suite which is still executed in full to ensure all tests are passing correctly before proceeding.

Incremental Analysis

Incremental Analysis (IA) is an experimental unfinished mode of operation where results are cached locally between runs and reused where it makes sense. At present, this mode operates very naively by eliminating test runs where both the immediate file being mutated and the relevant tests for a mutated line have not been modified since the last run (as determined by comparing the SHA1 of the files involved).

humbug --incremental

The IA mode offers a significant performance increase for relatively stable code bases, and you're free to test it and see how it fares in real life. In the future, it does need to take into accounts changes in files which contain parent classes, imported traits and the classes of its immediate dependencies, all of which have an impact on the behaviour of any given object.

IA utilises a local permanent cache, e.g. /home/padraic/.humbug.

Performance

Mutation Testing has traditionally been slow. The concept being to re-run your test suite for each mutation generated. To speed things up significantly, Humbug does the following:

  • On each test run, it only uses those test classes which cover the specific file and line on which the mutation was inserted.
  • It orders test classes to run so that the slowest go last (hopefully the faster tests will detect mutations early!).
  • If a mutation falls on a line not covered by any tests, well, we don't bother running any tests.
  • Performance may, depending on the source code, be significantly impacted by timeouts. The default of 60s may be far too high for smaller codebases, and far too low for larger ones. As a rule of thumb, it shouldn't exceed the seconds needed to normally run the tests being mutated (and can be set lower).

While all of this speeds up Humbug, do be aware that a Humbug run will be slower than unit testing. A 2 second test suite may require 30 seconds for mutation testing. Or 5 minutes. It all depends on the interplay between lines of code, number of tests, level of code coverage, and the performance of both code and tests.

Mutators

Humbug implements a basic suite of Mutators, which essentially tells us when a particular PHP token can be mutated, and also apply that mutation to an array of tokens.

Note: Source code held within functions (rather than class methods) is not mutated at this time.

Binary Arithmetic:

Original Mutated Original Mutated
+ - /= *=
- + %= *=
* / **= /=
/ * & |
% * | &
** / ^ &
+= -= ~
-= += >> <<
*= /= << >>

Boolean Substitution:

This temporarily encompasses logical mutators.

Original Mutated
true false
false true
&& ||
|| &&
and or
or and
!

Conditional Boundaries:

Original Mutated
> >=
< <=
>= >
<= <

Negated Conditionals:

Original Mutated Original Mutated
== != > <=
!= == < >=
<> == >= <
=== !== <= >
!== ===

Increments:

Original Mutated
++ --
-- ++

Return Values:

Original Mutated Original Mutated
return true; return false; return 1.0>; return -( + 1);
return false; return true; return $this; return null;
return 0; return 1; return function(); function(); return null;
return ; return 0; return new Class; new Class; return null;
return 0.0; return 1.0; return (Anything); (Anything); return null;
return 1.0; return 0.0;

Literal Numbers:

Original Mutated
0 1
1 0
Int > 1 Int + 1
Float >= 1 / <= 2 Float + 1
Float > 2 1

If Statements:

All if statements are covered largely by previous mutators, but there are special cases such as using native functions or class methods without any compares or operations, e.g. is_int() or in_array(). This would not cover functions defined in files since they don't exist until runtime (something else to work on!).

Original Mutated
if(is_int(1)) if(!is_int(1))

More Mutators will be added over time.

JSON Log Stats

bin/humbug stats ../my-project/humbuglog.json ../my-project/list-of-classes.txt --skip-killed=yes [-vvv]

Parses stats from humbuglog.json or your custom named JSON log.

CLI reference:

humbug stats [humbuglog.json location] [class list location] [--skip-killed=yes] [-vvv]
humbuglog.json location, defaults to ./humbuglog.json

class list location, a path to a text file containing full class names, one per line.

only this files-related stats would be shown
--skip-killed=yes is used to completely skip output of "killed" section
various verbosity levels define amount of info to be displayed:
    by default, there's one line per class with amount of mutants killed/escaped/errored/timed out (depending on output section)
    -v adds one line per each mutant with line number and method name
    -vv adds extra line for each mutant, displaying diff view of line mutant is detected in
    -vvv shows full diff with several lines before and after

This can be tested on humbug itself, by running in humbug's dir:

bin/humbug bin/humbug stats [-vvv]

Did I Say Rough Edges?

This is a short list of known issues:

  • Humbug does initial test runs, logging and code coverage. Should allow user to do that optionally.
  • Test classes (not tests) are run in a specific order, fastest first. Interdependent test classes may therefore fail regularly which will skew the results.
  • Currently 100% PHPUnit specific, well 98.237%. There is an adapter where PHPUnit code is being shovelled.
  • Certain test suite may make assumptions about having sole access to resources like /tmp which will cause errors when Humbug tries using same.
  • Fine grained test ordering by speed (as opposed to large grained test class ordering) is awaiting implementation.
  • Should test classes be used to carry non-PHPUnit dependent testing code (e.g. register_shutdown_function()), it may create issues when combined with one or more of Humbugs optimisations which assume a finished test really is finished.

Bah, Humbug!

Courtesy of Craig Davis who saw potential in a once empty repository :P.

                    .:::::::::::...
                  .::::::::::::::::::::.
                .::::::::::::::::::::::::.
               ::::::::::::::::::::::::::::.
              :::::::::::::::::::::::::::::::  .,uuu   ...
             :::::::::::::::::::::::::::::::: dHHHHHLdHHHHb
       ....:::::::'`    ::::::::::::::::::' uHHHHHHHHHHHHHF
   .uHHHHHHHHH'         ::::::::::::::`.  uHHHHHHHHHHHHHP"
   HHHHHHHHHHH          `:::::::::::',dHHuHHHHHHHHP".g@@g
  J"HHHHHHHHHP        4H ::::::::'  u$$$.
  ".HHHHHHHHP"     .,uHP :::::' uHHHHHHHHHHP"",e$$$$$c
   HHHHHHHF'      dHHHHf `````.HHHHHHHHHHP",d$$$$$$$P%C
 .dHHHP""         JHHHHbuuuu,JHHHHHHHHP",d$$$$$$$$$e=,z$$$$$$$$ee..
 ""              .HHHHHHHHHHHHHHHHHP",gdP"  ..3$$$Jd$$$$$$$$$$$$$$e.
                 dHHHHHHHHHHHHHHP".edP    " .zd$$$$$$$$$$$"3$$$$$$$$c
                 `???""??HHHHP",e$$F" .d$,?$$$$$$$$$$$$$F d$$$$$$$$F"
                       ?be.eze$$$$$".d$$$$ $$$E$$$$P".,ede`?$$$$$$$$
                      4."?$$$$$$$  z$$$$$$ $$$$r.,.e ?$$$$ $$$$$$$$$
                      '$c  "$$$$ .d$$$$$$$ 3$$$.$$$$ 4$$$ d$$$$P"`,,
                       """- "$$".`$$"    " $$f,d$$P".$$P zeee.zd$$$$$.
                     ze.    .C$C"=^"    ..$$$$$$P".$$$'e$$$$$P?$$$$$$
                 .e$$$$$$$"="$f",c,3eee$$$$$$$$P $$$P'd$$$$"..::.."?$%
                4d$$$P d$$$dF.d$$$$$$$$$$$$$$$$f $$$ d$$$" :::::::::.
               $$$$$$ d$$$$$ $$$$$$$$$$$$$$$$$$ J$$",$$$'.::::::::::::
              "$$$$$$ ?$$$$ d$$$$$$$$$$$$$$$P".dP'e$$$$':::::::::::::::
              4$$$$$$c $$$$b`$$$$$$$$$$$P"",e$$",$$$$$' ::::::::::::::::
              ' ?"?$$$b."$$$$.?$$$$$$P".e$$$$F,d$$$$$F ::::::::::::::::::
                    "?$$bc."$b.$$$$F z$$P?$$",$$$$$$$ ::::::::::::::::::::
                        `"$$c"?$$$".$$$)e$$F,$$$$$$$' ::::::::::::::::::::
                        ':. "$b...d$$P4$$$",$$$$$$$" :::::::::::::::::::::
                        ':::: "$$$$$".,"".d$$$$$$$F ::::::::::::::::::::::
                         :::: be."".d$$$4$$$$$$$$F :::::::::::::::::::::::
                          :::: "??$$$$$$$$$$?$P" :::::::::::::::::::::::::
                           :::::: ?$$$$$$$$f .::::::::::::::::::::::::::::
                            :::::::`"????"".::::::::::::::::::::::::::::::

humbug's People

Contributors

andrewholgate avatar aztech-dev avatar bcremer avatar cmb69 avatar craig-davis avatar daniel-mueller avatar davedevelopment avatar emmajane avatar globin avatar gm-alex avatar jyggen avatar ksimka avatar markredeman avatar naktibalda avatar onigoetz avatar padraic avatar pamil avatar pfrenssen avatar pierredup avatar piotr-zuralski avatar robertbasic avatar rollenes avatar samsonasik avatar scrutinizer-auto-fixer avatar spomky avatar srkdg avatar stof avatar theofidry avatar villfa avatar zanbaldwin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

humbug's Issues

Getting insta-failure on running

Humbug running test suite to generate logs and code coverage data...

    0 [>---------------------------------------------------------]  1 sec

 Tests must be in a fully passing state before Humbug is run.
 Incomplete, skipped or risky tests are allowed.
 The testing framework reported an exit code of 255.
 Stdout:
    >
    > Fatal error: Cannot redeclare class PhpParser\Autoloader in /Users/anahkiasen/Sites/rocketeers/rocketeer/vendor/nikic/php-parser/lib/PhpParser/Autoloader.php on line 9
    >
    > Call Stack:
    >     0.0002     225536   1. {main}() -:0
    >     0.0050     807768   2. Humbug\Adapter\Phpunit::main() -:6
    >     0.0068     979536   3. PHPUnit_TextUI_Command->run() /Users/anahkiasen/.composer/vendor/humbug/humbug/src/Humbug/Adapter/Phpunit.php:157
    >     0.0068     982256   4. PHPUnit_TextUI_Command->handleArguments() /Users/anahkiasen/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:114
    >     0.0131    1577768   5. PHPUnit_TextUI_Command->handleBootstrap() /Users/anahkiasen/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:622
    >     0.0132    1588200   6. PHPUnit_Util_Fileloader::checkAndLoad() /Users/anahkiasen/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:792
    >     0.0133    1588624   7. PHPUnit_Util_Fileloader::load() /Users/anahkiasen/.composer/vendor/phpunit/phpunit/src/Util/Fileloader.php:42
    >     0.0133    1591248   8. include_once('/private/var/folders/fs/grc6ws5d37jfjbl27s73w8n40000gn/T/humbug.phpunit.bootstrap.php') /Users/anahkiasen/.composer/vendor/phpunit/phpunit/src/Util/Fileloader.php:58
    >     0.0133    1593496   9. require_once('/Users/anahkiasen/Sites/rocketeers/rocketeer/vendor/autoload.php') /private/var/folders/fs/grc6ws5d37jfjbl27s73w8n40000gn/T/humbug.phpunit.bootstrap.php:2
    >     0.0135    1609088  10. ComposerAutoloaderInit53a0ac4fb954920498b64b9da42c33b0::getLoader() /Users/anahkiasen/Sites/rocketeers/rocketeer/vendor/autoload.php:7
    >     0.0172    2049720  11. composerRequire53a0ac4fb954920498b64b9da42c33b0() /Users/anahkiasen/Sites/rocketeers/rocketeer/vendor/composer/autoload_real.php:49
    >     0.0173    2052392  12. require('/Users/anahkiasen/Sites/rocketeers/rocketeer/vendor/nikic/php-parser/lib/bootstrap.php') /Users/anahkiasen/Sites/rocketeers/rocketeer/vendor/composer/autoload_real.php:58
    >     0.0177    2143496  13. require('/Users/anahkiasen/Sites/rocketeers/rocketeer/vendor/nikic/php-parser/lib/PhpParser/Autoloader.php') /Users/anahkiasen/Sites/rocketeers/rocketeer/vendor/nikic/php-parser/lib/bootstrap.php:3
    >
    >

[feature request] Improve log

I think it would be nice to have more information in the log file. Here is the list of the improvements I would love to see:

  • Result count in the title: instead of Escapes, having Escapes (120) for instance
  • Section with mutants not covered by tests similar to Escapes or Errors sections

Error in WildCard Testsuite paths

Since the commit 93d47cf
the testsuite search results in an error like:
Could not find file 4DevelopmentOnly/tests/unit//./module working from /var/www/foo/4DevelopmentOnly/tests/unit
The Problem is in Line 118 with the $path. In the past, it was the nodeValue with ./module,
now it is "4DevelopmentOnly/tests/unit//./module" with the working directory "/var/www/foo/4DevelopmentOnly/tests/unit".
This cannot work anymore.. plz revert until fully comp or plz fix it.
Greetings Jan

{
  "chdir": "4DevelopmentOnly/tests/unit",
  "timeout": 10,
  "source": {
    "directories": [
      "."
    ],
    "excludes": [
      "4DevelopmentOnly",
      "vendor"
    ]
  },
  "logs": {
    "text": "build/humbuglog.txt"
  }
}

Write Log File after Tests are completed

It would be nice, if the log files were written after the tests are completed.
The log files shouldnt be deleted at the beginning of the tests.
An analysis of the errors etc. is so longer possible.

certain mutants are impossible to cover

I have a fully covered codebase that I tried humbug on, but to my surprise it claimed some mutants were uncovered. After some digging around, I found that it will try to create mutants for the following:

class FooBar
{
  protected $a = 0;

  public function foo()
  {
    return $a;
  }

  public function bar($b = 0)
  {
    return $b;
  }
}

Even if I were to write a test that covered foo() and bar(), PHP's code coverage won't claim that either lines with $a=0 or $b=0 are covered.

Is there a way to work around this behavior with options for either PHPUnit or humbug?

If not, my initial thoughts are:

  • If 'method' is ???, then add all tests that cover the class. So, to test the mutant $a = 1, any test that covers FooBar should be run.
  • If the mutant is on a function declaration, then add all tests that cover any part of the function (i.e., the first line of code in the function).

Otherwise, it's kind of annoying to have a report with uncovered mutants (which I actually didn't find a way to figure out what they are, without hacking humbug) that you cannot do anything about.

Mutation idea: removing "return $this;" statements

I often use methods that return $this so they are chainable. And more than a few time I saw that here and there I forget to check for that in my tests. I guess a lot of people may do the same.

At first I was thinking to suggest a mutation that would remove all return statements, but that seems a bit too much, which "return $this" is a very popular use case

Humbug unexpectedly ends up with "Tests must be in a fully passing state..."

Sometimes humbug does this

Humbug running test suite to generate logs and code coverage data...

  118 [==========================================================] 11 secs

<warning>Tests must be in a fully passing state before Humbug is run.</warning>
<warning>Incomplete, skipped or risky tests are allowed.</warning>
<warning>Stdout: \n    > 1..120
    >
    > Generating code coverage report in PHP format ... done
    > \n</warning>

(Yes, tags are printed, not converted)
Tests are really ok, and the strange thing is that after manually running phpunit (and ensure that tests are really ok) this error disappears, otherwise you will get this error every time. Looks like some cache issue.

Mutants marked as escaped when, not?

Maybe I'm not fully getting how to read the logs here so, might just be misunderstanding the docs.
Humbug ran and generated a log with 61 escaped mutants โ€“ in my mind those are the mutants that the tests did not catch (ie. all tests ran green when they shouldn't have).

However I tried to redo the changes of 3 or 4 of them and when running PHPUnit I do get failing tests.
Example diff:

-        return $this->usesStages && !empty($stages);
+        return $this->usesStages || !empty($stages);

Now if I redo that very change myself and run phpunit it seems to be caught as expected:

$ phpunit --stop-on-failure
PHPUnit 4.4.1 by Sebastian Bergmann.

There was 1 failure:

1) Rocketeer\Traits\BashModules\FlowTest::testCanCheckIfUsesStages
Failed asserting that true is false.

Am I just misunderstanding how to read the logs or is this a bug?

FunctionCall Mutator falsely triggered

The FunctionCall Mutator reacts to statements like: "return true;".
I added a UnitTests with a pull request with this Problem.

Another UnitTest from yourself is hinting to the problem, too.
Humbug\Test\MutableTest::testShouldGenerateBooleanFalseMutationWhenBoolFalseDetected

Greetings
Jan

#55

[rfc] use psr-4 under src/ directly

currently, directory structure is ( "like" psr-0 but set psr-4 in src/Humbug ) :

src
   Humbug/
         Adapter
         Utility
  bootstrap.php

How about use psr-4 under src/ directly so directory structure will be like this :

src
     Adapter
     Utility
bootstrap.php

?

make humbug installable via composer require

currently, humbug can't be installed directly via composer require :

$ composer require humbug/humbug 1.0.*@dev
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for humbug/humbug 1.0.*@dev -> satisfiable by humbug/humbug[1.0.x-dev].
    - humbug/humbug 1.0.x-dev requires padraic/phpunit-extensions ~1.0@dev -> no matching package found.

Potential causes:
 - A typo in the package name
 - The package is not available in a stable-enough version according to your minimum-stability setting
   see <https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.

Read <http://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.

Installation failed, deleting ./composer.json.

GeneratorTest fails

I got an issue running humbug unit tests.

There was 1 failure:

1) Humbug\Test\GeneratorTest::testShouldCollateAllFilesValidForMutationTesting
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'/home/rolen/php/humbug/tests/Humbug/Test/_files/root/base1/library/bool1.php'
+'/home/rolen/php/humbug/tests/Humbug/Test/_files/root/base1/library/bool2.php'

GenerateTest::testShouldCollateAllFilesValidForMutationTesting relies on Symfony Finder, and as it iterates over file system it may return files in diferent orders. That is why this test is not deterministic.

Cannot install humbug via composer

When attempting to install humbug via composer, I get the following error message:

Your requirements could not be resolved to an installable set of packages.
[...]
humbug/humbug dev-master requires padraic/phpunit-extensions ~1.0@dev -> no matching package found.

Immediate Fatal Error on PHP 5.4

I'm getting this error immediately after I run Humbug:

Humbug running test suite to generate logs and code coverage data...

<warning>Tests must be in a fully passing state before Humbug is run.</warning>
<warning>Incomplete, skipped or risky tests are allowed.</warning>
<warning>Stderr: \n    > PHP Parse error:  syntax error, unexpected '"/mnt/hd/home/alex/web/g/mine/' (T_CONSTANT_ENCAPSED_STRING) in - on line 5
    > \n</warning>

This happens because the generated PHPUnit invocation snippet is passing a string to empty function, which is only allowed since PHP 5.5:

if (!empty(".../vendor/autoload.php")) require_once ".../vendor/autoload.php";

Unable to run initial test run on Windows

While trying to run Humbug on Windows I receive the follow error messsage:

 _  _            _
| || |_  _ _ __ | |__ _  _ __ _
| __ | || | '  \| '_ \ || / _` |
|_||_|\_,_|_|_|_|_.__/\_,_\__, |
                          |___/
Humbug version 1.0-dev

Humbug running test suite to generate logs and code coverage data...

    0 [>---------------------------------------------------------]  1 sec

<warning>Tests must be in a fully passing state before Humbug is run.</warning>
<warning>Incomplete, skipped or risky tests are allowed.</warning>
<warning>The testing framework reported an exit code of 255.</warning>
<warning>Stdout: \n    >
    > Warning: require_once(C:\Users\jyggen\git\bnetโ™‚endor\humbug\humbug\src\bootstrap.php): failed to open stream: Invalid argument in - on line 3
    >
    > Call Stack:
    >     0.0001     214608   1. {main}() -:0
    >
    >
    > Fatal error: require_once(): Failed opening required 'C:\Users\jyggen\git\bnetโ™‚endor\humbug\humbug\src\bootstrap.php' (include_path='.;C:\php\pear') in - on line 3
    >
    > Call Stack:
    >     0.0001     214608   1. {main}() -:0
    >
    > \n</warning>
<warning>Stderr: \n    > PHP Warning:  require_once(C:\Users\jyggen\git\bnetโ™‚endor\humbug\humbug\src\bootstrap.php): failed to open stream: Invalid argument in - on line 3
    > PHP Stack trace:
    > PHP   1. {main}() -:0
    > PHP Fatal error:  require_once(): Failed opening required 'C:\Users\jyggen\git\bnetโ™‚endor\humbug\humbug\src\bootstrap.php' (include_path='.;C:\php\pear') in - on line 3
    > PHP Stack trace:
    > PHP   1. {main}() -:0
    > \n</warning>                                                                                                                                                            

The path should obviously be C:\Users\jyggen\git\bnet\vendor\humbug\humbug\src\bootstrap.php, but for some reason it transforms \v into โ™‚ (U+2642, MALE SIGN). When dumping the generated "job code" everything looks fine, so maybe it's an issue with my environment;

<?php
namespace Humbug\Env;
require_once "C:\Users\jyggen\git\bnet\vendor\humbug\humbug\src\bootstrap.php";
error_reporting(error_reporting() & ~E_NOTICE);
use Humbug\Adapter\Phpunit;
Phpunit::main("YTo2OntzOjc6InRlc3RkaXIiO047czo3OiJiYXNlZGlyIjtzOjI0OiJDOlxVc2Vyc1xqeWdnZW5cZ2l0XGJuZXQiO3M6NzoidGltZW91dCI7aToxMDtzOjg6ImNhY2hlZGlyIjtzOjM0OiJDOlxVc2Vyc1xqeWdnZW5cQXBwRGF0YVxMb2NhbFxUZW1wIjtzOjc6ImNsaW9wdHMiO2E6Njp7aTowO3M6NzoicGhwdW5pdCI7aToxO3M6Njk6Ii0tY29uZmlndXJhdGlvbj1DOlxVc2Vyc1xqeWdnZW5cQXBwRGF0YVxMb2NhbFxUZW1wL3BocHVuaXQuaHVtYnVnLnhtbCI7aToyO3M6MTc6Ii0tc3RvcC1vbi1mYWlsdXJlIjtpOjM7czo1OiItLXRhcCI7aTo0O3M6MDoiIjtpOjU7czowOiIiO31zOjExOiJjb25zdHJhaW50cyI7Tjt9");

I was able to work around it by adding a str_replace() around the realpath() call on line 23 in src/Humbug/Utility/Job.php and replace backslash with forward slashes, but that feels like a weird fix.

Any ideas? Running on dev-master @ 0898315.

Bootstrap location XmlConfiguration::handleSuite

In order to finish #104 i got questions to XmlConfiguration::handleSuite method.
https://github.com/padraic/humbug/blob/master/src/Adapter/Phpunit/XmlConfiguration.php#L221

It is called here: https://github.com/padraic/humbug/blob/master/src/Adapter/Phpunit/XmlConfiguration.php#L163

The logic: Get the first defined testsuite and in its direcories search for bootstrap.php file in those direcories.
PhpUnit does not work this way. It looks for bootstrap configuration. If it ommits bootstrap location(as in comment in line 174) its just because configuration does not contain bootstrap.

So I found this part of code as dead. Am I right? Or maybe there are some other reasons to maintain this code?

Can't install Humbug alongside Laravel 4.2

Currently Humbug cannot be installed alongside Laravel 4.2. Laravel requires symfony/process 2.5.* and Humbug requires symfony/process ~2.6 . I have the following in composer.json:

"require": {
    "laravel/framework": "4.2.*"
},
"require-dev": {
    "humbug/humbug": "dev-master"
},

Running composer update produces the following output:

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Conclusion: remove laravel/framework 4.2.x-dev
    - Conclusion: don't install symfony/process 2.7.x-dev
    - Conclusion: remove symfony/process 2.5.x-dev|install symfony/process 2.7.x-dev
    - Conclusion: don't install laravel/framework 4.2.x-dev
    - Conclusion: don't install symfony/process 2.6.x-dev
    - Conclusion: don't install symfony/process v2.5.10|install symfony/process 2.6.x-dev
    - Conclusion: don't install symfony/process 2.5.x-dev|install symfony/process 2.6.x-dev|install symfony/process 2.7.x-dev
    - Conclusion: don't install laravel/framework v4.2.17
    - Conclusion: don't install laravel/framework v4.2.16
    - Conclusion: don't install laravel/framework v4.2.15
    - Conclusion: don't install laravel/framework v4.2.14
    - Conclusion: don't install laravel/framework v4.2.13
    - Conclusion: don't install laravel/framework v4.2.12
    - Conclusion: don't install laravel/framework v4.2.11
    - Conclusion: don't install laravel/framework v4.2.10
    - Conclusion: don't install laravel/framework v4.2.9
    - Conclusion: don't install laravel/framework v4.2.8
    - Conclusion: don't install laravel/framework v4.2.7
    - Conclusion: don't install laravel/framework v4.2.6
    - Conclusion: don't install laravel/framework v4.2.5
    - Conclusion: don't install laravel/framework v4.2.4
    - Conclusion: don't install laravel/framework v4.2.3
    - Conclusion: don't install laravel/framework v4.2.2
    - Conclusion: don't install laravel/framework v4.2.1
    - Conclusion: don't install laravel/framework v4.2.0
    - Conclusion: don't install symfony/process v2.5.9
    - Conclusion: don't install symfony/process v2.6.4
    - Conclusion: don't install symfony/process v2.5.0-BETA2|install symfony/process v2.6.4
    - Conclusion: don't install symfony/process v2.5.0-RC1|install symfony/process v2.6.4
    - Conclusion: don't install symfony/process v2.5.0|install symfony/process v2.6.4
    - Conclusion: don't install symfony/process v2.5.1|install symfony/process v2.6.4
    - Conclusion: don't install symfony/process v2.5.2|install symfony/process v2.6.4
    - Conclusion: don't install symfony/process v2.5.3|install symfony/process v2.6.4
    - Conclusion: don't install symfony/process v2.5.4|install symfony/process v2.6.4
    - Conclusion: don't install symfony/process v2.5.5|install symfony/process v2.6.4
    - Conclusion: don't install symfony/process v2.5.6|install symfony/process v2.6.4
    - Conclusion: don't install symfony/process v2.5.7|install symfony/process v2.6.4
    - Installation request for laravel/framework 4.2.* -> satisfiable by laravel/framework[4.2.x-dev, v4.2.0, v4.2.0-BETA1, v4.2.1, v4.2.10, v4.2.11, v4.2.12, v4.2.13, v4.2.14, v4.2.15, v4.2.16, v4.2.17, v4.2.2, v4.2.3, v4.2.4, v4.2.5, v4.2.6, v4.2.7, v4.2.8, v4.2.9].
    - Installation request for humbug/humbug dev-master -> satisfiable by humbug/humbug[dev-master].
    - humbug/humbug dev-master requires symfony/process ~2.6 -> satisfiable by symfony/process[2.6.x-dev, 2.7.x-dev, v2.6.0, v2.6.0-BETA1, v2.6.0-BETA2, v2.6.1, v2.6.2, v2.6.3, v2.6.4].
    - Can only install one of: symfony/process[v2.6.0, v2.5.0-BETA1].
    - Can only install one of: symfony/process[v2.6.0-BETA1, v2.5.0-BETA1].
    - Can only install one of: symfony/process[v2.6.0-BETA2, v2.5.0-BETA1].
    - Can only install one of: symfony/process[v2.6.1, v2.5.0-BETA1].
    - Can only install one of: symfony/process[v2.6.2, v2.5.0-BETA1].
    - Can only install one of: symfony/process[v2.6.3, v2.5.0-BETA1].
    - laravel/framework v4.2.0-BETA1 requires symfony/process 2.5.* -> satisfiable by symfony/process[2.5.x-dev, v2.5.0, v2.5.0-BETA1, v2.5.0-BETA2, v2.5.0-RC1, v2.5.1, v2.5.10, v2.5.2, v2.5.3, v2.5.4, v2.5.5, v2.5.6, v2.5.7, v2.5.8, v2.5.9].
    - Conclusion: don't install symfony/process v2.5.8|install symfony/process v2.6.4

Is there a way around this issue?

Incorrect replacement of & by |

It seems the bitwise mutator is replacing reference operators by mistake:

-  foreach ($shared as &$file) {
+  foreach ($shared as |$file) {

Which causes a syntax error for that mutation.

Cannot run humbug on a Symfony project

I am trying to use humbug on my Symfony project and I have this weird behavior. Humbug does not find my test suite.

Here is my humbug configuration:

{
    "chdir": "app",
    "timeout": 10,
    "source": {
        "directories": [
            "src"
    ]
    },
    "logs": {
        "text": "humbuglog.txt",
        "json": "humbuglog.json"
    }
}

Here is my phpunit configuration file:

<?xml version="1.0" encoding="UTF-8"?>

<!-- http://phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit
    backupGlobals               = "false"
    backupStaticAttributes      = "false"
    colors                      = "true"
    convertErrorsToExceptions   = "true"
    convertNoticesToExceptions  = "true"
    convertWarningsToExceptions = "true"
    processIsolation            = "false"
    stopOnFailure               = "false"
    syntaxCheck                 = "false"
    bootstrap                   = "bootstrap.php.cache" >

    <testsuites>
        <testsuite name="Test Suite">
            <directory>../src/*/*Bundle/Tests</directory>
        </testsuite>
    </testsuites>

    <filter>
        <whitelist>
            <directory>../src</directory>
            <exclude>
                <directory>../src/*/*Bundle/Resources</directory>
                <directory>../src/*/*Bundle/Tests</directory>
            </exclude>
        </whitelist>
    </filter>
</phpunit>

I can run my test suite directly from the command line or from netbeans, but it seems that there is something blocking the use of it from humbug. Here is humbug output:


 _  _            _
| || |_  _ _ __ | |__ _  _ __ _
| __ | || | '  \| '_ \ || / _` |
|_||_|\_,_|_|_|_|_.__/\_,_\__, |
                          |___/
Humbug version 1.0-dev

Humbug running test suite to generate logs and code coverage data...

    0 [>---------------------------------------------------------]  1 sec

Humbug has completed the initial test run successfully.
Tests: 0 Line Coverage: 0.00%

Humbug is analysing source files...

Mutation Testing is commencing on 91 files...
(.: killed, M: escaped, S: uncovered, E: fatal error, T: timed out)

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |   60 (13/91)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  120 (13/91)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  180 (16/91)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  240 (17/91)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  300 (48/91)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  360 (60/91)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  420 (65/91)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  480 (65/91)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  540 (66/91)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  600 (77/91)
SSSSSSSSSSSSSSSSSSSSSSS

623 mutations were generated:
       0 mutants were killed
     623 mutants were not covered by tests
       0 covered mutants were not detected
       0 fatal errors were encountered
       0 time outs were encountered

Out of 0 test covered mutations, 0% were detected.
Out of 623 total mutations, 0% were detected.
Out of 623 total mutations, 0% were covered by tests.

Remember that some mutants will inevitably be harmless (i.e. false positives).

Humbug results are being logged as JSON to: humbuglog.json
Humbug results are being logged as TEXT to: humbuglog.txt

Time: 36.42 seconds Memory: 7.25MB

And phpunit output:

PHPUnit 4.3.5 by Sebastian Bergmann.

Configuration read from C:\Work\Sources\app\phpunit.xml.dist

...............................................................  63 / 677 (  9%)
............................................................... 126 / 677 ( 18%)
............................................................... 189 / 677 ( 27%)
............................................................... 252 / 677 ( 37%)
............................................................... 315 / 677 ( 46%)
............................................................... 378 / 677 ( 55%)
............................................................... 441 / 677 ( 65%)
............................................................... 504 / 677 ( 74%)
............................................................... 567 / 677 ( 83%)
............................................................... 630 / 677 ( 93%)
...............................................

Time: 44.55 seconds, Memory: 30.25Mb

โ†[30;42mOK (677 tests, 1057 assertions)โ†[0m

Generating code coverage report in Clover XML format ... done

I am running that on W7 in git bash CLI interface.

Do you have any ideas why it is not working?

Write Many Unit Tests

Humbug was written quite quickly, reworked from some old legacy code, and has rapidly evolved.

With a failing test suite...

However, three weeks later, it's basic design is essentially stable and there is no exploratory coding at 2am being done any longer. It's time someone (i.e. me) pruned the current test suite, expanded it, and did any remaining refactoring (e.g. logging) before going any further.

Weird escaped mutations

I tried running Humbung on Mink. It reports me a bunch of escapes, which I was expecting (though we are catching 84% of them on first run, which could have been worse).
However, some of the escaped mutations look really suspicious to me. Here is the first reported escaped mutation:

1) \Humbug\Mutator\Number\Integer
Diff on \Behat\Mink\Selector\NamedSelector::translateToXPath() in /home/stof/Code/mink/Mink/src/Behat/Mink/Selector/NamedSelector.php:
--- Original
+++ New
@@ @@
     {
-        if (2 < count($locator)) {
+        if (3 < count($locator)) {
             throw new \InvalidArgumentException('NamedSelector expects array(name, locator) as argument');
         }

         if (2 == count($locator)) {
             $selector   = $locator[0];
             $locator    = $locator[1];
        {
            "file": "\/home\/stof\/Code\/mink\/Mink\/src\/Behat\/Mink\/Selector\/NamedSelector.php",
            "mutator": "\\Humbug\\Mutator\\Number\\Integer",
            "class": "\\Behat\\Mink\\Selector\\NamedSelector",
            "method": "translateToXPath",
            "line": 197,
            "diff": "--- Original\n+++ New\n@@ @@\n     {\n-        if (2 < count($locator)) {\n+        if (3 < count($locator)) {\n             throw new \\InvalidArgumentException('NamedSelector expects array(name, locator) as argument');\n         }\n \n         if (2 == count($locator)) {\n             $selector   = $locator[0];\n             $locator    = $locator[1];",
            "stdout": "",
            "stderr": null,
            "tests": [
                {
                    "class": "Behat\\Mink\\Tests\\Selector\\PartialNamedSelectorTest",
                    "file": "\/home\/stof\/Code\/mink\/Mink\/tests\/Selector\/PartialNamedSelectorTest.php",
                    "time": 0.009642
                },
                {
                    "class": "Behat\\Mink\\Tests\\Selector\\ExactNamedSelectorTest",
                    "file": "\/home\/stof\/Code\/mink\/Mink\/tests\/Selector\/ExactNamedSelectorTest.php",
                    "time": 0.009797
                }
            ]
        },

However, running phpunit --tap tests/Selector/PartialNamedSelectorTest.php gives me this output (I truncated the end as we have more tests in it):

TAP version 13
ok 1 - Behat\Mink\Tests\Selector\PartialNamedSelectorTest::testRegisterXpath
not ok 2 - Error: Behat\Mink\Tests\Selector\PartialNamedSelectorTest::testInvalidLocator
ok 3 - Behat\Mink\Tests\Selector\PartialNamedSelectorTest::testSelectors with data set "fieldset" ('test.html', 'fieldset', 'fieldset-text', 2, 3)
...

I don't see how this would qualify as an escaped mutation.

Running Humbug on Humbug

I get lots of errors running Humbug on Humbug. Errors are connected with phpunit-extensions installed some time ago.

PHP Fatal error:  Uncaught exception 'RuntimeException' with message 'FastestFirstFilter has encountered an unlogged test suite which cannot be sorted' in /home/rolen/php/humbug/vendor/padraic/phpunit-extensions/src/Humbug/Phpunit/Filter/TestSuite/FastestFirstFilter.php:30
Stack trace:
#0 [internal function]: Humbug\Phpunit\Filter\TestSuite\FastestFirstFilter->Humbug\Phpunit\Filter\TestSuite\{closure}(Object(PHPUnit_Framework_TestSuite), Object(PHPUnit_Framework_TestSuite))
#1 /home/rolen/php/humbug/vendor/padraic/phpunit-extensions/src/Humbug/Phpunit/Filter/TestSuite/FastestFirstFilter.php(41): usort(Array, Object(Closure))
#2 /home/rolen/php/humbug/vendor/padraic/phpunit-extensions/src/Humbug/Phpunit/Listener/FilterListener.php(61): Humbug\Phpunit\Filter\TestSuite\FastestFirstFilter->filter(Array)
#3 /home/rolen/php/humbug/vendor/padraic/phpunit-extensions/src/Humbug/Phpunit/Listener/FilterListener.php(47): Humbug\Phpunit\Listener\FilterListener->filterSuites(Array)
#4 /home/rolen/php/humbug/vendor/phpunit/phpunit/src/Framew in /home/rolen/php/humbug/vendor/padraic/phpunit-extensions/src/Humbug/Phpunit/Filter/TestSuite/FastestFirstFilter.php on line 30

This error occurs 214 times. See https://gist.github.com/rollenes/fd4930184ad7e2375de5#file-humbug-issue-txt

FastestFirstFilter is not able to find time logs for specific tests in phpunit.times.humbug.json.

After some analysis I found that this file(phpunit.times.humbug.json) is changing while running humbug.

I run tail on phpunit.times.humbug.json. See https://gist.github.com/rollenes/fd4930184ad7e2375de5#file-humbug-times-tail-log

It appears to truncate file many times while running.

So I simply run phpunit on humbug test harness and effect was the same. It overrides phpunit.times.humbug.json several times.
See tail below:

rolen@rollen:/tmp $ tail -f phpunit.times.humbug.json 
{
    "suites": [],
    "tests": {
        "MM1_MathTest": [
            {
                "title": "testAdds",
                "time": 0.010890007019043
            }
        ]
    }
}.0039820671081543
            }
        ]
    }
}tail: phpunit.times.humbug.json: file truncated
tail: phpunit.times.humbug.json: file truncated
{
    "suites": [],
    "tests": {
        "ExceptionTest": [
            {
                "title": "testSomeException",
                "time": 0.0022640228271484
            }
        ]
    }
}tail: phpunit.times.humbug.json: file truncated

So each time tests are run in humbug as a background phpunit process it truncates file and pushes new content to phpunit.times.humbug.json.

I tracked those tests under Humbug\Test\Adapter\PhpunitTest. Skipping this tests made possible to run phpunit without affecting phpunit.times.humbug.json.
Running humbug then also did not affected this file. And all errors disappeared.
But I didn`t want to skip them...

After some another analysis of Humbug\Test\Adapter\PhpunitTest I found that those test writes all of its contents to cacheDirectory.
So I changed it, and unskipped those tests.
Phpunit run didnt affects phpunit.times.humbug.json .So problem should disappear...

But I still get the same errors running Humbug on Humbug:

PHP Fatal error:  Uncaught exception 'RuntimeException' with message 'FastestFirstFilter has encountered an unlogged test suite which cannot be sorted' in /home/rolen/php/humbug/vendor/padraic/phpunit-extensions/src/Humbug/Phpunit/Filter/TestSuite/FastestFirstFilter.php:30

Also tail of phpunit.times.humbug.json behaved in the same way(truncated and replaced content).

Digging dipper...

First some cleanups - I left those new cache folders in test suites so I thought it would be nice to remove it in tearDown.

PhpUnit failed:

rmdir(/tmp/8583355): Directory not empty

rolen@rollen:~/php/humbug (master) $ ls /tmp/5117136
coverage.humbug.php  coverage.humbug.txt  phpunit.humbug.xml

So there are some files in this place after running phpunit. This is some kind of side effect, so I remove this files.
PhpUnit run: passes
Humbug on humbug: no changes...
Still lots of errors with FastestFirstFilter and tail of phpunit.times.humbug.json reports to be truncated many times.

Lets debug then, and trace changes in files with side effects mentioned before:

  • phpunit.humbug.xml
  • phpunit.times.humbug.json

with debugging points after initial check is done and after each mutant process is executed. No results as those processes are run in background and from debugging tools its hard to jump into new paraller process.

I put some logs here and there...

It turns out that one of mutant was creating phpunit.humbug.xml with content bellow:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.4/phpunit.xsd" backupGlobals="false" backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" syntaxCheck="false" bootstrap="/tmp/humbug.phpunit.bootstrap.php" cacheTokens="false">

    <testsuites>
        <testsuite name="Test Suite">

        <directory>/home/rolen/php/humbug/tests/Adapter/_files/phpunit</directory></testsuite>
    </testsuites>



<listeners><listener class="\MyBuilder\PhpunitAccelerator\TestListener"><arguments><boolean>true</boolean></arguments></listener><listener class="\Humbug\Phpunit\Listener\TimeCollectorListener"><arguments><object class="\Humbug\Phpunit\Logger\JsonLogger"><arguments><string>/tmp/1514814/phpunit.times.humbug.json</string></arguments></object></arguments></listener></listeners><logging><log type="coverage-php" target="/tmp/1514814/coverage.humbug.php"/><log type="coverage-text" target="/tmp/1514814/coverage.humbug.txt"/></logging><filter><whitelist/></filter></phpunit>

This mutant was not written in humbuglog.txt:

XmlConfiguration:
-        self::$dom->preserveWhiteSpace = false;
+        self::$dom->preserveWhiteSpace = true;

Running phpunit against phpunit.humbug.xml produced:

rolen@rollen:~/php/humbug (master) $ vendor/bin/phpunit -c phpunit.humbug.xml 
PHP Notice:  Trying to get property of non-object in /home/rolen/php/humbug/vendor/phpunit/phpunit/src/Util/XML.php on line 231
PHP Stack trace:
PHP   1. {main}() /home/rolen/php/humbug/vendor/phpunit/phpunit/phpunit:0
PHP   2. PHPUnit_TextUI_Command::main($exit = *uninitialized*) /home/rolen/php/humbug/vendor/phpunit/phpunit/phpunit:36
PHP   3. PHPUnit_TextUI_Command->run($argv = *uninitialized*, $exit = *uninitialized*) /home/rolen/php/humbug/vendor/phpunit/phpunit/src/TextUI/Command.php:103
PHP   4. PHPUnit_TextUI_TestRunner->doRun($suite = *uninitialized*, $arguments = *uninitialized*) /home/rolen/php/humbug/vendor/phpunit/phpunit/src/TextUI/Command.php:151
PHP   5. PHPUnit_TextUI_TestRunner->handleConfiguration($arguments = *uninitialized*) /home/rolen/php/humbug/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:153
PHP   6. PHPUnit_Util_Configuration->getListenerConfiguration() /home/rolen/php/humbug/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:687
PHP   7. PHPUnit_Util_XML::xmlToVariable($element = *uninitialized*) /home/rolen/php/humbug/vendor/phpunit/phpunit/src/Util/Configuration.php:339
PHP Warning:  Invalid argument supplied for foreach() in /home/rolen/php/humbug/vendor/phpunit/phpunit/src/Util/XML.php on line 234
PHP Stack trace:
PHP   1. {main}() /home/rolen/php/humbug/vendor/phpunit/phpunit/phpunit:0
PHP   2. PHPUnit_TextUI_Command::main($exit = *uninitialized*) /home/rolen/php/humbug/vendor/phpunit/phpunit/phpunit:36
PHP   3. PHPUnit_TextUI_Command->run($argv = *uninitialized*, $exit = *uninitialized*) /home/rolen/php/humbug/vendor/phpunit/phpunit/src/TextUI/Command.php:103
PHP   4. PHPUnit_TextUI_TestRunner->doRun($suite = *uninitialized*, $arguments = *uninitialized*) /home/rolen/php/humbug/vendor/phpunit/phpunit/src/TextUI/Command.php:151
PHP   5. PHPUnit_TextUI_TestRunner->handleConfiguration($arguments = *uninitialized*) /home/rolen/php/humbug/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:153
PHP   6. PHPUnit_Util_Configuration->getListenerConfiguration() /home/rolen/php/humbug/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:687
PHP   7. PHPUnit_Util_XML::xmlToVariable($element = *uninitialized*) /home/rolen/php/humbug/vendor/phpunit/phpunit/src/Util/Configuration.php:339
PHPUnit 4.4.5 by Sebastian Bergmann.

Configuration read from /home/rolen/php/humbug/phpunit.humbug.xml

.

Time: 10.25 seconds, Memory: 6.50Mb

OK (1 test, 1 assertion)

Generating code coverage report in PHP format ... done

and /tmp/phpunit.times.humbug.json was affected with content:

{
    "suites": {
        "MM1_MathTest": 0.012728929519653
    },
    "tests": {
        "MM1_MathTest": [
            {
                "title": "testAdds",
                "time": 0.012728929519653
            }
        ]
    }
}

Eureka!
All because of JsonLogger::__construct(), If there is no $target given it will write into /tmp/phpunit.times.humbug.json.

I changed logic in JsonLogger::__construct() and revert this mutant in codebase.
Phpunit: failed with fatal error.
Humbug on humbug: no errors :) And phpunit.times.humbug.json was not changed since first run.

Check code coverage whitelisting variations

Reminder to self on a passing Tweet about failing code coverage generation. Humbug only tells PHPUnit to do it, but our XML config rewrites may be omitting essential info in certain cases.

Humbug does not scream when my tests are failing

While working on my project with humbug in it I found that humbug didn`t stop though I got one failing test.
I reproduced this bug on humbug itself(Running humbug on humbug). See output below:

rolen@rollen:~/php/humbug (master) $ vendor/bin/phpunit
PHPUnit 4.4.5 by Sebastian Bergmann.

Configuration read from /home/rolen/php/humbug/phpunit.xml.dist

............................................................S..  63 / 150 ( 42%)
.............S................................................. 126 / 150 ( 84%)
.......................F

Time: 2.42 seconds, Memory: 12.50Mb

There was 1 failure:

1) Humbug\Test\TempTest::testFail

/home/rolen/php/humbug/tests/TempTest.php:15

FAILURES!
Tests: 150, Assertions: 200, Failures: 1, Skipped: 2.
rolen@rollen:~/php/humbug (master) $ bin/humbug

 _  _            _
| || |_  _ _ __ | |__ _  _ __ _
| __ | || | '  \| '_ \ || / _` |
|_||_|\_,_|_|_|_|_.__/\_,_\__, |
                          |___/
Humbug version 1.0-dev

Humbug running test suite to generate logs and code coverage data...

  149 [==========================================================] 6 secs

Humbug has completed the initial test run successfully.
Tests: 149 Line Coverage: 36.68%

Humbug is analysing source files...

Mutation Testing is commencing on 75 files...
(.: killed, M: escaped, S: uncovered, E: fatal error, T: timed out)

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |   60 ( 4/75)
SSSSSSSSSSSSSSSM..MMM.MM.SSSS.M.MSMMMMSSMMMMSSSSSMMSSEM.MMMM |  120 (11/75)
MMM..M.MTMMMMMM..SMMMMMMSS...MMSSSSSSSSSSSSSSS..SSSSSSSSSSSS |  180 (12/75)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSEEE.EEEE.EEEE.EEEE.EEEE.EEEE.EEE |  240 (19/75)
E.EEEE.EEEE.EEEE.EEEE.EEEEEEEEEEEE.EEEEEEEEEEEE.ESSSSSSSSSSS |  300 (28/75)
SSSSSSSSSSSSMMM.MMMMMMMM.M.SMMMSSSSMM...MM...M.M.......EEEEE |  360 (29/75)
EEEEEEEEEE.EEEE.SSSSSSSSSSSSSSSMMMMMMMM.MMM.MM.MMMSMMM..S... |  420 (30/75)
SSSSSSSSSSSSSSSMMMMMMMM.MMM.MM.MMMSMMM..S...SSSSSSSSSSSSSSSS |  480 (32/75)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSMMMMMMMM.MMM.MM.M |  540 (33/75)
.SMMM..S...SSSSSSSSSSSSSSSSSSSSSSSSSEEEEEEEEEEEEEEEEE.EEEEEE |  600 (34/75)
.EEEEEEEE.EEEE..M.MMMS..M.MMMSM..MMM.MMMS.EEEE.EEEE.EEEEEEEE |  660 (41/75)
E.EEEE.EEEE.EEEEEEEEE............MMM..EEEE.E..SSSSSEEE.EEEE. |  720 (48/75)
E..SSSSSEEE.EEEE.EEEE.EEEE.EEEE.EEEE.EEEE.EEEE.EEEE.E....... |  780 (59/75)
..EEE.E.............EEE.ESSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  840 (62/75)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.. |  900 (63/75)
M.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  960 (71/75)
SSSSMM.M.M..MM.M.SSSSSSSSSSS....M..MMM........M.M........... | 1020 (72/75)
...M....M.M...MM..MM..M..........SSSSSSSSMSSSS

1066 mutations were generated:
     206 mutants were killed
     486 mutants were not covered by tests
     159 covered mutants were not detected
     214 fatal errors were encountered
       1 time outs were encountered

Out of 580 test covered mutations, 73% were detected.
Out of 1066 total mutations, 39% were detected.
Out of 1066 total mutations, 54% were covered by tests.

Remember that some mutants will inevitably be harmless (i.e. false positives).

Humbug results are being logged as JSON to: humbuglog.json
Humbug results are being logged as TEXT to: humbuglog.txt

Time: 4.43 minutes Memory: 13.25MB

The problem is in checking initial tests run. If it hasOks then it don`t checks failures.

I also discovered that if you have little tests in your test harness, than those checks may not be performed.
All because of usage $process->isRunning(). Before method is invoked process may end, and there will be no checks inside while loop.

Removing usleep does not really resolve problem, because isRunning may return false when system may put this process idle.

Mutator Ignore Comment

A feature comment like @codeCoverageIgnoreStart for humbug mutators would be nice.
A ignore comment could avoid errors from mutators.

        /**
         * @humbugMutatorIgnoreStart
         */
        if ($foo instanceof Bar && $bar->getData()) {
            return true;
        }
        /**
         * @humbugMutatorIgnoreEnd
         */

Output file creation fails when /tmp has unreadable directories

I get the following errors with verbose output turned on. The path /tmp/pulse-PKdhtXMmr18n is readable/executable only by root.

Error 1:

  [Symfony\Component\Finder\Exception\AccessDeniedException]                                               
  RecursiveDirectoryIterator::__construct(/tmp/pulse-PKdhtXMmr18n): failed to open dir: Permission denied  

Exception trace:
 () at /home/michael/projects/php-tools/humbug/vendor/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php:84
 Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator->getChildren() at n/a:n/a
 FilterIterator->next() at n/a:n/a
 FilterIterator->next() at n/a:n/a
 FilterIterator->next() at n/a:n/a
 FilterIterator->next() at /home/michael/projects/php-tools/humbug/src/Humbug/Utility/CoverageData.php:105
 Humbug\Utility\CoverageData->cleanup() at /home/michael/projects/php-tools/humbug/src/Humbug/Command/Humbug.php:332
 Humbug\Command\Humbug->execute() at /home/michael/projects/php-tools/humbug/vendor/symfony/console/Symfony/Component/Console/Command/Command.php:253
 Symfony\Component\Console\Command\Command->run() at /home/michael/projects/php-tools/humbug/vendor/symfony/console/Symfony/Component/Console/Application.php:878
 Symfony\Component\Console\Application->doRunCommand() at /home/michael/projects/php-tools/humbug/vendor/symfony/console/Symfony/Component/Console/Application.php:195
 Symfony\Component\Console\Application->doRun() at /home/michael/projects/php-tools/humbug/vendor/symfony/console/Symfony/Component/Console/Application.php:126
 Symfony\Component\Console\Application->run() at /home/michael/projects/php-tools/humbug/bin/humbug:23

Error 2:

 [UnexpectedValueException]                                                                               
  RecursiveDirectoryIterator::__construct(/tmp/pulse-PKdhtXMmr18n): failed to open dir: Permission denied  

Exception trace:
 () at /home/michael/projects/php-tools/humbug/vendor/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php:49
 RecursiveDirectoryIterator->__construct() at /home/michael/projects/php-tools/humbug/vendor/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php:49
 Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator->__construct() at n/a:n/a
 RecursiveDirectoryIterator->getChildren() at /home/michael/projects/php-tools/humbug/vendor/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php:71
 Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator->getChildren() at n/a:n/a
 FilterIterator->next() at n/a:n/a
 FilterIterator->next() at n/a:n/a
 FilterIterator->next() at n/a:n/a
 FilterIterator->next() at /home/michael/projects/php-tools/humbug/src/Humbug/Utility/CoverageData.php:105
 Humbug\Utility\CoverageData->cleanup() at /home/michael/projects/php-tools/humbug/src/Humbug/Command/Humbug.php:332
 Humbug\Command\Humbug->execute() at /home/michael/projects/php-tools/humbug/vendor/symfony/console/Symfony/Component/Console/Command/Command.php:253
 Symfony\Component\Console\Command\Command->run() at /home/michael/projects/php-tools/humbug/vendor/symfony/console/Symfony/Component/Console/Application.php:878
 Symfony\Component\Console\Application->doRunCommand() at /home/michael/projects/php-tools/humbug/vendor/symfony/console/Symfony/Component/Console/Application.php:195
 Symfony\Component\Console\Application->doRun() at /home/michael/projects/php-tools/humbug/vendor/symfony/console/Symfony/Component/Console/Application.php:126
 Symfony\Component\Console\Application->run() at /home/michael/projects/php-tools/humbug/bin/humbug:23

Sneeky Mutants escaping when they shouldn't

I ran humbug over a (private) code base and the results were "disastrous" barely 10% of mutants were caught by my test suite. Looking at the log file, I went and added some of the mutations to the files manually in order to help improve coverage, however the tests now failed.

The tests in question use a PHPunit data provider to provide different cases to test, I'm wondering if humbug's handling of data providers might be a bit wonky?

Ability to run PHPUnit from vendor

As the title says, I've got PHPUnit set up as a require-dev, but it seems running Humbug it was trying to use (parts of??) the PEAR installed version.

Particularly, this is what happened:

 _  _            _
| || |_  _ _ __ | |__ _  _ __ _
| __ | || | '  \| '_ \ || / _` |
|_||_|\_,_|_|_|_|_.__/\_,_\__, |
                          |___/ 
Humbug version 1.0-dev

Humbug running test suite to generate logs and code coverage data...

    0 [>---------------------------------------------------------]  1 sec

 Tests must be in a fully passing state before Humbug is run.                                                                                                                                                                                                                          
 Incomplete, skipped or risky tests are allowed.                                                                                                                                                                                                                                       
 The testing framework reported an exit code of 255.                                                                                                                                                                                                                                   
 Stdout:                                                                                                                                                                                                                                                                               
    >                                                                                                                                                                                                                                                                                  
    > Fatal error: Class PHPUnit_Util_Log_TAP contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (PHPUnit_Framework_TestListener::addRiskyTest) in /Applications/XAMPP/xamppfiles/lib/php/PHPUnit/Util/Log/TAP.php on line 253      
    >                                                                                                                                                                                                                                                                                  
    > Call Stack:                                                                                                                                                                                                                                                                      
    >     0.0001     218032   1. {main}() -:0                                                                                                                                                                                                                                          
    >     0.0028     531552   2. Humbug\Adapter\Phpunit::main() -:5                                                                                                                                                                                                                    
    >     0.0036     683728   3. PHPUnit_TextUI_Command->run() /Users/maarten/Documents/GitHub/phpta/vendor/humbug/humbug/src/Adapter/Phpunit.php:149                                                                                                                                  
    >     0.0036     686376   4. PHPUnit_TextUI_Command->handleArguments() /Users/maarten/Documents/GitHub/phpta/vendor/phpunit/phpunit/src/TextUI/Command.php:114                                                                                                                     
    >     0.0528    4700264   5. PHPUnit_TextUI_Command->handlePrinter() /Users/maarten/Documents/GitHub/phpta/vendor/phpunit/phpunit/src/TextUI/Command.php:676                                                                                                                       
    >     0.0538    4734120   6. require('/Applications/XAMPP/xamppfiles/lib/php/PHPUnit/Util/Log/TAP.php') /Users/maarten/Documents/GitHub/phpta/vendor/phpunit/phpunit/src/TextUI/Command.php:756           

As you can see, it's trying to include a file from the PEAR directory, FROM the vendor directory. I worked around this by removing the PEAR directory from my include_path.

Reports in different formats are inconsistent

Here is the report summary in different format, for the same run of Humbug on Mink:

CLI output:

119 mutations were generated:
      95 mutants were killed
       0 mutants were not covered by tests
      19 covered mutants were not detected
       4 fatal errors were encountered
       1 time outs were encountered

Out of 119 test covered mutations, 84% were detected.
Out of 119 total mutations, 84% were detected.
Out of 119 total mutations, 100% were covered by tests.

Text report:

119 mutations were generated:
      95 mutants were killed
       0 mutants were not covered by tests
      19 covered mutants were not detected
       4 fatal errors were encountered
       1 time outs were encountered

Out of 119 test covered mutations, 84% were detected.
Out of 119 total mutations, 84% were detected.
Out of 119 total mutations, 100% were covered by tests.

JSON report:

{
    "summary": {
        "total": 119,
        "kills": 95,
        "escapes": 19,
        "errors": 4,
        "timeouts": 1,
        "notests": 4,
        "covered_score": 87,
        "combined_score": 84,
        "mutation_coverage": 97
    }
}

While the CLI and text report are identical, the JSON report does not match (see how it reports 4 mutations not covered by tests). There is a bug somewhere in humbug...

Humbug breaks on Symfony when test tries to boot a new kernel

  • Using Symfony 2.6
  • Using Humbug from master branch

Some of my unit tests need to use the Symfony Container.
For that, I have use the static::bootKernel(); from Symfony\Bundle\FrameworkBundle\Test\KernelTestCase.
When running PhpUnit, it works fine.
But, when using Humbug, I get the " tests must be fully passing" message.

I've been trying to identify why my tests fail, and I think the problem is here:
https://github.com/symfony/symfony/blob/2.6/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php#L44

It seems Humbug adapter isn't fully configuring Phpunit.

Fatal error: Class 'PHPUnit_TextUI_Command' not found

Hi!

Wanted to give humbug a try, got the following error:

<warning>Stdout: \n    >
    > Fatal error: Class 'PHPUnit_TextUI_Command' not found in /path/to/geocoder-php/Geocoder/vendor/humbug/humbug/src/Humbug/Adapter/Phpunit.php on line 157
    >
    > Call Stack:
    >     0.0003     219672   1. {main}() -:0
    >     0.0060     413624   2. Humbug\Adapter\Phpunit::main() -:8
    >
    > \n</warning>

Requiring composer require phpunit/phpunit solved the issue, so I think a dependency is missing in humbug, but maybe requiring phpunit/phpunit is too much.

Cheers,
William

How to debug humbug?

Still can't get humbug work for me. Ends up with

Humbug is analysing source files...

Mutation Testing is commencing...
(.: killed, M: escaped, S: uncovered, E: fatal error, T: timed out)

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |   60 (14/72)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  120 (42/72)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  180 (52/72)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  240 (55/72)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  300 (63/72)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  360 (69/72)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS |  420 (69/72)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS


  [Symfony\Component\Finder\Exception\AccessDeniedException]
  RecursiveDirectoryIterator::__construct(/tmp/phpcs-pre-receive-hook.UjazU6RA): failed to open dir: Permission denied






  [UnexpectedValueException]
  RecursiveDirectoryIterator::__construct(/tmp/phpcs-pre-receive-hook.UjazU6RA): failed to open dir: Permission denied

I want to know why humbug needs that file, but debugging it is kinda hard :) Is there any debug mode or something that helps to get at least the line number of the error, not only exception type and a message? Or please show me that place in code where exceptions are catched, so I can get more information about this issue.

Humbug stops, saying tests must pass, but they do

I'm getting a weird bug in the latest version, Humbug just stops and tell me my test must pass for it to run properly, but when I run the tests they all pass properly:

$ humbug
Humbug running test suite to generate logs and code coverage data...

   21 [==========================================================] 33 secs

<warning>Tests must be in a fully passing state before Humbug is run.</warning>
<warning>Incomplete, skipped or risky tests are allowed.</warning>
<warning>The testing framework reported an exit code of 143.</warning>
<warning>The testing framework ran into a failure or error. Refer to out below.</warning>
<warning>Stdout: \n    > ok 22 - Rocketeer\Abstracts\AbstractTaskTest::testCanGetOptionsViaCommandOrSetters
    > not ok 23 - Error: Rocketeer\Abstracts\Strategies\AbstractDependenciesStrategyTest::testCanShareDependenciesFolder
    > \n</warning>
$ phpunit
PHPUnit 4.4.1 by Sebastian Bergmann.

Configuration read from /Users/anahkiasen/Sites/rocketeers/rocketeer/phpunit.xml

...............................................................  63 / 323 ( 19%)
............................................................... 126 / 323 ( 39%)
............................................................... 189 / 323 ( 58%)
............................................................... 252 / 323 ( 78%)
............................................................... 315 / 323 ( 97%)
........

Time: 38.27 seconds, Memory: 389.75Mb

OK (323 tests, 400 assertions)

The progress bar is complete so I assumed this was just a warning but the humbug log is empty and just contains the error above

self-update fails: Current version check has failed. Please try again.

Using Ubuntu 14.04, I receive the error Current version check has failed. Please try again. when running ./humbug.phar self-update

How to reproduce:

wget https://padraic.github.io/humbug/downloads/humbug.phar
chmod +x humbug.phar
wget https://padraic.github.io/humbug/downloads/humbug.phar.pubkey
./humbug.phar self-update

Fix descriptions in Mutator Classes

The "getMutation" Description is often a copy/paste. It is a little bit frustrating to see a false comment
and manually have to find out what the intention really is.
Like "Float" Mutator => description = "Replace (return $this;) with (return null;)"

Greetings Jan

Invalid format of composer.json file.

When I ran composer install, I got this error.

$composer install



  [UnexpectedValueException]
  Could not parse version constraint ^1.0.2: Invalid version string "^1.0.2"



install [--prefer-source] [--prefer-dist] [--dry-run] [--dev] [--no-dev] [--no-plugins] [--no-custom-installers] [--no-scripts] [--no-progress] [-v|vv|vvv|--verbose] [-o|--optimize-autoloader] [--ignore-platform-reqs] [packages1] ... [packagesN]

"Division by zero" error

I'm not sure if it is an error that is expected. I've run vendor/bin/humbug and got :

$ cd ~/www/app
$ vendor/bin/humbug

 _  _            _              
| || |_  _ _ __ | |__ _  _ __ _ 
| __ | || | '  \| '_ \ || / _` |
|_||_|\_,_|_|_|_|_.__/\_,_\__, |
                          |___/ 
Humbug version 1.0-dev

Humbug running test suite to generate logs and code coverage data...

Humbug has completed the initial test run successfully.

Humbug is analysing source files...

Mutation Testing is commencing...
(.: killed, M: escaped, S: uncovered, E: fatal error, T: timed out)



0 mutations were generated:
       0 mutants were killed
       0 mutants were never detected
       0 fatal errors were encountered
       0 time outs were encountered
       0 mutants were not covered by any test

PHP Warning:  Division by zero in /Users/samsonasik/www/app/vendor/humbug/humbug/src/Humbug/Renderer/Text.php on line 203
PHP Stack trace:
PHP   1. {main}() /Users/samsonasik/www/app/vendor/humbug/humbug/bin/humbug:0
PHP   2. Symfony\Component\Console\Application->run() /Users/samsonasik/www/app/vendor/humbug/humbug/bin/humbug:23
PHP   3. Symfony\Component\Console\Application->doRun() /Users/samsonasik/www/app/vendor/symfony/console/Symfony/Component/Console/Application.php:126
PHP   4. Symfony\Component\Console\Application->doRunCommand() /Users/samsonasik/www/app/vendor/symfony/console/Symfony/Component/Console/Application.php:195
PHP   5. Symfony\Component\Console\Command\Command->run() /Users/samsonasik/www/app/vendor/symfony/console/Symfony/Component/Console/Application.php:874
PHP   6. Humbug\Command\Humbug->execute() /Users/samsonasik/www/app/vendor/symfony/console/Symfony/Component/Console/Command/Command.php:252
PHP   7. Humbug\Renderer\Text->renderSummaryReport() /Users/samsonasik/www/app/vendor/humbug/humbug/src/Humbug/Command/Humbug.php:269

Warning: Division by zero in /Users/samsonasik/www/app/vendor/humbug/humbug/src/Humbug/Renderer/Text.php on line 203

my app structure is :

app
   src
   test
   phpunit.xml

Phar builds likely broken

Was working, but last few builds showing "inappropriate ioctl" error arising from PHPUnit when it tries the first initial run.

inconsistent detection of fatal errors

Both $process->isSuccessful() and strlen($result['stderr']) == 0 are used to track whether there was a fatal error or not, but those aren't always equivalent. In my case, stderr is showing up as NULL. So the tracker marks it as M-escaped, but the final report (correctly) marks them as fatal errors. (I'm pretty sure that the unit tests in these cases silently die due to how the mutation affects a weirdly mocked mysqli object.)

I patched my version with the following (although not sure that $result needs so many mutually exclusive keys given that it's passed to a single function and then discarded):

diff --git a/src/Humbug/Command/Humbug.php b/src/Humbug/Command/Humbug.php
index dea119f..1a709ce 100644
--- a/src/Humbug/Command/Humbug.php
+++ b/src/Humbug/Command/Humbug.php
@@ -250,9 +250,10 @@ class Humbug extends Command
                      * Define the result for each process
                      */
                     $result = [
-                        'passed'    => true,
-                        'timeout'   => false,
-                        'stderr'    => $process->getErrorOutput(),
+                        'passed'     => true,
+                        'successful' => $process->isSuccessful(),
+                        'timeout'    => false,
+                        'stderr'     => $process->getErrorOutput(),
                     ];

                     if ($group->timedOut($tracker)) {
@@ -274,7 +275,7 @@ class Humbug extends Command
                     if ($result['timeout'] === true) {
                         $countMutantTimeouts++;
                         $mutantTimeouts[] = $mutant;
-                    } elseif (!$process->isSuccessful()) {
+                    } elseif ($result['successful'] === false) {
                         $countMutantErrors++;
                         $mutantErrors[] = $mutant;
                     } elseif ($result['passed'] === false) {
diff --git a/src/Humbug/Renderer/Text.php b/src/Humbug/Renderer/Text.php
index e63cb1b..73bb756 100644
--- a/src/Humbug/Renderer/Text.php
+++ b/src/Humbug/Renderer/Text.php
@@ -145,7 +145,7 @@ class Text
         $this->progressCount++;
         if ($result['timeout'] === true) {
             $this->write('<fg=cyan;options=bold>T</fg=cyan;options=bold>', false);
-        } elseif (strlen($result['stderr']) > 0) {
+        } elseif ($result['successful'] === false) {
             $this->write('<fg=yellow;options=bold>E</fg=yellow;options=bold>', false);
         } elseif ($result['passed'] === true) {
             $this->write('<fg=red;options=bold>M</fg=red;options=bold>', false);

Running unit tests extended from Laravel5's TestCase MutantKills not being counted.

This is the escape from the humbug log:

--- Original
+++ New
@@ @@
$experiment = $this->active[$uuid];

  •    $experiment->a_conversions++;
    
  •    $experiment->a_conversions--;
     $experiment->save();
    
    }

When I manually mutate my code and run my unit test it fails.

.......F..................

Time: 4.16 seconds, Memory: 20.50Mb

There was 1 failure:

  1. EloquentExperimentRepositoryTest::testConvertAIncrementsAConversions
    Failed asserting that -1 matches expected 1.

When I run Humbug:

Mutation Testing is commencing on 40 files...
(.: killed, M: escaped, S: uncovered, E: fatal error, T: timed out)

SSSSSSSSSSSMMMMMMMMMSMMMMMMMMMMMMMMMSSSSSSS

43 mutations were generated:
0 mutants were killed
19 mutants were not covered by tests
24 covered mutants were not detected
0 fatal errors were encountered
0 time outs were encountered

Out of 24 test covered mutations, 0% were detected.
Out of 43 total mutations, 0% were detected.
Out of 43 total mutations, 56% were covered by tests.

Humbug Reports Escaped when more than one test suite is ran via phpunit.xml

example phpunit.xml

<phpunit bootstrap="test-bootstrap.php"
    cacheTokens="true"
    colors="true"
    processIsolation="false">
    <testsuites>
        <testsuite name="App Test Suite">
            <directory>module/App/tests</directory>
        </testsuite>
        <testsuite name="Common Test Suite">
            <directory>module/Common/tests</directory>
        </testsuite>
    </testsuites>
    <filter>
      <whitelist>
          <directory suffix=".php">module</directory>
          <exclude>
              <directory suffix=".php">module/*/config</directory>
              <directory suffix=".php">module/*/tests</directory>
              <directory suffix="Module.php">module/*</directory>
              <directory>vendor/*</directory>
        </exclude>
      </whitelist>
    </filter>
</phpunit>

example humbug.json

{
    "timeout": 10,
    "source": {
        "directories": [

            "module/Common/src"
        ],
        "excludes": [
        ]
    },
    "logs": {
        "text": "humbuglog.txt"
    }
}

Output:-

 _  _            _
| || |_  _ _ __ | |__ _  _ __ _
| __ | || | '  \| '_ \ || / _` |
|_||_|\_,_|_|_|_|_.__/\_,_\__, |
                          |___/ 
Humbug version 1.0-dev

Humbug running test suite to generate logs and code coverage data...

  567 [==========================================================] 15 secs

Humbug has completed the initial test run successfully.
Tests: 567 Line Coverage: 99.16%

Humbug is analysing source files...

Mutation Testing is commencing on 202 files...
(.: killed, M: escaped, S: uncovered, E: fatal error, T: timed out)

MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM |   60 ( 60/202)
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM |  120 ( 98/202)
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM |  180 (129/202)
MMMMMMMMMMMMMSMMMMMMMMMMSSSSSSSSSSSSMMMMMMSSSSSSSSSSSSSSSSSS |  240 (153/202)
SSSSSSSSSSSSSSSSSSSMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM |  300 (165/202)
MMMMMSSSMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMSMMMMMMMMMMMMMSMMM |  360 (186/202)
MMMMMMMSSSSSSSMMMMMMMMMMMMMMMMMMMMM

395 mutations were generated:
       0 mutants were killed
      62 mutants were not covered by tests
     333 covered mutants were not detected
       0 fatal errors were encountered
       0 time outs were encountered

Out of 333 test covered mutations, 0% were detected.
Out of 395 total mutations, 0% were detected.
Out of 395 total mutations, 84% were covered by tests.

Remember that some mutants will inevitably be harmless (i.e. false positives).

Humbug results are being logged as TEXT to: humbuglog.txt

Time: 1.45 minutes Memory: 10.50MB

If I change my phpunit.xml to only have one suite.

<phpunit bootstrap="test-bootstrap.php"
    cacheTokens="true"
    colors="true"
    processIsolation="false">
    <testsuites>
        <testsuite name="Common Test Suite">
            <directory>module/Common/tests</directory>
        </testsuite>
    </testsuites>
    <filter>
      <whitelist>
          <directory suffix=".php">module</directory>
          <exclude>
              <directory suffix=".php">module/*/config</directory>
              <directory suffix=".php">module/*/tests</directory>
              <directory suffix="Module.php">module/*</directory>
              <directory>vendor/*</directory>
        </exclude>
      </whitelist>
    </filter>
</phpunit>

My output changes to have Mutants killed.


 _  _            _
| || |_  _ _ __ | |__ _  _ __ _
| __ | || | '  \| '_ \ || / _` |
|_||_|\_,_|_|_|_|_.__/\_,_\__, |
                          |___/ 
Humbug version 1.0-dev

Humbug running test suite to generate logs and code coverage data...

  547 [==========================================================] 14 secs

Humbug has completed the initial test run successfully.
Tests: 547 Line Coverage: 99.16%

Humbug is analysing source files...

Mutation Testing is commencing on 202 files...
(.: killed, M: escaped, S: uncovered, E: fatal error, T: timed out)

MM.EE...M.M.E.M.MMMMMM..MEEE..MMM...M.M.MMM..EE............. |   60 ( 60/202)
MMMMM..MMMMMMMMMMEEMMMMMM.........M..M..............MMMMMMM. |  120 ( 98/202)
.........M......MM.M.....MM.M.M.M.M.M.....M.EM.EEE..M...M... |  180 (129/202)
..M.......M..SM.......M.SSSSSSSSSSSSEMM...SSSSSSSSSSSSSSSSSS |  240 (153/202)
SSSSSSSSSSSSSSSSSSS....T.......MM......M......M.M...M...M... |  300 (165/202)
...MMSSS.MMME.MMM.M.MMM....MMM.......E....SM....ME......S... |  360 (186/202)
......MSSSSSSS..MM.M.....MM..MMMMMM

395 mutations were generated:
     207 mutants were killed
      62 mutants were not covered by tests
     107 covered mutants were not detected
      18 fatal errors were encountered
       1 time outs were encountered

Out of 333 test covered mutations, 68% were detected.
Out of 395 total mutations, 57% were detected.
Out of 395 total mutations, 84% were covered by tests.

Remember that some mutants will inevitably be harmless (i.e. false positives).

Humbug results are being logged as TEXT to: humbuglog.txt

Time: 1.73 minutes Memory: 8.75MB

It seems that having multiple in the test suite causes these issues. If I add the extra paths to the humbug.json, I will get uncovered for the things that aren't in the coverage.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.