heckle 1.1.1 → 1.2.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.
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.0
2
+ rubygems_version: 0.9.0.9
3
3
  specification_version: 1
4
4
  name: heckle
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.1.1
7
- date: 2006-12-20 00:00:00 -08:00
6
+ version: 1.2.0
7
+ date: 2007-01-15 00:00:00 -08:00
8
8
  summary: Unit Test Sadism
9
9
  require_paths:
10
10
  - lib
@@ -35,8 +35,6 @@ files:
35
35
  - Rakefile
36
36
  - bin/heckle
37
37
  - lib/heckle.rb
38
- - lib/heckle/base.rb
39
- - lib/heckle/reporter.rb
40
38
  - lib/test_unit_heckler.rb
41
39
  - sample/Rakefile
42
40
  - sample/changes.log
@@ -58,20 +56,20 @@ requirements: []
58
56
 
59
57
  dependencies:
60
58
  - !ruby/object:Gem::Dependency
61
- name: hoe
59
+ name: ruby2ruby
62
60
  version_requirement:
63
61
  version_requirements: !ruby/object:Gem::Version::Requirement
64
62
  requirements:
65
63
  - - ">="
66
64
  - !ruby/object:Gem::Version
67
- version: 1.1.4
65
+ version: 1.1.0
68
66
  version:
69
67
  - !ruby/object:Gem::Dependency
70
- name: ruby2ruby
68
+ name: hoe
71
69
  version_requirement:
72
70
  version_requirements: !ruby/object:Gem::Version::Requirement
73
71
  requirements:
74
72
  - - ">="
75
73
  - !ruby/object:Gem::Version
76
- version: 1.1.0
74
+ version: 1.1.7
77
75
  version:
