mutant 0.10.7 → 0.10.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: defe26105634d28a81b10e876713ab4e29a53a5d78ac8fe7e2d8a44d32b644c6
4
- data.tar.gz: fad9645931798ed165a249e8e8d4a1c032896ceba6b823a1464ff2f60a7cadbb
3
+ metadata.gz: 4c573735f4e77ea0dd9313412e82bbca47771e8fd379c5d53393e855248b7792
4
+ data.tar.gz: 7c59c273789c9fb3d85dd858a7ed7d6bec85b8440ad05736f70541325d214947
5
5
  SHA512:
6
- metadata.gz: 79da9f75f18510bbf81e43e2b98e43c4b92ddf2e6cb68b66828f9f4240f7d3e8747d6e475130f83a2d1edec8c1dda43a34f48524bd7439372e545a046099c28d
7
- data.tar.gz: 7d8aac2776eca84aff051b7a146863bb271c5df4ec7dc344f041d3a22a17b4d87f9f48d5201e2dceec30f548adc7135d443ee78bc00764a9399fb677f2cf0aa5
6
+ metadata.gz: ff3a349908f6f3f83ace0ce4de9555b981f6a82515954e635a7539b2d8852d9bfa740a2c33afd2b3b49a34e0358920be9d0c22b8c05c2903356d75b9c4a4ccc6
7
+ data.tar.gz: 2b3cc3f947b50bc407c8c5a3239ad5bbddf2ff2adc773e661f8279d650297accaad05cdcdbce5e64ae04e419dd63740e0de493542f44464bb5d7d7ee60ab37c1
@@ -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'
@@ -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.env
22
+ @config = Config::DEFAULT
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.merge(file_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 = Config.env.merge(file_config).merge(@config)
70
33
  end
71
34
 
72
35
  def parse_remaining_arguments(arguments)
@@ -158,7 +121,6 @@ module Mutant
158
121
  end
159
122
  end
160
123
  end # Run
161
- # rubocop:enable Metrics/ClassLength
162
124
  end # Command
163
125
  end # CLI
164
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
@@ -38,11 +38,12 @@ module Mutant
38
38
  private_constant(*constants(false))
39
39
 
40
40
  class CoverageCriteria
41
- include Anima.new(:timeout, :test_result)
41
+ include Anima.new(:process_abort, :test_result, :timeout)
42
42
 
43
43
  DEFAULT = new(
44
- timeout: false,
45
- test_result: true
44
+ process_abort: false,
45
+ test_result: true,
46
+ timeout: false
46
47
  )
47
48
 
48
49
  TRANSFORM =
@@ -50,8 +51,9 @@ module Mutant
50
51
  [
51
52
  Transform::Hash.new(
52
53
  optional: [
53
- Transform::Hash::Key.new('timeout', Transform::BOOLEAN),
54
- Transform::Hash::Key.new('test_result', Transform::BOOLEAN)
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)
55
57
  ],
56
58
  required: []
57
59
  ),
@@ -69,12 +71,12 @@ module Mutant
69
71
  def merge(other)
70
72
  other.with(
71
73
  fail_fast: fail_fast || other.fail_fast,
72
- includes: other.includes + includes,
74
+ includes: includes + other.includes,
73
75
  jobs: other.jobs || jobs,
74
76
  integration: other.integration || integration,
75
77
  mutation_timeout: other.mutation_timeout || mutation_timeout,
76
78
  matcher: matcher.merge(other.matcher),
77
- requires: other.requires + requires,
79
+ requires: requires + other.requires,
78
80
  zombie: zombie || other.zombie
79
81
  )
80
82
  end
@@ -19,6 +19,11 @@ module Mutant
19
19
  The integration is supposed to define %<constant_name>s!
20
20
  MESSAGE
21
21
 
22
+ INTEGRATION_MISSING = <<~'MESSAGE'
23
+ No test framework integration configured.
24
+ See https://github.com/mbj/mutant/blob/master/docs/configuration.md#integration
25
+ MESSAGE
26
+
22
27
  private_constant(*constants(false))
23
28
 
24
29
  # Setup integration
@@ -27,6 +32,10 @@ module Mutant
27
32
  #
28
33
  # @return [Either<String, Integration>]
29
34
  def self.setup(env)
35
+ unless env.config.integration
36
+ return Either::Left.new(INTEGRATION_MISSING)
37
+ end
38
+
30
39
  attempt_require(env).bind { attempt_const_get(env) }.fmap do |klass|
31
40
  klass.new(
32
41
  expression_parser: env.config.expression_parser,
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  module License
5
5
  NAME = 'mutant-license'
6
- VERSION = '~> 0.1.0'
6
+ VERSION = ['>= 0.1', '< 0.3'].freeze
7
7
 
8
8
  # Load license
9
9
  #
@@ -20,7 +20,7 @@ module Mutant
20
20
 
21
21
  def self.load_mutant_license(world)
22
22
  Either
23
- .wrap_error(LoadError) { world.gem_method.call(NAME, VERSION) }
23
+ .wrap_error(LoadError) { world.gem_method.call(NAME, *VERSION) }
24
24
  .lmap(&:message)
25
25
  .lmap(&method(:check_for_rubygems_mutant_license))
26
26
  end
@@ -17,8 +17,7 @@ module Mutant
17
17
  #
18
18
  # @return [undefined]
19
19
  def self.add(*types, &block)
20
- file = caller.first.split(':in', 2).first
21
- ALL << DSL.call(file, Set.new(types), block)
20
+ ALL << DSL.call(caller_locations(1).first, Set.new(types), block)
22
21
  end
23
22
 
24
23
  Pathname.glob(Pathname.new(__dir__).parent.parent.join('meta', '*.rb'))
@@ -3,10 +3,11 @@
3
3
  module Mutant
4
4
  module Meta
5
5
  class Example
6
- include Adamantium
6
+ include Adamantium::Flat
7
+
7
8
  include Anima.new(
8
9
  :expected,
9
- :file,
10
+ :location,
10
11
  :lvars,
11
12
  :node,
12
13
  :original_source,
@@ -25,6 +26,23 @@ module Mutant
25
26
  end
26
27
  memoize :verification
27
28
 
29
+ # Example identification
30
+ #
31
+ # @return [String]
32
+ def identification
33
+ location.to_s
34
+ end
35
+
36
+ # Context of mutation
37
+ #
38
+ # @return [Context]
39
+ def context
40
+ Context.new(
41
+ Object,
42
+ location.path
43
+ )
44
+ end
45
+
28
46
  # Original source as generated by unparser
29
47
  #
30
48
  # @return [String]
@@ -9,12 +9,12 @@ module Mutant
9
9
 
10
10
  # Run DSL on block
11
11
  #
12
- # @param [Pathname] file
12
+ # @param [Thread::Backtrace::Location] location
13
13
  # @param [Set<Symbol>] types
14
14
  #
15
15
  # @return [Example]
16
- def self.call(file, types, block)
17
- instance = new(file, types)
16
+ def self.call(location, types, block)
17
+ instance = new(location, types)
18
18
  instance.instance_eval(&block)
19
19
  instance.example
20
20
  end
@@ -24,12 +24,12 @@ module Mutant
24
24
  # Initialize object
25
25
  #
26
26
  # @return [undefined]
27
- def initialize(file, types)
28
- @expected = []
29
- @file = file
30
- @lvars = []
31
- @source = nil
32
- @types = types
27
+ def initialize(location, types)
28
+ @expected = []
29
+ @location = location
30
+ @lvars = []
31
+ @source = nil
32
+ @types = types
33
33
  end
34
34
 
35
35
  # Example captured by DSL
@@ -40,9 +40,10 @@ module Mutant
40
40
  # in case example cannot be build
41
41
  def example
42
42
  fail 'source not defined' unless @source
43
+
43
44
  Example.new(
44
45
  expected: @expected,
45
- file: @file,
46
+ location: @location,
46
47
  lvars: @lvars,
47
48
  node: @node,
48
49
  original_source: @source,
@@ -28,7 +28,7 @@ module Mutant
28
28
  private
29
29
 
30
30
  def reports
31
- reports = [example.file]
31
+ reports = [example.location]
32
32
  reports.concat(original)
33
33
  reports.concat(original_verification)
34
34
  reports.concat(make_report('Missing mutations:', missing))
@@ -94,7 +94,7 @@ module Mutant
94
94
 
95
95
  def missing
96
96
  (example.expected.map(&:node) - mutations.map(&:node)).map do |node|
97
- Mutation::Evil.new(nil, node)
97
+ Mutation::Evil.new(example, node)
98
98
  end
99
99
  end
100
100
  memoize :missing
@@ -9,37 +9,34 @@ module Mutant
9
9
  CODE_DELIMITER = "\0"
10
10
  CODE_RANGE = (0..4).freeze
11
11
 
12
- # Identification string
13
- #
14
- # @return [String]
15
- def identification
16
- "#{self.class::SYMBOL}:#{subject.identification}:#{code}"
12
+ def initialize(subject, node)
13
+ super(subject, node)
14
+
15
+ @source = Unparser.unparse(node)
16
+ @code = sha1[CODE_RANGE]
17
+ @identification = "#{self.class::SYMBOL}:#{subject.identification}:#{code}"
18
+ @monkeypatch = Unparser.unparse(subject.context.root(node))
17
19
  end
18
- memoize :identification
19
20
 
20
- # Mutation code
21
+ # Mutation identification code
21
22
  #
22
23
  # @return [String]
23
- def code
24
- sha1[CODE_RANGE]
25
- end
26
- memoize :code
24
+ attr_reader :code
27
25
 
28
26
  # Normalized mutation source
29
27
  #
30
28
  # @return [String]
31
- def source
32
- Unparser.unparse(node)
33
- end
34
- memoize :source
29
+ attr_reader :source
30
+
31
+ # Identification string
32
+ #
33
+ # @return [String]
34
+ attr_reader :identification
35
35
 
36
36
  # The monkeypatch to insert the mutation
37
37
  #
38
38
  # @return [String]
39
- def monkeypatch
40
- Unparser.unparse(subject.context.root(node))
41
- end
42
- memoize :monkeypatch
39
+ attr_reader :monkeypatch
43
40
 
44
41
  # Normalized original source
45
42
  #
@@ -77,7 +74,6 @@ module Mutant
77
74
  def sha1
78
75
  Digest::SHA1.hexdigest(subject.identification + CODE_DELIMITER + source)
79
76
  end
80
- memoize :sha1
81
77
 
82
78
  # Evil mutation that should case mutations to fail tests
83
79
  class Evil < self
@@ -233,7 +233,7 @@ module Mutant
233
233
  #
234
234
  # @return [Boolean]
235
235
  def success?
236
- test_result || timeout
236
+ process_abort || test_result || timeout
237
237
  end
238
238
  end
239
239
 
@@ -250,8 +250,9 @@ module Mutant
250
250
  # @praam [Result::CoverageCriteria]
251
251
  def criteria_result(coverage_criteria)
252
252
  CoverageCriteria.new(
253
- test_result: coverage_criteria.test_result && test_result_success?,
254
- timeout: coverage_criteria.timeout && timeout?
253
+ process_abort: coverage_criteria.process_abort && process_abort?,
254
+ test_result: coverage_criteria.test_result && test_result_success?,
255
+ timeout: coverage_criteria.timeout && timeout?
255
256
  )
256
257
  end
257
258
 
@@ -269,6 +270,15 @@ module Mutant
269
270
  !isolation_result.timeout.nil?
270
271
  end
271
272
 
273
+ # Test for unexpected process abort
274
+ #
275
+ # @return [Boolean]
276
+ def process_abort?
277
+ process_status = isolation_result.process_status or return false
278
+
279
+ !timeout? && !process_status.exited?
280
+ end
281
+
272
282
  private
273
283
 
274
284
  # Test if mutation was handled successfully
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # Current mutant version
5
- VERSION = '0.10.7'
5
+ VERSION = '0.10.12'
6
6
  end # Mutant
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mutant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.7
4
+ version: 0.10.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Schirp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-22 00:00:00.000000000 Z
11
+ date: 2020-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: abstract_type
@@ -304,8 +304,10 @@ files:
304
304
  - lib/mutant/bootstrap.rb
305
305
  - lib/mutant/cli.rb
306
306
  - lib/mutant/cli/command.rb
307
+ - lib/mutant/cli/command/environment.rb
308
+ - lib/mutant/cli/command/environment/run.rb
309
+ - lib/mutant/cli/command/environment/show.rb
307
310
  - lib/mutant/cli/command/root.rb
308
- - lib/mutant/cli/command/run.rb
309
311
  - lib/mutant/cli/command/subscription.rb
310
312
  - lib/mutant/config.rb
311
313
  - lib/mutant/context.rb