mutant 0.10.6 → 0.10.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mutant.rb +7 -4
  3. data/lib/mutant/cli/command.rb +1 -1
  4. data/lib/mutant/cli/command/{run.rb → environment.rb} +10 -45
  5. data/lib/mutant/cli/command/environment/run.rb +56 -0
  6. data/lib/mutant/cli/command/environment/show.rb +29 -0
  7. data/lib/mutant/cli/command/root.rb +7 -3
  8. data/lib/mutant/config.rb +61 -34
  9. data/lib/mutant/env.rb +14 -4
  10. data/lib/mutant/integration.rb +16 -10
  11. data/lib/mutant/integration/null.rb +0 -1
  12. data/lib/mutant/isolation.rb +11 -48
  13. data/lib/mutant/isolation/fork.rb +107 -40
  14. data/lib/mutant/isolation/none.rb +18 -5
  15. data/lib/mutant/license/subscription/commercial.rb +2 -3
  16. data/lib/mutant/license/subscription/opensource.rb +0 -1
  17. data/lib/mutant/matcher/method/instance.rb +0 -2
  18. data/lib/mutant/mutator/node/send.rb +1 -1
  19. data/lib/mutant/parallel.rb +0 -1
  20. data/lib/mutant/parallel/worker.rb +0 -2
  21. data/lib/mutant/reporter/cli.rb +0 -2
  22. data/lib/mutant/reporter/cli/printer/config.rb +9 -5
  23. data/lib/mutant/reporter/cli/printer/coverage_result.rb +19 -0
  24. data/lib/mutant/reporter/cli/printer/env_progress.rb +2 -0
  25. data/lib/mutant/reporter/cli/printer/isolation_result.rb +19 -35
  26. data/lib/mutant/reporter/cli/printer/mutation_result.rb +4 -9
  27. data/lib/mutant/reporter/cli/printer/subject_result.rb +2 -2
  28. data/lib/mutant/result.rb +91 -30
  29. data/lib/mutant/runner/sink.rb +12 -5
  30. data/lib/mutant/timer.rb +60 -11
  31. data/lib/mutant/transform.rb +25 -21
  32. data/lib/mutant/version.rb +1 -1
  33. data/lib/mutant/warnings.rb +0 -1
  34. data/lib/mutant/world.rb +15 -0
  35. metadata +6 -6
  36. data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +0 -28
  37. data/lib/mutant/reporter/cli/printer/subject_progress.rb +0 -58
  38. data/lib/mutant/reporter/cli/printer/test_result.rb +0 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b5a7d1620b6792fa1cef90ead01124cf50287a165a0bdd415fbc9aab9ecfde8
4
- data.tar.gz: 758609938988d4d8de30a6f95a47c603fc4d5365ba98ad1a113a6de11f8e074f
3
+ metadata.gz: f812d15b5c09f8681cc6c75eb01bd00fffe412bc53397b9e13c29c7cad69db9a
4
+ data.tar.gz: 45c24afcbfbef29a50b90b81946e75391c58ecea7ac32b9545a5e7fdc22fd063
5
5
  SHA512:
6
- metadata.gz: 3ecd6aea62e2d01cb68cc9c9c651190305459b7f1b796f4360ce872ce0dbb1871cb40821744d1c09a029a80f39a93cbda9a242591ceae405e47ed971d3d861f9
7
- data.tar.gz: '09e132e435163308309c0185abdd39ea9b6be0d2bd0efa0956049c366f0b521df866a3b7fd04d2e13550bd4f2b09993c659530f86b00142adff69ebc14af3a16'
6
+ metadata.gz: fcb16a15ec71fe5dc51d07e08b426e8dc68e2f4bad2bd47f95a19b136cd06dbefdc9be44f4a7e0723b8a9450b4b924ab2744f8be05b09fbf1d6e9774431f2e97
7
+ data.tar.gz: b9213ce07ce70908f072b23d92bd86c86492ed8f348ae2c7419ed625989a36b7b2d1e110048e14364e044790e7e086894c124f081043d989844de02adcc81684
@@ -167,8 +167,10 @@ require 'mutant/world'
167
167
  require 'mutant/config'
168
168
  require 'mutant/cli'
169
169
  require 'mutant/cli/command'
