Skip to content

Commit 58996c0

Browse files
authored
[Util] Only run --update-tests functions on failing tests (llvm#155148)
The early exit we relied on to only invoke test updaters for failing tests requires that there was no output to stdout or stderr, and that timeouts weren't enabled. When these conditions weren't fulfilled, test updaters would be invoked even on passing or XFAILed tests.
1 parent c6dfbc5 commit 58996c0

File tree

10 files changed

+92
-3
lines changed

10 files changed

+92
-3
lines changed

llvm/utils/lit/lit/TestRunner.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import tempfile
1414
import threading
1515
import typing
16+
import traceback
1617
from typing import Optional, Tuple
1718

1819
import io
@@ -47,6 +48,16 @@ def __init__(self, message):
4748
super().__init__(message)
4849

4950

51+
class TestUpdaterException(Exception):
52+
"""
53+
There was an error not during test execution, but while invoking a function
54+
in test_updaters on a failing RUN line.
55+
"""
56+
57+
def __init__(self, message):
58+
super().__init__(message)
59+
60+
5061
kIsWindows = platform.system() == "Windows"
5162

5263
# Don't use close_fds on Windows.
@@ -1192,13 +1203,23 @@ def executeScriptInternal(
11921203
str(result.timeoutReached),
11931204
)
11941205

1195-
if litConfig.update_tests:
1206+
if (
1207+
litConfig.update_tests
1208+
and result.exitCode != 0
1209+
and not timeoutInfo
1210+
# In theory tests marked XFAIL can fail in the form of XPASS, but the
1211+
# test updaters are not expected to be able to fix that, so always skip for XFAIL
1212+
and not test.isExpectedToFail()
1213+
):
11961214
for test_updater in litConfig.test_updaters:
11971215
try:
11981216
update_output = test_updater(result, test)
11991217
except Exception as e:
1200-
out += f"Exception occurred in test updater: {e}"
1201-
continue
1218+
output = out
1219+
output += err
1220+
output += "Exception occurred in test updater:\n"
1221+
output += traceback.format_exc()
1222+
raise TestUpdaterException(output)
12021223
if update_output:
12031224
for line in update_output.splitlines():
12041225
out += f"# {line}\n"

llvm/utils/lit/lit/worker.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import lit.Test
1515
import lit.util
16+
from lit.TestRunner import TestUpdaterException
1617

1718

1819
_lit_config = None
@@ -75,6 +76,10 @@ def _execute_test_handle_errors(test, lit_config):
7576
try:
7677
result = test.config.test_format.execute(test, lit_config)
7778
return _adapt_result(result)
79+
except TestUpdaterException as e:
80+
if lit_config.debug:
81+
raise
82+
return lit.Test.Result(lit.Test.UNRESOLVED, str(e))
7883
except:
7984
if lit_config.debug:
8085
raise
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# RUN: not echo "fail"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import lit.formats
2+
3+
config.name = "pass-test-update"
4+
config.suffixes = [".test"]
5+
config.test_format = lit.formats.ShTest()
6+
config.test_source_root = None
7+
config.test_exec_root = None
8+
9+
import sys
10+
import os
11+
sys.path.append(os.path.dirname(__file__))
12+
from should_not_run import should_not_run
13+
lit_config.test_updaters.append(should_not_run)
14+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# RUN: echo ""
2+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# RUN: echo "passed"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def should_not_run(foo, bar):
2+
raise Exception("this test updater should only run on failure")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# XFAIL: *
2+
# RUN: not echo "failed"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# XFAIL: *
2+
# RUN: echo "accidentally passed"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# RUN: %{lit} --update-tests --ignore-fail -v %S/Inputs/pass-test-update | FileCheck %s --implicit-check-not Exception
2+
3+
# CHECK: UNRESOLVED: pass-test-update :: fail.test (1 of 5)
4+
# CHECK-NEXT: ******************** TEST 'pass-test-update :: fail.test' FAILED ********************
5+
# CHECK-NEXT: # {{R}}UN: at line 1
6+
# CHECK-NEXT: not echo "fail"
7+
# CHECK-NEXT: # executed command: not echo fail
8+
# CHECK-NEXT: # .---command stdout------------
9+
# CHECK-NEXT: # | fail
10+
# CHECK-NEXT: # `-----------------------------
11+
# CHECK-NEXT: # error: command failed with exit status: 1
12+
# CHECK-NEXT: Exception occurred in test updater:
13+
# CHECK-NEXT: Traceback (most recent call last):
14+
# CHECK-NEXT: File {{.*}}, line {{.*}}, in {{.*}}
15+
# CHECK-NEXT: update_output = test_updater(result, test)
16+
# CHECK-NEXT: File "{{.*}}/should_not_run.py", line {{.*}}, in should_not_run
17+
# CHECK-NEXT: raise Exception("this test updater should only run on failure")
18+
# CHECK-NEXT: Exception: this test updater should only run on failure
19+
# CHECK-EMPTY:
20+
# CHECK-NEXT: ********************
21+
# CHECK-NEXT: PASS: pass-test-update :: pass-silent.test (2 of 5)
22+
# CHECK-NEXT: PASS: pass-test-update :: pass.test (3 of 5)
23+
# CHECK-NEXT: {{X}}FAIL: pass-test-update :: xfail.test (4 of 5)
24+
# CHECK-NEXT: XPASS: pass-test-update :: xpass.test (5 of 5)
25+
# CHECK-NEXT: ******************** TEST 'pass-test-update :: xpass.test' FAILED ********************
26+
# CHECK-NEXT: Exit Code: 0
27+
# CHECK-EMPTY:
28+
# CHECK-NEXT: Command Output (stdout):
29+
# CHECK-NEXT: --
30+
# CHECK-NEXT: # {{R}}UN: at line 2
31+
# CHECK-NEXT: echo "accidentally passed"
32+
# CHECK-NEXT: # executed command: echo 'accidentally passed'
33+
# CHECK-NEXT: # .---command stdout------------
34+
# CHECK-NEXT: # | accidentally passed
35+
# CHECK-NEXT: # `-----------------------------
36+
# CHECK-EMPTY:
37+
# CHECK-NEXT: --
38+
# CHECK-EMPTY:
39+
# CHECK-NEXT: ********************

0 commit comments

Comments
 (0)