em-synchrony 0.3.0.beta.1 → 1.0.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,50 @@
1
+ require "spec/helper/all"
2
+ require "em-synchrony/activerecord"
3
+
4
+ # create database widgets;
5
+ # use widgets;
6
+ # create table widgets (idx INT);
7
+
8
+ class Widget < ActiveRecord::Base; end;
9
+
10
+ describe "Fiberized ActiveRecord driver for mysql2" do
11
+ DELAY = 0.25
12
+ QUERY = "SELECT sleep(#{DELAY})"
13
+
14
+ it "should establish AR connection" do
15
+ EventMachine.synchrony do
16
+ ActiveRecord::Base.establish_connection(
17
+ :adapter => 'em_mysql2',
18
+ :database => 'widgets',
19
+ :username => 'root'
20
+ )
21
+
22
+ result = Widget.find_by_sql(QUERY)
23
+ result.size.should == 1
24
+
25
+ EventMachine.stop
26
+ end
27
+ end
28
+
29
+ it "should fire sequential, synchronous requests within single fiber" do
30
+ EventMachine.synchrony do
31
+ ActiveRecord::Base.establish_connection(
32
+ :adapter => 'em_mysql2',
33
+ :database => 'widgets',
34
+ :username => 'root'
35
+ )
36
+
37
+ start = now
38
+ res = []
39
+
40
+ res.push Widget.find_by_sql(QUERY)
41
+ res.push Widget.find_by_sql(QUERY)
42
+
43
+ (now - start.to_f).should be_within(DELAY * res.size * 0.15).of(DELAY * res.size)
44
+ res.size.should == 2
45
+
46
+ EventMachine.stop
47
+ end
48
+ end
49
+
50
+ end
@@ -1,7 +1,6 @@
1
1
  require "spec/helper/all"
2
2
 
3
3
  describe EM::Mongo do
4
-
5
4
  it "should yield until connection is ready" do
6
5
  EventMachine.synchrony do
7
6
  connection = EM::Mongo::Connection.new
@@ -14,41 +13,207 @@ describe EM::Mongo do
14
13
  end
15
14
  end
16
15
 
17
- it "should insert a record into db" do
18
- EventMachine.synchrony do
19
- collection = EM::Mongo::Connection.new.db('db').collection('test')
20
- collection.remove({}) # nuke all keys in collection
16
+ describe 'Synchronously (find & first)' do
17
+ it "should insert a record into db" do
18
+ EventMachine.synchrony do
19
+ collection = EM::Mongo::Connection.new.db('db').collection('test')
20
+ collection.remove({}) # nuke all keys in collection
21
21
 
22
- obj = collection.insert('hello' => 'world')
23
- obj.keys.should include '_id'
22
+ obj = collection.insert('hello' => 'world')
23
+ obj.should be_a(BSON::ObjectId)
24
24
 
25
- obj = collection.find
26
- obj.size.should == 1
27
- obj.first['hello'].should == 'world'
25
+ obj = collection.find
26
+ obj.size.should == 1
27
+ obj.first['hello'].should == 'world'
28
28
 
29
- EventMachine.stop
29
+ EventMachine.stop
30
+ end
30
31
  end
31
- end
32
32
 
33
- it "should insert a record into db" do
34
- EventMachine.synchrony do
35
- collection = EM::Mongo::Connection.new.db('db').collection('test')
36
- collection.remove({}) # nuke all keys in collection
33
+ it "should insert a record into db and be able to find it" do
34
+ EventMachine.synchrony do
35
+ collection = EM::Mongo::Connection.new.db('db').collection('test')
36
+ collection.remove({}) # nuke all keys in collection
37
37
 
38
- obj = collection.insert('hello' => 'world')
39
- obj = collection.insert('hello2' => 'world2')
38
+ obj = collection.insert('hello' => 'world')
39
+ obj = collection.insert('hello2' => 'world2')
40
40
 
41
- obj = collection.find({})
42
- obj.size.should == 2
41
+ obj = collection.find({})
42
+ obj.size.should == 2
43
43
 
44
- obj2 = collection.find({}, {:limit => 1})
45
- obj2.size.should == 1
44
+ obj2 = collection.find({}, {:limit => 1})
45
+ obj2.size.should == 1
46
46
 
47
- obj3 = collection.first
48
- obj3.is_a?(Hash).should be_true
47
+ obj3 = collection.first
48
+ obj3.is_a?(Hash).should be_true
49
49
 
