[tool] Accept plutil diagnostics from stderr in plist_parser_test (macOS 26) (#193292) Fixes https://github.com/flutter/flutter/issues/193159 ## What's failing Two tests in `plist_parser_test.dart` fail on macOS 26 (first seen on `staging.shadow/Mac tool_integration_tests_1` at Mac-26.6.2 / Xcode 17C52, https://ci.chromium.org/b/8670252478857878881): - `PlistParser.getValueFromFile<String> returns null for a malformed plist file` - `PlistParser.replaceKey returns false for a malformed plist file` Both tests still get the right result (`null` / `false`, and `plutil` still exits 1). What changed is where `plutil` prints its error: - Before macOS 26, `plutil` printed `<path>: Property List error: … / JSON error: …` on **stdout**. - On macOS 26, `plutil` was rewritten (Apple open-sourced the new version in swift-corelibs-foundation, `Sources/plutil/PLUContext.swift`) and the same message now goes to **stderr**. `ProcessUtils.runSync` logs a failed command's stdout with `printStatus` and its stderr with `printError`. So on macOS 26 `logger.statusText` is empty and the diagnostic shows up in `logger.errorText` instead, ahead of the `ProcessException:` line. The tests only looked at `statusText`. #172913 ran into the same plutil change last year in `xcode_backend.dart`. ## The fix Test-only change. `PlistParser` itself is fine. - Look for the plutil diagnostic in `statusText + errorText` rather than `statusText` alone, so the test passes whichever stream plutil uses. The full message is still asserted, so a change in the wording would still be caught. - Check that `errorText` ends with the `ProcessException` block instead of equalling it, since on macOS 26 the plutil message now comes first. ## How I verified it - Ran the tests locally. - led run of `Mac tool_integration_tests_1` (subshard `1_10`, the one that contains `plist_parser_test.dart`) on a Mac-26.6.2 bot (`mac-340-g140`, the same bot as the original failure) at this PR's head: https://ci.chromium.org/b/8669753954317678657 — green, both tests pass. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
diff --git a/packages/flutter_tools/test/integration.shard/plist_parser_test.dart b/packages/flutter_tools/test/integration.shard/plist_parser_test.dart index bf517be..a827fbe 100644 --- a/packages/flutter_tools/test/integration.shard/plist_parser_test.dart +++ b/packages/flutter_tools/test/integration.shard/plist_parser_test.dart
@@ -141,8 +141,13 @@ file.writeAsBytesSync(const <int>[1, 2, 3, 4, 5, 6]); expect(parser.getValueFromFile<String>(file.path, 'CFBundleIdentifier'), null); + // Before macOS 26, plutil writes its parse diagnostic to stdout; on + // macOS 26 it writes the same text to stderr. ProcessUtils.runSync + // forwards stdout to the status log and stderr to the error log, so + // accept the diagnostic from either stream. + final String output = logger.statusText + logger.errorText; expect( - logger.statusText, + output, contains( 'Property List error: Unexpected character \x01 at line 1 / ' 'JSON error: JSON text did not start with array or object and option to allow fragments not ' @@ -151,8 +156,10 @@ ); expect( logger.errorText, - 'ProcessException: The command failed with exit code 1\n' - ' Command: /usr/bin/plutil -convert xml1 -o - ${file.absolute.path}\n', + endsWith( + 'ProcessException: The command failed with exit code 1\n' + ' Command: /usr/bin/plutil -convert xml1 -o - ${file.absolute.path}\n', + ), ); }, skip: !platform.isMacOS, // [intended] requires macos tool chain. @@ -229,8 +236,11 @@ parser.replaceKey(file.path, key: 'CFBundleIdentifier', value: 'dev.flutter.fake'), isFalse, ); + // See the malformed-plist getValueFromFile test above: the diagnostic + // comes from stdout before macOS 26 and from stderr on macOS 26. + final String output = logger.statusText + logger.errorText; expect( - logger.statusText, + output, contains( 'foo.plist: Property List error: Unexpected character \x01 ' 'at line 1 / JSON error: JSON text did not start with array or object and option to allow ' @@ -239,7 +249,7 @@ ); expect( logger.errorText, - equals( + endsWith( 'ProcessException: The command failed with exit code 1\n' ' Command: /usr/bin/plutil -replace CFBundleIdentifier -string dev.flutter.fake foo.plist\n', ),