results_cache.py revision 9099a788cd7124024559c064e425ed9caef6e0ac
1 2# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5"""Module to deal with result cache.""" 6 7from __future__ import print_function 8 9import glob 10import hashlib 11import os 12import pickle 13import re 14import tempfile 15import json 16import sys 17 18from cros_utils import command_executer 19from cros_utils import misc 20 21from image_checksummer import ImageChecksummer 22 23import results_report 24import test_flag 25 26SCRATCH_DIR = os.path.expanduser('~/cros_scratch') 27RESULTS_FILE = 'results.txt' 28MACHINE_FILE = 'machine.txt' 29AUTOTEST_TARBALL = 'autotest.tbz2' 30PERF_RESULTS_FILE = 'perf-results.txt' 31CACHE_KEYS_FILE = 'cache_keys.txt' 32 33 34class Result(object): 35 """Class for holding the results of a single test run. 36 37 This class manages what exactly is stored inside the cache without knowing 38 what the key of the cache is. For runs with perf, it stores perf.data, 39 perf.report, etc. The key generation is handled by the ResultsCache class. 40 """ 41 42 def __init__(self, logger, label, log_level, machine, cmd_exec=None): 43 self.chromeos_root = label.chromeos_root 44 self._logger = logger 45 self.ce = cmd_exec or command_executer.GetCommandExecuter( 46 self._logger, 47 log_level=log_level) 48 self.temp_dir = None 49 self.label = label 50 self.results_dir = None 51 self.log_level = log_level 52 self.machine = machine 53 self.perf_data_files = [] 54 self.perf_report_files = [] 55 self.results_file = [] 56 self.chrome_version = '' 57 self.err = None 58 self.chroot_results_dir = '' 59 self.test_name = '' 60 self.keyvals = None 61 self.board = None 62 self.suite = None 63 self.retval = None 64 self.out = None 65 66 def CopyFilesTo(self, dest_dir, files_to_copy): 67 file_index = 0 68 for file_to_copy in files_to_copy: 69 if not os.path.isdir(dest_dir): 70 command = 'mkdir -p %s' % dest_dir 71 self.ce.RunCommand(command) 72 dest_file = os.path.join(dest_dir, 73 ('%s.%s' % (os.path.basename(file_to_copy), 74 file_index))) 75 ret = self.ce.CopyFiles(file_to_copy, dest_file, recursive=False) 76 if ret: 77 raise IOError('Could not copy results file: %s' % file_to_copy) 78 79 def CopyResultsTo(self, dest_dir): 80 self.CopyFilesTo(dest_dir, self.perf_data_files) 81 self.CopyFilesTo(dest_dir, self.perf_report_files) 82 if len(self.perf_data_files) or len(self.perf_report_files): 83 self._logger.LogOutput('Perf results files stored in %s.' % dest_dir) 84 85 def GetNewKeyvals(self, keyvals_dict): 86 # Initialize 'units' dictionary. 87 units_dict = {} 88 for k in keyvals_dict: 89 units_dict[k] = '' 90 results_files = self.GetDataMeasurementsFiles() 91 for f in results_files: 92 # Make sure we can find the results file 93 if os.path.exists(f): 94 data_filename = f 95 else: 96 # Otherwise get the base filename and create the correct 97 # path for it. 98 _, f_base = misc.GetRoot(f) 99 data_filename = os.path.join(self.chromeos_root, 'chroot/tmp', 100 self.temp_dir, f_base) 101 if data_filename.find('.json') > 0: 102 raw_dict = dict() 103 if os.path.exists(data_filename): 104 with open(data_filename, 'r') as data_file: 105 raw_dict = json.load(data_file) 106 107 for k1 in raw_dict: 108 field_dict = raw_dict[k1] 109 for k2 in field_dict: 110 result_dict = field_dict[k2] 111 key = k1 + '__' + k2 112 if 'value' in result_dict: 113 keyvals_dict[key] = result_dict['value'] 114 elif 'values' in result_dict: 115 keyvals_dict[key] = result_dict['values'] 116 units_dict[key] = result_dict['units'] 117 else: 118 if os.path.exists(data_filename): 119 with open(data_filename, 'r') as data_file: 120 lines = data_file.readlines() 121 for line in lines: 122 tmp_dict = json.loads(line) 123 graph_name = tmp_dict['graph'] 124 graph_str = (graph_name + '__') if graph_name else '' 125 key = graph_str + tmp_dict['description'] 126 keyvals_dict[key] = tmp_dict['value'] 127 units_dict[key] = tmp_dict['units'] 128 129 return keyvals_dict, units_dict 130 131 def AppendTelemetryUnits(self, keyvals_dict, units_dict): 132 """keyvals_dict is the dict of key-value used to generate Crosperf reports. 133 134 units_dict is a dictionary of the units for the return values in 135 keyvals_dict. We need to associate the units with the return values, 136 for Telemetry tests, so that we can include the units in the reports. 137 This function takes each value in keyvals_dict, finds the corresponding 138 unit in the units_dict, and replaces the old value with a list of the 139 old value and the units. This later gets properly parsed in the 140 ResultOrganizer class, for generating the reports. 141 """ 142 143 results_dict = {} 144 for k in keyvals_dict: 145 # We don't want these lines in our reports; they add no useful data. 146 if k == '' or k == 'telemetry_Crosperf': 147 continue 148 val = keyvals_dict[k] 149 units = units_dict[k] 150 new_val = [val, units] 151 results_dict[k] = new_val 152 return results_dict 153 154 def GetKeyvals(self): 155 results_in_chroot = os.path.join(self.chromeos_root, 'chroot', 'tmp') 156 if not self.temp_dir: 157 self.temp_dir = tempfile.mkdtemp(dir=results_in_chroot) 158 command = 'cp -r {0}/* {1}'.format(self.results_dir, self.temp_dir) 159 self.ce.RunCommand(command, print_to_console=False) 160 161 command = ('python generate_test_report --no-color --csv %s' % 162 (os.path.join('/tmp', os.path.basename(self.temp_dir)))) 163 _, out, _ = self.ce.ChrootRunCommandWOutput(self.chromeos_root, 164 command, 165 print_to_console=False) 166 keyvals_dict = {} 167 tmp_dir_in_chroot = misc.GetInsideChrootPath(self.chromeos_root, 168 self.temp_dir) 169 for line in out.splitlines(): 170 tokens = re.split('=|,', line) 171 key = tokens[-2] 172 if key.startswith(tmp_dir_in_chroot): 173 key = key[len(tmp_dir_in_chroot) + 1:] 174 value = tokens[-1] 175 keyvals_dict[key] = value 176 177 # Check to see if there is a perf_measurements file and get the 178 # data from it if so. 179 keyvals_dict, units_dict = self.GetNewKeyvals(keyvals_dict) 180 if self.suite == 'telemetry_Crosperf': 181 # For telemtry_Crosperf results, append the units to the return 182 # results, for use in generating the reports. 183 keyvals_dict = self.AppendTelemetryUnits(keyvals_dict, units_dict) 184 return keyvals_dict 185 186 def GetResultsDir(self): 187 mo = re.search(r'Results placed in (\S+)', self.out) 188 if mo: 189 result = mo.group(1) 190 return result 191 raise RuntimeError('Could not find results directory.') 192 193 def FindFilesInResultsDir(self, find_args): 194 if not self.results_dir: 195 return None 196 197 command = 'find %s %s' % (self.results_dir, find_args) 198 ret, out, _ = self.ce.RunCommandWOutput(command, print_to_console=False) 199 if ret: 200 raise RuntimeError('Could not run find command!') 201 return out 202 203 def GetResultsFile(self): 204 return self.FindFilesInResultsDir('-name results-chart.json').splitlines() 205 206 def GetPerfDataFiles(self): 207 return self.FindFilesInResultsDir('-name perf.data').splitlines() 208 209 def GetPerfReportFiles(self): 210 return self.FindFilesInResultsDir('-name perf.data.report').splitlines() 211 212 def GetDataMeasurementsFiles(self): 213 result = self.FindFilesInResultsDir('-name perf_measurements').splitlines() 214 if not result: 215 result = \ 216 self.FindFilesInResultsDir('-name results-chart.json').splitlines() 217 return result 218 219 def GeneratePerfReportFiles(self): 220 perf_report_files = [] 221 for perf_data_file in self.perf_data_files: 222 # Generate a perf.report and store it side-by-side with the perf.data 223 # file. 224 chroot_perf_data_file = misc.GetInsideChrootPath(self.chromeos_root, 225 perf_data_file) 226 perf_report_file = '%s.report' % perf_data_file 227 if os.path.exists(perf_report_file): 228 raise RuntimeError('Perf report file already exists: %s' % 229 perf_report_file) 230 chroot_perf_report_file = misc.GetInsideChrootPath(self.chromeos_root, 231 perf_report_file) 232 perf_path = os.path.join(self.chromeos_root, 'chroot', 'usr/bin/perf') 233 234 perf_file = '/usr/sbin/perf' 235 if os.path.exists(perf_path): 236 perf_file = '/usr/bin/perf' 237 238 # The following is a hack, to use the perf.static binary that 239 # was given to us by Stephane Eranian, until he can figure out 240 # why "normal" perf cannot properly symbolize ChromeOS perf.data files. 241 # Get the directory containing the 'crosperf' script. 242 dirname, _ = misc.GetRoot(sys.argv[0]) 243 perf_path = os.path.join(dirname, '..', 'perf.static') 244 if os.path.exists(perf_path): 245 # copy the executable into the chroot so that it can be found. 246 src_path = perf_path 247 dst_path = os.path.join(self.chromeos_root, 'chroot', 248 'tmp/perf.static') 249 command = 'cp %s %s' % (src_path, dst_path) 250 self.ce.RunCommand(command) 251 perf_file = '/tmp/perf.static' 252 253 command = ('%s report ' 254 '-n ' 255 '--symfs /build/%s ' 256 '--vmlinux /build/%s/usr/lib/debug/boot/vmlinux ' 257 '--kallsyms /build/%s/boot/System.map-* ' 258 '-i %s --stdio ' 259 '> %s' % (perf_file, self.board, self.board, self.board, 260 chroot_perf_data_file, chroot_perf_report_file)) 261 self.ce.ChrootRunCommand(self.chromeos_root, command) 262 263 # Add a keyval to the dictionary for the events captured. 264 perf_report_files.append(misc.GetOutsideChrootPath( 265 self.chromeos_root, chroot_perf_report_file)) 266 return perf_report_files 267 268 def GatherPerfResults(self): 269 report_id = 0 270 for perf_report_file in self.perf_report_files: 271 with open(perf_report_file, 'r') as f: 272 report_contents = f.read() 273 for group in re.findall(r'Events: (\S+) (\S+)', report_contents): 274 num_events = group[0] 275 event_name = group[1] 276 key = 'perf_%s_%s' % (report_id, event_name) 277 value = str(misc.UnitToNumber(num_events)) 278 self.keyvals[key] = value 279 280 def PopulateFromRun(self, out, err, retval, test, suite): 281 self.board = self.label.board 282 self.out = out 283 self.err = err 284 self.retval = retval 285 self.test_name = test 286 self.suite = suite 287 self.chroot_results_dir = self.GetResultsDir() 288 self.results_dir = misc.GetOutsideChrootPath(self.chromeos_root, 289 self.chroot_results_dir) 290 self.results_file = self.GetResultsFile() 291 self.perf_data_files = self.GetPerfDataFiles() 292 # Include all perf.report data in table. 293 self.perf_report_files = self.GeneratePerfReportFiles() 294 # TODO(asharif): Do something similar with perf stat. 295 296 # Grab keyvals from the directory. 297 self.ProcessResults() 298 299 def ProcessJsonResults(self): 300 # Open and parse the json results file generated by telemetry/test_that. 301 if not self.results_file: 302 raise IOError('No results file found.') 303 filename = self.results_file[0] 304 if not filename.endswith('.json'): 305 raise IOError('Attempt to call json on non-json file: %s' % filename) 306 307 keyvals = dict() 308 if os.path.exists(filename): 309 raw_dict = dict() 310 with open(filename, 'r') as f: 311 raw_dict = json.load(f) 312 for k in raw_dict: 313 field_dict = raw_dict[k] 314 for item in field_dict: 315 keyname = k + "__" + item 316 value_dict = field_dict[item] 317 result = value_dict['value'] 318 units = value_dict['units'] 319 new_value = [ result, units ] 320 keyvals[keyname] = new_value 321 return keyvals 322 323 def ProcessResults(self): 324 # Note that this function doesn't know anything about whether there is a 325 # cache hit or miss. It should process results agnostic of the cache hit 326 # state. 327 if self.results_file and self.results_file[0].find('results_chart.json'): 328 self.keyvals = self.ProcessJsonResults() 329 else: 330 print('\n ** WARNING **: Had to use deprecated output-method to ' 331 'collect results.\n') 332 self.keyvals = self.GetKeyvals() 333 self.keyvals['retval'] = self.retval 334 # Generate report from all perf.data files. 335 # Now parse all perf report files and include them in keyvals. 336 self.GatherPerfResults() 337 338 def GetChromeVersionFromCache(self, cache_dir): 339 # Read chrome_version from keys file, if present. 340 chrome_version = '' 341 keys_file = os.path.join(cache_dir, CACHE_KEYS_FILE) 342 if os.path.exists(keys_file): 343 with open(keys_file, 'r') as f: 344 lines = f.readlines() 345 for l in lines: 346 if l.find('Google Chrome ') == 0: 347 chrome_version = l 348 if chrome_version[-1] == '\n': 349 chrome_version = chrome_version[:-1] 350 break 351 return chrome_version 352 353 def PopulateFromCacheDir(self, cache_dir, test, suite): 354 self.test_name = test 355 self.suite = suite 356 # Read in everything from the cache directory. 357 with open(os.path.join(cache_dir, RESULTS_FILE), 'r') as f: 358 self.out = pickle.load(f) 359 self.err = pickle.load(f) 360 self.retval = pickle.load(f) 361 362 # Untar the tarball to a temporary directory 363 self.temp_dir = tempfile.mkdtemp( 364 dir=os.path.join(self.chromeos_root, 'chroot', 'tmp')) 365 366 command = ('cd %s && tar xf %s' % 367 (self.temp_dir, os.path.join(cache_dir, AUTOTEST_TARBALL))) 368 ret = self.ce.RunCommand(command, print_to_console=False) 369 if ret: 370 raise RuntimeError('Could not untar cached tarball') 371 self.results_dir = self.temp_dir 372 self.perf_data_files = self.GetPerfDataFiles() 373 self.perf_report_files = self.GetPerfReportFiles() 374 self.chrome_version = self.GetChromeVersionFromCache(cache_dir) 375 self.ProcessResults() 376 377 def CleanUp(self, rm_chroot_tmp): 378 if rm_chroot_tmp and self.results_dir: 379 dirname, basename = misc.GetRoot(self.results_dir) 380 if basename.find('test_that_results_') != -1: 381 command = 'rm -rf %s' % self.results_dir 382 else: 383 command = 'rm -rf %s' % dirname 384 self.ce.RunCommand(command) 385 if self.temp_dir: 386 command = 'rm -rf %s' % self.temp_dir 387 self.ce.RunCommand(command) 388 389 def StoreToCacheDir(self, cache_dir, machine_manager, key_list): 390 # Create the dir if it doesn't exist. 391 temp_dir = tempfile.mkdtemp() 392 393 # Store to the temp directory. 394 with open(os.path.join(temp_dir, RESULTS_FILE), 'w') as f: 395 pickle.dump(self.out, f) 396 pickle.dump(self.err, f) 397 pickle.dump(self.retval, f) 398 399 if not test_flag.GetTestMode(): 400 with open(os.path.join(temp_dir, CACHE_KEYS_FILE), 'w') as f: 401 f.write('%s\n' % self.label.name) 402 f.write('%s\n' % self.label.chrome_version) 403 f.write('%s\n' % self.machine.checksum_string) 404 for k in key_list: 405 f.write(k) 406 f.write('\n') 407 408 if self.results_dir: 409 tarball = os.path.join(temp_dir, AUTOTEST_TARBALL) 410 command = ('cd %s && ' 411 'tar ' 412 '--exclude=var/spool ' 413 '--exclude=var/log ' 414 '-cjf %s .' % (self.results_dir, tarball)) 415 ret = self.ce.RunCommand(command) 416 if ret: 417 raise RuntimeError("Couldn't store autotest output directory.") 418 # Store machine info. 419 # TODO(asharif): Make machine_manager a singleton, and don't pass it into 420 # this function. 421 with open(os.path.join(temp_dir, MACHINE_FILE), 'w') as f: 422 f.write(machine_manager.machine_checksum_string[self.label.name]) 423 424 if os.path.exists(cache_dir): 425 command = 'rm -rf {0}'.format(cache_dir) 426 self.ce.RunCommand(command) 427 428 command = 'mkdir -p {0} && '.format(os.path.dirname(cache_dir)) 429 command += 'chmod g+x {0} && '.format(temp_dir) 430 command += 'mv {0} {1}'.format(temp_dir, cache_dir) 431 ret = self.ce.RunCommand(command) 432 if ret: 433 command = 'rm -rf {0}'.format(temp_dir) 434 self.ce.RunCommand(command) 435 raise RuntimeError('Could not move dir %s to dir %s' % 436 (temp_dir, cache_dir)) 437 438 @classmethod 439 def CreateFromRun(cls, 440 logger, 441 log_level, 442 label, 443 machine, 444 out, 445 err, 446 retval, 447 test, 448 suite='telemetry_Crosperf'): 449 if suite == 'telemetry': 450 result = TelemetryResult(logger, label, log_level, machine) 451 else: 452 result = cls(logger, label, log_level, machine) 453 result.PopulateFromRun(out, err, retval, test, suite) 454 return result 455 456 @classmethod 457 def CreateFromCacheHit(cls, 458 logger, 459 log_level, 460 label, 461 machine, 462 cache_dir, 463 test, 464 suite='telemetry_Crosperf'): 465 if suite == 'telemetry': 466 result = TelemetryResult(logger, label, log_level, machine) 467 else: 468 result = cls(logger, label, log_level, machine) 469 try: 470 result.PopulateFromCacheDir(cache_dir, test, suite) 471 472 except RuntimeError as e: 473 logger.LogError('Exception while using cache: %s' % e) 474 return None 475 return result 476 477 478class TelemetryResult(Result): 479 """Class to hold the results of a single Telemetry run.""" 480 481 def __init__(self, logger, label, log_level, machine, cmd_exec=None): 482 super(TelemetryResult, self).__init__(logger, label, log_level, machine, 483 cmd_exec) 484 485 def PopulateFromRun(self, out, err, retval, test, suite): 486 self.out = out 487 self.err = err 488 self.retval = retval 489 490 self.ProcessResults() 491 492 def ProcessResults(self): 493 # The output is: 494 # url,average_commit_time (ms),... 495 # www.google.com,33.4,21.2,... 496 # We need to convert to this format: 497 # {"www.google.com:average_commit_time (ms)": "33.4", 498 # "www.google.com:...": "21.2"} 499 # Added note: Occasionally the output comes back 500 # with "JSON.stringify(window.automation.GetResults())" on 501 # the first line, and then the rest of the output as 502 # described above. 503 504 lines = self.out.splitlines() 505 self.keyvals = {} 506 507 if lines: 508 if lines[0].startswith('JSON.stringify'): 509 lines = lines[1:] 510 511 if not lines: 512 return 513 labels = lines[0].split(',') 514 for line in lines[1:]: 515 fields = line.split(',') 516 if len(fields) != len(labels): 517 continue 518 for i in range(1, len(labels)): 519 key = '%s %s' % (fields[0], labels[i]) 520 value = fields[i] 521 self.keyvals[key] = value 522 self.keyvals['retval'] = self.retval 523 524 def PopulateFromCacheDir(self, cache_dir, test, suite): 525 self.test_name = test 526 self.suite = suite 527 with open(os.path.join(cache_dir, RESULTS_FILE), 'r') as f: 528 self.out = pickle.load(f) 529 self.err = pickle.load(f) 530 self.retval = pickle.load(f) 531 532 self.chrome_version = \ 533 super(TelemetryResult, self).GetChromeVersionFromCache(cache_dir) 534 self.ProcessResults() 535 536 537class CacheConditions(object): 538 """Various Cache condition values, for export.""" 539 540 # Cache hit only if the result file exists. 541 CACHE_FILE_EXISTS = 0 542 543 # Cache hit if the checksum of cpuinfo and totalmem of 544 # the cached result and the new run match. 545 MACHINES_MATCH = 1 546 547 # Cache hit if the image checksum of the cached result and the new run match. 548 CHECKSUMS_MATCH = 2 549 550 # Cache hit only if the cached result was successful 551 RUN_SUCCEEDED = 3 552 553 # Never a cache hit. 554 FALSE = 4 555 556 # Cache hit if the image path matches the cached image path. 557 IMAGE_PATH_MATCH = 5 558 559 # Cache hit if the uuid of hard disk mataches the cached one 560 561 SAME_MACHINE_MATCH = 6 562 563 564class ResultsCache(object): 565 """Class to handle the cache for storing/retrieving test run results. 566 567 This class manages the key of the cached runs without worrying about what 568 is exactly stored (value). The value generation is handled by the Results 569 class. 570 """ 571 CACHE_VERSION = 6 572 573 def __init__(self): 574 # Proper initialization happens in the Init function below. 575 self.chromeos_image = None 576 self.chromeos_root = None 577 self.test_name = None 578 self.iteration = None 579 self.test_args = None 580 self.profiler_args = None 581 self.board = None 582 self.cache_conditions = None 583 self.machine_manager = None 584 self.machine = None 585 self._logger = None 586 self.ce = None 587 self.label = None 588 self.share_cache = None 589 self.suite = None 590 self.log_level = None 591 self.show_all = None 592 self.run_local = None 593 594 595 def Init(self, chromeos_image, chromeos_root, test_name, iteration, test_args, 596 profiler_args, machine_manager, machine, board, cache_conditions, 597 logger_to_use, log_level, label, share_cache, suite, 598 show_all_results, run_local): 599 self.chromeos_image = chromeos_image 600 self.chromeos_root = chromeos_root 601 self.test_name = test_name 602 self.iteration = iteration 603 self.test_args = test_args 604 self.profiler_args = profiler_args 605 self.board = board 606 self.cache_conditions = cache_conditions 607 self.machine_manager = machine_manager 608 self.machine = machine 609 self._logger = logger_to_use 610 self.ce = command_executer.GetCommandExecuter(self._logger, 611 log_level=log_level) 612 self.label = label 613 self.share_cache = share_cache 614 self.suite = suite 615 self.log_level = log_level 616 self.show_all = show_all_results 617 self.run_local = run_local 618 619 def GetCacheDirForRead(self): 620 matching_dirs = [] 621 for glob_path in self.FormCacheDir(self.GetCacheKeyList(True)): 622 matching_dirs += glob.glob(glob_path) 623 624 if matching_dirs: 625 # Cache file found. 626 return matching_dirs[0] 627 else: 628 return None 629 630 def GetCacheDirForWrite(self, get_keylist=False): 631 cache_path = self.FormCacheDir(self.GetCacheKeyList(False))[0] 632 if get_keylist: 633 args_str = '%s_%s_%s' % (self.test_args, self.profiler_args, 634 self.run_local) 635 version, image = results_report.ParseChromeosImage( 636 self.label.chromeos_image) 637 keylist = [version, image, self.label.board, self.machine.name, 638 self.test_name, str(self.iteration), args_str] 639 return cache_path, keylist 640 return cache_path 641 642 def FormCacheDir(self, list_of_strings): 643 cache_key = ' '.join(list_of_strings) 644 cache_dir = misc.GetFilenameFromString(cache_key) 645 if self.label.cache_dir: 646 cache_home = os.path.abspath(os.path.expanduser(self.label.cache_dir)) 647 cache_path = [os.path.join(cache_home, cache_dir)] 648 else: 649 cache_path = [os.path.join(SCRATCH_DIR, cache_dir)] 650 651 if len(self.share_cache): 652 for path in [x.strip() for x in self.share_cache.split(',')]: 653 if os.path.exists(path): 654 cache_path.append(os.path.join(path, cache_dir)) 655 else: 656 self._logger.LogFatal('Unable to find shared cache: %s' % path) 657 658 return cache_path 659 660 def GetCacheKeyList(self, read): 661 if read and CacheConditions.MACHINES_MATCH not in self.cache_conditions: 662 machine_checksum = '*' 663 else: 664 machine_checksum = self.machine_manager.machine_checksum[self.label.name] 665 if read and CacheConditions.CHECKSUMS_MATCH not in self.cache_conditions: 666 checksum = '*' 667 elif self.label.image_type == 'trybot': 668 checksum = hashlib.md5(self.label.chromeos_image).hexdigest() 669 elif self.label.image_type == 'official': 670 checksum = '*' 671 else: 672 checksum = ImageChecksummer().Checksum(self.label, self.log_level) 673 674 if read and CacheConditions.IMAGE_PATH_MATCH not in self.cache_conditions: 675 image_path_checksum = '*' 676 else: 677 image_path_checksum = hashlib.md5(self.chromeos_image).hexdigest() 678 679 machine_id_checksum = '' 680 if read and CacheConditions.SAME_MACHINE_MATCH not in self.cache_conditions: 681 machine_id_checksum = '*' 682 else: 683 if self.machine and self.machine.name in self.label.remote: 684 machine_id_checksum = self.machine.machine_id_checksum 685 else: 686 for machine in self.machine_manager.GetMachines(self.label): 687 if machine.name == self.label.remote[0]: 688 machine_id_checksum = machine.machine_id_checksum 689 break 690 691 temp_test_args = '%s %s %s' % (self.test_args, self.profiler_args, 692 self.run_local) 693 test_args_checksum = hashlib.md5(''.join(temp_test_args)).hexdigest() 694 return (image_path_checksum, self.test_name, str(self.iteration), 695 test_args_checksum, checksum, machine_checksum, machine_id_checksum, 696 str(self.CACHE_VERSION)) 697 698 def ReadResult(self): 699 if CacheConditions.FALSE in self.cache_conditions: 700 cache_dir = self.GetCacheDirForWrite() 701 command = 'rm -rf {0}'.format(cache_dir) 702 self.ce.RunCommand(command) 703 return None 704 cache_dir = self.GetCacheDirForRead() 705 706 if not cache_dir: 707 return None 708 709 if not os.path.isdir(cache_dir): 710 return None 711 712 if self.log_level == 'verbose': 713 self._logger.LogOutput('Trying to read from cache dir: %s' % cache_dir) 714 result = Result.CreateFromCacheHit(self._logger, self.log_level, self.label, 715 self.machine, cache_dir, 716 self.test_name, self.suite) 717 if not result: 718 return None 719 720 if (result.retval == 0 or 721 CacheConditions.RUN_SUCCEEDED not in self.cache_conditions): 722 return result 723 724 return None 725 726 def StoreResult(self, result): 727 cache_dir, keylist = self.GetCacheDirForWrite(get_keylist=True) 728 result.StoreToCacheDir(cache_dir, self.machine_manager, keylist) 729 730 731class MockResultsCache(ResultsCache): 732 """Class for mock testing, corresponding to ResultsCache class.""" 733 734 def Init(self, *args): 735 pass 736 737 def ReadResult(self): 738 return None 739 740 def StoreResult(self, result): 741 pass 742 743 744class MockResult(Result): 745 """Class for mock testing, corresponding to Result class.""" 746 747 def PopulateFromRun(self, out, err, retval, test, suite): 748 self.out = out 749 self.err = err 750 self.retval = retval 751