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
@@ -37,6 +37,33 @@ module Mutant
37
37
  @inspect = format(INSPECT_FORMAT, syntax)
38
38
  end
39
39
 
40
+ # Return marshallable representation
41
+ #
42
+ # FIXME: Remove the need for this.
43
+ #
44
+ # Refactoring Expression objects not to reference a MatchData instance.
45
+ # This will make this hack unneeded.
46
+ #
47
+ # @return [String]
48
+ #
49
+ # @api private
50
+ #
51
+ def _dump(_level)
52
+ syntax
53
+ end
54
+
55
+ # Load serializable representation
56
+ #
57
+ # @return [String]
58
+ #
59
+ # @return [Expression]
60
+ #
61
+ # @api private
62
+ #
63
+ def self._load(syntax)
64
+ parse(syntax)
65
+ end
66
+
40
67
  # Return inspection
41
68
  #
42
69
  # @return [String]
@@ -37,7 +37,7 @@ module Mutant
37
37
 
38
38
  # Return matcher
39
39
  #
40
- # @param [Env] env
40
+ # @param [Env::Bootstrap] env
41
41
  #
42
42
  # @return [Matcher]
43
43
  #
@@ -74,7 +74,7 @@ module Mutant
74
74
 
75
75
  # Return matcher
76
76
  #
77
- # @param [Cache] env
77
+ # @param [Env::Bootstrap] env
78
78
  #
79
79
  # @return [Matcher]
80
80
  #
@@ -123,7 +123,7 @@ module Mutant
123
123
  def subject
124
124
  node = matched_node_path.last
125
125
  return unless node
126
- self.class::SUBJECT_CLASS.new(env.config, context, node)
126
+ self.class::SUBJECT_CLASS.new(context, node)
127
127
  end
128
128
  memoize :subject
129
129
 
@@ -7,7 +7,7 @@ module Mutant
7
7
 
8
8
  # Dispatching builder, detects memoizable case
9
9
  #
10
- # @param [Env] env
10
+ # @param [Env::Boostrap] env
11
11
  # @param [Class, Module] scope
12
12
  # @param [UnboundMethod] method
13
13
  #
@@ -7,34 +7,6 @@ module Mutant
7
7
  CODE_DELIMITER = "\0".freeze
8
8
  CODE_RANGE = (0..4).freeze
9
9
 
10
- # Kill mutation under isolation with integration
11
- #
12
- # @param [Isolation] isolation
13
- # @param [Integration] integration
14
- #
15
- # @return [Result::Test]
16
- #
17
- # @api private
18
- #
19
- # rubocop:disable MethodLength
20
- #
21
- def kill(isolation, integration)
22
- start = Time.now
23
- tests = subject.tests
24
-
25
- isolation.call do
26
- insert
27
- integration.call(tests)
28
- end.update(tests: tests)
29
- rescue Isolation::Error => error
30
- Result::Test.new(
31
- tests: tests,
32
- output: error.message,
33
- runtime: Time.now - start,
34
- passed: false
35
- )
36
- end
37
-
38
10
  # Return identification
39
11
  #
40
12
  # @return [String]
@@ -90,8 +62,6 @@ module Mutant
90
62
  self::TEST_PASS_SUCCESS.equal?(test_result.passed)
91
63
  end
92
64
 
93
- private
94
-
95
65
  # Insert mutated node
96
66
  #
97
67
  # FIXME: Cache subject visibility in a better way! Ideally dont mutate it
@@ -109,6 +79,8 @@ module Mutant
109
79
  self
110
80
  end
111
81
 
82
+ private
83
+
112
84
  # Return sha1 sum of source and subject identification
113
85
  #
114
86
  # @return [String]
@@ -16,9 +16,7 @@ module Mutant
16
16
  # @api private
17
17
  #
18
18
  def dispatch
19
- Util::Array.each(children, self) do |children|
20
- emit_child_subset(children)
21
- end
19
+ Util::Array.each(children, self, &method(:emit_child_subset))
22
20
  children.each_with_index do |child, index|
23
21
  mutate_child(index)
24
22
  emit(child) unless children.eql?([child])
@@ -30,8 +30,8 @@ module Mutant
30
30
  # @api private
31
31
  #
32
32
  def mutate_condition
33
- emit_condition_mutations do |condition|
34
- !n_self?(condition)
33
+ emit_condition_mutations do |node|
34
+ !n_self?(node)
35
35
  end
36
36
  emit_type(n_not(condition), if_branch, else_branch) unless n_match_current_line?(condition)
37
37
  emit_type(N_TRUE, if_branch, else_branch)
