Home | History | Annotate | Download | only in platform_MetricsUploader
      1 # Copyright 2014 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 
      5 import logging
      6 import os
      7 import shutil
      8 import SimpleHTTPServer
      9 import sys
     10 import threading
     11 
     12 from autotest_lib.client.bin import test
     13 from autotest_lib.client.common_lib import autotemp, error, file_utils, utils
     14 from autotest_lib.client.cros import httpd, service_stopper
     15 
     16 
     17 SERVER_PORT=51793
     18 SERVER_ADDRESS = "http://localhost:%s/uma/v2" % SERVER_PORT
     19 
     20 class FakeHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
     21     """
     22     Fake Uma handler.
     23 
     24     Answer OK on well formed request and add the data to the server's list of
     25     messages.
     26     """
     27 
     28     def do_POST(self):
     29         """
     30         Handle post request to the fake UMA backend.
     31 
     32         Answer 'OK' with a 200 HTTP status code on POST requests to /uma/v2
     33         and an empty message with error code 404 otherwise.
     34         """
     35         if self.path != '/uma/v2':
     36             self.send_response(404)
     37             self.end_headers()
     38             return
     39 
     40         message = self.rfile.read(int(self.headers['Content-Length']))
     41         self.server.messages.append(message)
     42 
     43         self.send_response(200)
     44         self.send_header('Content-type', 'text/html')
     45         self.end_headers()
     46         self.wfile.write('OK')
     47 
     48 
     49 class FakeServer(httpd.ThreadedHTTPServer):
     50     """
     51     Wrapper around ThreadedHTTPServer.
     52 
     53     Provides helpers to start/stop the instance and hold the list of
     54     received messages.
     55     """
     56 
     57     def __init__(self):
     58         httpd.ThreadedHTTPServer.__init__(self, ('', SERVER_PORT), FakeHandler)
     59         self.messages = []
     60 
     61 
     62     def Start(self):
     63         """
     64         Start the server on a new thread.
     65         """
     66         self.server_thread = threading.Thread(target=self.serve_forever)
     67         self.server_thread.start()
     68 
     69 
     70     def Stop(self):
     71         """
     72         Stop the server thread.
     73         """
     74         self.shutdown()
     75         self.socket.close()
     76         self.server_thread.join()
     77 
     78 
     79 class platform_MetricsUploader(test.test):
     80     """
     81     End-to-End test of the metrics uploader
     82 
     83     Test that metrics_daemon is sending the metrics to the Uma server when
     84     started with the -uploader flag and that the messages are well formatted.
     85     """
     86 
     87     version = 1
     88     _CONSENT_FILE = '/home/chronos/Consent To Send Stats'
     89 
     90     def setup(self):
     91         os.chdir(self.srcdir)
     92         utils.make('OUT_DIR=.')
     93 
     94 
     95     def initialize(self):
     96         self._services = service_stopper.ServiceStopper(['metrics_daemon'])
     97         self._services.stop_services()
     98         self._tempdir = autotemp.tempdir()
     99 
    100 
    101     def _create_one_sample(self):
    102         utils.system_output('truncate --size=0 /var/run/metrics/uma-events')
    103         utils.system_output('metrics_client test 10 0 100 10')
    104 
    105 
    106     def _test_simple_upload(self):
    107         self._create_one_sample()
    108 
    109         self.server = FakeServer()
    110         self.server.Start()
    111 
    112         utils.system_output('metrics_daemon -uploader_test '
    113                             '-server="%s"' % SERVER_ADDRESS,
    114                             timeout=10, retain_output=True)
    115 
    116         self.server.Stop()
    117 
    118         if len(self.server.messages) != 1:
    119             raise error.TestFail('no messages received by the server')
    120 
    121 
    122     def _test_server_unavailable(self):
    123         """
    124         metrics_daemon should not crash when the server is unavailable.
    125         """
    126         self._create_one_sample()
    127         utils.system_output('metrics_daemon -uploader_test '
    128                             '-server="http://localhost:12345"',
    129                             retain_output=True)
    130 
    131 
    132     def _test_check_product_id(self):
    133         """
    134         metrics_daemon should set the product id when it is specified.
    135 
    136         The product id can be set through the GOOGLE_METRICS_PRODUCT_ID file in
    137         /etc/os-release.d/.
    138         """
    139 
    140         # The product id must be an integer, declared in the upstream UMA
    141         # backend protobuf.
    142         EXPECTED_PRODUCT_ID = 3
    143 
    144         sys.path.append(self.srcdir)
    145         from chrome_user_metrics_extension_pb2 import ChromeUserMetricsExtension
    146 
    147         self._create_one_sample()
    148 
    149         self.server = FakeServer()
    150         self.server.Start()
    151         osreleased_path = os.path.join(self._tempdir.name, 'etc',
    152                                        'os-release.d')
    153         file_utils.make_leaf_dir(osreleased_path)
    154         utils.write_one_line(os.path.join(osreleased_path,
    155                                           'GOOGLE_METRICS_PRODUCT_ID'),
    156                              str(EXPECTED_PRODUCT_ID))
    157 
    158         utils.system_output('metrics_daemon -uploader_test '
    159                             '-server="%s" '
    160                             '-config_root="%s"' % (SERVER_ADDRESS,
    161                                                    self._tempdir.name),
    162                             retain_output=True)
    163 
    164         self.server.Stop()
    165 
    166         if len(self.server.messages) != 1:
    167             raise error.TestFail('should have received 1 message. Received: '
    168                                + str(len(self.server.messages)))
    169 
    170         proto = ChromeUserMetricsExtension.FromString(self.server.messages[0])
    171         logging.debug('Proto received is: ' + str(proto))
    172         if proto.product != EXPECTED_PRODUCT_ID:
    173             raise error.TestFail('Product id should be set to 3. Was: '
    174                                  + str(proto.product))
    175 
    176 
    177     def _test_metrics_disabled(self):
    178         """
    179         When metrics are disabled, nothing should get uploaded.
    180         """
    181         self._create_one_sample()
    182 
    183         self.server = FakeServer()
    184         self.server.Start()
    185 
    186         utils.system_output('metrics_daemon -uploader_test '
    187                             '-server="%s"' % SERVER_ADDRESS,
    188                             timeout=10, retain_output=True)
    189 
    190         self.server.Stop()
    191 
    192         if len(self.server.messages) != 0:
    193             raise error.TestFail('message received by the server')
    194 
    195 
    196     def _get_saved_consent_file_path(self):
    197         return os.path.join(self.bindir, 'saved_consent')
    198 
    199 
    200     def run_once(self):
    201         """
    202         Run the tests.
    203         """
    204         if os.path.exists(self._CONSENT_FILE):
    205           shutil.move(self._CONSENT_FILE, self._get_saved_consent_file_path())
    206         # enable metrics reporting
    207         utils.open_write_close(self._CONSENT_FILE, 'foo')
    208 
    209         logging.info(('=' * 4) + 'Check that metrics samples can be uploaded '
    210                      'with the default configuration')
    211         self._test_simple_upload()
    212 
    213         logging.info(('=' * 4) + 'Check that the metrics uploader does not '
    214                      'crash when the backend server is unreachable')
    215         self._test_server_unavailable()
    216 
    217         logging.info(('=' * 4) + 'Check that the product id can be set '
    218                      'through the GOOGLE_METRICS_PRODUCT_ID field in '
    219                      '/etc/os-release.d/')
    220         self._test_check_product_id()
    221 
    222         os.remove(self._CONSENT_FILE)
    223         logging.info(('=' * 4) + 'Check that metrics are not uploaded when '
    224                      'metrics are disabled.')
    225         self._test_metrics_disabled()
    226 
    227 
    228     def cleanup(self):
    229         self._services.restore_services()
    230         self._tempdir.clean()
    231 
    232         # The consent file might or might not exist depending on whether a test
    233         # failed or not. Handle both cases.
    234         if os.path.exists(self._CONSENT_FILE):
    235           os.remove(self._CONSENT_FILE)
    236 
    237         if os.path.exists(self._get_saved_consent_file_path()):
    238           shutil.move(self._get_saved_consent_file_path(), self._CONSENT_FILE)
    239