mutant 0.7.4 → 0.7.5

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