-
-
Notifications
You must be signed in to change notification settings - Fork 115
Expand file tree
/
Copy pathtest.js
More file actions
210 lines (177 loc) · 6.94 KB
/
test.js
File metadata and controls
210 lines (177 loc) · 6.94 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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
import process from 'node:process';
import path from 'node:path';
import {once} from 'node:events';
import childProcess from 'node:child_process';
import test from 'ava';
import psList from './index.js';
const isWindows = process.platform === 'win32';
const nodeBinaryName = isWindows ? 'node.exe' : 'node';
const testBinaryName = isWindows ? nodeBinaryName : 'ava';
test('main', async t => {
const list = await psList();
if (isWindows) {
t.true(list.some(x => x.name.includes(testBinaryName)));
} else {
t.true(list.some(x => x.cmd.includes(testBinaryName)));
}
t.true(list.every(x =>
typeof x.pid === 'number'
&& typeof x.name === 'string'
&& typeof x.ppid === 'number'));
if (!isWindows) {
t.true(list.every(x =>
typeof x.cmd === 'string'
&& typeof x.cpu === 'number'
&& typeof x.memory === 'number'
&& (typeof x.uid === 'number' || x.uid === undefined)
&& typeof x.path === 'string'
&& (x.startTime instanceof Date || x.startTime === undefined)));
// Verify Date objects are valid (not Invalid Date)
for (const process_ of list) {
if (process_.startTime) {
t.false(Number.isNaN(process_.startTime.getTime()), `Process ${process_.pid} has Invalid Date`);
t.true(process_.startTime <= new Date(), `Process ${process_.pid} startTime is not in future`);
}
}
// Verify percentages are in valid range
t.true(
list.every(x => x.cpu >= 0 && x.cpu <= 100 * 10), // Allow up to 1000% for multi-core
'CPU percentages should be valid',
);
t.true(
list.every(x => x.memory >= 0 && x.memory <= 100),
'Memory percentages should be valid',
);
}
});
test('custom binary', async t => {
const arguments_ = ['./fixtures/sleep-forever.js', 'arg1', 'arg2'];
const sleepForever = childProcess.spawn(nodeBinaryName, arguments_);
const list = await psList();
const record = list.find(process_ => process_.pid === sleepForever.pid);
sleepForever.kill(9);
await once(sleepForever, 'exit');
t.is(record.pid, sleepForever.pid);
t.is(record.name, nodeBinaryName);
t.is(record.ppid, process.pid);
if (!isWindows) {
t.is(record.cmd, `${nodeBinaryName} ${arguments_.join(' ')}`);
t.is(record.uid, process.getuid());
// Path can be empty (relative command) or absolute (CI environments)
t.true(typeof record.path === 'string', 'Path should be a string');
if (record.path) {
// If path is resolved, it should contain node
t.true(record.path.includes('node'), 'Resolved path should contain node');
}
t.true(record.startTime instanceof Date);
t.true(record.startTime <= new Date());
// Verify startTime is valid (not Invalid Date)
if (record.startTime) {
t.false(Number.isNaN(record.startTime.getTime()), 'startTime should be valid Date');
}
}
});
test('path resolution', async t => {
if (isWindows) {
t.pass('Path resolution test skipped on Windows');
return;
}
const list = await psList();
// On Linux, test /proc/{pid}/exe resolution for current process
if (process.platform === 'linux') {
const currentProcess = list.find(x => x.pid === process.pid);
if (currentProcess) {
t.true(currentProcess.path.startsWith('/'), 'Current process should have absolute path from /proc/pid/exe');
t.true(currentProcess.path.includes('node'), 'Current process path should contain node');
// Path should be different from name (which is truncated)
t.not(currentProcess.path, currentProcess.name, 'Path should be full path, not truncated name');
}
}
// Find init process - should have absolute path
const initProcess = list.find(x => x.pid === 1);
if (initProcess) {
t.true(initProcess.path.startsWith('/'), 'Init process should have absolute path');
}
// Find any node process - should have path resolved
const nodeProcess = list.find(x => x.name === 'node' || x.name === nodeBinaryName);
if (nodeProcess) {
t.true(nodeProcess.path.includes('node'), 'Node process path should contain node');
// Verify path is not just the truncated comm
if (nodeProcess.path.startsWith('/')) {
t.true(nodeProcess.path.length > nodeProcess.name.length, 'Path should be longer than truncated name');
}
}
});
test('large command line', async t => {
if (isWindows) {
t.pass('Large command test skipped on Windows');
return;
}
// Create a process with very long arguments
const longArgument = 'x'.repeat(10_000);
const sleepForever = childProcess.spawn(nodeBinaryName, ['./fixtures/sleep-forever.js', longArgument]);
try {
const list = await psList();
const record = list.find(process_ => process_.pid === sleepForever.pid);
t.truthy(record, 'Should find process with large command line');
if (record) {
t.true(record.cmd.length > 9000, 'Should capture long command line');
}
} finally {
sleepForever.kill(9);
await once(sleepForever, 'exit');
}
});
test('quoted paths and name selection', async t => {
if (isWindows) {
t.pass('Quoted paths test skipped on Windows');
return;
}
const list = await psList();
// Find processes with absolute paths to test name selection
const processesWithPaths = list.filter(x => x.path && x.path.startsWith('/'));
t.true(processesWithPaths.length > 0, 'Should have processes with absolute paths');
for (const process_ of processesWithPaths) {
// When path is absolute, name should be basename of path
const expectedName = path.basename(process_.path);
t.is(process_.name, expectedName, `Process ${process_.pid} name should be basename of path: ${process_.path}`);
// Name should be different from raw comm when path is resolved
if (process_.path !== process_.comm) {
t.not(process_.name, process_.comm, `Process ${process_.pid} name should be derived from path, not truncated comm`);
}
}
});
test('locale handling', async t => {
if (isWindows) {
t.pass('Locale test skipped on Windows');
return;
}
// Test with Japanese locale (this ensures LC_ALL=C is working)
const originalLang = process.env.LANG;
const originalLcAll = process.env.LC_ALL;
process.env.LANG = 'ja_JP.UTF-8';
process.env.LC_ALL = 'ja_JP.UTF-8';
try {
const list = await psList();
t.true(list.length > 0, 'Should get processes even with non-English locale');
let validDateCount = 0;
let undefinedDateCount = 0;
// Check all dates are either valid Date objects or undefined
for (const process_ of list) {
if (process_.startTime === undefined) {
undefinedDateCount++;
} else {
t.true(process_.startTime instanceof Date, `Process ${process_.pid} startTime should be Date object`);
t.false(Number.isNaN(process_.startTime.getTime()), `Process ${process_.pid} should not have Invalid Date`);
t.true(process_.startTime <= new Date(), `Process ${process_.pid} startTime should not be in future`);
validDateCount++;
}
}
// Ensure we have some valid dates (proves LC_ALL=C is working)
t.true(validDateCount > 0, 'Should have at least some valid dates with LC_ALL=C forcing English');
t.log(`Valid dates: ${validDateCount}, Undefined dates: ${undefinedDateCount}`);
} finally {
process.env.LANG = originalLang;
process.env.LC_ALL = originalLcAll;
}
});