mutant 0.8.11 → 0.8.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -0
  3. data/Changelog.md +29 -3
  4. data/README.md +2 -0
  5. data/config/flay.yml +1 -1
  6. data/lib/mutant.rb +6 -0
  7. data/lib/mutant/ast/meta/send.rb +26 -0
  8. data/lib/mutant/ast/regexp/transformer/direct.rb +1 -0
  9. data/lib/mutant/env/bootstrap.rb +7 -2
  10. data/lib/mutant/meta/example/dsl.rb +8 -0
  11. data/lib/mutant/mutator/node/generic.rb +52 -8
  12. data/lib/mutant/mutator/node/regexp.rb +0 -11
  13. data/lib/mutant/mutator/node/regexp/alternation_meta.rb +21 -0
  14. data/lib/mutant/mutator/node/regexp/capture_group.rb +26 -0
  15. data/lib/mutant/mutator/node/regexp/character_type.rb +29 -0
  16. data/lib/mutant/mutator/node/regexp/end_of_line_anchor.rb +21 -0
  17. data/lib/mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor.rb +21 -0
  18. data/lib/mutant/mutator/node/regexp/greedy_zero_or_more.rb +25 -0
  19. data/lib/mutant/mutator/node/send.rb +20 -1
  20. data/lib/mutant/reporter/cli.rb +2 -0
  21. data/lib/mutant/version.rb +1 -1
  22. data/lib/mutant/zombifier.rb +6 -1
  23. data/meta/regexp.rb +8 -30
  24. data/meta/regexp/character_types.rb +20 -0
  25. data/meta/regexp/regexp_alternation_meta.rb +11 -0
  26. data/meta/regexp/regexp_bol_anchor.rb +1 -6
  27. data/meta/regexp/regexp_bos_anchor.rb +2 -12
  28. data/meta/regexp/regexp_capture_group.rb +17 -0
  29. data/meta/regexp/regexp_eol_anchor.rb +8 -0
  30. data/meta/regexp/regexp_eos_anchor.rb +6 -0
  31. data/meta/regexp/regexp_eos_ob_eol_anchor.rb +8 -0
  32. data/meta/regexp/regexp_greedy_zero_or_more.rb +10 -0
  33. data/meta/regexp/regexp_root_expression.rb +1 -6
  34. data/meta/send.rb +65 -0
  35. data/mutant.gemspec +2 -2
  36. data/spec/integrations.yml +0 -21
  37. data/spec/support/shared_context.rb +0 -13
  38. data/spec/support/warnings.yml +1 -1
  39. data/spec/unit/mutant/actor/mailbox_spec.rb +0 -3
  40. data/spec/unit/mutant/ast/meta/send/proc_predicate_spec.rb +28 -0
  41. data/spec/unit/mutant/ast/regexp_spec.rb +8 -3
  42. data/spec/unit/mutant/cli_spec.rb +0 -1
  43. data/spec/unit/mutant/context_spec.rb +0 -7
  44. data/spec/unit/mutant/env_spec.rb +2 -17
  45. data/spec/unit/mutant/expression_spec.rb +0 -2
  46. data/spec/unit/mutant/matcher/compiler_spec.rb +0 -2
  47. data/spec/unit/mutant/matcher/method/instance_spec.rb +0 -2
  48. data/spec/unit/mutant/matcher/method/singleton_spec.rb +1 -2
  49. data/spec/unit/mutant/meta/example/dsl_spec.rb +16 -0
  50. data/spec/unit/mutant/mutation_spec.rb +0 -3
  51. data/spec/unit/mutant/mutator_spec.rb +0 -2
  52. data/spec/unit/mutant/parallel/master_spec.rb +0 -3
  53. data/spec/unit/mutant/parallel/worker_spec.rb +0 -1
  54. data/spec/unit/mutant/registry_spec.rb +3 -5
  55. data/spec/unit/mutant/reporter/cli/tput_spec.rb +1 -1
  56. data/spec/unit/mutant/result/env_spec.rb +0 -5
  57. data/spec/unit/mutant/result/mutation_spec.rb +1 -13
  58. data/spec/unit/mutant/runner_spec.rb +3 -16
  59. data/spec/unit/mutant/selector/expression_spec.rb +0 -1
  60. data/spec/unit/mutant/subject_spec.rb +1 -6
  61. metadata +24 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2b6726d9cc7696a22bc3de412e2aec93c6098212
