15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Copyright (C) 2010 Google Inc. All rights reserved. 25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Redistribution and use in source and binary forms, with or without 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# modification, are permitted provided that the following conditions are 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# met: 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# * Redistributions of source code must retain the above copyright 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# notice, this list of conditions and the following disclaimer. 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# * Redistributions in binary form must reproduce the above 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# copyright notice, this list of conditions and the following disclaimer 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# in the documentation and/or other materials provided with the 125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# distribution. 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# * Neither the name of Google Inc. nor the names of its 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# contributors may be used to endorse or promote products derived from 155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# this software without specific prior written permission. 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)import json 305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)import logging 315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)_log = logging.getLogger(__name__) 335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)_JSON_PREFIX = "ADD_RESULTS(" 355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)_JSON_SUFFIX = ");" 365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)def has_json_wrapper(string): 395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return string.startswith(_JSON_PREFIX) and string.endswith(_JSON_SUFFIX) 405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)def strip_json_wrapper(json_content): 435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # FIXME: Kill this code once the server returns json instead of jsonp. 445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if has_json_wrapper(json_content): 455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return json_content[len(_JSON_PREFIX):len(json_content) - len(_JSON_SUFFIX)] 465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return json_content 475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)def load_json(filesystem, file_path): 505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) content = filesystem.read_text_file(file_path) 515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) content = strip_json_wrapper(content) 525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return json.loads(content) 535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)def write_json(filesystem, json_object, file_path, callback=None): 565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Specify separators in order to get compact encoding. 575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) json_string = json.dumps(json_object, separators=(',', ':')) 585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if callback: 595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) json_string = callback + "(" + json_string + ");" 605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) filesystem.write_text_file(file_path, json_string) 615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)def convert_trie_to_flat_paths(trie, prefix=None): 645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) """Converts the directory structure in the given trie to flat paths, prepending a prefix to each.""" 655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) result = {} 665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for name, data in trie.iteritems(): 675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if prefix: 685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) name = prefix + "/" + name 695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if len(data) and not "results" in data: 715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) result.update(convert_trie_to_flat_paths(data, name)) 725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else: 735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) result[name] = data 745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return result 765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)def add_path_to_trie(path, value, trie): 795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) """Inserts a single flat directory path and associated value into a directory trie structure.""" 805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if not "/" in path: 815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) trie[path] = value 825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return 835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) directory, slash, rest = path.partition("/") 855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if not directory in trie: 865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) trie[directory] = {} 875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) add_path_to_trie(rest, value, trie[directory]) 885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 89197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 90197021e6b966cfb06891637935ef33fff06433d1Ben Murdochdef test_timings_trie(individual_test_timings): 915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) """Breaks a test name into chunks by directory and puts the test time as a value in the lowest part, e.g. 925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) foo/bar/baz.html: 1ms 935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) foo/bar/baz1.html: 3ms 945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) becomes 965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) foo: { 975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) bar: { 985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) baz.html: 1, 995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) baz1.html: 3 1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) """ 1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) trie = {} 1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for test_result in individual_test_timings: 1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) test = test_result.test_name 1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) add_path_to_trie(test, int(1000 * test_result.test_run_time), trie) 1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return trie 1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 111197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# FIXME: We already have a TestResult class in test_results.py 1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class TestResult(object): 1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) """A simple class that represents a single test result.""" 1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Test modifier constants. 1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) (NONE, FAILS, FLAKY, DISABLED) = range(4) 1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) def __init__(self, test, failed=False, elapsed_time=0): 1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self.test_name = test 1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self.failed = failed 1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self.test_run_time = elapsed_time 1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) test_name = test 1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) try: 1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) test_name = test.split('.')[1] 1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) except IndexError: 1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.warn("Invalid test name: %s.", test) 1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) pass 1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if test_name.startswith('FAILS_'): 1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self.modifier = self.FAILS 1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) elif test_name.startswith('FLAKY_'): 1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self.modifier = self.FLAKY 1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) elif test_name.startswith('DISABLED_'): 1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self.modifier = self.DISABLED 1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else: 1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self.modifier = self.NONE 1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) def fixable(self): 1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return self.failed or self.modifier == self.DISABLED 142