diff --git a/packages/flutter_tools/lib/src/debug_adapters/flutter_adapter.dart b/packages/flutter_tools/lib/src/debug_adapters/flutter_adapter.dart index b3947846ff1..f271f259ebf 100644 --- a/packages/flutter_tools/lib/src/debug_adapters/flutter_adapter.dart +++ b/packages/flutter_tools/lib/src/debug_adapters/flutter_adapter.dart @@ -305,7 +305,11 @@ class FlutterDebugAdapter extends DartDebugAdapter launchAsProcess(String executable, List processArgs) async { + Future launchAsProcess({ + required String executable, + required List processArgs, + required Map? env, + }) async { logger?.call('Spawning $executable with $processArgs in ${args.cwd}'); final Process process = await Process.start( executable, processArgs, workingDirectory: args.cwd, + environment: env, ); _process = process; pidsToTerminate.add(process.pid); diff --git a/packages/flutter_tools/lib/src/debug_adapters/flutter_adapter_args.dart b/packages/flutter_tools/lib/src/debug_adapters/flutter_adapter_args.dart index 3c81a8790a2..85947f75df6 100644 --- a/packages/flutter_tools/lib/src/debug_adapters/flutter_adapter_args.dart +++ b/packages/flutter_tools/lib/src/debug_adapters/flutter_adapter_args.dart @@ -20,6 +20,7 @@ class FlutterAttachRequestArguments super.restart, super.name, super.cwd, + super.env, super.additionalProjectPaths, super.debugSdkLibraries, super.debugExternalPackageLibraries, @@ -91,6 +92,7 @@ class FlutterLaunchRequestArguments super.restart, super.name, super.cwd, + super.env, super.additionalProjectPaths, super.debugSdkLibraries, super.debugExternalPackageLibraries, diff --git a/packages/flutter_tools/lib/src/debug_adapters/flutter_test_adapter.dart b/packages/flutter_tools/lib/src/debug_adapters/flutter_test_adapter.dart index 1f0f77416e0..d7af0eb2089 100644 --- a/packages/flutter_tools/lib/src/debug_adapters/flutter_test_adapter.dart +++ b/packages/flutter_tools/lib/src/debug_adapters/flutter_test_adapter.dart @@ -135,7 +135,11 @@ class FlutterTestDebugAdapter extends DartDebugAdapter launchAsProcess(String executable, List processArgs) async { + Future launchAsProcess({ + required String executable, + required List processArgs, + required Map? env, + }) async { logger?.call('Spawning $executable with $processArgs in ${args.cwd}'); final Process process = await Process.start( executable, processArgs, workingDirectory: args.cwd, + environment: env, ); _process = process; pidsToTerminate.add(process.pid); diff --git a/packages/flutter_tools/test/general.shard/dap/flutter_adapter_test.dart b/packages/flutter_tools/test/general.shard/dap/flutter_adapter_test.dart index 36f46962b73..bdb53060b31 100644 --- a/packages/flutter_tools/test/general.shard/dap/flutter_adapter_test.dart +++ b/packages/flutter_tools/test/general.shard/dap/flutter_adapter_test.dart @@ -4,9 +4,11 @@ import 'dart:async'; +import 'package:file/memory.dart'; +import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/debug_adapters/flutter_adapter_args.dart'; -import 'package:flutter_tools/src/globals.dart' as globals; +import 'package:flutter_tools/src/globals.dart' as globals show platform; import 'package:test/fake.dart'; import 'package:test/test.dart'; import 'package:vm_service/vm_service.dart'; @@ -14,21 +16,27 @@ import 'package:vm_service/vm_service.dart'; import 'mocks.dart'; void main() { + // Use the real platform as a base so that Windows bots test paths. + final FakePlatform platform = FakePlatform.fromPlatform(globals.platform); + final FileSystemStyle fsStyle = platform.isWindows ? FileSystemStyle.windows : FileSystemStyle.posix; + group('flutter adapter', () { - final String expectedFlutterExecutable = globals.platform.isWindows + final String expectedFlutterExecutable = platform.isWindows ? r'C:\fake\flutter\bin\flutter.bat' : '/fake/flutter/bin/flutter'; setUpAll(() { - Cache.flutterRoot = globals.platform.isWindows + Cache.flutterRoot = platform.isWindows ? r'C:\fake\flutter' : '/fake/flutter'; }); - - group('launchRequest', () { - test('runs "flutter run" with --machine', () async { - final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); + group('launchRequest', () { + test('runs "flutter run" with --machine', () async { + final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final Completer responseCompleter = Completer(); final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( @@ -43,8 +51,33 @@ void main() { expect(adapter.processArgs, containsAllInOrder(['run', '--machine'])); }); + test('includes env variables', () async { + final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); + final Completer responseCompleter = Completer(); + + final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( + cwd: '/project', + program: 'foo.dart', + env: { + 'MY_TEST_ENV': 'MY_TEST_VALUE', + }, + ); + + await adapter.configurationDoneRequest(MockRequest(), null, () {}); + await adapter.launchRequest(MockRequest(), args, responseCompleter.complete); + await responseCompleter.future; + + expect(adapter.env!['MY_TEST_ENV'], 'MY_TEST_VALUE'); + }); + test('does not record the VMs PID for terminating', () async { - final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); + final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final Completer responseCompleter = Completer(); final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( @@ -65,10 +98,12 @@ void main() { }); }); - - group('attachRequest', () { - test('runs "flutter attach" with --machine', () async { - final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); + group('attachRequest', () { + test('runs "flutter attach" with --machine', () async { + final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final Completer responseCompleter = Completer(); final FlutterAttachRequestArguments args = FlutterAttachRequestArguments( @@ -83,7 +118,10 @@ void main() { }); test('does not record the VMs PID for terminating', () async { - final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); + final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final Completer responseCompleter = Completer(); final FlutterAttachRequestArguments args = FlutterAttachRequestArguments( @@ -105,7 +143,10 @@ void main() { group('--start-paused', () { test('is passed for debug mode', () async { - final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); + final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final Completer responseCompleter = Completer(); final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( @@ -121,7 +162,10 @@ void main() { }); test('is not passed for noDebug mode', () async { - final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); + final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final Completer responseCompleter = Completer(); final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( @@ -138,7 +182,10 @@ void main() { }); test('is not passed if toolArgs contains --profile', () async { - final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); + final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final Completer responseCompleter = Completer(); final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( @@ -155,7 +202,10 @@ void main() { }); test('is not passed if toolArgs contains --release', () async { - final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); + final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final Completer responseCompleter = Completer(); final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( @@ -173,7 +223,10 @@ void main() { }); test('includes toolArgs', () async { - final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); + final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final Completer responseCompleter = Completer(); final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( @@ -193,7 +246,10 @@ void main() { group('includes customTool', () { test('with no args replaced', () async { - final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); + final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( cwd: '/project', program: 'foo.dart', @@ -212,7 +268,10 @@ void main() { }); test('with all args replaced', () async { - final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); + final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( cwd: '/project', program: 'foo.dart', diff --git a/packages/flutter_tools/test/general.shard/dap/flutter_test_adapter_test.dart b/packages/flutter_tools/test/general.shard/dap/flutter_test_adapter_test.dart index 79064e81ca1..a2717ea7382 100644 --- a/packages/flutter_tools/test/general.shard/dap/flutter_test_adapter_test.dart +++ b/packages/flutter_tools/test/general.shard/dap/flutter_test_adapter_test.dart @@ -4,29 +4,35 @@ import 'dart:async'; +import 'package:file/memory.dart'; +import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/debug_adapters/flutter_adapter_args.dart'; -import 'package:flutter_tools/src/globals.dart' as globals; +import 'package:flutter_tools/src/globals.dart' as globals show platform; import 'package:test/test.dart'; import 'mocks.dart'; void main() { + // Use the real platform as a base so that Windows bots test paths. + final FakePlatform platform = FakePlatform.fromPlatform(globals.platform); + final FileSystemStyle fsStyle = platform.isWindows ? FileSystemStyle.windows : FileSystemStyle.posix; + group('flutter test adapter', () { - final String expectedFlutterExecutable = globals.platform.isWindows + final String expectedFlutterExecutable = platform.isWindows ? r'C:\fake\flutter\bin\flutter.bat' : '/fake/flutter/bin/flutter'; setUpAll(() { - Cache.flutterRoot = globals.platform.isWindows + Cache.flutterRoot = platform.isWindows ? r'C:\fake\flutter' : '/fake/flutter'; }); test('includes toolArgs', () async { final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter( - fileSystem: globals.fs, - platform: globals.platform, + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, ); final Completer responseCompleter = Completer(); final MockRequest request = MockRequest(); @@ -45,10 +51,34 @@ void main() { expect(adapter.processArgs, contains('tool_arg')); }); + test('includes env variables', () async { + final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); + final Completer responseCompleter = Completer(); + final MockRequest request = MockRequest(); + final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( + cwd: '/project', + program: 'foo.dart', + env: { + 'MY_TEST_ENV': 'MY_TEST_VALUE', + }, + ); + + await adapter.configurationDoneRequest(request, null, () {}); + await adapter.launchRequest(request, args, responseCompleter.complete); + await responseCompleter.future; + + expect(adapter.env!['MY_TEST_ENV'], 'MY_TEST_VALUE'); + }); + group('includes customTool', () { test('with no args replaced', () async { - final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter(fileSystem: globals.fs, - platform: globals.platform,); + final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final Completer responseCompleter = Completer(); final MockRequest request = MockRequest(); final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( @@ -68,8 +98,10 @@ void main() { }); test('with all args replaced', () async { - final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter(fileSystem: globals.fs, - platform: globals.platform,); + final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter( + fileSystem: MemoryFileSystem.test(style: fsStyle), + platform: platform, + ); final Completer responseCompleter = Completer(); final MockRequest request = MockRequest(); final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( diff --git a/packages/flutter_tools/test/general.shard/dap/mocks.dart b/packages/flutter_tools/test/general.shard/dap/mocks.dart index b544213e504..da8873a2f99 100644 --- a/packages/flutter_tools/test/general.shard/dap/mocks.dart +++ b/packages/flutter_tools/test/general.shard/dap/mocks.dart @@ -42,11 +42,17 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter { late String executable; late List processArgs; + late Map? env; @override - Future launchAsProcess(String executable, List processArgs) async { + Future launchAsProcess({ + required String executable, + required List processArgs, + required Map? env, + }) async { this.executable = executable; this.processArgs = processArgs; + this.env = env; // Pretend we launched the app and got the app.started event so that // launchRequest will complete. @@ -94,11 +100,25 @@ class MockFlutterTestDebugAdapter extends FlutterTestDebugAdapter { late String executable; late List processArgs; + late Map? env; @override - Future launchAsProcess(String executable, List processArgs,) async { + Future launchAsProcess({ + required String executable, + required List processArgs, + required Map? env, + }) async { this.executable = executable; this.processArgs = processArgs; + this.env = env; + } + + @override + Future get debuggerInitialized { + // If we were mocking debug mode, then simulate the debugger initializing. + return enableDebugger + ? Future.value() + : throw StateError('Invalid attempt to wait for debuggerInitialized when not debugging'); } }