blob: 1ec3a71d41aaa98abf6b9b9b15accb834afe9d31 [file]
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Regression test for running commands whose executable path or arguments
// contain a space (e.g. the default pub cache under a Windows user name with
// a space, `C:\Users\First Last\AppData\Local\Pub\Cache\...`).
//
// `runProcess` never runs through a shell. Absolute paths (with a file
// extension on Windows) are required; relative paths and PATHEXT are not
// supported. Spaces in the executable path and arguments are handled by
// passing them as separate CreateProcess / exec arguments.
import 'dart:io';
import 'package:test/test.dart';
import '../helpers.dart';
void main() {
late Uri scriptUri;
setUpAll(() async {
final scriptDir = await tempDirForTest();
scriptUri = scriptDir.resolve('echo_args.dart');
await File.fromUri(scriptUri).writeAsString('''
void main(List<String> args) {
print('ARGC:\${args.length}');
print('ARGV:\${args.join('|')}');
}
''');
});
test(
'runProcess handles an executable path containing a space',
timeout: const Timeout.factor(5),
() async {
final outDir = await tempDirForTest();
final exeUri = outDir.resolve(
'echo args${Platform.isWindows ? '.exe' : ''}',
);
final compileResult = await Process.run(Platform.resolvedExecutable, [
'compile',
'exe',
scriptUri.toFilePath(),
'-o',
exeUri.toFilePath(),
]);
expect(compileResult.exitCode, 0, reason: '${compileResult.stderr}');
final workingDir = await tempDirForTest();
final result = await runProcess(
executable: exeUri,
arguments: ['an argument with spaces'],
workingDirectory: workingDir,
logger: logger,
);
expect(result.exitCode, 0);
expect(result.stdout, contains('ARGC:1'));
expect(result.stdout, contains('ARGV:an argument with spaces'));
},
);
test('runProcess handles arguments containing a space', () async {
final workingDir = await tempDirForTest();
final result = await runProcess(
executable: Uri.file(Platform.resolvedExecutable),
arguments: [scriptUri.toFilePath(), 'first arg', 'second arg'],
workingDirectory: workingDir,
logger: logger,
);
expect(result.exitCode, 0);
expect(result.stdout, contains('ARGC:2'));
expect(result.stdout, contains('ARGV:first arg|second arg'));
});
test('runProcess handles arguments containing a space and quotes', () async {
final workingDir = await tempDirForTest();
final result = await runProcess(
executable: Uri.file(Platform.resolvedExecutable),
arguments: [scriptUri.toFilePath(), 'fir"st arg', 'sec\'ond arg'],
workingDirectory: workingDir,
logger: logger,
);
expect(result.exitCode, 0);
expect(result.stdout, contains('ARGC:2'));
expect(result.stdout, contains('ARGV:fir"st arg|sec\'ond arg'));
});
test('runProcess rejects a relative executable path', () async {
await expectLater(
runProcess(
executable: Uri(path: 'dart'),
logger: logger,
),
throwsA(
isA<ArgumentError>().having(
(e) => e.message,
'message',
contains('absolute'),
),
),
);
});
test(
'runProcess rejects a Windows executable without a file extension',
() async {
if (!Platform.isWindows) return;
await expectLater(
runProcess(
executable: Uri.file(r'C:\path\to\dart'),
logger: logger,
),
throwsA(
isA<ArgumentError>().having(
(e) => e.message,
'message',
contains('PATHEXT'),
),
),
);
},
);
}