4
- data.tar.gz: 1c68cebbe3bd9faf603b604621a5af7cfb6402d1
3
+ metadata.gz: e9fccb5b88d832378fa591e769d03f29bd9157ad
4
+ data.tar.gz: 7a84e4ba5e25a538218b4a4d2efab2be9a7c90e0
5
5
  SHA512:
6
- metadata.gz: c9a1dd4866a3264565adccb269d212b0609bdd806fc94e9f42b3366deb951967c48d0595f6044158a1719155557af450fb949821eadb7699432aa73262e7e1cf
7
- data.tar.gz: 2cb50707ab5a178bb9eb8d1288251e978db32fbd92efddc1a4417e772df2ec4f2cefa86fceaeeb2dc5a7b7a654afe9c9182d548183a733497d17fdc9459515ea
6
+ metadata.gz: 0590a8d9f4775c4c21b1599a67f2360a8319b5fce2ec4264846c731a9468a09108bccb8ed56bc72ca7ba5087eac13135e004ab02fb4848f490bbe86c2c6e9833
7
+ data.tar.gz: c2a3e39dbb3ee300d3c526e0aecba9fcf866fef79a4954eb004a928f448f2c67e56655a84df4f0145e9b06f4b14e65e6de85a1ffa8e98dc7c540b8936335f4cc
@@ -0,0 +1 @@
1
+ Changelog.md merge=union
@@ -1,10 +1,36 @@
1
- # v0.8.11 2016-01-xx
1
+ # v0.8.12 2016-10-17
2
+
3
+ * Add mutation from `/foo|bar/` to `/foo/` and `/bar/`
4
+ * Add mutation from `/$/` to `/\z/`
5
+ * Add mutation from `/\h/` to `/\H/`
6
+ * Add mutation from `/\H/` to `/\h/`
7
+ * Add mutation from `/\Z/` to `/\z/`
8
+ * Add mutation from `flat_map` to `map`
9
+ * Add mutation from `/(foo)/` to `/(?:foo)/`
10
+ * Add mutation from `/a*/` to `/a+/`
11
+ * Add mutation from `/a*/` to `/a/`
12
+ * Add mutation from `!!foo` to `foo`
13
+ * Add mutation from `proc { }` to `lambda { }`
14
+
15
+ # v0.8.11 2016-08-01
2
16
 
3
17
  * Add support for rspec-3.5
4
- * Remove misleading --debug option
5
- * Remove misleading --expect-coverage option
18
+ * Remove misleading `--debug` option
19
+ * Remove misleading `--expect-coverage` option
6
20
  * Add basic support for regexp mutations (machinery and simple anchor mutations)
7
21
  * Add support for mutating csend (duck tape operator) into regular sends
22
+ * Add mutation from `foo&.bar` to `foo.bar`
23
+ * Add mutation from `#to_a` to `#to_set`
24
+ * Add mutation from `foo.dig(a, b)` to `foo.fetch(a).dig(b)`
25
+ * Add mutation from `def foo(bar:); end` to `def foo(_bar:); end`
26
+ * Add mutation from `def foo(bar: baz); end` to `def foo(_bar: baz); end`
27
+ * Add mutation from `/regex/i` to `/regex/`
28
+ * Add mutation from `foo[n..-1]` to `foo.drop(n)`
29
+ * Add mutation from `/^/` to `/\A/`
30
+ * Add mutation from `#first` to `#last`
31
+ * Add mutation from `#last` to `#first`
32
+ * Add mutation from `#sample` to `#first` and `#last`
33
+ * Remove mutations from `1..n` to `1..(0.0 / 0.0)` and `1..(1.0 / 0.0)`
8
34
 
