@@ -9,8 +9,10 @@ import 'package:flutter_tools/src/base/platform.dart';
99import 'package:flutter_tools/src/cache.dart' ;
1010import 'package:flutter_tools/src/commands/build.dart' ;
1111import 'package:flutter_tools/src/commands/build_ios.dart' ;
12+ import 'package:flutter_tools/src/ios/plist_parser.dart' ;
1213import 'package:flutter_tools/src/ios/xcodeproj.dart' ;
1314import 'package:flutter_tools/src/reporting/reporting.dart' ;
15+ import 'package:test/fake.dart' ;
1416
1517import '../../general.shard/ios/xcresult_test_data.dart' ;
1618import '../../src/common.dart' ;
@@ -50,10 +52,20 @@ final Platform notMacosPlatform = FakePlatform(
5052 }
5153);
5254
55+ class FakePlistUtils extends Fake implements PlistParser {
56+ final Map <String , Map <String , Object >> fileContents = < String , Map <String , Object >> {};
57+
58+ @override
59+ String ? getStringValueFromFile (String plistFilePath, String key) {
60+ return fileContents[plistFilePath]! [key] as String ? ;
61+ }
62+ }
63+
5364void main () {
5465 late FileSystem fileSystem;
5566 late TestUsage usage;
5667 late FakeProcessManager fakeProcessManager;
68+ late FakePlistUtils plistUtils;
5769
5870 setUpAll (() {
5971 Cache .disableLocking ();
@@ -63,6 +75,7 @@ void main() {
6375 fileSystem = MemoryFileSystem .test ();
6476 usage = TestUsage ();
6577 fakeProcessManager = FakeProcessManager .empty ();
78+ plistUtils = FakePlistUtils ();
6679 });
6780
6881 // Sets up the minimal mock project files necessary to look like a Flutter project.
@@ -246,8 +259,7 @@ void main() {
246259 FileSystem : () => fileSystem,
247260 ProcessManager : () => FakeProcessManager .any (),
248261 Platform : () => macosPlatform,
249- XcodeProjectInterpreter : () =>
250- FakeXcodeProjectInterpreterWithBuildSettings (),
262+ XcodeProjectInterpreter : () => FakeXcodeProjectInterpreterWithBuildSettings (),
251263 });
252264
253265 testUsingContext ('ipa build fails when --export-options-plist and --export-method are used together' , () async {
@@ -270,8 +282,7 @@ void main() {
270282 FileSystem : () => fileSystem,
271283 ProcessManager : () => FakeProcessManager .any (),
272284 Platform : () => macosPlatform,
273- XcodeProjectInterpreter : () =>
274- FakeXcodeProjectInterpreterWithBuildSettings (),
285+ XcodeProjectInterpreter : () => FakeXcodeProjectInterpreterWithBuildSettings (),
275286 });
276287
277288 testUsingContext ('ipa build reports when IPA fails' , () async {
@@ -521,8 +532,7 @@ void main() {
521532 FileSystem : () => fileSystem,
522533 ProcessManager : () => FakeProcessManager .any (),
523534 Platform : () => macosPlatform,
524- XcodeProjectInterpreter : () =>
525- FakeXcodeProjectInterpreterWithBuildSettings (),
535+ XcodeProjectInterpreter : () => FakeXcodeProjectInterpreterWithBuildSettings (),
526536 });
527537
528538 testUsingContext ('Performs code size analysis and sends analytics' , () async {
@@ -601,8 +611,7 @@ void main() {
601611 FileSystem : () => fileSystem,
602612 ProcessManager : () => fakeProcessManager,
603613 Platform : () => macosPlatform,
604- XcodeProjectInterpreter : () =>
605- FakeXcodeProjectInterpreterWithBuildSettings (),
614+ XcodeProjectInterpreter : () => FakeXcodeProjectInterpreterWithBuildSettings (),
606615 });
607616
608617 testUsingContext ('Trace error if xcresult is empty.' , () async {
@@ -735,6 +744,97 @@ void main() {
735744 Platform : () => macosPlatform,
736745 XcodeProjectInterpreter : () => FakeXcodeProjectInterpreterWithBuildSettings (),
737746 });
747+
748+ testUsingContext (
749+ 'Validate basic Xcode settings with missing settings' , () async {
750+
751+ const String plistPath = 'build/ios/archive/Runner.xcarchive/Products/Applications/Runner.app/Info.plist' ;
752+ fakeProcessManager.addCommands (< FakeCommand > [
753+ xattrCommand,
754+ setUpFakeXcodeBuildHandler (onRun: () {
755+ fileSystem.file (plistPath).createSync (recursive: true );
756+ }),
757+ exportArchiveCommand (exportOptionsPlist: _exportOptionsPlist),
758+ ]);
759+
760+ createMinimalMockProjectFiles ();
761+
762+ plistUtils.fileContents[plistPath] = < String ,String > {
763+ 'CFBundleIdentifier' : 'io.flutter.someProject' ,
764+ };
765+
766+ final BuildCommand command = BuildCommand ();
767+ await createTestCommandRunner (command).run (
768+ < String > ['build' , 'ipa' , '--no-pub' ]);
769+
770+ expect (
771+ testLogger.statusText,
772+ contains (
773+ '┌─ App Settings ────────────────────────────────────────┐\n '
774+ '│ Version Number: Missing │\n '
775+ '│ Build Number: Missing │\n '
776+ '│ Display Name: Missing │\n '
777+ '│ Deployment Target: Missing │\n '
778+ '│ Bundle Identifier: io.flutter.someProject │\n '
779+ '│ │\n '
780+ '│ You must set up the missing settings │\n '
781+ '│ Instructions: https://docs.flutter.dev/deployment/ios │\n '
782+ '└───────────────────────────────────────────────────────┘'
783+ )
784+ );
785+ }, overrides: < Type , Generator > {
786+ FileSystem : () => fileSystem,
787+ ProcessManager : () => fakeProcessManager,
788+ Platform : () => macosPlatform,
789+ XcodeProjectInterpreter : () => FakeXcodeProjectInterpreterWithBuildSettings (),
790+ PlistParser : () => plistUtils,
791+ });
792+
793+ testUsingContext (
794+ 'Validate basic Xcode settings with full settings' , () async {
795+ const String plistPath = 'build/ios/archive/Runner.xcarchive/Products/Applications/Runner.app/Info.plist' ;
796+ fakeProcessManager.addCommands (< FakeCommand > [
797+ xattrCommand,
798+ setUpFakeXcodeBuildHandler (onRun: () {
799+ fileSystem.file (plistPath).createSync (recursive: true );
800+ }),
801+ exportArchiveCommand (exportOptionsPlist: _exportOptionsPlist),
802+ ]);
803+
804+ createMinimalMockProjectFiles ();
805+
806+ plistUtils.fileContents[plistPath] = < String ,String > {
807+ 'CFBundleIdentifier' : 'io.flutter.someProject' ,
808+ 'CFBundleDisplayName' : 'Awesome Gallery' ,
809+ 'MinimumOSVersion' : '11.0' ,
810+ 'CFBundleVersion' : '666' ,
811+ 'CFBundleShortVersionString' : '12.34.56' ,
812+ };
813+
814+ final BuildCommand command = BuildCommand ();
815+ await createTestCommandRunner (command).run (
816+ < String > ['build' , 'ipa' , '--no-pub' ]);
817+
818+ expect (
819+ testLogger.statusText,
820+ contains (
821+ '┌─ App Settings ────────────────────────────┐\n '
822+ '│ Version Number: 12.34.56 │\n '
823+ '│ Build Number: 666 │\n '
824+ '│ Display Name: Awesome Gallery │\n '
825+ '│ Deployment Target: 11.0 │\n '
826+ '│ Bundle Identifier: io.flutter.someProject │\n '
827+ '└───────────────────────────────────────────┘\n '
828+ )
829+ );
830+ }, overrides: < Type , Generator > {
831+ FileSystem : () => fileSystem,
832+ ProcessManager : () => fakeProcessManager,
833+ Platform : () => macosPlatform,
834+ XcodeProjectInterpreter : () => FakeXcodeProjectInterpreterWithBuildSettings (),
835+ PlistParser : () => plistUtils,
836+ });
837+
738838}
739839
740840const String _xcBundleFilePath = '/.tmp_rand0/flutter_ios_build_temp_dirrand0/temporary_xcresult_bundle' ;
0 commit comments