def Wait_for_privet_mdns_service(t_seconds, service, logger, wifi_interfaces=[]): """Listens for t_seconds and returns an information object for each service. This is the primary interface to discover mDNS services. It blocks for t_seconds while listening, and returns a list of information objects, one for each service discovered. Args: t_seconds: Time to listen for mDNS records, in seconds. Floating point ok. service: The service to wait for, if found, return early is_add: If True, wait for service to be added If False, wait for service to be removed wifi_interfaces: The interfaces to listen on as strings, if empty listen on all interfaces. For example: ['192.168.1.2']. Returns: If Add event observed, return the Zeroconf information class; otherwise, return None """ l = _Listener(logger) if not wifi_interfaces: z = Zeroconf() else: z = Zeroconf(wifi_interfaces) sb = ServiceBrowser(zc=z, type_='_privet._tcp.local.', listener=l) service_info = wait_for_service_add(t_seconds, service, l) sb.cancel() # Only method available to kill all threads pylint: disable=protected-access z._GLOBAL_DONE = True zeroconf_threads = _find_zeroconf_threads() # Wait up to 30 seconds for zeroconf to terminate its threads t_end = time.time() + 30 while len(zeroconf_threads) > 1 and time.time() < t_end: time.sleep(0.01) zeroconf_threads = _find_zeroconf_threads() z.close() if len(zeroconf_threads) > 1: logger.info('Zeroconf failed to terminate its threads in 30 seconds.') else: logger.info('All listeners have been stopped.') return service_info
def test_lots_of_names(self): # instantiate a zeroconf instance zc = Zeroconf(interfaces=["127.0.0.1"]) # create a bunch of servers type_ = "_my-service._tcp.local." name = "a wonderful service" server_count = 300 self.generate_many_hosts(zc, type_, name, server_count) # verify that name changing works self.verify_name_change(zc, type_, name, server_count) # we are going to monkey patch the zeroconf send to check packet sizes old_send = zc.send # needs to be a list so that we can modify it in our phony send longest_packet = [0, None] def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT): """Sends an outgoing packet.""" packet = out.packet() if longest_packet[0] < len(packet): longest_packet[0] = len(packet) longest_packet[1] = out old_send(out, addr=addr, port=port) # monkey patch the zeroconf send zc.send = send # dummy service callback def on_service_state_change(zeroconf, service_type, state_change, name): pass # start a browser browser = ServiceBrowser(zc, type_, [on_service_state_change]) # wait until the browse request packet has maxed out in size sleep_count = 0 while sleep_count < 100 and longest_packet[ 0] < r._MAX_MSG_ABSOLUTE - 100: sleep_count += 1 time.sleep(0.1) browser.cancel() time.sleep(0.5) import zeroconf zeroconf.log.debug("sleep_count %d, sized %d", sleep_count, longest_packet[0]) # now the browser has sent at least one request, verify the size assert longest_packet[0] <= r._MAX_MSG_ABSOLUTE assert longest_packet[0] >= r._MAX_MSG_ABSOLUTE - 100 # mock zeroconf's logger warning() and debug() from mock import patch patch_warn = patch("zeroconf.log.warning") patch_debug = patch("zeroconf.log.debug") mocked_log_warn = patch_warn.start() mocked_log_debug = patch_debug.start() # now that we have a long packet in our possession, let's verify the # exception handling. out = longest_packet[1] out.data.append(b"\0" * 1000) # mock the zeroconf logger and check for the correct logging backoff call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count # try to send an oversized packet zc.send(out) assert mocked_log_warn.call_count == call_counts[0] + 1 assert mocked_log_debug.call_count == call_counts[0] zc.send(out) assert mocked_log_warn.call_count == call_counts[0] + 1 assert mocked_log_debug.call_count == call_counts[0] + 1 # force a receive of an oversized packet packet = out.packet() s = zc._respond_sockets[0] # mock the zeroconf logger and check for the correct logging backoff call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count # force receive on oversized packet s.sendto(packet, 0, (r._MDNS_ADDR, r._MDNS_PORT)) s.sendto(packet, 0, (r._MDNS_ADDR, r._MDNS_PORT)) time.sleep(2.0) zeroconf.log.debug( "warn %d debug %d was %s", mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts, ) assert mocked_log_debug.call_count > call_counts[0] # close our zeroconf which will close the sockets zc.close() # pop the big chunk off the end of the data and send on a closed socket out.data.pop() zc._GLOBAL_DONE = False # mock the zeroconf logger and check for the correct logging backoff call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count # send on a closed socket (force a socket error) zc.send(out) zeroconf.log.debug( "warn %d debug %d was %s", mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts, ) assert mocked_log_warn.call_count > call_counts[0] assert mocked_log_debug.call_count > call_counts[0] zc.send(out) zeroconf.log.debug( "warn %d debug %d was %s", mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts, ) assert mocked_log_debug.call_count > call_counts[0] + 2 mocked_log_warn.stop() mocked_log_debug.stop()
def test_lots_of_names(self): # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) # create a bunch of servers type_ = "_my-service._tcp.local." name = 'a wonderful service' server_count = 300 self.generate_many_hosts(zc, type_, name, server_count) # verify that name changing works self.verify_name_change(zc, type_, name, server_count) # we are going to monkey patch the zeroconf send to check packet sizes old_send = zc.send longest_packet_len = 0 longest_packet = None # type: Optional[r.DNSOutgoing] def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT): """Sends an outgoing packet.""" packet = out.packet() nonlocal longest_packet_len, longest_packet if longest_packet_len < len(packet): longest_packet_len = len(packet) longest_packet = out old_send(out, addr=addr, port=port) # monkey patch the zeroconf send setattr(zc, "send", send) # dummy service callback def on_service_state_change(zeroconf, service_type, state_change, name): pass # start a browser browser = ServiceBrowser(zc, type_, [on_service_state_change]) # wait until the browse request packet has maxed out in size sleep_count = 0 while sleep_count < 100 and longest_packet_len < r._MAX_MSG_ABSOLUTE - 100: sleep_count += 1 time.sleep(0.1) browser.cancel() time.sleep(0.5) import zeroconf zeroconf.log.debug('sleep_count %d, sized %d', sleep_count, longest_packet_len) # now the browser has sent at least one request, verify the size assert longest_packet_len <= r._MAX_MSG_ABSOLUTE assert longest_packet_len >= r._MAX_MSG_ABSOLUTE - 100 # mock zeroconf's logger warning() and debug() from unittest.mock import patch patch_warn = patch('zeroconf.log.warning') patch_debug = patch('zeroconf.log.debug') mocked_log_warn = patch_warn.start() mocked_log_debug = patch_debug.start() # now that we have a long packet in our possession, let's verify the # exception handling. out = longest_packet assert out is not None out.data.append(b'\0' * 1000) # mock the zeroconf logger and check for the correct logging backoff call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count # try to send an oversized packet zc.send(out) assert mocked_log_warn.call_count == call_counts[0] + 1 assert mocked_log_debug.call_count == call_counts[0] zc.send(out) assert mocked_log_warn.call_count == call_counts[0] + 1 assert mocked_log_debug.call_count == call_counts[0] + 1 # force a receive of an oversized packet packet = out.packet() s = zc._respond_sockets[0] # mock the zeroconf logger and check for the correct logging backoff call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count # force receive on oversized packet s.sendto(packet, 0, (r._MDNS_ADDR, r._MDNS_PORT)) s.sendto(packet, 0, (r._MDNS_ADDR, r._MDNS_PORT)) time.sleep(2.0) zeroconf.log.debug( 'warn %d debug %d was %s', mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts ) assert mocked_log_debug.call_count > call_counts[0] # close our zeroconf which will close the sockets zc.close() # pop the big chunk off the end of the data and send on a closed socket out.data.pop() zc._GLOBAL_DONE = False # mock the zeroconf logger and check for the correct logging backoff call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count # send on a closed socket (force a socket error) zc.send(out) zeroconf.log.debug( 'warn %d debug %d was %s', mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts ) assert mocked_log_warn.call_count > call_counts[0] assert mocked_log_debug.call_count > call_counts[0] zc.send(out) zeroconf.log.debug( 'warn %d debug %d was %s', mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts ) assert mocked_log_debug.call_count > call_counts[0] + 2 mocked_log_warn.stop() mocked_log_debug.stop()