em-spec 0.2.0

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