Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
test: implement setproctitle for windows
  • Loading branch information
refack committed Sep 20, 2017
commit 98a7501177cae9506a81b966bc1ee13547975f95
69 changes: 49 additions & 20 deletions test/parallel/test-setproctitle.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,65 @@ if (common.isSunOS)
common.skip(`Unsupported platform [${process.platform}]`);

const assert = require('assert');
const exec = require('child_process').exec;
const { exec, spawn } = require('child_process');
const path = require('path');

// The title shouldn't be too long; libuv's uv_set_process_title() out of
// security considerations no longer overwrites envp, only argv, so the
// maximum title length is possibly quite short.
let title = String(process.pid);
const title = String(Date.now()).substr(-4, 4);
Copy link
Copy Markdown
Member

@addaleax addaleax Jul 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change? (to clarify: I’m sure there’s a reason, I just don’t see it?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was checking what is actually the limit, and it turns out to be 4 chars, and it fails on my machine (when pid > 10000), so I picked a fairly random seed, and truncated to 4.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use a constant value then? I know this wasn’t the case before, but it’s not really a good idea to use random values in tests, where reproducibility matters (alternative: print the title to stderr, that should work as well)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohhh. that's a good idea!
...
But it only give us node's "internal" view, while ps gives us how the external system sees the title
...
The issue with a constant value is that zombies will give us a false positive. We need a secret that only know to the testing process

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right. Yeah, in that case, print it out to stderr.


assert.notStrictEqual(process.title, title);
process.title = title;
assert.strictEqual(process.title, title);
if (process.argv[2] === 'child') {
const childTitle = process.argv[3];
assert.notStrictEqual(process.title, childTitle);
process.title = childTitle;
assert.strictEqual(process.title, childTitle);
// Just wait a little bit before exiting
setTimeout(() => {}, common.platformTimeout(300));
return;
}

// Test setting the title but do not try to run `ps` on Windows.
if (common.isWindows)
common.skip('Windows does not have "ps" utility');
// This should run only in the parent
assert.strictEqual(process.argv.length, 2);
let child;
const cmd = (() => {
if (common.isWindows) {
// For windows we need to spawn a new `window` so it will get the title.
// For that we need a `shell`, and to use `start`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could do with a short comment here stating that changing the title on Windows changes the title of the Window, which may not be the node process. This add s bit of context as to why the test is different on Windows.

child = spawn(
'start /MIN /WAIT',
[process.execPath, __filename, 'child', title],
{ shell: true, stdio: 'ignore' },
);
const PSL = 'Powershell -NoProfile -ExecutionPolicy Unrestricted -Command';
const winTitle = '$_.mainWindowTitle';
return `${PSL} "ps | ? {${winTitle} -eq '${title}'} | % {${winTitle}}"`;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is subject to race condition, where the PowerShell script may run before the spawned child process changes the title? Maybe we can use fork() instead of spawn() and use the IPC channel for sequencing? Would also have the benefit of not having to kill the child.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll research that, also I think there is a guarantee that something (not sure what) has to finish before spawn returns.
Also AFAIK it's possible to establish an IPC via spawn

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P.S. I have seen inconsistant results but I attributed them to the child exiting too soon 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, with IPC we could get the child to wait around instead of arbitrary timeout.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't use IPC because the script is run not in the child but in a grandchild created with START...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, is there any solution to this? Because without this solved it seems like there can not be any progress.

} else {
// for non Windows we change our own title
assert.notStrictEqual(process.title, title);
process.title = title;
assert.strictEqual(process.title, title);

// To pass this test on alpine, since Busybox `ps` does not
// support `-p` switch, use `ps -o` and `grep` instead.
const cmd = common.isLinux ?
`ps -o pid,args | grep '${process.pid} ${title}' | grep -v grep` :
`ps -p ${process.pid} -o args=`;
// To pass this test on alpine, since Busybox `ps` does not
// support `-p` switch, use `ps -o` and `grep` instead.
return common.isLinux ?
`ps -o pid,args | grep '${process.pid} ${title}' | grep -v grep` :
`ps -p ${child.pid} -o args=`;
}
})();

exec(cmd, common.mustCall((error, stdout, stderr) => {
// We don't need the child anymore...
if (child) {
child.kill();
child.unref();
}
assert.ifError(error);
assert.strictEqual(stderr, '');
const ret = stdout.trim();

// freebsd always add ' (procname)' to the process title
if (common.isFreeBSD)
title += ` (${path.basename(process.execPath)})`;

// omitting trailing whitespace and \n
assert.strictEqual(stdout.replace(/\s+$/, '').endsWith(title), true);
// freeBSD adds ' (procname)' to the process title
const bsdSuffix = ` (${path.basename(process.execPath)})`;
const expected = title + (common.isFreeBSD ? bsdSuffix : '');
assert.strictEqual(ret.endsWith(expected), true);
assert.strictEqual(ret.endsWith(expected), true);
}));