mutant 0.8.19 → 0.8.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +46 -8
- data/Changelog.md +4 -0
- data/Gemfile.lock +10 -12
- data/README.md +22 -250
- data/config/flay.yml +1 -1
- data/docs/concurrency.md +39 -0
- data/docs/known-problems.md +44 -0
- data/docs/limitations.md +50 -0
- data/docs/mutant-minitest.md +147 -0
- data/docs/mutant-rspec.md +62 -0
- data/docs/nomenclature.md +82 -0
- data/docs/reading-reports.md +74 -0
- data/lib/mutant.rb +4 -3
- data/lib/mutant/env.rb +2 -2
- data/lib/mutant/expression/namespace.rb +3 -1
- data/lib/mutant/runner/sink.rb +2 -2
- data/lib/mutant/timer.rb +21 -0
- data/lib/mutant/version.rb +1 -1
- data/mutant-minitest.gemspec +22 -0
- data/mutant.gemspec +4 -5
- data/spec/integration/mutant/corpus_spec.rb +1 -7
- data/spec/integration/mutant/minitest_spec.rb +10 -0
- data/spec/integration/mutant/rspec_spec.rb +1 -1
- data/spec/integrations.yml +14 -0
- data/spec/shared/framework_integration_behavior.rb +8 -5
- data/spec/support/corpus.rb +20 -14
- data/spec/unit/mutant/clock_monotonic_spec.rb +52 -0
- data/spec/unit/mutant/env_spec.rb +2 -2
- data/spec/unit/mutant/expression/namespace/recursive_spec.rb +1 -1
- data/spec/unit/mutant/integration/rspec_spec.rb +1 -1
- data/spec/unit/mutant/reporter/cli_spec.rb +1 -1
- data/spec/unit/mutant/runner/sink_spec.rb +1 -1
- data/test_app/Gemfile.minitest +6 -0
- data/test_app/{Gemfile.rspec3.6 → Gemfile.rspec3.8} +2 -2
- data/test_app/test/unit/test_app/literal_test.rb +16 -0
- metadata +23 -26
- data/spec/rcov.opts +0 -7
- data/spec/support/rb_bug.rb +0 -18
- data/test_app/Gemfile.rspec3.4 +0 -7
- data/test_app/Gemfile.rspec3.5 +0 -7
data/config/flay.yml
CHANGED
data/docs/concurrency.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
Concurrency
|
2
|
+
===========
|
3
|
+
|
4
|
+
By default, mutant will test mutations in parallel by running up
|
5
|
+
to one process for each core on your system. You can control the
|
6
|
+
number of processes created using the `-j/--jobs` argument.
|
7
|
+
|
8
|
+
Mutant forks a new process for each mutation to be tested to prevent side
|
9
|
+
affects in your specs and the lack of thread safety in integrations from
|
10
|
+
impacting the results.
|
11
|
+
|
12
|
+
Database
|
13
|
+
--------
|
14
|
+
|
15
|
+
If the code under test relies on a database, you may experience problems
|
16
|
+
when running mutant because of conflicting data in the database. For
|
17
|
+
example, if you have a test like this:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
resource = MyModel.create!(...)
|
21
|
+
expect(MyModel.first.name).to eql(resource.name)
|
22
|
+
```
|
23
|
+
|
24
|
+
It might fail if some other test wrote a record to the `MyModel` table
|
25
|
+
at the same time as this test was executed. (It would find the MyModel
|
26
|
+
record created by the other test.) Most of these issues can be fixed
|
27
|
+
by writing more specific tests. Here is a concurrent safe version of
|
28
|
+
the same test:
|
29
|
+
|
30
|
+
```
|
31
|
+
resource = MyModel.create!(...)
|
32
|
+
expect(MyModel.find_by_id(m.id).name).to eql(resource.name)
|
33
|
+
```
|
34
|
+
|
35
|
+
An alternative is to try wrapping each test into an enclosing transaction.
|
36
|
+
|
37
|
+
Note that some databases, SQLite in particular, are not designed for
|
38
|
+
concurrent access and will fail if used in this manner. If you are
|
39
|
+
using SQLite, you should set the `--jobs` to 1.
|
@@ -0,0 +1,44 @@
|
|
1
|
+
Known Problems
|
2
|
+
==============
|
3
|
+
|
4
|
+
Mutations with Infinite Runtimes
|
5
|
+
---------------------------------
|
6
|
+
|
7
|
+
Occasionally mutant will produce a mutation with an infinite runtime. When this happens
|
8
|
+
mutant will look like it is running indefinitely without killing a remaining mutation. To
|
9
|
+
avoid mutations like this, consider adding a timeout around your tests. For example, in
|
10
|
+
RSpec you can add the following to your `spec_helper`:
|
11
|
+
```ruby
|
12
|
+
config.around(:each) do |example|
|
13
|
+
Timeout.timeout(5, &example)
|
14
|
+
end
|
15
|
+
```
|
16
|
+
which will fail specs which run for longer than 5 seconds.
|
17
|
+
|
18
|
+
The Crash / Stuck Problem (MRI)
|
19
|
+
-------------------------------
|
20
|
+
|
21
|
+
Mutations generated by mutant can cause MRI to enter VM states its not prepared for.
|
22
|
+
All MRI versions > 1.9 and < 2.2.1 are affected by this depending on your compiler flags,
|
23
|
+
compiler version, and OS scheduling behavior.
|
24
|
+
|
25
|
+
This can have the following unintended effects:
|
26
|
+
|
27
|
+
* MRI crashes with a segfault. Mutant kills each mutation in a dedicated fork to isolate
|
28
|
+
the mutations side effects when this fork terminates abnormally (segfault) mutant
|
29
|
+
counts the mutation as killed.
|
30
|
+
|
31
|
+
* MRI crashes with a segfault and gets stuck when handling the segfault.
|
32
|
+
Depending on the number of active kill jobs mutant might appear to continue normally until
|
33
|
+
all workers are stuck into this state when it begins to hang.
|
34
|
+
Currently mutant must assume that your test suite simply not terminated yet as from the outside
|
35
|
+
(parent process) the difference between a long running test and a stuck MRI is not observable.
|
36
|
+
Its planned to implement a timeout enforced from the parent process, but ideally MRI simply gets fixed.
|
37
|
+
|
38
|
+
References:
|
39
|
+
|
40
|
+
* [MRI fix](https://github.com/ruby/ruby/commit/8fe95fea9d238a6deb70c8953ceb3a28a67f4636)
|
41
|
+
* [MRI backport to 2.2.1](https://github.com/ruby/ruby/commit/8fe95fea9d238a6deb70c8953ceb3a28a67f4636)
|
42
|
+
* [Mutant issue](https://github.com/mbj/mutant/issues/265)
|
43
|
+
* [Upstream bug redmine](https://bugs.ruby-lang.org/issues/10460)
|
44
|
+
* [Upstream bug github](https://github.com/ruby/ruby/pull/822)
|
data/docs/limitations.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
Limitations
|
2
|
+
===========
|
3
|
+
|
4
|
+
Subject
|
5
|
+
-------
|
6
|
+
|
7
|
+
Mutant cannot emit mutations for some subjects.
|
8
|
+
|
9
|
+
* methods defined within a closure. For example, methods defined using `module_eval`, `class_eval`,
|
10
|
+
`define_method`, or `define_singleton_method`:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
class Example
|
14
|
+
class_eval do
|
15
|
+
def example1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module_eval do
|
20
|
+
def example2
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
define_method(:example3) do
|
25
|
+
end
|
26
|
+
|
27
|
+
define_singleton_method(:example4) do
|
28
|
+
end
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
32
|
+
* singleton methods not defined on a constant or `self`
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
class Foo
|
36
|
+
def self.bar; end # ok
|
37
|
+
def Foo.baz; end # ok
|
38
|
+
|
39
|
+
myself = self
|
40
|
+
def myself.qux; end # cannot mutate
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
* methods defined with eval:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class Foo
|
48
|
+
class_eval('def bar; end') # cannot mutate
|
49
|
+
end
|
50
|
+
```
|
@@ -0,0 +1,147 @@
|
|
1
|
+
mutant-minitest
|
2
|
+
===============
|
3
|
+
|
4
|
+
Before starting with mutant its recommended to understand the
|
5
|
+
[nomenclature](/docs/nomenclature.md).
|
6
|
+
|
7
|
+
## Setup
|
8
|
+
|
9
|
+
To add mutant to your minitest code base you need to:
|
10
|
+
|
11
|
+
1. Add `mutant-minitest` as development dependency to your `Gemfile` or `.gemspec`
|
12
|
+
|
13
|
+
This may look like:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
# A gemfile
|
17
|
+
gem 'mutant-minitest'
|
18
|
+
```
|
19
|
+
|
20
|
+
2. Add `require 'mutant/minitest/coverage'` to your test environment (example to your `test/test_helper.rb`)
|
21
|
+
|
22
|
+
Example:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
require 'minitest/autorun'
|
26
|
+
require 'mutant/minitest/coverage'
|
27
|
+
|
28
|
+
class YourTestBaseClass < MiniTest::Test
|
29
|
+
# ...
|
30
|
+
```
|
31
|
+
|
32
|
+
3. Add `.cover` call sides to your test suite to mark them as eligible for killing mutations in subjects.
|
33
|
+
|
34
|
+
Example:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
class YourLibrarySomeClassTest < YourTestBaseClass
|
38
|
+
cover 'YourLibrary::SomeClass*' # tells mutant which subjects this tests should cover
|
39
|
+
# ...
|
40
|
+
```
|
41
|
+
|
42
|
+
4. Run mutant against the minitest integration
|
43
|
+
|
44
|
+
```sh
|
45
|
+
bundle exec mutant --include lib --require 'your_library.rb' --use minitest -- 'YourLibrary*'
|
46
|
+
```
|
47
|
+
|
48
|
+
## Run through example
|
49
|
+
|
50
|
+
This uses [mbj/auom](https://github.com/mbj/auom) a small library that
|
51
|
+
has 100% mutation coverage. Its tests execute very fast and do not have any IO
|
52
|
+
so its a good playground example to interact with.
|
53
|
+
|
54
|
+
All the setup described above is already done.
|
55
|
+
|
56
|
+
```sh
|
57
|
+
git clone https://github.com/mbj/auom
|
58
|
+
bundle install # gemfile references mutant-minitest already
|
59
|
+
bundle exec mutant --include lib --require auom --use minitest -- 'AUOM*'
|
60
|
+
```
|
61
|
+
|
62
|
+
This prints a report like:
|
63
|
+
|
64
|
+
```sh
|
65
|
+
Mutant configuration:
|
66
|
+
Matcher: #<Mutant::Matcher::Config match_expressions: [AUOM*]>
|
67
|
+
Integration: Mutant::Integration::Minitest
|
68
|
+
Jobs: 8
|
69
|
+
Includes: ["lib"]
|
70
|
+
Requires: ["auom"]
|
71
|
+
Subjects: 23
|
72
|
+
Mutations: 1003
|
73
|
+
Results: 1003
|
74
|
+
Kills: 1003
|
75
|
+
Alive: 0
|
76
|
+
Runtime: 9.68s
|
77
|
+
Killtime: 3.80s
|
78
|
+
Overhead: 154.30%
|
79
|
+
Mutations/s: 103.67
|
80
|
+
Coverage: 100.00%
|
81
|
+
```
|
82
|
+
|
83
|
+
Now lets try adding some redundant (or unspecified) code:
|
84
|
+
|
85
|
+
```sh
|
86
|
+
patch -p1 <<'PATCH'
|
87
|
+
--- a/lib/auom/unit.rb
|
88
|
+
+++ b/lib/auom/unit.rb
|
89
|
+
@@ -170,7 +170,7 @@ module AUOM
|
90
|
+
# TODO: Move defaults coercions etc to .build method
|
91
|
+
#
|
92
|
+
def self.new(scalar, numerators = nil, denominators = nil)
|
93
|
+
- scalar = rational(scalar)
|
94
|
+
+ scalar = rational(scalar) if true
|
95
|
+
|
96
|
+
scalar, numerators = resolve([*numerators], scalar, :*)
|
97
|
+
scalar, denominators = resolve([*denominators], scalar, :/)
|
98
|
+
PATCH
|
99
|
+
```
|
100
|
+
|
101
|
+
Running mutant again prints the following:
|
102
|
+
|
103
|
+
```
|
104
|
+
AUOM::Unit.new:/home/mrh-dev/auom/lib/auom/unit.rb:172
|
105
|
+
- minitest:AUOMTest::ClassMethods::New#test_reduced_unit
|
106
|
+
- minitest:AUOMTest::ClassMethods::New#test_normalized_denominator_scalar
|
107
|
+
- minitest:AUOMTest::ClassMethods::New#test_normalized_numerator_unit
|
108
|
+
- minitest:AUOMTest::ClassMethods::New#test_incompatible_scalar
|
109
|
+
- minitest:AUOMTest::ClassMethods::New#test_integer
|
110
|
+
- minitest:AUOMTest::ClassMethods::New#test_sorted_numerator
|
111
|
+
- minitest:AUOMTest::ClassMethods::New#test_unknown_unit
|
112
|
+
- minitest:AUOMTest::ClassMethods::New#test_rational
|
113
|
+
- minitest:AUOMTest::ClassMethods::New#test_normalized_numerator_scalar
|
114
|
+
- minitest:AUOMTest::ClassMethods::New#test_sorted_denominator
|
115
|
+
- minitest:AUOMTest::ClassMethods::New#test_normalized_denominator_unit
|
116
|
+
evil:AUOM::Unit.new:/home/mrh-dev/auom/lib/auom/unit.rb:172:cd9ee
|
117
|
+
@@ -1,9 +1,7 @@
|
118
|
+
def self.new(scalar, numerators = nil, denominators = nil)
|
119
|
+
- if true
|
120
|
+
- scalar = rational(scalar)
|
121
|
+
- end
|
122
|
+
+ scalar = rational(scalar)
|
123
|
+
scalar, numerators = resolve([*numerators], scalar, :*)
|
124
|
+
scalar, denominators = resolve([*denominators], scalar, :/)
|
125
|
+
super(scalar, *[numerators, denominators].map(&:sort)).freeze
|
126
|
+
end
|
127
|
+
-----------------------
|
128
|
+
Mutant configuration:
|
129
|
+
Matcher: #<Mutant::Matcher::Config match_expressions: [AUOM*]>
|
130
|
+
Integration: Mutant::Integration::Minitest
|
131
|
+
Jobs: 8
|
132
|
+
Includes: ["lib"]
|
133
|
+
Requires: ["auom"]
|
134
|
+
Subjects: 23
|
135
|
+
Mutations: 1009
|
136
|
+
Results: 1009
|
137
|
+
Kills: 1008
|
138
|
+
Alive: 1
|
139
|
+
Runtime: 9.38s
|
140
|
+
Killtime: 3.47s
|
141
|
+
Overhead: 170.06%
|
142
|
+
Mutations/s: 107.60
|
143
|
+
Coverage: 99.90%
|
144
|
+
```
|
145
|
+
|
146
|
+
This shows mutant detected the redundant alive conditional.
|
147
|
+
Feel free to also remove some tests. Or do other modifications to either tests or code.
|
@@ -0,0 +1,62 @@
|
|
1
|
+
mutant-rspec
|
2
|
+
============
|
3
|
+
|
4
|
+
The integration into rspec.
|
5
|
+
|
6
|
+
Install `mutant-rspec` and use the `--use rspec` switch in your mutant command line.
|
7
|
+
|
8
|
+
```sh
|
9
|
+
bundle exec mutant --include lib --require 'your_code' --use rspec -- 'YourCode*'
|
10
|
+
```
|
11
|
+
|
12
|
+
Examples
|
13
|
+
--------
|
14
|
+
|
15
|
+
```
|
16
|
+
cd virtus
|
17
|
+
# Run mutant on virtus namespace
|
18
|
+
bundle exec mutant --include lib --require virtus --use rspec Virtus*
|
19
|
+
# Run mutant on specific virtus class
|
20
|
+
bundle exec mutant --include lib --require virtus --use rspec Virtus::Attribute
|
21
|
+
# Run mutant on specific virtus class method
|
22
|
+
bundle exec mutant --include lib --require virtus --use rspec Virtus::Attribute.build
|
23
|
+
# Run mutant on specific virtus instance method
|
24
|
+
bundle exec mutant --include lib --require virtus --use rspec Virtus::Attribute#type
|
25
|
+
```
|
26
|
+
|
27
|
+
Test-Selection
|
28
|
+
--------------
|
29
|
+
|
30
|
+
Mutation testing is slow. The key to making it fast is selecting the correct
|
31
|
+
set of tests to run. Mutant currently supports the following built-in
|
32
|
+
strategy for selecting tests/specs:
|
33
|
+
|
34
|
+
Mutant uses the "longest rspec example group descriptions prefix match" to
|
35
|
+
select the tests to run.
|
36
|
+
|
37
|
+
Example for a subject like `Foo::Bar#baz` it will run all example groups with
|
38
|
+
description prefixes in `Foo::Bar#baz`, `Foo::Bar` and `Foo`. The order is
|
39
|
+
important, so if mutant finds example groups in the current prefix level,
|
40
|
+
these example groups *must* kill the mutation.
|
41
|
+
|
42
|
+
Rails
|
43
|
+
-------
|
44
|
+
|
45
|
+
To mutation test Rails models with rspec, comment out `require 'rspec/autorun'`
|
46
|
+
from your `spec_helper.rb` file. Having done so you should be able to use
|
47
|
+
commands like the following:
|
48
|
+
|
49
|
+
```sh
|
50
|
+
RAILS_ENV=test bundle exec mutant -r ./config/environment --use rspec User
|
51
|
+
```
|
52
|
+
|
53
|
+
Passing in RSpec Options
|
54
|
+
------------------------
|
55
|
+
|
56
|
+
**NOTE: Experimental**
|
57
|
+
|
58
|
+
You can control some aspects of RSpec using the `SPEC_OPTS` environment variable as usual. If you want mutant to only pay attention to specs in a certain directory, you can run
|
59
|
+
|
60
|
+
```sh
|
61
|
+
SPEC_OPTS="--pattern spec/subdir_only/**/*_spec.rb" bundle exec mutant --use rspec SomeClass
|
62
|
+
```
|
@@ -0,0 +1,82 @@
|
|
1
|
+
Nomenclature
|
2
|
+
============
|
3
|
+
|
4
|
+
The following explains several nouns you may experience in mutant's documentation.
|
5
|
+
It's a good idea to familiarize yourself before moving on.
|
6
|
+
|
7
|
+
## AST
|
8
|
+
|
9
|
+
Acronym for [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree)
|
10
|
+
and the level of abstraction mutant operates on.
|
11
|
+
|
12
|
+
## Subject
|
13
|
+
|
14
|
+
An addressable piece of code to be targeted for mutation testing.
|
15
|
+
|
16
|
+
Mutant currently supports the following subjects:
|
17
|
+
|
18
|
+
* Instance methods
|
19
|
+
* Singleton (class) methods
|
20
|
+
|
21
|
+
Other subjects (constants, class bodies for DSLs, ...) are possible but aren't
|
22
|
+
implemented in the OSS version.
|
23
|
+
|
24
|
+
## Mutation operator
|
25
|
+
|
26
|
+
A transformation applied to the AST of a subject. Mutant knows the following high level operator
|
27
|
+
classes:
|
28
|
+
|
29
|
+
* Semantic Reduction
|
30
|
+
* Orthogonal Replacement
|
31
|
+
* [Noop](#neutral-noop-tests)
|
32
|
+
|
33
|
+
An exhaustive list can be found in the [mutant-meta](https://github.com/mbj/mutant/tree/master/meta)
|
34
|
+
subdirectory of the source.
|
35
|
+
|
36
|
+
## Mutation
|
37
|
+
|
38
|
+
The result of applying a mutation operator to the AST of a subject. A mutation represents a
|
39
|
+
hypothesis that ideally gets falsified by the tests.
|
40
|
+
|
41
|
+
## Insertion
|
42
|
+
|
43
|
+
The process of inserting a mutation into the runtime environment.
|
44
|
+
Mutant currently supports insertion via dynamically created monkeypatches.
|
45
|
+
|
46
|
+
Other insertion strategies (such as "boot time") are possible but aren't implemented
|
47
|
+
in the OSS version.
|
48
|
+
|
49
|
+
## Isolation
|
50
|
+
|
51
|
+
The attempt to isolate the (side) effects of killing a mutation via an integration
|
52
|
+
to prevent a mutation leaking into adjacent concurrent, or future mutations.
|
53
|
+
|
54
|
+
Examples of sources for leaks are
|
55
|
+
|
56
|
+
* Global variable writes
|
57
|
+
* Thread local writes
|
58
|
+
* DB State
|
59
|
+
* File system
|
60
|
+
|
61
|
+
Natively, mutant offers fork isolation. This works for any state within the executing
|
62
|
+
Ruby process. For all state reachable via IO, it's the test author's responsibility to
|
63
|
+
provide proper isolation.
|
64
|
+
|
65
|
+
## Integration
|
66
|
+
|
67
|
+
The method used to determine if a specific inserted mutation is covered by tests.
|
68
|
+
|
69
|
+
Currently mutant supports integrations for:
|
70
|
+
|
71
|
+
* [mutant-rspec](/docs/mutant-rspec.md) for [rspec](https://rspec.info)
|
72
|
+
* [mutant-minitest](/docs/mutant-minitest.md) for [minitest](https://github.com/seattlerb/minitest)
|
73
|
+
|
74
|
+
## Report
|
75
|
+
|
76
|
+
Mutant currently provides two different reporters:
|
77
|
+
|
78
|
+
* Progress (printed during mutation testing).
|
79
|
+
* Summary (printed at the end of a finished analysis run)
|
80
|
+
|
81
|
+
A reporter producing a machine readable report does not exist in the OSS version
|
82
|
+
at the time of writing this documentation.
|
@@ -0,0 +1,74 @@
|
|
1
|
+
Reading Reports
|
2
|
+
===============
|
3
|
+
|
4
|
+
Mutation output is grouped by selection groups. Each group contains three sections:
|
5
|
+
|
6
|
+
1. An identifier for the current group.
|
7
|
+
|
8
|
+
**Format**:
|
9
|
+
|
10
|
+
```text
|
11
|
+
[SUBJECT EXPRESSION]:[SOURCE LOCATION]:[LINENO]
|
12
|
+
```
|
13
|
+
|
14
|
+
**Example**:
|
15
|
+
|
16
|
+
```text
|
17
|
+
Book#add_page:Book#add_page:/home/dev/mutant-examples/lib/book.rb:18
|
18
|
+
```
|
19
|
+
|
20
|
+
2. A list of specs that mutant ran to try to kill mutations for the current group.
|
21
|
+
|
22
|
+
**Format**:
|
23
|
+
|
24
|
+
```text
|
25
|
+
- [INTEGRATION]:0:[SPEC LOCATION]:[SPEC DESCRIPTION]
|
26
|
+
- [INTEGRATION]:1:[SPEC LOCATION]:[SPEC DESCRIPTION]
|
27
|
+
```
|
28
|
+
|
29
|
+
**Example**:
|
30
|
+
|
31
|
+
```text
|
32
|
+
- rspec:0:./spec/unit/book_spec.rb:9/Book#add_page should return self
|
33
|
+
- rspec:1:./spec/unit/book_spec.rb:13/Book#add_page should add page to book
|
34
|
+
```
|
35
|
+
|
36
|
+
3. A list of unkilled mutations diffed against the original unparsed source
|
37
|
+
|
38
|
+
**Format**:
|
39
|
+
|
40
|
+
```text
|
41
|
+
[MUTATION TYPE]:[SUBJECT EXPRESSION]:[SOURCE LOCATION]:[SOURCE LINENO]:[IDENTIFIER]
|
42
|
+
[DIFF]
|
43
|
+
-----------------------
|
44
|
+
```
|
45
|
+
|
46
|
+
- `[MUTATION TYPE]` will be one of the following:
|
47
|
+
- `evil` - a mutation of your source was not killed by your tests
|
48
|
+
- `neutral` your original source was injected and one or more tests failed
|
49
|
+
- `[IDENTIFIER]` - Unique identifier for this mutation
|
50
|
+
|
51
|
+
**Example**:
|
52
|
+
|
53
|
+
```diff
|
54
|
+
evil:Book#add_page:Book#add_page:/home/dev/mutant-examples/lib/book.rb:18:01f69
|
55
|
+
@@ -1,6 +1,6 @@
|
56
|
+
def add_page(page)
|
57
|
+
- @pages << page
|
58
|
+
+ @pages
|
59
|
+
@index[page.number] = page
|
60
|
+
self
|
61
|
+
end
|
62
|
+
-----------------------
|
63
|
+
evil:Book#add_page:Book#add_page:/home/dev/mutant-examples/lib/book.rb:18:b1ff2
|
64
|
+
@@ -1,6 +1,6 @@
|
65
|
+
def add_page(page)
|
66
|
+
- @pages << page
|
67
|
+
+ self
|
68
|
+
@index[page.number] = page
|
69
|
+
self
|
70
|
+
end
|
71
|
+
-----------------------
|
72
|
+
```
|
73
|
+
|
74
|
+
At this time no machine readable output exists in the opensourced versions of mutant.
|