mac_bacon 1.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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