mac_bacon 1.1.21
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +18 -0
- data/ChangeLog +272 -0
- data/RDOX +96 -0
- data/README +324 -0
- data/Rakefile +145 -0
- data/bin/macbacon +118 -0
- data/lib/mac_bacon.rb +450 -0
- data/test/spec_bacon.rb +390 -0
- data/test/spec_mac_bacon.rb +20 -0
- data/test/spec_nontrue.rb +16 -0
- data/test/spec_should.rb +33 -0
- metadata +86 -0
data/Rakefile
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# Rakefile for Bacon. -*-ruby-*-
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
|
6
|
+
desc "Run all the tests"
|
7
|
+
task :default => [:test]
|
8
|
+
|
9
|
+
desc "Do predistribution stuff"
|
10
|
+
task :predist => [:chmod, :changelog, :rdoc]
|
11
|
+
|
12
|
+
|
13
|
+
desc "Make an archive as .tar.gz"
|
14
|
+
task :dist => [:test, :predist] do
|
15
|
+
sh "git archive --format=tar --prefix=#{release}/ HEAD^{tree} >#{release}.tar"
|
16
|
+
sh "pax -waf #{release}.tar -s ':^:#{release}/:' RDOX ChangeLog doc"
|
17
|
+
sh "gzip -f -9 #{release}.tar"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Helper to retrieve the "revision number" of the git tree.
|
21
|
+
def git_tree_version
|
22
|
+
if File.directory?(".git")
|
23
|
+
@tree_version ||= `git describe`.strip.sub('-', '.')
|
24
|
+
@tree_version << ".0" unless @tree_version.count('.') == 2
|
25
|
+
else
|
26
|
+
#$: << "lib"
|
27
|
+
#require 'mac_bacon'
|
28
|
+
#@tree_version = Bacon::VERSION
|
29
|
+
@tree_version = "1.1"
|
30
|
+
end
|
31
|
+
@tree_version
|
32
|
+
end
|
33
|
+
|
34
|
+
def gem_version
|
35
|
+
git_tree_version.gsub(/-.*/, '')
|
36
|
+
end
|
37
|
+
|
38
|
+
def release
|
39
|
+
"macbacon-#{git_tree_version}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def manifest
|
43
|
+
`git ls-files`.split("\n") - [".gitignore"]
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
desc "Make binaries executable"
|
48
|
+
task :chmod do
|
49
|
+
Dir["bin/*"].each { |binary| File.chmod(0775, binary) }
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "Generate a ChangeLog"
|
53
|
+
task :changelog do
|
54
|
+
File.open("ChangeLog", "w") { |out|
|
55
|
+
`git log -z`.split("\0").map { |chunk|
|
56
|
+
author = chunk[/Author: (.*)/, 1].strip
|
57
|
+
date = chunk[/Date: (.*)/, 1].strip
|
58
|
+
desc, detail = $'.strip.split("\n", 2)
|
59
|
+
detail ||= ""
|
60
|
+
detail = detail.gsub(/.*darcs-hash:.*/, '')
|
61
|
+
detail.rstrip!
|
62
|
+
out.puts "#{date} #{author}"
|
63
|
+
out.puts " * #{desc.strip}"
|
64
|
+
out.puts detail unless detail.empty?
|
65
|
+
out.puts
|
66
|
+
}
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
desc "Generate RDox"
|
72
|
+
task "RDOX" do
|
73
|
+
sh "macruby -Ilib bin/macbacon --automatic --specdox >RDOX"
|
74
|
+
end
|
75
|
+
|
76
|
+
desc "Run all the tests"
|
77
|
+
task :test do
|
78
|
+
sh "macruby -Ilib bin/macbacon --automatic --quiet"
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
begin
|
83
|
+
$" << "sources" if defined? FromSrc
|
84
|
+
require 'rubygems'
|
85
|
+
|
86
|
+
require 'rake'
|
87
|
+
require 'rake/clean'
|
88
|
+
require 'rake/packagetask'
|
89
|
+
require 'rake/gempackagetask'
|
90
|
+
require 'fileutils'
|
91
|
+
rescue LoadError
|
92
|
+
# Too bad.
|
93
|
+
else
|
94
|
+
spec = Gem::Specification.new do |s|
|
95
|
+
s.name = "mac_bacon"
|
96
|
+
s.version = gem_version
|
97
|
+
s.platform = Gem::Platform::RUBY
|
98
|
+
s.summary = "a small RSpec clone for MacRuby"
|
99
|
+
|
100
|
+
s.description = <<-EOF
|
101
|
+
Bacon is a small RSpec clone weighing less than 350 LoC but
|
102
|
+
nevertheless providing all essential features.
|
103
|
+
|
104
|
+
This MacBacon fork differs with regular Bacon in that it operates
|
105
|
+
properly in a NSRunloop based environment. I.e. MacRuby/Objective-C.
|
106
|
+
|
107
|
+
https://github.com/alloy/MacBacon
|
108
|
+
EOF
|
109
|
+
|
110
|
+
s.files = manifest + %w(RDOX ChangeLog)
|
111
|
+
s.bindir = 'bin'
|
112
|
+
s.executables << 'macbacon'
|
113
|
+
s.require_path = 'lib'
|
114
|
+
s.has_rdoc = true
|
115
|
+
s.extra_rdoc_files = ['README', 'RDOX']
|
116
|
+
s.test_files = []
|
117
|
+
|
118
|
+
s.author = 'Eloy Durán'
|
119
|
+
s.email = 'eloy.de.enige@gmail.com'
|
120
|
+
s.homepage = 'https://github.com/alloy/MacBacon'
|
121
|
+
end
|
122
|
+
|
123
|
+
#task :gem => [:chmod, :changelog]
|
124
|
+
task :gem => [:chmod]
|
125
|
+
|
126
|
+
Rake::GemPackageTask.new(spec) do |p|
|
127
|
+
p.gem_spec = spec
|
128
|
+
p.need_tar = false
|
129
|
+
p.need_zip = false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
desc "Generate RDoc documentation"
|
134
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
135
|
+
rdoc.options << '--line-numbers' << '--inline-source' <<
|
136
|
+
'--main' << 'README' <<
|
137
|
+
'--title' << 'Bacon Documentation' <<
|
138
|
+
'--charset' << 'utf-8'
|
139
|
+
rdoc.rdoc_dir = "doc"
|
140
|
+
rdoc.rdoc_files.include 'README'
|
141
|
+
rdoc.rdoc_files.include 'COPYING'
|
142
|
+
rdoc.rdoc_files.include 'RDOX'
|
143
|
+
rdoc.rdoc_files.include('lib/mac_bacon.rb')
|
144
|
+
end
|
145
|
+
task :rdoc => ["RDOX"]
|
data/bin/macbacon
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '../lib/')
|
6
|
+
module Bacon; end
|
7
|
+
|
8
|
+
automatic = false
|
9
|
+
output = 'SpecDoxOutput'
|
10
|
+
|
11
|
+
opts = OptionParser.new("", 24, ' ') { |opts|
|
12
|
+
opts.banner = "Usage: macbacon [options] [files | -a] [-- untouched arguments]"
|
13
|
+
|
14
|
+
opts.separator ""
|
15
|
+
opts.separator "Ruby options:"
|
16
|
+
|
17
|
+
lineno = 1
|
18
|
+
opts.on("-e", "--eval LINE", "evaluate a LINE of code") { |line|
|
19
|
+
eval line, TOPLEVEL_BINDING, "-e", lineno
|
20
|
+
lineno += 1
|
21
|
+
}
|
22
|
+
|
23
|
+
opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
|
24
|
+
$DEBUG = true
|
25
|
+
}
|
26
|
+
opts.on("-w", "--warn", "turn warnings on for your script") {
|
27
|
+
$-w = true
|
28
|
+
}
|
29
|
+
|
30
|
+
opts.on("-I", "--include PATH",
|
31
|
+
"specify $LOAD_PATH (may be used more than once)") { |path|
|
32
|
+
$LOAD_PATH.unshift(*path.split(":"))
|
33
|
+
}
|
34
|
+
|
35
|
+
opts.on("-r", "--require LIBRARY",
|
36
|
+
"require the library, before executing your script") { |library|
|
37
|
+
require library
|
38
|
+
}
|
39
|
+
|
40
|
+
opts.separator ""
|
41
|
+
opts.separator "bacon options:"
|
42
|
+
|
43
|
+
opts.on("-s", "--specdox", "do AgileDox-like output (default)") {
|
44
|
+
output = 'SpecDoxOutput'
|
45
|
+
}
|
46
|
+
opts.on("-q", "--quiet", "do Test::Unit-like non-verbose output") {
|
47
|
+
output = 'TestUnitOutput'
|
48
|
+
}
|
49
|
+
opts.on("-p", "--tap", "do TAP (Test Anything Protocol) output") {
|
50
|
+
output = 'TapOutput'
|
51
|
+
}
|
52
|
+
opts.on("-k", "--knock", "do Knock output") {
|
53
|
+
output = 'KnockOutput'
|
54
|
+
}
|
55
|
+
|
56
|
+
opts.on("-o", "--output FORMAT",
|
57
|
+
"do FORMAT (SpecDox/TestUnit/Tap) output") { |format|
|
58
|
+
output = format + "Output"
|
59
|
+
}
|
60
|
+
opts.on("-Q", "--no-backtrace", "don't print backtraces") {
|
61
|
+
Bacon.const_set :Backtraces, false
|
62
|
+
}
|
63
|
+
|
64
|
+
opts.on("-a", "--automatic", "gather tests from ./test/, include ./lib/") {
|
65
|
+
$LOAD_PATH.unshift "lib" if File.directory? "lib"
|
66
|
+
automatic = true
|
67
|
+
}
|
68
|
+
|
69
|
+
opts.on('-n', '--name NAME', String,
|
70
|
+
"runs tests matching regexp NAME") { |n|
|
71
|
+
Bacon.const_set :RestrictName, Regexp.new(n)
|
72
|
+
}
|
73
|
+
|
74
|
+
opts.on('-t', '--testcase TESTCASE', String,
|
75
|
+
"runs tests in TestCases matching regexp TESTCASE") { |t|
|
76
|
+
Bacon.const_set :RestrictContext, Regexp.new(t)
|
77
|
+
}
|
78
|
+
|
79
|
+
opts.separator ""
|
80
|
+
opts.separator "Common options:"
|
81
|
+
|
82
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
83
|
+
puts opts
|
84
|
+
exit
|
85
|
+
end
|
86
|
+
|
87
|
+
opts.on_tail("--version", "Show version") do
|
88
|
+
require 'mac_bacon'
|
89
|
+
puts "bacon #{Bacon::VERSION}"
|
90
|
+
exit
|
91
|
+
end
|
92
|
+
|
93
|
+
opts.parse! ARGV
|
94
|
+
}
|
95
|
+
|
96
|
+
files = ARGV
|
97
|
+
|
98
|
+
if automatic
|
99
|
+
files.concat Dir["test/**/test_*.rb"]
|
100
|
+
files.concat Dir["test/**/spec_*.rb"]
|
101
|
+
files.concat Dir["spec/**/spec_*.rb"]
|
102
|
+
files.concat Dir["spec/**/*_spec.rb"]
|
103
|
+
end
|
104
|
+
|
105
|
+
if files.empty?
|
106
|
+
puts opts.banner
|
107
|
+
exit 1
|
108
|
+
end
|
109
|
+
|
110
|
+
require 'mac_bacon'
|
111
|
+
|
112
|
+
Bacon.extend Bacon.const_get(output) rescue abort "No such formatter: #{output}"
|
113
|
+
|
114
|
+
files.each { |file|
|
115
|
+
load file
|
116
|
+
}
|
117
|
+
|
118
|
+
Bacon.run
|
data/lib/mac_bacon.rb
ADDED
@@ -0,0 +1,450 @@
|
|
1
|
+
# Bacon -- small RSpec clone.
|
2
|
+
#
|
3
|
+
# "Truth will sooner come out from error than from confusion." ---Francis Bacon
|
4
|
+
|
5
|
+
# Copyright (C) 2007, 2008 Christian Neukirchen <purl.org/net/chneukirchen>
|
6
|
+
#
|
7
|
+
# Bacon is freely distributable under the terms of an MIT-style license.
|
8
|
+
# See COPYING or http://www.opensource.org/licenses/mit-license.php.
|
9
|
+
|
10
|
+
framework "Cocoa"
|
11
|
+
|
12
|
+
module Bacon
|
13
|
+
VERSION = "1.1"
|
14
|
+
|
15
|
+
Counter = Hash.new(0)
|
16
|
+
ErrorLog = ""
|
17
|
+
Shared = Hash.new { |_, name|
|
18
|
+
raise NameError, "no such context: #{name.inspect}"
|
19
|
+
}
|
20
|
+
|
21
|
+
RestrictName = // unless defined? RestrictName
|
22
|
+
RestrictContext = // unless defined? RestrictContext
|
23
|
+
|
24
|
+
Backtraces = true unless defined? Backtraces
|
25
|
+
|
26
|
+
module SpecDoxOutput
|
27
|
+
def handle_specification_begin(name)
|
28
|
+
puts spaces + name
|
29
|
+
end
|
30
|
+
|
31
|
+
def handle_specification_end
|
32
|
+
puts if Counter[:context_depth] == 1
|
33
|
+
end
|
34
|
+
|
35
|
+
def handle_requirement_begin(description)
|
36
|
+
print "#{spaces} - #{description}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def handle_requirement_end(error)
|
40
|
+
puts error.empty? ? "" : " [#{error}]"
|
41
|
+
end
|
42
|
+
|
43
|
+
def handle_summary
|
44
|
+
print ErrorLog if Backtraces
|
45
|
+
puts "%d specifications (%d requirements), %d failures, %d errors" %
|
46
|
+
Counter.values_at(:specifications, :requirements, :failed, :errors)
|
47
|
+
end
|
48
|
+
|
49
|
+
def spaces
|
50
|
+
" " * (Counter[:context_depth] - 1)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
module TestUnitOutput
|
55
|
+
def handle_specification_begin(name); end
|
56
|
+
def handle_specification_end ; end
|
57
|
+
|
58
|
+
def handle_requirement_begin(description) end
|
59
|
+
def handle_requirement_end(error)
|
60
|
+
if error.empty?
|
61
|
+
print "."
|
62
|
+
else
|
63
|
+
print error[0..0]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def handle_summary
|
68
|
+
puts "", "Finished in #{Time.now - @timer} seconds."
|
69
|
+
puts ErrorLog if Backtraces
|
70
|
+
puts "%d tests, %d assertions, %d failures, %d errors" %
|
71
|
+
Counter.values_at(:specifications, :requirements, :failed, :errors)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module TapOutput
|
76
|
+
def handle_specification_begin(name); end
|
77
|
+
def handle_specification_end ; end
|
78
|
+
|
79
|
+
def handle_requirement_begin(description)
|
80
|
+
ErrorLog.replace ""
|
81
|
+
end
|
82
|
+
|
83
|
+
def handle_requirement_end(error)
|
84
|
+
if error.empty?
|
85
|
+
puts "ok %-3d - %s" % [Counter[:specifications], description]
|
86
|
+
else
|
87
|
+
puts "not ok %d - %s: %s" %
|
88
|
+
[Counter[:specifications], description, error]
|
89
|
+
puts ErrorLog.strip.gsub(/^/, '# ') if Backtraces
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def handle_summary
|
94
|
+
puts "1..#{Counter[:specifications]}"
|
95
|
+
puts "# %d tests, %d assertions, %d failures, %d errors" %
|
96
|
+
Counter.values_at(:specifications, :requirements, :failed, :errors)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
module KnockOutput
|
101
|
+
def handle_specification_begin(name); end
|
102
|
+
def handle_specification_end ; end
|
103
|
+
|
104
|
+
def handle_requirement_begin(description)
|
105
|
+
ErrorLog.replace ""
|
106
|
+
end
|
107
|
+
|
108
|
+
def handle_requirement_end(error)
|
109
|
+
if error.empty?
|
110
|
+
puts "ok - %s" % [description]
|
111
|
+
else
|
112
|
+
puts "not ok - %s: %s" % [description, error]
|
113
|
+
puts ErrorLog.strip.gsub(/^/, '# ') if Backtraces
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def handle_summary; end
|
118
|
+
end
|
119
|
+
|
120
|
+
extend SpecDoxOutput # default
|
121
|
+
|
122
|
+
class Error < RuntimeError
|
123
|
+
attr_accessor :count_as
|
124
|
+
|
125
|
+
def initialize(count_as, message)
|
126
|
+
@count_as = count_as
|
127
|
+
super message
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class Specification
|
132
|
+
attr_reader :description
|
133
|
+
|
134
|
+
def initialize(context, description, block, before_filters, after_filters)
|
135
|
+
@context, @description, @block = context, description, block
|
136
|
+
@before_filters, @after_filters = before_filters.dup, after_filters.dup
|
137
|
+
|
138
|
+
@postponed_blocks_count = 0
|
139
|
+
@exception_occurred = false
|
140
|
+
@error = ""
|
141
|
+
end
|
142
|
+
|
143
|
+
def run_before_filters
|
144
|
+
@before_filters.each { |f| @context.instance_eval(&f) }
|
145
|
+
end
|
146
|
+
|
147
|
+
def run_after_filters
|
148
|
+
@after_filters.each { |f| @context.instance_eval(&f) }
|
149
|
+
end
|
150
|
+
|
151
|
+
def run
|
152
|
+
Bacon.handle_requirement_begin(@description)
|
153
|
+
execute_block do
|
154
|
+
Counter[:depth] += 1
|
155
|
+
run_before_filters
|
156
|
+
@number_of_requirements_before = Counter[:requirements]
|
157
|
+
@context.instance_eval(&@block)
|
158
|
+
end
|
159
|
+
|
160
|
+
finalize if @postponed_blocks_count == 0
|
161
|
+
end
|
162
|
+
|
163
|
+
def postpone_block(seconds, &block)
|
164
|
+
# If an exception occurred, we definitely don't need to schedule any more blocks
|
165
|
+
unless @exception_occurred
|
166
|
+
@postponed_blocks_count += 1
|
167
|
+
performSelector("run_postponed_block:", withObject:block, afterDelay:seconds)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def run_postponed_block(block)
|
172
|
+
# If an exception occurred, we definitely don't need execute any more blocks
|
173
|
+
execute_block(&block) unless @exception_occurred
|
174
|
+
@postponed_blocks_count -= 1
|
175
|
+
finalize if @postponed_blocks_count == 0
|
176
|
+
end
|
177
|
+
|
178
|
+
def finalize
|
179
|
+
if Counter[:requirements] == @number_of_requirements_before
|
180
|
+
# the specification did not contain any requirements, so it flunked
|
181
|
+
# TODO ugh, exceptions for control flow, need to clean this up
|
182
|
+
execute_block { raise Error.new(:missing, "empty specification: #{@context.name} #{@description}") }
|
183
|
+
end
|
184
|
+
|
185
|
+
execute_block { run_after_filters }
|
186
|
+
|
187
|
+
Counter[:depth] -= 1
|
188
|
+
Bacon.handle_requirement_end(@error)
|
189
|
+
@context.specification_did_finish(self)
|
190
|
+
end
|
191
|
+
|
192
|
+
def execute_block
|
193
|
+
begin
|
194
|
+
yield
|
195
|
+
rescue Object => e
|
196
|
+
@exception_occurred = true
|
197
|
+
|
198
|
+
ErrorLog << "#{e.class}: #{e.message}\n"
|
199
|
+
e.backtrace.find_all { |line| line !~ /bin\/bacon|\/bacon\.rb:\d+/ }.
|
200
|
+
each_with_index { |line, i|
|
201
|
+
ErrorLog << "\t#{line}#{i==0 ? ": #{@context.name} - #{@description}" : ""}\n"
|
202
|
+
}
|
203
|
+
ErrorLog << "\n"
|
204
|
+
|
205
|
+
@error = if e.kind_of? Error
|
206
|
+
Counter[e.count_as] += 1
|
207
|
+
e.count_as.to_s.upcase
|
208
|
+
else
|
209
|
+
Counter[:errors] += 1
|
210
|
+
"ERROR: #{e.class}"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def self.add_context(context)
|
217
|
+
(@contexts ||= []) << context
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.current_context_index
|
221
|
+
@current_context_index ||= 0
|
222
|
+
end
|
223
|
+
|
224
|
+
def self.current_context
|
225
|
+
@contexts[current_context_index]
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.run
|
229
|
+
@timer ||= Time.now
|
230
|
+
Counter[:context_depth] += 1
|
231
|
+
handle_specification_begin(current_context.name)
|
232
|
+
current_context.performSelector("run", withObject:nil, afterDelay:0)
|
233
|
+
NSApplication.sharedApplication.run
|
234
|
+
end
|
235
|
+
|
236
|
+
def self.context_did_finish(context)
|
237
|
+
handle_specification_end
|
238
|
+
Counter[:context_depth] -= 1
|
239
|
+
if (@current_context_index + 1) < @contexts.size
|
240
|
+
@current_context_index += 1
|
241
|
+
run
|
242
|
+
else
|
243
|
+
# DONE
|
244
|
+
handle_summary
|
245
|
+
NSApplication.sharedApplication.terminate(self)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
class Context
|
250
|
+
attr_reader :name, :block
|
251
|
+
|
252
|
+
def initialize(name, before = nil, after = nil, &block)
|
253
|
+
@name = name
|
254
|
+
@before, @after = (before ? before.dup : []), (after ? after.dup : [])
|
255
|
+
@block = block
|
256
|
+
@specifications = []
|
257
|
+
@current_specification_index = 0
|
258
|
+
|
259
|
+
Bacon.add_context(self)
|
260
|
+
|
261
|
+
instance_eval(&block)
|
262
|
+
end
|
263
|
+
|
264
|
+
def run
|
265
|
+
# TODO
|
266
|
+
#return unless name =~ RestrictContext
|
267
|
+
if spec = current_specification
|
268
|
+
spec.performSelector("run", withObject:nil, afterDelay:0)
|
269
|
+
else
|
270
|
+
Bacon.context_did_finish(self)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def current_specification
|
275
|
+
@specifications[@current_specification_index]
|
276
|
+
end
|
277
|
+
|
278
|
+
def specification_did_finish(spec)
|
279
|
+
if (@current_specification_index + 1) < @specifications.size
|
280
|
+
@current_specification_index += 1
|
281
|
+
run
|
282
|
+
else
|
283
|
+
Bacon.context_did_finish(self)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def before(&block); @before << block; end
|
288
|
+
def after(&block); @after << block; end
|
289
|
+
|
290
|
+
def behaves_like(*names)
|
291
|
+
names.each { |name| instance_eval(&Shared[name]) }
|
292
|
+
end
|
293
|
+
|
294
|
+
def it(description, &block)
|
295
|
+
return unless description =~ RestrictName
|
296
|
+
block ||= lambda { should.flunk "not implemented" }
|
297
|
+
Counter[:specifications] += 1
|
298
|
+
@specifications << Specification.new(self, description, block, @before, @after)
|
299
|
+
end
|
300
|
+
|
301
|
+
def should(*args, &block)
|
302
|
+
if Counter[:depth]==0
|
303
|
+
it('should '+args.first,&block)
|
304
|
+
else
|
305
|
+
super(*args,&block)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def describe(*args, &block)
|
310
|
+
context = Bacon::Context.new(args.join(' '), @before, @after, &block)
|
311
|
+
(parent_context = self).methods(false).each {|e|
|
312
|
+
class<<context; self end.send(:define_method, e) {|*args| parent_context.send(e, *args)}
|
313
|
+
}
|
314
|
+
context
|
315
|
+
end
|
316
|
+
|
317
|
+
def wait(seconds, &block)
|
318
|
+
current_specification.postpone_block(seconds, &block)
|
319
|
+
end
|
320
|
+
|
321
|
+
def raise?(*args, &block); block.raise?(*args); end
|
322
|
+
def throw?(*args, &block); block.throw?(*args); end
|
323
|
+
def change?(*args, &block); block.change?(*args); end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
|
328
|
+
class Object
|
329
|
+
def true?; false; end
|
330
|
+
def false?; false; end
|
331
|
+
end
|
332
|
+
|
333
|
+
class TrueClass
|
334
|
+
def true?; true; end
|
335
|
+
end
|
336
|
+
|
337
|
+
class FalseClass
|
338
|
+
def false?; true; end
|
339
|
+
end
|
340
|
+
|
341
|
+
class Proc
|
342
|
+
def raise?(*exceptions)
|
343
|
+
call
|
344
|
+
rescue *(exceptions.empty? ? RuntimeError : exceptions) => e
|
345
|
+
e
|
346
|
+
else
|
347
|
+
false
|
348
|
+
end
|
349
|
+
|
350
|
+
def throw?(sym)
|
351
|
+
catch(sym) {
|
352
|
+
call
|
353
|
+
return false
|
354
|
+
}
|
355
|
+
return true
|
356
|
+
end
|
357
|
+
|
358
|
+
def change?
|
359
|
+
pre_result = yield
|
360
|
+
called = call
|
361
|
+
post_result = yield
|
362
|
+
pre_result != post_result
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
class Numeric
|
367
|
+
def close?(to, delta)
|
368
|
+
(to.to_f - self).abs <= delta.to_f rescue false
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
|
373
|
+
class Object
|
374
|
+
def should(*args, &block) Should.new(self).be(*args, &block) end
|
375
|
+
end
|
376
|
+
|
377
|
+
module Kernel
|
378
|
+
private
|
379
|
+
def describe(*args, &block) Bacon::Context.new(args.join(' '), &block) end
|
380
|
+
def shared(name, &block) Bacon::Shared[name] = block end
|
381
|
+
end
|
382
|
+
|
383
|
+
class Should
|
384
|
+
# Kills ==, ===, =~, eql?, equal?, frozen?, instance_of?, is_a?,
|
385
|
+
# kind_of?, nil?, respond_to?, tainted?
|
386
|
+
instance_methods.each { |name| undef_method name if name =~ /\?|^\W+$/ }
|
387
|
+
|
388
|
+
def initialize(object)
|
389
|
+
@object = object
|
390
|
+
@negated = false
|
391
|
+
end
|
392
|
+
|
393
|
+
def not(*args, &block)
|
394
|
+
@negated = !@negated
|
395
|
+
|
396
|
+
if args.empty?
|
397
|
+
self
|
398
|
+
else
|
399
|
+
be(*args, &block)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def be(*args, &block)
|
404
|
+
if args.empty?
|
405
|
+
self
|
406
|
+
else
|
407
|
+
block = args.shift unless block_given?
|
408
|
+
satisfy(*args, &block)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
alias a be
|
413
|
+
alias an be
|
414
|
+
|
415
|
+
def satisfy(*args, &block)
|
416
|
+
if args.size == 1 && String === args.first
|
417
|
+
description = args.shift
|
418
|
+
else
|
419
|
+
description = ""
|
420
|
+
end
|
421
|
+
|
422
|
+
r = yield(@object, *args)
|
423
|
+
if Bacon::Counter[:depth] > 0
|
424
|
+
Bacon::Counter[:requirements] += 1
|
425
|
+
raise Bacon::Error.new(:failed, description) unless @negated ^ r
|
426
|
+
r
|
427
|
+
else
|
428
|
+
@negated ? !r : !!r
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
def method_missing(name, *args, &block)
|
433
|
+
name = "#{name}?" if name.to_s =~ /\w[^?]\z/
|
434
|
+
|
435
|
+
desc = @negated ? "not " : ""
|
436
|
+
desc << @object.inspect << "." << name.to_s
|
437
|
+
desc << "(" << args.map{|x|x.inspect}.join(", ") << ") failed"
|
438
|
+
|
439
|
+
satisfy(desc) { |x| x.__send__(name, *args, &block) }
|
440
|
+
end
|
441
|
+
|
442
|
+
def equal(value) self == value end
|
443
|
+
def match(value) self =~ value end
|
444
|
+
def identical_to(value) self.equal? value end
|
445
|
+
alias same_as identical_to
|
446
|
+
|
447
|
+
def flunk(reason="Flunked")
|
448
|
+
raise Bacon::Error.new(:failed, reason)
|
449
|
+
end
|
450
|
+
end
|