mongo/buildscripts/tests/test_exception_extractor.py
Sean Lyons b270115356 SERVER-126501: Add bazel targets for buildscripts tests (#53703)
GitOrigin-RevId: eeec1eaf457d23e5fd9e67dd10c171f65df0899b
2026-05-15 17:04:02 +00:00

258 lines
9.7 KiB
Python

import unittest
import buildscripts.resmokelib.logging.handlers as under_test
class TestExceptionExtractor(unittest.TestCase):
def get_exception_extractor(self, truncate=under_test.Truncate.FIRST):
return under_test.ExceptionExtractor("START", "END", truncate)
def get_js_exception_extractor(self):
return under_test.ExceptionExtractor(
r"^uncaught exception:",
r"^exiting with code|^Failure detected from Mocha",
under_test.Truncate.LAST,
)
def get_mocha_exception_extractor(self):
return under_test.ExceptionExtractor(
r"Test Report Summary:",
r"failing tests detected",
under_test.Truncate.LAST,
)
def test_successful_extraction(self):
logs = [
"not captured",
"START",
"captured",
"END",
"not captured",
]
expected_exception = [
"START",
"captured",
"END",
]
exception_extractor = self.get_exception_extractor()
for log in logs:
exception_extractor.process_log_line(log)
assert exception_extractor.exception_detected is True
assert exception_extractor.get_exception() == expected_exception
def test_partial_extraction(self):
logs = [
"not captured",
"START",
"captured",
]
expected_current_exception = [
"START",
"captured",
]
exception_extractor = self.get_exception_extractor()
for log in logs:
exception_extractor.process_log_line(log)
assert exception_extractor.active is True
assert exception_extractor.exception_detected is False
assert list(exception_extractor.current_exception) == expected_current_exception
assert not exception_extractor.get_exception()
def test_no_extraction(self):
logs = [
"not captured",
"not captured",
]
expected_current_exception = []
exception_extractor = self.get_exception_extractor()
for log in logs:
exception_extractor.process_log_line(log)
assert exception_extractor.active is False
assert exception_extractor.exception_detected is False
assert list(exception_extractor.current_exception) == expected_current_exception
assert not exception_extractor.get_exception()
def test_successful_extraction_truncate_first(self):
logs = (
["START"]
+ ["not captured"]
+ ["captured"] * (under_test.DEFAULT_MAX_EXCEPTION_LENGTH - 1)
+ ["END"]
)
expected_exception = (
["[LAST Part of Exception]"]
+ ["captured"] * (under_test.DEFAULT_MAX_EXCEPTION_LENGTH - 1)
+ ["END"]
)
exception_extractor = self.get_exception_extractor()
for log in logs:
exception_extractor.process_log_line(log)
assert exception_extractor.exception_detected is True
assert exception_extractor.get_exception() == expected_exception
def test_successful_extraction_truncate_last(self):
logs = (
["START"]
+ ["captured"] * (under_test.DEFAULT_MAX_EXCEPTION_LENGTH - 1)
+ ["not captured"]
+ ["END"]
)
expected_exception = (
["[FIRST Part of Exception]"]
+ ["START"]
+ ["captured"] * (under_test.DEFAULT_MAX_EXCEPTION_LENGTH - 1)
)
exception_extractor = self.get_exception_extractor(under_test.Truncate.LAST)
for log in logs:
exception_extractor.process_log_line(log)
assert exception_extractor.exception_detected is True
assert exception_extractor.get_exception() == expected_exception
def test_js_extractor_ends_on_exiting_with_code(self):
"""The JS extractor still works for the traditional non-mocha path."""
logs = [
"some other output",
"uncaught exception: Error: assert.eq failed",
"doassert@src/mongo/shell/assert.js:47:14",
"exiting with code 253",
"not captured",
]
expected = [
"uncaught exception: Error: assert.eq failed",
"doassert@src/mongo/shell/assert.js:47:14",
"exiting with code 253",
]
extractor = self.get_js_exception_extractor()
for log in logs:
extractor.process_log_line(log)
assert extractor.exception_detected is True
assert extractor.get_exception() == expected
def test_js_extractor_ends_on_mocha_failure(self):
"""The JS extractor terminates on 'Failure detected from Mocha' for mocha tests."""
logs = [
"[jsTest] some passing test output",
"uncaught exception: Error: 1 failing tests detected :",
"report@jstests/libs/mochalite.js:126:19",
"runTests@jstests/libs/mochalite.js:554:18",
"Error: 1 failing tests detected",
"Failure detected from Mocha test runner",
"not captured",
]
expected = [
"uncaught exception: Error: 1 failing tests detected :",
"report@jstests/libs/mochalite.js:126:19",
"runTests@jstests/libs/mochalite.js:554:18",
"Error: 1 failing tests detected",
"Failure detected from Mocha test runner",
]
extractor = self.get_js_exception_extractor()
for log in logs:
extractor.process_log_line(log)
assert extractor.exception_detected is True
assert extractor.get_exception() == expected
def test_mocha_extractor_captures_summary(self):
"""The mocha extractor captures from 'Test Report Summary:' to 'failing tests detected'."""
logs = [
"[jsTest] some passing test output",
"[jsTest] Test Report Summary:",
"[jsTest] 3 passing",
"[jsTest] 1 failing",
"[jsTest] Failures and stacks are reprinted below.",
"[jsTest] ----",
"[jsTest] ✘ my test > should work",
"[jsTest] Error: assert failed",
"[jsTest] stack@line:1",
"uncaught exception: Error: 1 failing tests detected :",
"not captured",
]
expected = [
"[jsTest] Test Report Summary:",
"[jsTest] 3 passing",
"[jsTest] 1 failing",
"[jsTest] Failures and stacks are reprinted below.",
"[jsTest] ----",
"[jsTest] ✘ my test > should work",
"[jsTest] Error: assert failed",
"[jsTest] stack@line:1",
"uncaught exception: Error: 1 failing tests detected :",
]
extractor = self.get_mocha_exception_extractor()
for log in logs:
extractor.process_log_line(log)
assert extractor.exception_detected is True
assert extractor.get_exception() == expected
def test_mocha_extractor_truncates_long_failure(self):
"""When mocha failure output is long, the summary header (first lines) is preserved."""
# Generate enough lines to exceed DEFAULT_MAX_EXCEPTION_LENGTH (150).
num_filler = under_test.DEFAULT_MAX_EXCEPTION_LENGTH + 10
logs = (
["[jsTest] Test Report Summary:"]
+ ["[jsTest] 36 passing"]
+ ["[jsTest] 1 failing"]
+ ["[jsTest] Failures and stacks are reprinted below."]
+ ["[jsTest] line %d" % i for i in range(num_filler)]
+ ["uncaught exception: Error: 1 failing tests detected :"]
)
extractor = self.get_mocha_exception_extractor()
for log in logs:
extractor.process_log_line(log)
assert extractor.exception_detected is True
result = extractor.get_exception()
# Truncated to DEFAULT_MAX_EXCEPTION_LENGTH, keeping first lines (Truncate.LAST),
# plus the "[FIRST Part of Exception]" label prepended.
assert len(result) == under_test.DEFAULT_MAX_EXCEPTION_LENGTH + 1 # +1 for truncation label
assert result[0] == "[FIRST Part of Exception]"
assert result[1] == "[jsTest] Test Report Summary:"
assert result[2] == "[jsTest] 36 passing"
assert result[3] == "[jsTest] 1 failing"
def test_custom_max_length(self):
"""ExceptionExtractor respects a custom max_length parameter."""
custom_max = 5
extractor = under_test.ExceptionExtractor(
"START", "END", under_test.Truncate.LAST, max_length=custom_max
)
logs = ["START"] + ["line %d" % i for i in range(10)] + ["END"]
for log in logs:
extractor.process_log_line(log)
assert extractor.exception_detected is True
result = extractor.get_exception()
# Truncated to custom_max, plus the truncation label
assert len(result) == custom_max + 1
assert result[0] == "[FIRST Part of Exception]"
assert result[1] == "START"
def test_mocha_extractor_no_match_on_passing_test(self):
"""The mocha extractor does not fire when all tests pass (no 'failing tests detected')."""
logs = [
"[jsTest] Test Report Summary:",
"[jsTest] 3 passing",
"[jsTest] ----",
"some other output",
]
extractor = self.get_mocha_exception_extractor()
for log in logs:
extractor.process_log_line(log)
# start_regex matched so the extractor is active, but end_regex never fired
assert extractor.active is True
assert extractor.exception_detected is False
assert extractor.get_exception() == []
if __name__ == "__main__":
unittest.main()