From f6bc147c91805e7d5e17ce753b1958e19648a9bc Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Fri, 17 Mar 2023 11:31:48 -0700 Subject: [PATCH] Revert "[flutter_tools] Remove sound null safety flag (#120936)" (#122909) This reverts commit 7c3088cf22c8a863ca38e3890851fd8dc776768d. --- dev/bots/test.dart | 51 +++++++---- .../test/foundation/isolates_test.dart | 7 +- .../test_private/bin/test_private.dart | 2 +- packages/flutter_tools/lib/executable.dart | 1 + packages/flutter_tools/lib/src/artifacts.dart | 49 ++++++++++- .../flutter_tools/lib/src/build_info.dart | 6 ++ .../lib/src/commands/attach.dart | 1 + .../flutter_tools/lib/src/commands/build.dart | 51 +++++++++-- .../lib/src/commands/build_aar.dart | 6 ++ .../lib/src/commands/build_apk.dart | 4 +- .../lib/src/commands/build_appbundle.dart | 3 + .../lib/src/commands/build_bundle.dart | 6 +- .../lib/src/commands/build_ios.dart | 8 +- .../lib/src/commands/build_ios_framework.dart | 7 ++ .../lib/src/commands/build_linux.dart | 2 + .../lib/src/commands/build_macos.dart | 2 + .../src/commands/build_macos_framework.dart | 2 + .../lib/src/commands/build_web.dart | 3 + .../lib/src/commands/build_windows.dart | 2 + .../flutter_tools/lib/src/commands/run.dart | 5 ++ .../flutter_tools/lib/src/commands/test.dart | 2 + packages/flutter_tools/lib/src/device.dart | 19 +++- .../src/fuchsia/fuchsia_kernel_compiler.dart | 1 + .../lib/src/isolated/devfs_web.dart | 16 +++- .../lib/src/isolated/resident_web_runner.dart | 12 +++ .../lib/src/reporting/custom_dimensions.dart | 16 +++- .../lib/src/reporting/events.dart | 64 ++++++++++++++ .../lib/src/reporting/reporting.dart | 3 + .../lib/src/resident_runner.dart | 15 +++- packages/flutter_tools/lib/src/run_hot.dart | 10 +++ .../lib/src/runner/flutter_command.dart | 78 ++++++++++++++++ .../lib/src/test/flutter_tester_device.dart | 2 + .../lib/src/test/flutter_web_platform.dart | 15 +++- .../flutter_tools/lib/src/test/runner.dart | 1 + .../lib/src/test/web_test_compiler.dart | 19 +++- .../flutter_tools/lib/src/web/bootstrap.dart | 2 + .../flutter_tools/lib/src/web/compile.dart | 34 +++++-- .../hermetic/build_darwin_framework_test.dart | 17 ++++ .../hermetic/build_ios_test.dart | 27 +++++- .../hermetic/build_ipa_test.dart | 36 +++++++- .../hermetic/build_linux_test.dart | 23 ++++- .../hermetic/build_macos_test.dart | 19 +++- .../commands.shard/hermetic/build_test.dart | 12 ++- .../hermetic/build_web_test.dart | 11 ++- .../hermetic/build_windows_test.dart | 45 +++++----- .../commands.shard/hermetic/drive_test.dart | 2 + .../commands.shard/hermetic/run_test.dart | 2 + .../permeable/build_aar_test.dart | 4 + .../permeable/build_apk_test.dart | 3 +- .../permeable/build_appbundle_test.dart | 3 +- .../permeable/build_bundle_test.dart | 78 ++++++++++++---- .../test/general.shard/analytics_test.dart | 2 + .../android/android_device_start_test.dart | 3 +- .../test/general.shard/artifacts_test.dart | 16 ++++ .../general.shard/commands/build_test.dart | 88 ++++++++++++++++++- .../general.shard/desktop_device_test.dart | 8 +- .../test/general.shard/device_test.dart | 10 ++- .../ios/ios_device_start_prebuilt_test.dart | 3 +- .../general.shard/ios/simulators_test.dart | 3 +- .../general.shard/reporting/events_test.dart | 70 +++++++++++++++ .../general.shard/resident_runner_test.dart | 34 ++++++- .../runner/flutter_command_test.dart | 79 +++++++++++++++++ .../test/web_test_compiler_test.dart | 5 +- .../general.shard/web/bootstrap_test.dart | 7 ++ .../general.shard/web/devfs_web_test.dart | 30 ++++++- ...crub_generated_plugin_registrant_test.dart | 6 ++ .../flutter_build_null_unsafe_test.dart | 83 +++++++++++++++++ 67 files changed, 1126 insertions(+), 130 deletions(-) create mode 100644 packages/flutter_tools/test/integration.shard/flutter_build_null_unsafe_test.dart diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 01d740538d..b9fb83498c 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -573,7 +573,9 @@ Future _runExampleProjectBuildTests(Directory exampleDirectory, [File? mai // Only verify caching with flutter gallery. final bool verifyCaching = exampleDirectory.path.contains('flutter_gallery'); final String examplePath = path.relative(exampleDirectory.path, from: Directory.current.path); + final bool hasNullSafety = File(path.join(examplePath, 'null_safety')).existsSync(); final List additionalArgs = [ + if (hasNullSafety) '--no-sound-null-safety', if (mainFile != null) path.relative(mainFile.path, from: exampleDirectory.absolute.path), ]; if (Directory(path.join(examplePath, 'android')).existsSync()) { @@ -769,6 +771,8 @@ Future _runAddToAppLifeCycleTests() async { } Future _runFrameworkTests() async { + final List soundNullSafetyOptions = ['--null-assertions', '--sound-null-safety']; + final List mixedModeNullSafetyOptions = ['--null-assertions', '--no-sound-null-safety']; final List trackWidgetCreationAlternatives = ['--track-widget-creation', '--no-track-widget-creation']; Future runWidgets() async { @@ -776,7 +780,7 @@ Future _runFrameworkTests() async { for (final String trackWidgetCreationOption in trackWidgetCreationAlternatives) { await _runFlutterTest( path.join(flutterRoot, 'packages', 'flutter'), - options: [trackWidgetCreationOption], + options: [trackWidgetCreationOption, ...soundNullSafetyOptions], tests: [ path.join('test', 'widgets') + path.separator ], ); } @@ -791,13 +795,13 @@ Future _runFrameworkTests() async { // Run release mode tests (see packages/flutter/test_release/README.md) await _runFlutterTest( path.join(flutterRoot, 'packages', 'flutter'), - options: ['--dart-define=dart.vm.product=true'], + options: ['--dart-define=dart.vm.product=true', ...soundNullSafetyOptions], tests: ['test_release${path.separator}'], ); // Run profile mode tests (see packages/flutter/test_profile/README.md) await _runFlutterTest( path.join(flutterRoot, 'packages', 'flutter'), - options: ['--dart-define=dart.vm.product=false', '--dart-define=dart.vm.profile=true'], + options: ['--dart-define=dart.vm.product=false', '--dart-define=dart.vm.profile=true', ...soundNullSafetyOptions], tests: ['test_profile${path.separator}'], ); } @@ -813,7 +817,7 @@ Future _runFrameworkTests() async { for (final String trackWidgetCreationOption in trackWidgetCreationAlternatives) { await _runFlutterTest( path.join(flutterRoot, 'packages', 'flutter'), - options: [trackWidgetCreationOption], + options: [trackWidgetCreationOption, ...soundNullSafetyOptions], tests: tests, ); } @@ -833,9 +837,9 @@ Future _runFrameworkTests() async { workingDirectory: path.join(flutterRoot, 'examples', 'api'), ); } - await _runFlutterTest(path.join(flutterRoot, 'examples', 'api')); - await _runFlutterTest(path.join(flutterRoot, 'examples', 'hello_world')); - await _runFlutterTest(path.join(flutterRoot, 'examples', 'layers')); + await _runFlutterTest(path.join(flutterRoot, 'examples', 'api'), options: soundNullSafetyOptions); + await _runFlutterTest(path.join(flutterRoot, 'examples', 'hello_world'), options: soundNullSafetyOptions); + await _runFlutterTest(path.join(flutterRoot, 'examples', 'layers'), options: soundNullSafetyOptions); } Future runTracingTests() async { @@ -941,6 +945,7 @@ Future _runFrameworkTests() async { Future runPrivateTests() async { final List args = [ + '--sound-null-safety', 'run', 'bin/test_private.dart', ]; @@ -984,17 +989,17 @@ Future _runFrameworkTests() async { await _runFlutterTest(path.join(flutterRoot, 'dev', 'tools', 'gen_defaults')); await _runFlutterTest(path.join(flutterRoot, 'dev', 'tools', 'gen_keycodes')); await _runFlutterTest(path.join(flutterRoot, 'dev', 'benchmarks', 'test_apps', 'stocks')); - await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_driver'), tests: [path.join('test', 'src', 'real_tests')]); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_driver'), tests: [path.join('test', 'src', 'real_tests')], options: soundNullSafetyOptions); await _runFlutterTest(path.join(flutterRoot, 'packages', 'integration_test'), options: [ '--enable-vmservice', // Web-specific tests depend on Chromium, so they run as part of the web_long_running_tests shard. '--exclude-tags=web', ]); - await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_goldens')); - await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations')); - await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test')); - await _runFlutterTest(path.join(flutterRoot, 'packages', 'fuchsia_remote_debug_protocol')); - await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'non_nullable')); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_goldens'), options: soundNullSafetyOptions); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations'), options: soundNullSafetyOptions); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test'), options: soundNullSafetyOptions); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'fuchsia_remote_debug_protocol'), options: soundNullSafetyOptions); + await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'non_nullable'), options: mixedModeNullSafetyOptions); const String httpClientWarning = 'Warning: At least one test in this suite creates an HttpClient. When\n' 'running a test suite that uses TestWidgetsFlutterBinding, all HTTP\n' @@ -1224,7 +1229,7 @@ Future _runWebLongRunningTests() async { () => _runWebDebugTest('lib/framework_stack_trace.dart'), () => _runWebDebugTest('lib/web_directory_loading.dart'), () => _runWebDebugTest('test/test.dart'), - () => _runWebDebugTest('lib/null_safe_main.dart'), + () => _runWebDebugTest('lib/null_safe_main.dart', enableNullSafety: true), () => _runWebDebugTest('lib/web_define_loading.dart', additionalArguments: [ '--dart-define=test.valueA=Example,A', @@ -1237,8 +1242,12 @@ Future _runWebLongRunningTests() async { '--dart-define=test.valueB=Value', ] ), - () => _runWebDebugTest('lib/sound_mode.dart'), - () => _runWebReleaseTest('lib/sound_mode.dart'), + () => _runWebDebugTest('lib/sound_mode.dart', additionalArguments: [ + '--sound-null-safety', + ]), + () => _runWebReleaseTest('lib/sound_mode.dart', additionalArguments: [ + '--sound-null-safety', + ]), () => _runFlutterWebTest( 'html', path.join(flutterRoot, 'packages', 'integration_test'), @@ -1297,6 +1306,7 @@ Future _runFlutterDriverWebTest({ if (driver != null) '--driver=$driver', '--target=$target', '--browser-name=chrome', + '--no-sound-null-safety', '-d', 'web-server', '--$buildMode', @@ -1338,6 +1348,7 @@ Future _runWebTreeshakeTest() async { 'build', 'web', '--target=$target', + '--no-sound-null-safety', '--profile', ], workingDirectory: testAppDirectory, @@ -1563,6 +1574,7 @@ Future _runGalleryE2eWebTest(String buildMode, { bool canvasKit = false }) '--driver=test_driver/transitions_perf_e2e_test.dart', '--target=test_driver/transitions_perf_e2e.dart', '--browser-name=chrome', + '--no-sound-null-safety', '-d', 'web-server', '--$buildMode', @@ -1669,6 +1681,7 @@ Future _runWebReleaseTest(String target, { /// /// Instead, we use `flutter run --debug` and sniff out the standard output. Future _runWebDebugTest(String target, { + bool enableNullSafety = false, List additionalArguments = const[], }) async { final String testAppDirectory = path.join(flutterRoot, 'dev', 'integration_tests', 'web'); @@ -1682,6 +1695,11 @@ Future _runWebDebugTest(String target, { [ 'run', '--debug', + if (enableNullSafety) + ...[ + '--no-sound-null-safety', + '--null-assertions', + ], '-d', 'chrome', '--web-run-headless', @@ -1724,6 +1742,7 @@ Future _runFlutterWebTest(String webRenderer, String workingDirectory, Lis '--platform=chrome', '--web-renderer=$webRenderer', '--dart-define=DART_HHH_BOT=$_runningInDartHHHBot', + '--sound-null-safety', ...flutterTestArgs, ...tests, ], diff --git a/packages/flutter/test/foundation/isolates_test.dart b/packages/flutter/test/foundation/isolates_test.dart index 571d9a1e6b..912e9baa66 100644 --- a/packages/flutter/test/foundation/isolates_test.dart +++ b/packages/flutter/test/foundation/isolates_test.dart @@ -80,7 +80,8 @@ Future test5CallCompute(int value) { return compute(test5, value); } -Future expectFileSuccessfullyCompletes(String filename) async { +Future expectFileSuccessfullyCompletes(String filename, + [bool unsound = false]) async { // Run a Dart script that calls compute(). // The Dart process will terminate only if the script exits cleanly with // all isolate ports closed. @@ -92,10 +93,12 @@ Future expectFileSuccessfullyCompletes(String filename) async { final String packageRoot = fs.path.dirname(fs.path.fromUri(platform.script)); final String scriptPath = fs.path.join(packageRoot, 'test', 'foundation', filename); + final String nullSafetyArg = + unsound ? '--no-sound-null-safety' : '--sound-null-safety'; // Enable asserts to also catch potentially invalid assertions. final ProcessResult result = await Process.run( - dartPath, ['run', '--enable-asserts', scriptPath]); + dartPath, [nullSafetyArg, 'run', '--enable-asserts', scriptPath]); expect(result.exitCode, 0); } diff --git a/packages/flutter/test_private/bin/test_private.dart b/packages/flutter/test_private/bin/test_private.dart index 25b89f3c38..7005e34926 100644 --- a/packages/flutter/test_private/bin/test_private.dart +++ b/packages/flutter/test_private/bin/test_private.dart @@ -225,7 +225,7 @@ class TestCase { for (final File test in tests) { final String testPath = path.join(path.dirname(test.path), 'lib', path.basenameWithoutExtension(test.path)); final ProcessRunnerResult result = await runner.runProcess( - [flutter, 'test', testPath], + [flutter, 'test', '--enable-experiment=non-nullable', '--no-sound-null-safety', '--null-assertions', testPath], failOk: true, ); if (result.exitCode != 0) { diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index 3bbdc8f4e6..b4b57444a6 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -171,6 +171,7 @@ List generateCommands({ osUtils: globals.os, verboseHelp: verboseHelp, androidSdk: globals.androidSdk, + logger: globals.logger, ), ChannelCommand(verboseHelp: verboseHelp), CleanCommand(verbose: verbose), diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 2d7367b252..0d575ebd48 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -91,7 +91,12 @@ enum HostArtifact { webPlatformDart2JSSoundKernelDill, /// The precompiled SDKs and sourcemaps for web debug builds. - + webPrecompiledSdk, + webPrecompiledSdkSourcemaps, + webPrecompiledCanvaskitSdk, + webPrecompiledCanvaskitSdkSourcemaps, + webPrecompiledCanvaskitAndHtmlSdk, + webPrecompiledCanvaskitAndHtmlSdkSourcemaps, webPrecompiledSoundSdk, webPrecompiledSoundSdkSourcemaps, webPrecompiledCanvaskitSoundSdk, @@ -243,10 +248,16 @@ String _hostArtifactToFileName(HostArtifact artifact, Platform platform) { return 'dart2js_platform.dill'; case HostArtifact.flutterWebLibrariesJson: return 'libraries.json'; + case HostArtifact.webPrecompiledSdk: + case HostArtifact.webPrecompiledCanvaskitSdk: + case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk: case HostArtifact.webPrecompiledSoundSdk: case HostArtifact.webPrecompiledCanvaskitSoundSdk: case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk: return 'dart_sdk.js'; + case HostArtifact.webPrecompiledSdkSourcemaps: + case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps: + case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps: case HostArtifact.webPrecompiledSoundSdkSourcemaps: case HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps: case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps: @@ -396,6 +407,18 @@ class CachedArtifacts implements Artifacts { case HostArtifact.webPlatformDart2JSSoundKernelDill: final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform)); return _fileSystem.file(path); + case HostArtifact.webPrecompiledSdk: + case HostArtifact.webPrecompiledSdkSourcemaps: + final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform)); + return _fileSystem.file(path); + case HostArtifact.webPrecompiledCanvaskitSdk: + case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps: + final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform)); + return _fileSystem.file(path); + case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk: + case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps: + final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform)); + return _fileSystem.file(path); case HostArtifact.webPrecompiledSoundSdk: case HostArtifact.webPrecompiledSoundSdkSourcemaps: final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform)); @@ -828,6 +851,18 @@ class CachedLocalEngineArtifacts implements Artifacts { case HostArtifact.webPlatformDart2JSSoundKernelDill: final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform)); return _fileSystem.file(path); + case HostArtifact.webPrecompiledSdk: + case HostArtifact.webPrecompiledSdkSourcemaps: + final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform)); + return _fileSystem.file(path); + case HostArtifact.webPrecompiledCanvaskitSdk: + case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps: + final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform)); + return _fileSystem.file(path); + case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk: + case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps: + final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform)); + return _fileSystem.file(path); case HostArtifact.webPrecompiledSoundSdk: case HostArtifact.webPrecompiledSoundSdkSourcemaps: final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform)); @@ -1118,6 +1153,18 @@ class CachedLocalWebSdkArtifacts implements Artifacts { case HostArtifact.webPlatformDart2JSSoundKernelDill: final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform)); return _fileSystem.file(path); + case HostArtifact.webPrecompiledSdk: + case HostArtifact.webPrecompiledSdkSourcemaps: + final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform)); + return _fileSystem.file(path); + case HostArtifact.webPrecompiledCanvaskitSdk: + case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps: + final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform)); + return _fileSystem.file(path); + case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk: + case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps: + final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform)); + return _fileSystem.file(path); case HostArtifact.webPrecompiledSoundSdk: case HostArtifact.webPrecompiledSoundSdkSourcemaps: final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform)); diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart index 629429edb4..1df9fba756 100644 --- a/packages/flutter_tools/lib/src/build_info.dart +++ b/packages/flutter_tools/lib/src/build_info.dart @@ -40,6 +40,7 @@ class BuildInfo { this.performanceMeasurementFile, this.dartDefineConfigJsonMap, this.packagesPath = '.dart_tool/package_config.json', // TODO(zanderso): make this required and remove the default. + this.nullSafetyMode = NullSafetyMode.sound, this.codeSizeDirectory, this.androidGradleDaemon = true, this.packageConfig = PackageConfig.empty, @@ -53,6 +54,11 @@ class BuildInfo { final BuildMode mode; + /// The null safety mode the application should be run in. + /// + /// If not provided, defaults to [NullSafetyMode.autodetect]. + final NullSafetyMode nullSafetyMode; + /// Whether the build should subset icon fonts. final bool treeShakeIcons; diff --git a/packages/flutter_tools/lib/src/commands/attach.dart b/packages/flutter_tools/lib/src/commands/attach.dart index 3a1152ab39..21cae52f3c 100644 --- a/packages/flutter_tools/lib/src/commands/attach.dart +++ b/packages/flutter_tools/lib/src/commands/attach.dart @@ -90,6 +90,7 @@ class AttachCommand extends FlutterCommand { usesDartDefineOption(); usesDeviceUserOption(); addEnableExperimentation(hide: !verboseHelp); + addNullSafetyModeOptions(hide: !verboseHelp); usesInitializeFromDillOption(hide: !verboseHelp); argParser ..addOption( diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index 5f521cb847..cad3d81453 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -2,9 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:meta/meta.dart'; + import '../android/android_sdk.dart'; import '../base/file_system.dart'; +import '../base/logger.dart'; import '../base/os.dart'; +import '../build_info.dart'; import '../build_system/build_system.dart'; import '../commands/build_linux.dart'; import '../commands/build_macos.dart'; @@ -24,6 +28,7 @@ class BuildCommand extends FlutterCommand { required FileSystem fileSystem, required BuildSystem buildSystem, required OperatingSystemUtils osUtils, + required Logger logger, required AndroidSdk? androidSdk, bool verboseHelp = false, }){ @@ -31,32 +36,37 @@ class BuildCommand extends FlutterCommand { BuildAarCommand( fileSystem: fileSystem, androidSdk: androidSdk, + logger: logger, verboseHelp: verboseHelp, ) ); - _addSubcommand(BuildApkCommand(verboseHelp: verboseHelp)); - _addSubcommand(BuildAppBundleCommand(verboseHelp: verboseHelp)); - _addSubcommand(BuildIOSCommand(verboseHelp: verboseHelp)); + _addSubcommand(BuildApkCommand(logger: logger, verboseHelp: verboseHelp)); + _addSubcommand(BuildAppBundleCommand(logger: logger, verboseHelp: verboseHelp)); + _addSubcommand(BuildIOSCommand(logger: logger, verboseHelp: verboseHelp)); _addSubcommand(BuildIOSFrameworkCommand( + logger: logger, buildSystem: buildSystem, verboseHelp: verboseHelp, )); _addSubcommand(BuildMacOSFrameworkCommand( + logger: logger, buildSystem: buildSystem, verboseHelp: verboseHelp, )); - _addSubcommand(BuildIOSArchiveCommand(verboseHelp: verboseHelp)); - _addSubcommand(BuildBundleCommand(verboseHelp: verboseHelp)); + _addSubcommand(BuildIOSArchiveCommand(logger: logger, verboseHelp: verboseHelp)); + _addSubcommand(BuildBundleCommand(logger: logger, verboseHelp: verboseHelp)); _addSubcommand(BuildWebCommand( fileSystem: fileSystem, + logger: logger, verboseHelp: verboseHelp, )); - _addSubcommand(BuildMacosCommand(verboseHelp: verboseHelp)); + _addSubcommand(BuildMacosCommand(logger: logger, verboseHelp: verboseHelp)); _addSubcommand(BuildLinuxCommand( + logger: logger, operatingSystemUtils: osUtils, verboseHelp: verboseHelp )); - _addSubcommand(BuildWindowsCommand(verboseHelp: verboseHelp)); + _addSubcommand(BuildWindowsCommand(logger: logger, verboseHelp: verboseHelp)); } void _addSubcommand(BuildSubCommand command) { @@ -80,11 +90,36 @@ class BuildCommand extends FlutterCommand { abstract class BuildSubCommand extends FlutterCommand { BuildSubCommand({ + required Logger logger, required bool verboseHelp - }) { + }): _logger = logger { requiresPubspecYaml(); usesFatalWarningsOption(verboseHelp: verboseHelp); } + final Logger _logger; + + @override + bool get reportNullSafety => true; + bool get supported => true; + + /// Display a message describing the current null safety runtime mode + /// that was selected. + /// + /// This is similar to the run message in run_hot.dart + @protected + void displayNullSafetyMode(BuildInfo buildInfo) { + if (buildInfo.nullSafetyMode != NullSafetyMode.sound) { + _logger.printStatus(''); + _logger.printStatus( + 'Building without sound null safety ⚠️', + emphasis: true, + ); + _logger.printStatus( + 'Dart 3 will only support sound null safety, see https://dart.dev/null-safety', + ); + } + _logger.printStatus(''); + } } diff --git a/packages/flutter_tools/lib/src/commands/build_aar.dart b/packages/flutter_tools/lib/src/commands/build_aar.dart index 0770545249..c1144118d7 100644 --- a/packages/flutter_tools/lib/src/commands/build_aar.dart +++ b/packages/flutter_tools/lib/src/commands/build_aar.dart @@ -17,6 +17,7 @@ import 'build.dart'; class BuildAarCommand extends BuildSubCommand { BuildAarCommand({ + required super.logger, required AndroidSdk? androidSdk, required FileSystem fileSystem, required bool verboseHelp, @@ -49,6 +50,7 @@ class BuildAarCommand extends BuildSubCommand { usesDartDefineOption(); usesExtraDartFlagOptions(verboseHelp: verboseHelp); usesTrackWidgetCreation(verboseHelp: false); + addNullSafetyModeOptions(hide: !verboseHelp); addEnableExperimentation(hide: !verboseHelp); addAndroidSpecificBuildOptions(hide: !verboseHelp); argParser @@ -65,6 +67,9 @@ class BuildAarCommand extends BuildSubCommand { @override final String name = 'aar'; + @override + bool get reportNullSafety => false; + @override Future> get requiredArtifacts async => { DevelopmentArtifact.androidGenSnapshot, @@ -132,6 +137,7 @@ class BuildAarCommand extends BuildSubCommand { throwToolExit('Please specify a build mode and try again.'); } + displayNullSafetyMode(androidBuildInfo.first.buildInfo); await androidBuilder?.buildAar( project: _getProject(), target: targetFile.path, diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart index 6023f75c1d..d255d4dd68 100644 --- a/packages/flutter_tools/lib/src/commands/build_apk.dart +++ b/packages/flutter_tools/lib/src/commands/build_apk.dart @@ -15,7 +15,7 @@ import 'build.dart'; class BuildApkCommand extends BuildSubCommand { BuildApkCommand({ - bool verboseHelp = false + required super.logger, bool verboseHelp = false }) : super(verboseHelp: verboseHelp) { addTreeShakeIconsFlag(); usesTargetOption(); @@ -32,6 +32,7 @@ class BuildApkCommand extends BuildSubCommand { addBundleSkSLPathOption(hide: !verboseHelp); addEnableExperimentation(hide: !verboseHelp); addBuildPerformanceFile(hide: !verboseHelp); + addNullSafetyModeOptions(hide: !verboseHelp); usesAnalyzeSizeFlag(); addAndroidSpecificBuildOptions(hide: !verboseHelp); addMultidexOption(); @@ -110,6 +111,7 @@ class BuildApkCommand extends BuildSubCommand { multidexEnabled: boolArg('multidex'), ); validateBuild(androidBuildInfo); + displayNullSafetyMode(androidBuildInfo.buildInfo); globals.terminal.usesTerminalUi = true; await androidBuilder?.buildApk( project: FlutterProject.current(), diff --git a/packages/flutter_tools/lib/src/commands/build_appbundle.dart b/packages/flutter_tools/lib/src/commands/build_appbundle.dart index 29c0d26df2..742e544818 100644 --- a/packages/flutter_tools/lib/src/commands/build_appbundle.dart +++ b/packages/flutter_tools/lib/src/commands/build_appbundle.dart @@ -18,6 +18,7 @@ import 'build.dart'; class BuildAppBundleCommand extends BuildSubCommand { BuildAppBundleCommand({ + required super.logger, bool verboseHelp = false, }) : super(verboseHelp: verboseHelp) { addTreeShakeIconsFlag(); @@ -35,6 +36,7 @@ class BuildAppBundleCommand extends BuildSubCommand { addBundleSkSLPathOption(hide: !verboseHelp); addBuildPerformanceFile(hide: !verboseHelp); usesTrackWidgetCreation(verboseHelp: verboseHelp); + addNullSafetyModeOptions(hide: !verboseHelp); addEnableExperimentation(hide: !verboseHelp); usesAnalyzeSizeFlag(); addAndroidSpecificBuildOptions(hide: !verboseHelp); @@ -146,6 +148,7 @@ class BuildAppBundleCommand extends BuildSubCommand { } validateBuild(androidBuildInfo); + displayNullSafetyMode(androidBuildInfo.buildInfo); globals.terminal.usesTerminalUi = true; await androidBuilder?.buildAab( project: FlutterProject.current(), diff --git a/packages/flutter_tools/lib/src/commands/build_bundle.dart b/packages/flutter_tools/lib/src/commands/build_bundle.dart index 6cdecad322..bece84cf98 100644 --- a/packages/flutter_tools/lib/src/commands/build_bundle.dart +++ b/packages/flutter_tools/lib/src/commands/build_bundle.dart @@ -15,6 +15,7 @@ import 'build.dart'; class BuildBundleCommand extends BuildSubCommand { BuildBundleCommand({ + required super.logger, bool verboseHelp = false, BundleBuilder? bundleBuilder, }) : _bundleBuilder = bundleBuilder ?? BundleBuilder(), super(verboseHelp: verboseHelp) { @@ -125,9 +126,12 @@ class BuildBundleCommand extends BuildSubCommand { break; } + final BuildInfo buildInfo = await getBuildInfo(); + displayNullSafetyMode(buildInfo); + await _bundleBuilder.build( platform: platform, - buildInfo: await getBuildInfo(), + buildInfo: buildInfo, mainPath: targetFile, depfilePath: stringArg('depfile'), assetDirPath: stringArg('asset-dir'), diff --git a/packages/flutter_tools/lib/src/commands/build_ios.dart b/packages/flutter_tools/lib/src/commands/build_ios.dart index 6531995251..d3a7eb079c 100644 --- a/packages/flutter_tools/lib/src/commands/build_ios.dart +++ b/packages/flutter_tools/lib/src/commands/build_ios.dart @@ -26,7 +26,7 @@ import 'build.dart'; /// Builds an .app for an iOS app to be used for local testing on an iOS device /// or simulator. Can only be run on a macOS host. class BuildIOSCommand extends _BuildIOSSubCommand { - BuildIOSCommand({ required super.verboseHelp }) { + BuildIOSCommand({ required super.logger, required super.verboseHelp }) { argParser ..addFlag('config-only', help: 'Update the project configuration without performing a build. ' @@ -91,7 +91,7 @@ class _ImageAssetFileKey { /// /// Can only be run on a macOS host. class BuildIOSArchiveCommand extends _BuildIOSSubCommand { - BuildIOSArchiveCommand({ required super.verboseHelp }) { + BuildIOSArchiveCommand({required super.logger, required super.verboseHelp}) { argParser.addOption( 'export-method', defaultsTo: 'app-store', @@ -412,6 +412,8 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand { @override Future runCommand() async { + final BuildInfo buildInfo = await cachedBuildInfo; + displayNullSafetyMode(buildInfo); final FlutterCommandResult xcarchiveResult = await super.runCommand(); final List validationResults = []; @@ -555,6 +557,7 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand { abstract class _BuildIOSSubCommand extends BuildSubCommand { _BuildIOSSubCommand({ + required super.logger, required bool verboseHelp }) : super(verboseHelp: verboseHelp) { addTreeShakeIconsFlag(); @@ -571,6 +574,7 @@ abstract class _BuildIOSSubCommand extends BuildSubCommand { addEnableExperimentation(hide: !verboseHelp); addBuildPerformanceFile(hide: !verboseHelp); addBundleSkSLPathOption(hide: !verboseHelp); + addNullSafetyModeOptions(hide: !verboseHelp); usesAnalyzeSizeFlag(); argParser.addFlag('codesign', defaultsTo: true, diff --git a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart index 0c3400f805..69ee07c960 100644 --- a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart +++ b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart @@ -33,6 +33,7 @@ abstract class BuildFrameworkCommand extends BuildSubCommand { required bool verboseHelp, Cache? cache, Platform? platform, + required super.logger, }) : _injectedFlutterVersion = flutterVersion, _buildSystem = buildSystem, _injectedCache = cache, @@ -45,6 +46,7 @@ abstract class BuildFrameworkCommand extends BuildSubCommand { addSplitDebugInfoOption(); addDartObfuscationOption(); usesExtraDartFlagOptions(verboseHelp: verboseHelp); + addNullSafetyModeOptions(hide: !verboseHelp); addEnableExperimentation(hide: !verboseHelp); argParser @@ -98,6 +100,9 @@ abstract class BuildFrameworkCommand extends BuildSubCommand { FlutterVersion get flutterVersion => _injectedFlutterVersion ?? globals.flutterVersion; final FlutterVersion? _injectedFlutterVersion; + @override + bool get reportNullSafety => false; + @protected late final FlutterProject project = FlutterProject.current(); @@ -172,6 +177,7 @@ abstract class BuildFrameworkCommand extends BuildSubCommand { /// managers. class BuildIOSFrameworkCommand extends BuildFrameworkCommand { BuildIOSFrameworkCommand({ + required super.logger, super.flutterVersion, required super.buildSystem, required bool verboseHelp, @@ -230,6 +236,7 @@ class BuildIOSFrameworkCommand extends BuildFrameworkCommand { final Directory outputDirectory = globals.fs.directory(globals.fs.path.absolute(globals.fs.path.normalize(outputArgument))); final List buildInfos = await getBuildInfos(); + displayNullSafetyMode(buildInfos.first); for (final BuildInfo buildInfo in buildInfos) { final String? productBundleIdentifier = await project.ios.productBundleIdentifier(buildInfo); globals.printStatus('Building frameworks for $productBundleIdentifier in ${getNameForBuildMode(buildInfo.mode)} mode...'); diff --git a/packages/flutter_tools/lib/src/commands/build_linux.dart b/packages/flutter_tools/lib/src/commands/build_linux.dart index d4a12f7af9..b96810437b 100644 --- a/packages/flutter_tools/lib/src/commands/build_linux.dart +++ b/packages/flutter_tools/lib/src/commands/build_linux.dart @@ -17,6 +17,7 @@ import 'build.dart'; /// A command to build a linux desktop target through a build shell script. class BuildLinuxCommand extends BuildSubCommand { BuildLinuxCommand({ + required super.logger, required OperatingSystemUtils operatingSystemUtils, bool verboseHelp = false, }) : _operatingSystemUtils = operatingSystemUtils, @@ -81,6 +82,7 @@ class BuildLinuxCommand extends BuildSubCommand { throwToolExit( 'Cross-build from Linux x64 host to Linux arm64 target is not currently supported.'); } + displayNullSafetyMode(buildInfo); await buildLinux( flutterProject.linux, buildInfo, diff --git a/packages/flutter_tools/lib/src/commands/build_macos.dart b/packages/flutter_tools/lib/src/commands/build_macos.dart index b4afa89578..091afbdabc 100644 --- a/packages/flutter_tools/lib/src/commands/build_macos.dart +++ b/packages/flutter_tools/lib/src/commands/build_macos.dart @@ -16,6 +16,7 @@ import 'build.dart'; /// A command to build a macOS desktop target through a build shell script. class BuildMacosCommand extends BuildSubCommand { BuildMacosCommand({ + required super.logger, required bool verboseHelp, }) : super(verboseHelp: verboseHelp) { addCommonDesktopBuildOptions(verboseHelp: verboseHelp); @@ -57,6 +58,7 @@ class BuildMacosCommand extends BuildSubCommand { if (!supported) { throwToolExit('"build macos" only supported on macOS hosts.'); } + displayNullSafetyMode(buildInfo); await buildMacOS( flutterProject: flutterProject, buildInfo: buildInfo, diff --git a/packages/flutter_tools/lib/src/commands/build_macos_framework.dart b/packages/flutter_tools/lib/src/commands/build_macos_framework.dart index 81f4512009..e229533e22 100644 --- a/packages/flutter_tools/lib/src/commands/build_macos_framework.dart +++ b/packages/flutter_tools/lib/src/commands/build_macos_framework.dart @@ -29,6 +29,7 @@ class BuildMacOSFrameworkCommand extends BuildFrameworkCommand { super.flutterVersion, required super.buildSystem, required super.verboseHelp, + required super.logger, super.cache, super.platform, }); @@ -68,6 +69,7 @@ class BuildMacOSFrameworkCommand extends BuildFrameworkCommand { globals.fs.directory(globals.fs.path.absolute(globals.fs.path.normalize(outputArgument))); final List buildInfos = await getBuildInfos(); + displayNullSafetyMode(buildInfos.first); for (final BuildInfo buildInfo in buildInfos) { globals.printStatus('Building macOS frameworks in ${getNameForBuildMode(buildInfo.mode)} mode...'); diff --git a/packages/flutter_tools/lib/src/commands/build_web.dart b/packages/flutter_tools/lib/src/commands/build_web.dart index be1110a137..f8ca2c5f23 100644 --- a/packages/flutter_tools/lib/src/commands/build_web.dart +++ b/packages/flutter_tools/lib/src/commands/build_web.dart @@ -16,6 +16,7 @@ import 'build.dart'; class BuildWebCommand extends BuildSubCommand { BuildWebCommand({ + required super.logger, required FileSystem fileSystem, required bool verboseHelp, }) : _fileSystem = fileSystem, super(verboseHelp: verboseHelp) { @@ -28,6 +29,7 @@ class BuildWebCommand extends BuildSubCommand { addBuildModeFlags(verboseHelp: verboseHelp, excludeDebug: true); usesDartDefineOption(); addEnableExperimentation(hide: !verboseHelp); + addNullSafetyModeOptions(hide: !verboseHelp); addNativeNullAssertions(); // @@ -164,6 +166,7 @@ class BuildWebCommand extends BuildSubCommand { // valid approaches for setting output directory of build artifacts final String? outputDirectoryPath = stringArg('output'); + displayNullSafetyMode(buildInfo); await buildWeb( flutterProject, target, diff --git a/packages/flutter_tools/lib/src/commands/build_windows.dart b/packages/flutter_tools/lib/src/commands/build_windows.dart index 95a1cdbc4f..0c94f53afd 100644 --- a/packages/flutter_tools/lib/src/commands/build_windows.dart +++ b/packages/flutter_tools/lib/src/commands/build_windows.dart @@ -19,6 +19,7 @@ import 'build.dart'; /// A command to build a windows desktop target through a build shell script. class BuildWindowsCommand extends BuildSubCommand { BuildWindowsCommand({ + required super.logger, bool verboseHelp = false, }) : super(verboseHelp: verboseHelp) { addCommonDesktopBuildOptions(verboseHelp: verboseHelp); @@ -51,6 +52,7 @@ class BuildWindowsCommand extends BuildSubCommand { if (!globals.platform.isWindows) { throwToolExit('"build windows" only supported on Windows hosts.'); } + displayNullSafetyMode(buildInfo); await buildWindows( flutterProject.windows, buildInfo, diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 067e378347..b002f4d25d 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -173,6 +173,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment usesIpv6Flag(verboseHelp: verboseHelp); usesPubOption(); usesTrackWidgetCreation(verboseHelp: verboseHelp); + addNullSafetyModeOptions(hide: !verboseHelp); usesDeviceUserOption(); usesDeviceTimeoutOption(); addDdsOptions(verboseHelp: verboseHelp); @@ -197,6 +198,9 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment bool get uninstallFirst => boolArg('uninstall-first'); bool get enableEmbedderApi => boolArg('enable-embedder-api'); + @override + bool get reportNullSafety => true; + /// Whether to start the application paused by default. bool get startPausedDefault; @@ -274,6 +278,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment fastStart: argParser.options.containsKey('fast-start') && boolArg('fast-start') && !runningWithPrebuiltApplication, + nullAssertions: boolArg('null-assertions'), nativeNullAssertions: boolArg('native-null-assertions'), enableImpeller: enableImpeller, uninstallFirst: uninstallFirst, diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart index dc59e95398..fbd85c346f 100644 --- a/packages/flutter_tools/lib/src/commands/test.dart +++ b/packages/flutter_tools/lib/src/commands/test.dart @@ -67,6 +67,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts { }) { requiresPubspecYaml(); usesPubOption(); + addNullSafetyModeOptions(hide: !verboseHelp); usesTrackWidgetCreation(verboseHelp: verboseHelp); addEnableExperimentation(hide: !verboseHelp); usesDartDefineOption(); @@ -419,6 +420,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts { // On iOS >=14, keeping this enabled will leave a prompt on the screen. disablePortPublication: true, enableDds: enableDds, + nullAssertions: boolArg(FlutterOptions.kNullAssertions), ); Device? integrationTestDevice; diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index c4deb10fa9..3984ed4550 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -916,6 +916,7 @@ class DebuggingOptions { this.webLaunchUrl, this.vmserviceOutFile, this.fastStart = false, + this.nullAssertions = false, this.nativeNullAssertions = false, this.enableImpeller = false, this.uninstallFirst = false, @@ -966,6 +967,7 @@ class DebuggingOptions { vmserviceOutFile = null, fastStart = false, webEnableExpressionEvaluation = false, + nullAssertions = false, nativeNullAssertions = false, serveObservatory = false; @@ -1008,6 +1010,7 @@ class DebuggingOptions { required this.webLaunchUrl, required this.vmserviceOutFile, required this.fastStart, + required this.nullAssertions, required this.nativeNullAssertions, required this.enableImpeller, required this.uninstallFirst, @@ -1081,6 +1084,8 @@ class DebuggingOptions { final String? vmserviceOutFile; final bool fastStart; + final bool nullAssertions; + /// Additional null runtime checks inserted for web applications. /// /// See also: @@ -1175,6 +1180,7 @@ class DebuggingOptions { 'webLaunchUrl': webLaunchUrl, 'vmserviceOutFile': vmserviceOutFile, 'fastStart': fastStart, + 'nullAssertions': nullAssertions, 'nativeNullAssertions': nativeNullAssertions, 'enableImpeller': enableImpeller, 'serveObservatory': serveObservatory, @@ -1222,6 +1228,7 @@ class DebuggingOptions { webLaunchUrl: json['webLaunchUrl'] as String?, vmserviceOutFile: json['vmserviceOutFile'] as String?, fastStart: json['fastStart']! as bool, + nullAssertions: json['nullAssertions']! as bool, nativeNullAssertions: json['nativeNullAssertions']! as bool, enableImpeller: (json['enableImpeller'] as bool?) ?? false, uninstallFirst: (json['uninstallFirst'] as bool?) ?? false, @@ -1303,9 +1310,13 @@ class NoOpDeviceLogReader implements DeviceLogReader { void dispose() { } } +/// Append --null_assertions to any existing Dart VM flags if +/// [debuggingOptions.nullAssertions] is true. String computeDartVmFlags(DebuggingOptions debuggingOptions) { - if (debuggingOptions.dartFlags.isNotEmpty) { - return debuggingOptions.dartFlags; - } - return ''; + return [ + if (debuggingOptions.dartFlags.isNotEmpty) + debuggingOptions.dartFlags, + if (debuggingOptions.nullAssertions) + '--null_assertions', + ].join(','); } diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_kernel_compiler.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_kernel_compiler.dart index e41ab4bec8..65358f9977 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_kernel_compiler.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_kernel_compiler.dart @@ -48,6 +48,7 @@ class FuchsiaKernelCompiler { throwToolExit('Fuchsia platform file not found at "$platformDill"'); } List flags = [ + '--no-sound-null-safety', '--target', 'flutter_runner', '--platform', diff --git a/packages/flutter_tools/lib/src/isolated/devfs_web.dart b/packages/flutter_tools/lib/src/isolated/devfs_web.dart index b836b276a4..79a33a7298 100644 --- a/packages/flutter_tools/lib/src/isolated/devfs_web.dart +++ b/packages/flutter_tools/lib/src/isolated/devfs_web.dart @@ -133,6 +133,7 @@ class WebAssetServer implements AssetReader { this.internetAddress, this._modules, this._digests, + this._nullSafetyMode, ) : basePath = _getIndexHtml().getBaseHref(); // Fallback to "application/octet-stream" on null which @@ -187,7 +188,8 @@ class WebAssetServer implements AssetReader { bool enableDwds, bool enableDds, Uri entrypoint, - ExpressionCompiler? expressionCompiler, { + ExpressionCompiler? expressionCompiler, + NullSafetyMode nullSafetyMode, { bool testMode = false, DwdsLauncher dwdsLauncher = Dwds.start, }) async { @@ -224,6 +226,7 @@ class WebAssetServer implements AssetReader { address, modules, digests, + nullSafetyMode, ); if (testMode) { return server; @@ -316,6 +319,7 @@ class WebAssetServer implements AssetReader { return server; } + final NullSafetyMode _nullSafetyMode; final HttpServer _httpServer; final WebMemoryFS _webMemoryFS = WebMemoryFS(); final PackageConfig _packages; @@ -572,12 +576,12 @@ class WebAssetServer implements AssetReader { File get _resolveDartSdkJsFile => globals.fs.file(globals.artifacts!.getHostArtifact( - kDartSdkJsArtifactMap[webRenderer]! + kDartSdkJsArtifactMap[webRenderer]![_nullSafetyMode]! )); File get _resolveDartSdkJsMapFile => globals.fs.file(globals.artifacts!.getHostArtifact( - kDartSdkJsMapArtifactMap[webRenderer]! + kDartSdkJsMapArtifactMap[webRenderer]![_nullSafetyMode]! )); @override @@ -646,7 +650,9 @@ class WebDevFS implements DevFS { required this.entrypoint, required this.expressionCompiler, required this.chromiumLauncher, + required this.nullAssertions, required this.nativeNullAssertions, + required this.nullSafetyMode, this.testMode = false, }) : _port = port; @@ -663,8 +669,10 @@ class WebDevFS implements DevFS { final bool testMode; final ExpressionCompiler? expressionCompiler; final ChromiumLauncher? chromiumLauncher; + final bool nullAssertions; final bool nativeNullAssertions; final int? _port; + final NullSafetyMode nullSafetyMode; late WebAssetServer webAssetServer; @@ -760,6 +768,7 @@ class WebDevFS implements DevFS { enableDds, entrypoint, expressionCompiler, + nullSafetyMode, testMode: testMode, ); @@ -848,6 +857,7 @@ class WebDevFS implements DevFS { 'main_module.bootstrap.js', generateMainModule( entrypoint: entrypoint, + nullAssertions: nullAssertions, nativeNullAssertions: nativeNullAssertions, ), ); diff --git a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart index 4a108fecea..035fe08862 100644 --- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart @@ -274,6 +274,8 @@ class ResidentWebRunner extends ResidentRunner { entrypoint: _fileSystem.file(target).uri, expressionCompiler: expressionCompiler, chromiumLauncher: _chromiumLauncher, + nullAssertions: debuggingOptions.nullAssertions, + nullSafetyMode: debuggingOptions.buildInfo.nullSafetyMode, nativeNullAssertions: debuggingOptions.nativeNullAssertions, ); final Uri url = await device!.devFS!.create(); @@ -601,6 +603,16 @@ class ResidentWebRunner extends ResidentRunner { ..writeAsStringSync(websocketUri.toString()); } _logger.printStatus('Debug service listening on $websocketUri'); + if (debuggingOptions.buildInfo.nullSafetyMode != NullSafetyMode.sound) { + _logger.printStatus(''); + _logger.printStatus( + 'Running without sound null safety ⚠️', + emphasis: true, + ); + _logger.printStatus( + 'Dart 3 will only support sound null safety, see https://dart.dev/null-safety', + ); + } } appStartedCompleter?.complete(); connectionInfoCompleter?.complete(DebugConnectionInfo(wsUri: websocketUri)); diff --git a/packages/flutter_tools/lib/src/reporting/custom_dimensions.dart b/packages/flutter_tools/lib/src/reporting/custom_dimensions.dart index b6ec43d2ed..ed5dd053ce 100644 --- a/packages/flutter_tools/lib/src/reporting/custom_dimensions.dart +++ b/packages/flutter_tools/lib/src/reporting/custom_dimensions.dart @@ -59,6 +59,8 @@ class CustomDimensions { this.commandPackagesAndroidEmbeddingVersion, this.nullSafety, this.fastReassemble, + this.nullSafeMigratedLibraries, + this.nullSafeTotalLibraries, this.hotEventCompileTimeInMs, this.hotEventFindInvalidatedTimeInMs, this.hotEventScannedSourcesCount, @@ -116,8 +118,8 @@ class CustomDimensions { final String? commandPackagesAndroidEmbeddingVersion; // cd46 final bool? nullSafety; // cd47 final bool? fastReassemble; // cd48 - // final int? nullSafeMigratedLibraries; // cd49 - deprecated - // final int? nullSafeTotalLibraries; // cd50 - deprecated + final int? nullSafeMigratedLibraries; // cd49 + final int? nullSafeTotalLibraries; // cd50 final int? hotEventCompileTimeInMs; // cd 51 final int? hotEventFindInvalidatedTimeInMs; // cd 52 final int? hotEventScannedSourcesCount; // cd 53 @@ -176,6 +178,8 @@ class CustomDimensions { if (commandPackagesAndroidEmbeddingVersion != null) cdKey(CustomDimensionsEnum.commandPackagesAndroidEmbeddingVersion): commandPackagesAndroidEmbeddingVersion.toString(), if (nullSafety != null) cdKey(CustomDimensionsEnum.nullSafety): nullSafety.toString(), if (fastReassemble != null) cdKey(CustomDimensionsEnum.fastReassemble): fastReassemble.toString(), + if (nullSafeMigratedLibraries != null) cdKey(CustomDimensionsEnum.nullSafeMigratedLibraries): nullSafeMigratedLibraries.toString(), + if (nullSafeTotalLibraries != null) cdKey(CustomDimensionsEnum.nullSafeTotalLibraries): nullSafeTotalLibraries.toString(), if (hotEventCompileTimeInMs != null) cdKey(CustomDimensionsEnum.hotEventCompileTimeInMs): hotEventCompileTimeInMs.toString(), if (hotEventFindInvalidatedTimeInMs != null) cdKey(CustomDimensionsEnum.hotEventFindInvalidatedTimeInMs): hotEventFindInvalidatedTimeInMs.toString(), if (hotEventScannedSourcesCount != null) cdKey(CustomDimensionsEnum.hotEventScannedSourcesCount): hotEventScannedSourcesCount.toString(), @@ -241,6 +245,8 @@ class CustomDimensions { commandPackagesAndroidEmbeddingVersion: other.commandPackagesAndroidEmbeddingVersion ?? commandPackagesAndroidEmbeddingVersion, nullSafety: other.nullSafety ?? nullSafety, fastReassemble: other.fastReassemble ?? fastReassemble, + nullSafeMigratedLibraries: other.nullSafeMigratedLibraries ?? nullSafeMigratedLibraries, + nullSafeTotalLibraries: other.nullSafeTotalLibraries ?? nullSafeTotalLibraries, hotEventCompileTimeInMs: other.hotEventCompileTimeInMs ?? hotEventCompileTimeInMs, hotEventFindInvalidatedTimeInMs: other.hotEventFindInvalidatedTimeInMs ?? hotEventFindInvalidatedTimeInMs, hotEventScannedSourcesCount: other.hotEventScannedSourcesCount ?? hotEventScannedSourcesCount, @@ -300,6 +306,8 @@ class CustomDimensions { commandPackagesAndroidEmbeddingVersion: _extractString(map, CustomDimensionsEnum.commandPackagesAndroidEmbeddingVersion), nullSafety: _extractBool(map, CustomDimensionsEnum.nullSafety), fastReassemble: _extractBool(map, CustomDimensionsEnum.fastReassemble), + nullSafeMigratedLibraries: _extractInt(map, CustomDimensionsEnum.nullSafeMigratedLibraries), + nullSafeTotalLibraries: _extractInt(map, CustomDimensionsEnum.nullSafeTotalLibraries), hotEventCompileTimeInMs: _extractInt(map, CustomDimensionsEnum.hotEventCompileTimeInMs), hotEventFindInvalidatedTimeInMs: _extractInt(map, CustomDimensionsEnum.hotEventFindInvalidatedTimeInMs), hotEventScannedSourcesCount: _extractInt(map, CustomDimensionsEnum.hotEventScannedSourcesCount), @@ -385,8 +393,8 @@ enum CustomDimensionsEnum { commandPackagesAndroidEmbeddingVersion, // cd46 nullSafety, // cd47 fastReassemble, // cd48 - nullSafeMigratedLibraries, // cd49 - deprecated - nullSafeTotalLibraries, // cd50 - deprecated + nullSafeMigratedLibraries, // cd49 + nullSafeTotalLibraries, // cd50 hotEventCompileTimeInMs, // cd51 hotEventFindInvalidatedTimeInMs, // cd52 hotEventScannedSourcesCount, // cd53 diff --git a/packages/flutter_tools/lib/src/reporting/events.dart b/packages/flutter_tools/lib/src/reporting/events.dart index 23de4a60f5..5ffcb2d584 100644 --- a/packages/flutter_tools/lib/src/reporting/events.dart +++ b/packages/flutter_tools/lib/src/reporting/events.dart @@ -248,3 +248,67 @@ class CodeSizeEvent extends UsageEvent { class ErrorHandlingEvent extends UsageEvent { ErrorHandlingEvent(String parameter) : super('error-handling', parameter, flutterUsage: globals.flutterUsage); } + +/// Emit various null safety analytic events. +/// +/// 1. The current null safety runtime mode. +/// 2. The number of packages that are migrated, along with the total number of packages +/// 3. The main packages language version. +class NullSafetyAnalysisEvent implements UsageEvent { + NullSafetyAnalysisEvent( + this.packageConfig, + this.nullSafetyMode, + this.currentPackage, + this.flutterUsage, + ); + + /// The category for analytics events related to null safety. + static const String kNullSafetyCategory = 'null-safety'; + + final PackageConfig packageConfig; + final NullSafetyMode nullSafetyMode; + final String currentPackage; + @override + final Usage flutterUsage; + + @override + void send() { + if (packageConfig.packages.isEmpty) { + return; + } + int migrated = 0; + LanguageVersion? languageVersion; + for (final Package package in packageConfig.packages) { + final LanguageVersion? packageLanguageVersion = package.languageVersion; + if (package.name == currentPackage) { + languageVersion = packageLanguageVersion; + } + if (packageLanguageVersion != null && + packageLanguageVersion.major >= nullSafeVersion.major && + packageLanguageVersion.minor >= nullSafeVersion.minor) { + migrated += 1; + } + } + flutterUsage.sendEvent(kNullSafetyCategory, 'runtime-mode', label: nullSafetyMode.toString()); + flutterUsage.sendEvent(kNullSafetyCategory, 'stats', parameters: CustomDimensions( + nullSafeMigratedLibraries: migrated, + nullSafeTotalLibraries: packageConfig.packages.length, + )); + if (languageVersion != null) { + final String formattedVersion = '${languageVersion.major}.${languageVersion.minor}'; + flutterUsage.sendEvent(kNullSafetyCategory, 'language-version', label: formattedVersion); + } + } + + @override + String get category => kNullSafetyCategory; + + @override + String get label => throw UnsupportedError(''); + + @override + String get parameter => throw UnsupportedError(''); + + @override + int get value => throw UnsupportedError(''); +} diff --git a/packages/flutter_tools/lib/src/reporting/reporting.dart b/packages/flutter_tools/lib/src/reporting/reporting.dart index ef9015ccb4..6047127486 100644 --- a/packages/flutter_tools/lib/src/reporting/reporting.dart +++ b/packages/flutter_tools/lib/src/reporting/reporting.dart @@ -8,11 +8,14 @@ import 'dart:async'; import 'package:file/file.dart'; import 'package:meta/meta.dart'; +import 'package:package_config/package_config.dart'; import 'package:usage/usage_io.dart'; import '../base/error_handling_io.dart'; import '../base/file_system.dart'; import '../base/time.dart'; +import '../build_info.dart'; +import '../dart/language_version.dart'; import '../doctor_validator.dart'; import '../features.dart'; import '../globals.dart' as globals; diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 3c61f39944..1ec5659ed9 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -118,8 +118,21 @@ class FlutterDevice { // used to file a bug, but the compiler will still start up correctly. if (targetPlatform == TargetPlatform.web_javascript) { // TODO(zanderso): consistently provide these flags across platforms. - const String platformDillName = 'ddc_outline_sound.dill'; + final String platformDillName; final List extraFrontEndOptions = List.of(buildInfo.extraFrontEndOptions); + if (buildInfo.nullSafetyMode == NullSafetyMode.unsound) { + platformDillName = 'ddc_outline.dill'; + if (!extraFrontEndOptions.contains('--no-sound-null-safety')) { + extraFrontEndOptions.add('--no-sound-null-safety'); + } + } else if (buildInfo.nullSafetyMode == NullSafetyMode.sound) { + platformDillName = 'ddc_outline_sound.dill'; + if (!extraFrontEndOptions.contains('--sound-null-safety')) { + extraFrontEndOptions.add('--sound-null-safety'); + } + } else { + throw StateError('Expected buildInfo.nullSafetyMode to be one of unsound or sound, got ${buildInfo.nullSafetyMode}'); + } final String platformDillPath = globals.fs.path.join( getWebPlatformBinariesDirectory(globals.artifacts!, buildInfo.webRenderer).path, diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index e1cd274232..515c8df102 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart @@ -1039,6 +1039,16 @@ class HotRunner extends ResidentRunner { } commandHelp.c.print(); commandHelp.q.print(); + if (debuggingOptions.buildInfo.nullSafetyMode != NullSafetyMode.sound) { + globals.printStatus(''); + globals.printStatus( + 'Running without sound null safety ⚠️', + emphasis: true, + ); + globals.printStatus( + 'Dart 3 will only support sound null safety, see https://dart.dev/null-safety', + ); + } globals.printStatus(''); printDebuggerList(); } diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 7e33fa518d..306779de7d 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -21,6 +21,7 @@ import '../bundle.dart' as bundle; import '../cache.dart'; import '../convert.dart'; import '../dart/generate_synthetic_packages.dart'; +import '../dart/language_version.dart'; import '../dart/package_map.dart'; import '../dart/pub.dart'; import '../device.dart'; @@ -109,10 +110,12 @@ class FlutterOptions { static const String kDartDefineFromFileOption = 'dart-define-from-file'; static const String kBundleSkSLPathOption = 'bundle-sksl-path'; static const String kPerformanceMeasurementFile = 'performance-measurement-file'; + static const String kNullSafety = 'sound-null-safety'; static const String kDeviceUser = 'device-user'; static const String kDeviceTimeout = 'device-timeout'; static const String kAnalyzeSize = 'analyze-size'; static const String kCodeSizeDirectory = 'code-size-directory'; + static const String kNullAssertions = 'null-assertions'; static const String kAndroidGradleDaemon = 'android-gradle-daemon'; static const String kDeferredComponents = 'deferred-components'; static const String kAndroidProjectArgs = 'android-project-arg'; @@ -692,6 +695,9 @@ abstract class FlutterCommand extends Command { /// Whether it is safe for this command to use a cached pub invocation. bool get cachePubGet => true; + /// Whether this command should report null safety analytics. + bool get reportNullSafety => false; + late final Duration? deviceDiscoveryTimeout = () { if ((argResults?.options.contains(FlutterOptions.kDeviceTimeout) ?? false) && (argResults?.wasParsed(FlutterOptions.kDeviceTimeout) ?? false)) { @@ -800,6 +806,25 @@ abstract class FlutterCommand extends Command { ); } + void addNullSafetyModeOptions({ required bool hide }) { + argParser.addFlag(FlutterOptions.kNullSafety, + help: + 'Whether to override the inferred null safety mode. This allows null-safe ' + 'libraries to depend on un-migrated (non-null safe) libraries. By default, ' + 'Flutter mobile & desktop applications will attempt to run at the null safety ' + 'level of their entrypoint library (usually lib/main.dart). Flutter web ' + 'applications will default to sound null-safety, unless specifically configured.', + defaultsTo: true, + hide: hide, + ); + argParser.addFlag(FlutterOptions.kNullAssertions, + help: + 'Perform additional null assertions on the boundaries of migrated and ' + 'un-migrated code. This setting is not currently supported on desktop ' + 'devices.' + ); + } + /// Enables support for the hidden options --extra-front-end-options and /// --extra-gen-snapshot-options. void usesExtraDartFlagOptions({ required bool verboseHelp }) { @@ -928,6 +953,7 @@ abstract class FlutterCommand extends Command { addBundleSkSLPathOption(hide: !verboseHelp); addDartObfuscationOption(); addEnableExperimentation(hide: !verboseHelp); + addNullSafetyModeOptions(hide: !verboseHelp); addSplitDebugInfoOption(); addTreeShakeIconsFlag(); usesAnalyzeSizeFlag(); @@ -1085,6 +1111,44 @@ abstract class FlutterCommand extends Command { codeSizeDirectory = directory.path; } + NullSafetyMode nullSafetyMode = NullSafetyMode.sound; + if (argParser.options.containsKey(FlutterOptions.kNullSafety)) { + // Explicitly check for `true` and `false` so that `null` results in not + // passing a flag. Examine the entrypoint file to determine if it + // is opted in or out. + final bool wasNullSafetyFlagParsed = argResults?.wasParsed(FlutterOptions.kNullSafety) ?? false; + if (!wasNullSafetyFlagParsed && (argParser.options.containsKey('target') || forcedTargetFile != null)) { + final File entrypointFile = forcedTargetFile ?? globals.fs.file(targetFile); + final LanguageVersion languageVersion = determineLanguageVersion( + entrypointFile, + packageConfig.packageOf(entrypointFile.absolute.uri), + Cache.flutterRoot!, + ); + // Extra frontend options are only provided if explicitly + // requested. + if ((languageVersion.major > nullSafeVersion.major) || + (languageVersion.major == nullSafeVersion.major && languageVersion.minor >= nullSafeVersion.minor)) { + nullSafetyMode = NullSafetyMode.sound; + } else { + throwToolExit( + 'This application does not support sound null-safety (its language version is $languageVersion).\n' + 'To build this application, you must provide the CLI flag --no-sound-null-safety. Dart 3 will only ' + 'support sound null safety, see https://dart.dev/null-safety.', + ); + } + } else if (!wasNullSafetyFlagParsed) { + // This mode is only used for commands which do not build a single target like + // 'flutter test'. + nullSafetyMode = NullSafetyMode.autodetect; + } else if (boolArg(FlutterOptions.kNullSafety)) { + nullSafetyMode = NullSafetyMode.sound; + extraFrontEndOptions.add('--sound-null-safety'); + } else { + nullSafetyMode = NullSafetyMode.unsound; + extraFrontEndOptions.add('--no-sound-null-safety'); + } + } + final bool dartObfuscation = argParser.options.containsKey(FlutterOptions.kDartObfuscationOption) && boolArg(FlutterOptions.kDartObfuscationOption); @@ -1168,6 +1232,7 @@ abstract class FlutterCommand extends Command { performanceMeasurementFile: performanceMeasurementFile, dartDefineConfigJsonMap: defineConfigJsonMap, packagesPath: packagesPath ?? globals.fs.path.absolute('.dart_tool', 'package_config.json'), + nullSafetyMode: nullSafetyMode, codeSizeDirectory: codeSizeDirectory, androidGradleDaemon: androidGradleDaemon, packageConfig: packageConfig, @@ -1427,6 +1492,9 @@ abstract class FlutterCommand extends Command { checkUpToDate: cachePubGet, ); await project.regeneratePlatformSpecificTooling(); + if (reportNullSafety) { + await _sendNullSafetyAnalyticsEvents(project); + } } setupApplicationPackages(); @@ -1440,6 +1508,16 @@ abstract class FlutterCommand extends Command { return runCommand(); } + Future _sendNullSafetyAnalyticsEvents(FlutterProject project) async { + final BuildInfo buildInfo = await getBuildInfo(); + NullSafetyAnalysisEvent( + buildInfo.packageConfig, + buildInfo.nullSafetyMode, + project.manifest.appName, + globals.flutterUsage, + ).send(); + } + /// The set of development artifacts required for this command. /// /// Defaults to an empty set. Including [DevelopmentArtifact.universal] is diff --git a/packages/flutter_tools/lib/src/test/flutter_tester_device.dart b/packages/flutter_tools/lib/src/test/flutter_tester_device.dart index 6d40e6890d..8a39c2d2c7 100644 --- a/packages/flutter_tools/lib/src/test/flutter_tester_device.dart +++ b/packages/flutter_tools/lib/src/test/flutter_tester_device.dart @@ -119,6 +119,8 @@ class FlutterTesterTestDevice extends TestDevice { '--packages=${debuggingOptions.buildInfo.packagesPath}', if (testAssetDirectory != null) '--flutter-assets-dir=$testAssetDirectory', + if (debuggingOptions.nullAssertions) + '--dart-flags=--null_assertions', ...debuggingOptions.dartEntrypointArgs, entrypointPath, ]; diff --git a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart index 537cc5d5e8..a780ca5b23 100644 --- a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart @@ -42,6 +42,7 @@ class FlutterWebPlatform extends PlatformPlugin { FlutterProject? flutterProject, String? shellPath, this.updateGoldens, + this.nullAssertions, required this.buildInfo, required this.webMemoryFS, required FileSystem fileSystem, @@ -91,6 +92,7 @@ class FlutterWebPlatform extends PlatformPlugin { final Logger _logger; final Artifacts? _artifacts; final bool? updateGoldens; + final bool? nullAssertions; final OneOffHandler _webSocketHandler = OneOffHandler(); final AsyncMemoizer _closeMemo = AsyncMemoizer(); final String _root; @@ -108,6 +110,7 @@ class FlutterWebPlatform extends PlatformPlugin { String? shellPath, bool updateGoldens = false, bool pauseAfterLoad = false, + bool nullAssertions = false, required BuildInfo buildInfo, required WebMemoryFS webMemoryFS, required FileSystem fileSystem, @@ -142,6 +145,7 @@ class FlutterWebPlatform extends PlatformPlugin { chromiumLauncher: chromiumLauncher, artifacts: artifacts, logger: logger, + nullAssertions: nullAssertions, processManager: processManager, testTimeRecorder: testTimeRecorder, ); @@ -158,6 +162,12 @@ class FlutterWebPlatform extends PlatformPlugin { : WebRendererMode.html; } + NullSafetyMode get _nullSafetyMode { + return buildInfo.nullSafetyMode == NullSafetyMode.sound + ? NullSafetyMode.sound + : NullSafetyMode.unsound; + } + final Configuration _config; final shelf.Server _server; Uri get url => _server.url; @@ -190,10 +200,10 @@ class FlutterWebPlatform extends PlatformPlugin { )); File get _dartSdk => _fileSystem.file( - _artifacts!.getHostArtifact(kDartSdkJsArtifactMap[_rendererMode]!)); + _artifacts!.getHostArtifact(kDartSdkJsArtifactMap[_rendererMode]![_nullSafetyMode]!)); File get _dartSdkSourcemaps => _fileSystem.file( - _artifacts!.getHostArtifact(kDartSdkJsMapArtifactMap[_rendererMode]!)); + _artifacts!.getHostArtifact(kDartSdkJsMapArtifactMap[_rendererMode]![_nullSafetyMode]!)); /// The precompiled test javascript. File get _testDartJs => _fileSystem.file(_fileSystem.path.join( @@ -234,6 +244,7 @@ class FlutterWebPlatform extends PlatformPlugin { final String leadingPath = request.url.path.split('.dart.bootstrap.js')[0]; final String generatedFile = '${_fileSystem.path.split(leadingPath).join('_')}.dart.test.dart.js'; return shelf.Response.ok(generateMainModule( + nullAssertions: nullAssertions!, nativeNullAssertions: true, bootstrapModule: '${_fileSystem.path.basename(leadingPath)}.dart.bootstrap', entrypoint: '/$generatedFile' diff --git a/packages/flutter_tools/lib/src/test/runner.dart b/packages/flutter_tools/lib/src/test/runner.dart index 8b2b2654d8..08972f4cb9 100644 --- a/packages/flutter_tools/lib/src/test/runner.dart +++ b/packages/flutter_tools/lib/src/test/runner.dart @@ -161,6 +161,7 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner { shellPath: shellPath, flutterProject: flutterProject, pauseAfterLoad: debuggingOptions.startPaused, + nullAssertions: debuggingOptions.nullAssertions, buildInfo: debuggingOptions.buildInfo, webMemoryFS: result, logger: globals.logger, diff --git a/packages/flutter_tools/lib/src/test/web_test_compiler.dart b/packages/flutter_tools/lib/src/test/web_test_compiler.dart index 08a84af421..ecf28ae080 100644 --- a/packages/flutter_tools/lib/src/test/web_test_compiler.dart +++ b/packages/flutter_tools/lib/src/test/web_test_compiler.dart @@ -49,9 +49,24 @@ class WebTestCompiler { required List testFiles, required BuildInfo buildInfo, }) async { + LanguageVersion languageVersion = LanguageVersion(2, 8); + late final String platformDillName; + + // TODO(zanderso): to support autodetect this would need to partition the source code into + // a sound and unsound set and perform separate compilations final List extraFrontEndOptions = List.of(buildInfo.extraFrontEndOptions); - final LanguageVersion languageVersion = currentLanguageVersion(_fileSystem, Cache.flutterRoot!); - const String platformDillName = 'ddc_outline_sound.dill'; + if (buildInfo.nullSafetyMode == NullSafetyMode.unsound || buildInfo.nullSafetyMode == NullSafetyMode.autodetect) { + platformDillName = 'ddc_outline.dill'; + if (!extraFrontEndOptions.contains('--no-sound-null-safety')) { + extraFrontEndOptions.add('--no-sound-null-safety'); + } + } else if (buildInfo.nullSafetyMode == NullSafetyMode.sound) { + languageVersion = currentLanguageVersion(_fileSystem, Cache.flutterRoot!); + platformDillName = 'ddc_outline_sound.dill'; + if (!extraFrontEndOptions.contains('--sound-null-safety')) { + extraFrontEndOptions.add('--sound-null-safety'); + } + } final String platformDillPath = _fileSystem.path.join( getWebPlatformBinariesDirectory(_artifacts, buildInfo.webRenderer).path, diff --git a/packages/flutter_tools/lib/src/web/bootstrap.dart b/packages/flutter_tools/lib/src/web/bootstrap.dart index e0d1650985..f05428a25d 100644 --- a/packages/flutter_tools/lib/src/web/bootstrap.dart +++ b/packages/flutter_tools/lib/src/web/bootstrap.dart @@ -153,6 +153,7 @@ document.head.appendChild(requireEl); /// this object is the module. String generateMainModule({ required String entrypoint, + required bool nullAssertions, required bool nativeNullAssertions, String bootstrapModule = 'main_module.bootstrap', }) { @@ -167,6 +168,7 @@ require.config({ define("$bootstrapModule", ["$entrypoint", "dart_sdk"], function(app, dart_sdk) { dart_sdk.dart.setStartAsyncSynchronously(true); dart_sdk._debugger.registerDevtoolsFormatter(); + dart_sdk.dart.nonNullAsserts($nullAssertions); dart_sdk.dart.nativeNonNullAsserts($nativeNullAssertions); // See the generateMainModule doc comment. diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart index 7f4e461506..3e094464cf 100644 --- a/packages/flutter_tools/lib/src/web/compile.dart +++ b/packages/flutter_tools/lib/src/web/compile.dart @@ -119,15 +119,33 @@ enum WebRendererMode { } /// The correct precompiled artifact to use for each build and render mode. -const Map kDartSdkJsArtifactMap = { - WebRendererMode.autoDetect: HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk, - WebRendererMode.canvaskit: HostArtifact.webPrecompiledCanvaskitSoundSdk, - WebRendererMode.html: HostArtifact.webPrecompiledSoundSdk, +const Map> kDartSdkJsArtifactMap = >{ + WebRendererMode.autoDetect: { + NullSafetyMode.sound: HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk, + NullSafetyMode.unsound: HostArtifact.webPrecompiledCanvaskitAndHtmlSdk, + }, + WebRendererMode.canvaskit: { + NullSafetyMode.sound: HostArtifact.webPrecompiledCanvaskitSoundSdk, + NullSafetyMode.unsound: HostArtifact.webPrecompiledCanvaskitSdk, + }, + WebRendererMode.html: { + NullSafetyMode.sound: HostArtifact.webPrecompiledSoundSdk, + NullSafetyMode.unsound: HostArtifact.webPrecompiledSdk, + }, }; /// The correct source map artifact to use for each build and render mode. -const Map kDartSdkJsMapArtifactMap = { - WebRendererMode.autoDetect: HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps, - WebRendererMode.canvaskit: HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps, - WebRendererMode.html: HostArtifact.webPrecompiledSoundSdkSourcemaps, +const Map> kDartSdkJsMapArtifactMap = >{ + WebRendererMode.autoDetect: { + NullSafetyMode.sound: HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps, + NullSafetyMode.unsound: HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps, + }, + WebRendererMode.canvaskit: { + NullSafetyMode.sound: HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps, + NullSafetyMode.unsound: HostArtifact.webPrecompiledCanvaskitSdkSourcemaps, + }, + WebRendererMode.html: { + NullSafetyMode.sound: HostArtifact.webPrecompiledSoundSdkSourcemaps, + NullSafetyMode.unsound: HostArtifact.webPrecompiledSdkSourcemaps, + }, }; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_darwin_framework_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_darwin_framework_test.dart index fa50ec7e50..5b84d3f659 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_darwin_framework_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_darwin_framework_test.dart @@ -4,6 +4,7 @@ import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; @@ -66,6 +67,7 @@ void main() { final FakeFlutterVersion fakeFlutterVersion = FakeFlutterVersion(frameworkVersion: frameworkVersion); final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -95,6 +97,7 @@ void main() { ); final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -121,6 +124,7 @@ void main() { ); final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -162,6 +166,7 @@ void main() { ); final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -196,6 +201,7 @@ void main() { testUsingContext('contains license and version', () async { final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -216,6 +222,7 @@ void main() { testUsingContext('debug URL', () async { final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -234,6 +241,7 @@ void main() { testUsingContext('profile URL', () async { final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -252,6 +260,7 @@ void main() { testUsingContext('release URL', () async { final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -295,6 +304,7 @@ void main() { final FakeFlutterVersion fakeFlutterVersion = FakeFlutterVersion(frameworkVersion: frameworkVersion); final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -324,6 +334,7 @@ void main() { ); final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -350,6 +361,7 @@ void main() { ); final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -391,6 +403,7 @@ void main() { ); final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -425,6 +438,7 @@ void main() { testUsingContext('contains license and version', () async { final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -445,6 +459,7 @@ void main() { testUsingContext('debug URL', () async { final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -463,6 +478,7 @@ void main() { testUsingContext('profile URL', () async { final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, @@ -481,6 +497,7 @@ void main() { testUsingContext('release URL', () async { final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand( + logger: BufferLogger.test(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), platform: fakePlatform, flutterVersion: fakeFlutterVersion, diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_ios_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_ios_test.dart index f542ef1fca..3774a33050 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_ios_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_ios_test.dart @@ -6,6 +6,7 @@ import 'package:args/command_runner.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/build_info.dart'; @@ -185,6 +186,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createCoreMockProjectFiles(); @@ -204,6 +206,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createCoreMockProjectFiles(); @@ -223,6 +226,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fileSystem.file('pubspec.yaml').createSync(); @@ -230,7 +234,7 @@ void main() { fileSystem.file(fileSystem.path.join('lib', 'main.dart')) .createSync(recursive: true); - final bool supported = BuildIOSCommand(verboseHelp: false).supported; + final bool supported = BuildIOSCommand(logger: BufferLogger.test(), verboseHelp: false).supported; expect(createTestCommandRunner(command).run( const ['build', 'ios', '--no-pub'] ), supported ? throwsToolExit() : throwsA(isA())); @@ -246,6 +250,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -272,6 +277,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -298,6 +304,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -323,6 +330,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -348,6 +356,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -397,6 +406,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -427,6 +437,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -461,6 +472,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -494,6 +506,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -522,6 +535,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -556,6 +570,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -590,6 +605,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -626,6 +642,7 @@ Runner requires a provisioning profile. Select a provisioning profile in the Sig androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -659,6 +676,7 @@ Runner requires a provisioning profile. Select a provisioning profile in the Sig androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -696,6 +714,7 @@ Runner requires a provisioning profile. Select a provisioning profile in the Sig androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -729,6 +748,7 @@ Runner requires a provisioning profile. Select a provisioning profile in the Sig androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -763,6 +783,7 @@ Runner requires a provisioning profile. Select a provisioning profile in the Sig androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -799,6 +820,7 @@ Runner requires a provisioning profile. Select a provisioning profile in the Sig androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -833,6 +855,7 @@ Runner requires a provisioning profile. Select a provisioning profile in the Sig androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -868,6 +891,7 @@ Runner requires a provisioning profile. Select a provisioning profile in the Sig androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -905,6 +929,7 @@ Runner requires a provisioning profile. Select a provisioning profile in the Sig androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart index 41711fd30f..3d061105ee 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart @@ -7,6 +7,7 @@ import 'dart:typed_data'; import 'package:args/command_runner.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/cache.dart'; @@ -197,6 +198,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createCoreMockProjectFiles(); @@ -216,6 +218,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createCoreMockProjectFiles(); @@ -235,6 +238,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fileSystem.file('pubspec.yaml').createSync(); @@ -242,7 +246,7 @@ void main() { fileSystem.file(fileSystem.path.join('lib', 'main.dart')) .createSync(recursive: true); - final bool supported = BuildIOSArchiveCommand(verboseHelp: false).supported; + final bool supported = BuildIOSArchiveCommand(logger: BufferLogger.test(), verboseHelp: false).supported; expect(createTestCommandRunner(command).run( const ['build', 'ipa', '--no-pub'] ), supported ? throwsToolExit() : throwsA(isA())); @@ -259,6 +263,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -287,6 +292,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -313,6 +319,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -341,6 +348,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fakeProcessManager.addCommands([ @@ -388,6 +396,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fakeProcessManager.addCommands([ @@ -436,6 +445,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fakeProcessManager.addCommands([ @@ -484,6 +494,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fakeProcessManager.addCommands([ @@ -531,6 +542,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fakeProcessManager.addCommands([ @@ -556,6 +568,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fakeProcessManager.addCommands([ @@ -604,6 +617,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -626,6 +640,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -682,6 +697,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fakeProcessManager.addCommands([ @@ -715,6 +731,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fakeProcessManager.addCommands([ @@ -745,6 +762,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fakeProcessManager.addCommands([ @@ -776,6 +794,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fakeProcessManager.addCommands([ @@ -809,6 +828,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fakeProcessManager.addCommands([ @@ -837,6 +857,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fakeProcessManager.addCommands([ @@ -888,6 +909,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( @@ -941,6 +963,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( @@ -990,6 +1013,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( @@ -1028,6 +1052,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( @@ -1108,6 +1133,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( @@ -1186,6 +1212,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( @@ -1244,6 +1271,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( @@ -1302,6 +1330,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( @@ -1360,6 +1389,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( @@ -1419,6 +1449,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( @@ -1522,6 +1553,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( @@ -1601,6 +1633,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( @@ -1678,6 +1711,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); await createTestCommandRunner(command).run( diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart index 2a3ecbaaba..d5f533465e 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart @@ -6,6 +6,7 @@ import 'package:args/command_runner.dart'; import 'package:file/memory.dart'; import 'package:file_testing/file_testing.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/utils.dart'; @@ -112,6 +113,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); setUpMockCoreProjectFiles(); @@ -133,6 +135,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); setUpMockProjectFilesForBuild(); @@ -152,6 +155,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); setUpMockProjectFilesForBuild(); @@ -171,6 +175,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); processManager = FakeProcessManager.list([ @@ -197,6 +202,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); setUpMockProjectFilesForBuild(); @@ -219,6 +225,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); setUpMockProjectFilesForBuild(); @@ -245,6 +252,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); setUpMockProjectFilesForBuild(); @@ -275,6 +283,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); setUpMockProjectFilesForBuild(); @@ -334,6 +343,7 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); setUpMockProjectFilesForBuild(); @@ -367,6 +377,7 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); setUpMockProjectFilesForBuild(); @@ -391,6 +402,7 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64), ); setUpMockProjectFilesForBuild(); @@ -414,6 +426,7 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); setUpMockProjectFilesForBuild(); @@ -438,6 +451,7 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64), ); setUpMockProjectFilesForBuild(); @@ -461,6 +475,7 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64), ); @@ -477,6 +492,7 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); setUpMockProjectFilesForBuild(); @@ -569,6 +585,7 @@ set(BINARY_NAME "fizz_bar") androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), )); @@ -579,14 +596,14 @@ set(BINARY_NAME "fizz_bar") }); testUsingContext('hidden when not enabled on Linux host', () { - expect(BuildLinuxCommand(operatingSystemUtils: FakeOperatingSystemUtils()).hidden, true); + expect(BuildLinuxCommand(logger: BufferLogger.test(), operatingSystemUtils: FakeOperatingSystemUtils()).hidden, true); }, overrides: { FeatureFlags: () => TestFeatureFlags(), Platform: () => notLinuxPlatform, }); testUsingContext('Not hidden when enabled and on Linux host', () { - expect(BuildLinuxCommand(operatingSystemUtils: FakeOperatingSystemUtils()).hidden, false); + expect(BuildLinuxCommand(logger: BufferLogger.test(), operatingSystemUtils: FakeOperatingSystemUtils()).hidden, false); }, overrides: { FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), Platform: () => linuxPlatform, @@ -597,6 +614,7 @@ set(BINARY_NAME "fizz_bar") androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); setUpMockProjectFilesForBuild(); @@ -647,6 +665,7 @@ set(BINARY_NAME "fizz_bar") androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64), ); setUpMockProjectFilesForBuild(); diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart index 3e6f6183e6..92a34a5518 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart @@ -142,6 +142,7 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createCoreMockProjectFiles(); @@ -163,6 +164,7 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fileSystem.file('pubspec.yaml').createSync(); @@ -184,6 +186,7 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); fileSystem.file('pubspec.yaml').createSync(); @@ -205,6 +208,7 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -234,6 +238,7 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -255,6 +260,7 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -277,6 +283,7 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -299,6 +306,7 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -320,6 +328,7 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -410,6 +419,7 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: fileSystem, + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); @@ -431,6 +441,7 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); @@ -465,10 +476,11 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), )); - final bool supported = BuildMacosCommand(verboseHelp: false).supported; + final bool supported = BuildMacosCommand(logger: BufferLogger.test(), verboseHelp: false).supported; expect(() => runner.run(['build', 'macos', '--no-pub']), supported ? throwsToolExit() : throwsA(isA())); }, overrides: { @@ -476,14 +488,14 @@ STDERR STUFF }); testUsingContext('hidden when not enabled on macOS host', () { - expect(BuildMacosCommand(verboseHelp: false).hidden, true); + expect(BuildMacosCommand(logger: BufferLogger.test(), verboseHelp: false).hidden, true); }, overrides: { FeatureFlags: () => TestFeatureFlags(), Platform: () => macosPlatform, }); testUsingContext('Not hidden when enabled and on macOS host', () { - expect(BuildMacosCommand(verboseHelp: false).hidden, false); + expect(BuildMacosCommand(logger: BufferLogger.test(), verboseHelp: false).hidden, false); }, overrides: { FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true), Platform: () => macosPlatform, @@ -494,6 +506,7 @@ STDERR STUFF androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); createMinimalMockProjectFiles(); diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_test.dart index 707007b05d..435c5d5834 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_test.dart @@ -5,6 +5,7 @@ import 'package:args/command_runner.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build.dart'; @@ -43,6 +44,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); try { @@ -64,6 +66,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); testLogger.printWarning('Warning: Mild annoyance Will Robinson!'); @@ -85,6 +88,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); testLogger.printWarning('Warning: Mild annoyance Will Robinson!'); @@ -103,6 +107,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); testLogger.printError('Error: Danger Will Robinson!'); @@ -142,10 +147,11 @@ class FakeBuildCommand extends BuildCommand { required super.fileSystem, required super.buildSystem, required super.osUtils, + required Logger logger, required super.androidSdk, bool verboseHelp = false, - }) : super(verboseHelp: verboseHelp,) { - addSubcommand(FakeBuildSubcommand(verboseHelp: verboseHelp)); + }) : super(logger: logger, verboseHelp: verboseHelp,) { + addSubcommand(FakeBuildSubcommand(logger: logger, verboseHelp: verboseHelp)); } @override @@ -161,7 +167,7 @@ class FakeBuildCommand extends BuildCommand { } class FakeBuildSubcommand extends BuildSubCommand { - FakeBuildSubcommand({required super.verboseHelp}); + FakeBuildSubcommand({required super.logger, required super.verboseHelp}); @override String get description => ''; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart index dbd2f32ce3..f93c3aa5f5 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart @@ -5,6 +5,7 @@ import 'package:args/command_runner.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; @@ -49,6 +50,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), )); @@ -68,6 +70,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: fileSystem, + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), )); @@ -84,6 +87,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), )); @@ -103,6 +107,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: fileSystem, + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); final CommandRunner runner = createTestCommandRunner(buildCommand); @@ -143,6 +148,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: fileSystem, + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); final CommandRunner runner = createTestCommandRunner(buildCommand); @@ -188,7 +194,7 @@ void main() { }); testUsingContext('hidden if feature flag is not enabled', () async { - expect(BuildWebCommand(fileSystem: fileSystem, verboseHelp: false).hidden, true); + expect(BuildWebCommand(fileSystem: fileSystem, logger: BufferLogger.test(), verboseHelp: false).hidden, true); }, overrides: { Platform: () => fakePlatform, FileSystem: () => fileSystem, @@ -197,7 +203,7 @@ void main() { }); testUsingContext('not hidden if feature flag is enabled', () async { - expect(BuildWebCommand(fileSystem: fileSystem, verboseHelp: false).hidden, false); + expect(BuildWebCommand(fileSystem: fileSystem, logger: BufferLogger.test(), verboseHelp: false).hidden, false); }, overrides: { Platform: () => fakePlatform, FileSystem: () => fileSystem, @@ -301,6 +307,7 @@ class TestWebBuildCommand extends FlutterCommand { TestWebBuildCommand({ required FileSystem fileSystem, bool verboseHelp = false }) : webCommand = BuildWebCommand( fileSystem: fileSystem, + logger: BufferLogger.test(), verboseHelp: verboseHelp) { addSubcommand(webCommand); } diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart index f20daef368..921b14ecfa 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart @@ -5,6 +5,7 @@ import 'package:file/memory.dart'; import 'package:file_testing/file_testing.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build_windows.dart'; @@ -116,7 +117,7 @@ void main() { } testUsingContext('Windows build fails when there is no cmake path', () async { - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = FakeVisualStudio(cmakePath: null); setUpMockProjectFilesForBuild(); @@ -132,7 +133,7 @@ void main() { testUsingContext('Windows build fails when there is no windows project', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockCoreProjectFiles(); @@ -150,7 +151,7 @@ void main() { testUsingContext('Windows build fails on non windows platform', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -166,7 +167,7 @@ void main() { testUsingContext('Windows build fails when feature is disabled', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -182,7 +183,7 @@ void main() { testUsingContext('Windows build does not spew stdout to status logger', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -207,7 +208,7 @@ void main() { testUsingContext('Windows build extracts errors from stdout', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -265,7 +266,7 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier testUsingContext('Windows verbose build sets VERBOSE_SCRIPT_LOGGING', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -291,7 +292,7 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier testUsingContext('Windows build works around CMake generation bug', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(displayVersion: '17.1.0'); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -427,7 +428,7 @@ if %errorlevel% neq 0 goto :VCEnd testUsingContext('Windows build invokes build and writes generated files', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -497,7 +498,7 @@ if %errorlevel% neq 0 goto :VCEnd testUsingContext('Windows profile build passes Profile configuration', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -520,7 +521,7 @@ if %errorlevel% neq 0 goto :VCEnd const String generator = 'A different generator'; final FakeVisualStudio fakeVisualStudio = FakeVisualStudio( cmakeGenerator: generator); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -541,7 +542,7 @@ if %errorlevel% neq 0 goto :VCEnd testUsingContext("Windows build uses pubspec's version", () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -587,7 +588,7 @@ if %errorlevel% neq 0 goto :VCEnd testUsingContext('Windows build uses build-name and build-number', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -631,7 +632,7 @@ if %errorlevel% neq 0 goto :VCEnd testUsingContext('Windows build build-name overrides pubspec', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -678,7 +679,7 @@ if %errorlevel% neq 0 goto :VCEnd testUsingContext('Windows build build-number overrides pubspec', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -725,7 +726,7 @@ if %errorlevel% neq 0 goto :VCEnd testUsingContext('Windows build build-name and build-number override pubspec', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -773,7 +774,7 @@ if %errorlevel% neq 0 goto :VCEnd testUsingContext('Windows build warns on non-numeric build-number', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -823,7 +824,7 @@ if %errorlevel% neq 0 goto :VCEnd testUsingContext('Windows build warns on complex build-number', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -872,14 +873,14 @@ if %errorlevel% neq 0 goto :VCEnd }); testUsingContext('hidden when not enabled on Windows host', () { - expect(BuildWindowsCommand().hidden, true); + expect(BuildWindowsCommand(logger: BufferLogger.test()).hidden, true); }, overrides: { FeatureFlags: () => TestFeatureFlags(), Platform: () => windowsPlatform, }); testUsingContext('Not hidden when enabled and on Windows host', () { - expect(BuildWindowsCommand().hidden, false); + expect(BuildWindowsCommand(logger: BufferLogger.test()).hidden, false); }, overrides: { FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), Platform: () => windowsPlatform, @@ -887,7 +888,7 @@ if %errorlevel% neq 0 goto :VCEnd testUsingContext('Performs code size analysis and sends analytics', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; setUpMockProjectFilesForBuild(); @@ -939,7 +940,7 @@ if %errorlevel% neq 0 goto :VCEnd // is resolved on the VS side, we can allow these paths again testUsingContext('Test bad path characters', () async { final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(); - final BuildWindowsCommand command = BuildWindowsCommand() + final BuildWindowsCommand command = BuildWindowsCommand(logger: BufferLogger.test()) ..visualStudioOverride = fakeVisualStudio; fileSystem.currentDirectory = fileSystem.directory("test_'path") ..createSync(); diff --git a/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart index e0871361f8..ae655dd25e 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart @@ -381,6 +381,7 @@ void main() { '--trace-skia', '--trace-systrace', '--verbose-system-logs', + '--null-assertions', '--native-null-assertions', '--enable-impeller', '--trace-systrace', @@ -396,6 +397,7 @@ void main() { expect(options.traceSkia, true); expect(options.traceSystrace, true); expect(options.verboseSystemLogs, true); + expect(options.nullAssertions, true); expect(options.nativeNullAssertions, true); expect(options.enableImpeller, true); expect(options.traceSystrace, true); diff --git a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart index da2ef4389e..d70b825a2f 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart @@ -1005,6 +1005,7 @@ void main() { '--trace-skia', '--trace-systrace', '--verbose-system-logs', + '--null-assertions', '--native-null-assertions', '--enable-impeller', '--trace-systrace', @@ -1021,6 +1022,7 @@ void main() { expect(options.traceSkia, true); expect(options.traceSystrace, true); expect(options.verboseSystemLogs, true); + expect(options.nullAssertions, true); expect(options.nativeNullAssertions, true); expect(options.traceSystrace, true); expect(options.enableImpeller, true); diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_aar_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_aar_test.dart index ad4c221d09..a88a3ad534 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_aar_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_aar_test.dart @@ -7,6 +7,7 @@ import 'package:flutter_tools/src/android/android_builder.dart'; import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/android/android_studio.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build_aar.dart'; @@ -30,6 +31,7 @@ void main() { final BuildAarCommand command = BuildAarCommand( androidSdk: FakeAndroidSdk(), fileSystem: globals.fs, + logger: BufferLogger.test(), verboseHelp: false, ); final CommandRunner runner = createTestCommandRunner(command); @@ -187,6 +189,7 @@ void main() { expect(buildInfo.splitDebugInfoPath, '/project-name/v1.2.3/'); expect(buildInfo.dartObfuscation, isTrue); expect(buildInfo.dartDefines.contains('foo=bar'), isTrue); + expect(buildInfo.nullSafetyMode, NullSafetyMode.sound); }, overrides: { AndroidBuilder: () => fakeAndroidBuilder, }); @@ -306,6 +309,7 @@ Future runBuildAarCommand( final BuildAarCommand command = BuildAarCommand( androidSdk: androidSdk, fileSystem: globals.fs, + logger: BufferLogger.test(), verboseHelp: false, ); final CommandRunner runner = createTestCommandRunner(command); diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart index 19540e56de..daac72e655 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart @@ -7,6 +7,7 @@ import 'package:flutter_tools/src/android/android_builder.dart'; import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/android/android_studio.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build_apk.dart'; import 'package:flutter_tools/src/globals.dart' as globals; @@ -429,7 +430,7 @@ Future runBuildApkCommand( String target, { List? arguments, }) async { - final BuildApkCommand command = BuildApkCommand(); + final BuildApkCommand command = BuildApkCommand(logger: BufferLogger.test()); final CommandRunner runner = createTestCommandRunner(command); await runner.run([ 'apk', diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_appbundle_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_appbundle_test.dart index 9f21267168..0bd3eaa08a 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_appbundle_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_appbundle_test.dart @@ -6,6 +6,7 @@ import 'package:args/command_runner.dart'; import 'package:flutter_tools/src/android/android_builder.dart'; import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build_appbundle.dart'; import 'package:flutter_tools/src/globals.dart' as globals; @@ -213,7 +214,7 @@ Future runBuildAppBundleCommand( String target, { List? arguments, }) async { - final BuildAppBundleCommand command = BuildAppBundleCommand(); + final BuildAppBundleCommand command = BuildAppBundleCommand(logger: BufferLogger.test()); final CommandRunner runner = createTestCommandRunner(command); await runner.run([ 'appbundle', 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 13e454adc6..db99b2b75e 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 @@ -5,6 +5,7 @@ import 'package:args/command_runner.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/bundle.dart'; @@ -46,6 +47,7 @@ void main() { Future runCommandIn(String projectPath, { List? arguments }) async { final BuildBundleCommand command = BuildBundleCommand( + logger: BufferLogger.test(), bundleBuilder: fakeBundleBuilder, ); final CommandRunner runner = createTestCommandRunner(command); @@ -89,7 +91,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(recursive: true); globals.fs.file('.packages').createSync(recursive: true); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); expect(() => runner.run([ 'bundle', @@ -107,7 +111,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); expect(() => runner.run([ 'bundle', @@ -125,7 +131,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); expect(() => runner.run([ 'bundle', @@ -143,7 +151,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); expect(() => runner.run([ 'bundle', @@ -160,7 +170,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -178,7 +190,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -196,7 +210,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -214,7 +230,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -244,7 +262,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -275,7 +295,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -305,7 +327,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -336,7 +360,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -367,7 +393,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -398,7 +426,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -437,7 +467,9 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -493,7 +525,9 @@ void main() { } ''' ); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -530,7 +564,9 @@ void main() { } ''' ); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); await runner.run([ 'bundle', @@ -551,7 +587,9 @@ void main() { globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); globals.fs.directory('config').createSync(); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); expect(() => runner.run([ 'bundle', @@ -578,7 +616,9 @@ void main() { } ''' ); - final CommandRunner runner = createTestCommandRunner(BuildBundleCommand()); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand( + logger: BufferLogger.test(), + )); expect(() => runner.run([ 'bundle', diff --git a/packages/flutter_tools/test/general.shard/analytics_test.dart b/packages/flutter_tools/test/general.shard/analytics_test.dart index 1f4b067d6c..e44ef7debb 100644 --- a/packages/flutter_tools/test/general.shard/analytics_test.dart +++ b/packages/flutter_tools/test/general.shard/analytics_test.dart @@ -9,6 +9,7 @@ import 'package:flutter_tools/src/android/android_workflow.dart'; import 'package:flutter_tools/src/base/config.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/time.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; @@ -219,6 +220,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); final FlutterCommand buildApkCommand = buildCommand.subcommands['apk']! as FlutterCommand; diff --git a/packages/flutter_tools/test/general.shard/android/android_device_start_test.dart b/packages/flutter_tools/test/general.shard/android/android_device_start_test.dart index 573a2b348d..ac67828cbb 100644 --- a/packages/flutter_tools/test/general.shard/android/android_device_start_test.dart +++ b/packages/flutter_tools/test/general.shard/android/android_device_start_test.dart @@ -250,7 +250,7 @@ void main() { '--ez', 'verify-entry-points', 'true', '--ez', 'start-paused', 'true', '--ez', 'disable-service-auth-codes', 'true', - '--es', 'dart-flags', 'foo', + '--es', 'dart-flags', 'foo,--null_assertions', '--ez', 'use-test-fonts', 'true', '--ez', 'verbose-logging', 'true', '--user', '10', @@ -278,6 +278,7 @@ void main() { purgePersistentCache: true, useTestFonts: true, verboseSystemLogs: true, + nullAssertions: true, enableImpeller: true, ), platformArgs: {}, diff --git a/packages/flutter_tools/test/general.shard/artifacts_test.dart b/packages/flutter_tools/test/general.shard/artifacts_test.dart index f09c1db695..dbbc815b56 100644 --- a/packages/flutter_tools/test/general.shard/artifacts_test.dart +++ b/packages/flutter_tools/test/general.shard/artifacts_test.dart @@ -152,6 +152,22 @@ void main() { }); testWithoutContext('precompiled web artifact paths are correct', () { + expect( + artifacts.getHostArtifact(HostArtifact.webPrecompiledSdk).path, + 'root/bin/cache/flutter_web_sdk/kernel/amd/dart_sdk.js', + ); + expect( + artifacts.getHostArtifact(HostArtifact.webPrecompiledSdkSourcemaps).path, + 'root/bin/cache/flutter_web_sdk/kernel/amd/dart_sdk.js.map', + ); + expect( + artifacts.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSdk).path, + 'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit/dart_sdk.js', + ); + expect( + artifacts.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSdkSourcemaps).path, + 'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit/dart_sdk.js.map', + ); expect( artifacts.getHostArtifact(HostArtifact.webPrecompiledSoundSdk).path, 'root/bin/cache/flutter_web_sdk/kernel/amd-sound/dart_sdk.js', diff --git a/packages/flutter_tools/test/general.shard/commands/build_test.dart b/packages/flutter_tools/test/general.shard/commands/build_test.dart index 062508c409..33e6a5e78d 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_test.dart @@ -2,13 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:args/args.dart'; import 'package:args/command_runner.dart'; import 'package:file/memory.dart'; +import 'package:flutter_tools/src/artifacts.dart'; +import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; +import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/base/platform.dart'; +import 'package:flutter_tools/src/base/signals.dart'; import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; +import 'package:flutter_tools/src/commands/attach.dart'; import 'package:flutter_tools/src/commands/build.dart'; +import 'package:flutter_tools/src/commands/build_aar.dart'; +import 'package:flutter_tools/src/commands/build_apk.dart'; +import 'package:flutter_tools/src/commands/build_appbundle.dart'; +import 'package:flutter_tools/src/commands/build_ios.dart'; +import 'package:flutter_tools/src/commands/build_ios_framework.dart'; +import 'package:flutter_tools/src/commands/build_linux.dart'; +import 'package:flutter_tools/src/commands/build_macos.dart'; +import 'package:flutter_tools/src/commands/build_web.dart'; +import 'package:flutter_tools/src/commands/build_windows.dart'; import 'package:flutter_tools/src/runner/flutter_command.dart'; import 'package:test/fake.dart'; @@ -30,11 +46,78 @@ class FakeProcessInfo extends Fake implements ProcessInfo { } void main() { + testUsingContext('All build commands support null safety options', () { + final FileSystem fileSystem = MemoryFileSystem.test(); + final Platform platform = FakePlatform(); + final BufferLogger logger = BufferLogger.test(); + final List commands = [ + BuildWindowsCommand(logger: BufferLogger.test()), + BuildLinuxCommand(logger: BufferLogger.test(), operatingSystemUtils: FakeOperatingSystemUtils()), + BuildMacosCommand(logger: BufferLogger.test(), verboseHelp: false), + BuildWebCommand(fileSystem: fileSystem, logger: BufferLogger.test(), verboseHelp: false), + BuildApkCommand(logger: BufferLogger.test()), + BuildIOSCommand(logger: BufferLogger.test(), verboseHelp: false), + BuildIOSArchiveCommand(logger: BufferLogger.test(), verboseHelp: false), + BuildAppBundleCommand(logger: BufferLogger.test()), + BuildAarCommand( + logger: BufferLogger.test(), + androidSdk: FakeAndroidSdk(), + fileSystem: fileSystem, + verboseHelp: false, + ), + BuildIOSFrameworkCommand( + logger: BufferLogger.test(), + verboseHelp: false, + buildSystem: FlutterBuildSystem( + fileSystem: fileSystem, + platform: platform, + logger: logger, + ), + ), + AttachCommand( + artifacts: Artifacts.test(), + stdio: FakeStdio(), + logger: logger, + terminal: FakeTerminal(), + signals: Signals.test(), + platform: platform, + processInfo: FakeProcessInfo(), + fileSystem: MemoryFileSystem.test(), + ), + ]; + + for (final FlutterCommand command in commands) { + final ArgResults results = command.argParser.parse([ + '--sound-null-safety', + '--enable-experiment=non-nullable', + ]); + + expect(results.wasParsed('sound-null-safety'), true); + expect(results.wasParsed('enable-experiment'), true); + } + }); + + testUsingContext('BuildSubCommand displays current null safety mode', + () async { + const BuildInfo unsound = BuildInfo( + BuildMode.debug, + '', + nullSafetyMode: NullSafetyMode.unsound, + treeShakeIcons: false, + ); + + final BufferLogger logger = BufferLogger.test(); + FakeBuildSubCommand(logger).test(unsound); + expect(logger.statusText, + contains('Building without sound null safety ⚠️')); + }); + testUsingContext('Include only supported sub commands', () { final BuildCommand command = BuildCommand( androidSdk: FakeAndroidSdk(), buildSystem: TestBuildSystem.all(BuildResult(success: true)), fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), ); for (final Command x in command.subcommands.values) { @@ -44,7 +127,7 @@ void main() { } class FakeBuildSubCommand extends BuildSubCommand { - FakeBuildSubCommand() : super(verboseHelp: false); + FakeBuildSubCommand(Logger logger) : super(logger: logger, verboseHelp: false); @override String get description => throw UnimplementedError(); @@ -53,8 +136,7 @@ class FakeBuildSubCommand extends BuildSubCommand { String get name => throw UnimplementedError(); void test(BuildInfo buildInfo) { - throw UnimplementedError('TODO what should we do here?'); - //displayNullSafetyMode(buildInfo); + displayNullSafetyMode(buildInfo); } @override diff --git a/packages/flutter_tools/test/general.shard/desktop_device_test.dart b/packages/flutter_tools/test/general.shard/desktop_device_test.dart index 1aa8280d2f..582e50efd8 100644 --- a/packages/flutter_tools/test/general.shard/desktop_device_test.dart +++ b/packages/flutter_tools/test/general.shard/desktop_device_test.dart @@ -159,9 +159,10 @@ void main() { 'FLUTTER_ENGINE_SWITCH_14': 'verify-entry-points=true', 'FLUTTER_ENGINE_SWITCH_15': 'start-paused=true', 'FLUTTER_ENGINE_SWITCH_16': 'disable-service-auth-codes=true', - 'FLUTTER_ENGINE_SWITCH_17': 'use-test-fonts=true', - 'FLUTTER_ENGINE_SWITCH_18': 'verbose-logging=true', - 'FLUTTER_ENGINE_SWITCHES': '18', + 'FLUTTER_ENGINE_SWITCH_17': 'dart-flags=--null_assertions', + 'FLUTTER_ENGINE_SWITCH_18': 'use-test-fonts=true', + 'FLUTTER_ENGINE_SWITCH_19': 'verbose-logging=true', + 'FLUTTER_ENGINE_SWITCHES': '19', } ), ]); @@ -189,6 +190,7 @@ void main() { purgePersistentCache: true, useTestFonts: true, verboseSystemLogs: true, + nullAssertions: true, ), ); diff --git a/packages/flutter_tools/test/general.shard/device_test.dart b/packages/flutter_tools/test/general.shard/device_test.dart index f7845c47d5..423e9873a2 100644 --- a/packages/flutter_tools/test/general.shard/device_test.dart +++ b/packages/flutter_tools/test/general.shard/device_test.dart @@ -701,9 +701,11 @@ void main() { }); }); - testWithoutContext('computeDartVmFlags handles various combinations of Dart VM flags', () { + testWithoutContext('computeDartVmFlags handles various combinations of Dart VM flags and null_assertions', () { expect(computeDartVmFlags(DebuggingOptions.enabled(BuildInfo.debug)), ''); expect(computeDartVmFlags(DebuggingOptions.enabled(BuildInfo.debug, dartFlags: '--foo')), '--foo'); + expect(computeDartVmFlags(DebuggingOptions.enabled(BuildInfo.debug, nullAssertions: true)), '--null_assertions'); + expect(computeDartVmFlags(DebuggingOptions.enabled(BuildInfo.debug, dartFlags: '--foo', nullAssertions: true)), '--foo,--null_assertions'); }); group('JSON encode DebuggingOptions', () { @@ -755,6 +757,7 @@ void main() { cacheSkSL: true, purgePersistentCache: true, verboseSystemLogs: true, + nullAssertions: true, enableImpeller: true, deviceVmServicePort: 0, hostVmServicePort: 1, @@ -775,7 +778,7 @@ void main() { '--disable-service-auth-codes', '--disable-vm-service-publication', '--start-paused', - '--dart-flags="--foo"', + '--dart-flags="--foo,--null_assertions"', '--use-test-fonts', '--enable-checked-mode', '--verify-entry-points', @@ -914,6 +917,7 @@ void main() { cacheSkSL: true, purgePersistentCache: true, verboseSystemLogs: true, + nullAssertions: true, enableImpeller: true, deviceVmServicePort: 0, hostVmServicePort: 1, @@ -934,7 +938,7 @@ void main() { '--disable-service-auth-codes', '--disable-vm-service-publication', '--start-paused', - '--dart-flags=--foo', + '--dart-flags=--foo,--null_assertions', '--use-test-fonts', '--enable-checked-mode', '--verify-entry-points', diff --git a/packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart index 7bd6988877..7926d62b6e 100644 --- a/packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart @@ -337,7 +337,7 @@ void main() { '--disable-service-auth-codes', '--disable-vm-service-publication', '--start-paused', - '--dart-flags="--foo"', + '--dart-flags="--foo,--null_assertions"', '--use-test-fonts', '--enable-checked-mode', '--verify-entry-points', @@ -404,6 +404,7 @@ void main() { cacheSkSL: true, purgePersistentCache: true, verboseSystemLogs: true, + nullAssertions: true, enableImpeller: true, enableEmbedderApi: true, ), diff --git a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart index fb90e83942..3bd597e36a 100644 --- a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart @@ -1041,6 +1041,7 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' cacheSkSL: true, purgePersistentCache: true, dartFlags: '--baz', + nullAssertions: true, enableImpeller: true, hostVmServicePort: 0, ); @@ -1064,7 +1065,7 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' '--verbose-logging', '--cache-sksl', '--purge-persistent-cache', - '--dart-flags=--baz', + '--dart-flags=--baz,--null_assertions', '--enable-impeller', '--vm-service-port=0', ])); diff --git a/packages/flutter_tools/test/general.shard/reporting/events_test.dart b/packages/flutter_tools/test/general.shard/reporting/events_test.dart index f1bd2319f7..4b9bba7a5a 100644 --- a/packages/flutter_tools/test/general.shard/reporting/events_test.dart +++ b/packages/flutter_tools/test/general.shard/reporting/events_test.dart @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/doctor_validator.dart'; import 'package:flutter_tools/src/reporting/reporting.dart'; +import 'package:package_config/package_config.dart'; import '../../src/common.dart'; @@ -54,6 +56,74 @@ void main() { const TestUsageEvent('doctor-result', 'FakeGroupedValidator', label: 'crash'), )); }); + + testWithoutContext('Reports null safe analytics events', () { + final TestUsage usage = TestUsage(); + final PackageConfig packageConfig = PackageConfig([ + Package('foo', Uri.parse('file:///foo/'), languageVersion: LanguageVersion(2, 12)), + Package('bar', Uri.parse('file:///fizz/'), languageVersion: LanguageVersion(2, 1)), + Package('baz', Uri.parse('file:///bar/'), languageVersion: LanguageVersion(2, 2)), + ]); + + NullSafetyAnalysisEvent( + packageConfig, + NullSafetyMode.sound, + 'foo', + usage, + ).send(); + + expect(usage.events, unorderedEquals([ + const TestUsageEvent(NullSafetyAnalysisEvent.kNullSafetyCategory, 'runtime-mode', label: 'NullSafetyMode.sound'), + TestUsageEvent(NullSafetyAnalysisEvent.kNullSafetyCategory, 'stats', parameters: CustomDimensions.fromMap({ + 'cd49': '1', 'cd50': '3', + })), + const TestUsageEvent(NullSafetyAnalysisEvent.kNullSafetyCategory, 'language-version', label: '2.12'), + ])); + }); + + testWithoutContext('Does not crash if main package is missing', () { + final TestUsage usage = TestUsage(); + final PackageConfig packageConfig = PackageConfig([ + Package('foo', Uri.parse('file:///foo/lib/'), languageVersion: LanguageVersion(2, 12)), + Package('bar', Uri.parse('file:///fizz/lib/'), languageVersion: LanguageVersion(2, 1)), + Package('baz', Uri.parse('file:///bar/lib/'), languageVersion: LanguageVersion(2, 2)), + ]); + + NullSafetyAnalysisEvent( + packageConfig, + NullSafetyMode.sound, + 'something-unrelated', + usage, + ).send(); + + expect(usage.events, unorderedEquals([ + const TestUsageEvent(NullSafetyAnalysisEvent.kNullSafetyCategory, 'runtime-mode', label: 'NullSafetyMode.sound'), + TestUsageEvent(NullSafetyAnalysisEvent.kNullSafetyCategory, 'stats', parameters: CustomDimensions.fromMap({ + 'cd49': '1', 'cd50': '3', + })), + ])); + }); + + testWithoutContext('a null language version is treated as unmigrated', () { + final TestUsage usage = TestUsage(); + final PackageConfig packageConfig = PackageConfig([ + Package('foo', Uri.parse('file:///foo/lib/')), + ]); + + NullSafetyAnalysisEvent( + packageConfig, + NullSafetyMode.sound, + 'something-unrelated', + usage, + ).send(); + + expect(usage.events, unorderedEquals([ + const TestUsageEvent(NullSafetyAnalysisEvent.kNullSafetyCategory, 'runtime-mode', label: 'NullSafetyMode.sound'), + TestUsageEvent(NullSafetyAnalysisEvent.kNullSafetyCategory, 'stats', parameters: CustomDimensions.fromMap({ + 'cd49': '0', 'cd50': '1', + })), + ])); + }); } class FakeGroupedValidator extends GroupedValidator { diff --git a/packages/flutter_tools/test/general.shard/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_runner_test.dart index 6249aa9c2f..deee43d290 100644 --- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart @@ -1960,13 +1960,45 @@ flutter: BuildMode.debug, '', treeShakeIcons: false, + nullSafetyMode: NullSafetyMode.unsound, ), target: null, platform: FakePlatform(), )).generator as DefaultResidentCompiler?; expect(residentCompiler!.initializeFromDill, - globals.fs.path.join(getBuildDirectory(), 'cache.dill')); + globals.fs.path.join(getBuildDirectory(), 'fbbe6a61fb7a1de317d381f8df4814e5.cache.dill')); + expect(residentCompiler.librariesSpec, + globals.fs.file(globals.artifacts!.getHostArtifact(HostArtifact.flutterWebLibrariesJson)) + .uri.toString()); + expect(residentCompiler.targetModel, TargetModel.dartdevc); + expect(residentCompiler.sdkRoot, + '${globals.artifacts!.getHostArtifact(HostArtifact.flutterWebSdk).path}/'); + expect(residentCompiler.platformDill, 'file:///HostArtifact.webPlatformKernelFolder/ddc_outline.dill'); + }, overrides: { + Artifacts: () => Artifacts.test(), + FileSystem: () => MemoryFileSystem.test(), + ProcessManager: () => FakeProcessManager.any(), + }); + + testUsingContext('FlutterDevice uses dartdevc configuration when targeting web with null-safety autodetected', () async { + fakeVmServiceHost = FakeVmServiceHost(requests: []); + final FakeDevice device = FakeDevice(targetPlatform: TargetPlatform.web_javascript); + + final DefaultResidentCompiler? residentCompiler = (await FlutterDevice.create( + device, + buildInfo: const BuildInfo( + BuildMode.debug, + '', + treeShakeIcons: false, + extraFrontEndOptions: ['--enable-experiment=non-nullable'], + ), + target: null, + platform: FakePlatform(), + )).generator as DefaultResidentCompiler?; + + expect(residentCompiler!.initializeFromDill, + globals.fs.path.join(getBuildDirectory(), '80b1a4cf4e7b90e1ab5f72022a0bc624.cache.dill')); expect(residentCompiler.librariesSpec, globals.fs.file(globals.artifacts!.getHostArtifact(HostArtifact.flutterWebLibrariesJson)) .uri.toString()); 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 46ffdfff3c..a9a6594a08 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 @@ -497,6 +497,82 @@ void main() { )); }); + testUsingContext('reports null safety analytics when reportNullSafety is true', () async { + globals.fs.file('lib/main.dart') + ..createSync(recursive: true) + ..writeAsStringSync('// @dart=2.12'); + globals.fs.file('pubspec.yaml') + .writeAsStringSync('name: example\n'); + globals.fs.file('.dart_tool/package_config.json') + ..createSync(recursive: true) + ..writeAsStringSync(r''' +{ + "configVersion": 2, + "packages": [ + { + "name": "example", + "rootUri": "../", + "packageUri": "lib/", + "languageVersion": "2.12" + } + ], + "generated": "2020-12-02T19:30:53.862346Z", + "generator": "pub", + "generatorVersion": "2.12.0-76.0.dev" +} +'''); + final FakeReportingNullSafetyCommand command = FakeReportingNullSafetyCommand(); + final CommandRunner runner = createTestCommandRunner(command); + + await runner.run(['test']); + + expect(usage.events, containsAll([ + const TestUsageEvent( + NullSafetyAnalysisEvent.kNullSafetyCategory, + 'runtime-mode', + label: 'NullSafetyMode.sound', + ), + TestUsageEvent( + NullSafetyAnalysisEvent.kNullSafetyCategory, + 'stats', + parameters: CustomDimensions.fromMap({ + 'cd49': '1', 'cd50': '1', + }), + ), + const TestUsageEvent( + NullSafetyAnalysisEvent.kNullSafetyCategory, + 'language-version', + label: '2.12', + ), + ])); + }, overrides: { + Pub: () => FakePub(), + Usage: () => usage, + FileSystem: () => fileSystem, + ProcessManager: () => processManager, + }); + + testUsingContext('tool exits on non-sound-null-safe code when explicit flag not passed', () async { + final DummyFlutterCommand flutterCommand = DummyFlutterCommand(packagesPath: 'foo'); + flutterCommand.argParser + ..addFlag(FlutterOptions.kNullSafety, defaultsTo: true) + ..addOption('target'); + final File targetFile = fileSystem.file('targetFile.dart') + ..writeAsStringSync('// @dart = 2.11'); + expect( + () async => flutterCommand.getBuildInfo( + forcedBuildMode: BuildMode.debug, + forcedTargetFile: targetFile, + ), + throwsToolExit( + message: 'This application does not support sound null-safety (its language version is 2.11)', + ), + ); + }, overrides: { + FileSystem: () => fileSystem, + ProcessManager: () => processManager, + }); + testUsingContext('use packagesPath to generate BuildInfo', () async { final DummyFlutterCommand flutterCommand = DummyFlutterCommand(packagesPath: 'foo'); final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug); @@ -701,6 +777,9 @@ class FakeReportingNullSafetyCommand extends FlutterCommand { @override bool get shouldRunPub => true; + @override + bool get reportNullSafety => true; + @override Future runCommand() async { return FlutterCommandResult.success(); diff --git a/packages/flutter_tools/test/general.shard/test/web_test_compiler_test.dart b/packages/flutter_tools/test/general.shard/test/web_test_compiler_test.dart index d177922035..54f8be20c9 100644 --- a/packages/flutter_tools/test/general.shard/test/web_test_compiler_test.dart +++ b/packages/flutter_tools/test/general.shard/test/web_test_compiler_test.dart @@ -35,7 +35,7 @@ void main() { platform: platform, ); final FakeProcessManager processManager = FakeProcessManager.list([ - const FakeCommand(command: [ + FakeCommand(command: [ 'Artifact.engineDartBinary.TargetPlatform.web_javascript', '--disable-dart-dev', 'Artifact.frontendServerSnapshotForEngineDartSdk.TargetPlatform.web_javascript', @@ -58,10 +58,11 @@ void main() { '--filesystem-scheme', 'org-dartlang-app', '--initialize-from-dill', - r'build/cache.dill', + RegExp(r'^build\/(?:[a-z0-9]{32})\.cache\.dill$'), '--platform', 'file:///HostArtifact.webPlatformKernelFolder/ddc_outline_sound.dill', '--verbosity=error', + '--sound-null-safety' ], stdout: 'result abc\nline0\nline1\nabc\nabc build/out 0') ]); final WebTestCompiler compiler = WebTestCompiler( diff --git a/packages/flutter_tools/test/general.shard/web/bootstrap_test.dart b/packages/flutter_tools/test/general.shard/web/bootstrap_test.dart index d2c5e597d8..f0c28e1107 100644 --- a/packages/flutter_tools/test/general.shard/web/bootstrap_test.dart +++ b/packages/flutter_tools/test/general.shard/web/bootstrap_test.dart @@ -49,6 +49,7 @@ void main() { test('generateMainModule removes timeout from requireJS', () { final String result = generateMainModule( entrypoint: 'foo/bar/main.js', + nullAssertions: false, nativeNullAssertions: false, ); @@ -64,6 +65,7 @@ void main() { test('generateMainModule embeds urls correctly', () { final String result = generateMainModule( entrypoint: 'foo/bar/main.js', + nullAssertions: false, nativeNullAssertions: false, ); // bootstrap main module has correct defined module. @@ -74,6 +76,7 @@ void main() { test('generateMainModule can set bootstrap name', () { final String result = generateMainModule( entrypoint: 'foo/bar/main.js', + nullAssertions: false, nativeNullAssertions: false, bootstrapModule: 'foo_module.bootstrap', ); @@ -85,18 +88,22 @@ void main() { test('generateMainModule includes null safety switches', () { final String result = generateMainModule( entrypoint: 'foo/bar/main.js', + nullAssertions: true, nativeNullAssertions: true, ); + expect(result, contains('''dart_sdk.dart.nonNullAsserts(true);''')); expect(result, contains('''dart_sdk.dart.nativeNonNullAsserts(true);''')); }); test('generateMainModule can disable null safety switches', () { final String result = generateMainModule( entrypoint: 'foo/bar/main.js', + nullAssertions: false, nativeNullAssertions: false, ); + expect(result, contains('''dart_sdk.dart.nonNullAsserts(false);''')); expect(result, contains('''dart_sdk.dart.nativeNonNullAsserts(false);''')); }); diff --git a/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart b/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart index 3c031887bb..f4cbe890c0 100644 --- a/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart +++ b/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart @@ -65,6 +65,7 @@ void main() { InternetAddress.loopbackIPv4, {}, {}, + NullSafetyMode.unsound, ); releaseAssetServer = ReleaseAssetServer( globals.fs.file('main.dart').uri, @@ -289,6 +290,7 @@ void main() { InternetAddress.loopbackIPv4, {}, {}, + NullSafetyMode.unsound, ); expect(webAssetServer.basePath, 'foo/bar'); @@ -307,6 +309,7 @@ void main() { InternetAddress.loopbackIPv4, {}, {}, + NullSafetyMode.unsound, ); // Defaults to "/" when there's no base element. @@ -327,6 +330,7 @@ void main() { InternetAddress.loopbackIPv4, {}, {}, + NullSafetyMode.unsound, ), throwsToolExit(), ); @@ -346,6 +350,7 @@ void main() { InternetAddress.loopbackIPv4, {}, {}, + NullSafetyMode.unsound, ), throwsToolExit(), ); @@ -661,11 +666,13 @@ void main() { useSseForDebugProxy: true, useSseForDebugBackend: true, useSseForInjectedClient: true, + nullAssertions: true, nativeNullAssertions: true, buildInfo: const BuildInfo( BuildMode.debug, '', treeShakeIcons: false, + nullSafetyMode: NullSafetyMode.unsound, ), enableDwds: false, enableDds: false, @@ -673,6 +680,7 @@ void main() { testMode: true, expressionCompiler: null, // ignore: avoid_redundant_argument_values chromiumLauncher: null, // ignore: avoid_redundant_argument_values + nullSafetyMode: NullSafetyMode.unsound, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -680,13 +688,13 @@ void main() { final Uri uri = await webDevFS.create(); webDevFS.webAssetServer.entrypointCacheDirectory = globals.fs.currentDirectory; final String webPrecompiledSdk = globals.artifacts! - .getHostArtifact(HostArtifact.webPrecompiledSoundSdk).path; + .getHostArtifact(HostArtifact.webPrecompiledSdk).path; final String webPrecompiledSdkSourcemaps = globals.artifacts! - .getHostArtifact(HostArtifact.webPrecompiledSoundSdkSourcemaps).path; + .getHostArtifact(HostArtifact.webPrecompiledSdkSourcemaps).path; final String webPrecompiledCanvaskitSdk = globals.artifacts! - .getHostArtifact(HostArtifact.webPrecompiledCanvaskitSoundSdk).path; + .getHostArtifact(HostArtifact.webPrecompiledCanvaskitSdk).path; final String webPrecompiledCanvaskitSdkSourcemaps = globals.artifacts! - .getHostArtifact(HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps).path; + .getHostArtifact(HostArtifact.webPrecompiledCanvaskitSdkSourcemaps).path; globals.fs.currentDirectory .childDirectory('lib') .childFile('web_entrypoint.dart') @@ -771,6 +779,7 @@ void main() { useSseForDebugProxy: true, useSseForDebugBackend: true, useSseForInjectedClient: true, + nullAssertions: true, nativeNullAssertions: true, buildInfo: const BuildInfo( BuildMode.debug, @@ -783,6 +792,7 @@ void main() { testMode: true, expressionCompiler: null, // ignore: avoid_redundant_argument_values chromiumLauncher: null, // ignore: avoid_redundant_argument_values + nullSafetyMode: NullSafetyMode.sound, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -878,6 +888,7 @@ void main() { useSseForDebugProxy: true, useSseForDebugBackend: true, useSseForInjectedClient: true, + nullAssertions: true, nativeNullAssertions: true, buildInfo: const BuildInfo( BuildMode.debug, @@ -890,6 +901,7 @@ void main() { testMode: true, expressionCompiler: null, chromiumLauncher: null, + nullSafetyMode: NullSafetyMode.sound, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -945,7 +957,9 @@ void main() { testMode: true, expressionCompiler: null, // ignore: avoid_redundant_argument_values chromiumLauncher: null, // ignore: avoid_redundant_argument_values + nullAssertions: true, nativeNullAssertions: true, + nullSafetyMode: NullSafetyMode.sound, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -971,6 +985,7 @@ void main() { useSseForDebugProxy: true, useSseForDebugBackend: true, useSseForInjectedClient: true, + nullAssertions: true, nativeNullAssertions: true, buildInfo: const BuildInfo( BuildMode.debug, @@ -986,6 +1001,7 @@ void main() { testMode: true, expressionCompiler: null, // ignore: avoid_redundant_argument_values chromiumLauncher: null, // ignore: avoid_redundant_argument_values + nullSafetyMode: NullSafetyMode.sound, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -1012,6 +1028,7 @@ void main() { useSseForDebugProxy: true, useSseForDebugBackend: true, useSseForInjectedClient: true, + nullAssertions: true, nativeNullAssertions: true, buildInfo: const BuildInfo( BuildMode.debug, @@ -1027,6 +1044,7 @@ void main() { testMode: true, expressionCompiler: null, // ignore: avoid_redundant_argument_values chromiumLauncher: null, // ignore: avoid_redundant_argument_values + nullSafetyMode: NullSafetyMode.sound, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -1056,6 +1074,7 @@ void main() { false, Uri.base, null, + NullSafetyMode.unsound, testMode: true); expect(webAssetServer.defaultResponseHeaders['x-frame-options'], null); @@ -1089,6 +1108,7 @@ void main() { InternetAddress.anyIPv4, {}, {}, + NullSafetyMode.sound, ); expect(await webAssetServer.metadataContents('foo/main_module.ddc_merged_metadata'), null); @@ -1118,6 +1138,7 @@ void main() { useSseForDebugProxy: true, useSseForDebugBackend: true, useSseForInjectedClient: true, + nullAssertions: true, nativeNullAssertions: true, buildInfo: BuildInfo.debug, enableDwds: false, @@ -1126,6 +1147,7 @@ void main() { testMode: true, expressionCompiler: null, // ignore: avoid_redundant_argument_values chromiumLauncher: null, // ignore: avoid_redundant_argument_values + nullSafetyMode: NullSafetyMode.unsound, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); diff --git a/packages/flutter_tools/test/general.shard/web/migrations/scrub_generated_plugin_registrant_test.dart b/packages/flutter_tools/test/general.shard/web/migrations/scrub_generated_plugin_registrant_test.dart index c6a10eafb1..4261dd88a7 100644 --- a/packages/flutter_tools/test/general.shard/web/migrations/scrub_generated_plugin_registrant_test.dart +++ b/packages/flutter_tools/test/general.shard/web/migrations/scrub_generated_plugin_registrant_test.dart @@ -4,6 +4,7 @@ import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build.dart'; @@ -49,6 +50,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: buildSystem, fileSystem: fileSystem, + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), )) .run(['build', 'web', '--no-pub']); @@ -71,6 +73,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: buildSystem, fileSystem: fileSystem, + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), )) .run(['build', 'web', '--no-pub']); @@ -92,6 +95,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: buildSystem, fileSystem: fileSystem, + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), )) .run(['build', 'web', '--no-pub']); @@ -112,6 +116,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: buildSystem, fileSystem: fileSystem, + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), )) .run(['build', 'web', '--no-pub']); @@ -134,6 +139,7 @@ void main() { androidSdk: FakeAndroidSdk(), buildSystem: buildSystem, fileSystem: fileSystem, + logger: BufferLogger.test(), osUtils: FakeOperatingSystemUtils(), )) .run(['build', 'web', '--no-pub']); diff --git a/packages/flutter_tools/test/integration.shard/flutter_build_null_unsafe_test.dart b/packages/flutter_tools/test/integration.shard/flutter_build_null_unsafe_test.dart new file mode 100644 index 0000000000..b4f6705c5c --- /dev/null +++ b/packages/flutter_tools/test/integration.shard/flutter_build_null_unsafe_test.dart @@ -0,0 +1,83 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file/file.dart'; +import 'package:flutter_tools/src/base/io.dart'; + +import '../src/common.dart'; +import 'test_utils.dart'; + +void main() { + late Directory tempDir; + late Directory projectRoot; + late String flutterBin; + final List targetPlatforms = [ + 'apk', + 'web', + if (platform.isWindows) 'windows', + if (platform.isMacOS) ...['macos', 'ios'], + ]; + + setUpAll(() { + tempDir = createResolvedTempDirectorySync('build_null_unsafe_test.'); + flutterBin = fileSystem.path.join( + getFlutterRoot(), + 'bin', + 'flutter', + ); + processManager.runSync([ + flutterBin, + 'config', + '--enable-macos-desktop', + '--enable-windows-desktop', + '--enable-web', + ]); + + processManager.runSync([ + flutterBin, + ...getLocalEngineArguments(), + 'create', + 'hello', + ], workingDirectory: tempDir.path); + + projectRoot = tempDir.childDirectory('hello'); + writeFile(fileSystem.path.join(projectRoot.path, 'pubspec.yaml'), ''' +name: hello +environment: + sdk: '>=2.12.0 <4.0.0' +'''); + writeFile(fileSystem.path.join(projectRoot.path, 'lib', 'main.dart'), ''' +import 'unsafe.dart'; +void main() { + print(unsafeString); +} +'''); + writeFile(fileSystem.path.join(projectRoot.path, 'lib', 'unsafe.dart'), ''' +// @dart=2.9 +String unsafeString = null; +'''); + }); + + tearDownAll(() { + tryToDelete(tempDir); + }); + + for (final String targetPlatform in targetPlatforms) { + testWithoutContext('flutter build $targetPlatform --no-sound-null-safety', () { + final ProcessResult result = processManager.runSync([ + flutterBin, + ...getLocalEngineArguments(), + 'build', + targetPlatform, + '--no-pub', + '--no-sound-null-safety', + if (targetPlatform == 'ios') '--no-codesign', + ], workingDirectory: projectRoot.path); + + if (result.exitCode != 0) { + fail('build --no-sound-null-safety failed: ${result.exitCode}\n${result.stderr}\n${result.stdout}'); + } + }); + } +}