mutant 0.8.11 → 0.8.12

Sign up to get free protection for your applications and to get access to all the features.
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