Skip to content

Commit 9f94fbd

Browse files
committed
tests: Add worker teardown tests for iOS
* Add test for crashes during worker tear down * Add test for leaked runtimes * Plug leaked workers * Account for workers that need to be alive
1 parent 1844a35 commit 9f94fbd

3 files changed

Lines changed: 97 additions & 4 deletions

File tree

Workers/TeardownCrashWorker.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
let workerIndex = -1;
2+
onmessage = function(msg) {
3+
workerIndex = msg.data;
4+
};
5+
6+
// Notify test that we are starting
7+
postMessage("starting");
8+
9+
let o = NSNotificationCenter.defaultCenter.addObserverForNameObjectQueueUsingBlock('send-to-worker', null, null, (obj) => {
10+
// console.log("received NSNotificationCenter message " + workerIndex, obj);
11+
});
12+
13+
// Allocate some native objects with JS wrappers
14+
for (var i = 0; i < 100; i++) {
15+
NSString.alloc();
16+
}
17+
18+
require("../../Infrastructure/timers");
19+
setTimeout(() => {
20+
NSNotificationCenter.defaultCenter.removeObserver(o);
21+
// Notify test that we have finished
22+
postMessage("closing");
23+
// Necessary for the correct teardown of the worker thread, otherwise native JSBlock will never be deallocated (it holds a strong reference to the VM and
24+
// the worker thread waits for VM's refcount to reach 1 before destroying it)
25+
// o = null;
26+
// __collect();
27+
// Alternatively, __releaseNativeCounterpart can be used.
28+
__releaseNativeCounterpart(o);
29+
30+
global.close();
31+
}, 700);

Workers/WorkerStressJSTest.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ for (var i = 0; i < 10000; i++) {
1717
} catch (ex) {
1818
postMessage("Unexpected exception: " + ex);
1919
}
20-
}
20+
}
21+
22+
postMessage("end");

Workers/index.js

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
describe("TNS Workers", () => {
2+
let expectedAliveRuntimes = 1; // Main thread's TNSRuntime
23
var originalTimeout;
34
var DEFAULT_TIMEOUT_BEFORE_ASSERT = 500;
45

@@ -26,6 +27,7 @@ describe("TNS Workers", () => {
2627
worker.postMessage({ eval: "postMessage(self === global);" });
2728
worker.onmessage = (msg) => {
2829
expect(msg.data).toBe(true);
30+
worker.terminate();
2931
done();
3032
};
3133
});
@@ -39,6 +41,7 @@ describe("TNS Workers", () => {
3941
var worker = new Worker("./idonot-exist.js");
4042
worker.onerror = (e) => {
4143
expect(e).not.toEqual(null);
44+
worker.terminate();
4245
done();
4346
}
4447
});
@@ -318,6 +321,7 @@ describe("TNS Workers", () => {
318321
setTimeout(() => {
319322
expect(onerrorCounter).toBe(2);
320323
expect(onmessageCounter).toBe(1);
324+
worker.terminate();
321325
done();
322326
}, DEFAULT_TIMEOUT_BEFORE_ASSERT);
323327
});
@@ -354,12 +358,19 @@ describe("TNS Workers", () => {
354358

355359
it("Should not throw or crash when executing too much JS inside Worker", (done) => {
356360
var worker = new Worker("./WorkerStressJSTest.js");
361+
// Worker is not guaranteed to have finished before the check for runtimes leak, so track it manually
362+
expectedAliveRuntimes++;
357363
// the specific worker will post a message if something isn't right
358364
worker.onmessage = (msg) => {
359-
worker.terminate();
360-
done("Exception is thrown in the web worker: " + msg);
365+
// Worker sends this message when it finishes successfully
366+
expectedAliveRuntimes--;
367+
if (msg.data !== "end") {
368+
worker.terminate();
369+
done("Exception is thrown in the web worker: " + msg);
370+
}
361371
}
362372
worker.onerror = (e) => {
373+
expectedAliveRuntimes--;
363374
worker.terminate();
364375
done("Exception is thrown in the web worker: " + e);
365376
}
@@ -383,13 +394,13 @@ describe("TNS Workers", () => {
383394
gC();
384395

385396
setTimeout(() => {
397+
expectedAliveRuntimes++; // This nature of this test prevents us from closing the worker (we need not store a reference to it)
386398
expect(onmessageCalled).toBe(true);
387399
done();
388400
}, DEFAULT_TIMEOUT_BEFORE_ASSERT);
389401
});
390402

391403
it("Test worker should close and not receive messages after close() call", (done) => {
392-
var messageReceived = false;
393404
var worker = new Worker("./EvalWorker.js");
394405

395406
worker.postMessage({
@@ -469,6 +480,55 @@ describe("TNS Workers", () => {
469480
}, DEFAULT_TIMEOUT_BEFORE_ASSERT);
470481
});
471482

483+
if(global.NSObject) { // platform is iOS
484+
it("no crash during or after runtime teardown on iOS", (done) => {
485+
// reduce number of workers on older (32-bit devices) to avoid sporadic failures due to timeout
486+
const numWorkers = (interop.sizeof(interop.types.id) == 4) ? 4 : 10;
487+
const timeout = DEFAULT_TIMEOUT_BEFORE_ASSERT*3.5;
488+
489+
let messageProducerTimeout = null;
490+
let iteration = 0;
491+
const produceMessageInLoop = () => {
492+
NSNotificationCenter.defaultCenter.postNotificationNameObjectUserInfo('send-to-worker', { iteration }, null);
493+
iteration++;
494+
messageProducerTimeout = setTimeout(produceMessageInLoop, 1);
495+
};
496+
produceMessageInLoop();
497+
498+
let onCloseEvents = 0;
499+
let onStartEvents = 0;
500+
for (let i = 0; i < numWorkers; i++) {;
501+
const worker = new Worker("./TeardownCrashWorker.js");
502+
worker.onmessage = (msg) => {
503+
if (msg.data === "closing") {
504+
onCloseEvents++;
505+
}
506+
else if (msg.data === "starting") {
507+
onStartEvents++;
508+
worker.postMessage(i);
509+
}
510+
}
511+
}
512+
513+
setTimeout(() => {
514+
clearTimeout(messageProducerTimeout);
515+
516+
expect(onStartEvents).toBeGreaterThan(0, `At least 1 worker should have started in ${timeout} ms`);
517+
expect(onCloseEvents).toBeGreaterThan(0, `At least 1 worker should have finished in ${timeout} ms`);
518+
done();
519+
}, timeout);
520+
});
521+
522+
it("Check for leaked runtimes", function(done) {
523+
setTimeout(() => {
524+
const runtimesCount = TNSRuntime.runtimes().count;
525+
expect(runtimesCount).toBe(expectedAliveRuntimes, `Found ${runtimesCount} runtimes alive. Expected ${expectedAliveRuntimes}.`);
526+
done();
527+
}, 1000);
528+
});
529+
530+
} // platform is iOS
531+
472532
function generateRandomString(strLen) {
473533
var chars = "abcAbc defgDEFG 1234567890 ";
474534
var len = chars.length;

0 commit comments

Comments
 (0)