Fix: support xcresult node types - #503
Conversation
priitlatt
left a comment
There was a problem hiding this comment.
Outputs of xcrun xcresulttool get test-results summary --schema on Xcode 26.2 and Xcode 27.0:
And their diff
57c57,66
< "$ref": "#/schemas/TestFailure"
---
> "type": "array",
> "items": {
> "$ref": "#/schemas/TestFailure"
> }
> },
> "runtimeWarnings": {
> "type": "array",
> "items": {
> "$ref": "#/schemas/Issue"
> }
72c81,82
< "testFailures"
---
> "testFailures",
> "runtimeWarnings"
229a240,263
> },
> "Issue": {
> "type": "object",
> "properties": {
> "issueType": {
> "type": "string"
> },
> "message": {
> "type": "string"
> },
> "targetName": {
> "type": "string"
> },
> "sourceURL": {
> "type": "string"
> },
> "className": {
> "type": "string"
> }
> },
> "required": [
> "issueType",
> "message"
> ]Similarly, outputs of xcrun xcresulttool get test-results tests --schema on Xcode 26.2 and Xcode 27.0:
And their diff
84a85,87
> "nodeIdentifierURL": {
> "type": "string"
> },
111a115,117
> "sourceLocation": {
> "$ref": "#/schemas/SourceLocation"
> },
152c158,160
< "Runtime Warning"
---
> "Runtime Warning",
> "Skip Message",
> "Expected Failure"
153a162,176
> },
> "SourceLocation": {
> "type": "object",
> "properties": {
> "filePath": {
> "type": "string"
> },
> "lineNumber": {
> "type": "integer"
> }
> },
> "required": [
> "filePath",
> "lineNumber"
> ]Note that schemas are identical on Xcode versions 27.0, 27.1 and 27.2.
From the above diffs we can see that in addition to the crash patch there are some missing parts that we're currently not modeling in any way:
Summary.runtimeWarnings: [Issue]is missing fromXcSummaryTestNode.sourceLocationis missing fromXcTestNode. Various Junit XML implementations support both file and line refs, see nextest-rs/nextest#2420 for example.TestNode.nodeIdentifierURLis missing fromXcTestNode.
What are the plans with missing data that is present in the new schema?
| skip_message_nodes = cls._iter_nodes(xc_test_case, XcTestNodeType.SKIP_MESSAGE) | ||
| skipped_messages.extend(node.name for node in skip_message_nodes if node.name) |
There was a problem hiding this comment.
There are now different sources for skipped messages. Is it possible that we can capture duplicate messages? I'd add this to filter out anything that we don't need:
unique_skipped_messages = dict.fromkeys(skipped_messages)There was a problem hiding this comment.
Now that there is support for expected failures I think this has an effect on our _get_test_case_error function. It might be that expected-failure messages can leak into the Error of a failed test. I'm not sure if this is indeed the case, but if so, we should filter out the expected-failures from failure_messages. That would have to be verified with an actual test which contains both expected failures and errors.
If true, then the error building needs to be made "smarter" to exclude the expected-failures.
Possible patch
class Xcode16XcResultConverter(XcResultConverter):
def _iter_nodes(
cls,
root_node: XcTestNode,
node_type: XcTestNodeType,
skip_subtree_node_type: Optional[XcTestNodeType] = None,
) -> Iterator[XcTestNode]:
if root_node.node_type is node_type:
yield root_node
elif root_node.node_type is not skip_subtree_node_type:
for child in root_node.children:
yield from cls._iter_nodes(child, node_type, skip_subtree_node_type)
@classmethod
def _get_test_case_error(cls, xc_test_case: XcTestNode) -> Optional[Error]:
if xc_test_case.result is not XcTestResult.FAILED:
return None
# Exclude expected failures which can accompany actual failures in the same test case.
failure_messages_nodes = cls._iter_nodes(
xc_test_case,
XcTestNodeType.FAILURE_MESSAGE,
skip_subtree_node_type=XcTestNodeType.EXPECTED_FAILURE,
)
unexpected_failure_message_nodes = (
node for node in failure_messages_nodes if node.result is not XcTestResult.EXPECTED_FAILURE
)
failure_messages = [node.name for node in unexpected_failure_message_nodes if node.name]
return Error(
message=failure_messages[0] if failure_messages else "",
type="Error" if any("caught error" in m for m in failure_messages) else "Failure",
error_description="\n".join(failure_messages) if len(failure_messages) > 1 else None,
)|
This PR is a great reminder that we should get rid of |
|
Thanks for sharing schema notes diff.
I propose to keep the new fields out of the scope of this PR. It seems like they don't break parsing. Happy to follow up separately. |
Co-authored-by: Priit Lätt <lattpriit@gmail.com>
xcresulttooltest-results schema 0.2.0 (default on Xcode 27) adds XCTest node typesExpected FailureandSkip Message. Neither was supported inXcTestNodeType, so parsing failed when such a node was present in a*.xcresultbundle (e.g. fromXCTExpectFailure/XCTSkip):Summary
Expected FailureandSkip MessageXCTest node types from xcresulttool schema 0.2.0+Skip Messagechildren (still support Xcode 16Failure Message+result=Skipped); dedupe identical messages from both sources<error>when a test case also has a real failureXCTExpectFailurereason text in JUnit<system-out>for expected failures, including failed cases that also have expected failures (not only whenstatus="Expected Failure")Out of scope
Additive schema fields (
runtimeWarnings,sourceLocation,nodeIdentifierURL) are ignored by ourfrom_dictpaths and are not modeled in this PR.Codemagic builder/UI does not yet map
@status="Expected Failure"/<system-out>(expected failures still show as passes in the overview until a follow-up).QA
Failure Message+result=Skippedstill works (unit test)_get_test_case_erroron mixed fail cases (unit test)status+<system-out>as intended (unit + least)<skipped message="...">Xcode app tests
JUnit from Xcode app tests
Build: 6ac49e8084481fd34b0c621c — convert succeeded; build failed intentionally on mixed probe (exit 65). Artifact:
test-reports/test-results.xml.@statustestExpectedFailure()Expected Failure<system-out>known bad assertion</system-out>testExpectedFailureThenRealFailure()Failed<error message="XCTAssertEqual failed: ("3") is not equal to ("4") - should be real failure" type="Failure"/>+<system-out>known bad assertion</system-out>testPass()PassedtestSkip()Skipped<skipped message="Test skipped - skipping on purpose"/>Details about schema changes in 0.2.0+