mutant 0.7.9 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +11 -1
  3. data/README.md +5 -12
  4. data/bin/mutant +17 -1
  5. data/config/flay.yml +1 -1
  6. data/config/flog.yml +1 -1
  7. data/config/reek.yml +4 -4
  8. data/config/rubocop.yml +21 -3
  9. data/lib/mutant.rb +15 -61
  10. data/lib/mutant/cli.rb +1 -7
  11. data/lib/mutant/color.rb +2 -2
  12. data/lib/mutant/config.rb +1 -1
  13. data/lib/mutant/expression/method.rb +1 -1
  14. data/lib/mutant/expression/methods.rb +1 -1
  15. data/lib/mutant/expression/namespace.rb +1 -1
  16. data/lib/mutant/mutator/node/send.rb +18 -18
  17. data/lib/mutant/reporter/cli.rb +1 -6
  18. data/lib/mutant/reporter/cli/format.rb +2 -2
  19. data/lib/mutant/reporter/cli/printer.rb +5 -500
  20. data/lib/mutant/reporter/cli/printer/config.rb +32 -0
  21. data/lib/mutant/reporter/cli/printer/env_progress.rb +66 -0
  22. data/lib/mutant/reporter/cli/printer/env_result.rb +23 -0
  23. data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +37 -0
  24. data/lib/mutant/reporter/cli/printer/mutation_result.rb +151 -0
  25. data/lib/mutant/reporter/cli/printer/status.rb +60 -0
  26. data/lib/mutant/reporter/cli/printer/status_progressive.rb +52 -0
  27. data/lib/mutant/reporter/cli/printer/subject_progress.rb +90 -0
  28. data/lib/mutant/reporter/cli/printer/subject_result.rb +28 -0
  29. data/lib/mutant/reporter/cli/printer/test_result.rb +33 -0
  30. data/lib/mutant/require_highjack.rb +11 -50
  31. data/lib/mutant/version.rb +1 -1
  32. data/lib/mutant/zombifier.rb +79 -37
  33. data/meta/send.rb +1 -1
  34. data/mutant-rspec.gemspec +1 -1
  35. data/mutant.gemspec +3 -3
  36. data/spec/integration/mutant/corpus_spec.rb +2 -2
  37. data/spec/integration/mutant/rspec_spec.rb +5 -15
  38. data/spec/integrations.yml +3 -3
  39. data/spec/spec_helper.rb +1 -0
  40. data/spec/support/corpus.rb +233 -220
  41. data/spec/support/file_system.rb +60 -0
  42. data/spec/support/rb_bug.rb +1 -1
  43. data/spec/support/ruby_vm.rb +82 -0
  44. data/spec/support/shared_context.rb +19 -10
  45. data/spec/unit/mutant/ast_spec.rb +2 -2
  46. data/spec/unit/mutant/cache_spec.rb +22 -0
  47. data/spec/unit/mutant/cli_spec.rb +1 -30
  48. data/spec/unit/mutant/context_spec.rb +1 -0
  49. data/spec/unit/mutant/expression/method_spec.rb +6 -4
  50. data/spec/unit/mutant/parallel/master_spec.rb +1 -1
  51. data/spec/unit/mutant/reporter/cli/printer/config_spec.rb +33 -0
  52. data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +76 -0
  53. data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +35 -0
  54. data/spec/unit/mutant/reporter/cli/printer/mutation_progress_result_spec.rb +23 -0
  55. data/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb +110 -0
  56. data/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb +51 -0
  57. data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +145 -0
  58. data/spec/unit/mutant/reporter/cli/printer/subject_progress_spec.rb +37 -0
  59. data/spec/unit/mutant/reporter/cli/printer/subject_result_spec.rb +37 -0
  60. data/spec/unit/mutant/reporter/cli/printer/test_result_spec.rb +14 -0
  61. data/spec/unit/mutant/reporter/cli/printer_spec.rb +140 -0
  62. data/spec/unit/mutant/reporter/cli_spec.rb +69 -313
  63. data/spec/unit/mutant/reporter/trace_spec.rb +12 -0
  64. data/spec/unit/mutant/require_highjack_spec.rb +25 -28
  65. data/spec/unit/mutant/warning_filter_spec.rb +7 -0
  66. data/spec/unit/mutant/zombifier_spec.rb +120 -0
  67. data/spec/unit/mutant_spec.rb +0 -43
  68. data/test_app/Gemfile.rspec3.3 +6 -0
  69. metadata +46 -17
  70. data/.travis.yml +0 -20
  71. data/lib/mutant/zombifier/file.rb +0 -100
  72. data/spec/integration/mutant/zombie_spec.rb +0 -6
