blob: 274f7f555ca9a5d06bc81bf304edc816f04f1354 [file] [log] [blame]
# Copyright 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
import os
import shutil
import SimpleHTTPServer
import sys
import threading
from autotest_lib.client.bin import test
from autotest_lib.client.common_lib import autotemp, error, file_utils, utils
from autotest_lib.client.cros import httpd, service_stopper
SERVER_PORT=51793
SERVER_ADDRESS = "http://localhost:%s/uma/v2" % SERVER_PORT
class FakeHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
"""
Fake Uma handler.
Answer OK on well formed request and add the data to the server's list of
messages.
"""
def do_POST(self):
"""
Handle post request to the fake UMA backend.
Answer 'OK' with a 200 HTTP status code on POST requests to /uma/v2
and an empty message with error code 404 otherwise.
"""
if self.path != '/uma/v2':
self.send_response(404)
self.end_headers()
return
message = self.rfile.read(int(self.headers['Content-Length']))
self.server.messages.append(message)
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write('OK')
class FakeServer(httpd.ThreadedHTTPServer):
"""
Wrapper around ThreadedHTTPServer.
Provides helpers to start/stop the instance and hold the list of
received messages.
"""
def __init__(self):
httpd.ThreadedHTTPServer.__init__(self, ('', SERVER_PORT), FakeHandler)
self.messages = []
def Start(self):
"""
Start the server on a new thread.
"""
self.server_thread = threading.Thread(target=self.serve_forever)
self.server_thread.start()
def Stop(self):
"""
Stop the server thread.
"""
self.shutdown()
self.socket.close()
self.server_thread.join()
class platform_MetricsUploader(test.test):
"""
End-to-End test of the metrics uploader
Test that metrics_daemon is sending the metrics to the Uma server when
started with the -uploader flag and that the messages are well formatted.
"""
version = 1
_CONSENT_FILE = '/home/chronos/Consent To Send Stats'
def setup(self):
os.chdir(self.srcdir)
utils.make('OUT_DIR=.')
def initialize(self):
self._services = service_stopper.ServiceStopper(['metrics_daemon'])
self._services.stop_services()
self._tempdir = autotemp.tempdir()
def _create_one_sample(self):
utils.system_output('truncate --size=0 /run/metrics/uma-events')
utils.system_output('metrics_client test 10 0 100 10')
def _test_simple_upload(self):
self._create_one_sample()
self.server = FakeServer()
self.server.Start()
utils.system_output('metrics_daemon -uploader_test '
'-server="%s"' % SERVER_ADDRESS,
timeout=10, retain_output=True)
self.server.Stop()
if len(self.server.messages) != 1:
raise error.TestFail('no messages received by the server')
def _test_server_unavailable(self):
"""
metrics_daemon should not crash when the server is unavailable.
"""
self._create_one_sample()
utils.system_output('metrics_daemon -uploader_test '
'-server="http://localhost:12345"',
retain_output=True)
def _test_check_product_id(self):
"""
metrics_daemon should set the product id when it is specified.
The product id can be set through the GOOGLE_METRICS_PRODUCT_ID file in
/etc/os-release.d/.
"""
# The product id must be an integer, declared in the upstream UMA
# backend protobuf.
EXPECTED_PRODUCT_ID = 3
sys.path.append(self.srcdir)
from chrome_user_metrics_extension_pb2 import ChromeUserMetricsExtension
self._create_one_sample()
self.server = FakeServer()
self.server.Start()
osreleased_path = os.path.join(self._tempdir.name, 'etc',
'os-release.d')
file_utils.make_leaf_dir(osreleased_path)
utils.write_one_line(os.path.join(osreleased_path,
'GOOGLE_METRICS_PRODUCT_ID'),
str(EXPECTED_PRODUCT_ID))
utils.system_output('metrics_daemon -uploader_test '
'-server="%s" '
'-config_root="%s"' % (SERVER_ADDRESS,
self._tempdir.name),
retain_output=True)
self.server.Stop()
if len(self.server.messages) != 1:
raise error.TestFail('should have received 1 message. Received: '
+ str(len(self.server.messages)))
proto = ChromeUserMetricsExtension.FromString(self.server.messages[0])
logging.debug('Proto received is: ' + str(proto))
if proto.product != EXPECTED_PRODUCT_ID:
raise error.TestFail('Product id should be set to 3. Was: '
+ str(proto.product))
def _test_metrics_disabled(self):
"""
When metrics are disabled, nothing should get uploaded.
"""
self._create_one_sample()
self.server = FakeServer()
self.server.Start()
utils.system_output('metrics_daemon -uploader_test '
'-server="%s"' % SERVER_ADDRESS,
timeout=10, retain_output=True)
self.server.Stop()
if len(self.server.messages) != 0:
raise error.TestFail('message received by the server')
def _get_saved_consent_file_path(self):
return os.path.join(self.bindir, 'saved_consent')
def run_once(self):
"""
Run the tests.
"""
if os.path.exists(self._CONSENT_FILE):
shutil.move(self._CONSENT_FILE, self._get_saved_consent_file_path())
# enable metrics reporting
utils.open_write_close(self._CONSENT_FILE, 'foo')
logging.info(('=' * 4) + 'Check that metrics samples can be uploaded '
'with the default configuration')
self._test_simple_upload()
logging.info(('=' * 4) + 'Check that the metrics uploader does not '
'crash when the backend server is unreachable')
self._test_server_unavailable()
logging.info(('=' * 4) + 'Check that the product id can be set '
'through the GOOGLE_METRICS_PRODUCT_ID field in '
'/etc/os-release.d/')
self._test_check_product_id()
os.remove(self._CONSENT_FILE)
logging.info(('=' * 4) + 'Check that metrics are not uploaded when '
'metrics are disabled.')
self._test_metrics_disabled()
def cleanup(self):
self._services.restore_services()
self._tempdir.clean()
# The consent file might or might not exist depending on whether a test
# failed or not. Handle both cases.
if os.path.exists(self._CONSENT_FILE):
os.remove(self._CONSENT_FILE)
if os.path.exists(self._get_saved_consent_file_path()):
shutil.move(self._get_saved_consent_file_path(), self._CONSENT_FILE)