Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Part 2 - use xcrun xcresulttool get test-results tests for parsing xcresults #300

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

dfrankland
Copy link
Member

@dfrankland dfrankland commented Jan 15, 2025

Relies on #299

Uses JSON schema provided by xcrun xcresulttool help get test-results tests to generate Rust types and parse JSON output. This also helps parse things like retries (now shows up as a reruns in JUnit XML) or test bundles without test suites (or maybe it's the opposite?). There are a few drawbacks though:

  • No file paths to tests
  • Potentially less precise test durations
  • No test plan duration without extra shell out
  • Requires XCode 16 or newer

/trunk skip-check

Trunk Check doesn't work with codegen very well

Copy link

trunk-io bot commented Jan 15, 2025

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

@dfrankland dfrankland changed the title use xcrun xcresulttool get test-results tests for parsing xcresults Part 2 - use xcrun xcresulttool get test-results tests for parsing xcresults Jan 15, 2025
Copy link

trunk-staging-io bot commented Jan 15, 2025

Static BadgeStatic BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

Comment on lines -79 to +78
&org_url_slug,
org_url_slug.clone(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reference was being passed all the way down only to be cloned. That makes it difficult to reason about the overhead these functions have. It's better to push clones upwards in the call stack to make it clearer how much data we're cloning.

Comment on lines -298 to +306
let xcresult = XCResult::new(xcresult_path, repo, org_url_slug);
let junits = xcresult?
.generate_junits()
.map_err(|e| anyhow::anyhow!("Failed to generate junit files from xcresult: {}", e))?;
let xcresult = XCResult::new(xcresult_path, org_url_slug, repo.repo_full_name())?;
let junits = xcresult.generate_junits();
if junits.is_empty() {
return Err(anyhow::anyhow!(
"Failed to generate junit files from xcresult."
));
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new way of parsing things uses a schema, so we don't do any extra parsing after the JSON from xcrun xcresulttool is retrieved.

indexmap = "2.6.0"
lazy_static = "1.5.0"
log = "0.4.22"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this now. Also, crates like this shouldn't log like a CLI, they should function as a library which returns specific errors. We should eventually switch to using something like thiserror to improve error handling ergonomics.

quick-junit = "0.5.0"
regex = "1.11.0"
serde = { version = "1.0.215", default-features = false }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed by the code from JSON schema codegen via typify

["xcrun", "xcresulttool", "help", "get", "test-results", "tests"],
capture_output=True,
text=True,
).stdout.replace("#/schemas", "#/$defs")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typify doesn't work with a top-level schemas property, but does with the standard $defs property

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weird that a help command is used here.

Copy link
Member Author

@dfrankland dfrankland Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I accidentally deleted the comment explaining that this is the only way to get the JSON schema I could find

<testsuites name="xcresult" tests="17" failures="0" errors="0" time="20.664">
<testsuite name="XcresultparserTests.xctest" tests="17" disabled="0" errors="0" failures="0" time="11.490" id="808a5981-6ebf-5882-a048-5e06b4318cee">
<testcase name="testCleanCodeErrors()" classname="XcresultparserTests" time="0.959" id="99d37723-0bfe-5158-a239-3f887805070d">
<testsuites name="xcresult: Xcresultparser-Package" tests="17" failures="0" errors="0">
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to naming this differently, but this is test plan name

<testsuite name="XcresultparserTests.xctest" tests="17" disabled="0" errors="0" failures="0" time="11.490" id="808a5981-6ebf-5882-a048-5e06b4318cee">
<testcase name="testCleanCodeErrors()" classname="XcresultparserTests" time="0.959" id="99d37723-0bfe-5158-a239-3f887805070d">
<testsuites name="xcresult: Xcresultparser-Package" tests="17" failures="0" errors="0">
<testsuite name="XcresultparserTests.XcresultparserTests" tests="17" disabled="0" errors="0" failures="0">
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to naming this differently too. This is the bundle name and the test suite name joined by a .

</testcase>
<testcase name="testCleanCodeWarnings()" classname="XcresultparserTests" time="0.174" id="6240defe-8527-5be7-9b44-1a997021aee5">
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

classname is essentially moved to test suite name, but we can do this differently

<testcase name="testCleanCodeWarnings()" classname="XcresultparserTests" time="0.174" id="6240defe-8527-5be7-9b44-1a997021aee5">
<testcase name="testCleanCodeWarnings()" time="0.170" id="f851e19c-ea03-51c9-8723-f3be81ee8f32">
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why time is slightly different, but I assume it's because the new xcrun xcresulttool reports seconds

Comment on lines -403 to +472
<testcase name="testInitWithMovieSearch()" classname="MovieSearchModelTests" time="0.011" id="6a0ee60a-0a5e-5050-83ac-3eb22a6cb142" file="/Users/work/src/iOS-example/UpcomingMoviesTests/MovieSearchModelTests.swift">
<failure message="XCTAssertEqual failed: (&quot;12345&quot;) is not equal to (&quot;12346&quot;)"/>
</testsuite>
<testsuite name="UpcomingMoviesTests.MovieSearchModelTests" tests="1" disabled="0" errors="0" failures="1">
<testcase name="testInitWithMovieSearch()" time="0.011" id="a4988fb7-1943-5c3b-8ac5-1eccca86cc46">
<failure message="MovieSearchModelTests.swift:21: XCTAssertEqual failed: (&quot;12345&quot;) is not equal to (&quot;12346&quot;)"/>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot about this file attribute... The new xcrun xcresulttool output does not include any files now. It does include "source code references" in the output of the xcrun xcresulttool get test-results test-details though. Let's discuss on Slack or on a call

@dfrankland dfrankland requested review from mmatheson and gnalh January 15, 2025 17:19
Copy link
Collaborator

@gnalh gnalh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes based on this generating a new ID. We don't want to break quarantining and test identification for our users.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if this will work across Xcode versions? I suspect the schema changes with the version -- although probably slightly.

["xcrun", "xcresulttool", "help", "get", "test-results", "tests"],
capture_output=True,
text=True,
).stdout.replace("#/schemas", "#/$defs")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weird that a help command is used here.

capture_output=True,
text=True,
).stdout.replace("#/schemas", "#/$defs")
json_schema_lines = result.split("\n")[3:148]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would be safer with a regex. This could change with versions.

</testcase>
<testcase name="testCleanCodeWarnings()" classname="XcresultparserTests" time="0.174" id="6240defe-8527-5be7-9b44-1a997021aee5">
<testcase name="testCleanCodeWarnings()" time="0.170" id="f851e19c-ea03-51c9-8723-f3be81ee8f32">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this ID the same as previous?

Comment on lines +176 to +179
if let Some(node_identifier) = &xcresult_test_case.node_identifier {
let id = self.generate_id(node_identifier);
test_case.extra.insert("id".into(), id.into());
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will break quarantining and test identification for Zillow. That ID is how we are identifying the test.

Comment on lines +103 to +117
fn xcresult_test_suite_to_junit_test_suite<T: AsRef<str>>(
&self,
xcresult_test_suite: &schema::TestNode,
bundle_name: Option<T>,
) -> TestSuite {
let name = bundle_name
.as_ref()
.map(|bn| format!("{}.{}", bn.as_ref(), xcresult_test_suite.name))
.unwrap_or_else(|| String::from(&xcresult_test_suite.name));
let mut test_suite = TestSuite::new(name);
test_suite.add_test_cases(
self.xcresult_test_cases_to_junit_test_cases(xcresult_test_suite.children.as_slice()),
);
test_suite
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

classpath is optional -- do we even show it in the UI? XCResult has its own id generation so leaving it out should have no impact.

@dfrankland dfrankland changed the base branch from dylan/reorganize-xcresult-test-fixtures to main January 20, 2025 04:02
@dfrankland dfrankland force-pushed the dylan/xcresult-parse branch from e1518d0 to bbafe11 Compare January 20, 2025 04:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants