Add a --print-dtd flag to print the DTD address served by DevTools server (#144272)

This commit is contained in:
Kenzie Davisson 2024-03-25 13:04:18 -07:00 committed by GitHub
parent dbdcead932
commit 31f4f2b6c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 326 additions and 191 deletions

View file

@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'runner.dart' as runner;
import 'src/artifacts.dart';
import 'src/base/context.dart';
import 'src/base/io.dart';
import 'src/base/logger.dart';
@ -110,7 +109,7 @@ Future<void> main(List<String> args) async {
// devtools source code.
DevtoolsLauncher: () => DevtoolsServerLauncher(
processManager: globals.processManager,
dartExecutable: globals.artifacts!.getArtifactPath(Artifact.engineDartBinary),
artifacts: globals.artifacts!,
logger: globals.logger,
botDetector: globals.botDetector,
),

View file

@ -218,7 +218,7 @@ Future<T> runInContext<T>(
),
DevtoolsLauncher: () => DevtoolsServerLauncher(
processManager: globals.processManager,
dartExecutable: globals.artifacts!.getArtifactPath(Artifact.engineDartBinary),
artifacts: globals.artifacts!,
logger: globals.logger,
botDetector: globals.botDetector,
),

View file

@ -2,13 +2,12 @@
// 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:meta/meta.dart';
import 'package:process/process.dart';
import 'artifacts.dart';
import 'base/bot_detector.dart';
import 'base/common.dart';
import 'base/io.dart' as io;
@ -16,21 +15,22 @@ import 'base/logger.dart';
import 'convert.dart';
import 'resident_runner.dart';
/// An implementation of the devtools launcher that uses `pub global activate` to
/// start a server instance.
/// An implementation of the devtools launcher that uses `dart devtools` to
/// start a DevTools server instance.
class DevtoolsServerLauncher extends DevtoolsLauncher {
DevtoolsServerLauncher({
required ProcessManager processManager,
required String dartExecutable,
required Logger logger,
required BotDetector botDetector,
required Artifacts artifacts,
}) : _processManager = processManager,
_dartExecutable = dartExecutable,
_logger = logger,
_botDetector = botDetector;
_botDetector = botDetector,
_artifacts = artifacts;
final ProcessManager _processManager;
final String _dartExecutable;
final Artifacts _artifacts;
late final String _dartExecutable = _artifacts.getArtifactPath(Artifact.engineDartBinary);
final Logger _logger;
final BotDetector _botDetector;
final Completer<void> _processStartCompleter = Completer<void>();
@ -42,6 +42,8 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
static final RegExp _serveDevToolsPattern =
RegExp(r'Serving DevTools at ((http|//)[a-zA-Z0-9:/=_\-\.\[\]]+?)\.?$');
static final RegExp _serveDtdPattern =
RegExp(r'Serving the Dart Tooling Daemon at (ws:\/\/[a-zA-Z0-9:/=_\-\.\[\]]+?)\.?$');
@override
Future<void> get processStart => _processStartCompleter.future;
@ -55,21 +57,28 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
_dartExecutable,
'devtools',
'--no-launch-browser',
if (printDtdUri) '--print-dtd',
if (vmServiceUri != null) '--vm-uri=$vmServiceUri',
...?additionalArguments,
]);
_processStartCompleter.complete();
final Completer<Uri> completer = Completer<Uri>();
final Completer<Uri> devToolsCompleter = Completer<Uri>();
_devToolsProcess!.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen((String line) {
final Match? match = _serveDevToolsPattern.firstMatch(line);
if (match != null) {
final String url = match[1]!;
completer.complete(Uri.parse(url));
}
});
final Match? dtdMatch = _serveDtdPattern.firstMatch(line);
if (dtdMatch != null) {
final String uri = dtdMatch[1]!;
dtdUri = Uri.parse(uri);
}
final Match? devToolsMatch = _serveDevToolsPattern.firstMatch(line);
if (devToolsMatch != null) {
final String url = devToolsMatch[1]!;
devToolsCompleter.complete(Uri.parse(url));
}
});
_devToolsProcess!.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
@ -84,7 +93,11 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
}
);
devToolsUrl = await completer.future;
// We do not need to wait for a [Completer] holding the DTD URI because
// the DTD URI will be output to stdout before the DevTools URI. Awaiting
// a [Completer] for the DevTools URI ensures both values will be
// populated before returning.
devToolsUrl = await devToolsCompleter.future;
} on Exception catch (e, st) {
_logger.printError('Failed to launch DevTools: $e', stackTrace: st);
}

