Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flutter_releases] Flutter 1.26.0-17.2.pre Beta Framework Cherrypicks #75303

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/internal/engine.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5d3477eab415fbcc3d58624b07f0a768f8208f98
2c527d6c7e70e2f51bca1a46f1174b250f84c5da
2 changes: 1 addition & 1 deletion bin/internal/flutter_plugins.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
919eaef4dcc7b3e272850b74e9995a87d7b5ab39
9e1d573e1e953ddc2e4c7c823f442244f1ff4ebf
4 changes: 2 additions & 2 deletions bin/internal/shared.bat
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@ GOTO :after_subroutine
POPD

IF "%FLUTTER_TOOL_ARGS%" == "" (
"%dart%" --snapshot="%snapshot_path%" --packages="%flutter_tools_dir%\.packages" --no-enable-mirrors "%script_path%"
"%dart%" --verbosity=error --snapshot="%snapshot_path%" --packages="%flutter_tools_dir%\.packages" --no-enable-mirrors "%script_path%"
) else (
"%dart%" "%FLUTTER_TOOL_ARGS%" --snapshot="%snapshot_path%" --packages="%flutter_tools_dir%\.packages" "%script_path%"
"%dart%" "%FLUTTER_TOOL_ARGS%" --verbosity=error --snapshot="%snapshot_path%" --packages="%flutter_tools_dir%\.packages" "%script_path%"
)
IF "%ERRORLEVEL%" NEQ "0" (
ECHO Error: Unable to create dart snapshot for flutter tool. 1>&2
Expand Down
2 changes: 1 addition & 1 deletion bin/internal/shared.sh
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ function upgrade_flutter () (

retry_upgrade

"$DART" --disable-dart-dev $FLUTTER_TOOL_ARGS --snapshot="$SNAPSHOT_PATH" --packages="$FLUTTER_TOOLS_DIR/.packages" --no-enable-mirrors "$SCRIPT_PATH"
"$DART" --verbosity=error --disable-dart-dev $FLUTTER_TOOL_ARGS --snapshot="$SNAPSHOT_PATH" --packages="$FLUTTER_TOOLS_DIR/.packages" --no-enable-mirrors "$SCRIPT_PATH"
echo "$revision" > "$STAMP_PATH"
fi
# The exit here is extraneous since the function is run in a subshell, but
Expand Down
293 changes: 198 additions & 95 deletions dev/bots/test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ final bool useFlutterTestFormatter = Platform.environment['FLUTTER_TEST_FORMATTE
/// and make sure it runs _all_ shards.
const int kBuildTestShardCount = 2;

const String kShardKey = 'SHARD';
const String kSubshardKey = 'SUBSHARD';

/// The number of Cirrus jobs that run Web tests in parallel.
///
/// The default is 8 shards. Typically .cirrus.yml would define the
Expand All @@ -88,6 +91,8 @@ const List<String> kWebTestFileKnownFailures = <String>[
'test/examples/sector_layout_test.dart',
];

const String kSmokeTestShardName = 'smoke_tests';

/// When you call this, you can pass additional arguments to pass custom
/// arguments to flutter test. For example, you might want to call this
/// script with the parameter --local-engine=host_debug_unopt to
Expand All @@ -107,18 +112,20 @@ Future<void> main(List<String> args) async {
print('═' * 80);
await _runSmokeTests();
print('═' * 80);
await selectShard(const <String, ShardRunner>{
await selectShard(<String, ShardRunner>{
'add_to_app_life_cycle_tests': _runAddToAppLifeCycleTests,
'build_tests': _runBuildTests,
'framework_coverage': _runFrameworkCoverage,
'framework_tests': _runFrameworkTests,
'tool_coverage': _runToolCoverage,
'tool_tests': _runToolTests,
'tool_integration_tests': _runIntegrationToolTests,
'web_tool_tests': _runWebToolTests,
'web_tests': _runWebUnitTests,
'web_integration_tests': _runWebIntegrationTests,
'web_long_running_tests': _runWebLongRunningTests,
'flutter_plugins': _runFlutterPluginsTests,
kSmokeTestShardName: () async {}, // No-op, the smoke tests already ran. Used for testing this script.
});
} on ExitException catch (error) {
error.apply();
Expand Down Expand Up @@ -183,68 +190,96 @@ Future<void> _runSmokeTests() async {
// We run the "pass" and "fail" smoke tests first, and alone, because those
// are particularly critical and sensitive. If one of these fails, there's no
// point even trying the others.
await _runFlutterTest(automatedTests,
script: path.join('test_smoke_test', 'pass_test.dart'),
printOutput: false,
);
await _runFlutterTest(automatedTests,
script: path.join('test_smoke_test', 'fail_test.dart'),
expectFailure: true,
printOutput: false,
);
// We run the timeout tests individually because they are timing-sensitive.
await _runFlutterTest(automatedTests,
script: path.join('test_smoke_test', 'timeout_pass_test.dart'),
expectFailure: false,
printOutput: false,
);
await _runFlutterTest(automatedTests,
script: path.join('test_smoke_test', 'timeout_fail_test.dart'),
expectFailure: true,
printOutput: false,
);
await _runFlutterTest(automatedTests,
script: path.join('test_smoke_test', 'pending_timer_fail_test.dart'),
expectFailure: true,
printOutput: false,
outputChecker: (CommandResult result) {
return result.flattenedStdout.contains('failingPendingTimerTest')
? null
: 'Failed to find the stack trace for the pending Timer.';
}
);
// We run the remaining smoketests in parallel, because they each take some
// time to run (e.g. compiling), so we don't want to run them in series,
// especially on 20-core machines...
await Future.wait<void>(
<Future<void>>[
_runFlutterTest(automatedTests,
script: path.join('test_smoke_test', 'crash1_test.dart'),
expectFailure: true,
printOutput: false,
),
_runFlutterTest(automatedTests,
script: path.join('test_smoke_test', 'crash2_test.dart'),
expectFailure: true,
printOutput: false,
),
_runFlutterTest(automatedTests,
script: path.join('test_smoke_test', 'syntax_error_test.broken_dart'),
expectFailure: true,
printOutput: false,
),
_runFlutterTest(automatedTests,
script: path.join('test_smoke_test', 'missing_import_test.broken_dart'),
expectFailure: true,
printOutput: false,
),
_runFlutterTest(automatedTests,
script: path.join('test_smoke_test', 'disallow_error_reporter_modification_test.dart'),
expectFailure: true,
printOutput: false,
),
],
);
final List<ShardRunner> tests = <ShardRunner>[
() => _runFlutterTest(
automatedTests,
script: path.join('test_smoke_test', 'pass_test.dart'),
printOutput: false,
),
() => _runFlutterTest(
automatedTests,
script: path.join('test_smoke_test', 'fail_test.dart'),
expectFailure: true,
printOutput: false,
),
// We run the timeout tests individually because they are timing-sensitive.
() => _runFlutterTest(
automatedTests,
script: path.join('test_smoke_test', 'timeout_pass_test.dart'),
expectFailure: false,
printOutput: false,
),
() => _runFlutterTest(
automatedTests,
script: path.join('test_smoke_test', 'timeout_fail_test.dart'),
expectFailure: true,
printOutput: false,
),
() => _runFlutterTest(automatedTests,
script:
path.join('test_smoke_test', 'pending_timer_fail_test.dart'),
expectFailure: true,
printOutput: false, outputChecker: (CommandResult result) {
return result.flattenedStdout.contains('failingPendingTimerTest')
? null
: 'Failed to find the stack trace for the pending Timer.';
}),
// We run the remaining smoketests in parallel, because they each take some
// time to run (e.g. compiling), so we don't want to run them in series,
// especially on 20-core machines...
() => Future.wait<void>(
<Future<void>>[
_runFlutterTest(
automatedTests,
script: path.join('test_smoke_test', 'crash1_test.dart'),
expectFailure: true,
printOutput: false,
),
_runFlutterTest(
automatedTests,
script: path.join('test_smoke_test', 'crash2_test.dart'),
expectFailure: true,
printOutput: false,
),
_runFlutterTest(
automatedTests,
script:
path.join('test_smoke_test', 'syntax_error_test.broken_dart'),
expectFailure: true,
printOutput: false,
),
_runFlutterTest(
automatedTests,
script: path.join(
'test_smoke_test', 'missing_import_test.broken_dart'),
expectFailure: true,
printOutput: false,
),
_runFlutterTest(
automatedTests,
script: path.join('test_smoke_test',
'disallow_error_reporter_modification_test.dart'),
expectFailure: true,
printOutput: false,
),
],
),
];

List<ShardRunner> testsToRun;

// Smoke tests are special and run first for all test shards.
// Run all smoke tests for other shards.
// Only shard smoke tests when explicitly specified.
final String shardName = Platform.environment[kShardKey];
if (shardName == kSmokeTestShardName) {
testsToRun = _selectIndexOfTotalSubshard<ShardRunner>(tests);
} else {
testsToRun = tests;
}
for (final ShardRunner test in testsToRun) {
await test();
}

// Verify that we correctly generated the version file.
final String versionError = await verifyVersion(File(path.join(flutterRoot, 'version')));
Expand Down Expand Up @@ -276,38 +311,46 @@ Future<void> _runToolCoverage() async {
);
}

Future<void> _runToolTests() async {
const String kDotShard = '.shard';
const String kWeb = 'web';
const String kTest = 'test';
Future<void> _runGeneralToolTests() async {
await _pubRunTest(
path.join(flutterRoot, 'packages', 'flutter_tools'),
testPaths: <String>[path.join('test', 'general.shard')],
enableFlutterToolAsserts: false,
// Detect unit test time regressions (poor time delay handling, etc).
perTestTimeout: const Duration(seconds: 2),
);
}

Future<void> _runCommandsToolTests() async {
// Due to https://github.com/flutter/flutter/issues/46180, skip the hermetic directory
// on Windows.
final String suffix = Platform.isWindows ? 'permeable' : '';
await _pubRunTest(
path.join(flutterRoot, 'packages', 'flutter_tools'),
forceSingleCore: true,
testPaths: <String>[path.join('test', 'commands.shard', suffix)],
);
}

Future<void> _runIntegrationToolTests() async {
final String toolsPath = path.join(flutterRoot, 'packages', 'flutter_tools');
final List<String> allTests = Directory(path.join(toolsPath, 'test', 'integration.shard'))
.listSync(recursive: true).whereType<File>()
.map<String>((FileSystemEntity entry) => path.relative(entry.path, from: toolsPath))
.where((String testPath) => path.basename(testPath).endsWith('_test.dart')).toList();

final Map<String, ShardRunner> subshards = Map<String, ShardRunner>.fromIterable(
Directory(path.join(toolsPath, kTest))
.listSync()
.map<String>((FileSystemEntity entry) => entry.path)
.where((String name) => name.endsWith(kDotShard))
.where((String name) => path.basenameWithoutExtension(name) != kWeb)
.map<String>((String name) => path.basenameWithoutExtension(name)),
// The `dynamic` on the next line is because Map.fromIterable isn't generic.
value: (dynamic subshard) => () async {
// Due to https://github.com/flutter/flutter/issues/46180, skip the hermetic directory
// on Windows.
final String suffix = Platform.isWindows && subshard == 'commands'
? 'permeable'
: '';
await _pubRunTest(
toolsPath,
forceSingleCore: subshard != 'general',
testPaths: <String>[path.join(kTest, '$subshard$kDotShard', suffix)],
enableFlutterToolAsserts: subshard != 'general',
// Detect unit test time regressions (poor time delay handling, etc).
perTestTimeout: (subshard == 'general') ? const Duration(seconds: 2) : null,
);
},
await _pubRunTest(
toolsPath,
forceSingleCore: true,
testPaths: _selectIndexOfTotalSubshard<String>(allTests),
);
}

await selectSubshard(subshards);
Future<void> _runToolTests() async {
await selectSubshard(<String, ShardRunner>{
'general': _runGeneralToolTests,
'commands': _runCommandsToolTests,
});
}

Future<void> _runWebToolTests() async {
Expand Down Expand Up @@ -362,7 +405,10 @@ Future<void> _runBuildTests() async {
],
]..shuffle(math.Random(0));

await _selectIndexedSubshard(tests, kBuildTestShardCount);
if (!await _runShardRunnerIndexOfTotalSubshard(tests)) {
// TODO(jmagman): Remove fallback once LUCI configs are migrated to d+_d+ subshard format.
await _selectIndexedSubshard(tests, kBuildTestShardCount);
}
}

Future<void> _runExampleProjectBuildTests(FileSystemEntity exampleDirectory) async {
Expand Down Expand Up @@ -810,7 +856,10 @@ Future<void> _runWebLongRunningTests() async {
() => _runGalleryE2eWebTest('release', canvasKit: true),
];
await _ensureChromeDriverIsRunning();
await _selectIndexedSubshard(tests, kWebLongRunningTestShardCount);
if (!await _runShardRunnerIndexOfTotalSubshard(tests)) {
// TODO(jmagman): Remove fallback once LUCI configs are migrated to d+_d+ subshard format.
await _selectIndexedSubshard(tests, kWebLongRunningTestShardCount);
}
await _stopChromeDriver();
}

Expand Down Expand Up @@ -1457,6 +1506,60 @@ Future<void> _selectIndexedSubshard(List<ShardRunner> tests, int numberOfShards)
await selectSubshard(subshards);
}

/// Parse (one-)index/total-named subshards from environment variable SUBSHARD
/// and equally distribute [tests] between them.
/// Subshard format is "{index}_{total number of shards}".
/// The scheduler can change the number of total shards without needing an additional
/// commit in this repository.
///
/// Examples:
/// 1_3
/// 2_3
/// 3_3
List<T> _selectIndexOfTotalSubshard<T>(List<T> tests, {String subshardKey = kSubshardKey}) {
// Example: "1_3" means the first (one-indexed) shard of three total shards.
final String subshardName = Platform.environment[subshardKey];
if (subshardName == null) {
print('$kSubshardKey environment variable is missing, skipping sharding');
return tests;
}
print('$bold$subshardKey=$subshardName$reset');

final RegExp pattern = RegExp(r'^(\d+)_(\d+)$');
final Match match = pattern.firstMatch(subshardName);
if (match == null || match.groupCount != 2) {
print('${red}Invalid subshard name "$subshardName". Expected format "[int]_[int]" ex. "1_3"');
// TODO(jmagman): exit(1) here instead once LUCI configs are migrated to d+_d+ subshard format.
return null;
}
// One-indexed.
final int index = int.parse(match.group(1));
final int total = int.parse(match.group(2));
if (index > total) {
print('${red}Invalid subshard name "$subshardName". Index number must be greater or equal to total.');
exit(1);
}

final int testsPerShard = tests.length ~/ total;
final int start = (index - 1) * testsPerShard;
final int end = index * testsPerShard;

print('Selecting subshard $index of $total (range ${start + 1}-$end of ${tests.length})');
return tests.sublist(start, end);
}

Future<bool> _runShardRunnerIndexOfTotalSubshard(List<ShardRunner> tests) async {
final List<ShardRunner> sublist = _selectIndexOfTotalSubshard<ShardRunner>(tests);
// TODO(jmagman): Remove the boolean return to indicate fallback to unsharded variant once LUCI configs are migrated to d+_d+ subshard format.
if (sublist == null) {
return false;
}
for (final ShardRunner test in sublist) {
await test();
}
return true;
}

/// If the CIRRUS_TASK_NAME environment variable exists, we use that to determine
/// the shard and sub-shard (parsing it in the form shard-subshard-platform, ignoring
/// the platform).
Expand All @@ -1465,8 +1568,8 @@ Future<void> _selectIndexedSubshard(List<ShardRunner> tests, int numberOfShards)
/// environment variables. For example, to run all the framework tests you can
/// just set SHARD=framework_tests. To run specifically the third subshard of
/// the Web tests you can set SHARD=web_tests SUBSHARD=2 (it's zero-based).
Future<void> selectShard(Map<String, ShardRunner> shards) => _runFromList(shards, 'SHARD', 'shard', 0);
Future<void> selectSubshard(Map<String, ShardRunner> subshards) => _runFromList(subshards, 'SUBSHARD', 'subshard', 1);
Future<void> selectShard(Map<String, ShardRunner> shards) => _runFromList(shards, kShardKey, 'shard', 0);
Future<void> selectSubshard(Map<String, ShardRunner> subshards) => _runFromList(subshards, kSubshardKey, 'subshard', 1);

const String CIRRUS_TASK_NAME = 'CIRRUS_TASK_NAME';

Expand Down