( server_extra_args=[], client_extra_args=[], expected_route=None, ice_impl=1, expected_counters=None, expected_candidates=None, timeout_sec=None, stun=_DEFAULT_STUN, turn=_DEFAULT_TURN, server_ice_impl=None, client_ice_impl=None )
| 719 | ] |
| 720 | |
| 721 | def ClientServerTest( server_extra_args=[], client_extra_args=[], expected_route=None, ice_impl=1, expected_counters=None, expected_candidates=None, timeout_sec=None, stun=_DEFAULT_STUN, turn=_DEFAULT_TURN, server_ice_impl=None, client_ice_impl=None ): |
| 722 | global g_failed |
| 723 | _srv_impl = server_ice_impl if server_ice_impl is not None else ice_impl |
| 724 | _cli_impl = client_ice_impl if client_ice_impl is not None else ice_impl |
| 725 | srv_impl_args = [ '--ice-implementation', str(_srv_impl) ] |
| 726 | cli_impl_args = [ '--ice-implementation', str(_cli_impl) ] |
| 727 | server = StartClientInThread( "server", "peer_server", "peer_client", server_extra_args + srv_impl_args, stun=stun, turn=turn ) |
| 728 | client = StartClientInThread( "client", "peer_client", "peer_server", client_extra_args + cli_impl_args, stun=stun, turn=turn ) |
| 729 | |
| 730 | # Wait for clients to shutdown. Nuke them if necessary |
| 731 | t = timeout_sec if timeout_sec is not None else 20 * g_repeat |
| 732 | server.join( timeout=t ) |
| 733 | client.join( timeout=t ) |
| 734 | |
| 735 | # Verify route types if an expected value was provided |
| 736 | if expected_route is not None: |
| 737 | for peer, thread in [ ( 'server', server ), ( 'client', client ) ]: |
| 738 | if thread.route_type is None: |
| 739 | print( "ERROR: %s did not report a route type" % peer ) |
| 740 | g_failed = True |
| 741 | elif thread.route_type != expected_route: |
| 742 | print( "ERROR: %s route type '%s', expected '%s'" % ( peer, thread.route_type, expected_route ) ) |
| 743 | g_failed = True |
| 744 | |
| 745 | # Verify packet counters if constraints were provided. |
| 746 | # Skip for WebRTC (impl=0) peers -- the WebRTC ICE stack doesn't emit TEST_ICE_ctr_* lines. |
| 747 | if expected_counters is not None: |
| 748 | for peer, thread, impl in [ ( 'server', server, _srv_impl ), ( 'client', client, _cli_impl ) ]: |
| 749 | if impl == 0: |
| 750 | continue |
| 751 | for name, (lo, hi) in expected_counters.items(): |
| 752 | val = thread.counters.get( name, 0 ) |
| 753 | if lo is not None and val < lo: |
| 754 | print( "ERROR: %s TEST_ICE_ctr_%s=%d, expected >= %d" % ( peer, name, val, lo ) ) |
| 755 | g_failed = True |
| 756 | if hi is not None and val > hi: |
| 757 | print( "ERROR: %s TEST_ICE_ctr_%s=%d, expected <= %d" % ( peer, name, val, hi ) ) |
| 758 | g_failed = True |
| 759 | |
| 760 | # Candidate checks only make sense for a single connection (log files accumulate across repeats). |
| 761 | if g_repeat == 1: |
| 762 | srv_local, srv_remote = _parse_candidate_log( "peer_server.verbose.log" ) |
| 763 | cli_local, cli_remote = _parse_candidate_log( "peer_client.verbose.log" ) |
| 764 | |
| 765 | # Cross-check: received candidates must be a subset of what the other side gathered. |
| 766 | # Strict equality would fail on fast connections (e.g. same-LAN) where the |
| 767 | # connection closes before late-arriving relay candidates finish signaling. |
| 768 | def _check_subset( received, gathered, receiver, gatherer ): |
| 769 | for ctype, count in received.items(): |
| 770 | if count > gathered.get( ctype, 0 ): |
| 771 | print( "ERROR: %s received %d %s candidate(s) from %s but %s only gathered %d" % ( |
| 772 | receiver, count, ctype, gatherer, gatherer, gathered.get( ctype, 0 ) ) ) |
| 773 | g_failed = True |
| 774 | _check_subset( cli_remote, srv_local, 'client', 'server' ) |
| 775 | _check_subset( srv_remote, cli_local, 'server', 'client' ) |
| 776 | |
| 777 | # Check against expected topology if provided. |
| 778 | if expected_candidates is not None: |
no test coverage detected