mutant 0.5.24 → 0.5.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +8 -0
  3. data/config/flay.yml +1 -1
  4. data/config/flog.yml +1 -1
  5. data/config/reek.yml +15 -13
  6. data/lib/mutant.rb +28 -12
  7. data/lib/mutant/ast/meta.rb +0 -10
  8. data/lib/mutant/ast/named_children.rb +1 -0
  9. data/lib/mutant/ast/types.rb +5 -5
  10. data/lib/mutant/cli.rb +84 -64
  11. data/lib/mutant/config.rb +7 -39
  12. data/lib/mutant/delegator.rb +2 -0
  13. data/lib/mutant/env.rb +119 -16
  14. data/lib/mutant/expression.rb +8 -2
  15. data/lib/mutant/expression/method.rb +6 -16
  16. data/lib/mutant/expression/methods.rb +5 -5
  17. data/lib/mutant/expression/namespace.rb +7 -7
  18. data/lib/mutant/integration.rb +0 -10
  19. data/lib/mutant/isolation.rb +41 -15
  20. data/lib/mutant/matcher/chain.rb +1 -17
  21. data/lib/mutant/matcher/compiler.rb +108 -0
  22. data/lib/mutant/matcher/config.rb +28 -0
  23. data/lib/mutant/matcher/method.rb +1 -1
  24. data/lib/mutant/matcher/namespace.rb +5 -52
  25. data/lib/mutant/matcher/null.rb +1 -1
  26. data/lib/mutant/matcher/scope.rb +1 -1
  27. data/lib/mutant/mutation.rb +29 -13
  28. data/lib/mutant/mutator/node.rb +2 -12
  29. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +1 -1
  30. data/lib/mutant/reporter/cli.rb +0 -2
  31. data/lib/mutant/reporter/cli/printer.rb +14 -0
  32. data/lib/mutant/reporter/cli/progress.rb +1 -3
  33. data/lib/mutant/reporter/cli/progress/config.rb +5 -9
  34. data/lib/mutant/reporter/cli/progress/env.rb +30 -0
  35. data/lib/mutant/reporter/cli/progress/noop.rb +4 -1
  36. data/lib/mutant/reporter/cli/progress/result.rb +12 -0
  37. data/lib/mutant/reporter/cli/progress/result/mutation.rb +45 -0
  38. data/lib/mutant/reporter/cli/progress/result/subject.rb +54 -0
  39. data/lib/mutant/reporter/cli/progress/subject.rb +7 -90
  40. data/lib/mutant/reporter/cli/registry.rb +2 -0
  41. data/lib/mutant/reporter/cli/report/env.rb +92 -0
  42. data/lib/mutant/reporter/cli/report/mutation.rb +58 -77
  43. data/lib/mutant/reporter/cli/report/subject.rb +4 -3
  44. data/lib/mutant/reporter/cli/report/test.rb +28 -0
  45. data/lib/mutant/reporter/null.rb +1 -1
  46. data/lib/mutant/reporter/trace.rb +16 -3
  47. data/lib/mutant/result.rb +302 -0
  48. data/lib/mutant/runner.rb +77 -123
  49. data/lib/mutant/subject.rb +32 -16
  50. data/lib/mutant/subject/method.rb +0 -15
  51. data/lib/mutant/subject/method/instance.rb +3 -3
  52. data/lib/mutant/version.rb +1 -1
  53. data/lib/mutant/warning_expectation.rb +12 -5
  54. data/spec/integration/mutant/corpus_spec.rb +1 -1
  55. data/spec/spec_helper.rb +5 -1
  56. data/spec/unit/mutant/cli_spec.rb +248 -0
  57. data/spec/unit/mutant/expression/namespace/flat_spec.rb +1 -1
  58. data/spec/unit/mutant/expression_spec.rb +55 -0
  59. data/spec/unit/mutant/integration_spec.rb +0 -5
  60. data/spec/unit/mutant/isolation_spec.rb +36 -5
  61. data/spec/unit/mutant/matcher/chain_spec.rb +1 -13
  62. data/spec/unit/mutant/matcher/compiler_spec.rb +95 -0
  63. data/spec/unit/mutant/matcher/filter_spec.rb +31 -0
  64. data/spec/unit/mutant/matcher/method/instance_spec.rb +33 -2
  65. data/spec/unit/mutant/matcher/method/singleton_spec.rb +1 -1
  66. data/spec/unit/mutant/matcher/methods/instance_spec.rb +1 -1
  67. data/spec/unit/mutant/matcher/methods/singleton_spec.rb +1 -1
  68. data/spec/unit/mutant/matcher/namespace_spec.rb +10 -6
  69. data/spec/unit/mutant/matcher/null_spec.rb +26 -0
  70. data/spec/unit/mutant/reporter/cli_spec.rb +337 -0
  71. data/spec/unit/mutant/reporter/null_spec.rb +12 -0
  72. data/spec/unit/mutant/runner_spec.rb +130 -0
  73. data/spec/unit/mutant/subject/context_spec.rb +4 -3
  74. data/spec/unit/mutant/subject/method/instance_spec.rb +5 -3
  75. data/spec/unit/mutant/subject/method/singleton_spec.rb +3 -2
  76. data/spec/unit/mutant/subject_spec.rb +36 -1
  77. data/spec/unit/mutant/test_spec.rb +25 -0
  78. data/spec/unit/mutant/warning_expectation.rb +11 -8
  79. data/spec/unit/mutant_spec.rb +11 -2
  80. metadata +27 -28
  81. data/lib/mutant/killer.rb +0 -44
  82. data/lib/mutant/matcher/builder.rb +0 -142
  83. data/lib/mutant/mutation/evil.rb +0 -23
  84. data/lib/mutant/mutation/neutral.rb +0 -18
  85. data/lib/mutant/reporter/cli/progress/mutation.rb +0 -46
  86. data/lib/mutant/reporter/cli/report/config.rb +0 -116
  87. data/lib/mutant/rspec.rb +0 -0
  88. data/lib/mutant/runner/config.rb +0 -138
  89. data/lib/mutant/runner/killer.rb +0 -75
  90. data/lib/mutant/runner/mutation.rb +0 -78
  91. data/lib/mutant/runner/subject.rb +0 -85
  92. data/lib/mutant/test/report.rb +0 -59
  93. data/spec/unit/mutant/cli_new_spec.rb +0 -147
  94. data/spec/unit/mutant/cli_run_spec.rb +0 -46
  95. data/spec/unit/mutant/runner/config_spec.rb +0 -157
  96. data/spec/unit/mutant/runner/mutation_spec.rb +0 -101
  97. data/spec/unit/mutant/runner/subject_spec.rb +0 -59
  98. data/spec/unit/mutant/subject/mutations_spec.rb +0 -23
  99. data/spec/unit/mutant/subject/node_spec.rb +0 -17
