mutant 0.9.5 → 0.9.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (190) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +121 -0
  3. data/.rubocop.yml +203 -0
  4. data/Changelog.md +27 -0
  5. data/Gemfile +0 -7
  6. data/Gemfile.lock +30 -87
  7. data/Gemfile.shared +7 -0
  8. data/README.md +88 -27
  9. data/config/reek.yml +1 -0
  10. data/lib/mutant.rb +5 -3
  11. data/lib/mutant/ast.rb +0 -9
  12. data/lib/mutant/ast/find_metaclass_containing.rb +48 -0
  13. data/lib/mutant/ast/meta/send.rb +0 -6
  14. data/lib/mutant/bootstrap.rb +0 -36
  15. data/lib/mutant/cli.rb +5 -49
  16. data/lib/mutant/config.rb +0 -8
  17. data/lib/mutant/context.rb +0 -3
  18. data/lib/mutant/env.rb +0 -6
  19. data/lib/mutant/expression/method.rb +6 -6
  20. data/lib/mutant/expression/methods.rb +6 -6
  21. data/lib/mutant/expression/parser.rb +0 -6
  22. data/lib/mutant/integration.rb +0 -18
  23. data/lib/mutant/isolation/fork.rb +0 -22
  24. data/lib/mutant/license.rb +12 -1
  25. data/lib/mutant/matcher.rb +0 -14
  26. data/lib/mutant/matcher/config.rb +0 -11
  27. data/lib/mutant/matcher/method.rb +0 -31
  28. data/lib/mutant/matcher/method/instance.rb +0 -8
  29. data/lib/mutant/matcher/method/metaclass.rb +86 -0
  30. data/lib/mutant/matcher/method/singleton.rb +0 -25
  31. data/lib/mutant/matcher/methods.rb +17 -28
  32. data/lib/mutant/matcher/namespace.rb +0 -10
  33. data/lib/mutant/matcher/scope.rb +2 -4
  34. data/lib/mutant/meta/example/dsl.rb +0 -21
  35. data/lib/mutant/meta/example/verification.rb +0 -20
  36. data/lib/mutant/mutation.rb +0 -3
  37. data/lib/mutant/mutator.rb +1 -29
  38. data/lib/mutant/mutator/node.rb +1 -66
  39. data/lib/mutant/mutator/node/and_asgn.rb +0 -3
  40. data/lib/mutant/mutator/node/argument.rb +0 -15
  41. data/lib/mutant/mutator/node/arguments.rb +0 -20
  42. data/lib/mutant/mutator/node/begin.rb +0 -3
  43. data/lib/mutant/mutator/node/binary.rb +0 -23
  44. data/lib/mutant/mutator/node/block.rb +0 -15
  45. data/lib/mutant/mutator/node/break.rb +0 -3
  46. data/lib/mutant/mutator/node/case.rb +0 -9
  47. data/lib/mutant/mutator/node/class.rb +0 -3
  48. data/lib/mutant/mutator/node/conditional_loop.rb +0 -3
  49. data/lib/mutant/mutator/node/const.rb +0 -3
  50. data/lib/mutant/mutator/node/define.rb +0 -11
  51. data/lib/mutant/mutator/node/defined.rb +0 -3
  52. data/lib/mutant/mutator/node/dstr.rb +0 -3
  53. data/lib/mutant/mutator/node/dsym.rb +0 -3
  54. data/lib/mutant/mutator/node/generic.rb +0 -67
  55. data/lib/mutant/mutator/node/if.rb +0 -12
  56. data/lib/mutant/mutator/node/index.rb +0 -27
  57. data/lib/mutant/mutator/node/kwbegin.rb +0 -3
  58. data/lib/mutant/mutator/node/literal.rb +0 -3
  59. data/lib/mutant/mutator/node/literal/array.rb +0 -6
  60. data/lib/mutant/mutator/node/literal/boolean.rb +0 -4
  61. data/lib/mutant/mutator/node/literal/float.rb +0 -9
  62. data/lib/mutant/mutator/node/literal/hash.rb +0 -9
  63. data/lib/mutant/mutator/node/literal/integer.rb +0 -9
  64. data/lib/mutant/mutator/node/literal/nil.rb +0 -3
  65. data/lib/mutant/mutator/node/literal/range.rb +1 -7
  66. data/lib/mutant/mutator/node/literal/regex.rb +0 -6
  67. data/lib/mutant/mutator/node/literal/string.rb +0 -3
  68. data/lib/mutant/mutator/node/literal/symbol.rb +0 -3
  69. data/lib/mutant/mutator/node/masgn.rb +0 -3
  70. data/lib/mutant/mutator/node/match_current_line.rb +0 -3
  71. data/lib/mutant/mutator/node/mlhs.rb +0 -3
  72. data/lib/mutant/mutator/node/named_value/access.rb +2 -14
  73. data/lib/mutant/mutator/node/named_value/constant_assignment.rb +0 -9
  74. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +0 -6
  75. data/lib/mutant/mutator/node/next.rb +0 -3
  76. data/lib/mutant/mutator/node/noop.rb +0 -3
  77. data/lib/mutant/mutator/node/nthref.rb +0 -3
  78. data/lib/mutant/mutator/node/op_asgn.rb +0 -3
  79. data/lib/mutant/mutator/node/or_asgn.rb +0 -3
  80. data/lib/mutant/mutator/node/procarg_zero.rb +0 -3
  81. data/lib/mutant/mutator/node/regopt.rb +0 -6
  82. data/lib/mutant/mutator/node/resbody.rb +0 -6
  83. data/lib/mutant/mutator/node/rescue.rb +2 -19
  84. data/lib/mutant/mutator/node/return.rb +0 -3
  85. data/lib/mutant/mutator/node/sclass.rb +20 -0
  86. data/lib/mutant/mutator/node/send.rb +2 -61
  87. data/lib/mutant/mutator/node/send/attribute_assignment.rb +0 -9
  88. data/lib/mutant/mutator/node/send/binary.rb +0 -11
  89. data/lib/mutant/mutator/node/send/conditional.rb +0 -3
  90. data/lib/mutant/mutator/node/splat.rb +0 -3
  91. data/lib/mutant/mutator/node/super.rb +0 -3
  92. data/lib/mutant/mutator/node/when.rb +0 -19
  93. data/lib/mutant/mutator/node/yield.rb +0 -3
  94. data/lib/mutant/mutator/node/zsuper.rb +0 -3
  95. data/lib/mutant/mutator/util/array.rb +0 -6
  96. data/lib/mutant/mutator/util/symbol.rb +0 -3
  97. data/lib/mutant/parallel.rb +0 -13
  98. data/lib/mutant/parallel/driver.rb +0 -10
  99. data/lib/mutant/parallel/worker.rb +0 -22
  100. data/lib/mutant/registry.rb +2 -7
  101. data/lib/mutant/reporter/cli.rb +0 -5
  102. data/lib/mutant/reporter/cli/format.rb +0 -9
  103. data/lib/mutant/reporter/cli/printer.rb +2 -42
  104. data/lib/mutant/reporter/cli/printer/env_progress.rb +0 -15
  105. data/lib/mutant/reporter/cli/printer/isolation_result.rb +0 -18
  106. data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +0 -5
  107. data/lib/mutant/reporter/cli/printer/mutation_result.rb +1 -22
  108. data/lib/mutant/reporter/cli/printer/status_progressive.rb +0 -8
  109. data/lib/mutant/reporter/cli/printer/subject_progress.rb +0 -9
  110. data/lib/mutant/repository/diff.rb +1 -13
  111. data/lib/mutant/repository/diff/ranges.rb +0 -11
  112. data/lib/mutant/result.rb +0 -3
  113. data/lib/mutant/runner.rb +0 -18
  114. data/lib/mutant/runner/sink.rb +0 -5
  115. data/lib/mutant/subject.rb +0 -8
  116. data/lib/mutant/subject/method.rb +0 -3
  117. data/lib/mutant/subject/method/instance.rb +40 -6
  118. data/lib/mutant/subject/method/metaclass.rb +30 -0
  119. data/lib/mutant/transform.rb +0 -92
  120. data/lib/mutant/version.rb +1 -1
  121. data/lib/mutant/warnings.rb +0 -6
  122. data/lib/mutant/zombifier.rb +2 -34
  123. data/meta/and.rb +0 -2
  124. data/meta/array.rb +0 -3
  125. data/meta/begin.rb +0 -3
  126. data/meta/block.rb +0 -3
  127. data/meta/break.rb +0 -1
  128. data/meta/case.rb +0 -6
  129. data/meta/casgn.rb +0 -3
  130. data/meta/cvasgn.rb +0 -1
  131. data/meta/def.rb +0 -7
  132. data/meta/ensure.rb +0 -1
  133. data/meta/false.rb +0 -1
  134. data/meta/gvasgn.rb +0 -1
  135. data/meta/hash.rb +0 -4
  136. data/meta/if.rb +0 -5
  137. data/meta/ivasgn.rb +0 -1
  138. data/meta/kwbegin.rb +0 -1
  139. data/meta/lvasgn.rb +0 -1
  140. data/meta/match_current_line.rb +0 -1
  141. data/meta/next.rb +0 -1
  142. data/meta/or.rb +0 -2
  143. data/meta/range.rb +26 -0
  144. data/meta/regexp.rb +0 -1
  145. data/meta/rescue.rb +0 -6
  146. data/meta/sclass.rb +12 -0
  147. data/meta/send.rb +0 -4
  148. data/meta/true.rb +0 -1
  149. data/meta/until.rb +0 -1
  150. data/meta/while.rb +0 -2
  151. data/meta/yield.rb +0 -1
  152. data/mutant.gemspec +8 -4
  153. data/mutant.sh +12 -0
  154. data/spec/integrations.yml +3 -1
  155. data/spec/spec_helper.rb +37 -22
  156. data/spec/unit/mutant/ast/find_metaclass_containing_spec.rb +64 -0
  157. data/spec/unit/mutant/expression/methods_spec.rb +7 -2
  158. data/spec/unit/mutant/license_spec.rb +17 -5
  159. data/spec/unit/mutant/matcher/method/metaclass_spec.rb +108 -0
  160. data/spec/unit/mutant/matcher/methods/metaclass_spec.rb +62 -0
  161. data/spec/unit/mutant/matcher/namespace_spec.rb +3 -1
  162. data/spec/unit/mutant/matcher/scope_spec.rb +11 -1
  163. data/spec/unit/mutant/meta/example_spec.rb +3 -3
  164. data/spec/unit/mutant/mutator/node_spec.rb +1 -6
  165. data/spec/unit/mutant/parallel/driver_spec.rb +4 -4
  166. data/spec/unit/mutant/parallel/worker_spec.rb +5 -5
  167. data/spec/unit/mutant/parallel_spec.rb +7 -7
  168. data/spec/unit/mutant/registry_spec.rb +52 -25
  169. data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +1 -1
  170. data/spec/unit/mutant/reporter/cli/printer/mutation_progress_result_spec.rb +2 -2
  171. data/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb +12 -12
  172. data/spec/unit/mutant/reporter/cli/printer/subject_result_spec.rb +1 -1
  173. data/spec/unit/mutant/reporter/cli/printer_spec.rb +4 -4
  174. data/spec/unit/mutant/reporter/cli_spec.rb +1 -1
  175. data/spec/unit/mutant/subject/method/instance_spec.rb +117 -22
  176. data/spec/unit/mutant/subject/method/metaclass_spec.rb +63 -0
  177. data/test_app/Gemfile.minitest +2 -0
  178. data/test_app/lib/test_app.rb +5 -0
  179. data/test_app/lib/test_app/metaclasses.rb +108 -0
  180. metadata +85 -25
  181. data/.circleci/config.yml +0 -53
  182. data/config/rubocop.yml +0 -205
  183. data/lib/mutant/color.rb +0 -43
  184. data/lib/mutant/diff.rb +0 -114
  185. data/lib/mutant/variable.rb +0 -282
  186. data/spec/shared/base_behavior.rb +0 -45
  187. data/spec/support/test_app.rb +0 -7
  188. data/spec/support/warnings.yml +0 -6
  189. data/spec/unit/mutant/diff_spec.rb +0 -189
  190. data/spec/unit/mutant/variable_spec.rb +0 -618
