1#!/usr/bin/env python
2# Copyright (C) 2010 Google Inc. All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions
6# are met:
7#
8# 1.  Redistributions of source code must retain the above copyright
9#     notice, this list of conditions and the following disclaimer.
10# 2.  Redistributions in binary form must reproduce the above copyright
11#     notice, this list of conditions and the following disclaimer in the
12#     documentation and/or other materials provided with the distribution.
13#
14# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
25from webkitpy.common.system.directoryfileset import DirectoryFileSet
26from webkitpy.common.system.zipfileset import ZipFileSet
27import re
28import testoutput
29import urllib
30
31
32class TestOutputSet(object):
33    def __init__(self, name, platform, zip_file, **kwargs):
34        self._name = name
35        self._platform = platform
36        self._zip_file = zip_file
37        self._include_expected = kwargs.get('include_expected', True)
38
39    @classmethod
40    def from_zip_url(cls, platform, zip_path):
41        return TestOutputSet('local zip %s builder' % platform, platform, ZipFileSet(zip_path))
42
43    @classmethod
44    def from_zip(cls, platform, zip):
45        return TestOutputSet('local zip %s builder' % platform, platform, zip)
46
47    @classmethod
48    def from_zip_map(cls, zip_map):
49        output_sets = []
50        for k, v in zip_map.items():
51            output_sets.append(TestOutputSet.from_zip(k, v))
52        return AggregateTestOutputSet(output_sets)
53
54    @classmethod
55    def from_path(self, path, platform=None):
56        return TestOutputSet('local %s builder' % platform, platform, DirectoryFileSet(path))
57
58    def name(self):
59        return self._name
60
61    def set_platform(self, platform):
62        self._platform = platform
63
64    def files(self):
65        return [self._zip_file.open(filename) for filename in self._zip_file.namelist()]
66
67    def _extract_output_files(self, name, exact_match):
68        name_matcher = re.compile(name)
69        actual_matcher = re.compile(r'-actual\.')
70        expected_matcher = re.compile(r'-expected\.')
71
72        checksum_files = []
73        text_files = []
74        image_files = []
75        for output_file in self.files():
76            name_match = name_matcher.search(output_file.name())
77            actual_match = actual_matcher.search(output_file.name())
78            expected_match = expected_matcher.search(output_file.name())
79            if not (name_match and (actual_match or (self._include_expected and expected_match))):
80                continue
81            if output_file.name().endswith('.checksum'):
82                checksum_files.append(output_file)
83            elif output_file.name().endswith('.txt'):
84                text_files.append(output_file)
85            elif output_file.name().endswith('.png'):
86                image_files.append(output_file)
87
88        return (checksum_files, text_files, image_files)
89
90    def _extract_file_with_name(self, name, files):
91        for file in files:
92            if file.name() == name:
93                return file
94        return None
95
96    def _make_output_from_image(self, image_file, checksum_files):
97        checksum_file_name = re.sub('\.png', '.checksum', image_file.name())
98        checksum_file = self._extract_file_with_name(checksum_file_name, checksum_files)
99        return testoutput.ImageTestOutput(self._platform, image_file, checksum_file)
100
101    def outputs_for(self, name, **kwargs):
102        target_type = kwargs.get('target_type', None)
103        exact_match = kwargs.get('exact_match', False)
104        if re.search(r'\.x?html', name):
105            name = name[:name.rindex('.')]
106
107        (checksum_files, text_files, image_files) = self._extract_output_files(name, exact_match)
108
109        outputs = [self._make_output_from_image(image_file, checksum_files) for image_file in image_files]
110
111        outputs += [testoutput.TextTestOutput(self._platform, text_file) for text_file in text_files]
112
113        if exact_match:
114            outputs = filter(lambda output: output.name() == name, outputs)
115
116        outputs = filter(lambda r: target_type in [None, r.type()], outputs)
117
118        return outputs
119
120
121class AggregateTestOutputSet(object):
122    """Set of test outputs from a list of builders"""
123    def __init__(self, builders):
124        self._builders = builders
125
126    def outputs_for(self, name, **kwargs):
127        return sum([builder.outputs_for(name, **kwargs) for builder in self._builders], [])
128
129    def builders(self):
130        return self._builders
131