mutant 0.6.3 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/Changelog.md +6 -0
- data/Gemfile.devtools +1 -1
- data/bin/mutant +2 -1
- data/config/flay.yml +1 -1
- data/config/reek.yml +3 -0
- data/lib/mutant.rb +5 -3
- data/lib/mutant/cli.rb +2 -2
- data/lib/mutant/config.rb +1 -1
- data/lib/mutant/diff.rb +1 -1
- data/lib/mutant/env.rb +1 -1
- data/lib/mutant/expression/methods.rb +16 -0
- data/lib/mutant/isolation.rb +16 -2
- data/lib/mutant/mutator/node/const.rb +1 -1
- data/lib/mutant/mutator/node/generic.rb +1 -1
- data/lib/mutant/mutator/registry.rb +1 -1
- data/lib/mutant/reporter/cli.rb +1 -1
- data/lib/mutant/reporter/cli/format.rb +0 -12
- data/lib/mutant/reporter/cli/printer.rb +1 -1
- data/lib/mutant/result.rb +1 -1
- data/lib/mutant/runner.rb +50 -29
- data/lib/mutant/runner/collector.rb +10 -11
- data/lib/mutant/subject.rb +1 -4
- data/lib/mutant/subject/method.rb +11 -0
- data/lib/mutant/version.rb +1 -1
- data/meta/send.rb +13 -0
- data/mutant.gemspec +2 -2
- data/spec/spec_helper.rb +2 -9
- data/spec/support/corpus.rb +5 -5
- data/spec/support/rb_bug.rb +18 -0
- data/spec/unit/mutant/cli_spec.rb +2 -2
- data/spec/unit/mutant/expression/methods_spec.rb +7 -1
- data/spec/unit/mutant/isolation_spec.rb +22 -2
- data/spec/unit/mutant/matcher/method/instance_spec.rb +5 -5
- data/spec/unit/mutant/matcher/method/singleton_spec.rb +5 -5
- data/spec/unit/mutant/reporter/cli_spec.rb +13 -14
- data/spec/unit/mutant/runner/collector_spec.rb +198 -0
- data/spec/unit/mutant/runner_spec.rb +2 -3
- data/spec/unit/mutant/subject/method/instance_spec.rb +8 -0
- data/spec/unit/mutant/subject/method/singleton_spec.rb +8 -0
- data/spec/unit/mutant/warning_filter_spec.rb +1 -1
- data/test_app/Gemfile.devtools +1 -1
- data/test_app/lib/test_app.rb +4 -0
- metadata +22 -19
- data/lib/parser_extensions.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee4fb487c413c8106f0b826458a2914efa842775
|
4
|
+
data.tar.gz: 005c891d8fbcd19a1e4ec806ce39a2d05defcdd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 537342f44d70f3c23696de4962157ec74b7a0383f587c6db897bf216b7e2fe7f22572e84a54e3c055434a5bb5cbd061109655f25d1e3d2418643f9ec716cb147
|
7
|
+
data.tar.gz: 77045f8132668b54c86989ff3c0fa8e75615ebd555bbba305d7da26bded1c7414d6503d0ef16a5ae52547a3fa0a92f142f01f43678762d46dccf57d1bb9e1318
|
data/.travis.yml
CHANGED
@@ -6,11 +6,11 @@ rvm:
|
|
6
6
|
- 1.9.3
|
7
7
|
- 2.0.0
|
8
8
|
- 2.1.2
|
9
|
+
- 2.1.3
|
9
10
|
- rbx-2
|
10
11
|
matrix:
|
11
12
|
allow_failures:
|
12
|
-
- rvm: 2
|
13
|
-
- rvm: rbx-2 # Travis does not take care about RBX
|
13
|
+
- rvm: rbx-2 # travis does not maintain rbx setup correctly
|
14
14
|
notifications:
|
15
15
|
irc:
|
16
16
|
channels:
|
data/Changelog.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# v0.6.4 2014-10-xx
|
2
|
+
|
3
|
+
* Do not buffer report prints, speedup large report generation.
|
4
|
+
* Fix some cases where --fail-fast semantics stopped far to late.
|
5
|
+
* Fix crashing / stuckage from using parallel in a nested way.
|
6
|
+
|
1
7
|
# v0.6.3 2014-09-22
|
2
8
|
|
3
9
|
* Add support for rspec-3.1.
|
data/Gemfile.devtools
CHANGED
data/bin/mutant
CHANGED
data/config/flay.yml
CHANGED
data/config/reek.yml
CHANGED
@@ -64,6 +64,7 @@ NestedIterators:
|
|
64
64
|
- Mutant#self.singleton_subclass_instance
|
65
65
|
- Mutant::CLI#parse
|
66
66
|
- Mutant::Integration::Rspec#run
|
67
|
+
- Mutant::Isolation::Fork#self.call
|
67
68
|
- Mutant::Mutator::Util::Array::Element#dispatch
|
68
69
|
- Mutant::Mutator::Node::Resbody#mutate_captures
|
69
70
|
- Mutant::Mutator::Node::Arguments#emit_argument_mutations
|
@@ -95,6 +96,7 @@ TooManyMethods:
|
|
95
96
|
- Mutant::Subject
|
96
97
|
- Mutant::Mutator::Node
|
97
98
|
- Mutant::Reporter::CLI
|
99
|
+
- Mutant::Runner
|
98
100
|
- Mutant::Meta::Example::Verification
|
99
101
|
max_methods: 10
|
100
102
|
TooManyStatements:
|
@@ -102,6 +104,7 @@ TooManyStatements:
|
|
102
104
|
exclude:
|
103
105
|
- Mutant#self.singleton_subclass_instance
|
104
106
|
- Mutant::Integration::Rspec#run
|
107
|
+
- Mutant::Isolation::Fork#self.call
|
105
108
|
- Mutant::Reporter::CLI#colorized_diff
|
106
109
|
- Mutant::Reporter::CLI::Printer::EnvProgress#run
|
107
110
|
- Mutant::Reporter::CLI::Printer::Config#run
|
data/lib/mutant.rb
CHANGED
@@ -5,10 +5,8 @@ require 'ice_nine'
|
|
5
5
|
require 'abstract_type'
|
6
6
|
require 'equalizer'
|
7
7
|
require 'digest/sha1'
|
8
|
-
require 'inflecto'
|
9
8
|
require 'parser'
|
10
9
|
require 'parser/current'
|
11
|
-
require 'parser_extensions'
|
12
10
|
require 'unparser'
|
13
11
|
require 'ice_nine'
|
14
12
|
require 'diff/lcs'
|
@@ -19,6 +17,10 @@ require 'morpher'
|
|
19
17
|
require 'parallel'
|
20
18
|
require 'open3'
|
21
19
|
|
20
|
+
# This setting is done to make errors within the parallel
|
21
|
+
# reporter / execution visible in the main thread.
|
22
|
+
Thread.abort_on_exception = true
|
23
|
+
|
22
24
|
# Library namespace
|
23
25
|
module Mutant
|
24
26
|
# The frozen empty string used within mutant
|
@@ -221,7 +223,7 @@ module Mutant
|
|
221
223
|
isolation: Mutant::Isolation::Fork,
|
222
224
|
reporter: Reporter::CLI.build($stdout),
|
223
225
|
zombie: false,
|
224
|
-
|
226
|
+
jobs: Mutant.ci? ? CI_DEFAULT_PROCESSOR_COUNT : Parallel.processor_count,
|
225
227
|
expected_coverage: 100.0
|
226
228
|
)
|
227
229
|
end # Config
|
data/lib/mutant/cli.rb
CHANGED
@@ -112,8 +112,8 @@ module Mutant
|
|
112
112
|
opts.on('-r', '--require NAME', 'Require file with NAME') do |name|
|
113
113
|
add(:requires, name)
|
114
114
|
end
|
115
|
-
opts.on('-j', '--jobs NUMBER', 'Number of kill
|
116
|
-
update(
|
115
|
+
opts.on('-j', '--jobs NUMBER', 'Number of kill jobs. Defaults to number of processors.') do |number|
|
116
|
+
update(jobs: Integer(number))
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
data/lib/mutant/config.rb
CHANGED
data/lib/mutant/diff.rb
CHANGED
data/lib/mutant/env.rb
CHANGED
@@ -115,7 +115,7 @@ module Mutant
|
|
115
115
|
def expression(scope)
|
116
116
|
name = scope_name(scope) or return
|
117
117
|
|
118
|
-
unless name.
|
118
|
+
unless name.is_a?(String)
|
119
119
|
warn("#{scope.class}#name from: #{scope.inspect} did not return a String or nil. Fix your lib to support normal ruby semantics!")
|
120
120
|
return
|
121
121
|
end
|
@@ -25,6 +25,22 @@ module Mutant
|
|
25
25
|
MATCHERS.fetch(scope_symbol).new(env, scope)
|
26
26
|
end
|
27
27
|
|
28
|
+
# Return length of match
|
29
|
+
#
|
30
|
+
# @param [Expression] expression
|
31
|
+
#
|
32
|
+
# @return [Fixnum]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
#
|
36
|
+
def match_length(expression)
|
37
|
+
if expression.syntax.start_with?(syntax)
|
38
|
+
syntax.length
|
39
|
+
else
|
40
|
+
0
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
28
44
|
private
|
29
45
|
|
30
46
|
# Return scope name
|
data/lib/mutant/isolation.rb
CHANGED
@@ -38,8 +38,22 @@ module Mutant
|
|
38
38
|
# @api private
|
39
39
|
#
|
40
40
|
def self.call(&block)
|
41
|
-
|
42
|
-
|
41
|
+
reader, writer = IO.pipe
|
42
|
+
|
43
|
+
pid = fork do
|
44
|
+
File.open('/dev/null', 'w') do |file|
|
45
|
+
$stderr.reopen(file)
|
46
|
+
reader.close
|
47
|
+
writer.write(Marshal.dump(block.call))
|
48
|
+
writer.close
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
writer.close
|
53
|
+
result = Marshal.load(reader.read)
|
54
|
+
Process.waitpid(pid)
|
55
|
+
result
|
56
|
+
rescue => exception
|
43
57
|
fail Error, exception
|
44
58
|
end
|
45
59
|
|
@@ -19,7 +19,7 @@ module Mutant
|
|
19
19
|
emit_singletons unless parent_node && n_const?(parent_node)
|
20
20
|
emit_type(nil, *children.drop(1))
|
21
21
|
children.each_with_index do |child, index|
|
22
|
-
mutate_child(index) if child.
|
22
|
+
mutate_child(index) if child.is_a?(Parser::AST::Node)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -64,7 +64,7 @@ module Mutant
|
|
64
64
|
# @api private
|
65
65
|
#
|
66
66
|
def self.assert_valid_type(type)
|
67
|
-
unless AST::Types::ALL.include?(type) || type.
|
67
|
+
unless AST::Types::ALL.include?(type) || type.is_a?(Class)
|
68
68
|
raise InvalidTypeError, "invalid type registration: #{type}"
|
69
69
|
end
|
70
70
|
end
|
data/lib/mutant/reporter/cli.rb
CHANGED
@@ -25,18 +25,6 @@ module Mutant
|
|
25
25
|
#
|
26
26
|
abstract_method :progress
|
27
27
|
|
28
|
-
# Format result
|
29
|
-
#
|
30
|
-
# @param [Result::Env] env
|
31
|
-
#
|
32
|
-
# @return [String]
|
33
|
-
#
|
34
|
-
# @api private
|
35
|
-
#
|
36
|
-
def report(env)
|
37
|
-
format(Printer::EnvResult, env)
|
38
|
-
end
|
39
|
-
|
40
28
|
# Output abstraction to decouple tty? from buffer
|
41
29
|
class Output
|
42
30
|
include Concord.new(:tty, :buffer)
|
@@ -177,7 +177,7 @@ module Mutant
|
|
177
177
|
info 'Matcher: %s', object.matcher_config.inspect
|
178
178
|
info 'Integration: %s', object.integration.name
|
179
179
|
info 'Expect Coverage: %0.2f%%', object.expected_coverage.inspect
|
180
|
-
info '
|
180
|
+
info 'Jobs: %d', object.jobs
|
181
181
|
info 'Includes: %s', object.includes.inspect
|
182
182
|
info 'Requires: %s', object.requires.inspect
|
183
183
|
self
|
data/lib/mutant/result.rb
CHANGED
data/lib/mutant/runner.rb
CHANGED
@@ -12,19 +12,21 @@ module Mutant
|
|
12
12
|
def initialize(env)
|
13
13
|
super
|
14
14
|
|
15
|
-
@collector
|
16
|
-
@mutex
|
17
|
-
@mutations
|
15
|
+
@collector = Collector.new(env)
|
16
|
+
@mutex = Mutex.new
|
17
|
+
@mutations = env.mutations.dup
|
18
|
+
@index = 0
|
19
|
+
@continue = true
|
18
20
|
|
19
21
|
config.integration.setup
|
20
22
|
|
21
|
-
|
23
|
+
reporter.start(env)
|
22
24
|
|
23
25
|
run
|
24
26
|
|
25
|
-
@result = @collector.result
|
27
|
+
@result = @collector.result
|
26
28
|
|
27
|
-
|
29
|
+
reporter.report(result)
|
28
30
|
end
|
29
31
|
|
30
32
|
# Return result
|
@@ -45,14 +47,36 @@ module Mutant
|
|
45
47
|
#
|
46
48
|
def run
|
47
49
|
Parallel.map(
|
48
|
-
|
49
|
-
|
50
|
-
finish:
|
51
|
-
start:
|
50
|
+
method(:next),
|
51
|
+
in_threads: config.jobs,
|
52
|
+
finish: method(:finish),
|
53
|
+
start: method(:start),
|
52
54
|
&method(:run_mutation)
|
53
55
|
)
|
54
56
|
end
|
55
57
|
|
58
|
+
# Return next mutation or stop
|
59
|
+
#
|
60
|
+
# @return [Mutation]
|
61
|
+
# in case there is a next mutation
|
62
|
+
#
|
63
|
+
# @return [Parallel::Stop]
|
64
|
+
# in case there is no next mutation or runner should stop early
|
65
|
+
#
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
def next
|
69
|
+
@mutex.synchronize do
|
70
|
+
mutation = @mutations.at(@index)
|
71
|
+
if @continue && mutation
|
72
|
+
@index += 1
|
73
|
+
mutation
|
74
|
+
else
|
75
|
+
Parallel::Stop
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
56
80
|
# Handle started mutation
|
57
81
|
#
|
58
82
|
# @param [Mutation] mutation
|
@@ -79,7 +103,7 @@ module Mutant
|
|
79
103
|
# @api private
|
80
104
|
#
|
81
105
|
def finish(mutation, index, result)
|
82
|
-
return unless result.
|
106
|
+
return unless result.is_a?(Mutant::Result::Mutation)
|
83
107
|
|
84
108
|
test_results = result.test_results.zip(mutation.subject.tests).map do |test_result, test|
|
85
109
|
test_result.update(test: test, mutation: mutation) if test_result
|
@@ -100,22 +124,9 @@ module Mutant
|
|
100
124
|
#
|
101
125
|
def process_result(result)
|
102
126
|
@collector.finish(result)
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
# Handle exit if needed
|
108
|
-
#
|
109
|
-
# @param [Result::Mutation] result
|
110
|
-
#
|
111
|
-
# @return [undefined]
|
112
|
-
#
|
113
|
-
# @api private
|
114
|
-
#
|
115
|
-
def handle_exit(result)
|
116
|
-
return if !config.fail_fast || result.success?
|
117
|
-
|
118
|
-
@mutations.clear
|
127
|
+
reporter.progress(@collector)
|
128
|
+
return unless config.fail_fast && !result.success?
|
129
|
+
@continue = false
|
119
130
|
end
|
120
131
|
|
121
132
|
# Run mutation
|
@@ -175,13 +186,23 @@ module Mutant
|
|
175
186
|
end
|
176
187
|
rescue Isolation::Error => exception
|
177
188
|
Result::Test.new(
|
178
|
-
test:
|
179
|
-
mutation:
|
189
|
+
test: test,
|
190
|
+
mutation: mutation,
|
180
191
|
runtime: Time.now - time,
|
181
192
|
output: exception.message,
|
182
193
|
passed: false
|
183
194
|
)
|
184
195
|
end
|
185
196
|
|
197
|
+
# Return reporter
|
198
|
+
#
|
199
|
+
# @return [Reporter]
|
200
|
+
#
|
201
|
+
# @api private
|
202
|
+
#
|
203
|
+
def reporter
|
204
|
+
config.reporter
|
205
|
+
end
|
206
|
+
|
186
207
|
end # Runner
|
187
208
|
end # Mutant
|
@@ -14,7 +14,7 @@ module Mutant
|
|
14
14
|
super
|
15
15
|
@start = Time.now
|
16
16
|
@aggregate = Hash.new { |hash, key| hash[key] = [] }
|
17
|
-
@
|
17
|
+
@active = Set.new
|
18
18
|
@last_mutation_result = nil
|
19
19
|
end
|
20
20
|
|
@@ -50,12 +50,11 @@ module Mutant
|
|
50
50
|
Result::Env.new(
|
51
51
|
env: env,
|
52
52
|
runtime: Time.now - @start,
|
53
|
-
subject_results: subject_results
|
54
|
-
done: false
|
53
|
+
subject_results: subject_results
|
55
54
|
)
|
56
55
|
end
|
57
56
|
|
58
|
-
#
|
57
|
+
# Register mutation start
|
59
58
|
#
|
60
59
|
# @param [Mutation] mutation
|
61
60
|
#
|
@@ -64,7 +63,7 @@ module Mutant
|
|
64
63
|
# @api private
|
65
64
|
#
|
66
65
|
def start(mutation)
|
67
|
-
@
|
66
|
+
@active << mutation
|
68
67
|
self
|
69
68
|
end
|
70
69
|
|
@@ -79,10 +78,10 @@ module Mutant
|
|
79
78
|
def finish(mutation_result)
|
80
79
|
@last_mutation_result = mutation_result
|
81
80
|
|
82
|
-
|
81
|
+
mutation = mutation_result.mutation
|
82
|
+
@active.delete(mutation)
|
83
83
|
|
84
|
-
@
|
85
|
-
@aggregate[subject] << mutation_result
|
84
|
+
@aggregate[mutation.subject] << mutation_result
|
86
85
|
|
87
86
|
self
|
88
87
|
end
|
@@ -106,9 +105,9 @@ module Mutant
|
|
106
105
|
# @api private
|
107
106
|
#
|
108
107
|
def active_subjects
|
109
|
-
@
|
110
|
-
|
111
|
-
end.
|
108
|
+
@active.each_with_object(Set.new) do |mutation, subjects|
|
109
|
+
subjects << mutation.subject
|
110
|
+
end.sort_by(&:identification)
|
112
111
|
end
|
113
112
|
|
114
113
|
# Return current subject result
|