OLD | NEW |
1 # Copyright (C) 2011 Google Inc. All rights reserved. | 1 # Copyright (C) 2011 Google Inc. All rights reserved. |
2 # | 2 # |
3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
5 # met: | 5 # met: |
6 # | 6 # |
7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
11 # in the documentation and/or other materials provided with the | 11 # in the documentation and/or other materials provided with the |
12 # distribution. | 12 # distribution. |
13 # * Neither the Google name nor the names of its | 13 # * Neither the Google name nor the names of its |
14 # contributors may be used to endorse or promote products derived from | 14 # contributors may be used to endorse or promote products derived from |
15 # this software without specific prior written permission. | 15 # this software without specific prior written permission. |
16 # | 16 # |
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | 28 |
29 import copy | 29 import copy |
| 30 from functools import reduce |
30 | 31 |
31 | 32 |
32 class TestConfiguration(object): | 33 class TestConfiguration(object): |
| 34 |
33 def __init__(self, version, architecture, build_type): | 35 def __init__(self, version, architecture, build_type): |
34 self.version = version | 36 self.version = version |
35 self.architecture = architecture | 37 self.architecture = architecture |
36 self.build_type = build_type | 38 self.build_type = build_type |
37 | 39 |
38 @classmethod | 40 @classmethod |
39 def category_order(cls): | 41 def category_order(cls): |
40 """The most common human-readable order in which the configuration prope
rties are listed.""" | 42 """The most common human-readable order in which the configuration prope
rties are listed.""" |
41 return ['version', 'architecture', 'build_type'] | 43 return ['version', 'architecture', 'build_type'] |
42 | 44 |
43 def items(self): | 45 def items(self): |
44 return self.__dict__.items() | 46 return self.__dict__.items() |
45 | 47 |
46 def keys(self): | 48 def keys(self): |
47 return self.__dict__.keys() | 49 return self.__dict__.keys() |
48 | 50 |
49 def __str__(self): | 51 def __str__(self): |
50 return ("<%(version)s, %(architecture)s, %(build_type)s>" % | 52 return ('<%(version)s, %(architecture)s, %(build_type)s>' % |
51 self.__dict__) | 53 self.__dict__) |
52 | 54 |
53 def __repr__(self): | 55 def __repr__(self): |
54 return "TestConfig(version='%(version)s', architecture='%(architecture)s
', build_type='%(build_type)s')" % self.__dict__ | 56 return "TestConfig(version='%(version)s', architecture='%(architecture)s
', build_type='%(build_type)s')" % self.__dict__ |
55 | 57 |
56 def __hash__(self): | 58 def __hash__(self): |
57 return hash(self.version + self.architecture + self.build_type) | 59 return hash(self.version + self.architecture + self.build_type) |
58 | 60 |
59 def __eq__(self, other): | 61 def __eq__(self, other): |
60 return self.__hash__() == other.__hash__() | 62 return self.__hash__() == other.__hash__() |
61 | 63 |
62 def values(self): | 64 def values(self): |
63 """Returns the configuration values of this instance as a tuple.""" | 65 """Returns the configuration values of this instance as a tuple.""" |
64 return self.__dict__.values() | 66 return self.__dict__.values() |
65 | 67 |
66 | 68 |
67 class SpecifierSorter(object): | 69 class SpecifierSorter(object): |
| 70 |
68 def __init__(self, all_test_configurations=None, macros=None): | 71 def __init__(self, all_test_configurations=None, macros=None): |
69 self._specifier_to_category = {} | 72 self._specifier_to_category = {} |
70 | 73 |
71 if not all_test_configurations: | 74 if not all_test_configurations: |
72 return | 75 return |
73 for test_configuration in all_test_configurations: | 76 for test_configuration in all_test_configurations: |
74 for category, specifier in test_configuration.items(): | 77 for category, specifier in test_configuration.items(): |
75 self.add_specifier(category, specifier) | 78 self.add_specifier(category, specifier) |
76 | 79 |
77 self.add_macros(macros) | 80 self.add_macros(macros) |
(...skipping 24 matching lines...) Expand all Loading... |
102 category_slots[self.specifier_priority(specifier)].append(specifier) | 105 category_slots[self.specifier_priority(specifier)].append(specifier) |
103 | 106 |
104 def sort_and_return(result, specifier_list): | 107 def sort_and_return(result, specifier_list): |
105 specifier_list.sort() | 108 specifier_list.sort() |
106 return result + specifier_list | 109 return result + specifier_list |
107 | 110 |
108 return reduce(sort_and_return, category_slots, []) | 111 return reduce(sort_and_return, category_slots, []) |
109 | 112 |
110 | 113 |
111 class TestConfigurationConverter(object): | 114 class TestConfigurationConverter(object): |
| 115 |
112 def __init__(self, all_test_configurations, configuration_macros=None): | 116 def __init__(self, all_test_configurations, configuration_macros=None): |
113 self._all_test_configurations = all_test_configurations | 117 self._all_test_configurations = all_test_configurations |
114 self._configuration_macros = configuration_macros or {} | 118 self._configuration_macros = configuration_macros or {} |
115 self._specifier_to_configuration_set = {} | 119 self._specifier_to_configuration_set = {} |
116 self._specifier_sorter = SpecifierSorter() | 120 self._specifier_sorter = SpecifierSorter() |
117 self._collapsing_sets_by_size = {} | 121 self._collapsing_sets_by_size = {} |
118 self._junk_specifier_combinations = {} | 122 self._junk_specifier_combinations = {} |
119 self._collapsing_sets_by_category = {} | 123 self._collapsing_sets_by_category = {} |
120 matching_sets_by_category = {} | 124 matching_sets_by_category = {} |
121 for configuration in all_test_configurations: | 125 for configuration in all_test_configurations: |
122 for category, specifier in configuration.items(): | 126 for category, specifier in configuration.items(): |
123 self._specifier_to_configuration_set.setdefault(specifier, set()
).add(configuration) | 127 self._specifier_to_configuration_set.setdefault(specifier, set()
).add(configuration) |
124 self._specifier_sorter.add_specifier(category, specifier) | 128 self._specifier_sorter.add_specifier(category, specifier) |
125 self._collapsing_sets_by_category.setdefault(category, set()).ad
d(specifier) | 129 self._collapsing_sets_by_category.setdefault(category, set()).ad
d(specifier) |
126 # FIXME: This seems extra-awful. | 130 # FIXME: This seems extra-awful. |
127 for cat2, spec2 in configuration.items(): | 131 for cat2, spec2 in configuration.items(): |
128 if category == cat2: | 132 if category == cat2: |
129 continue | 133 continue |
130 matching_sets_by_category.setdefault(specifier, {}).setdefau
lt(cat2, set()).add(spec2) | 134 matching_sets_by_category.setdefault(specifier, {}).setdefau
lt(cat2, set()).add(spec2) |
131 for collapsing_set in self._collapsing_sets_by_category.values(): | 135 for collapsing_set in self._collapsing_sets_by_category.values(): |
132 self._collapsing_sets_by_size.setdefault(len(collapsing_set), set())
.add(frozenset(collapsing_set)) | 136 self._collapsing_sets_by_size.setdefault(len(collapsing_set), set())
.add(frozenset(collapsing_set)) |
133 | 137 |
134 for specifier, sets_by_category in matching_sets_by_category.items(): | 138 for specifier, sets_by_category in matching_sets_by_category.items(): |
135 for category, set_by_category in sets_by_category.items(): | 139 for category, set_by_category in sets_by_category.items(): |
136 if len(set_by_category) == 1 and self._specifier_sorter.category
_priority(category) > self._specifier_sorter.specifier_priority(specifier): | 140 if len(set_by_category) == 1 and self._specifier_sorter.category
_priority( |
| 141 category) > self._specifier_sorter.specifier_priority(sp
ecifier): |
137 self._junk_specifier_combinations[specifier] = set_by_catego
ry | 142 self._junk_specifier_combinations[specifier] = set_by_catego
ry |
138 | 143 |
139 self._specifier_sorter.add_macros(configuration_macros) | 144 self._specifier_sorter.add_macros(configuration_macros) |
140 | 145 |
141 def specifier_sorter(self): | 146 def specifier_sorter(self): |
142 return self._specifier_sorter | 147 return self._specifier_sorter |
143 | 148 |
144 def _expand_macros(self, specifier): | 149 def _expand_macros(self, specifier): |
145 expanded_specifiers = self._configuration_macros.get(specifier) | 150 expanded_specifiers = self._configuration_macros.get(specifier) |
146 return expanded_specifiers or [specifier] | 151 return expanded_specifiers or [specifier] |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
275 specifiers_list.remove(item) | 280 specifiers_list.remove(item) |
276 specifiers_list.append(frozenset(common | diff)) | 281 specifiers_list.append(frozenset(common | diff)) |
277 return True | 282 return True |
278 return False | 283 return False |
279 | 284 |
280 # 3) Abbreviate specifier sets by combining specifiers across categories
. | 285 # 3) Abbreviate specifier sets by combining specifiers across categories
. |
281 # (xp, release), (win7, release) --> (xp, win7, release) | 286 # (xp, release), (win7, release) --> (xp, win7, release) |
282 while try_abbreviating(self._collapsing_sets_by_size.values()): | 287 while try_abbreviating(self._collapsing_sets_by_size.values()): |
283 pass | 288 pass |
284 | 289 |
285 | |
286 # 4) Substitute specifier subsets that match macros witin each set: | 290 # 4) Substitute specifier subsets that match macros witin each set: |
287 # (xp, win7, release) -> (win, release) | 291 # (xp, win7, release) -> (win, release) |
288 self.collapse_macros(self._configuration_macros, specifiers_list) | 292 self.collapse_macros(self._configuration_macros, specifiers_list) |
289 | 293 |
290 macro_keys = set(self._configuration_macros.keys()) | 294 macro_keys = set(self._configuration_macros.keys()) |
291 | 295 |
292 # 5) Collapsing macros may have created combinations the can now be abbr
eviated. | 296 # 5) Collapsing macros may have created combinations the can now be abbr
eviated. |
293 # (xp, release), (linux, x86, release), (linux, x86_64, release) --> (
xp, release), (linux, release) --> (xp, linux, release) | 297 # (xp, release), (linux, x86, release), (linux, x86_64, release) --> (
xp, release), (linux, release) --> (xp, linux, release) |
294 while try_abbreviating([self._collapsing_sets_by_category['version'] | m
acro_keys]): | 298 while try_abbreviating([self._collapsing_sets_by_category['version'] | m
acro_keys]): |
295 pass | 299 pass |
296 | 300 |
297 # 6) Remove cases where we have collapsed but have all macros. | 301 # 6) Remove cases where we have collapsed but have all macros. |
298 # (android, win, mac, linux, release) --> (release) | 302 # (android, win, mac, linux, release) --> (release) |
299 specifiers_to_remove = [] | 303 specifiers_to_remove = [] |
300 for specifier_set in specifiers_list: | 304 for specifier_set in specifiers_list: |
301 if macro_keys <= specifier_set: | 305 if macro_keys <= specifier_set: |
302 specifiers_to_remove.append(specifier_set) | 306 specifiers_to_remove.append(specifier_set) |
303 | 307 |
304 for specifier_set in specifiers_to_remove: | 308 for specifier_set in specifiers_to_remove: |
305 specifiers_list.remove(specifier_set) | 309 specifiers_list.remove(specifier_set) |
306 specifiers_list.append(frozenset(specifier_set - macro_keys)) | 310 specifiers_list.append(frozenset(specifier_set - macro_keys)) |
307 | 311 |
308 return specifiers_list | 312 return specifiers_list |
OLD | NEW |