forked from microsoft/rushstack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExecutable.test.ts
More file actions
180 lines (150 loc) · 6.74 KB
/
Executable.test.ts
File metadata and controls
180 lines (150 loc) · 6.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import * as os from 'os';
import * as path from 'path';
import * as child_process from 'child_process';
import { Executable, IExecutableSpawnSyncOptions } from '../Executable';
import { FileSystem } from '../FileSystem';
import { PosixModeBits } from '../PosixModeBits';
import { Text } from '../Text';
// The PosixModeBits are intended to be used with bitwise operations.
/* eslint-disable no-bitwise */
// Use src/test/test-data instead of lib/test/test-data
const executableFolder: string = path.join(__dirname, '..', '..', 'src', 'test', 'test-data', 'executable');
let environment: NodeJS.ProcessEnv;
if (os.platform() === 'win32') {
environment = {
PATH: [
path.join(executableFolder, 'skipped'),
path.join(executableFolder, 'success'),
path.join(executableFolder, 'fail'),
path.dirname(process.execPath) // the folder where node.exe can be found
].join(path.delimiter),
PATHEXT: '.COM;.EXE;.BAT;.CMD;.VBS',
TEST_VAR: '123'
};
} else {
environment = {
PATH: [
path.join(executableFolder, 'skipped'),
path.join(executableFolder, 'success'),
path.join(executableFolder, 'fail'),
path.dirname(process.execPath), // the folder where node.exe can be found
// These are needed because our example script needs to find bash
'/usr/local/bin',
'/usr/bin',
'/bin'
].join(path.delimiter),
TEST_VAR: '123'
};
}
const options: IExecutableSpawnSyncOptions = {
environment: environment,
currentWorkingDirectory: executableFolder,
stdio: 'pipe'
};
beforeAll(() => {
// Make sure the test folder exists where we expect it
expect(FileSystem.exists(executableFolder)).toEqual(true);
// Git's core.filemode setting wrongly defaults to true on Windows. This design flaw makes
// it completely impractical to store POSIX file permissions in a cross-platform Git repo.
// So instead we set them before the test runs, and then revert them after the test completes.
if (os.platform() !== 'win32') {
FileSystem.changePosixModeBits(path.join(executableFolder, 'success', 'npm-binary-wrapper'),
PosixModeBits.AllRead | PosixModeBits.AllWrite | PosixModeBits.AllExecute);
FileSystem.changePosixModeBits(path.join(executableFolder, 'success', 'bash-script.sh'),
PosixModeBits.AllRead | PosixModeBits.AllWrite | PosixModeBits.AllExecute);
}
});
afterAll(() => {
// Revert the permissions to the defaults
if (os.platform() !== 'win32') {
FileSystem.changePosixModeBits(path.join(executableFolder, 'success', 'npm-binary-wrapper'),
PosixModeBits.AllRead | PosixModeBits.AllWrite);
FileSystem.changePosixModeBits(path.join(executableFolder, 'success', 'bash-script.sh'),
PosixModeBits.AllRead | PosixModeBits.AllWrite);
}
});
test('Executable.tryResolve() pathless', () => {
const resolved: string | undefined = Executable.tryResolve('npm-binary-wrapper', options);
expect(resolved).toBeDefined();
const resolvedRelative: string = Text.replaceAll(path.relative(executableFolder, resolved!),
'\\', '/');
if (os.platform() === 'win32') {
// On Windows, we should find npm-binary-wrapper.cmd instead of npm-binary-wrapper
expect(resolvedRelative).toEqual('success/npm-binary-wrapper.cmd');
} else {
expect(resolvedRelative).toEqual('success/npm-binary-wrapper');
}
// We should not find the "missing-extension" at all, because its file extension
// is not executable on Windows (and the execute bit is missing on Unix)
expect(Executable.tryResolve('missing-extension', options)).toBeUndefined();
});
test('Executable.tryResolve() with path', () => {
const resolved: string | undefined = Executable.tryResolve('./npm-binary-wrapper', options);
expect(resolved).toBeUndefined();
});
function executeNpmBinaryWrapper(args: string[]): string[] {
const result: child_process.SpawnSyncReturns<string>
= Executable.spawnSync('npm-binary-wrapper', args, options);
expect(result.error).toBeUndefined();
expect(result.stderr).toBeDefined();
expect(result.stderr.toString()).toEqual('');
expect(result.stdout).toBeDefined();
const outputLines: string[] = result.stdout.toString().split(/[\r\n]+/g).map(x => x.trim());
let lineIndex: number = 0;
if (os.platform() === 'win32') {
expect(outputLines[lineIndex++]).toEqual('Executing npm-binary-wrapper.cmd with args:');
} else {
expect(outputLines[lineIndex++]).toEqual('Executing npm-binary-wrapper with args:');
}
// console.log('npm-binary-wrapper.cmd ARGS: ' + outputLines[lineIndex]);
++lineIndex; // skip npm-binary-wrapper's args
expect(outputLines[lineIndex++]).toEqual('Executing javascript-file.js with args:');
const stringifiedArgv: string = outputLines[lineIndex++];
expect(stringifiedArgv.substr(0, 2)).toEqual('[\"');
const argv: string[] = JSON.parse(stringifiedArgv);
// Discard the first two array entries whose path is nondeterministic
argv.shift(); // the path to node.exe
argv.shift(); // the path to javascript-file.js
return argv;
}
test('Executable.spawnSync("npm-binary-wrapper") simple', () => {
const args: string[] = ['arg1', 'arg2', 'arg3'];
expect(executeNpmBinaryWrapper(args)).toEqual(args);
});
test('Executable.spawnSync("npm-binary-wrapper") edge cases 1', () => {
// Characters that confuse the CreateProcess() WIN32 API's encoding
const args: string[] = ['', '/', ' \t ', '"a', 'b"', '"c"', '\\"\\d', '!', '!TEST_VAR!'];
expect(executeNpmBinaryWrapper(args)).toEqual(args);
});
test('Executable.spawnSync("npm-binary-wrapper") edge cases 2', () => {
// All ASCII punctuation
const args: string[] = [
// Characters that are impossible to escape for cmd.exe:
// %^&|<> newline
'~!@#$*()_+`={}[]\:";\'?,./',
'~!@#$*()_+`={}[]\:";\'?,./'
];
expect(executeNpmBinaryWrapper(args)).toEqual(args);
});
test('Executable.spawnSync("npm-binary-wrapper") edge cases 2', () => {
// All ASCII punctuation
const args: string[] = [
// Characters that are impossible to escape for cmd.exe:
// %^&|<> newline
'~!@#$*()_+`={}[]\:";\'?,./',
'~!@#$*()_+`={}[]\:";\'?,./'
];
expect(executeNpmBinaryWrapper(args)).toEqual(args);
});
test('Executable.spawnSync("npm-binary-wrapper") bad characters', () => {
if (os.platform() === 'win32') {
expect(() => { executeNpmBinaryWrapper(['abc%123']); })
.toThrowError('The command line argument "abc%123" contains a special character "%"'
+ ' that cannot be escaped for the Windows shell');
expect(() => { executeNpmBinaryWrapper(['abc<>123']); })
.toThrowError('The command line argument "abc<>123" contains a special character "<"'
+ ' that cannot be escaped for the Windows shell');
}
});