50
- EventMachine.stop
50
+ EventMachine.stop
51
+ end
52
+ end
53
+
54
+ it "should be able to order results" do
55
+ EventMachine.synchrony do
56
+ collection = EM::Mongo::Connection.new.db('db').collection('test')
57
+ collection.remove({}) # nuke all keys in collection
58
+
59
+ collection.insert(:name => 'one', :position => 0)
60
+ collection.insert(:name => 'three', :position => 2)
61
+ collection.insert(:name => 'two', :position => 1)
62
+
63
+ res = collection.find({}, {:order => 'position'})
64
+ res[0]["name"].should == 'one'
65
+ res[1]["name"].should == 'two'
66
+ res[2]["name"].should == 'three'
67
+
68
+ res1 = collection.find({}, {:order => [:position, :desc]})
69
+ res1[0]["name"].should == 'three'
70
+ res1[1]["name"].should == 'two'
71
+ res1[2]["name"].should == 'one'
72
+
73
+ EventMachine.stop
74
+ end
75
+ end
76
+ end
77
+
78
+
79
+ #
80
+ # em-mongo version > 0.3.6
81
+ #
82
+ if defined?(EM::Mongo::Cursor)
83
+ describe '*A*synchronously (afind & afirst) [Mongo > 0.3.6, using cursor]' do
84
+ it "should insert a record into db" do
85
+ EventMachine.synchrony do
86
+ collection = EM::Mongo::Connection.new.db('db').collection('test')
87
+ collection.remove({}) # nuke all keys in collection
88
+
89
+ obj = collection.insert('hello' => 'world')
90
+ obj.should be_a(BSON::ObjectId)
91
+
92
+ cursor = collection.afind
93
+ cursor.should be_a(EM::Mongo::Cursor)
94
+ cursor.to_a.callback do |obj|
95
+ obj.size.should == 1
96
+ obj.first['hello'].should == 'world'
97
+ EM.next_tick{ EventMachine.stop }
98
+ end
99
+ end
100
+ end
101
+
102
+ it "should insert a record into db and be able to find it" do
103
+ EventMachine.synchrony do
104
+ collection = EM::Mongo::Connection.new.db('db').collection('test')
105
+ collection.remove({}) # nuke all keys in collection
106
+
107
+ obj = collection.insert('hello' => 'world')
108
+ obj = collection.insert('hello2' => 'world2')
109
+
110
+ collection.afind({}).to_a.callback do |obj|
111
+ obj.size.should == 2
112
+ end
113
+ collection.afind({}, {:limit => 1}).to_a.callback do |obj2|
114
+ obj2.size.should == 1
115
+ end
116
+ collection.afirst.callback do |obj3|
117
+ obj3.is_a?(Hash).should be_true
118
+ obj3['hello'].should == 'world'
119
+ EM.next_tick{ EventMachine.stop }
120
+ end
121
+ end
122
+ end
123
+
124
+ it "should be able to order results" do
125
+ EventMachine.synchrony do
126
+ collection = EM::Mongo::Connection.new.db('db').collection('test')
127
+ collection.remove({}) # nuke all keys in collection
128
+
129
+ collection.insert(:name => 'one', :position => 0)
130
+ collection.insert(:name => 'three', :position => 2)
131
+ collection.insert(:name => 'two', :position => 1)
132
+
133
+ collection.afind({}, {:order => 'position'}).to_a.callback do |res|
134
+ res[0]["name"].should == 'one'
135
+ res[1]["name"].should == 'two'
136
+ res[2]["name"].should == 'three'
137
+ end
138
+
139
+ collection.afind({}, {:order => [:position, :desc]}).to_a.callback do |res1|
140
+ res1[0]["name"].should == 'three'
141
+ res1[1]["name"].should == 'two'
142
+ res1[2]["name"].should == 'one'
143
+ EM.next_tick{ EventMachine.stop }
144
+ end
145
+
146
+ end
147
+ end
148
+ end
149
+
150
+ else
151
+ describe '*A*synchronously (afind & afirst) [Mongo <= 0.3.6, using blocks]' do
152
+ it "should insert a record into db" do
153
+ EventMachine.synchrony do
154
+ collection = EM::Mongo::Connection.new.db('db').collection('test')
155
+ collection.remove({}) # nuke all keys in collection
156
+
157
+ obj = collection.insert('hello' => 'world')
158
+ obj.should be_a(BSON::ObjectId)
159
+
160
+ ret_val = collection.afind do |obj|
161
+ obj.size.should == 1
162
+ obj.first['hello'].should == 'world'
163
+ EM.next_tick{ EventMachine.stop }
164
+ end
165
+ ret_val.should be_a(Integer)
166
+ end
167
+ end
168
+
169
+ it "should insert a record into db and be able to find it" do
170
+ EventMachine.synchrony do
171
+ collection = EM::Mongo::Connection.new.db('db').collection('test')
172
+ collection.remove({}) # nuke all keys in collection
173
+
174
+ obj = collection.insert('hello' => 'world')
175
+ obj = collection.insert('hello2' => 'world2')
176
+
177
+ collection.afind({}) do |obj|
178
+ obj.size.should == 2
179
+ end
180
+ collection.afind({}, {:limit => 1}) do |obj2|
181
+ obj2.size.should == 1
182
+ end
183
+ collection.afirst do |obj3|
184
+ obj3.is_a?(Hash).should be_true
185
+ obj3['hello'].should == 'world'
186
+ EM.next_tick{ EventMachine.stop }
187
+ end
188
+ end
189
+ end
190
+
191
+ it "should be able to order results" do
192
+ EventMachine.synchrony do
193
+ collection = EM::Mongo::Connection.new.db('db').collection('test')
194
+ collection.remove({}) # nuke all keys in collection
195
+
196
+ collection.insert(:name => 'one', :position => 0)
197
+ collection.insert(:name => 'three', :position => 2)
198
+ collection.insert(:name => 'two', :position => 1)
199
+
200
+ collection.afind({}, {:order => 'position'}) do |res|
201
+ res[0]["name"].should == 'one'
202
+ res[1]["name"].should == 'two'
203
+ res[2]["name"].should == 'three'
204
+ end
205
+
206
+ collection.afind({}, {:order => [:position, :desc]}) do |res1|
207
+ res1[0]["name"].should == 'three'
208
+ res1[1]["name"].should == 'two'
209
+ res1[2]["name"].should == 'one'
210
+ EM.next_tick{ EventMachine.stop }
211
+ end
212
+
213
+ end
214
+ end
51
215
  end
