mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[ Widget Preview ] Improve --machine output (#175003)
This change adds the following events: - `widget_preview.initializing`: sent immediately after the widget previewer starts. This event includes the PID of the process starting the widget previewer for use by tooling to explicitly kill the tool process. - `widget_preview.logMessage`: sent for calls to `Logger` methods when in machine mode. This follows the same format as `daemon.logMessage`. This change also fixes and improves testing of `WidgetPreviewMachineAwareLogger` in `widget_preview_machine_test.dart` as the existing test validator did not cause tests to fail correctly. Fixes https://github.com/flutter/flutter/issues/175002
This commit is contained in:
parent
6ae27959de
commit
e4b7bdbe22
@ -88,6 +88,7 @@ Future<void> main(List<String> args) async {
|
||||
final bool muteCommandLogging = (help || doctor) && !veryVerbose;
|
||||
final bool verboseHelp = help && verbose;
|
||||
final bool daemon = args.contains('daemon');
|
||||
final bool widgetPreviews = args.contains(WidgetPreviewCommand.kWidgetPreview);
|
||||
final bool runMachine = args.contains('--machine');
|
||||
|
||||
// Cache.flutterRoot must be set early because other features use it (e.g.
|
||||
@ -133,6 +134,7 @@ Future<void> main(List<String> args) async {
|
||||
verbose: verbose && !muteCommandLogging,
|
||||
prefixedErrors: prefixedErrors,
|
||||
windows: globals.platform.isWindows,
|
||||
widgetPreviews: widgetPreviews,
|
||||
);
|
||||
},
|
||||
AnsiTerminal: () {
|
||||
@ -294,6 +296,7 @@ class LoggerFactory {
|
||||
required bool machine,
|
||||
required bool daemon,
|
||||
required bool windows,
|
||||
required bool widgetPreviews,
|
||||
}) {
|
||||
Logger logger;
|
||||
if (windows) {
|
||||
@ -317,6 +320,9 @@ class LoggerFactory {
|
||||
if (prefixedErrors) {
|
||||
logger = PrefixedErrorLogger(logger);
|
||||
}
|
||||
if (widgetPreviews) {
|
||||
return WidgetPreviewMachineAwareLogger(logger, machine: machine, verbose: verbose);
|
||||
}
|
||||
if (daemon) {
|
||||
return NotifyingLogger(verbose: verbose, parent: logger);
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import 'package:process/process.dart';
|
||||
import '../artifacts.dart';
|
||||
import '../base/common.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/logger.dart';
|
||||
import '../base/os.dart';
|
||||
import '../base/platform.dart';
|
||||
@ -28,7 +29,6 @@ import '../isolated/resident_web_runner.dart';
|
||||
import '../project.dart';
|
||||
import '../resident_runner.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
import '../runner/flutter_command_runner.dart';
|
||||
import '../web/web_device.dart';
|
||||
import '../widget_preview/analytics.dart';
|
||||
import '../widget_preview/dependency_graph.dart';
|
||||
@ -77,7 +77,8 @@ class WidgetPreviewCommand extends FlutterCommand {
|
||||
String get description => 'Manage the widget preview environment.';
|
||||
|
||||
@override
|
||||
String get name => 'widget-preview';
|
||||
String get name => kWidgetPreview;
|
||||
static const kWidgetPreview = 'widget-preview';
|
||||
|
||||
@override
|
||||
String get category => FlutterCommandCategory.tools;
|
||||
@ -130,7 +131,7 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C
|
||||
required this.processManager,
|
||||
required this.artifacts,
|
||||
@visibleForTesting WidgetPreviewDtdServices? dtdServicesOverride,
|
||||
}) : logger = WidgetPreviewMachineAwareLogger(logger) {
|
||||
}) : _logger = logger {
|
||||
if (dtdServicesOverride != null) {
|
||||
_dtdService = dtdServicesOverride;
|
||||
}
|
||||
@ -200,7 +201,8 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C
|
||||
final FileSystem fs;
|
||||
|
||||
@override
|
||||
final WidgetPreviewMachineAwareLogger logger;
|
||||
WidgetPreviewMachineAwareLogger get logger => _logger as WidgetPreviewMachineAwareLogger;
|
||||
final Logger _logger;
|
||||
|
||||
@override
|
||||
final FlutterProjectFactory projectFactory;
|
||||
@ -259,17 +261,17 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C
|
||||
|
||||
@override
|
||||
Future<FlutterCommandResult> runCommand() async {
|
||||
assert(_logger is WidgetPreviewMachineAwareLogger);
|
||||
|
||||
// Start the timer tracking how long it takes to launch the preview environment.
|
||||
previewAnalytics.initializeLaunchStopwatch();
|
||||
logger.sendInitializingEvent();
|
||||
|
||||
final String? customPreviewScaffoldOutput = stringArg(kWidgetPreviewScaffoldOutputDir);
|
||||
final Directory widgetPreviewScaffold = customPreviewScaffoldOutput != null
|
||||
? fs.directory(customPreviewScaffoldOutput)
|
||||
: rootProject.widgetPreviewScaffold;
|
||||
|
||||
final bool machine = boolArg(FlutterGlobalOptions.kMachineFlag);
|
||||
logger.machine = machine;
|
||||
|
||||
// Check to see if a preview scaffold has already been generated. If not,
|
||||
// generate one.
|
||||
final bool generateScaffoldProject =
|
||||
@ -479,7 +481,7 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C
|
||||
);
|
||||
unawaited(_widgetPreviewApp!.run(appStartedCompleter: appStarted));
|
||||
await appStarted.future;
|
||||
logger.sendEvent('started', {'url': flutterDevice.devFS!.baseUri.toString()});
|
||||
logger.sendStartedEvent(applicationUrl: flutterDevice.devFS!.baseUri!);
|
||||
}
|
||||
} on Exception catch (error) {
|
||||
throwToolExit(error.toString());
|
||||
@ -531,9 +533,10 @@ final class WidgetPreviewCleanCommand extends WidgetPreviewSubCommandBase {
|
||||
/// A custom logger for the widget-preview commands that disables non-event output to stdio when
|
||||
/// machine mode is enabled.
|
||||
final class WidgetPreviewMachineAwareLogger extends DelegatingLogger {
|
||||
WidgetPreviewMachineAwareLogger(super.delegate);
|
||||
WidgetPreviewMachineAwareLogger(super.delegate, {required this.machine, required this.verbose});
|
||||
|
||||
var machine = false;
|
||||
final bool machine;
|
||||
final bool verbose;
|
||||
|
||||
@override
|
||||
void printError(
|
||||
@ -546,6 +549,11 @@ final class WidgetPreviewMachineAwareLogger extends DelegatingLogger {
|
||||
bool? wrap,
|
||||
}) {
|
||||
if (machine) {
|
||||
sendEvent('logMessage', <String, Object?>{
|
||||
'level': 'error',
|
||||
'message': message,
|
||||
'stackTrace': ?stackTrace?.toString(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
super.printError(
|
||||
@ -570,6 +578,7 @@ final class WidgetPreviewMachineAwareLogger extends DelegatingLogger {
|
||||
bool fatal = true,
|
||||
}) {
|
||||
if (machine) {
|
||||
sendEvent('logMessage', <String, Object?>{'level': 'warning', 'message': message});
|
||||
return;
|
||||
}
|
||||
super.printWarning(
|
||||
@ -594,6 +603,7 @@ final class WidgetPreviewMachineAwareLogger extends DelegatingLogger {
|
||||
bool? wrap,
|
||||
}) {
|
||||
if (machine) {
|
||||
sendEvent('logMessage', <String, Object?>{'level': 'status', 'message': message});
|
||||
return;
|
||||
}
|
||||
super.printStatus(
|
||||
@ -617,12 +627,27 @@ final class WidgetPreviewMachineAwareLogger extends DelegatingLogger {
|
||||
|
||||
@override
|
||||
void printTrace(String message) {
|
||||
if (!verbose) {
|
||||
return;
|
||||
}
|
||||
if (machine) {
|
||||
sendEvent('logMessage', <String, Object?>{'level': 'trace', 'message': message});
|
||||
return;
|
||||
}
|
||||
super.printTrace(message);
|
||||
}
|
||||
|
||||
/// Notifies tooling that the widget previewer is initializing.
|
||||
void sendInitializingEvent() {
|
||||
sendEvent('initializing', {'pid': pid});
|
||||
}
|
||||
|
||||
/// Notifies tooling that the widget previewer has started and is being
|
||||
/// served at [applicationUrl].
|
||||
void sendStartedEvent({required Uri applicationUrl}) {
|
||||
sendEvent('started', {'url': applicationUrl.toString()});
|
||||
}
|
||||
|
||||
@override
|
||||
void sendEvent(String name, [Map<String, dynamic>? args]) {
|
||||
if (!machine) {
|
||||
@ -642,6 +667,7 @@ final class WidgetPreviewMachineAwareLogger extends DelegatingLogger {
|
||||
int progressIndicatorPadding = kDefaultStatusPadding,
|
||||
}) {
|
||||
if (machine) {
|
||||
printStatus(message);
|
||||
return SilentStatus(stopwatch: Stopwatch());
|
||||
}
|
||||
return super.startProgress(
|
||||
|
||||
@ -109,7 +109,7 @@ void main() {
|
||||
await ensureFlutterToolsSnapshot();
|
||||
loggingProcessManager = LoggingProcessManager();
|
||||
shutdownHooks = ShutdownHooks();
|
||||
logger = BufferLogger.test();
|
||||
logger = WidgetPreviewMachineAwareLogger(BufferLogger.test(), machine: false, verbose: false);
|
||||
fs = LocalFileSystem.test(signals: Signals.test());
|
||||
botDetector = const FakeBotDetector(false);
|
||||
tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_create_test.');
|
||||
@ -190,7 +190,7 @@ void main() {
|
||||
void expectSinglePreviewLaunchTimingEvent() => expectNPreviewLaunchTimingEvents(1);
|
||||
|
||||
void expectDeviceSelected(Device device) {
|
||||
final bufferLogger = logger as BufferLogger;
|
||||
final BufferLogger bufferLogger = asLogger<BufferLogger>(logger);
|
||||
expect(
|
||||
bufferLogger.statusText,
|
||||
contains('Launching the Widget Preview Scaffold on ${device.displayName}...'),
|
||||
|
||||
@ -12,6 +12,7 @@ import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/base/terminal.dart';
|
||||
import 'package:flutter_tools/src/commands/daemon.dart';
|
||||
import 'package:flutter_tools/src/commands/widget_preview.dart';
|
||||
import 'package:test/fake.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
@ -37,6 +38,7 @@ void main() {
|
||||
prefixedErrors: false,
|
||||
machine: false,
|
||||
daemon: false,
|
||||
widgetPreviews: false,
|
||||
windows: false,
|
||||
),
|
||||
isA<StdoutLogger>(),
|
||||
@ -47,6 +49,7 @@ void main() {
|
||||
prefixedErrors: false,
|
||||
machine: false,
|
||||
daemon: false,
|
||||
widgetPreviews: false,
|
||||
windows: true,
|
||||
),
|
||||
isA<WindowsStdoutLogger>(),
|
||||
@ -57,6 +60,7 @@ void main() {
|
||||
prefixedErrors: false,
|
||||
machine: false,
|
||||
daemon: false,
|
||||
widgetPreviews: false,
|
||||
windows: true,
|
||||
),
|
||||
isA<VerboseLogger>(),
|
||||
@ -67,6 +71,7 @@ void main() {
|
||||
prefixedErrors: false,
|
||||
machine: false,
|
||||
daemon: false,
|
||||
widgetPreviews: false,
|
||||
windows: false,
|
||||
),
|
||||
isA<VerboseLogger>(),
|
||||
@ -77,6 +82,7 @@ void main() {
|
||||
prefixedErrors: true,
|
||||
machine: false,
|
||||
daemon: false,
|
||||
widgetPreviews: false,
|
||||
windows: false,
|
||||
),
|
||||
isA<PrefixedErrorLogger>(),
|
||||
@ -87,6 +93,7 @@ void main() {
|
||||
prefixedErrors: false,
|
||||
machine: false,
|
||||
daemon: true,
|
||||
widgetPreviews: false,
|
||||
windows: false,
|
||||
),
|
||||
isA<NotifyingLogger>(),
|
||||
@ -97,10 +104,32 @@ void main() {
|
||||
prefixedErrors: false,
|
||||
machine: true,
|
||||
daemon: false,
|
||||
widgetPreviews: false,
|
||||
windows: false,
|
||||
),
|
||||
isA<MachineOutputLogger>(),
|
||||
);
|
||||
|
||||
for (final verbose in [false, true]) {
|
||||
for (final machine in [false, true]) {
|
||||
for (final windows in [false, true]) {
|
||||
expect(
|
||||
loggerFactory.createLogger(
|
||||
verbose: verbose,
|
||||
prefixedErrors: false,
|
||||
machine: machine,
|
||||
daemon: false,
|
||||
widgetPreviews: true,
|
||||
windows: windows,
|
||||
),
|
||||
isA<WidgetPreviewMachineAwareLogger>(),
|
||||
reason:
|
||||
'Did not produce WidgetPreviewMachineAwareLogger for '
|
||||
'verbose: $verbose machine: $machine windows: $windows.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
testWithoutContext(
|
||||
|
||||
@ -604,6 +604,7 @@ void main() {
|
||||
machine: true,
|
||||
verbose: false,
|
||||
prefixedErrors: false,
|
||||
widgetPreviews: false,
|
||||
windows: globals.platform.isWindows,
|
||||
);
|
||||
},
|
||||
|
||||
@ -16,20 +16,28 @@ import '../src/common.dart';
|
||||
import 'test_data/basic_project.dart';
|
||||
import 'test_utils.dart';
|
||||
|
||||
typedef ExpectedEvent = ({String event, FutureOr<void> Function(Map<String, Object?>)? validator});
|
||||
typedef ExpectedEvent = ({String event, FutureOr<bool> Function(Map<String, Object?>)? validator});
|
||||
|
||||
final launchEvents = <ExpectedEvent>[
|
||||
(
|
||||
event: 'widget_preview.initializing',
|
||||
validator: (Map<String, Object?> params) {
|
||||
return params.containsKey('pid');
|
||||
},
|
||||
),
|
||||
(
|
||||
event: 'widget_preview.started',
|
||||
validator: (Map<String, Object?> params) async {
|
||||
if (params case {'uri': final String uri}) {
|
||||
if (params case {'url': final String uri}) {
|
||||
try {
|
||||
final Response response = await get(Uri.parse(uri));
|
||||
expect(response.statusCode, HttpStatus.ok, reason: 'Failed to retrieve widget previewer');
|
||||
return response.statusCode == HttpStatus.ok;
|
||||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e) {
|
||||
fail('Failed to access widget previewer: $e');
|
||||
printOnFailure('Failed to access widget previewer: $e');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
),
|
||||
];
|
||||
@ -82,7 +90,11 @@ void main() {
|
||||
if (event case [final Map<String, Object?> eventObject]) {
|
||||
final ExpectedEvent expectation = expectedEvents[nextExpectationIndex];
|
||||
if (expectation.event == eventObject['event']) {
|
||||
await expectation.validator?.call(eventObject);
|
||||
expect(
|
||||
await expectation.validator?.call(eventObject['params']! as Map<String, Object?>),
|
||||
true,
|
||||
reason: 'Validator[$nextExpectationIndex] failed.',
|
||||
);
|
||||
++nextExpectationIndex;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user