|
1 | 1 | #! /usr/bin/env python |
2 | 2 |
|
3 | 3 | import os |
| 4 | +import re |
4 | 5 | import sys |
5 | 6 | import platform |
6 | 7 | import importlib.util |
@@ -150,27 +151,71 @@ def run_test( |
150 | 151 | print(" ".join(cmd), flush=True) |
151 | 152 | res = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
152 | 153 | total_output = b"" |
| 154 | + subprocess_done = False |
153 | 155 | while True: |
154 | 156 | buff: bytes = res.stdout.read1() |
155 | 157 | total_output += buff |
156 | 158 | os.write(sys.stdout.fileno(), buff) |
| 159 | + if subprocess_done: |
| 160 | + break |
157 | 161 | try: |
158 | 162 | res.wait(timeout=0.1) |
159 | | - buff: bytes = res.stdout.read1() |
160 | | - total_output += buff |
161 | | - os.write(sys.stdout.fileno(), buff) |
162 | | - break |
| 163 | + # Subprocess is done, but we still have one more stdin/stderr pump to do |
| 164 | + subprocess_done = True |
163 | 165 | except subprocess.TimeoutExpired: |
164 | 166 | # Subprocess is still running |
165 | 167 | pass |
166 | 168 | if res.returncode != 0: |
167 | 169 | raise SystemExit(f"{RED}{test_name}: Non-zero return code: {res.returncode}{NO_COLOR}") |
168 | | - for line in total_output.splitlines(): |
169 | | - lower_line = line.lower() |
170 | | - if b"error" in lower_line or b"warning" in lower_line: |
| 170 | + |
| 171 | + try: |
| 172 | + expected_output = (test_workdir / "expected.output").read_text() |
| 173 | + |
| 174 | + except FileNotFoundError: |
| 175 | + for line in total_output.splitlines(): |
| 176 | + lower_line = line.lower() |
| 177 | + if b"error" in lower_line or b"warning" in lower_line: |
| 178 | + raise SystemExit( |
| 179 | + f"{RED}{test_name}: stdout/stderr contains logs with error and/or warning ({line!r}){NO_COLOR}" |
| 180 | + ) |
| 181 | + |
| 182 | + else: |
| 183 | + expected_lines = expected_output.splitlines() |
| 184 | + actual_lines = total_output.splitlines() |
| 185 | + msg = [] |
| 186 | + mismatch = False |
| 187 | + for i in range(0, max(len(expected_lines), len(actual_lines))): |
| 188 | + try: |
| 189 | + expected_line = expected_lines[i] |
| 190 | + except IndexError: |
| 191 | + expected_line = None |
| 192 | + try: |
| 193 | + actual_line = actual_lines[i].decode() |
| 194 | + except IndexError: |
| 195 | + actual_line = None |
| 196 | + |
| 197 | + assert expected_line is not None or actual_line is not None |
| 198 | + if expected_line is not None and actual_line is None: |
| 199 | + msg.append(f"{RED}- {expected_line}{NO_COLOR}") |
| 200 | + mismatch = True |
| 201 | + elif expected_line is None and actual_line is not None: |
| 202 | + msg.append(f"{GREEN}+ {actual_line}{NO_COLOR}") |
| 203 | + mismatch = True |
| 204 | + else: |
| 205 | + assert expected_line is not None |
| 206 | + assert actual_line is not None |
| 207 | + if not re.match(f"^{expected_line}$", actual_line): |
| 208 | + msg.append(f"{RED}- {expected_line}{NO_COLOR}") |
| 209 | + msg.append(f"{GREEN}+ {actual_line}{NO_COLOR}") |
| 210 | + mismatch = True |
| 211 | + else: |
| 212 | + msg.append(f"~ {actual_line}") |
| 213 | + |
| 214 | + if mismatch: |
171 | 215 | raise SystemExit( |
172 | | - f"{RED}{test_name}: stdout/stderr contains logs with error and/or warning ({line!r}){NO_COLOR}" |
| 216 | + f"{RED}{test_name}: unexpected stdout/stderr:{NO_COLOR}\n\n " + "\n ".join(msg) |
173 | 217 | ) |
| 218 | + |
174 | 219 | print(f"{GREEN}{test_name}: All good \\o/{NO_COLOR}", flush=True) |
175 | 220 |
|
176 | 221 |
|
|
0 commit comments