mutant 0.5.12 → 0.5.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +10 -0
  3. data/circle.yml +1 -1
  4. data/config/flay.yml +1 -1
  5. data/config/flog.yml +1 -1
  6. data/config/mutant.yml +1 -2
  7. data/config/reek.yml +12 -3
  8. data/config/rubocop.yml +4 -0
  9. data/lib/mutant.rb +45 -16
  10. data/lib/mutant/constants.rb +11 -11
  11. data/lib/mutant/delegator.rb +50 -0
  12. data/lib/mutant/{differ.rb → diff.rb} +5 -5
  13. data/lib/mutant/killer.rb +29 -106
  14. data/lib/mutant/matcher/method.rb +2 -11
  15. data/lib/mutant/mutation.rb +17 -3
  16. data/lib/mutant/mutation/evil.rb +2 -10
  17. data/lib/mutant/mutation/neutral.rb +4 -30
  18. data/lib/mutant/mutator/node/literal/fixnum.rb +0 -1
  19. data/lib/mutant/mutator/node/literal/float.rb +0 -1
  20. data/lib/mutant/mutator/node/literal/string.rb +0 -1
  21. data/lib/mutant/mutator/node/literal/symbol.rb +6 -2
  22. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +8 -3
  23. data/lib/mutant/mutator/util/symbol.rb +3 -1
  24. data/lib/mutant/node_helpers.rb +1 -3
  25. data/lib/mutant/reporter.rb +10 -0
  26. data/lib/mutant/reporter/cli.rb +15 -2
  27. data/lib/mutant/reporter/cli/printer.rb +12 -105
  28. data/lib/mutant/reporter/cli/progress.rb +12 -0
  29. data/lib/mutant/reporter/cli/progress/config.rb +32 -0
  30. data/lib/mutant/reporter/cli/{printer/killer.rb → progress/mutation.rb} +9 -16
  31. data/lib/mutant/reporter/cli/progress/noop.rb +22 -0
  32. data/lib/mutant/reporter/cli/progress/subject.rb +118 -0
  33. data/lib/mutant/reporter/cli/registry.rb +77 -0
  34. data/lib/mutant/reporter/cli/report.rb +12 -0
  35. data/lib/mutant/reporter/cli/report/config.rb +118 -0
  36. data/lib/mutant/reporter/cli/report/mutation.rb +112 -0
  37. data/lib/mutant/reporter/cli/report/subject.rb +33 -0
  38. data/lib/mutant/reporter/null.rb +13 -0
  39. data/lib/mutant/reporter/trace.rb +41 -0
  40. data/lib/mutant/runner.rb +22 -20
  41. data/lib/mutant/runner/config.rb +6 -5
  42. data/lib/mutant/runner/killer.rb +59 -0
  43. data/lib/mutant/runner/mutation.rb +17 -10
  44. data/lib/mutant/runner/subject.rb +14 -4
  45. data/lib/mutant/strategy.rb +30 -16
  46. data/lib/mutant/subject/method/instance.rb +1 -1
  47. data/lib/mutant/test.rb +86 -0
  48. data/lib/mutant/version.rb +1 -1
  49. data/spec/integration/mutant/null_spec.rb +18 -0
  50. data/spec/integration/mutant/rspec_spec.rb +1 -1
  51. data/spec/spec_helper.rb +0 -2
  52. data/spec/unit/mutant/diff_spec.rb +162 -0
  53. data/spec/unit/mutant/mutation_spec.rb +8 -5
  54. data/spec/unit/mutant/mutator/node/and_asgn_spec.rb +1 -9
  55. data/spec/unit/mutant/mutator/node/block_spec.rb +6 -18
  56. data/spec/unit/mutant/mutator/node/case_spec.rb +10 -16
  57. data/spec/unit/mutant/mutator/node/define_spec.rb +5 -17
  58. data/spec/unit/mutant/mutator/node/dstr_spec.rb +0 -6
  59. data/spec/unit/mutant/mutator/node/dsym_spec.rb +0 -5
  60. data/spec/unit/mutant/mutator/node/if_spec.rb +13 -17
  61. data/spec/unit/mutant/mutator/node/literal/fixnum_spec.rb +1 -7
  62. data/spec/unit/mutant/mutator/node/literal/float_spec.rb +0 -9
  63. data/spec/unit/mutant/mutator/node/literal/range_spec.rb +0 -10
  64. data/spec/unit/mutant/mutator/node/literal/string_spec.rb +1 -5
  65. data/spec/unit/mutant/mutator/node/literal/symbol_spec.rb +1 -5
  66. data/spec/unit/mutant/mutator/node/named_value/access_spec.rb +4 -7
  67. data/spec/unit/mutant/mutator/node/named_value/constant_assignment_spec.rb +1 -5
  68. data/spec/unit/mutant/mutator/node/named_value/variable_assignment_spec.rb +4 -8
  69. data/spec/unit/mutant/mutator/node/op_assgn_spec.rb +0 -7
  70. data/spec/unit/mutant/mutator/node/or_asgn_spec.rb +1 -9
  71. data/spec/unit/mutant/mutator/node/rescue_spec.rb +0 -4
  72. data/spec/unit/mutant/reporter/null_spec.rb +11 -0
  73. data/spec/unit/mutant/runner/config_spec.rb +6 -7
  74. data/spec/unit/mutant/runner/mutation_spec.rb +101 -0
  75. data/spec/unit/mutant/runner/subject_spec.rb +10 -7
  76. data/spec/unit/mutant_spec.rb +53 -0
  77. metadata +65 -62
  78. data/lib/mutant/killer/forked.rb +0 -46
  79. data/lib/mutant/killer/forking.rb +0 -46
  80. data/lib/mutant/killer/static.rb +0 -34
  81. data/lib/mutant/mutator/node/literal/dynamic.rb +0 -27
  82. data/lib/mutant/random.rb +0 -38
  83. data/lib/mutant/reporter/cli/printer/config.rb +0 -154
  84. data/lib/mutant/reporter/cli/printer/mutation.rb +0 -103
  85. data/lib/mutant/reporter/cli/printer/subject.rb +0 -150
  86. data/spec/unit/mutant/differ/diff_spec.rb +0 -123
  87. data/spec/unit/mutant/differ_spec.rb +0 -42
  88. data/spec/unit/mutant/killer/success_predicate_spec.rb +0 -30
  89. data/spec/unit/mutant/rspec/killer_spec.rb +0 -57
  90. data/spec/unit/mutant/runner/mutation/killer_spec.rb +0 -44
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ module Mutant
4
+ class Reporter
5
+ class CLI
6
+ # Abstract base class for process printers
7
+ class Progress < Printer
8
+ include AbstractType, Registry.new
9
+ end # Progress
10
+ end # CLI
11
+ end # Reporter
12
+ end # Mutant
@@ -0,0 +1,32 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Progress
5
+ # Progress printer for configuration
6
+ class Config < self
7
+
8
+ handle(Mutant::Config)
9
+
10
+ delegate :matcher, :strategy, :expected_coverage
11
+
12
+ # Report configuration
13
+ #
14
+ # @param [Mutant::Config] config
15
+ #
16
+ # @return [self]
17
+ #
18
+ # @api private
19
+ #
20
+ def run
21
+ info 'Mutant configuration:'
22
+ info 'Matcher: %s', matcher.inspect
23
+ info 'Strategy: %s', strategy.inspect
24
+ info 'Expect Coverage: %02f%%', expected_coverage.inspect
25
+ self
26
+ end
27
+
28
+ end # Progress
29
+ end # Printer
30
+ end # CLI
31
+ end # Reporter
32
+ end # Mutant
@@ -1,14 +1,12 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Reporter
5
3
  class CLI
