
SkSL precompilation was only ever beneficial for iOS. For other platforms, we recommended against it as Skia generated shaders per target architecture which could be invalid on other devices. It is no longer possible to use Skia on iOS. Delete all Skia shader bundling logic. Fixes https://github.com/flutter/flutter/issues/80091
516 lines
18 KiB
Dart
516 lines
18 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:fake_async/fake_async.dart';
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/application_package.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/base/os.dart';
|
|
import 'package:flutter_tools/src/build_info.dart';
|
|
import 'package:flutter_tools/src/desktop_device.dart';
|
|
import 'package:flutter_tools/src/devfs.dart';
|
|
import 'package:flutter_tools/src/device.dart';
|
|
import 'package:flutter_tools/src/device_port_forwarder.dart';
|
|
import 'package:flutter_tools/src/macos/macos_device.dart';
|
|
import 'package:flutter_tools/src/project.dart';
|
|
|
|
import 'package:test/fake.dart';
|
|
|
|
import '../src/common.dart';
|
|
import '../src/context.dart';
|
|
|
|
void main() {
|
|
group('Basic info', () {
|
|
testWithoutContext('Category is desktop', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
|
|
expect(device.category, Category.desktop);
|
|
});
|
|
|
|
testWithoutContext('Not an emulator', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
|
|
expect(await device.isLocalEmulator, false);
|
|
expect(await device.emulatorId, null);
|
|
});
|
|
|
|
testWithoutContext('Uses OS name as SDK name', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
|
|
expect(await device.sdkNameAndVersion, 'Example');
|
|
});
|
|
});
|
|
|
|
group('Install', () {
|
|
testWithoutContext('Install checks always return true', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
|
|
expect(await device.isAppInstalled(FakeApplicationPackage()), true);
|
|
expect(await device.isLatestBuildInstalled(FakeApplicationPackage()), true);
|
|
expect(device.category, Category.desktop);
|
|
});
|
|
|
|
testWithoutContext('Install and uninstall are no-ops that report success', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
|
|
expect(await device.uninstallApp(package), true);
|
|
expect(await device.isAppInstalled(package), true);
|
|
expect(await device.isLatestBuildInstalled(package), true);
|
|
|
|
expect(await device.installApp(package), true);
|
|
expect(await device.isAppInstalled(package), true);
|
|
expect(await device.isLatestBuildInstalled(package), true);
|
|
expect(device.category, Category.desktop);
|
|
});
|
|
});
|
|
|
|
group('Starting and stopping application', () {
|
|
testWithoutContext('Stop without start is a successful no-op', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
|
|
expect(await device.stopApp(package), true);
|
|
});
|
|
|
|
testWithoutContext('Can run from prebuilt application', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final Completer<void> completer = Completer<void>();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>['debug'],
|
|
stdout: 'The Dart VM service is listening on http://127.0.0.1/0\n',
|
|
completer: completer,
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(
|
|
processManager: processManager,
|
|
fileSystem: fileSystem,
|
|
);
|
|
final String? executableName = device.executablePathForDevice(
|
|
FakeApplicationPackage(),
|
|
BuildInfo.debug,
|
|
);
|
|
fileSystem.file(executableName).writeAsStringSync('\n');
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
final LaunchResult result = await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
);
|
|
|
|
expect(result.started, true);
|
|
expect(result.vmServiceUri, Uri.parse('http://127.0.0.1/0'));
|
|
});
|
|
|
|
testWithoutContext('Null executable path fails gracefully', () async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final DesktopDevice device = setUpDesktopDevice(
|
|
nullExecutablePathForDevice: true,
|
|
logger: logger,
|
|
);
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
final LaunchResult result = await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
);
|
|
|
|
expect(result.started, false);
|
|
expect(logger.errorText, contains('Unable to find executable to run'));
|
|
});
|
|
|
|
testWithoutContext('stopApp kills process started by startApp', () async {
|
|
final Completer<void> completer = Completer<void>();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>['debug'],
|
|
stdout: 'The Dart VM service is listening on http://127.0.0.1/0\n',
|
|
completer: completer,
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager);
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
final LaunchResult result = await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
);
|
|
|
|
expect(result.started, true);
|
|
expect(await device.stopApp(package), true);
|
|
});
|
|
});
|
|
|
|
testWithoutContext(
|
|
'startApp supports DebuggingOptions through FLUTTER_ENGINE_SWITCH environment variables',
|
|
() async {
|
|
final Completer<void> completer = Completer<void>();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>['debug'],
|
|
stdout: 'The Dart VM service is listening on http://127.0.0.1/0\n',
|
|
completer: completer,
|
|
environment: const <String, String>{
|
|
'FLUTTER_ENGINE_SWITCH_1': 'enable-dart-profiling=true',
|
|
'FLUTTER_ENGINE_SWITCH_2': 'trace-startup=true',
|
|
'FLUTTER_ENGINE_SWITCH_3': 'enable-software-rendering=true',
|
|
'FLUTTER_ENGINE_SWITCH_4': 'skia-deterministic-rendering=true',
|
|
'FLUTTER_ENGINE_SWITCH_5': 'trace-skia=true',
|
|
'FLUTTER_ENGINE_SWITCH_6': 'trace-allowlist=foo,bar',
|
|
'FLUTTER_ENGINE_SWITCH_7': 'trace-skia-allowlist=skia.a,skia.b',
|
|
'FLUTTER_ENGINE_SWITCH_8': 'trace-systrace=true',
|
|
'FLUTTER_ENGINE_SWITCH_9': 'trace-to-file=path/to/trace.binpb',
|
|
'FLUTTER_ENGINE_SWITCH_10': 'endless-trace-buffer=true',
|
|
'FLUTTER_ENGINE_SWITCH_11': 'purge-persistent-cache=true',
|
|
'FLUTTER_ENGINE_SWITCH_12': 'enable-impeller=false',
|
|
'FLUTTER_ENGINE_SWITCH_13': 'enable-checked-mode=true',
|
|
'FLUTTER_ENGINE_SWITCH_14': 'verify-entry-points=true',
|
|
'FLUTTER_ENGINE_SWITCH_15': 'start-paused=true',
|
|
'FLUTTER_ENGINE_SWITCH_16': 'disable-service-auth-codes=true',
|
|
'FLUTTER_ENGINE_SWITCH_17': 'dart-flags=--null_assertions',
|
|
'FLUTTER_ENGINE_SWITCH_18': 'use-test-fonts=true',
|
|
'FLUTTER_ENGINE_SWITCH_19': 'verbose-logging=true',
|
|
'FLUTTER_ENGINE_SWITCHES': '19',
|
|
},
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager);
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
final LaunchResult result = await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
platformArgs: <String, Object>{'trace-startup': true},
|
|
debuggingOptions: DebuggingOptions.enabled(
|
|
BuildInfo.debug,
|
|
startPaused: true,
|
|
disableServiceAuthCodes: true,
|
|
enableSoftwareRendering: true,
|
|
skiaDeterministicRendering: true,
|
|
traceSkia: true,
|
|
traceAllowlist: 'foo,bar',
|
|
traceSkiaAllowlist: 'skia.a,skia.b',
|
|
traceSystrace: true,
|
|
traceToFile: 'path/to/trace.binpb',
|
|
endlessTraceBuffer: true,
|
|
purgePersistentCache: true,
|
|
useTestFonts: true,
|
|
verboseSystemLogs: true,
|
|
nullAssertions: true,
|
|
),
|
|
);
|
|
|
|
expect(result.started, true);
|
|
},
|
|
);
|
|
|
|
testWithoutContext(
|
|
'startApp supports DebuggingOptions through FLUTTER_ENGINE_SWITCH environment variables when debugging is disabled',
|
|
() async {
|
|
final Completer<void> completer = Completer<void>();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>['debug'],
|
|
stdout: 'The Dart VM service is listening on http://127.0.0.1/0\n',
|
|
completer: completer,
|
|
environment: const <String, String>{
|
|
'FLUTTER_ENGINE_SWITCH_1': 'enable-dart-profiling=true',
|
|
'FLUTTER_ENGINE_SWITCH_2': 'trace-startup=true',
|
|
'FLUTTER_ENGINE_SWITCH_3': 'trace-allowlist=foo,bar',
|
|
'FLUTTER_ENGINE_SWITCH_4': 'enable-impeller=false',
|
|
'FLUTTER_ENGINE_SWITCHES': '4',
|
|
},
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager);
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
final LaunchResult result = await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
platformArgs: <String, Object>{'trace-startup': true},
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug, traceAllowlist: 'foo,bar'),
|
|
);
|
|
|
|
expect(result.started, true);
|
|
},
|
|
);
|
|
|
|
testWithoutContext('Port forwarder is a no-op', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
final DevicePortForwarder portForwarder = device.portForwarder;
|
|
final int result = await portForwarder.forward(2);
|
|
|
|
expect(result, 2);
|
|
expect(portForwarder.forwardedPorts.isEmpty, true);
|
|
});
|
|
|
|
testWithoutContext('createDevFSWriter returns a LocalDevFSWriter', () {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
|
|
expect(device.createDevFSWriter(FakeApplicationPackage(), ''), isA<LocalDevFSWriter>());
|
|
});
|
|
|
|
testWithoutContext('startApp supports dartEntrypointArgs', () async {
|
|
final Completer<void> completer = Completer<void>();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>['debug', 'arg1', 'arg2'],
|
|
stdout: 'The Dart VM service is listening on http://127.0.0.1/0\n',
|
|
completer: completer,
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager);
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
final LaunchResult result = await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(
|
|
BuildInfo.debug,
|
|
dartEntrypointArgs: <String>['arg1', 'arg2'],
|
|
),
|
|
);
|
|
|
|
expect(result.started, true);
|
|
});
|
|
|
|
testWithoutContext('Device logger captures all output', () async {
|
|
final Completer<void> exitCompleter = Completer<void>();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>['debug', 'arg1', 'arg2'],
|
|
exitCode: -1,
|
|
stderr: 'Oops\n',
|
|
completer: exitCompleter,
|
|
outputFollowsExit: true,
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager);
|
|
unawaited(
|
|
Future<void>(() {
|
|
exitCompleter.complete();
|
|
}),
|
|
);
|
|
|
|
// Start looking for 'Oops' in the stream before starting the app.
|
|
expect(device.getLogReader().logLines, emits('Oops'));
|
|
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(
|
|
BuildInfo.debug,
|
|
dartEntrypointArgs: <String>['arg1', 'arg2'],
|
|
),
|
|
);
|
|
});
|
|
|
|
testWithoutContext('Desktop devices pass through the enable-impeller flag', () async {
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
const FakeCommand(
|
|
command: <String>['debug'],
|
|
exitCode: -1,
|
|
environment: <String, String>{
|
|
'FLUTTER_ENGINE_SWITCH_1': 'enable-dart-profiling=true',
|
|
'FLUTTER_ENGINE_SWITCH_2': 'enable-impeller=true',
|
|
'FLUTTER_ENGINE_SWITCH_3': 'enable-checked-mode=true',
|
|
'FLUTTER_ENGINE_SWITCH_4': 'verify-entry-points=true',
|
|
'FLUTTER_ENGINE_SWITCHES': '4',
|
|
},
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager);
|
|
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(
|
|
BuildInfo.debug,
|
|
enableImpeller: ImpellerStatus.enabled,
|
|
dartEntrypointArgs: <String>[],
|
|
),
|
|
);
|
|
});
|
|
|
|
testWithoutContext('Desktop devices pass through the --no-enable-impeller flag', () async {
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
const FakeCommand(
|
|
command: <String>['debug'],
|
|
exitCode: -1,
|
|
environment: <String, String>{
|
|
'FLUTTER_ENGINE_SWITCH_1': 'enable-dart-profiling=true',
|
|
'FLUTTER_ENGINE_SWITCH_2': 'enable-impeller=false',
|
|
'FLUTTER_ENGINE_SWITCH_3': 'enable-checked-mode=true',
|
|
'FLUTTER_ENGINE_SWITCH_4': 'verify-entry-points=true',
|
|
'FLUTTER_ENGINE_SWITCHES': '4',
|
|
},
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager);
|
|
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(
|
|
BuildInfo.debug,
|
|
enableImpeller: ImpellerStatus.disabled,
|
|
dartEntrypointArgs: <String>[],
|
|
),
|
|
);
|
|
});
|
|
|
|
testUsingContext(
|
|
'macOS devices print warning if Dart VM not found within timeframe in CI',
|
|
() async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final FakeMacOSDevice device = FakeMacOSDevice(
|
|
fileSystem: MemoryFileSystem.test(),
|
|
processManager: FakeProcessManager.any(),
|
|
operatingSystemUtils: FakeOperatingSystemUtils(),
|
|
logger: logger,
|
|
);
|
|
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
|
|
FakeAsync().run((FakeAsync fakeAsync) {
|
|
device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(
|
|
BuildInfo.debug,
|
|
enableImpeller: ImpellerStatus.disabled,
|
|
dartEntrypointArgs: <String>[],
|
|
usingCISystem: true,
|
|
),
|
|
);
|
|
fakeAsync.flushTimers();
|
|
expect(
|
|
logger.errorText,
|
|
contains('Ensure sandboxing is disabled by checking the set CODE_SIGN_ENTITLEMENTS'),
|
|
);
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
FakeDesktopDevice setUpDesktopDevice({
|
|
FileSystem? fileSystem,
|
|
Logger? logger,
|
|
ProcessManager? processManager,
|
|
OperatingSystemUtils? operatingSystemUtils,
|
|
bool nullExecutablePathForDevice = false,
|
|
}) {
|
|
return FakeDesktopDevice(
|
|
fileSystem: fileSystem ?? MemoryFileSystem.test(),
|
|
logger: logger ?? BufferLogger.test(),
|
|
processManager: processManager ?? FakeProcessManager.any(),
|
|
operatingSystemUtils: operatingSystemUtils ?? FakeOperatingSystemUtils(),
|
|
nullExecutablePathForDevice: nullExecutablePathForDevice,
|
|
);
|
|
}
|
|
|
|
/// A trivial subclass of DesktopDevice for testing the shared functionality.
|
|
class FakeDesktopDevice extends DesktopDevice {
|
|
FakeDesktopDevice({
|
|
required ProcessManager processManager,
|
|
required Logger logger,
|
|
required FileSystem fileSystem,
|
|
required OperatingSystemUtils operatingSystemUtils,
|
|
this.nullExecutablePathForDevice = false,
|
|
}) : super(
|
|
'dummy',
|
|
platformType: PlatformType.linux,
|
|
ephemeral: false,
|
|
processManager: processManager,
|
|
logger: logger,
|
|
fileSystem: fileSystem,
|
|
operatingSystemUtils: operatingSystemUtils,
|
|
);
|
|
|
|
/// The [mainPath] last passed to [buildForDevice].
|
|
String? lastBuiltMainPath;
|
|
|
|
/// The [buildInfo] last passed to [buildForDevice].
|
|
BuildInfo? lastBuildInfo;
|
|
|
|
final bool nullExecutablePathForDevice;
|
|
|
|
@override
|
|
String get name => 'dummy';
|
|
|
|
@override
|
|
Future<TargetPlatform> get targetPlatform async => TargetPlatform.tester;
|
|
|
|
@override
|
|
bool isSupported() => true;
|
|
|
|
@override
|
|
bool isSupportedForProject(FlutterProject flutterProject) => true;
|
|
|
|
@override
|
|
Future<void> buildForDevice({
|
|
String? mainPath,
|
|
BuildInfo? buildInfo,
|
|
bool usingCISystem = false,
|
|
}) async {
|
|
lastBuiltMainPath = mainPath;
|
|
lastBuildInfo = buildInfo;
|
|
}
|
|
|
|
// Dummy implementation that just returns the build mode name.
|
|
@override
|
|
String? executablePathForDevice(ApplicationPackage package, BuildInfo buildInfo) {
|
|
if (nullExecutablePathForDevice) {
|
|
return null;
|
|
}
|
|
return buildInfo.mode.cliName;
|
|
}
|
|
}
|
|
|
|
class FakeApplicationPackage extends Fake implements ApplicationPackage {}
|
|
|
|
class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils {
|
|
@override
|
|
String get name => 'Example';
|
|
}
|
|
|
|
class FakeMacOSDevice extends MacOSDevice {
|
|
FakeMacOSDevice({
|
|
required super.processManager,
|
|
required super.logger,
|
|
required super.fileSystem,
|
|
required super.operatingSystemUtils,
|
|
});
|
|
|
|
@override
|
|
String get name => 'dummy';
|
|
|
|
@override
|
|
Future<TargetPlatform> get targetPlatform async => TargetPlatform.tester;
|
|
|
|
@override
|
|
bool isSupported() => true;
|
|
|
|
@override
|
|
bool isSupportedForProject(FlutterProject flutterProject) => true;
|
|
|
|
@override
|
|
Future<void> buildForDevice({
|
|
String? mainPath,
|
|
BuildInfo? buildInfo,
|
|
bool usingCISystem = false,
|
|
}) async {}
|
|
|
|
// Dummy implementation that just returns the build mode name.
|
|
@override
|
|
String? executablePathForDevice(ApplicationPackage package, BuildInfo buildInfo) {
|
|
return buildInfo.mode.cliName;
|
|
}
|
|
}
|