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: fix flaky test-dgram-exclusive-implicit-bind
test-dgram-exclusive-implicit-bind is written assuming that dgram
messages are received with 100% reliability. While missing a dgram
message sent to localhost is rare, we do see it as evidenced by CI
failures from time to time.

The test has been rewritten to send dgram messages over and over until
the test requirements have been met.

Additional incidental refactoring includes:

* var -> const
* use of common.mustCall() instead of exit listener + boolean
  • Loading branch information
Trott committed Dec 10, 2016
commit 151d2acc0d987f49a6a4aa3768d25304ef08e5e5
47 changes: 29 additions & 18 deletions test/parallel/test-dgram-exclusive-implicit-bind.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var common = require('../common');
var assert = require('assert');
var cluster = require('cluster');
var dgram = require('dgram');
const common = require('../common');
const assert = require('assert');
const cluster = require('cluster');
const dgram = require('dgram');

// Without an explicit bind, send() causes an implicit bind, which always
// generate a unique per-socket ephemeral port. An explicit bind to a port
Expand All @@ -40,17 +40,21 @@ var dgram = require('dgram');
// with ENOTSUP.

if (cluster.isMaster) {
var pass;
var messages = 0;
var ports = {};

process.on('exit', function() {
assert.strictEqual(pass, true);
});
const ports = {};
const pids = [];

var target = dgram.createSocket('udp4');

const done = common.mustCall(function() {
cluster.disconnect();
target.close();
});

target.on('message', function(buf, rinfo) {
if (pids.includes(buf.toString()))
return;
pids.push(buf.toString());
messages++;
ports[rinfo.port] = true;

Expand All @@ -63,12 +67,6 @@ if (cluster.isMaster) {
assert.strictEqual(Object.keys(ports).length, 3);
done();
}

function done() {
pass = true;
cluster.disconnect();
target.close();
}
});

target.on('listening', function() {
Expand All @@ -85,7 +83,12 @@ if (cluster.isMaster) {
return;
}

var source = dgram.createSocket('udp4');
const source = dgram.createSocket('udp4');
var timer;

source.on('close', function() {
clearTimeout(timer);
});
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.

From a previous comment I'm not sure this was a problem but can't you get rid of this as the interval is already unrefed? At least locally it works for me

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If I remove that line, the test still passes, but it produces a messy output that looks like it's failing:

$ ./node test/parallel/test-dgram-exclusive-implicit-bind.js
dgram.js:527
    throw new Error('Not running'); // error message from dgram_legacy.js
    ^

Error: Not running
    at Socket._healthCheck (dgram.js:527:11)
    at Socket.send (dgram.js:347:8)
    at Timeout.setInterval [as _onTimeout] (/Users/trott/io.js/test/parallel/test-dgram-exclusive-implicit-bind.js:104:10)
    at ontimeout (timers.js:365:14)
    at Timer.unrefdHandle (timers.js:471:5)
dgram.js:527
    throw new Error('Not running'); // error message from dgram_legacy.js
    ^

Error: Not running
    at Socket._healthCheck (dgram.js:527:11)
    at Socket.send (dgram.js:347:8)
    at Timeout.setInterval [as _onTimeout] (/Users/trott/io.js/test/parallel/test-dgram-exclusive-implicit-bind.js:104:10)
    at ontimeout (timers.js:365:14)
    at Timer.unrefdHandle (timers.js:471:5)
$

I'd prefer the processes clean up after themselves and not generate irrelevant errors like that, even if the errors don't cause the test to fail, so I'd prefer to keep the clearInterval() call.

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.

Understood


if (process.env.BOUND === 'y') {
source.bind(0);
Expand All @@ -96,4 +99,12 @@ if (process.env.BOUND === 'y') {
source.unref();
}

source.send(Buffer.from('abc'), 0, 3, common.PORT, '127.0.0.1');
function send() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could all of this just be simplified to:

setInterval(() => {
  source.send(Buffer.from('abc'), 0, 3, common.PORT, '127.0.0.1');
}, 1).unref();

I think you shouldn't have to worry about clearing the interval if you unref it. That would reduce some complexity.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I was concerned that competing 1ms interval timers on some operating systems could result in increased flakiness, but I didn't actually test it, so yeah, let's try that. :-D

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If I don't keep the timer-clearing logic, this happens:

Error: Not running
    at Socket._healthCheck (dgram.js:527:11)
    at Socket.send (dgram.js:347:8)
    at Timeout.setInterval [as _onTimeout] (/Users/trott/io.js/test/parallel/test-dgram-exclusive-implicit-bind.js:104:10)
    at ontimeout (timers.js:365:14)
    at Timer.unrefdHandle (timers.js:471:5)
dgram.js:527
    throw new Error('Not running'); // error message from dgram_legacy.js
    ^

const buf = Buffer.from(process.pid.toString());
timer = setTimeout(function() {
source.send(buf, common.PORT, '127.0.0.1', send);
}, 1);
timer.unref();
}

send();