reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    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
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
#!/usr/bin/env python

"""
A simple utility to redo the failed/errored tests.

You need to specify the session directory in order for this script to locate the
tests which need to be re-run.

See also dotest.py, the test driver running the test suite.

Type:

./dotest.py -h

for help.
"""

from __future__ import print_function

import os
import sys
import datetime
import re

# If True, redo with no '-t' option for the test driver.
no_trace = False

# To be filled with the filterspecs found in the session logs.
redo_specs = []

# The filename components to match for.  Only files with the contained component names
# will be considered for re-run.  Examples: ['X86_64', 'clang'].
filename_components = []

do_delay = False

# There is a known bug with respect to comp_specs and arch_specs, in that if we
# encountered "-C clang" and "-C gcc" when visiting the session files, both
# compilers will end up in the invocation of the test driver when rerunning.
# That is: ./dotest -v -C clang^gcc ... -f ...".  Ditto for "-A" flags.

# The "-C compiler" for comp_specs.
comp_specs = set()
# The "-A arch" for arch_specs.
arch_specs = set()


def usage():
    print("""\
Usage: redo.py [-F filename_component] [-n] [session_dir] [-d]
where options:
-F : only consider the test for re-run if the session filename contains the filename component
     for example: -F x86_64
-n : when running the tests, do not turn on trace mode, i.e, no '-t' option
     is passed to the test driver (this will run the tests faster)
-d : pass -d down to the test driver (introduces a delay so you can attach with a debugger)

and session_dir specifies the session directory which contains previously
recorded session infos for all the test cases which either failed or errored.

If sessin_dir is left unspecified, this script uses the heuristic to find the
possible session directories with names starting with %Y-%m-%d- (for example,
2012-01-23-) and employs the one with the latest timestamp.""")
    sys.exit(0)


def where(session_dir, test_dir):
    """Returns the full path to the session directory; None if non-existent."""
    abspath = os.path.abspath(session_dir)
    if os.path.isdir(abspath):
        return abspath

    session_dir_path = os.path.join(test_dir, session_dir)
    if os.path.isdir(session_dir_path):
        return session_dir_path

    return None

# This is the pattern for the line from the log file to redo a test.
# We want the filter spec.
filter_pattern = re.compile("^\./dotest\.py.*-f (.*)$")
comp_pattern = re.compile(" -C ([^ ]+) ")
arch_pattern = re.compile(" -A ([^ ]+) ")


def redo(suffix, dir, names):
    """Visitor function for os.path.walk(path, visit, arg)."""
    global redo_specs
    global comp_specs
    global arch_specs
    global filter_pattern
    global comp_pattern
    global arch_pattern
    global filename_components
    global do_delay

    for name in names:
        if name.endswith(suffix):
            #print("Find a log file:", name)
            if name.startswith("Error") or name.startswith("Failure"):
                if filename_components:
                    if not all([comp in name for comp in filename_components]):
                        continue
                with open(os.path.join(dir, name), 'r') as log:
                    content = log.read()
                    for line in content.splitlines():
                        match = filter_pattern.match(line)
                        if match:
                            filterspec = match.group(1)
                            print("adding filterspec:", filterspec)
                            redo_specs.append(filterspec)
                            comp = comp_pattern.search(line)
                            if comp:
                                comp_specs.add(comp.group(1))
                            arch = arch_pattern.search(line)
                            if arch:
                                arch_specs.add(arch.group(1))
            else:
                continue


def main():
    """Read the session directory and run the failed test cases one by one."""
    global no_trace
    global redo_specs
    global filename_components
    global do_delay

    test_dir = sys.path[0]
    if not test_dir:
        test_dir = os.getcwd()
    if not test_dir.endswith('test'):
        print("This script expects to reside in lldb's test directory.")
        sys.exit(-1)

    index = 1
    while index < len(sys.argv):
        if sys.argv[index].startswith(
                '-h') or sys.argv[index].startswith('--help'):
            usage()

        if sys.argv[index].startswith('-'):
            # We should continue processing...
            pass
        else:
            # End of option processing.
            break

        if sys.argv[index] == '-F':
            # Increment by 1 to fetch the filename component spec.
            index += 1
            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
                usage()
            filename_components.append(sys.argv[index])
        elif sys.argv[index] == '-n':
            no_trace = True
        elif sys.argv[index] == '-d':
            do_delay = True

        index += 1

    if index < len(sys.argv):
        # Get the specified session directory.
        session_dir = sys.argv[index]
    else:
        # Use heuristic to find the latest session directory.
        name = datetime.datetime.now().strftime("%Y-%m-%d-")
        dirs = [d for d in os.listdir(os.getcwd()) if d.startswith(name)]
        if len(dirs) == 0:
            print("No default session directory found, please specify it explicitly.")
            usage()
        session_dir = max(dirs, key=os.path.getmtime)
        if not session_dir or not os.path.exists(session_dir):
            print("No default session directory found, please specify it explicitly.")
            usage()

    #print("The test directory:", test_dir)
    session_dir_path = where(session_dir, test_dir)

    print("Using session dir path:", session_dir_path)
    os.chdir(test_dir)
    os.path.walk(session_dir_path, redo, ".log")

    if not redo_specs:
        print("No failures/errors recorded within the session directory, please specify a different session directory.\n")
        usage()

    filters = " -f ".join(redo_specs)
    compilers = ''
    for comp in comp_specs:
        compilers += " -C %s" % (comp)
    archs = ''
    for arch in arch_specs:
        archs += "--arch %s " % (arch)

    command = "./dotest.py %s %s -v %s %s -f " % (
        compilers, archs, "" if no_trace else "-t", "-d" if do_delay else "")

    print("Running %s" % (command + filters))
    os.system(command + filters)

if __name__ == '__main__':
    main()