170
- require 'mutant/cli/command/run'
171
170
  require 'mutant/cli/command/subscription'
171
+ require 'mutant/cli/command/environment'
172
+ require 'mutant/cli/command/environment/run'
173
+ require 'mutant/cli/command/environment/show'
172
174
  require 'mutant/cli/command/root'
173
175
  require 'mutant/runner'
174
176
  require 'mutant/runner/sink'
@@ -179,16 +181,14 @@ require 'mutant/reporter/sequence'
179
181
  require 'mutant/reporter/cli'
180
182
  require 'mutant/reporter/cli/printer'
181
183
  require 'mutant/reporter/cli/printer/config'
184
+ require 'mutant/reporter/cli/printer/coverage_result'
182
185
  require 'mutant/reporter/cli/printer/env'
183
186
  require 'mutant/reporter/cli/printer/env_progress'
184
187
  require 'mutant/reporter/cli/printer/env_result'
185
188
  require 'mutant/reporter/cli/printer/isolation_result'
186
- require 'mutant/reporter/cli/printer/mutation_progress_result'
187
189
  require 'mutant/reporter/cli/printer/mutation_result'
188
190
  require 'mutant/reporter/cli/printer/status_progressive'
189
- require 'mutant/reporter/cli/printer/subject_progress'
190
191
  require 'mutant/reporter/cli/printer/subject_result'
191
- require 'mutant/reporter/cli/printer/test_result'
192
192
  require 'mutant/reporter/cli/format'
193
193
  require 'mutant/repository'
194
194
  require 'mutant/repository/diff'
@@ -219,12 +219,14 @@ module Mutant
219
219
  stderr: $stderr,
220
220
  stdout: $stdout,
221
221
  thread: Thread,
222
+ timer: Timer.new(Process),
222
223
  warnings: Warnings.new(Warning)
223
224
  )
224
225
 
225
226
  # Reopen class to initialize constant to avoid dep circle
226
227
  class Config
