mutant 0.7.4 → 0.7.5

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -2
  3. data/Changelog.md +6 -2
  4. data/Gemfile +1 -0
  5. data/config/flay.yml +1 -1
  6. data/config/reek.yml +1 -1
  7. data/lib/mutant.rb +4 -3
  8. data/lib/mutant/cli.rb +2 -2
  9. data/lib/mutant/config.rb +2 -3
  10. data/lib/mutant/env.rb +29 -132
  11. data/lib/mutant/env/bootstrap.rb +155 -0
  12. data/lib/mutant/expression.rb +27 -0
  13. data/lib/mutant/expression/namespace.rb +2 -2
  14. data/lib/mutant/matcher/method.rb +1 -1
  15. data/lib/mutant/matcher/method/instance.rb +1 -1
  16. data/lib/mutant/mutation.rb +2 -30
  17. data/lib/mutant/mutator/node/begin.rb +1 -3
  18. data/lib/mutant/mutator/node/if.rb +2 -2
  19. data/lib/mutant/reporter/cli/printer.rb +5 -4
  20. data/lib/mutant/result.rb +1 -1
  21. data/lib/mutant/runner.rb +3 -3
  22. data/lib/mutant/runner/sink.rb +86 -53
  23. data/lib/mutant/selector.rb +17 -0
  24. data/lib/mutant/selector/expression.rb +28 -0
  25. data/lib/mutant/subject.rb +1 -19
  26. data/lib/mutant/version.rb +1 -1
  27. data/mutant.gemspec +3 -3
  28. data/spec/spec_helper.rb +1 -1
  29. data/spec/support/corpus.rb +5 -5
  30. data/spec/support/shared_context.rb +1 -6
  31. data/spec/unit/mutant/cli_spec.rb +2 -2
  32. data/spec/unit/mutant/env/boostrap_spec.rb +130 -0
  33. data/spec/unit/mutant/env_spec.rb +61 -49
  34. data/spec/unit/mutant/expression_spec.rb +13 -0
  35. data/spec/unit/mutant/matcher/filter_spec.rb +1 -1
  36. data/spec/unit/mutant/matcher/method/instance_spec.rb +1 -1
  37. data/spec/unit/mutant/matcher/method/singleton_spec.rb +1 -1
  38. data/spec/unit/mutant/mutation_spec.rb +0 -36
  39. data/spec/unit/mutant/reporter/trace_spec.rb +1 -1
  40. data/spec/unit/mutant/require_highjack_spec.rb +2 -4
  41. data/spec/unit/mutant/result/subject_spec.rb +2 -1
  42. data/spec/unit/mutant/runner/{sink_spec.rb → sink/mutation_spec.rb} +1 -3
  43. data/spec/unit/mutant/runner_spec.rb +4 -3
  44. data/spec/unit/mutant/selector/expression_spec.rb +60 -0
  45. data/spec/unit/mutant/subject/method/instance_spec.rb +3 -5
  46. data/spec/unit/mutant/subject/method/singleton_spec.rb +2 -3
  47. data/spec/unit/mutant/subject_spec.rb +1 -53
  48. data/spec/unit/mutant/warning_filter_spec.rb +2 -4
  49. metadata +18 -15
  50. data/lib/mutant/line_trace.rb +0 -34
  51. data/spec/unit/mutant/line_trace_spec.rb +0 -38
  52. data/spec/unit/mutant/subject/context_spec.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 064e05498cecb534a1466bfe3ff858ff0487acf3
4
- data.tar.gz: 585a00d32574fdabf20943a96d4ee48c6ec9408f
3
+ metadata.gz: 9aa6cee93941cfb9307169023bb523bdc18c290a
4
+ data.tar.gz: 3130e644fa7c02db1ed0adef9300d711269e7323
5
5
  SHA512:
