1# Copyright (c) 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"""Mock out test results for puppylab. 6""" 7 8 9import logging 10import os 11import time 12 13import common 14from autotest_lib.client.common_lib import time_utils 15 16# TODO(beeps): The right way to create these status logs is by creating a job 17# object and invoking job.record on it. However we perform this template 18# hack instead for the following reasons: 19# * The templates are a lot easier to understand at first glance, which 20# is really what one wants from a testing interface built for a 21# framework like autotest. 22# * Creating the job is wedged into core autotest code, so it has 23# unintended consequences like checking for hosts/labels etc. 24# * We are guaranteed to create the bare minimum by hand specifying the file 25# to write, and their contents. Job.record ends up checking and creating 26# several non-essential directoris in the process or recording status. 27 28_SUCCESS_TEST_TEMPLATE = ( 29 "\tSTART\t%(test_name)s\t%(test_name)s" 30 "\ttimestamp=%(timestamp)s\tlocaltime=%(date)s\n" 31 "\t\tGOOD\t%(test_name)s\t%(test_name)s\ttimestamp=%(timestamp)s\t" 32 "localtime=%(date)s\tcompleted successfully\n" 33 "\tEND GOOD\t%(test_name)s\t%(test_name)s\ttimestamp=%(timestamp)s\t" 34 "localtime=%(date)s") 35 36 37_SUCCESS_JOB_TEMPLATE = ( 38 "START\t----\t----\ttimestamp=%(timestamp)s\tlocaltime=%(date)s" 39 "\n\tSTART\t%(test_name)s\t%(test_name)s\ttimestamp=%(timestamp)s\t" 40 "localtime=%(date)s\n\t\tGOOD\t%(test_name)s\t%(test_name)s" 41 "\ttimestamp=%(timestamp)s\tlocaltime=%(date)s\tcompleted " 42 "successfully\n\tEND GOOD\t%(test_name)s\t%(test_name)s" 43 "\ttimestamp=%(timestamp)s\tlocaltime=%(date)s\n" 44 "END GOOD\t----\t----\ttimestamp=%(timestamp)s\tlocaltime=%(date)s") 45 46 47_JOB_KEYVALS_TEMPLATE = "hostname=%(hostname)s\nstatus_version=1\n" 48 49 50class ResultsMocker(object): 51 """Class to mock out the results of a test.""" 52 53 def _make_dirs(self): 54 """Create essential directories needed for faking test results. 55 56 @raises ValueError: If the directories crucial to reporting 57 test status already exist. 58 @raises OSError: If we cannot make one of the directories for 59 an os related reason (eg: permissions). 60 @raises AssertionError: If one of the directories silently failed 61 creation. 62 """ 63 logging.info("creating dir %s, %s, %s", 64 self.results_dir, self.test_results, self.host_keyval_dir) 65 if not os.path.exists(self.results_dir): 66 os.makedirs(self.results_dir) 67 if not os.path.exists(self.test_results): 68 os.makedirs(self.test_results) 69 if not os.path.exists(self.host_keyval_dir): 70 os.makedirs(self.host_keyval_dir) 71 assert(os.path.exists(self.test_results) and 72 os.path.exists(self.results_dir) and 73 os.path.exists(self.host_keyval_dir)) 74 75 76 def __init__(self, test_name, results_dir, machine_name): 77 """Initialize a results mocker. 78 79 @param test_name: The name of the test, eg: dummy_Pass. 80 @param results_dir: The results directory this test will use. 81 @param machine_name: A string representing the hostname the test will 82 run on. 83 """ 84 self.results_dir = results_dir 85 self.test_results = os.path.join(results_dir, test_name) 86 self.host_keyval_dir = os.path.join(self.results_dir, 'host_keyvals') 87 self.machine_name = machine_name 88 self.test_name = test_name 89 90 self._make_dirs() 91 92 # Status logs are used by the parser to declare a test as pass/fail. 93 self.job_status = os.path.join(self.results_dir, 'status') 94 self.job_status_log = os.path.join(self.results_dir, 'status.log') 95 self.test_status = os.path.join(self.test_results, 'status') 96 97 # keyvals are used by the parser to figure out fine grained information 98 # about a test. Only job_keyvals are crucial to parsing. 99 self.test_keyvals = os.path.join(self.test_results, 'keyval') 100 self.job_keyvals = os.path.join(self.results_dir, 'keyval') 101 self.host_keyvals = os.path.join(self.results_dir, machine_name) 102 103 104 def _write(self, results_path, results): 105 """Write the content in results to the file in results_path. 106 107 @param results_path: The path to the results file. 108 @param results: The content to write to the file. 109 """ 110 logging.info('Writing results to %s', results_path) 111 with open(results_path, 'w') as results_file: 112 results_file.write(results) 113 114 115 def generate_keyvals(self): 116 """Apply templates to keyval files. 117 118 There are 3 important keyvals files, only one of which is actually 119 crucial to results parsing: 120 host_keyvals - information about the DUT 121 job_keyvals - information about the server_job 122 test_keyvals - information about the test 123 124 Parsing cannot complete without the job_keyvals. Everything else is 125 optional. Keyvals are parsed into tko tables. 126 """ 127 #TODO(beeps): Include other keyvals. 128 self._write( 129 self.job_keyvals, 130 _JOB_KEYVALS_TEMPLATE % 131 {'hostname': self.machine_name}) 132 133 134 def generate_status(self): 135 """Generate status logs. 136 137 3 important status logs are required for successful parsing: 138 test_name/status - core test status 139 results_dir/status - server job status (has test status in it) 140 status.log - compiled final status log 141 """ 142 current_timestamp = int(time.time()) 143 test_info = { 144 'test_name': self.test_name, 145 'timestamp': current_timestamp, 146 'date': time_utils.epoch_time_to_date_string( 147 current_timestamp, fmt_string='%b %d %H:%M:%S'), 148 } 149 self._write( 150 self.job_status, 151 _SUCCESS_JOB_TEMPLATE % test_info) 152 self._write( 153 self.job_status_log, 154 _SUCCESS_JOB_TEMPLATE % test_info) 155 self._write( 156 self.test_status, 157 _SUCCESS_TEST_TEMPLATE % test_info) 158 159 160 def mock_results(self): 161 """Create mock results in the directories used to init the instance.""" 162 self.generate_status() 163 self.generate_keyvals() 164 165 166