1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6import os
7import sys
8
9from autotest_lib.client.bin import test
10from autotest_lib.client.common_lib import error
11from autotest_lib.client.cros import p2p_utils
12from autotest_lib.client.cros.netprotos import cros_p2p, zeroconf
13
14
15class p2p_ShareFiles(test.test):
16    """The P2P Server class tester.
17
18    This class runs the p2p service (p2p-server and p2p-http-server) and checks
19    that the DUT is sharing the files on the network.
20    """
21    version = 1
22
23    def setup(self):
24        self.job.setup_dep(['lansim'])
25
26
27    def initialize(self):
28        dep = 'lansim'
29        dep_dir = os.path.join(self.autodir, 'deps', dep)
30        logging.info('lansim is at %s', dep_dir)
31        self.job.install_pkg(dep, 'dep', dep_dir)
32
33        # Import the lansim modules installed on lansim/build/
34        sys.path.append(os.path.join(dep_dir, 'build'))
35
36        self._p2p = p2p_utils.P2PServerOverTap()
37
38
39    def cleanup(self):
40        self._p2p.cleanup()
41
42
43    def _run_lansim_loop(self, timeout=None, until=None):
44        """Run the Simulator main loop for a given time."""
45        try:
46            self._sim.run(timeout=timeout, until=until)
47        except Exception, e:
48            logging.exception('Simulator ended with an exception:')
49            raise error.TestError('Simulator ended with an exception: %r' % e)
50
51
52    def run_once(self):
53        from lansim import simulator, host
54
55        # Setup the environment where avahi-daemon runs during the test.
56        try:
57            self._p2p.setup()
58        except:
59            logging.exception('Failed to start tested services.')
60            raise
61
62        self._sim = simulator.Simulator(self._p2p.tap)
63        # Create a single fake peer that will be sending the multicast requests.
64        peer = host.SimpleHost(self._sim, '94:EB:2C:00:00:61', '169.254.10.97')
65
66        # Run a userspace implementation of avahi + p2p-client on the fake
67        # host. This will use the P2P services exported by the DUT.
68        zero = zeroconf.ZeroconfDaemon(peer, 'a-peer')
69        p2pcli = cros_p2p.CrosP2PClient(zero)
70
71        # On p2p-server startup, it should announce the service even if we
72        # aren't sharing any file. Usually it doesn't take more than 2 seconds
73        # to start announcing the service, repeated a few times.
74        self._run_lansim_loop(timeout=20, until=p2pcli.get_peers())
75        # Check that we see the DUT on the list of peers.
76        peers = p2pcli.get_peers()
77        if len(peers) != 1:
78            logging.info('Found peers: %r', peers)
79            raise error.TestFail('Expected one peer (the DUT) but %d found.' %
80                                 len(peers))
81
82        # Check that the announced information is correct.
83        peer_name, _hostname, ips, port = peers[0]
84        if len(ips) != 1 or ips[0] != self._p2p.tap.addr:
85            logging.info('Peer ips: %r', ips)
86            raise error.TestFail('Found wrong peer IP address on the DUT.')
87        if port != cros_p2p.CROS_P2P_PORT:
88            logging.info('Peer p2p port is: %r', port)
89            raise error.TestFail('Found wrong p2p port exported on the DUT.')
90
91        files = p2pcli.get_peer_files(peer_name)
92        if files:
93            logging.info('Peer files: %r', files)
94            raise error.TestFail('Found exported files on the DUT.')
95
96        num_connections = p2pcli.get_peer_connections(peer_name)
97        if num_connections:
98            logging.info('Peer connections: %r', num_connections)
99            raise error.TestFail('DUT already has p2p connections.')
100
101        # Share a small file and check that it is broadcasted.
102        with open(os.path.join(p2p_utils.P2P_SHARE_PATH, 'my_file=HASH==.p2p'),
103                  'w') as f:
104            f.write('0123456789')
105
106        # Run the loop until the file is shared. Normally, the p2p-server takes
107        # up to 1 second to detect a change on the shared directory and
108        # announces it right away a few times. Wait until the file is announced,
109        # what should not take more than a few seconds. If after 30 seconds the
110        # files isn't announced, that is an error.
111        self._run_lansim_loop(timeout=30,
112                              until=lambda: p2pcli.get_peer_files(peer_name))
113
114        files = p2pcli.get_peer_files(peer_name)
115        if files != [('my_file=HASH==', 10)]:
116            logging.info('Peer files: %r', files)
117            raise error.TestFail('Expected exported file on the DUT.')
118
119        # Test that the DUT replies to active requests.
120        zero.clear_cache()
121        p2pcli.start_query()
122        # A query can be replied by several peers after it is send, but there's
123        # no one-to-one mapping between these two. A query simply forces other
124        # peers to send the requested information shortly after. Thus, here we
125        # just wait a few seconds until we decide that the query timeouted.
126        self._run_lansim_loop(timeout=3)
127        p2pcli.stop_query()
128
129        files = p2pcli.get_peer_files(peer_name)
130        if files != [('my_file=HASH==', 10)]:
131            logging.info('Peer files: %r', files)
132            raise error.TestFail('Expected exported file on the DUT.')
133