xref: /minix3/external/bsd/llvm/dist/clang/utils/FuzzTest (revision f4a2713ac843a11c696ec80c0a5e3e5d80b4d338)
1*f4a2713aSLionel Sambuc#!/usr/bin/env python
2*f4a2713aSLionel Sambuc
3*f4a2713aSLionel Sambuc"""
4*f4a2713aSLionel SambucThis is a generic fuzz testing tool, see --help for more information.
5*f4a2713aSLionel Sambuc"""
6*f4a2713aSLionel Sambuc
7*f4a2713aSLionel Sambucimport os
8*f4a2713aSLionel Sambucimport sys
9*f4a2713aSLionel Sambucimport random
10*f4a2713aSLionel Sambucimport subprocess
11*f4a2713aSLionel Sambucimport itertools
12*f4a2713aSLionel Sambuc
13*f4a2713aSLionel Sambucclass TestGenerator:
14*f4a2713aSLionel Sambuc    def __init__(self, inputs, delete, insert, replace,
15*f4a2713aSLionel Sambuc                 insert_strings, pick_input):
16*f4a2713aSLionel Sambuc        self.inputs = [(s, open(s).read()) for s in inputs]
17*f4a2713aSLionel Sambuc
18*f4a2713aSLionel Sambuc        self.delete = bool(delete)
19*f4a2713aSLionel Sambuc        self.insert = bool(insert)
20*f4a2713aSLionel Sambuc        self.replace = bool(replace)
21*f4a2713aSLionel Sambuc        self.pick_input = bool(pick_input)
22*f4a2713aSLionel Sambuc        self.insert_strings = list(insert_strings)
23*f4a2713aSLionel Sambuc
24*f4a2713aSLionel Sambuc        self.num_positions = sum([len(d) for _,d in self.inputs])
25*f4a2713aSLionel Sambuc        self.num_insert_strings = len(insert_strings)
26*f4a2713aSLionel Sambuc        self.num_tests = ((delete + (insert + replace)*self.num_insert_strings)
27*f4a2713aSLionel Sambuc                          * self.num_positions)
28*f4a2713aSLionel Sambuc        self.num_tests += 1
29*f4a2713aSLionel Sambuc
30*f4a2713aSLionel Sambuc        if self.pick_input:
31*f4a2713aSLionel Sambuc            self.num_tests *= self.num_positions
32*f4a2713aSLionel Sambuc
33*f4a2713aSLionel Sambuc    def position_to_source_index(self, position):
34*f4a2713aSLionel Sambuc        for i,(s,d) in enumerate(self.inputs):
35*f4a2713aSLionel Sambuc            n = len(d)
36*f4a2713aSLionel Sambuc            if position < n:
37*f4a2713aSLionel Sambuc                return (i,position)
38*f4a2713aSLionel Sambuc            position -= n
39*f4a2713aSLionel Sambuc        raise ValueError,'Invalid position.'
40*f4a2713aSLionel Sambuc
41*f4a2713aSLionel Sambuc    def get_test(self, index):
42*f4a2713aSLionel Sambuc        assert 0 <= index < self.num_tests
43*f4a2713aSLionel Sambuc
44*f4a2713aSLionel Sambuc        picked_position = None
45*f4a2713aSLionel Sambuc        if self.pick_input:
46*f4a2713aSLionel Sambuc            index,picked_position = divmod(index, self.num_positions)
47*f4a2713aSLionel Sambuc            picked_position = self.position_to_source_index(picked_position)
48*f4a2713aSLionel Sambuc
49*f4a2713aSLionel Sambuc        if index == 0:
50*f4a2713aSLionel Sambuc            return ('nothing', None, None, picked_position)
51*f4a2713aSLionel Sambuc
52*f4a2713aSLionel Sambuc        index -= 1
53*f4a2713aSLionel Sambuc        index,position = divmod(index, self.num_positions)
54*f4a2713aSLionel Sambuc        position = self.position_to_source_index(position)
55*f4a2713aSLionel Sambuc        if self.delete:
56*f4a2713aSLionel Sambuc            if index == 0:
57*f4a2713aSLionel Sambuc                return ('delete', position, None, picked_position)
58*f4a2713aSLionel Sambuc            index -= 1
59*f4a2713aSLionel Sambuc
60*f4a2713aSLionel Sambuc        index,insert_index = divmod(index, self.num_insert_strings)
61*f4a2713aSLionel Sambuc        insert_str = self.insert_strings[insert_index]
62*f4a2713aSLionel Sambuc        if self.insert:
63*f4a2713aSLionel Sambuc            if index == 0:
64*f4a2713aSLionel Sambuc                return ('insert', position, insert_str, picked_position)
65*f4a2713aSLionel Sambuc            index -= 1
66*f4a2713aSLionel Sambuc
67*f4a2713aSLionel Sambuc        assert self.replace
68*f4a2713aSLionel Sambuc        assert index == 0
69*f4a2713aSLionel Sambuc        return ('replace', position, insert_str, picked_position)
70*f4a2713aSLionel Sambuc
71*f4a2713aSLionel Sambucclass TestApplication:
72*f4a2713aSLionel Sambuc    def __init__(self, tg, test):
73*f4a2713aSLionel Sambuc        self.tg = tg
74*f4a2713aSLionel Sambuc        self.test = test
75*f4a2713aSLionel Sambuc
76*f4a2713aSLionel Sambuc    def apply(self):
77*f4a2713aSLionel Sambuc        if self.test[0] == 'nothing':
78*f4a2713aSLionel Sambuc            pass
79*f4a2713aSLionel Sambuc        else:
80*f4a2713aSLionel Sambuc            i,j = self.test[1]
81*f4a2713aSLionel Sambuc            name,data = self.tg.inputs[i]
82*f4a2713aSLionel Sambuc            if self.test[0] == 'delete':
83*f4a2713aSLionel Sambuc                data = data[:j] + data[j+1:]
84*f4a2713aSLionel Sambuc            elif self.test[0] == 'insert':
85*f4a2713aSLionel Sambuc                data = data[:j] + self.test[2] + data[j:]
86*f4a2713aSLionel Sambuc            elif self.test[0] == 'replace':
87*f4a2713aSLionel Sambuc                data = data[:j] + self.test[2] + data[j+1:]
88*f4a2713aSLionel Sambuc            else:
89*f4a2713aSLionel Sambuc                raise ValueError,'Invalid test %r' % self.test
90*f4a2713aSLionel Sambuc            open(name,'wb').write(data)
91*f4a2713aSLionel Sambuc
92*f4a2713aSLionel Sambuc    def revert(self):
93*f4a2713aSLionel Sambuc        if self.test[0] != 'nothing':
94*f4a2713aSLionel Sambuc            i,j = self.test[1]
95*f4a2713aSLionel Sambuc            name,data = self.tg.inputs[i]
96*f4a2713aSLionel Sambuc            open(name,'wb').write(data)
97*f4a2713aSLionel Sambuc
98*f4a2713aSLionel Sambucdef quote(str):
99*f4a2713aSLionel Sambuc    return '"' + str + '"'
100*f4a2713aSLionel Sambuc
101*f4a2713aSLionel Sambucdef run_one_test(test_application, index, input_files, args):
102*f4a2713aSLionel Sambuc    test = test_application.test
103*f4a2713aSLionel Sambuc
104*f4a2713aSLionel Sambuc    # Interpolate arguments.
105*f4a2713aSLionel Sambuc    options = { 'index' : index,
106*f4a2713aSLionel Sambuc                'inputs' : ' '.join(quote(f) for f in input_files) }
107*f4a2713aSLionel Sambuc
108*f4a2713aSLionel Sambuc    # Add picked input interpolation arguments, if used.
109*f4a2713aSLionel Sambuc    if test[3] is not None:
110*f4a2713aSLionel Sambuc        pos = test[3][1]
111*f4a2713aSLionel Sambuc        options['picked_input'] = input_files[test[3][0]]
112*f4a2713aSLionel Sambuc        options['picked_input_pos'] = pos
113*f4a2713aSLionel Sambuc        # Compute the line and column.
114*f4a2713aSLionel Sambuc        file_data = test_application.tg.inputs[test[3][0]][1]
115*f4a2713aSLionel Sambuc        line = column = 1
116*f4a2713aSLionel Sambuc        for i in range(pos):
117*f4a2713aSLionel Sambuc            c = file_data[i]
118*f4a2713aSLionel Sambuc            if c == '\n':
119*f4a2713aSLionel Sambuc                line += 1
120*f4a2713aSLionel Sambuc                column = 1
121*f4a2713aSLionel Sambuc            else:
122*f4a2713aSLionel Sambuc                column += 1
123*f4a2713aSLionel Sambuc        options['picked_input_line'] = line
124*f4a2713aSLionel Sambuc        options['picked_input_col'] = column
125*f4a2713aSLionel Sambuc
126*f4a2713aSLionel Sambuc    test_args = [a % options for a in args]
127*f4a2713aSLionel Sambuc    if opts.verbose:
128*f4a2713aSLionel Sambuc        print '%s: note: executing %r' % (sys.argv[0], test_args)
129*f4a2713aSLionel Sambuc
130*f4a2713aSLionel Sambuc    stdout = None
131*f4a2713aSLionel Sambuc    stderr = None
132*f4a2713aSLionel Sambuc    if opts.log_dir:
133*f4a2713aSLionel Sambuc        stdout_log_path = os.path.join(opts.log_dir, '%s.out' % index)
134*f4a2713aSLionel Sambuc        stderr_log_path = os.path.join(opts.log_dir, '%s.err' % index)
135*f4a2713aSLionel Sambuc        stdout = open(stdout_log_path, 'wb')
136*f4a2713aSLionel Sambuc        stderr = open(stderr_log_path, 'wb')
137*f4a2713aSLionel Sambuc    else:
138*f4a2713aSLionel Sambuc        sys.stdout.flush()
139*f4a2713aSLionel Sambuc    p = subprocess.Popen(test_args, stdout=stdout, stderr=stderr)
140*f4a2713aSLionel Sambuc    p.communicate()
141*f4a2713aSLionel Sambuc    exit_code = p.wait()
142*f4a2713aSLionel Sambuc
143*f4a2713aSLionel Sambuc    test_result = (exit_code == opts.expected_exit_code or
144*f4a2713aSLionel Sambuc                   exit_code in opts.extra_exit_codes)
145*f4a2713aSLionel Sambuc
146*f4a2713aSLionel Sambuc    if stdout is not None:
147*f4a2713aSLionel Sambuc        stdout.close()
148*f4a2713aSLionel Sambuc        stderr.close()
149*f4a2713aSLionel Sambuc
150*f4a2713aSLionel Sambuc        # Remove the logs for passes, unless logging all results.
151*f4a2713aSLionel Sambuc        if not opts.log_all and test_result:
152*f4a2713aSLionel Sambuc            os.remove(stdout_log_path)
153*f4a2713aSLionel Sambuc            os.remove(stderr_log_path)
154*f4a2713aSLionel Sambuc
155*f4a2713aSLionel Sambuc    if not test_result:
156*f4a2713aSLionel Sambuc        print 'FAIL: %d' % index
157*f4a2713aSLionel Sambuc    elif not opts.succinct:
158*f4a2713aSLionel Sambuc        print 'PASS: %d' % index
159*f4a2713aSLionel Sambuc    return test_result
160*f4a2713aSLionel Sambuc
161*f4a2713aSLionel Sambucdef main():
162*f4a2713aSLionel Sambuc    global opts
163*f4a2713aSLionel Sambuc    from optparse import OptionParser, OptionGroup
164*f4a2713aSLionel Sambuc    parser = OptionParser("""%prog [options] ... test command args ...
165*f4a2713aSLionel Sambuc
166*f4a2713aSLionel Sambuc%prog is a tool for fuzzing inputs and testing them.
167*f4a2713aSLionel Sambuc
168*f4a2713aSLionel SambucThe most basic usage is something like:
169*f4a2713aSLionel Sambuc
170*f4a2713aSLionel Sambuc  $ %prog --file foo.txt ./test.sh
171*f4a2713aSLionel Sambuc
172*f4a2713aSLionel Sambucwhich will run a default list of fuzzing strategies on the input. For each
173*f4a2713aSLionel Sambucfuzzed input, it will overwrite the input files (in place), run the test script,
174*f4a2713aSLionel Sambucthen restore the files back to their original contents.
175*f4a2713aSLionel Sambuc
176*f4a2713aSLionel SambucNOTE: You should make sure you have a backup copy of your inputs, in case
177*f4a2713aSLionel Sambucsomething goes wrong!!!
178*f4a2713aSLionel Sambuc
179*f4a2713aSLionel SambucYou can cause the fuzzing to not restore the original files with
180*f4a2713aSLionel Sambuc'--no-revert'. Generally this is used with '--test <index>' to run one failing
181*f4a2713aSLionel Sambuctest and then leave the fuzzed inputs in place to examine the failure.
182*f4a2713aSLionel Sambuc
183*f4a2713aSLionel SambucFor each fuzzed input, %prog will run the test command given on the command
184*f4a2713aSLionel Sambucline. Each argument in the command is subject to string interpolation before
185*f4a2713aSLionel Sambucbeing executed. The syntax is "%(VARIABLE)FORMAT" where FORMAT is a standard
186*f4a2713aSLionel Sambucprintf format, and VARIABLE is one of:
187*f4a2713aSLionel Sambuc
188*f4a2713aSLionel Sambuc  'index' - the test index being run
189*f4a2713aSLionel Sambuc  'inputs' - the full list of test inputs
190*f4a2713aSLionel Sambuc  'picked_input'      - (with --pick-input) the selected input file
191*f4a2713aSLionel Sambuc  'picked_input_pos'  - (with --pick-input) the selected input position
192*f4a2713aSLionel Sambuc  'picked_input_line' - (with --pick-input) the selected input line
193*f4a2713aSLionel Sambuc  'picked_input_col'  - (with --pick-input) the selected input column
194*f4a2713aSLionel Sambuc
195*f4a2713aSLionel SambucBy default, the script will run forever continually picking new tests to
196*f4a2713aSLionel Sambucrun. You can limit the number of tests that are run with '--max-tests <number>',
197*f4a2713aSLionel Sambucand you can run a particular test with '--test <index>'.
198*f4a2713aSLionel Sambuc
199*f4a2713aSLionel SambucYou can specify '--stop-on-fail' to stop the script on the first failure
200*f4a2713aSLionel Sambucwithout reverting the changes.
201*f4a2713aSLionel Sambuc
202*f4a2713aSLionel Sambuc""")
203*f4a2713aSLionel Sambuc    parser.add_option("-v", "--verbose", help="Show more output",
204*f4a2713aSLionel Sambuc                      action='store_true', dest="verbose", default=False)
205*f4a2713aSLionel Sambuc    parser.add_option("-s", "--succinct",  help="Reduce amount of output",
206*f4a2713aSLionel Sambuc                      action="store_true", dest="succinct", default=False)
207*f4a2713aSLionel Sambuc
208*f4a2713aSLionel Sambuc    group = OptionGroup(parser, "Test Execution")
209*f4a2713aSLionel Sambuc    group.add_option("", "--expected-exit-code", help="Set expected exit code",
210*f4a2713aSLionel Sambuc                     type=int, dest="expected_exit_code",
211*f4a2713aSLionel Sambuc                     default=0)
212*f4a2713aSLionel Sambuc    group.add_option("", "--extra-exit-code",
213*f4a2713aSLionel Sambuc                     help="Set additional expected exit code",
214*f4a2713aSLionel Sambuc                     type=int, action="append", dest="extra_exit_codes",
215*f4a2713aSLionel Sambuc                     default=[])
216*f4a2713aSLionel Sambuc    group.add_option("", "--log-dir",
217*f4a2713aSLionel Sambuc                     help="Capture test logs to an output directory",
218*f4a2713aSLionel Sambuc                     type=str, dest="log_dir",
219*f4a2713aSLionel Sambuc                     default=None)
220*f4a2713aSLionel Sambuc    group.add_option("", "--log-all",
221*f4a2713aSLionel Sambuc                     help="Log all outputs (not just failures)",
222*f4a2713aSLionel Sambuc                     action="store_true", dest="log_all", default=False)
223*f4a2713aSLionel Sambuc    parser.add_option_group(group)
224*f4a2713aSLionel Sambuc
225*f4a2713aSLionel Sambuc    group = OptionGroup(parser, "Input Files")
226*f4a2713aSLionel Sambuc    group.add_option("", "--file", metavar="PATH",
227*f4a2713aSLionel Sambuc                     help="Add an input file to fuzz",
228*f4a2713aSLionel Sambuc                     type=str, action="append", dest="input_files", default=[])
229*f4a2713aSLionel Sambuc    group.add_option("", "--filelist", metavar="LIST",
230*f4a2713aSLionel Sambuc                     help="Add a list of inputs files to fuzz (one per line)",
231*f4a2713aSLionel Sambuc                     type=str, action="append", dest="filelists", default=[])
232*f4a2713aSLionel Sambuc    parser.add_option_group(group)
233*f4a2713aSLionel Sambuc
234*f4a2713aSLionel Sambuc    group = OptionGroup(parser, "Fuzz Options")
235*f4a2713aSLionel Sambuc    group.add_option("", "--replacement-chars", dest="replacement_chars",
236*f4a2713aSLionel Sambuc                     help="Characters to insert/replace",
237*f4a2713aSLionel Sambuc                     default="0{}[]<>\;@#$^%& ")
238*f4a2713aSLionel Sambuc    group.add_option("", "--replacement-string", dest="replacement_strings",
239*f4a2713aSLionel Sambuc                     action="append", help="Add a replacement string to use",
240*f4a2713aSLionel Sambuc                     default=[])
241*f4a2713aSLionel Sambuc    group.add_option("", "--replacement-list", dest="replacement_lists",
242*f4a2713aSLionel Sambuc                     help="Add a list of replacement strings (one per line)",
243*f4a2713aSLionel Sambuc                     action="append", default=[])
244*f4a2713aSLionel Sambuc    group.add_option("", "--no-delete", help="Don't delete characters",
245*f4a2713aSLionel Sambuc                     action='store_false', dest="enable_delete", default=True)
246*f4a2713aSLionel Sambuc    group.add_option("", "--no-insert", help="Don't insert strings",
247*f4a2713aSLionel Sambuc                     action='store_false', dest="enable_insert", default=True)
248*f4a2713aSLionel Sambuc    group.add_option("", "--no-replace", help="Don't replace strings",
249*f4a2713aSLionel Sambuc                     action='store_false', dest="enable_replace", default=True)
250*f4a2713aSLionel Sambuc    group.add_option("", "--no-revert", help="Don't revert changes",
251*f4a2713aSLionel Sambuc                     action='store_false', dest="revert", default=True)
252*f4a2713aSLionel Sambuc    group.add_option("", "--stop-on-fail", help="Stop on first failure",
253*f4a2713aSLionel Sambuc                     action='store_true', dest="stop_on_fail", default=False)
254*f4a2713aSLionel Sambuc    parser.add_option_group(group)
255*f4a2713aSLionel Sambuc
256*f4a2713aSLionel Sambuc    group = OptionGroup(parser, "Test Selection")
257*f4a2713aSLionel Sambuc    group.add_option("", "--test", help="Run a particular test",
258*f4a2713aSLionel Sambuc                     type=int, dest="test", default=None, metavar="INDEX")
259*f4a2713aSLionel Sambuc    group.add_option("", "--max-tests", help="Maximum number of tests",
260*f4a2713aSLionel Sambuc                     type=int, dest="max_tests", default=None, metavar="COUNT")
261*f4a2713aSLionel Sambuc    group.add_option("", "--pick-input",
262*f4a2713aSLionel Sambuc                     help="Randomly select an input byte as well as fuzzing",
263*f4a2713aSLionel Sambuc                     action='store_true', dest="pick_input", default=False)
264*f4a2713aSLionel Sambuc    parser.add_option_group(group)
265*f4a2713aSLionel Sambuc
266*f4a2713aSLionel Sambuc    parser.disable_interspersed_args()
267*f4a2713aSLionel Sambuc
268*f4a2713aSLionel Sambuc    (opts, args) = parser.parse_args()
269*f4a2713aSLionel Sambuc
270*f4a2713aSLionel Sambuc    if not args:
271*f4a2713aSLionel Sambuc        parser.error("Invalid number of arguments")
272*f4a2713aSLionel Sambuc
273*f4a2713aSLionel Sambuc    # Collect the list of inputs.
274*f4a2713aSLionel Sambuc    input_files = list(opts.input_files)
275*f4a2713aSLionel Sambuc    for filelist in opts.filelists:
276*f4a2713aSLionel Sambuc        f = open(filelist)
277*f4a2713aSLionel Sambuc        try:
278*f4a2713aSLionel Sambuc            for ln in f:
279*f4a2713aSLionel Sambuc                ln = ln.strip()
280*f4a2713aSLionel Sambuc                if ln:
281*f4a2713aSLionel Sambuc                    input_files.append(ln)
282*f4a2713aSLionel Sambuc        finally:
283*f4a2713aSLionel Sambuc            f.close()
284*f4a2713aSLionel Sambuc    input_files.sort()
285*f4a2713aSLionel Sambuc
286*f4a2713aSLionel Sambuc    if not input_files:
287*f4a2713aSLionel Sambuc        parser.error("No input files!")
288*f4a2713aSLionel Sambuc
289*f4a2713aSLionel Sambuc    print '%s: note: fuzzing %d files.' % (sys.argv[0], len(input_files))
290*f4a2713aSLionel Sambuc
291*f4a2713aSLionel Sambuc    # Make sure the log directory exists if used.
292*f4a2713aSLionel Sambuc    if opts.log_dir:
293*f4a2713aSLionel Sambuc        if not os.path.exists(opts.log_dir):
294*f4a2713aSLionel Sambuc            try:
295*f4a2713aSLionel Sambuc                os.mkdir(opts.log_dir)
296*f4a2713aSLionel Sambuc            except OSError:
297*f4a2713aSLionel Sambuc                print "%s: error: log directory couldn't be created!" % (
298*f4a2713aSLionel Sambuc                    sys.argv[0],)
299*f4a2713aSLionel Sambuc                raise SystemExit,1
300*f4a2713aSLionel Sambuc
301*f4a2713aSLionel Sambuc    # Get the list if insert/replacement strings.
302*f4a2713aSLionel Sambuc    replacements = list(opts.replacement_chars)
303*f4a2713aSLionel Sambuc    replacements.extend(opts.replacement_strings)
304*f4a2713aSLionel Sambuc    for replacement_list in opts.replacement_lists:
305*f4a2713aSLionel Sambuc        f = open(replacement_list)
306*f4a2713aSLionel Sambuc        try:
307*f4a2713aSLionel Sambuc            for ln in f:
308*f4a2713aSLionel Sambuc                ln = ln[:-1]
309*f4a2713aSLionel Sambuc                if ln:
310*f4a2713aSLionel Sambuc                    replacements.append(ln)
311*f4a2713aSLionel Sambuc        finally:
312*f4a2713aSLionel Sambuc            f.close()
313*f4a2713aSLionel Sambuc
314*f4a2713aSLionel Sambuc    # Unique and order the replacement list.
315*f4a2713aSLionel Sambuc    replacements = list(set(replacements))
316*f4a2713aSLionel Sambuc    replacements.sort()
317*f4a2713aSLionel Sambuc
318*f4a2713aSLionel Sambuc    # Create the test generator.
319*f4a2713aSLionel Sambuc    tg = TestGenerator(input_files, opts.enable_delete, opts.enable_insert,
320*f4a2713aSLionel Sambuc                       opts.enable_replace, replacements, opts.pick_input)
321*f4a2713aSLionel Sambuc
322*f4a2713aSLionel Sambuc    print '%s: note: %d input bytes.' % (sys.argv[0], tg.num_positions)
323*f4a2713aSLionel Sambuc    print '%s: note: %d total tests.' % (sys.argv[0], tg.num_tests)
324*f4a2713aSLionel Sambuc    if opts.test is not None:
325*f4a2713aSLionel Sambuc        it = [opts.test]
326*f4a2713aSLionel Sambuc    elif opts.max_tests is not None:
327*f4a2713aSLionel Sambuc        it = itertools.imap(random.randrange,
328*f4a2713aSLionel Sambuc                            itertools.repeat(tg.num_tests, opts.max_tests))
329*f4a2713aSLionel Sambuc    else:
330*f4a2713aSLionel Sambuc        it = itertools.imap(random.randrange, itertools.repeat(tg.num_tests))
331*f4a2713aSLionel Sambuc    for test in it:
332*f4a2713aSLionel Sambuc        t = tg.get_test(test)
333*f4a2713aSLionel Sambuc
334*f4a2713aSLionel Sambuc        if opts.verbose:
335*f4a2713aSLionel Sambuc            print '%s: note: running test %d: %r' % (sys.argv[0], test, t)
336*f4a2713aSLionel Sambuc        ta = TestApplication(tg, t)
337*f4a2713aSLionel Sambuc        try:
338*f4a2713aSLionel Sambuc            ta.apply()
339*f4a2713aSLionel Sambuc            test_result = run_one_test(ta, test, input_files, args)
340*f4a2713aSLionel Sambuc            if not test_result and opts.stop_on_fail:
341*f4a2713aSLionel Sambuc                opts.revert = False
342*f4a2713aSLionel Sambuc                sys.exit(1)
343*f4a2713aSLionel Sambuc        finally:
344*f4a2713aSLionel Sambuc            if opts.revert:
345*f4a2713aSLionel Sambuc                ta.revert()
346*f4a2713aSLionel Sambuc
347*f4a2713aSLionel Sambuc        sys.stdout.flush()
348*f4a2713aSLionel Sambuc
349*f4a2713aSLionel Sambucif __name__ == '__main__':
350*f4a2713aSLionel Sambuc    main()
351