em-spec 0.2.0

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.
@@ -0,0 +1,84 @@
1
+ Simple BDD API for testing asynchronous Ruby/EventMachine code
2
+ (c) 2008 Aman Gupta (tmm1)
3
+
4
+ em-spec can be used with either bacon or rspec.
5
+
6
+ =Rspec
7
+ There are two ways to use the Rspec extension. To use it as a helper, include EM::SpecHelper in your describe block. You then use the em method to wrap your evented test code. Inside the em block, you must call #done after your expectations. Everything works normally otherwise.
8
+
9
+ require "em-spec/rspec"
10
+ describe EventMachine do
11
+ include EM::SpecHelper
12
+
13
+ it "works normally when not using #em" do
14
+ 1.should == 1
15
+ end
16
+
17
+ it "makes testing evented code easy with #em" do
18
+ em do
19
+ start = Time.now
20
+
21
+ EM.add_timer(0.5){
22
+ (Time.now-start).should be_close( 0.5, 0.1 )
23
+ done
24
+ }
25
+ end
26
+ end
27
+ end
28
+ The other option is to include EM::Spec in your describe block. This will patch Rspec so that all of your examples run inside an em block automatically:
29
+ require "em-spec/rspec"
30
+ describe EventMachine do
31
+ include EM::Spec
32
+
33
+ it "requires a call to #done every time" do
34
+ 1.should == 1
35
+ done
36
+ end
37
+
38
+ it "runs test code in an em block automatically" do
39
+ start = Time.now
40
+
41
+ EM.add_timer(0.5){
42
+ (Time.now-start).should be_close( 0.5, 0.1 )
43
+ done
44
+ }
45
+ end
46
+ end
47
+
48
+ =Bacon
49
+ The API is identical to Bacon, except that you must explicitly call 'done' after all the current behavior's assertions have been made:
50
+
51
+ require 'em-spec/bacon'
52
+
53
+ EM.describe EventMachine do
54
+
55
+ should 'have timers' do
56
+ start = Time.now
57
+
58
+ EM.add_timer(0.5){
59
+ (Time.now-start).should.be.close 0.5, 0.1
60
+ done
61
+ }
62
+ end
63
+
64
+ should 'have periodic timers' do
65
+ num = 0
66
+ start = Time.now
67
+
68
+ timer = EM.add_periodic_timer(0.5){
69
+ if (num += 1) == 2
70
+ (Time.now-start).should.be.close 1.0, 0.1
71
+ EM.__send__ :cancel_timer, timer
72
+ done
73
+ end
74
+ }
75
+ end
76
+
77
+ end
78
+
79
+
80
+ Resources:
81
+
82
+ * Git repository: http://github.com/tmm1/em-spec
83
+ * Bacon: http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/30b07b651b0662fd
84
+ * Initial announcement: http://groups.google.com/group/eventmachine/browse_thread/thread/8b4e7ead72f9d013
@@ -0,0 +1,30 @@
1
+ require 'rake/testtask'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |s|
6
+ s.name = "em-spec"
7
+ s.description = s.summary = "Simple BDD API for testing asynchronous Ruby/EventMachine code"
8
+ s.email = "aman@tmm1.net"
9
+ s.homepage = "http://github.com/joshbuddy/em-spec"
10
+ s.authors = ["Aman Gupta"]
11
+ s.files = FileList["[A-Z]*", "{lib,test}/**/*"]
12
+ end
13
+ Jeweler::GemcutterTasks.new
14
+ rescue LoadError
15
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
16
+ end
17
+
18
+ task :default => :spec
19
+
20
+ Rake::TestTask.new do |t|
21
+ t.libs << "test"
22
+ t.test_files = FileList['test/test_spec.rb']
23
+ t.verbose = true
24
+ end
25
+
26
+ task :spec do
27
+ sh('rake test') rescue nil
28
+ sh('bacon test/bacon_spec.rb') rescue nil
29
+ sh 'spec -f specdoc test/rspec_spec.rb test/rspec_fail_examples.rb'
30
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,89 @@
1
+ require File.dirname(__FILE__) + '/../ext/fiber18'
2
+ require 'rubygems'
3
+ require 'eventmachine'
4
+
5
+ module EventMachine
6
+
7
+ def self.spec_backend=( backend )
8
+ @spec_backend = backend
9
+ end
10
+
11
+ def self.spec_backend
12
+ @spec_backend
13
+ end
14
+
15
+ def self.spec *args, &blk
16
+ raise ArgumentError, 'block required' unless block_given?
17
+ raise 'EventMachine reactor already running' if EM.reactor_running?
18
+
19
+ spec_backend.spec( args, blk )
20
+ end
21
+ class << self; alias :describe :spec; end
22
+
23
+ def self.bacon( *args, &block )
24
+ require File.dirname(__FILE__) + '/spec/bacon'
25
+ self.spec_backend = EventMachine::Spec::Bacon
26
+ self.spec( args, &block )
27
+ end
28
+
29
+ end
30
+
31
+ module EventMachine
32
+ module Spec
33
+ module Bacon
34
+
35
+ def self.spec( args, blk )
36
+ EM.run do
37
+ ::Bacon.summary_on_exit
38
+ ($em_spec_fiber = Fiber.new{
39
+ ::Bacon::FiberedContext.new(args.join(' '), &blk).run
40
+ EM.stop_event_loop
41
+ }).resume
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+
49
+ class Bacon::FiberedContext < Bacon::Context
50
+
51
+ SpecTimeoutExceededError = Class.new(RuntimeError)
52
+
53
+ def default_timeout(timeout)
54
+ $_em_default_time_to_finish = timeout
55
+ end
56
+
57
+ def cancel_timer
58
+ EM.cancel_timer($_em_timer) if $_em_timer
59
+ end
60
+
61
+ def timeout(time_to_run)
62
+ cancel_timer
63
+ $_em_timer = EM.add_timer(time_to_run) { done(false) }
64
+ end
65
+
66
+ def it *args
67
+ super{
68
+ if block_given?
69
+ if $_em_default_time_to_finish
70
+ timeout($_em_default_time_to_finish)
71
+ end
72
+ yield
73
+ Fiber.yield
74
+ end
75
+ }
76
+ end
77
+
78
+ def done(succeed = true)
79
+ cancel_timer
80
+ EM.next_tick{
81
+ if succeed
82
+ :done.should == :done
83
+ else
84
+ should.flunk
85
+ end
86
+ $em_spec_fiber.resume if $em_spec_fiber
87
+ }
88
+ end
89
+ end
@@ -0,0 +1,71 @@
1
+ require 'eventmachine'
2
+ require File.dirname(__FILE__) + '/../ext/fiber18'
3
+
4
+ module EventMachine
5
+ module SpecHelper
6
+
7
+ SpecTimeoutExceededError = Class.new(RuntimeError)
8
+
9
+ def self.included(cls)
10
+ ::Spec::Example::ExampleGroup.instance_eval "
11
+ @@_em_default_time_to_finish = nil
12
+ def self.default_timeout(timeout)
13
+ @@_em_default_time_to_finish = timeout
14
+ end
15
+ "
16
+ end
17
+
18
+ def timeout(time_to_run)
19
+ EM.cancel_timer(@_em_timer) if @_em_timer
20
+ @_em_timer = EM.add_timer(time_to_run) { done; raise SpecTimeoutExceededError.new }
21
+ end
22
+
23
+ def em(time_to_run = @@_em_default_time_to_finish, &block)
24
+ EM.run do
25
+ timeout(time_to_run) if time_to_run
26
+ em_spec_exception = nil
27
+ @_em_spec_fiber = Fiber.new do
28
+ begin
29
+ block.call
30
+ rescue Exception => em_spec_exception
31
+ done
32
+ end
33
+ Fiber.yield
34
+ end
35
+
36
+ @_em_spec_fiber.resume
37
+
38
+ raise em_spec_exception if em_spec_exception
39
+ end
40
+ end
41
+
42
+ def done
43
+ EM.next_tick{
44
+ finish_em_spec_fiber
45
+ }
46
+ end
47
+
48
+ private
49
+
50
+ def finish_em_spec_fiber
51
+ EM.stop_event_loop if EM.reactor_running?
52
+ @_em_spec_fiber.resume if @_em_spec_fiber.alive?
53
+ end
54
+
55
+ end
56
+
57
+ module Spec
58
+
59
+ include SpecHelper
60
+
61
+ def instance_eval(&block)
62
+ em do
63
+ super(&block)
64
+ end
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+
71
+
@@ -0,0 +1,93 @@
1
+ require 'eventmachine'
2
+ require File.dirname(__FILE__) + '/../ext/fiber18'
3
+
4
+ module EventMachine
5
+ TestTimeoutExceededError = Class.new(RuntimeError)
6
+
7
+ module TestHelper
8
+
9
+ def self.included(cls)
10
+ cls.class_eval(<<-HERE_DOC, __FILE__, __LINE__)
11
+ DefaultTimeout = nil unless const_defined?(:DefaultTimeout)
12
+
13
+ def self.default_timeout(timeout)
14
+ self.send(:remove_const, :DefaultTimeout)
15
+ self.send(:const_set, :DefaultTimeout, timeout)
16
+ end
17
+
18
+ def current_default_timeout
19
+ DefaultTimeout
20
+ end
21
+ HERE_DOC
22
+
23
+ end
24
+
25
+
26
+ def timeout(time_to_run)
27
+ EM.cancel_timer(@_em_timer) if @_em_timer
28
+ @_em_timer = EM.add_timer(time_to_run) { done('timeout exceeded') }
29
+ end
30
+
31
+ def em(time_to_run = current_default_timeout, &block)
32
+ @flunk_test = nil
33
+ EM.run do
34
+ timeout(time_to_run) if time_to_run
35
+ em_spec_exception = nil
36
+ @_em_spec_fiber = Fiber.new do
37
+ begin
38
+ block.call
39
+ rescue Exception => em_spec_exception
40
+ done
41
+ end
42
+ Fiber.yield
43
+ end
44
+
45
+ @_em_spec_fiber.resume
46
+
47
+ raise em_spec_exception if em_spec_exception
48
+ end
49
+ raise(@flunk_test) if @flunk_test
50
+ end
51
+
52
+ def done(flunk_reason = nil)
53
+ EM.next_tick{
54
+ @flunk_test = flunk_reason
55
+ finish_em_spec_fiber
56
+ }
57
+ end
58
+
59
+ private
60
+
61
+ def finish_em_spec_fiber
62
+ EM.stop_event_loop if EM.reactor_running?
63
+ @_em_spec_fiber.resume if @_em_spec_fiber.alive?
64
+ end
65
+
66
+ end
67
+
68
+ module Test
69
+
70
+ def self.included(cls)
71
+ cls.class_eval(<<-HERE_DOC, __FILE__, __LINE__)
72
+ include TestHelper
73
+
74
+ def self.default_timeout(timeout)
75
+ self.send(:remove_const, :DefaultTimeout)
76
+ self.send(:const_set, :DefaultTimeout, timeout)
77
+ end
78
+
79
+ include TestHelper
80
+ alias_method :run_without_em, :run
81
+ def run(result, &block)
82
+ em(DefaultTimeout) { run_without_em(result, &block) }
83
+ end
84
+
85
+ HERE_DOC
86
+
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+
93
+
@@ -0,0 +1,81 @@
1
+ # Poor Man's Fiber (API compatible Thread based Fiber implementation for Ruby 1.8)
2
+ # (c) 2008 Aman Gupta (tmm1)
3
+
4
+ unless defined? Fiber
5
+ require 'thread'
6
+
7
+ class FiberError < StandardError; end
8
+
9
+ class Fiber
10
+ def initialize
11
+ raise ArgumentError, 'new Fiber requires a block' unless block_given?
12
+
13
+ @yield = Queue.new
14
+ @resume = Queue.new
15
+
16
+ @thread = Thread.new{ @yield.push [ *yield(*@resume.pop) ] }
17
+ @thread.abort_on_exception = true
18
+ @thread[:fiber] = self
19
+ end
20
+ attr_reader :thread
21
+
22
+ def alive?
23
+ @thread.alive?
24
+ end
25
+
26
+ def resume *args
27
+ raise FiberError, 'dead fiber called' unless @thread.alive?
28
+ raise FiberError, 'double resume' if @thread == Thread.current
29
+ @resume.push(args)
30
+ result = @yield.pop
31
+ result.size > 1 ? result : result.first
32
+ end
33
+
34
+ def resume!
35
+ @resume.push []
36
+ end
37
+
38
+ def yield *args
39
+ @yield.push(args)
40
+ result = @resume.pop
41
+ result.size > 1 ? result : result.first
42
+ end
43
+
44
+ def self.yield *args
45
+ raise FiberError, "can't yield from root fiber" unless fiber = Thread.current[:fiber]
46
+ fiber.yield(*args)
47
+ end
48
+
49
+ def self.current
50
+ Thread.current[:fiber] or raise FiberError, 'not inside a fiber'
51
+ end
52
+
53
+ def inspect
54
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}>"
55
+ end
56
+ end
57
+ else
58
+ require 'fiber' unless Fiber.respond_to?(:current)
59
+ end
60
+
61
+ if __FILE__ == $0
62
+ f = Fiber.new{ puts 'hi'; p Fiber.yield(1); puts 'bye'; :done }
63
+ p f.resume
64
+ p f.resume(2)
65
+ end
66
+
67
+ __END__
68
+
69
+ $ ruby fbr.rb
70
+ hi
71
+ 1
72
+ 2
73
+ bye
74
+ :done
75
+
76
+ $ ruby1.9 fbr.rb
77
+ hi
78
+ 1
79
+ 2
80
+ bye
81
+ :done
@@ -0,0 +1,70 @@
1
+ require 'bacon'
2
+ require File.dirname(__FILE__) + '/../lib/em-spec/bacon'
3
+
4
+ EM.spec_backend = EventMachine::Spec::Bacon
5
+
6
+ describe 'Bacon' do
7
+ should 'work as normal outside EM.describe' do
8
+ 1.should == 1
9
+ end
10
+ end
11
+
12
+ EM.describe EventMachine do
13
+ should 'work' do
14
+ done
15
+ end
16
+
17
+ should 'have timers' do
18
+ start = Time.now
19
+
20
+ EM.add_timer(0.5){
21
+ (Time.now-start).should.be.close 0.5, 0.1
22
+ done
23
+ }
24
+ end
25
+
26
+ should 'have periodic timers' do
27
+ num = 0
28
+ start = Time.now
29
+
30
+ timer = EM.add_periodic_timer(0.5){
31
+ if (num += 1) == 2
32
+ (Time.now-start).should.be.close 1.0, 0.1
33
+ EM.__send__ :cancel_timer, timer
34
+ done
35
+ end
36
+ }
37
+ end
38
+
39
+ should 'have deferrables' do
40
+ defr = EM::DefaultDeferrable.new
41
+ defr.timeout(1)
42
+ defr.errback{
43
+ done
44
+ }
45
+ end
46
+
47
+ # it "should not block on failure" do
48
+ # 1.should == 2
49
+ # end
50
+
51
+ end
52
+
53
+ EM.describe EventMachine, "with time restrictions" do
54
+ default_timeout 2
55
+
56
+ should 'succeed here' do
57
+ timeout(5)
58
+ EM.add_timer(3) { done }
59
+ end
60
+
61
+ end
62
+
63
+ EM.describe EventMachine, "with time restrictions" do
64
+ default_timeout 2
65
+
66
+ should 'raise fail here' do
67
+ EM.add_timer(3) { done }
68
+ end
69
+
70
+ end
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__) + '/../lib/em-spec/rspec'
2
+
3
+ describe EventMachine, "when running failing examples" do
4
+ include EM::Spec
5
+
6
+ it "should not bubble failures beyond rspec" do
7
+ EM.add_timer(0.1) do
8
+ :should_not_bubble.should == :failures
9
+ done
10
+ end
11
+ end
12
+
13
+ it "should not block on failure" do
14
+ 1.should == 2
15
+ end
16
+
17
+
18
+ end
@@ -0,0 +1,92 @@
1
+ require File.dirname(__FILE__) + '/../lib/em-spec/rspec'
2
+
3
+ describe 'Rspec' do
4
+ it 'should work as normal outside EM.describe' do
5
+ 1.should == 1
6
+ end
7
+ end
8
+
9
+ describe EventMachine, "when testing with EM::SpecHelper" do
10
+ include EM::SpecHelper
11
+
12
+ it "should not require a call to done when #em is not used" do
13
+ 1.should == 1
14
+ end
15
+
16
+ it "should have timers" do
17
+ em do
18
+ start = Time.now
19
+
20
+ EM.add_timer(0.5){
21
+ (Time.now-start).should be_close( 0.5, 0.1 )
22
+ done
23
+ }
24
+ end
25
+ end
26
+ end
27
+
28
+ describe EventMachine, "when testing with EM::Spec" do
29
+ include EM::Spec
30
+
31
+ it 'should work' do
32
+ done
33
+ end
34
+
35
+ it 'should have timers' do
36
+ start = Time.now
37
+
38
+ EM.add_timer(0.5){
39
+ (Time.now-start).should be_close( 0.5, 0.1 )
40
+ done
41
+ }
42
+ end
43
+
44
+ it 'should have periodic timers' do
45
+ num = 0
46
+ start = Time.now
47
+
48
+ timer = EM.add_periodic_timer(0.5){
49
+ if (num += 1) == 2
50
+ (Time.now-start).should be_close( 1.0, 0.1 )
51
+ EM.__send__ :cancel_timer, timer
52
+ done
53
+ end
54
+ }
55
+ end
56
+
57
+ it 'should have deferrables' do
58
+ defr = EM::DefaultDeferrable.new
59
+ defr.timeout(1)
60
+ defr.errback{
61
+ done
62
+ }
63
+ end
64
+
65
+ end
66
+
67
+ describe EventMachine, "when testing with EM::Spec with a maximum execution time per test" do
68
+
69
+ include EM::Spec
70
+
71
+ default_timeout 2
72
+
73
+ it 'should timeout before reaching done' do
74
+ EM.add_timer(3) {
75
+ done
76
+ }
77
+ end
78
+
79
+ it 'should timeout before reaching done' do
80
+ timeout(4)
81
+ EM.add_timer(3) {
82
+ done
83
+ }
84
+ end
85
+
86
+ end
87
+
88
+ describe "Rspec", "when running an example group after another group that uses EMSpec " do
89
+ it "should work normally" do
90
+ :does_not_hang.should_not be_false
91
+ end
92
+ end
@@ -0,0 +1,96 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'eventmachine'
4
+ require 'lib/em-spec/test'
5
+
6
+ class NormalTest < Test::Unit::TestCase
7
+ def test_trivial
8
+ assert_equal 1, 1
9
+ end
10
+ end
11
+
12
+ class EmSpecHelperTest < Test::Unit::TestCase
13
+
14
+ include EventMachine::TestHelper
15
+
16
+ def test_trivial
17
+ em do
18
+ assert_equal 1, 1
19
+ done
20
+ end
21
+ end
22
+
23
+ def test_timer
24
+ em do
25
+ start = Time.now
26
+
27
+ EM.add_timer(0.5){
28
+ assert_in_delta 0.5, Time.now-start, 0.1
29
+ done
30
+ }
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ class EmSpecTest < Test::Unit::TestCase
37
+
38
+ include EventMachine::Test
39
+
40
+ def test_working
41
+ done
42
+ end
43
+
44
+ def test_timer
45
+ start = Time.now
46
+
47
+ EM.add_timer(0.5){
48
+ assert_in_delta 0.5, Time.now-start, 0.1
49
+ done
50
+ }
51
+ end
52
+
53
+ def test_deferrable
54
+ defr = EM::DefaultDeferrable.new
55
+ defr.timeout(1)
56
+ defr.errback{
57
+ done
58
+ }
59
+ end
60
+
61
+ end
62
+
63
+ class EmSpecWithTimeoutTest < Test::Unit::TestCase
64
+
65
+ include EventMachine::Test
66
+
67
+ default_timeout 2
68
+
69
+ def test_should_fail_to_timeout
70
+ EM.add_timer(3) {
71
+ done
72
+ }
73
+ end
74
+
75
+ end
76
+
77
+ class EmSpecWithTimeoutOverrideTest < Test::Unit::TestCase
78
+
79
+ include EventMachine::Test
80
+
81
+ default_timeout 2
82
+
83
+ def test_timeout_override
84
+ timeout(4)
85
+ EM.add_timer(3) {
86
+ done
87
+ }
88
+ end
89
+
90
+ end
91
+
92
+ class AnotherNormalTest < Test::Unit::TestCase
93
+ def normal_test
94
+ assert_equal 1, 1
95
+ end
96
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-spec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Aman Gupta
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-19 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Simple BDD API for testing asynchronous Ruby/EventMachine code
17
+ email: aman@tmm1.net
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - README.rdoc
26
+ - Rakefile
27
+ - VERSION
28
+ - lib/em-spec/bacon.rb
29
+ - lib/em-spec/rspec.rb
30
+ - lib/em-spec/test.rb
31
+ - lib/ext/fiber18.rb
32
+ - test/bacon_spec.rb
33
+ - test/rspec_fail_examples.rb
34
+ - test/rspec_spec.rb
35
+ - test/test_spec.rb
36
+ has_rdoc: true
37
+ homepage: http://github.com/joshbuddy/em-spec
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --charset=UTF-8
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.3.5
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Simple BDD API for testing asynchronous Ruby/EventMachine code
64
+ test_files:
65
+ - test/bacon_spec.rb
66
+ - test/rspec_fail_examples.rb
67
+ - test/rspec_spec.rb
68
+ - test/test_spec.rb