@@ -0,0 +1,32 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Printer
5
+ # Printer for mutation config
6
+ class Config < self
7
+
8
+ # Report configuration
9
+ #
10
+ # @param [Mutant::Config] config
11
+ #
12
+ # @return [undefined]
13
+ #
14
+ # @api private
15
+ #
16
+ # rubocop:disable AbcSize
17
+ #
18
+ def run
19
+ info 'Mutant configuration:'
20
+ info 'Matcher: %s', object.matcher.inspect
21
+ info 'Integration: %s', object.integration.name
22
+ info 'Expect Coverage: %0.2f%%', (object.expected_coverage * 100)
23
+ info 'Jobs: %d', object.jobs
24
+ info 'Includes: %s', object.includes
25
+ info 'Requires: %s', object.requires
26
+ end
27
+
28
+ end # Config
29
+ end # Printer
30
+ end # CLI
31
+ end # Reporter
32
+ end # Mutant
@@ -0,0 +1,66 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Printer
5
+ # Env progress printer
6
+ class EnvProgress < self
7
+ delegate(
8
+ :coverage,
9
+ :amount_subjects,
10
+ :amount_mutations,
11
+ :amount_mutations_alive,
12
+ :amount_mutations_killed,
13
+ :runtime,
14
+ :killtime,
15
+ :overhead,
16
+ :env
17
+ )
18
+
19
+ # Run printer
20
+ #
21
+ # @return [undefined]
22
+ #
23
+ # @api private
24
+ #
25
+ # rubocop:disable MethodLength
26
+ # rubocop:disable AbcSize
27
+ #
28
+ def run
29
+ visit(Config, env.config)
30
+ info 'Subjects: %s', amount_subjects
31
+ info 'Mutations: %s', amount_mutations
32
+ info 'Kills: %s', amount_mutations_killed
33
+ info 'Alive: %s', amount_mutations_alive
34
+ info 'Runtime: %0.2fs', runtime
35
+ info 'Killtime: %0.2fs', killtime
36
+ info 'Overhead: %0.2f%%', overhead_percent
37
+ status 'Coverage: %0.2f%%', coverage_percent
38
+ status 'Expected: %0.2f%%', (env.config.expected_coverage * 100)
39
+ end
40
+
41
+ private
42
+
43
+ # Return coverage percent
44
+ #
45
+ # @return [Float]
46
+ #
47
+ # @api private
48
+ #
49
+ def coverage_percent
50
+ coverage * 100
51
+ end
52
+
53
+ # Return overhead percent
54
+ #
55
+ # @return [Float]
56
+ #
57
+ # @api private
58
+ #
59
+ def overhead_percent
60
+ (overhead / killtime) * 100
61
+ end
62
+ end # EnvProgress
63
+ end # Printer
64
+ end # CLI
65
+ end # Reporter
66
+ end # Mutant
@@ -0,0 +1,23 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Printer
5
+ # Full env result reporter
6
+ class EnvResult < self
7
+ delegate(:failed_subject_results)
8
+
9
+ # Run printer
10
+ #
11
+ # @return [undefined]
12
+ #
13
+ # @api private
14
+ #
15
+ def run
16
+ visit_collection(SubjectResult, failed_subject_results)
17
+ visit(EnvProgress, object)
18
+ end
19
+ end # EnvResult
20
+ end # Printer
21
+ end # CLI
22
+ end # Reporter
23
+ end # Mutant
@@ -0,0 +1,37 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Printer
5
+ # Printer for mutation progress results
6
+ class MutationProgressResult < self
7
+ SUCCESS = '.'.freeze
8
+ FAILURE = 'F'.freeze
9
+
10
+ # Run printer
11
+ #
12
+ # @return [undefined]
13
+ #
14
+ # @api private
15
+ #
16
+ def run
17
+ char(success? ? SUCCESS : FAILURE)
18
+ end
19
+
20
+ private
21
+
22
+ # Write colorized char
23
+ #
24
+ # @param [String] char
25
+ #
26
+ # @return [undefined]
27
+ #
28
+ # @api private
29
+ #
30
+ def char(char)
31
+ output.write(colorize(status_color, char))
32
+ end
33
+ end # MutationProgressResult
34
+ end # Printer
35
+ end # CLI
36
+ end # Reporter
37
+ end # Mutant
@@ -0,0 +1,151 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Printer
5
+ # Reporter for mutation results
6
+ class MutationResult < self
7
+
8
+ delegate :mutation, :test_result
9
+
10
+ DIFF_ERROR_MESSAGE =
11
+ 'BUG: Mutation NOT resulted in exactly one diff hunk. Please report a reproduction!'.freeze
12
+
13
+ MAP = {
14
+ Mutant::Mutation::Evil => :evil_details,
15
+ Mutant::Mutation::Neutral => :neutral_details,
16
+ Mutant::Mutation::Noop => :noop_details
17
+ }.freeze
18
+
19
+ NEUTRAL_MESSAGE =
20
+ "--- Neutral failure ---\n" \
21
+ "Original code was inserted unmutated. And the test did NOT PASS.\n" \
22
+ "Your tests do not pass initially or you found a bug in mutant / unparser.\n" \
23
+ "Subject AST:\n" \
24
+ "%s\n" \
25
+ "Unparsed Source:\n" \
26
+ "%s\n" \
27
+ "Test Result:\n".freeze
28
+
29
+ NO_DIFF_MESSAGE =
30
+ "--- Internal failure ---\n" \
31
+ "BUG: Mutation NOT resulted in exactly one diff hunk. Please report a reproduction!\n" \
32
+ "Original unparsed source:\n" \
33
+ "%s\n" \
34
+ "Original AST:\n" \
35
+ "%s\n" \
36
+ "Mutated unparsed source:\n" \
37
+ "%s\n" \
38
+ "Mutated AST:\n" \
39
+ "%s\n".freeze
40
+
41
+ NOOP_MESSAGE =
42
+ "---- Noop failure -----\n" \
43
+ "No code was inserted. And the test did NOT PASS.\n" \
44
+ "This is typically a problem of your specs not passing unmutated.\n" \
45
+ "Test Result:\n".freeze
46
+
47
+ FOOTER = '-----------------------'.freeze
48
+
49
+ # Run report printer
50
+ #
51
+ # @return [undefined]
52
+ #
53
+ # @api private
54
+ #
55
+ def run
56
+ puts(mutation.identification)
57
+ print_details
58
+ puts(FOOTER)
59
+ end
60
+
61
+ private
62
+
63
+ # Return details
64
+ #
65
+ # @return [undefined]
66
+ #
67
+ # @api private
68
+ #
69
+ def print_details
70
+ __send__(MAP.fetch(mutation.class))
71
+ end
72
+
73
+ # Return evil details
74
+ #
75
+ # @return [String]
76
+ #
77
+ # @api private
78
+ #
79
+ def evil_details
80
+ diff = Diff.build(mutation.original_source, mutation.source)
81
+ diff = color? ? diff.colorized_diff : diff.diff
82
+ if diff
83
+ output.write(diff)
84
+ else
85
+ print_no_diff_message
86
+ end
87
+ end
88
+
89
+ # Print no diff message
90
+ #
91
+ # @return [undefined]
92
+ #
93
+ # @api private
94
+ #
95
+ def print_no_diff_message
96
+ info(
97
+ NO_DIFF_MESSAGE,
98
+ mutation.original_source,
99
+ original_node.inspect,
100
+ mutation.source,
101
+ mutation.node.inspect
102
+ )
103
+ end
104
+
105
+ # Noop details
106
+ #
107
+ # @return [String]
108
+ #
109
+ # @api private
110
+ #
111
+ def noop_details
112
+ info(NOOP_MESSAGE)
113
+ visit_test_result
114
+ end
115
+
116
+ # Neutral details
117
+ #
118
+ # @return [String]
119
+ #
120
+ # @api private
121
+ #
122
+ def neutral_details
123
+ info(NEUTRAL_MESSAGE, original_node.inspect, mutation.source)
124
+ visit_test_result
125
+ end
126
+
127
+ # Visit failed test results
128
+ #
129
+ # @return [undefined]
130
+ #
131
+ # @api private
132
+ #
133
+ def visit_test_result
134
+ visit(TestResult, test_result)
135
+ end
136
+
137
+ # Return original node
138
+ #
139
+ # @return [Parser::AST::Node]
140
+ #
141
+ # @api private
142
+ #
143
+ def original_node
144
+ mutation.subject.node
145
+ end
146
+
147
+ end # MutationResult
148
+ end # Printer
149
+ end # CLI
150
+ end # Reporter
151
+ end # Mutant
@@ -0,0 +1,60 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Printer
5
+ # Printer for runner status
6
+ class Status < self
7
+
8
+ delegate(:active_jobs, :payload)
9
+
10
+ ACTIVE_JOB_HEADER = 'Active Jobs:'.freeze
11
+ ACTIVE_JOB_FORMAT = '%d: %s'.freeze
12
+
13
+ # Print progress for collector
14
+ #
15
+ # @return [undefined]
16
+ #
17
+ # @api private
18
+ #
19
+ def run
20
+ visit(EnvProgress, payload)
21
+ job_status
22
+ info('Active subjects: %d', active_subject_results.length)
23
+ visit_collection(SubjectProgress, active_subject_results)
24
+ end
25
+
26
+ private
27
+
28
+ # Print worker status
29
+ #
30
+ # @return [undefined]
31
+ #
32
+ # @api private
33
+ #
34
+ def job_status
35
+ return if active_jobs.empty?
36
+ info(ACTIVE_JOB_HEADER)
37
+ active_jobs.sort_by(&:index).each do |job|
38
+ info(ACTIVE_JOB_FORMAT, job.index, job.payload.identification)
39
+ end
40
+ end
41
+
42
+ # Return active subject results
43
+ #
44
+ # @return [Array<Result::Subject>]
45
+ #
46
+ # @api private
47
+ #
48
+ def active_subject_results
49
+ active_subjects = active_jobs.map(&:payload).flat_map(&:subject)
50
+
51
+ payload.subject_results.select do |subject_result|
52
+ active_subjects.include?(subject_result.subject)
53
+ end
54
+ end
55
+
56
+ end # Status
57
+ end # Printer
58
+ end # CLI
59
+ end # Reporter
60
+ end # Mutant
@@ -0,0 +1,52 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Printer
5
+ # Reporter for progressive output format on scheduler Status objects
6
+ class StatusProgressive < self
7
+ FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze
8
+
9
+ delegate(
10
+ :coverage,
11
+ :runtime,
12
+ :amount_mutations_killed,
13
+ :amount_mutations,
14
+ :amount_mutation_results,
15
+ :killtime,
16
+ :overhead
17
+ )
18
+
19
+ # Run printer
20
+ #
21
+ # @return [undefined]
22
+ #
23
+ # @api private
24
+ #
25
+ def run
26
+ status(
27
+ FORMAT,
28
+ amount_mutations_killed,
29
+ amount_mutations,
30
+ coverage * 100,
31
+ killtime,
32
+ runtime,
33
+ overhead
34
+ )
35
+ end
36
+
37
+ private
38
+
39
+ # Return object being printed
40
+ #
41
+ # @return [Result::Env]
42
+ #
43
+ # @api private
44
+ #
45
+ def object
46
+ super.payload
47
+ end
48
+ end # StatusProgressive
49
+ end # Printer
50
+ end # CLI
51
+ end # Reporter
52
+ end # Mutant
@@ -0,0 +1,90 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Printer
5
+ # Reporter for subject progress
6
+ class SubjectProgress < self
7
+ FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze
8
+
9
+ delegate(
10
+ :tests,
11
+ :subject,
12
+ :coverage,
13
+ :runtime,
14
+ :amount_mutations_killed,
15
+ :amount_mutations,
16
+ :amount_mutation_results,
17
+ :killtime,
18
+ :overhead
19
+ )
20
+
21
+ # Run printer
22
+ #
23
+ # @return [undefined]
24
+ #
25
+ # @api private
26
+ #
27
+ def run
28
+ puts("#{subject.identification} mutations: #{amount_mutations}")
29
+ print_mutation_results
30
+ print_progress_bar_finish
31
+ print_stats
32
+ print_tests
33
+ end
34
+
35
+ private
36
+
37
+ # Print stats
38
+ #
39
+ # @return [undefined]
40
+ #
41
+ # @api private
42
+ #
43
+ def print_stats
44
+ status(
45
+ FORMAT,
46
+ amount_mutations_killed,
47
+ amount_mutations,
48
+ coverage * 100,
49
+ killtime,
50
+ runtime,
51
+ overhead
52
+ )
53
+ end
54
+
55
+ # Print tests
56
+ #
57
+ # @return [undefined]
58
+ #
59
+ # @api private
60
+ #
61
+ def print_tests
62
+ tests.each do |test|
63
+ puts "- #{test.identification}"
64
+ end
65
+ end
66
+
67
+ # Print progress bar finish
68
+ #
69
+ # @return [undefined]
70
+ #
71
+ # @api private
72
+ #
73
+ def print_progress_bar_finish
74
+ puts(nil) unless amount_mutation_results.zero?
75
+ end
76
+
77
+ # Print mutation results
78
+ #
79
+ # @return [undefined]
80
+ #
81
+ # @api private
82
+ #
83
+ def print_mutation_results
84
+ visit_collection(MutationProgressResult, object.mutation_results)
85
+ end
86
+ end # SubjectProgress
87
+ end # Printer
88
+ end # CLI
89
+ end # Reporter
90
+ end # Mutant