258 lines
9.7 KiB
Python
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()
|