6
- class Printer
4
+ class Progress
7
5
 
8
- # Printer for killer results
9
- class Killer < self
6
+ # Mutation progress reporter
7
+ class Mutation < self
10
8
 
11
- handle(Mutant::Killer)
9
+ handle(Runner::Mutation)
12
10
 
13
11
  SUCCESS = '.'.freeze
14
12
  FAILURE = 'F'.freeze
@@ -20,11 +18,7 @@ module Mutant
20
18
  # @api private
21
19
  #
22
20
  def run
23
- if success?
24
- char(SUCCESS, Color::GREEN)
25
- else
26
- char(FAILURE, Color::RED)
27
- end
21
+ char(success? ? SUCCESS : FAILURE)
28
22
  end
29
23
 
30
24
  private
@@ -32,19 +26,18 @@ module Mutant
32
26
  # Write colorized char
33
27
  #
34
28
  # @param [String] char
35
- # @param [Color]
36
29
  #
37
30
  # @return [undefined]
38
31
  #
39
32
  # @api private
40
33
  #
41
- def char(char, color)
42
- output.write(colorize(color, char))
34
+ def char(char)
35
+ output.write(colorize(status_color, char))
43
36
  output.flush
44
37
  end
45
38
 
46
- end # Killer
47
- end # Printer
39
+ end # Mutation
40
+ end # Progress
48
41
  end # CLI
