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
...?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
// is connected.
......@@ -316,12 +320,17 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
}
@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}');
final Process process = await Process.start(
executable,
processArgs,
workingDirectory: args.cwd,
environment: env,
);
_process = process;
pidsToTerminate.add(process.pid);
......
......@@ -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,
......
......@@ -135,7 +135,11 @@ class FlutterTestDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArgum
...?args.args,
];
await launchAsProcess(executable, processArgs);
await launchAsProcess(
executable: executable,
processArgs: processArgs,
env: args.env,
);
// Delay responding until the debugger is connected.
if (debug) {
......@@ -144,12 +148,17 @@ class FlutterTestDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArgum
}
@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}');
final Process process = await Process.start(
executable,
processArgs,
workingDirectory: args.cwd,
environment: env,
);
_process = process;
pidsToTerminate.add(process.pid);
......
......@@ -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<void> responseCompleter = Completer<void>();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
......@@ -43,8 +51,33 @@ void main() {
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 {
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 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<void> responseCompleter = Completer<void>();
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<void> responseCompleter = Completer<void>();
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<void> responseCompleter = Completer<void>();
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<void> responseCompleter = Completer<void>();
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<void> responseCompleter = Completer<void>();
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<void> responseCompleter = Completer<void>();
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<void> responseCompleter = Completer<void>();
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',
......
......@@ -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<void> responseCompleter = Completer<void>();
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<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', () {
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<void> responseCompleter = Completer<void>();
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<void> responseCompleter = Completer<void>();
final MockRequest request = MockRequest();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
......
......@@ -42,11 +42,17 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
late String executable;
late List<String> processArgs;
late Map<String, String>? env;
@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.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<String> processArgs;
late Map<String, String>? env;
@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.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