diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index a505ebbe697..9571bbd286f 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -154,7 +154,17 @@ List generateCommands({ ], ), AssembleCommand(verboseHelp: verboseHelp, buildSystem: globals.buildSystem), - AttachCommand(verboseHelp: verboseHelp), + AttachCommand( + verboseHelp: verboseHelp, + artifacts: globals.artifacts, + stdio: globals.stdio, + logger: globals.logger, + terminal: globals.terminal, + signals: globals.signals, + platform: globals.platform, + processInfo: globals.processInfo, + fileSystem: globals.fs, + ), BuildCommand(verboseHelp: verboseHelp), ChannelCommand(verboseHelp: verboseHelp), CleanCommand(verbose: verbose), diff --git a/packages/flutter_tools/lib/src/commands/attach.dart b/packages/flutter_tools/lib/src/commands/attach.dart index 55e350958a5..82c60881d71 100644 --- a/packages/flutter_tools/lib/src/commands/attach.dart +++ b/packages/flutter_tools/lib/src/commands/attach.dart @@ -12,6 +12,10 @@ import '../base/common.dart'; import '../base/context.dart'; import '../base/file_system.dart'; import '../base/io.dart'; +import '../base/logger.dart'; +import '../base/platform.dart'; +import '../base/signals.dart'; +import '../base/terminal.dart'; import '../build_info.dart'; import '../commands/daemon.dart'; import '../compile.dart'; @@ -19,7 +23,6 @@ import '../daemon.dart'; import '../device.dart'; import '../device_port_forwarder.dart'; import '../fuchsia/fuchsia_device.dart'; -import '../globals.dart' as globals; import '../ios/devices.dart'; import '../ios/simulators.dart'; import '../macos/macos_ipad_device.dart'; @@ -58,7 +61,26 @@ import '../vmservice.dart'; /// To attach to a flutter mod running on a fuchsia device, `--module` must /// also be provided. class AttachCommand extends FlutterCommand { - AttachCommand({bool verboseHelp = false, this.hotRunnerFactory}) { + AttachCommand({ + bool verboseHelp = false, + HotRunnerFactory? hotRunnerFactory, + required Artifacts? artifacts, + required Stdio stdio, + required Logger logger, + required Terminal terminal, + required Signals signals, + required Platform platform, + required ProcessInfo processInfo, + required FileSystem fileSystem, + }): _artifacts = artifacts, + _hotRunnerFactory = hotRunnerFactory ?? HotRunnerFactory(), + _stdio = stdio, + _logger = logger, + _terminal = terminal, + _signals = signals, + _platform = platform, + _processInfo = processInfo, + _fileSystem = fileSystem { addBuildModeFlags(verboseHelp: verboseHelp, defaultToRelease: false, excludeRelease: true); usesTargetOption(); usesPortOptions(verboseHelp: verboseHelp); @@ -117,10 +139,17 @@ class AttachCommand extends FlutterCommand { addDdsOptions(verboseHelp: verboseHelp); addDevToolsOptions(verboseHelp: verboseHelp); usesDeviceTimeoutOption(); - hotRunnerFactory ??= HotRunnerFactory(); } - HotRunnerFactory? hotRunnerFactory; + final HotRunnerFactory _hotRunnerFactory; + final Artifacts? _artifacts; + final Stdio _stdio; + final Logger _logger; + final Terminal _terminal; + final Signals _signals; + final Platform _platform; + final ProcessInfo _processInfo; + final FileSystem _fileSystem; @override final String name = 'attach'; @@ -221,7 +250,7 @@ known, it can be explicitly provided to attach via the command-line, e.g. throwToolExit('Did not find any valid target devices.'); } - final Artifacts? overrideArtifacts = device.artifactOverrides ?? globals.artifacts; + final Artifacts? overrideArtifacts = device.artifactOverrides ?? _artifacts; await context.run( body: () => _attachToDevice(device), overrides: { @@ -238,12 +267,12 @@ known, it can be explicitly provided to attach via the command-line, e.g. final Daemon? daemon = boolArgDeprecated('machine') ? Daemon( DaemonConnection( - daemonStreams: DaemonStreams.fromStdio(globals.stdio, logger: globals.logger), - logger: globals.logger, + daemonStreams: DaemonStreams.fromStdio(_stdio, logger: _logger), + logger: _logger, ), - notifyingLogger: (globals.logger is NotifyingLogger) - ? globals.logger as NotifyingLogger - : NotifyingLogger(verbose: globals.logger.isVerbose, parent: globals.logger), + notifyingLogger: (_logger is NotifyingLogger) + ? _logger as NotifyingLogger + : NotifyingLogger(verbose: _logger.isVerbose, parent: _logger), logToStdout: true, ) : null; @@ -296,9 +325,9 @@ known, it can be explicitly provided to attach via the command-line, e.g. ipv6: ipv6!, devicePort: deviceVmservicePort, hostPort: hostVmservicePort, - logger: globals.logger, + logger: _logger, ); - globals.printStatus('Waiting for a connection from Flutter on ${device.name}...'); + _logger.printStatus('Waiting for a connection from Flutter on ${device.name}...'); observatoryUri = observatoryDiscovery.uris; // Determine ipv6 status from the scanned logs. usesIpv6 = observatoryDiscovery.ipv6; @@ -316,7 +345,7 @@ known, it can be explicitly provided to attach via the command-line, e.g. ).asBroadcastStream(); } - globals.terminal.usesTerminalUi = daemon == null; + _terminal.usesTerminalUi = daemon == null; try { int? result; @@ -343,9 +372,9 @@ known, it can be explicitly provided to attach via the command-line, e.g. device, null, true, - globals.fs.currentDirectory, + _fileSystem.currentDirectory, LaunchMode.attach, - globals.logger as AppRunLogger, + _logger as AppRunLogger, ); } on Exception catch (error) { throwToolExit(error.toString()); @@ -366,10 +395,10 @@ known, it can be explicitly provided to attach via the command-line, e.g. unawaited(onAppStart.future.whenComplete(() { terminalHandler = TerminalHandler( runner, - logger: globals.logger, - terminal: globals.terminal, - signals: globals.signals, - processInfo: globals.processInfo, + logger: _logger, + terminal: _terminal, + signals: _signals, + processInfo: _processInfo, reportReady: boolArgDeprecated('report-ready'), pidFile: stringArgDeprecated('pid-file'), ) @@ -389,7 +418,7 @@ known, it can be explicitly provided to attach via the command-line, e.g. if (runner.exited || !runner.isWaitingForObservatory) { break; } - globals.printStatus('Waiting for a new connection from Flutter on ${device.name}...'); + _logger.printStatus('Waiting for a new connection from Flutter on ${device.name}...'); } } on RPCError catch (err) { if (err.code == RPCErrorCodes.kServiceDisappeared) { @@ -422,7 +451,7 @@ known, it can be explicitly provided to attach via the command-line, e.g. targetModel: TargetModel(stringArgDeprecated('target-model')!), buildInfo: buildInfo, userIdentifier: userIdentifier, - platform: globals.platform, + platform: _platform, ); flutterDevice.observatoryUris = observatoryUris; final List flutterDevices = [flutterDevice]; @@ -434,7 +463,7 @@ known, it can be explicitly provided to attach via the command-line, e.g. ); return buildInfo.isDebug - ? hotRunnerFactory!.build( + ? _hotRunnerFactory.build( flutterDevices, target: targetFile, debuggingOptions: debuggingOptions, diff --git a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart index 4c7482befd2..e6e3526d4eb 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart @@ -13,13 +13,14 @@ import 'package:flutter_tools/src/base/dds.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/base/platform.dart'; +import 'package:flutter_tools/src/base/signals.dart'; import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/attach.dart'; import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device_port_forwarder.dart'; -import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/ios/application_package.dart'; import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/macos/macos_ipad_device.dart'; @@ -38,6 +39,16 @@ import '../../src/context.dart'; import '../../src/fake_devices.dart'; import '../../src/test_flutter_command_runner.dart'; +class FakeStdio extends Fake implements Stdio { + @override + bool stdinHasTerminal = false; +} + +class FakeProcessInfo extends Fake implements ProcessInfo { + @override + int maxRss = 0; +} + void main() { tearDown(() { MacOSDesignedForIPadDevices.allowDiscovery = false; @@ -47,17 +58,25 @@ void main() { late StreamLogger logger; late FileSystem testFileSystem; late TestDeviceManager testDeviceManager; + late Artifacts artifacts; + late Stdio stdio; + late Terminal terminal; + late Signals signals; + late Platform platform; + late ProcessInfo processInfo; setUp(() { Cache.disableLocking(); logger = StreamLogger(); - testFileSystem = MemoryFileSystem( - style: globals.platform.isWindows - ? FileSystemStyle.windows - : FileSystemStyle.posix, - ); + platform = FakePlatform(); + testFileSystem = MemoryFileSystem.test(); testFileSystem.directory('lib').createSync(); testFileSystem.file(testFileSystem.path.join('lib', 'main.dart')).createSync(); + artifacts = Artifacts.test(); + stdio = FakeStdio(); + terminal = FakeTerminal(); + signals = Signals.test(); + processInfo = FakeProcessInfo(); testDeviceManager = TestDeviceManager(logger: BufferLogger.test()); }); @@ -102,7 +121,16 @@ void main() { completer.complete(); } }); - final Future task = createTestCommandRunner(AttachCommand()).run(['attach']); + final Future task = createTestCommandRunner(AttachCommand( + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + )).run(['attach']); await completer.future; expect(portForwarder.devicePort, devicePort); @@ -137,7 +165,16 @@ void main() { completer.complete(); } }); - final Future task = createTestCommandRunner(AttachCommand()).run(['attach']); + final Future task = createTestCommandRunner(AttachCommand( + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + )).run(['attach']); await completer.future; expect(portForwarder.devicePort, devicePort); @@ -161,7 +198,16 @@ void main() { return fakeLogReader; }; testDeviceManager.devices = [device]; - expect(() => createTestCommandRunner(AttachCommand()).run(['attach']), throwsToolExit()); + expect(() => createTestCommandRunner(AttachCommand( + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + )).run(['attach']), throwsToolExit()); }, overrides: { FileSystem: () => testFileSystem, ProcessManager: () => FakeProcessManager.any(), @@ -197,6 +243,14 @@ void main() { final AttachCommand command = AttachCommand( hotRunnerFactory: hotRunnerFactory, + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, ); await createTestCommandRunner(command).run([ 'attach', @@ -232,7 +286,16 @@ void main() { testUsingContext('exits when ipv6 is specified and debug-port is not', () async { testDeviceManager.devices = [device]; - final AttachCommand command = AttachCommand(); + final AttachCommand command = AttachCommand( + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + ); await expectLater( createTestCommandRunner(command).run(['attach', '--ipv6']), throwsToolExit( @@ -254,7 +317,16 @@ void main() { }; testDeviceManager.devices = [device]; - final AttachCommand command = AttachCommand(); + final AttachCommand command = AttachCommand( + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + ); await expectLater( createTestCommandRunner(command).run(['attach', '--observatory-port', '100']), throwsToolExit( @@ -294,7 +366,16 @@ void main() { completer.complete(); } }); - final Future task = createTestCommandRunner(AttachCommand()) + final Future task = createTestCommandRunner(AttachCommand( + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + )) .run(['attach', '--debug-port', '$devicePort']); await completer.future; expect(portForwarder.devicePort, devicePort); @@ -320,7 +401,16 @@ void main() { completer.complete(); } }); - final Future task = createTestCommandRunner(AttachCommand()) + final Future task = createTestCommandRunner(AttachCommand( + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + )) .run(['attach', '--debug-port', '$devicePort', '--ipv6']); await completer.future; @@ -347,7 +437,16 @@ void main() { completer.complete(); } }); - final Future task = createTestCommandRunner(AttachCommand()).run( + final Future task = createTestCommandRunner(AttachCommand( + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + )).run( [ 'attach', '--debug-port', @@ -383,7 +482,16 @@ void main() { completer.complete(); } }); - final Future task = createTestCommandRunner(AttachCommand()).run( + final Future task = createTestCommandRunner(AttachCommand( + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + )).run( [ 'attach', '--debug-port', @@ -411,7 +519,16 @@ void main() { }); testUsingContext('exits when no device connected', () async { - final AttachCommand command = AttachCommand(); + final AttachCommand command = AttachCommand( + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + ); await expectLater( createTestCommandRunner(command).run(['attach']), throwsToolExit(), @@ -426,7 +543,16 @@ void main() { testUsingContext('fails when targeted device is not Android with --device-user', () async { final FakeIOSDevice device = FakeIOSDevice(); testDeviceManager.devices = [device]; - expect(createTestCommandRunner(AttachCommand()).run([ + expect(createTestCommandRunner(AttachCommand( + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + )).run([ 'attach', '--device-user', '10', @@ -438,7 +564,16 @@ void main() { }); testUsingContext('exits when multiple devices connected', () async { - final AttachCommand command = AttachCommand(); + final AttachCommand command = AttachCommand( + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + ); testDeviceManager.devices = [ FakeAndroidDevice(id: 'xx1'), FakeAndroidDevice(id: 'yy2'), @@ -478,7 +613,17 @@ void main() { testDeviceManager.devices = [device]; testFileSystem.file('lib/main.dart').createSync(); - final AttachCommand command = AttachCommand(hotRunnerFactory: hotRunnerFactory); + final AttachCommand command = AttachCommand( + hotRunnerFactory: hotRunnerFactory, + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + ); await expectLater(createTestCommandRunner(command).run([ 'attach', ]), throwsToolExit(message: 'Lost connection to device.')); @@ -509,7 +654,17 @@ void main() { testDeviceManager.devices = [device]; testFileSystem.file('lib/main.dart').createSync(); - final AttachCommand command = AttachCommand(hotRunnerFactory: hotRunnerFactory); + final AttachCommand command = AttachCommand( + hotRunnerFactory: hotRunnerFactory, + artifacts: artifacts, + stdio: stdio, + logger: logger, + terminal: terminal, + signals: signals, + platform: platform, + processInfo: processInfo, + fileSystem: testFileSystem, + ); await expectLater(createTestCommandRunner(command).run([ 'attach', ]), throwsA(isA())); @@ -709,7 +864,7 @@ class StreamLogger extends Logger { bool get hasTerminal => false; @override - void clear() => _log('[stdout] ${globals.terminal.clearScreen()}\n'); + void clear() => _log('[stdout] ${terminal.clearScreen()}\n'); @override Terminal get terminal => Terminal.test(); @@ -936,4 +1091,7 @@ class FakeTerminal extends Fake implements AnsiTerminal { @override final bool stdinHasTerminal; + + @override + bool usesTerminalUi = false; } diff --git a/packages/flutter_tools/test/general.shard/commands/build_test.dart b/packages/flutter_tools/test/general.shard/commands/build_test.dart index 5f7d9f1bbf8..7097ecf615d 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_test.dart @@ -4,7 +4,16 @@ import 'package:args/args.dart'; import 'package:args/command_runner.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_tools/src/artifacts.dart'; +import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/io.dart'; +import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/base/platform.dart'; +import 'package:flutter_tools/src/base/signals.dart'; +import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/build_info.dart'; +import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/commands/attach.dart'; import 'package:flutter_tools/src/commands/build.dart'; import 'package:flutter_tools/src/commands/build_aar.dart'; @@ -16,17 +25,33 @@ import 'package:flutter_tools/src/commands/build_linux.dart'; import 'package:flutter_tools/src/commands/build_macos.dart'; import 'package:flutter_tools/src/commands/build_web.dart'; import 'package:flutter_tools/src/commands/build_windows.dart'; -import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/runner/flutter_command.dart'; +import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/fakes.dart'; + +class FakeTerminal extends Fake implements AnsiTerminal { + FakeTerminal({this.stdinHasTerminal = true}); + + @override + final bool stdinHasTerminal; +} + +class FakeProcessInfo extends Fake implements ProcessInfo { + @override + int maxRss = 0; +} void main() { testUsingContext('All build commands support null safety options', () { + final FileSystem fileSystem = MemoryFileSystem.test(); + final Platform platform = FakePlatform(); + final BufferLogger logger = BufferLogger.test(); final List commands = [ BuildWindowsCommand(), - BuildLinuxCommand(operatingSystemUtils: globals.os), + BuildLinuxCommand(operatingSystemUtils: FakeOperatingSystemUtils()), BuildMacosCommand(verboseHelp: false), BuildWebCommand(verboseHelp: false), BuildApkCommand(), @@ -36,9 +61,22 @@ void main() { BuildAarCommand(verboseHelp: false), BuildIOSFrameworkCommand( verboseHelp: false, - buildSystem: globals.buildSystem, + buildSystem: FlutterBuildSystem( + fileSystem: fileSystem, + platform: platform, + logger: logger, + ), + ), + AttachCommand( + artifacts: Artifacts.test(), + stdio: FakeStdio(), + logger: logger, + terminal: FakeTerminal(), + signals: Signals.test(), + platform: platform, + processInfo: FakeProcessInfo(), + fileSystem: MemoryFileSystem.test(), ), - AttachCommand(), ]; for (final FlutterCommand command in commands) {