普通文本  |  166行  |  6.43 KB

# Copyright (c) 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.

"""Mock out test results for puppylab.
"""


import logging
import os
import time

import common
from autotest_lib.client.common_lib import time_utils

# TODO(beeps): The right way to create these status logs is by creating a job
# object and invoking job.record on it. However we perform this template
# hack instead for the following reasons:
#   * The templates are a lot easier to understand at first glance, which
#     is really what one wants from a testing interface built for a
#     framework like autotest.
#   * Creating the job is wedged into core autotest code, so it has
#     unintended consequences like checking for hosts/labels etc.
#   * We are guaranteed to create the bare minimum by hand specifying the file
#     to write, and their contents. Job.record ends up checking and creating
#     several non-essential directoris in the process or recording status.

_SUCCESS_TEST_TEMPLATE = (
        "\tSTART\t%(test_name)s\t%(test_name)s"
        "\ttimestamp=%(timestamp)s\tlocaltime=%(date)s\n"
        "\t\tGOOD\t%(test_name)s\t%(test_name)s\ttimestamp=%(timestamp)s\t"
        "localtime=%(date)s\tcompleted successfully\n"
        "\tEND GOOD\t%(test_name)s\t%(test_name)s\ttimestamp=%(timestamp)s\t"
        "localtime=%(date)s")


_SUCCESS_JOB_TEMPLATE = (
        "START\t----\t----\ttimestamp=%(timestamp)s\tlocaltime=%(date)s"
        "\n\tSTART\t%(test_name)s\t%(test_name)s\ttimestamp=%(timestamp)s\t"
        "localtime=%(date)s\n\t\tGOOD\t%(test_name)s\t%(test_name)s"
        "\ttimestamp=%(timestamp)s\tlocaltime=%(date)s\tcompleted "
        "successfully\n\tEND GOOD\t%(test_name)s\t%(test_name)s"
        "\ttimestamp=%(timestamp)s\tlocaltime=%(date)s\n"
        "END GOOD\t----\t----\ttimestamp=%(timestamp)s\tlocaltime=%(date)s")


_JOB_KEYVALS_TEMPLATE = "hostname=%(hostname)s\nstatus_version=1\n"


class ResultsMocker(object):
    """Class to mock out the results of a test."""

    def _make_dirs(self):
        """Create essential directories needed for faking test results.

        @raises ValueError: If the directories crucial to reporting
            test status already exist.
        @raises OSError: If we cannot make one of the directories for
            an os related reason (eg: permissions).
        @raises AssertionError: If one of the directories silently failed
            creation.
        """
        logging.info("creating dir %s, %s, %s",
                self.results_dir, self.test_results, self.host_keyval_dir)
        if not os.path.exists(self.results_dir):
            os.makedirs(self.results_dir)
        if not os.path.exists(self.test_results):
            os.makedirs(self.test_results)
        if not os.path.exists(self.host_keyval_dir):
            os.makedirs(self.host_keyval_dir)
        assert(os.path.exists(self.test_results) and
               os.path.exists(self.results_dir) and
               os.path.exists(self.host_keyval_dir))


    def __init__(self, test_name, results_dir, machine_name):
        """Initialize a results mocker.

        @param test_name: The name of the test, eg: dummy_Pass.
        @param results_dir: The results directory this test will use.
        @param machine_name: A string representing the hostname the test will
            run on.
        """
        self.results_dir = results_dir
        self.test_results = os.path.join(results_dir, test_name)
        self.host_keyval_dir = os.path.join(self.results_dir, 'host_keyvals')
        self.machine_name = machine_name
        self.test_name = test_name

        self._make_dirs()

        # Status logs are used by the parser to declare a test as pass/fail.
        self.job_status = os.path.join(self.results_dir, 'status')
        self.job_status_log = os.path.join(self.results_dir, 'status.log')
        self.test_status = os.path.join(self.test_results, 'status')

        # keyvals are used by the parser to figure out fine grained information
        # about a test. Only job_keyvals are crucial to parsing.
        self.test_keyvals = os.path.join(self.test_results, 'keyval')
        self.job_keyvals = os.path.join(self.results_dir, 'keyval')
        self.host_keyvals = os.path.join(self.results_dir, machine_name)


    def _write(self, results_path, results):
        """Write the content in results to the file in results_path.

        @param results_path: The path to the results file.
        @param results: The content to write to the file.
        """
        logging.info('Writing results to %s', results_path)
        with open(results_path, 'w') as results_file:
            results_file.write(results)


    def generate_keyvals(self):
        """Apply templates to keyval files.

        There are 3 important keyvals files, only one of which is actually
        crucial to results parsing:
            host_keyvals - information about the DUT
            job_keyvals - information about the server_job
            test_keyvals - information about the test

        Parsing cannot complete without the job_keyvals. Everything else is
        optional. Keyvals are parsed into tko tables.
        """
        #TODO(beeps): Include other keyvals.
        self._write(
                self.job_keyvals,
                _JOB_KEYVALS_TEMPLATE %
                        {'hostname': self.machine_name})


    def generate_status(self):
        """Generate status logs.

        3 important status logs are required for successful parsing:
            test_name/status - core test status
            results_dir/status - server job status (has test status in it)
            status.log - compiled final status log
        """
        current_timestamp = int(time.time())
        test_info = {
            'test_name': self.test_name,
            'timestamp': current_timestamp,
            'date': time_utils.epoch_time_to_date_string(
                            current_timestamp, fmt_string='%b %d %H:%M:%S'),
        }
        self._write(
                self.job_status,
                _SUCCESS_JOB_TEMPLATE % test_info)
        self._write(
                self.job_status_log,
                _SUCCESS_JOB_TEMPLATE % test_info)
        self._write(
                self.test_status,
                _SUCCESS_TEST_TEMPLATE % test_info)


    def mock_results(self):
        """Create mock results in the directories used to init the instance."""
        self.generate_status()
        self.generate_keyvals()