1from __future__ import absolute_import
2import os
3import tempfile
4import subprocess
5import sys
6import platform
7
8import lit.Test
9import lit.TestRunner
10import lit.util
11from lit.formats.base import TestFormat
12
13
14class LLDBTest(TestFormat):
15    def __init__(self, dotest_cmd):
16        self.dotest_cmd = dotest_cmd
17
18    def getTestsInDirectory(self, testSuite, path_in_suite, litConfig,
19                            localConfig):
20        source_path = testSuite.getSourcePath(path_in_suite)
21        for filename in os.listdir(source_path):
22            # Ignore dot files and excluded tests.
23            if (filename.startswith('.') or filename in localConfig.excludes):
24                continue
25
26            # Ignore files that don't start with 'Test'.
27            if not filename.startswith('Test'):
28                continue
29
30            filepath = os.path.join(source_path, filename)
31            if not os.path.isdir(filepath):
32                base, ext = os.path.splitext(filename)
33                if ext in localConfig.suffixes:
34                    yield lit.Test.Test(testSuite, path_in_suite +
35                                        (filename, ), localConfig)
36
37    def execute(self, test, litConfig):
38        if litConfig.noExecute:
39            return lit.Test.PASS, ''
40
41        if not getattr(test.config, 'lldb_enable_python', False):
42            return (lit.Test.UNSUPPORTED, 'Python module disabled')
43
44        if test.config.unsupported:
45            return (lit.Test.UNSUPPORTED, 'Test is unsupported')
46
47        testPath, testFile = os.path.split(test.getSourcePath())
48
49        # The Python used to run lit can be different from the Python LLDB was
50        # build with.
51        executable = test.config.python_executable
52
53        # On Windows, the system does not always correctly interpret
54        # shebang lines.  To make sure we can execute the tests, add
55        # python exe as the first parameter of the command.
56        cmd = [executable] + self.dotest_cmd + [testPath, '-p', testFile]
57
58        timeoutInfo = None
59        try:
60            out, err, exitCode = lit.util.executeCommand(
61                cmd,
62                env=test.config.environment,
63                timeout=litConfig.maxIndividualTestTime)
64        except lit.util.ExecuteCommandTimeoutException as e:
65            out = e.out
66            err = e.err
67            exitCode = e.exitCode
68            timeoutInfo = 'Reached timeout of {} seconds'.format(
69                litConfig.maxIndividualTestTime)
70
71        if sys.version_info.major == 2:
72            # In Python 2, string objects can contain Unicode characters. Use
73            # the non-strict 'replace' decoding mode. We cannot use the strict
74            # mode right now because lldb's StringPrinter facility and the
75            # Python utf8 decoder have different interpretations of which
76            # characters are "printable". This leads to Python utf8 decoding
77            # exceptions even though lldb is behaving as expected.
78            out = out.decode('utf-8', 'replace')
79            err = err.decode('utf-8', 'replace')
80
81        output = """Script:\n--\n%s\n--\nExit Code: %d\n""" % (
82            ' '.join(cmd), exitCode)
83        if timeoutInfo is not None:
84            output += """Timeout: %s\n""" % (timeoutInfo,)
85        output += "\n"
86
87        if out:
88            output += """Command Output (stdout):\n--\n%s\n--\n""" % (out,)
89        if err:
90            output += """Command Output (stderr):\n--\n%s\n--\n""" % (err,)
91
92        if timeoutInfo:
93            return lit.Test.TIMEOUT, output
94
95        if exitCode:
96            if 'XPASS:' in out or 'XPASS:' in err:
97                return lit.Test.XPASS, output
98
99            # Otherwise this is just a failure.
100            return lit.Test.FAIL, output
101
102        has_unsupported_tests = 'UNSUPPORTED:' in out or 'UNSUPPORTED:' in err
103        has_passing_tests = 'PASS:' in out or 'PASS:' in err
104        if has_unsupported_tests and not has_passing_tests:
105            return lit.Test.UNSUPPORTED, output
106
107        passing_test_line = 'RESULT: PASSED'
108        if passing_test_line not in out and passing_test_line not in err:
109            return lit.Test.UNRESOLVED, output
110
111        return lit.Test.PASS, output
112