227
228
  DEFAULT = new(
229
+ coverage_criteria: Config::CoverageCriteria::DEFAULT,
228
230
  expression_parser: Expression::Parser.new([
229
231
  Expression::Method,
230
232
  Expression::Methods,
@@ -237,6 +239,7 @@ module Mutant
237
239
  isolation: Mutant::Isolation::Fork.new(WORLD),
238
240
  jobs: nil,
239
241
  matcher: Matcher::Config::DEFAULT,
242
+ mutation_timeout: nil,
240
243
  reporter: Reporter::CLI.build(WORLD.stdout),
241
244
  requires: EMPTY_ARRAY,
242
245
  zombie: false
@@ -66,7 +66,7 @@ module Mutant
66
66
  #
67
67
  # @return [Bool]
68
68
  def zombie?
69
- instance_of?(Run)
69
+ false
70
70
  end
71
71
 
72
72
  private
@@ -3,10 +3,9 @@
3
3
  module Mutant
4
4
  module CLI
5
5
  class Command
6
- # rubocop:disable Metrics/ClassLength
7
- class Run < self
8
- NAME = 'run'
9
- SHORT_DESCRIPTION = 'Run code analysis'
6
+ class Environment < self
7
+ NAME = 'environment'
8
+ SHORT_DESCRIPTION = 'Environment subcommands'
10
9
 
11
10
  OPTIONS =
12
11
  %i[
@@ -16,57 +15,21 @@ module Mutant
16
15
  add_matcher_options
17
16
  ].freeze
18
17
 
19
- SLEEP = 40
20
-
21
- UNLICENSED = <<~MESSAGE.lines.freeze
22
- Soft fail, continuing in #{SLEEP} seconds
23
- Next major version will enforce the license
24
- See https://github.com/mbj/mutant#licensing
25
- MESSAGE
26
-
27
- # Test if command needs to be executed in zombie environment
28
- #
29
- # @return [Bool]
30
- def zombie?
31
- @config.zombie
32
- end
33
-
34
18
  private
35
19
 
36
20
  def initialize(attributes)
37
21
  super(attributes)
38
- @config = Config::DEFAULT
22
+ @config = Config.env
39
23
  end
40
24
 
41
- def execute
42
- soft_fail(License.apply(world))
43
- .bind { Config.load_config_file(world) }
25
+ def bootstrap
26
+ Config.load_config_file(world)
44
27
  .fmap(&method(:expand))
45
28
  .bind { Bootstrap.apply(world, @config) }
46
- .bind(&Runner.public_method(:apply))
47
- .from_right { |error| world.stderr.puts(error); return false }
48
- .success?
49
29
  end
50
30
 
51
31
  def expand(file_config)
52
- @config = Config.env.merge(file_config).merge(@config)
53
- end
54
-
55
- def soft_fail(result)
56
- result.either(
57
- lambda do |message|
58
- stderr = world.stderr
59
- stderr.puts(message)
60
- UNLICENSED.each { |line| stderr.puts(unlicensed(line)) }
61
- world.kernel.sleep(SLEEP)
62
- Either::Right.new(nil)
63
- end,
64
- ->(_subscription) { Either::Right.new(nil) }
65
- )
66
- end
67
-
68
- def unlicensed(message)
69
- "[Mutant-License-Error]: #{message}"
32
+ @config = file_config.merge(@config)
70
33
  end
71
34
 
72
35
  def parse_remaining_arguments(arguments)
@@ -153,9 +116,11 @@ module Mutant
153
116
  parser.on('-j', '--jobs NUMBER', 'Number of kill jobs. Defaults to number of processors.') do |number|
154
117
  set(jobs: Integer(number))
155
118
  end
119
+ parser.on('-t', '--mutation-timeout NUMBER', 'Per mutation analysis timeout') do |number|
120
+ set(mutation_timeout: Float(number))
121
+ end
156
122
  end
157
123
  end # Run
158
- # rubocop:enable Metrics/ClassLength
159
124
  end # Command
160
125
  end # CLI
161
126
  end # Mutant
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ module CLI
5
+ class Command
6
+ class Environment
7
+ class Run < self
8
+ NAME = 'run'
9
+ SHORT_DESCRIPTION = 'Run code analysis'
10
+ SLEEP = 40
11
+ SUBCOMMANDS = EMPTY_ARRAY
12
+
13
+ UNLICENSED = <<~MESSAGE.lines.freeze
14
+ Soft fail, continuing in #{SLEEP} seconds
15
+ Next major version will enforce the license
16
+ See https://github.com/mbj/mutant#licensing
17
+ MESSAGE
18
+
19
+ # Test if command needs to be executed in zombie environment
20
+ #
21
+ # @return [Bool]
22
+ def zombie?
23
+ @config.zombie
24
+ end
25
+
26
+ private
27
+
28
+ def execute
29
+ soft_fail(License.apply(world))
30
+ .bind { bootstrap }
31
+ .bind(&Runner.public_method(:apply))
32
+ .from_right { |error| world.stderr.puts(error); return false }
33
+ .success?
34
+ end
35
+
36
+ def soft_fail(result)
37
+ result.either(
38
+ lambda do |message|
39
+ stderr = world.stderr
40
+ stderr.puts(message)
41
+ UNLICENSED.each { |line| stderr.puts(unlicensed(line)) }
42
+ world.kernel.sleep(SLEEP)
43
+ Either::Right.new(nil)
44
+ end,
45
+ ->(_subscription) { Either::Right.new(nil) }
46
+ )
47
+ end
48
+
49
+ def unlicensed(message)
50
+ "[Mutant-License-Error]: #{message}"
51
+ end
52
+ end # Run
53
+ end # Environment
54
+ end # Command
55
+ end # CLI
56
+ end # Mutant
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ module CLI
5
+ class Command
6
+ class Environment
7
+ class Show < self
8
+ NAME = 'show'
9
+ SHORT_DESCRIPTION = 'Display environment without coverage analysis'
10
+ SUBCOMMANDS = EMPTY_ARRAY
11
+
12
+ private
13
+
14
+ def execute
15
+ Config.load_config_file(world)
16
+ .fmap(&method(:expand))
17
+ .bind { Bootstrap.apply(world, @config) }
18
+ .fmap(&method(:report_env))
19
+ .right?
20
+ end
21
+
22
+ def report_env(env)
23
+ env.config.reporter.start(env)
24
+ end
25
+ end # Show
26
+ end # Environment
27
+ end # Command
28
+ end # CLI
29
+ end # Mutant
@@ -3,11 +3,15 @@
3
3
  module Mutant
4
4
  module CLI
5
5
  class Command
6
+ class Environment < self
7
+ SUBCOMMANDS = [Environment::Show].freeze
8
+ end # Environment
9
+
6
10
  class Root < self
7
- SUBCOMMANDS = [Run, Subscription].freeze
8
- SHORT_DESCRIPTION = 'mutation testing engine main command'
9
11
  NAME = 'mutant'
10
- end
12
+ SHORT_DESCRIPTION = 'mutation testing engine main command'
13
+ SUBCOMMANDS = [Environment::Run, Environment, Subscription].freeze
14
+ end # Root
11
15
  end # Command
12
16
  end # CLI
13
17
  end # Mutant
@@ -7,6 +7,7 @@ module Mutant
7
7
  # to current environment is being represented by the Mutant::Env object.
8
8
  class Config
9
9
  include Adamantium::Flat, Anima.new(
10
+ :coverage_criteria,
10
11
  :expression_parser,
11
12
  :fail_fast,
12
13
  :includes,
@@ -14,6 +15,7 @@ module Mutant
14
15
  :isolation,
15
16
  :jobs,
16
17
  :matcher,
18
+ :mutation_timeout,
17
19
  :reporter,
18
20
  :requires,
19
21
  :zombie
@@ -23,30 +25,6 @@ module Mutant
23
25
  define_method(:"#{name}?") { public_send(name) }
24
26
  end
25
27
 
26
- boolean = Transform::Boolean.new
27
- integer = Transform::Primitive.new(Integer)
28
- string = Transform::Primitive.new(String)
29
-
30
- string_array = Transform::Array.new(string)
31
-
32
- TRANSFORM = Transform::Sequence.new(
33
- [
34
- Transform::Exception.new(SystemCallError, :read.to_proc),
35
- Transform::Exception.new(YAML::SyntaxError, YAML.method(:safe_load)),
36
- Transform::Hash.new(
37
- optional: [
38
- Transform::Hash::Key.new('fail_fast', boolean),
39
- Transform::Hash::Key.new('includes', string_array),
40
- Transform::Hash::Key.new('integration', string),
41
- Transform::Hash::Key.new('jobs', integer),
42
- Transform::Hash::Key.new('requires', string_array)
43
- ],
44
- required: []
45
- ),
46
- Transform::Hash::Symbolize.new
47
- ]
48
- )
49
-
50
28
  MORE_THAN_ONE_CONFIG_FILE = <<~'MESSAGE'
51
29
  Found more than one candidate for use as implicit config file: %s
52
30
  MESSAGE
@@ -57,6 +35,34 @@ module Mutant
57
35
  mutant.yml
58
36
  ].freeze
59
37
 
38
+ private_constant(*constants(false))
39
+
40
+ class CoverageCriteria
41
+ include Anima.new(:process_abort, :test_result, :timeout)
42
+
43
+ DEFAULT = new(
44
+ process_abort: false,
45
+ test_result: true,
46
+ timeout: false
47
+ )
48
+
49
+ TRANSFORM =
50
+ Transform::Sequence.new(
51
+ [
52
+ Transform::Hash.new(
53
+ optional: [
54
+ Transform::Hash::Key.new('process_abort', Transform::BOOLEAN),
55
+ Transform::Hash::Key.new('test_result', Transform::BOOLEAN),
56
+ Transform::Hash::Key.new('timeout', Transform::BOOLEAN)
57
+ ],
58
+ required: []
59
+ ),
60
+ Transform::Hash::Symbolize.new,
61
+ ->(value) { Either::Right.new(DEFAULT.with(**value)) }
62
+ ]
63
+ )
64
+ end # CoverageCriteria
65
+
60
66
  # Merge with other config
61
67
  #
62
68
  # @param [Config] other
@@ -64,18 +70,17 @@ module Mutant
64
70
  # @return [Config]
65
71
  def merge(other)
66
72
  other.with(
67
- fail_fast: fail_fast || other.fail_fast,
68
- includes: includes + other.includes,
69
- jobs: other.jobs || jobs,
70
- integration: other.integration || integration,
71
- matcher: matcher.merge(other.matcher),
72
- requires: requires + other.requires,
73
- zombie: zombie || other.zombie
73
+ fail_fast: fail_fast || other.fail_fast,
74
+ includes: includes + other.includes,
75
+ jobs: other.jobs || jobs,
76
+ integration: other.integration || integration,
77
+ mutation_timeout: other.mutation_timeout || mutation_timeout,
78
+ matcher: matcher.merge(other.matcher),
79
+ requires: requires + other.requires,
80
+ zombie: zombie || other.zombie
74
81
  )
75
82
  end
76
83
 
77
- private_constant(*constants(false))
78
-
79
84
  # Load config file
80
85
  #
81
86
  # @param [World] world
@@ -98,7 +103,7 @@ module Mutant
98
103
  def self.load_contents(path)
99
104
  Transform::Named
100
105
  .new(path.to_s, TRANSFORM)
101
- .apply(path)
106
+ .call(path)
102
107
  .lmap(&:compact_message)
103
108
  end
104
109
  private_class_method :load_contents
@@ -109,5 +114,27 @@ module Mutant
109
114
  def self.env
110
115
  DEFAULT.with(jobs: Etc.nprocessors)
111
116
  end
117
+
118
+ TRANSFORM = Transform::Sequence.new(
119
+ [
120
+ Transform::Exception.new(SystemCallError, :read.to_proc),
121
+ Transform::Exception.new(YAML::SyntaxError, YAML.method(:safe_load)),
122
+ Transform::Hash.new(
123
+ optional: [
124
+ Transform::Hash::Key.new('coverage_criteria', CoverageCriteria::TRANSFORM),
125
+ Transform::Hash::Key.new('fail_fast', Transform::BOOLEAN),
126
+ Transform::Hash::Key.new('includes', Transform::STRING_ARRAY),
127
+ Transform::Hash::Key.new('integration', Transform::STRING),
128
+ Transform::Hash::Key.new('jobs', Transform::INTEGER),
129
+ Transform::Hash::Key.new('mutation_timeout', Transform::FLOAT),
130
+ Transform::Hash::Key.new('requires', Transform::STRING_ARRAY)
131
+ ],
132
+ required: []
133
+ ),
134
+ Transform::Hash::Symbolize.new
135
+ ]
136
+ )
137
+
138
+ private_constant(:TRANSFORM)
112
139
  end # Config
113
140
  end # Mutant
@@ -24,10 +24,15 @@ module Mutant
24
24
  # @param [Config] config
25
25
  #
26
26
  # @return [Env]
27
+ #
28
+ # rubocop:disable Metrics/MethodLength
27
29
  def self.empty(world, config)
28
30
  new(
29
31
  config: config,
30
- integration: Integration::Null.new(config),
32
+ integration: Integration::Null.new(
33
+ expression_parser: config.expression_parser,
34
+ timer: world.timer
35
+ ),
31
36
  matchable_scopes: EMPTY_ARRAY,
32
37
  mutations: EMPTY_ARRAY,
33
38
  parser: Parser.new,
@@ -36,6 +41,7 @@ module Mutant
36
41
  world: world
37
42
  )
38
43
  end
44
+ # rubocop:enable Metrics/MethodLength
39
45
 
40
46
  # Kill mutation
41
47
  #
@@ -43,14 +49,14 @@ module Mutant
43
49
  #
44
50
  # @return [Result::Mutation]
45
51
  def kill(mutation)
46
- start = Timer.now
52
+ start = timer.now
47
53
 
48
54
  tests = selections.fetch(mutation.subject)
49
55
 
50
56
  Result::Mutation.new(
51
57
  isolation_result: run_mutation_tests(mutation, tests),
52
58
  mutation: mutation,
53
- runtime: Timer.now - start
59
+ runtime: timer.now - start
54
60
  )
55
61
  end
56
62
 
@@ -127,7 +133,7 @@ module Mutant
127
133
  private
128
134
 
129
135
  def run_mutation_tests(mutation, tests)
130
- config.isolation.call do
136
+ config.isolation.call(config.mutation_timeout) do
131
137
  result = mutation.insert(world.kernel)
132
138
 
133
139
  if result.equal?(Loader::Result::VoidValue.instance)
@@ -138,5 +144,9 @@ module Mutant
138
144
  end
139
145
  end
140
146
 
147
+ def timer
148
+ world.timer
149
+ end
150
+
141
151
  end # Env
142
152
  end # Mutant