spectra-xmpp4r-observable 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +339 -0
- data/README +87 -0
- data/lib/observable_thing.rb +187 -0
- data/lib/thread_store.rb +58 -0
- data/lib/xmpp4r-observable.rb +648 -0
- data/test/simple_observer.rb +28 -0
- data/test/tc_observable_thing.rb +211 -0
- data/test/tc_thread_store.rb +75 -0
- data/test/tc_xmpp4r-observable.rb +178 -0
- metadata +73 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
class Observer
|
2
|
+
attr_accessor :delete
|
3
|
+
attr_reader :last, :last_args
|
4
|
+
def initialize(name, time = 0)
|
5
|
+
@name = name
|
6
|
+
@last = ""
|
7
|
+
@last_args = []
|
8
|
+
@delete = false
|
9
|
+
@time = time
|
10
|
+
end
|
11
|
+
|
12
|
+
def update(thing, *args)
|
13
|
+
sleep @time if @time > 0
|
14
|
+
@last = "#{@name}: got an update on #{thing} with args = #{args.join(', ')}"
|
15
|
+
@last_args = args
|
16
|
+
return :delete_me if @delete
|
17
|
+
end
|
18
|
+
|
19
|
+
def check(str)
|
20
|
+
@last == str
|
21
|
+
end
|
22
|
+
|
23
|
+
def clear
|
24
|
+
@last.clear if @last.respond_to?(:clear)
|
25
|
+
@last_args.clear if @last_args.respond_to?(:clear)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,211 @@
|
|
1
|
+
$: << File.dirname(__FILE__) + "/../lib"
|
2
|
+
require 'test/unit'
|
3
|
+
require 'observable_thing'
|
4
|
+
require 'simple_observer.rb'
|
5
|
+
|
6
|
+
class TestObservableThing < Test::Unit::TestCase
|
7
|
+
include ObservableThing
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@observer1 = Observer.new("observer 1")
|
11
|
+
@observer2 = Observer.new("observer 2")
|
12
|
+
@observer3 = Observer.new("observer 3", 10)
|
13
|
+
end
|
14
|
+
|
15
|
+
def teardown
|
16
|
+
@things.clear if defined? @things
|
17
|
+
@things_counter.clear if defined? @things_counter
|
18
|
+
@things_state.clear if defined? @things_state
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_add_observer
|
22
|
+
self.add_observer(:one_thing, @observer1)
|
23
|
+
assert @things.include?(:one_thing)
|
24
|
+
assert @things[:one_thing].include?(@observer1)
|
25
|
+
assert_equal :update, @things[:one_thing][@observer1]
|
26
|
+
|
27
|
+
self.add_observer(:other_thing, @observer2)
|
28
|
+
assert @things.include?(:other_thing)
|
29
|
+
assert @things[:other_thing].include?(@observer2)
|
30
|
+
assert_equal :update, @things[:other_thing][@observer2]
|
31
|
+
|
32
|
+
assert_raise(NoMethodError) {
|
33
|
+
self.add_observer(:other_thing, @observer1, :no_method)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_delete_observer
|
38
|
+
self.add_observer(:one_thing, @observer1)
|
39
|
+
|
40
|
+
assert @things[:one_thing].include?(@observer1)
|
41
|
+
self.delete_observer(:one_thing, @observer1)
|
42
|
+
assert ! @things[:one_thing].include?(@observer1)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_delete_observers
|
46
|
+
self.add_observer(:one_thing, @observer1)
|
47
|
+
self.add_observer(:one_thing, @observer2)
|
48
|
+
self.add_observer(:other_thing, @observer1)
|
49
|
+
|
50
|
+
assert @things[:one_thing].include?(@observer1)
|
51
|
+
assert @things[:one_thing].include?(@observer2)
|
52
|
+
assert @things[:other_thing].include?(@observer1)
|
53
|
+
self.delete_observers(:one_thing)
|
54
|
+
assert ! @things[:one_thing].include?(@observer1)
|
55
|
+
assert ! @things[:one_thing].include?(@observer2)
|
56
|
+
assert @things[:other_thing].include?(@observer1)
|
57
|
+
|
58
|
+
self.add_observer(:one_thing, @observer1)
|
59
|
+
self.add_observer(:one_thing, @observer2)
|
60
|
+
assert ! @things.empty?
|
61
|
+
self.delete_observers
|
62
|
+
assert @things.empty?
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_count_observers
|
66
|
+
self.add_observer(:one_thing, @observer1)
|
67
|
+
self.add_observer(:one_thing, @observer2)
|
68
|
+
self.add_observer(:other_thing, @observer1)
|
69
|
+
|
70
|
+
assert_equal 3, self.count_observers
|
71
|
+
assert_equal 2, self.count_observers(:one_thing)
|
72
|
+
assert_equal 1, self.count_observers(:other_thing)
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_count_notifications
|
76
|
+
self.add_observer(:one_thing, @observer1)
|
77
|
+
|
78
|
+
assert_equal 0, self.count_notifications(:no_thing)
|
79
|
+
assert_equal 0, self.count_notifications(:one_thing)
|
80
|
+
|
81
|
+
self.changed(:one_thing)
|
82
|
+
self.notify_observers(:one_thing, :something)
|
83
|
+
|
84
|
+
assert_equal 0, self.count_notifications(:no_thing)
|
85
|
+
assert_equal 1, self.count_notifications(:one_thing)
|
86
|
+
|
87
|
+
self.changed(:no_thing)
|
88
|
+
self.notify_observers(:no_thing, :something)
|
89
|
+
|
90
|
+
assert_equal 0, self.count_notifications(:no_thing)
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_changed
|
94
|
+
assert ! self.changed?(:something)
|
95
|
+
self.changed(:something)
|
96
|
+
assert self.changed?(:something)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_notify_observers
|
100
|
+
self.add_observer(:one_thing, @observer1)
|
101
|
+
self.add_observer(:one_thing, @observer2)
|
102
|
+
self.add_observer(:other_thing, @observer1)
|
103
|
+
assert @observer1.check("")
|
104
|
+
assert @observer2.check("")
|
105
|
+
|
106
|
+
self.changed(:one_thing)
|
107
|
+
self.notify_observers(:one_thing, "test")
|
108
|
+
self.wait_notifications
|
109
|
+
assert @observer1.check("observer 1: got an update on one_thing with args = test")
|
110
|
+
assert @observer2.check("observer 2: got an update on one_thing with args = test")
|
111
|
+
|
112
|
+
self.changed(:other_thing)
|
113
|
+
self.notify_observers(:other_thing, "foo", "bar")
|
114
|
+
self.wait_notifications
|
115
|
+
assert @observer1.check("observer 1: got an update on other_thing with args = foo, bar")
|
116
|
+
assert @observer2.check("observer 2: got an update on one_thing with args = test")
|
117
|
+
|
118
|
+
@observer2.delete = true
|
119
|
+
self.changed(:one_thing)
|
120
|
+
self.notify_observers(:one_thing, "foo", "bar")
|
121
|
+
self.wait_notifications
|
122
|
+
assert @observer2.check("observer 2: got an update on one_thing with args = foo, bar")
|
123
|
+
assert @observer1.check("observer 1: got an update on one_thing with args = foo, bar")
|
124
|
+
|
125
|
+
self.changed(:one_thing)
|
126
|
+
self.notify_observers(:one_thing, "fooo", "barr")
|
127
|
+
self.wait_notifications
|
128
|
+
assert @observer2.check("observer 2: got an update on one_thing with args = foo, bar")
|
129
|
+
assert @observer1.check("observer 1: got an update on one_thing with args = fooo, barr")
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_pending_notifications?
|
134
|
+
self.add_observer(:delayed_thing, @observer3)
|
135
|
+
1.upto(3) do
|
136
|
+
self.changed(:delayed_thing)
|
137
|
+
self.notify_observers(:delayed_thing, "foo bar")
|
138
|
+
end
|
139
|
+
assert self.pending_notifications?, "should have notifications pending"
|
140
|
+
sleep 15
|
141
|
+
assert ! self.pending_notifications?, "should not have anything pending"
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_wait_notifications
|
145
|
+
time = Time.now
|
146
|
+
self.add_observer(:delayed_thing, @observer3)
|
147
|
+
self.changed(:delayed_thing)
|
148
|
+
self.notify_observers(:delayed_thing, "foo bar")
|
149
|
+
self.wait_notifications
|
150
|
+
dif = Time.now - time
|
151
|
+
assert dif >= 10, "should take around 10 seconds"
|
152
|
+
assert dif < 12, "should not take so long"
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
class TestQObserver < Test::Unit::TestCase
|
158
|
+
|
159
|
+
def setup
|
160
|
+
@qobserver = QObserver.new
|
161
|
+
end
|
162
|
+
|
163
|
+
def teardown
|
164
|
+
@qobserver = nil
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_queues
|
168
|
+
assert @qobserver.queues.empty?
|
169
|
+
@qobserver.update(:thing1, 123)
|
170
|
+
assert_equal 1, @qobserver.queues.length
|
171
|
+
@qobserver.update(:thing2, 123)
|
172
|
+
assert @qobserver.queues.include?(:thing1)
|
173
|
+
assert @qobserver.queues.include?(:thing2)
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_received?
|
177
|
+
assert ! @qobserver.received?(:thing1)
|
178
|
+
@qobserver.update(:thing1, 123)
|
179
|
+
assert @qobserver.received?(:thing1)
|
180
|
+
extract = @qobserver.received(:thing1)
|
181
|
+
assert ! @qobserver.received?(:thing1)
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_received
|
185
|
+
@qobserver.update(:thing1, 123)
|
186
|
+
@qobserver.update(:thing1, 456)
|
187
|
+
assert ! @qobserver.received?(:nothing)
|
188
|
+
extract = @qobserver.received(:thing1)
|
189
|
+
assert_kind_of Array, extract
|
190
|
+
assert extract.include?([123])
|
191
|
+
assert extract.include?([456])
|
192
|
+
assert ! @qobserver.received?(:thing1)
|
193
|
+
|
194
|
+
@qobserver.update(:thing1, 789)
|
195
|
+
@qobserver.update(:thing1, 890)
|
196
|
+
|
197
|
+
@qobserver.received(:thing1) do |arg|
|
198
|
+
assert [[789], [890]].include?(arg)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_size
|
203
|
+
@qobserver.update(:thing1, 123)
|
204
|
+
assert_equal 1, @qobserver.size(:thing1)
|
205
|
+
assert_equal 0, @qobserver.size(:thing2)
|
206
|
+
|
207
|
+
@qobserver.received(:thing1)
|
208
|
+
assert_equal 0, @qobserver.size(:thing1)
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
$: << File.dirname(__FILE__) + "/../lib"
|
2
|
+
require 'test/unit'
|
3
|
+
require 'thread_store'
|
4
|
+
|
5
|
+
class TestThreadStore < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@ts = ThreadStore.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
@ts.kill!
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_initialize
|
15
|
+
ts1 = ThreadStore.new
|
16
|
+
assert_equal 100, ts1.max
|
17
|
+
|
18
|
+
ts2 = ThreadStore.new(50)
|
19
|
+
assert_equal 50, ts2.max
|
20
|
+
|
21
|
+
ts3 = ThreadStore.new(0)
|
22
|
+
assert_equal 0, ts3.max
|
23
|
+
|
24
|
+
ts4 = ThreadStore.new(-1)
|
25
|
+
assert_equal 0, ts4.max
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_add_more_than_max
|
29
|
+
@ts = ThreadStore.new
|
30
|
+
1.upto(200) do |n|
|
31
|
+
@ts.add Thread.new { sleep 10 while true }
|
32
|
+
end
|
33
|
+
assert @ts.size <= @ts.max
|
34
|
+
@ts.add Thread.new { sleep 10 while true }
|
35
|
+
assert @ts.size <= @ts.max
|
36
|
+
@ts.kill!
|
37
|
+
|
38
|
+
@ts = ThreadStore.new(0)
|
39
|
+
1.upto(200) do |n|
|
40
|
+
@ts.add Thread.new { sleep 10 while true }
|
41
|
+
end
|
42
|
+
assert 200, @ts.size
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_auto_kill
|
46
|
+
@ts = ThreadStore.new(10)
|
47
|
+
1.upto(10) do |n|
|
48
|
+
@ts.add Thread.new { sleep 10 }
|
49
|
+
end
|
50
|
+
assert_equal 10, @ts.size
|
51
|
+
sleep 15
|
52
|
+
assert_equal 0, @ts.size
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_kill!
|
56
|
+
@ts = ThreadStore.new(10)
|
57
|
+
1.upto(10) do |n|
|
58
|
+
@ts.add Thread.new { sleep 10 }
|
59
|
+
end
|
60
|
+
assert_equal 10, @ts.size
|
61
|
+
@ts.kill!
|
62
|
+
assert_equal 0, @ts.size
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_keep
|
66
|
+
@ts = ThreadStore.new(0)
|
67
|
+
1.upto(100) do |n|
|
68
|
+
@ts.add Thread.new { sleep 10 while true }
|
69
|
+
end
|
70
|
+
assert_equal 100, @ts.size
|
71
|
+
@ts.keep(50)
|
72
|
+
assert_equal 50, @ts.size
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
$: << File.dirname(__FILE__)
|
2
|
+
$: << File.dirname(__FILE__) + "/../lib"
|
3
|
+
require 'test/unit'
|
4
|
+
require 'timeout'
|
5
|
+
require 'xmpp4r-observable'
|
6
|
+
require 'simple_observer'
|
7
|
+
|
8
|
+
class TestJabberObservable < Test::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
@@connections ||= {}
|
11
|
+
|
12
|
+
if @@connections.include?(:client1)
|
13
|
+
@client1 = @@connections[:client1]
|
14
|
+
@client2 = @@connections[:client2]
|
15
|
+
@client1.subs.accept = true
|
16
|
+
@client2.subs.accept = true
|
17
|
+
@jid1_raw = @@connections[:jid1_raw]
|
18
|
+
@jid2_raw = @@connections[:jid2_raw]
|
19
|
+
@jid1 = @jid1_raw.strip.to_s
|
20
|
+
@jid2 = @jid2_raw.strip.to_s
|
21
|
+
@domain1 = @jid1_raw.domain
|
22
|
+
@domain2 = @jid2_raw.domain
|
23
|
+
@message_observer = Observer.new("message_observer")
|
24
|
+
@subscription_observer = Observer.new("subscription_observer")
|
25
|
+
return true
|
26
|
+
end
|
27
|
+
|
28
|
+
logins = []
|
29
|
+
begin
|
30
|
+
logins = File.readlines(File.expand_path("~/.xmpp4r-observable-test-config")).map! { |login| login.split(" ") }
|
31
|
+
raise StandardError unless logins.size == 2
|
32
|
+
rescue => e
|
33
|
+
puts "\nConfiguration Error!\n\nYou must make available two unique Jabber accounts in order for the tests to pass."
|
34
|
+
puts "Place them in ~/.xmpp4r-observable-test-config, one per line like so:\n\n"
|
35
|
+
puts "user1@example.com/res password"
|
36
|
+
puts "user2@example.com/res password\n\n"
|
37
|
+
raise e
|
38
|
+
end
|
39
|
+
|
40
|
+
@@connections[:client1] = Jabber::Observable.new(*logins[0])
|
41
|
+
@@connections[:client2] = Jabber::Observable.new(*logins[1])
|
42
|
+
|
43
|
+
@@connections[:jid1_raw] = Jabber::JID.new(logins[0][0])
|
44
|
+
@@connections[:jid2_raw] = Jabber::JID.new(logins[1][0])
|
45
|
+
|
46
|
+
# Force load the client and roster, just to be safe.
|
47
|
+
@@connections[:client1].roster
|
48
|
+
@@connections[:client2].roster
|
49
|
+
|
50
|
+
# Re-run this method to setup the local instance variables the first time.
|
51
|
+
setup
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_ensure_the_jabber_clients_are_connected_after_setup
|
55
|
+
assert @client1.client.is_connected?
|
56
|
+
assert @client2.client.is_connected?
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_remove_users_from_our_roster_should_succeed
|
60
|
+
@client2.subs.remove(@jid1)
|
61
|
+
@client1.subs.remove(@jid2)
|
62
|
+
|
63
|
+
assert_before 60 do
|
64
|
+
assert_equal false, @client1.subs.subscribed_to?(@jid2)
|
65
|
+
assert_equal false, @client2.subs.subscribed_to?(@jid1)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_add_users_to_our_roster_should_succeed_with_automatic_approval
|
70
|
+
@client1.subs.remove(@jid2)
|
71
|
+
@client2.subs.remove(@jid1)
|
72
|
+
|
73
|
+
assert_before 10 do
|
74
|
+
assert_equal false, @client1.subs.subscribed_to?(@jid2)
|
75
|
+
assert_equal false, @client2.subs.subscribed_to?(@jid1)
|
76
|
+
end
|
77
|
+
|
78
|
+
@client1.add_observer(:new_subscription, @subscription_observer)
|
79
|
+
@subscription_observer.clear
|
80
|
+
@client1.subs.add(@jid2)
|
81
|
+
|
82
|
+
assert_before 10 do
|
83
|
+
assert @client1.subs.subscribed_to?(@jid2)
|
84
|
+
end
|
85
|
+
|
86
|
+
assert_equal 1, @subscription_observer.last_args.size
|
87
|
+
assert_equal @jid2, @subscription_observer.last_args[0][0].jid.strip.to_s
|
88
|
+
@client1.delete_observer(:new_subscription, @subscription_observer)
|
89
|
+
@subscription_observer.clear
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_disable_auto_accept_subscription_requests
|
93
|
+
@client1.subs.remove(@jid2)
|
94
|
+
@client2.subs.remove(@jid1)
|
95
|
+
|
96
|
+
assert_before(60) do
|
97
|
+
assert ! @client1.subs.subscribed_to?(@jid2)
|
98
|
+
assert ! @client2.subs.subscribed_to?(@jid1)
|
99
|
+
end
|
100
|
+
|
101
|
+
@client1.add_observer(:subscription_request, @subscription_observer)
|
102
|
+
@client1.subs.accept = false
|
103
|
+
@subscription_observer.clear
|
104
|
+
assert ! @client1.subs.accept?
|
105
|
+
|
106
|
+
assert_before(60) { assert @subscription_observer.last.empty? }
|
107
|
+
|
108
|
+
@client2.subs.add(@jid1)
|
109
|
+
|
110
|
+
assert_before(60) do
|
111
|
+
assert ! @subscription_observer.last.empty?
|
112
|
+
end
|
113
|
+
|
114
|
+
assert_equal @jid2, @subscription_observer.last_args[0][0].jid.strip.to_s
|
115
|
+
assert_equal :subscribe, @subscription_observer.last_args[0][1].type
|
116
|
+
|
117
|
+
@client1.delete_observer(:subscription_request, @subscription_observer)
|
118
|
+
@subscription_observer.clear
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_automatically_reconnect
|
122
|
+
@client1.client.close
|
123
|
+
|
124
|
+
sleep 2
|
125
|
+
assert_equal false, @client1.connected?
|
126
|
+
|
127
|
+
@client2.add_observer(:message, @message_observer)
|
128
|
+
|
129
|
+
assert @message_observer.last.empty?
|
130
|
+
@client1.deliver(@jid2, "Testing")
|
131
|
+
|
132
|
+
sleep 2
|
133
|
+
assert @client1.connected?
|
134
|
+
assert @client1.roster.instance_variable_get('@stream').is_connected?
|
135
|
+
sleep 2
|
136
|
+
assert ! @message_observer.last.empty?
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_disconnect_doesnt_allow_auto_reconnects
|
140
|
+
@client1.disconnect
|
141
|
+
|
142
|
+
assert_equal false, @client1.connected?
|
143
|
+
|
144
|
+
assert_raises Jabber::ConnectionError do
|
145
|
+
@client1.deliver(@jid2, "testing")
|
146
|
+
end
|
147
|
+
|
148
|
+
@client1.reconnect
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def assert_before(seconds, &block)
|
154
|
+
error = nil
|
155
|
+
|
156
|
+
# This is for Ruby 1.9.1 compatibility
|
157
|
+
assertion_exception_class = begin
|
158
|
+
MiniTest::Assertion
|
159
|
+
rescue NameError
|
160
|
+
Test::Unit::AssertionFailedError
|
161
|
+
end
|
162
|
+
|
163
|
+
begin
|
164
|
+
Timeout::timeout(seconds) {
|
165
|
+
begin
|
166
|
+
yield
|
167
|
+
rescue assertion_exception_class => e
|
168
|
+
error = e
|
169
|
+
sleep 0.5
|
170
|
+
retry
|
171
|
+
end
|
172
|
+
}
|
173
|
+
rescue Timeout::Error
|
174
|
+
raise error
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|