data/lib/heckle/base.rb DELETED
@@ -1,352 +0,0 @@
1
- require 'parse_tree'
2
- require 'ruby2ruby'
3
-
4
- class String
5
- def to_class
6
- split(/::/).inject(Object) { |klass, name| klass.const_get(name) }
7
- end
8
- end
9
-
10
- module Heckle
11
- VERSION = '1.1.1'
12
-
13
- class Base < SexpProcessor
14
- MUTATABLE_NODES = [:if, :lit, :str, :true, :false, :while, :until]
15
-
16
- attr_accessor :klass_name, :method_name, :klass, :method, :mutatees, :original_tree,
17
- :mutation_count, :node_count, :failures, :count
18
-
19
- @@debug = false;
20
-
21
- def self.debug=(value)
22
- @@debug = value
23
- end
24
-
25
- def initialize(klass_name=nil, method_name=nil, reporter = Reporter.new)
26
- super()
27
-
28
- @klass_name, @method_name = klass_name, method_name.intern
29
- @klass = @method = nil
30
- @reporter = reporter
31
-
32
- self.strict = false
33
- self.auto_shift_type = true
34
- self.expected = Array
35
-
36
- @mutatees = Hash.new
37
- @mutation_count = Hash.new
38
- @node_count = Hash.new
39
- @count = 0
40
-
41
- MUTATABLE_NODES.each {|type| @mutatees[type] = [] }
42
-
43
- @failures = []
44
-
45
- @mutated = false
46
-
47
- grab_mutatees
48
-
49
- @original_tree = current_tree.deep_clone
50
- @original_mutatees = mutatees.deep_clone
51
- end
52
-
53
- ############################################################
54
- ### Overwrite test_pass? for your own Heckle runner.
55
- def tests_pass?
56
- raise NotImplementedError
57
- end
58
-
59
- def run_tests
60
- if tests_pass? then
61
- record_passing_mutation
62
- else
63
- @reporter.report_test_failures
64
- end
65
- end
66
-
67
- ############################################################
68
- ### Running the script
69
-
70
- def validate
71
- if mutations_left == 0
72
- @reporter.no_mutations(method_name)
73
- return
74
- end
75
-
76
- @reporter.method_loaded(klass_name, method_name, mutations_left)
77
-
78
- until mutations_left == 0
79
- @reporter.remaining_mutations(mutations_left)
80
- reset_tree
81
- begin
82
- process current_tree
83
- silence_stream(STDOUT) { run_tests }
84
- rescue SyntaxError => e
85
- puts "Mutation caused a syntax error: #{e.message}"
86
- end
87
- end
88
-
89
- reset # in case we're validating again. we should clean up.
90
-
91
- unless @failures.empty?
92
- @reporter.no_failures
93
- @failures.each do |failure|
94
- @reporter.failure(failure)
95
- end
96
- else
97
- @reporter.no_surviving_mutants
98
- end
99
- end
100
-
101
- def record_passing_mutation
102
- @failures << current_code
103
- end
104
-
105
- def heckle(exp)
106
- src = RubyToRuby.new.process(exp)
107
- @reporter.replacing(klass_name, method_name, src) if @@debug
108
- klass = klass_name.to_class
109
- self.count += 1
110
- new_name = "#{method_name}_#{count}"
111
-
112
- klass.send :undef_method, new_name rescue nil
113
- klass.send :alias_method, new_name, method_name
114
- klass.class_eval(src)
115
- end
116
-
117
- ############################################################
118
- ### Processing sexps
119
-
120
- def process_defn(exp)
121
- self.method = exp.shift
122
- result = [:defn, method]
123
- result << process(exp.shift) until exp.empty?
124
- heckle(result) if method == method_name
125
- @mutated = false
126
- reset_node_count
127
-
128
- return result
129
- end
130
-
131
- def process_lit(exp)
132
- mutate_node [:lit, exp.shift]
133
- end
134
-
135
- def mutate_lit(exp)
136
- case exp[1]
137
- when Fixnum, Float, Bignum
138
- [:lit, exp[1] + rand_number]
139
- when Symbol
140
- [:lit, rand_symbol]
141
- when Regexp
142
- [:lit, /#{Regexp.escape(rand_string)}/]
143
- when Range
144
- [:lit, rand_range]
145
- end
146
- end
147
-
148
- def process_str(exp)
149
- mutate_node [:str, exp.shift]
150
- end
151
-
152
- def mutate_str(node)
153
- [:str, rand_string]
154
- end
155
-
156
- def process_if(exp)
157
- mutate_node [:if, process(exp.shift), process(exp.shift), process(exp.shift)]
158
- end
159
-
160
- def mutate_if(node)
161
- [:if, node[1], node[3], node[2]]
162
- end
163
-
164
- def process_true(exp)
165
- mutate_node [:true]
166
- end
167
-
168
- def mutate_true(node)
169
- [:false]
170
- end
171
-
172
- def process_false(exp)
173
- mutate_node [:false]
174
- end
175
-
176
- def mutate_false(node)
177
- [:true]
178
- end
179
-
180
- def process_while(exp)
181
- cond, body, head_controlled = grab_conditional_loop_parts(exp)
182
- mutate_node [:while, cond, body, head_controlled]
183
- end
184
-
185
- def mutate_while(node)
186
- [:until, node[1], node[2], node[3]]
187
- end
188
-
189
- def process_until(exp)
190
- cond, body, head_controlled = grab_conditional_loop_parts(exp)
191
- mutate_node [:until, cond, body, head_controlled]
192
- end
193
-
194
- def mutate_until(node)
195
- [:while, node[1], node[2], node[3]]
196
- end
197
-
198
- def mutate_node(node)
199
- raise UnsupportedNodeError unless respond_to? "mutate_#{node.first}"
200
- increment_node_count node
201
- if should_heckle? node
202
- increment_mutation_count node
203
- return send("mutate_#{node.first}", node)
204
- else
205
- node
206
- end
207
- end
208
-
209
- ############################################################
210
- ### Tree operations
211
-
212
- def walk_and_push(node)
213
- return unless node.respond_to? :each
214
- return if node.is_a? String
215
- node.each { |child| walk_and_push(child) }
216
- if MUTATABLE_NODES.include? node.first
217
- @mutatees[node.first.to_sym].push(node)
218
- mutation_count[node] = 0
219
- end
220
- end
221
-
222
- def grab_mutatees
223
- walk_and_push(current_tree)
224
- end
225
-
226
- def current_tree
227
- ParseTree.translate(klass_name.to_class, method_name)
228
- end
229
-
230
- def reset
231
- reset_tree
232
- reset_mutatees
233
- reset_mutation_count
234
- end
235
-
236
- def reset_tree
237
- return unless original_tree != current_tree
238
- @mutated = false
239
-
240
- klass = klass_name.to_class
241
-
242
- self.count += 1
243
- new_name = "#{method_name}_#{count}"
244
- klass.send :undef_method, new_name rescue nil
245
- klass.send :alias_method, new_name, method_name
246
- klass.send :alias_method, method_name, "#{method_name}_1"
247
- end
248
-
249
- def reset_mutatees
250
- @mutatees = @original_mutatees.deep_clone
251
- end
252
-
253
- def reset_mutation_count
254
- mutation_count.each {|k,v| mutation_count[k] = 0}
255
- end
256
-
257
- def reset_node_count
258
- node_count.each {|k,v| node_count[k] = 0}
259
- end
260
-
261
- def increment_node_count(node)
262
- if node_count[node].nil?
263
- node_count[node] = 1
264
- else
265
- node_count[node] += 1
266
- end
267
- end
268
-
269
- def increment_mutation_count(node)
270
- # So we don't re-mutate this later if the tree is reset
271
- mutation_count[node] += 1
272
- @mutatees[node.first].delete_at(@mutatees[node.first].index(node))
273
- @mutated = true
274
- end
275
-
276
- ############################################################
277
- ### Convenience methods
278
-
279
- def should_heckle?(exp)
280
- return false unless method == method_name
281
- mutation_count[exp] = 0 if mutation_count[exp].nil?
282
- return false if node_count[exp] <= mutation_count[exp]
283
- mutatees[exp.first.to_sym].include?(exp) && !already_mutated?
284
- end
285
-
286
- def grab_conditional_loop_parts(exp)
287
- cond = process(exp.shift)
288
- body = process(exp.shift)
289
- head_controlled = exp.shift
290
- return cond, body, head_controlled
291
- end
292
-
293
- def already_mutated?
294
- @mutated
295
- end
296
-
297
- def mutations_left
298
- sum = 0
299
- @mutatees.each {|mut| sum += mut.last.size }
300
- sum
301
- end
302
-
303
- def current_code
304
- RubyToRuby.translate(klass_name.to_class, method_name)
305
- end
306
-
307
- def rand_number
308
- (rand(10) + 1)*((-1)**rand(2))
309
- end
310
-
311
- def rand_string
312
- size = rand(50)
313
- str = ""
314
- size.times { str << rand(126).chr }
315
- str
316
- end
317
-
318
- def rand_symbol
319
- letters = ('a'..'z').to_a + ('A'..'Z').to_a
320
- str = ""
321
- (rand(50) + 1).times { str << letters[rand(letters.size)] }
322
- :"#{str}"
323
- end
324
-
325
- def rand_range
326
- min = rand(50)
327
- max = min + rand(50)
328
- min..max
329
- end
330
-
331
- # silence_stream taken from Rails ActiveSupport reporting.rb
332
-
333
- # Silences any stream for the duration of the block.
334
- #
335
- # silence_stream(STDOUT) do
336
- # puts 'This will never be seen'
337
- # end
338
- #
339
- # puts 'But this will'
340
- def silence_stream(stream)
341
- unless @@debug
342
- old_stream = stream.dup
343
- stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
344
- stream.sync = true
345
- end
346
- yield
347
- ensure
348
- stream.reopen(old_stream) unless @@debug
349
- end
350
-
351
- end
352
- end
@@ -1,43 +0,0 @@
1
- module Heckle
2
- class Reporter
3
- def no_mutations(method_name)
4
- puts
5
- puts "!"*70
6
- puts "!!! #{method_name} has a thick skin. There's nothing to heckle."
7
- puts "!"*70
8
- puts
9
- end
10
-
11
- def method_loaded(klass_name, method_name, mutations_left)
12
- puts
13
- puts "*"*70
14
- puts "*** #{klass_name}\##{method_name} loaded with #{mutations_left} possible mutations"
15
- puts "*"*70
16
- puts
17
- end
18
-
19
- def remaining_mutations(mutations_left)
20
- puts "#{mutations_left} mutations remaining..."
21
- end
22
-
23
- def no_failures
24
- puts "\nThe following mutations didn't cause test failures:\n"
25
- end
26
-
27
- def failure(failure)
28
- puts "\n#{failure}\n"
29
- end
30
-
31
- def no_surviving_mutants
32
- puts "No mutants survived. Cool!\n\n"
33
- end
34
-
35
- def replacing(klass_name, method_name, src)
36
- puts "Replacing #{klass_name}##{method_name} with:\n\n#{src}\n"
37
- end
38
-
39
- def report_test_failures
40
- puts "Tests failed -- this is good"
41
- end
42
- end
43
- end