blob: 35196b8acca3add043957eafd70dd6d5298cc2c2 [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 'package:conductor_core/src/repository.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:platform/platform.dart';
import './common.dart';
void main() {
group('repository', () {
late FakePlatform platform;
const String rootDir = '/';
const String revision = 'deadbeef';
late MemoryFileSystem fileSystem;
late FakeProcessManager processManager;
late TestStdio stdio;
setUp(() {
final String pathSeparator = const LocalPlatform().pathSeparator;
fileSystem = MemoryFileSystem.test();
platform = FakePlatform(
environment: <String, String>{
'HOME': <String>['path', 'to', 'home'].join(pathSeparator),
},
pathSeparator: pathSeparator,
);
processManager = FakeProcessManager.empty();
stdio = TestStdio();
});
test('canCherryPick returns true if git cherry-pick returns 0', () async {
processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
FrameworkRepository.defaultUpstream,
fileSystem.path
.join(rootDir, 'flutter_conductor_checkouts', 'framework'),
]),
const FakeCommand(command: <String>[
'git',
'checkout',
FrameworkRepository.defaultBranch,
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: revision),
const FakeCommand(command: <String>[
'git',
'status',
'--porcelain',
]),
const FakeCommand(command: <String>[
'git',
'cherry-pick',
'--no-commit',
revision,
]),
const FakeCommand(command: <String>[
'git',
'reset',
'HEAD',
'--hard',
]),
]);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(rootDir),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final Repository repository = FrameworkRepository(checkouts);
expect(await repository.canCherryPick(revision), true);
});
test('canCherryPick returns false if git cherry-pick returns non-zero', () async {
const String commit = 'abc123';
processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
FrameworkRepository.defaultUpstream,
fileSystem.path
.join(rootDir, 'flutter_conductor_checkouts', 'framework'),
]),
const FakeCommand(command: <String>[
'git',
'checkout',
FrameworkRepository.defaultBranch,
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit),
const FakeCommand(command: <String>[
'git',
'status',
'--porcelain',
]),
const FakeCommand(command: <String>[
'git',
'cherry-pick',
'--no-commit',
commit,
], exitCode: 1),
const FakeCommand(command: <String>[
'git',
'diff',
]),
const FakeCommand(command: <String>[
'git',
'reset',
'HEAD',
'--hard',
]),
]);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(rootDir),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final Repository repository = FrameworkRepository(checkouts);
expect(await repository.canCherryPick(commit), false);
});
test('cherryPick() applies the commit', () async {
const String commit = 'abc123';
processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
FrameworkRepository.defaultUpstream,
fileSystem.path
.join(rootDir, 'flutter_conductor_checkouts', 'framework'),
]),
const FakeCommand(command: <String>[
'git',
'checkout',
FrameworkRepository.defaultBranch,
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit),
const FakeCommand(command: <String>[
'git',
'status',
'--porcelain',
]),
const FakeCommand(command: <String>[
'git',
'cherry-pick',
commit,
]),
]);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(rootDir),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final Repository repository = FrameworkRepository(checkouts);
await repository.cherryPick(commit);
expect(processManager.hasRemainingExpectations, false);
});
test('updateDartRevision() updates the DEPS file', () async {
const String previousDartRevision = '171876a4e6cf56ee6da1f97d203926bd7afda7ef';
const String nextDartRevision = 'f6c91128be6b77aef8351e1e3a9d07c85bc2e46e';
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(rootDir),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final EngineRepository repo = EngineRepository(checkouts);
final File depsFile = fileSystem.file('/DEPS');
depsFile.writeAsStringSync(generateMockDeps(previousDartRevision));
await repo.updateDartRevision(nextDartRevision, depsFile: depsFile);
final String updatedDepsFileContent = depsFile.readAsStringSync();
expect(updatedDepsFileContent, generateMockDeps(nextDartRevision));
});
test('updateDartRevision() throws exception on malformed DEPS file', () {
const String nextDartRevision = 'f6c91128be6b77aef8351e1e3a9d07c85bc2e46e';
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(rootDir),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final EngineRepository repo = EngineRepository(checkouts);
final File depsFile = fileSystem.file('/DEPS');
depsFile.writeAsStringSync('''
vars = {
}''');
expect(
() async => repo.updateDartRevision(nextDartRevision, depsFile: depsFile),
throwsExceptionWith('Unexpected content in the DEPS file at'),
);
});
test('commit() throws if there are no local changes to commit', () {
const String commit1 = 'abc123';
const String commit2 = 'def456';
const String message = 'This is a commit message.';
processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
EngineRepository.defaultUpstream,
fileSystem.path
.join(rootDir, 'flutter_conductor_checkouts', 'engine'),
]),
const FakeCommand(command: <String>[
'git',
'checkout',
EngineRepository.defaultBranch,
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit1),
const FakeCommand(command: <String>[
'git',
'status',
'--porcelain',
]),
const FakeCommand(command: <String>[
'git',
'commit',
'--message',
message,
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit2),
]);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(rootDir),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final EngineRepository repo = EngineRepository(checkouts);
expect(
() async => repo.commit(message),
throwsExceptionWith('Tried to commit with message $message but no changes were present'),
);
});
test('commit() passes correct commit message', () async {
const String commit1 = 'abc123';
const String commit2 = 'def456';
const String message = 'This is a commit message.';
processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
EngineRepository.defaultUpstream,
fileSystem.path
.join(rootDir, 'flutter_conductor_checkouts', 'engine'),
]),
const FakeCommand(command: <String>[
'git',
'checkout',
EngineRepository.defaultBranch,
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit1),
const FakeCommand(
command: <String>['git', 'status', '--porcelain'],
stdout: 'MM path/to/file.txt',
),
const FakeCommand(command: <String>[
'git',
'commit',
'--message',
message,
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit2),
]);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(rootDir),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final EngineRepository repo = EngineRepository(checkouts);
await repo.commit(message);
expect(processManager.hasRemainingExpectations, false);
});
test('updateCandidateBranchVersion() returns false if branch is the same as version file', () async {
const String branch = 'flutter-2.15-candidate.3';
final File versionFile = fileSystem.file('/release-candidate-branch.version')..writeAsStringSync(branch);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(rootDir),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final FrameworkRepository repo = FrameworkRepository(checkouts);
final bool didUpdate = await repo.updateCandidateBranchVersion(branch, versionFile: versionFile);
expect(didUpdate, false);
});
test('updateEngineRevision() returns false if newCommit is the same as version file', () async {
const String commit1 = 'abc123';
const String commit2 = 'def456';
final File engineVersionFile = fileSystem.file('/engine.version')..writeAsStringSync(commit2);
processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
FrameworkRepository.defaultUpstream,
fileSystem.path
.join(rootDir, 'flutter_conductor_checkouts', 'framework'),
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit1),
]);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(rootDir),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final FrameworkRepository repo = FrameworkRepository(checkouts);
final bool didUpdate = await repo.updateEngineRevision(commit2, engineVersionFile: engineVersionFile);
expect(didUpdate, false);
});
test('CiYaml(file) will throw if file does not exist', () {
final File file = fileSystem.file('/non/existent/file.txt');
expect(
() => CiYaml(file),
throwsExceptionWith('Could not find the .ci.yaml file at /non/existent/file.txt'),
);
});
test('framework repo set as localUpstream ensures requiredLocalBranches exist locally', () async {
const String commit = 'deadbeef';
const String candidateBranch = 'flutter-1.2-candidate.3';
bool createdCandidateBranch = false;
processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
FrameworkRepository.defaultUpstream,
fileSystem.path.join(rootDir, 'flutter_conductor_checkouts', 'framework'),
]),
FakeCommand(
command: const <String>['git', 'checkout', candidateBranch, '--'],
onRun: () => createdCandidateBranch = true,
),
const FakeCommand(
command: <String>['git', 'checkout', 'stable', '--'],
),
const FakeCommand(
command: <String>['git', 'checkout', 'beta', '--'],
),
const FakeCommand(
command: <String>['git', 'checkout', FrameworkRepository.defaultBranch, '--'],
),
const FakeCommand(
command: <String>['git', 'checkout', FrameworkRepository.defaultBranch],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: commit,
),
]);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(rootDir),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final Repository repo = FrameworkRepository(
checkouts,
additionalRequiredLocalBranches: <String>[candidateBranch],
localUpstream: true,
);
// call this so that repo.lazilyInitialize() is called.
await repo.checkoutDirectory;
expect(processManager.hasRemainingExpectations, false);
expect(createdCandidateBranch, true);
});
test('engine repo set as localUpstream ensures requiredLocalBranches exist locally', () async {
const String commit = 'deadbeef';
const String candidateBranch = 'flutter-1.2-candidate.3';
bool createdCandidateBranch = false;
processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
EngineRepository.defaultUpstream,
fileSystem.path.join(rootDir, 'flutter_conductor_checkouts', 'engine'),
]),
FakeCommand(
command: const <String>['git', 'checkout', candidateBranch, '--'],
onRun: () => createdCandidateBranch = true,
),
const FakeCommand(
command: <String>['git', 'checkout', EngineRepository.defaultBranch],
),
const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'],
stdout: commit,
),
]);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(rootDir),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final Repository repo = EngineRepository(
checkouts,
additionalRequiredLocalBranches: <String>[candidateBranch],
localUpstream: true,
);
// call this so that repo.lazilyInitialize() is called.
await repo.checkoutDirectory;
expect(processManager.hasRemainingExpectations, false);
expect(createdCandidateBranch, true);
});
test('.listRemoteBranches() parses git output', () async {
const String remoteName = 'mirror';
const String lsRemoteOutput = '''
Extraneous debug information that should be ignored.
4d44dca340603e25d4918c6ef070821181202e69 refs/heads/experiment
35185330c6af3a435f615ee8ac2fed8b8bb7d9d4 refs/heads/feature-a
6f60a1e7b2f3d2c2460c9dc20fe54d0e9654b131 refs/heads/feature-b
c1436c42c0f3f98808ae767e390c3407787f1a67 refs/heads/fix_bug_1234
bbbcae73699263764ad4421a4b2ca3952a6f96cb refs/heads/stable
Extraneous debug information that should be ignored.
''';
processManager.addCommands(const <FakeCommand>[
FakeCommand(command: <String>[
'git',
'clone',
'--origin',
'upstream',
'--',
EngineRepository.defaultUpstream,
'${rootDir}flutter_conductor_checkouts/engine',
]),
FakeCommand(command: <String>[
'git',
'checkout',
'main',
]),
FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: revision),
FakeCommand(
command: <String>['git', 'ls-remote', '--heads', remoteName],
stdout: lsRemoteOutput,
),
]);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(rootDir),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final Repository repo = EngineRepository(
checkouts,
localUpstream: true,
);
final List<String> branchNames = await repo.listRemoteBranches(remoteName);
expect(branchNames, equals(<String>[
'experiment',
'feature-a',
'feature-b',
'fix_bug_1234',
'stable',
]));
});
});
}
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',
}''';
}