ZenTest-4.12.2/0000755000004100000410000000000014661577214013241 5ustar www-datawww-dataZenTest-4.12.2/Manifest.txt0000444000004100000410000000060314661577214015545 0ustar www-datawww-data.autotest History.txt Manifest.txt README.txt Rakefile articles/Article.css articles/how_to_use_zentest.txt bin/multigem bin/multiruby bin/unit_diff bin/zentest example.txt example1.rb example2.rb lib/focus.rb lib/functional_test_matrix.rb lib/unit_diff.rb lib/zentest.rb lib/zentest_mapping.rb test/test_focus.rb test/test_unit_diff.rb test/test_zentest.rb test/test_zentest_mapping.rb ZenTest-4.12.2/.autotest0000444000004100000410000000056214661577214015113 0ustar www-datawww-data# -*- ruby -*- require 'autotest/restart' Autotest.add_hook :initialize do |at| at.add_exception 'coverage' at.add_exception 'coverage.info' at.libs << ':../../minitest/dev/lib' %w(TestZenTest).each do |klass| at.extra_class_map[klass] = "test/test_zentest.rb" end end Autotest.add_hook :all_good do |at| system "rake rcov_info" end if ENV['RCOV'] ZenTest-4.12.2/bin/0000755000004100000410000000000014661577214014011 5ustar www-datawww-dataZenTest-4.12.2/bin/multigem0000555000004100000410000000016214661577214015557 0ustar www-datawww-data#!/usr/bin/env ruby -w multiruby = File.expand_path "../multiruby", __FILE__ exec multiruby, "-S", "gem", *ARGV ZenTest-4.12.2/bin/zentest0000555000004100000410000000054514661577214015435 0ustar www-datawww-data#!/usr/bin/env ruby -swI . require 'zentest' $TESTING = true # for ZenWeb and any other testing infrastructure code if defined? $v then puts "#{File.basename $0} v#{ZenTest::VERSION}" exit 0 end if defined? $h then ZenTest.usage_with_exit end code = ZenTest.fix(*ARGV) if defined? $e then require 'test/unit' eval code else print code end ZenTest-4.12.2/bin/unit_diff0000555000004100000410000000141114661577214015701 0ustar www-datawww-data#!/usr/bin/env ruby -ws # # unit_diff - a ruby unit test filter by Ryan Davis # # usage: # # test.rb | unit_diff [options] # options: # -b ignore whitespace differences # -c contextual diff # -h show usage # -k keep temp diff files around # -u unified diff [default] # -p plain diff # -v display version require 'unit_diff' require 'zentest' ############################################################ if defined? $v then puts "#{File.basename $0} v. #{ZenTest::VERSION}" exit 0 end if defined? $h then File.open(__FILE__) do |f| begin; end until f.readline =~ /usage:/ f.readline while line = f.readline and line.sub!(/^# ?/, '') $stderr.puts line end end exit 0 end UnitDiff.unit_diff ZenTest-4.12.2/bin/multiruby0000555000004100000410000000406214661577214015773 0ustar www-datawww-data#!/usr/bin/env ruby -w require "yaml" class Array def human_sort sort_by { |item| item.to_s.split(/(\d+)/).map { |e| [e.to_i, e] } } end end root_dir = File.expand_path "~/.rubies" versions = Dir.chdir(root_dir) { Dir["*"] }.human_sort def setenv dir ENV["PATH"] = "#{dir}/bin:#{ENV["PATH"]}" end def unsetenv key if ENV[key] then warn "WARNING: %s is set to %p. Removing..." % [key, ENV[key]] ENV.delete key end end unsetenv "GEM_HOME" unsetenv "GEM_PATH" ## # multiruby -1 2.0 ruby_args... if ARGV.first == "-1" then ARGV.shift vers = Dir["#{root_dir}/#{ARGV.shift}*"] abort "ambiguous version: #{vers.map { |p| File.basename p }.inspect}" if vers.size != 1 dir = vers.first setenv dir exec "#{dir}/bin/ruby", *ARGV end def maybe_load_yaml_file config if config then if YAML.respond_to? :safe_load_file then YAML.safe_load_file config, permitted_classes: [Regexp, Symbol] else YAML.load_file config end end end rcpath = File.expand_path "~/.hoerc" skip = if File.exist? rcpath then conf = maybe_load_yaml_file rcpath conf["multiruby_skip"] || [] end excl = (ENV["EXCLUDED_VERSIONS"] || "").split(/:/) + skip unless excl.empty? then excludes = Regexp.union(*excl) versions = versions.delete_if { |v| v =~ excludes } end # safekeep original PATH original_path = ENV['PATH'] results = {} versions.each do |version| dir = "#{root_dir}/#{version}" ruby = "#{dir}/bin/ruby" ver = version.delete_prefix "ruby-" puts puts "VERSION = #{ver}" cmd = [ruby, ARGV].flatten.map { |s| s =~ /\"/ ? "'#{s}'" : s }.join(' ') cmd.sub!(/#{ENV['HOME']}/, '~') puts "CMD = #{cmd}" puts setenv dir system ruby, *ARGV puts puts "RESULT = #{$?}" results[ver] = $? # restore the path to original state ENV['PATH'] = original_path end passed, failed = results.keys.partition { |v| results[v] == 0 } puts puts "TOTAL RESULT = #{failed.size} failures out of #{results.size}" puts puts "Passed: #{passed.join(", ")}" puts "Failed: #{failed.join(", ")}" exit failed.size ZenTest-4.12.2/README.txt0000444000004100000410000001236314661577214014742 0ustar www-datawww-data= ZenTest home :: https://github.com/seattlerb/zentest rdoc :: http://docs.seattlerb.org/ZenTest == DESCRIPTION ZenTest provides 4 different tools: zentest, unit_diff, autotest, and multiruby. zentest scans your target and unit-test code and writes your missing code based on simple naming rules, enabling XP at a much quicker pace. zentest only works with Ruby and Minitest or Test::Unit. There is enough evidence to show that this is still proving useful to users, so it stays. unit_diff is a command-line filter to diff expected results from actual results and allow you to quickly see exactly what is wrong. Do note that minitest 2.2+ provides an enhanced assert_equal obviating the need for unit_diff autotest is a continous testing facility meant to be used during development. As soon as you save a file, autotest will run the corresponding dependent tests. multiruby runs anything you want on multiple versions of ruby. Great for compatibility checking! Use multiruby_setup to manage your installed versions. *NOTE:* The next major release of zentest will not include autotest (use minitest-autotest instead) and multiruby will use rbenv / ruby-build for version management. == FEATURES * Scans your ruby code and tests and generates missing methods for you. * Includes a very helpful filter for Test/Spec output called unit_diff. * Continually and intelligently test only those files you change with autotest. * Test against multiple versions with multiruby. * Includes a LinuxJournal article on testing with ZenTest written by Pat Eyler. * See also: http://blog.zenspider.com/archives/zentest/ * See also: http://blog.segment7.net/articles/category/zentest == STRATEGERY There are two strategeries intended for ZenTest: test conformance auditing and rapid XP. For auditing, ZenTest provides an excellent means of finding methods that have slipped through the testing process. I've run it against my own software and found I missed a lot in a well tested package. Writing those tests found 4 bugs I had no idea existed. ZenTest can also be used to evaluate generated code and execute your tests, allowing for very rapid development of both tests and implementation. == AUTOTEST TIPS Setting up your project with a custom setup is easily done by creating a ".autotest" file in your project. Here is an example of adding some plugins, using minitest as your test library, and running rcov on full passes: require 'autotest/restart' Autotest.add_hook :initialize do |at| at.testlib = "minitest/autorun" end Autotest.add_hook :all_good do |at| system "rake rcov_info" end if ENV['RCOV'] Do note, since minitest ships with ruby19, if you want to use the latest minitest gem you need to ensure that the gem activation occurs! To do this, add the gem activation and the proper require to a separate file (like ".minitest.rb" or even a test helper if you have one) and use that for your testlib instead: .minitest.rb: gem "minitest" require "minitest/autorun" .autotest: Autotest.add_hook :initialize do |at| at.testlib = ".minitest" end If you prefer to suffix test files with "_test.rb" (instead of the default which prefixes test files with "test_") you can change the mapping by installing the autotest-suffix plugin. To do this first install the autotest-suffix gem: $ gem install autotest-suffix Then add the following to the ".autotest" file: require "autotest/suffix" If you prefer minitest/spec to minitest/unit, you can still use autotest by installing the autotest-spec plugin. To do this first install the autotest-spec gem: $ gem install autotest-spec Then add the following to the ".autotest" file: require "autotest/spec" == SYNOPSIS ZenTest MyProject.rb TestMyProject.rb > missing.rb ./TestMyProject.rb | unit_diff autotest multiruby_setup mri:svn:current multiruby ./TestMyProject.rb == Windows and Color Read this: http://blog.mmediasys.com/2010/11/24/we-all-love-colors/ == REQUIREMENTS * Ruby 1.8+, JRuby 1.1.2+, or rubinius * A test/spec framework of your choice. * Hoe (development) * rubygems, 1.8+ * diff.exe on windows. Use http://gnuwin32.sourceforge.net/packages.html == INSTALL * sudo gem install ZenTest == LICENSE (The MIT License) Copyright (c) Ryan Davis, Eric Hodel, seattle.rb 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. ZenTest-4.12.2/articles/0000755000004100000410000000000014661577214015047 5ustar www-datawww-dataZenTest-4.12.2/articles/how_to_use_zentest.txt0000444000004100000410000003120714661577214021540 0ustar www-datawww-dataHow to Use ZenTest with Ruby by Pat Eyler http://linuxjournal.com/article.php?sid=7776 (included in this package with permission) Refactoring and unit testing are a great pair of tools for every programmer's workbench. Sadly, not every programmer knows how to use them. My first exposure to them came when I started using Ruby, refactoring and unit testing are a big part of the landscape in the Ruby community. Some time ago, I translated the refactoring example from the first chapter of Martin Fowler's excellent book, Refactoring, out of Java and into Ruby. I felt this would be a great way to learn more about refactoring and brush up on my Ruby while I was at it. Recently, I decided to update the translation for Ruby 1.8.X. One of the things I needed to change was to convert the old unit tests to work with Test::Unit, the new unit testing framework for Ruby. I wasn't really looking forward to building a new test suite though. Fortunately, help was available. Ryan Davis has written a great tool called ZenTest, which creates test suites for existing bodies of code. Since a lot of people are new to refactoring, unit testing, and ZenTest, I thought this would be a great chance to introduce you to this trio of tools. Martin's example code is built around a video store application. In his original code, there are three classes; Customer, Movie, and Rental. I'll focus on just the Customer class in this article. Here's the original code: class Customer attr_accessor :name def initialize(name) @name = name @rentals = Array.new end def addRental(aRental) @rentals.push(aRental) end def statement totalAmount = 0.0 frequentRenterPoints = 0 rentals = @rentals.length result = "\nRental Record for #{@name}\n" thisAmount = 0.0 @rentals.each do |rental| # determine amounts for each line case rental.aMovie.pricecode when Movie::REGULAR thisAmount += 2 if rental.daysRented > 2 thisAmount += (rental.daysRented - 2) * 1.5 end when Movie::NEW_RELEASE thisAmount += rental.daysRented * 3 when Movie::CHILDRENS thisAmount += 1.5 if each.daysRented > 3 thisAmount += (rental.daysRented - 3) * 1.5 end end # add frequent renter points frequentRenterPoints += 1 # add bonus for a two day new release rental if ( rental.daysRented > 1) && (Movie::NEW_RELEASE == rental.aMovie.pricecode) frequentRenterPoints += 1 end # show figures for this rental result +="\t#{rental.aMovie.title}\t#{thisAmount}\n" totalAmount += thisAmount end result += "Amount owed is #{totalAmount}\n" result += "You earned #{frequentRenterPoints} frequent renter points" end end Not the cleanest code in the world, but it is supposed to be that way. This represents the code as you get it from the user. No tests, poorly laid out, but working -- and it's your job to make it better without breaking it. So, where to start? With unit tests of course. Time to grab ZenTest. You can run it like this: $ zentest videostore.rb > test_videostore.rb which produces a file full of tests. Running the test suite doesn't do quite what we were hoping though: $ ruby testVideoStore.rb Loaded suite testVideoStore Started EEEEEEEEEEE Finished in 0.008974 seconds. 1) Error!!! test_addRental(TestCustomer): NotImplementedError: Need to write test_addRental testVideoStore.rb:11:in `test_addRental' testVideoStore.rb:54 2) Error!!! test_name=(TestCustomer): NotImplementedError: Need to write test_name= testVideoStore.rb:15:in `test_name=' testVideoStore.rb:54 3) Error!!! test_statement=(TestCustomer): NotImplementedError: Need to write test_statement testVideoStore.rb:19:in `test_statement' testVideoStore.rb:54 . . . 11 tests, 0 assertions, 0 failures, 11 errors $ So what exactly did we get out of this? Here's the portion of our new test suite that matters for the Customer class: # Code Generated by ZenTest v. 2.1.2 # classname: asrt / meth = ratio% # Customer: 0 / 3 = 0.00% require 'test/unit' class TestCustomer < Test::Unit::TestCase def test_addRental raise NotImplementedError, 'Need to write test_addRental' end def test_name= raise NotImplementedError, 'Need to write test_name=' end def test_statement raise NotImplementedError, 'Need to write test_statement' end end ZenTest built three test methods: one for the accessor method, one for the addRental method, and one for the statement method. Why nothing for the initializer? Well, initializers tend to be pretty bulletproof (if they're not, it's pretty easy to add the test method yourself). Besides, we'll be testing it indirectly when we write test_name= (the tests for the accessor method). There's one other thing we'll need to add, the test suite doesn't load the code we're testing. Changing the beginning of the script to require the videostore.rb file will do the trick for us. # Code Generated by ZenTest v. 2.1.2 # classname: asrt / meth = ratio% # Customer: 0 / 3 = 0.00% require 'test/unit' require 'videostore' That little snippet of comments at the top lets us know that we have three methods under test in the Customer class, zero assertions testing them, and no coverage. Let's fix that. We'll start by writing some tests for test_name= (no, it really doesn't matter what order we go in -- this is just a convenient place to start). def test_name= aCustomer = Customer.new("Fred Jones") assert_equal("Fred Jones",aCustomer.name) aCustomer.name = "Freddy Jones" assert_equal("Freddy Jones",aCustomer.name end Running testVideoStore.rb again gives us: $ ruby testVideoStore.rb Loaded suite testVideoStore Started E.EEEEEEEEE Finished in 0.011233 seconds. 1) Error!!! test_addRental(TestCustomer): NotImplementedError: Need to write test_addRental testVideoStore.rb:13:in `test_addRental' testVideoStore.rb:58 2) Error!!! test_statement(TestCustomer): NotImplementedError: Need to write test_statement testVideoStore.rb:23:in `test_statement' testVideoStore.rb:58 . . . 11 tests, 2 assertions, 0 failures, 10 errors $ So far, so good. The line of 'E's (which shows errors in the test run) has been reduced by one, and the summary line at the bottom tells us roughly the same thing. We really don't have a way to test addRental directly, so we'll just write an stub test for now. def test_addRental assert(1) # stub test, since there is nothing in the method to test end When we run the tests again, we get: $ ruby testVideoStore.rb Loaded suite testVideoStore Started ..EEEEEEEEE Finished in 0.008682 seconds. 1) Error!!! test_statement(TestCustomer): NotImplementedError: Need to write test_statement testVideoStore.rb:22:in `test_statement' testVideoStore.rb:57 . . . 11 tests, 3 assertions, 0 failures, 9 errors $ Better and better, just one error left in the TestCustomer class. Let's finish up with a test that will clear our test_statement error and verify that addRental works correctly: def test_statement aMovie = Movie.new("Legacy",0) aRental = Rental.new(aMovie,2) aCustomer = Customer.new("Fred Jones") aCustomer.addRental(aRental) aStatement = "\nRental Record for Fred Jones\n\tLegacy\t2.0 Amount owed is 2.0\nYou earned 1 frequent renter points" assert_equal(aStatement,aCustomer.statement) end We run the tests again, and see: $ ruby testVideoStore.rb Loaded suite testVideoStore Started ...EEEEEEEE Finished in 0.009378 seconds. . . . 11 tests, 4 assertions, 0 failures, 8 errors $ Great! The only errors left are on the Movie and Rental classes, the Customer class is clean. We can continue along like this for the remaining classes, but I'll not bore you with those details. Instead, I'd like to look at how ZenTest can help when you've already got some tests in place. Later development allows us to do just that -- the video store owner wants a new web based statement for web using customers. After a bit of refactoring and new development, the code looks like this: class Customer attr_accessor :name def initialize(name) @name = name @rentals = Array.new end def addRental(aRental) @rentals.push(aRental) end def statement result = "\nRental Record for #{@name}\n" @rentals.each do |each| # show figures for this rental result +="\t#{each.aMovie.title}\t#{each.getCharge}\n" end result += "Amount owed is #{getTotalCharge}\n" result += "You earned #{getFrequentRenterPoints} frequent renter points" end def htmlStatement result = "\n

