testomaton 0.2.2__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- testomaton/__init__.py +0 -0
- testomaton/beaver.py +180 -0
- testomaton/constraint.py +669 -0
- testomaton/exit_codes.py +20 -0
- testomaton/generator.py +300 -0
- testomaton/jigsaw.py +347 -0
- testomaton/model.py +605 -0
- testomaton/solver.py +293 -0
- testomaton/tomato.py +473 -0
- testomaton/validator.py +238 -0
- testomaton-0.2.2.dist-info/LICENSE +661 -0
- testomaton-0.2.2.dist-info/METADATA +1193 -0
- testomaton-0.2.2.dist-info/RECORD +16 -0
- testomaton-0.2.2.dist-info/WHEEL +6 -0
- testomaton-0.2.2.dist-info/entry_points.txt +4 -0
- testomaton-0.2.2.dist-info/top_level.txt +7 -0
testomaton/tomato.py
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# Copyright Testify AS
|
|
6
|
+
#
|
|
7
|
+
# This file is part of testomaton suite
|
|
8
|
+
#
|
|
9
|
+
# testomaton is free software: you can redistribute it and/or modify
|
|
10
|
+
# it under the terms of the GNU Affero General Public License as published by
|
|
11
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
12
|
+
# (at your option) any later version.
|
|
13
|
+
#
|
|
14
|
+
# For the commercial license, please contact Testify AS.
|
|
15
|
+
#
|
|
16
|
+
# testomaton is distributed in the hope that it will be useful,
|
|
17
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19
|
+
# GNU Affero General Public License for more details.
|
|
20
|
+
#
|
|
21
|
+
# You should have received a copy of the GNU General Public License
|
|
22
|
+
# along with testomaton. If not, see <http://www.gnu.org/licenses/>.
|
|
23
|
+
#
|
|
24
|
+
# See LICENSE file for the complete license text.
|
|
25
|
+
#
|
|
26
|
+
|
|
27
|
+
import sys
|
|
28
|
+
import fileinput
|
|
29
|
+
from io import StringIO
|
|
30
|
+
import argparse
|
|
31
|
+
import importlib.metadata as metadata
|
|
32
|
+
from pyaml import yaml
|
|
33
|
+
import itertools
|
|
34
|
+
|
|
35
|
+
import testomaton.generator as generator
|
|
36
|
+
import testomaton.model as model
|
|
37
|
+
import testomaton.validator as validator
|
|
38
|
+
from testomaton.exit_codes import ExitCodes as ec
|
|
39
|
+
from testomaton.solver import TomatoSolver
|
|
40
|
+
|
|
41
|
+
import csv
|
|
42
|
+
|
|
43
|
+
def version():
|
|
44
|
+
"""Get the version of tomato"""
|
|
45
|
+
return metadata.version('testomaton')
|
|
46
|
+
|
|
47
|
+
def parse_args():
|
|
48
|
+
"""Parse the command line arguments"""
|
|
49
|
+
parser = argparse.ArgumentParser()
|
|
50
|
+
|
|
51
|
+
parser.add_argument("--version", action='store_true',
|
|
52
|
+
default=False,
|
|
53
|
+
help="Print version and exit")
|
|
54
|
+
parser.add_argument("--demo-level", type=int, default=0,
|
|
55
|
+
help="Can be used to demostrate working of the algorithm. \
|
|
56
|
+
1: Show progress, 2: Show progress and introduces delay at each step",
|
|
57
|
+
choices=[1, 2, 3])
|
|
58
|
+
parser.add_argument("-f", "--function", type=str, default=None,
|
|
59
|
+
help="Function name to generate tests for. If not provided\
|
|
60
|
+
tests for the first function defined in the model will be generated")
|
|
61
|
+
|
|
62
|
+
validation_group = parser.add_argument_group('Validation')
|
|
63
|
+
# validation_group.add_argument("-v", "--validate-model", action='store_true', default=False,
|
|
64
|
+
# help=f"Validate the model and exit. Returns {ec.SUCCESS} if the model is valid, \
|
|
65
|
+
# {ec.MODEL_VALIDATION_ERROR} if the model is not syntatically correct. \
|
|
66
|
+
# If this flag is used, the only effective arguments are -f/--function and the arguments from \
|
|
67
|
+
# the 'Model content filtering'. Validated elements can still be filtered using whitelists \
|
|
68
|
+
# and blacklists. Other arguments are silently ignored.")
|
|
69
|
+
validation_group.add_argument("-V", "--validate-tests", nargs='?', type=str, const=None,
|
|
70
|
+
default=argparse.SUPPRESS, metavar='TEST_FILE',
|
|
71
|
+
help=f"Validate the tests and exit. Prints on stdandard output the tests are valid, ie. \
|
|
72
|
+
they could be generated from this model using all the elements and constraints (blacklists, \
|
|
73
|
+
whitelists, negations and invertions still can be used). If a test or the headrow could not \
|
|
74
|
+
be generated using given model, it is printed in the stderr with an error message, \
|
|
75
|
+
The opional argument is a path to the file with tests (with headrow or not, configured by \
|
|
76
|
+
-H/--no-headrow flag). If no test file is provided, the tests are read from the standard input. \
|
|
77
|
+
In this case, if the model is also read from the standard input, the model is read first and \
|
|
78
|
+
must be separated from the tests by a line starting from '---' \
|
|
79
|
+
(three dashes). ")
|
|
80
|
+
validation_group.add_argument("--exit-on-error", action='store_true', default=False,
|
|
81
|
+
help="Exit on the first error. If this flag is not set, the program will continue \
|
|
82
|
+
to the next test or model element after an error.")
|
|
83
|
+
validation_group.add_argument("-F", "--no-error-formatting", action='store_true', default=False,
|
|
84
|
+
help="Do not format error messages. If this flag is not set, the error messages are formatted \
|
|
85
|
+
to be more readable and distinguishable from valid tests.")
|
|
86
|
+
validation_group.add_argument("-M", "--no-error-messages", action='store_true', default=False,
|
|
87
|
+
help="Do not print error messages. If this flag is set, only the tests that fail validation \
|
|
88
|
+
are printed on stderr, without any additional messages.")
|
|
89
|
+
validation_group.add_argument("--duplicate-headrow", action='store_true', default=False,
|
|
90
|
+
help="Print the headrow both on top of the valid tests and the tests that fail validation.")
|
|
91
|
+
|
|
92
|
+
model_content_group = parser.add_argument_group('Model content filtering')
|
|
93
|
+
model_content_group.add_argument("-w", "--whitelist", type=str, default=None,
|
|
94
|
+
help="Comma separated list of names and labels of elements\
|
|
95
|
+
that should be parsed. All function elements (parameters, choices, \
|
|
96
|
+
constraints etc.) without name or label on that list will be ignored.")
|
|
97
|
+
model_content_group.add_argument("-b", "--blacklist", type=str, default=None,
|
|
98
|
+
help="Comma separated list of names and labels of elements\
|
|
99
|
+
that should be ignored. All function elements (parameters, choices, \
|
|
100
|
+
constraints etc.) with name or label on that list will be ignored.")
|
|
101
|
+
model_content_group.add_argument("--input-whitelist", type=str, default=None,
|
|
102
|
+
help="Whitelist of input elements (parameters and choices) to parse.\
|
|
103
|
+
Labels for input elements are inherited from parent element of the same type.")
|
|
104
|
+
model_content_group.add_argument("--input-blacklist", type=str, default=None,
|
|
105
|
+
help="Blacklist of input elements (parameters and choices) to ignore.\
|
|
106
|
+
Labels for input elements are inherited from parent element of the same type.")
|
|
107
|
+
model_content_group.add_argument("--parameters-whitelist", type=str, default=None,
|
|
108
|
+
help="Whitelist of parameters to parse. Labels within structures are inherited\
|
|
109
|
+
from parent parameter.")
|
|
110
|
+
model_content_group.add_argument("--parameters-blacklist", type=str, default=None,
|
|
111
|
+
help="Blacklist of parameters to ignore. Labels within structures are inherited")
|
|
112
|
+
model_content_group.add_argument("--choices-whitelist", type=str, default=None,
|
|
113
|
+
help="Whitelist of choices to parse. Labels of nested choices are inherited from the parent choice.")
|
|
114
|
+
model_content_group.add_argument("--choices-blacklist", type=str, default=None,
|
|
115
|
+
help="Blacklist of choices to ignore. Labels of nested choices are inherited from the parent choice.")
|
|
116
|
+
model_content_group.add_argument("--logic-whitelist", type=str, default=None,
|
|
117
|
+
help="Whitelist of logic elements (constraints and assignments) to parse.")
|
|
118
|
+
model_content_group.add_argument("--logic-blacklist", type=str, default=None,
|
|
119
|
+
help="Blacklist of logic elements (constraints and assignments) to ignore.")
|
|
120
|
+
model_content_group.add_argument("--constraints-whitelist", type=str, default=None,
|
|
121
|
+
help="Whitelist of constraints to parse.")
|
|
122
|
+
model_content_group.add_argument("--constraints-blacklist", type=str, default=None,
|
|
123
|
+
help="Blacklist of constraints to ignore.")
|
|
124
|
+
model_content_group.add_argument("--assignments-whitelist", type=str, default=None,
|
|
125
|
+
help="Whitelist of assignments to parse.")
|
|
126
|
+
model_content_group.add_argument("--assignments-blacklist", type=str, default=None,
|
|
127
|
+
help="Blacklist of assignments to ignore.")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
parser.add_argument("input_file", type=str, nargs='?', default=None,
|
|
131
|
+
help="Input file containing the model. If not provided, the model is read from the standard input.")
|
|
132
|
+
|
|
133
|
+
formatting_group = parser.add_argument_group('Formatting')
|
|
134
|
+
formatting_group.add_argument("-H", "--no-headrow", action='store_true',
|
|
135
|
+
default=False,
|
|
136
|
+
help="do not print the head row with the parameter names")
|
|
137
|
+
formatting_group.add_argument("--use-choice-names", action='store_true',
|
|
138
|
+
default=False,
|
|
139
|
+
help="When printing the output, do not print choice values but their full names.")
|
|
140
|
+
formatting_group.add_argument("-s", "--separator", type=str, default=',',
|
|
141
|
+
help="Separator to use in the output")
|
|
142
|
+
|
|
143
|
+
group = parser.add_argument_group('Generators', 'Define a generator to use')
|
|
144
|
+
generator_group = group.add_mutually_exclusive_group()
|
|
145
|
+
generator_group.add_argument("--cartesian", action='store_true',
|
|
146
|
+
help="generate cartesian product of input choices")
|
|
147
|
+
generator_group.add_argument("--random", action='store_true',
|
|
148
|
+
help="generate random test cases")
|
|
149
|
+
generator_group.add_argument("--nwise", action='store_true',
|
|
150
|
+
help="nwise generator (default)")
|
|
151
|
+
|
|
152
|
+
random_group = parser.add_argument_group('Random generator options:')
|
|
153
|
+
random_group.add_argument("-l", "--length", type=int, default=None,
|
|
154
|
+
help="length of the randomly generated suite. 0 for infinite")
|
|
155
|
+
random_group.add_argument("-D", "--duplicates", action='store_true', default=None,
|
|
156
|
+
help="""Allows test cases to repeat. If not set and the number of test cases is 0,
|
|
157
|
+
tests will be generated indifinitely""")
|
|
158
|
+
random_group.add_argument("--adaptive", action='store_true', default=None,
|
|
159
|
+
help="""Use adaptive techniques to generate tests that are far from
|
|
160
|
+
the ones already generated""")
|
|
161
|
+
|
|
162
|
+
nwise_group = parser.add_argument_group('Nwise generator options')
|
|
163
|
+
nwise_group.add_argument("-n", type=int, default=None, help="n in nwise. Default value: 2")
|
|
164
|
+
nwise_group.add_argument("-c", "--coverage", default=None,
|
|
165
|
+
help="""Required coverage. 100 means all test cases that are allowed
|
|
166
|
+
by constraints. Available for nwise and cartesian generators. Default vaule: 100""")
|
|
167
|
+
|
|
168
|
+
constraints_manipulation_group = parser.add_argument_group('Constraints manipulation:')
|
|
169
|
+
constraints_manipulation_group.add_argument("--ignore-constraints", action='store_true', default=False,
|
|
170
|
+
help="Ignore constraints when generating tests")
|
|
171
|
+
constraints_manipulation_group.add_argument("--ignore-assignments", action='store_true', default=False,
|
|
172
|
+
help="Ignore assignments when generating tests")
|
|
173
|
+
constraints_manipulation_group.add_argument("--negate-constraints", action='store_true', default=False,
|
|
174
|
+
help="Negate the aggregated constraint. This will mean that the generated tests will fail to satisfy at least one of the constraints.\
|
|
175
|
+
Negating implication constraint in fact means negating its invariant equivalence. So a constraint 'A => B' will be negated to 'A AND NOT B'.")
|
|
176
|
+
constraints_manipulation_group.add_argument("--invert-constraints", action='store_true', default=False,
|
|
177
|
+
help="Negate all individual constraints. This will mean that the generated tests will fail to satisfy all the defined constraints.")
|
|
178
|
+
|
|
179
|
+
args = parser.parse_args()
|
|
180
|
+
|
|
181
|
+
# Check if version is requested, then print and exit
|
|
182
|
+
if args.version:
|
|
183
|
+
print(f"tomato {version()}")
|
|
184
|
+
sys.exit(int(ec.SUCCESS))
|
|
185
|
+
|
|
186
|
+
# Set default generator
|
|
187
|
+
if not (args.random or args.nwise or args.cartesian):
|
|
188
|
+
args.nwise = True
|
|
189
|
+
|
|
190
|
+
# Check if random options are used correctly
|
|
191
|
+
if not args.random and (args.length is not None or args.duplicates is not None or args.adaptive is not None):
|
|
192
|
+
parser.error("--length, --duplicates and --no-adaptive can only be used with --random")
|
|
193
|
+
|
|
194
|
+
# Check if nwise options are used correctly
|
|
195
|
+
if not args.nwise and (args.n is not None or args.coverage is not None):
|
|
196
|
+
parser.error("-n and --coverage can only be used with --nwise")
|
|
197
|
+
|
|
198
|
+
# Check if input file is provided
|
|
199
|
+
if args.input_file is None:
|
|
200
|
+
sys.stderr.write("Reading model from stdin\n")
|
|
201
|
+
sys.stderr.flush()
|
|
202
|
+
|
|
203
|
+
# Check if whitelist and blacklist are used together
|
|
204
|
+
if args.whitelist is not None and args.blacklist is not None:
|
|
205
|
+
parser.error("--whitelist and --blacklist cannot be used together")
|
|
206
|
+
|
|
207
|
+
if args.input_whitelist is not None and (
|
|
208
|
+
args.input_blacklist is not None or args.parameters_blacklist is not None or args.choices_blacklist is not None):
|
|
209
|
+
parser.error("--input-whitelist and --input-blacklist/--parameters-blacklist/--choices-blacklist cannot be used together")
|
|
210
|
+
|
|
211
|
+
if args.input_blacklist is not None and (
|
|
212
|
+
args.input_whitelist is not None or args.parameters_whitelist is not None or args.choices_whitelist is not None):
|
|
213
|
+
parser.error("--input-whitelist and --input-blacklist/--parameters-blacklist/--choices-blacklist cannot be used together")
|
|
214
|
+
|
|
215
|
+
if args.parameters_whitelist is not None and (
|
|
216
|
+
args.parameters_blacklist is not None or args.input_blacklist is not None):
|
|
217
|
+
parser.error("--parameters-whitelist and --parameters-blacklist/--input-blacklist cannot be used together")
|
|
218
|
+
|
|
219
|
+
if args.parameters_blacklist is not None and (
|
|
220
|
+
args.parameters_whitelist is not None or args.input_whitelist is not None):
|
|
221
|
+
parser.error("--parameters-whitelist and --parameters-blacklist/--input-blacklist cannot be used together")
|
|
222
|
+
|
|
223
|
+
if args.choices_whitelist is not None and (
|
|
224
|
+
args.choices_blacklist is not None or args.input_blacklist is not None):
|
|
225
|
+
parser.error("--choices-whitelist and --choices-blacklist/--input-blacklist cannot be used together")
|
|
226
|
+
|
|
227
|
+
if args.choices_blacklist is not None and (
|
|
228
|
+
args.choices_whitelist is not None or args.input_whitelist is not None):
|
|
229
|
+
parser.error("--choices-whitelist and --choices-blacklist/--input-blacklist cannot be used together")
|
|
230
|
+
|
|
231
|
+
if args.logic_whitelist is not None and (args.logic_blacklist is not None or args.constraints_blacklist is not None or args.assignments_blacklist is not None):
|
|
232
|
+
parser.error("--logic-whitelist and --logic-blacklist/--constraints-blacklist/--assignments-blacklist cannot be used together")
|
|
233
|
+
|
|
234
|
+
if args.logic_blacklist is not None and (args.logic_whitelist is not None or args.constraints_whitelist is not None or args.assignments_whitelist is not None):
|
|
235
|
+
parser.error("--logic-whitelist and --logic-blacklist/--constraints-blacklist/--assignments-blacklist cannot be used together")
|
|
236
|
+
|
|
237
|
+
if args.constraints_whitelist is not None and (args.constraints_blacklist is not None or args.logic_blacklist is not None):
|
|
238
|
+
parser.error("--constraints-whitelist and --constraints-blacklist/--logic-blacklist cannot be used together")
|
|
239
|
+
|
|
240
|
+
if args.constraints_blacklist is not None and (args.constraints_whitelist is not None or args.logic_whitelist is not None):
|
|
241
|
+
parser.error("--constraints-whitelist and --constraints-blacklist/--logic-blacklist cannot be used together")
|
|
242
|
+
|
|
243
|
+
if args.assignments_whitelist is not None and (args.assignments_blacklist is not None or args.logic_blacklist is not None):
|
|
244
|
+
parser.error("--assignments-whitelist and --assignments-blacklist/--logic-blacklist cannot be used together")
|
|
245
|
+
|
|
246
|
+
if args.assignments_blacklist is not None and (args.assignments_whitelist is not None or args.logic_whitelist is not None):
|
|
247
|
+
parser.error("--assignments-whitelist and --assignments-blacklist/--logic-blacklist cannot be used together")
|
|
248
|
+
|
|
249
|
+
if args.ignore_constraints and (args.constraints_whitelist is not None or args.constraints_blacklist is not None):
|
|
250
|
+
parser.error("--ignore-constraints and --constraints-whitelist/--constraints-blacklist cannot be used together")
|
|
251
|
+
|
|
252
|
+
if args.ignore_assignments and (args.assignments_whitelist is not None or args.assignments_blacklist is not None):
|
|
253
|
+
parser.error("--ignore-assignments and --assignments-whitelist/--assignments-blacklist cannot be used together")
|
|
254
|
+
|
|
255
|
+
# Check if whitelist/blacklist is used with other whitelists/blacklists
|
|
256
|
+
if (args.whitelist is not None or args.blacklist is not None) and \
|
|
257
|
+
(args.input_whitelist is not None or
|
|
258
|
+
args.parameters_whitelist is not None or
|
|
259
|
+
args.choices_whitelist is not None or
|
|
260
|
+
args.logic_whitelist is not None or
|
|
261
|
+
args.constraints_whitelist is not None or
|
|
262
|
+
args.assignments_whitelist is not None or
|
|
263
|
+
args.ignore_constraints or
|
|
264
|
+
args.ignore_assignments or
|
|
265
|
+
args.input_blacklist is not None or
|
|
266
|
+
args.parameters_blacklist is not None or
|
|
267
|
+
args.choices_blacklist is not None or
|
|
268
|
+
args.logic_blacklist is not None or
|
|
269
|
+
args.constraints_blacklist is not None or
|
|
270
|
+
args.assignments_blacklist is not None):
|
|
271
|
+
|
|
272
|
+
parser.error("--whitelist/--blacklist cannot be used with other whitelists or blacklists")
|
|
273
|
+
|
|
274
|
+
if (args.exit_on_error or args.no_error_formatting or args.no_error_messages or args.duplicate_headrow) and ('validate_tests' not in args):
|
|
275
|
+
parser.error("--exit-on-error, --no-error-formatting, --no-error-messages, --duplicate-headrow can only be used with --validate-tests")
|
|
276
|
+
if args.no_headrow and args.duplicate_headrow:
|
|
277
|
+
parser.error("--no-headrow and --duplicate-headrow cannot be used together")
|
|
278
|
+
|
|
279
|
+
return args
|
|
280
|
+
|
|
281
|
+
def validate_tests(function, solver, args):
|
|
282
|
+
test_file = args.validate_tests
|
|
283
|
+
|
|
284
|
+
def validate(in_stream):
|
|
285
|
+
return validator.validate_test_stream(in_stream, function, solver,
|
|
286
|
+
separator=args.separator,
|
|
287
|
+
no_headrow=args.no_headrow,
|
|
288
|
+
use_choice_names=args.use_choice_names,
|
|
289
|
+
exit_on_error=args.exit_on_error,
|
|
290
|
+
no_error_formatting=args.no_error_formatting,
|
|
291
|
+
no_error_messages=args.no_error_messages,
|
|
292
|
+
duplicate_headrow=args.duplicate_headrow)
|
|
293
|
+
|
|
294
|
+
if test_file == None:
|
|
295
|
+
result = validate(sys.stdin)
|
|
296
|
+
else:
|
|
297
|
+
try:
|
|
298
|
+
with open(test_file, 'r', encoding='utf-8') as f:
|
|
299
|
+
result = validate(f)
|
|
300
|
+
except OSError as e:
|
|
301
|
+
print(f'Error: unable to open file {test_file}: {e}', file=sys.stderr)
|
|
302
|
+
sys.exit(int(ec.TEST_FILE_NOT_FOUND))
|
|
303
|
+
return result
|
|
304
|
+
|
|
305
|
+
def main():
|
|
306
|
+
args = parse_args()
|
|
307
|
+
|
|
308
|
+
function_name = args.function
|
|
309
|
+
|
|
310
|
+
whitelist = []
|
|
311
|
+
blacklist = []
|
|
312
|
+
parameters_blacklist = []
|
|
313
|
+
parameters_whitelist = []
|
|
314
|
+
choices_blacklist = []
|
|
315
|
+
choices_whitelist = []
|
|
316
|
+
constraints_blacklist = []
|
|
317
|
+
constraints_whitelist = []
|
|
318
|
+
assignments_blacklist = []
|
|
319
|
+
assignments_whitelist = []
|
|
320
|
+
|
|
321
|
+
# Parse filter lists
|
|
322
|
+
def parse_filter_list(list_str):
|
|
323
|
+
return [l.strip() for l in list_str.split(',')]
|
|
324
|
+
|
|
325
|
+
if args.whitelist is not None:
|
|
326
|
+
whitelist = parse_filter_list(args.whitelist)
|
|
327
|
+
if args.blacklist is not None:
|
|
328
|
+
blacklist = parse_filter_list(args.blacklist)
|
|
329
|
+
if args.input_whitelist is not None:
|
|
330
|
+
parameters_whitelist.extend(parse_filter_list(args.input_whitelist))
|
|
331
|
+
choices_whitelist.extend(parse_filter_list(args.input_whitelist))
|
|
332
|
+
if args.input_blacklist is not None:
|
|
333
|
+
parameters_blacklist.extend(parse_filter_list(args.input_blacklist))
|
|
334
|
+
choices_blacklist.extend(parse_filter_list(args.input_blacklist))
|
|
335
|
+
if args.parameters_whitelist is not None:
|
|
336
|
+
parameters_whitelist.extend(parse_filter_list(args.parameters_whitelist))
|
|
337
|
+
if args.parameters_blacklist is not None:
|
|
338
|
+
parameters_blacklist.extend(parse_filter_list(args.parameters_blacklist))
|
|
339
|
+
if args.choices_whitelist is not None:
|
|
340
|
+
choices_whitelist.extend(parse_filter_list(args.choices_whitelist))
|
|
341
|
+
if args.choices_blacklist is not None:
|
|
342
|
+
choices_blacklist.extend(parse_filter_list(args.choices_blacklist))
|
|
343
|
+
if args.logic_whitelist is not None:
|
|
344
|
+
constraints_whitelist.extend(parse_filter_list(args.logic_whitelist))
|
|
345
|
+
assignments_whitelist.extend(parse_filter_list(args.logic_whitelist))
|
|
346
|
+
if args.logic_blacklist is not None:
|
|
347
|
+
constraints_blacklist.extend(parse_filter_list(args.logic_blacklist))
|
|
348
|
+
assignments_blacklist.extend(parse_filter_list(args.logic_blacklist))
|
|
349
|
+
if args.constraints_whitelist is not None:
|
|
350
|
+
constraints_whitelist.extend(parse_filter_list(args.constraints_whitelist))
|
|
351
|
+
if args.constraints_blacklist is not None:
|
|
352
|
+
constraints_blacklist.extend(parse_filter_list(args.constraints_blacklist))
|
|
353
|
+
if args.assignments_whitelist is not None:
|
|
354
|
+
assignments_whitelist.extend(parse_filter_list(args.assignments_whitelist))
|
|
355
|
+
if args.assignments_blacklist is not None:
|
|
356
|
+
assignments_blacklist.extend(parse_filter_list(args.assignments_blacklist))
|
|
357
|
+
|
|
358
|
+
input_file = args.input_file
|
|
359
|
+
|
|
360
|
+
parsed_yaml_model = None
|
|
361
|
+
if input_file is not None:
|
|
362
|
+
try:
|
|
363
|
+
with open(input_file, 'r', encoding='utf-8') as f:
|
|
364
|
+
parsed_yaml_model = yaml.safe_load(f.read())
|
|
365
|
+
except OSError as e:
|
|
366
|
+
print(f'Error: unable to open file {input_file}: {e}', file=sys.stderr)
|
|
367
|
+
sys.exit(int(ec.MODEL_FILE_NOT_FOUND))
|
|
368
|
+
else:
|
|
369
|
+
try:
|
|
370
|
+
yaml_lines = []
|
|
371
|
+
#read from stdin until the end of input, or a line that starts with '----'
|
|
372
|
+
for line in sys.stdin:
|
|
373
|
+
if line.startswith('----'):
|
|
374
|
+
break
|
|
375
|
+
yaml_lines.append(line)
|
|
376
|
+
yaml_content = ''.join(yaml_lines)
|
|
377
|
+
parsed_yaml_model = yaml.safe_load(StringIO(yaml_content))
|
|
378
|
+
except yaml.YAMLError as e:
|
|
379
|
+
print(f"YAML parsing error: {e}", file=sys.stderr)
|
|
380
|
+
sys.exit(int(ec.YAML_PARSING_ERROR))
|
|
381
|
+
|
|
382
|
+
# Parse the model
|
|
383
|
+
function = model.parse_function(parsed_yaml_model, function_name,
|
|
384
|
+
ignore_constraints=args.ignore_constraints,
|
|
385
|
+
ignore_assignments=args.ignore_assignments,
|
|
386
|
+
whitelist=whitelist,
|
|
387
|
+
blacklist=blacklist,
|
|
388
|
+
parameters_whitelist=parameters_whitelist,
|
|
389
|
+
parameters_blacklist=parameters_blacklist,
|
|
390
|
+
choices_whitelist=choices_whitelist,
|
|
391
|
+
choices_blacklist=choices_blacklist,
|
|
392
|
+
constraints_whitelist=constraints_whitelist,
|
|
393
|
+
constraints_blacklist=constraints_blacklist,
|
|
394
|
+
assignments_whitelist=assignments_whitelist,
|
|
395
|
+
assignments_blacklist=assignments_blacklist)
|
|
396
|
+
|
|
397
|
+
# set the solver
|
|
398
|
+
solver = TomatoSolver(function,
|
|
399
|
+
negate_constraints=args.negate_constraints,
|
|
400
|
+
invert_constraints=args.invert_constraints)
|
|
401
|
+
|
|
402
|
+
if 'validate_tests' in args:
|
|
403
|
+
result = validate_tests(function, solver, args)
|
|
404
|
+
if not result:
|
|
405
|
+
sys.exit(int(ec.TEST_PARSING_ERROR))
|
|
406
|
+
else:
|
|
407
|
+
sys.exit(int(ec.SUCCESS))
|
|
408
|
+
|
|
409
|
+
# select the nwise generator
|
|
410
|
+
if args.nwise:
|
|
411
|
+
n = args.n
|
|
412
|
+
if n is None:
|
|
413
|
+
n = 2
|
|
414
|
+
if args.coverage is None:
|
|
415
|
+
args.coverage = 100
|
|
416
|
+
coverage = int(args.coverage)
|
|
417
|
+
|
|
418
|
+
gen = generator.nwise(function, solver, n, coverage=coverage)
|
|
419
|
+
|
|
420
|
+
# select the cartesian generator
|
|
421
|
+
if args.cartesian:
|
|
422
|
+
gen = generator.cartesian(function, solver)
|
|
423
|
+
|
|
424
|
+
# select the random generator
|
|
425
|
+
if args.random:
|
|
426
|
+
length = args.length
|
|
427
|
+
duplicates = args.duplicates
|
|
428
|
+
adaptive = args.adaptive
|
|
429
|
+
if length is None:
|
|
430
|
+
length = 0
|
|
431
|
+
if duplicates is None:
|
|
432
|
+
duplicates = False
|
|
433
|
+
if adaptive is None:
|
|
434
|
+
adaptive = False
|
|
435
|
+
|
|
436
|
+
gen = generator.random(function,
|
|
437
|
+
solver,
|
|
438
|
+
length=length,
|
|
439
|
+
duplicates=duplicates,
|
|
440
|
+
adaptive=adaptive)
|
|
441
|
+
|
|
442
|
+
sep = args.separator
|
|
443
|
+
|
|
444
|
+
# Set demo level
|
|
445
|
+
if args.demo_level > 0:
|
|
446
|
+
generator.show_progress = True
|
|
447
|
+
if args.demo_level == 2:
|
|
448
|
+
generator.step_delay = 0.05
|
|
449
|
+
if args.demo_level == 3:
|
|
450
|
+
generator.step_delay = 0.2
|
|
451
|
+
|
|
452
|
+
writer = csv.writer(sys.stdout, delimiter=sep)
|
|
453
|
+
|
|
454
|
+
if not args.no_headrow:
|
|
455
|
+
writer.writerow(function.get_parameter_names())
|
|
456
|
+
#generate test cases with the selected generator
|
|
457
|
+
for test in gen:
|
|
458
|
+
if not args.use_choice_names:
|
|
459
|
+
parameter_names = function.get_parameter_names()
|
|
460
|
+
for i, name in enumerate(parameter_names):
|
|
461
|
+
if name in [p.name for p in function.output_parameters]:
|
|
462
|
+
continue
|
|
463
|
+
parameter = function.get_parameter(name)
|
|
464
|
+
choice_name = test[i]
|
|
465
|
+
choice = parameter.get_choice(choice_name)
|
|
466
|
+
try:
|
|
467
|
+
test[i] = choice.value
|
|
468
|
+
except AttributeError:
|
|
469
|
+
raise ValueError(f"Choice '{choice_name}' not found in parameter {name}")
|
|
470
|
+
writer.writerow(test)
|
|
471
|
+
|
|
472
|
+
if __name__ == '__main__':
|
|
473
|
+
main()
|