xref: /llvm-project/llvm/utils/update_mc_test_checks.py (revision 6f973fd4ab18ff58689e83383190ed4767c2a7dd)
12b892b05SBrox Chen#!/usr/bin/env python3
22b892b05SBrox Chen"""
32b892b05SBrox ChenA test update script.  This script is a utility to update LLVM 'llvm-mc' based test cases with new FileCheck patterns.
42b892b05SBrox Chen"""
52b892b05SBrox Chen
62b892b05SBrox Chenfrom __future__ import print_function
72b892b05SBrox Chen
82b892b05SBrox Chenimport argparse
9528e975aSBrox Chenimport functools
102b892b05SBrox Chenimport os  # Used to advertise this file's name ("autogenerated_note").
112b892b05SBrox Chen
122b892b05SBrox Chenfrom UpdateTestChecks import common
132b892b05SBrox Chen
142b892b05SBrox Chenimport subprocess
152b892b05SBrox Chenimport re
162b892b05SBrox Chen
172b892b05SBrox Chenmc_LIKE_TOOLS = [
182b892b05SBrox Chen    "llvm-mc",
192b892b05SBrox Chen]
202b892b05SBrox ChenERROR_RE = re.compile(r":\d+: (warning|error): .*")
212b892b05SBrox ChenERROR_CHECK_RE = re.compile(r"# COM: .*")
222b892b05SBrox ChenOUTPUT_SKIPPED_RE = re.compile(r"(.text)")
232b892b05SBrox ChenCOMMENT = {"asm": "//", "dasm": "#"}
242b892b05SBrox Chen
252b892b05SBrox Chen
26*6f973fd4SBrox Chendef invoke_tool(exe, check_rc, cmd_args, testline, verbose=False):
272b892b05SBrox Chen    if isinstance(cmd_args, list):
282b892b05SBrox Chen        args = [applySubstitutions(a, substitutions) for a in cmd_args]
292b892b05SBrox Chen    else:
302b892b05SBrox Chen        args = cmd_args
312b892b05SBrox Chen
322b892b05SBrox Chen    cmd = 'echo "' + testline + '" | ' + exe + " " + args
332b892b05SBrox Chen    if verbose:
342b892b05SBrox Chen        print("Command: ", cmd)
35*6f973fd4SBrox Chen
36*6f973fd4SBrox Chen    out = subprocess.run(
37*6f973fd4SBrox Chen        cmd,
38*6f973fd4SBrox Chen        shell=True,
39*6f973fd4SBrox Chen        check=check_rc,
40*6f973fd4SBrox Chen        stdout=subprocess.PIPE,
41*6f973fd4SBrox Chen        stderr=subprocess.DEVNULL,
42*6f973fd4SBrox Chen    ).stdout
43*6f973fd4SBrox Chen
442b892b05SBrox Chen    # Fix line endings to unix CR style.
452b892b05SBrox Chen    return out.decode().replace("\r\n", "\n")
462b892b05SBrox Chen
472b892b05SBrox Chen
482b892b05SBrox Chen# create tests line-by-line, here we just filter out the check lines and comments
492b892b05SBrox Chen# and treat all others as tests
502b892b05SBrox Chendef isTestLine(input_line, mc_mode):
512b892b05SBrox Chen    line = input_line.strip()
522b892b05SBrox Chen    # Skip empty and comment lines
532b892b05SBrox Chen    if not line or line.startswith(COMMENT[mc_mode]):
542b892b05SBrox Chen        return False
552b892b05SBrox Chen    # skip any CHECK lines.
562b892b05SBrox Chen    elif common.CHECK_RE.match(input_line):
572b892b05SBrox Chen        return False
582b892b05SBrox Chen    return True
592b892b05SBrox Chen
602b892b05SBrox Chen
61528e975aSBrox Chendef isRunLine(l):
62528e975aSBrox Chen    return common.RUN_LINE_RE.match(l)
63528e975aSBrox Chen
64528e975aSBrox Chen
652b892b05SBrox Chendef hasErr(err):
662b892b05SBrox Chen    return err and ERROR_RE.search(err) is not None
672b892b05SBrox Chen
682b892b05SBrox Chen
692b892b05SBrox Chendef getErrString(err):
702b892b05SBrox Chen    if not err:
712b892b05SBrox Chen        return ""
722b892b05SBrox Chen
732b892b05SBrox Chen    # take the first match
742b892b05SBrox Chen    for line in err.splitlines():
752b892b05SBrox Chen        s = ERROR_RE.search(line)
762b892b05SBrox Chen        if s:
772b892b05SBrox Chen            return s.group(0)
782b892b05SBrox Chen    return ""
792b892b05SBrox Chen
802b892b05SBrox Chen
812b892b05SBrox Chendef getOutputString(out):
822b892b05SBrox Chen    if not out:
832b892b05SBrox Chen        return ""
842b892b05SBrox Chen    output = ""
852b892b05SBrox Chen
862b892b05SBrox Chen    for line in out.splitlines():
872b892b05SBrox Chen        if OUTPUT_SKIPPED_RE.search(line):
882b892b05SBrox Chen            continue
892b892b05SBrox Chen        if line.strip("\t ") == "":
902b892b05SBrox Chen            continue
912b892b05SBrox Chen        output += line.lstrip("\t ")
922b892b05SBrox Chen    return output
932b892b05SBrox Chen
942b892b05SBrox Chen
952b892b05SBrox Chendef should_add_line_to_output(input_line, prefix_set, mc_mode):
962b892b05SBrox Chen    # special check line
972b892b05SBrox Chen    if mc_mode == "dasm" and ERROR_CHECK_RE.search(input_line):
982b892b05SBrox Chen        return False
992b892b05SBrox Chen    else:
1002b892b05SBrox Chen        return common.should_add_line_to_output(
1012b892b05SBrox Chen            input_line, prefix_set, comment_marker=COMMENT[mc_mode]
1022b892b05SBrox Chen        )
1032b892b05SBrox Chen
1042b892b05SBrox Chen
1052b892b05SBrox Chendef getStdCheckLine(prefix, output, mc_mode):
1062b892b05SBrox Chen    o = ""
1072b892b05SBrox Chen    for line in output.splitlines():
1082b892b05SBrox Chen        o += COMMENT[mc_mode] + " " + prefix + ": " + line + "\n"
1092b892b05SBrox Chen    return o
1102b892b05SBrox Chen
1112b892b05SBrox Chen
112*6f973fd4SBrox Chendef getErrCheckLine(prefix, output, mc_mode, line_offset=1):
113*6f973fd4SBrox Chen    return (
114*6f973fd4SBrox Chen        COMMENT[mc_mode]
115*6f973fd4SBrox Chen        + " "
116*6f973fd4SBrox Chen        + prefix
117*6f973fd4SBrox Chen        + ": "
118*6f973fd4SBrox Chen        + ":[[@LINE-{}]]".format(line_offset)
119*6f973fd4SBrox Chen        + output
120*6f973fd4SBrox Chen        + "\n"
121*6f973fd4SBrox Chen    )
1222b892b05SBrox Chen
1232b892b05SBrox Chen
1242b892b05SBrox Chendef main():
1252b892b05SBrox Chen    parser = argparse.ArgumentParser(description=__doc__)
1262b892b05SBrox Chen    parser.add_argument(
1272b892b05SBrox Chen        "--llvm-mc-binary",
1282b892b05SBrox Chen        default=None,
1292b892b05SBrox Chen        help='The "mc" binary to use to generate the test case',
1302b892b05SBrox Chen    )
1312b892b05SBrox Chen    parser.add_argument(
1322b892b05SBrox Chen        "--tool",
1332b892b05SBrox Chen        default=None,
1342b892b05SBrox Chen        help="Treat the given tool name as an mc-like tool for which check lines should be generated",
1352b892b05SBrox Chen    )
1362b892b05SBrox Chen    parser.add_argument(
1372b892b05SBrox Chen        "--default-march",
1382b892b05SBrox Chen        default=None,
1392b892b05SBrox Chen        help="Set a default -march for when neither triple nor arch are found in a RUN line",
1402b892b05SBrox Chen    )
141528e975aSBrox Chen    parser.add_argument(
142528e975aSBrox Chen        "--unique",
143528e975aSBrox Chen        action="store_true",
144528e975aSBrox Chen        default=False,
145528e975aSBrox Chen        help="remove duplicated test line if found",
146528e975aSBrox Chen    )
147528e975aSBrox Chen    parser.add_argument(
148528e975aSBrox Chen        "--sort",
149528e975aSBrox Chen        action="store_true",
150528e975aSBrox Chen        default=False,
151528e975aSBrox Chen        help="sort testline in alphabetic order (keep run-lines on top), this option could be dangerous as it"
152528e975aSBrox Chen        "could change the order of lines that are not expected",
153528e975aSBrox Chen    )
1542b892b05SBrox Chen    parser.add_argument("tests", nargs="+")
1552b892b05SBrox Chen    initial_args = common.parse_commandline_args(parser)
1562b892b05SBrox Chen
1572b892b05SBrox Chen    script_name = os.path.basename(__file__)
1582b892b05SBrox Chen
1592b892b05SBrox Chen    for ti in common.itertests(
1602b892b05SBrox Chen        initial_args.tests, parser, script_name="utils/" + script_name
1612b892b05SBrox Chen    ):
1622b892b05SBrox Chen        if ti.path.endswith(".s"):
1632b892b05SBrox Chen            mc_mode = "asm"
1642b892b05SBrox Chen        elif ti.path.endswith(".txt"):
1652b892b05SBrox Chen            mc_mode = "dasm"
166528e975aSBrox Chen
167528e975aSBrox Chen            if ti.args.sort:
168528e975aSBrox Chen                print("sorting with dasm(.txt) file is not supported!")
169528e975aSBrox Chen                return -1
170528e975aSBrox Chen
1712b892b05SBrox Chen        else:
1722b892b05SBrox Chen            common.warn("Expected .s and .txt, Skipping file : ", ti.path)
1732b892b05SBrox Chen            continue
1742b892b05SBrox Chen
1752b892b05SBrox Chen        triple_in_ir = None
1762b892b05SBrox Chen        for l in ti.input_lines:
1772b892b05SBrox Chen            m = common.TRIPLE_IR_RE.match(l)
1782b892b05SBrox Chen            if m:
1792b892b05SBrox Chen                triple_in_ir = m.groups()[0]
1802b892b05SBrox Chen                break
1812b892b05SBrox Chen
1822b892b05SBrox Chen        run_list = []
1832b892b05SBrox Chen        for l in ti.run_lines:
1842b892b05SBrox Chen            if "|" not in l:
1852b892b05SBrox Chen                common.warn("Skipping unparsable RUN line: " + l)
1862b892b05SBrox Chen                continue
1872b892b05SBrox Chen
1882b892b05SBrox Chen            commands = [cmd.strip() for cmd in l.split("|")]
1892b892b05SBrox Chen            assert len(commands) >= 2
1902b892b05SBrox Chen            mc_cmd = " | ".join(commands[:-1])
1912b892b05SBrox Chen            filecheck_cmd = commands[-1]
1922b892b05SBrox Chen
1932b892b05SBrox Chen            # special handling for negating exit status
194*6f973fd4SBrox Chen            # if not is used in runline, disable rc check, since
195*6f973fd4SBrox Chen            # the command might or might not
196*6f973fd4SBrox Chen            # return non-zero code on a single line run
197*6f973fd4SBrox Chen            check_rc = True
198*6f973fd4SBrox Chen            mc_cmd_args = mc_cmd.strip().split()
199*6f973fd4SBrox Chen            if mc_cmd_args[0] == "not":
200*6f973fd4SBrox Chen                check_rc = False
201*6f973fd4SBrox Chen                mc_tool = mc_cmd_args[1]
202*6f973fd4SBrox Chen                mc_cmd = mc_cmd[len(mc_cmd_args[0]) :].strip()
203*6f973fd4SBrox Chen            else:
204*6f973fd4SBrox Chen                mc_tool = mc_cmd_args[0]
2052b892b05SBrox Chen
2062b892b05SBrox Chen            triple_in_cmd = None
2072b892b05SBrox Chen            m = common.TRIPLE_ARG_RE.search(mc_cmd)
2082b892b05SBrox Chen            if m:
2092b892b05SBrox Chen                triple_in_cmd = m.groups()[0]
2102b892b05SBrox Chen
2112b892b05SBrox Chen            march_in_cmd = ti.args.default_march
2122b892b05SBrox Chen            m = common.MARCH_ARG_RE.search(mc_cmd)
2132b892b05SBrox Chen            if m:
2142b892b05SBrox Chen                march_in_cmd = m.groups()[0]
2152b892b05SBrox Chen
2162b892b05SBrox Chen            common.verify_filecheck_prefixes(filecheck_cmd)
2172b892b05SBrox Chen
2182b892b05SBrox Chen            mc_like_tools = mc_LIKE_TOOLS[:]
2192b892b05SBrox Chen            if ti.args.tool:
2202b892b05SBrox Chen                mc_like_tools.append(ti.args.tool)
2212b892b05SBrox Chen            if mc_tool not in mc_like_tools:
2222b892b05SBrox Chen                common.warn("Skipping non-mc RUN line: " + l)
2232b892b05SBrox Chen                continue
2242b892b05SBrox Chen
2252b892b05SBrox Chen            if not filecheck_cmd.startswith("FileCheck "):
2262b892b05SBrox Chen                common.warn("Skipping non-FileChecked RUN line: " + l)
2272b892b05SBrox Chen                continue
2282b892b05SBrox Chen
2292b892b05SBrox Chen            mc_cmd_args = mc_cmd[len(mc_tool) :].strip()
2302b892b05SBrox Chen            mc_cmd_args = mc_cmd_args.replace("< %s", "").replace("%s", "").strip()
2312b892b05SBrox Chen            check_prefixes = common.get_check_prefixes(filecheck_cmd)
2322b892b05SBrox Chen
2332b892b05SBrox Chen            run_list.append(
2342b892b05SBrox Chen                (
2352b892b05SBrox Chen                    check_prefixes,
2362b892b05SBrox Chen                    mc_tool,
237*6f973fd4SBrox Chen                    check_rc,
2382b892b05SBrox Chen                    mc_cmd_args,
2392b892b05SBrox Chen                    triple_in_cmd,
2402b892b05SBrox Chen                    march_in_cmd,
2412b892b05SBrox Chen                )
2422b892b05SBrox Chen            )
2432b892b05SBrox Chen
2442b892b05SBrox Chen        # find all test line from input
2452b892b05SBrox Chen        testlines = [l for l in ti.input_lines if isTestLine(l, mc_mode)]
246528e975aSBrox Chen        # remove duplicated lines to save running time
247528e975aSBrox Chen        testlines = list(dict.fromkeys(testlines))
248528e975aSBrox Chen        common.debug("Valid test line found: ", len(testlines))
249528e975aSBrox Chen
2502b892b05SBrox Chen        run_list_size = len(run_list)
2512b892b05SBrox Chen        testnum = len(testlines)
2522b892b05SBrox Chen
2532b892b05SBrox Chen        raw_output = []
2542b892b05SBrox Chen        raw_prefixes = []
2552b892b05SBrox Chen        for (
2562b892b05SBrox Chen            prefixes,
2572b892b05SBrox Chen            mc_tool,
258*6f973fd4SBrox Chen            check_rc,
2592b892b05SBrox Chen            mc_args,
2602b892b05SBrox Chen            triple_in_cmd,
2612b892b05SBrox Chen            march_in_cmd,
2622b892b05SBrox Chen        ) in run_list:
2632b892b05SBrox Chen            common.debug("Extracted mc cmd:", mc_tool, mc_args)
2642b892b05SBrox Chen            common.debug("Extracted FileCheck prefixes:", str(prefixes))
2652b892b05SBrox Chen            common.debug("Extracted triple :", str(triple_in_cmd))
2662b892b05SBrox Chen            common.debug("Extracted march:", str(march_in_cmd))
2672b892b05SBrox Chen
2682b892b05SBrox Chen            triple = triple_in_cmd or triple_in_ir
2692b892b05SBrox Chen            if not triple:
2702b892b05SBrox Chen                triple = common.get_triple_from_march(march_in_cmd)
2712b892b05SBrox Chen
2722b892b05SBrox Chen            raw_output.append([])
2732b892b05SBrox Chen            for line in testlines:
2742b892b05SBrox Chen                # get output for each testline
2752b892b05SBrox Chen                out = invoke_tool(
2762b892b05SBrox Chen                    ti.args.llvm_mc_binary or mc_tool,
277*6f973fd4SBrox Chen                    check_rc,
2782b892b05SBrox Chen                    mc_args,
2792b892b05SBrox Chen                    line,
2802b892b05SBrox Chen                    verbose=ti.args.verbose,
2812b892b05SBrox Chen                )
2822b892b05SBrox Chen                raw_output[-1].append(out)
2832b892b05SBrox Chen
2842b892b05SBrox Chen            common.debug("Collect raw tool lines:", str(len(raw_output[-1])))
2852b892b05SBrox Chen
2862b892b05SBrox Chen            raw_prefixes.append(prefixes)
2872b892b05SBrox Chen
2882b892b05SBrox Chen        output_lines = []
289528e975aSBrox Chen        generated_prefixes = {}
2902b892b05SBrox Chen        used_prefixes = set()
2912b892b05SBrox Chen        prefix_set = set([prefix for p in run_list for prefix in p[0]])
2922b892b05SBrox Chen        common.debug("Rewriting FileCheck prefixes:", str(prefix_set))
2932b892b05SBrox Chen
2942b892b05SBrox Chen        for test_id in range(testnum):
2952b892b05SBrox Chen            input_line = testlines[test_id]
2962b892b05SBrox Chen
2972b892b05SBrox Chen            # a {prefix : output, [runid] } dict
2982b892b05SBrox Chen            # insert output to a prefix-key dict, and do a max sorting
2992b892b05SBrox Chen            # to select the most-used prefix which share the same output string
3002b892b05SBrox Chen            p_dict = {}
3012b892b05SBrox Chen            for run_id in range(run_list_size):
3022b892b05SBrox Chen                out = raw_output[run_id][test_id]
3032b892b05SBrox Chen
3042b892b05SBrox Chen                if hasErr(out):
3052b892b05SBrox Chen                    o = getErrString(out)
3062b892b05SBrox Chen                else:
3072b892b05SBrox Chen                    o = getOutputString(out)
3082b892b05SBrox Chen
3092b892b05SBrox Chen                prefixes = raw_prefixes[run_id]
3102b892b05SBrox Chen
3112b892b05SBrox Chen                for p in prefixes:
3122b892b05SBrox Chen                    if p not in p_dict:
3132b892b05SBrox Chen                        p_dict[p] = o, [run_id]
3142b892b05SBrox Chen                    else:
3152b892b05SBrox Chen                        if p_dict[p] == (None, []):
3162b892b05SBrox Chen                            continue
3172b892b05SBrox Chen
3182b892b05SBrox Chen                        prev_o, run_ids = p_dict[p]
3192b892b05SBrox Chen                        if o == prev_o:
3202b892b05SBrox Chen                            run_ids.append(run_id)
3212b892b05SBrox Chen                            p_dict[p] = o, run_ids
3222b892b05SBrox Chen                        else:
3232b892b05SBrox Chen                            # conflict, discard
3242b892b05SBrox Chen                            p_dict[p] = None, []
3252b892b05SBrox Chen
3262b892b05SBrox Chen            p_dict_sorted = dict(
3272b892b05SBrox Chen                sorted(p_dict.items(), key=lambda item: -len(item[1][1]))
3282b892b05SBrox Chen            )
3292b892b05SBrox Chen
3302b892b05SBrox Chen            # prefix is selected and generated with most shared output lines
3312b892b05SBrox Chen            # each run_id can only be used once
3322b892b05SBrox Chen            gen_prefix = ""
3332b892b05SBrox Chen            used_runid = set()
334*6f973fd4SBrox Chen
335*6f973fd4SBrox Chen            # line number diff between generated prefix and testline
336*6f973fd4SBrox Chen            line_offset = 1
3372b892b05SBrox Chen            for prefix, tup in p_dict_sorted.items():
3382b892b05SBrox Chen                o, run_ids = tup
3392b892b05SBrox Chen
3402b892b05SBrox Chen                if len(run_ids) == 0:
3412b892b05SBrox Chen                    continue
3422b892b05SBrox Chen
3432b892b05SBrox Chen                skip = False
3442b892b05SBrox Chen                for i in run_ids:
3452b892b05SBrox Chen                    if i in used_runid:
3462b892b05SBrox Chen                        skip = True
3472b892b05SBrox Chen                    else:
3482b892b05SBrox Chen                        used_runid.add(i)
3492b892b05SBrox Chen                if not skip:
3502b892b05SBrox Chen                    used_prefixes.add(prefix)
3512b892b05SBrox Chen
3522b892b05SBrox Chen                    if hasErr(o):
353*6f973fd4SBrox Chen                        newline = getErrCheckLine(prefix, o, mc_mode, line_offset)
3542b892b05SBrox Chen                    else:
355*6f973fd4SBrox Chen                        newline = getStdCheckLine(prefix, o, mc_mode)
356*6f973fd4SBrox Chen
357*6f973fd4SBrox Chen                    if newline:
358*6f973fd4SBrox Chen                        gen_prefix += newline
359*6f973fd4SBrox Chen                        line_offset += 1
3602b892b05SBrox Chen
361528e975aSBrox Chen            generated_prefixes[input_line] = gen_prefix.rstrip("\n")
3622b892b05SBrox Chen
3632b892b05SBrox Chen        # write output
3642b892b05SBrox Chen        for input_info in ti.iterlines(output_lines):
3652b892b05SBrox Chen            input_line = input_info.line
366528e975aSBrox Chen            if input_line in testlines:
3672b892b05SBrox Chen                output_lines.append(input_line)
368528e975aSBrox Chen                output_lines.append(generated_prefixes[input_line])
3692b892b05SBrox Chen
3702b892b05SBrox Chen            elif should_add_line_to_output(input_line, prefix_set, mc_mode):
3712b892b05SBrox Chen                output_lines.append(input_line)
3722b892b05SBrox Chen
373528e975aSBrox Chen        if ti.args.unique or ti.args.sort:
374528e975aSBrox Chen            # split with double newlines
375528e975aSBrox Chen            test_units = "\n".join(output_lines).split("\n\n")
3762b892b05SBrox Chen
377528e975aSBrox Chen            # select the key line for each test unit
378528e975aSBrox Chen            test_dic = {}
379528e975aSBrox Chen            for unit in test_units:
380528e975aSBrox Chen                lines = unit.split("\n")
381528e975aSBrox Chen                for l in lines:
382528e975aSBrox Chen                    # if contains multiple lines, use
383528e975aSBrox Chen                    # the first testline or runline as key
384528e975aSBrox Chen                    if isTestLine(l, mc_mode):
385528e975aSBrox Chen                        test_dic[unit] = l
386528e975aSBrox Chen                        break
387528e975aSBrox Chen                    if isRunLine(l):
388528e975aSBrox Chen                        test_dic[unit] = l
389528e975aSBrox Chen                        break
390528e975aSBrox Chen
391528e975aSBrox Chen            # unique
392528e975aSBrox Chen            if ti.args.unique:
393528e975aSBrox Chen                new_test_units = []
394528e975aSBrox Chen                written_lines = set()
395528e975aSBrox Chen                for unit in test_units:
396528e975aSBrox Chen                    # if not testline/runline, we just add it
397528e975aSBrox Chen                    if unit not in test_dic:
398528e975aSBrox Chen                        new_test_units.append(unit)
399528e975aSBrox Chen                    else:
400528e975aSBrox Chen                        if test_dic[unit] in written_lines:
401528e975aSBrox Chen                            common.debug("Duplicated test skipped: ", unit)
402528e975aSBrox Chen                            continue
403528e975aSBrox Chen
404528e975aSBrox Chen                        written_lines.add(test_dic[unit])
405528e975aSBrox Chen                        new_test_units.append(unit)
406528e975aSBrox Chen                test_units = new_test_units
407528e975aSBrox Chen
408528e975aSBrox Chen            # sort
409528e975aSBrox Chen            if ti.args.sort:
410528e975aSBrox Chen
411528e975aSBrox Chen                def getkey(l):
412528e975aSBrox Chen                    # find key of test unit, otherwise use first line
413528e975aSBrox Chen                    if l in test_dic:
414528e975aSBrox Chen                        line = test_dic[l]
415528e975aSBrox Chen                    else:
416528e975aSBrox Chen                        line = l.split("\n")[0]
417528e975aSBrox Chen
418528e975aSBrox Chen                    # runline placed on the top
419528e975aSBrox Chen                    return (not isRunLine(line), line)
420528e975aSBrox Chen
421528e975aSBrox Chen                test_units = sorted(test_units, key=getkey)
422528e975aSBrox Chen
423528e975aSBrox Chen            # join back to be output string
424528e975aSBrox Chen            output_lines = "\n\n".join(test_units).split("\n")
425528e975aSBrox Chen
426528e975aSBrox Chen        # output
4272b892b05SBrox Chen        if ti.args.gen_unused_prefix_body:
4282b892b05SBrox Chen            output_lines.extend(
4292b892b05SBrox Chen                ti.get_checks_for_unused_prefixes(run_list, used_prefixes)
4302b892b05SBrox Chen            )
4312b892b05SBrox Chen
4322b892b05SBrox Chen        common.debug("Writing %d lines to %s..." % (len(output_lines), ti.path))
4332b892b05SBrox Chen        with open(ti.path, "wb") as f:
4342b892b05SBrox Chen            f.writelines(["{}\n".format(l).encode("utf-8") for l in output_lines])
4352b892b05SBrox Chen
4362b892b05SBrox Chen
4372b892b05SBrox Chenif __name__ == "__main__":
4382b892b05SBrox Chen    main()
439