Rentals for #{name}

\n" @rentals.each do |each| result += "#{each.aMovie.title}: #{each.getCharge}
\n" end result += "You owe #{getTotalCharge}

\n" result += "On this rental you earned #{getFrequentRenterPoints}" + " frequent renter points

" end def getTotalCharge result = 0.0 @rentals.each do |each| result += each.getCharge() end result end def getFrequentRenterPoints result = 0 @rentals.each do |each| result += each.getFrequentRenterPoints end result end end There's a lot of new stuff in here. If we run ZenTest again, it'll pick up the methods we don't have any coverage on (we should have written them as we wrote the new methods, but this is a bit more illustrative). This time, we'll invoke ZenTest a little bit differently: $ zentest videostore.rb testVideoStore.rb > Missing_tests and our (trimmed) output looks like this: # Code Generated by ZenTest v. 2.1.2 # classname: asrt / meth = ratio% # Customer: 4 / 6 = 66.67% require 'test/unit' class TestCustomer < Test::Unit::TestCase def test_getFrequentRenterPoints raise NotImplementedError, 'Need to write test_getFrequentRenterPoints' end def test_getTotalCharge raise NotImplementedError, 'Need to write test_getTotalCharge' end def test_htmlStatement raise NotImplementedError, 'Need to write test_htmlStatement' end end Hmmm, three more test methods to fill in to get our complete coverage. As we write these, we can just migrate them into our existing testVideoStore.rb test suite. Then we can keep moving ahead with refactoring and adding new features. In the future, let's just be sure we add tests as we go along. ZenTest can help you here too. You can write stubs for new development, then run ZenTest to create your new test stubs as well. After some refactorings (like 'extract method'), ZenTest can be used the same way. Refactoring and unit testing are powerful tools for programmers, and ZenTest provides an easy way to start using them in a Ruby environment. Hopefully, this introduction has whetted your appetite. If you're interested in learning more about refactoring, please grab a copy of 'Refactoring: Improving the Design of Existing Code' and take a look at www.refactoring.com. For more information about unit testing, please see: c2.com/cgi/wiki?UnitTest, www.junit.org/index.htm, and www.extremeprogramming.org/rules/unittests.html. The latest information about Test::Unit and ZenTest are available at their home pages: testunit.talbott.ws (for Test::Unit) and www.zenspider.com/ZSS/Products/ZenTest. ZenTest-4.12.2/articles/Article.css0000444000004100000410000002676114661577214017156 0ustar www-datawww-data/* * Basic styles for core document typography. * * Influenced by: * - "How to size text using ems" [http://www.clagnut.com/blog/348] */ /*************************** Basic Typography ******************************/ a:link { color: black; } /* unvisited link */ a:visited { color: black; } /* visited link */ a:hover { color: black; } /* mouse over link */ a:active { color: black; } /* selected link */ html { background: white; color: black; text-align: center; /* IE hack to workaround for block centering. */ margin: 0; padding: 0; } body { margin: 0; padding: 0; font-family: Lucida Grande, sans-serif; } * { font-family: Lucida Grande, sans-serif; } img { vertical-align: middle; } p, li, dt, dd { line-height: 1.5; font-size: .95em; } p, ul, ol, dl { margin-left: 3em; margin-right: 2em; } p + p { text-indent: 1.4em; } dl { margin: 1em 3em; } dl dt a { font: 1.5em bold Lucida Grande, sans-serif; } dl dt { font-weight: bold; } dl dd { margin: 0em 3em; } ul { list-style: square outside; } li p { /* Markdown define paragraphs for list items. */ margin: 0; } /* * Ensure that nested items have the same size as their parent as we are * using (relative) em sizing. */ li li, li p, td p, blockquote p { font-size: 1em; } abbr, acronym { letter-spacing:0.1em font-variant: small-caps; } em { font-style: italic; } term { font-style: italic; } cite { font-style: italic; } input, select, th, td {font-size:1em} /******************************* Headers ********************************/ h1 { text-align: center; padding: .3em 2em 0em 2em; margin: 0; color: black; font-size: 1.3em; } h2 { margin-left: 0.4em; font-weight: bold; font-size: 1.2em; border-bottom: medium #5089da solid; } h3 { font-size: 1.2em; margin-left: .5em; margin-right: auto; color: #929292; border-bottom: thin solid #929292; } /************************ Editing / Authoring ********************/ .todo { background: red; color: yellow; font-weight: bold; } /************************ Samples, Input, Code, Commands ********************/ code { font-family: monospace; } kbd:before { content: open-quote; } kbd:after { content: close-quote; } code, kbd, var, pre { font-family: monaco, "Courier New", courier, monospace; font-size: 14px; } .command-box { clear: right; /* Side notes. */ border: 1px dotted #888; background: #151515; color: #eee; margin: .5em 2em .5em 3em; padding: .5em; text-align: left; font-family: monospace; -moz-border-radius: .5em; -webkit-border-radius: .5em; border-radius: .5em; } span.placeholder { font-style: italic; } span.placeholder:before { content: "<"; } span.placeholder:after { content: ">"; } .command-box span.placeholder { font-style: normal; color: #cc2; } .source-code-box { border: 1px dotted #888; background: #151515; color: #eee; margin: .5em 2em .5em 3em; padding: .5em; text-align: left; font-family: monospace; -moz-border-radius: .5em; -webkit-border-radius: .5em; border-radius: .5em; } .output-box { border: 1px dotted #888; background: #151515; color: #eee; margin: .5em 2em .5em 3em; padding: .5em; text-align: left; font-family: monospace; } .sample-box { border: 1px dotted #444; margin: .5em 2em .5em 3em; padding: .5em; text-align: left; } /* * Global styles for PH Web */ /* * Banner and Main navigation menu */ div.banner { margin: 0; padding: 0; float: left; width: 100%; font-size: 110%; line-height: normal; background: #5089da url( Main-Menu-Background-2.jpg ) repeat-y bottom left; border-bottom: thin #666 solid; } div.banner div.signature span.by { font: italic .7em Didot, serif; padding-right: .6em; text-shadow: .2em .2em .2em #222; } div.banner div.signature { float: right; color: gainsboro; font: 1.1em Didot, serif; margin: 0; padding: 60px .1em 0 0; vertical-align: baseline; } img#banner-logo { float: left; margin: .3em 6em .1em 1em; border: 0; } div.banner a#feed_link img { vertical-align: middle; border: 0; float: right; margin: .5em 1em; } div.banner a#contact_me_link img { vertical-align: middle; border: 0; float: right; margin: .5em 1em; } ul.section-menu { float: left; margin: 0 auto; padding: 0; width: 40em; list-style: none; } ul.section-menu li { display:block; float: left; margin: .8em 1px .5em 1px; padding: 1em 0; background: transparent url( 'Button Gradient.png' ) repeat-x center; text-shadow: .1em .1em .2em #444; } ul.section-menu li a:visited { color: gainsboro; } ul.section-menu li a { padding: 0 1em; margin: 0; font: bold .9em Lucida Grande, sans-serif; text-decoration: none; color: gainsboro; } ul.section-menu li.first a { padding-left: .6em; } ul.section-menu li.last a { padding-left: .6em; } ul.section-menu li a:hover { color: white; } ul.section-menu li#current a { color: #ff8; } div.content { float: left; background: url(Shore.jpg) no-repeat left top; width: 100%; padding: 0; padding-top: 1.3em; padding-bottom: 1.3em; text-align: left; clear: both; margin: 0; } p.Cartouche { float: right; width: 15em; margin: 0 2em 2em 3em; background: #5089da; padding: 1em; text-align: center; color: white; font-weight: bold; border: 0; text-indent: 0; -moz-border-radius: .5em; -webkit-border-radius: .5em; border-radius: .5em; -webkit-box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.5) } p.Cartouche a img { border: 0; } .Copyright { clear: both; text-align: center; font: italic 1em Lucida Grande, sans-serif; color: #444; margin-bottom: .2em; } .License { margin-top: 0; font: italic 1em Lucida Grande, sans-serif; color: #444; } a.discussion_link { text-decoration: none; } a.comment_counter { color: #5089da; padding: 0 1.5em; } h2.document_reference a { text-decoration: none; } span.tag_list { padding-left: 4em; font-weight: normal; } span.tag_list img { vertical-align: middle; } span.tag_list:before { content: '['; } span.tag_list:after { content: ']'; } p.read_more, p.read_more a { font-variant : small-caps; font-weight: bold; color: #5089da; } h2 span.post_date { padding-right: 1em; color: #5089da; } div#contact_form { margin: 2em; } div#contact_form label { margin-bottom: 100px; padding: 5em 1em 5em 0; } div#contact_form textarea { margin-top: 1.5em; width: 50em; } div.errorExplanation { background: #fbb; color: red; padding: 1em; margin: 1em; -moz-border-radius: 1em; -webkit-border-radius: 1em; border-radius: 1em; } div.errorExplanation h2 { font-size: 1em; border: 0; color: red; } /* * Round box */ table.box { table-layout: fixed; border-spacing: 0; border: none; } table.box tr, table.box tr td { padding: 0; margin: 0; border: none; } table.box tr.top, table.box tr.bottom { height: 30px; } table.box tr.bottom td { /* Fix for Safari who does not pick up height from the rule. */ height: 30px; } table.box tr td.left, table.box tr td.right { width: 30px; } table.box tr.top td.left { background: url('Blue Box Top Left.png') no-repeat top left; } table.box tr.top td.center { background: url('Blue Box Top.png') repeat-x top; } table.box tr.top td.right { background: url('Blue Box Top Right.png') no-repeat top right; } table.box tr.middle td.left { background: url('Blue Box Left.png') repeat-y left; } table.box tr.middle td.center { background: url('Blue Box Center.png') repeat; padding: 0px; } table.box tr.middle td.right { background: url('Blue Box Right.png') repeat-y right; } table.box tr.bottom td.left { background: url('Blue Box Bottom Left.png') no-repeat bottom left; } table.box tr.bottom td.center { background: url('Blue Box Bottom.png') repeat-x bottom; } table.box tr.bottom td.right { background: url('Blue Box Bottom Right.png') no-repeat bottom right; } /* * Styles for PH articles. */ div.content { float: left; width: 90%; padding: 0 3em; } div.content * { text-align: left; } div.content h1 { padding: 1em 2em 1em 2em; text-align: center; font-size: 1.7em; text-shadow: .1em .1em .2em #666; } div.content table.reference-table { margin-left: 4em; } div.content table.reference-table tr td, div.content table.reference-table tr th { padding: .3em 1em; text-align: left; } div.Header { margin: .5em 3em 1em 3em; padding: .5em 1em .5em 1em; background: #999; color: #fff; border: thin solid #888; text-align: center; } div.Author { padding: 0; margin: 0; color: #fff; font: italic 1em serif; } div.Author:before { content: "By " } div.LastUpdate:before { content: "Last significant update: " } p.LastUpdate { padding: 0; margin: -.5em 0 1.2em 0; color: #222; font: italic 1em sans-serif; text-align: center; } div.Abstract { margin: .5em 6em; background: #5089da; color: white; text-align: justify; padding: 1.2em 1.2em; -moz-border-radius: 1em; -webkit-border-radius: 1em; border-radius: 1em; -webkit-box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.5) } div.Abstract p { margin: 1em 0 0 0; padding: 0; } div.Abstract p.first { margin-top: 0; } div.Audience:before { content: "Audience: "; } div.Audience { margin: .5em 2em; color: #444; font: italic 1em Verdana sans-serif; text-align: justify; } h2 { margin-top: 1.5em; margin-left: 0em; font-weight: bold; font-size: 1.2em; border-bottom: medium #5089da solid; text-shadow: .1em .1em .2em #666; } h3 { font-size: 1.1em; margin-top: 1em; margin-left: 1em; margin-right: auto; color: #5089da; border-bottom: thin solid #929292; text-shadow: .1em .1em .2em #bbb; } h4 { font-size: 1em; font-weight: bold; margin-left: 2em; margin-right: auto; color: #000; border-bottom: thin #5089da solid; } div#what-s-next { margin: .5em 0em; background: #5089da; color: white; text-align: justify; padding: 1.5em 1em 1em 1em; -moz-border-radius: 1em; -webkit-border-radius: 1em; border-radius: 1em; -webkit-box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.5) } div#what-s-next h2 { color: white; padding-top: 0em; padding: 0; margin: 0; } div#what-s-next h3 { color: #060c14; border-color: #305282; } div.side-note { float: right; width: 23em; margin: 0 0 1.2em 2em; padding: 0; background: lightyellow; border: thin solid #a8a86d; } div.side-note .header { margin: .25em 1em; padding: 0 0 .3em 0; color: black; border-bottom: solid medium #5089da; } div.side-note p { margin: .5em 1em; } div.side-note .header p { text-align: center; font-weight: bold; margin: 0; padding: 0; } h2, h3 { clear: both; } div.maruku_toc ul { margin: 0 2em; padding: 0; } div.maruku_toc ul li ul { margin: 0 2em; padding: 0; } div.content li { margin-top: 1em; margin-bottom: 1em; } blockquote { color: #333; font-style: Italic; } .ruby .normal {} .ruby .comment { color: #005; font-style: italic; } .ruby .keyword { color: #A00; font-weight: bold; } .ruby .method { color: #077; } .ruby .class { color: #074; } .ruby .module { color: #050; } .ruby .punct { color: #447; font-weight: bold; } .ruby .symbol { color: #099; } .ruby .string { color: #944; background: #FFE; } .ruby .char { color: #F07; } .ruby .ident { color: #004; } .ruby .constant { color: #07F; } .ruby .regex { color: #B66; background: #FEF; } .ruby .number { color: #F99; } .ruby .attribute { color: #7BB; } .ruby .global { color: #7FB; } .ruby .expr { color: #227; } .ruby .escape { color: #277; } ZenTest-4.12.2/example1.rb0000444000004100000410000000012314661577214015274 0ustar www-datawww-datamodule Something class Thingy def do_something # ... end end end ZenTest-4.12.2/lib/0000755000004100000410000000000014661577214014007 5ustar www-datawww-dataZenTest-4.12.2/lib/focus.rb0000444000004100000410000000105314661577214015450 0ustar www-datawww-dataclass Module def focus *wanteds wanteds.map! { |m| m.to_s } unwanteds = public_instance_methods(false).grep(/test_/).map(&:to_s) unwanteds -= wanteds unwanteds.each do |unwanted| remove_method unwanted end end def focus_re regexp focus(*public_instance_methods.grep(regexp)) end def blur parent = self.superclass ObjectSpace.each_object Class do |klass| next unless parent > klass next if klass == self klass.send :focus klass.send :undef_method, :default_test end end end ZenTest-4.12.2/lib/functional_test_matrix.rb0000444000004100000410000000634714661577214021131 0ustar www-datawww-data ######################################################################## # The Idea: # # This is supposed to get us thinking about the various dimensions our # testing should address. If there are states orthogonal to each other # (eg. readable vs unreadable, logged in vs not logged in) each of # those states should comprise a dimension in the matrix. By # addressing it this way, we should be able to minimize the amount of # setup/teardown code and get full coverage across our actions for all # these edge cases and as a result have extremely clear tests. # ######################################################################## # Example Test Matrix Specification: # # matrix :example, :edge1, :edge2, :edge3, ... # action :action1, :OK, :e_NF, :mod, ... # action :action2, :OK, :e_RO, :na, ... # action ... # ######################################################################## # Matrix: # # I envision the setups being a code that combines the different # dimensions of edge case state. # # Something for a CMS might look like: `[df]_[ugo]_[rRwW]` where: # # + `[df]` for dir/file. # + and the rest is in the style of symbolic args to chmod: # + u/g/o = user, group, or other # + lowercase `X` == `X`able, uppercase `X` == un`X`able, where `X` # is read/write. # ######################################################################## # Action: # # :new/:err/:del are just examples, they should have semantic info # attached to them. # # Use :na to specify an inapplicable edge case for that action. # # Use :OK to specify the standard positive state. It is equivalent to # a result with the same name as the action. (eg # matrix_test_index). This cleans up the matrix a lot and allows for # narrower and more readable columns. # # Edge cases specific to an action that fall outside the matrix are # regular tests. # ######################################################################## # Matrix Methods (the legos): # # Everything else basically equates to lego pieces: # # + There is one "init" method per matrix: matrix_init_#{descr}(setup_args) # + There is one "setup" method per action: matrix_setup_#{action}(setup, expect) # + There is one "test" method per result: matrix_test_#{result}(setup) # # Thus, for the matrix "example" above, the top left-most test will # end up calling: # # matrix_init_example(:edge1) # matrix_setup_action1(:edge1, :new) # matrix_test_new(:edge1) # # Read the action method for exact details. ######################################################################## module FunctionalTestMatrix def matrix(name, *setups) @@matrix, @@setups = name, setups end def action(action, *results) testcases = @@setups.zip(results).reject { |a,b| b == :na } testcases = Hash[*testcases.flatten] matrix = @@matrix # bind to local scope for define_method closure testcases.each do |setup, expected| expected_action = expected == :OK ? action : expected define_method "test_#{matrix}_#{action}_#{setup}" do @action = action send "matrix_init_#{matrix}", *setup.to_s.split(/_/).map {|c| c.intern } send "matrix_setup_#{action}", setup, expected send "matrix_test_#{expected_action}", setup end end end module_function :matrix, :action end ZenTest-4.12.2/lib/zentest_mapping.rb0000444000004100000410000000613314661577214017544 0ustar www-datawww-data## # ZenTestMapping - mapping method names from impl to test. # # Method names are mapped bidirectionally in the following way: # # method test_method # method? test_method_eh (too much exposure to Canadians :) # method! test_method_bang # method= test_method_equals # [] test_index # * test_times # == test_equals2 # === test_equals3 # # Further, any of the test methods should be able to have arbitrary # extensions put on the name to distinguish edge cases: # # method test_method # method test_method_simple # method test_method_no_network # # To allow for unmapped test methods (ie, non-unit tests), name them: # # test_integration_.* module ZenTestMapping @@orig_method_map = { '!' => 'bang', '%' => 'percent', '&' => 'and', '*' => 'times', '**' => 'times2', '+' => 'plus', '-' => 'minus', '/' => 'div', '<' => 'lt', '<=' => 'lte', '<=>' => 'spaceship', "<\<" => 'lt2', '==' => 'equals2', '===' => 'equals3', '=~' => 'equalstilde', '>' => 'gt', '>=' => 'ge', '>>' => 'gt2', '+@' => 'unary_plus', '-@' => 'unary_minus', '[]' => 'index', '[]=' => 'index_equals', '^' => 'carat', '|' => 'or', '~' => 'tilde', } @@method_map = @@orig_method_map.merge(@@orig_method_map.invert) @@mapped_re = @@orig_method_map.values.sort_by { |k| k.length }.map {|s| Regexp.escape(s) }.reverse.join("|") def munge name name = name.to_s.dup is_cls_method = name.sub!(/^self\./, '') name = @@method_map[name] if @@method_map.has_key? name name = name.sub(/=$/, '_equals') name = name.sub(/\?$/, '_eh') name = name.sub(/\!$/, '_bang') name = yield name if block_given? name = "class_" + name if is_cls_method name end # Generates a test method name from a normal method, # taking into account names composed of metacharacters # (used for arithmetic, etc def normal_to_test name "test_#{munge name}" end def unmunge name name = name.to_s.dup is_cls_method = name.sub!(/^class_/, '') name = name.sub(/_equals(_.*)?$/, '=') unless name =~ /index/ name = name.sub(/_bang(_.*)?$/, '!') name = name.sub(/_eh(_.*)?$/, '?') name = name.sub(/^(#{@@mapped_re})(_.*)?$/) {$1} name = yield name if block_given? name = @@method_map[name] if @@method_map.has_key? name name = 'self.' + name if is_cls_method name end # Converts a method name beginning with test to its # corresponding normal method name, taking into account # symbolic names which may have been anglicised by # #normal_to_test(). def test_to_normal(name, klassname=nil) unmunge(name.to_s.sub(/^test_/, '')) do |n| if defined? @inherited_methods then known_methods = (@inherited_methods[klassname] || {}).keys.sort.reverse known_methods_re = known_methods.map {|s| Regexp.escape(s) }.join("|") n = n.sub(/^(#{known_methods_re})(_.*)?$/) { $1 } unless known_methods_re.empty? n end end end end ZenTest-4.12.2/lib/zentest.rb0000444000004100000410000004410614661577214016033 0ustar www-datawww-dataif (defined? RUBY_ENGINE) && RUBY_ENGINE == 'jruby' require 'java' JRuby.objectspace=true end $stdlib = {} if ObjectSpace.respond_to?(:loaded_classes, true) then ObjectSpace.loaded_classes(true).each do |m| $stdlib[m.name] = true if m.respond_to? :name end else ObjectSpace.each_object(Module) do |m| $stdlib[m.name] = true if m.respond_to? :name end end require 'zentest_mapping' $:.unshift( *$I.split(/:/) ) if defined? $I and String === $I $r = false unless defined? $r # reverse mapping for testclass names $t ||= false # test/unit instead of minitest if $r then # all this is needed because rails is retarded $-w = false $: << 'test' $: << 'lib' require 'config/environment' f = './app/controllers/application.rb' require f if test ?f, f end $TESTING = true class Module def zentest at_exit { ZenTest.autotest(self) } end end ## # ZenTest scans your target and unit-test code and writes your missing # code based on simple naming rules, enabling XP at a much quicker # pace. ZenTest only works with Ruby and Minitest or Test::Unit. # # == RULES # # ZenTest uses the following rules to figure out what code should be # generated: # # * Definition: # * CUT = Class Under Test # * TC = Test Class (for CUT) # * TC's name is the same as CUT w/ "Test" prepended at every scope level. # * Example: TestA::TestB vs A::B. # * CUT method names are used in CT, with "test_" prependend and optional "_ext" extensions for differentiating test case edge boundaries. # * Example: # * A::B#blah # * TestA::TestB#test_blah_normal # * TestA::TestB#test_blah_missing_file # * All naming conventions are bidirectional with the exception of test extensions. # # See ZenTestMapping for documentation on method naming. class ZenTest VERSION = "4.12.2" include ZenTestMapping if $TESTING then attr_reader :missing_methods attr_accessor :test_klasses attr_accessor :klasses attr_accessor :inherited_methods else def missing_methods; raise "Something is wack"; end end def initialize @result = [] @test_klasses = {} @klasses = {} @error_count = 0 @inherited_methods = Hash.new { |h,k| h[k] = {} } # key = klassname, val = hash of methods => true @missing_methods = Hash.new { |h,k| h[k] = {} } end # load_file wraps require, skipping the loading of $0. def load_file(file) puts "# loading #{file} // #{$0}" if $DEBUG unless file == $0 then begin require file rescue LoadError => err puts "Could not load #{file}: #{err}" end else puts "# Skipping loading myself (#{file})" if $DEBUG end end # obtain the class klassname def get_class(klassname) klass = nil begin klass = klassname.split(/::/).inject(Object) { |k,n| k.const_get n } puts "# found class #{klass.name}" if $DEBUG rescue NameError end if klass.nil? and not $TESTING then puts "Could not figure out how to get #{klassname}..." puts "Report to support-zentest@zenspider.com w/ relevant source" end return klass end # Get the public instance, class and singleton methods for # class klass. If full is true, include the methods from # Kernel and other modules that get included. The methods # suite, new, pretty_print, pretty_print_cycle will not # be included in the resuting array. def get_methods_for(klass, full=false) klass = self.get_class(klass) if klass.kind_of? String # WTF? public_instance_methods: default vs true vs false = 3 answers # to_s on all results if ruby >= 1.9 public_methods = klass.public_instance_methods(false) public_methods -= Kernel.methods unless full public_methods.map! { |m| m.to_s } public_methods -= %w(pretty_print pretty_print_cycle) klass_methods = klass.singleton_methods(full) klass_methods -= Class.public_methods(true) klass_methods = klass_methods.map { |m| "self.#{m}" } klass_methods -= %w(self.suite new) result = {} (public_methods + klass_methods).each do |meth| puts "# found method #{meth}" if $DEBUG result[meth] = true end return result end # Return the methods for class klass, as a hash with the # method nemas as keys, and true as the value for all keys. # Unless full is true, leave out the methods for Object which # all classes get. def get_inherited_methods_for(klass, full) klass = self.get_class(klass) if klass.kind_of? String klassmethods = {} if (klass.class.method_defined?(:superclass)) then superklass = klass.superclass if superklass then the_methods = superklass.instance_methods(true) # generally we don't test Object's methods... unless full then the_methods -= Object.instance_methods(true) the_methods -= Kernel.methods # FIX (true) - check 1.6 vs 1.8 end the_methods.each do |meth| klassmethods[meth.to_s] = true end end end return klassmethods end # Check the class klass is a testing class # (by inspecting its name). def is_test_class(klass) klass = klass.to_s klasspath = klass.split(/::/) a_bad_classpath = klasspath.find do |s| s !~ ($r ? /Test$/ : /^Test/) end return a_bad_classpath.nil? end # Generate the name of a testclass from non-test class # so that Foo::Blah => TestFoo::TestBlah, etc. It the # name is already a test class, convert it the other way. def convert_class_name(name) name = name.to_s if self.is_test_class(name) then if $r then name = name.gsub(/Test($|::)/, '\1') # FooTest::BlahTest => Foo::Blah else name = name.gsub(/(^|::)Test/, '\1') # TestFoo::TestBlah => Foo::Blah end else if $r then name = name.gsub(/($|::)/, 'Test\1') # Foo::Blah => FooTest::BlahTest else name = name.gsub(/(^|::)/, '\1Test') # Foo::Blah => TestFoo::TestBlah end end return name end # Does all the work of finding a class by name, # obtaining its methods and those of its superclass. # The full parameter determines if all the methods # including those of Object and mixed in modules # are obtained (true if they are, false by default). def process_class(klassname, full=false) klass = self.get_class(klassname) raise "Couldn't get class for #{klassname}" if klass.nil? klassname = klass.name # refetch to get full name is_test_class = self.is_test_class(klassname) target = is_test_class ? @test_klasses : @klasses # record public instance methods JUST in this class target[klassname] = self.get_methods_for(klass, full) # record ALL instance methods including superclasses (minus Object) # Only minus Object if full is true. @inherited_methods[klassname] = self.get_inherited_methods_for(klass, full) return klassname end # Work through files, collecting class names, method names # and assertions. Detects ZenTest (SKIP|FULL) comments # in the bodies of classes. # For each class a count of methods and test methods is # kept, and the ratio noted. def scan_files(*files) assert_count = Hash.new(0) method_count = Hash.new(0) klassname = nil files.each do |path| is_loaded = false # if reading stdin, slurp the whole thing at once file = (path == "-" ? $stdin.read : File.new(path)) file.each_line do |line| if klassname then case line when /^\s*def/ then method_count[klassname] += 1 when /assert|flunk/ then assert_count[klassname] += 1 end end if line =~ /^\s*(?:class|module)\s+([\w:]+)/ then klassname = $1 if line =~ /\#\s*ZenTest SKIP/ then klassname = nil next end full = false if line =~ /\#\s*ZenTest FULL/ then full = true end unless is_loaded then unless path == "-" then self.load_file(path) else eval file, TOPLEVEL_BINDING end is_loaded = true end begin klassname = self.process_class(klassname, full) rescue puts "# Couldn't find class for name #{klassname}" next end # Special Case: ZenTest is already loaded since we are running it if klassname == "TestZenTest" then klassname = "ZenTest" self.process_class(klassname, false) end end # if /class/ end # IO.foreach end # files result = [] method_count.each_key do |classname| entry = {} next if is_test_class(classname) testclassname = convert_class_name(classname) a_count = assert_count[testclassname] m_count = method_count[classname] ratio = a_count.to_f / m_count.to_f * 100.0 entry['n'] = classname entry['r'] = ratio entry['a'] = a_count entry['m'] = m_count result.push entry end sorted_results = result.sort { |a,b| b['r'] <=> a['r'] } @result.push sprintf("# %25s: %4s / %4s = %6s%%", "classname", "asrt", "meth", "ratio") sorted_results.each do |e| @result.push sprintf("# %25s: %4d / %4d = %6.2f%%", e['n'], e['a'], e['m'], e['r']) end end # Adds a missing method to the collected results. def add_missing_method(klassname, methodname) @result.push "# ERROR method #{klassname}\##{methodname} does not exist (1)" if $DEBUG and not $TESTING @error_count += 1 @missing_methods[klassname][methodname] = true end # looks up the methods and the corresponding test methods # in the collection already built. To reduce duplication # and hide implementation details. def methods_and_tests(klassname, testklassname) return @klasses[klassname], @test_klasses[testklassname] end # Checks, for the given class klassname, that each method # has a corrsponding test method. If it doesn't this is # added to the information for that class def analyze_impl(klassname) testklassname = self.convert_class_name(klassname) if @test_klasses[testklassname] then _, testmethods = methods_and_tests(klassname, testklassname) # check that each method has a test method @klasses[klassname].each_key do | methodname | testmethodname = normal_to_test(methodname) unless testmethods[testmethodname] then begin unless testmethods.keys.find { |m| m =~ /#{testmethodname}(_\w+)+$/ } then self.add_missing_method(testklassname, testmethodname) end rescue RegexpError puts "# ERROR trying to use '#{testmethodname}' as a regex. Look at #{klassname}.#{methodname}" end end # testmethods[testmethodname] end # @klasses[klassname].each_key else # ! @test_klasses[testklassname] puts "# ERROR test class #{testklassname} does not exist" if $DEBUG @error_count += 1 @klasses[klassname].keys.each do | methodname | self.add_missing_method(testklassname, normal_to_test(methodname)) end end # @test_klasses[testklassname] end # For the given test class testklassname, ensure that all # the test methods have corresponding (normal) methods. # If not, add them to the information about that class. def analyze_test(testklassname) klassname = self.convert_class_name(testklassname) # CUT might be against a core class, if so, slurp it and analyze it if $stdlib[klassname] then self.process_class(klassname, true) self.analyze_impl(klassname) end if @klasses[klassname] then methods, testmethods = methods_and_tests(klassname,testklassname) # check that each test method has a method testmethods.each_key do | testmethodname | if testmethodname =~ /^test_(?!integration_)/ then # try the current name methodname = test_to_normal(testmethodname, klassname) orig_name = methodname.dup found = false until methodname == "" or methods[methodname] or @inherited_methods[klassname][methodname] do # try the name minus an option (ie mut_opt1 -> mut) if methodname.sub!(/_[^_]+$/, '') then if methods[methodname] or @inherited_methods[klassname][methodname] then found = true end else break # no more substitutions will take place end end # methodname == "" or ... unless found or methods[methodname] or methodname == "initialize" then self.add_missing_method(klassname, orig_name) end else # not a test_.* method unless testmethodname =~ /^util_/ then puts "# WARNING Skipping #{testklassname}\##{testmethodname}" if $DEBUG end end # testmethodname =~ ... end # testmethods.each_key else # ! @klasses[klassname] puts "# ERROR class #{klassname} does not exist" if $DEBUG @error_count += 1 @test_klasses[testklassname].keys.each do |testmethodname| @missing_methods[klassname][test_to_normal(testmethodname)] = true end end # @klasses[klassname] end # create a given method at a given # indentation. Returns an array containing # the lines of the method. def create_method(indentunit, indent, name) meth = [] meth.push indentunit*indent + "def #{name}" meth.last << "(*args)" unless name =~ /^test/ indent += 1 meth.push indentunit*indent + "raise NotImplementedError, 'Need to write #{name}'" indent -= 1 meth.push indentunit*indent + "end" return meth end # Walk each known class and test that each method has # a test method # Then do it in the other direction... def analyze # walk each known class and test that each method has a test method @klasses.each_key do |klassname| self.analyze_impl(klassname) end # now do it in the other direction... @test_klasses.each_key do |testklassname| self.analyze_test(testklassname) end end # Using the results gathered during analysis # generate skeletal code with methods raising # NotImplementedError, so that they can be filled # in later, and so the tests will fail to start with. def generate_code @result.unshift "# Code Generated by ZenTest v. #{VERSION}" if $DEBUG then @result.push "# found classes: #{@klasses.keys.join(', ')}" @result.push "# found test classes: #{@test_klasses.keys.join(', ')}" end if @missing_methods.size > 0 then @result.push "" if $t then @result.push "require 'test/unit/testcase'" @result.push "require 'test/unit' if $0 == __FILE__" else @result.push "require 'minitest/autorun'" end @result.push "" end indentunit = " " @missing_methods.keys.sort.each do |fullklasspath| methods = @missing_methods[fullklasspath] cls_methods = methods.keys.grep(/^(self\.|test_class_)/) methods.delete_if {|k,v| cls_methods.include? k } next if methods.empty? and cls_methods.empty? indent = 0 is_test_class = self.is_test_class(fullklasspath) clsname = $t ? "Test::Unit::TestCase" : "Minitest::Test" superclass = is_test_class ? " < #{clsname}" : '' @result.push indentunit*indent + "class #{fullklasspath}#{superclass}" indent += 1 meths = [] cls_methods.sort.each do |method| meth = create_method(indentunit, indent, method) meths.push meth.join("\n") end methods.keys.sort.each do |method| next if method =~ /pretty_print/ meth = create_method(indentunit, indent, method) meths.push meth.join("\n") end @result.push meths.join("\n\n") indent -= 1 @result.push indentunit*indent + "end" @result.push '' end @result.push "# Number of errors detected: #{@error_count}" @result.push '' end # presents results in a readable manner. def result return @result.join("\n") end # Provide a certain amount of help. def self.usage puts <<-EO_USAGE usage: #{File.basename $0} [options] test-and-implementation-files... ZenTest scans your target and unit-test code and writes your missing code based on simple naming rules, enabling XP at a much quicker pace. ZenTest only works with Ruby and Minitest or Test::Unit. ZenTest uses the following rules to figure out what code should be generated: * Definition: * CUT = Class Under Test * TC = Test Class (for CUT) * TC's name is the same as CUT w/ "Test" prepended at every scope level. * Example: TestA::TestB vs A::B. * CUT method names are used in CT, with "test_" prependend and optional "_ext" extensions for differentiating test case edge boundaries. * Example: * A::B#blah * TestA::TestB#test_blah_normal * TestA::TestB#test_blah_missing_file * All naming conventions are bidirectional with the exception of test extensions. options: -h display this information -v display version information -r Reverse mapping (ClassTest instead of TestClass) -e (Rapid XP) eval the code generated instead of printing it -t test/unit generation (default is minitest). EO_USAGE end # Give help, then quit. def self.usage_with_exit self.usage exit 0 end # Runs ZenTest over all the supplied files so that # they are analysed and the missing methods have # skeleton code written. # If no files are supplied, splutter out some help. def self.fix(*files) ZenTest.usage_with_exit if files.empty? zentest = ZenTest.new zentest.scan_files(*files) zentest.analyze zentest.generate_code return zentest.result end # Process all the supplied classes for methods etc, # and analyse the results. Generate the skeletal code # and eval it to put the methods into the runtime # environment. def self.autotest(*klasses) zentest = ZenTest.new klasses.each do |klass| zentest.process_class(klass) end zentest.analyze zentest.missing_methods.each do |klass,methods| methods.each do |method,x| warn "autotest generating #{klass}##{method}" end end zentest.generate_code code = zentest.result puts code if $DEBUG Object.class_eval code end end ZenTest-4.12.2/lib/unit_diff.rb0000444000004100000410000001572714661577214016315 0ustar www-datawww-datarequire 'tempfile' require 'rbconfig' ## # UnitDiff makes reading Test::Unit output easy and fun. Instead of a # confusing jumble of text with nearly unnoticable changes like this: # # 1) Failure: # test_to_gpoints(RouteTest) [test/unit/route_test.rb:29]: # <"new GPolyline([\n new GPoint( 47.00000, -122.00000),\n new GPoint( 46.5000 # 0, -122.50000),\n new GPoint( 46.75000, -122.75000),\n new GPoint( 46.00000, # -123.00000)])"> expected but was # <"new Gpolyline([\n new GPoint( 47.00000, -122.00000),\n new GPoint( 46.5000 # 0, -122.50000),\n new GPoint( 46.75000, -122.75000),\n new GPoint( 46.00000, # -123.00000)])">. # # # You get an easy-to-read diff output like this: # # 1) Failure: # test_to_gpoints(RouteTest) [test/unit/route_test.rb:29]: # 1c1 # < new GPolyline([ # --- # > new Gpolyline([ # # == Usage # # test.rb | unit_diff [options] # options: # -b ignore whitespace differences # -c contextual diff # -h show usage # -k keep temp diff files around # -l prefix line numbers on the diffs # -u unified diff [default] # -p plain diff # -v display version class UnitDiff WINDOZE = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ DIFF = if WINDOZE 'diff.exe' else if system("gdiff", __FILE__, __FILE__) 'gdiff' # solaris and kin suck else 'diff' end end unless defined? DIFF ## # Handy wrapper for UnitDiff#unit_diff. def self.unit_diff trap 'INT' do exit 1 end puts UnitDiff.new.unit_diff end def parse_input(input, output) current = [] data = [] data << current print_lines = true term = "\nFinished".split(//).map { |c| c[0] } term_length = term.size old_sync = output.sync output.sync = true while line = input.gets case line when /^(Loaded suite|Started|# Running tests:)/ then print_lines = true output.puts line chars = [] while c = input.getc do output.putc c chars << c tail = chars[-term_length..-1] break if chars.size >= term_length and tail == term end output.puts input.gets # the rest of "Finished in..." output.puts next when /^\s*$/, /^\(?\s*\d+\) (Failure|Error):/, /^\d+\)/ then print_lines = false current = [] data << current when /^Finished in \d/ then print_lines = false end output.puts line if print_lines current << line end output.sync = old_sync data = data.reject { |o| o == ["\n"] or o.empty? } footer = data.pop data.map do |result| break if result.any? { |l| l =~ / expected( but was|, not)/ } header = result.find do |l| l =~ /^\(?\s*\d+\) (Failure|Error):/ end break unless header message_index = result.index(header) + 2 result[message_index..-1] = result[message_index..-1].join end return data, footer end # Parses a single diff recording the header and what # was expected, and what was actually obtained. def parse_diff(result) header = [] expect = [] butwas = [] footer = [] state = :header until result.empty? do case state when :header then header << result.shift state = :expect if result.first =~ /^<|^Expected/ when :expect then case result.first when /^Expected (.*?) to equal (.*?):$/ then expect << $1 butwas << $2 state = :footer result.shift when /^Expected (.*?), not (.*)$/m then expect << $1 butwas << $2 state = :footer result.shift when /^Expected (.*?)$/ then expect << "#{$1}\n".dup result.shift when /^to equal / then state = :spec_butwas bw = result.shift.sub(/^to equal (.*):?$/, '\1') butwas << bw else ex = result.shift.dup state = :butwas if ex.sub!(/ expected( but was|, not)/, '') expect << ex end when :butwas then butwas = result[0..-1].map(&:dup) result.clear when :spec_butwas then if result.first =~ /^\s+\S+ at |^:\s*$/ state = :footer else butwas << result.shift.dup end when :footer then butwas.last.sub!(/:$/, '') footer = result.map {|l| l.chomp } result.clear else raise "unknown state #{state}" end end return header, expect, nil, footer if butwas.empty? expect.last.chomp! expect.first.sub!(/^<\"/, '') expect.last.sub!(/\">$/, '') butwas.last.chomp! butwas.last.chop! if butwas.last =~ /\.$/ butwas.first.sub!( /^<\"/, '') butwas.last.sub!(/\">$/, '') return header, expect, butwas, footer end ## # Scans Test::Unit output +input+ looking for comparison failures and makes # them easily readable by passing them through diff. def unit_diff(input=ARGF, output=$stdout) $b = false unless defined? $b $c = false unless defined? $c $k = false unless defined? $k $u = true unless defined? $u $p = false unless defined? $p data, footer = self.parse_input(input, output) output = [] # Output data.each do |result| if result.first =~ /Error/ then output.push result.join('') next end prefix, expect, butwas, result_footer = parse_diff(result) output.push prefix.compact.map {|line| line.strip}.join("\n") if butwas then output.push self.diff(expect, butwas) output.push result_footer output.push '' else output.push expect.join('') end end if footer then footer.shift if footer.first.strip.empty? output.push footer.compact.map {|line| line.strip}.join("\n") end return output.flatten.join("\n") end def diff expect, butwas output = nil Tempfile.open("expect") do |a| a.write(massage(expect)) a.rewind Tempfile.open("butwas") do |b| b.write(massage(butwas)) b.rewind diff_flags = $p ? "" : $c ? "-c" : "-u" diff_flags += " -b" if $b result = `#{DIFF} #{diff_flags} #{a.path} #{b.path}` result.sub!(/^\-\-\- .+/, "--- expected") result.sub!(/^\+\+\+ .+/, "+++ actual") output = if result.empty? then "[no difference--suspect ==]" else result.split(/\n/) end if $k then warn "moving #{a.path} to #{a.path}.keep" File.rename a.path, a.path + ".keep" warn "moving #{b.path} to #{b.path}.keep" File.rename b.path, b.path + ".keep" end end end output end def massage(data) # unescape newlines, strip <> from entire string data = data.join data = data.gsub(/\\n/, "\n").gsub(/0x[a-f0-9]+/m, '0xXXXXXX') + "\n" data += "\n" unless data[-1] == ?\n data end end ZenTest-4.12.2/test/0000755000004100000410000000000014661577214014220 5ustar www-datawww-dataZenTest-4.12.2/test/test_zentest.rb0000555000004100000410000004125014661577214017303 0ustar www-datawww-data#!/usr/local/bin/ruby -w abort "rubinius does not support features required by zentest" if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /rbx/ $TESTING = true require 'rubygems' require 'minitest/autorun' # I do this so I can still run ZenTest against the tests and itself... require 'zentest' unless defined? $ZENTEST # These are just classes set up for quick testing. # TODO: need to test a compound class name Mod::Cls class Cls1 # ZenTest SKIP def meth1; end def self.meth2; end end class TestCls1 # ZenTest SKIP def setup; end def teardown; end def test_meth1; end def test_meth2; assert(true, "something"); end end class SuperDuper # ZenTest SKIP def self.cls_inherited; end def inherited; end def overridden; end end class LowlyOne < SuperDuper # ZenTest SKIP def self.cls_xtended; end def overridden; end def xtended; end # renamed because maglev defines it globally :/ def pretty_print; end def pretty_print_cycle; end end # This is the good case where there are no missing methods on either side. class Blah0 def missingtest; end def notmissing1; end def notmissing2; end # found by zentest on testcase1.rb def missingimpl; end end class TestBlah0 def setup; end def teardown; end def test_notmissing1 assert(true, "a test") end def test_notmissing2_ext1 assert(true, "a test") end def test_notmissing2_ext2 flunk("a failed test") end def test_missingimpl; end def test_missingtest; end end class Blah1 def missingtest; end def notmissing1; end def notmissing2; end end class TestBlah1 def test_notmissing1; end def test_notmissing2_ext1; end def test_notmissing2_ext2; end def test_missingimpl; Blah1.new.missingimpl; end def test_integration_blah1; end def test_integration_blah2; end def test_integration_blah3; end end module Something2 class Blah2 def missingtest; end def notmissing1; end def notmissing2; end end end module TestSomething2 class TestBlah2 def test_notmissing1; end def test_notmissing2_ext1; end def test_notmissing2_ext2; end def test_missingimpl; end end end # only test classes class TestBlah3 def test_missingimpl; end end # only regular classes class Blah4 def missingtest1; end def missingtest2; end end # subclassing a builtin class class MyHash5 < Hash def []; end def missingtest1; end end # nested class module MyModule6 class MyClass6 def []; end def missingtest1; end end end # nested class module MyModule7; end # in 1.9+ you'll not need this class MyModule7::MyClass7 def []; end def missingtest1; end end class MyClass8 def self.foobar; end def MyClass8.foobaz; end end class TestTrueClass; end class TestZenTest < Minitest::Test def setup @tester = ZenTest.new() end ############################################################ # Utility Methods def util_simple_setup @tester.klasses = { "Something" => { "method1" => true, "method1!" => true, "method1=" => true, "method1?" => true, "attrib" => true, "attrib=" => true, "equal?" => true, "self.method3" => true, "self.[]" => true, }, } @tester.test_klasses = { "TestSomething" => { "test_class_method4" => true, "test_method2" => true, "setup" => true, "teardown" => true, "test_class_index" => true, }, } @tester.inherited_methods = @tester.test_klasses.merge(@tester.klasses) @generated_code = " require 'minitest/autorun' class Something def self.method4(*args) raise NotImplementedError, 'Need to write self.method4' end def method2(*args) raise NotImplementedError, 'Need to write method2' end end class TestSomething < Minitest::Test def test_class_method3 raise NotImplementedError, 'Need to write test_class_method3' end def test_attrib raise NotImplementedError, 'Need to write test_attrib' end def test_attrib_equals raise NotImplementedError, 'Need to write test_attrib_equals' end def test_equal_eh raise NotImplementedError, 'Need to write test_equal_eh' end def test_method1 raise NotImplementedError, 'Need to write test_method1' end def test_method1_bang raise NotImplementedError, 'Need to write test_method1_bang' end def test_method1_eh raise NotImplementedError, 'Need to write test_method1_eh' end def test_method1_equals raise NotImplementedError, 'Need to write test_method1_equals' end end # Number of errors detected: 10 " end ############################################################ # Accessors & Adders: def test_initialize refute_nil(@tester, "Tester must be initialized") # TODO: should do more at this stage end ############################################################ # Converters and Testers: def test_is_test_class # classes assert(@tester.is_test_class(TestCls1), "All test classes must start with Test") assert(!@tester.is_test_class(Cls1), "Classes not starting with Test must not be test classes") # strings assert(@tester.is_test_class("TestCls1"), "All test classes must start with Test") assert(@tester.is_test_class("TestMod::TestCls1"), "All test modules must start with test as well") assert(!@tester.is_test_class("Cls1"), "Classes not starting with Test must not be test classes") assert(!@tester.is_test_class("NotTestMod::TestCls1"), "Modules not starting with Test must not be test classes") assert(!@tester.is_test_class("NotTestMod::NotTestCls1"), "All names must start with Test to be test classes") end def test_is_test_class_reversed old = $r $r = true assert(@tester.is_test_class("Cls1Test"), "Reversed: All test classes must end with Test") assert(@tester.is_test_class("ModTest::Cls1Test"), "Reversed: All test classes must end with Test") assert(!@tester.is_test_class("TestMod::TestCls1"), "Reversed: All test classes must end with Test") $r = old end def test_convert_class_name assert_equal('Cls1', @tester.convert_class_name(TestCls1)) assert_equal('TestCls1', @tester.convert_class_name(Cls1)) assert_equal('Cls1', @tester.convert_class_name('TestCls1')) assert_equal('TestCls1', @tester.convert_class_name('Cls1')) assert_equal('TestModule::TestCls1', @tester.convert_class_name('Module::Cls1')) assert_equal('Module::Cls1', @tester.convert_class_name('TestModule::TestCls1')) end def test_convert_class_name_reversed old = $r $r = true assert_equal('Cls1', @tester.convert_class_name("Cls1Test")) assert_equal('Cls1Test', @tester.convert_class_name(Cls1)) assert_equal('Cls1', @tester.convert_class_name('Cls1Test')) assert_equal('Cls1Test', @tester.convert_class_name('Cls1')) assert_equal('ModuleTest::Cls1Test', @tester.convert_class_name('Module::Cls1')) assert_equal('Module::Cls1', @tester.convert_class_name('ModuleTest::Cls1Test')) $r = old end ############################################################ # Missing Classes and Methods: def test_missing_methods_empty missing = @tester.missing_methods assert_equal({}, missing) end def test_add_missing_method_normal @tester.add_missing_method("SomeClass", "some_method") missing = @tester.missing_methods assert_equal({"SomeClass" => { "some_method" => true } }, missing) end def test_add_missing_method_duplicates @tester.add_missing_method("SomeClass", "some_method") @tester.add_missing_method("SomeClass", "some_method") @tester.add_missing_method("SomeClass", "some_method") missing = @tester.missing_methods assert_equal({"SomeClass" => { "some_method" => true } }, missing) end def test_analyze_simple self.util_simple_setup @tester.analyze missing = @tester.missing_methods expected = { "Something" => { "method2" => true, "self.method4" => true, }, "TestSomething" => { "test_class_method3" => true, "test_attrib" => true, "test_attrib_equals" => true, "test_equal_eh" => true, "test_method1" => true, "test_method1_eh"=>true, "test_method1_bang"=>true, "test_method1_equals"=>true, } } assert_equal(expected, missing) end def test_create_method list = @tester.create_method(" ", 1, "wobble") assert_equal([" def wobble(*args)", " raise NotImplementedError, 'Need to write wobble'", " end"],list) end def test_methods_and_tests @tester.process_class("ZenTest") @tester.process_class("TestZenTest") m,t = @tester.methods_and_tests("ZenTest", "TestZenTest") assert(m.include?("methods_and_tests")) assert(t.include?("test_methods_and_tests")) end def test_generate_code_simple self.util_simple_setup @tester.analyze str = @tester.generate_code[1..-1].join("\n") exp = @generated_code assert_equal(exp, str) end def test_get_class_good assert_equal(Object, @tester.get_class("Object")) end def test_get_class_bad assert_nil(@tester.get_class("ZZZObject")) end def test_get_inherited_methods_for_subclass expect = { "inherited" => true, "overridden" => true } result = @tester.get_inherited_methods_for("LowlyOne", false) assert_equal(expect, result) end def test_get_inherited_methods_for_subclass_full expect = Object.instance_methods + %w( inherited overridden ) expect.map! { |m| m.to_s } result = @tester.get_inherited_methods_for("LowlyOne", true) assert_equal(expect.sort, result.keys.sort) end def test_get_inherited_methods_for_superclass expect = { } result = @tester.get_inherited_methods_for("SuperDuper", false) assert_equal(expect.keys.sort, result.keys.sort) end def test_get_inherited_methods_for_superclass_full expect = Object.instance_methods.map { |m| m.to_s } result = @tester.get_inherited_methods_for("SuperDuper", true) assert_equal(expect.sort, result.keys.sort) end def test_get_methods_for_subclass expect = { "self.cls_xtended" => true, "overridden" => true, "xtended" => true } result = @tester.get_methods_for("LowlyOne") assert_equal(expect, result) end def test_get_methods_for_subclass_full expect = { "self.cls_inherited" => true, "self.cls_xtended" => true, "overridden" => true, "xtended" => true } result = @tester.get_methods_for("LowlyOne", true) assert_equal(expect, result) end def test_get_methods_for_superclass expect = { "self.cls_inherited" => true, "overridden" => true, "inherited" => true } result = @tester.get_methods_for("SuperDuper") assert_equal(expect, result) end def test_result self.util_simple_setup @tester.analyze @tester.generate_code str = @tester.result.split($/, 2).last exp = @generated_code assert_equal(exp, str) end def test_load_file skip 'Need to write test_load_file' end def test_scan_files skip 'Need to write test_scan_files' end def test_process_class assert_equal({}, @tester.klasses) assert_equal({}, @tester.test_klasses) assert_equal({}, @tester.inherited_methods["SuperDuper"]) @tester.process_class("SuperDuper") assert_equal({"SuperDuper"=> { "self.cls_inherited"=>true, "inherited"=>true, "overridden"=>true}}, @tester.klasses) assert_equal({}, @tester.test_klasses) assert_equal({}, @tester.inherited_methods["SuperDuper"]) end def test_klasses_equals self.util_simple_setup assert_equal({"Something"=> { "self.method3"=>true, "equal?"=>true, "attrib="=>true, "self.[]"=>true, "method1"=>true, "method1="=>true, "method1?"=>true, "method1!"=>true, "attrib"=>true}}, @tester.klasses) @tester.klasses= {"whoopie" => {}} assert_equal({"whoopie"=> {}}, @tester.klasses) end # REFACTOR: this should probably be cleaned up and on ZenTest side def util_testcase(*klasses) zentest = ZenTest.new klasses.each do |klass| zentest.process_class(klass) end zentest.analyze zentest.generate_code return zentest.result.split("\n")[1..-1].join("\n") end def test_testcase0 expected = '# Number of errors detected: 0' assert_equal expected, util_testcase("Blah0", "TestBlah0") end HEADER = "\nrequire 'minitest/autorun'\n\n" def test_testcase1 expected = "#{HEADER}class Blah1\n def missingimpl(*args)\n raise NotImplementedError, 'Need to write missingimpl'\n end\nend\n\nclass TestBlah1 < Minitest::Test\n def test_missingtest\n raise NotImplementedError, 'Need to write test_missingtest'\n end\nend\n\n# Number of errors detected: 2" assert_equal expected, util_testcase("Blah1", "TestBlah1") end def test_testcase2 expected = "#{HEADER}class Something2::Blah2\n def missingimpl(*args)\n raise NotImplementedError, 'Need to write missingimpl'\n end\nend\n\nclass TestSomething2::TestBlah2 < Minitest::Test\n def test_missingtest\n raise NotImplementedError, 'Need to write test_missingtest'\n end\nend\n\n# Number of errors detected: 2" assert_equal expected, util_testcase("Something2::Blah2", "TestSomething2::TestBlah2") end def test_testcase3 expected = "#{HEADER}class Blah3\n def missingimpl(*args)\n raise NotImplementedError, 'Need to write missingimpl'\n end\nend\n\n# Number of errors detected: 1" assert_equal expected, util_testcase("TestBlah3") end def test_testcase4 expected = "#{HEADER}class TestBlah4 < Minitest::Test\n def test_missingtest1\n raise NotImplementedError, 'Need to write test_missingtest1'\n end\n\n def test_missingtest2\n raise NotImplementedError, 'Need to write test_missingtest2'\n end\nend\n\n# Number of errors detected: 3" assert_equal expected, util_testcase("Blah4") end def test_testcase5 expected = "#{HEADER}class TestMyHash5 < Minitest::Test\n def test_index\n raise NotImplementedError, 'Need to write test_index'\n end\n\n def test_missingtest1\n raise NotImplementedError, 'Need to write test_missingtest1'\n end\nend\n\n# Number of errors detected: 3" assert_equal expected, util_testcase("MyHash5") end def test_testcase6 expected = "#{HEADER}class TestMyModule6::TestMyClass6 < Minitest::Test\n def test_index\n raise NotImplementedError, 'Need to write test_index'\n end\n\n def test_missingtest1\n raise NotImplementedError, 'Need to write test_missingtest1'\n end\nend\n\n# Number of errors detected: 3" assert_equal expected, util_testcase("MyModule6::MyClass6") end def test_testcase7 expected = "#{HEADER}class TestMyModule7::TestMyClass7 < Minitest::Test\n def test_index\n raise NotImplementedError, 'Need to write test_index'\n end\n\n def test_missingtest1\n raise NotImplementedError, 'Need to write test_missingtest1'\n end\nend\n\n# Number of errors detected: 3" assert_equal expected, util_testcase("MyModule7::MyClass7") end def test_testcase8 expected = "#{HEADER}class TestMyClass8 < Minitest::Test\n def test_class_foobar\n raise NotImplementedError, 'Need to write test_class_foobar'\n end\n\n def test_class_foobaz\n raise NotImplementedError, 'Need to write test_class_foobaz'\n end\nend\n\n# Number of errors detected: 3" assert_equal expected, util_testcase("MyClass8") end def test_testcase9 # stupid YAML is breaking my tests. Enters via Test::Rails. order dependent. if defined? YAML then TrueClass.send :remove_method, :taguri, :taguri=, :to_yaml rescue nil end pims = TrueClass.public_instance_methods(false) TrueClass.send :remove_method, :inspect if pims.include? :inspect # 2.0 only is_ruby_23 = RUBY_VERSION >= "2.3" count = "4" extra = "" if is_ruby_23 then count = "5" extra = " def test_equals3\n raise NotImplementedError, 'Need to write test_equals3'\n end\n\n" end expected = "#{HEADER}class TestTrueClass < Minitest::Test\n def test_and\n raise NotImplementedError, 'Need to write test_and'\n end\n\n def test_carat\n raise NotImplementedError, 'Need to write test_carat'\n end\n\n#{extra} def test_or\n raise NotImplementedError, 'Need to write test_or'\n end\n\n def test_to_s\n raise NotImplementedError, 'Need to write test_to_s'\n end\nend\n\n# Number of errors detected: #{count}" assert_equal expected, util_testcase("TestTrueClass") end end ZenTest-4.12.2/test/test_unit_diff.rb0000555000004100000410000003371314661577214017563 0ustar www-datawww-data#!/usr/local/bin/ruby -w require 'rubygems' require 'minitest/autorun' require 'stringio' $TESTING = true require 'unit_diff' class TestUnitDiff < Minitest::Test def setup @diff = UnitDiff.new end def test_input header = "Loaded suite ./blah\nStarted\nFF\nFinished in 0.035332 seconds.\n\n" input = "#{header} 1) Failure:\ntest_test1(TestBlah) [./blah.rb:25]:\n<\"line1\\nline2\\nline3\\n\"> expected but was\n<\"line4\\nline5\\nline6\\n\">.\n\n 2) Failure:\ntest_test2(TestBlah) [./blah.rb:29]:\n<\"line1\"> expected but was\n<\"line2\\nline3\\n\\n\">.\n\n2 tests, 2 assertions, 2 failures, 0 errors\n" # TODO: I think I'd like a separate footer array as well expected = [[[" 1) Failure:\n", "test_test1(TestBlah) [./blah.rb:25]:\n", "<\"line1\\nline2\\nline3\\n\"> expected but was\n", "<\"line4\\nline5\\nline6\\n\">.\n"], [" 2) Failure:\n", "test_test2(TestBlah) [./blah.rb:29]:\n", "<\"line1\"> expected but was\n", "<\"line2\\nline3\\n\\n\">.\n"]], ["\n", "2 tests, 2 assertions, 2 failures, 0 errors\n"]] util_unit_diff(header, input, expected, :parse_input) end def test_input_miniunit header = "Loaded suite -e\nStarted\nF\nFinished in 0.035332 seconds.\n\n" input = "#{header} 1) Failure: test_blah(TestBlah) [./blah.rb:25]: Expected ['a', 'b', 'c'], not ['a', 'c', 'b']. 1 tests, 1 assertions, 1 failures, 0 errors " expected = [[[" 1) Failure:\n", "test_blah(TestBlah) [./blah.rb:25]:\n", "Expected ['a', 'b', 'c'], not ['a', 'c', 'b'].\n"]], ["\n", "1 tests, 1 assertions, 1 failures, 0 errors\n"]] util_unit_diff(header, input, expected, :parse_input) end def test_input_miniunit_multiline header = "Loaded suite -e\nStarted\nF\nFinished in 0.035332 seconds.\n\n" input = "#{header} 1) Failure: test_blah(TestBlah) [./blah.rb:25]: Expected ['a', 'b', 'c'], not ['a', 'c', 'b']. 1 tests, 1 assertions, 1 failures, 0 errors " expected = [[[" 1) Failure:\n", "test_blah(TestBlah) [./blah.rb:25]:\n", "Expected ['a',\n 'b',\n 'c'], not ['a',\n 'c',\n 'b'].\n"]], ["\n", "1 tests, 1 assertions, 1 failures, 0 errors\n"]] util_unit_diff(header, input, expected, :parse_input) end def test_input_mspec header = <<-HEADER Started .......F Finished in 0.1 seconds HEADER failure = <<-FAILURE 1) The unless expression should fail FAILED Expected nil to equal "baz": FAILURE backtrace = <<-BACKTRACE PositiveExpectation#== at spec/mspec.rb:217 main.__script__ {} at spec/language/unless_spec.rb:49 Proc#call at kernel/core/proc.rb:127 SpecRunner#it at spec/mspec.rb:368 main.it at spec/mspec.rb:412 main.__script__ {} at spec/language/unless_spec.rb:48 Proc#call at kernel/core/proc.rb:127 SpecRunner#describe at spec/mspec.rb:378 main.describe at spec/mspec.rb:408 main.__script__ at spec/language/unless_spec.rb:3 CompiledMethod#as_script at kernel/bootstrap/primitives.rb:41 main.load at kernel/core/compile.rb:150 main.__script__ {} at last_mspec.rb:11 Array#each {} at kernel/core/array.rb:545 Integer(Fixnum)#times at kernel/core/integer.rb:15 Array#each at kernel/core/array.rb:545 main.__script__ at last_mspec.rb:16 CompiledMethod#as_script at kernel/bootstrap/primitives.rb:41 main.load at kernel/core/compile.rb:150 main.__script__ at kernel/loader.rb:145 BACKTRACE footer = "\n8 examples, 1 failures\n" input = header + failure + backtrace + footer expected_backtrace = backtrace.split("\n").map {|l| "#{l}\n"} expected = [[["1)\n", "The unless expression should fail FAILED\n", "Expected nil to equal \"baz\":\n", *expected_backtrace]], ["\n", "8 examples, 1 failures\n"]] util_unit_diff(header, input, expected, :parse_input) end def test_input_mspec_multiline header = <<-HEADER Started .......F Finished in 0.1 seconds HEADER failure = <<-FAILURE 1) Compiler compiles a case without an argument FAILED Expected #], [:push_literal, "foo"], [:string_dup], [:goto, #