flutter/dev/tools/test/codesign_test.dart
Jonah Williams 8b6baae44c
[flutter_tools] move process manager into tool (#75350)
Our current top crasher is an unclear error when ProcessManager fails to resolve an executable path. To fix this, we'd like to being adjusting the process resolution logic and adding more instrumentation to track failures. In order to begin the process, the ProcessManager has been folded back into the flutter tool
2021-02-04 13:19:11 -08:00

417 lines
13 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.
import 'package:args/command_runner.dart';
import 'package:dev_tools/codesign.dart';
import 'package:dev_tools/globals.dart';
import 'package:dev_tools/repository.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import './common.dart';
import 'fake_process_manager.dart';
void main() {
group('codesign command', () {
const String flutterRoot = '/flutter';
const String checkoutsParentDirectory = '$flutterRoot/dev/tools/';
const String flutterCache =
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework/bin/cache';
const String flutterBin =
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework/bin/flutter';
const String revision = 'abcd1234';
CommandRunner<void> runner;
Checkouts checkouts;
MemoryFileSystem fileSystem;
FakePlatform platform;
TestStdio stdio;
FakeProcessManager processManager;
const List<String> binariesWithEntitlements = <String>[
'$flutterCache/dart-sdk/bin/dart',
'$flutterCache/dart-sdk/bin/dartaotruntime',
];
const List<String> binariesWithoutEntitlements = <String>[
'$flutterCache/engine/darwin-x64/font-subset',
];
const List<String> allBinaries = <String>[
...binariesWithEntitlements,
...binariesWithoutEntitlements,
];
void createRunner({
String operatingSystem = 'macos',
List<FakeCommand> commands,
}) {
stdio = TestStdio();
fileSystem = MemoryFileSystem.test();
platform = FakePlatform(operatingSystem: operatingSystem);
processManager = FakeProcessManager.list(commands ?? <FakeCommand>[]);
checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(checkoutsParentDirectory),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final FakeCodesignCommand command = FakeCodesignCommand(
checkouts: checkouts,
binariesWithEntitlements: binariesWithEntitlements,
binariesWithoutEntitlements: binariesWithoutEntitlements,
flutterRoot: fileSystem.directory(flutterRoot),
);
runner = CommandRunner<void>('codesign-test', '')
..addCommand(command);
}
test('throws exception if not run from macos', () async {
createRunner(operatingSystem: 'linux');
expect(
() async => await runner.run(<String>['codesign']),
throwsExceptionWith('Error! Expected operating system "macos"'),
);
});
test('throws exception if verify flag is not provided', () async {
createRunner();
expect(
() async => await runner.run(<String>['codesign']),
throwsExceptionWith(
'Sorry, but codesigning is not implemented yet. Please pass the --$kVerify flag to verify signatures'),
);
});
test('succeeds if every binary is codesigned and has correct entitlements', () async {
final List<FakeCommand> codesignCheckCommands = <FakeCommand>[];
for (final String bin in binariesWithEntitlements) {
codesignCheckCommands.add(
FakeCommand(
command: <String>['codesign', '-vvv', bin],
),
);
codesignCheckCommands.add(
FakeCommand(
command: <String>['codesign', '--display', '--entitlements', ':-', bin],
stdout: expectedEntitlements.join('\n'),
),
);
}
for (final String bin in binariesWithoutEntitlements) {
codesignCheckCommands.add(
FakeCommand(
command: <String>['codesign', '-vvv', bin],
),
);
}
createRunner(commands: <FakeCommand>[
const FakeCommand(command: <String>[
'git',
'clone',
'--',
'file://$flutterRoot/',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: revision),
const FakeCommand(command: <String>[
'git',
'checkout',
revision,
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'precache',
'--android',
'--ios',
'--macos',
]),
FakeCommand(
command: const <String>[
'find',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework/bin/cache',
'-type',
'f',
],
stdout: allBinaries.join('\n'),
),
for (String bin in allBinaries)
FakeCommand(
command: <String>['file', '--mime-type', '-b', bin],
stdout: 'application/x-mach-binary',
),
...codesignCheckCommands,
]);
await runner.run(<String>['codesign', '--$kVerify', '--$kRevision', revision]);
expect(processManager.hasRemainingExpectations, false);
});
test('fails if a single binary is not codesigned', () async {
final List<FakeCommand> codesignCheckCommands = <FakeCommand>[];
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '-vvv', '$flutterCache/dart-sdk/bin/dart'],
),
);
codesignCheckCommands.add(
FakeCommand(
command: const <String>[
'codesign',
'--display',
'--entitlements',
':-',
'$flutterCache/dart-sdk/bin/dart',
],
stdout: expectedEntitlements.join('\n'),
)
);
// Not signed
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '-vvv', '$flutterCache/dart-sdk/bin/dartaotruntime'],
exitCode: 1,
),
);
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '-vvv', '$flutterCache/engine/darwin-x64/font-subset'],
),
);
createRunner(commands: <FakeCommand>[
const FakeCommand(command: <String>[
'git',
'clone',
'--',
'file://$flutterRoot/',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: revision),
const FakeCommand(command: <String>[
'git',
'checkout',
revision,
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'precache',
'--android',
'--ios',
'--macos',
]),
FakeCommand(
command: const <String>[
'find',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework/bin/cache',
'-type',
'f',
],
stdout: allBinaries.join('\n'),
),
for (String bin in allBinaries)
FakeCommand(
command: <String>['file', '--mime-type', '-b', bin],
stdout: 'application/x-mach-binary',
),
...codesignCheckCommands,
]);
expect(
() async => await runner.run(<String>['codesign', '--$kVerify', '--$kRevision', revision]),
throwsExceptionWith('Test failed because unsigned binaries detected.'),
);
expect(processManager.hasRemainingExpectations, false);
});
test('fails if a single binary has the wrong entitlements', () async {
final List<FakeCommand> codesignCheckCommands = <FakeCommand>[];
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '-vvv', '$flutterCache/dart-sdk/bin/dart'],
),
);
codesignCheckCommands.add(
FakeCommand(
command: const <String>['codesign', '--display', '--entitlements', ':-', '$flutterCache/dart-sdk/bin/dart'],
stdout: expectedEntitlements.join('\n'),
)
);
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '-vvv', '$flutterCache/dart-sdk/bin/dartaotruntime'],
),
);
// No entitlements
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '--display', '--entitlements', ':-', '$flutterCache/dart-sdk/bin/dartaotruntime'],
)
);
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '-vvv', '$flutterCache/engine/darwin-x64/font-subset'],
),
);
createRunner(commands: <FakeCommand>[
const FakeCommand(command: <String>[
'git',
'clone',
'--',
'file://$flutterRoot/',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: revision),
const FakeCommand(command: <String>[
'git',
'checkout',
revision,
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'precache',
'--android',
'--ios',
'--macos',
]),
FakeCommand(
command: const <String>[
'find',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework/bin/cache',
'-type',
'f',
],
stdout: allBinaries.join('\n'),
),
for (String bin in allBinaries)
FakeCommand(
command: <String>['file', '--mime-type', '-b', bin],
stdout: 'application/x-mach-binary',
),
...codesignCheckCommands,
]);
expect(
() async => await runner.run(<String>['codesign', '--$kVerify', '--$kRevision', revision]),
throwsExceptionWith('Test failed because files found with the wrong entitlements'),
);
expect(processManager.hasRemainingExpectations, false);
});
test('does not check signatures or entitlements if --no-$kSignatures specified', () async {
createRunner(commands: <FakeCommand>[
const FakeCommand(command: <String>[
'git',
'clone',
'--',
'file://$flutterRoot/',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: revision),
const FakeCommand(command: <String>[
'git',
'checkout',
revision,
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'precache',
'--android',
'--ios',
'--macos',
]),
FakeCommand(
command: const <String>[
'find',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework/bin/cache',
'-type',
'f',
],
stdout: allBinaries.join('\n'),
),
for (String bin in allBinaries)
FakeCommand(
command: <String>['file', '--mime-type', '-b', bin],
stdout: 'application/x-mach-binary',
),
]);
try {
await runner.run(<String>[
'codesign',
'--$kVerify',
'--no-$kSignatures',
'--$kRevision',
revision,
]);
} on ConductorException {
//print(stdio.error);
rethrow;
}
expect(
processManager.hasRemainingExpectations,
false,
);
});
});
}
class FakeCodesignCommand extends CodesignCommand {
FakeCodesignCommand({
@required Checkouts checkouts,
@required this.binariesWithEntitlements,
@required this.binariesWithoutEntitlements,
@required Directory flutterRoot,
}) : super(checkouts: checkouts, flutterRoot: flutterRoot);
@override
final List<String> binariesWithEntitlements;
@override
final List<String> binariesWithoutEntitlements;
}