mac_bacon 1.1.21

Sign up to get free protection for your applications and to get access to all the features.
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