@@ -201,7 +201,7 @@ module Mutant
201
201
  #
202
202
  def run
203
203
  info 'Mutant configuration:'
204
- info 'Matcher: %s', object.matcher_config.inspect
204
+ info 'Matcher: %s', object.matcher.inspect
205
205
  info 'Integration: %s', object.integration.name
206
206
  info 'Expect Coverage: %0.2f%%', object.expected_coverage.inspect
207
207
  info 'Jobs: %d', object.jobs
@@ -295,7 +295,7 @@ module Mutant
295
295
  # Subject report printer
296
296
  class SubjectResult < self
297
297
 
298
- delegate :subject, :failed_mutations
298
+ delegate :subject, :failed_mutations, :tests
299
299
 
300
300
  # Run report printer
301
301
  #
@@ -305,7 +305,7 @@ module Mutant
305
305
  #
306
306
  def run
307
307
  status(subject.identification)
308
- subject.tests.each do |test|
308
+ tests.each do |test|
309
309
  puts("- #{test.identification}")
310
310
  end
311
311
  visit_collection(MutationResult, object.alive_mutation_results)
@@ -400,6 +400,7 @@ module Mutant
400
400
  FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze
401
401
 
402
402
  delegate(
403
+ :tests,
403
404
  :subject,
404
405
  :coverage,
405
406
  :runtime,
@@ -452,7 +453,7 @@ module Mutant
452
453
  # @api private
453
454
  #
454
455
  def print_tests
455
- subject.tests.each do |test|
456
+ tests.each do |test|
456
457
  puts "- #{test.identification}"
457
458
  end
458
459
  end
@@ -147,7 +147,7 @@ module Mutant
147
147
 
148
148
  # Subject result
149
149
  class Subject
150
- include Coverage, Result, Anima.new(:subject, :mutation_results)
150
+ include Coverage, Result, Anima.new(:subject, :tests, :mutation_results)
151
151
 
152
152
  sum :killtime, :mutation_results
153
153
  sum :runtime, :mutation_results
@@ -72,11 +72,11 @@ module Mutant
72
72
  #
73
73
  def mutation_test_config
74
74
  Parallel::Config.new(
75
- env: config.actor_env,
75
+ env: env.actor_env,
76
76
  jobs: config.jobs,
77
77
  source: Parallel::Source::Array.new(env.mutations),
78
- sink: Sink.new(env),
79
- processor: env.method(:kill_mutation)
78
+ sink: Sink::Mutation.new(env),
79
+ processor: env.method(:kill)
80
80
  )
81
81
  end
82
82
 
@@ -1,82 +1,115 @@
1
1
  module Mutant
2
2
  class Runner
3
- # Mutation result sink
3
+ # Abstract base class for computation sinks
4
4
  class Sink
5
- include Concord.new(:env)
5
+ include AbstractType
6
6
 
7
- # Initialize object
7
+ # Return sink status
8
8
  #
9
- # @return [undefined]
9
+ # @return [Object]
10
10
  #
11
11
  # @api private
12
12
  #
13
- def initialize(*)
14
- super
15
- @start = Time.now
16
- @subject_results = Hash.new do |_hash, subject|
17
- Result::Subject.new(
18
- subject: subject,
19
- mutation_results: []
20
- )
21
- end
22
- end
23
-
24
- # Return runner status
25
- #
26
- # @return [Status]
27
- #
28
- # @api private
29
- #
30
- def status
31
- env_result
32
- end
13
+ abstract_method :status
33
14
 
34
- # Test if scheduling stopped
15
+ # Test if computation should be stopped
35
16
  #
36
17
  # @return [Boolean]
37
18
  #
38
19
  # @api private
39
20
  #
40
- def stop?
41
- env.config.fail_fast && !env_result.subject_results.all?(&:success?)
42
- end
21
+ abstract_method :stop?
43
22
 
44
- # Handle mutation finish
23
+ # Consume result
45
24
  #
46
- # @param [Result::Mutation] mutation_result
25
+ # @param [Object] result
47
26
  #
48
27
  # @return [self]
49
28
  #
50
29
  # @api private
51
30
  #
52
- def result(mutation_result)
53
- mutation = mutation_result.mutation
31
+ abstract_method :result
54
32
 
55
- original = @subject_results[mutation.subject]
33
+ # Mutation result sink
34
+ class Mutation < self
35
+ include Concord.new(:env)
56
36
 
57
- @subject_results[mutation.subject] = original.update(
58
- mutation_results: (original.mutation_results.dup << mutation_result)
59
- )
37
+ # Initialize object
38
+ #
39
+ # @return [undefined]
40
+ #
41
+ # @api private
42
+ #
43
+ def initialize(*)
44
+ super
45
+ @start = Time.now
46
+ @subject_results = Hash.new do |_hash, subject|
47
+ Result::Subject.new(
48
+ subject: subject,
49
+ tests: [],
50
+ mutation_results: []
51
+ )
52
+ end
53
+ end
60
54
 
61
- self
62
- end
55
+ # Return runner status
56
+ #
57
+ # @return [Status]
58
+ #
59
+ # @api private
60
+ #
61
+ def status
62
+ env_result
63
+ end
63
64
 
64
- private
65
+ # Test if scheduling stopped
66
+ #
67
+ # @return [Boolean]
68
+ #
69
+ # @api private
70
+ #
71
+ def stop?
72
+ env.config.fail_fast && !env_result.subject_results.all?(&:success?)
73
+ end
65
74
 
66
- # Return current result
67
- #
68
- # @return [Result::Env]
69
- #
70
- # @api private
71
- #
72
- def env_result
73
- Result::Env.new(
74
- env: env,
75
- runtime: Time.now - @start,
76
- subject_results: @subject_results.values
77
- )
78
- end
75
+ # Handle mutation finish
76
+ #
77
+ # @param [Result::Mutation] mutation_result
78
+ #
79
+ # @return [self]
80
+ #
81
+ # @api private
82
+ #
83
+ def result(mutation_result)
84
+ mutation = mutation_result.mutation
85
+
86
+ original = @subject_results[mutation.subject]
87
+
88
+ @subject_results[mutation.subject] = original.update(
89
+ mutation_results: (original.mutation_results.dup << mutation_result),
90
+ tests: mutation_result.test_result.tests
91
+ )
92
+
93
+ self
94
+ end
95
+
96
+ private
97
+
98
+ # Return current result
99
+ #
100
+ # @return [Result::Env]
101
+ #
102
+ # @api private
103
+ #
104
+ def env_result
105
+ Result::Env.new(
106
+ env: env,
107
+ runtime: Time.now - @start,
108
+ subject_results: @subject_results.values
109
+ )
110
+ end
79
111
 
80
- end # Scheduler
112
+ end # Mutation
113
+ end # Sink
81
114
  end # Runner
82
115
  end # Mutant
@@ -0,0 +1,17 @@
1
+ module Mutant
2
+ # Abstract base class for test selectors
3
+ class Selector
4
+ include AbstractType, Adamantium::Flat
5
+
6
+ # Return tests for subject
7
+ #
8
+ # @param [Subject] subjecto
9
+ #
10
+ # @return [Enumerable<Test>]
11
+ #
12
+ # @api private
13
+ #
14
+ abstract_method :call
15
+
16
+ end # Selector
17
+ end # Mutant
@@ -0,0 +1,28 @@
1
+ module Mutant
2
+ class Selector
3
+ # Expression based test selector
4
+ class Expression < self
5
+ include Concord.new(:integration)
6
+
7
+ # Return tests for subject
8
+ #
9
+ # @param [Subject] subject
10
+ #
11
+ # @return [Enumerable<Test>]
12
+ #
13
+ # @api private
14
+ #
15
+ def call(subject)
16
+ subject.match_expressions.each do |match_expression|
17
+ subject_tests = integration.all_tests.select do |test|
18
+ match_expression.prefix?(test.expression)
19
+ end
20
+ return subject_tests if subject_tests.any?
21
+ end
22
+
23
+ EMPTY_ARRAY
24
+ end
25
+
26
+ end # Expression
27
+ end # Selector
28
+ end # Mutant
@@ -2,7 +2,7 @@ module Mutant
2
2
  # Subject of a mutation
3
3
  class Subject
4
4
  include AbstractType, Adamantium::Flat, Enumerable
5
- include Concord::Public.new(:config, :context, :node)
5
+ include Concord::Public.new(:context, :node)
6
6
 
7
7
  # Return mutations
8
8
  #
@@ -30,24 +30,6 @@ module Mutant
30
30
  context.source_path
31
31
  end
32
32
 
33
- # Return tests for mutation
34
- #
35
- # @return [Array<Test>]
36
- #
37
- # @api private
38
- #
39
- def tests
40
- match_expressions.each do |match_expression|
41
- tests = config.integration.all_tests.select do |test|
42
- match_expression.prefix?(test.expression)
43
- end
44
- return tests if tests.any?
45
- end
46
-
47
- EMPTY_ARRAY
48
- end
49
- memoize :tests
50
-
51
33
  # Prepare the subject for the insertion of mutation
52
34
  #
53
35
  # @return [self]