mirror of
https://github.com/flutter/flutter.git
synced 2026-01-17 11:02:24 +08:00
This fixes https://github.com/flutter/flutter/pull/172184 by considering the last commit to `release-candidate-branch.version` as the significant commit. Otherwise, operationally, it works the same. I tested this on 3.35 and it works as expected (where the current does not).
301 lines
10 KiB
Dart
301 lines
10 KiB
Dart
// 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.
|
|
|
|
@TestOn('vm')
|
|
library;
|
|
|
|
import 'dart:io' as io;
|
|
|
|
import 'package:file/file.dart';
|
|
import 'package:file/local.dart';
|
|
import 'package:platform/platform.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// ✨ THINKING OF MOVING/REFACTORING THIS FILE? READ ME FIRST! ✨ //
|
|
// //
|
|
// There is a link to this file in //docs/tool/Engine-artfiacts.md //
|
|
// and it would be very kind of you to update the link, if needed. //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void main() {
|
|
// Want to test the powershell (update_engine_version.ps1) file, but running
|
|
// a macOS or Linux machine? You can install powershell and then opt-in to
|
|
// running `pwsh bin/internal/update_engine_version.ps1`.
|
|
//
|
|
// macOS: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-macos
|
|
// linux: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-linux
|
|
//
|
|
// Then, set this variable to true:
|
|
final bool usePowershellOnPosix = () {
|
|
// Intentionally not a const so that linting doesn't go wild across the test.
|
|
return false;
|
|
}();
|
|
|
|
const FileSystem localFs = LocalFileSystem();
|
|
final _FlutterRootUnderTest flutterRoot = _FlutterRootUnderTest.findWithin(
|
|
forcePowershell: usePowershellOnPosix,
|
|
);
|
|
|
|
late Directory tmpDir;
|
|
late _FlutterRootUnderTest testRoot;
|
|
late Map<String, String> environment;
|
|
|
|
void printIfNotEmpty(String prefix, String string) {
|
|
if (string.isNotEmpty) {
|
|
string.split(io.Platform.lineTerminator).forEach((String s) {
|
|
print('$prefix:>$s<');
|
|
});
|
|
}
|
|
}
|
|
|
|
io.ProcessResult run(String executable, List<String> args) {
|
|
final io.ProcessResult result = io.Process.runSync(
|
|
executable,
|
|
args,
|
|
environment: environment,
|
|
workingDirectory: testRoot.root.absolute.path,
|
|
includeParentEnvironment: false,
|
|
);
|
|
if (result.exitCode != 0) {
|
|
fail(
|
|
'Failed running "$executable $args" (exit code = ${result.exitCode}),'
|
|
'\nstdout: ${result.stdout}'
|
|
'\nstderr: ${result.stderr}',
|
|
);
|
|
}
|
|
printIfNotEmpty('stdout', (result.stdout as String).trim());
|
|
printIfNotEmpty('stderr', (result.stderr as String).trim());
|
|
return result;
|
|
}
|
|
|
|
setUpAll(() async {
|
|
if (usePowershellOnPosix) {
|
|
final io.ProcessResult result = io.Process.runSync('pwsh', <String>['--version']);
|
|
print(
|
|
'Using Powershell (${(result.stdout as String).trim()}) on POSIX for local debugging and testing',
|
|
);
|
|
}
|
|
});
|
|
|
|
/// Initializes a blank git repo in [testRoot.root].
|
|
void initGitRepoWithBlankInitialCommit() {
|
|
run('git', <String>['init', '--initial-branch', 'master']);
|
|
run('git', <String>['config', '--local', 'user.email', 'test@example.com']);
|
|
run('git', <String>['config', '--local', 'user.name', 'Test User']);
|
|
run('git', <String>['add', '.']);
|
|
run('git', <String>['commit', '--allow-empty', '-m', 'Initial commit']);
|
|
}
|
|
|
|
late int commitCount;
|
|
|
|
setUp(() async {
|
|
commitCount = 0;
|
|
|
|
tmpDir = localFs.systemTempDirectory.createTempSync('last_engine_commit_test.');
|
|
testRoot = _FlutterRootUnderTest.fromPath(
|
|
tmpDir.childDirectory('flutter').path,
|
|
forcePowershell: usePowershellOnPosix,
|
|
);
|
|
|
|
environment = <String, String>{};
|
|
|
|
if (const LocalPlatform().isWindows || usePowershellOnPosix) {
|
|
// Copy a minimal set of environment variables needed to run the update_engine_version script in PowerShell.
|
|
const List<String> powerShellVariables = <String>['SYSTEMROOT', 'PATH', 'PATHEXT'];
|
|
for (final String key in powerShellVariables) {
|
|
final String? value = io.Platform.environment[key];
|
|
if (value != null) {
|
|
environment[key] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy the update_engine_version script and create a rough directory structure.
|
|
flutterRoot.binInternalLastEngineCommit.copySyncRecursive(
|
|
testRoot.binInternalLastEngineCommit.path,
|
|
);
|
|
|
|
initGitRepoWithBlankInitialCommit();
|
|
});
|
|
|
|
tearDown(() {
|
|
tmpDir.deleteSync(recursive: true);
|
|
});
|
|
|
|
/// Runs `bin/internal/last_engine_commit.{sh|ps1}` and returns the stdout.
|
|
///
|
|
/// - On Windows, `powershell` is used (to run `last_engine_commit.ps1`);
|
|
/// - On POSIX, if [usePowershellOnPosix] is set, `pwsh` is used (to run `last_engine_commit.ps1`);
|
|
/// - Otherwise, `last_engine_commit.sh` is used.
|
|
String getLastEngineCommit() {
|
|
final String executable;
|
|
final List<String> args;
|
|
if (const LocalPlatform().isWindows) {
|
|
executable = 'powershell';
|
|
args = <String>[testRoot.binInternalLastEngineCommit.path];
|
|
} else if (usePowershellOnPosix) {
|
|
executable = 'pwsh';
|
|
args = <String>[testRoot.binInternalLastEngineCommit.path];
|
|
} else {
|
|
executable = testRoot.binInternalLastEngineCommit.path;
|
|
args = <String>[];
|
|
}
|
|
return (run(executable, args).stdout as String).trim();
|
|
}
|
|
|
|
/// Gets the latest commit on the current branch.
|
|
String getLastCommit() {
|
|
return (run('git', <String>['rev-parse', 'HEAD']).stdout as String).trim();
|
|
}
|
|
|
|
void writeCommit(Iterable<String> files) {
|
|
commitCount++;
|
|
for (final String relativePath in files) {
|
|
localFs.file(localFs.path.join(testRoot.root.path, relativePath))
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('$commitCount');
|
|
}
|
|
run('git', <String>['add', '.']);
|
|
run('git', <String>['commit', '-m', 'Wrote ${files.length} files']);
|
|
}
|
|
|
|
test('returns the last engine commit', () {
|
|
writeCommit(<String>['bin/internal/release-candidate-branch.version']);
|
|
writeCommit(<String>['DEPS', 'engine/README.md']);
|
|
|
|
final String lastEngine = getLastEngineCommit();
|
|
expect(lastEngine, isNotEmpty);
|
|
|
|
writeCommit(<String>['CHANGELOG.md', 'dev/folder/called/engine/README.md']);
|
|
expect(getLastEngineCommit(), lastEngine);
|
|
});
|
|
|
|
test('considers DEPS an engine change', () {
|
|
writeCommit(<String>['bin/internal/release-candidate-branch.version']);
|
|
writeCommit(<String>['DEPS', 'engine/README.md']);
|
|
|
|
final String lastEngineA = getLastEngineCommit();
|
|
expect(lastEngineA, isNotEmpty);
|
|
|
|
writeCommit(<String>['DEPS']);
|
|
final String lastEngineB = getLastEngineCommit();
|
|
expect(lastEngineB, allOf(isNotEmpty, isNot(equals(lastEngineA))));
|
|
});
|
|
|
|
test('if there have been no engine changes, uses the first commit since the branch point', () {
|
|
final String initialStartingCommit = getLastCommit();
|
|
|
|
// Make an engine change *before* the branch.
|
|
writeCommit(<String>['engine/README.md']);
|
|
final String engineCommitPreBranch = getLastCommit();
|
|
|
|
// Write the branch file.
|
|
writeCommit(<String>['bin/internal/release-candidate-branch.version']);
|
|
final String initialBranchCommit = getLastCommit();
|
|
|
|
// Write another commit to make sure we don't always use the latest.
|
|
writeCommit(<String>['CHANGELOG.md']);
|
|
final String latestCommitIgnore = getLastCommit();
|
|
|
|
// Get the engine commit, which should fallback to HEAD~2 (in this case).
|
|
final String lastCommitToEngine = getLastEngineCommit();
|
|
expect(
|
|
lastCommitToEngine,
|
|
initialBranchCommit,
|
|
reason:
|
|
'The git history for this simulation looks like this:\n'
|
|
'master | $initialStartingCommit\n'
|
|
'master | $engineCommitPreBranch\n'
|
|
'release | $initialBranchCommit\n'
|
|
'release | $latestCommitIgnore\n'
|
|
'\n'
|
|
'We expected our script to select HEAD~2, $initialBranchCommit, but '
|
|
'instead it selected $lastCommitToEngine, which is incorrect. See '
|
|
'the table above to help debug.',
|
|
);
|
|
});
|
|
}
|
|
|
|
extension on File {
|
|
void copySyncRecursive(String newPath) {
|
|
fileSystem.directory(fileSystem.path.dirname(newPath)).createSync(recursive: true);
|
|
copySync(newPath);
|
|
}
|
|
}
|
|
|
|
/// A FrUT, or "Flutter Root"-Under Test (parallel to a SUT, System Under Test).
|
|
///
|
|
/// For the intent of this test case, the "Flutter Root" is a directory
|
|
/// structure with the following elements:
|
|
///
|
|
/// ```txt
|
|
/// ├── DEPS
|
|
/// ├── engine/
|
|
/// ├── bin/
|
|
/// │ ├── internal/
|
|
/// │ │ └── last_engine_commit.{sh|ps1}
|
|
/// ```
|
|
final class _FlutterRootUnderTest {
|
|
/// Creates a root-under test using [path] as the root directory.
|
|
///
|
|
/// It is assumed the files already exist or will be created if needed.
|
|
factory _FlutterRootUnderTest.fromPath(
|
|
String path, {
|
|
FileSystem fileSystem = const LocalFileSystem(),
|
|
Platform platform = const LocalPlatform(),
|
|
bool forcePowershell = false,
|
|
}) {
|
|
final Directory root = fileSystem.directory(path);
|
|
return _FlutterRootUnderTest._(
|
|
root,
|
|
depsFile: root.childFile('DEPS'),
|
|
engineDirectory: root.childDirectory('engine'),
|
|
binInternalLastEngineCommit: root.childFile(
|
|
fileSystem.path.join(
|
|
'bin',
|
|
'internal',
|
|
'last_engine_commit.${platform.isWindows || forcePowershell ? 'ps1' : 'sh'}',
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
factory _FlutterRootUnderTest.findWithin({
|
|
String? path,
|
|
FileSystem fileSystem = const LocalFileSystem(),
|
|
bool forcePowershell = false,
|
|
}) {
|
|
path ??= fileSystem.currentDirectory.path;
|
|
Directory current = fileSystem.directory(path);
|
|
while (!current.childFile('DEPS').existsSync()) {
|
|
if (current.path == current.parent.path) {
|
|
throw ArgumentError.value(path, 'path', 'Could not resolve flutter root');
|
|
}
|
|
current = current.parent;
|
|
}
|
|
return _FlutterRootUnderTest.fromPath(current.path, forcePowershell: forcePowershell);
|
|
}
|
|
|
|
const _FlutterRootUnderTest._(
|
|
this.root, {
|
|
required this.binInternalLastEngineCommit,
|
|
required this.depsFile,
|
|
required this.engineDirectory,
|
|
});
|
|
|
|
final Directory root;
|
|
|
|
/// `DEPS`.
|
|
final File depsFile;
|
|
|
|
/// The `engine/` directory.
|
|
final Directory engineDirectory;
|
|
|
|
/// `bin/internal/last_engine_commit.{sh|ps1}`.
|
|
final File binInternalLastEngineCommit;
|
|
}
|