[tool] xcresult issue discarder (#95273)
This commit is contained in:
parent
82bf318968
commit
e4dadf788b
@ -35,7 +35,11 @@ class XCResultGenerator {
|
||||
///
|
||||
/// Calls `xcrun xcresulttool get --path <resultPath> --format json`,
|
||||
/// then stores the useful information the json into an [XCResult] object.
|
||||
Future<XCResult> generate() async {
|
||||
///
|
||||
/// A`issueDiscarders` can be passed to discard any issues that matches the description of any [XCResultIssueDiscarder] in the list.
|
||||
Future<XCResult> generate(
|
||||
{List<XCResultIssueDiscarder> issueDiscarders =
|
||||
const <XCResultIssueDiscarder>[]}) async {
|
||||
final RunResult result = await processUtils.run(
|
||||
<String>[
|
||||
...xcode.xcrunCommand(),
|
||||
@ -62,7 +66,7 @@ class XCResultGenerator {
|
||||
return XCResult.failed(
|
||||
errorMessage: 'xcresult parser: Unrecognized top level json format.');
|
||||
}
|
||||
return XCResult(resultJson: resultJson);
|
||||
return XCResult(resultJson: resultJson, issueDiscarders: issueDiscarders);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +76,7 @@ class XCResultGenerator {
|
||||
/// The result contains useful information such as build errors and warnings.
|
||||
class XCResult {
|
||||
/// Parse the `resultJson` and stores useful informations in the returned `XCResult`.
|
||||
factory XCResult({required Map<String, Object?> resultJson}) {
|
||||
factory XCResult({required Map<String, Object?> resultJson, List<XCResultIssueDiscarder> issueDiscarders = const <XCResultIssueDiscarder>[]}) {
|
||||
final List<XCResultIssue> issues = <XCResultIssue>[];
|
||||
final Object? actionsMap = resultJson['actions'];
|
||||
if (actionsMap == null || actionsMap is! Map<String, Object?>) {
|
||||
@ -103,32 +107,13 @@ class XCResult {
|
||||
return XCResult.failed(
|
||||
errorMessage: 'xcresult parser: Failed to parse the issues map.');
|
||||
}
|
||||
List<XCResultIssue> _parseIssuesFromIssueSummariesJson({
|
||||
required XCResultIssueType type,
|
||||
required Map<String, Object?> issueSummariesJson,
|
||||
}) {
|
||||
final List<XCResultIssue> issues = <XCResultIssue>[];
|
||||
final Object? errorsList = issueSummariesJson['_values'];
|
||||
if (errorsList is List<Object?>) {
|
||||
for (final Object? issueJson in errorsList) {
|
||||
if (issueJson == null || issueJson is! Map<String, Object?>) {
|
||||
continue;
|
||||
}
|
||||
final XCResultIssue resultIssue = XCResultIssue(
|
||||
type: type,
|
||||
issueJson: issueJson,
|
||||
);
|
||||
issues.add(resultIssue);
|
||||
}
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
|
||||
final Object? errorSummaries = issuesMap['errorSummaries'];
|
||||
if (errorSummaries is Map<String, Object?>) {
|
||||
issues.addAll(_parseIssuesFromIssueSummariesJson(
|
||||
type: XCResultIssueType.error,
|
||||
issueSummariesJson: errorSummaries,
|
||||
issueDiscarder: issueDiscarders,
|
||||
));
|
||||
}
|
||||
|
||||
@ -137,6 +122,7 @@ class XCResult {
|
||||
issues.addAll(_parseIssuesFromIssueSummariesJson(
|
||||
type: XCResultIssueType.warning,
|
||||
issueSummariesJson: warningSummaries,
|
||||
issueDiscarder: issueDiscarders,
|
||||
));
|
||||
}
|
||||
return XCResult._(issues: issues);
|
||||
@ -210,7 +196,8 @@ class XCResultIssue {
|
||||
if (urlValue is String) {
|
||||
location = _convertUrlToLocationString(urlValue);
|
||||
if (location == null) {
|
||||
warnings.add('(XCResult) The `url` exists but it was failed to be parsed. url: $urlValue');
|
||||
warnings.add(
|
||||
'(XCResult) The `url` exists but it was failed to be parsed. url: $urlValue');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -270,6 +257,39 @@ enum XCResultIssueType {
|
||||
error,
|
||||
}
|
||||
|
||||
/// Discards the [XCResultIssue] that matches any of the matchers.
|
||||
class XCResultIssueDiscarder {
|
||||
XCResultIssueDiscarder(
|
||||
{this.typeMatcher,
|
||||
this.subTypeMatcher,
|
||||
this.messageMatcher,
|
||||
this.locationMatcher})
|
||||
: assert(typeMatcher != null ||
|
||||
subTypeMatcher != null ||
|
||||
messageMatcher != null ||
|
||||
locationMatcher != null);
|
||||
|
||||
/// The type of the discarder.
|
||||
///
|
||||
/// A [XCResultIssue] should be discarded if its `type` equals to this.
|
||||
final XCResultIssueType? typeMatcher;
|
||||
|
||||
/// The subType of the discarder.
|
||||
///
|
||||
/// A [XCResultIssue] should be discarded if its `subType` matches the RegExp.
|
||||
final RegExp? subTypeMatcher;
|
||||
|
||||
/// The message of the discarder.
|
||||
///
|
||||
/// A [XCResultIssue] should be discarded if its `message` matches the RegExp.
|
||||
final RegExp? messageMatcher;
|
||||
|
||||
/// The location of the discarder.
|
||||
///
|
||||
/// A [XCResultIssue] should be discarded if its `location` matches the RegExp.
|
||||
final RegExp? locationMatcher;
|
||||
}
|
||||
|
||||
// A typical location url string looks like file:///foo.swift#CharacterRangeLen=0&EndingColumnNumber=82&EndingLineNumber=7&StartingColumnNumber=82&StartingLineNumber=7.
|
||||
//
|
||||
// This function converts it to something like: /foo.swift:<StartingLineNumber>:<StartingColumnNumber>.
|
||||
@ -295,3 +315,60 @@ String? _convertUrlToLocationString(String url) {
|
||||
}
|
||||
return '${fileLocation.path}$startingLineNumber$startingColumnNumber';
|
||||
}
|
||||
|
||||
// Determine if an `issue` should be discarded based on the `discarder`.
|
||||
bool _shouldDiscardIssue(
|
||||
{required XCResultIssue issue, required XCResultIssueDiscarder discarder}) {
|
||||
if (issue.type == discarder.typeMatcher) {
|
||||
return true;
|
||||
}
|
||||
if (issue.subType != null &&
|
||||
discarder.subTypeMatcher != null &&
|
||||
discarder.subTypeMatcher!.hasMatch(issue.subType!)) {
|
||||
return true;
|
||||
}
|
||||
if (issue.message != null &&
|
||||
discarder.messageMatcher != null &&
|
||||
discarder.messageMatcher!.hasMatch(issue.message!)) {
|
||||
return true;
|
||||
}
|
||||
if (issue.location != null &&
|
||||
discarder.locationMatcher != null &&
|
||||
discarder.locationMatcher!.hasMatch(issue.location!)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
List<XCResultIssue> _parseIssuesFromIssueSummariesJson({
|
||||
required XCResultIssueType type,
|
||||
required Map<String, Object?> issueSummariesJson,
|
||||
required List<XCResultIssueDiscarder> issueDiscarder,
|
||||
}) {
|
||||
final List<XCResultIssue> issues = <XCResultIssue>[];
|
||||
final Object? errorsList = issueSummariesJson['_values'];
|
||||
if (errorsList is List<Object?>) {
|
||||
for (final Object? issueJson in errorsList) {
|
||||
if (issueJson == null || issueJson is! Map<String, Object?>) {
|
||||
continue;
|
||||
}
|
||||
final XCResultIssue resultIssue = XCResultIssue(
|
||||
type: type,
|
||||
issueJson: issueJson,
|
||||
);
|
||||
bool discard = false;
|
||||
for (final XCResultIssueDiscarder discarder in issueDiscarder) {
|
||||
if (_shouldDiscardIssue(issue: resultIssue, discarder: discarder)) {
|
||||
discard = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (discard) {
|
||||
continue;
|
||||
}
|
||||
issues.add(resultIssue);
|
||||
}
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
|
@ -128,6 +128,72 @@ void main() {
|
||||
expect(result.parsingErrorMessage, isNull);
|
||||
});
|
||||
|
||||
testWithoutContext(
|
||||
'correctly parse sample result json and discard all warnings', () async {
|
||||
final XCResultGenerator generator = _setupGenerator(resultJson: kSampleResultJsonWithIssues);
|
||||
final XCResultIssueDiscarder discarder = XCResultIssueDiscarder(typeMatcher: XCResultIssueType.warning);
|
||||
final XCResult result = await generator.generate(issueDiscarders: <XCResultIssueDiscarder>[discarder]);
|
||||
expect(result.issues.length, 1);
|
||||
expect(result.issues.first.type, XCResultIssueType.error);
|
||||
expect(result.issues.first.subType, 'Semantic Issue');
|
||||
expect(result.issues.first.message, "Use of undeclared identifier 'asdas'");
|
||||
expect(result.issues.first.location, '/Users/m/Projects/test_create/ios/Runner/AppDelegate.m:7:56');
|
||||
expect(result.parseSuccess, isTrue);
|
||||
expect(result.parsingErrorMessage, isNull);
|
||||
});
|
||||
|
||||
testWithoutContext(
|
||||
'correctly parse sample result json and discard base on subType', () async {
|
||||
final XCResultGenerator generator = _setupGenerator(resultJson: kSampleResultJsonWithIssues);
|
||||
final XCResultIssueDiscarder discarder = XCResultIssueDiscarder(subTypeMatcher: RegExp(r'^Warning$'));
|
||||
final XCResult result = await generator.generate(issueDiscarders: <XCResultIssueDiscarder>[discarder]);
|
||||
expect(result.issues.length, 1);
|
||||
expect(result.issues.first.type, XCResultIssueType.error);
|
||||
expect(result.issues.first.subType, 'Semantic Issue');
|
||||
expect(result.issues.first.message, "Use of undeclared identifier 'asdas'");
|
||||
expect(result.issues.first.location, '/Users/m/Projects/test_create/ios/Runner/AppDelegate.m:7:56');
|
||||
expect(result.parseSuccess, isTrue);
|
||||
expect(result.parsingErrorMessage, isNull);
|
||||
});
|
||||
|
||||
testWithoutContext(
|
||||
'correctly parse sample result json and discard base on message', () async {
|
||||
final XCResultGenerator generator = _setupGenerator(resultJson: kSampleResultJsonWithIssues);
|
||||
final XCResultIssueDiscarder discarder = XCResultIssueDiscarder(messageMatcher: RegExp(r"^The iOS deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.0.99.$"));
|
||||
final XCResult result = await generator.generate(issueDiscarders: <XCResultIssueDiscarder>[discarder]);
|
||||
expect(result.issues.length, 1);
|
||||
expect(result.issues.first.type, XCResultIssueType.error);
|
||||
expect(result.issues.first.subType, 'Semantic Issue');
|
||||
expect(result.issues.first.message, "Use of undeclared identifier 'asdas'");
|
||||
expect(result.issues.first.location, '/Users/m/Projects/test_create/ios/Runner/AppDelegate.m:7:56');
|
||||
expect(result.parseSuccess, isTrue);
|
||||
expect(result.parsingErrorMessage, isNull);
|
||||
});
|
||||
|
||||
testWithoutContext(
|
||||
'correctly parse sample result json and discard base on location', () async {
|
||||
final XCResultGenerator generator = _setupGenerator(resultJson: kSampleResultJsonWithIssues);
|
||||
final XCResultIssueDiscarder discarder = XCResultIssueDiscarder(locationMatcher: RegExp(r'/Users/m/Projects/test_create/ios/Runner/AppDelegate.m'));
|
||||
final XCResult result = await generator.generate(issueDiscarders: <XCResultIssueDiscarder>[discarder]);
|
||||
expect(result.issues.length, 1);
|
||||
expect(result.issues.first.type, XCResultIssueType.warning);
|
||||
expect(result.issues.first.subType, 'Warning');
|
||||
expect(result.issues.first.message,
|
||||
"The iOS deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.0.99.");
|
||||
expect(result.parseSuccess, isTrue);
|
||||
expect(result.parsingErrorMessage, isNull);
|
||||
});
|
||||
|
||||
testWithoutContext(
|
||||
'correctly parse sample result json with multiple discarders.', () async {
|
||||
final XCResultGenerator generator = _setupGenerator(resultJson: kSampleResultJsonWithIssues);
|
||||
final XCResultIssueDiscarder discardWarnings = XCResultIssueDiscarder(typeMatcher: XCResultIssueType.warning);
|
||||
final XCResultIssueDiscarder discardSemanticIssues = XCResultIssueDiscarder(subTypeMatcher: RegExp(r'^Semantic Issue$'));
|
||||
final XCResult result = await generator.generate(issueDiscarders: <XCResultIssueDiscarder>[discardWarnings, discardSemanticIssues]);
|
||||
expect(result.issues, isEmpty);
|
||||
expect(result.parseSuccess, isTrue);
|
||||
expect(result.parsingErrorMessage, isNull);
|
||||
});
|
||||
|
||||
testWithoutContext('correctly parse sample result json when no issues.',
|
||||
() async {
|
||||
|
Loading…
x
Reference in New Issue
Block a user