9
35
  # v0.8.10 2016-01-24
10
36
 
data/README.md CHANGED
@@ -340,6 +340,7 @@ Blog posts
340
340
 
341
341
  Sorted by recency:
342
342
 
343
+ * [A deep dive into mutation testing and how the Mutant gem works][troessner]
343
344
  * [How to write better code using mutation testing (November 2015)][blockscore]
344
345
  * [How good are your Ruby tests? Testing your tests with mutant (June 2015)][arkency1]
345
346
  * [Mutation testing and continuous integration (May 2015)][arkency2]
@@ -347,6 +348,7 @@ Sorted by recency:
347
348
  * [Mutation testing with mutant (April 2014)][sitepoint]
348
349
  * [Mutation testing with mutant (January 2013)][solnic]
349
350
 
351
+ [troessner]: https://troessner.svbtle.com/kill-all-the-mutants-a-deep-dive-into-mutation-testing-and-how-the-mutant-gem-works
350
352
  [blockscore]: https://blog.blockscore.com/how-to-write-better-code-using-mutation-testing/
351
353
  [sitepoint]: http://www.sitepoint.com/mutation-testing-mutant/
352
354
  [arkency1]: http://blog.arkency.com/2015/06/how-good-are-your-ruby-tests-testing-your-tests-with-mutant/
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 16
3
- total_score: 1284
3
+ total_score: 1317
@@ -91,6 +91,12 @@ require 'mutant/mutator/util/symbol'
91
91
  require 'mutant/mutator/node'
92
92
  require 'mutant/mutator/node/generic'
93
93
  require 'mutant/mutator/node/regexp'
94
+ require 'mutant/mutator/node/regexp/alternation_meta'
95
+ require 'mutant/mutator/node/regexp/capture_group'
96
+ require 'mutant/mutator/node/regexp/character_type'
97
+ require 'mutant/mutator/node/regexp/end_of_line_anchor'
98
+ require 'mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor'
99
+ require 'mutant/mutator/node/regexp/greedy_zero_or_more'
94
100
  require 'mutant/mutator/node/literal'
95
101
  require 'mutant/mutator/node/literal/boolean'
96
102
  require 'mutant/mutator/node/literal/range'
@@ -21,6 +21,13 @@ module Mutant
21
21
 
22
22
  public :arguments
23
23
 
24
+ # Test if node is defining a proc
25
+ #
26
+ # @return [Boolean]
27
+ def proc?
28
+ naked_proc? || proc_new?
29
+ end
30
+
24
31
  # Test if AST node is a valid assignment target
25
32
  #
26
33
  # @return [Boolean]
@@ -59,6 +66,25 @@ module Mutant
59
66
  Const.new(receiver).possible_top_level?
60
67
  end
61
68
 
69
+ private
70
+
71
+ # Test if node is `proc { ... }`
72
+ #
73
+ # @return [Boolean]
74
+ def naked_proc?
75
+ !receiver && selector.equal?(:proc)
76
+ end
77
+
78
+ # Test if node is `Proc.new { ... }`
79
+ #
80
+ # @return [Boolean]
81
+ def proc_new?
82
+ receiver &&
83
+ selector.equal?(:new) &&
84
+ n_const?(receiver) &&
85
+ Const.new(receiver).name.equal?(:Proc)
86
+ end
87
+
62
88
  end # Send
63
89
  end # Meta
64
90
  end # AST
@@ -70,6 +70,7 @@ module Mutant
70
70
  [:regexp_script_hiragana_property, [:property, :script_hiragana, '\p{Hiragana}'], ::Regexp::Expression::UnicodeProperty::Script],
71
71
  [:regexp_script_katakana_property, [:property, :script_katakana, '\p{Katakana}'], ::Regexp::Expression::UnicodeProperty::Script],
72
72
  [:regexp_letter_any_property, [:property, :letter_any, '\p{L}'], ::Regexp::Expression::UnicodeProperty::Letter::Any],
73
+ [:regexp_hex_type, [:type, :hex, '\h'], ::Regexp::Expression::CharacterType::Hex],
73
74
  [:regexp_digit_type, [:type, :digit, '\d'], ::Regexp::Expression::CharacterType::Digit],
74
75
  [:regexp_space_type, [:type, :space, '\s'], ::Regexp::Expression::CharacterType::Space],
75
76
  [:regexp_word_type, [:type, :word, '\w'], ::Regexp::Expression::CharacterType::Word],
@@ -56,17 +56,22 @@ module Mutant
56
56
  Env.new(
57
57
  actor_env: Actor::Env.new(Thread),
58
58
  config: config,
59
- integration: @integration,
59
+ integration: integration,
60
60
  matchable_scopes: matchable_scopes,
61
61
  mutations: subjects.flat_map(&:mutations),
62
62
  parser: parser,
63
- selector: Selector::Expression.new(@integration),
63
+ selector: Selector::Expression.new(integration),
64
64
  subjects: subjects
65
65
  )
66
66
  end
67
67
 
68
68
  private
69
69
 
70
+ # Configured mutant integration
71
+ #
72
+ # @return [Mutant::Integration]
73
+ attr_reader :integration
74
+
70
75
  # Scope name from scoping object
71
76
  #
72
77
  # @param [Class, Module] scope
@@ -75,6 +75,14 @@ module Mutant
75
75
  mutation('self')
76
76
  end
77
77
 
78
+ # Add regexp mutations
79
+ #
80
+ # @return [undefined]
81
+ def regexp_mutations
82
+ mutation('//')
83
+ mutation('/nomatch\A/')
84
+ end
85
+
78
86
  # Helper method to coerce input to node
79
87
  #
80
88
  # @param [String,Parser::AST::Node] input
@@ -5,16 +5,60 @@ module Mutant
5
5
  # Generic mutator
6
6
  class Generic < self
7
7
 
8
+ unsupported_nodes = %i[
9
+ ensure
10
+ redo
11
+ retry
12
+ arg_expr
13
+ blockarg
14
+ kwrestarg
15
+ undef
16
+ module
17
+ empty
18
+ alias
19
+ for
20
+ xstr
21
+ back_ref
22
+ restarg
23
+ sclass
24
+ match_with_lvasgn
25
+ while_post
26
+ until_post
27
+ preexe
28
+ postexe
29
+ iflipflop
30
+ eflipflop
31
+ kwsplat
32
+ shadowarg
33
+ rational
34
+ complex
35
+ __FILE__
36
+ __LINE__
37
+ ]
38
+
39
+ unsupported_regexp_nodes = AST::Types::REGEXP.to_a - %i[
40
+ regexp_alternation_meta
41
+ regexp_bol_anchor
42
+ regexp_capture_group
43
+ regexp_digit_type
44
+ regexp_eol_anchor
45
+ regexp_eos_ob_eol_anchor
46
+ regexp_greedy_zero_or_more
47
+ regexp_hex_type
48
+ regexp_nondigit_type
49
+ regexp_nonhex_type
50
+ regexp_nonspace_type
51
+ regexp_nonword_boundary_anchor
52
+ regexp_nonword_type
53
+ regexp_root_expression
54
+ regexp_space_type
55
+ regexp_word_boundary_anchor
56
+ regexp_word_type
57
+ ]
58
+
8
59
  # These nodes still need a dedicated mutator,
9
60
  # your contribution is that close!
10
- handle(
11
- :ensure, :redo, :retry, :arg_expr, :blockarg,
12
- :kwrestarg, :undef, :module, :empty,
13
- :alias, :for, :xstr, :back_ref, :restarg,
14
- :sclass, :match_with_lvasgn, :while_post,
15
- :until_post, :preexe, :postexe, :iflipflop, :eflipflop, :kwsplat,
16
- :shadowarg, :rational, :complex, :__FILE__, :__LINE__
17
- )
61
+ handle(*(unsupported_nodes + unsupported_regexp_nodes))
18
62
 
19
63
  private
20
64
 
@@ -2,17 +2,6 @@ module Mutant
2
2
  class Mutator
3
3
  class Node
4
4
  module Regexp
5
- # Generic regexp mutator
6
- class Generic < Node
7
- handle(*(AST::Types::REGEXP - %i[regexp_root_expression regexp_bol_anchor]))
8
-
9
- # Noop dispatch
10
- #
11
- # @return [undefined]
12
- def dispatch
13
- end
14
- end # Generic
15
-
16
5
  # Mutator for root expression regexp wrapper
17
6
  class RootExpression < Node
18
7
  handle(:regexp_root_expression)
@@ -0,0 +1,21 @@
1
+ module Mutant
2
+ class Mutator
3
+ class Node
4
+ module Regexp
5
+ # Mutator for pipe in `/foo|bar/` regexp
6
+ class AlternationMeta < Node
7
+ handle(:regexp_alternation_meta)
8
+
9
+ private
10
+
11
+ # Dispatch mutations
12
+ #
13
+ # @return [undefined]
14
+ def dispatch
15
+ children.each_index(&method(:delete_child))
16
+ end
17
+ end # AlternationMeta
18
+ end # Regexp
19
+ end # Node
20
+ end # Mutator
21
+ end # Mutant
@@ -0,0 +1,26 @@
1
+ module Mutant
2
+ class Mutator
3
+ class Node
4
+ module Regexp
5
+ # Mutator for regexp capture groups, such as `/(foo)/`
6
+ class CaptureGroup < Node
7
+ handle(:regexp_capture_group)
8
+
9
+ children :group
10
+
11
+ # Emit mutations
12
+ #
13
+ # Replace `(captured_group)` with `(?:non_captured_group)`
14
+ #
15
+ # @return [undefined]
16
+ def dispatch
17
+ return unless group
18
+
19
+ emit(s(:regexp_passive_group, group))
20
+ emit_group_mutations
21
+ end
22
+ end # EndOfLineAnchor
23
+ end # Regexp
24
+ end # Node
25
+ end # Mutator
26
+ end # Mutant
@@ -0,0 +1,29 @@
1
+ module Mutant
2
+ class Mutator
3
+ class Node
4
+ module Regexp
5
+ # Character type mutator
6
+ class CharacterType < Node
7
+ map = {
8
+ regexp_digit_type: :regexp_nondigit_type,
9
+ regexp_hex_type: :regexp_nonhex_type,
10
+ regexp_space_type: :regexp_nonspace_type,
11
+ regexp_word_boundary_anchor: :regexp_nonword_boundary_anchor,
12
+ regexp_word_type: :regexp_nonword_type
13
+ }
14
+
15
+ MAP = IceNine.deep_freeze(map.merge(map.invert))
16
+
17
+ handle(*MAP.keys)
18
+
19
+ # Mutate to invert character type
20
+ #
21
+ # @return [undefined]
22
+ def dispatch
23
+ emit(s(MAP.fetch(node.type)))
24
+ end
25
+ end # CharacterType
26
+ end # Regexp
27
+ end # Node
28
+ end # Mutator
29
+ end # Mutant
@@ -0,0 +1,21 @@
1
+ module Mutant
2
+ class Mutator
3
+ class Node
4
+ module Regexp
5
+ # Mutator for end of line anchor `$`
6
+ class EndOfLineAnchor < Node
7
+ handle(:regexp_eol_anchor)
8
+
9
+ # Emit mutations
10
+ #
11
+ # Replace `$` with `\z`
12
+ #
13
+ # @return [undefined]
14
+ def dispatch
15
+ emit(s(:regexp_eos_anchor))
16
+ end
17
+ end # EndOfLineAnchor
18
+ end # Regexp
19
+ end # Node
20
+ end # Mutator
21
+ end # Mutant
@@ -0,0 +1,21 @@
1
+ module Mutant
2
+ class Mutator
3
+ class Node
4
+ module Regexp
5
+ # Mutator for end of line or before end of string anchor `\Z`
6
+ class EndOfStringOrBeforeEndOfLineAnchor < Node
7
+ handle(:regexp_eos_ob_eol_anchor)
8
+
9
+ # Emit mutations
10
+ #
11
+ # Replace `\Z` with `\z`
12
+ #
13
+ # @return [undefined]
14
+ def dispatch
15
+ emit(s(:regexp_eos_anchor))
16
+ end
17
+ end # EndOfStringOrBeforeEndOfLineAnchor
18
+ end # Regexp
19
+ end # Node
20
+ end # Mutator
21
+ end # Mutant
@@ -0,0 +1,25 @@
1
+ module Mutant
2
+ class Mutator
3
+ class Node
4
+ module Regexp
5
+ # Mutator for greedy zero-or-more quantifier, `*`
6
+ class GreedyZeroOrMore < Node
7
+ handle(:regexp_greedy_zero_or_more)
8
+
9
+ children :min, :max, :subject
10
+
11
+ # Emit mutations
12
+ #
13
+ # Replace `/a*/` with `/a+/`
14
+ #
15
+ # @return [undefined]
16
+ def dispatch
17
+ emit(s(:regexp_greedy_one_or_more, *children))
18
+ emit_subject_mutations
19
+ emit(subject)
20
+ end
21
+ end # GreedyZeroOrMore
22
+ end # Regexp
23
+ end # Node
24
+ end # Mutator
25
+ end # Mutant
@@ -18,6 +18,7 @@ module Mutant
18
18
  reverse_each: %i[each],