@@ -0,0 +1,30 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Progress
5
+ # Progress printer for configuration
6
+ class Env < self
7
+
8
+ handle Mutant::Env
9
+
10
+ delegate :config
11
+
12
+ # Report configuration
13
+ #
14
+ # @param [Mutant::Config] config
15
+ #
16
+ # @return [self]
17
+ #
18
+ # @api private
19
+ #
20
+ def run
21
+ visit(config)
22
+ info 'Available Subjects: %d', object.matchable_scopes.length
23
+ info 'Subjects: %d', object.subjects.length
24
+ end
25
+
26
+ end # Progress
27
+ end # Printer
28
+ end # CLI
29
+ end # Reporter
30
+ end # Mutant
@@ -5,7 +5,10 @@ module Mutant
5
5
  # Noop CLI progress reporter
6
6
  class Noop < self
7
7
 
8
- handle(Mutant::Runner::Killer)
8
+ handle(Mutant::Test)
9
+ handle(Mutant::Mutation)
10
+ handle(Mutant::Result::Env)
11
+ handle(Mutant::Result::Test)
9
12
 
10
13
  # Noop progress report
11
14
  #
@@ -0,0 +1,12 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Progress
5
+ # Abstract namespace class for result progress printers
6
+ class Result < self
7
+ include AbstractType
8
+ end # Result
9
+ end # Progress
10
+ end # CLI
11
+ end # Reporter
12
+ end # Mutant
@@ -0,0 +1,45 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Progress
5
+ class Result
6
+ # Mutation test result progress reporter
7
+ class Mutation < self
8
+
9
+ handle(Mutant::Result::Mutation)
10
+
11
+ SUCCESS = '.'.freeze
12
+ FAILURE = 'F'.freeze
13
+
14
+ # Run printer
15
+ #
16
+ # @return [self]
17
+ #
18
+ # @api private
19
+ #
20
+ def run
21
+ char(success? ? SUCCESS : FAILURE)
22
+ self
23
+ end
24
+
25
+ private
26
+
27
+ # Write colorized char
28
+ #
29
+ # @param [String] char
30
+ #
31
+ # @return [undefined]
32
+ #
33
+ # @api private
34
+ #
35
+ def char(char)
36
+ output.write(colorize(status_color, char))
37
+ output.flush
38
+ end
39
+
40
+ end # Mutation
41
+ end # Result
42
+ end # Progress
43
+ end # CLI
44
+ end # Reporter
45
+ end # Mutant
@@ -0,0 +1,54 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Progress
5
+ class Result
6
+ # Reporter for subject runners
7
+ class Subject < self
8
+
9
+ FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze
10
+
11
+ handle(Mutant::Result::Subject)
12
+
13
+ # Run printer
14
+ #
15
+ # @return [self]
16
+ #
17
+ # @api private
18
+ #
19
+ def run
20
+ print_progress_bar_finish
21
+ print_stats
22
+ self
23
+ end
24
+
25
+ delegate :coverage, :runtime, :amount_mutations_killed, :amount_mutations, :killtime, :overhead
26
+
27
+ private
28
+
29
+ # Print stats
30
+ #
31
+ # @return [undefined]
32
+ #
33
+ # @api private
34
+ #
35
+ def print_stats
36
+ status(FORMAT, amount_mutations_killed, amount_mutations, coverage * 100, killtime, runtime, overhead)
37
+ end
38
+
39
+ # Print progress bar finish
40
+ #
41
+ # @return [undefined]
42
+ #
43
+ # @api private
44
+ #
45
+ def print_progress_bar_finish
46
+ puts unless amount_mutations.zero?
47
+ end
48
+
49
+ end # Subject
50
+ end # Result
51
+ end # Progress
52
+ end # CLI
53
+ end # Reporter
54
+ end # Mutant
@@ -2,14 +2,10 @@ module Mutant
2
2
  class Reporter
