Experimental Event Stream Doesn't Issue `issueRecorded`,`testEnded`, `runEnded` Events For Crashed Tests?

I'm trying to use the --experimental-event-stream-output to get events about testing, like the following.

swift test \
  --experimental-event-stream-output <path> \
  --experimental-event-stream-version <ABI-version>

I noticed that if a test crashes, e.g. because of index out of bound, assert, fatalError, ..., the last event for the crashed test will always be runStarted, as if the test never ends.

For example, given tests

import Testing

@Test func `array access crash`() {
    let array = [UInt8]()
    #expect(array[0] == 0)
}

@Test func `fatal error crash`() {
    fatalError("This is a fatal error crash test.")
}

The even stream will be

{
    "kind": "test",
    "payload": {
        "displayName": "array access crash",
        "id": "SimpleTestCrashTests.`array access crash`()\/SimpleTestCrashTests.swift:3:2",
        "isParameterized": false,
        "kind": "function",
        "name": "`array access crash`()",
        "sourceLocation": {
            "_filePath": "\/SimpleTestCrash\/Tests\/SimpleTestCrashTests\/SimpleTestCrashTests.swift",
            "column": 2,
            "fileID": "SimpleTestCrashTests\/SimpleTestCrashTests.swift",
            "line": 3
        }
    },
    "version": 0
}
{
    "kind": "test",
    "payload": {
        "displayName": "fatal error crash",
        "id": "SimpleTestCrashTests.`fatal error crash`()\/SimpleTestCrashTests.swift:8:2",
        "isParameterized": false,
        "kind": "function",
        "name": "`fatal error crash`()",
        "sourceLocation": {
            "_filePath": "\/SimpleTestCrash\/Tests\/SimpleTestCrashTests\/SimpleTestCrashTests.swift",
            "column": 2,
            "fileID": "SimpleTestCrashTests\/SimpleTestCrashTests.swift",
            "line": 8
        }
    },
    "version": 0
}
{
    "kind": "event",
    "payload": {
        "instant": {
            "absolute": 376990.417884416,
            "since1970": 1774627675.367574
        },
        "kind": "runStarted",
        "messages": [
            {
                "symbol": "default",
                "text": "Test run started."
            },
            {
                "symbol": "details",
                "text": "Testing Library Version: 6.3 (980fec0f03c56f7)"
            },
            {
                "symbol": "details",
                "text": "Target Platform: arm64-apple-macosx"
            }
        ]
    },
    "version": 0
}
{
    "kind": "event",
    "payload": {
        "instant": {
            "absolute": 376990.418342375,
            "since1970": 1774627675.368032
        },
        "kind": "testStarted",
        "messages": [
            {
                "symbol": "default",
                "text": "Test \"fatal error crash\" started."
            }
        ],
        "testID": "SimpleTestCrashTests.`fatal error crash`()\/SimpleTestCrashTests.swift:8:2"
    },
    "version": 0
}
{
    "kind": "event",
    "payload": {
        "instant": {
            "absolute": 376990.418342333,
            "since1970": 1774627675.368032
        },
        "kind": "testStarted",
        "messages": [
            {
                "symbol": "default",
                "text": "Test \"array access crash\" started."
            }
        ],
        "testID": "SimpleTestCrashTests.`array access crash`()\/SimpleTestCrashTests.swift:3:2"
    },
    "version": 0
}

I would expect to receive issueRecorded, testEnded, and runEnded like regular failed tests, like

import Testing

@Test func `failed test`() {
    #expect(false)
}

which issues the following events

