playful 0.1.0.alpha.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.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/History.rdoc +3 -0
- data/LICENSE.rdoc +22 -0
- data/README.rdoc +194 -0
- data/Rakefile +20 -0
- data/features/control_point.feature +13 -0
- data/features/device.feature +22 -0
- data/features/device_discovery.feature +9 -0
- data/features/step_definitions/control_point_steps.rb +19 -0
- data/features/step_definitions/device_discovery_steps.rb +40 -0
- data/features/step_definitions/device_steps.rb +28 -0
- data/features/support/common.rb +9 -0
- data/features/support/env.rb +17 -0
- data/features/support/fake_upnp_device_collection.rb +108 -0
- data/features/support/world_extensions.rb +15 -0
- data/lib/core_ext/hash_patch.rb +5 -0
- data/lib/core_ext/socket_patch.rb +16 -0
- data/lib/core_ext/to_upnp_s.rb +65 -0
- data/lib/playful.rb +5 -0
- data/lib/playful/control_point.rb +175 -0
- data/lib/playful/control_point/base.rb +74 -0
- data/lib/playful/control_point/device.rb +511 -0
- data/lib/playful/control_point/error.rb +13 -0
- data/lib/playful/control_point/service.rb +404 -0
- data/lib/playful/device.rb +28 -0
- data/lib/playful/logger.rb +8 -0
- data/lib/playful/ssdp.rb +195 -0
- data/lib/playful/ssdp/broadcast_searcher.rb +114 -0
- data/lib/playful/ssdp/error.rb +6 -0
- data/lib/playful/ssdp/listener.rb +38 -0
- data/lib/playful/ssdp/multicast_connection.rb +112 -0
- data/lib/playful/ssdp/network_constants.rb +17 -0
- data/lib/playful/ssdp/notifier.rb +41 -0
- data/lib/playful/ssdp/searcher.rb +87 -0
- data/lib/playful/version.rb +3 -0
- data/lib/rack/upnp_control_point.rb +70 -0
- data/playful.gemspec +38 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/support/search_responses.rb +134 -0
- data/spec/unit/core_ext/to_upnp_s_spec.rb +105 -0
- data/spec/unit/playful/control_point/device_spec.rb +7 -0
- data/spec/unit/playful/control_point_spec.rb +45 -0
- data/spec/unit/playful/ssdp/listener_spec.rb +29 -0
- data/spec/unit/playful/ssdp/multicast_connection_spec.rb +157 -0
- data/spec/unit/playful/ssdp/notifier_spec.rb +76 -0
- data/spec/unit/playful/ssdp/searcher_spec.rb +110 -0
- data/spec/unit/playful/ssdp_spec.rb +214 -0
- data/tasks/control_point.html +30 -0
- data/tasks/control_point.thor +43 -0
- data/tasks/search.thor +128 -0
- data/tasks/test_js/FABridge.js +1425 -0
- data/tasks/test_js/WebSocketMain.swf +807 -0
- data/tasks/test_js/swfobject.js +825 -0
- data/tasks/test_js/web_socket.js +1133 -0
- data/test/test_ssdp.rb +298 -0
- data/test/test_ssdp_notification.rb +74 -0
- data/test/test_ssdp_response.rb +31 -0
- data/test/test_ssdp_search.rb +23 -0
- metadata +339 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'playful/control_point'
|
3
|
+
|
4
|
+
|
5
|
+
describe Playful::ControlPoint do
|
6
|
+
subject do
|
7
|
+
Playful::ControlPoint.new(1)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#ssdp_search_and_listen' do
|
11
|
+
let(:notification) do
|
12
|
+
double 'notification'
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:searcher) do
|
16
|
+
s = double 'Playful::SSDP::Searcher'
|
17
|
+
s.stub_chain(:discovery_responses, :subscribe).and_yield notification
|
18
|
+
|
19
|
+
s
|
20
|
+
end
|
21
|
+
|
22
|
+
before do
|
23
|
+
expect(Playful::SSDP).to receive(:search).with('ssdp:all', {}).and_return searcher
|
24
|
+
EM.stub(:add_periodic_timer)
|
25
|
+
end
|
26
|
+
|
27
|
+
after do
|
28
|
+
EM.unstub(:add_periodic_timer)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'creates a ControlPoint::Device for every discovery response' do
|
32
|
+
EM.stub(:add_timer)
|
33
|
+
subject.should_receive(:create_device).with(notification)
|
34
|
+
subject.ssdp_search_and_listen('ssdp:all')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'shuts down the searcher and starts the listener after the given response wait time' do
|
38
|
+
EM.stub(:add_timer).and_yield
|
39
|
+
subject.stub(:create_device)
|
40
|
+
searcher.should_receive(:close_connection)
|
41
|
+
subject.should_receive(:listen)
|
42
|
+
subject.ssdp_search_and_listen('ssdp:all')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'playful/ssdp/listener'
|
3
|
+
|
4
|
+
|
5
|
+
describe Playful::SSDP::Listener do
|
6
|
+
around(:each) do |example|
|
7
|
+
EM.synchrony do
|
8
|
+
example.run
|
9
|
+
EM.stop
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow_any_instance_of(Playful::SSDP::Listener).to receive(:setup_multicast_socket)
|
15
|
+
end
|
16
|
+
|
17
|
+
subject { Playful::SSDP::Listener.new(1) }
|
18
|
+
|
19
|
+
describe '#receive_data' do
|
20
|
+
it 'logs the IP and port from which the request came from' do
|
21
|
+
expect(subject).to receive(:peer_info).and_return %w[ip port]
|
22
|
+
expect(subject).to receive(:log).
|
23
|
+
with("Response from ip:port:\nmessage\n")
|
24
|
+
allow(subject).to receive(:parse).and_return({})
|
25
|
+
|
26
|
+
subject.receive_data('message')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'playful/ssdp/multicast_connection'
|
3
|
+
|
4
|
+
|
5
|
+
describe Playful::SSDP::MulticastConnection do
|
6
|
+
around(:each) do |example|
|
7
|
+
EM.synchrony do
|
8
|
+
example.run
|
9
|
+
EM.stop
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
subject { Playful::SSDP::MulticastConnection.new(1) }
|
14
|
+
|
15
|
+
before do
|
16
|
+
Playful.log = false
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#peer_info' do
|
20
|
+
before do
|
21
|
+
allow_any_instance_of(Playful::SSDP::MulticastConnection).to receive(:setup_multicast_socket)
|
22
|
+
subject.stub_chain(:get_peername, :[], :unpack).
|
23
|
+
and_return(%w[1234 1 2 3 4])
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns an Array with IP and port' do
|
27
|
+
expect(subject.peer_info).to eq ['1.2.3.4', 1234]
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns IP as a String' do
|
31
|
+
expect(subject.peer_info.first).to be_a String
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'returns port as a Fixnum' do
|
35
|
+
expect(subject.peer_info.last).to be_a Fixnum
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#parse' do
|
40
|
+
before do
|
41
|
+
allow_any_instance_of(Playful::SSDP::MulticastConnection).to receive(:setup_multicast_socket)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'turns headers into Hash keys' do
|
45
|
+
result = subject.parse ROOT_DEVICE1
|
46
|
+
expect(result).to have_key :cache_control
|
47
|
+
expect(result).to have_key :date
|
48
|
+
expect(result).to have_key :location
|
49
|
+
expect(result).to have_key :server
|
50
|
+
expect(result).to have_key :st
|
51
|
+
expect(result).to have_key :ext
|
52
|
+
expect(result).to have_key :usn
|
53
|
+
expect(result).to have_key :content_length
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'turns header values into Hash values' do
|
57
|
+
result = subject.parse ROOT_DEVICE1
|
58
|
+
expect(result[:cache_control]).to eq 'max-age=1200'
|
59
|
+
expect(result[:date]).to eq 'Mon, 26 Sep 2011 06:40:19 GMT'
|
60
|
+
expect(result[:location]).to eq 'http://1.2.3.4:5678/description/fetch'
|
61
|
+
expect(result[:server]).to eq 'Linux-i386-2.6.38-10-generic-pae, UPnP/1.0, PMS/1.25.1'
|
62
|
+
expect(result[:st]).to eq 'upnp:rootdevice'
|
63
|
+
expect(result[:ext]).to be_empty
|
64
|
+
expect(result[:usn]).to eq 'uuid:3c202906-992d-3f0f-b94c-90e1902a136d::upnp:rootdevice'
|
65
|
+
expect(result[:content_length]).to eq '0'
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'single line String as response data' do
|
69
|
+
before { @data = ROOT_DEVICE1.gsub("\n", ' ') }
|
70
|
+
|
71
|
+
it 'returns an empty Hash' do
|
72
|
+
expect(subject.parse(@data)).to eq({ })
|
73
|
+
end
|
74
|
+
|
75
|
+
it "logs the 'bad' response" do
|
76
|
+
subject.should_receive(:log).twice
|
77
|
+
subject.parse @data
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#setup_multicast_socket' do
|
83
|
+
before do
|
84
|
+
allow_any_instance_of(Playful::SSDP::MulticastConnection).to receive(:set_membership)
|
85
|
+
allow_any_instance_of(Playful::SSDP::MulticastConnection).to receive(:switch_multicast_loop)
|
86
|
+
allow_any_instance_of(Playful::SSDP::MulticastConnection).to receive(:set_multicast_ttl)
|
87
|
+
allow_any_instance_of(Playful::SSDP::MulticastConnection).to receive(:set_ttl)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'adds 0.0.0.0 and 239.255.255.250 to the membership group' do
|
91
|
+
expect(subject).to receive(:set_membership).with(
|
92
|
+
IPAddr.new('239.255.255.250').hton + IPAddr.new('0.0.0.0').hton
|
93
|
+
)
|
94
|
+
subject.setup_multicast_socket
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'sets multicast TTL to 4' do
|
98
|
+
expect(subject).to receive(:set_multicast_ttl).with(4)
|
99
|
+
subject.setup_multicast_socket
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'sets TTL to 4' do
|
103
|
+
expect(subject).to receive(:set_ttl).with(4)
|
104
|
+
subject.setup_multicast_socket
|
105
|
+
end
|
106
|
+
|
107
|
+
context "ENV['RUBY_UPNP_ENV'] != testing" do
|
108
|
+
after { ENV['RUBY_UPNP_ENV'] = 'testing' }
|
109
|
+
|
110
|
+
it 'turns multicast loop off' do
|
111
|
+
ENV['RUBY_UPNP_ENV'] = 'development'
|
112
|
+
expect(subject).to receive(:switch_multicast_loop).with(:off)
|
113
|
+
subject.setup_multicast_socket
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#switch_multicast_loop' do
|
119
|
+
before do
|
120
|
+
allow_any_instance_of(Playful::SSDP::MulticastConnection).to receive(:setup_multicast_socket)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "passes '\\001' to the socket option call when param == :on" do
|
124
|
+
expect(subject).to receive(:set_sock_opt).with(
|
125
|
+
Socket::IPPROTO_IP, Socket::IP_MULTICAST_LOOP, "\001"
|
126
|
+
)
|
127
|
+
subject.switch_multicast_loop :on
|
128
|
+
end
|
129
|
+
|
130
|
+
it "passes '\\001' to the socket option call when param == '\\001'" do
|
131
|
+
expect(subject).to receive(:set_sock_opt).with(
|
132
|
+
Socket::IPPROTO_IP, Socket::IP_MULTICAST_LOOP, "\001"
|
133
|
+
)
|
134
|
+
subject.switch_multicast_loop "\001"
|
135
|
+
end
|
136
|
+
|
137
|
+
it "passes '\\000' to the socket option call when param == :off" do
|
138
|
+
expect(subject).to receive(:set_sock_opt).with(
|
139
|
+
Socket::IPPROTO_IP, Socket::IP_MULTICAST_LOOP, "\000"
|
140
|
+
)
|
141
|
+
subject.switch_multicast_loop :off
|
142
|
+
end
|
143
|
+
|
144
|
+
it "passes '\\000' to the socket option call when param == '\\000'" do
|
145
|
+
expect(subject).to receive(:set_sock_opt).with(
|
146
|
+
Socket::IPPROTO_IP, Socket::IP_MULTICAST_LOOP, "\000"
|
147
|
+
)
|
148
|
+
subject.switch_multicast_loop "\000"
|
149
|
+
end
|
150
|
+
|
151
|
+
it "raises when not :on, :off, '\\000', or '\\001'" do
|
152
|
+
expect { subject.switch_multicast_loop 12312312 }.
|
153
|
+
to raise_error(Playful::SSDP::Error)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'playful/ssdp/notifier'
|
3
|
+
|
4
|
+
|
5
|
+
describe Playful::SSDP::Notifier do
|
6
|
+
around(:each) do |example|
|
7
|
+
EM.synchrony do
|
8
|
+
example.run
|
9
|
+
EM.stop
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:nt) { 'en tee' }
|
14
|
+
let(:usn) { 'you ess en' }
|
15
|
+
let(:ddf_url) { 'ddf url' }
|
16
|
+
let(:duration) { 567 }
|
17
|
+
|
18
|
+
subject do
|
19
|
+
Playful::SSDP::Notifier.new(1, nt, usn, ddf_url, duration)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#initialize' do
|
23
|
+
it 'creates a notification' do
|
24
|
+
expect_any_instance_of(Playful::SSDP::Notifier).to receive(:notification).
|
25
|
+
with(nt, usn, ddf_url, duration)
|
26
|
+
|
27
|
+
subject
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#post_init' do
|
32
|
+
context 'send_datagram returns positive value' do
|
33
|
+
before do
|
34
|
+
expect(subject).to receive(:send_datagram).and_return 1
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'logs what was sent' do
|
38
|
+
expect(subject).to receive(:log).with /Sent notification/
|
39
|
+
|
40
|
+
subject.post_init
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'send_datagram returns 0' do
|
45
|
+
before do
|
46
|
+
expect(subject).to receive(:send_datagram).and_return 0
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'does not log what was sent' do
|
50
|
+
expect(subject).to_not receive(:log)
|
51
|
+
|
52
|
+
subject.post_init
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#notification' do
|
58
|
+
before do
|
59
|
+
subject.instance_variable_set(:@os, 'my OS')
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'builds the notification message' do
|
63
|
+
expect(subject.notification(nt, usn, ddf_url, duration)).to eq <<-NOTE
|
64
|
+
NOTIFY * HTTP/1.1\r
|
65
|
+
HOST: 239.255.255.250:1900\r
|
66
|
+
CACHE-CONTROL: max-age=567\r
|
67
|
+
LOCATION: ddf url\r
|
68
|
+
NT: en tee\r
|
69
|
+
NTS: ssdp:alive\r
|
70
|
+
SERVER: my OS UPnP/1.0 Playful/#{Playful::VERSION}\r
|
71
|
+
USN: you ess en\r
|
72
|
+
\r
|
73
|
+
NOTE
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'playful/ssdp/searcher'
|
3
|
+
|
4
|
+
|
5
|
+
describe Playful::SSDP::Searcher do
|
6
|
+
around(:each) do |example|
|
7
|
+
EM.synchrony do
|
8
|
+
example.run
|
9
|
+
EM.stop
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
Playful.log = false
|
15
|
+
allow_any_instance_of(Playful::SSDP::MulticastConnection).to receive(:setup_multicast_socket)
|
16
|
+
end
|
17
|
+
|
18
|
+
subject do
|
19
|
+
Playful::SSDP::Searcher.new(1, 'ssdp:all', {})
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'lets you read its responses' do
|
23
|
+
responses = double 'responses'
|
24
|
+
subject.instance_variable_set(:@discovery_responses, responses)
|
25
|
+
expect(subject.discovery_responses).to eq responses
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#initialize' do
|
29
|
+
it 'does an #m_search' do
|
30
|
+
expect_any_instance_of(Playful::SSDP::Searcher).to receive(:m_search).and_return(<<-MSEARCH
|
31
|
+
M-SEARCH * HTTP/1.1\r
|
32
|
+
HOST: 239.255.255.250:1900\r
|
33
|
+
MAN: "ssdp:discover"\r
|
34
|
+
MX: 5\r
|
35
|
+
ST: ssdp:all\r
|
36
|
+
\r
|
37
|
+
MSEARCH
|
38
|
+
)
|
39
|
+
subject
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#receive_data' do
|
44
|
+
let(:parsed_response) do
|
45
|
+
parsed_response = double 'parsed response'
|
46
|
+
expect(parsed_response).to receive(:has_key?).with(:nts).and_return false
|
47
|
+
expect(parsed_response).to receive(:[]).and_return false
|
48
|
+
|
49
|
+
parsed_response
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'takes a response and adds it to the list of responses' do
|
53
|
+
response = double 'response'
|
54
|
+
allow(subject).to receive(:peer_info).and_return(['0.0.0.0', 4567])
|
55
|
+
|
56
|
+
expect(subject).to receive(:parse).with(response).exactly(1).times.
|
57
|
+
and_return(parsed_response)
|
58
|
+
expect(subject.instance_variable_get(:@discovery_responses)).to receive(:<<).
|
59
|
+
with(parsed_response)
|
60
|
+
|
61
|
+
subject.receive_data(response)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#post_init' do
|
66
|
+
before { allow_any_instance_of(Playful::SSDP::Searcher).to receive(:m_search).and_return('hi') }
|
67
|
+
|
68
|
+
it 'sends an M-SEARCH as a datagram over 239.255.255.250:1900' do
|
69
|
+
m_search_count_times = subject.instance_variable_get(:@m_search_count)
|
70
|
+
expect(subject).to receive(:send_datagram).
|
71
|
+
with('hi', '239.255.255.250', 1900).
|
72
|
+
exactly(m_search_count_times).times.
|
73
|
+
and_return 0
|
74
|
+
subject.post_init
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#m_search' do
|
79
|
+
it 'builds the MSEARCH string using the given parameters' do
|
80
|
+
expect(subject.m_search('ssdp:all', 10)).to eq <<-MSEARCH
|
81
|
+
M-SEARCH * HTTP/1.1\r
|
82
|
+
HOST: 239.255.255.250:1900\r
|
83
|
+
MAN: "ssdp:discover"\r
|
84
|
+
MX: 10\r
|
85
|
+
ST: ssdp:all\r
|
86
|
+
\r
|
87
|
+
MSEARCH
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'uses 239.255.255.250 as the HOST IP' do
|
91
|
+
expect(subject.m_search('ssdp:all', 10)).to match(/HOST: 239.255.255.250/m)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'uses 1900 as the HOST port' do
|
95
|
+
expect(subject.m_search('ssdp:all', 10)).to match(/HOST:.*1900/m)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'lets you search for undefined search target types' do
|
99
|
+
expect(subject.m_search('spaceship', 10)).to eq <<-MSEARCH
|
100
|
+
M-SEARCH * HTTP/1.1\r
|
101
|
+
HOST: 239.255.255.250:1900\r
|
102
|
+
MAN: "ssdp:discover"\r
|
103
|
+
MX: 10\r
|
104
|
+
ST: spaceship\r
|
105
|
+
\r
|
106
|
+
MSEARCH
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'playful/ssdp'
|
3
|
+
|
4
|
+
|
5
|
+
describe Playful::SSDP do
|
6
|
+
subject { Playful::SSDP }
|
7
|
+
|
8
|
+
describe '.listen' do
|
9
|
+
let(:listener) do
|
10
|
+
searcher = double 'Playful::SSDP::Listener'
|
11
|
+
searcher.stub_chain(:alive_notifications, :pop).and_yield(%w[one two])
|
12
|
+
searcher.stub_chain(:byebye_notifications, :pop).and_yield(%w[three four])
|
13
|
+
|
14
|
+
searcher
|
15
|
+
end
|
16
|
+
|
17
|
+
before do
|
18
|
+
allow(EM).to receive(:run).and_yield
|
19
|
+
allow(EM).to receive(:add_timer)
|
20
|
+
allow(EM).to receive(:open_datagram_socket).and_return listener
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'reactor is already running' do
|
24
|
+
it 'returns a Playful::SSDP::Listener' do
|
25
|
+
allow(EM).to receive(:reactor_running?).and_return true
|
26
|
+
expect(subject.listen).to eq listener
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'reactor is not already running' do
|
31
|
+
it 'returns a Hash of available and byebye responses' do
|
32
|
+
allow(EM).to receive(:add_shutdown_hook).and_yield
|
33
|
+
expect(subject.listen).to eq({
|
34
|
+
alive_notifications: %w[one two],
|
35
|
+
byebye_notifications: %w[three four]
|
36
|
+
})
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'opens a UDP socket on 239.255.255.250, port 1900' do
|
40
|
+
allow(EM).to receive(:add_shutdown_hook)
|
41
|
+
expect(EM).to receive(:open_datagram_socket).with('239.255.255.250', 1900,
|
42
|
+
Playful::SSDP::Listener, 4)
|
43
|
+
subject.listen
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '.search' do
|
49
|
+
let(:multicast_searcher) do
|
50
|
+
searcher = double 'Playful::SSDP::Searcher'
|
51
|
+
searcher.stub_chain(:discovery_responses, :subscribe).and_yield(%w[one two])
|
52
|
+
|
53
|
+
searcher
|
54
|
+
end
|
55
|
+
|
56
|
+
let(:broadcast_searcher) do
|
57
|
+
searcher = double 'Playful::SSDP::BroadcastSearcher'
|
58
|
+
searcher.stub_chain(:discovery_responses, :subscribe).and_yield(%w[three four])
|
59
|
+
|
60
|
+
searcher
|
61
|
+
end
|
62
|
+
|
63
|
+
before do
|
64
|
+
allow(EM).to receive(:run).and_yield
|
65
|
+
allow(EM).to receive(:add_timer)
|
66
|
+
allow(EM).to receive(:open_datagram_socket).and_return multicast_searcher
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when search_target is not a String' do
|
70
|
+
it 'calls #to_upnp_s on search_target' do
|
71
|
+
search_target = double('search_target')
|
72
|
+
expect(search_target).to receive(:to_upnp_s)
|
73
|
+
subject.search(search_target)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when search_target is a String' do
|
78
|
+
it 'calls #to_upnp_s on search_target but does not alter it' do
|
79
|
+
search_target = "I'm a string"
|
80
|
+
expect(search_target).to receive(:to_upnp_s).and_call_original
|
81
|
+
|
82
|
+
expect(EM).to receive(:open_datagram_socket).with('0.0.0.0', 0,
|
83
|
+
Playful::SSDP::Searcher, "I'm a string", {})
|
84
|
+
subject.search(search_target)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'reactor is already running' do
|
89
|
+
it 'returns a Playful::SSDP::Searcher' do
|
90
|
+
allow(EM).to receive(:reactor_running?).and_return true
|
91
|
+
expect(subject.search).to eq multicast_searcher
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'reactor is not already running' do
|
96
|
+
context 'options hash includes do_broadcast_search' do
|
97
|
+
before do
|
98
|
+
allow(EM).to receive(:open_datagram_socket).
|
99
|
+
and_return(multicast_searcher, broadcast_searcher)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'returns an Array of responses' do
|
103
|
+
allow(EM).to receive(:add_shutdown_hook).and_yield
|
104
|
+
expect(subject.search(:all, do_broadcast_search: true)).to eq %w[one two three four]
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'opens 2 UDP sockets on 0.0.0.0, port 0' do
|
108
|
+
allow(EM).to receive(:add_shutdown_hook)
|
109
|
+
expect(EM).to receive(:open_datagram_socket).with('0.0.0.0', 0, Playful::SSDP::Searcher,
|
110
|
+
'ssdp:all', {})
|
111
|
+
expect(EM).to receive(:open_datagram_socket).with('0.0.0.0', 0, Playful::SSDP::BroadcastSearcher,
|
112
|
+
'ssdp:all', 5, 4)
|
113
|
+
subject.search(:all, do_broadcast_search: true)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'options hash does not include do_broadcast_search' do
|
118
|
+
it 'returns an Array of responses' do
|
119
|
+
allow(EM).to receive(:add_shutdown_hook).and_yield
|
120
|
+
expect(subject.search).to eq %w[one two]
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'opens a UDP socket on 0.0.0.0, port 0' do
|
124
|
+
allow(EM).to receive(:add_shutdown_hook)
|
125
|
+
expect(EM).to receive(:open_datagram_socket).with('0.0.0.0', 0, Playful::SSDP::Searcher,
|
126
|
+
'ssdp:all', {})
|
127
|
+
subject.search
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '.notify' do
|
134
|
+
pending 'Implementation of UPnP Devices'
|
135
|
+
end
|
136
|
+
|
137
|
+
describe '.send_notification' do
|
138
|
+
pending 'Implementation of UPnP Devices'
|
139
|
+
end
|
140
|
+
|
141
|
+
=begin
|
142
|
+
context 'by default' do
|
143
|
+
it "searches for 'ssdp:all'" do
|
144
|
+
pending
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'waits for 5 seconds for responses' do
|
148
|
+
before = Time.now
|
149
|
+
|
150
|
+
SSDP.search
|
151
|
+
|
152
|
+
after = Time.now
|
153
|
+
(after - before).should < 5.1
|
154
|
+
(after - before).should > 5.0
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "finds 'upnp:rootdevice's" do
|
159
|
+
it "by using the spec's string 'upnp:rootdevice'" do
|
160
|
+
SSDP.search('upnp:rootdevice').should == [
|
161
|
+
{
|
162
|
+
:cache_control=>'max-age=1200',
|
163
|
+
:date=>'Mon, 26 Sep 2011 06:40:19 GMT',
|
164
|
+
:location=>'http://192.168.10.3:5001/description/fetch',
|
165
|
+
:server=>'Linux-i386-2.6.38-10-generic-pae, UPnP/1.0, PMS/1.25.1',
|
166
|
+
:st=>'upnp:rootdevice',
|
167
|
+
:ext=>'',
|
168
|
+
:usn=>'uuid:3c202906-992d-3f0f-b94c-90e1902a136d::upnp:rootdevice',
|
169
|
+
:content_length=>'0'
|
170
|
+
}
|
171
|
+
]
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'by using :root' do
|
175
|
+
pending
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'can wait for user-defined seconds for responses' do
|
180
|
+
before = Time.now
|
181
|
+
|
182
|
+
SSDP.search(:all, 1)
|
183
|
+
|
184
|
+
after = Time.now
|
185
|
+
(after - before).should < 1.1
|
186
|
+
(after - before).should > 1.0
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'finds a device by its URN' do
|
190
|
+
pending
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'finds a device by its UUID' do
|
194
|
+
pending
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'finds a device by its UPnP device type' do
|
198
|
+
pending
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'finds a device by its UPnP device type using a non-standard domain name' do
|
202
|
+
pending
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'finds a service by its UPnP service type' do
|
206
|
+
pending
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'find a service by its UPnP service type using a non-standard domain name' do
|
210
|
+
pending
|
211
|
+
end
|
212
|
+
=end
|
213
|
+
end
|
214
|
+
|