mutant 0.9.5 → 0.9.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +121 -0
- data/.rubocop.yml +203 -0
- data/Changelog.md +27 -0
- data/Gemfile +0 -7
- data/Gemfile.lock +30 -87
- data/Gemfile.shared +7 -0
- data/README.md +88 -27
- data/config/reek.yml +1 -0
- data/lib/mutant.rb +5 -3
- data/lib/mutant/ast.rb +0 -9
- data/lib/mutant/ast/find_metaclass_containing.rb +48 -0
- data/lib/mutant/ast/meta/send.rb +0 -6
- data/lib/mutant/bootstrap.rb +0 -36
- data/lib/mutant/cli.rb +5 -49
- data/lib/mutant/config.rb +0 -8
- data/lib/mutant/context.rb +0 -3
- data/lib/mutant/env.rb +0 -6
- data/lib/mutant/expression/method.rb +6 -6
- data/lib/mutant/expression/methods.rb +6 -6
- data/lib/mutant/expression/parser.rb +0 -6
- data/lib/mutant/integration.rb +0 -18
- data/lib/mutant/isolation/fork.rb +0 -22
- data/lib/mutant/license.rb +12 -1
- data/lib/mutant/matcher.rb +0 -14
- data/lib/mutant/matcher/config.rb +0 -11
- data/lib/mutant/matcher/method.rb +0 -31
- data/lib/mutant/matcher/method/instance.rb +0 -8
- data/lib/mutant/matcher/method/metaclass.rb +86 -0
- data/lib/mutant/matcher/method/singleton.rb +0 -25
- data/lib/mutant/matcher/methods.rb +17 -28
- data/lib/mutant/matcher/namespace.rb +0 -10
- data/lib/mutant/matcher/scope.rb +2 -4
- data/lib/mutant/meta/example/dsl.rb +0 -21
- data/lib/mutant/meta/example/verification.rb +0 -20
- data/lib/mutant/mutation.rb +0 -3
- data/lib/mutant/mutator.rb +1 -29
- data/lib/mutant/mutator/node.rb +1 -66
- data/lib/mutant/mutator/node/and_asgn.rb +0 -3
- data/lib/mutant/mutator/node/argument.rb +0 -15
- data/lib/mutant/mutator/node/arguments.rb +0 -20
- data/lib/mutant/mutator/node/begin.rb +0 -3
- data/lib/mutant/mutator/node/binary.rb +0 -23
- data/lib/mutant/mutator/node/block.rb +0 -15
- data/lib/mutant/mutator/node/break.rb +0 -3
- data/lib/mutant/mutator/node/case.rb +0 -9
- data/lib/mutant/mutator/node/class.rb +0 -3
- data/lib/mutant/mutator/node/conditional_loop.rb +0 -3
- data/lib/mutant/mutator/node/const.rb +0 -3
- data/lib/mutant/mutator/node/define.rb +0 -11
- data/lib/mutant/mutator/node/defined.rb +0 -3
- data/lib/mutant/mutator/node/dstr.rb +0 -3
- data/lib/mutant/mutator/node/dsym.rb +0 -3
- data/lib/mutant/mutator/node/generic.rb +0 -67
- data/lib/mutant/mutator/node/if.rb +0 -12
- data/lib/mutant/mutator/node/index.rb +0 -27
- data/lib/mutant/mutator/node/kwbegin.rb +0 -3
- data/lib/mutant/mutator/node/literal.rb +0 -3
- data/lib/mutant/mutator/node/literal/array.rb +0 -6
- data/lib/mutant/mutator/node/literal/boolean.rb +0 -4
- data/lib/mutant/mutator/node/literal/float.rb +0 -9
- data/lib/mutant/mutator/node/literal/hash.rb +0 -9
- data/lib/mutant/mutator/node/literal/integer.rb +0 -9
- data/lib/mutant/mutator/node/literal/nil.rb +0 -3
- data/lib/mutant/mutator/node/literal/range.rb +1 -7
- data/lib/mutant/mutator/node/literal/regex.rb +0 -6
- data/lib/mutant/mutator/node/literal/string.rb +0 -3
- data/lib/mutant/mutator/node/literal/symbol.rb +0 -3
- data/lib/mutant/mutator/node/masgn.rb +0 -3
- data/lib/mutant/mutator/node/match_current_line.rb +0 -3
- data/lib/mutant/mutator/node/mlhs.rb +0 -3
- data/lib/mutant/mutator/node/named_value/access.rb +2 -14
- data/lib/mutant/mutator/node/named_value/constant_assignment.rb +0 -9
- data/lib/mutant/mutator/node/named_value/variable_assignment.rb +0 -6
- data/lib/mutant/mutator/node/next.rb +0 -3
- data/lib/mutant/mutator/node/noop.rb +0 -3
- data/lib/mutant/mutator/node/nthref.rb +0 -3
- data/lib/mutant/mutator/node/op_asgn.rb +0 -3
- data/lib/mutant/mutator/node/or_asgn.rb +0 -3
- data/lib/mutant/mutator/node/procarg_zero.rb +0 -3
- data/lib/mutant/mutator/node/regopt.rb +0 -6
- data/lib/mutant/mutator/node/resbody.rb +0 -6
- data/lib/mutant/mutator/node/rescue.rb +2 -19
- data/lib/mutant/mutator/node/return.rb +0 -3
- data/lib/mutant/mutator/node/sclass.rb +20 -0
- data/lib/mutant/mutator/node/send.rb +2 -61
- data/lib/mutant/mutator/node/send/attribute_assignment.rb +0 -9
- data/lib/mutant/mutator/node/send/binary.rb +0 -11
- data/lib/mutant/mutator/node/send/conditional.rb +0 -3
- data/lib/mutant/mutator/node/splat.rb +0 -3
- data/lib/mutant/mutator/node/super.rb +0 -3
- data/lib/mutant/mutator/node/when.rb +0 -19
- data/lib/mutant/mutator/node/yield.rb +0 -3
- data/lib/mutant/mutator/node/zsuper.rb +0 -3
- data/lib/mutant/mutator/util/array.rb +0 -6
- data/lib/mutant/mutator/util/symbol.rb +0 -3
- data/lib/mutant/parallel.rb +0 -13
- data/lib/mutant/parallel/driver.rb +0 -10
- data/lib/mutant/parallel/worker.rb +0 -22
- data/lib/mutant/registry.rb +2 -7
- data/lib/mutant/reporter/cli.rb +0 -5
- data/lib/mutant/reporter/cli/format.rb +0 -9
- data/lib/mutant/reporter/cli/printer.rb +2 -42
- data/lib/mutant/reporter/cli/printer/env_progress.rb +0 -15
- data/lib/mutant/reporter/cli/printer/isolation_result.rb +0 -18
- data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +0 -5
- data/lib/mutant/reporter/cli/printer/mutation_result.rb +1 -22
- data/lib/mutant/reporter/cli/printer/status_progressive.rb +0 -8
- data/lib/mutant/reporter/cli/printer/subject_progress.rb +0 -9
- data/lib/mutant/repository/diff.rb +1 -13
- data/lib/mutant/repository/diff/ranges.rb +0 -11
- data/lib/mutant/result.rb +0 -3
- data/lib/mutant/runner.rb +0 -18
- data/lib/mutant/runner/sink.rb +0 -5
- data/lib/mutant/subject.rb +0 -8
- data/lib/mutant/subject/method.rb +0 -3
- data/lib/mutant/subject/method/instance.rb +40 -6
- data/lib/mutant/subject/method/metaclass.rb +30 -0
- data/lib/mutant/transform.rb +0 -92
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/warnings.rb +0 -6
- data/lib/mutant/zombifier.rb +2 -34
- data/meta/and.rb +0 -2
- data/meta/array.rb +0 -3
- data/meta/begin.rb +0 -3
- data/meta/block.rb +0 -3
- data/meta/break.rb +0 -1
- data/meta/case.rb +0 -6
- data/meta/casgn.rb +0 -3
- data/meta/cvasgn.rb +0 -1
- data/meta/def.rb +0 -7
- data/meta/ensure.rb +0 -1
- data/meta/false.rb +0 -1
- data/meta/gvasgn.rb +0 -1
- data/meta/hash.rb +0 -4
- data/meta/if.rb +0 -5
- data/meta/ivasgn.rb +0 -1
- data/meta/kwbegin.rb +0 -1
- data/meta/lvasgn.rb +0 -1
- data/meta/match_current_line.rb +0 -1
- data/meta/next.rb +0 -1
- data/meta/or.rb +0 -2
- data/meta/range.rb +26 -0
- data/meta/regexp.rb +0 -1
- data/meta/rescue.rb +0 -6
- data/meta/sclass.rb +12 -0
- data/meta/send.rb +0 -4
- data/meta/true.rb +0 -1
- data/meta/until.rb +0 -1
- data/meta/while.rb +0 -2
- data/meta/yield.rb +0 -1
- data/mutant.gemspec +8 -4
- data/mutant.sh +12 -0
- data/spec/integrations.yml +3 -1
- data/spec/spec_helper.rb +37 -22
- data/spec/unit/mutant/ast/find_metaclass_containing_spec.rb +64 -0
- data/spec/unit/mutant/expression/methods_spec.rb +7 -2
- data/spec/unit/mutant/license_spec.rb +17 -5
- data/spec/unit/mutant/matcher/method/metaclass_spec.rb +108 -0
- data/spec/unit/mutant/matcher/methods/metaclass_spec.rb +62 -0
- data/spec/unit/mutant/matcher/namespace_spec.rb +3 -1
- data/spec/unit/mutant/matcher/scope_spec.rb +11 -1
- data/spec/unit/mutant/meta/example_spec.rb +3 -3
- data/spec/unit/mutant/mutator/node_spec.rb +1 -6
- data/spec/unit/mutant/parallel/driver_spec.rb +4 -4
- data/spec/unit/mutant/parallel/worker_spec.rb +5 -5
- data/spec/unit/mutant/parallel_spec.rb +7 -7
- data/spec/unit/mutant/registry_spec.rb +52 -25
- data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +1 -1
- data/spec/unit/mutant/reporter/cli/printer/mutation_progress_result_spec.rb +2 -2
- data/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb +12 -12
- data/spec/unit/mutant/reporter/cli/printer/subject_result_spec.rb +1 -1
- data/spec/unit/mutant/reporter/cli/printer_spec.rb +4 -4
- data/spec/unit/mutant/reporter/cli_spec.rb +1 -1
- data/spec/unit/mutant/subject/method/instance_spec.rb +117 -22
- data/spec/unit/mutant/subject/method/metaclass_spec.rb +63 -0
- data/test_app/Gemfile.minitest +2 -0
- data/test_app/lib/test_app.rb +5 -0
- data/test_app/lib/test_app/metaclasses.rb +108 -0
- metadata +85 -25
- data/.circleci/config.yml +0 -53
- data/config/rubocop.yml +0 -205
- data/lib/mutant/color.rb +0 -43
- data/lib/mutant/diff.rb +0 -114
- data/lib/mutant/variable.rb +0 -282
- data/spec/shared/base_behavior.rb +0 -45
- data/spec/support/test_app.rb +0 -7
- data/spec/support/warnings.yml +0 -6
- data/spec/unit/mutant/diff_spec.rb +0 -189
- data/spec/unit/mutant/variable_spec.rb +0 -618
data/Gemfile.shared
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
# Mutant itself uses an opensource license key.
|
2
|
+
# Scoped to https://github.com/mbj/mutant it'll
|
3
|
+
# not be useful elsewhere.
|
4
|
+
source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do
|
5
|
+
gem 'mutant-license'
|
6
|
+
end
|
7
|
+
|
1
8
|
# Place for shared git sources, used for developing updates to depedencies
|
2
9
|
# where the git sources (without this file) need to be consistently edited
|
3
10
|
# into multiple Gemfiles.
|
data/README.md
CHANGED
@@ -1,49 +1,80 @@
|
|
1
1
|
mutant
|
2
2
|
======
|
3
3
|
|
4
|
-
|
4
|
+
![Build Status](https://github.com/mbj/mutant/workflows/CI/badge.svg)
|
5
5
|
[![Gem Version](https://img.shields.io/gem/v/mutant.svg)](https://rubygems.org/gems/mutant)
|
6
6
|
[![Slack Status](https://mutation-testing-slack.herokuapp.com/badge.svg)](https://mutation-testing.slack.com/messages/mutant)
|
7
7
|
|
8
8
|
## What is Mutant?
|
9
9
|
|
10
|
-
|
10
|
+
An automated code review tool, with a side effect of producing semantic code coverage
|
11
|
+
metrics.
|
11
12
|
|
12
|
-
|
13
|
+
Think of mutant as an expert developer that simplifies your code while making sure that all tests pass.
|
13
14
|
|
14
|
-
|
15
|
-
Coverage becomes a meaningful metric!
|
15
|
+
That developer never has a bad day and is always ready to jump on your PR.
|
16
16
|
|
17
|
-
|
17
|
+
Each reported simplification signifies either:
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
* Learn something new about the semantics of Ruby and your direct and indirect dependencies.
|
19
|
+
A) A piece of code that does more than the tests ask for.
|
20
|
+
You can probably use the simplified version of the code. OR:
|
22
21
|
|
23
|
-
|
22
|
+
B) If you have a reason to not take the simplified version as it violates a requirement:
|
23
|
+
There was no test that proves the extra requirement. Likely you are missing an
|
24
|
+
important test for that requirement.
|
24
25
|
|
25
|
-
|
26
|
+
On extensive mutant use A) happens more often than B), which leads to overall less code enter
|
27
|
+
your repository at higher confidence for both the author and the reviewer.
|
28
|
+
|
29
|
+
BTW: Mutant is a mutation testing tool, which is a form of code coverage.
|
30
|
+
But each reported uncovered mutation is actually a call to action, just like a flag in a code review
|
31
|
+
would be.
|
32
|
+
|
33
|
+
## Getting started:
|
34
|
+
|
35
|
+
* Start with reading the [nomenclature](/docs/nomenclature.md). No way around that one, sorry.
|
26
36
|
* Then select and setup your [integration](/docs/nomenclature.md#integration), also make sure
|
27
37
|
you can reproduce the examples in the integration specific documentation.
|
28
|
-
*
|
29
|
-
|
30
|
-
|
38
|
+
* Use mutant during code reviews and on CI in [incremental](/docs/incremental.md) mode.
|
39
|
+
* Do not merge code with new alive mutations. If you really must bypass:
|
40
|
+
Add the subjects with open problems to the ignored subjects.
|
31
41
|
|
32
42
|
## Ruby Versions
|
33
43
|
|
34
|
-
Mutant
|
35
|
-
|
44
|
+
Mutant supports multiple ruby versions at different levels. The levels arge staged:
|
45
|
+
|
46
|
+
* Runtime, indicates mutant can execute on a specific Ruby Version / implementation.
|
47
|
+
* Syntax, depends on Runtime support, and indicates syntax new to that Ruby version can be used.
|
48
|
+
* Mutations, depends on Syntax support, and indicates syntax new to that Ruby verison is being analysed.
|
49
|
+
|
50
|
+
Supported indicates if a specific Ruby version / Implementation is actively supported. Which means:
|
51
|
+
|
52
|
+
* New releases will only be done if all tests pass on supported Ruby versions / implementations.
|
53
|
+
* New features will be available.
|
36
54
|
|
37
|
-
|
55
|
+
| Implementation | Version | Runtime | Syntax | Mutations | Supported |
|
56
|
+
| -------------- | -------------- | ------- | ------------------ | ------------------ | ------------------ |
|
57
|
+
| cRUBY/MRI | 2.5 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
58
|
+
| cRUBY/MRI | 2.6 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
59
|
+
| cRUBY/MRI | 2.7 | :heavy_check_mark: | :soon: | :soon: | :heavy_check_mark: |
|
60
|
+
| jruby | TBD | :email: | :email: | :email: | :email: |
|
61
|
+
| mruby | TBD | :email: | :email: | :email: | :email: |
|
62
|
+
| cRUBY/MRI | < 2.5 | :no_entry: | :no_entry: | :no_entry: | :no_entry: |
|
38
63
|
|
39
|
-
|
64
|
+
|
65
|
+
Labels:
|
66
|
+
|
67
|
+
* :heavy_check_mark: Supported.
|
68
|
+
* :soon: Active work in progress.
|
69
|
+
* :email: Planned, please contact me on interest.
|
70
|
+
* :no_entry: Not being planned, or considered, still contact me on interest.
|
40
71
|
|
41
72
|
## Licensing
|
42
73
|
|
43
74
|
Mutant was recently transitioned commercial software, with a free usage plan for opensource projects.
|
44
75
|
|
45
76
|
Commercial projects have to acquire a license per developer, with unlimited repositories
|
46
|
-
per developer.
|
77
|
+
per developer. CI usage for licensed developers is included.
|
47
78
|
|
48
79
|
Opensource projects have to acquire their free license per repository.
|
49
80
|
|
@@ -65,22 +96,52 @@ The mutant license gem contains metadata that allows mutant to verify licensed u
|
|
65
96
|
For commercial licenses mutant checks the git commit author or the configured git email
|
66
97
|
to be in the set of licensed developers.
|
67
98
|
|
68
|
-
For opensource licenses mutant checks the git remotes against the
|
99
|
+
For opensource licenses mutant checks the git remotes against the licensed git repositories.
|
69
100
|
This allows the project maintainer to sign up and not bother collaborators with the details.
|
70
101
|
|
71
|
-
There are, apart from initial license gem installation, no remote
|
102
|
+
There are, apart from initial license gem installation, no remote interaction for
|
72
103
|
license validation.
|
73
104
|
|
74
|
-
|
105
|
+
### Getting an Opensource license
|
106
|
+
|
107
|
+
As stated above: Opensource projects of any kind are free to use mutant.
|
108
|
+
|
109
|
+
Just mail [me](mailto:mbj@schirp-dso.com?subject=Mutant%20Opensource%20License): Please
|
110
|
+
include:
|
111
|
+
|
112
|
+
* Just the git remote URL of your repository. Repository can be anywhere, must not be on Github, just has to be public.
|
113
|
+
|
114
|
+
I do not need any more details.
|
115
|
+
|
116
|
+
### Getting a commercial license
|
117
|
+
|
118
|
+
Mutant offers a per developer subscription a monthly plan for 30$, or an annual plan for 300$.
|
119
|
+
|
120
|
+
Above 10 developer licensees per customer I'm open to negotiate more discounts.
|
121
|
+
|
122
|
+
Should you want to procure a commercial mutant license please [mail me](mailto:mbj@schirp-dso.com?subject=Mutant%20Commercial%20License).
|
123
|
+
|
124
|
+
Please include the following information:
|
125
|
+
|
126
|
+
* Your invoice address, including your Tax ID (For EU customers VAT-ID is mandatory)
|
127
|
+
* Per licensed user the git author email address as returned by `git config user.email`
|
128
|
+
|
129
|
+
Also feel free to ask any other question I forgot to proactively answer here.
|
130
|
+
|
131
|
+
#### Payment methods
|
132
|
+
|
133
|
+
* For monthly subscriptions: Exclusively CC.
|
134
|
+
* For annual subscriptions: CC (worldwide) or ACH (US) / SEPA (EU) wire transfer.
|
75
135
|
|
76
|
-
|
136
|
+
#### Pricing Why?
|
77
137
|
|
78
|
-
|
138
|
+
The idea is to charge 1$ per developer per day. Mutant reduces the time spend on code reviews.
|
79
139
|
|
80
|
-
|
81
|
-
Yearly prepayments with discounts are available.
|
140
|
+
This time saved should be worth way more than the 1$ per day.
|
82
141
|
|
83
|
-
|
142
|
+
If you think this is not true for your code base, either my claims are wrong our your use of mutant is wrong.
|
143
|
+
I'd be happy to hear about your case as I'm certainly willing to help you in using mutant right, and in case
|
144
|
+
I'm wrong I'd be happy to improve mutant to the point I'm right again.
|
84
145
|
|
85
146
|
## Topics
|
86
147
|
|
data/config/reek.yml
CHANGED
@@ -136,3 +136,4 @@ detectors:
|
|
136
136
|
- Mutant::Reporter::CLI::Printer::StatusProgressive#object # False positive calls super
|
137
137
|
- Mutant::Repository::Diff#tracks? # intentional, private
|
138
138
|
- Mutant::Repository::Diff#within_working_directory? # intentional, private
|
139
|
+
- Mutant::AST::FindMetaclassContaining#include_exact? # intentional, private
|
data/lib/mutant.rb
CHANGED
@@ -21,6 +21,7 @@ require 'set'
|
|
21
21
|
require 'singleton'
|
22
22
|
require 'stringio'
|
23
23
|
require 'unparser'
|
24
|
+
require 'variable'
|
24
25
|
require 'yaml'
|
25
26
|
|
26
27
|
# This setting is done to make errors within the parallel
|
@@ -50,6 +51,7 @@ require 'mutant/ast/types'
|
|
50
51
|
require 'mutant/ast/nodes'
|
51
52
|
require 'mutant/ast/named_children'
|
52
53
|
require 'mutant/ast/node_predicates'
|
54
|
+
require 'mutant/ast/find_metaclass_containing'
|
53
55
|
require 'mutant/ast/meta'
|
54
56
|
require 'mutant/ast/meta/send'
|
55
57
|
require 'mutant/ast/meta/const'
|
@@ -111,6 +113,7 @@ require 'mutant/mutator/node/send/conditional'
|
|
111
113
|
require 'mutant/mutator/node/send/attribute_assignment'
|
112
114
|
require 'mutant/mutator/node/when'
|
113
115
|
require 'mutant/mutator/node/class'
|
116
|
+
require 'mutant/mutator/node/sclass'
|
114
117
|
require 'mutant/mutator/node/define'
|
115
118
|
require 'mutant/mutator/node/mlhs'
|
116
119
|
require 'mutant/mutator/node/nthref'
|
@@ -133,11 +136,13 @@ require 'mutant/subject'
|
|
133
136
|
require 'mutant/subject/method'
|
134
137
|
require 'mutant/subject/method/instance'
|
135
138
|
require 'mutant/subject/method/singleton'
|
139
|
+
require 'mutant/subject/method/metaclass'
|
136
140
|
require 'mutant/matcher'
|
137
141
|
require 'mutant/matcher/config'
|
138
142
|
require 'mutant/matcher/chain'
|
139
143
|
require 'mutant/matcher/method'
|
140
144
|
require 'mutant/matcher/method/singleton'
|
145
|
+
require 'mutant/matcher/method/metaclass'
|
141
146
|
require 'mutant/matcher/method/instance'
|
142
147
|
require 'mutant/matcher/methods'
|
143
148
|
require 'mutant/matcher/namespace'
|
@@ -160,8 +165,6 @@ require 'mutant/selector/expression'
|
|
160
165
|
require 'mutant/selector/null'
|
161
166
|
require 'mutant/config'
|
162
167
|
require 'mutant/cli'
|
163
|
-
require 'mutant/color'
|
164
|
-
require 'mutant/diff'
|
165
168
|
require 'mutant/runner'
|
166
169
|
require 'mutant/runner/sink'
|
167
170
|
require 'mutant/result'
|
@@ -185,7 +188,6 @@ require 'mutant/reporter/cli/format'
|
|
185
188
|
require 'mutant/repository'
|
186
189
|
require 'mutant/repository/diff'
|
187
190
|
require 'mutant/repository/diff/ranges'
|
188
|
-
require 'mutant/variable'
|
189
191
|
require 'mutant/warnings'
|
190
192
|
require 'mutant/zombifier'
|
191
193
|
require 'mutant/range'
|
data/lib/mutant/ast.rb
CHANGED
@@ -27,15 +27,6 @@ module Mutant
|
|
27
27
|
path
|
28
28
|
end
|
29
29
|
|
30
|
-
# Walk all ast nodes keeping track of path
|
31
|
-
#
|
32
|
-
# @param [Parser::AST::Node] root
|
33
|
-
# @param [Array<Parser::AST::Node>] stack
|
34
|
-
#
|
35
|
-
# @yield [Parser::AST::Node]
|
36
|
-
# all nodes visited recursively including root
|
37
|
-
#
|
38
|
-
# @return [undefined]
|
39
30
|
def self.walk(node, stack, &block)
|
40
31
|
block.call(node, stack)
|
41
32
|
node.children.grep(::Parser::AST::Node) do |child|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
module AST
|
5
|
+
# Given an AST, finds the sclass that directly(-ish) contains the provided
|
6
|
+
# node.
|
7
|
+
# This won't match arbitrarily complex structures - it only searches the
|
8
|
+
# first level deep (no begins-in-begins, for example). This is in
|
9
|
+
# keeping with mutant generally not supporting 'weird' syntax.
|
10
|
+
# Descending into 'begin' nodes is supported because these are generated for
|
11
|
+
# the one-line syntax class << self; def foo; end
|
12
|
+
class FindMetaclassContaining
|
13
|
+
include NodePredicates, Concord.new(:root, :target), Procto.call
|
14
|
+
|
15
|
+
SCLASS_BODY_INDEX = 1
|
16
|
+
|
17
|
+
private_constant(*constants(false))
|
18
|
+
|
19
|
+
# Find metaclass node containing target node
|
20
|
+
#
|
21
|
+
# @return [Parser::AST::Node, nil]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
def call
|
25
|
+
AST.find_last_path(root) do |current|
|
26
|
+
next unless n_sclass?(current)
|
27
|
+
|
28
|
+
metaclass_of?(current)
|
29
|
+
end.last
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def metaclass_of?(sclass)
|
35
|
+
body = sclass.children.fetch(SCLASS_BODY_INDEX)
|
36
|
+
body.equal?(target) || transparently_contains?(body)
|
37
|
+
end
|
38
|
+
|
39
|
+
def transparently_contains?(body)
|
40
|
+
n_begin?(body) && include_exact?(body.children, target)
|
41
|
+
end
|
42
|
+
|
43
|
+
def include_exact?(haystack, needle)
|
44
|
+
haystack.any? { |elem| elem.equal?(needle) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/mutant/ast/meta/send.rb
CHANGED
@@ -56,16 +56,10 @@ module Mutant
|
|
56
56
|
|
57
57
|
private
|
58
58
|
|
59
|
-
# Test if node is `proc { ... }`
|
60
|
-
#
|
61
|
-
# @return [Boolean]
|
62
59
|
def naked_proc?
|
63
60
|
!receiver && selector.equal?(:proc)
|
64
61
|
end
|
65
62
|
|
66
|
-
# Test if node is `Proc.new { ... }`
|
67
|
-
#
|
68
|
-
# @return [Boolean]
|
69
63
|
def proc_new?
|
70
64
|
receiver &&
|
71
65
|
selector.equal?(:new) &&
|
data/lib/mutant/bootstrap.rb
CHANGED
@@ -49,9 +49,6 @@ module Mutant
|
|
49
49
|
end
|
50
50
|
# rubocop:enable Metrics/MethodLength
|
51
51
|
|
52
|
-
# Infect environment
|
53
|
-
#
|
54
|
-
# @return [undefined]
|
55
52
|
def self.infect(env)
|
56
53
|
config, world = env.config, env.world
|
57
54
|
|
@@ -60,12 +57,6 @@ module Mutant
|
|
60
57
|
end
|
61
58
|
private_class_method :infect
|
62
59
|
|
63
|
-
# Matchable scopes
|
64
|
-
#
|
65
|
-
# @param [World] world
|
66
|
-
# @param [Config] config
|
67
|
-
#
|
68
|
-
# @return [Array<Scope>]
|
69
60
|
def self.matchable_scopes(world, config)
|
70
61
|
scopes = world.object_space.each_object(Module).each_with_object([]) do |scope, aggregate|
|
71
62
|
expression = expression(config.reporter, config.expression_parser, scope) || next
|
@@ -76,15 +67,6 @@ module Mutant
|
|
76
67
|
end
|
77
68
|
private_class_method :matchable_scopes
|
78
69
|
|
79
|
-
# Scope name from scoping object
|
80
|
-
#
|
81
|
-
# @param [Class, Module] scope
|
82
|
-
#
|
83
|
-
# @return [String]
|
84
|
-
# if scope has a name and does not raise exceptions obtaining it
|
85
|
-
#
|
86
|
-
# @return [nil]
|
87
|
-
# otherwise
|
88
70
|
def self.scope_name(reporter, scope)
|
89
71
|
scope.name
|
90
72
|
rescue => exception
|
@@ -99,20 +81,7 @@ module Mutant
|
|
99
81
|
end
|
100
82
|
private_class_method :scope_name
|
101
83
|
|
102
|
-
# Try to turn scope into expression
|
103
|
-
#
|
104
|
-
# @param [Expression::Parser] expression_parser
|
105
|
-
# @param [Class, Module] scope
|
106
|
-
#
|
107
|
-
# @return [Expression]
|
108
|
-
# if scope can be represented in an expression
|
109
|
-
#
|
110
|
-
# @return [nil]
|
111
|
-
# otherwise
|
112
|
-
#
|
113
84
|
# rubocop:disable Metrics/MethodLength
|
114
|
-
#
|
115
|
-
# ignore :reek:LongParameterList
|
116
85
|
def self.expression(reporter, expression_parser, scope)
|
117
86
|
name = scope_name(reporter, scope) or return
|
118
87
|
|
@@ -132,11 +101,6 @@ module Mutant
|
|
132
101
|
private_class_method :expression
|
133
102
|
# rubocop:enable Metrics/MethodLength
|
134
103
|
|
135
|
-
# Write a semantics warning
|
136
|
-
#
|
137
|
-
# @return [undefined]
|
138
|
-
#
|
139
|
-
# ignore :reek:LongParameterList
|
140
104
|
def self.semantics_warning(reporter, format, options)
|
141
105
|
reporter.warn(SEMANTICS_MESSAGE_FORMAT % { message: format % options })
|
142
106
|
end
|
data/lib/mutant/cli.rb
CHANGED
@@ -88,23 +88,12 @@ module Mutant
|
|
88
88
|
|
89
89
|
private
|
90
90
|
|
91
|
-
# Parse matchers
|
92
|
-
#
|
93
|
-
# @param [Array<String>] expressions
|
94
|
-
#
|
95
|
-
# @return [undefined]
|
96
91
|
def parse_match_expressions(expressions)
|
97
92
|
expressions.each do |expression|
|
98
93
|
add_matcher(:match_expressions, config.expression_parser.apply(expression).from_right)
|
99
94
|
end
|
100
95
|
end
|
101
96
|
|
102
|
-
# Add environmental options
|
103
|
-
#
|
104
|
-
# @param [Object] opts
|
105
|
-
#
|
106
|
-
# @return [undefined]
|
107
|
-
#
|
108
97
|
# rubocop:disable Metrics/MethodLength
|
109
98
|
def add_environment_options(opts)
|
110
99
|
opts.separator('Environment:')
|
@@ -121,12 +110,8 @@ module Mutant
|
|
121
110
|
with(jobs: Integer(number))
|
122
111
|
end
|
123
112
|
end
|
113
|
+
# rubocop:enable Metrics/MethodLength
|
124
114
|
|
125
|
-
# Add mutation options
|
126
|
-
#
|
127
|
-
# @param [OptionParser] opts
|
128
|
-
#
|
129
|
-
# @return [undefined]
|
130
115
|
def add_mutation_options(opts)
|
131
116
|
opts.separator(nil)
|
132
117
|
opts.separator('Options:')
|
@@ -136,11 +121,7 @@ module Mutant
|
|
136
121
|
end
|
137
122
|
end
|
138
123
|
|
139
|
-
#
|
140
|
-
#
|
141
|
-
# @param [OptionParser] opts
|
142
|
-
#
|
143
|
-
# @return [undefined]
|
124
|
+
# rubocop:disable Metrics/MethodLength
|
144
125
|
def add_filter_options(opts)
|
145
126
|
opts.on('--ignore-subject EXPRESSION', 'Ignore subjects that match EXPRESSION as prefix') do |pattern|
|
146
127
|
add_matcher(:ignore_expressions, config.expression_parser.apply(pattern).from_right)
|
@@ -154,12 +135,9 @@ module Mutant
|
|
154
135
|
)
|
155
136
|
end
|
156
137
|
end
|
138
|
+
# rubocop:enable Metrics/MethodLength
|
157
139
|
|
158
|
-
#
|
159
|
-
#
|
160
|
-
# @param [OptionParser] opts
|
161
|
-
#
|
162
|
-
# @return [undefined]
|
140
|
+
# rubocop:disable Metrics/MethodLength
|
163
141
|
def add_debug_options(opts)
|
164
142
|
opts.on('--fail-fast', 'Fail fast') do
|
165
143
|
with(fail_fast: true)
|
@@ -173,38 +151,16 @@ module Mutant
|
|
173
151
|
world.kernel.exit
|
174
152
|
end
|
175
153
|
end
|
154
|
+
# rubocop:enable Metrics/MethodLength
|
176
155
|
|
177
|
-
# With configuration
|
178
|
-
#
|
179
|
-
# @param [Hash<Symbol, Object>] attributes
|
180
|
-
#
|
181
|
-
# @return [undefined]
|
182
156
|
def with(attributes)
|
183
157
|
@config = config.with(attributes)
|
184
158
|
end
|
185
159
|
|
186
|
-
# Add configuration
|
187
|
-
#
|
188
|
-
# @param [Symbol] attribute
|
189
|
-
# the attribute to add to
|
190
|
-
#
|
191
|
-
# @param [Object] value
|
192
|
-
# the value to add
|
193
|
-
#
|
194
|
-
# @return [undefined]
|
195
160
|
def add(attribute, value)
|
196
161
|
with(attribute => config.public_send(attribute) + [value])
|
197
162
|
end
|
198
163
|
|
199
|
-
# Add matcher configuration
|
200
|
-
#
|
201
|
-
# @param [Symbol] attribute
|
202
|
-
# the attribute to add to
|
203
|
-
#
|
204
|
-
# @param [Object] value
|
205
|
-
# the value to add
|
206
|
-
#
|
207
|
-
# @return [undefined]
|
208
164
|
def add_matcher(attribute, value)
|
209
165
|
with(matcher: config.matcher.add(attribute, value))
|
210
166
|
end
|