2
# Copyright (c) 2008 Canonical
4
# Written by Marc Tardif <marc@interunion.ca>
6
# This file is part of Checkbox.
8
# Checkbox is free software: you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation, either version 3 of the License, or
11
# (at your option) any later version.
13
# Checkbox is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License
19
# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
22
The traverser exposes a simple iterator interface for accessing the graph
23
of tests as expressed by the dependency tree. The constructor provides an
24
additional callbacks instance for processing tests which might be skipped.
29
from checkbox.lib.iterator import (Iterator, IteratorContain,
30
IteratorExclude, IteratorPostRepeat, IteratorPreRepeat)
32
from checkbox.resolver import Resolver
33
from checkbox.test import FAIL, SKIP
36
class TraverserCallbacks(object):
38
def get_architecture(self):
41
def get_category(self):
44
def get_priorities(self):
47
def skipped_dependent(self, test, result):
48
logging.info("Test '%s' dependent on skipped test",
51
def failed_dependent(self, test, result):
52
logging.info("Test '%s' dependent on failed test",
55
def unsupported_requires(self, test, result):
56
logging.info("Test '%s' does not support requires field: %s",
57
test.name, test.requires)
59
def undefined_architecture(self, test, result):
60
logging.info("No system architecture defined, skipping test: %s",
63
def unsupported_architecture(self, test, result):
64
logging.info("System architecture not supported, skipping test: %s",
67
def undefined_category(self, test, result):
68
logging.info("No system category defined, skipping test: %s",
71
def unsupported_category(self, test, result):
72
logging.info("System category not supported, skipping test: %s",
76
class Traverser(IteratorContain):
78
def __init__(self, tests=[], callbacks=TraverserCallbacks, *args, **kwargs):
79
self._callbacks = callbacks(*args, **kwargs)
81
self._dependent_next = []
82
self._dependent_prev = []
83
self._dependent_status = None
85
# Express dependents as objects rather than string names
86
resolver = Resolver(self._compare_func)
87
test_dict = dict(((t.suite, t.name), t) for t in tests)
89
test_depends = [test_dict[(test.suite, d)] for d in test.depends]
90
resolver.add(test, *test_depends)
92
tests = resolver.get_dependents()
93
iterator = Iterator(tests)
94
iterator = IteratorExclude(iterator,
95
self._requires_exclude_func,
96
self._requires_exclude_func)
97
iterator = IteratorExclude(iterator,
98
self._architecture_exclude_func,
99
self._architecture_exclude_func)
100
iterator = IteratorExclude(iterator,
101
self._category_exclude_func,
102
self._category_exclude_func)
103
iterator = IteratorExclude(iterator,
104
self._dependent_exclude_next_func,
105
self._dependent_exclude_prev_func)
106
iterator = IteratorPreRepeat(iterator,
107
lambda test, result, resolver=resolver: \
108
self._dependent_prerepeat_next_func(test, result, resolver))
109
iterator = IteratorPostRepeat(iterator,
110
prev_func=lambda test, result, resolver=resolver: \
111
self._dependent_prerepeat_prev_func(test, result, resolver))
113
super(Traverser, self).__init__(iterator)
115
def _compare_func(self, a, b):
116
priorities = self._callbacks.get_priorities()
117
if a.plugin in priorities:
118
if b.plugin in priorities:
119
ia = priorities.index(a.plugin)
120
ib = priorities.index(b.plugin)
125
elif b.plugin in priorities:
128
return cmp((a.suite, a.name), (b.suite, b.name))
130
def _dependent_prerepeat_next_func(self, test, result, resolver):
131
"""IteratorPreRepeat function which completely skips dependents
132
of tests which either have a status of FAIL or SKIP."""
133
if result and result.test == test:
134
if result.status == FAIL or result.status == SKIP:
135
self._dependent_next = resolver.get_dependents(test)
136
self._dependent_status = result.status
138
self._dependent_next = []
140
self._dependent_prev.append(test)
142
def _dependent_prerepeat_prev_func(self, test, result, resolver):
143
"""IteratorPreRepeat function which supplements the above by
144
keeping track of a stack of previously run tests."""
145
self._dependent_prev.pop()
147
def _dependent_exclude_next_func(self, test, result):
148
if test in self._dependent_next:
149
if self._dependent_status == FAIL:
150
self._callbacks.failed_dependent(test, result)
151
elif self._dependent_status == SKIP:
152
self._callbacks.skipped_dependent(test, result)
157
def _dependent_exclude_prev_func(self, test, result):
158
if self._dependent_prev[-1] != test:
163
def _requires_exclude_func(self, test, result):
164
"""IteratorExclude function which removes test when the
165
requires field contains a False value."""
166
if False in test.requires.get_mask():
167
self._callbacks.unsupported_requires(test, result)
172
def _architecture_exclude_func(self, test, result):
173
"""IteratorExclude function which removes test when the
174
architectures field exists and doesn't meet the given
176
if test.architectures:
177
architecture = self._callbacks.get_architecture()
178
if architecture is None:
179
self._callbacks.undefined_architecture(test, result)
181
elif architecture not in test.architectures:
182
self._callbacks.unsupported_architecture(test, result)
187
def _category_exclude_func(self, test, result):
188
"""IteratorExclude function which removes test when
189
the categories field exists and doesn't meet the given
192
category = self._callbacks.get_category()
194
self._callbacks.undefined_category(test, result)
196
elif category not in test.categories:
197
self._callbacks.unsupported_category(test, result)