diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 68dae76a2da..f188ae7e815 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' @@ -1229,7 +1234,7 @@ Future _runWebLongRunningTests() async { '--dart-define=TEST_FLUTTER_ENGINE_VERSION=$engineVersion', ]), () => _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', @@ -1242,8 +1247,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'), @@ -1302,6 +1311,7 @@ Future _runFlutterDriverWebTest({ if (driver != null) '--driver=$driver', '--target=$target', '--browser-name=chrome', + '--no-sound-null-safety', '-d', 'web-server', '--$buildMode', @@ -1343,6 +1353,7 @@ Future _runWebTreeshakeTest() async { 'build', 'web', '--target=$target', + '--no-sound-null-safety', '--profile', ], workingDirectory: testAppDirectory, @@ -1568,6 +1579,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', @@ -1674,6 +1686,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'); @@ -1687,6 +1700,11 @@ Future _runWebDebugTest(String target, { [ 'run', '--debug', + if (enableNullSafety) + ...[ + '--no-sound-null-safety', + '--null-assertions', + ], '-d', 'chrome', '--web-run-headless', @@ -1729,6 +1747,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/lib/src/services/clipboard.dart b/packages/flutter/lib/src/services/clipboard.dart index b490e80e163..88ede40fca0 100644 --- a/packages/flutter/lib/src/services/clipboard.dart +++ b/packages/flutter/lib/src/services/clipboard.dart @@ -56,7 +56,7 @@ abstract final class Clipboard { if (result == null) { return null; } - return ClipboardData(text: result['text']! as String); + return ClipboardData(text: result['text'] as String); } /// Returns a future that resolves to true iff the clipboard contains string diff --git a/packages/flutter/test/foundation/isolates_test.dart b/packages/flutter/test/foundation/isolates_test.dart index 571d9a1e6b8..912e9baa669 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 25b89f3c380..7005e34926d 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/src/globals.dart b/packages/flutter_tools/lib/src/globals.dart index 68904a25ee7..596c5a6c41e 100644 --- a/packages/flutter_tools/lib/src/globals.dart +++ b/packages/flutter_tools/lib/src/globals.dart @@ -43,7 +43,6 @@ import 'pre_run_validator.dart'; import 'project.dart'; import 'reporting/crash_reporting.dart'; import 'reporting/reporting.dart'; -import 'runner/flutter_command.dart'; import 'runner/local_engine.dart'; import 'version.dart'; @@ -286,7 +285,3 @@ const String kDefaultFrameworkChannel = 'master'; // Used to build RegExp instances which can detect the VM service message. final RegExp kVMServiceMessageRegExp = RegExp(r'The Dart VM service is listening on ((http|//)[a-zA-Z0-9:/=_\-\.\[\]]+)'); - -// The official tool no longer allows non-null safe builds. This can be -// overridden in other clients. -NonNullSafeBuilds get nonNullSafeBuilds => context.get() ?? NonNullSafeBuilds.notAllowed; diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 059a3d1b782..95eefe2dfed 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -822,13 +822,20 @@ abstract class FlutterCommand extends Command { void addNullSafetyModeOptions({ required bool hide }) { argParser.addFlag(FlutterOptions.kNullSafety, - help: 'This flag is deprecated as only null-safe code is supported.', + 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: true, + hide: hide, ); argParser.addFlag(FlutterOptions.kNullAssertions, - help: 'This flag is deprecated as only null-safe code is supported.', - hide: true, + help: + 'Perform additional null assertions on the boundaries of migrated and ' + 'un-migrated code. This setting is not currently supported on desktop ' + 'devices.' ); } @@ -1480,16 +1487,6 @@ abstract class FlutterCommand extends Command { /// rather than calling [runCommand] directly. @mustCallSuper Future verifyThenRunCommand(String? commandPath) async { - if (argParser.options.containsKey(FlutterOptions.kNullSafety) && - argResults![FlutterOptions.kNullSafety] == false && - globals.nonNullSafeBuilds == NonNullSafeBuilds.notAllowed) { - throwToolExit(''' -Could not find an option named "no-${FlutterOptions.kNullSafety}". - -Run 'flutter -h' (or 'flutter -h') for available flutter commands and options. -'''); - } - globals.preRunValidator.validate(); if (refreshWirelessDevices) { @@ -1757,12 +1754,3 @@ DevelopmentArtifact? artifactFromTargetPlatform(TargetPlatform targetPlatform) { /// Returns true if s is either null, empty or is solely made of whitespace characters (as defined by String.trim). bool _isBlank(String s) => s.trim().isEmpty; - -/// Whether the tool should allow non-null safe builds. -/// -/// The Dart SDK no longer supports non-null safe builds, so this value in the -/// tool's context should always be [NonNullSafeBuilds.notAllowed]. -enum NonNullSafeBuilds { - allowed, - notAllowed, -} 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 631341ad4ad..c3d7524ffb3 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart @@ -67,52 +67,6 @@ void main() { Logger: () => BufferLogger.test(), }); - testUsingContext('does not support --no-sound-null-safety by default', () async { - fileSystem.file('lib/main.dart').createSync(recursive: true); - fileSystem.file('pubspec.yaml').createSync(); - fileSystem.file('.packages').createSync(); - - final TestRunCommandThatOnlyValidates command = TestRunCommandThatOnlyValidates(); - await expectLater( - () => createTestCommandRunner(command).run([ - 'run', - '--use-application-binary=app/bar/faz', - '--no-sound-null-safety', - ]), - throwsA(isException.having( - (Exception exception) => exception.toString(), - 'toString', - contains('Could not find an option named "no-sound-null-safety"'), - )), - ); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => FakeProcessManager.any(), - Logger: () => BufferLogger.test(), - }); - - testUsingContext('supports --no-sound-null-safety with an overridden NonNullSafeBuilds', () async { - fileSystem.file('lib/main.dart').createSync(recursive: true); - fileSystem.file('pubspec.yaml').createSync(); - fileSystem.file('.packages').createSync(); - - final FakeDevice device = FakeDevice(isLocalEmulator: true, platformType: PlatformType.android); - - testDeviceManager.devices = [device]; - final TestRunCommandThatOnlyValidates command = TestRunCommandThatOnlyValidates(); - await createTestCommandRunner(command).run(const [ - 'run', - '--use-application-binary=app/bar/faz', - '--no-sound-null-safety', - ]); - }, overrides: { - DeviceManager: () => testDeviceManager, - FileSystem: () => fileSystem, - Logger: () => BufferLogger.test(), - NonNullSafeBuilds: () => NonNullSafeBuilds.allowed, - ProcessManager: () => FakeProcessManager.any(), - }); - testUsingContext('does not support "--use-application-binary" and "--fast-start"', () async { fileSystem.file('lib/main.dart').createSync(recursive: true); fileSystem.file('pubspec.yaml').createSync(); diff --git a/packages/flutter_tools/test/general.shard/args_test.dart b/packages/flutter_tools/test/general.shard/args_test.dart index 6fe315e4b92..0dc1b58b877 100644 --- a/packages/flutter_tools/test/general.shard/args_test.dart +++ b/packages/flutter_tools/test/general.shard/args_test.dart @@ -176,10 +176,7 @@ void verifyOptions(String? command, Iterable