From cdc40b52260c57923192fcf9fc96718d0b07a44d Mon Sep 17 00:00:00 2001 From: Andrew Kolos Date: Tue, 17 Oct 2023 15:31:38 -0700 Subject: [PATCH] clean up `--dart-define-from-file` option tests (#135980) Fixes https://github.com/flutter/flutter/issues/134279. Changes: * Moves all tests of `--dart-define-from-file` behavior from `build_bundle_test.dart` and `assemble_test.dart` to `flutter_command_test.dart`. * Deletes a duplicate test of malformed JSON detection behavior. * Renames the `useDartDefineFromFileOption` method of `FlutterCommand` to `_usesDartDefineFromFileOption`. This 1) makes the name more consistent with the other `uses*Option` methods and 2) hides the method since it is not used outside of the file. * Renames several tests to better articulate what is under test and what the expected result is. * Adds a test for the case where a `.env` file with a malformed line is provided to `--dart-define-from-file`. --- .../lib/src/runner/flutter_command.dart | 11 +- .../hermetic/assemble_test.dart | 45 --- .../permeable/build_bundle_test.dart | 340 ---------------- .../runner/flutter_command_test.dart | 365 ++++++++++++++++++ 4 files changed, 371 insertions(+), 390 deletions(-) diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 4a07581a90..13af44547b 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -656,10 +656,10 @@ abstract class FlutterCommand extends Command { valueHelp: 'foo=bar', splitCommas: false, ); - useDartDefineFromFileOption(); + _usesDartDefineFromFileOption(); } - void useDartDefineFromFileOption() { + void _usesDartDefineFromFileOption() { argParser.addMultiOption( FlutterOptions.kDartDefineFromFileOption, help: @@ -1441,9 +1441,10 @@ abstract class FlutterCommand extends Command { dartDefineConfigJsonMap[key] = value; }); } on FormatException catch (err) { - throwToolExit('Json config define file "--${FlutterOptions - .kDartDefineFromFileOption}=$path" format err, ' - 'please fix first! format err:\n$err'); + throwToolExit('Unable to parse the file at path "$path" due to a formatting error. ' + 'Ensure that the file contains valid JSON.\n' + 'Error details: $err' + ); } } } diff --git a/packages/flutter_tools/test/commands.shard/hermetic/assemble_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/assemble_test.dart index 4e302df9b8..8d4208f85c 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/assemble_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/assemble_test.dart @@ -290,49 +290,4 @@ void main() { ], }); }); - - testUsingContext('test --dart-define-from-file option with err file format', () { - globals.fs.directory('config').createSync(); - final CommandRunner commandRunner = createTestCommandRunner(AssembleCommand( - buildSystem: TestBuildSystem.all(BuildResult(success: true)), - )); - - expect(commandRunner.run(['assemble', - '-o Output', - 'debug_macos_bundle_flutter_assets', - '--dart-define=k=v', - '--dart-define-from-file=config']), - throwsToolExit(message: 'Did not find the file passed to "--dart-define-from-file". Path: config')); - }, overrides: { - Cache: () => Cache.test(processManager: FakeProcessManager.any()), - FileSystem: () => MemoryFileSystem.test(), - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('test --dart-define-from-file option with err json format', () async { - await globals.fs.file('config.json').writeAsString( - ''' - { - "kInt": 1, - "kDouble": 1.1, - "name": "err json format, - "title": "this is title from config json file" - } - ''' - ); - final CommandRunner commandRunner = createTestCommandRunner(AssembleCommand( - buildSystem: TestBuildSystem.all(BuildResult(success: true)), - )); - - expect(commandRunner.run(['assemble', - '-o Output', - 'debug_macos_bundle_flutter_assets', - '--dart-define=k=v', - '--dart-define-from-file=config.json']), - throwsToolExit(message: 'Json config define file "--dart-define-from-file=config.json" format err, please fix first! format err:')); - }, overrides: { - Cache: () => Cache.test(processManager: FakeProcessManager.any()), - FileSystem: () => MemoryFileSystem.test(), - ProcessManager: () => FakeProcessManager.any(), - }); } diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart index f20ec4999d..761bc18904 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:typed_data'; - import 'package:args/command_runner.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; @@ -14,7 +12,6 @@ import 'package:flutter_tools/src/bundle.dart'; import 'package:flutter_tools/src/bundle_builder.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build_bundle.dart'; -import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/project.dart'; @@ -506,343 +503,6 @@ void main() { FileSystem: fsFactory, ProcessManager: () => FakeProcessManager.any(), }); - - testUsingContext('--dart-define-from-file successfully forwards values to build env', () async { - globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); - globals.fs.file('pubspec.yaml').createSync(); - globals.fs.file('.packages').createSync(); - await globals.fs.file('config1.json').writeAsString( - ''' - { - "kInt": 1, - "kDouble": 1.1, - "name": "denghaizhu", - "title": "this is title from config json file", - "nullValue": null, - "containEqual": "sfadsfv=432f" - } - ''' - ); - await globals.fs.file('config2.json').writeAsString( - ''' - { - "body": "this is body from config json file" - } - ''' - ); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( - logger: BufferLogger.test(), - )); - - await runner.run([ - 'bundle', - '--no-pub', - '--dart-define-from-file=config1.json', - '--dart-define-from-file=config2.json', - ]); - }, overrides: { - BuildSystem: () => TestBuildSystem.all(BuildResult(success: true), (Target target, Environment environment) { - expect( - _decodeDartDefines(environment), - containsAllInOrder(const [ - 'kInt=1', - 'kDouble=1.1', - 'name=denghaizhu', - 'title=this is title from config json file', - 'nullValue=null', - 'containEqual=sfadsfv=432f', - 'body=this is body from config json file', - ]), - ); - }), - FileSystem: fsFactory, - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('values from --dart-define supersede values from --dart-define-from-file', () async { - globals.fs - .file(globals.fs.path.join('lib', 'main.dart')) - .createSync(recursive: true); - globals.fs.file('pubspec.yaml').createSync(); - globals.fs.file('.packages').createSync(); - globals.fs.file('.env').writeAsStringSync(''' - MY_VALUE=VALUE_FROM_ENV_FILE - '''); - final CommandRunner runner = - createTestCommandRunner(BuildBundleCommand( - logger: BufferLogger.test(), - )); - - await runner.run([ - 'bundle', - '--no-pub', - '--dart-define=MY_VALUE=VALUE_FROM_COMMAND', - '--dart-define-from-file=.env', - ]); - - }, overrides: { - BuildSystem: () => TestBuildSystem.all(BuildResult(success: true), - (Target target, Environment environment) { - expect( - _decodeDartDefines(environment), - containsAllInOrder(const [ - 'MY_VALUE=VALUE_FROM_ENV_FILE', - 'MY_VALUE=VALUE_FROM_COMMAND', - ]), - ); - }), - FileSystem: fsFactory, - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('--dart-define-from-file correctly parses a valid env file', () async { - globals.fs - .file(globals.fs.path.join('lib', 'main.dart')) - .createSync(recursive: true); - globals.fs.file('pubspec.yaml').createSync(); - globals.fs.file('.packages').createSync(); - await globals.fs.file('.env').writeAsString(''' - # comment - kInt=1 - kDouble=1.1 # should be double - - name=piotrfleury - title=this is title from config env file - empty= - - doubleQuotes="double quotes 'value'#=" # double quotes - singleQuotes='single quotes "value"#=' # single quotes - backQuotes=`back quotes "value" '#=` # back quotes - - hashString="some-#-hash-string-value" - - # Play around with spaces around the equals sign. - spaceBeforeEqual =value - spaceAroundEqual = value - spaceAfterEqual= value - - '''); - await globals.fs.file('.env2').writeAsString(''' - # second comment - - body=this is body from config env file - '''); - final CommandRunner runner = - createTestCommandRunner(BuildBundleCommand( - logger: BufferLogger.test(), - )); - - await runner.run([ - 'bundle', - '--no-pub', - '--dart-define-from-file=.env', - '--dart-define-from-file=.env2', - ]); - }, overrides: { - BuildSystem: () => TestBuildSystem.all(BuildResult(success: true), - (Target target, Environment environment) { - expect( - _decodeDartDefines(environment), - containsAllInOrder(const [ - 'kInt=1', - 'kDouble=1.1', - 'name=piotrfleury', - 'title=this is title from config env file', - 'empty=', - "doubleQuotes=double quotes 'value'#=", - 'singleQuotes=single quotes "value"#=', - 'backQuotes=back quotes "value" \'#=', - 'hashString=some-#-hash-string-value', - 'spaceBeforeEqual=value', - 'spaceAroundEqual=value', - 'spaceAfterEqual=value', - 'body=this is body from config env file' - ]), - ); - }), - FileSystem: fsFactory, - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('--dart-define-from-file option env file throws a ToolExit when .env file contains a multiline value', () async { - globals.fs - .file(globals.fs.path.join('lib', 'main.dart')) - .createSync(recursive: true); - globals.fs.file('pubspec.yaml').createSync(); - globals.fs.file('.packages').createSync(); - await globals.fs.file('.env').writeAsString(''' - # single line value - name=piotrfleury - - # multi-line value - multiline = """ Welcome to .env demo - a simple counter app with .env file support - for more info, check out the README.md file - Thanks! """ # This is the welcome message that will be displayed on the counter app - - '''); - final CommandRunner runner = - createTestCommandRunner(BuildBundleCommand( - logger: BufferLogger.test(), - )); - - expect(() => runner.run([ - 'bundle', - '--no-pub', - '--dart-define-from-file=.env', - ]), throwsToolExit(message: 'Multi-line value is not supported: multiline = """ Welcome to .env demo')); - }, overrides: { - BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)), - FileSystem: fsFactory, - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('--dart-define-from-file option works with mixed file formats', - () async { - globals.fs - .file(globals.fs.path.join('lib', 'main.dart')) - .createSync(recursive: true); - globals.fs.file('pubspec.yaml').createSync(); - globals.fs.file('.packages').createSync(); - await globals.fs.file('.env').writeAsString(''' - kInt=1 - kDouble=1.1 - name=piotrfleury - title=this is title from config env file - '''); - await globals.fs.file('config.json').writeAsString(''' - { - "body": "this is body from config json file" - } - '''); - final CommandRunner runner = - createTestCommandRunner(BuildBundleCommand( - logger: BufferLogger.test(), - )); - - await runner.run([ - 'bundle', - '--no-pub', - '--dart-define-from-file=.env', - '--dart-define-from-file=config.json', - ]); - }, overrides: { - BuildSystem: () => TestBuildSystem.all(BuildResult(success: true), - (Target target, Environment environment) { - expect( - _decodeDartDefines(environment), - containsAllInOrder(const [ - 'kInt=1', - 'kDouble=1.1', - 'name=piotrfleury', - 'title=this is title from config env file', - 'body=this is body from config json file', - ]), - ); - }), - FileSystem: fsFactory, - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('test --dart-define-from-file option if conflict', () async { - globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); - globals.fs.file('pubspec.yaml').createSync(); - globals.fs.file('.packages').createSync(); - await globals.fs.file('config1.json').writeAsString( - ''' - { - "kInt": 1, - "kDouble": 1.1, - "name": "denghaizhu", - "title": "this is title from config json file" - } - ''' - ); - await globals.fs.file('config2.json').writeAsString( - ''' - { - "kInt": "2" - } - ''' - ); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( - logger: BufferLogger.test(), - )); - - await runner.run([ - 'bundle', - '--no-pub', - '--dart-define-from-file=config1.json', - '--dart-define-from-file=config2.json', - ]); - }, overrides: { - BuildSystem: () => TestBuildSystem.all(BuildResult(success: true), (Target target, Environment environment) { - expect( - _decodeDartDefines(environment), - containsAllInOrder(['kInt=2', 'kDouble=1.1', 'name=denghaizhu', 'title=this is title from config json file']), - ); - }), - FileSystem: fsFactory, - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('test --dart-define-from-file option by invalid file type', () { - globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); - globals.fs.file('pubspec.yaml').createSync(); - globals.fs.file('.packages').createSync(); - globals.fs.directory('config').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( - logger: BufferLogger.test(), - )); - - expect(() => runner.run([ - 'bundle', - '--no-pub', - '--dart-define-from-file=config', - ]), throwsToolExit(message: 'Did not find the file passed to "--dart-define-from-file". Path: config')); - }, overrides: { - FileSystem: fsFactory, - BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)), - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('test --dart-define-from-file option by corrupted json', () async { - globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); - globals.fs.file('pubspec.yaml').createSync(); - globals.fs.file('.packages').createSync(); - await globals.fs.file('config.json').writeAsString( - ''' - { - "kInt": 1Error json format - "kDouble": 1.1, - "name": "denghaizhu", - "title": "this is title from config json file" - } - ''' - ); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( - logger: BufferLogger.test(), - )); - - expect(() => runner.run([ - 'bundle', - '--no-pub', - '--dart-define-from-file=config.json', - ]), throwsToolExit(message: 'Json config define file "--dart-define-from-file=config.json" format err')); - }, overrides: { - FileSystem: fsFactory, - BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)), - ProcessManager: () => FakeProcessManager.any(), - }); -} - -Iterable _decodeDartDefines(Environment environment) { - final String encodedDefines = environment.defines[kDartDefines]!; - const Utf8Decoder byteDecoder = Utf8Decoder(); - return encodedDefines - .split(',') - .map(base64.decode) - .map(byteDecoder.convert); } class FakeBundleBuilder extends Fake implements BundleBuilder { diff --git a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart index 9f6d9c5e2c..7a18c3170d 100644 --- a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart @@ -41,6 +41,9 @@ void main() { late FakeClock clock; late FakeProcessInfo processInfo; late MemoryFileSystem fileSystem; + late Platform platform; + late FileSystemUtils fileSystemUtils; + late Logger logger; late FakeProcessManager processManager; late PreRunValidator preRunValidator; @@ -56,6 +59,9 @@ void main() { processInfo = FakeProcessInfo(); processInfo.maxRss = 10; fileSystem = MemoryFileSystem.test(); + platform = FakePlatform(); + fileSystemUtils = FileSystemUtils(fileSystem: fileSystem, platform: platform); + logger = BufferLogger.test(); processManager = FakeProcessManager.empty(); preRunValidator = PreRunValidator(fileSystem: fileSystem); }); @@ -704,6 +710,365 @@ void main() { }); }); + group('--dart-define-from-file', () { + + late FlutterCommand dummyCommand; + late CommandRunner dummyCommandRunner; + + setUp(() { + dummyCommand = DummyFlutterCommand()..usesDartDefineOption(); + dummyCommandRunner = createTestCommandRunner(dummyCommand); + }); + + testUsingContext('parses values from JSON files and includes them in defines list', () async { + fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true); + fileSystem.file('pubspec.yaml').createSync(); + fileSystem.file('.packages').createSync(); + await fileSystem.file('config1.json').writeAsString( + ''' + { + "kInt": 1, + "kDouble": 1.1, + "name": "denghaizhu", + "title": "this is title from config json file", + "nullValue": null, + "containEqual": "sfadsfv=432f" + } + ''' + ); + await fileSystem.file('config2.json').writeAsString( + ''' + { + "body": "this is body from config json file" + } + ''' + ); + + await dummyCommandRunner.run([ + 'dummy', + '--dart-define-from-file=config1.json', + '--dart-define-from-file=config2.json', + ]); + + final BuildInfo buildInfo = await dummyCommand.getBuildInfo(forcedBuildMode: BuildMode.debug); + expect(buildInfo.dartDefines, containsAll(const [ + 'kInt=1', + 'kDouble=1.1', + 'name=denghaizhu', + 'title=this is title from config json file', + 'nullValue=null', + 'containEqual=sfadsfv=432f', + 'body=this is body from config json file', + ])); + }, overrides: { + FileSystem: () => fileSystem, + Logger: () => logger, + FileSystemUtils: () => fileSystemUtils, + Platform: () => platform, + ProcessManager: () => processManager, + }); + + testUsingContext('has values with identical keys from --dart-define take precedence', () async { + fileSystem + .file(fileSystem.path.join('lib', 'main.dart')) + .createSync(recursive: true); + fileSystem.file('pubspec.yaml').createSync(); + fileSystem.file('.packages').createSync(); + fileSystem.file('.env').writeAsStringSync(''' + MY_VALUE=VALUE_FROM_ENV_FILE + '''); + + await dummyCommandRunner.run([ + 'dummy', + '--dart-define=MY_VALUE=VALUE_FROM_COMMAND', + '--dart-define-from-file=.env', + ]); + + final BuildInfo buildInfo = await dummyCommand.getBuildInfo(forcedBuildMode: BuildMode.debug); + expect(buildInfo.dartDefines, containsAll(const [ + 'MY_VALUE=VALUE_FROM_ENV_FILE', + 'MY_VALUE=VALUE_FROM_COMMAND', + ])); + }, overrides: { + FileSystem: () => fileSystem, + Logger: () => logger, + FileSystemUtils: () => fileSystemUtils, + Platform: () => platform, + ProcessManager: () => processManager, + }); + + testUsingContext('correctly parses a valid env file', () async { + fileSystem + .file(fileSystem.path.join('lib', 'main.dart')) + .createSync(recursive: true); + fileSystem.file('pubspec.yaml').createSync(); + fileSystem.file('.packages').createSync(); + await fileSystem.file('.env').writeAsString(''' + # comment + kInt=1 + kDouble=1.1 # should be double + + name=piotrfleury + title=this is title from config env file + empty= + + doubleQuotes="double quotes 'value'#=" # double quotes + singleQuotes='single quotes "value"#=' # single quotes + backQuotes=`back quotes "value" '#=` # back quotes + + hashString="some-#-hash-string-value" + + # Play around with spaces around the equals sign. + spaceBeforeEqual =value + spaceAroundEqual = value + spaceAfterEqual= value + + '''); + await fileSystem.file('.env2').writeAsString(''' + # second comment + + body=this is body from config env file + '''); + + await dummyCommandRunner.run([ + 'dummy', + '--dart-define-from-file=.env', + '--dart-define-from-file=.env2', + ]); + + final BuildInfo buildInfo = await dummyCommand.getBuildInfo(forcedBuildMode: BuildMode.debug); + expect(buildInfo.dartDefines, containsAll(const [ + 'kInt=1', + 'kDouble=1.1', + 'name=piotrfleury', + 'title=this is title from config env file', + 'empty=', + "doubleQuotes=double quotes 'value'#=", + 'singleQuotes=single quotes "value"#=', + 'backQuotes=back quotes "value" \'#=', + 'hashString=some-#-hash-string-value', + 'spaceBeforeEqual=value', + 'spaceAroundEqual=value', + 'spaceAfterEqual=value', + 'body=this is body from config env file' + ])); + }, overrides: { + FileSystem: () => fileSystem, + Logger: () => logger, + FileSystemUtils: () => fileSystemUtils, + Platform: () => platform, + ProcessManager: () => processManager, + }); + + testUsingContext('throws a ToolExit when the provided .env file is malformed', () async { + fileSystem + .file(fileSystem.path.join('lib', 'main.dart')) + .createSync(recursive: true); + fileSystem.file('pubspec.yaml').createSync(); + fileSystem.file('.packages').createSync(); + await fileSystem.file('.env').writeAsString('what is this'); + + await dummyCommandRunner.run([ + 'dummy', + '--dart-define-from-file=.env', + ]); + + expect(dummyCommand.getBuildInfo(forcedBuildMode: BuildMode.debug), + throwsToolExit(message: 'Unable to parse file provided for ' + '--${FlutterOptions.kDartDefineFromFileOption}.\n' + 'Invalid property line: what is this')); + + }, overrides: { + FileSystem: () => fileSystem, + Logger: () => logger, + FileSystemUtils: () => fileSystemUtils, + Platform: () => platform, + ProcessManager: () => processManager, + }); + + testUsingContext('throws a ToolExit when .env file contains a multiline value', () async { + fileSystem + .file(fileSystem.path.join('lib', 'main.dart')) + .createSync(recursive: true); + fileSystem.file('pubspec.yaml').createSync(); + fileSystem.file('.packages').createSync(); + await fileSystem.file('.env').writeAsString(''' + # single line value + name=piotrfleury + + # multi-line value + multiline = """ Welcome to .env demo + a simple counter app with .env file support + for more info, check out the README.md file + Thanks! """ # This is the welcome message that will be displayed on the counter app + + '''); + + await dummyCommandRunner.run([ + 'dummy', + '--dart-define-from-file=.env', + ]); + expect(dummyCommand.getBuildInfo(forcedBuildMode: BuildMode.debug), + throwsToolExit(message: 'Multi-line value is not supported: multiline = """ Welcome to .env demo')); + }, overrides: { + FileSystem: () => fileSystem, + Logger: () => logger, + FileSystemUtils: () => fileSystemUtils, + Platform: () => platform, + ProcessManager: () => processManager, + }); + + testUsingContext('works with mixed file formats', + () async { + fileSystem + .file(fileSystem.path.join('lib', 'main.dart')) + .createSync(recursive: true); + fileSystem.file('pubspec.yaml').createSync(); + fileSystem.file('.packages').createSync(); + await fileSystem.file('.env').writeAsString(''' + kInt=1 + kDouble=1.1 + name=piotrfleury + title=this is title from config env file + '''); + await fileSystem.file('config.json').writeAsString(''' + { + "body": "this is body from config json file" + } + '''); + + await dummyCommandRunner.run([ + 'dummy', + '--dart-define-from-file=.env', + '--dart-define-from-file=config.json', + ]); + + final BuildInfo buildInfo = await dummyCommand.getBuildInfo(forcedBuildMode: BuildMode.debug); + expect(buildInfo.dartDefines, containsAll(const [ + 'kInt=1', + 'kDouble=1.1', + 'name=piotrfleury', + 'title=this is title from config env file', + 'body=this is body from config json file', + ])); + }, overrides: { + FileSystem: () => fileSystem, + Logger: () => logger, + FileSystemUtils: () => fileSystemUtils, + Platform: () => platform, + ProcessManager: () => processManager, + }); + + testUsingContext('when files contain entries with duplicate keys, uses the value from the lattermost file', () async { + fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true); + fileSystem.file('pubspec.yaml').createSync(); + fileSystem.file('.packages').createSync(); + await fileSystem.file('config1.json').writeAsString( + ''' + { + "kInt": 1, + "kDouble": 1.1, + "name": "denghaizhu", + "title": "this is title from config json file" + } + ''' + ); + await fileSystem.file('config2.json').writeAsString( + ''' + { + "kInt": "2" + } + ''' + ); + + await dummyCommandRunner.run([ + 'dummy', + '--dart-define-from-file=config1.json', + '--dart-define-from-file=config2.json', + ]); + final BuildInfo buildInfo = await dummyCommand.getBuildInfo(forcedBuildMode: BuildMode.debug); + expect(buildInfo.dartDefines, containsAll(const [ + 'kInt=2', + 'kDouble=1.1', + 'name=denghaizhu', + 'title=this is title from config json file' + ])); + }, overrides: { + FileSystem: () => fileSystem, + Logger: () => logger, + FileSystemUtils: () => fileSystemUtils, + Platform: () => platform, + ProcessManager: () => processManager, + }); + + testUsingContext('throws a ToolExit when the argued path points to a directory', () async { + fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true); + fileSystem.file('pubspec.yaml').createSync(); + fileSystem.file('.packages').createSync(); + fileSystem.directory('config').createSync(); + + await dummyCommandRunner.run([ + 'dummy', + '--dart-define-from-file=config', + ]); + expect(dummyCommand.getBuildInfo(forcedBuildMode: BuildMode.debug), + throwsToolExit(message: 'Did not find the file passed to "--dart-define-from-file". Path: config')); + }, overrides: { + FileSystem: () => fileSystem, + Logger: () => logger, + FileSystemUtils: () => fileSystemUtils, + Platform: () => platform, + ProcessManager: () => processManager, + }); + + testUsingContext('throws a ToolExit when the given JSON file is malformed', () async { + fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true); + fileSystem.file('pubspec.yaml').createSync(); + fileSystem.file('.packages').createSync(); + await fileSystem.file('config.json').writeAsString( + ''' + { + "kInt": 1Error json format + "kDouble": 1.1, + "name": "denghaizhu", + "title": "this is title from config json file" + } + ''' + ); + + await dummyCommandRunner.run([ + 'dummy', + '--dart-define-from-file=config.json', + ]); + expect(dummyCommand.getBuildInfo(forcedBuildMode: BuildMode.debug), + throwsToolExit(message: 'Unable to parse the file at path "config.json" due to ' + 'a formatting error. Ensure that the file contains valid JSON.\n' + 'Error details: FormatException: Missing expected digit (at line 2, character 25)')); + }, overrides: { + FileSystem: () => fileSystem, + Logger: () => logger, + FileSystemUtils: () => fileSystemUtils, + Platform: () => platform, + ProcessManager: () => processManager, + }); + + testUsingContext('throws a ToolExit when the provided file does not exist', () async { + fileSystem.directory('config').createSync(); + await dummyCommandRunner.run([ + 'dummy', + '--dart-define=k=v', + '--dart-define-from-file=config']); + expect(dummyCommand.getBuildInfo(forcedBuildMode: BuildMode.debug), + throwsToolExit(message: 'Did not find the file passed to "--dart-define-from-file". Path: config')); + }, overrides: { + FileSystem: () => fileSystem, + Logger: () => logger, + FileSystemUtils: () => fileSystemUtils, + Platform: () => platform, + ProcessManager: () => processManager, + }); + }); + group('--flavor', () { late _TestDeviceManager testDeviceManager; late Logger logger;