1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module containing information about the python-driven tests."""
import logging
import os
import tests_annotations
class TestInfo(object):
"""An object containing and representing a test function, plus metadata."""
def __init__(self, runnable, set_up=None, tear_down=None):
# The actual test function/method.
self.runnable = runnable
# Qualified name of test function/method (e.g. FooModule.testBar).
self.qualified_name = self._GetQualifiedName(runnable)
# setUp and teardown functions, if any.
self.set_up = set_up
self.tear_down = tear_down
def _GetQualifiedName(self, runnable):
"""Helper method to infer a runnable's name and module name.
Many filters and lists presuppose a format of module_name.testMethodName.
To make this easy on everyone, we use some reflection magic to infer this
name automatically.
Args:
runnable: the test method to get the qualified name for
Returns:
qualified name for this runnable, incl. module name and method name.
"""
runnable_name = runnable.__name__
# See also tests_annotations.
module_name = os.path.splitext(
os.path.basename(runnable.__globals__['__file__']))[0]
return '.'.join([module_name, runnable_name])
def __str__(self):
return self.qualified_name
class TestInfoCollection(object):
"""A collection of TestInfo objects which facilitates filtering."""
def __init__(self):
"""Initialize a new TestInfoCollection."""
# Master list of all valid tests.
self.all_tests = []
def AddTests(self, test_infos):
"""Adds a set of tests to this collection.
The user may then retrieve them, optionally according to criteria, via
GetAvailableTests().
Args:
test_infos: a list of TestInfos representing test functions/methods.
"""
self.all_tests = test_infos
def GetAvailableTests(self, annotation, name_filter):
"""Get a collection of TestInfos which match the supplied criteria.
Args:
annotation: annotation which tests must match, if any
name_filter: name filter which tests must match, if any
Returns:
List of available tests.
"""
available_tests = self.all_tests
# Filter out tests which match neither the requested annotation, nor the
# requested name filter, if any.
available_tests = [t for t in available_tests if
self._AnnotationIncludesTest(t, annotation)]
if annotation and len(annotation) == 1 and annotation[0] == 'SmallTest':
tests_without_annotation = [
t for t in self.all_tests if
not tests_annotations.AnnotatedFunctions.GetTestAnnotations(
t.qualified_name)]
test_names = [t.qualified_name for t in tests_without_annotation]
logging.warning('The following tests do not contain any annotation. '
'Assuming "SmallTest":\n%s',
'\n'.join(test_names))
available_tests += tests_without_annotation
available_tests = [t for t in available_tests if
self._NameFilterIncludesTest(t, name_filter)]
return available_tests
def _AnnotationIncludesTest(self, test_info, annotation_filter_list):
"""Checks whether a given test represented by test_info matches annotation.
Args:
test_info: TestInfo object representing the test
annotation_filter_list: list of annotation filters to match (e.g. Smoke)
Returns:
True if no annotation was supplied or the test matches; false otherwise.
"""
if not annotation_filter_list:
return True
for annotation_filter in annotation_filter_list:
filters = annotation_filter.split('=')
if len(filters) == 2:
key = filters[0]
value_list = filters[1].split(',')
for value in value_list:
if tests_annotations.AnnotatedFunctions.IsAnnotated(
key + ':' + value, test_info.qualified_name):
return True
elif tests_annotations.AnnotatedFunctions.IsAnnotated(
annotation_filter, test_info.qualified_name):
return True
return False
def _NameFilterIncludesTest(self, test_info, name_filter):
"""Checks whether a name filter matches a given test_info's method name.
This is a case-sensitive, substring comparison: 'Foo' will match methods
Foo.testBar and Bar.testFoo. 'foo' would not match either.
Args:
test_info: TestInfo object representing the test
name_filter: substring to check for in the qualified name of the test
Returns:
True if no name filter supplied or it matches; False otherwise.
"""
return not name_filter or name_filter in test_info.qualified_name
|