[flutter_releases] Flutter beta 2.8.0-3.2.pre Framework Cherrypicks (#93841)
* [flutter_tools] Catch lack of flutter tools source missing (#93168)
* 'Update Engine revision to bcc2b7f12cada3d1359e353c416568b4c3f6df69 for beta release 2.8.0-3.2.pre'
* Replace text directionality control characters with escape sequences in the semantics_tester (#93034)
Co-authored-by: Christopher Fujino <christopherfujino@gmail.com>
Co-authored-by: Jason Simmons <jason-simmons@users.noreply.github.com>
diff --git a/bin/internal/engine.version b/bin/internal/engine.version
index d2dd5b3..c4e64cc 100644
--- a/bin/internal/engine.version
+++ b/bin/internal/engine.version
@@ -1 +1 @@
-09f1520e8b9585d133faf1eccced9357670c6d11
+bcc2b7f12cada3d1359e353c416568b4c3f6df69
diff --git a/packages/flutter/test/widgets/semantics_tester.dart b/packages/flutter/test/widgets/semantics_tester.dart
index ed2e8eb..03cc46d 100644
--- a/packages/flutter/test/widgets/semantics_tester.dart
+++ b/packages/flutter/test/widgets/semantics_tester.dart
@@ -635,12 +635,9 @@
if (nodeData.actions != 0)
buf.writeln(' actions: ${_actionsToSemanticsActionExpression(nodeData.actions)},');
if (node.label != null && node.label.isNotEmpty) {
- final String escapedLabel = node.label.replaceAll('\n', r'\n');
- if (escapedLabel != node.label) {
- buf.writeln(" label: r'$escapedLabel',");
- } else {
- buf.writeln(" label: '$escapedLabel',");
- }
+ // Escape newlines and text directionality control characters.
+ final String escapedLabel = node.label.replaceAll('\n', r'\n').replaceAll('\u202a', r'\u202a').replaceAll('\u202c', r'\u202c');
+ buf.writeln(" label: '$escapedLabel',");
}
if (node.value != null && node.value.isNotEmpty)
buf.writeln(" value: '${node.value}',");
diff --git a/packages/flutter/test/widgets/semantics_tester_generate_test_semantics_expression_for_current_semantics_tree_test.dart b/packages/flutter/test/widgets/semantics_tester_generate_test_semantics_expression_for_current_semantics_tree_test.dart
index 0336aaf..eb3c58f 100644
--- a/packages/flutter/test/widgets/semantics_tester_generate_test_semantics_expression_for_current_semantics_tree_test.dart
+++ b/packages/flutter/test/widgets/semantics_tester_generate_test_semantics_expression_for_current_semantics_tree_test.dart
@@ -132,7 +132,7 @@
tags: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')],
flags: <SemanticsFlag>[SemanticsFlag.hasCheckedState, SemanticsFlag.isChecked, SemanticsFlag.isSelected],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.decrease],
- label: '‪Interactive text‬',
+ label: '\u202aInteractive text\u202c',
value: 'test-value',
increasedValue: 'test-increasedValue',
decreasedValue: 'test-decreasedValue',
diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart
index dffc009..de2ce3c 100644
--- a/packages/flutter_tools/lib/executable.dart
+++ b/packages/flutter_tools/lib/executable.dart
@@ -54,6 +54,7 @@
// Files in `isolated` are intentionally excluded from google3 tooling.
import 'src/isolated/mustache_template.dart';
import 'src/isolated/resident_web_runner.dart';
+import 'src/pre_run_validator.dart';
import 'src/resident_runner.dart';
import 'src/runner/flutter_command.dart';
import 'src/web/web_runner.dart';
@@ -126,6 +127,7 @@
windows: globals.platform.isWindows,
);
},
+ PreRunValidator: () => PreRunValidator(fileSystem: globals.fs),
},
);
}
diff --git a/packages/flutter_tools/lib/src/globals_null_migrated.dart b/packages/flutter_tools/lib/src/globals_null_migrated.dart
index e59eea7..29b2423 100644
--- a/packages/flutter_tools/lib/src/globals_null_migrated.dart
+++ b/packages/flutter_tools/lib/src/globals_null_migrated.dart
@@ -39,6 +39,7 @@
import 'macos/xcdevice.dart';
import 'macos/xcode.dart';
import 'persistent_tool_state.dart';
+import 'pre_run_validator.dart';
import 'project.dart';
import 'reporting/crash_reporting.dart';
import 'reporting/reporting.dart';
@@ -233,3 +234,5 @@
}
CustomDevicesConfig get customDevicesConfig => context.get<CustomDevicesConfig>()!;
+
+PreRunValidator get preRunValidator => context.get<PreRunValidator>() ?? const NoOpPreRunValidator();
diff --git a/packages/flutter_tools/lib/src/pre_run_validator.dart b/packages/flutter_tools/lib/src/pre_run_validator.dart
new file mode 100644
index 0000000..e08cf1c
--- /dev/null
+++ b/packages/flutter_tools/lib/src/pre_run_validator.dart
@@ -0,0 +1,51 @@
+// 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 'base/common.dart';
+import 'base/file_system.dart';
+import 'cache.dart';
+
+/// A validator that runs before the tool runs any command.
+abstract class PreRunValidator {
+ factory PreRunValidator({
+ required FileSystem fileSystem,
+ }) => _DefaultPreRunValidator(fileSystem: fileSystem);
+
+ void validate();
+}
+
+class _DefaultPreRunValidator implements PreRunValidator {
+ _DefaultPreRunValidator({
+ required this.fileSystem,
+ });
+
+ final FileSystem fileSystem;
+
+ late final Directory _toolsDir = fileSystem.directory(
+ fileSystem.path.join(Cache.flutterRoot!, 'packages', 'flutter_tools'),
+ );
+
+ @override
+ void validate() {
+ // If a user downloads the Flutter SDK via a pre-built archive and there is
+ // an error during extraction, the user could have a valid Dart snapshot of
+ // the tool but not the source directory. We still need the source, so
+ // validate the source directory exists and toolExit if not.
+ if (!_toolsDir.existsSync()) {
+ throwToolExit(
+ 'Flutter SDK installation appears corrupted: expected to find the '
+ 'directory ${_toolsDir.path} but it does not exist! Please go to '
+ 'https://flutter.dev/setup for instructions on how to re-install '
+ 'Flutter.',
+ );
+ }
+ }
+}
+
+class NoOpPreRunValidator implements PreRunValidator {
+ const NoOpPreRunValidator();
+
+ @override
+ void validate() {}
+}
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 4b3c8be..3398299 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -1234,6 +1234,7 @@
/// rather than calling [runCommand] directly.
@mustCallSuper
Future<FlutterCommandResult> verifyThenRunCommand(String commandPath) async {
+ globals.preRunValidator.validate();
// Populate the cache. We call this before pub get below so that the
// sky_engine package is available in the flutter cache for pub to find.
if (shouldUpdateCache) {
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/shell_completion_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/shell_completion_test.dart
index d2fda26..914e8b1 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/shell_completion_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/shell_completion_test.dart
@@ -31,6 +31,8 @@
expect(fakeStdio.writtenToStdout.length, equals(1));
expect(fakeStdio.writtenToStdout.first, contains('__flutter_completion'));
}, overrides: <Type, Generator>{
+ FileSystem: () => MemoryFileSystem.test(),
+ ProcessManager: () => FakeProcessManager.any(),
Stdio: () => fakeStdio,
});
@@ -40,6 +42,8 @@
expect(fakeStdio.writtenToStdout.length, equals(1));
expect(fakeStdio.writtenToStdout.first, contains('__flutter_completion'));
}, overrides: <Type, Generator>{
+ FileSystem: () => MemoryFileSystem.test(),
+ ProcessManager: () => FakeProcessManager.any(),
Stdio: () => fakeStdio,
});
diff --git a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart
index 95c5b0b..e0bafd7 100644
--- a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart
+++ b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart
@@ -449,6 +449,7 @@
testUsingContext('test without bot', () async {
Cache.flutterRoot = '';
+ globals.fs.directory('/packages/flutter_tools').createSync(recursive: true);
globals.fs.file('pubspec.yaml').createSync();
processManager.addCommand(
const FakeCommand(command: <String>['/bin/cache/dart-sdk/bin/dart', '__deprecated_pub', 'run', 'test']),
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 e770c18..77a09dd 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
@@ -18,7 +18,8 @@
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/dart/pub.dart';
-import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
+import 'package:flutter_tools/src/globals.dart' as globals;
+import 'package:flutter_tools/src/pre_run_validator.dart';
import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:test/fake.dart';
@@ -34,6 +35,13 @@
TestUsage usage;
FakeClock clock;
FakeProcessInfo processInfo;
+ MemoryFileSystem fileSystem;
+ FakeProcessManager processManager;
+ PreRunValidator preRunValidator;
+
+ setUpAll(() {
+ Cache.flutterRoot = '/path/to/sdk/flutter';
+ });
setUp(() {
Cache.disableLocking();
@@ -42,6 +50,9 @@
clock = FakeClock();
processInfo = FakeProcessInfo();
processInfo.maxRss = 10;
+ fileSystem = MemoryFileSystem.test();
+ processManager = FakeProcessManager.empty();
+ preRunValidator = PreRunValidator(fileSystem: fileSystem);
});
tearDown(() {
@@ -63,6 +74,8 @@
expect(flutterCommand.hidden, isFalse);
},
overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
Cache: () => cache,
});
@@ -79,9 +92,25 @@
);
},
overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
Cache: () => cache,
});
+ testUsingContext("throws toolExit if flutter_tools source dir doesn't exist", () async {
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+ await expectToolExitLater(
+ flutterCommand.run(),
+ contains('Flutter SDK installation appears corrupted'),
+ );
+ },
+ overrides: <Type, Generator>{
+ Cache: () => cache,
+ FileSystem: () => fileSystem,
+ PreRunValidator: () => preRunValidator,
+ ProcessManager: () => processManager,
+ });
+
testUsingContext('deprecated command should warn', () async {
final FakeDeprecatedCommand flutterCommand = FakeDeprecatedCommand();
final CommandRunner<void> runner = createTestCommandRunner(flutterCommand);
@@ -95,6 +124,9 @@
'of Flutter.'));
expect(flutterCommand.deprecated, isTrue);
expect(flutterCommand.hidden, isTrue);
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('uses the error handling file system', () async {
@@ -105,6 +137,9 @@
}
);
await flutterCommand.run();
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('finds the target file with default values', () async {
@@ -115,8 +150,8 @@
expect(fakeTargetCommand.cachedTargetFile, 'lib/main.dart');
}, overrides: <Type, Generator>{
- FileSystem: () => MemoryFileSystem.test(),
- ProcessManager: () => FakeProcessManager.any(),
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('finds the target file with specified value', () async {
@@ -127,8 +162,8 @@
expect(fakeTargetCommand.cachedTargetFile, 'lib/foo.dart');
}, overrides: <Type, Generator>{
- FileSystem: () => MemoryFileSystem.test(),
- ProcessManager: () => FakeProcessManager.any(),
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('throws tool exit if specified file does not exist', () async {
@@ -137,13 +172,15 @@
expect(() async => runner.run(<String>['test', '-t', 'lib/foo.dart']), throwsToolExit());
}, overrides: <Type, Generator>{
- FileSystem: () => MemoryFileSystem.test(),
- ProcessManager: () => FakeProcessManager.any(),
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
void testUsingCommandContext(String testName, dynamic Function() testBody) {
testUsingContext(testName, testBody, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
ProcessInfo: () => processInfo,
+ ProcessManager: () => processManager,
SystemClock: () => clock,
Usage: () => usage,
});
@@ -245,6 +282,9 @@
'http://127.0.0.1:9105',
]);
expect(command.devToolsServerAddress.toString(), equals('http://127.0.0.1:9105'));
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('devToolsServerAddress returns null for bad input', () async {
@@ -277,6 +317,9 @@
'127.0.0.1:9101',
]);
expect(command.devToolsServerAddress, isNull);
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
group('signals tests', () {
@@ -328,6 +371,8 @@
),
]);
}, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
ProcessInfo: () => processInfo,
Signals: () => FakeSignals(
subForSigTerm: signalUnderTest,
@@ -363,6 +408,8 @@
signalController.add(mockSignal);
await completer.future;
}, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
ProcessInfo: () => processInfo,
Signals: () => FakeSignals(
subForSigTerm: signalUnderTest,
@@ -499,26 +546,35 @@
}, overrides: <Type, Generator>{
Pub: () => FakePub(),
Usage: () => usage,
- FileSystem: () => MemoryFileSystem.test(),
- ProcessManager: () => FakeProcessManager.any(),
+ 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);
expect(buildInfo.packagesPath, 'foo');
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('use fileSystemScheme to generate BuildInfo', () async {
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(fileSystemScheme: 'foo');
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug);
expect(buildInfo.fileSystemScheme, 'foo');
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('use fileSystemRoots to generate BuildInfo', () async {
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(fileSystemRoots: <String>['foo', 'bar']);
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug);
expect(buildInfo.fileSystemRoots, <String>['foo', 'bar']);
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('includes initializeFromDill in BuildInfo', () async {
@@ -527,6 +583,9 @@
await runner.run(<String>['dummy', '--initialize-from-dill=/foo/bar.dill']);
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug);
expect(buildInfo.initializeFromDill, '/foo/bar.dill');
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('dds options', () async {
@@ -535,6 +594,9 @@
await runner.run(<String>['test', '--dds-port=1']);
expect(ddsCommand.enableDds, isTrue);
expect(ddsCommand.ddsPort, 1);
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('dds options --dds', () async {
@@ -542,6 +604,9 @@
final CommandRunner<void> runner = createTestCommandRunner(ddsCommand);
await runner.run(<String>['test', '--dds']);
expect(ddsCommand.enableDds, isTrue);
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('dds options --no-dds', () async {
@@ -549,6 +614,9 @@
final CommandRunner<void> runner = createTestCommandRunner(ddsCommand);
await runner.run(<String>['test', '--no-dds']);
expect(ddsCommand.enableDds, isFalse);
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('dds options --disable-dds', () async {
@@ -556,6 +624,9 @@
final CommandRunner<void> runner = createTestCommandRunner(ddsCommand);
await runner.run(<String>['test', '--disable-dds']);
expect(ddsCommand.enableDds, isFalse);
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('dds options --no-disable-dds', () async {
@@ -563,6 +634,9 @@
final CommandRunner<void> runner = createTestCommandRunner(ddsCommand);
await runner.run(<String>['test', '--no-disable-dds']);
expect(ddsCommand.enableDds, isTrue);
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
testUsingContext('dds options --dds --disable-dds', () async {
@@ -570,6 +644,9 @@
final CommandRunner<void> runner = createTestCommandRunner(ddsCommand);
await runner.run(<String>['test', '--dds', '--disable-dds']);
expect(() => ddsCommand.enableDds, throwsToolExit());
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
});
});
}
diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart
index 6f0e311..2e00942 100644
--- a/packages/flutter_tools/test/src/common.dart
+++ b/packages/flutter_tools/test/src/common.dart
@@ -162,6 +162,7 @@
addTearDown(() async {
await globals.localFileSystem.dispose();
});
+
return body();
},
skip: skip,