216
+
52
217
  end
53
218
 
54
219
  it "should update records in db" do
@@ -56,14 +221,13 @@ describe EM::Mongo do
56
221
  collection = EM::Mongo::Connection.new.db('db').collection('test')
57
222
  collection.remove({}) # nuke all keys in collection
58
223
 
59
- obj = collection.insert('hello' => 'world')
224
+ obj_id = collection.insert('hello' => 'world')
60
225
  collection.update({'hello' => 'world'}, {'hello' => 'newworld'})
61
226
 
62
- new_obj = collection.first({'_id' => obj['_id']})
227
+ new_obj = collection.first({'_id' => obj_id})
63
228
  new_obj['hello'].should == 'newworld'
64
229
 
65
230
  EventMachine.stop
66
231
  end
67
232
  end
68
-
69
233
  end
@@ -0,0 +1,39 @@
1
+ require "spec/helper/all"
2
+ require "em-synchrony/fiber_iterator"
3
+
4
+ describe EventMachine::Synchrony::FiberIterator do
5
+
6
+ it "should wait until the iterator is done and wrap internal block within a fiber" do
7
+ EM.synchrony do
8
+
9
+ results = []
10
+ i = EM::Synchrony::FiberIterator.new(1..5, 2).each do |num|
11
+ EM::Synchrony.sleep(0.1)
12
+ results.push num
13
+ end
14
+
15
+ results.should == (1..5).to_a
16
+ results.size.should == 5
17
+ EventMachine.stop
18
+ end
19
+ end
20
+
21
+ #
22
+ # it "should sum values within the iterator" do
23
+ # EM.synchrony do
24
+ # data = (1..5).to_a
25
+ # res = EM::Synchrony::FiberIterator.new(data, 2).inject(0) do |total, num, iter|
26
+ # EM::Synchrony.sleep(0.1)
27
+ #
28
+ # p [:sync, total, num]
29
+ # iter.return(total += num)
30
+ # end
31
+ #
32
+ # res.should == data.inject(:+)
33
+ # EventMachine.stop
34
+ # end
35
+ # end
36
+
37
+
38
+
39
+ end
data/spec/helper/all.rb CHANGED
@@ -4,11 +4,12 @@ require 'pp'
4
4
 
5
5
  require 'lib/em-synchrony'
6
6
  require 'lib/em-synchrony/em-http'
7
- require 'lib/em-synchrony/em-mysqlplus'
7
+ require 'lib/em-synchrony/mysql2'
8
8
  require 'lib/em-synchrony/em-remcached'
9
9
  require 'lib/em-synchrony/em-memcache'
10
10
  require 'lib/em-synchrony/em-mongo'
