Unverified Commit 78d924af authored by Danny Tuppeny's avatar Danny Tuppeny Committed by GitHub

[flutter_tools] [dap] Add support for passing env variables to spawned processes (#107415)

* [flutter_tools] [dap] Add support for passing env variables to spawned processes

* Use named args

* Use in-memory fs and FakePlatform

* Pass filesystem style to MemoryFileSystem
parent 66cd09dd
...@@ -305,7 +305,11 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -305,7 +305,11 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
...?userArgs, ...?userArgs,
]; ];
await launchAsProcess(executable, processArgs); await launchAsProcess(
executable: executable,
processArgs: processArgs,
env: args.env,
);
// Delay responding until the app is launched and (optionally) the debugger // Delay responding until the app is launched and (optionally) the debugger
// is connected. // is connected.
...@@ -316,12 +320,17 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -316,12 +320,17 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
} }
@visibleForOverriding @visibleForOverriding
Future<void> launchAsProcess(String executable, List<String> processArgs) async { Future<void> launchAsProcess({
required String executable,
required List<String> processArgs,
required Map<String, String>? env,
}) async {
logger?.call('Spawning $executable with $processArgs in ${args.cwd}'); logger?.call('Spawning $executable with $processArgs in ${args.cwd}');
final Process process = await Process.start( final Process process = await Process.start(
executable, executable,
processArgs, processArgs,
workingDirectory: args.cwd, workingDirectory: args.cwd,
environment: env,
); );
_process = process; _process = process;
pidsToTerminate.add(process.pid); pidsToTerminate.add(process.pid);
......
...@@ -20,6 +20,7 @@ class FlutterAttachRequestArguments ...@@ -20,6 +20,7 @@ class FlutterAttachRequestArguments
super.restart, super.restart,
super.name, super.name,
super.cwd, super.cwd,
super.env,
super.additionalProjectPaths, super.additionalProjectPaths,
super.debugSdkLibraries, super.debugSdkLibraries,
super.debugExternalPackageLibraries, super.debugExternalPackageLibraries,
...@@ -91,6 +92,7 @@ class FlutterLaunchRequestArguments ...@@ -91,6 +92,7 @@ class FlutterLaunchRequestArguments
super.restart, super.restart,
super.name, super.name,
super.cwd, super.cwd,
super.env,
super.additionalProjectPaths, super.additionalProjectPaths,
super.debugSdkLibraries, super.debugSdkLibraries,
super.debugExternalPackageLibraries, super.debugExternalPackageLibraries,
......
...@@ -135,7 +135,11 @@ class FlutterTestDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArgum ...@@ -135,7 +135,11 @@ class FlutterTestDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArgum
...?args.args, ...?args.args,
]; ];
await launchAsProcess(executable, processArgs); await launchAsProcess(
executable: executable,
processArgs: processArgs,
env: args.env,
);
// Delay responding until the debugger is connected. // Delay responding until the debugger is connected.
if (debug) { if (debug) {
...@@ -144,12 +148,17 @@ class FlutterTestDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArgum ...@@ -144,12 +148,17 @@ class FlutterTestDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArgum
} }
@visibleForOverriding @visibleForOverriding
Future<void> launchAsProcess(String executable, List<String> processArgs) async { Future<void> launchAsProcess({
required String executable,
required List<String> processArgs,
required Map<String, String>? env,
}) async {
logger?.call('Spawning $executable with $processArgs in ${args.cwd}'); logger?.call('Spawning $executable with $processArgs in ${args.cwd}');
final Process process = await Process.start( final Process process = await Process.start(
executable, executable,
processArgs, processArgs,
workingDirectory: args.cwd, workingDirectory: args.cwd,
environment: env,
); );
_process = process; _process = process;
pidsToTerminate.add(process.pid); pidsToTerminate.add(process.pid);
......
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
import 'dart:async'; 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/cache.dart';
import 'package:flutter_tools/src/debug_adapters/flutter_adapter_args.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/fake.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart'; import 'package:vm_service/vm_service.dart';
...@@ -14,21 +16,27 @@ import 'package:vm_service/vm_service.dart'; ...@@ -14,21 +16,27 @@ import 'package:vm_service/vm_service.dart';
import 'mocks.dart'; import 'mocks.dart';
void main() { 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', () { group('flutter adapter', () {
final String expectedFlutterExecutable = globals.platform.isWindows final String expectedFlutterExecutable = platform.isWindows
? r'C:\fake\flutter\bin\flutter.bat' ? r'C:\fake\flutter\bin\flutter.bat'
: '/fake/flutter/bin/flutter'; : '/fake/flutter/bin/flutter';
setUpAll(() { setUpAll(() {
Cache.flutterRoot = globals.platform.isWindows Cache.flutterRoot = platform.isWindows
? r'C:\fake\flutter' ? r'C:\fake\flutter'
: '/fake/flutter'; : '/fake/flutter';
}); });
group('launchRequest', () { group('launchRequest', () {
test('runs "flutter run" with --machine', () async { test('runs "flutter run" with --machine', () async {
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
);
final Completer<void> responseCompleter = Completer<void>(); final Completer<void> responseCompleter = Completer<void>();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
...@@ -43,8 +51,33 @@ void main() { ...@@ -43,8 +51,33 @@ void main() {
expect(adapter.processArgs, containsAllInOrder(<String>['run', '--machine'])); expect(adapter.processArgs, containsAllInOrder(<String>['run', '--machine']));
}); });
test('includes env variables', () async {
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
);
final Completer<void> responseCompleter = Completer<void>();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
cwd: '/project',
program: 'foo.dart',
env: <String, String>{
'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 { 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<void> responseCompleter = Completer<void>(); final Completer<void> responseCompleter = Completer<void>();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
...@@ -65,10 +98,12 @@ void main() { ...@@ -65,10 +98,12 @@ void main() {
}); });
}); });
group('attachRequest', () { group('attachRequest', () {
test('runs "flutter attach" with --machine', () async { test('runs "flutter attach" with --machine', () async {
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(fileSystem: globals.fs, platform: globals.platform); final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
);
final Completer<void> responseCompleter = Completer<void>(); final Completer<void> responseCompleter = Completer<void>();
final FlutterAttachRequestArguments args = FlutterAttachRequestArguments( final FlutterAttachRequestArguments args = FlutterAttachRequestArguments(
...@@ -83,7 +118,10 @@ void main() { ...@@ -83,7 +118,10 @@ void main() {
}); });
test('does not record the VMs PID for terminating', () async { 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<void> responseCompleter = Completer<void>(); final Completer<void> responseCompleter = Completer<void>();
final FlutterAttachRequestArguments args = FlutterAttachRequestArguments( final FlutterAttachRequestArguments args = FlutterAttachRequestArguments(
...@@ -105,7 +143,10 @@ void main() { ...@@ -105,7 +143,10 @@ void main() {
group('--start-paused', () { group('--start-paused', () {
test('is passed for debug mode', () async { 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<void> responseCompleter = Completer<void>(); final Completer<void> responseCompleter = Completer<void>();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
...@@ -121,7 +162,10 @@ void main() { ...@@ -121,7 +162,10 @@ void main() {
}); });
test('is not passed for noDebug mode', () async { 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<void> responseCompleter = Completer<void>(); final Completer<void> responseCompleter = Completer<void>();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
...@@ -138,7 +182,10 @@ void main() { ...@@ -138,7 +182,10 @@ void main() {
}); });
test('is not passed if toolArgs contains --profile', () async { 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<void> responseCompleter = Completer<void>(); final Completer<void> responseCompleter = Completer<void>();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
...@@ -155,7 +202,10 @@ void main() { ...@@ -155,7 +202,10 @@ void main() {
}); });
test('is not passed if toolArgs contains --release', () async { 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<void> responseCompleter = Completer<void>(); final Completer<void> responseCompleter = Completer<void>();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
...@@ -173,7 +223,10 @@ void main() { ...@@ -173,7 +223,10 @@ void main() {
}); });
test('includes toolArgs', () async { 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<void> responseCompleter = Completer<void>(); final Completer<void> responseCompleter = Completer<void>();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
...@@ -193,7 +246,10 @@ void main() { ...@@ -193,7 +246,10 @@ void main() {
group('includes customTool', () { group('includes customTool', () {
test('with no args replaced', () async { 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( final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
cwd: '/project', cwd: '/project',
program: 'foo.dart', program: 'foo.dart',
...@@ -212,7 +268,10 @@ void main() { ...@@ -212,7 +268,10 @@ void main() {
}); });
test('with all args replaced', () async { 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( final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
cwd: '/project', cwd: '/project',
program: 'foo.dart', program: 'foo.dart',
......
...@@ -4,29 +4,35 @@ ...@@ -4,29 +4,35 @@
import 'dart:async'; 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/cache.dart';
import 'package:flutter_tools/src/debug_adapters/flutter_adapter_args.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 'package:test/test.dart';
import 'mocks.dart'; import 'mocks.dart';
void main() { 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', () { group('flutter test adapter', () {
final String expectedFlutterExecutable = globals.platform.isWindows final String expectedFlutterExecutable = platform.isWindows
? r'C:\fake\flutter\bin\flutter.bat' ? r'C:\fake\flutter\bin\flutter.bat'
: '/fake/flutter/bin/flutter'; : '/fake/flutter/bin/flutter';
setUpAll(() { setUpAll(() {
Cache.flutterRoot = globals.platform.isWindows Cache.flutterRoot = platform.isWindows
? r'C:\fake\flutter' ? r'C:\fake\flutter'
: '/fake/flutter'; : '/fake/flutter';
}); });
test('includes toolArgs', () async { test('includes toolArgs', () async {
final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter( final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter(
fileSystem: globals.fs, fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: globals.platform, platform: platform,
); );
final Completer<void> responseCompleter = Completer<void>(); final Completer<void> responseCompleter = Completer<void>();
final MockRequest request = MockRequest(); final MockRequest request = MockRequest();
...@@ -45,10 +51,34 @@ void main() { ...@@ -45,10 +51,34 @@ void main() {
expect(adapter.processArgs, contains('tool_arg')); expect(adapter.processArgs, contains('tool_arg'));
}); });
test('includes env variables', () async {
final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
);
final Completer<void> responseCompleter = Completer<void>();
final MockRequest request = MockRequest();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
cwd: '/project',
program: 'foo.dart',
env: <String, String>{
'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', () { group('includes customTool', () {
test('with no args replaced', () async { test('with no args replaced', () async {
final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter(fileSystem: globals.fs, final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter(
platform: globals.platform,); fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
);
final Completer<void> responseCompleter = Completer<void>(); final Completer<void> responseCompleter = Completer<void>();
final MockRequest request = MockRequest(); final MockRequest request = MockRequest();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
...@@ -68,8 +98,10 @@ void main() { ...@@ -68,8 +98,10 @@ void main() {
}); });
test('with all args replaced', () async { test('with all args replaced', () async {
final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter(fileSystem: globals.fs, final MockFlutterTestDebugAdapter adapter = MockFlutterTestDebugAdapter(
platform: globals.platform,); fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
);
final Completer<void> responseCompleter = Completer<void>(); final Completer<void> responseCompleter = Completer<void>();
final MockRequest request = MockRequest(); final MockRequest request = MockRequest();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments( final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
......
...@@ -42,11 +42,17 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter { ...@@ -42,11 +42,17 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
late String executable; late String executable;
late List<String> processArgs; late List<String> processArgs;
late Map<String, String>? env;
@override @override
Future<void> launchAsProcess(String executable, List<String> processArgs) async { Future<void> launchAsProcess({
required String executable,
required List<String> processArgs,
required Map<String, String>? env,
}) async {
this.executable = executable; this.executable = executable;
this.processArgs = processArgs; this.processArgs = processArgs;
this.env = env;
// Pretend we launched the app and got the app.started event so that // Pretend we launched the app and got the app.started event so that
// launchRequest will complete. // launchRequest will complete.
...@@ -94,11 +100,25 @@ class MockFlutterTestDebugAdapter extends FlutterTestDebugAdapter { ...@@ -94,11 +100,25 @@ class MockFlutterTestDebugAdapter extends FlutterTestDebugAdapter {
late String executable; late String executable;
late List<String> processArgs; late List<String> processArgs;
late Map<String, String>? env;
@override @override
Future<void> launchAsProcess(String executable, List<String> processArgs,) async { Future<void> launchAsProcess({
required String executable,
required List<String> processArgs,
required Map<String, String>? env,
}) async {
this.executable = executable; this.executable = executable;
this.processArgs = processArgs; this.processArgs = processArgs;
this.env = env;
}
@override
Future<void> get debuggerInitialized {
// If we were mocking debug mode, then simulate the debugger initializing.
return enableDebugger
? Future<void>.value()
: throw StateError('Invalid attempt to wait for debuggerInitialized when not debugging');
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment