heckle-2.0.0.b1/0000755000175000017500000000000012254560366012634 5ustar boutilboutilheckle-2.0.0.b1/.gemtest0000644000175000017500000000000012254560366014273 0ustar boutilboutilheckle-2.0.0.b1/Rakefile0000444000175000017500000000146012254560366014300 0ustar boutilboutil# -*- ruby -*- require 'rubygems' require 'hoe' Hoe.add_include_dirs("../../RubyInline/dev/lib", "../../ruby2ruby/1.3.1/lib", "../../ZenTest/dev/lib", "../../sexp_processor/dev/lib", "../../ruby_parser/2.3.1/lib", "lib") Hoe.plugin :seattlerb Hoe.spec 'heckle' do developer 'Ryan Davis', 'ryand-ruby@zenspider.com' developer 'Pete Higgins', 'pete@peterhiggins.org' # developer 'Eric Hodel', 'drbrain@segment7.net' # developer 'Kevin Clark', 'kevin.clark@gmail.com' clean_globs << File.expand_path("~/.ruby_inline") dependency 'ruby_parser', '~> 2.3.1' dependency 'ruby2ruby', '~> 1.3.0' dependency 'ZenTest', '~> 4.7.0' self.test_globs = ["test/test_*.rb"] end # vim: syntax=ruby heckle-2.0.0.b1/data.tar.gz.sig0000644000175000017500000000040012254560366015447 0ustar boutilboutiliǮk-y [F#avVjumYW8e}w'JɃ$ RۅAV/D6&`6x*1_P VxAON +xVyidX-RojC;-dwA6ҭS{dlmgK FR N6@ɑᣴiXKq /_4mLa 9S$}$sX&USDuBeb5eb?!HLZXheckle-2.0.0.b1/metadata.yml0000644000175000017500000001305712254560366015145 0ustar boutilboutil--- !ruby/object:Gem::Specification name: heckle version: !ruby/object:Gem::Version hash: -1277703938 prerelease: 6 segments: - 2 - 0 - 0 - b - 1 version: 2.0.0.b1 platform: ruby authors: - Ryan Davis - Pete Higgins autorequire: bindir: bin cert_chain: - | -----BEGIN CERTIFICATE----- MIIDPjCCAiagAwIBAgIBADANBgkqhkiG9w0BAQUFADBFMRMwEQYDVQQDDApyeWFu ZC1ydWJ5MRkwFwYKCZImiZPyLGQBGRYJemVuc3BpZGVyMRMwEQYKCZImiZPyLGQB GRYDY29tMB4XDTA5MDMwNjE4NTMxNVoXDTEwMDMwNjE4NTMxNVowRTETMBEGA1UE AwwKcnlhbmQtcnVieTEZMBcGCgmSJomT8ixkARkWCXplbnNwaWRlcjETMBEGCgmS JomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALda b9DCgK+627gPJkB6XfjZ1itoOQvpqH1EXScSaba9/S2VF22VYQbXU1xQXL/WzCkx taCPaLmfYIaFcHHCSY4hYDJijRQkLxPeB3xbOfzfLoBDbjvx5JxgJxUjmGa7xhcT oOvjtt5P8+GSK9zLzxQP0gVLS/D0FmoE44XuDr3iQkVS2ujU5zZL84mMNqNB1znh GiadM9GHRaDiaxuX0cIUBj19T01mVE2iymf9I6bEsiayK/n6QujtyCbTWsAS9Rqt qhtV7HJxNKuPj/JFH0D2cswvzznE/a5FOYO68g+YCuFi5L8wZuuM8zzdwjrWHqSV gBEfoTEGr7Zii72cx+sCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAw HQYDVR0OBBYEFEfFe9md/r/tj/Wmwpy+MI8d9k/hMA0GCSqGSIb3DQEBBQUAA4IB AQAY59gYvDxqSqgC92nAP9P8dnGgfZgLxP237xS6XxFGJSghdz/nI6pusfCWKM8m vzjjH2wUMSSf3tNudQ3rCGLf2epkcU13/rguI88wO6MrE0wi4ZqLQX+eZQFskJb/ w6x9W1ur8eR01s397LSMexySDBrJOh34cm2AlfKr/jokKCTwcM0OvVZnAutaovC0 l1SVZ0ecg88bsWHA0Yhh7NFxK1utWoIhtB6AFC/+trM0FQEB/jZkIS8SaNzn96Rl n0sZEf77FLf5peR8TP/PtmIg7Cyqz23sLM4mCOoTGIy5OcZ8TdyiyINUHtb5ej/T FBHgymkyj/AOSqKRIpXPhjC6 -----END CERTIFICATE----- date: 2012-10-03 00:00:00 Z dependencies: - !ruby/object:Gem::Dependency name: ruby_parser prerelease: false requirement: &id001 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 1 segments: - 2 - 3 - 1 version: 2.3.1 type: :runtime version_requirements: *id001 - !ruby/object:Gem::Dependency name: ruby2ruby prerelease: false requirement: &id002 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 27 segments: - 1 - 3 - 0 version: 1.3.0 type: :runtime version_requirements: *id002 - !ruby/object:Gem::Dependency name: ZenTest prerelease: false requirement: &id003 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 35 segments: - 4 - 7 - 0 version: 4.7.0 type: :runtime version_requirements: *id003 - !ruby/object:Gem::Dependency name: minitest prerelease: false requirement: &id004 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 1 segments: - 3 - 3 version: "3.3" type: :development version_requirements: *id004 - !ruby/object:Gem::Dependency name: rdoc prerelease: false requirement: &id005 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 19 segments: - 3 - 10 version: "3.10" type: :development version_requirements: *id005 - !ruby/object:Gem::Dependency name: hoe prerelease: false requirement: &id006 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 7 segments: - 3 - 0 version: "3.0" type: :development version_requirements: *id006 description: |- Heckle is unit test sadism(tm) at its core. Heckle is a mutation tester. It modifies your code and runs your tests to make sure they fail. The idea is that if code can be changed and your tests don't notice, either that code isn't being covered or it doesn't do anything. It's like hiring a white-hat hacker to try to break into your server and making sure you detect it. You learn the most by trying to break things and watching the outcome in an act of unit test sadism. email: - ryand-ruby@zenspider.com - pete@peterhiggins.org executables: - heckle extensions: [] extra_rdoc_files: - History.txt - Manifest.txt - README.txt files: - .autotest - History.txt - Manifest.txt - README.txt - Rakefile - bin/heckle - lib/autotest/heckle.rb - lib/heckle.rb - lib/heckle_runner.rb - lib/minitest_heckler.rb - lib/test_unit_heckler.rb - sample/Rakefile - sample/changes.log - sample/test/test_heckled.rb - test/fixtures/heckle_dummy.rb - test/fixtures/heckled.rb - test/fixtures/minitest_project/README.txt - test/fixtures/minitest_project/Rakefile - test/fixtures/minitest_project/lib/doubler.rb - test/fixtures/minitest_project/test/test_doubler_with_a_number.rb - test/fixtures/minitest_project/test/test_doubler_with_bad_input.rb - test/test_heckle.rb - test/test_heckle_runner.rb - .gemtest homepage: http://ruby.sadi.st/Heckle.html licenses: [] post_install_message: rdoc_options: - --main - README.txt require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ">" - !ruby/object:Gem::Version hash: 25 segments: - 1 - 3 - 1 version: 1.3.1 requirements: [] rubyforge_project: heckle rubygems_version: 1.8.24 signing_key: specification_version: 3 summary: Heckle is unit test sadism(tm) at its core test_files: - test/test_heckle.rb - test/test_heckle_runner.rb heckle-2.0.0.b1/test/0000755000175000017500000000000012254560366013613 5ustar boutilboutilheckle-2.0.0.b1/test/test_heckle_runner.rb0000444000175000017500000000251212254560366020021 0ustar boutilboutilrequire 'minitest/autorun' require 'heckle_runner' # Tests needed: # * if no method, loads all local methods # * should raise an exception if the class/module can't be found # * should raise an exception if the method can't be found # * Tests for option parsing. class TestHeckleRunnerRun < MiniTest::Unit::TestCase @@dummy_dir = File.expand_path('test/fixtures/minitest_project') dummy_lib = File.join(@@dummy_dir, 'lib') $LOAD_PATH << dummy_lib def setup super @old_pwd = Dir.pwd Dir.chdir @@dummy_dir # See MiniTest's test/minitest/metametameta.rb @output = StringIO.new("") MiniTest::Unit::TestCase.reset MiniTest::Unit.output = @output end def teardown super Dir.chdir @old_pwd MiniTest::Unit.output = $stdout MiniTest::Unit::TestCase.test_suites.each do |test| Object.send :remove_const, test.to_s.to_sym end end def test_run_with_full_coverage out, _ = capture_io do HeckleRunner.run %w[Doubler double] end assert_match %r{No mutants survived.}, out end def test_run_with_partial_coverage out, _ = capture_io do HeckleRunner.run %w[Doubler double --tests test/test_doubler_with_a_number.rb] end assert_match %r{The following mutations didn't cause test failures:}, out refute_match %{No mutants survived.}, out end end heckle-2.0.0.b1/test/fixtures/0000755000175000017500000000000012254560366015464 5ustar boutilboutilheckle-2.0.0.b1/test/fixtures/heckled.rb0000444000175000017500000000372612254560366017416 0ustar boutilboutilclass Heckled attr_accessor :names def initialize @names = [] end def uses_call some_func + some_other_func end def uses_callblock x.y { 1 } end def uses_cvasgn @@cvar = 5 @@cvar = nil end def uses_dasgn loop do |dvar| loop do dvar = 5 dvar = nil end end end def uses_dasgncurr loop do |dvar| dvar = 5 dvar = nil end end def uses_iasgn @ivar = 5 @ivar = nil end def uses_gasgn $gvar = 5 $gvar = nil end def uses_lasgn lvar = 5 lvar = nil end def uses_masgn @a, $b, c = 5, 6, 7 end def uses_many_things i = 1 while i < 10 i += 1 until some_func some_other_func end return true if "hi there" == "changeling" return false end i end def uses_while while some_func some_other_func end end def uses_until until some_func some_other_func end end def uses_numeric_literals i = 1 i += 2147483648 i -= 3.5 end def uses_strings @names << "Hello, Robert" @names << "Hello, Jeff" @names << "Hi, Frank" end def uses_different_types i = 1 b = "Hello, Joe" c = 3.3 end def uses_literal i = 1 end def uses_same_literal i = 1 i = 1 i = 1 end def uses_if if some_func if some_other_func return end end end def uses_boolean a = true b = false end def uses_unless unless true if false return end end end def uses_symbols i = :blah i = :blah i = :and_blah end def uses_regexes i = /a.*/ i = /c{2,4}+/ i = /123/ end def uses_ranges i = 6..100 i = -1..9 i = 1..4 end def uses_nothing end def uses_iter x = [ 1, 2, 3 ] x.each { |y| y } end def self.is_a_klass_method? true end private def some_func; end def some_other_func; end end heckle-2.0.0.b1/test/fixtures/minitest_project/0000755000175000017500000000000012254560366021046 5ustar boutilboutilheckle-2.0.0.b1/test/fixtures/minitest_project/Rakefile0000444000175000017500000000053212254560366022511 0ustar boutilboutilrequire 'rake/testtask' Rake::TestTask.new namespace :heckle do desc "Run heckle with all tests." task :pass do puts `../../../bin/heckle Doubler double` end desc "Run heckle with some test." task :fail do puts `../../../bin/heckle Doubler double --tests test/test_doubler_with_a_number.rb` end end task :default => :test heckle-2.0.0.b1/test/fixtures/minitest_project/test/0000755000175000017500000000000012254560366022025 5ustar boutilboutilheckle-2.0.0.b1/test/fixtures/minitest_project/test/test_doubler_with_a_number.rb0000444000175000017500000000034512254560366027750 0ustar boutilboutilrequire "minitest/autorun" require "doubler" class TestDoublerWithANumber < MiniTest::Unit::TestCase def setup @doubler = Doubler.new end def test_double_with_a_number assert_equal 4, @doubler.double(2) end end heckle-2.0.0.b1/test/fixtures/minitest_project/test/test_doubler_with_bad_input.rb0000444000175000017500000000047512254560366030131 0ustar boutilboutilrequire "minitest/autorun" require "doubler" class TestDoublerWithBadInput < MiniTest::Unit::TestCase def setup @doubler = Doubler.new end def test_doubler_with_a_string assert_equal "NaN", @doubler.double("2") end def test_doubler_with_nil assert_equal "NaN", @doubler.double(nil) end end heckle-2.0.0.b1/test/fixtures/minitest_project/lib/0000755000175000017500000000000012254560366021614 5ustar boutilboutilheckle-2.0.0.b1/test/fixtures/minitest_project/lib/doubler.rb0000444000175000017500000000014512254560366023573 0ustar boutilboutilclass Doubler def double x if Numeric === x x * 2 else "NaN" end end end heckle-2.0.0.b1/test/fixtures/minitest_project/README.txt0000444000175000017500000000276712254560366022556 0ustar boutilboutil= minitest_project * FIX (url) == DESCRIPTION: FIX (describe your package) == FEATURES/PROBLEMS: * FIX (list of features or problems) == SYNOPSIS: FIX (code sample of usage) == REQUIREMENTS: * FIX (list of requirements) == INSTALL: * FIX (sudo gem install, anything else) == DEVELOPERS: After checking out the source, run: $ rake newb This task will install any missing dependencies, run the tests/specs, and generate the RDoc. == LICENSE: (The MIT License) Copyright (c) 2012 FIX Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. heckle-2.0.0.b1/test/fixtures/heckle_dummy.rb0000444000175000017500000000461512254560366020463 0ustar boutilboutilclass HeckleDummy attr_accessor :names def initialize @names = [] end def uses_call some_func + some_other_func end def uses_callblock x.y { 1 } end def uses_cvasgn @@cvar = 5 @@cvar = nil end def uses_dasgn loop do |dvar| loop do dvar = 5 dvar = nil end end end def uses_dasgncurr loop do |dvar| dvar = 5 dvar = nil end end def uses_iasgn @ivar = 5 @ivar = nil end def uses_gasgn $gvar = 5 $gvar = nil end def uses_lasgn lvar = 5 lvar = nil end def uses_masgn @a, $b, c = 5, 6, 7 end def uses_many_things i = 1 while i < 10 i += 1 until some_func some_other_func end return true if "hi there" == "changeling" return false end i end def uses_while while some_func some_other_func end end def uses_until until some_func some_other_func end end def uses_numeric_literals i = 1 i += 2147483648 i -= 3.5 end def uses_strings @names << "Hello, Robert" @names << "Hello, Jeff" @names << "Hi, Frank" end def uses_different_types i = 1 b = "Hello, Joe" c = 3.3 end def uses_literal i = 1 end def uses_same_literal i = 1 i = 1 i = 1 end def uses_if if some_func if some_other_func return end end end def uses_boolean a = true b = false end def uses_unless unless true if false return end end end def uses_symbols i = :blah i = :blah i = :and_blah end def uses_regexes i = /a.*/ i = /c{2,4}+/ i = /123/ end def uses_ranges i = 6..100 i = -1..9 i = 1..4 end def uses_nothing end def uses_iter x = [ 1, 2, 3 ] x.each { |y| y } end def self.is_a_klass_method? true end module OuterNesting def foo -1 end module InnerNesting def foo -2 end class InnerClass def bar -1 end end end module InnerNesting def foo -3 end class InnerClass module DecoyNesting def foo -4 end end def foo 1337 end end end end private def some_func; end def some_other_func; end end heckle-2.0.0.b1/test/test_heckle.rb0000444000175000017500000006574712254560366016453 0ustar boutilboutilrequire 'fixtures/heckle_dummy' require 'heckle' class TestHeckler < Heckle def rand(*args) 5 end def rand_string "l33t h4x0r" end def rand_number(*args) 5 end def rand_symbol :"l33t h4x0r" end # HAX def expand_dirs_to_files(*) super('test/fixtures/heckle_dummy.rb') end end class HeckleTestCase < MiniTest::Unit::TestCase def setup @klass ||= "HeckleDummy" @nodes ||= Heckle::MUTATABLE_NODES @method_heckled ||= 'uses_many_things' @heckler = TestHeckler.new(@klass, @method_heckled, @nodes) end def teardown @heckler.reset if defined?(@heckler) && @heckler end def assert_mutations expected, heckle initial = heckle.current_tree.deep_clone mutations = [] begin heckle.process(heckle.current_tree) mutant = heckle.current_tree mutations << mutant heckle.reset_tree end until initial == mutant mutations.delete(initial) # HAX: Sorting an array of Sexps blows up in some cases. assert_equal expected.map {|sexp| sexp.to_s }.sort, mutations.map {|sexp| sexp.to_s }.sort, [ "expected(#{expected.size}):", (expected - mutations).map {|m| m.pretty_inspect}, "mutations(#{mutations.size}):", (mutations - expected).map {|m| m.pretty_inspect} ].join("\n") end end class TestHeckle < HeckleTestCase def test_should_set_original_tree expected = s(:defn, :uses_many_things, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 1)), s(:while, s(:call, s(:lvar, :i), :<, s(:arglist, s(:lit, 10))), s(:block, s(:lasgn, :i, s(:call, s(:lvar, :i), :+, s(:arglist, s(:lit, 1)))), s(:until, s(:call, nil, :some_func, s(:arglist)), s(:call, nil, :some_other_func, s(:arglist)), true), s(:if, s(:call, s(:str, "hi there"), :==, s(:arglist, s(:str, "changeling"))), s(:return, s(:true)), nil), s(:return, s(:false))), true), s(:lvar, :i)))) assert_equal expected, @heckler.original_tree end def test_should_grab_mutatees_from_method # expected is from tree of uses_while expected = { :call => [s(:call, s(:lvar, :i), :<, s(:arglist, s(:lit, 10))), s(:call, s(:lvar, :i), :+, s(:arglist, s(:lit, 1))), s(:call, nil, :some_func, s(:arglist)), # FIX: why added? s(:call, nil, :some_other_func, s(:arglist)), # FIX: why added? s(:call, s(:str, "hi there"), :==, s(:arglist, s(:str, "changeling")))], :cvasgn => [], # no cvasgns here :dasgn => [], # no dasgns here :dasgn_curr => [], # no dasgn_currs here :iasgn => [], # no iasgns here :iter => [], :gasgn => [], # no gasgns here :lasgn => [s(:lasgn, :i, s(:lit, 1)), s(:lasgn, :i, s(:call, s(:lvar, :i), :+, s(:arglist, s(:lit, 1))))], :lit => [s(:lit, 1), s(:lit, 10), s(:lit, 1)], :if => [s(:if, s(:call, s(:str, "hi there"), :==, s(:arglist, s(:str, "changeling"))), s(:return, s(:true)), nil)], :str => [s(:str, "hi there"), s(:str, "changeling")], :true => [s(:true)], :false => [s(:false)], :while => [s(:while, s(:call, s(:lvar, :i), :<, s(:arglist, s(:lit, 10))), s(:block, s(:lasgn, :i, s(:call, s(:lvar, :i), :+, s(:arglist, s(:lit, 1)))), s(:until, s(:call, nil, :some_func, s(:arglist)), s(:call, nil, :some_other_func, s(:arglist)), true), s(:if, s(:call, s(:str, "hi there"), :==, s(:arglist, s(:str, "changeling"))), s(:return, s(:true)), nil), s(:return, s(:false))), true)], :until => [s(:until, s(:call, nil, :some_func, s(:arglist)), s(:call, nil, :some_other_func, s(:arglist)), true)], } assert_equal expected, @heckler.mutatees end def test_should_count_mutatees_left assert_equal 17, @heckler.mutations_left # FIX WHY?!? end def test_reset original_tree = @heckler.current_tree.deep_clone original_mutatees = @heckler.mutatees.deep_clone 3.times { @heckler.process(@heckler.current_tree) } refute_equal original_tree, @heckler.current_tree refute_equal original_mutatees, @heckler.mutatees @heckler.reset assert_equal original_tree[2], @heckler.current_tree[2] assert_equal original_mutatees, @heckler.mutatees end def test_reset_tree original_tree = @heckler.current_tree.deep_clone @heckler.process(@heckler.current_tree) refute_equal original_tree, @heckler.current_tree @heckler.reset_tree assert_equal original_tree, @heckler.current_tree end def test_reset_should_work_over_several_process_calls original_tree = @heckler.current_tree.deep_clone original_mutatees = @heckler.mutatees.deep_clone @heckler.process(@heckler.current_tree) refute_equal original_tree, @heckler.current_tree refute_equal original_mutatees, @heckler.mutatees @heckler.reset assert_equal original_tree, @heckler.current_tree assert_equal original_mutatees, @heckler.mutatees 3.times { @heckler.process(@heckler.current_tree) } refute_equal original_tree, @heckler.current_tree refute_equal original_mutatees, @heckler.mutatees @heckler.reset assert_equal original_tree, @heckler.current_tree assert_equal original_mutatees, @heckler.mutatees end def test_reset_mutatees original_mutatees = @heckler.mutatees.deep_clone @heckler.process(@heckler.current_tree) refute_equal original_mutatees, @heckler.mutatees @heckler.reset_mutatees assert_equal original_mutatees, @heckler.mutatees end end class TestHeckleNumericLiterals < HeckleTestCase def setup @method_heckled = "uses_numeric_literals" @nodes = s(:lit, :str) super end def test_numeric_literals_original_tree expected = s(:defn, :uses_numeric_literals, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 1)), s(:lasgn, :i, s(:call, s(:lvar, :i), :+, s(:arglist, s(:lit, 2147483648)))), s(:lasgn, :i, s(:call, s(:lvar, :i), :-, s(:arglist, s(:lit, 3.5))))))) assert_equal expected, @heckler.current_tree end def test_numeric_literals_mutations expected = [ s(:defn, :uses_numeric_literals, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 6)), s(:lasgn, :i, s(:call, s(:lvar, :i), :+, s(:arglist, s(:lit, 2147483648)))), s(:lasgn, :i, s(:call, s(:lvar, :i), :-, s(:arglist, s(:lit, 3.5))))))), s(:defn, :uses_numeric_literals, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 1)), s(:lasgn, :i, s(:call, s(:lvar, :i), :+, s(:arglist, s(:lit, 2147483653)))), s(:lasgn, :i, s(:call, s(:lvar, :i), :-, s(:arglist, s(:lit, 3.5))))))), s(:defn, :uses_numeric_literals, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 1)), s(:lasgn, :i, s(:call, s(:lvar, :i), :+, s(:arglist, s(:lit, 2147483648)))), s(:lasgn, :i, s(:call, s(:lvar, :i), :-, s(:arglist, s(:lit, 8.5))))))), ] assert_mutations expected, @heckler end end class TestHeckleSymbols < HeckleTestCase def setup @method_heckled = "uses_symbols" @nodes = s(:lit, :str) super end def test_symbols_original_tree expected = s(:defn, :uses_symbols, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, :blah)), s(:lasgn, :i, s(:lit, :blah)), s(:lasgn, :i, s(:lit, :and_blah))))) assert_equal expected, @heckler.current_tree end def test_symbols_mutations expected = [ s(:defn, :uses_symbols, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, :"l33t h4x0r")), s(:lasgn, :i, s(:lit, :blah)), s(:lasgn, :i, s(:lit, :and_blah))))), s(:defn, :uses_symbols, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, :blah)), s(:lasgn, :i, s(:lit, :"l33t h4x0r")), s(:lasgn, :i, s(:lit, :and_blah))))), s(:defn, :uses_symbols, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, :blah)), s(:lasgn, :i, s(:lit, :blah)), s(:lasgn, :i, s(:lit, :"l33t h4x0r"))))), ] assert_mutations expected, @heckler end end class TestHeckleRegexes < HeckleTestCase def setup @method_heckled = "uses_regexes" @nodes = s(:lit, :str) super end def test_regexes_original_tree expected = s(:defn, :uses_regexes, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, /a.*/)), s(:lasgn, :i, s(:lit, /c{2,4}+/)), s(:lasgn, :i, s(:lit, /123/))))) assert_equal expected, @heckler.original_tree end def test_regexes_mutuations expected = [ s(:defn, :uses_regexes, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, /l33t\ h4x0r/)), s(:lasgn, :i, s(:lit, /c{2,4}+/)), s(:lasgn, :i, s(:lit, /123/))))), s(:defn, :uses_regexes, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, /a.*/)), s(:lasgn, :i, s(:lit, /l33t\ h4x0r/)), s(:lasgn, :i, s(:lit, /123/))))), s(:defn, :uses_regexes, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, /a.*/)), s(:lasgn, :i, s(:lit, /c{2,4}+/)), s(:lasgn, :i, s(:lit, /l33t\ h4x0r/))))), ] assert_mutations expected, @heckler end end class TestHeckleRanges < HeckleTestCase def setup @method_heckled = "uses_ranges" @nodes = s(:lit, :str) super end def test_ranges_original_tree expected = s(:defn, :uses_ranges, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 6..100)), s(:lasgn, :i, s(:lit, -1..9)), s(:lasgn, :i, s(:lit, 1..4))))) assert_equal expected, @heckler.current_tree end def test_ranges_mutations expected = [ s(:defn, :uses_ranges, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 5..10)), s(:lasgn, :i, s(:lit, -1..9)), s(:lasgn, :i, s(:lit, 1..4))))), s(:defn, :uses_ranges, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 6..100)), s(:lasgn, :i, s(:lit, 5..10)), s(:lasgn, :i, s(:lit, 1..4))))), s(:defn, :uses_ranges, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 6..100)), s(:lasgn, :i, s(:lit, -1..9)), s(:lasgn, :i, s(:lit, 5..10))))), ] assert_mutations expected, @heckler end end class TestHeckleSameLiteral < HeckleTestCase def setup @method_heckled = "uses_same_literal" @nodes = s(:lit, :str) super end def test_same_literal_original_tree expected = s(:defn, :uses_same_literal, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 1)), s(:lasgn, :i, s(:lit, 1)), s(:lasgn, :i, s(:lit, 1))))) assert_equal expected, @heckler.current_tree end def test_same_literal_mutations expected = [ s(:defn, :uses_same_literal, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 6)), s(:lasgn, :i, s(:lit, 1)), s(:lasgn, :i, s(:lit, 1))))), s(:defn, :uses_same_literal, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 1)), s(:lasgn, :i, s(:lit, 6)), s(:lasgn, :i, s(:lit, 1))))), s(:defn, :uses_same_literal, s(:args), s(:scope, s(:block, s(:lasgn, :i, s(:lit, 1)), s(:lasgn, :i, s(:lit, 1)), s(:lasgn, :i, s(:lit, 6))))), ] assert_mutations expected, @heckler end end class TestHeckleStrings < HeckleTestCase def setup @method_heckled = "uses_strings" @nodes = s(:lit, :str) super end def test_strings_original_tree expected = s(:defn, :uses_strings, s(:args), s(:scope, s(:block, s(:call, s(:ivar, :@names), :<<, s(:arglist, s(:str, "Hello, Robert"))), s(:call, s(:ivar, :@names), :<<, s(:arglist, s(:str, "Hello, Jeff"))), s(:call, s(:ivar, :@names), :<<, s(:arglist, s(:str, "Hi, Frank")))))) assert_equal expected, @heckler.current_tree end def test_strings_mutations expected = [ s(:defn, :uses_strings, s(:args), s(:scope, s(:block, s(:call, s(:ivar, :@names), :<<, s(:arglist, s(:str, "l33t h4x0r"))), s(:call, s(:ivar, :@names), :<<, s(:arglist, s(:str, "Hello, Jeff"))), s(:call, s(:ivar, :@names), :<<, s(:arglist, s(:str, "Hi, Frank")))))), s(:defn, :uses_strings, s(:args), s(:scope, s(:block, s(:call, s(:ivar, :@names), :<<, s(:arglist, s(:str, "Hello, Robert"))), s(:call, s(:ivar, :@names), :<<, s(:arglist, s(:str, "l33t h4x0r"))), s(:call, s(:ivar, :@names), :<<, s(:arglist, s(:str, "Hi, Frank")))))), s(:defn, :uses_strings, s(:args), s(:scope, s(:block, s(:call, s(:ivar, :@names), :<<, s(:arglist, s(:str, "Hello, Robert"))), s(:call, s(:ivar, :@names), :<<, s(:arglist, s(:str, "Hello, Jeff"))), s(:call, s(:ivar, :@names), :<<, s(:arglist, s(:str, "l33t h4x0r")))))), ] assert_mutations expected, @heckler end end class TestHeckleIf < HeckleTestCase def setup @method_heckled = "uses_if" @nodes = s(:if) super end def test_if_original_tree expected = s(:defn, :uses_if, s(:args), s(:scope, s(:block, s(:if, s(:call, nil, :some_func, s(:arglist)), s(:if, s(:call, nil, :some_other_func, s(:arglist)), s(:return), nil), nil)))) assert_equal expected, @heckler.current_tree end def test_if_mutations expected = [ s(:defn, :uses_if, s(:args), s(:scope, s(:block, s(:if, s(:call, nil, :some_func, s(:arglist)), nil, s(:if, s(:call, nil, :some_other_func, s(:arglist)), s(:return), nil))))), s(:defn, :uses_if, s(:args), s(:scope, s(:block, s(:if, s(:call, nil, :some_func, s(:arglist)), s(:if, s(:call, nil, :some_other_func, s(:arglist)), nil, s(:return)), nil)))) ] assert_mutations expected, @heckler end end class TestHeckleBoolean < HeckleTestCase def setup @method_heckled = "uses_boolean" @nodes = s(:true, :false) super end def test_boolean_original_tree expected = s(:defn, :uses_boolean, s(:args), s(:scope, s(:block, s(:lasgn, :a, s(:true)), s(:lasgn, :b, s(:false))))) assert_equal expected, @heckler.current_tree end def test_boolean_mutations expected = [ s(:defn, :uses_boolean, s(:args), s(:scope, s(:block, s(:lasgn, :a, s(:false)), s(:lasgn, :b, s(:false))))), s(:defn, :uses_boolean, s(:args), s(:scope, s(:block, s(:lasgn, :a, s(:true)), s(:lasgn, :b, s(:true))))), ] assert_mutations expected, @heckler end end class TestHeckleWhile < HeckleTestCase def setup @method_heckled = "uses_while" @nodes = s(:while) super end def test_while_original_tree expected = s(:defn, :uses_while, s(:args), s(:scope, s(:block, s(:while, s(:call, nil, :some_func, s(:arglist)), s(:call, nil, :some_other_func, s(:arglist)), true)))) assert_equal expected, @heckler.current_tree end def test_while_mutations expected = [ s(:defn, :uses_while, s(:args), s(:scope, s(:block, s(:until, s(:call, nil, :some_func, s(:arglist)), s(:call, nil, :some_other_func, s(:arglist)), true))))] assert_mutations expected, @heckler end end class TestHeckleUntil < HeckleTestCase def setup @method_heckled = "uses_until" @nodes = s(:until) super end def test_until_original_tree expected = s(:defn, :uses_until, s(:args), s(:scope, s(:block, s(:until, s(:call, nil, :some_func, s(:arglist)), s(:call, nil, :some_other_func, s(:arglist)), true)))) assert_equal expected, @heckler.current_tree end def test_until_mutations expected = [ s(:defn, :uses_until, s(:args), s(:scope, s(:block, s(:while, s(:call, nil, :some_func, s(:arglist)), s(:call, nil, :some_other_func, s(:arglist)), true))))] assert_mutations expected, @heckler end end class TestHeckleCall < HeckleTestCase def setup @method_heckled = "uses_call" super end def test_call_original_tree expected = s(:defn, :uses_call, s(:args), s(:scope, s(:block, s(:call, s(:call, nil, :some_func, s(:arglist)), :+, s(:arglist, s(:call, nil, :some_other_func, s(:arglist))))))) assert_equal expected, @heckler.current_tree end def test_call_mutations expected = [ s(:defn, :uses_call, s(:args), s(:scope, s(:block, s(:call, s(:call, nil, :some_func, s(:arglist)), :+, s(:arglist, s(:nil)))))), s(:defn, :uses_call, s(:args), s(:scope, s(:block, s(:call, s(:nil), :+, s(:arglist, s(:call, nil, :some_other_func, s(:arglist))))))), s(:defn, :uses_call, s(:args), s(:scope, s(:block, s(:nil)))), ] assert_mutations expected, @heckler end end class TestHeckleCallblock < HeckleTestCase def setup @method_heckled = "uses_callblock" @nodes = s(:call) super end def test_callblock_original_tree expected = s(:defn, :uses_callblock, s(:args), s(:scope, s(:block, s(:iter, s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist)), nil, s(:lit, 1))))) assert_equal expected, @heckler.current_tree end def test_callblock_mutations expected = [ s(:defn, :uses_callblock, s(:args), s(:scope, s(:block, s(:iter, s(:call, s(:nil), :y, s(:arglist)), nil, s(:lit, 1))))) ] assert_mutations expected, @heckler end end class TestHeckleClassMethod < HeckleTestCase def setup @method_heckled = "self.is_a_klass_method?" @nodes = s(:true) super end def test_class_method_original_tree expected = s(:defs, s(:self), :is_a_klass_method?, s(:args), s(:scope, s(:block, s(:true)))) assert_equal expected, @heckler.current_tree end def test_class_methods_mutations expected = [ s(:defs, s(:self), :is_a_klass_method?, s(:args), s(:scope, s(:block, s(:false)))) ] assert_mutations expected, @heckler end end class TestHeckleCvasgn < HeckleTestCase def setup @method_heckled = "uses_cvasgn" @nodes = s(:cvasgn) super end def test_cvasgn_original_tree expected = s(:defn, :uses_cvasgn, s(:args), s(:scope, s(:block, s(:cvasgn, :@@cvar, s(:lit, 5)), s(:cvasgn, :@@cvar, s(:nil))))) assert_equal expected, @heckler.current_tree end def test_cvasgn_mutations expected = [ s(:defn, :uses_cvasgn, s(:args), s(:scope, s(:block, s(:cvasgn, :@@cvar, s(:lit, 5)), s(:cvasgn, :@@cvar, s(:lit, 42))))), s(:defn, :uses_cvasgn, s(:args), s(:scope, s(:block, s(:cvasgn, :@@cvar, s(:nil)), s(:cvasgn, :@@cvar, s(:nil))))), ] assert_mutations expected, @heckler end end class TestHeckleIasgn < HeckleTestCase def setup @method_heckled = "uses_iasgn" @nodes = s(:iasgn) super end def test_iasgn_original_tree expected = s(:defn, :uses_iasgn, s(:args), s(:scope, s(:block, s(:iasgn, :@ivar, s(:lit, 5)), s(:iasgn, :@ivar, s(:nil))))) assert_equal expected, @heckler.current_tree end def test_iasgn_mutations expected = [ s(:defn, :uses_iasgn, s(:args), s(:scope, s(:block, s(:iasgn, :@ivar, s(:lit, 5)), s(:iasgn, :@ivar, s(:lit, 42))))), s(:defn, :uses_iasgn, s(:args), s(:scope, s(:block, s(:iasgn, :@ivar, s(:nil)), s(:iasgn, :@ivar, s(:nil))))), ] assert_mutations expected, @heckler end end class TestHeckleGasgn < HeckleTestCase def setup @method_heckled = "uses_gasgn" s(:defn, :uses_cvasgn, s(:args), s(:scope, s(:block, s(:cvasgn, :@@cvar, s(:lit, 5)), s(:cvasgn, :@@cvar, s(:lit, 42))))) @nodes = s(:gasgn) super end def test_gasgn_original_tree expected = s(:defn, :uses_gasgn, s(:args), s(:scope, s(:block, s(:gasgn, :$gvar, s(:lit, 5)), s(:gasgn, :$gvar, s(:nil))))) assert_equal expected, @heckler.current_tree end def test_gasgn_mutations expected = [ s(:defn, :uses_gasgn, s(:args), s(:scope, s(:block, s(:gasgn, :$gvar, s(:lit, 5)), s(:gasgn, :$gvar, s(:lit, 42))))), s(:defn, :uses_gasgn, s(:args), s(:scope, s(:block, s(:gasgn, :$gvar, s(:nil)), s(:gasgn, :$gvar, s(:nil))))), ] assert_mutations expected, @heckler end end class TestHeckleLasgn < HeckleTestCase def setup @method_heckled = "uses_lasgn" @nodes = s(:lasgn) super end def test_lasgn_original_tree expected = s(:defn, :uses_lasgn, s(:args), s(:scope, s(:block, s(:lasgn, :lvar, s(:lit, 5)), s(:lasgn, :lvar, s(:nil))))) assert_equal expected, @heckler.current_tree end def test_lasgn_mutations expected = [ s(:defn, :uses_lasgn, s(:args), s(:scope, s(:block, s(:lasgn, :lvar, s(:nil)), s(:lasgn, :lvar, s(:nil))))), s(:defn, :uses_lasgn, s(:args), s(:scope, s(:block, s(:lasgn, :lvar, s(:lit, 5)), s(:lasgn, :lvar, s(:lit, 42))))), ] assert_mutations expected, @heckler end end class TestHeckleMasgn < HeckleTestCase def setup @method_heckled = "uses_masgn" @nodes = s(:dasgn, :dasgn_curr, :iasgn, :gasgn, :lasgn) super end def test_masgn_original_tree expected = s(:defn, :uses_masgn, s(:args), s(:scope, s(:block, s(:masgn, s(:array, s(:iasgn, :@a), s(:gasgn, :$b), s(:lasgn, :c)), s(:array, s(:lit, 5), s(:lit, 6), s(:lit, 7)))))) assert_equal expected, @heckler.current_tree end def test_masgn_mutations expected = [ s(:defn, :uses_masgn, s(:args), s(:scope, s(:block, s(:masgn, s(:array, s(:iasgn, :_heckle_dummy), s(:gasgn, :$b), s(:lasgn, :c)), s(:array, s(:lit, 5), s(:lit, 6), s(:lit, 7)))))), s(:defn, :uses_masgn, s(:args), s(:scope, s(:block, s(:masgn, s(:array, s(:iasgn, :@a), s(:gasgn, :_heckle_dummy), s(:lasgn, :c)), s(:array, s(:lit, 5), s(:lit, 6), s(:lit, 7)))))), s(:defn, :uses_masgn, s(:args), s(:scope, s(:block, s(:masgn, s(:array,s(:iasgn, :@a), s(:gasgn, :$b), s(:lasgn, :_heckle_dummy)), s(:array, s(:lit, 5), s(:lit, 6), s(:lit, 7)))))), ] assert_mutations expected, @heckler end end class TestHeckleIter < HeckleTestCase def setup @method_heckled = "uses_iter" @nodes = [ :call, :lasgn ] super end def test_iter_original_tree expected = s(:defn, :uses_iter, s(:args), s(:scope, s(:block, s(:lasgn, :x, s(:array, s(:lit, 1), s(:lit, 2), s(:lit, 3))), s(:iter, s(:call, s(:lvar, :x), :each, s(:arglist)), s(:lasgn, :y), s(:lvar, :y))))) assert_equal expected, @heckler.current_tree end def test_iter_mutations expected = [ s(:defn, :uses_iter, s(:args), s(:scope, s(:block, s(:lasgn, :x, s(:nil)), s(:iter, s(:call, s(:lvar, :x), :each, s(:arglist)), s(:lasgn, :y), s(:lvar, :y))))), s(:defn, :uses_iter, s(:args), s(:scope, s(:block, s(:lasgn, :x, s(:array, s(:lit, 1), s(:lit, 2), s(:lit, 3))), s(:iter, s(:call, s(:lvar, :x), :each, s(:arglist)), s(:lasgn, :_heckle_dummy), s(:lvar, :y))))), ] assert_mutations expected, @heckler end end class TestHeckleFindsNestedClassAndModule < HeckleTestCase def setup @klass = "HeckleDummy::OuterNesting::InnerNesting::InnerClass" @method_heckled = "foo" @nodes = [] super end def test_nested_class_and_module_original_tree expected = s(:defn, :foo, s(:args), s(:scope, s(:block, s(:lit, 1337)))) assert_equal expected, @heckler.current_tree end end heckle-2.0.0.b1/.autotest0000444000175000017500000000111212254560366014476 0ustar boutilboutil# -*- ruby -*- require 'autotest/restart' Autotest.add_hook :initialize do |at| at.libs << ":../../ParseTree/dev/lib" at.libs << ":../../ParseTree/dev/test" at.libs << ":../../RubyInline/dev/lib" at.libs << ":../../ruby2ruby/1.3.1/lib" at.libs << ":../../ZenTest/dev/lib" at.libs << ":../../sexp_processor/dev/lib" %w(Dasgn Iter Dasgncurr Cvasgn Boolean Call Callblock ClassMethod Gasgn Iasgn If Lasgn Masgn Ranges Regexes SameLiteral Strings Symbols Until While).each do |klass| at.extra_class_map["TestHeckle#{klass}"] = "test/test_heckle.rb" end end heckle-2.0.0.b1/metadata.gz.sig0000644000175000017500000000040012254560366015531 0ustar boutilboutil/6S?yn# $:V]!ʺSf$E%{q-SVRck'5Z_UTZ$/gQA%,#ЇO0biL %1)uDzY[]}#۷څ:!V_S|0uň"ޫw'}n@Gyyi( X$K'אSkr4[d>ڹ5 0~F+K:d|^heckle-2.0.0.b1/Manifest.txt0000444000175000017500000000112712254560366015142 0ustar boutilboutil.autotest History.txt Manifest.txt README.txt Rakefile bin/heckle lib/autotest/heckle.rb lib/heckle.rb lib/heckle_runner.rb lib/minitest_heckler.rb lib/test_unit_heckler.rb sample/Rakefile sample/changes.log sample/test/test_heckled.rb test/fixtures/heckle_dummy.rb test/fixtures/heckled.rb test/fixtures/minitest_project/README.txt test/fixtures/minitest_project/Rakefile test/fixtures/minitest_project/lib/doubler.rb test/fixtures/minitest_project/test/test_doubler_with_a_number.rb test/fixtures/minitest_project/test/test_doubler_with_bad_input.rb test/test_heckle.rb test/test_heckle_runner.rb heckle-2.0.0.b1/lib/0000755000175000017500000000000012254560366013402 5ustar boutilboutilheckle-2.0.0.b1/lib/test_unit_heckler.rb0000444000175000017500000000556212254560366017450 0ustar boutilboutil#!/usr/bin/env ruby require 'test/unit/testcase' require 'heckle' require 'zentest_mapping' $: << 'lib' << 'test' # Make sure test/unit doesn't swallow our timeout begin Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS << Heckle::Timeout rescue NameError # ignore end class TestUnitHeckler < Heckle @@test_pattern = 'test/test_*.rb' @@tests_loaded = false @@focus = false def self.test_pattern=(value) @@test_pattern = value end def self.focus=(value) @@focus = value end def self.load_test_files @@tests_loaded = true Dir.glob(@@test_pattern).each {|test| require test} end def self.validate(klass_name, method_name = nil, nodes = Heckle::MUTATABLE_NODES, force = false) load_test_files klass = klass_name.to_class # Does the method exist? klass_methods = klass.singleton_methods(false).collect {|meth| "self.#{meth}"} if method_name if method_name =~ /self\./ abort "Unknown method: #{klass_name}.#{method_name.gsub('self.', '')}" unless klass_methods.include? method_name else abort "Unknown method: #{klass_name}##{method_name}" unless klass.instance_methods(false).include? method_name end end initial_time = Time.now heckle = self.new(klass_name) passed = heckle.tests_pass? unless force or passed then abort "Initial run of tests failed... fix and run heckle again" end if self.guess_timeout? then running_time = Time.now - initial_time adjusted_timeout = (running_time * 2 < 5) ? 5 : (running_time * 2).ceil self.timeout = adjusted_timeout end puts "Timeout set to #{adjusted_timeout} seconds." if passed then puts "Initial tests pass. Let's rumble." else puts "Initial tests failed but you forced things. Let's rumble." end puts methods = method_name ? Array(method_name) : klass.instance_methods(false) + klass_methods counts = Hash.new(0) methods.sort.each do |method_name| result = self.new(klass_name, method_name, nodes).validate counts[result] += 1 end all_good = counts[false] == 0 puts "Heckle Results:" puts puts "Passed : %3d" % counts[true] puts "Failed : %3d" % counts[false] puts "Thick Skin: %3d" % counts[nil] puts if all_good then puts "All heckling was thwarted! YAY!!!" else puts "Improve the tests and try again." end all_good end def initialize(klass_name=nil, method_name=nil, nodes=Heckle::MUTATABLE_NODES) super self.class.load_test_files unless @@tests_loaded end include ZenTestMapping def tests_pass? silence_stream do if @@focus and @method_name then name = normal_to_test @method_name.to_s ARGV.clear ARGV << "--name=/#{name}/" end result = Test::Unit::AutoRunner.run ARGV.clear result end end end heckle-2.0.0.b1/lib/heckle.rb0000444000175000017500000004303512254560366015165 0ustar boutilboutilrequire 'rubygems' require 'ruby_parser' require 'sexp_processor' require 'ruby2ruby' require 'timeout' require 'tempfile' class String # :nodoc: def to_class split(/::/).inject(Object) { |klass, name| klass.const_get(name) } end end class Sexp # REFACTOR: move to sexp.rb def each_sexp self.each do |sexp| next unless Sexp === sexp yield sexp end end end ## # Test Unit Sadism class Heckle < SexpProcessor class Timeout < Timeout::Error; end ## # The version of Heckle you are using. VERSION = '2.0.0.b1' ## # Branch node types. BRANCH_NODES = [:if, :until, :while] ## # Is this platform MS Windows-like? WINDOZE = RUBY_PLATFORM =~ /mswin/ ## # Path to the bit bucket. NULL_PATH = WINDOZE ? 'NUL:' : '/dev/null' ## # diff(1) executable DIFF = WINDOZE ? 'diff.exe' : 'diff' ## # Mutation count attr_accessor :count ## # Mutations that caused failures attr_accessor :failures ## # Class being heckled attr_accessor :klass ## # Name of class being heckled attr_accessor :klass_name ## # Method being heckled attr_accessor :method ## # Name of method being heckled attr_accessor :method_name attr_accessor :mutatees # :nodoc: attr_accessor :mutation_count # :nodoc: attr_accessor :node_count # :nodoc: attr_accessor :original_tree # :nodoc: @@debug = false @@guess_timeout = true @@timeout = 60 # default to something longer (can be overridden by runners) def self.debug @@debug end def self.debug=(value) @@debug = value end def self.timeout=(value) @@timeout = value @@guess_timeout = false # We've set the timeout, don't guess end def self.guess_timeout? @@guess_timeout end ## # Creates a new Heckle that will heckle +klass_name+ and +method_name+, # sending results to +reporter+. def initialize(klass_name = nil, method_name = nil, nodes = Heckle::MUTATABLE_NODES, reporter = Reporter.new) super() @klass_name = klass_name @method_name = method_name.intern if method_name @klass = klass_name.to_class @method = nil @reporter = reporter self.strict = false self.auto_shift_type = true self.expected = Sexp @mutatees = Hash.new @mutation_count = Hash.new 0 @node_count = Hash.new 0 @count = 0 @mutatable_nodes = nodes @mutatable_nodes.each {|type| @mutatees[type] = [] } @failures = [] @mutated = false @original_tree = rewrite find_scope_and_method @current_tree = @original_tree.deep_clone grab_mutatees @original_mutatees = mutatees.deep_clone end ## # Overwrite test_pass? for your own Heckle runner. def tests_pass? raise NotImplementedError end def run_tests if tests_pass? then record_passing_mutation else @reporter.report_test_failures end end ############################################################ ### Running the script def validate left = mutations_left if left == 0 then @reporter.no_mutations(method_name) return end @reporter.method_loaded(klass_name, method_name, left) until left == 0 do @reporter.remaining_mutations left reset_tree begin process current_tree timeout(@@timeout, Heckle::Timeout) { run_tests } rescue SyntaxError => e @reporter.warning "Mutation caused a syntax error:\n\n#{e.message}}" rescue Heckle::Timeout @reporter.warning "Your tests timed out. Heckle may have caused an infinite loop." rescue Interrupt @reporter.warning 'Mutation canceled, hit ^C again to exit' sleep 2 end left = mutations_left end reset # in case we're validating again. we should clean up. unless @failures.empty? @reporter.no_failures @failures.each do |failure| original = Ruby2Ruby.new.process(@original_tree.deep_clone) @reporter.failure(original, failure) end false else @reporter.no_surviving_mutants true end end def record_passing_mutation @failures << current_code end def heckle(exp) @current_tree = exp.deep_clone src = begin Ruby2Ruby.new.process(exp) rescue => e puts "Error: #{e.message} with: #{klass_name}##{method_name}: #{@current_tree.inspect}" raise e end if @@debug original = Ruby2Ruby.new.process(@original_tree.deep_clone) @reporter.replacing(klass_name, method_name, original, src) end self.count += 1 clean_name = method_name.to_s.gsub(/self\./, '') new_name = "h#{count}_#{clean_name}" klass = aliasing_class method_name klass.send :remove_method, new_name rescue nil klass.send :alias_method, new_name, clean_name klass.send :remove_method, clean_name rescue nil @klass.class_eval src, "(#{new_name})" end ############################################################ ### Processing sexps def process_call(exp) recv = process(exp.shift) meth = exp.shift args = process(exp.shift) mutate_node s(:call, recv, meth, args) end ## # Replaces the call node with nil. def mutate_call(node) s(:nil) end def process_defn(exp) self.method = exp.shift result = s(:defn, method) result << process(exp.shift) until exp.empty? heckle(result) if method == method_name return result ensure @mutated = false node_count.clear end def process_defs(exp) recv = process exp.shift meth = exp.shift self.method = "#{Ruby2Ruby.new.process(recv.deep_clone)}.#{meth}".intern result = s(:defs, recv, meth) result << process(exp.shift) until exp.empty? heckle(result) if method == method_name return result ensure @mutated = false node_count.clear end ## # So process_call works correctly def process_iter(exp) call = process exp.shift args = process exp.shift body = process exp.shift mutate_node s(:iter, call, args, body) end def mutate_iter(exp) s(:nil) end def process_asgn(type, exp) var = exp.shift if exp.empty? then mutate_node s(type, var) else mutate_node s(type, var, process(exp.shift)) end end def mutate_asgn(node) type = node.shift var = node.shift if node.empty? then s(type, :_heckle_dummy) else if node.last.first == :nil then s(type, var, s(:lit, 42)) else s(type, var, s(:nil)) end end end def process_cvasgn(exp) process_asgn :cvasgn, exp end ## # Replaces the value of the cvasgn with nil if its some value, and 42 if its # nil. alias mutate_cvasgn mutate_asgn def process_dasgn(exp) process_asgn :dasgn, exp end ## # Replaces the value of the dasgn with nil if its some value, and 42 if its # nil. alias mutate_dasgn mutate_asgn def process_dasgn_curr(exp) process_asgn :dasgn_curr, exp end ## # Replaces the value of the dasgn_curr with nil if its some value, and 42 if # its nil. alias mutate_dasgn_curr mutate_asgn def process_iasgn(exp) process_asgn :iasgn, exp end ## # Replaces the value of the iasgn with nil if its some value, and 42 if its # nil. alias mutate_iasgn mutate_asgn def process_gasgn(exp) process_asgn :gasgn, exp end ## # Replaces the value of the gasgn with nil if its some value, and 42 if its # nil. alias mutate_gasgn mutate_asgn def process_lasgn(exp) process_asgn :lasgn, exp end ## # Replaces the value of the lasgn with nil if its some value, and 42 if its # nil. alias mutate_lasgn mutate_asgn def process_lit(exp) mutate_node s(:lit, exp.shift) end ## # Replaces the value of the :lit node with a random value. def mutate_lit(exp) case exp[1] when Fixnum, Float, Bignum s(:lit, exp[1] + rand_number) when Symbol s(:lit, rand_symbol) when Regexp s(:lit, Regexp.new(Regexp.escape(rand_string.gsub(/\//, '\\/')))) when Range s(:lit, rand_range) end end def process_str(exp) mutate_node s(:str, exp.shift) end ## # Replaces the value of the :str node with a random value. def mutate_str(node) s(:str, rand_string) end def process_if(exp) mutate_node s(:if, process(exp.shift), process(exp.shift), process(exp.shift)) end ## # Swaps the then and else parts of the :if node. def mutate_if(node) s(:if, node[1], node[3], node[2]) end def process_true(exp) mutate_node s(:true) end ## # Swaps for a :false node. def mutate_true(node) s(:false) end def process_false(exp) mutate_node s(:false) end ## # Swaps for a :true node. def mutate_false(node) s(:true) end def process_while(exp) cond, body, head_controlled = grab_conditional_loop_parts(exp) mutate_node s(:while, cond, body, head_controlled) end ## # Swaps for a :until node. def mutate_while(node) s(:until, node[1], node[2], node[3]) end def process_until(exp) cond, body, head_controlled = grab_conditional_loop_parts(exp) mutate_node s(:until, cond, body, head_controlled) end ## # Swaps for a :while node. def mutate_until(node) s(:while, node[1], node[2], node[3]) end def mutate_node(node) raise UnsupportedNodeError unless respond_to? "mutate_#{node.first}" increment_node_count node if should_heckle? node then increment_mutation_count node return send("mutate_#{node.first}", node) else node end end ############################################################ ### Tree operations def walk_and_push(node, index = 0) return unless node.respond_to? :each return if node.is_a? String @walk_stack.push node.first node.each_with_index { |child_node, i| walk_and_push child_node, i } @walk_stack.pop if @mutatable_nodes.include? node.first and # HACK skip over call nodes that are the first child of an iter or # they'll get added twice # # I think heckle really needs two processors, one for finding and one # for heckling. !(node.first == :call and index == 1 and @walk_stack.last == :iter) then @mutatees[node.first].push(node) end end def grab_mutatees @walk_stack = [] walk_and_push current_tree end def current_tree @current_tree.deep_clone end # Copied from Flay#process def find_scope_and_method expand_dirs_to_files.each do |file| #warn "Processing #{file}" if option[:verbose] ext = File.extname(file).sub(/^\./, '') ext = "rb" if ext.nil? || ext.empty? msg = "process_#{ext}" unless respond_to? msg then warn " Unknown file type: #{ext}, defaulting to ruby" msg = "process_rb" end begin sexp = begin send msg, file rescue => e warn " #{e.message.strip}" warn " skipping #{file}" nil end next unless sexp found = find_scope sexp return found if found rescue SyntaxError => e warn " skipping #{file}: #{e.message}" end end raise "Couldn't find method." end def process_rb file RubyParser.new.process(File.read(file), file) end def find_scope sexp, nesting=nil nesting ||= klass_name.split("::").map {|k| k.to_sym } current, *nesting = nesting sexp = s(:block, sexp) unless sexp.first == :block sexp.each_sexp do |node| next unless [:class, :module].include? node.first next unless node[1] == current block = node.detect {|s| Sexp === s && s[0] == :scope }[1] if nesting.empty? return sexp if method_name.nil? m = find_method block return m if m else s = find_scope block, nesting return s if s end end nil end def find_method sexp class_method = method_name.to_s =~ /^self\./ clean_name = method_name.to_s.sub(/^self\./, '').to_sym sexp = s(:block, sexp) unless sexp.first == :block sexp.each_sexp do |node| if class_method return node if node[0] == :defs && node[2] == clean_name else return node if node[0] == :defn && node[1] == clean_name end end nil end def expand_dirs_to_files(dirs='.') Array(dirs).flatten.map { |p| if File.directory? p then Dir[File.join(p, '**', "*.rb")] else p end }.flatten end def reset reset_tree reset_mutatees mutation_count.clear end def reset_tree return unless original_tree != current_tree @mutated = false @current_tree = original_tree.deep_clone self.count += 1 clean_name = method_name.to_s.gsub(/self\./, '') new_name = "h#{count}_#{clean_name}" klass = aliasing_class method_name klass.send :undef_method, new_name rescue nil klass.send :alias_method, new_name, clean_name klass.send :alias_method, clean_name, "h1_#{clean_name}" end def reset_mutatees @mutatees = @original_mutatees.deep_clone end def increment_node_count(node) node_count[node] += 1 end def increment_mutation_count(node) # So we don't re-mutate this later if the tree is reset mutation_count[node] += 1 mutatee_type = @mutatees[node.first] mutatee_type.delete_at mutatee_type.index(node) @mutated = true end ############################################################ ### Convenience methods def aliasing_class(method_name) method_name.to_s =~ /self\./ ? class << @klass; self; end : @klass end def should_heckle?(exp) return false unless method == method_name return false if node_count[exp] <= mutation_count[exp] key = exp.first.to_sym mutatees.include?(key) && mutatees[key].include?(exp) && !already_mutated? end def grab_conditional_loop_parts(exp) cond = process(exp.shift) body = process(exp.shift) head_controlled = exp.shift return cond, body, head_controlled end def already_mutated? @mutated end def mutations_left @last_mutations_left ||= -1 sum = 0 @mutatees.each { |mut| sum += mut.last.size } if sum == @last_mutations_left then puts 'bug!' puts require 'pp' puts 'mutatees:' pp @mutatees puts puts 'original tree:' pp @original_tree puts puts "Infinite loop detected!" puts "Please save this output to an attachment and submit a ticket here:" puts "http://rubyforge.org/tracker/?func=add&group_id=1513&atid=5921" exit 1 else @last_mutations_left = sum end sum end def current_code Ruby2Ruby.new.process current_tree end ## # Returns a random Fixnum. def rand_number (rand(100) + 1)*((-1)**rand(2)) end ## # Returns a random String def rand_string size = rand(50) str = "" size.times { str << rand(126).chr } str end ## # Returns a random Symbol def rand_symbol letters = ('a'..'z').to_a + ('A'..'Z').to_a str = "" (rand(50) + 1).times { str << letters[rand(letters.size)] } :"#{str}" end ## # Returns a random Range def rand_range min = rand(50) max = min + rand(50) min..max end ## # Suppresses output on $stdout and $stderr. def silence_stream return yield if @@debug begin dead = File.open("/dev/null", "w") $stdout.flush $stderr.flush oldstdout = $stdout.dup oldstderr = $stderr.dup $stdout.reopen(dead) $stderr.reopen(dead) result = yield ensure $stdout.flush $stderr.flush $stdout.reopen(oldstdout) $stderr.reopen(oldstderr) result end end class Reporter def no_mutations(method_name) warning "#{method_name} has a thick skin. There's nothing to heckle." end def method_loaded(klass_name, method_name, mutations_left) info "#{klass_name}\##{method_name} loaded with #{mutations_left} possible mutations" end def remaining_mutations(mutations_left) puts "#{mutations_left} mutations remaining..." end def warning(message) puts "!" * 70 puts "!!! #{message}" puts "!" * 70 puts end def info(message) puts "*"*70 puts "*** #{message}" puts "*"*70 puts end def no_failures puts puts "The following mutations didn't cause test failures:" puts end def diff(original, mutation) length = [original.split(/\n/).size, mutation.split(/\n/).size].max Tempfile.open("orig") do |a| a.puts(original) a.flush Tempfile.open("fail") do |b| b.puts(mutation) b.flush diff_flags = " " output = `#{Heckle::DIFF} -U #{length} --label original #{a.path} --label mutation #{b.path}` puts output.sub(/^@@.*?\n/, '') puts end end end def failure(original, failure) self.diff original, failure end def no_surviving_mutants puts "No mutants survived. Cool!\n\n" end def replacing(klass_name, method_name, original, src) puts "Replacing #{klass_name}##{method_name} with:\n\n" diff(original, src) end def report_test_failures puts "Tests failed -- this is good" if Heckle.debug end end ## # All nodes that can be mutated by Heckle. MUTATABLE_NODES = instance_methods.grep(/mutate_/).sort.map do |meth| meth.to_s.sub(/mutate_/, '').intern end - [:asgn, :node] # Ignore these methods ## # All assignment nodes that can be mutated by Heckle.. ASGN_NODES = MUTATABLE_NODES.map { |n| n.to_s }.grep(/asgn/).map do |n| n.intern end end heckle-2.0.0.b1/lib/minitest_heckler.rb0000444000175000017500000000123712254560366017261 0ustar boutilboutilclass MiniTestHeckler < Heckle def initialize(class_or_module, method, options) Dir.glob(options[:test_pattern]).each {|t| load File.expand_path(t) } super(class_or_module, method, options[:nodes]) end def tests_pass? silence do MiniTest::Unit.runner = nil MiniTest::Unit.new.run runner = MiniTest::Unit.runner runner.failures == 0 && runner.errors == 0 end end # TODO: Windows. def silence return yield if Heckle.debug begin original = MiniTest::Unit.output MiniTest::Unit.output = File.open("/dev/null", "w") yield ensure MiniTest::Unit.output = original end end end heckle-2.0.0.b1/lib/autotest/0000755000175000017500000000000012254560366015252 5ustar boutilboutilheckle-2.0.0.b1/lib/autotest/heckle.rb0000444000175000017500000000052712254560366017034 0ustar boutilboutil# -*- ruby -*- module Autotest::Heckle @@flags = [] @@klasses = [] def self.flags; @@flags; end def self.klasses; @@klasses; end Autotest.add_hook :all_good do |at| heckle = "heckle" + (@@flags.empty? ? '' : " #{flags.join(' ')}") cmd = @@klasses.map { |klass| "#{heckle} #{klass}" }.join(" && ") system cmd end end heckle-2.0.0.b1/lib/heckle_runner.rb0000444000175000017500000000577112254560366016563 0ustar boutilboutilrequire 'optparse' require 'heckle' require 'minitest_heckler' class HeckleRunner def self.run argv=ARGV options = parse_args argv class_or_module, method = argv.shift, argv.shift new(class_or_module, method, options).run end def self.parse_args argv options = { :force => false, :nodes => Heckle::MUTATABLE_NODES, :debug => false, :focus => false, :timeout => 5, :test_pattern => 'test/test_*.rb', } OptionParser.new do |opts| opts.version = Heckle::VERSION opts.program_name = File.basename $0 opts.banner = "Usage: #{opts.program_name} class_name [method_name]" opts.on("-v", "--verbose", "Loudly explain heckle run") do options[:debug] = true end opts.on("-t", "--tests TEST_PATTERN", "Location of tests (glob)") do |pattern| options[:test_pattern] = pattern end opts.on("-F", "--force", "Ignore initial test failures", "Best used with --focus") do options[:force] = true end opts.on( "--assignments", "Only mutate assignments") do puts "!"*70 puts "!!! Heckling assignments only" puts "!"*70 puts options[:nodes] = Heckle::ASGN_NODES end opts.on("-b", "--branches", "Only mutate branches") do puts "!"*70 puts "!!! Heckling branches only" puts "!"*70 puts options[:nodes] = Heckle::BRANCH_NODES end opts.on("-f", "--focus", "Apply the eye of sauron") do puts "!"*70 puts "!!! Running in focused mode. FEEL THE EYE OF SAURON!!!" puts "!"*70 puts options[:focus] = true end opts.on("-T", "--timeout SECONDS", "The maximum time for a test run in seconds", "Used to catch infinite loops") do |timeout| timeout = timeout.to_i puts "Setting timeout at #{timeout} seconds." options[:timeout] = timeout end opts.on("-n", "--nodes NODES", "Nodes to mutate", "Possible values: #{Heckle::MUTATABLE_NODES.join(',')}") do |opt| nodes = opt.split(',').collect {|n| n.to_sym } options[:nodes] = nodes puts "Mutating nodes: #{nodes.inspect}" end opts.on("-x", "--exclude-nodes NODES", "Nodes to exclude") do |opt| exclusions = opt.split(',').collect {|n| n.to_sym } options[:nodes] = options[:nodes] - exclusions puts "Mutating without nodes: #{exclusions.inspect}" end end.parse! argv p options if options[:debug] # TODO: Pass options to Heckle's initializer instead. Heckle.debug = options[:debug] Heckle.timeout = options[:timeout] options end # TODO: this sucks def heckler MiniTestHeckler end def initialize class_or_module, method, options={} @class_or_module = class_or_module @method = method @options = options end def run heckler.new(@class_or_module, @method, @options).validate end end heckle-2.0.0.b1/History.txt0000444000175000017500000000541412254560366015040 0ustar boutilboutil=== 2.0.0.b1 / 2012-10-02 * 1 major enhancement: * Hot damn! It works with ruby_parser!!! (phiggins) * 1 minor enhancement: * 1.9 support! (phiggins) * 3 bug fixes: * Fixed Ruby2Ruby usage (raggi) * Fixed dependencies so heckle doesn't use ruby_parser 3 and friends. * Fixed grammar in description. Reported by Jean Lange. === 1.4.3 / 2009-06-23 * 2 minor enhancements: * Added autotest/heckle plugin * Skipping testing on 1.9 === 1.4.2 / 2009-02-08 * 2 bug fixes: * Fixed Ruby2Ruby dependency and Ruby2Ruby references (name changed). Reported by David Chelimsky * Fix bug #11435 where [:iter, [:call], ...] would cause an endless loop. Reported by Thomas Preymesser. === 1.4.1 / 2007-06-05 * 3 bug fixes: * Add zentest as a heckle dependency. Closes #10996 * Fixed heckling of call with blocks. * Fix test_unit_heckler's test_pass? so it returns the result of the run rather than ARGV.clear === 1.4.0 / 2007-05-18 * 2 major enhancements: * Method calls are now heckled (by removal). * Assignments are now heckled (by value changing). * 3 minor enhancements: * Added --focus to feel the Eye of Sauron (specify unit tests to run). * Specify nodes to be included/excluded in heckle with -n/-x. * Test only assignments with --assignments === 1.3.0 / 2007-02-12 * 1 major enhancement: * Unified diffs for mutatated methods * 4 minor enhancements: * Now returns exit status 1 if failed. * Added a simple report at the end. * Runs are now sorted by method. * Autodetects rails and changes test_pattern accordingly. * 2 bug fixes: * Aborts when an unknown method is supplied. * Escapes slashes in random regexps. === 1.2.0 / 2007-01-15 * 2 major enhancements: * Timeout for tests set dynamically and overridable with -T * Class method support with "self.method_name" * 3 minor enhancements: * -b allows heckling of branches only * Restructured class heirarchy and got rid of Base and others. * Revamped the tests and reduced size by 60%. * 1 bug fix: * Fixed the infinite loop caused by syntax errors === 1.1.1 / 2006-12-20 * 3 bug fixes: * Load tests properly when supplying method name. * Make sure random symbols have at least one character. * Removed all extra warnings from the unit tests. Consolidated and cleaned. === 1.1.0 / 2006-12-19 * 12 major enhancements: * Able to roll back original method after processing. * Can mutate numeric literals. * Can mutate strings. * Can mutate a node at a time. * Can mutate if/unless * Decoupled from Test::Unit * Cleaner output * Can mutate true and false. * Can mutate while and until. * Can mutate regexes, ranges, symbols * Can run against entire classes * Command line options! === 1.0.0 / 2006-10-22 * 1 major enhancement * Birthday! heckle-2.0.0.b1/sample/0000755000175000017500000000000012254560366014115 5ustar boutilboutilheckle-2.0.0.b1/sample/Rakefile0000444000175000017500000000034012254560366015555 0ustar boutilboutil# -*- ruby -*- require 'rubygems' require 'rake/testtask' task :default => :test desc "Run basic tests" Rake::TestTask.new { |t| t.pattern = 'test/test_*.rb' t.verbose = true t.warning = true } # vim: syntax=Ruby heckle-2.0.0.b1/sample/changes.log0000444000175000017500000001021512254560366016225 0ustar boutilboutil# Logfile created on Fri Nov 10 22:52:10 PST 2006 by logger.rb/1.5.2.7 I, [2006-11-10T22:52:10.310486 #498] INFO -- : Validating I, [2006-11-10T22:52:10.326577 #498] INFO -- : Heckling Heckled#uses_strings I, [2006-11-10T22:52:10.328115 #498] INFO -- : Replacing #uses_strings with: def uses_strings (@names << "I\rmeo&+eIZ{bD2dj1Z_e\001{Y!](CbI'!`@\020Wlg4,w\t\02181/4J\t\v@][ L!Xy[\r\\MJ[0.+gh}\001ks\026_-BK") (@names << "Hello, Jeff") (@names << "Hi, Frank") end I, [2006-11-10T22:52:10.344547 #498] INFO -- : Heckling Heckled#uses_strings I, [2006-11-10T22:52:10.346021 #498] INFO -- : Replacing #uses_strings with: def uses_strings (@names << "Hello, Robert") (@names << "\030ym+3\f:\031\023`\r:O\a") (@names << "Hi, Frank") end I, [2006-11-10T22:52:10.356743 #498] INFO -- : Heckling Heckled#uses_strings I, [2006-11-10T22:52:10.358234 #498] INFO -- : Replacing #uses_strings with: def uses_strings (@names << "Hello, Robert") (@names << "Hello, Jeff") (@names << "o,NY\020\027V0v\032dEw\010<*4\026uh$\026\010vhl(A\"ybX$sd\"M\006qbH\032TAV/") end I, [2006-11-10T22:52:10.369174 #498] INFO -- : Heckling Heckled#uses_strings I, [2006-11-10T22:52:10.370636 #498] INFO -- : Replacing #uses_strings with: def uses_strings (@names << "Hello, Robert") (@names << "Hello, Jeff") (@names << "Hi, Frank") end I, [2006-11-10T23:17:54.953429 #569] INFO -- : Validating I, [2006-11-10T23:19:05.369315 #570] INFO -- : Validating I, [2006-11-10T23:20:01.512545 #571] INFO -- : Validating I, [2006-11-10T23:20:08.080030 #572] INFO -- : Validating I, [2006-11-10T23:20:08.100363 #572] INFO -- : Heckling Heckled#uses_strings I, [2006-11-10T23:20:08.101943 #572] INFO -- : Replacing #uses_strings with: def uses_strings (@names << "\034\000\021[\r\aM!L=qfU'#") (@names << "Hello, Jeff") (@names << "Hi, Frank") end I, [2006-11-10T23:20:08.120455 #572] INFO -- : Heckling Heckled#uses_strings I, [2006-11-10T23:20:08.122176 #572] INFO -- : Replacing #uses_strings with: def uses_strings (@names << "Hello, Robert") (@names << "\r\025 $\n(&6P^X<\000\t..[\003$I\025\037\001\027E$/P\025