From de872dd864518d436062354052ea8604c38e8c94 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Fri, 31 Jan 2025 14:17:45 -0500 Subject: [PATCH] [ Tool ] Remove use of globals from widget-preview commands (#162522) --- packages/flutter_tools/lib/executable.dart | 7 +- .../lib/src/commands/widget_preview.dart | 91 +++++++++++++----- .../src/widget_preview/preview_detector.dart | 6 +- .../hermetic/widget_preview_test.dart | 13 ++- .../permeable/widget_preview_test.dart | 94 +++++++++++-------- .../widget_preview/preview_detector_test.dart | 6 +- 6 files changed, 148 insertions(+), 69 deletions(-) diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index 69683c4316..8d888ced94 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -246,7 +246,12 @@ List generateCommands({required bool verboseHelp, required bool verbose: verbose, nativeAssetsBuilder: globals.nativeAssetsBuilder, ), - WidgetPreviewCommand(), + WidgetPreviewCommand( + logger: globals.logger, + fs: globals.fs, + projectFactory: globals.projectFactory, + cache: globals.cache, + ), UpgradeCommand(verboseHelp: verboseHelp), SymbolizeCommand(stdio: globals.stdio, fileSystem: globals.fs), // Development-only commands. These are always hidden, diff --git a/packages/flutter_tools/lib/src/commands/widget_preview.dart b/packages/flutter_tools/lib/src/commands/widget_preview.dart index 692fe59aee..ddc0dfebb6 100644 --- a/packages/flutter_tools/lib/src/commands/widget_preview.dart +++ b/packages/flutter_tools/lib/src/commands/widget_preview.dart @@ -8,24 +8,37 @@ import 'package:meta/meta.dart'; import '../base/common.dart'; import '../base/deferred_component.dart'; import '../base/file_system.dart'; +import '../base/logger.dart'; import '../base/platform.dart'; import '../cache.dart'; import '../convert.dart'; import '../dart/pub.dart'; import '../flutter_manifest.dart'; -import '../globals.dart' as globals; import '../project.dart'; import '../runner/flutter_command.dart'; import '../widget_preview/preview_code_generator.dart'; import '../widget_preview/preview_detector.dart'; import 'create_base.dart'; -// TODO(bkonyi): use dependency injection instead of global accessors throughout this file. class WidgetPreviewCommand extends FlutterCommand { - WidgetPreviewCommand() { - addSubcommand(WidgetPreviewStartCommand()); - addSubcommand(WidgetPreviewCleanCommand()); + WidgetPreviewCommand({ + required Logger logger, + required FileSystem fs, + required FlutterProjectFactory projectFactory, + required Cache cache, + }) { + addSubcommand( + WidgetPreviewStartCommand( + logger: logger, + fs: fs, + projectFactory: projectFactory, + cache: cache, + ), + ); + addSubcommand( + WidgetPreviewCleanCommand(logger: logger, fs: fs, projectFactory: projectFactory), + ); } @override @@ -46,27 +59,30 @@ class WidgetPreviewCommand extends FlutterCommand { Future runCommand() async => FlutterCommandResult.fail(); } -/// Common utilities for the 'start' and 'clean' commands. -mixin WidgetPreviewSubCommandMixin on FlutterCommand { +abstract base class WidgetPreviewSubCommandBase extends FlutterCommand { + FileSystem get fs; + Logger get logger; + FlutterProjectFactory get projectFactory; + FlutterProject getRootProject() { final ArgResults results = argResults!; final Directory projectDir; if (results.rest case [final String directory]) { - projectDir = globals.fs.directory(directory); + projectDir = fs.directory(directory); if (!projectDir.existsSync()) { throwToolExit('Could not find ${projectDir.path}.'); } } else if (results.rest.length > 1) { throwToolExit('Only one directory should be provided.'); } else { - projectDir = globals.fs.currentDirectory; + projectDir = fs.currentDirectory; } return validateFlutterProjectForPreview(projectDir); } FlutterProject validateFlutterProjectForPreview(Directory directory) { - globals.logger.printTrace('Verifying that ${directory.path} is a Flutter project.'); - final FlutterProject flutterProject = globals.projectFactory.fromDirectory(directory); + logger.printTrace('Verifying that ${directory.path} is a Flutter project.'); + final FlutterProject flutterProject = projectFactory.fromDirectory(directory); if (!flutterProject.dartTool.existsSync()) { throwToolExit('${flutterProject.directory.path} is not a valid Flutter project.'); } @@ -74,9 +90,13 @@ mixin WidgetPreviewSubCommandMixin on FlutterCommand { } } -class WidgetPreviewStartCommand extends FlutterCommand - with CreateBase, WidgetPreviewSubCommandMixin { - WidgetPreviewStartCommand() { +final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with CreateBase { + WidgetPreviewStartCommand({ + required this.logger, + required this.fs, + required this.projectFactory, + required this.cache, + }) { addPubOptions(); } @@ -86,8 +106,20 @@ class WidgetPreviewStartCommand extends FlutterCommand @override String get name => 'start'; + @override + final FileSystem fs; + + @override + final Logger logger; + + @override + final FlutterProjectFactory projectFactory; + + final Cache cache; + late final PreviewDetector _previewDetector = PreviewDetector( - logger: globals.logger, + logger: logger, + fs: fs, onChangeDetected: onChangeDetected, ); @@ -101,9 +133,7 @@ class WidgetPreviewStartCommand extends FlutterCommand // Check to see if a preview scaffold has already been generated. If not, // generate one. if (!widgetPreviewScaffold.existsSync()) { - globals.logger.printStatus( - 'Creating widget preview scaffolding at: ${widgetPreviewScaffold.path}', - ); + logger.printStatus('Creating widget preview scaffolding at: ${widgetPreviewScaffold.path}'); await generateApp( ['widget_preview_scaffold'], widgetPreviewScaffold, @@ -112,7 +142,7 @@ class WidgetPreviewStartCommand extends FlutterCommand projectName: 'widget_preview_scaffold', titleCaseProjectName: 'Widget Preview Scaffold', flutterRoot: Cache.flutterRoot!, - dartSdkVersionBounds: '^${globals.cache.dartSdkBuild}', + dartSdkVersionBounds: '^${cache.dartSdkBuild}', linux: const LocalPlatform().isLinux, macos: const LocalPlatform().isMacOS, windows: const LocalPlatform().isWindows, @@ -128,7 +158,7 @@ class WidgetPreviewStartCommand extends FlutterCommand // FlutterManifest before the scaffold project's pubspec has been generated. _previewCodeGenerator = PreviewCodeGenerator( widgetPreviewScaffoldProject: rootProject.widgetPreviewScaffoldProject, - fs: globals.fs, + fs: fs, ); final PreviewMapping initialPreviews = await _previewDetector.initialize(rootProject.directory); @@ -207,7 +237,7 @@ class WidgetPreviewStartCommand extends FlutterCommand rootManifest.deferredComponents?.map(transformDeferredComponent).toList(); return widgetPreviewManifest.copyWith( - logger: globals.logger, + logger: logger, assets: assets, fonts: fonts, shaders: shaders, @@ -292,26 +322,37 @@ class WidgetPreviewStartCommand extends FlutterCommand flutterGenPackageConfigEntry, ); packageConfig.writeAsStringSync(json.encode(packageConfigJson)); - globals.logger.printStatus('Added flutter_gen dependency to $previewPackageConfigPath'); + logger.printStatus('Added flutter_gen dependency to $previewPackageConfigPath'); } } -class WidgetPreviewCleanCommand extends FlutterCommand with WidgetPreviewSubCommandMixin { +final class WidgetPreviewCleanCommand extends WidgetPreviewSubCommandBase { + WidgetPreviewCleanCommand({required this.fs, required this.logger, required this.projectFactory}); + @override String get description => 'Cleans up widget preview state.'; @override String get name => 'clean'; + @override + final FileSystem fs; + + @override + final Logger logger; + + @override + final FlutterProjectFactory projectFactory; + @override Future runCommand() async { final Directory widgetPreviewScaffold = getRootProject().widgetPreviewScaffold; if (widgetPreviewScaffold.existsSync()) { final String scaffoldPath = widgetPreviewScaffold.path; - globals.logger.printStatus('Deleting widget preview scaffold at $scaffoldPath.'); + logger.printStatus('Deleting widget preview scaffold at $scaffoldPath.'); widgetPreviewScaffold.deleteSync(recursive: true); } else { - globals.logger.printStatus('Nothing to clean up.'); + logger.printStatus('Nothing to clean up.'); } return FlutterCommandResult.success(); } diff --git a/packages/flutter_tools/lib/src/widget_preview/preview_detector.dart b/packages/flutter_tools/lib/src/widget_preview/preview_detector.dart index 70d01df3ed..0ca345e1e2 100644 --- a/packages/flutter_tools/lib/src/widget_preview/preview_detector.dart +++ b/packages/flutter_tools/lib/src/widget_preview/preview_detector.dart @@ -16,14 +16,14 @@ import 'package:watcher/watcher.dart'; import '../base/file_system.dart'; import '../base/logger.dart'; import '../base/utils.dart'; -import '../globals.dart' as globals; import 'preview_code_generator.dart'; typedef PreviewMapping = Map>; class PreviewDetector { - PreviewDetector({required this.logger, required this.onChangeDetected}); + PreviewDetector({required this.fs, required this.logger, required this.onChangeDetected}); + final FileSystem fs; final Logger logger; final void Function(PreviewMapping) onChangeDetected; StreamSubscription? _fileWatcher; @@ -47,7 +47,7 @@ class PreviewDetector { } logger.printStatus('Detected change in $eventPath.'); final PreviewMapping filePreviewsMapping = findPreviewFunctions( - globals.fs.file(Uri.file(event.path)), + fs.file(Uri.file(event.path)), ); if (filePreviewsMapping.isEmpty && !_pathToPreviews.containsKey(eventPath)) { // No previews found or removed, nothing to do. diff --git a/packages/flutter_tools/test/commands.shard/hermetic/widget_preview_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/widget_preview_test.dart index 751540a7a1..db8a316a5e 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/widget_preview_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/widget_preview_test.dart @@ -5,6 +5,7 @@ import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/widget_preview.dart'; import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/flutter_manifest.dart'; @@ -14,6 +15,7 @@ import 'package:test_api/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/fakes.dart'; void main() { group('WidgetPreviewStartCommand', () { @@ -21,15 +23,22 @@ void main() { late ProcessManager processManager; late WidgetPreviewStartCommand command; late FlutterProject rootProject; + late Logger logger; setUp(() { fileSystem = MemoryFileSystem.test(); processManager = FakeProcessManager.any(); - command = WidgetPreviewStartCommand(); + logger = BufferLogger.test(); + command = WidgetPreviewStartCommand( + fs: fileSystem, + projectFactory: FakeFlutterProjectFactory(), + logger: logger, + cache: Cache.test(processManager: processManager), + ); rootProject = FakeFlutterProject( projectRoot: 'some_project', fileSystem: fileSystem, - logger: BufferLogger.test(), + logger: logger, ); }); diff --git a/packages/flutter_tools/test/commands.shard/permeable/widget_preview_test.dart b/packages/flutter_tools/test/commands.shard/permeable/widget_preview_test.dart index 849ad7f03e..711490b1fd 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/widget_preview_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/widget_preview_test.dart @@ -6,11 +6,16 @@ import 'dart:io' as io show IOOverrides; import 'package:args/command_runner.dart'; import 'package:file_testing/file_testing.dart'; +import 'package:flutter_tools/src/base/bot_detector.dart'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/file_system.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/cache.dart'; import 'package:flutter_tools/src/commands/widget_preview.dart'; import 'package:flutter_tools/src/dart/pub.dart'; -import 'package:flutter_tools/src/globals.dart' as globals; +import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/widget_preview/preview_code_generator.dart'; import '../../src/common.dart'; @@ -23,11 +28,19 @@ void main() { late Directory tempDir; late LoggingProcessManager loggingProcessManager; late FakeStdio mockStdio; + late Logger logger; + late FileSystem fs; + late BotDetector botDetector; + late Platform platform; setUp(() { loggingProcessManager = LoggingProcessManager(); - tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_create_test.'); + logger = BufferLogger.test(); + fs = LocalFileSystem.test(signals: Signals.test()); + botDetector = const FakeBotDetector(false); + tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_create_test.'); mockStdio = FakeStdio(); + platform = const LocalPlatform(); }); tearDown(() { @@ -35,7 +48,7 @@ void main() { }); Future createRootProject() async { - return globals.fs.directory(await createProject(tempDir, arguments: ['--pub'])); + return fs.directory(await createProject(tempDir, arguments: ['--pub'])); } Directory widgetPreviewScaffoldFromRootProject({required Directory rootProject}) { @@ -43,7 +56,14 @@ void main() { } Future runWidgetPreviewCommand(List arguments) async { - final CommandRunner runner = createTestCommandRunner(WidgetPreviewCommand()); + final CommandRunner runner = createTestCommandRunner( + WidgetPreviewCommand( + logger: logger, + fs: fs, + projectFactory: FlutterProjectFactory(logger: logger, fileSystem: fs), + cache: Cache.test(processManager: loggingProcessManager, platform: platform), + ), + ); await runner.run(['widget-preview', ...arguments]); } @@ -57,7 +77,7 @@ void main() { if (rootProject != null) rootProject.path, ]); final Directory widgetPreviewScaffoldDir = widgetPreviewScaffoldFromRootProject( - rootProject: rootProject ?? globals.fs.currentDirectory, + rootProject: rootProject ?? fs.currentDirectory, ); expect(widgetPreviewScaffoldDir, exists); expect( @@ -69,7 +89,7 @@ void main() { Future cleanWidgetPreview({required Directory rootProject}) async { await runWidgetPreviewCommand(['clean', rootProject.path]); expect( - globals.fs + fs .directory(rootProject) .childDirectory('.dart_tool') .childDirectory('widget_preview_scaffold'), @@ -116,11 +136,11 @@ void main() { overrides: { Pub: () => Pub.test( - fileSystem: globals.fs, - logger: globals.logger, - processManager: globals.processManager, - botDetector: globals.botDetector, - platform: globals.platform, + fileSystem: fs, + logger: logger, + processManager: loggingProcessManager, + botDetector: botDetector, + platform: platform, stdio: mockStdio, ), }, @@ -138,11 +158,11 @@ void main() { overrides: { Pub: () => Pub.test( - fileSystem: globals.fs, - logger: globals.logger, - processManager: globals.processManager, - botDetector: globals.botDetector, - platform: globals.platform, + fileSystem: fs, + logger: logger, + processManager: loggingProcessManager, + botDetector: botDetector, + platform: platform, stdio: mockStdio, ), }, @@ -179,11 +199,11 @@ import 'package:flutter_project/foo.dart' as _i1;import 'package:widget_preview/ overrides: { Pub: () => Pub.test( - fileSystem: globals.fs, - logger: globals.logger, - processManager: globals.processManager, - botDetector: globals.botDetector, - platform: globals.platform, + fileSystem: fs, + logger: logger, + processManager: loggingProcessManager, + botDetector: botDetector, + platform: platform, stdio: mockStdio, ), }, @@ -209,16 +229,16 @@ import 'package:flutter_project/foo.dart' as _i1;import 'package:widget_preview/ // Try to execute using the CWD. await startWidgetPreview(rootProject: null); expect(generatedFile.readAsStringSync(), expectedGeneratedFileContents); - }, getCurrentDirectory: () => globals.fs.directory(rootProject)); + }, getCurrentDirectory: () => fs.directory(rootProject)); }, overrides: { Pub: () => Pub.test( - fileSystem: globals.fs, - logger: globals.logger, - processManager: globals.processManager, - botDetector: globals.botDetector, - platform: globals.platform, + fileSystem: fs, + logger: logger, + processManager: loggingProcessManager, + botDetector: botDetector, + platform: platform, stdio: mockStdio, ), }, @@ -234,11 +254,11 @@ import 'package:flutter_project/foo.dart' as _i1;import 'package:widget_preview/ overrides: { Pub: () => Pub.test( - fileSystem: globals.fs, - logger: globals.logger, - processManager: globals.processManager, - botDetector: globals.botDetector, - platform: globals.platform, + fileSystem: fs, + logger: logger, + processManager: loggingProcessManager, + botDetector: botDetector, + platform: platform, stdio: mockStdio, ), }, @@ -287,11 +307,11 @@ import 'package:flutter_project/foo.dart' as _i1;import 'package:widget_preview/ ProcessManager: () => loggingProcessManager, Pub: () => Pub.test( - fileSystem: globals.fs, - logger: globals.logger, - processManager: globals.processManager, - botDetector: globals.botDetector, - platform: globals.platform, + fileSystem: fs, + logger: logger, + processManager: loggingProcessManager, + botDetector: botDetector, + platform: platform, stdio: mockStdio, ), }, diff --git a/packages/flutter_tools/test/general.shard/widget_preview/preview_detector_test.dart b/packages/flutter_tools/test/general.shard/widget_preview/preview_detector_test.dart index b00c70d07b..6c55b64580 100644 --- a/packages/flutter_tools/test/general.shard/widget_preview/preview_detector_test.dart +++ b/packages/flutter_tools/test/general.shard/widget_preview/preview_detector_test.dart @@ -48,7 +48,11 @@ void main() { fs = LocalFileSystem.test(signals: Signals.test()); projectRoot = createBasicProjectStructure(fs); logger = BufferLogger.test(); - previewDetector = PreviewDetector(logger: logger, onChangeDetected: onChangeDetectedRoot); + previewDetector = PreviewDetector( + logger: logger, + fs: fs, + onChangeDetected: onChangeDetectedRoot, + ); }); tearDown(() async {