Permalink
Browse files

import

  • Loading branch information...
0 parents commit 2598db9317443b837cb6b9c8486e06c7685d2d96 @ahoward committed Mar 28, 2009
Showing with 435 additions and 0 deletions.
  1. +159 −0 README
  2. +59 −0 README.tmpl
  3. +38 −0 gemspec.rb
  4. +43 −0 gen_readme.rb
  5. +89 −0 lib/testy.rb
  6. +21 −0 samples/a.rb
  7. +26 −0 samples/b.rb
159 README
@@ -0,0 +1,159 @@
+NAME
+ testy.rb
+
+DESCRIPTION
+ a BDD testing framework for ruby that's mad at the world and plans to kick
+ it's ass in 78 freakin lines of code
+
+SYNOPSIS
+ Testy.testing 'your code' do
+ test 'some behaviour' do |result|
+ ultimate = Ultimate.new
+
+ result.check :name, :expect => 42, :actual => ultimate.answer
+ end
+ end
+
+PRINCIPLES AND GOALS
+ . it.should.not.merely.be.a.unit.testing.with.a.clever.dsl
+
+ . testing should not require learning a framework. ruby is a great
+ framework so testy uses it instead, requiring programmers learn exactly 2
+ new method calls
+
+ . testing loc should not dwarf those of the application
+
+ . testing framework loc should not dwarf those of the application
+
+ . testing frameworks should *never* alter ruby built-ins nor add methods to
+ Object, Kernel, .et al
+
+ . the output of tests should be machine parsable for reporting and ci tools
+ to easily integrate with
+
+ . the output of tests should be beautiful so that humans can read it
+
+ . the shape of the test file should not insult the programmer so that tests
+ can double as sample code
+
+ . the testing framework should never alter exception semantics
+
+ . hi-jacking at_exit sucks ass
+
+ . the exit status of running a test suite should indicate the degree of it's
+ failure state: the more failures the higher the exit status
+
+ . sample code should easily be able to double as a test suite, including
+ it's output
+
+ . testing should improve your code and help your users, not make you want to
+ kill yourself
+
+ . using a format that aligns in terminal is sanity saving when comparing
+ output
+
+ . testing frameworks should provide as few shortcuts for making brittle
+ tightly coupled tests as possible
+
+SAMPLES
+
+ <========< samples/a.rb >========>
+
+ ~ > cat samples/a.rb
+
+ # simple use of testy involves simply writing code, and recording the result
+ # you expect against the actual result
+ #
+ # notice that the output is very pretty and that the exitstatus is 0 when all
+ # tests pass
+ #
+ require 'testy'
+
+ Testy.testing 'the kick-ass-ed-ness of testy' do
+
+ test 'the ultimate answer to life' do |result|
+ list = []
+
+ list << 42
+ result.check :a, :expect => 42, :actual => list.first
+
+ list << 42.0
+ result.check :b, 42.0, list.last
+ end
+
+ end
+
+ ~ > ruby samples/a.rb #=> exitstatus=0
+
+ ---
+ the kick-ass-ed-ness of testy:
+ the ultimate answer to life:
+ success:
+ a: 42
+ b: 42.0
+
+
+ <========< samples/b.rb >========>
+
+ ~ > cat samples/b.rb
+
+ # testy will handle unexpected results and exceptions thrown in your code in
+ # exactly the same way - by reporting on them in a beautiful fashion and
+ # continuing to run other tests. notice, however, that an unexpected result
+ # or raised exception will cause a non-zero exitstatus (equalling the number
+ # of failed tests) for the suite as a whole. also note that previously
+ # accumulate expect/actual pairs are still reported on in the error report.
+ #
+ require 'testy'
+
+ Testy.testing 'the exception handling of testy' do
+
+ test 'raising an exception' do |result|
+ list = []
+
+ list << 42
+ result.check :a, :expect => 42, :actual => list.first
+
+ list.method_that_does_not_exist
+ end
+
+ test 'returning unexpected results' do |result|
+ result.check 'a', 42, 42
+ result.check :b, :expect => 'forty-two', :actual => 42.0
+ end
+
+ end
+
+ ~ > ruby samples/b.rb #=> exitstatus=2
+
+ ---
+ the exception handling of testy:
+ raising an exception:
+ failure:
+ error:
+ class: NoMethodError
+ message: undefined method `method_that_does_not_exist' for [42]:Array
+ bactrace:
+ - samples/b.rb:18
+ - ./lib/testy.rb:63:in `call'
+ - ./lib/testy.rb:63:in `run'
+ - /opt/local/lib/ruby/site_ruby/1.8/orderedhash.rb:65:in `each'
+ - /opt/local/lib/ruby/site_ruby/1.8/orderedhash.rb:65:in `each'
+ - ./lib/testy.rb:59:in `run'
+ - ./lib/testy.rb:87:in `testing'
+ - samples/b.rb:10
+ expect:
+ a: 42
+ actual:
+ a: 42
+ returning unexpected results:
+ failure:
+ expect:
+ a: 42
+ b: forty-two
+ actual:
+ a: 42
+ b: 42.0
+
+
+URI
59 README.tmpl
@@ -0,0 +1,59 @@
+NAME
+ testy.rb
+
+DESCRIPTION
+ a BDD testing framework for ruby that's mad at the world and plans to kick
+ it's ass in 78 freakin lines of code
+
+SYNOPSIS
+ Testy.testing 'your code' do
+ test 'some behaviour' do |result|
+ ultimate = Ultimate.new
+
+ result.check :name, :expect => 42, :actual => ultimate.answer
+ end
+ end
+
+PRINCIPLES AND GOALS
+ . it.should.not.merely.be.a.unit.testing.with.a.clever.dsl
+
+ . testing should not require learning a framework. ruby is a great
+ framework so testy uses it instead, requiring programmers learn exactly 2
+ new method calls
+
+ . testing loc should not dwarf those of the application
+
+ . testing framework loc should not dwarf those of the application
+
+ . testing frameworks should *never* alter ruby built-ins nor add methods to
+ Object, Kernel, .et al
+
+ . the output of tests should be machine parsable for reporting and ci tools
+ to easily integrate with
+
+ . the output of tests should be beautiful so that humans can read it
+
+ . the shape of the test file should not insult the programmer so that tests
+ can double as sample code
+
+ . the testing framework should never alter exception semantics
+
+ . hi-jacking at_exit sucks ass
+
+ . the exit status of running a test suite should indicate the degree of it's
+ failure state: the more failures the higher the exit status
+
+ . sample code should easily be able to double as a test suite, including
+ it's output
+
+ . testing should improve your code and help your users, not make you want to
+ kill yourself
+
+ . using a format that aligns in terminal is sanity saving when comparing
+ output
+
+ . testing frameworks should provide as few shortcuts for making brittle
+ tightly coupled tests as possible
+
+SAMPLES
+ @samples
38 gemspec.rb
@@ -0,0 +1,38 @@
+#! /usr/bin/env gem build
+
+lib, version = File::basename(File::dirname(File::expand_path(__FILE__))).split %r/-/, 2
+
+require 'rubygems'
+
+Gem::Specification::new do |spec|
+ $VERBOSE = nil
+
+ shiteless = lambda do |list|
+ list.delete_if do |file|
+ file =~ %r/\.svn/ or
+ file =~ %r/\.tmp/
+ end
+ end
+
+ spec.name = lib
+ spec.version = version
+ spec.platform = Gem::Platform::RUBY
+ spec.summary = lib
+
+ spec.files = shiteless[Dir::glob("**/**")]
+ spec.executables = shiteless[Dir::glob("bin/*")].map{|exe| File::basename exe}
+
+ spec.require_path = "lib"
+
+ spec.has_rdoc = true # File::exist?("doc")
+ spec.test_suite_file = "test/#{ lib }.rb" if File::file?("test/#{ lib }.rb")
+ #spec.add_dependency 'lib', '>= version'
+ #spec.add_dependency 'fattr'
+
+ spec.extensions << "extconf.rb" if File::exists? "extconf.rb"
+
+ spec.rubyforge_project = 'codeforpeople'
+ spec.author = "Ara T. Howard"
+ spec.email = "ara.t.howard@gmail.com"
+ spec.homepage = "http://codeforpeople.com/lib/ruby/#{ lib }/"
+end
43 gen_readme.rb
@@ -0,0 +1,43 @@
+#! /usr/bin/env ruby
+
+require 'pathname'
+
+$VERBOSE=nil
+
+def indent s, n = 2
+ ws = ' ' * n
+ s.gsub %r/^/, ws
+end
+
+template = IO::read 'README.tmpl'
+
+samples = ''
+prompt = '~ > '
+
+Dir['sample*/*'].sort.each do |sample|
+ samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
+
+ cmd = "cat #{ sample }"
+ samples << indent(prompt + cmd, 2) << "\n\n"
+ samples << indent(`#{ cmd }`, 4) << "\n"
+
+ cmd = "ruby #{ sample }"
+ #samples << indent(prompt + cmd, 2) << "\n\n"
+
+ cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -Ilib #{ sample })'"
+ out = `#{ cmd } 2>&1`
+ #samples << indent(`#{ cmd } 2>&1`, 4) << "\n"
+
+ exitstatus = " #=> exitstatus=#{ $?.exitstatus }"
+ cmd = "ruby #{ sample }"
+ samples << indent(prompt + cmd + exitstatus, 2) << "\n\n"
+ samples << indent(out, 4) << "\n"
+
+ #samples << indent("\n#{ prompt } #{ $?.exitstatus } ### exitstatus\n")
+end
+
+#samples.gsub! %r/^/, ' '
+
+readme = template.gsub %r/^\s*@samples\s*$/, samples
+open('README', 'w'){|fd| fd.write readme}
+print readme
89 lib/testy.rb
@@ -0,0 +1,89 @@
+require 'rubygems'
+require 'orderedhash'
+require 'yaml'
+
+module Testy
+ class Test
+ class BadResult < StandardError
+ end
+
+ class OrderedHash < ::OrderedHash
+ def with_string_keys
+ oh = self.class.new
+ each_pair{|key, val| oh[key.to_s] = val}
+ oh
+ end
+ end
+
+ class Result
+ attr_accessor :expect
+ attr_accessor :actual
+
+ def initialize
+ @expect = OrderedHash.new
+ @actual = OrderedHash.new
+ end
+
+ def check(name, *args)
+ options = args.last.is_a?(Hash) ? args.pop : {}
+ value = args.size==0 ? (options[:expect]||options['expect']) : args.shift
+ expect[name.to_s] = value
+ value = args.size==0 ? (options[:actual]||options['actual']) : args.shift
+ actual[name.to_s] = value
+ end
+
+ def ok?
+ expect == actual
+ end
+ end
+
+ attr_accessor :name
+ attr_accessor :tests
+ attr_accessor :block
+
+ def initialize(*args, &block)
+ options = args.last.is_a?(Hash) ? args.pop : {}
+ @name = args.first || options[:name] || options['name']
+ @tests = OrderedHash.new
+ @block = block
+ end
+
+ def test(name, &block)
+ @tests[name.to_s] = block
+ end
+
+ def run port = STDOUT
+ instance_eval(&@block) if @block
+ report = OrderedHash.new
+ status = 0
+ tests.each do |name, block|
+ result = Result.new
+ report[name] =
+ begin
+ block.call(result)
+ raise BadResult, name unless result.ok?
+ {'success' => result.actual}
+ rescue Object => e
+ status += 1
+ failure = OrderedHash.new
+ unless e.is_a?(BadResult)
+ error = OrderedHash.new
+ error['class'] = e.class.name
+ error['message'] = e.message.to_s
+ error['bactrace'] = e.backtrace||[]
+ failure['error'] = error
+ end
+ failure['expect'] = result.expect
+ failure['actual'] = result.actual
+ {'failure' => failure}
+ end
+ end
+ port << {name => report}.to_yaml
+ exit(status)
+ end
+ end
+
+ def Testy.testing(*args, &block)
+ Test.new(*args, &block).run
+ end
+end
21 samples/a.rb
@@ -0,0 +1,21 @@
+# simple use of testy involves simply writing code, and recording the result
+# you expect against the actual result
+#
+# notice that the output is very pretty and that the exitstatus is 0 when all
+# tests pass
+#
+ require 'testy'
+
+ Testy.testing 'the kick-ass-ed-ness of testy' do
+
+ test 'the ultimate answer to life' do |result|
+ list = []
+
+ list << 42
+ result.check :a, :expect => 42, :actual => list.first
+
+ list << 42.0
+ result.check :b, 42.0, list.last
+ end
+
+ end
26 samples/b.rb
@@ -0,0 +1,26 @@
+# testy will handle unexpected results and exceptions thrown in your code in
+# exactly the same way - by reporting on them in a beautiful fashion and
+# continuing to run other tests. notice, however, that an unexpected result
+# or raised exception will cause a non-zero exitstatus (equalling the number
+# of failed tests) for the suite as a whole. also note that previously
+# accumulate expect/actual pairs are still reported on in the error report.
+#
+ require 'testy'
+
+ Testy.testing 'the exception handling of testy' do
+
+ test 'raising an exception' do |result|
+ list = []
+
+ list << 42
+ result.check :a, :expect => 42, :actual => list.first
+
+ list.method_that_does_not_exist
+ end
+
+ test 'returning unexpected results' do |result|
+ result.check 'a', 42, 42
+ result.check :b, :expect => 'forty-two', :actual => 42.0
+ end
+
+ end

0 comments on commit 2598db9

Please sign in to comment.