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