19
19
  reverse_merge: %i[merge],
20
20
  map: %i[each],
21
+ flat_map: %i[map],
21
22
  sample: %i[first last],
22
23
  pop: %i[last],
23
24
  shift: %i[first],
@@ -110,6 +111,8 @@ module Mutant
110
111
  emit_const_get_mutation
111
112
  emit_integer_mutation
112
113
  emit_dig_mutation
114
+ emit_double_negation_mutation
115
+ emit_lambda_mutation
113
116
  emit_drop_mutation
114
117
  end
115
118
 
@@ -125,6 +128,23 @@ module Mutant
125
128
  .each(&method(:emit_selector))
126
129
  end
127
130
 
131
+ # Emit mutation from `!!foo` to `foo`
132
+ #
133
+ # @return [undefined]
134
+ def emit_double_negation_mutation
135
+ return unless selector.equal?(:!) && n_send?(receiver)
136
+
137
+ negated = AST::Meta::Send.new(meta.receiver)
138
+ emit(negated.receiver) if negated.selector.equal?(:!)
139
+ end
140
+
141
+ # Emit mutation from proc definition to lambda
142
+ #
143
+ # @return [undefined]
144
+ def emit_lambda_mutation
145
+ emit(s(:send, nil, :lambda)) if meta.proc?
146
+ end
147
+
128
148
  # Emit mutation for `#dig`
129
149
  #
130
150
  # - Mutates `foo.dig(a, b)` to `foo.fetch(a).dig(b)`
@@ -232,5 +252,4 @@ module Mutant
232
252
  end # Send
233
253
  end # Node
234
254
  end # Mutator
235
-
236
255
  end # Mutant