File: run_unittest.py

package info (click to toggle)
clazy 1.17-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,248 kB
  • sloc: cpp: 23,552; python: 1,450; xml: 450; sh: 237; makefile: 46
file content (198 lines) | stat: -rw-r--r-- 7,727 bytes parent folder | download | duplicates (2)
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
import io
import os
import queue
import subprocess
from pathlib import Path
from sys import platform as _platform
import platform

from . import Args
from .commands import clang_tidy_command, clazy_standalone_command, clazy_command
from .os_utils import run_command, compare_files
from .qtinstallation import qt_installation
from .test import Test


def clazy_standalone_binary():
    if 'CLAZYSTANDALONE_CXX' in os.environ:  # in case we want to use "clazy.AppImage --standalone" instead
        return os.environ['CLAZYSTANDALONE_CXX']
    return 'clazy-standalone'


def is32Bit():
    return platform.architecture()[0] == '32bit'


def file_contains(filename, text):
    f = io.open(filename, 'r', encoding='utf-8')
    contents = f.read()
    f.close()
    return text in contents

def print_file(filename):
    f = open(filename, 'r')
    print(f.read())
    f.close()

def normalizedCwd():
    if _platform.startswith('linux'):
        return subprocess.check_output("pwd -L", shell=True, universal_newlines=True).rstrip('\n')
    else:
        return os.getcwd().replace('\\', '/')


def extract_word(word, in_file, out_file):
    in_f = io.open(in_file, 'r', encoding='utf-8')
    out_f = io.open(out_file, 'w', encoding='utf-8')
    for line in in_f:
        if '[-Wdeprecated-declarations]' in line:
            continue

        if word in line:
            line = line.replace('\\', '/')
            # clazy-standalone prints the complete cpp file path for some reason. Normalize it so it compares OK with the expected output.
            line = line.replace(f"{normalizedCwd()}/", "")
            out_f.write(line)
    in_f.close()
    out_f.close()


def run_unit_test(test: Test, is_standalone, is_tidy, cppStandard, qt_major_version, config: Args):
    if test.check.clazy_standalone_only and not is_standalone:
        return True

    qt = qt_installation(qt_major_version, config.verbose)
    if qt is None:
        return True  # silently skip

    if is_tidy and (test.isScript() or test.only_qt or test.qt_developer):
        print("Options not supported with clang-tidy")
        return True

    if config.verbose:
        print("Qt major versions required by the test: " + str(test.qt_major_versions))
        print("Currently considering Qt major version: " + str(qt_major_version))
        print("Qt versions required by the test: min=" + str(test.minimum_qt_version) + " max=" + str(test.maximum_qt_version))
        print("Qt int version: " + str(qt.int_version))
        print("Qt headers: " + qt.qmake_header_path)

    printableName = test.printableName(cppStandard, qt_major_version, is_standalone, is_tidy, False)

    if qt.int_version < test.minimum_qt_version or qt.int_version > test.maximum_qt_version or config.clang_version < test.minimum_clang_version:
        if config.verbose:
            print(f"Skipping {printableName} because required version is not available")
        return True

    if _platform in test.blacklist_platforms:
        if config.verbose:
            print(f"Skipping {printableName} because it is blacklisted for this platform")
        return True

    if not test.should_run_on_32bit and is32Bit():
        if config.verbose:
            print(f"Skipping {printableName} because it is blacklisted on 32bit")
        return True

    checkname = test.check.name
    filename = checkname + "/" + test.filename
    # Easy copying of command to reproduce manually. Saves me a ton of work - Alex
    abs_filename = str(Path(filename).absolute())

    output_file = filename + ".out"
    result_file = filename + ".result"
    expected_file = filename + ".expected"
    expected_file_tidy = filename + ".expected.tidy"
    expected_file_namespaced = filename + ".expected.qtnamespaced"
    qt_namespaced_separate_file = False
    if config.qt_namespaced and os.path.exists(expected_file_namespaced):
        expected_file = expected_file_namespaced
        qt_namespaced_separate_file = True
    if is_tidy and os.path.exists(expected_file_tidy):
        expected_file = expected_file_tidy
    elif not os.path.exists(expected_file):
        expected_file = filename + ".qt" + str(qt_major_version) + ".expected"

    # Some tests have different output on 32 bit
    if is32Bit() and os.path.exists(expected_file + '.x86'):
        expected_file = expected_file + '.x86'

    if is_standalone and test.isScript():
        return True

    if is_standalone:
        cmd_to_run = clazy_standalone_binary() + " " + abs_filename + " " + \
            clazy_standalone_command(test, cppStandard, qt, config)
    elif is_tidy:
        cmd_to_run = clang_tidy_command(test, cppStandard, qt, abs_filename, config)
    else:
        cmd_to_run = clazy_command(test, cppStandard, qt, abs_filename, config)

    if test.compare_everything:
        result_file = output_file

    must_fail = test.must_fail

    cmd_success = run_command(cmd_to_run, output_file, test.env, ignore_verbose_command=True, qt_namespaced=config.qt_namespaced, qt_replace_namespace=not qt_namespaced_separate_file, verbose=config.verbose)

    if file_contains(output_file, 'Invalid check: '):
        return True

    if (not cmd_success and not must_fail) or (cmd_success and must_fail):
        print(f"[FAIL] {printableName} (Failed to build test. Check {output_file} for details)")
        print("-------------------")
        print(f"Contents of {output_file}:")
        print_file(output_file)
        print("-------------------")
        return False

    if not test.compare_everything:
        word_to_grep = "warning:" if not must_fail else "error:"
        extract_word(word_to_grep, output_file, result_file)

    # Check that it printed the expected warnings
    if not compare_files(test.expects_failure, expected_file, result_file, printableName, verbose=config.verbose):
        return False

    if test.has_fixits and not is_tidy and not config.qt_namespaced:
        # The normal tests succeeded, we can run the respective fixits then
        test.should_run_fixits_test = True

    return True


def run_unit_test_for_each_configuration(test, is_standalone, is_tidy, config: Args):
    """Runs unit tests for cpp standards and qt version combinations"""
    if test.check.clazy_standalone_only and not is_standalone:
        return True
    if config.qt_namespaced and test.skip_qtnamespaced:
        return True
    result = True
    for qt_major_version in test.qt_major_versions:
        for cppStandard in test.cppStandards:
            if cppStandard == "c++14" and qt_major_version == 6: # Qt6 requires C++17
                continue
            if cppStandard == "c++17" and qt_major_version == 5 and len(test.cppStandards) > 1: # valid combination but let's skip it unless it was the only specified standard
                continue
            result = result and run_unit_test(test, is_standalone, is_tidy, cppStandard, qt_major_version, config)
    return result

def run_unit_tests(tests, config: Args, finished_job_results: queue.Queue[bool]):
    result = True
    for test in tests:
        test_result = True
        if not config.only_standalone:
            test_result = run_unit_test_for_each_configuration(test, False, False, config)
            result = result and test_result

        if not config.no_standalone:
            test_result = test_result and run_unit_test_for_each_configuration(test, True, False, config)
            result = result and test_result

        if not config.no_clang_tidy:
            test_result = test_result and run_unit_test_for_each_configuration(test, False, True, config)
            result = result and test_result

        if not test_result:
            test.removeYamlFiles()

    finished_job_results.put(result)