@@ -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
- [![Build Status](https://circleci.com/gh/mbj/mutant.svg?style=shield&circle-token=1afd77e8f0f9d0a11fd8f15f5d7b10270f4665e2)](https://circleci.com/gh/mbj/mutant/tree/master)
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
- Mutant is a mutation testing tool for Ruby. Mutation testing is a technique to verify semantic coverage of your code.
10
+ An automated code review tool, with a side effect of producing semantic code coverage
11
+ metrics.
11
12
 
12
- ## Why do I want it?
13
+ Think of mutant as an expert developer that simplifies your code while making sure that all tests pass.
13
14
 
14
- Mutant adds to your toolbox: Detection of uncovered semantics in your code.
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
- On each detection of uncovered semantics you have the opportunity to:
17
+ Each reported simplification signifies either:
18
18
 
19
- * Delete dead code, as you do not want the extra semantics not specified by the tests
20
- * Add (or improve a test) to cover the unwanted semantics.
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
- ## How Do I use it?
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
- * Start with reading the [nomenclature](/docs/nomenclature.md) documentation.
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
- * Identify your preferred mutation testing strategy. It is recommended to start at the commit level,
29
- to test only the code you had been touching. See the [incremental](#only-mutating-changed-code)
30
- mutation testing documentation.
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 currently only works on cRuby/MRI. Starting with version 2.5.x. It supports all syntax features upto and
35
- including Ruby 2.6.
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
- Support for 2.7 syntax features is pending, see unparser issue: https://github.com/mbj/unparser/issues/129.
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
- Mutant will work under Ruby 2.7 just fine, unless a 2.7 syntax feature is used. This will be resolved shortly.
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 license whitelist.
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 interactions for
102
+ There are, apart from initial license gem installation, no remote interaction for
72
103
  license validation.
73
104
 
74
- To inquire for license please contact [Markus Schirp](mailto:mbj@schirp-dso.com?subject=Mutant%20License).
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
- ### Pricing
136
+ #### Pricing Why?
77
137
 
78
- Only relevant for commercial use.
138
+ The idea is to charge 1$ per developer per day. Mutant reduces the time spend on code reviews.
79
139
 
80
- Mutant offers a subscription model under a monthly plan.
81
- Yearly prepayments with discounts are available.
140
+ This time saved should be worth way more than the 1$ per day.
82
141
 
83
- For higher volumes different arrangements can be negotiated.
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
 
@@ -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
@@ -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'
@@ -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
@@ -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) &&
@@ -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
@@ -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
- # Add filter options
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
- # Add debug options
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