require 'simplecov' SimpleCov.start do add_filter "/spec/" end $:.unshift '../lib' require 'rupnp' require 'em-spec/rspec' require 'webmock/rspec' RUPNP.log_level = :failure class FakeMulticast < RUPNP::SSDP::MulticastConnection attr_reader :handshake_response, :packets def onclose(&blk); @onclose = blk; end def onmessage(&blk); @onmessage = blk; end def initialize @packets = [] end def receive_data(data) @onmessage.call(data) if defined? @onmessage @packets << data end def unbind @onclose.call if defined? @onclose end end def generate_search_responder(uuid, port) responder = EM.open_datagram_socket(RUPNP::MULTICAST_IP, RUPNP::DISCOVERY_PORT, FakeMulticast) responder.onmessage do |data| data =~ /ST: (.*)\r\n/ target = $1 response =< 1, :version_minor => 1, :device_type => :base, }.merge(options) desc=< #{opt[:version_major]} #{opt[:version_minor]} urn:schemas-upnp-org:device:#{opt[:device_type].capitalize}:1-0 Friendly name RUPNP Model name uuid:#{uuid} EOD if opt[:device_type] != :base desc << < usrn:schemas-upnp-org:service:ContentDirectory:1 urn:upnp-org:serviceId:ContentDirectory /cd/description.xml /cd/control EOD end desc << " \n\n" end def generate_scpd(options={}) opt = { :version_major => 1, :version_minor => 1, :nb_state_var => 1, :define_action => false, :send_event => false }.merge(options) scpd=< #{opt[:version_major]} #{opt[:version_minor]} EOD if opt[:define_action] scpd << < testAction var1 in X_variableName1 var2 out X_variableName2 EOAL end scpd << ' ' opt[:nb_state_var].times do |i| scpd << < X_variableName#{i+1} ui4 #{i} 0 #{64*(i+1) - 1} EOSV end scpd << " \n\n" end def action_response(hash) r = < EOD hash.each { |k, v| r << " <#{k}>#{v}\n"} r << < EOD end NOTIFY_REGEX = { :common => [ /^NOTIFY \* HTTP\/1.1\r\n/, /HOST: 239\.255\.255\.250:1900\r\n/, /NT: [0-9A-Za-z:-]+\r\n/, /USN: uuid:(.*)\r\n/, /BOOTID.UPNP.ORG: \d+\r\n/, /CONFIGID.UPNP.ORG: \d+\r\n/, ].freeze, :alive => [ /CACHE-CONTROL:\s+max-age\s+=\s+\d+\r\n/, /LOCATION: http:\/\/(.*)\r\n/, /NTS: ssdp:alive\r\n/, /SERVER: (.*)\r\n/, ].freeze, :byebye => [ /NTS: ssdp:byebye\r\n/, ].freeze, :update => [ /LOCATION: http:\/\/(.*)\r\n/, /NTS: ssdp:update\r\n/, /NEXTBOOTID.UPNP.ORG: \d+\r\n/, ].freeze } RSpec::Matchers.define :be_a_notify_packet do |type| match do |packet| success = NOTIFY_REGEX[:common].all? { |r| packet.match(r) } success &&= NOTIFY_REGEX[type].all? { |r| packet.match(r) } success && packet[-4..-1] == "\r\n\r\n" end end SEARCH_REGEX = [/^M-SEARCH \* HTTP\/1\.1\r\n/, /HOST: 239\.255\.255\.250:1900\r\n/, /MAN:\s+"ssdp:discover"\r\n/, /MX:\s+\d+\r\n/, /ST:\s+(ssdp:all|upnp:rootdevice|uuid:.+|urn:[\w:-]+)\r\n/, ].freeze RSpec::Matchers.define :be_a_msearch_packet do match do |packet| success = SEARCH_REGEX.all? { |r| packet.match(r) } success && packet[-4..-1] == "\r\n\r\n" end end