6
- metadata.gz: 97ffbc8174e5027a924037359084af1f49bf9fe8c14f8408ba25b28444a9822d145f59c8d90255c27fabfe744d6b609db12cf3e201fa460c955fca5c7e60ecbb
7
- data.tar.gz: 2d0bdcd5a5c974d6a9504cd6f8bbfa6b45bdef56526ce054cded8b98f1e53071063afe05e7a72cf68bee9752ba7b24be827365d28390985658ba099023f0009b
6
+ metadata.gz: eba0e605c2fd699c0bd22b08d65332701c14a190da37e017d400fadc8a7869b54dbfefb577c537d329da487039046e3868529a9ec88847d4c3f7a17cab0c1845
7
+ data.tar.gz: 3ff08e6ca56e920894f440e90084599116731914c662f61780a09d800db505794207c79919ffb3dc945d1a3f9780280523527598932f0ef7f5e077e8d499c771
@@ -4,12 +4,13 @@ script: "bundle exec rake ci"
4
4
  env:
5
5
  - TRAVIS=true
6
6
  rvm:
7
+ - '1.9'
7
8
  - '2.0'
8
9
  - '2.1'
9
- - rbx-2
10
+ - rbx
10
11
  matrix:
11
12
  allow_failures:
12
- - rvm: rbx-2 # travis does not maintain rbx setup correctly
13
+ - rvm: rbx
13
14
  notifications:
14
15
  irc:
15
16
  channels:
@@ -1,7 +1,11 @@
1
+ # v0.7.5 2015-01-14
2
+
3
+ * Bump deps to support MRI 2.2.
4
+
1
5
  # v0.7.4 2014-12-22
2
6
 
