Remove outdated Fuchsia concepts (#107335)

Fuchsia will soon remove all support for Component Framework version 1
components (recognized by component manifests ending in `.cmx`).
Notably, some of the `flutter` tool commands for Fuchsia devices--
notably, but not limited to, those related to CFv1--are outdated, and
either do not work today or soon won't work.

This PR removes the outdated components and commands, replacing some
with the newer version, or simply removing the non-working features,
in some cases.
This commit is contained in:
Rich Kadel 2022-07-22 16:42:48 -07:00 committed by GitHub
parent a3b531ddc9
commit 549f70c914
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 647 additions and 1361 deletions

View File

@ -1,22 +0,0 @@
{
"program": {
"data": "data/complex_layout"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.modular.ContextWriter",
"fuchsia.modular.DeviceMap",
"fuchsia.modular.ModuleContext",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.testing.runner.TestRunner",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}

View File

@ -1,22 +0,0 @@
{
"program": {
"data": "data/stocks"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.modular.ContextWriter",
"fuchsia.modular.DeviceMap",
"fuchsia.modular.ModuleContext",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.testing.runner.TestRunner",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}

View File

@ -1,18 +0,0 @@
{
"program": {
"data": "data/flutter_gallery"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}

View File

@ -1,22 +0,0 @@
{
"program": {
"data": "data/flutter_gallery"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.modular.ContextWriter",
"fuchsia.modular.DeviceMap",
"fuchsia.modular.ModuleContext",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.testing.runner.TestRunner",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}

View File

@ -1,22 +0,0 @@
{
"program": {
"data": "data/integration_ui"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.modular.ContextWriter",
"fuchsia.modular.DeviceMap",
"fuchsia.modular.ModuleContext",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.testing.runner.TestRunner",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}

View File

@ -1,18 +0,0 @@
{
"program": {
"data": "data/hello_world"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}

View File

@ -14,7 +14,6 @@ import 'build_aar.dart';
import 'build_apk.dart';
import 'build_appbundle.dart';
import 'build_bundle.dart';
import 'build_fuchsia.dart';
import 'build_ios.dart';
import 'build_ios_framework.dart';
import 'build_macos_framework.dart';
@ -43,7 +42,6 @@ class BuildCommand extends FlutterCommand {
verboseHelp: verboseHelp
));
_addSubcommand(BuildWindowsCommand(verboseHelp: verboseHelp));
_addSubcommand(BuildFuchsiaCommand(verboseHelp: verboseHelp));
}
void _addSubcommand(BuildSubCommand command) {
@ -83,8 +81,11 @@ abstract class BuildSubCommand extends FlutterCommand {
@protected
void displayNullSafetyMode(BuildInfo buildInfo) {
globals.printStatus('');
if (buildInfo.nullSafetyMode == NullSafetyMode.sound) {
globals.printStatus('💪 Building with sound null safety 💪', emphasis: true);
if (buildInfo.nullSafetyMode == NullSafetyMode.sound) {
globals.printStatus(
'💪 Building with sound null safety 💪',
emphasis: true,
);
} else {
globals.printStatus(
'Building without sound null safety',

View File

@ -1,88 +0,0 @@
// 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 '../base/common.dart';
import '../build_info.dart';
import '../cache.dart';
import '../features.dart';
import '../fuchsia/fuchsia_build.dart';
import '../fuchsia/fuchsia_pm.dart';
import '../globals.dart' as globals;
import '../project.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult;
import 'build.dart';
/// A command to build a Fuchsia target.
class BuildFuchsiaCommand extends BuildSubCommand {
BuildFuchsiaCommand({
required bool verboseHelp,
}) : super(verboseHelp: verboseHelp) {
addTreeShakeIconsFlag();
usesTargetOption();
usesDartDefineOption();
addBuildModeFlags(verboseHelp: verboseHelp);
addNullSafetyModeOptions(hide: !verboseHelp);
addEnableExperimentation(hide: !verboseHelp);
argParser.addOption(
'runner-source',
help: 'The package source to use for the flutter_runner. '
'"${FuchsiaPackageServer.deviceHost}" implies using a runner already on the device. '
'"${FuchsiaPackageServer.toolHost}" implies using a runner distributed with Flutter.',
allowed: <String>[
FuchsiaPackageServer.deviceHost,
FuchsiaPackageServer.toolHost,
],
defaultsTo: FuchsiaPackageServer.toolHost,
);
argParser.addOption('target-platform',
defaultsTo: 'fuchsia-x64',
allowed: <String>['fuchsia-arm64', 'fuchsia-x64'],
help: 'The target platform for which the app is compiled.',
);
}
@override
final String name = 'fuchsia';
@override
bool hidden = true;
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => <DevelopmentArtifact>{
DevelopmentArtifact.fuchsia,
};
@override
String get description => 'Build the Fuchsia target (Experimental).';
@override
bool get supported => globals.platform.isLinux || globals.platform.isMacOS;
@override
Future<FlutterCommandResult> runCommand() async {
if (!featureFlags.isFuchsiaEnabled) {
throwToolExit(
'"build fuchsia" is currently disabled. See "flutter config" for more '
'information.'
);
}
final BuildInfo buildInfo = await getBuildInfo();
final FlutterProject flutterProject = FlutterProject.current();
if (!supported) {
throwToolExit('"build fuchsia" is only supported on Linux and MacOS hosts.');
}
if (!flutterProject.fuchsia.existsSync()) {
throwToolExit('No Fuchsia project is configured.');
}
displayNullSafetyMode(buildInfo);
await buildFuchsia(
fuchsiaProject: flutterProject.fuchsia,
target: targetFile,
targetPlatform: getTargetPlatformForName(stringArgDeprecated('target-platform')!),
buildInfo: buildInfo,
runnerPackageSource: stringArgDeprecated('runner-source')!,
);
return FlutterCommandResult.success();
}
}

View File

@ -1,230 +0,0 @@
// 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 '../artifacts.dart';
import '../asset.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../bundle_builder.dart';
import '../convert.dart';
import '../devfs.dart';
import '../globals.dart' as globals;
import '../project.dart';
import 'fuchsia_pm.dart';
Future<void> _timedBuildStep(String name, Future<void> Function() action) async {
final Stopwatch sw = Stopwatch()..start();
await action();
globals.printTrace('$name: ${sw.elapsedMilliseconds} ms.');
globals.flutterUsage.sendTiming('build', name, Duration(milliseconds: sw.elapsedMilliseconds));
}
Future<void> _validateCmxFile(FuchsiaProject fuchsiaProject) async {
final String appName = fuchsiaProject.project.manifest.appName;
final String cmxPath = globals.fs.path.join(fuchsiaProject.meta.path, '$appName.cmx');
final File cmxFile = globals.fs.file(cmxPath);
if (!await cmxFile.exists()) {
throwToolExit('The Fuchsia build requires a .cmx file at $cmxPath for the app: $appName.');
}
}
// Building a Fuchsia package has a few steps:
// 1. Do the custom kernel compile using the kernel compiler from the Fuchsia
// SDK. This produces .dilp files (among others) and a manifest file.
// 2. Create a manifest file for assets.
// 3. Using these manifests, use the Fuchsia SDK 'pm' tool to create the
// Fuchsia package.
Future<void> buildFuchsia({
required FuchsiaProject fuchsiaProject,
required TargetPlatform targetPlatform,
String? target, // E.g., lib/main.dart
BuildInfo buildInfo = BuildInfo.debug,
String runnerPackageSource = FuchsiaPackageServer.toolHost,
}) async {
final String targetPath = target ??= 'lib/main.dart';
await _validateCmxFile(fuchsiaProject);
final Directory outDir = globals.fs.directory(getFuchsiaBuildDirectory());
if (!outDir.existsSync()) {
outDir.createSync(recursive: true);
}
await _timedBuildStep('fuchsia-kernel-compile',
() => globals.fuchsiaSdk!.fuchsiaKernelCompiler.build(
fuchsiaProject: fuchsiaProject, target: targetPath, buildInfo: buildInfo));
if (buildInfo.usesAot) {
await _timedBuildStep('fuchsia-gen-snapshot',
() => _genSnapshot(fuchsiaProject, targetPath, buildInfo, targetPlatform));
}
await _timedBuildStep('fuchsia-build-assets',
() => _buildAssets(fuchsiaProject, targetPath, buildInfo));
await _timedBuildStep('fuchsia-build-package',
() => _buildPackage(fuchsiaProject, targetPath, buildInfo, runnerPackageSource));
}
Future<void> _genSnapshot(
FuchsiaProject fuchsiaProject,
String target, // lib/main.dart
BuildInfo buildInfo,
TargetPlatform targetPlatform,
) async {
final String outDir = getFuchsiaBuildDirectory();
final String appName = fuchsiaProject.project.manifest.appName;
final String dilPath = globals.fs.path.join(outDir, '$appName.dil');
final String elf = globals.fs.path.join(outDir, 'elf.aotsnapshot');
final String genSnapshot = globals.artifacts!.getArtifactPath(
Artifact.genSnapshot,
platform: targetPlatform,
mode: buildInfo.mode,
);
final List<String> command = <String>[
genSnapshot,
'--deterministic',
'--snapshot_kind=app-aot-elf',
'--elf=$elf',
if (buildInfo.isDebug) '--enable-asserts',
dilPath,
];
int result;
final Status status = globals.logger.startProgress(
'Compiling Fuchsia application to native code...',
);
try {
result = await globals.processUtils.stream(command, trace: true);
} finally {
status.cancel();
}
if (result != 0) {
throwToolExit('Build process failed');
}
}
Future<void> _buildAssets(
FuchsiaProject fuchsiaProject,
String target, // lib/main.dart
BuildInfo buildInfo,
) async {
final String assetDir = getAssetBuildDirectory();
final AssetBundle? assets = await buildAssets(
manifestPath: fuchsiaProject.project.pubspecFile.path,
packagesPath: fuchsiaProject.project.packagesFile.path,
assetDirPath: assetDir,
);
if (assets == null) {
throwToolExit('Unable to find assets.', exitCode: 1);
}
final Map<String, DevFSContent> assetEntries = Map<String, DevFSContent>.of(
assets.entries,
);
await writeBundle(globals.fs.directory(assetDir), assetEntries, assets.entryKinds);
final String appName = fuchsiaProject.project.manifest.appName;
final String outDir = getFuchsiaBuildDirectory();
final String assetManifest = globals.fs.path.join(outDir, '${appName}_pkgassets');
final File destFile = globals.fs.file(assetManifest);
await destFile.create(recursive: true);
final IOSink outFile = destFile.openWrite();
for (final String path in assets.entries.keys) {
outFile.write('data/$appName/$path=$assetDir/$path\n');
}
await outFile.flush();
await outFile.close();
}
void _rewriteCmx(BuildMode mode, String runnerPackageSource, File src, File dst) {
final Map<String, Object?> cmx = castStringKeyedMap(json.decode(src.readAsStringSync())) ?? <String, Object?>{};
// If the app author has already specified the runner in the cmx file, then
// do not override it with something else.
if (cmx.containsKey('runner')) {
dst.writeAsStringSync(json.encode(cmx));
return;
}
String runner;
switch (mode) {
case BuildMode.debug:
runner = 'flutter_jit_runner';
break;
case BuildMode.profile:
runner = 'flutter_aot_runner';
break;
case BuildMode.jitRelease:
runner = 'flutter_jit_product_runner';
break;
case BuildMode.release:
runner = 'flutter_aot_product_runner';
break;
default:
throwToolExit('Fuchsia does not support build mode "$mode"');
}
cmx['runner'] = 'fuchsia-pkg://$runnerPackageSource/$runner#meta/$runner.cmx';
dst.writeAsStringSync(json.encode(cmx));
}
// TODO(zanderso): Allow supplying a signing key.
Future<void> _buildPackage(
FuchsiaProject fuchsiaProject,
String target, // lib/main.dart
BuildInfo buildInfo,
String runnerPackageSource,
) async {
final String outDir = getFuchsiaBuildDirectory();
final String pkgDir = globals.fs.path.join(outDir, 'pkg');
final String appName = fuchsiaProject.project.manifest.appName;
final String pkgassets = globals.fs.path.join(outDir, '${appName}_pkgassets');
final String packageManifest = globals.fs.path.join(pkgDir, 'package_manifest');
final Directory pkg = globals.fs.directory(pkgDir);
if (!pkg.existsSync()) {
pkg.createSync(recursive: true);
}
final File srcCmx =
globals.fs.file(globals.fs.path.join(fuchsiaProject.meta.path, '$appName.cmx'));
final File dstCmx = globals.fs.file(globals.fs.path.join(outDir, '$appName.cmx'));
_rewriteCmx(buildInfo.mode, runnerPackageSource, srcCmx, dstCmx);
final File manifestFile = globals.fs.file(packageManifest);
if (buildInfo.usesAot) {
final String elf = globals.fs.path.join(outDir, 'elf.aotsnapshot');
manifestFile.writeAsStringSync(
'data/$appName/app_aot_snapshot.so=$elf\n');
} else {
final String dilpmanifest = globals.fs.path.join(outDir, '$appName.dilpmanifest');
manifestFile.writeAsStringSync(globals.fs.file(dilpmanifest).readAsStringSync());
}
manifestFile.writeAsStringSync(globals.fs.file(pkgassets).readAsStringSync(),
mode: FileMode.append);
manifestFile.writeAsStringSync('meta/$appName.cmx=${dstCmx.path}\n',
mode: FileMode.append);
manifestFile.writeAsStringSync('meta/package=$pkgDir/meta/package\n',
mode: FileMode.append);
final FuchsiaPM? fuchsiaPM = globals.fuchsiaSdk?.fuchsiaPM;
if (fuchsiaPM == null) {
return;
}
if (!await fuchsiaPM.init(pkgDir, appName)) {
return;
}
if (!await fuchsiaPM.build(pkgDir, packageManifest)) {
return;
}
if (!await fuchsiaPM.archive(pkgDir, packageManifest)) {
return;
}
}

View File

@ -23,16 +23,15 @@ import '../device.dart';
import '../device_port_forwarder.dart';
import '../globals.dart' as globals;
import '../project.dart';
import '../runner/flutter_command.dart';
import '../vmservice.dart';
import 'application_package.dart';
import 'fuchsia_build.dart';
import 'fuchsia_ffx.dart';
import 'fuchsia_pm.dart';
import 'fuchsia_sdk.dart';
import 'fuchsia_workflow.dart';
import 'pkgctl.dart';
import 'session_control.dart';
import 'tiles_ctl.dart';
/// The [FuchsiaDeviceTools] instance.
FuchsiaDeviceTools get fuchsiaDeviceTools => context.get<FuchsiaDeviceTools>()!;
@ -40,8 +39,7 @@ FuchsiaDeviceTools get fuchsiaDeviceTools => context.get<FuchsiaDeviceTools>()!;
/// Fuchsia device-side tools.
class FuchsiaDeviceTools {
late final FuchsiaPkgctl pkgctl = FuchsiaPkgctl();
late final FuchsiaTilesCtl tilesCtl = FuchsiaTilesCtl();
late final FuchsiaSessionControl sessionControl = FuchsiaSessionControl();
late final FuchsiaFfx ffx = FuchsiaFfx();
}
final String _ipv4Loopback = InternetAddress.loopbackIPv4.address;
@ -100,7 +98,7 @@ class _FuchsiaLogReader extends DeviceLogReader {
final ApplicationPackage? app = _app;
final RegExp matchRegExp = app == null
? _flutterLogOutput
: RegExp('INFO: ${app.name}(\\.cmx)?\\(flutter\\): ');
: RegExp('INFO: ${app.name}(\\.cm)?\\(flutter\\): ');
return Stream<String>.eventTransformed(
lines,
(EventSink<String> output) => _FuchsiaLogSink(output, matchRegExp, startTime),
@ -212,7 +210,7 @@ class FuchsiaDevices extends PollingDeviceDiscovery {
}
final String name = words[1];
// TODO(omerlevran): Add support for resolve on the FuchsiaSdk Object.
// TODO(omerlevran): Add support for resolve on the FuchsiaSdk Object.
final String? resolvedHost = await _fuchsiaSdk.fuchsiaFfx.resolve(name);
if (resolvedHost == null) {
_logger.printError('Failed to resolve host for Fuchsia device `$name`');
@ -225,13 +223,13 @@ class FuchsiaDevices extends PollingDeviceDiscovery {
List<String> get wellKnownIds => const <String>[];
}
class FuchsiaDevice extends Device {
FuchsiaDevice(super.id, {required this.name}) : super(
platformType: PlatformType.fuchsia,
category: null,
ephemeral: true,
);
FuchsiaDevice(super.id, {required this.name})
: super(
platformType: PlatformType.fuchsia,
category: null,
ephemeral: true,
);
@override
bool get supportsHotReload => true;
@ -258,14 +256,11 @@ class FuchsiaDevice extends Device {
/// Determine if the Fuchsia device is running a session based build.
///
/// If the device is running a session based build, `session_control` should be
/// used to launch apps, otherwise `tiles_ctl` should be used.
/// If the device is running a session based build, `ffx session` should be
/// used to launch apps. Fuchsia flutter apps cannot currently be launched
/// without a session.
Future<bool> _initIsSession() async {
final RunResult result = await shell('which session_control');
if (result.exitCode != 0) {
return false;
}
return true;
return await globals.fuchsiaSdk?.fuchsiaFfx.sessionShow() != null;
}
@override
@ -293,7 +288,8 @@ class FuchsiaDevice extends Device {
bool isSupported() => true;
@override
bool supportsRuntimeMode(BuildMode buildMode) => buildMode != BuildMode.jitRelease;
bool supportsRuntimeMode(BuildMode buildMode) =>
buildMode != BuildMode.jitRelease;
@override
Future<LaunchResult> startApp(
@ -313,10 +309,11 @@ class FuchsiaDevice extends Device {
}
if (!prebuiltApplication) {
await buildFuchsia(fuchsiaProject: FlutterProject.current().fuchsia,
targetPlatform: await targetPlatform,
target: mainPath,
buildInfo: debuggingOptions.buildInfo);
throwToolExit(
'This tool does not currently build apps for fuchsia.\n'
'Build the app using a supported Fuchsia workflow.\n'
'Then use the --${FlutterOptions.kUseApplicationBinary} flag.'
);
}
// Stop the app if it's currently running.
await stopApp(package);
@ -330,8 +327,8 @@ class FuchsiaDevice extends Device {
// Try Start with a fresh package repo in case one was left over from a
// previous run.
final Directory packageRepo =
globals.fs.directory(globals.fs.path.join(getFuchsiaBuildDirectory(), '.pkg-repo'));
final Directory packageRepo = globals.fs.directory(
globals.fs.path.join(getFuchsiaBuildDirectory(), '.pkg-repo'));
try {
if (packageRepo.existsSync()) {
packageRepo.deleteSync(recursive: true);
@ -339,7 +336,7 @@ class FuchsiaDevice extends Device {
packageRepo.createSync(recursive: true);
} on Exception catch (e) {
globals.printError('Failed to create Fuchsia package repo directory '
'at ${packageRepo.path}: $e');
'at ${packageRepo.path}: $e');
return LaunchResult.failed();
}
@ -419,29 +416,20 @@ class FuchsiaDevice extends Device {
return LaunchResult.failed();
}
fuchsiaUrl =
'fuchsia-pkg://$packageServerName/$appName#meta/$appName.cmx';
fuchsiaUrl = 'fuchsia-pkg://$packageServerName/$appName#meta/$appName.cm';
if (await isSession) {
// Instruct session_control to start the app
if (!await fuchsiaDeviceTools.sessionControl.add(this, fuchsiaUrl)) {
globals.printError('Failed to add the app to session_control');
// Instruct ffx session to start the app
final bool addedApp =
await globals.fuchsiaSdk?.fuchsiaFfx.sessionAdd(fuchsiaUrl) ?? false;
if (!addedApp) {
globals.printError('Failed to add the app via `ffx session add`');
return LaunchResult.failed();
}
} else {
// Ensure tiles_ctl is started, and start the app.
if (!await FuchsiaTilesCtl.ensureStarted(this)) {
globals.printError(
'Failed to ensure that tiles is started on the device');
return LaunchResult.failed();
}
// Instruct tiles_ctl to start the app.
if (!await fuchsiaDeviceTools.tilesCtl
.add(this, fuchsiaUrl, <String>[])) {
globals.printError('Failed to add the app to tiles');
return LaunchResult.failed();
}
globals.printError(
'Fuchsia flutter apps can only be launched within a session');
return LaunchResult.failed();
}
} finally {
// Try to un-teach the package controller about the package server if
@ -491,13 +479,8 @@ class FuchsiaDevice extends Device {
// using the session framework afaik. So this is a no-op.
return true;
}
final int appKey = await FuchsiaTilesCtl.findAppKey(this, app.id);
if (appKey != -1) {
if (!await fuchsiaDeviceTools.tilesCtl.remove(this, appKey)) {
globals.printError('tiles_ctl remove on ${app.id} failed.');
return false;
}
}
// Fuchsia flutter apps currently require a session, but if that changes,
// add the relevant "stopApp" code here.
return true;
}
@ -537,12 +520,15 @@ class FuchsiaDevice extends Device {
if (outputFile.basename.split('.').last != 'ppm') {
throw Exception('${outputFile.path} must be a .ppm file');
}
final RunResult screencapResult = await shell('screencap > /tmp/screenshot.ppm');
final RunResult screencapResult =
await shell('screencap > /tmp/screenshot.ppm');
if (screencapResult.exitCode != 0) {
throw Exception('Could not take a screenshot on device $name:\n$screencapResult');
throw Exception(
'Could not take a screenshot on device $name:\n$screencapResult');
}
try {
final RunResult scpResult = await scp('/tmp/screenshot.ppm', outputFile.path);
final RunResult scpResult =
await scp('/tmp/screenshot.ppm', outputFile.path);
if (scpResult.exitCode != 0) {
throw Exception('Failed to copy screenshot from device:\n$scpResult');
}
@ -551,13 +537,11 @@ class FuchsiaDevice extends Device {
final RunResult deleteResult = await shell('rm /tmp/screenshot.ppm');
if (deleteResult.exitCode != 0) {
globals.printError(
'Failed to delete screenshot.ppm from the device:\n$deleteResult'
);
'Failed to delete screenshot.ppm from the device:\n$deleteResult');
}
} on Exception catch (e) {
globals.printError(
'Failed to delete screenshot.ppm from the device: $e'
);
globals
.printError('Failed to delete screenshot.ppm from the device: $e');
}
}
}
@ -570,7 +554,7 @@ class FuchsiaDevice extends Device {
const String defaultName = 'Fuchsia';
if (!globals.fuchsiaArtifacts!.hasSshConfig) {
globals.printTrace('Could not determine Fuchsia sdk name or version '
'because Fuchsia ssh configuration is missing.');
'because Fuchsia ssh configuration is missing.');
return defaultName;
}
const String versionPath = '/pkgfs/packages/build-info/0/data/version';
@ -595,6 +579,7 @@ class FuchsiaDevice extends Device {
assert(!includePastLogs, 'Past log reading not supported on Fuchsia.');
return _logReader ??= _FuchsiaLogReader(this, globals.systemClock, app);
}
_FuchsiaLogReader? _logReader;
@override
@ -620,6 +605,7 @@ class FuchsiaDevice extends Device {
void fail() {
throwToolExit('Failed to get local address, aborting.\n$result');
}
if (result.exitCode != 0) {
fail();
}
@ -639,7 +625,8 @@ class FuchsiaDevice extends Device {
const String findCommand = 'find /hub -name vmservice-port';
final RunResult findResult = await shell(findCommand);
if (findResult.exitCode != 0) {
throwToolExit("'$findCommand' on device $name failed. stderr: '${findResult.stderr}'");
throwToolExit(
"'$findCommand' on device $name failed. stderr: '${findResult.stderr}'");
}
final String findOutput = findResult.stdout;
if (findOutput.trim() == '') {
@ -675,7 +662,7 @@ class FuchsiaDevice extends Device {
final File? sshConfig = globals.fuchsiaArtifacts?.sshConfig;
if (sshConfig == null) {
throwToolExit('Cannot interact with device. No ssh config.\n'
'Try setting FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR.');
'Try setting FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR.');
}
return globals.processUtils.run(<String>[
'ssh',
@ -691,7 +678,7 @@ class FuchsiaDevice extends Device {
final File? sshConfig = globals.fuchsiaArtifacts!.sshConfig;
if (sshConfig == null) {
throwToolExit('Cannot interact with device. No ssh config.\n'
'Try setting FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR.');
'Try setting FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR.');
}
return globals.processUtils.run(<String>[
'scp',
@ -714,15 +701,18 @@ class FuchsiaDevice extends Device {
// netstat shows that the local port is actually being used on the IPv6
// loopback (::1).
final Uri uri = Uri.parse('http://[$_ipv6Loopback]:$port');
final FlutterVmService vmService = await connectToVmService(uri, logger: globals.logger);
final List<FlutterView> flutterViews = await vmService.getFlutterViews();
final FlutterVmService vmService =
await connectToVmService(uri, logger: globals.logger);
final List<FlutterView> flutterViews =
await vmService.getFlutterViews();
for (final FlutterView flutterView in flutterViews) {
final vm_service.IsolateRef? uiIsolate = flutterView.uiIsolate;
if (uiIsolate == null) {
continue;
}
final int? port = vmService.httpAddress?.port;
if (port != null && (uiIsolate.name?.contains(isolateName) ?? false)) {
if (port != null &&
(uiIsolate.name?.contains(isolateName) ?? false)) {
return port;
}
}
@ -733,7 +723,8 @@ class FuchsiaDevice extends Device {
throwToolExit('No ports found running $isolateName');
}
FuchsiaIsolateDiscoveryProtocol getIsolateDiscoveryProtocol(String isolateName) {
FuchsiaIsolateDiscoveryProtocol getIsolateDiscoveryProtocol(
String isolateName) {
return FuchsiaIsolateDiscoveryProtocol(this, isolateName);
}
@ -776,7 +767,7 @@ class FuchsiaIsolateDiscoveryProtocol {
_status ??= globals.logger.startProgress(
'Waiting for a connection from $_isolateName on ${_device.name}...',
);
unawaited(_findIsolate()); // Completes the _foundUri Future.
unawaited(_findIsolate()); // Completes the _foundUri Future.
return _foundUri.future.then((Uri uri) {
_uri = uri;
return uri;
@ -813,7 +804,8 @@ class FuchsiaIsolateDiscoveryProtocol {
continue;
}
}
final List<FlutterView> flutterViews = await service?.getFlutterViews() ?? <FlutterView>[];
final List<FlutterView> flutterViews =
await service?.getFlutterViews() ?? <FlutterView>[];
for (final FlutterView flutterView in flutterViews) {
final vm_service.IsolateRef? uiIsolate = flutterView.uiIsolate;
if (uiIsolate == null) {
@ -848,7 +840,8 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
Future<int> forward(int devicePort, {int? hostPort}) async {
hostPort ??= await globals.os.findFreePort();
if (hostPort == 0) {
throwToolExit('Failed to forward port $devicePort. No free host-side ports');
throwToolExit(
'Failed to forward port $devicePort. No free host-side ports');
}
final File? sshConfig = globals.fuchsiaArtifacts?.sshConfig;
if (sshConfig == null) {
@ -911,7 +904,7 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
throwToolExit(
'Unforward command failed:\n'
'stdout: ${result.stdout}\n'
'stderr: ${result.stderr}'
'stderr: ${result.stderr}',
);
}
}
@ -919,7 +912,7 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
@override
Future<void> dispose() async {
final List<ForwardedPort> forwardedPortsCopy =
List<ForwardedPort>.of(forwardedPorts);
List<ForwardedPort>.of(forwardedPorts);
for (final ForwardedPort port in forwardedPortsCopy) {
await unforward(port);
}

View File

@ -8,6 +8,7 @@ import 'package:process/process.dart';
import '../base/common.dart';
import '../base/logger.dart';
import '../base/process.dart';
import '../globals.dart' as globals;
import 'fuchsia_sdk.dart';
// Usage: ffx [-c <config>] [-e <env>] [-t <target>] [-T <timeout>] [-v] [<command>] [<args>]
@ -26,17 +27,21 @@ import 'fuchsia_sdk.dart';
// config View and switch default and user configurations
// daemon Interact with/control the ffx daemon
// target Interact with a target device or emulator
// session Control the current session. See
// https://fuchsia.dev/fuchsia-src/concepts/session/introduction
// for details.
/// A simple wrapper for the Fuchsia SDK's 'ffx' tool.
class FuchsiaFfx {
FuchsiaFfx({
required FuchsiaArtifacts? fuchsiaArtifacts,
required Logger logger,
required ProcessManager processManager,
}) : _fuchsiaArtifacts = fuchsiaArtifacts,
_logger = logger,
_processUtils =
ProcessUtils(logger: logger, processManager: processManager);
FuchsiaArtifacts? fuchsiaArtifacts,
Logger? logger,
ProcessManager? processManager,
}) : _fuchsiaArtifacts = fuchsiaArtifacts ?? globals.fuchsiaArtifacts,
_logger = logger ?? globals.logger,
_processUtils = ProcessUtils(
logger: logger ?? globals.logger,
processManager: processManager ?? globals.processManager);
final FuchsiaArtifacts? _fuchsiaArtifacts;
final Logger _logger;
@ -53,8 +58,7 @@ class FuchsiaFfx {
}
final List<String> command = <String>[
ffx.path,
if (timeout != null)
...<String>['-T', '${timeout.inSeconds}'],
if (timeout != null) ...<String>['-T', '${timeout.inSeconds}'],
'target',
'list',
// TODO(akbiggs): Revert -f back to --format once we've verified that
@ -97,4 +101,50 @@ class FuchsiaFfx {
}
return result.stdout.trim();
}
/// Show information about the current session
///
/// Returns `null` if the command failed, which can be interpreted as there is
/// no usable session.
Future<String?> sessionShow() async {
final File? ffx = _fuchsiaArtifacts?.ffx;
if (ffx == null || !ffx.existsSync()) {
throwToolExit('Fuchsia ffx tool not found.');
}
final List<String> command = <String>[
ffx.path,
'session',
'show',
];
final RunResult result = await _processUtils.run(command);
if (result.exitCode != 0) {
_logger.printError('ffx failed: ${result.stderr}');
return null;
}
return result.stdout;
}
/// Add an element to the current session
///
/// [url] should be formatted as a Fuchsia-style package URL, e.g.:
/// fuchsia-pkg://fuchsia.com/flutter_gallery#meta/flutter_gallery.cmx
/// Returns true on success and false on failure.
Future<bool> sessionAdd(String url) async {
final File? ffx = _fuchsiaArtifacts?.ffx;
if (ffx == null || !ffx.existsSync()) {
throwToolExit('Fuchsia ffx tool not found.');
}
final List<String> command = <String>[
ffx.path,
'session',
'add',
url,
];
final RunResult result = await _processUtils.run(command);
if (result.exitCode != 0) {
_logger.printError('ffx failed: ${result.stderr}');
return false;
}
return true;
}
}

View File

@ -41,7 +41,7 @@ class FuchsiaPM {
/// which describe the contents of the Fuchsia package. It must also contain
/// two other entries:
///
/// meta/$APPNAME.cmx=/path/to/cmx/on/the/host/$APPNAME.cmx
/// meta/$APPNAME.cm=/path/to/cm/on/the/host/$APPNAME.cm
/// meta/package=/path/to/package/file/from/init/package
///
/// where $APPNAME is the same [appName] passed to [init], and meta/package
@ -232,12 +232,14 @@ class FuchsiaPackageServer {
if (_process == null) {
return false;
}
return (await globals.fuchsiaSdk?.fuchsiaPM.publish(_repo, package.path)) ?? false;
return (await globals.fuchsiaSdk?.fuchsiaPM.publish(_repo, package.path)) ??
false;
}
@override
String toString() {
final String p = (_process == null) ? 'stopped' : 'running ${_process?.pid}';
final String p =
(_process == null) ? 'stopped' : 'running ${_process?.pid}';
return 'FuchsiaPackageServer at $_host:$_port ($p)';
}
}

View File

@ -31,11 +31,7 @@ class FuchsiaSdk {
late final FuchsiaKernelCompiler fuchsiaKernelCompiler = FuchsiaKernelCompiler();
/// Interface to the 'ffx' tool.
late final FuchsiaFfx fuchsiaFfx = FuchsiaFfx(
fuchsiaArtifacts: globals.fuchsiaArtifacts,
logger: globals.logger,
processManager: globals.processManager,
);
late final FuchsiaFfx fuchsiaFfx = FuchsiaFfx();
/// Returns any attached devices is a newline-denominated String.
///

View File

@ -1,54 +0,0 @@
// 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 '../base/process.dart';
import 'fuchsia_device.dart';
// Usage: session_control <command> [<args>]
//
// Various operations to control sessions.
//
// Options:
// --help display usage information
//
// Commands:
// launch Launch a new session.
// restart Restart the current session.
// add Add an element to the current session.
//
// Usage: session_control launch <session_url>
//
// Launch a new session.
//
// Options:
// --help display usage information
//
// Usage: session_control restart
//
// Restart the current session.
//
// Options:
// --help display usage information
//
//
// Usage: session_control add <element_url>
//
// Add an element to the current session.
//
// Options:
// --help display usage information
/// A simple wrapper around the 'session_control' tool running on the Fuchsia device.
class FuchsiaSessionControl {
/// Instructs session_control on the device to add the app at [url] as an element.
///
/// [url] should be formatted as a Fuchsia-style package URL, e.g.:
/// fuchsia-pkg://fuchsia.com/flutter_gallery#meta/flutter_gallery.cmx
/// Returns true on success and false on failure.
Future<bool> add(FuchsiaDevice device, String url) async {
final RunResult result = await device.shell('session_control add $url');
return result.exitCode == 0;
}
}

View File

@ -1,115 +0,0 @@
// 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 '../base/process.dart';
import '../globals.dart' as globals;
import 'fuchsia_device.dart';
// Usage: tiles_ctl <command>
// Supported commands:
// start
// add [--disable-focus] <url> [<args>...]
// remove <key>
// list
// quit
/// A simple wrapper around the 'tiles_ctl' tool running on the Fuchsia device.
class FuchsiaTilesCtl {
/// Finds the key for the app called [appName], or returns -1 if it can't be
/// found.
static Future<int> findAppKey(FuchsiaDevice device, String appName) async {
final FuchsiaTilesCtl tilesCtl = fuchsiaDeviceTools.tilesCtl;
final Map<int, String>? runningApps = await tilesCtl.list(device);
if (runningApps == null) {
globals.printTrace('tiles_ctl is not running');
return -1;
}
for (final MapEntry<int, String> entry in runningApps.entries) {
if (entry.value.contains('$appName#meta')) {
return entry.key;
}
}
return -1;
}
/// Ensures that tiles is running on the device.
static Future<bool> ensureStarted(FuchsiaDevice device) async {
final FuchsiaTilesCtl tilesCtl = fuchsiaDeviceTools.tilesCtl;
final Map<int, String>? runningApps = await tilesCtl.list(device);
if (runningApps == null) {
return tilesCtl.start(device);
}
return true;
}
/// Instructs 'tiles' to start on the device.
///
/// Returns true on success and false on failure.
Future<bool> start(FuchsiaDevice device) async {
final RunResult result = await device.shell('tiles_ctl start');
return result.exitCode == 0;
}
/// Returns a mapping of tile keys to app URLs.
///
/// Returns an empty mapping if tiles_ctl is running but no apps are running.
/// Returns null if tiles_ctl is not running.
Future<Map<int, String>?> list(FuchsiaDevice device) async {
// Output of tiles_ctl list has the format:
// Found 1 tiles:
// Tile key 1 url fuchsia-pkg://fuchsia.com/stocks#meta/stocks.cmx ...
final Map<int, String> tiles = <int, String>{};
final RunResult result = await device.shell('tiles_ctl list');
if (result.exitCode != 0) {
return null;
}
// Look for evidence that tiles_ctl is not running.
if (result.stdout.contains("Couldn't find tiles component in realm")) {
return null;
}
// Find lines beginning with 'Tile'
for (final String line in result.stdout.split('\n')) {
final List<String> words = line.split(' ');
if (words.isNotEmpty && words[0] == 'Tile') {
final int? key = int.tryParse(words[2]);
if (key != null) {
final String url = words[4];
tiles[key] = url;
}
}
}
return tiles;
}
/// Instructs tiles on the device to begin running the app at [url] in a new
/// tile.
///
/// The app is passed the arguments in [args]. Flutter apps receive these
/// arguments as arguments to `main()`. [url] should be formatted as a
/// Fuchsia-style package URL, e.g.:
/// fuchsia-pkg://fuchsia.com/flutter_gallery#meta/flutter_gallery.cmx
/// Returns true on success and false on failure.
Future<bool> add(FuchsiaDevice device, String url, List<String> args) async {
final RunResult result = await device.shell(
'tiles_ctl add $url ${args.join(" ")}');
return result.exitCode == 0;
}
/// Instructs tiles on the device to remove the app with key [key].
///
/// Returns true on success and false on failure.
Future<bool> remove(FuchsiaDevice device, int key) async {
final RunResult result = await device.shell('tiles_ctl remove $key');
return result.exitCode == 0;
}
/// Instructs tiles on the device to quit.
///
/// Returns true on success and false on failure.
Future<bool> quit(FuchsiaDevice device) async {
final RunResult result = await device.shell('tiles_ctl quit');
return result.exitCode == 0;
}
}

View File

@ -1,239 +0,0 @@
// 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.
// @dart = 2.8
import 'package:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/commands/build_fuchsia.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/fuchsia/fuchsia_kernel_compiler.dart';
import 'package:flutter_tools/src/fuchsia/fuchsia_pm.dart';
import 'package:flutter_tools/src/fuchsia/fuchsia_sdk.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:meta/meta.dart';
import 'package:test/fake.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fakes.dart';
import '../../src/test_flutter_command_runner.dart';
// Defined globally for fakes to use.
FileSystem fileSystem;
void main() {
Cache.disableLocking();
final Platform linuxPlatform = FakePlatform(
environment: const <String, String>{
'FLUTTER_ROOT': '/',
},
);
final Platform windowsPlatform = FakePlatform(
operatingSystem: 'windows',
environment: const <String, String>{
'FLUTTER_ROOT': '/',
},
);
FakeFuchsiaSdk fuchsiaSdk;
setUp(() {
fuchsiaSdk = FakeFuchsiaSdk();
fileSystem = MemoryFileSystem.test();
});
group('Fuchsia build fails gracefully when', () {
testUsingContext('The feature is disabled', () async {
final BuildCommand command = BuildCommand();
fileSystem.directory('fuchsia').createSync(recursive: true);
fileSystem.file('.packages').createSync();
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file('lib/main.dart').createSync(recursive: true);
expect(
createTestCommandRunner(command).run(const <String>['build', 'fuchsia']),
throwsToolExit(message: '"build fuchsia" is currently disabled'),
);
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(),
});
testUsingContext('there is no Fuchsia project', () async {
final BuildCommand command = BuildCommand();
expect(
createTestCommandRunner(command).run(const <String>['build', 'fuchsia']),
throwsToolExit(),
);
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true),
});
testUsingContext('there is no cmx file', () async {
final BuildCommand command = BuildCommand();
fileSystem.directory('fuchsia').createSync(recursive: true);
fileSystem.file('.packages').createSync();
fileSystem.file('pubspec.yaml').createSync();
expect(
createTestCommandRunner(command).run(const <String>['build', 'fuchsia']),
throwsToolExit(),
);
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true),
});
testUsingContext('on Windows platform', () async {
final BuildCommand command = BuildCommand();
const String appName = 'app_name';
fileSystem
.file(fileSystem.path.join('fuchsia', 'meta', '$appName.cmx'))
..createSync(recursive: true)
..writeAsStringSync('{}');
fileSystem.file('.packages').createSync();
final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync();
pubspecFile.writeAsStringSync('name: $appName');
final bool supported = BuildFuchsiaCommand(verboseHelp: false).supported;
expect(
createTestCommandRunner(command).run(const <String>['build', 'fuchsia']),
supported ? throwsToolExit() : throwsA(isA<UsageException>()),
);
}, overrides: <Type, Generator>{
Platform: () => windowsPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true),
});
testUsingContext('there is no Fuchsia kernel compiler', () async {
final BuildCommand command = BuildCommand();
const String appName = 'app_name';
fileSystem
.file(fileSystem.path.join('fuchsia', 'meta', '$appName.cmx'))
..createSync(recursive: true)
..writeAsStringSync('{}');
fileSystem.file('.packages').createSync();
fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true);
final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync();
pubspecFile.writeAsStringSync('name: $appName');
expect(
createTestCommandRunner(command).run(const <String>['build', 'fuchsia']),
throwsToolExit(),
);
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true),
});
});
testUsingContext('Fuchsia build parts fit together right', () async {
final BuildCommand command = BuildCommand();
const String appName = 'app_name';
fileSystem
.file(fileSystem.path.join('fuchsia', 'meta', '$appName.cmx'))
..createSync(recursive: true)
..writeAsStringSync('{}');
fileSystem.file('.packages').createSync();
fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true);
final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync();
pubspecFile.writeAsStringSync('name: $appName');
await createTestCommandRunner(command)
.run(const <String>['build', 'fuchsia']);
final String farPath = fileSystem.path.join(
getFuchsiaBuildDirectory(), 'pkg', 'app_name-0.far',
);
expect(fileSystem.file(farPath), exists);
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
FuchsiaSdk: () => fuchsiaSdk,
FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true),
});
}
class FakeFuchsiaPM extends Fake implements FuchsiaPM {
String _appName;
@override
Future<bool> init(String buildPath, String appName) async {
if (!fileSystem.directory(buildPath).existsSync()) {
return false;
}
fileSystem
.file(fileSystem.path.join(buildPath, 'meta', 'package'))
.createSync(recursive: true);
_appName = appName;
return true;
}
@override
Future<bool> build(String buildPath, String manifestPath) async {
if (!fileSystem.file(fileSystem.path.join(buildPath, 'meta', 'package')).existsSync() ||
!fileSystem.file(manifestPath).existsSync()) {
return false;
}
fileSystem.file(fileSystem.path.join(buildPath, 'meta.far')).createSync(recursive: true);
return true;
}
@override
Future<bool> archive(String buildPath, String manifestPath) async {
if (!fileSystem.file(fileSystem.path.join(buildPath, 'meta', 'package')).existsSync() ||
!fileSystem.file(manifestPath).existsSync()) {
return false;
}
if (_appName == null) {
return false;
}
fileSystem
.file(fileSystem.path.join(buildPath, '$_appName-0.far'))
.createSync(recursive: true);
return true;
}
}
class FakeFuchsiaKernelCompiler extends Fake implements FuchsiaKernelCompiler {
@override
Future<void> build({
@required FuchsiaProject fuchsiaProject,
@required String target, // E.g., lib/main.dart
BuildInfo buildInfo = BuildInfo.debug,
}) async {
final String outDir = getFuchsiaBuildDirectory();
final String appName = fuchsiaProject.project.manifest.appName;
final String manifestPath = fileSystem.path.join(outDir, '$appName.dilpmanifest');
fileSystem.file(manifestPath).createSync(recursive: true);
}
}
class FakeFuchsiaSdk extends Fake implements FuchsiaSdk {
@override
final FuchsiaPM fuchsiaPM = FakeFuchsiaPM();
@override
final FuchsiaKernelCompiler fuchsiaKernelCompiler =
FakeFuchsiaKernelCompiler();
}

View File

@ -12,7 +12,6 @@ import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/commands/build_aar.dart';
import 'package:flutter_tools/src/commands/build_apk.dart';
import 'package:flutter_tools/src/commands/build_appbundle.dart';
import 'package:flutter_tools/src/commands/build_fuchsia.dart';
import 'package:flutter_tools/src/commands/build_ios.dart';
import 'package:flutter_tools/src/commands/build_ios_framework.dart';
import 'package:flutter_tools/src/commands/build_linux.dart';
@ -36,9 +35,11 @@ void main() {
BuildIOSCommand(verboseHelp: false),
BuildIOSArchiveCommand(verboseHelp: false),
BuildAppBundleCommand(),
BuildFuchsiaCommand(verboseHelp: false),
BuildAarCommand(verboseHelp: false),
BuildIOSFrameworkCommand(verboseHelp: false, buildSystem: globals.buildSystem),
BuildIOSFrameworkCommand(
verboseHelp: false,
buildSystem: globals.buildSystem,
),
AttachCommand(),
];
@ -53,7 +54,8 @@ void main() {
}
});
testUsingContext('BuildSubCommand displays current null safety mode', () async {
testUsingContext('BuildSubCommand displays current null safety mode',
() async {
const BuildInfo unsound = BuildInfo(
BuildMode.debug,
'',
@ -67,11 +69,13 @@ void main() {
);
FakeBuildSubCommand().test(unsound);
expect(testLogger.statusText, contains('Building without sound null safety'));
expect(
testLogger.statusText, contains('Building without sound null safety'));
testLogger.clear();
FakeBuildSubCommand().test(sound);
expect(testLogger.statusText, contains('💪 Building with sound null safety 💪'));
expect(testLogger.statusText,
contains('💪 Building with sound null safety 💪'));
});
testUsingContext('Include only supported sub commands', () {

View File

@ -22,8 +22,6 @@ import 'package:flutter_tools/src/fuchsia/fuchsia_kernel_compiler.dart';
import 'package:flutter_tools/src/fuchsia/fuchsia_pm.dart';
import 'package:flutter_tools/src/fuchsia/fuchsia_sdk.dart';
import 'package:flutter_tools/src/fuchsia/pkgctl.dart';
import 'package:flutter_tools/src/fuchsia/session_control.dart';
import 'package:flutter_tools/src/fuchsia/tiles_ctl.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/project.dart';
import 'package:meta/meta.dart';
@ -42,7 +40,6 @@ void main() {
Artifacts artifacts;
FakeProcessManager fakeSuccessfulProcessManager;
FakeProcessManager fakeFailedProcessManagerForHostAddress;
FakeProcessManager fakeSuccessfulProcessManagerWithSession;
File sshConfig;
setUp(() {
@ -50,56 +47,69 @@ void main() {
osUtils = FakeOperatingSystemUtils();
fuchsiaDeviceTools = FakeFuchsiaDeviceTools();
fuchsiaSdk = FakeFuchsiaSdk();
sshConfig = MemoryFileSystem.test().file('ssh_config')..writeAsStringSync('\n');
sshConfig = MemoryFileSystem.test().file('ssh_config')
..writeAsStringSync('\n');
artifacts = Artifacts.test();
for (final BuildMode mode in <BuildMode>[BuildMode.debug, BuildMode.release]) {
memoryFileSystem.file(
artifacts.getArtifactPath(Artifact.fuchsiaKernelCompiler,
platform: TargetPlatform.fuchsia_arm64, mode: mode),
).createSync();
for (final BuildMode mode in <BuildMode>[
BuildMode.debug,
BuildMode.release
]) {
memoryFileSystem
.file(
artifacts.getArtifactPath(Artifact.fuchsiaKernelCompiler,
platform: TargetPlatform.fuchsia_arm64, mode: mode),
)
.createSync();
memoryFileSystem.file(
artifacts.getArtifactPath(Artifact.platformKernelDill,
platform: TargetPlatform.fuchsia_arm64, mode: mode),
).createSync();
memoryFileSystem
.file(
artifacts.getArtifactPath(Artifact.platformKernelDill,
platform: TargetPlatform.fuchsia_arm64, mode: mode),
)
.createSync();
memoryFileSystem.file(
artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath,
platform: TargetPlatform.fuchsia_arm64, mode: mode),
).createSync();
memoryFileSystem
.file(
artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath,
platform: TargetPlatform.fuchsia_arm64, mode: mode),
)
.createSync();
memoryFileSystem.file(
artifacts.getArtifactPath(Artifact.fuchsiaFlutterRunner,
platform: TargetPlatform.fuchsia_arm64, mode: mode),
).createSync();
memoryFileSystem
.file(
artifacts.getArtifactPath(Artifact.fuchsiaFlutterRunner,
platform: TargetPlatform.fuchsia_arm64, mode: mode),
)
.createSync();
}
fakeSuccessfulProcessManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['ssh', '-F', '/ssh_config', '123', 'which session_control'],
exitCode: 1,
),
FakeCommand(
command: <String>['ssh', '-F', sshConfig.absolute.path, '123', r'echo $SSH_CONNECTION'],
stdout: 'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22',
command: <String>[
'ssh',
'-F',
sshConfig.absolute.path,
'123',
r'echo $SSH_CONNECTION'
],
stdout:
'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22',
),
]);
fakeFailedProcessManagerForHostAddress = FakeProcessManager.list(<FakeCommand>[
fakeFailedProcessManagerForHostAddress =
FakeProcessManager.list(<FakeCommand>[
FakeCommand(
command: <String>['ssh', '-F', sshConfig.absolute.path, '123', r'echo $SSH_CONNECTION'],
stdout: 'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22',
command: <String>[
'ssh',
'-F',
sshConfig.absolute.path,
'123',
r'echo $SSH_CONNECTION'
],
stdout:
'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22',
exitCode: 1,
),
]);
fakeSuccessfulProcessManagerWithSession = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['ssh', '-F', '/ssh_config', '123', 'which session_control'],
stdout: '/bin/session_control',
),
FakeCommand(
command: <String>['ssh', '-F', sshConfig.absolute.path, '123', r'echo $SSH_CONNECTION'],
stdout: 'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22',
),
]);
});
Future<LaunchResult> setupAndStartApp({
@ -117,15 +127,21 @@ void main() {
final File far = globals.fs.file('app_name-0.far')..createSync();
app = FuchsiaApp.fromPrebuiltApp(far);
} else {
globals.fs.file(globals.fs.path.join('fuchsia', 'meta', '$appName.cmx'))
globals.fs.file(globals.fs.path.join('fuchsia', 'meta', '$appName.cm'))
..createSync(recursive: true)
..writeAsStringSync('{}');
globals.fs.file('.packages').createSync();
globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
app = BuildableFuchsiaApp(project: FlutterProject.fromDirectoryTest(globals.fs.currentDirectory).fuchsia);
globals.fs
.file(globals.fs.path.join('lib', 'main.dart'))
.createSync(recursive: true);
app = BuildableFuchsiaApp(
project:
FlutterProject.fromDirectoryTest(globals.fs.currentDirectory)
.fuchsia);
}
final DebuggingOptions debuggingOptions = DebuggingOptions.disabled(BuildInfo(mode, null, treeShakeIcons: false));
final DebuggingOptions debuggingOptions = DebuggingOptions.disabled(
BuildInfo(mode, null, treeShakeIcons: false));
return device.startApp(
app,
prebuiltApplication: prebuilt,
@ -133,10 +149,12 @@ void main() {
);
}
testUsingContext('start prebuilt in release mode', () async {
testUsingContext(
'start prebuilt in release mode fails without session',
() async {
final LaunchResult launchResult =
await setupAndStartApp(prebuilt: true, mode: BuildMode.release);
expect(launchResult.started, isTrue);
expect(launchResult.started, isFalse);
expect(launchResult.hasObservatory, isFalse);
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
@ -150,20 +168,22 @@ void main() {
testUsingContext('start prebuilt in release mode with session', () async {
final LaunchResult launchResult =
await setupAndStartApp(prebuilt: true, mode: BuildMode.release);
await setupAndStartApp(prebuilt: true, mode: BuildMode.release);
expect(launchResult.started, isTrue);
expect(launchResult.hasObservatory, isFalse);
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => memoryFileSystem,
ProcessManager: () => fakeSuccessfulProcessManagerWithSession,
ProcessManager: () => fakeSuccessfulProcessManager,
FuchsiaDeviceTools: () => fuchsiaDeviceTools,
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
FuchsiaSdk: () => fuchsiaSdk,
FuchsiaSdk: () => FakeFuchsiaSdk(ffx: FakeFuchsiaFfxWithSession()),
OperatingSystemUtils: () => osUtils,
});
testUsingContext('start and stop prebuilt in release mode', () async {
testUsingContext(
'start and stop prebuilt in release mode fails without session',
() async {
const String appName = 'app_name';
final FuchsiaDevice device = FuchsiaDeviceWithFakeDiscovery('123');
globals.fs.directory('fuchsia').createSync(recursive: true);
@ -172,14 +192,12 @@ void main() {
final File far = globals.fs.file('app_name-0.far')..createSync();
final FuchsiaApp app = FuchsiaApp.fromPrebuiltApp(far);
final DebuggingOptions debuggingOptions =
DebuggingOptions.disabled(const BuildInfo(BuildMode.release, null, treeShakeIcons: false));
final DebuggingOptions debuggingOptions = DebuggingOptions.disabled(
const BuildInfo(BuildMode.release, null, treeShakeIcons: false));
final LaunchResult launchResult = await device.startApp(app,
prebuiltApplication: true,
debuggingOptions: debuggingOptions);
expect(launchResult.started, isTrue);
prebuiltApplication: true, debuggingOptions: debuggingOptions);
expect(launchResult.started, isFalse);
expect(launchResult.hasObservatory, isFalse);
expect(await device.stopApp(app), isTrue);
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => memoryFileSystem,
@ -190,7 +208,8 @@ void main() {
OperatingSystemUtils: () => osUtils,
});
testUsingContext('start and stop prebuilt in release mode with session', () async {
testUsingContext('start and stop prebuilt in release mode with session',
() async {
const String appName = 'app_name';
final FuchsiaDevice device = FuchsiaDeviceWithFakeDiscovery('123');
globals.fs.directory('fuchsia').createSync(recursive: true);
@ -199,29 +218,29 @@ void main() {
final File far = globals.fs.file('app_name-0.far')..createSync();
final FuchsiaApp app = FuchsiaApp.fromPrebuiltApp(far);
final DebuggingOptions debuggingOptions =
DebuggingOptions.disabled(const BuildInfo(BuildMode.release, null, treeShakeIcons: false));
final DebuggingOptions debuggingOptions = DebuggingOptions.disabled(
const BuildInfo(BuildMode.release, null, treeShakeIcons: false));
final LaunchResult launchResult = await device.startApp(app,
prebuiltApplication: true,
debuggingOptions: debuggingOptions);
prebuiltApplication: true, debuggingOptions: debuggingOptions);
expect(launchResult.started, isTrue);
expect(launchResult.hasObservatory, isFalse);
expect(await device.stopApp(app), isTrue);
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => memoryFileSystem,
ProcessManager: () => fakeSuccessfulProcessManagerWithSession,
ProcessManager: () => fakeSuccessfulProcessManager,
FuchsiaDeviceTools: () => fuchsiaDeviceTools,
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
FuchsiaSdk: () => fuchsiaSdk,
FuchsiaSdk: () => FakeFuchsiaSdk(ffx: FakeFuchsiaFfxWithSession()),
OperatingSystemUtils: () => osUtils,
});
testUsingContext('start prebuilt in debug mode', () async {
testUsingContext(
'start prebuilt in debug mode fails without session',
() async {
final LaunchResult launchResult =
await setupAndStartApp(prebuilt: true, mode: BuildMode.debug);
expect(launchResult.started, isTrue);
expect(launchResult.hasObservatory, isTrue);
expect(launchResult.started, isFalse);
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => memoryFileSystem,
@ -234,32 +253,32 @@ void main() {
testUsingContext('start prebuilt in debug mode with session', () async {
final LaunchResult launchResult =
await setupAndStartApp(prebuilt: true, mode: BuildMode.debug);
await setupAndStartApp(prebuilt: true, mode: BuildMode.debug);
expect(launchResult.started, isTrue);
expect(launchResult.hasObservatory, isTrue);
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => memoryFileSystem,
ProcessManager: () => fakeSuccessfulProcessManagerWithSession,
ProcessManager: () => fakeSuccessfulProcessManager,
FuchsiaDeviceTools: () => fuchsiaDeviceTools,
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
FuchsiaSdk: () => fuchsiaSdk,
FuchsiaSdk: () => FakeFuchsiaSdk(ffx: FakeFuchsiaFfxWithSession()),
OperatingSystemUtils: () => osUtils,
});
testUsingContext('start buildable in release mode', () async {
final LaunchResult launchResult =
await setupAndStartApp(prebuilt: false, mode: BuildMode.release);
expect(launchResult.started, isTrue);
expect(launchResult.hasObservatory, isFalse);
testUsingContext(
'start buildable in release mode fails without session',
() async {
expect(
() async => setupAndStartApp(prebuilt: false, mode: BuildMode.release),
throwsToolExit(
message: 'This tool does not currently build apps for fuchsia.\n'
'Build the app using a supported Fuchsia workflow.\n'
'Then use the --use-application-binary flag.'));
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['ssh', '-F', '/ssh_config', '123', 'which session_control'],
exitCode: 1,
),
const FakeCommand(
command: <String>[
'Artifact.genSnapshot.TargetPlatform.fuchsia_arm64.release',
@ -270,8 +289,15 @@ void main() {
],
),
FakeCommand(
command: <String>['ssh', '-F', sshConfig.absolute.path, '123', r'echo $SSH_CONNECTION'],
stdout: 'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22',
command: <String>[
'ssh',
'-F',
sshConfig.absolute.path,
'123',
r'echo $SSH_CONNECTION'
],
stdout:
'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22',
),
]),
FuchsiaDeviceTools: () => fuchsiaDeviceTools,
@ -280,44 +306,55 @@ void main() {
OperatingSystemUtils: () => osUtils,
});
testUsingContext('start buildable in release mode with session', () async {
final LaunchResult launchResult =
await setupAndStartApp(prebuilt: false, mode: BuildMode.release);
expect(launchResult.started, isTrue);
expect(launchResult.hasObservatory, isFalse);
testUsingContext(
'start buildable in release mode with session fails, does not build apps yet',
() async {
expect(
() async => setupAndStartApp(prebuilt: false, mode: BuildMode.release),
throwsToolExit(
message: 'This tool does not currently build apps for fuchsia.\n'
'Build the app using a supported Fuchsia workflow.\n'
'Then use the --use-application-binary flag.'));
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['ssh', '-F', '/ssh_config', '123', 'which session_control'],
stdout: '/bin/session_control',
),
const FakeCommand(
command: <String>[
'Artifact.genSnapshot.TargetPlatform.fuchsia_arm64.release',
'--deterministic',
'--snapshot_kind=app-aot-elf',
'--elf=build/fuchsia/elf.aotsnapshot',
'build/fuchsia/app_name.dil',
],
),
FakeCommand(
command: <String>['ssh', '-F', sshConfig.absolute.path, '123', r'echo $SSH_CONNECTION'],
stdout: 'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22',
),
]),
const FakeCommand(
command: <String>[
'Artifact.genSnapshot.TargetPlatform.fuchsia_arm64.release',
'--deterministic',
'--snapshot_kind=app-aot-elf',
'--elf=build/fuchsia/elf.aotsnapshot',
'build/fuchsia/app_name.dil',
],
),
FakeCommand(
command: <String>[
'ssh',
'-F',
sshConfig.absolute.path,
'123',
r'echo $SSH_CONNECTION'
],
stdout:
'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22',
),
]),
FuchsiaDeviceTools: () => fuchsiaDeviceTools,
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
FuchsiaSdk: () => fuchsiaSdk,
FuchsiaSdk: () => FakeFuchsiaSdk(ffx: FakeFuchsiaFfxWithSession()),
OperatingSystemUtils: () => osUtils,
});
testUsingContext('start buildable in debug mode', () async {
final LaunchResult launchResult =
await setupAndStartApp(prebuilt: false, mode: BuildMode.debug);
expect(launchResult.started, isTrue);
expect(launchResult.hasObservatory, isTrue);
testUsingContext(
'start buildable in debug mode fails without session',
() async {
expect(
() async => setupAndStartApp(prebuilt: false, mode: BuildMode.debug),
throwsToolExit(
message: 'This tool does not currently build apps for fuchsia.\n'
'Build the app using a supported Fuchsia workflow.\n'
'Then use the --use-application-binary flag.'));
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => memoryFileSystem,
@ -328,37 +365,42 @@ void main() {
OperatingSystemUtils: () => osUtils,
});
testUsingContext('start buildable in debug mode with session', () async {
final LaunchResult launchResult =
await setupAndStartApp(prebuilt: false, mode: BuildMode.debug);
expect(launchResult.started, isTrue);
expect(launchResult.hasObservatory, isTrue);
testUsingContext(
'start buildable in debug mode with session fails, does not build apps yet',
() async {
expect(
() async => setupAndStartApp(prebuilt: false, mode: BuildMode.debug),
throwsToolExit(
message: 'This tool does not currently build apps for fuchsia.\n'
'Build the app using a supported Fuchsia workflow.\n'
'Then use the --use-application-binary flag.'));
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => memoryFileSystem,
ProcessManager: () => fakeSuccessfulProcessManagerWithSession,
ProcessManager: () => fakeSuccessfulProcessManager,
FuchsiaDeviceTools: () => fuchsiaDeviceTools,
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
FuchsiaSdk: () => fuchsiaSdk,
FuchsiaSdk: () => FakeFuchsiaSdk(ffx: FakeFuchsiaFfxWithSession()),
OperatingSystemUtils: () => osUtils,
});
testUsingContext('fail when cant get ssh config', () async {
expect(() async =>
setupAndStartApp(prebuilt: true, mode: BuildMode.release),
throwsToolExit(message: 'Cannot interact with device. No ssh config.\n'
'Try setting FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR.'));
expect(
() async => setupAndStartApp(prebuilt: true, mode: BuildMode.release),
throwsToolExit(
message: 'Cannot interact with device. No ssh config.\n'
'Try setting FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR.'));
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
FuchsiaArtifacts: () => FuchsiaArtifacts(),
FuchsiaSdk: () => FakeFuchsiaSdk(ffx: FakeFuchsiaFfxWithSession()),
OperatingSystemUtils: () => osUtils,
});
testUsingContext('fail when cant get host address', () async {
expect(() async =>
FuchsiaDeviceWithFakeDiscovery('123').hostAddress,
expect(() async => FuchsiaDeviceWithFakeDiscovery('123').hostAddress,
throwsToolExit(message: 'Failed to get local address, aborting.'));
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
@ -400,37 +442,6 @@ void main() {
FuchsiaSdk: () => fuchsiaSdk,
OperatingSystemUtils: () => osUtils,
});
testUsingContext('fail with correct LaunchResult when tiles fails', () async {
final LaunchResult launchResult =
await setupAndStartApp(prebuilt: true, mode: BuildMode.release);
expect(launchResult.started, isFalse);
expect(launchResult.hasObservatory, isFalse);
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => memoryFileSystem,
ProcessManager: () => fakeSuccessfulProcessManager,
FuchsiaDeviceTools: () => FakeFuchsiaDeviceTools(tiles: FailingTilesCtl()),
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
FuchsiaSdk: () => fuchsiaSdk,
OperatingSystemUtils: () => osUtils,
});
testUsingContext('fail with correct LaunchResult when tiles fails with session', () async {
final LaunchResult launchResult =
await setupAndStartApp(prebuilt: true, mode: BuildMode.release);
expect(launchResult.started, isFalse);
expect(launchResult.hasObservatory, isFalse);
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => memoryFileSystem,
ProcessManager: () => fakeSuccessfulProcessManagerWithSession,
FuchsiaDeviceTools: () => FakeFuchsiaDeviceTools(sessionControl: FailingFuchsiaSessionControl()),
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
FuchsiaSdk: () => fuchsiaSdk,
OperatingSystemUtils: () => osUtils,
});
});
}
@ -440,34 +451,41 @@ Process _createFakeProcess({
String stderr = '',
bool persistent = false,
}) {
final Stream<List<int>> stdoutStream = Stream<List<int>>.fromIterable(<List<int>>[
final Stream<List<int>> stdoutStream =
Stream<List<int>>.fromIterable(<List<int>>[
utf8.encode(stdout),
]);
final Stream<List<int>> stderrStream = Stream<List<int>>.fromIterable(<List<int>>[
final Stream<List<int>> stderrStream =
Stream<List<int>>.fromIterable(<List<int>>[
utf8.encode(stderr),
]);
final Completer<int> exitCodeCompleter = Completer<int>();
final Process process = FakeProcess(
stdout: stdoutStream,
stderr: stderrStream,
exitCode: persistent ? exitCodeCompleter.future : Future<int>.value(exitCode),
exitCode:
persistent ? exitCodeCompleter.future : Future<int>.value(exitCode),
);
return process;
}
class FuchsiaDeviceWithFakeDiscovery extends FuchsiaDevice {
FuchsiaDeviceWithFakeDiscovery(String id, {String name}) : super(id, name: name);
FuchsiaDeviceWithFakeDiscovery(String id, {String name})
: super(id, name: name);
@override
FuchsiaIsolateDiscoveryProtocol getIsolateDiscoveryProtocol(String isolateName) {
FuchsiaIsolateDiscoveryProtocol getIsolateDiscoveryProtocol(
String isolateName) {
return FakeFuchsiaIsolateDiscoveryProtocol();
}
@override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.fuchsia_arm64;
Future<TargetPlatform> get targetPlatform async =>
TargetPlatform.fuchsia_arm64;
}
class FakeFuchsiaIsolateDiscoveryProtocol implements FuchsiaIsolateDiscoveryProtocol {
class FakeFuchsiaIsolateDiscoveryProtocol
implements FuchsiaIsolateDiscoveryProtocol {
@override
FutureOr<Uri> get uri => Uri.parse('http://[::1]:37');
@ -513,112 +531,18 @@ class FailingPkgctl implements FuchsiaPkgctl {
}
}
class FakeFuchsiaTilesCtl implements FuchsiaTilesCtl {
final Map<int, String> _runningApps = <int, String>{};
bool _started = false;
int _nextAppId = 1;
@override
Future<bool> start(FuchsiaDevice device) async {
_started = true;
return true;
}
@override
Future<Map<int, String>> list(FuchsiaDevice device) async {
if (!_started) {
return null;
}
return _runningApps;
}
@override
Future<bool> add(FuchsiaDevice device, String url, List<String> args) async {
if (!_started) {
return false;
}
_runningApps[_nextAppId] = url;
_nextAppId++;
return true;
}
@override
Future<bool> remove(FuchsiaDevice device, int key) async {
if (!_started) {
return false;
}
_runningApps.remove(key);
return true;
}
@override
Future<bool> quit(FuchsiaDevice device) async {
if (!_started) {
return false;
}
_started = false;
return true;
}
}
class FailingTilesCtl implements FuchsiaTilesCtl {
@override
Future<bool> start(FuchsiaDevice device) async {
return false;
}
@override
Future<Map<int, String>> list(FuchsiaDevice device) async {
return null;
}
@override
Future<bool> add(FuchsiaDevice device, String url, List<String> args) async {
return false;
}
@override
Future<bool> remove(FuchsiaDevice device, int key) async {
return false;
}
@override
Future<bool> quit(FuchsiaDevice device) async {
return false;
}
}
class FakeFuchsiaSessionControl implements FuchsiaSessionControl {
@override
Future<bool> add(FuchsiaDevice device, String url) async {
return true;
}
}
class FailingFuchsiaSessionControl implements FuchsiaSessionControl {
@override
Future<bool> add(FuchsiaDevice device, String url) async {
return false;
}
}
class FakeFuchsiaDeviceTools implements FuchsiaDeviceTools {
FakeFuchsiaDeviceTools({
FuchsiaPkgctl pkgctl,
FuchsiaTilesCtl tiles,
FuchsiaSessionControl sessionControl,
FuchsiaFfx ffx,
}) : pkgctl = pkgctl ?? FakeFuchsiaPkgctl(),
tilesCtl = tiles ?? FakeFuchsiaTilesCtl(),
sessionControl = sessionControl ?? FakeFuchsiaSessionControl();
ffx = ffx ?? FakeFuchsiaFfx();
@override
final FuchsiaPkgctl pkgctl;
@override
final FuchsiaTilesCtl tilesCtl;
@override
final FuchsiaSessionControl sessionControl;
final FuchsiaFfx ffx;
}
class FakeFuchsiaPM implements FuchsiaPM {
@ -638,17 +562,23 @@ class FakeFuchsiaPM implements FuchsiaPM {
@override
Future<bool> build(String buildPath, String manifestPath) async {
if (!globals.fs.file(globals.fs.path.join(buildPath, 'meta', 'package')).existsSync() ||
if (!globals.fs
.file(globals.fs.path.join(buildPath, 'meta', 'package'))
.existsSync() ||
!globals.fs.file(manifestPath).existsSync()) {
return false;
}
globals.fs.file(globals.fs.path.join(buildPath, 'meta.far')).createSync(recursive: true);
globals.fs
.file(globals.fs.path.join(buildPath, 'meta.far'))
.createSync(recursive: true);
return true;
}
@override
Future<bool> archive(String buildPath, String manifestPath) async {
if (!globals.fs.file(globals.fs.path.join(buildPath, 'meta', 'package')).existsSync() ||
if (!globals.fs
.file(globals.fs.path.join(buildPath, 'meta', 'package'))
.existsSync() ||
!globals.fs.file(manifestPath).existsSync()) {
return false;
}
@ -692,7 +622,6 @@ class FailingPM implements FuchsiaPM {
return false;
}
@override
Future<bool> build(String buildPath, String manifestPath) async {
return false;
@ -728,7 +657,8 @@ class FakeFuchsiaKernelCompiler implements FuchsiaKernelCompiler {
}) async {
final String outDir = getFuchsiaBuildDirectory();
final String appName = fuchsiaProject.project.manifest.appName;
final String manifestPath = globals.fs.path.join(outDir, '$appName.dilpmanifest');
final String manifestPath =
globals.fs.path.join(outDir, '$appName.dilpmanifest');
globals.fs.file(manifestPath).createSync(recursive: true);
}
}
@ -743,6 +673,38 @@ class FakeFuchsiaFfx implements FuchsiaFfx {
Future<String> resolve(String deviceName) async {
return '192.168.42.10';
}
@override
Future<String> sessionShow() async {
return null;
}
@override
Future<bool> sessionAdd(String url) async {
return false;
}
}
class FakeFuchsiaFfxWithSession implements FuchsiaFfx {
@override
Future<List<String>> list({Duration timeout}) async {
return <String>['192.168.42.172 scare-cable-skip-ffx'];
}
@override
Future<String> resolve(String deviceName) async {
return '192.168.42.10';
}
@override
Future<String> sessionShow() async {
return 'session info';
}
@override
Future<bool> sessionAdd(String url) async {
return true;
}
}
class FakeFuchsiaSdk extends Fake implements FuchsiaSdk {
@ -750,9 +712,9 @@ class FakeFuchsiaSdk extends Fake implements FuchsiaSdk {
FuchsiaPM pm,
FuchsiaKernelCompiler compiler,
FuchsiaFfx ffx,
}) : fuchsiaPM = pm ?? FakeFuchsiaPM(),
fuchsiaKernelCompiler = compiler ?? FakeFuchsiaKernelCompiler(),
fuchsiaFfx = ffx ?? FakeFuchsiaFfx();
}) : fuchsiaPM = pm ?? FakeFuchsiaPM(),
fuchsiaKernelCompiler = compiler ?? FakeFuchsiaKernelCompiler(),
fuchsiaFfx = ffx ?? FakeFuchsiaFfx();
@override
final FuchsiaPM fuchsiaPM;

View File

@ -40,7 +40,7 @@ final vm_service.Isolate fakeIsolate = vm_service.Isolate(
id: '1',
pauseEvent: vm_service.Event(
kind: vm_service.EventKind.kResume,
timestamp: 0
timestamp: 0,
),
breakpoints: <vm_service.Breakpoint>[],
exceptionPauseMode: null,
@ -87,8 +87,10 @@ void main() {
expect(device.supportsRuntimeMode(BuildMode.jitRelease), false);
});
testWithoutContext('lists nothing when workflow cannot list devices', () async {
final FakeFuchsiaWorkflow fuchsiaWorkflow = FakeFuchsiaWorkflow(canListDevices: false);
testWithoutContext('lists nothing when workflow cannot list devices',
() async {
final FakeFuchsiaWorkflow fuchsiaWorkflow =
FakeFuchsiaWorkflow(canListDevices: false);
final FuchsiaDevices fuchsiaDevices = FuchsiaDevices(
platform: FakePlatform(),
fuchsiaSdk: FakeFuchsiaSdk(devices: 'ignored'),
@ -102,7 +104,9 @@ void main() {
testWithoutContext('can parse ffx output for single device', () async {
final FakeFuchsiaWorkflow fuchsiaWorkflow = FakeFuchsiaWorkflow();
final FakeFuchsiaSdk fuchsiaSdk = FakeFuchsiaSdk(devices: '2001:0db8:85a3:0000:0000:8a2e:0370:7334 paper-pulp-bush-angel');
final FakeFuchsiaSdk fuchsiaSdk = FakeFuchsiaSdk(
devices:
'2001:0db8:85a3:0000:0000:8a2e:0370:7334 paper-pulp-bush-angel');
final FuchsiaDevices fuchsiaDevices = FuchsiaDevices(
platform: FakePlatform(environment: <String, String>{}),
fuchsiaSdk: fuchsiaSdk,
@ -118,10 +122,10 @@ void main() {
testWithoutContext('can parse ffx output for multiple devices', () async {
final FakeFuchsiaWorkflow fuchsiaWorkflow = FakeFuchsiaWorkflow();
final FakeFuchsiaSdk fuchsiaSdk = FakeFuchsiaSdk(devices:
'2001:0db8:85a3:0000:0000:8a2e:0370:7334 paper-pulp-bush-angel\n'
'2001:0db8:85a3:0000:0000:8a2e:0370:7335 foo-bar-fiz-buzz'
);
final FakeFuchsiaSdk fuchsiaSdk = FakeFuchsiaSdk(
devices:
'2001:0db8:85a3:0000:0000:8a2e:0370:7334 paper-pulp-bush-angel\n'
'2001:0db8:85a3:0000:0000:8a2e:0370:7335 foo-bar-fiz-buzz');
final FuchsiaDevices fuchsiaDevices = FuchsiaDevices(
platform: FakePlatform(),
fuchsiaSdk: fuchsiaSdk,
@ -138,7 +142,8 @@ void main() {
});
testWithoutContext('can parse junk output from ffx', () async {
final FakeFuchsiaWorkflow fuchsiaWorkflow = FakeFuchsiaWorkflow(canListDevices: false);
final FakeFuchsiaWorkflow fuchsiaWorkflow =
FakeFuchsiaWorkflow(canListDevices: false);
final FakeFuchsiaSdk fuchsiaSdk = FakeFuchsiaSdk(devices: 'junk');
final FuchsiaDevices fuchsiaDevices = FuchsiaDevices(
platform: FakePlatform(),
@ -163,7 +168,8 @@ void main() {
testWithoutContext('default capabilities', () async {
final FuchsiaDevice device = FuchsiaDevice('123', name: 'device');
final FlutterProject project = FlutterProject.fromDirectoryTest(memoryFileSystem.currentDirectory);
final FlutterProject project =
FlutterProject.fromDirectoryTest(memoryFileSystem.currentDirectory);
memoryFileSystem.directory('fuchsia').createSync(recursive: true);
memoryFileSystem.file('pubspec.yaml').createSync();
@ -181,7 +187,8 @@ void main() {
testWithoutContext('supported for project', () async {
final FuchsiaDevice device = FuchsiaDevice('123', name: 'device');
final FlutterProject project = FlutterProject.fromDirectoryTest(memoryFileSystem.currentDirectory);
final FlutterProject project =
FlutterProject.fromDirectoryTest(memoryFileSystem.currentDirectory);
memoryFileSystem.directory('fuchsia').createSync(recursive: true);
memoryFileSystem.file('pubspec.yaml').createSync();
@ -190,13 +197,15 @@ void main() {
testWithoutContext('not supported for project', () async {
final FuchsiaDevice device = FuchsiaDevice('123', name: 'device');
final FlutterProject project = FlutterProject.fromDirectoryTest(memoryFileSystem.currentDirectory);
final FlutterProject project =
FlutterProject.fromDirectoryTest(memoryFileSystem.currentDirectory);
memoryFileSystem.file('pubspec.yaml').createSync();
expect(device.isSupportedForProject(project), false);
});
testUsingContext('targetPlatform does not throw when sshConfig is missing', () async {
testUsingContext('targetPlatform does not throw when sshConfig is missing',
() async {
final FuchsiaDevice device = FuchsiaDevice('123', name: 'device');
expect(await device.targetPlatform, TargetPlatform.fuchsia_arm64);
@ -236,8 +245,15 @@ void main() {
testUsingContext('hostAddress parsing works', () async {
processManager.addCommand(const FakeCommand(
command: <String>['ssh', '-F', '/ssh_config', 'id', r'echo $SSH_CONNECTION'],
stdout: 'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22',
command: <String>[
'ssh',
'-F',
'/ssh_config',
'id',
r'echo $SSH_CONNECTION'
],
stdout:
'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22',
));
final FuchsiaDevice device = FuchsiaDevice('id', name: 'device');
@ -248,9 +264,16 @@ void main() {
ProcessManager: () => processManager,
});
testUsingContext('hostAddress parsing throws tool error on failure', () async {
testUsingContext('hostAddress parsing throws tool error on failure',
() async {
processManager.addCommand(const FakeCommand(
command: <String>['ssh', '-F', '/ssh_config', 'id', r'echo $SSH_CONNECTION'],
command: <String>[
'ssh',
'-F',
'/ssh_config',
'id',
r'echo $SSH_CONNECTION'
],
exitCode: 1,
));
@ -262,9 +285,16 @@ void main() {
ProcessManager: () => processManager,
});
testUsingContext('hostAddress parsing throws tool error on empty response', () async {
testUsingContext('hostAddress parsing throws tool error on empty response',
() async {
processManager.addCommand(const FakeCommand(
command: <String>['ssh', '-F', '/ssh_config', 'id', r'echo $SSH_CONNECTION'],
command: <String>[
'ssh',
'-F',
'/ssh_config',
'id',
r'echo $SSH_CONNECTION'
],
));
final FuchsiaDevice device = FuchsiaDevice('id', name: 'device');
@ -287,28 +317,38 @@ void main() {
testUsingContext('No vmservices found', () async {
processManager.addCommand(const FakeCommand(
command: <String>['ssh', '-F', '/artifact', 'id', 'find /hub -name vmservice-port'],
command: <String>[
'ssh',
'-F',
'/artifact',
'id',
'find /hub -name vmservice-port'
],
));
final FuchsiaDevice device = FuchsiaDevice('id', name: 'device');
await expectLater(device.servicePorts, throwsToolExit(message: 'No Dart Observatories found. Are you running a debug build?'));
await expectLater(
device.servicePorts,
throwsToolExit(
message:
'No Dart Observatories found. Are you running a debug build?'));
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
FuchsiaArtifacts: () => FuchsiaArtifacts(
sshConfig: artifactFile,
ffx: artifactFile,
),
sshConfig: artifactFile,
ffx: artifactFile,
),
FuchsiaSdk: () => FakeFuchsiaSdk(),
});
group('device logs', () {
const String exampleUtcLogs = '''
[2018-11-09 01:27:45][3][297950920][log] INFO: example_app.cmx(flutter): Error doing thing
[2018-11-09 01:27:45][3][297950920][log] INFO: example_app.cm(flutter): Error doing thing
[2018-11-09 01:27:58][46257][46269][foo] INFO: Using a thing
[2018-11-09 01:29:58][46257][46269][foo] INFO: Blah blah blah
[2018-11-09 01:29:58][46257][46269][foo] INFO: other_app.cmx(flutter): Do thing
[2018-11-09 01:29:58][46257][46269][foo] INFO: other_app.cm(flutter): Do thing
[2018-11-09 01:30:02][41175][41187][bar] INFO: Invoking a bar
[2018-11-09 01:30:12][52580][52983][log] INFO: example_app.cmx(flutter): Did thing this time
[2018-11-09 01:30:12][52580][52983][log] INFO: example_app.cm(flutter): Did thing this time
''';
FakeProcessManager processManager;
@ -316,21 +356,29 @@ void main() {
File sshConfig;
setUp(() {
processManager = FakeProcessManager.empty();
processManager = FakeProcessManager.empty();
final FileSystem memoryFileSystem = MemoryFileSystem.test();
ffx = memoryFileSystem.file('ffx')..writeAsStringSync('\n');
sshConfig = memoryFileSystem.file('ssh_config')..writeAsStringSync('\n');
sshConfig = memoryFileSystem.file('ssh_config')
..writeAsStringSync('\n');
});
testUsingContext('can be parsed for an app', () async {
final Completer<void> lock = Completer<void>();
processManager.addCommand(FakeCommand(
command: const <String>['ssh', '-F', '/ssh_config', 'id', 'log_listener --clock Local'],
command: const <String>[
'ssh',
'-F',
'/ssh_config',
'id',
'log_listener --clock Local'
],
stdout: exampleUtcLogs,
completer: lock,
));
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
final DeviceLogReader reader = device.getLogReader(app: FuchsiaModulePackage(name: 'example_app'));
final DeviceLogReader reader =
device.getLogReader(app: FuchsiaModulePackage(name: 'example_app'));
final List<String> logLines = <String>[];
reader.logLines.listen((String line) {
logLines.add(line);
@ -355,12 +403,19 @@ void main() {
testUsingContext('cuts off prior logs', () async {
final Completer<void> lock = Completer<void>();
processManager.addCommand(FakeCommand(
command: const <String>['ssh', '-F', '/ssh_config', 'id', 'log_listener --clock Local'],
command: const <String>[
'ssh',
'-F',
'/ssh_config',
'id',
'log_listener --clock Local'
],
stdout: exampleUtcLogs,
completer: lock,
));
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
final DeviceLogReader reader = device.getLogReader(app: FuchsiaModulePackage(name: 'example_app'));
final DeviceLogReader reader =
device.getLogReader(app: FuchsiaModulePackage(name: 'example_app'));
final List<String> logLines = <String>[];
reader.logLines.listen((String line) {
logLines.add(line);
@ -376,13 +431,20 @@ void main() {
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 29, 45)),
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig, ffx: ffx),
FuchsiaArtifacts: () =>
FuchsiaArtifacts(sshConfig: sshConfig, ffx: ffx),
});
testUsingContext('can be parsed for all apps', () async {
final Completer<void> lock = Completer<void>();
processManager.addCommand(FakeCommand(
command: const <String>['ssh', '-F', '/ssh_config', 'id', 'log_listener --clock Local'],
command: const <String>[
'ssh',
'-F',
'/ssh_config',
'id',
'log_listener --clock Local'
],
stdout: exampleUtcLogs,
completer: lock,
));
@ -407,7 +469,8 @@ void main() {
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 25, 45)),
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig, ffx: ffx),
FuchsiaArtifacts: () =>
FuchsiaArtifacts(sshConfig: sshConfig, ffx: ffx),
});
});
});
@ -432,8 +495,8 @@ void main() {
expect(device.supportsScreenshot, false);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(
operatingSystem: 'windows',
),
operatingSystem: 'windows',
),
});
test("takeScreenshot throws if file isn't .ppm", () async {
@ -441,43 +504,38 @@ void main() {
await expectLater(
() => device.takeScreenshot(globals.fs.file('file.invalid')),
throwsA(isA<Exception>().having(
(Exception exception) => exception.toString(),
'message',
contains('file.invalid must be a .ppm file')
)),
(Exception exception) => exception.toString(),
'message',
contains('file.invalid must be a .ppm file'))),
);
});
testUsingContext('takeScreenshot throws if screencap failed', () async {
processManager.addCommand(const FakeCommand(
command: <String>[
'ssh',
'-F',
'/fuchsia/out/default/.ssh',
'0.0.0.0',
'screencap > /tmp/screenshot.ppm',
],
exitCode: 1,
stderr: '<error-message>'
));
processManager.addCommand(const FakeCommand(command: <String>[
'ssh',
'-F',
'/fuchsia/out/default/.ssh',
'0.0.0.0',
'screencap > /tmp/screenshot.ppm',
], exitCode: 1, stderr: '<error-message>'));
final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester');
await expectLater(
() => device.takeScreenshot(globals.fs.file('file.ppm')),
throwsA(isA<Exception>().having(
(Exception exception) => exception.toString(),
'message',
contains('Could not take a screenshot on device tester:\n<error-message>')
)),
(Exception exception) => exception.toString(),
'message',
contains(
'Could not take a screenshot on device tester:\n<error-message>'))),
);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
FileSystem: () => MemoryFileSystem.test(),
Platform: () => FakePlatform(
environment: <String, String>{
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
},
),
environment: <String, String>{
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
},
),
});
testUsingContext('takeScreenshot throws if scp failed', () async {
@ -515,22 +573,24 @@ void main() {
await expectLater(
() => device.takeScreenshot(globals.fs.file('file.ppm')),
throwsA(isA<Exception>().having(
(Exception exception) => exception.toString(),
'message',
contains('Failed to copy screenshot from device:\n<error-message>')
)),
(Exception exception) => exception.toString(),
'message',
contains(
'Failed to copy screenshot from device:\n<error-message>'))),
);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
FileSystem: () => MemoryFileSystem.test(),
Platform: () => FakePlatform(
environment: <String, String>{
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
},
),
environment: <String, String>{
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
},
),
});
testUsingContext("takeScreenshot prints error if can't delete file from device", () async {
testUsingContext(
"takeScreenshot prints error if can't delete file from device",
() async {
final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester');
processManager.addCommand(const FakeCommand(
command: <String>[
@ -565,16 +625,17 @@ void main() {
await device.takeScreenshot(globals.fs.file('file.ppm'));
expect(
testLogger.errorText,
contains('Failed to delete screenshot.ppm from the device:\n<error-message>'),
contains(
'Failed to delete screenshot.ppm from the device:\n<error-message>'),
);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
FileSystem: () => MemoryFileSystem.test(),
Platform: () => FakePlatform(
environment: <String, String>{
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
},
),
environment: <String, String>{
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
},
),
}, testOn: 'posix');
testUsingContext('takeScreenshot returns', () async {
@ -607,15 +668,16 @@ void main() {
],
));
expect(() => device.takeScreenshot(globals.fs.file('file.ppm')), returnsNormally);
expect(() => device.takeScreenshot(globals.fs.file('file.ppm')),
returnsNormally);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
FileSystem: () => MemoryFileSystem.test(),
Platform: () => FakePlatform(
environment: <String, String>{
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
},
),
environment: <String, String>{
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
},
),
});
});
@ -625,21 +687,36 @@ void main() {
setUp(() {
processManager = FakeProcessManager.empty();
sshConfig = MemoryFileSystem.test().file('irrelevant')..writeAsStringSync('\n');
sshConfig = MemoryFileSystem.test().file('irrelevant')
..writeAsStringSync('\n');
});
testUsingContext('`unforward` prints stdout and stderr if ssh command failed', () async {
testUsingContext(
'`unforward` prints stdout and stderr if ssh command failed', () async {
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
processManager.addCommand(const FakeCommand(
command: <String>['ssh', '-F', '/irrelevant', '-O', 'cancel', '-vvv', '-L', '0:127.0.0.1:1', 'id'],
command: <String>[
'ssh',
'-F',
'/irrelevant',
'-O',
'cancel',
'-vvv',
'-L',
'0:127.0.0.1:1',
'id'
],
exitCode: 1,
stdout: '<stdout>',
stderr: '<stderr>',
));
await expectLater(
() => device.portForwarder.unforward(ForwardedPort(/*hostPort=*/ 0, /*devicePort=*/ 1)),
throwsToolExit(message: 'Unforward command failed:\nstdout: <stdout>\nstderr: <stderr>'),
() => device.portForwarder
.unforward(ForwardedPort(/*hostPort=*/ 0, /*devicePort=*/ 1)),
throwsToolExit(
message:
'Unforward command failed:\nstdout: <stdout>\nstderr: <stderr>'),
);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
@ -647,26 +724,26 @@ void main() {
});
});
group('FuchsiaIsolateDiscoveryProtocol', () {
Future<Uri> findUri(List<FlutterView> views, String expectedIsolateName) async {
Future<Uri> findUri(
List<FlutterView> views, String expectedIsolateName) async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
FakeVmServiceRequest(
method: kListViewsMethod,
jsonResponse: <String, Object>{
'views': <Object>[
for (FlutterView view in views)
view.toJson(),
for (FlutterView view in views) view.toJson(),
],
},
),
],
httpAddress: Uri.parse('example'),
);
final MockFuchsiaDevice fuchsiaDevice = MockFuchsiaDevice('123', const NoOpDevicePortForwarder(), false);
final MockFuchsiaDevice fuchsiaDevice =
MockFuchsiaDevice('123', const NoOpDevicePortForwarder(), false);
final FuchsiaIsolateDiscoveryProtocol discoveryProtocol =
FuchsiaIsolateDiscoveryProtocol(
FuchsiaIsolateDiscoveryProtocol(
fuchsiaDevice,
expectedIsolateName,
(Uri uri) async => fakeVmServiceHost.vmService,
@ -676,7 +753,8 @@ void main() {
return discoveryProtocol.uri;
}
testUsingContext('can find flutter view with matching isolate name', () async {
testUsingContext('can find flutter view with matching isolate name',
() async {
const String expectedIsolateName = 'foobar';
final Uri uri = await findUri(<FlutterView>[
// no ui isolate.
@ -693,25 +771,29 @@ void main() {
FlutterView(
id: '3',
uiIsolate: vm_service.Isolate.parse(<String, dynamic>{
...fakeIsolate.toJson(),
...fakeIsolate.toJson(),
'name': expectedIsolateName,
}),
),
], expectedIsolateName);
expect(uri.toString(), 'http://${InternetAddress.loopbackIPv4.address}:0/');
expect(
uri.toString(), 'http://${InternetAddress.loopbackIPv4.address}:0/');
});
testUsingContext('can handle flutter view without matching isolate name', () async {
testUsingContext('can handle flutter view without matching isolate name',
() async {
const String expectedIsolateName = 'foobar';
final Future<Uri> uri = findUri(<FlutterView>[
// no ui isolate.
FlutterView(id: '1', uiIsolate: fakeIsolate),
// wrong name.
FlutterView(id: '2', uiIsolate: vm_service.Isolate.parse(<String, Object>{
...fakeIsolate.toJson(),
'name': 'wrong name',
})),
FlutterView(
id: '2',
uiIsolate: vm_service.Isolate.parse(<String, Object>{
...fakeIsolate.toJson(),
'name': 'wrong name',
})),
], expectedIsolateName);
expect(uri, throwsException);
@ -738,28 +820,32 @@ void main() {
platform: FakePlatform(),
operatingSystemUtils: globals.os,
);
expect(artifacts.getArtifactPath(
expect(
artifacts.getArtifactPath(
Artifact.fuchsiaFlutterRunner,
platform: TargetPlatform.fuchsia_x64,
mode: BuildMode.debug,
),
contains('flutter_jit_runner'),
);
expect(artifacts.getArtifactPath(
expect(
artifacts.getArtifactPath(
Artifact.fuchsiaFlutterRunner,
platform: TargetPlatform.fuchsia_x64,
mode: BuildMode.profile,
),
contains('flutter_aot_runner'),
);
expect(artifacts.getArtifactPath(
expect(
artifacts.getArtifactPath(
Artifact.fuchsiaFlutterRunner,
platform: TargetPlatform.fuchsia_x64,
mode: BuildMode.release,
),
contains('flutter_aot_product_runner'),
);
expect(artifacts.getArtifactPath(
expect(
artifacts.getArtifactPath(
Artifact.fuchsiaFlutterRunner,
platform: TargetPlatform.fuchsia_x64,
mode: BuildMode.jitRelease,
@ -773,7 +859,8 @@ void main() {
FakeProcessManager processManager;
setUp(() {
sshConfig = MemoryFileSystem.test().file('ssh_config')..writeAsStringSync('\n');
sshConfig = MemoryFileSystem.test().file('ssh_config')
..writeAsStringSync('\n');
processManager = FakeProcessManager.empty();
});
@ -787,11 +874,15 @@ void main() {
FuchsiaSdk: () => FakeFuchsiaSdk(),
});
testUsingContext('returns what we get from the device on success', () async {
processManager.addCommand(const FakeCommand(
command: <String>['ssh', '-F', '/ssh_config', '123', 'cat /pkgfs/packages/build-info/0/data/version'],
stdout: 'version'
));
testUsingContext('returns what we get from the device on success',
() async {
processManager.addCommand(const FakeCommand(command: <String>[
'ssh',
'-F',
'/ssh_config',
'123',
'cat /pkgfs/packages/build-info/0/data/version'
], stdout: 'version'));
final FuchsiaDevice device = FuchsiaDevice('123', name: 'device');
expect(await device.sdkNameAndVersion, equals('Fuchsia version'));
@ -803,7 +894,13 @@ void main() {
testUsingContext('returns "Fuchsia" when device command fails', () async {
processManager.addCommand(const FakeCommand(
command: <String>['ssh', '-F', '/ssh_config', '123', 'cat /pkgfs/packages/build-info/0/data/version'],
command: <String>[
'ssh',
'-F',
'/ssh_config',
'123',
'cat /pkgfs/packages/build-info/0/data/version'
],
exitCode: 1,
));
final FuchsiaDevice device = FuchsiaDevice('123', name: 'device');
@ -815,9 +912,16 @@ void main() {
FuchsiaSdk: () => FakeFuchsiaSdk(),
});
testUsingContext('returns "Fuchsia" when device gives an empty result', () async {
testUsingContext('returns "Fuchsia" when device gives an empty result',
() async {
processManager.addCommand(const FakeCommand(
command: <String>['ssh', '-F', '/ssh_config', '123', 'cat /pkgfs/packages/build-info/0/data/version'],
command: <String>[
'ssh',
'-F',
'/ssh_config',
'123',
'cat /pkgfs/packages/build-info/0/data/version'
],
));
final FuchsiaDevice device = FuchsiaDevice('123', name: 'device');
@ -855,7 +959,8 @@ class MockFuchsiaDevice extends Fake implements FuchsiaDevice {
final DevicePortForwarder portForwarder;
@override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.fuchsia_arm64;
Future<TargetPlatform> get targetPlatform async =>
TargetPlatform.fuchsia_arm64;
@override
String get name => 'fuchsia';
@ -886,6 +991,16 @@ class FakeFuchsiaFfx implements FuchsiaFfx {
Future<String> resolve(String deviceName) async {
return '192.168.42.10';
}
@override
Future<String> sessionShow() async {
return null;
}
@override
Future<bool> sessionAdd(String url) async {
return false;
}
}
class FakeFuchsiaSdk extends Fake implements FuchsiaSdk {
@ -894,10 +1009,10 @@ class FakeFuchsiaSdk extends Fake implements FuchsiaSdk {
FuchsiaKernelCompiler compiler,
FuchsiaFfx ffx,
String devices,
}) : fuchsiaPM = pm,
fuchsiaKernelCompiler = compiler,
fuchsiaFfx = ffx ?? FakeFuchsiaFfx(),
_devices = devices;
}) : fuchsiaPM = pm,
fuchsiaKernelCompiler = compiler,
fuchsiaFfx = ffx ?? FakeFuchsiaFfx(),
_devices = devices;
@override
final FuchsiaPM fuchsiaPM;
@ -916,7 +1031,8 @@ class FakeFuchsiaSdk extends Fake implements FuchsiaSdk {
}
}
class FakeDartDevelopmentService extends Fake implements DartDevelopmentService {
class FakeDartDevelopmentService extends Fake
implements DartDevelopmentService {
@override
Future<void> startDartDevelopmentService(
Uri observatoryUri, {

View File

@ -43,14 +43,14 @@ void main() {
]);
const String expected = '\n'
'┌────────────────────────────────────────────────────────────────────────────\n'
'│ General Info \n'
'│ [✓] App Name: flutter_gallery \n'
'│ [✓] Supported Platforms: android, ios, web, macos, linux, windows, fuchsia\n'
'│ [✓] Is Flutter Package: yes \n'
'│ [✓] Uses Material Design: yes \n'
'│ [✓] Is Plugin: no \n'
'└────────────────────────────────────────────────────────────────────────────\n';
'┌───────────────────────────────────────────────────────────────────\n'
'│ General Info \n'
'│ [✓] App Name: flutter_gallery \n'
'│ [✓] Supported Platforms: android, ios, web, macos, linux, windows\n'
'│ [✓] Is Flutter Package: yes \n'
'│ [✓] Uses Material Design: yes \n'
'│ [✓] Is Plugin: no \n'
'└───────────────────────────────────────────────────────────────────\n';
expect(loggerTest.statusText, contains(expected));
});

View File

@ -252,9 +252,6 @@ class FakeStdin extends Fake implements Stdin {
@override
bool echoMode = true;
@override
bool echoNewlineMode = true;
@override
bool lineMode = true;

View File

@ -25,7 +25,8 @@ void main() {
}
fuchsiaVmServiceConnectionFunction = fakeServiceFunction;
final DartVm vm = await DartVm.connect(Uri.parse('http://this.whatever/ws'));
final DartVm vm =
await DartVm.connect(Uri.parse('http://this.whatever/ws'));
expect(vm, isNot(null));
await vm.stop();
expect(service.disposed, true);
@ -79,12 +80,14 @@ void main() {
Uri uri, {
Duration? timeout,
}) {
fakeService.flutterListViews = vms.Response.parse(flutterViewCannedResponses);
fakeService.flutterListViews =
vms.Response.parse(flutterViewCannedResponses);
return Future<vms.VmService>(() => fakeService);
}
fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction;
final DartVm vm = await DartVm.connect(Uri.parse('http://whatever.com/ws'));
final DartVm vm =
await DartVm.connect(Uri.parse('http://whatever.com/ws'));
expect(vm, isNot(null));
final List<FlutterView> views = await vm.getAllFlutterViews();
expect(views.length, 3);
@ -135,12 +138,14 @@ void main() {
Uri uri, {
Duration? timeout,
}) {
fakeService.flutterListViews = vms.Response.parse(flutterViewCannedResponses);
fakeService.flutterListViews =
vms.Response.parse(flutterViewCannedResponses);
return Future<vms.VmService>(() => fakeService);
}
fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction;
final DartVm vm = await DartVm.connect(Uri.parse('http://whatever.com/ws'));
final DartVm vm =
await DartVm.connect(Uri.parse('http://whatever.com/ws'));
expect(vm, isNot(null));
final List<FlutterView> views = await vm.getAllFlutterViews();
expect(views.length, 3);
@ -156,7 +161,8 @@ void main() {
});
test('invalid flutter view missing ID', () async {
final Map<String, dynamic> flutterViewCannedResponseMissingId = <String, dynamic>{
final Map<String, dynamic> flutterViewCannedResponseMissingId =
<String, dynamic>{
'views': <Map<String, dynamic>>[
// Valid flutter view.
<String, dynamic>{
@ -182,12 +188,14 @@ void main() {
Uri uri, {
Duration? timeout,
}) {
fakeService.flutterListViews = vms.Response.parse(flutterViewCannedResponseMissingId);
fakeService.flutterListViews =
vms.Response.parse(flutterViewCannedResponseMissingId);
return Future<vms.VmService>(() => fakeService);
}
fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction;
final DartVm vm = await DartVm.connect(Uri.parse('http://whatever.com/ws'));
final DartVm vm =
await DartVm.connect(Uri.parse('http://whatever.com/ws'));
expect(vm, isNot(null));
Future<void> failingFunction() async {
await vm.getAllFlutterViews();
@ -217,7 +225,7 @@ void main() {
'type': '@Isolate',
'fixedId': 'true',
'id': 'isolates/3',
'name': 'flutterBinary.cmx',
'name': 'flutterBinary.cm',
'number': '3',
})!,
vms.IsolateRef.parse(<String, dynamic>{
@ -238,16 +246,20 @@ void main() {
}
fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction;
final DartVm vm = await DartVm.connect(Uri.parse('http://whatever.com/ws'));
final DartVm vm =
await DartVm.connect(Uri.parse('http://whatever.com/ws'));
expect(vm, isNot(null));
final List<IsolateRef> matchingFlutterIsolates = await vm.getMainIsolatesByPattern('flutterBinary.cmx');
final List<IsolateRef> matchingFlutterIsolates =
await vm.getMainIsolatesByPattern('flutterBinary.cm');
expect(matchingFlutterIsolates.length, 1);
final List<IsolateRef> allIsolates = await vm.getMainIsolatesByPattern('');
final List<IsolateRef> allIsolates =
await vm.getMainIsolatesByPattern('');
expect(allIsolates.length, 4);
});
test('invalid flutter view missing ID', () async {
final Map<String, dynamic> flutterViewCannedResponseMissingIsolateName = <String, dynamic>{
final Map<String, dynamic> flutterViewCannedResponseMissingIsolateName =
<String, dynamic>{
'views': <Map<String, dynamic>>[
// Missing isolate name.
<String, dynamic>{
@ -267,12 +279,14 @@ void main() {
Uri uri, {
Duration? timeout,
}) {
fakeService.flutterListViews = vms.Response.parse(flutterViewCannedResponseMissingIsolateName);
fakeService.flutterListViews =
vms.Response.parse(flutterViewCannedResponseMissingIsolateName);
return Future<vms.VmService>(() => fakeService);
}
fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction;
final DartVm vm = await DartVm.connect(Uri.parse('http://whatever.com/ws'));
final DartVm vm =
await DartVm.connect(Uri.parse('http://whatever.com/ws'));
expect(vm, isNot(null));
Future<void> failingFunction() async {
await vm.getAllFlutterViews();
@ -298,7 +312,8 @@ class FakeVmService extends Fake implements vms.VmService {
}
@override
Future<vms.Response> callMethod(String method, {String? isolateId, Map<String, dynamic>? args}) async {
Future<vms.Response> callMethod(String method,
{String? isolateId, Map<String, dynamic>? args}) async {
if (method == '_flutter.listViews') {
return flutterListViews!;
}