mutant 0.10.8 → 0.10.13

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: b8448aecff5544038b0427224ef0e14c8957fcb1fb90a6ab223855a364a7de67
4
- data.tar.gz: b27549faf5c9e9f610500fb48b8af63b68eedfc58ba84e600bbb120069c31b57
3
+ metadata.gz: 561ad5a2483b3678f3bbcd3ab1ddbef3259f589548b59f3f42e993b76e50e120
4
+ data.tar.gz: dd3b79cbeb3bca13e502b9c43cbb59d2d3656e0f37905d5cbb33ac1e1ac98fb1
5
5
  SHA512:
6
- metadata.gz: 2f845db6418eaecf71e45e076cec52bdeaf57bd40ea4c55e5f0b3d5ec51e95cd26cf1e7f299ce756fbd9450b86a18453f69ac16e40d10f1ec374173d0e95f0af
7
- data.tar.gz: f30e34bd368f29e124dedf212ebab64ecf75d807fd9c9a8b4a466e03b2e1bf8eb6b995cf54f6c4103ddabdc6902d62ff61af0a44058a6f6ab66ee7eb8fb08fd1
6
+ metadata.gz: 8d314dfd39fba6e6becf5d3410df2b6c95f94a5861df6446cb804b058edcad4d8b4a6ed98a6a21f4cfed3af48a450a17a3579b9b39e6814a60c4f10d746f77f9
7
+ data.tar.gz: 1ef113b288fca26803593efe136e2c8e49002eb6f33722373eaacf0276078ac6ac4624139c10636498cf2e9c5eec8c64512b08796833380f457e5f1497bcab61
@@ -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'
@@ -224,7 +226,7 @@ module Mutant
224
226
  # Reopen class to initialize constant to avoid dep circle
225
227
  class Config
226
228
  DEFAULT = new(
227
- coverage_criteria: Config::CoverageCriteria::DEFAULT,
229
+ coverage_criteria: Config::CoverageCriteria::EMPTY,
228
230
  expression_parser: Expression::Parser.new([
229
231
  Expression::Method,
230
232
  Expression::Methods,
@@ -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).expand_defaults
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
@@ -40,6 +40,12 @@ module Mutant
40
40
  class CoverageCriteria
41
41
  include Anima.new(:process_abort, :test_result, :timeout)
42
42
 
43
+ EMPTY = new(
44
+ process_abort: nil,
45
+ test_result: nil,
46
+ timeout: nil
47
+ )
48
+
43
49
  DEFAULT = new(
44
50
  process_abort: false,
45
51
  test_result: true,
@@ -61,6 +67,22 @@ module Mutant
61
67
  ->(value) { Either::Right.new(DEFAULT.with(**value)) }
62
68
  ]
63
69
  )
70
+
71
+ def merge(other)
72
+ self.class.new(
73
+ process_abort: overwrite(other, :process_abort),
74
+ test_result: overwrite(other, :test_result),
75
+ timeout: overwrite(other, :timeout)
76
+ )
77
+ end
78
+
79
+ private
80
+
81
+ def overwrite(other, attribute_name)
82
+ other_value = other.public_send(attribute_name)
83
+
84
+ other_value.nil? ? public_send(attribute_name) : other_value
85
+ end
64
86
  end # CoverageCriteria
65
87
 
66
88
  # Merge with other config
@@ -68,18 +90,24 @@ module Mutant
68
90
  # @param [Config] other
69
91
  #
70
92
  # @return [Config]
93
+ #
94
+ # rubocop:disable Metrics/AbcSize
95
+ # rubocop:disable Metrics/MethodLength
71
96
  def merge(other)
72
97
  other.with(
73
- fail_fast: fail_fast || other.fail_fast,
74
- includes: other.includes + 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: other.requires + requires,
80
- zombie: zombie || other.zombie
98
+ coverage_criteria: coverage_criteria.merge(other.coverage_criteria),
99
+ fail_fast: fail_fast || other.fail_fast,
100
+ includes: includes + other.includes,
101
+ jobs: other.jobs || jobs,
102
+ integration: other.integration || integration,
103
+ mutation_timeout: other.mutation_timeout || mutation_timeout,
104
+ matcher: matcher.merge(other.matcher),
105
+ requires: requires + other.requires,
106
+ zombie: zombie || other.zombie
81
107
  )
82
108
  end
109
+ # rubocop:enable Metrics/AbcSize
110
+ # rubocop:enable Metrics/MethodLength
83
111
 
84
112
  # Load config file
85
113
  #
@@ -100,6 +128,16 @@ module Mutant
100
128
  end
101
129
  end
102
130
 
131
+ # Expand config with defaults
132
+ #
133
+ # @return [Config]
134
+ def expand_defaults
135
+ with(
136
+ coverage_criteria: CoverageCriteria::DEFAULT.merge(coverage_criteria),
137
+ jobs: jobs || 1
138
+ )
139
+ end
140
+
103
141
  def self.load_contents(path)
104
142
  Transform::Named
105
143
  .new(path.to_s, TRANSFORM)
@@ -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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # Current mutant version
5
- VERSION = '0.10.8'
5
+ VERSION = '0.10.13'
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.8
4
+ version: 0.10.13
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