3
3
  class CLI
4
4
  class Progress
5
- # Reporter for subject runners
5
+ # CLI progress reporter for subjects
6
6
  class Subject < self
7
7
 
8
- FORMAT = '(%02d/%02d) %3d%% - %0.02fs'.freeze
9
-
10
- handle(Mutant::Runner::Subject)
11
-
12
- delegate :running?, :tests, :subject
8
+ handle Mutant::Subject
13
9
 
14
10
  # Run printer
15
11
  #
@@ -18,93 +14,14 @@ module Mutant
18
14
  # @api private
19
15
  #
20
16
  def run
21
- if running?
22
- puts(subject.identification)
23
- tests.each do |test|
24
- puts "- #{test.identification}"
25
- end
26
- else
27
- print_progress_bar_finish
28
- print_stats
17
+ puts("#{object.identification} mutations: #{object.mutations.length}")
18
+ object.tests.each do |test|
19
+ puts "- #{test.identification}"
29
20
  end
30
- self
31
- end
32
-
33
- private
34
-
35
- # Return mutation time on subject
36
- #
37
- # @return [Float]
38
- #
39
- # @api private
40
- #
41
- def time
42
- mutations.map(&:runtime).inject(0, :+)
43
- end
44
-
45
- # Print stats
46
- #
47
- # @return [undefined]
48
- #
49
- # @api private
50
- #
51
- def print_stats
52
- status(FORMAT, amount_kills, amount_mutations, coverage, time)
53
- end
54
-
55
- # Print progress bar finish
56
- #
57
- # @return [undefined]
58
- #
59
- # @api private
60
- #
61
- def print_progress_bar_finish
62
- puts unless amount_mutations.zero?
63
- end
64
-
65
- # Return kills
66
- #
67
- # @return [Fixnum]
68
- #
69
- # @api private
70
- #
71
- def amount_kills
72
- amount_mutations - object.failed_mutations.length
73
- end
74
-
75
- # Return amount of mutations
76
- #
77
- # @return [Array<Mutation>]
78
- #
79
- # @api private
80
- #
81
- def amount_mutations
82
- mutations.length
83
- end
84
-
85
- # Return mutations
86
- #
87
- # @return [Array<Mutation>]
88
- #
89
- # @api private
90
- #
91
- def mutations
92
- object.mutations
93
- end
94
-
95
- # Return subject coverage
96
- #
97
- # @return [Float]
98
- #
99
- # @api private
100
- #
101
- def coverage
102
- return 0 if amount_mutations.zero?
103
- Rational(amount_kills, amount_mutations) * 100
104
21
  end