View file

@ -24,6 +24,19 @@ abstract class ResidentDevtoolsHandler {
/// The current devtools server, or null if one is not running.
DevToolsServerAddress? get activeDevToolsServer;
/// The Dart Tooling Daemon (DTD) URI for the DTD instance being hosted by
/// DevTools server.
///
/// This will be null if the DevTools server is not served through Flutter
/// tools (e.g. if it is served from an IDE).
Uri? get dtdUri;
/// Whether to print the Dart Tooling Daemon URI.
///
/// This will always return false when there is not a DTD instance being
/// served from the DevTools server.
bool get printDtdUri;
/// Whether it's ok to announce the [activeDevToolsServer].
///
/// This should only return true once all the devices have been notified
@ -63,6 +76,12 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler {
return _devToolsLauncher?.activeDevToolsServer;
}
@override
Uri? get dtdUri => _devToolsLauncher?.dtdUri;
@override
bool get printDtdUri => _devToolsLauncher?.printDtdUri ?? false;
@override
bool get readyToAnnounce => _readyToAnnounce;
bool _readyToAnnounce = false;
@ -337,6 +356,12 @@ class NoOpDevtoolsHandler implements ResidentDevtoolsHandler {
wasShutdown = true;
return;
}
@override
Uri? get dtdUri => null;
@override
bool get printDtdUri => false;
}
/// Convert a [URI] with query parameters into a display format instead

View file

@ -1526,6 +1526,12 @@ abstract class ResidentRunner extends ResidentHandlers {
);
}
if (includeDevtools) {
if (_residentDevtoolsHandler!.printDtdUri) {
final Uri? dtdUri = residentDevtoolsHandler!.dtdUri;
if (dtdUri != null) {
globals.printStatus('The Dart Tooling Daemon is available at: $dtdUri\n');
}
}
final Uri? uri = devToolsServerAddress!.uri?.replace(
queryParameters: <String, dynamic>{'uri': '${device.vmService!.httpAddress}'},
);
@ -1945,6 +1951,26 @@ abstract class DevtoolsLauncher {
}
}
/// The Dart Tooling Daemon (DTD) URI for the DTD instance being hosted by
/// DevTools server.
///
/// This will be null if the DevTools server is not served through Flutter
/// tools (e.g. if it is served from an IDE).
Uri? get dtdUri => _dtdUri;
Uri? _dtdUri;
@protected
set dtdUri(Uri? value) => _dtdUri = value;
/// Whether to print the Dart Tooling Daemon URI.
///
/// This will always return false when there is not a DTD instance being
/// served from the DevTools server.
bool get printDtdUri => _printDtdUri ?? false;
bool? _printDtdUri;
set printDtdUri(bool value) {
_printDtdUri = value;
}
/// The URL of the current DevTools server.
///
/// Returns null if [ready] is not complete.

View file

