]> Git Repo - qemu.git/blob - tests/qemu-iotests/findtests.py
works with less than base ISA qemu-system-riscv32 -M virt -bios none -kernel output...
[qemu.git] / tests / qemu-iotests / findtests.py
1 # TestFinder class, define set of tests to run.
2 #
3 # Copyright (c) 2020-2021 Virtuozzo International GmbH
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18
19 import os
20 import glob
21 import re
22 from collections import defaultdict
23 from contextlib import contextmanager
24 from typing import Optional, List, Iterator, Set
25
26
27 @contextmanager
28 def chdir(path: Optional[str] = None) -> Iterator[None]:
29     if path is None:
30         yield
31         return
32
33     saved_dir = os.getcwd()
34     os.chdir(path)
35     try:
36         yield
37     finally:
38         os.chdir(saved_dir)
39
40
41 class TestFinder:
42     def __init__(self, test_dir: Optional[str] = None) -> None:
43         self.groups = defaultdict(set)
44
45         with chdir(test_dir):
46             self.all_tests = glob.glob('[0-9][0-9][0-9]')
47             self.all_tests += [f for f in glob.iglob('tests/*')
48                                if not f.endswith('.out') and
49                                os.path.isfile(f + '.out')]
50
51             for t in self.all_tests:
52                 with open(t, encoding="utf-8") as f:
53                     for line in f:
54                         if line.startswith('# group: '):
55                             for g in line.split()[2:]:
56                                 self.groups[g].add(t)
57                             break
58
59     def add_group_file(self, fname: str) -> None:
60         with open(fname, encoding="utf-8") as f:
61             for line in f:
62                 line = line.strip()
63
64                 if (not line) or line[0] == '#':
65                     continue
66
67                 words = line.split()
68                 test_file = self.parse_test_name(words[0])
69                 groups = words[1:]
70
71                 for g in groups:
72                     self.groups[g].add(test_file)
73
74     def parse_test_name(self, name: str) -> str:
75         if '/' in name:
76             raise ValueError('Paths are unsupported for test selection, '
77                              f'requiring "{name}" is wrong')
78
79         if re.fullmatch(r'\d+', name):
80             # Numbered tests are old naming convention. We should convert them
81             # to three-digit-length, like 1 --> 001.
82             name = f'{int(name):03}'
83         else:
84             # Named tests all should be in tests/ subdirectory
85             name = os.path.join('tests', name)
86
87         if name not in self.all_tests:
88             raise ValueError(f'Test "{name}" is not found')
89
90         return name
91
92     def find_tests(self, groups: Optional[List[str]] = None,
93                    exclude_groups: Optional[List[str]] = None,
94                    tests: Optional[List[str]] = None,
95                    start_from: Optional[str] = None) -> List[str]:
96         """Find tests
97
98         Algorithm:
99
100         1. a. if some @groups specified
101              a.1 Take all tests from @groups
102              a.2 Drop tests, which are in at least one of @exclude_groups or in
103                  'disabled' group (if 'disabled' is not listed in @groups)
104              a.3 Add tests from @tests (don't exclude anything from them)
105
106            b. else, if some @tests specified:
107              b.1 exclude_groups must be not specified, so just take @tests
108
109            c. else (only @exclude_groups list is non-empty):
110              c.1 Take all tests
111              c.2 Drop tests, which are in at least one of @exclude_groups or in
112                  'disabled' group
113
114         2. sort
115
116         3. If start_from specified, drop tests from first one to @start_from
117            (not inclusive)
118         """
119         if groups is None:
120             groups = []
121         if exclude_groups is None:
122             exclude_groups = []
123         if tests is None:
124             tests = []
125
126         res: Set[str] = set()
127         if groups:
128             # Some groups specified. exclude_groups supported, additionally
129             # selecting some individual tests supported as well.
130             res.update(*(self.groups[g] for g in groups))
131         elif tests:
132             # Some individual tests specified, but no groups. In this case
133             # we don't support exclude_groups.
134             if exclude_groups:
135                 raise ValueError("Can't exclude from individually specified "
136                                  "tests.")
137         else:
138             # No tests no groups: start from all tests, exclude_groups
139             # supported.
140             res.update(self.all_tests)
141
142         if 'disabled' not in groups and 'disabled' not in exclude_groups:
143             # Don't want to modify function argument, so create new list.
144             exclude_groups = exclude_groups + ['disabled']
145
146         res = res.difference(*(self.groups[g] for g in exclude_groups))
147
148         # We want to add @tests. But for compatibility with old test names,
149         # we should convert any number < 100 to number padded by
150         # leading zeroes, like 1 -> 001 and 23 -> 023.
151         for t in tests:
152             res.add(self.parse_test_name(t))
153
154         sequence = sorted(res)
155
156         if start_from is not None:
157             del sequence[:sequence.index(self.parse_test_name(start_from))]
158
159         return sequence
This page took 0.032588 seconds and 4 git commands to generate.