105
22
 
106
- end # Runner
23
+ end # Subject
107
24
  end # Progress
108
- end # CLI
25
+ end # CLI
109
26
  end # Reporter
110
27
  end # Mutant
@@ -61,6 +61,8 @@ module Mutant
61
61
  # @api private
62
62
  #
63
63
  def included(host)
64
+ super
65
+
64
66
  object = self
65
67
  host.class_eval do
66
68
  define_singleton_method(:lookup, &object.method(:lookup))
@@ -0,0 +1,92 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Report
5
+
6
+ # Env result reporter
7
+ class Env < self
8
+
9
+ handle(Result::Env)
10
+
11
+ delegate(
12
+ :coverage, :failed_subject_results, :amount_subjects, :amount_mutations,
13
+ :amount_mutations_alive, :amount_mutations_killed, :runtime, :killtime, :overhead, :env
14
+ )
15
+
16
+ # Run printer
17
+ #
18
+ # @return [self]
19
+ #
20
+ # @api private
21
+ #
22
+ def run
23
+ visit_collection(failed_subject_results)
24
+ info 'Subjects: %s', amount_subjects
25
+ info 'Mutations: %s', amount_mutations
26
+ info 'Kills: %s', amount_mutations_killed
27
+ info 'Alive: %s', amount_mutations_alive
28
+ info 'Runtime: %0.2fs', runtime
29
+ info 'Killtime: %0.2fs', killtime
30
+ info 'Overhead: %0.2f%%', overhead_percent
31
+ status 'Coverage: %0.2f%%', coverage_percent
32
+ status 'Expected: %0.2f%%', env.config.expected_coverage
33
+ print_generic_stats
34
+ self
35
+ end
36
+
37
+ private
38
+
39
+ # Print generic stats
40
+ #
41
+ # @return [undefined]
42
+ #
43
+ # @api private
44
+ #
45
+ def print_generic_stats
46
+ stats = generic_stats.to_a.sort_by(&:last)
47
+ return if stats.empty?
48
+ info('Nodes handled by generic mutator (type:occurrences):')
49
+ stats.reverse_each do |type, amount|
50
+ info('%-10s: %d', type, amount)
51
+ end
52
+ end
53
+
54
+ # Return coverage percent
55
+ #
56
+ # @return [Float]
57
+ #
58
+ # @api private
59
+ #
60
+ def coverage_percent
61
+ coverage * 100
62
+ end
63
+
64
+ # Return overhead percent
65
+ #
66
+ # @return [Float]
67
+ #
68
+ # @api private
69
+ #
70
+ def overhead_percent
71
+ (overhead / killtime) * 100
72
+ end
73
+
74
+ # Return stats for nodes handled by generic mutator
75
+ #
76
+ # @return [Hash<Symbo, Fixnum>]
77
+ #
78
+ # @api private
79
+ #
80
+ def generic_stats
81
+ object.subject_results.each_with_object(Hash.new(0)) do |result, stats|
82
+ Walker.run(result.subject.node) do |node|
83
+ stats[node.type] += 1 if Mutator::Registry.lookup(node).equal?(Mutator::Node::Generic)
84
+ end
85
+ end
86
+ end
87
+
88
+ end # Env
89
+ end # Report
90
+ end # CLI
91
+ end # Reporter
92
+ end # Mutant
@@ -6,72 +6,35 @@ module Mutant
6
6
  # Reporter for mutations
7
7
  class Mutation < self
8
8
 
9
- # Run report printer
10
- #
11
- # @return [self]
12
- #
13
- # @api private
14
- #
15
- def run
16
- puts(object.identification)
17
- puts(details)
18
- self
19
- end
20
-
21
- # Reporter for noop mutations
22
- class Noop < self
23
- handle(Mutant::Mutation::Neutral::Noop)
24
-
25
- MESSAGE = [
26
- 'Parsed subject AST:',
27
- '%s',
28
- 'Unparsed source:',
29
- '%s'
30
- ].join("\n").freeze
31
-
32
- delegate :killers
33
-
34
- private
35
-
36
- # Return details
37
- #
38
- # @return [self]
39
- #
40
- # @api private
41
- #
42
- def details
43
- info(MESSAGE, object.subject.node.inspect, object.original_source)
44
- end
9
+ handle Mutant::Result::Mutation
45
10
 
46
- end # Noop
11
+ delegate :mutation, :failed_test_results
47
12
 