3
- * Fix rspec example visibility on duplicate metadata examples
4
- * Add naked if/else body emitter
7
+ * Fix rspec example visibility on duplicate metadata examples [#279](https://github.com/mbj/mutant/issues/279).
8
+ * Add naked if/else body emitter [#280](https://github.com/mbj/mutant/issues/280).
5
9
 
6
10
  # v0.7.3 2014-12-09
7
11
 
data/Gemfile CHANGED
@@ -5,3 +5,4 @@ source 'https://rubygems.org'
5
5
  gemspec name: 'mutant'
6
6
 
7
7
  gem 'devtools', git: 'https://github.com/rom-rb/devtools.git'
8
+ eval_gemfile 'Gemfile.devtools'
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 18
3
- total_score: 1198
3
+ total_score: 1220
@@ -47,7 +47,7 @@ NestedIterators:
47
47
  - Mutant::Mutator::Node::Arguments#emit_argument_mutations
48
48
  - Mutant::RequireHighjack#infect
49
49
  - Mutant::RequireHighjack#disinfect
50
- - Mutant::Subject#tests
50
+ - Mutant::Selector::Expression#call
51
51
  - Mutant::Parallel::Master#run
52
52
  - Parser::Lexer#self.new
53
53
  max_allowed_nesting: 1
@@ -95,6 +95,7 @@ end # Mutant
95
95
 
96
96
  require 'mutant/version'
97
97
  require 'mutant/env'
98
+ require 'mutant/env/bootstrap'
98
99
  require 'mutant/ast'
99
100
  require 'mutant/ast/sexp'
100
101
  require 'mutant/ast/types'
@@ -202,6 +203,8 @@ require 'mutant/expression/methods'
202
203
  require 'mutant/expression/namespace'
203
204
  require 'mutant/test'
204
205
  require 'mutant/integration'
206
+ require 'mutant/selector'
207
+ require 'mutant/selector/expression'
205
208
  require 'mutant/cli'
206
209
  require 'mutant/color'
207
210
  require 'mutant/diff'
@@ -215,7 +218,6 @@ require 'mutant/reporter/cli'
215
218
  require 'mutant/reporter/cli/printer'
216
219
  require 'mutant/reporter/cli/tput'
217
220
  require 'mutant/reporter/cli/format'
218
- require 'mutant/line_trace'
219
221
  require 'mutant/zombifier'
220
222
  require 'mutant/zombifier/file'
221
223
 
@@ -228,14 +230,13 @@ module Mutant
228
230
  debug: false,
229
231
  fail_fast: false,
230
232
  integration: Integration::Null.new,
231
- matcher_config: Matcher::Config::DEFAULT,
233
+ matcher: Matcher::Config::DEFAULT,
232
234
  includes: EMPTY_ARRAY,
233
235
  requires: EMPTY_ARRAY,
234
236
  isolation: Mutant::Isolation::Fork,
235
237
  reporter: Reporter::CLI.build($stdout),
236
238
  zombie: false,
237
239
  jobs: Mutant.ci? ? CI_DEFAULT_PROCESSOR_COUNT : ::Parallel.processor_count,
238
- actor_env: Mutant::Actor::Env.new(Thread),
239
240
  expected_coverage: 100.0
240
241
  )
241
242
  end # Config
@@ -22,7 +22,7 @@ module Mutant
22
22
  # @api private
23
23
  #
24
24
  def self.run(arguments)
25
- Runner.call(Env.new(call(arguments))).success? ? EXIT_SUCCESS : EXIT_FAILURE
25
+ Runner.call(Env::Bootstrap.call(call(arguments))).success? ? EXIT_SUCCESS : EXIT_FAILURE
26
26
  rescue Error => exception
27
27
  $stderr.puts(exception.message)
28
28
  EXIT_FAILURE
@@ -235,7 +235,7 @@ module Mutant
235
235
  # @api private
236
236
  #
237
237
  def add_matcher(attribute, value)
238
- update(matcher_config: config.matcher_config.add(attribute, value))
238
+ update(matcher: config.matcher.add(attribute, value))
239
239
  end
240
240
 
241
241
  end # CLI
@@ -4,7 +4,7 @@ module Mutant
4
4
  include Adamantium::Flat, Anima::Update, Anima.new(
5
5
  :debug,
6
6
  :integration,
7
- :matcher_config,
7
+ :matcher,
8
8
  :includes,
9
9
  :requires,
10
10
  :reporter,
@@ -12,8 +12,7 @@ module Mutant
12
12
  :fail_fast,
13
13
  :jobs,
14
14
  :zombie,
15
- :expected_coverage,
16
- :actor_env
15
+ :expected_coverage
17
16
  )
18
17
 
19
18
  [:fail_fast, :zombie, :debug].each do |name|
@@ -1,39 +1,20 @@
1
1
  module Mutant
2
2
  # Abstract base class for mutant environments
3
3
  class Env
4
- include Adamantium::Flat, Concord::Public.new(:config, :cache)
4
+ include Adamantium::Flat, Anima::Update, Anima.new(
5
+ :config,
6
+ :actor_env,
7
+ :cache,
8
+ :subjects,
9
+ :matchable_scopes,
10
+ :selector,
11
+ :mutations
12
+ )
5
13
 
6
14
  SEMANTICS_MESSAGE =
7
15
  "Fix your lib to follow normal ruby semantics!\n" \
8
16
  '{Module,Class}#name should return resolvable constant name as String or nil'.freeze
9
17
 
10
- # Return new env
11
- #
12
- # @param [Config] config
13
- #
14
- # @return [Env]
15
- #
16
- # @api private
17
- #
18
- def self.new(config, cache = Cache.new)
19
- super(config, cache)
20
- end
21
-
22
- # Initialize env
23
- #
24
- # @return [undefined]
25
- #
26
- # @api private
27
- #
28
- def initialize(*)
29
- super
30
-
31
- infect
32
- initialize_matchable_scopes
33
- initialize_subjects
34
- initialize_mutations
35
- end
36
-
37
18
  # Print warning message
38
19
  #
39
20
  # @param [String]
@@ -47,30 +28,6 @@ module Mutant
47
28
  self
48
29
  end
49
30
 
50
- # Return subjects
51
- #
52
- # @return [Array<Subject>]
53
- #
54
- # @api private
55
- #
56
- attr_reader :subjects
57
-
58
- # Return mutations
59
- #
60
- # @return [Array<Mutation>]
61
- #
62
- # @api private
63
- #
64
- attr_reader :mutations
65
-
66
- # Return all usable match scopes
67
- #
68
- # @return [Array<Matcher::Scope>]
69
- #
70
- # @api private
71
- #
72
- attr_reader :matchable_scopes
73
-
74
31
  # Kill mutation
75
32
  #
76
33
  # @param [Mutation] mutation
@@ -79,8 +36,8 @@ module Mutant
79
36
  #
80
37
  # @api private
81
38
  #
82
- def kill_mutation(mutation)
83
- test_result = mutation.kill(config.isolation, config.integration)
39
+ def kill(mutation)
40
+ test_result = run_mutation_tests(mutation)
84
41
  Result::Mutation.new(
85
42
  mutation: mutation,
86
43
  test_result: test_result
@@ -89,92 +46,32 @@ module Mutant
89
46
 
90
47
  private
91
48
 
92
- # Return scope name
93
- #
94
- # @param [Class, Module] scope
49
+ # Kill mutation under isolation with integration
95
50
  #
96
- # @return [String]
97
- # if scope has a name and does not raise exceptions obtaining it
51
+ # @param [Isolation] isolation
52
+ # @param [Integration] integration
98
53
  #
99
- # @return [nil]
100
- # otherwise
54
+ # @return [Result::Test]
101
55
  #
102
56
  # @api private
103
57
  #
104
- # rubocop:disable LineLength
105
- #
106
- def scope_name(scope)
107
- scope.name
108
- rescue => exception
109
- warn("#{scope.class}#name from: #{scope.inspect} raised an error: #{exception.inspect}. #{SEMANTICS_MESSAGE}")
110
- nil
111
- end
112
-
113
- # Try to turn scope into expression
114
- #
115
- # @param [Class, Module] scope
116
- #
117
- # @return [Expression]
118
- # if scope can be represented in an expression
119
- #
120
- # @return [nil]
121
- # otherwise
58
+ # rubocop:disable MethodLength
122
59
  #
123
- # @api private
124
- #
125
- def expression(scope)
126
- name = scope_name(scope) or return
60
+ def run_mutation_tests(mutation)
61
+ start = Time.now
62
+ tests = selector.call(mutation.subject)
127
63
 
128
- unless name.instance_of?(String)
129
- warn("#{scope.class}#name from: #{scope.inspect} returned #{name.inspect}. #{SEMANTICS_MESSAGE}")
130
- return
64
+ config.isolation.call do
65
+ mutation.insert
66
+ config.integration.call(tests)
131
67
  end
132
-
133
- Expression.try_parse(name)
134
- end
135
-
136
- # Initialize subjects
137
- #
138
- # @return [undefined]
139
- #
140
- # @api private
141
- #
142
- def initialize_subjects
143
- @subjects = Matcher::Compiler.call(self, config.matcher_config).to_a
144
- end
145
-
146
- # Initialize mutations
147
- #
148
- # @return [undefined]
149
- #
150
- # @api private
151
- #
152
- def initialize_mutations
153
- @mutations = subjects.flat_map(&:mutations)
154
- end
155
-
156
- # Infect environment
157
- #
158
- # @return [undefined]
159
- #
160
- # @api private
161
- #
162
- def infect
163
- config.includes.each(&$LOAD_PATH.method(:<<))
164
- config.requires.each(&method(:require))
165
- end
166
-
167
- # Initialize matchable scopes
168
- #
169
- # @return [undefined]
170
- #
171
- # @api private
172
- #
173
- def initialize_matchable_scopes
174
- @matchable_scopes = ObjectSpace.each_object(Module).each_with_object([]) do |scope, aggregate|
175
- expression = expression(scope)
176
- aggregate << Matcher::Scope.new(self, scope, expression) if expression
177
- end.sort_by(&:identification)
68
+ rescue Isolation::Error => error
69
+ Result::Test.new(
70
+ tests: tests,
71
+ output: error.message,
72
+ runtime: Time.now - start,
73
+ passed: false
74
+ )
178
75
  end
179
76
 
180
77
  end # Env
@@ -0,0 +1,155 @@
1
+ module Mutant
2
+ class Env
3
+ # Boostrap environment
4
+ class Bootstrap
5
+ include Adamantium::Flat, Concord::Public.new(:config, :cache), Procto.call(:env)
6
+
7
+ SEMANTICS_MESSAGE =
8
+ "Fix your lib to follow normal ruby semantics!\n" \
9
+ '{Module,Class}#name should return resolvable constant name as String or nil'.freeze
10
+
11
+ # Return scopes that are eligible for mnatching
12
+ #
13
+ # @return [Enumerable<Matcher::Scope>]
14
+ #
15
+ # @api private
16
+ #
17
+ attr_reader :matchable_scopes
18
+
19
+ # Return new bootstrap env
20
+ #
21
+ # @return [Env]
22
+ #
23
+ # @api private
24
+ #
25
+ def self.new(_config, _cache = Cache.new)
26
+ super
27
+ end
28
+
29
+ # Initialize object
30
+ #
31
+ # @return [Object]
32
+ #
33
+ # @api private
34
+ #
35
+ def initialize(*)
36
+ super
37
+ infect
38
+ initialize_matchable_scopes
39
+ end
40
+
41
+ # Print warning message
42
+ #
43
+ # @param [String]
44
+ #
45
+ # @return [self]
46
+ #
47
+ # @api private
48
+ #
49
+ def warn(message)
50
+ config.reporter.warn(message)
51
+ self
52
+ end
53
+
54
+ # Return environment after bootstraping
55
+ #
56
+ # @return [Env]
57
+ #
58
+ # @api private
59
+ #
60
+ def env
61
+ subjects = matched_subjects
62
+
63
+ Env.new(
64
+ actor_env: Actor::Env.new(Thread),
65
+ config: config,
66
+ cache: cache,
67
+ subjects: subjects,
68
+ matchable_scopes: matchable_scopes,
69
+ selector: Selector::Expression.new(config.integration),
70
+ mutations: subjects.flat_map(&:mutations)
71
+ )
72
+ end
73
+
74
+ private
75
+
76
+ # Return scope name
77
+ #
78
+ # @param [Class, Module] scope
79
+ #
80
+ # @return [String]
81
+ # if scope has a name and does not raise exceptions obtaining it
82
+ #
83
+ # @return [nil]
84
+ # otherwise
85
+ #
86
+ # @api private
87
+ #
88
+ # rubocop:disable LineLength
89
+ #
90
+ def scope_name(scope)
91
+ scope.name
92
+ rescue => exception
93
+ warn("#{scope.class}#name from: #{scope.inspect} raised an error: #{exception.inspect}. #{SEMANTICS_MESSAGE}")
94
+ nil
95
+ end
96
+
97
+ # Infect environment
98
+ #
99
+ # @return [undefined]
100
+ #
101
+ # @api private
102
+ #
103
+ def infect
104
+ config.includes.each(&$LOAD_PATH.method(:<<))
105
+ config.requires.each(&method(:require))
106
+ end
107
+
108
+ # Try to turn scope into expression
109
+ #
110
+ # @param [Class, Module] scope
111
+ #
112
+ # @return [Expression]
113
+ # if scope can be represented in an expression
114
+ #
115
+ # @return [nil]
116
+ # otherwise
117
+ #
118
+ # @api private
119
+ #
120
+ def expression(scope)
121
+ name = scope_name(scope) or return
122
+
123
+ unless name.instance_of?(String)
124
+ warn("#{scope.class}#name from: #{scope.inspect} returned #{name.inspect}. #{SEMANTICS_MESSAGE}")
125
+ return
126
+ end
127
+
128
+ Expression.try_parse(name)
129
+ end
130
+
131
+ # Return matched subjects
132
+ #
133
+ # @return [Enumerable<Subject>]
134
+ #
135
+ # @api private
136
+ #
137
+ def matched_subjects
138
+ Matcher::Compiler.call(self, config.matcher).to_a
139
+ end
140
+
141
+ # Initialize matchable scopes
142
+ #
143
+ # @return [undefined]
144
+ #
145
+ # @api private
146
+ #
147
+ def initialize_matchable_scopes
148
+ @matchable_scopes = ObjectSpace.each_object(Module).each_with_object([]) do |scope, aggregate|
149
+ expression = expression(scope)
150
+ aggregate << Matcher::Scope.new(self, scope, expression) if expression
151
+ end.sort_by(&:identification)
152
+ end
153
+ end # Boostrap
154
+ end # Env
155
+ end # Mutant