blob: 7939b5614b7261cbf125bc0905c31be0945d4a4e [file] [log] [blame]
// 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 'dart:convert' show jsonDecode;
import 'package:args/command_runner.dart';
import 'package:conductor_core/src/proto/conductor_state.pb.dart' as pb;
import 'package:conductor_core/src/proto/conductor_state.pbenum.dart';
import 'package:conductor_core/src/repository.dart';
import 'package:conductor_core/src/start.dart';
import 'package:conductor_core/src/state.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:platform/platform.dart';
import './common.dart';
void main() {
group('start command', () {
const String branchPointRevision =
'5131a6e5e0c50b8b7b2906cd58dab8746d6450be';
const String flutterRoot = '/flutter';
const String checkoutsParentDirectory = '$flutterRoot/dev/tools/';
const String githubUsername = 'user';
const String frameworkMirror =
'git@github.com:$githubUsername/flutter.git';
const String engineMirror = 'git@github.com:$githubUsername/engine.git';
const String candidateBranch = 'flutter-1.2-candidate.3';
const String releaseChannel = 'beta';
const String revision = 'abcd1234';
const String conductorVersion = 'deadbeef';
late Checkouts checkouts;
late MemoryFileSystem fileSystem;
late FakePlatform platform;
late TestStdio stdio;
late FakeProcessManager processManager;
setUp(() {
stdio = TestStdio();
fileSystem = MemoryFileSystem.test();
});
CommandRunner<void> createRunner({
Map<String, String>? environment,
String? operatingSystem,
List<FakeCommand>? commands,
}) {
operatingSystem ??= const LocalPlatform().operatingSystem;
final String pathSeparator = operatingSystem == 'windows' ? r'\' : '/';
environment ??= <String, String>{
'HOME': '/path/to/user/home',
};
final Directory homeDir = fileSystem.directory(
environment['HOME'],
);
// Tool assumes this exists
homeDir.createSync(recursive: true);
platform = FakePlatform(
environment: environment,
operatingSystem: operatingSystem,
pathSeparator: pathSeparator,
);
processManager = FakeProcessManager.list(commands ?? <FakeCommand>[]);
checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(checkoutsParentDirectory),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final StartCommand command = StartCommand(
checkouts: checkouts,
conductorVersion: conductorVersion,
);
return CommandRunner<void>('codesign-test', '')..addCommand(command);
}
test('throws exception if run from Windows', () async {
final CommandRunner<void> runner = createRunner(
commands: <FakeCommand>[
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision,
),
],
operatingSystem: 'windows',
);
await expectLater(
() async => runner.run(<String>[
'start',
'--$kCandidateOption',
candidateBranch,
'--$kReleaseOption',
'beta',
'--$kStateOption',
'/path/to/statefile.json',
]),
throwsExceptionWith(
'Error! This tool is only supported on macOS and Linux',
),
);
});
test('throws if provided an invalid --$kVersionOverrideOption', () async {
final CommandRunner<void> runner = createRunner();
final String stateFilePath = fileSystem.path.join(
platform.environment['HOME']!,
kStateFileName,
);
await expectLater(
() async => runner.run(<String>[
'start',
'--$kCandidateOption',
candidateBranch,
'--$kReleaseOption',
releaseChannel,
'--$kStateOption',
stateFilePath,
'--$kVersionOverrideOption',
'an invalid version string',
'--$kGithubUsernameOption',
githubUsername,
]),
throwsExceptionWith('an invalid version string cannot be parsed'),
);
});
test('creates state file if provided correct inputs', () async {
stdio.stdin.add('y'); // accept prompt from ensureBranchPointTagged()
const String revision2 = 'def789';
const String revision3 = '123abc';
const String previousDartRevision =
'171876a4e6cf56ee6da1f97d203926bd7afda7ef';
const String nextDartRevision =
'f6c91128be6b77aef8351e1e3a9d07c85bc2e46e';
const String previousVersion = '1.2.0-1.0.pre';
// This is what this release will be
const String nextVersion = '1.2.0-1.1.pre';
const String candidateBranch = 'flutter-1.2-candidate.1';
final Directory engine = fileSystem
.directory(checkoutsParentDirectory)
.childDirectory('flutter_conductor_checkouts')
.childDirectory('engine');
final File depsFile = engine.childFile('DEPS');
final List<FakeCommand> engineCommands = <FakeCommand>[
FakeCommand(
command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
EngineRepository.defaultUpstream,
engine.path,
],
onRun: () {
// Create the DEPS file which the tool will update
engine.createSync(recursive: true);
depsFile
.writeAsStringSync(generateMockDeps(previousDartRevision));
}),
const FakeCommand(
command: <String>['git', 'remote', 'add', 'mirror', engineMirror],
),
const FakeCommand(
command: <String>['git', 'fetch', 'mirror'],
),
const FakeCommand(
command: <String>['git', 'checkout', 'upstream/$candidateBranch'],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
const FakeCommand(
command: <String>[
'git',
'checkout',
'-b',
'cherrypicks-$candidateBranch',
],
),
const FakeCommand(
command: <String>['git', 'status', '--porcelain'],
stdout: 'MM path/to/DEPS',
),
const FakeCommand(
command: <String>['git', 'add', '--all'],
),
const FakeCommand(
command: <String>[
'git',
'commit',
'--message',
'Update Dart SDK to $nextDartRevision',
],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
];
final List<FakeCommand> frameworkCommands = <FakeCommand>[
FakeCommand(
command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
FrameworkRepository.defaultUpstream,
fileSystem.path.join(
checkoutsParentDirectory,
'flutter_conductor_checkouts',
'framework',
),
],
),
const FakeCommand(
command: <String>['git', 'remote', 'add', 'mirror', frameworkMirror],
),
const FakeCommand(
command: <String>['git', 'fetch', 'mirror'],
),
const FakeCommand(
command: <String>['git', 'checkout', 'upstream/$candidateBranch'],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision3,
),
const FakeCommand(
command: <String>[
'git',
'checkout',
'-b',
'cherrypicks-$candidateBranch',
],
),
const FakeCommand(
command: <String>[
'git',
'describe',
'--match',
'*.*.*',
'--tags',
'refs/remotes/upstream/$candidateBranch',
],
stdout: '$previousVersion-42-gabc123',
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision3,
),
const FakeCommand(
command: <String>[
'git',
'merge-base',
'upstream/$candidateBranch',
'upstream/master',
],
stdout: branchPointRevision,
),
// check if commit is tagged, zero exit code means it is tagged
const FakeCommand(
command: <String>[
'git',
'describe',
'--exact-match',
'--tags',
branchPointRevision,
],
),
];
final CommandRunner<void> runner = createRunner(
commands: <FakeCommand>[
...engineCommands,
...frameworkCommands,
],
);
final String stateFilePath = fileSystem.path.join(
platform.environment['HOME']!,
kStateFileName,
);
await runner.run(<String>[
'start',
'--$kCandidateOption',
candidateBranch,
'--$kReleaseOption',
releaseChannel,
'--$kStateOption',
stateFilePath,
'--$kDartRevisionOption',
nextDartRevision,
'--$kGithubUsernameOption',
githubUsername,
]);
final File stateFile = fileSystem.file(stateFilePath);
final pb.ConductorState state = pb.ConductorState();
state.mergeFromProto3Json(
jsonDecode(stateFile.readAsStringSync()),
);
expect(state.releaseType, ReleaseType.BETA_HOTFIX);
expect(
stdio.error,
isNot(contains(
'Tried to tag the branch point, however the target version')));
expect(processManager, hasNoRemainingExpectations);
expect(state.isInitialized(), true);
expect(state.releaseChannel, releaseChannel);
expect(state.releaseVersion, nextVersion);
expect(state.engine.candidateBranch, candidateBranch);
expect(state.engine.startingGitHead, revision2);
expect(state.engine.dartRevision, nextDartRevision);
expect(state.engine.upstream.url, 'git@github.com:flutter/engine.git');
expect(state.framework.candidateBranch, candidateBranch);
expect(state.framework.startingGitHead, revision3);
expect(
state.framework.upstream.url, 'git@github.com:flutter/flutter.git');
expect(state.currentPhase, ReleasePhase.APPLY_ENGINE_CHERRYPICKS);
expect(state.conductorVersion, conductorVersion);
});
test('uses --$kVersionOverrideOption', () async {
stdio.stdin.add('y'); // accept prompt from ensureBranchPointTagged()
const String revision2 = 'def789';
const String revision3 = '123abc';
const String previousDartRevision =
'171876a4e6cf56ee6da1f97d203926bd7afda7ef';
const String nextDartRevision =
'f6c91128be6b77aef8351e1e3a9d07c85bc2e46e';
const String previousVersion = '1.2.0-1.0.pre';
const String candidateBranch = 'flutter-1.2-candidate.1';
const String versionOverride = '42.0.0-42.0.pre';
final Directory engine = fileSystem
.directory(checkoutsParentDirectory)
.childDirectory('flutter_conductor_checkouts')
.childDirectory('engine');
final File depsFile = engine.childFile('DEPS');
final List<FakeCommand> engineCommands = <FakeCommand>[
FakeCommand(
command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
EngineRepository.defaultUpstream,
engine.path,
],
onRun: () {
// Create the DEPS file which the tool will update
engine.createSync(recursive: true);
depsFile
.writeAsStringSync(generateMockDeps(previousDartRevision));
}),
const FakeCommand(
command: <String>['git', 'remote', 'add', 'mirror', engineMirror],
),
const FakeCommand(
command: <String>['git', 'fetch', 'mirror'],
),
const FakeCommand(
command: <String>['git', 'checkout', 'upstream/$candidateBranch'],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
const FakeCommand(
command: <String>[
'git',
'checkout',
'-b',
'cherrypicks-$candidateBranch',
],
),
const FakeCommand(
command: <String>['git', 'status', '--porcelain'],
stdout: 'MM path/to/DEPS',
),
const FakeCommand(
command: <String>['git', 'add', '--all'],
),
const FakeCommand(
command: <String>[
'git',
'commit',
'--message',
'Update Dart SDK to $nextDartRevision'
],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
];
final List<FakeCommand> frameworkCommands = <FakeCommand>[
FakeCommand(
command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
FrameworkRepository.defaultUpstream,
fileSystem.path.join(
checkoutsParentDirectory,
'flutter_conductor_checkouts',
'framework',
),
],
),
const FakeCommand(
command: <String>['git', 'remote', 'add', 'mirror', frameworkMirror],
),
const FakeCommand(
command: <String>['git', 'fetch', 'mirror'],
),
const FakeCommand(
command: <String>['git', 'checkout', 'upstream/$candidateBranch'],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision3,
),
const FakeCommand(
command: <String>[
'git',
'checkout',
'-b',
'cherrypicks-$candidateBranch',
],
),
const FakeCommand(
command: <String>[
'git',
'describe',
'--match',
'*.*.*',
'--tags',
'refs/remotes/upstream/$candidateBranch',
],
stdout: '$previousVersion-42-gabc123',
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision3,
),
const FakeCommand(
command: <String>[
'git',
'merge-base',
'upstream/$candidateBranch',
'upstream/master'
],
stdout: branchPointRevision,
),
];
final CommandRunner<void> runner = createRunner(
commands: <FakeCommand>[
...engineCommands,
...frameworkCommands,
],
);
final String stateFilePath = fileSystem.path.join(
platform.environment['HOME']!,
kStateFileName,
);
await runner.run(<String>[
'start',
'--$kCandidateOption',
candidateBranch,
'--$kReleaseOption',
releaseChannel,
'--$kStateOption',
stateFilePath,
'--$kDartRevisionOption',
nextDartRevision,
'--$kVersionOverrideOption',
versionOverride,
'--$kGithubUsernameOption',
githubUsername,
]);
final File stateFile = fileSystem.file(stateFilePath);
final pb.ConductorState state = pb.ConductorState();
state.mergeFromProto3Json(
jsonDecode(stateFile.readAsStringSync()),
);
expect(processManager, hasNoRemainingExpectations);
expect(state.releaseVersion, versionOverride);
});
test('logs to STDERR but does not fail on an unexpected candidate branch',
() async {
stdio.stdin.add('y'); // accept prompt from ensureBranchPointTagged()
const String revision2 = 'def789';
const String revision3 = '123abc';
const String previousDartRevision =
'171876a4e6cf56ee6da1f97d203926bd7afda7ef';
const String nextDartRevision =
'f6c91128be6b77aef8351e1e3a9d07c85bc2e46e';
// note that this significantly behind the candidate branch name
const String previousVersion = '0.9.0-1.0.pre';
// This is what this release will be
const String nextVersion = '0.9.0-1.1.pre';
final Directory engine = fileSystem
.directory(checkoutsParentDirectory)
.childDirectory('flutter_conductor_checkouts')
.childDirectory('engine');
final File depsFile = engine.childFile('DEPS');
final List<FakeCommand> engineCommands = <FakeCommand>[
FakeCommand(
command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
EngineRepository.defaultUpstream,
engine.path,
],
onRun: () {
// Create the DEPS file which the tool will update
engine.createSync(recursive: true);
depsFile
.writeAsStringSync(generateMockDeps(previousDartRevision));
}),
const FakeCommand(
command: <String>['git', 'remote', 'add', 'mirror', engineMirror],
),
const FakeCommand(
command: <String>['git', 'fetch', 'mirror'],
),
const FakeCommand(
command: <String>['git', 'checkout', 'upstream/$candidateBranch'],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
const FakeCommand(
command: <String>[
'git',
'checkout',
'-b',
'cherrypicks-$candidateBranch',
],
),
const FakeCommand(
command: <String>['git', 'status', '--porcelain'],
stdout: 'MM path/to/DEPS',
),
const FakeCommand(
command: <String>['git', 'add', '--all'],
),
const FakeCommand(
command: <String>[
'git',
'commit',
'--message',
'Update Dart SDK to $nextDartRevision',
],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
];
final List<FakeCommand> frameworkCommands = <FakeCommand>[
FakeCommand(
command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
FrameworkRepository.defaultUpstream,
fileSystem.path.join(
checkoutsParentDirectory,
'flutter_conductor_checkouts',
'framework',
),
],
),
const FakeCommand(
command: <String>['git', 'remote', 'add', 'mirror', frameworkMirror],
),
const FakeCommand(
command: <String>['git', 'fetch', 'mirror'],
),
const FakeCommand(
command: <String>['git', 'checkout', 'upstream/$candidateBranch'],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision3,
),
const FakeCommand(
command: <String>[
'git',
'checkout',
'-b',
'cherrypicks-$candidateBranch',
],
),
const FakeCommand(
command: <String>[
'git',
'describe',
'--match',
'*.*.*',
'--tags',
'refs/remotes/upstream/$candidateBranch',
],
stdout: '$previousVersion-42-gabc123',
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision3,
),
const FakeCommand(
command: <String>[
'git',
'merge-base',
'upstream/$candidateBranch',
'upstream/master',
],
stdout: branchPointRevision,
),
// check if commit is tagged, 0 exit code means it is tagged
const FakeCommand(
command: <String>[
'git',
'describe',
'--exact-match',
'--tags',
branchPointRevision,
],
),
];
final CommandRunner<void> runner = createRunner(
commands: <FakeCommand>[
...engineCommands,
...frameworkCommands,
],
);
final String stateFilePath = fileSystem.path.join(
platform.environment['HOME']!,
kStateFileName,
);
await runner.run(<String>[
'start',
'--$kCandidateOption',
candidateBranch,
'--$kReleaseOption',
releaseChannel,
'--$kStateOption',
stateFilePath,
'--$kDartRevisionOption',
nextDartRevision,
'--$kGithubUsernameOption',
githubUsername,
]);
final File stateFile = fileSystem.file(stateFilePath);
final pb.ConductorState state = pb.ConductorState();
state.mergeFromProto3Json(
jsonDecode(stateFile.readAsStringSync()),
);
expect(stdio.error,
isNot(contains('Tried to tag the branch point, however')));
expect(processManager, hasNoRemainingExpectations);
expect(state.isInitialized(), true);
expect(state.releaseChannel, releaseChannel);
expect(state.releaseVersion, nextVersion);
expect(state.engine.candidateBranch, candidateBranch);
expect(state.engine.startingGitHead, revision2);
expect(state.engine.dartRevision, nextDartRevision);
expect(state.engine.upstream.url, 'git@github.com:flutter/engine.git');
expect(state.framework.candidateBranch, candidateBranch);
expect(state.framework.startingGitHead, revision3);
expect(
state.framework.upstream.url, 'git@github.com:flutter/flutter.git');
expect(state.currentPhase, ReleasePhase.APPLY_ENGINE_CHERRYPICKS);
expect(state.conductorVersion, conductorVersion);
expect(state.releaseType, ReleaseType.BETA_HOTFIX);
expect(
stdio.error,
contains(
'Parsed version $previousVersion.42 has a different x value than candidate branch $candidateBranch'));
});
test('can convert from dev style version to stable version', () async {
const String revision2 = 'def789';
const String revision3 = '123abc';
const String previousDartRevision =
'171876a4e6cf56ee6da1f97d203926bd7afda7ef';
const String nextDartRevision =
'f6c91128be6b77aef8351e1e3a9d07c85bc2e46e';
const String previousVersion = '1.2.0-3.0.pre';
const String nextVersion = '1.2.0';
final Directory engine = fileSystem
.directory(checkoutsParentDirectory)
.childDirectory('flutter_conductor_checkouts')
.childDirectory('engine');
final File depsFile = engine.childFile('DEPS');
final List<FakeCommand> engineCommands = <FakeCommand>[
FakeCommand(
command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
EngineRepository.defaultUpstream,
engine.path,
],
onRun: () {
// Create the DEPS file which the tool will update
engine.createSync(recursive: true);
depsFile
.writeAsStringSync(generateMockDeps(previousDartRevision));
}),
const FakeCommand(
command: <String>['git', 'remote', 'add', 'mirror', engineMirror],
),
const FakeCommand(
command: <String>['git', 'fetch', 'mirror'],
),
const FakeCommand(
command: <String>['git', 'checkout', 'upstream/$candidateBranch'],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
const FakeCommand(
command: <String>[
'git',
'checkout',
'-b',
'cherrypicks-$candidateBranch',
],
),
const FakeCommand(
command: <String>['git', 'status', '--porcelain'],
stdout: 'MM path/to/DEPS',
),
const FakeCommand(
command: <String>['git', 'add', '--all'],
),
const FakeCommand(
command: <String>[
'git',
'commit',
'--message',
'Update Dart SDK to $nextDartRevision',
],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
];
final List<FakeCommand> frameworkCommands = <FakeCommand>[
FakeCommand(
command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
FrameworkRepository.defaultUpstream,
fileSystem.path.join(
checkoutsParentDirectory,
'flutter_conductor_checkouts',
'framework',
),
],
),
const FakeCommand(
command: <String>['git', 'remote', 'add', 'mirror', frameworkMirror],
),
const FakeCommand(
command: <String>['git', 'fetch', 'mirror'],
),
const FakeCommand(
command: <String>['git', 'checkout', 'upstream/$candidateBranch'],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision3,
),
const FakeCommand(
command: <String>[
'git',
'checkout',
'-b',
'cherrypicks-$candidateBranch',
],
),
const FakeCommand(
command: <String>[
'git',
'describe',
'--match',
'*.*.*',
'--tags',
'refs/remotes/upstream/$candidateBranch',
],
stdout: '$previousVersion-42-gabc123',
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision3,
),
const FakeCommand(
command: <String>[
'git',
'merge-base',
'upstream/$candidateBranch',
'upstream/master'
],
stdout: branchPointRevision,
),
// check if commit is tagged, 0 exit code thus it is tagged
const FakeCommand(
command: <String>[
'git',
'describe',
'--exact-match',
'--tags',
branchPointRevision,
],
),
];
final CommandRunner<void> runner = createRunner(
commands: <FakeCommand>[
...engineCommands,
...frameworkCommands,
],
);
final String stateFilePath = fileSystem.path.join(
platform.environment['HOME']!,
kStateFileName,
);
await runner.run(<String>[
'start',
'--$kCandidateOption',
candidateBranch,
'--$kReleaseOption',
'stable',
'--$kStateOption',
stateFilePath,
'--$kDartRevisionOption',
nextDartRevision,
'--$kGithubUsernameOption',
githubUsername,
]);
final File stateFile = fileSystem.file(stateFilePath);
final pb.ConductorState state = pb.ConductorState();
state.mergeFromProto3Json(
jsonDecode(stateFile.readAsStringSync()),
);
expect(processManager.hasRemainingExpectations, false);
expect(state.isInitialized(), true);
expect(state.releaseChannel, 'stable');
expect(state.releaseVersion, nextVersion);
expect(state.engine.candidateBranch, candidateBranch);
expect(state.engine.startingGitHead, revision2);
expect(state.engine.dartRevision, nextDartRevision);
expect(state.framework.candidateBranch, candidateBranch);
expect(state.framework.startingGitHead, revision3);
expect(state.currentPhase, ReleasePhase.APPLY_ENGINE_CHERRYPICKS);
expect(state.conductorVersion, conductorVersion);
expect(state.releaseType, ReleaseType.STABLE_INITIAL);
});
test(
'StartContext gets engine and framework checkout directories after run',
() async {
stdio.stdin.add('y');
const String revision2 = 'def789';
const String revision3 = '123abc';
const String branchPointRevision = 'deadbeef';
const String previousDartRevision =
'171876a4e6cf56ee6da1f97d203926bd7afda7ef';
const String nextDartRevision =
'f6c91128be6b77aef8351e1e3a9d07c85bc2e46e';
const String previousVersion = '1.2.0-1.0.pre';
// This is a git tag applied to the branch point, not an actual release
const String branchPointTag = '1.2.0-3.0.pre';
final Directory engine = fileSystem
.directory(checkoutsParentDirectory)
.childDirectory('flutter_conductor_checkouts')
.childDirectory('engine');
final Directory framework = fileSystem
.directory(checkoutsParentDirectory)
.childDirectory('flutter_conductor_checkouts')
.childDirectory('framework');
final File depsFile = engine.childFile('DEPS');
final List<FakeCommand> engineCommands = <FakeCommand>[
FakeCommand(
command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
EngineRepository.defaultUpstream,
engine.path,
],
onRun: () {
// Create the DEPS file which the tool will update
engine.createSync(recursive: true);
depsFile
.writeAsStringSync(generateMockDeps(previousDartRevision));
}),
const FakeCommand(
command: <String>['git', 'remote', 'add', 'mirror', engineMirror],
),
const FakeCommand(
command: <String>['git', 'fetch', 'mirror'],
),
const FakeCommand(
command: <String>['git', 'checkout', 'upstream/$candidateBranch'],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
const FakeCommand(
command: <String>[
'git',
'checkout',
'-b',
'cherrypicks-$candidateBranch',
],
),
const FakeCommand(
command: <String>['git', 'status', '--porcelain'],
stdout: 'MM path/to/DEPS',
),
const FakeCommand(
command: <String>['git', 'add', '--all'],
),
const FakeCommand(
command: <String>[
'git',
'commit',
'--message',
'Update Dart SDK to $nextDartRevision'
],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2,
),
];
final List<FakeCommand> frameworkCommands = <FakeCommand>[
FakeCommand(
command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
FrameworkRepository.defaultUpstream,
framework.path,
],
),
const FakeCommand(
command: <String>['git', 'remote', 'add', 'mirror', frameworkMirror],
),
const FakeCommand(
command: <String>['git', 'fetch', 'mirror'],
),
const FakeCommand(
command: <String>['git', 'checkout', 'upstream/$candidateBranch'],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision3,
),
const FakeCommand(
command: <String>[
'git',
'checkout',
'-b',
'cherrypicks-$candidateBranch',
],
),
const FakeCommand(
command: <String>[
'git',
'describe',
'--match',
'*.*.*',
'--tags',
'refs/remotes/upstream/$candidateBranch',
],
stdout: '$previousVersion-42-gabc123',
),
// HEAD and branch point are same
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: branchPointRevision,
),
const FakeCommand(
command: <String>[
'git',
'merge-base',
'upstream/$candidateBranch',
'upstream/master'
],
stdout: branchPointRevision,
),
// check if commit is tagged
const FakeCommand(
command: <String>[
'git',
'describe',
'--exact-match',
'--tags',
branchPointRevision
],
// non-zero exit code means branch point is NOT tagged
exitCode: 128,
),
const FakeCommand(
command: <String>['git', 'tag', branchPointTag, branchPointRevision],
),
const FakeCommand(
command: <String>[
'git',
'push',
FrameworkRepository.defaultUpstream,
branchPointTag
],
),
];
final String operatingSystem = const LocalPlatform().operatingSystem;
final Map<String, String> environment = <String, String>{
'HOME': '/path/to/user/home',
};
final Directory homeDir = fileSystem.directory(
environment['HOME'],
);
// Tool assumes this exists
homeDir.createSync(recursive: true);
platform = FakePlatform(
environment: environment,
operatingSystem: operatingSystem,
);
final String stateFilePath = fileSystem.path.join(
platform.environment['HOME']!,
kStateFileName,
);
final File stateFile = fileSystem.file(stateFilePath);
processManager = FakeProcessManager.list(<FakeCommand>[
...engineCommands,
...frameworkCommands,
]);
checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(checkoutsParentDirectory),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final StartContext startContext = StartContext(
candidateBranch: candidateBranch,
checkouts: checkouts,
dartRevision: nextDartRevision,
engineCherrypickRevisions: <String>[],
engineMirror: engineMirror,
engineUpstream: EngineRepository.defaultUpstream,
frameworkCherrypickRevisions: <String>[],
frameworkMirror: frameworkMirror,
frameworkUpstream: FrameworkRepository.defaultUpstream,
releaseChannel: releaseChannel,
processManager: processManager,
conductorVersion: conductorVersion,
githubUsername: githubUsername,
stateFile: stateFile,
);
await startContext.run();
final pb.ConductorState state = pb.ConductorState();
state.mergeFromProto3Json(
jsonDecode(stateFile.readAsStringSync()),
);
expect((await startContext.engine.checkoutDirectory).path,
equals(engine.path));
expect((await startContext.framework.checkoutDirectory).path,
equals(framework.path));
expect(state.releaseType, ReleaseType.BETA_INITIAL);
expect(processManager, hasNoRemainingExpectations);
});
}, onPlatform: <String, dynamic>{
'windows': const Skip('Flutter Conductor only supported on macos/linux'),
});
}
String generateMockDeps(String dartRevision) {
return '''
vars = {
'chromium_git': 'https://chromium.googlesource.com',
'swiftshader_git': 'https://swiftshader.googlesource.com',
'dart_git': 'https://dart.googlesource.com',
'flutter_git': 'https://flutter.googlesource.com',
'fuchsia_git': 'https://fuchsia.googlesource.com',
'github_git': 'https://github.com',
'skia_git': 'https://skia.googlesource.com',
'ocmock_git': 'https://github.com/erikdoe/ocmock.git',
'skia_revision': '4e9d5e2bdf04c58bc0bff57be7171e469e5d7175',
'dart_revision': '$dartRevision',
'dart_boringssl_gen_rev': '7322fc15cc065d8d2957fccce6b62a509dc4d641',
}''';
}