48
- # Reporter for mutations producing a diff
49
- class Diff < self
50
- handle(Mutant::Mutation::Evil)
51
- handle(Mutant::Mutation::Neutral)
13
+ DIFF_ERROR_MESSAGE = 'BUG: Mutation NOT resulted in exactly one diff. Please report a reproduction!'.freeze
52
14
 
53
- DIFF_ERROR_MESSAGE = 'BUG: Mutation NOT resulted in exactly one diff. Please report a reproduction'.freeze
15
+ MAP = {
16
+ Mutant::Mutation::Evil => :evil_details,
17
+ Mutant::Mutation::Neutral => :neutral_details,
18
+ Mutant::Mutation::Noop => :noop_details
19
+ }.freeze
54
20
 
55
- private
21
+ NEUTRAL_MESSAGE =
22
+ "--- Neutral failure ---\n" \
23
+ "Original code was inserted unmutated. And the test did NOT PASS.\n" \
24
+ "Your tests do not pass initially or you found a bug in mutant / unparser.\n" \
25
+ "Subject AST:\n" \
26
+ "%s\n" \
27
+ "Unparsed Source:\n" \
28
+ "%s\n" \
29
+ "Test Reports: %d\n"
56
30
 
57
- # Run report printer
58
- #
59
- # @return [String]
60
- #
61
- # @api private
62
- #
63
- def details
64
- original, current = object.original_source, object.source
65
- diff = Mutant::Diff.build(original, current)
66
- diff = color? ? diff.colorized_diff : diff.diff
67
- diff || DIFF_ERROR_MESSAGE
68
- end
69
- end # Diff
70
- end # Mutation
31
+ NOOP_MESSAGE =
32
+ "---- Noop failure -----\n" \
33
+ "No code was inserted. And the test did NOT PASS.\n" \
34
+ "This is typically a problem of your specs not passing unmutated.\n" \
35
+ "Test Reports: %d\n"
71
36
 
72
- # Subject report printer
73
- class MutationRunner < self
74
- handle(Mutant::Runner::Mutation)
37
+ FOOTER = '-----------------------'.freeze
75
38
 
76
39
  # Run report printer
77
40
  #
@@ -80,39 +43,57 @@ module Mutant
80
43
  # @api private
81
44
  #
82
45
  def run
83
- visit(object.mutation)
84
- if object.mutation.kind_of?(Mutant::Mutation::Neutral::Noop)
85
- report_noop
86
- end
46
+ puts(mutation.identification)
47
+ print_details
48
+ puts(FOOTER)
87
49
  self
88
50
  end
89
51
 
90
- delegate :killers
91
-
92
52
  private
93
53
 
94
- # Report noop output
54
+ # Return details
95
55
  #
96
56
  # @return [undefined]
97
57
  #
98
58
  # @api private
99
59
  #
100
- def report_noop
101
- info('NOOP MUTATION TESTS FAILED!')
102
- noop_reports.each do |report|
103
- puts(report.test.identification)
104
- puts(report.output)
105
- end
60
+ def print_details
61
+ send(MAP.fetch(mutation.class))
62
+ end
63
+
64
+ # Return evil details
65
+ #
66
+ # @return [String]
67
+ #
68
+ # @api private
69
+ #
70
+ def evil_details
71
+ original, current = mutation.original_source, mutation.source
72
+ diff = Mutant::Diff.build(original, current)
73
+ diff = color? ? diff.colorized_diff : diff.diff
74
+ info(diff || DIFF_ERROR_MESSAGE)
75
+ end
76
+
77
+ # Noop details
78
+ #
79
+ # @return [String]
80
+ #
81
+ # @api private
82
+ #
83
+ def noop_details
84
+ info(NOOP_MESSAGE, failed_test_results.length)
85
+ visit_collection(failed_test_results)
106
86
  end
107
87
 
108
- # Return test noop reports
88
+ # Neutral details
109
89
  #
110
- # @return [Enumerable<Test::Report>]
90
+ # @return [String]
111
91
  #
112
92
  # @api private
113
93
  #
114
- def noop_reports
115
- killers.reject(&:success?).map(&:report).map(&:test_report)
94
+ def neutral_details
95
+ info(NEUTRAL_MESSAGE, mutation.subject.node.inspect, mutation.source, failed_test_results.length)
96
+ visit_collection(failed_test_results)
116
97
  end
117
98
 
118
99
  end # Mutation