11
11
  require 'lib/em-synchrony/em-redis'
12
+ require 'lib/em-synchrony/em-hiredis'
12
13
 
13
14
  require 'helper/tolerance_matcher'
14
15
  require 'helper/stub-http-server'
@@ -0,0 +1,40 @@
1
+ require "spec/helper/all"
2
+
3
+ describe EM::Hiredis do
4
+
5
+ it "should yield until connection is ready" do
6
+ EventMachine.synchrony do
7
+ connection = EM::Hiredis::Client.connect
8
+ connection.connected.should be_true
9
+
10
+ EventMachine.stop
11
+ end
12
+ end
13
+
14
+ it "should get/set records synchronously" do
15
+ EventMachine.synchrony do
16
+ redis = EM::Hiredis::Client.connect
17
+
18
+ redis.set('a', 'foo')
19
+ redis.get('a').should == 'foo'
20
+ redis.get('c').should == nil
21
+
22
+ EM.stop
23
+ end
24
+ end
25
+
26
+ it "should incr/decr key synchronously" do
27
+ EventMachine.synchrony do
28
+ redis = EM::Hiredis::Client.connect
29
+ redis.delete('key')
30
+
31
+ redis.incr('key')
32
+ redis.get('key').to_i.should == 1
33
+
34
+ redis.decr('key')
35
+ redis.get('key').to_i.should == 0
36
+
37
+ EM.stop
38
+ end
39
+ end
40
+ end
data/spec/http_spec.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "spec/helper/all"
2
2
 
3
3
  URL = "http://localhost:8081/"
4
+ CONNECTION_ERROR_URL = "http://random-domain-blah.com/"
4
5
  DELAY = 0.25
5
6
 
6
7
  describe EventMachine::HttpRequest do
@@ -58,4 +59,26 @@ describe EventMachine::HttpRequest do
58
59
  EventMachine.stop
59
60
  end
60
61
  end
62
+
63
+ it "should terminate immediately in case of connection errors" do
64
+ EventMachine.synchrony do
65
+ response = EventMachine::HttpRequest.new(CONNECTION_ERROR_URL).get
66
+ response.error.should_not be_nil
67
+
68
+ EventMachine.stop
69
+ end
70
+ end
71
+
72
+ it "should process inactivity timeout correctly" do
73
+ EventMachine.synchrony do
74
+ s = StubServer.new("HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nFoo", 5)
75
+
76
+ start = now
77
+ r = EventMachine::HttpRequest.new(URL, :inactivity_timeout => 0.5).get
78
+ (now - start.to_f).should be_within(0.2).of(0.5)
79
+
80
+ s.stop
81
+ EventMachine.stop
82
+ end
83
+ end
61
84
  end
@@ -0,0 +1,59 @@
1
+ require "spec/helper/all"
2
+ require "tempfile"
3
+
4
+ DELAY = 0.1
5
+
6
+ describe EventMachine::Synchrony do
7
+ before(:each) { @temp_file = Tempfile.new("stdout") }
8
+ after(:each) { @temp_file.unlink }
9
+
10
+ def with_input(string = "", &block)
11
+ string = "#{string}\n"
12
+
13
+ @temp_file.write string
14
+ @temp_file.flush
15
+
16
+ EM::Synchrony.add_timer(DELAY) do
17
+ original_stdin = STDIN
18
+ STDIN.reopen(@temp_file.path)
19
+
20
+ block.call if block_given?
21
+
22
+ STDIN.reopen(original_stdin)
23
+ end
24
+ end
25
+
26
+ it "waits for input" do
27
+ EM.synchrony do
28
+ start = now
29
+
30
+ with_input do
31
+ EM::Synchrony.gets
32
+
33
+ (now - start.to_f).should be_within(DELAY * 0.15).of(DELAY)
34
+ end
35
+
36
+ EM.add_timer(DELAY * 2) { EM.stop }
37
+ end
38
+ end
39
+
40
+ it "trails input with a newline to emulate gets" do
41
+ EM.synchrony do
42
+ with_input("Hello") do
43
+ EM::Synchrony.gets.should == "Hello\n"
44
+ end
45
+
46
+ EM.add_timer(DELAY * 2) { EM.stop }
47
+ end
48
+ end
49
+
50
+ it "should stop after the first line" do
51
+ EM.synchrony do
52
+ with_input("Hello\nWorld!") do
53
+ EM::Synchrony.gets.should == "Hello\n"
54
+ end
55
+
56
+ EM.add_timer(DELAY * 2) { EM.stop }
57
+ end
58
+ end
59
+ end