{
    "kind": "test",
    "payload": {
        "displayName": "failed test",
        "id": "SimpleTestSomeFailedTests.`failed test`()\/SimpleTestSomeFailedTests.swift:3:2",
        "isParameterized": false,
        "kind": "function",
        "name": "`failed test`()",
        "sourceLocation": {
            "_filePath": "\/Users\/flicker_soul\/Documents\/Developer\/swift\/SwiftDocTest\/Tests\/DocTestRunnerTests\/Props\/SimpleTestSomeFailed\/Tests\/SimpleTestSomeFailedTests\/SimpleTestSomeFailedTests.swift",
            "column": 2,
            "fileID": "SimpleTestSomeFailedTests\/SimpleTestSomeFailedTests.swift",
            "filePath": "\/Users\/flicker_soul\/Documents\/Developer\/swift\/SwiftDocTest\/Tests\/DocTestRunnerTests\/Props\/SimpleTestSomeFailed\/Tests\/SimpleTestSomeFailedTests\/SimpleTestSomeFailedTests.swift",
            "line": 3
        }
    },
    "version": "6.3.0"
}
{
    "kind": "event",
    "payload": {
        "instant": {
            "absolute": 378958.348472375,
            "since1970": 1774629643.307811
        },
        "kind": "runStarted",
        "messages": [
            {
                "symbol": "default",
                "text": "Test run started."
            },
            {
                "symbol": "details",
                "text": "Testing Library Version: 6.3 (980fec0f03c56f7)"
            },
            {
                "symbol": "details",
                "text": "Target Platform: arm64-apple-macosx"
            }
        ]
    },
    "version": "6.3.0"
}
{
    "kind": "event",
    "payload": {
        "instant": {
            "absolute": 378958.348759333,
            "since1970": 1774629643.308098
        },
        "kind": "testStarted",
        "messages": [
            {
                "symbol": "default",
                "text": "Test \"failed test\" started."
            }
        ],
        "testID": "SimpleTestSomeFailedTests.`failed test`()\/SimpleTestSomeFailedTests.swift:3:2"
    },
    "version": "6.3.0"
}
{
    "kind": "event",
    "payload": {
        "instant": {
            "absolute": 378958.349141833,
            "since1970": 1774629643.30848
        },
        "issue": {
            "isFailure": true,
            "isKnown": false,
            "severity": "error",
            "sourceLocation": {
                "_filePath": "\/Users\/flicker_soul\/Documents\/Developer\/swift\/SwiftDocTest\/Tests\/DocTestRunnerTests\/Props\/SimpleTestSomeFailed\/Tests\/SimpleTestSomeFailedTests\/SimpleTestSomeFailedTests.swift",
                "column": 5,
                "fileID": "SimpleTestSomeFailedTests\/SimpleTestSomeFailedTests.swift",
                "filePath": "\/Users\/flicker_soul\/Documents\/Developer\/swift\/SwiftDocTest\/Tests\/DocTestRunnerTests\/Props\/SimpleTestSomeFailed\/Tests\/SimpleTestSomeFailedTests\/SimpleTestSomeFailedTests.swift",
                "line": 4
            }
        },
        "kind": "issueRecorded",
        "messages": [
            {
                "symbol": "fail",
                "text": "Expectation failed: false"
            }
        ],
        "testID": "SimpleTestSomeFailedTests.`failed test`()\/SimpleTestSomeFailedTests.swift:3:2"
    },
    "version": "6.3.0"
}
{
    "kind": "event",
    "payload": {
        "instant": {
            "absolute": 378958.349297041,
            "since1970": 1774629643.308635
        },
        "kind": "testEnded",
        "messages": [
            {
                "symbol": "fail",
                "text": "Test \"failed test\" failed after 0.001 seconds with 1 issue."
            }
        ],
        "testID": "SimpleTestSomeFailedTests.`failed test`()\/SimpleTestSomeFailedTests.swift:3:2"
    },
    "version": "6.3.0"
}
{
    "kind": "event",
    "payload": {
        "instant": {
            "absolute": 378958.349480958,
            "since1970": 1774629643.308819
        },
        "kind": "runEnded",
        "messages": [
            {
                "symbol": "fail",
                "text": "Test run with 1 test in 0 suites failed after 0.001 seconds with 1 issue."
            }
        ]
    },
    "version": "6.3.0"
}

Is this a design choice or a bug in the library? :thinking: I found a potential related issue Crashing tests result in empty xunit output with Swift Testing · Issue #1110 · swiftlang/swift-testing · GitHub , which seems to suggest this behavior is not expected.

Neither. A crashing test process will, by definition, stop the outflow of data from that process. From the log you posted, the last event is testStarted rather than runStarted, which is expected if the test immediately crashes upon starting.

#1110 covers a broader effort to separate the "test harness" from the test code itself. In theory, a resolution to that issue would allow us to provide some sort of diagnostic information in the event stream indicating that it ended abnormally, but we haven't designed anything in that area as of yet so I couldn't say exactly what output would be produced.