49
42
  end # Reporter
50
43
  end # Mutant
@@ -0,0 +1,22 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Progress
5
+ # Noop CLI progress reporter
6
+ class Noop < self
7
+
8
+ handle(Mutant::Mutation)
9
+
10
+ # Noop progress report
11
+ #
12
+ # @return [self]
13
+ #
14
+ def run
15
+ self
16
+ end
17
+
18
+ end # Noop
19
+ end # Progress
20
+ end # CLI
21
+ end # Reporter
22
+ end # Mutant
@@ -0,0 +1,118 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ class Progress
5
+ # Subject results printer
6
+ class Subject < self
7
+
8
+ handle(Mutant::Subject)
9
+
10
+ # Run subject results printer
11
+ #
12
+ # @return [undefined]
13
+ #
14
+ # @api private
15
+ #
16
+ def run
17
+ puts(object.identification)
18
+ end
19
+
20
+ end # Subject
21
+
22
+ # Reporter for subject runners
23
+ class SubjectRunner < self
24
+
25
+ FORMAT = '(%02d/%02d) %3d%% - %0.02fs'.freeze
26
+
27
+ handle(Mutant::Runner::Subject)
28
+
29
+ # Run printer
30
+ #
31
+ # @return [undefined]
32
+ #
33
+ # @api private
34
+ #
35
+ def run
36
+ print_progress_bar_finish
37
+ print_stats
38
+ self
39
+ end
40
+
41
+ private
42
+
43
+ # Return mutation time on subject
44
+ #
45
+ # @return [Float]
46
+ #
47
+ # @api private
48
+ #
49
+ def time
50
+ mutations.map(&:runtime).inject(0, :+)
51
+ end
52
+
53
+ # Print stats
54
+ #
55
+ # @return [undefined]
56
+ #
57
+ # @api private
58
+ #
59
+ def print_stats
60
+ status(FORMAT, amount_kills, amount_mutations, coverage, time)
61
+ end
62
+
63
+ # Print progress bar finish
64
+ #
65
+ # @return [undefined]
66
+ #
67
+ # @api private
68
+ #
69
+ def print_progress_bar_finish
70
+ puts unless amount_mutations.zero?
71
+ end
72
+
73
+ # Return kills
74
+ #
75
+ # @return [Fixnum]
76
+ #
77
+ # @api private
78
+ #
79
+ def amount_kills
80
+ amount_mutations - object.failed_mutations.length
81
+ end
82
+
83
+ # Return amount of mutations
84
+ #
85
+ # @return [Array<Mutation>]
86
+ #
87
+ # @api private
88
+ #
89
+ def amount_mutations
90
+ mutations.length
91
+ end
92
+
93
+ # Return mutations
94
+ #
95
+ # @return [Array<Mutation>]
96
+ #
97
+ # @api private
98
+ #
99
+ def mutations
100
+ object.mutations
101
+ end
102
+
103
+ # Return subject coverage
104
+ #
105
+ # @return [Float]
106
+ #
107
+ # @api private
108
+ #
109
+ def coverage
110
+ return 0 if amount_mutations.zero?
111
+ Rational(amount_kills, amount_mutations) * 100
112
+ end
113
+
114
+ end # Runner
115
+ end # Progress
116
+ end # CLI
117
+ end # Reporter
118
+ end # Mutant
@@ -0,0 +1,77 @@
1
+ module Mutant
2
+ class Reporter
3
+ class CLI
4
+ # Mixin to generate registry semantics
5
+ class Registry < Module
6
+ include Concord.new(:registry)
7
+
8
+ # Return new registry
9
+ #
10
+ # @return [Registry]
11
+ #
12
+ # @api private
13
+ #
14
+ def self.new
15
+ super({})
16
+ end
17
+
18
+ # Register handler for class
19
+ #
20
+ # @param [Class] klass
21
+ #
22
+ # @return [self]
23
+ #
24
+ # @api private
25
+ #
26
+ def handle(subject, handler)
27
+ raise "Duplicate registration of #{subject}" if registry.key?(subject)
28
+ registry[subject] = handler
29
+ self
30
+ end
31
+
32
+ # Lookup handler
33
+ #
34
+ # @param [Class] subject
35
+ #
36
+ # @return [Object]
37
+ # if found
38
+ #
39
+ # @raise [RuntimeError]
40
+ # otherwise
41
+ #
42
+ # @api private
43
+ #
44
+ def lookup(subject)
45
+ current = subject
46
+ until current == Object
47
+ if registry.key?(current)
48
+ return registry.fetch(current)
49
+ end
50
+ current = current.superclass
51
+ end
52
+ raise "No printer for: #{subject}"
53
+ end
54
+
55
+ # Hook called when module is included
56
+ #
57
+ # @param [Class,Module] host
58
+ #
59
+ # @return [undefined]
60
+ #
61
+ def included(host)
62
+ object = self
63
+ host.class_eval do
64
+ define_singleton_method(:lookup, &object.method(:lookup))
65
+ private_class_method :lookup
66
+
67
+ define_singleton_method(:handle) do |subject|
68
+ object.handle(subject, self)
69
+ end
70
+ private_class_method :handle
71
+ end
72
+ end
73
+
74
+ end # Registry
75
+ end # CLI
76
+ end # Reporter
77
+ end # Mutant
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ module Mutant
4
+ class Reporter
5
+ class CLI
6
+ # Abstract base class for process printers
7
+ class Report < Printer
8
+ include AbstractType, Registry.new
9
+ end # Report
10
+ end # CLI
11
+ end # Reporter
12
+ end # Mutant
@@ -0,0 +1,118 @@
1
+ # encoding: utf-8
2
+
3
+ module Mutant
4
+ class Reporter
5
+ class CLI
6
+ class Report
7
+
8
+ # Printer for configuration
9
+ class Config < self
10
+
11
+ handle(Mutant::Runner::Config)
12
+
13
+ delegate(
14
+ :amount_kills, :amount_mutations, :amount_kils,
15
+ :coverage, :subjects, :failed_subjects, :runtime, :mutations
16
+ )
17
+
18
+ # Run printer
19
+ #
20
+ # @return [self]
21
+ #
22
+ # @api private
23
+ #
24
+ def run
25
+ failed_subjects.each(&method(:visit))
26
+ info 'Subjects: %s', amount_subjects
27
+ info 'Mutations: %s', amount_mutations
28
+ info 'Kills: %s', amount_kills
29
+ info 'Alive: %s', amount_alive
30
+ info 'Runtime: %0.2fs', runtime
31
+ info 'Killtime: %0.2fs', killtime
32
+ info 'Overhead: %0.2f%%', overhead
33
+ status 'Coverage: %0.2f%%', coverage
34
+ status 'Expected: %0.2f%%', object.config.expected_coverage
35
+ print_generic_stats
36
+ self
37
+ end
38
+
39
+ private
40
+
41
+ # Print generic stats
42
+ #
43
+ # @return [undefined]
44
+ #
45
+ # @api private
46
+ #
47
+ def print_generic_stats
48
+ stats = generic_stats.to_a.sort_by(&:last)
49
+ return if stats.empty?
50
+ info('Nodes handled by generic mutator (type:occurrences):')
51
+ stats.reverse_each do |type, amount|
52
+ info('%-10s: %d', type, amount)
53
+ end
54
+ end
55
+
56
+ # Return stats for nodes handled by generic mutator
57
+ #
58
+ # @return [Hash<Symbo, Fixnum>]
59
+ #
60
+ # @api private
61
+ #
62
+ def generic_stats
63
+ subjects.each_with_object(Hash.new(0)) do |runner, stats|
64
+ Walker.run(runner.subject.node) do |node|
65
+ if Mutator::Registry.lookup(node) == Mutator::Node::Generic
66
+ stats[node.type] += 1
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ # Return amount of subjects
73
+ #
74
+ # @return [Fixnum]
75
+ #
76
+ # @api private
77
+ #
78
+ def amount_subjects
79
+ subjects.length
80
+ end
81
+
82
+ # Return amount of time in killers
83
+ #
84
+ # @return [Float]
85
+ #
86
+ # @api private
87
+ #
88
+ def killtime
89
+ mutations.map(&:runtime).inject(0, :+)
90
+ end
91
+ memoize :killtime
92
+
93
+ # Return mutant overhead
94
+ #
95
+ # @return [Float]
96
+ #
97
+ # @api private
98
+ #
99
+ def overhead
100
+ return 0 if runtime.zero?
101
+ Rational(runtime - killtime, runtime) * 100
102
+ end
103
+
104
+ # Return amount of alive mutations
105
+ #
106
+ # @return [Fixnum]
107
+ #
108
+ # @api private
109
+ #
110
+ def amount_alive
111
+ object.amount_mutations - amount_kills
112
+ end
113
+
114
+ end # Config
115
+ end # Report
116
+ end # CLI
117
+ end # Reporter
118
+ end # Mutant