spectra-xmpp4r-observable 0.5.1
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/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
|