@ -17,6 +17,7 @@ import '../base/utils.dart';
import '../cache.dart';
import '../convert.dart';
import '../globals.dart' as globals;
import '../resident_runner.dart';
import '../tester/flutter_tester.dart';
import '../version.dart';
import '../web/web_device.dart';
@ -35,6 +36,7 @@ abstract final class FlutterGlobalOptions {
static const String kMachineFlag = 'machine';
static const String kPackagesOption = 'packages';
static const String kPrefixedErrorsFlag = 'prefixed-errors';
static const String kPrintDtd = 'print-dtd';
static const String kQuietFlag = 'quiet';
static const String kShowTestDeviceFlag = 'show-test-device';
static const String kShowWebServerDeviceFlag = 'show-web-server-device';
@ -116,6 +118,12 @@ class FlutterCommandRunner extends CommandRunner<void> {
argParser.addOption(FlutterGlobalOptions.kPackagesOption,
hide: !verboseHelp,
help: 'Path to your "package_config.json" file.');
argParser.addFlag(
FlutterGlobalOptions.kPrintDtd,
negatable: false,
help: 'Print the address of the Dart Tooling Daemon, if one is hosted by the Flutter CLI.',
hide: !verboseHelp,
);
if (verboseHelp) {
argParser.addSeparator('Local build selection options (not normally required):');
}
@ -357,6 +365,10 @@ class FlutterCommandRunner extends CommandRunner<void> {
if (machineFlag && topLevelResults.command?.name != 'analyze') {
throwToolExit('The "--machine" flag is only valid with the "--version" flag or the "analyze --suggestions" command.', exitCode: 2);
}
final bool shouldPrintDtdUri = topLevelResults[FlutterGlobalOptions.kPrintDtd] as bool? ?? false;
DevtoolsLauncher.instance!.printDtdUri = shouldPrintDtdUri;
await super.runCommand(topLevelResults);
},
);

View file

@ -824,7 +824,7 @@ void main() {
expect(result['host'], '127.0.0.1');
expect(result['port'], 1234);
}, overrides: <Type, Generator>{
DevtoolsLauncher: () => FakeDevtoolsLauncher(DevToolsServerAddress('127.0.0.1', 1234)),
DevtoolsLauncher: () => FakeDevtoolsLauncher(serverAddress: DevToolsServerAddress('127.0.0.1', 1234)),
});
testUsingContext('devtools.serve command should return null fields if null returned', () async {
@ -840,7 +840,7 @@ void main() {
expect(result['host'], null);
expect(result['port'], null);
}, overrides: <Type, Generator>{
DevtoolsLauncher: () => FakeDevtoolsLauncher(null),
DevtoolsLauncher: () => FakeDevtoolsLauncher(),
});
testUsingContext('proxy.connect tries to connect to an ipv4 address and proxies the connection correctly', () async {
@ -1286,18 +1286,6 @@ class FakeDeviceLogReader implements DeviceLogReader {
}
class FakeDevtoolsLauncher extends Fake implements DevtoolsLauncher {
FakeDevtoolsLauncher(this._serverAddress);
final DevToolsServerAddress? _serverAddress;
@override
Future<DevToolsServerAddress?> serve() async => _serverAddress;
@override
Future<void> close() async {}
}
class FakeApplicationPackageFactory implements ApplicationPackageFactory {
TargetPlatform? platformRequested;
File? applicationBinaryRequested;

View file

@ -4,6 +4,7 @@
import 'dart:async';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/cache.dart';
@ -15,52 +16,87 @@ import '../src/fake_process_manager.dart';
import '../src/fakes.dart';
void main() {
late BufferLogger logger;
Cache.flutterRoot = '';
setUp(() {
logger = BufferLogger.test();
});
(BufferLogger, Artifacts) getTestState() => (BufferLogger.test(), Artifacts.test());
testWithoutContext('DevtoolsLauncher launches DevTools from the SDK and saves the URI', () async {
final (BufferLogger logger, Artifacts artifacts) = getTestState();
final Completer<void> completer = Completer<void>();
final DevtoolsLauncher launcher = DevtoolsServerLauncher(
dartExecutable: 'dart',
artifacts: artifacts,
logger: logger,
botDetector: const FakeBotDetector(false),
processManager: FakeProcessManager.list(<FakeCommand>[
FakeCommand(
command: const <String>[
'dart',
'Artifact.engineDartBinary',
'devtools',
'--no-launch-browser',
],
stdout: 'Serving DevTools at http://127.0.0.1:9100\n',
stdout: 'Serving DevTools at http://127.0.0.1:9100.\n',
completer: completer,
),
]),
);
expect(launcher.dtdUri, isNull);
expect(launcher.printDtdUri, false);
final DevToolsServerAddress? address = await launcher.serve();
expect(address?.host, '127.0.0.1');
expect(address?.port, 9100);
expect(launcher.dtdUri, isNull);
expect(launcher.printDtdUri, false);
});
testWithoutContext('DevtoolsLauncher does not launch a new DevTools instance if one is already active', () async {
testWithoutContext('DevtoolsLauncher saves the Dart Tooling Daemon uri', () async {
final (BufferLogger logger, Artifacts artifacts) = getTestState();
final Completer<void> completer = Completer<void>();
final DevtoolsLauncher launcher = DevtoolsServerLauncher(
dartExecutable: 'dart',
artifacts: artifacts,
logger: logger,
botDetector: const FakeBotDetector(false),
processManager: FakeProcessManager.list(<FakeCommand>[
FakeCommand(
command: const <String>[
'dart',
'Artifact.engineDartBinary',
'devtools',
'--no-launch-browser',
'--print-dtd',
],
stdout: '''
Serving the Dart Tooling Daemon at ws://127.0.0.1:53449/
Serving DevTools at http://127.0.0.1:9100.
''',
completer: completer,
),
]),
)..printDtdUri = true;
expect(launcher.dtdUri, isNull);
expect(launcher.printDtdUri, true);
final DevToolsServerAddress? address = await launcher.serve();
expect(address?.host, '127.0.0.1');
expect(address?.port, 9100);
expect(launcher.dtdUri?.toString(), 'ws://127.0.0.1:53449/');
expect(launcher.printDtdUri, true);
});
testWithoutContext('DevtoolsLauncher does not launch a new DevTools instance if one is already active', () async {
final (BufferLogger logger, Artifacts artifacts) = getTestState();
final Completer<void> completer = Completer<void>();
final DevtoolsLauncher launcher = DevtoolsServerLauncher(
artifacts: artifacts,
logger: logger,
botDetector: const FakeBotDetector(false),
processManager: FakeProcessManager.list(<FakeCommand>[
FakeCommand(
command: const <String>[
'Artifact.engineDartBinary',
'devtools',
'--no-launch-browser',
],
stdout: 'Serving DevTools at http://127.0.0.1:9100\n',
stdout: 'Serving DevTools at http://127.0.0.1:9100.\n',
completer: completer,
),
]),
@ -70,27 +106,28 @@ void main() {
expect(address?.host, '127.0.0.1');
expect(address?.port, 9100);
// Call `serve` again and verify that the already running server is returned.
// Call `serve` again and verify that the already-running server is returned.
address = await launcher.serve();
expect(address?.host, '127.0.0.1');
expect(address?.port, 9100);
});
testWithoutContext('DevtoolsLauncher can launch devtools with a memory profile', () async {
final (BufferLogger logger, Artifacts artifacts) = getTestState();
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
'dart',
'Artifact.engineDartBinary',
'devtools',
'--no-launch-browser',
'--vm-uri=localhost:8181/abcdefg',
'--profile-memory=foo',
],
stdout: 'Serving DevTools at http://127.0.0.1:9100\n',
stdout: 'Serving DevTools at http://127.0.0.1:9100.\n',
),
]);
final DevtoolsLauncher launcher = DevtoolsServerLauncher(
dartExecutable: 'dart',
artifacts: artifacts,
logger: logger,
botDetector: const FakeBotDetector(false),
processManager: processManager,
@ -103,14 +140,15 @@ void main() {
});
testWithoutContext('DevtoolsLauncher prints error if exception is thrown during launch', () async {
final (BufferLogger logger, Artifacts artifacts) = getTestState();
final DevtoolsLauncher launcher = DevtoolsServerLauncher(
dartExecutable: 'dart',
artifacts: artifacts,
logger: logger,
botDetector: const FakeBotDetector(false),
processManager: FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
'dart',
'Artifact.engineDartBinary',
'devtools',
'--no-launch-browser',
'--vm-uri=http://127.0.0.1:1234/abcdefg',
@ -126,19 +164,20 @@ void main() {
});
testWithoutContext('DevtoolsLauncher handles failure of DevTools process on a bot', () async {
final (BufferLogger logger, Artifacts artifacts) = getTestState();
final Completer<void> completer = Completer<void>();
final DevtoolsServerLauncher launcher = DevtoolsServerLauncher(
dartExecutable: 'dart',
artifacts: artifacts,
logger: logger,
botDetector: const FakeBotDetector(true),
processManager: FakeProcessManager.list(<FakeCommand>[
FakeCommand(
command: const <String>[
'dart',
'Artifact.engineDartBinary',
'devtools',
'--no-launch-browser',
],
stdout: 'Serving DevTools at http://127.0.0.1:9100\n',
stdout: 'Serving DevTools at http://127.0.0.1:9100.\n',
completer: completer,
exitCode: 255,
),

View file

@ -28,7 +28,6 @@ import '../../src/context.dart';
import '../../src/fake_vm_services.dart';
import '../../src/fakes.dart';
final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate(
id: '1',
pauseEvent: vm_service.Event(
@ -565,22 +564,3 @@ class FakeDartDevelopmentService extends Fake implements DartDevelopmentService
disposed = true;
}
}
class FakeDevtoolsLauncher extends Fake implements DevtoolsLauncher {
bool closed = false;
final Completer<void> _processStarted = Completer<void>();
@override
Future<void> launch(Uri vmServiceUri, {List<String>? additionalArguments}) {
_processStarted.complete();
return Completer<void>().future;
}
@override
Future<void> get processStart => _processStarted.future;
@override
Future<void> close() async {
closed = true;
}
}

View file

@ -4,6 +4,7 @@
import 'dart:async';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/dds.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
@ -61,6 +62,8 @@ final FakeVmServiceRequest listViews = FakeVmServiceRequest(
void main() {
Cache.flutterRoot = '';
(BufferLogger, Artifacts) getTestState() => (BufferLogger.test(), Artifacts.test());
testWithoutContext('Does not serve devtools if launcher is null', () async {
final ResidentDevtoolsHandler handler = FlutterResidentDevtoolsHandler(
null,
@ -86,10 +89,11 @@ void main() {
});
testWithoutContext('Can use devtools with existing devtools URI', () async {
final (BufferLogger logger, Artifacts artifacts) = getTestState();
final DevtoolsServerLauncher launcher = DevtoolsServerLauncher(
processManager: FakeProcessManager.empty(),
dartExecutable: 'dart',
logger: BufferLogger.test(),
artifacts: artifacts,
logger: logger,
botDetector: const FakeBotDetector(false),
);
final ResidentDevtoolsHandler handler = FlutterResidentDevtoolsHandler(
@ -429,22 +433,6 @@ void main() {
});
}
class FakeDevtoolsLauncher extends Fake implements DevtoolsLauncher {
@override
DevToolsServerAddress? activeDevToolsServer;
@override
Uri? devToolsUrl;
@override
Future<DevToolsServerAddress?> serve() async => null;
@override
Future<void> get ready => readyCompleter.future;
Completer<void> readyCompleter = Completer<void>()..complete();
}
class FakeResidentRunner extends Fake implements ResidentRunner {
@override
bool supportsServiceProtocol = true;

View file

@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
import 'package:flutter_tools/src/version.dart';
@ -175,116 +176,134 @@ void main() {
Analytics: () => fakeAnalytics,
});
testUsingContext("Doesn't crash on invalid .packages file", () async {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file('.packages')
..createSync()
..writeAsStringSync('Not a valid package');
group('${FlutterGlobalOptions.kPrintDtd} flag', () {
testUsingContext('sets DevtoolsLauncher.printDtdUri to false when not present', () async {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
await runner.run(<String>[]);
expect(DevtoolsLauncher.instance!.printDtdUri, false);
}, overrides: <Type, Generator>{
DevtoolsLauncher: () => FakeDevtoolsLauncher()..dtdUri = Uri(),
});
await runner.run(<String>['dummy']);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Platform: () => platform,
OutputPreferences: () => OutputPreferences.test(),
});
group('getRepoPackages', () {
late String? oldFlutterRoot;
setUp(() {
oldFlutterRoot = Cache.flutterRoot;
Cache.flutterRoot = _kFlutterRoot;
fileSystem.directory(fileSystem.path.join(_kFlutterRoot, 'examples'))
.createSync(recursive: true);
fileSystem.directory(fileSystem.path.join(_kFlutterRoot, 'packages'))
.createSync(recursive: true);
fileSystem.directory(fileSystem.path.join(_kFlutterRoot, 'dev', 'tools', 'aatool'))
.createSync(recursive: true);
fileSystem.file(fileSystem.path.join(_kFlutterRoot, 'dev', 'tools', 'pubspec.yaml'))
.createSync();
fileSystem.file(fileSystem.path.join(_kFlutterRoot, 'dev', 'tools', 'aatool', 'pubspec.yaml'))
.createSync();
testUsingContext('sets DevtoolsLauncher.printDtdUri to true when present', () async {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
await runner.run(<String>['--${FlutterGlobalOptions.kPrintDtd}']);
expect(DevtoolsLauncher.instance!.printDtdUri, true);
}, overrides: <Type, Generator>{
DevtoolsLauncher: () => FakeDevtoolsLauncher()..dtdUri = Uri(),
});
});
tearDown(() {
Cache.flutterRoot = oldFlutterRoot;
});
testUsingContext('', () {
testUsingContext("Doesn't crash on invalid .packages file", () async {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
final List<String> packagePaths = runner.getRepoPackages()
.map((Directory d) => d.path).toList();
expect(packagePaths, <String>[
fileSystem.directory(fileSystem.path.join(_kFlutterRoot, 'dev', 'tools', 'aatool')).path,
fileSystem.directory(fileSystem.path.join(_kFlutterRoot, 'dev', 'tools')).path,
]);
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file('.packages')
..createSync()
..writeAsStringSync('Not a valid package');
await runner.run(<String>['dummy']);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Platform: () => platform,
FlutterVersion: () => FakeFlutterVersion(),
OutputPreferences: () => OutputPreferences.test(),
});
group('getRepoPackages', () {
late String? oldFlutterRoot;
setUp(() {
oldFlutterRoot = Cache.flutterRoot;
Cache.flutterRoot = _kFlutterRoot;
fileSystem.directory(fileSystem.path.join(_kFlutterRoot, 'examples'))
.createSync(recursive: true);
fileSystem.directory(fileSystem.path.join(_kFlutterRoot, 'packages'))
.createSync(recursive: true);
fileSystem.directory(fileSystem.path.join(_kFlutterRoot, 'dev', 'tools', 'aatool'))
.createSync(recursive: true);
fileSystem.file(fileSystem.path.join(_kFlutterRoot, 'dev', 'tools', 'pubspec.yaml'))
.createSync();
fileSystem.file(fileSystem.path.join(_kFlutterRoot, 'dev', 'tools', 'aatool', 'pubspec.yaml'))
.createSync();
});
tearDown(() {
Cache.flutterRoot = oldFlutterRoot;
});
testUsingContext('', () {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
final List<String> packagePaths = runner.getRepoPackages()
.map((Directory d) => d.path).toList();
expect(packagePaths, <String>[
fileSystem.directory(fileSystem.path.join(_kFlutterRoot, 'dev', 'tools', 'aatool')).path,
fileSystem.directory(fileSystem.path.join(_kFlutterRoot, 'dev', 'tools')).path,
]);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Platform: () => platform,
FlutterVersion: () => FakeFlutterVersion(),
OutputPreferences: () => OutputPreferences.test(),
});
});
group('wrapping', () {
testUsingContext('checks that output wrapping is turned on when writing to a terminal', () async {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
final FakeFlutterCommand fakeCommand = FakeFlutterCommand();
runner.addCommand(fakeCommand);
await runner.run(<String>['fake']);
expect(fakeCommand.preferences.wrapText, isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Stdio: () => FakeStdio(hasFakeTerminal: true),
OutputPreferences: () => OutputPreferences.test(),
}, initializeFlutterRoot: false);
testUsingContext('checks that output wrapping is turned off when not writing to a terminal', () async {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
final FakeFlutterCommand fakeCommand = FakeFlutterCommand();
runner.addCommand(fakeCommand);
await runner.run(<String>['fake']);
expect(fakeCommand.preferences.wrapText, isFalse);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Stdio: () => FakeStdio(hasFakeTerminal: false),
OutputPreferences: () => OutputPreferences.test(),
}, initializeFlutterRoot: false);
testUsingContext('checks that output wrapping is turned off when set on the command line and writing to a terminal', () async {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
final FakeFlutterCommand fakeCommand = FakeFlutterCommand();
runner.addCommand(fakeCommand);
await runner.run(<String>['--no-wrap', 'fake']);
expect(fakeCommand.preferences.wrapText, isFalse);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Stdio: () => FakeStdio(hasFakeTerminal: true),
OutputPreferences: () => OutputPreferences.test(),
}, initializeFlutterRoot: false);
testUsingContext('checks that output wrapping is turned on when set on the command line, but not writing to a terminal', () async {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
final FakeFlutterCommand fakeCommand = FakeFlutterCommand();
runner.addCommand(fakeCommand);
await runner.run(<String>['--wrap', 'fake']);
expect(fakeCommand.preferences.wrapText, isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Stdio: () => FakeStdio(hasFakeTerminal: false),
OutputPreferences: () => OutputPreferences.test(),
}, initializeFlutterRoot: false);
});
});
group('wrapping', () {
testUsingContext('checks that output wrapping is turned on when writing to a terminal', () async {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
final FakeFlutterCommand fakeCommand = FakeFlutterCommand();
runner.addCommand(fakeCommand);
await runner.run(<String>['fake']);
expect(fakeCommand.preferences.wrapText, isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Stdio: () => FakeStdio(hasFakeTerminal: true),
OutputPreferences: () => OutputPreferences.test(),
}, initializeFlutterRoot: false);
testUsingContext('checks that output wrapping is turned off when not writing to a terminal', () async {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
final FakeFlutterCommand fakeCommand = FakeFlutterCommand();
runner.addCommand(fakeCommand);
await runner.run(<String>['fake']);
expect(fakeCommand.preferences.wrapText, isFalse);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Stdio: () => FakeStdio(hasFakeTerminal: false),
OutputPreferences: () => OutputPreferences.test(),
}, initializeFlutterRoot: false);
testUsingContext('checks that output wrapping is turned off when set on the command line and writing to a terminal', () async {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
final FakeFlutterCommand fakeCommand = FakeFlutterCommand();
runner.addCommand(fakeCommand);
await runner.run(<String>['--no-wrap', 'fake']);
expect(fakeCommand.preferences.wrapText, isFalse);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Stdio: () => FakeStdio(hasFakeTerminal: true),
OutputPreferences: () => OutputPreferences.test(),
}, initializeFlutterRoot: false);
testUsingContext('checks that output wrapping is turned on when set on the command line, but not writing to a terminal', () async {
final FlutterCommandRunner runner = createTestCommandRunner(DummyFlutterCommand()) as FlutterCommandRunner;
final FakeFlutterCommand fakeCommand = FakeFlutterCommand();
runner.addCommand(fakeCommand);
await runner.run(<String>['--wrap', 'fake']);
expect(fakeCommand.preferences.wrapText, isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Stdio: () => FakeStdio(hasFakeTerminal: false),
OutputPreferences: () => OutputPreferences.test(),
}, initializeFlutterRoot: false);
});
});
});
}

View file

@ -20,6 +20,7 @@ import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/ios/plist_parser.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/version.dart';
import 'package:test/fake.dart';
@ -686,3 +687,48 @@ class FakeJava extends Fake implements Java {
return _canRun;
}
}
class FakeDevtoolsLauncher extends Fake implements DevtoolsLauncher {
FakeDevtoolsLauncher({DevToolsServerAddress? serverAddress})
: _serverAddress = serverAddress;
@override
Future<void> get processStart => _processStarted.future;
final Completer<void> _processStarted = Completer<void>();
@override
Future<void> get ready => readyCompleter.future;
Completer<void> readyCompleter = Completer<void>()..complete();
@override
DevToolsServerAddress? activeDevToolsServer;
@override
Uri? devToolsUrl;
@override
Uri? dtdUri;
@override
bool printDtdUri = false;
final DevToolsServerAddress? _serverAddress;
@override
Future<DevToolsServerAddress?> serve() async => _serverAddress;
@override
Future<void> launch(Uri vmServiceUri, {List<String>? additionalArguments}) {
_processStarted.complete();
return Completer<void>().future;
}
bool closed = false;
@override
Future<void> close() async {
closed = true;
}
}