11cb0ef41Sopenharmony_ci// Test Steps Explained 21cb0ef41Sopenharmony_ci// ==================== 31cb0ef41Sopenharmony_ci// 41cb0ef41Sopenharmony_ci// Initializing hooks: 51cb0ef41Sopenharmony_ci// 61cb0ef41Sopenharmony_ci// We initialize 3 hooks. For hook2 and hook3 we register a callback for the 71cb0ef41Sopenharmony_ci// "before" and in case of hook3 also for the "after" invocations. 81cb0ef41Sopenharmony_ci// 91cb0ef41Sopenharmony_ci// Enabling hooks initially: 101cb0ef41Sopenharmony_ci// 111cb0ef41Sopenharmony_ci// We only enable hook1 and hook3 initially. 121cb0ef41Sopenharmony_ci// 131cb0ef41Sopenharmony_ci// Enabling hook2: 141cb0ef41Sopenharmony_ci// 151cb0ef41Sopenharmony_ci// When hook3's "before" invocation occurs we enable hook2. Since this 161cb0ef41Sopenharmony_ci// happens right before calling `onfirstImmediate` hook2 will miss all hook 171cb0ef41Sopenharmony_ci// invocations until then, including the "init" and "before" of the first 181cb0ef41Sopenharmony_ci// Immediate. 191cb0ef41Sopenharmony_ci// However afterwards it collects all invocations that follow on the first 201cb0ef41Sopenharmony_ci// Immediate as well as all invocations on the second Immediate. 211cb0ef41Sopenharmony_ci// 221cb0ef41Sopenharmony_ci// This shows that a hook can enable another hook inside a life time event 231cb0ef41Sopenharmony_ci// callback. 241cb0ef41Sopenharmony_ci// 251cb0ef41Sopenharmony_ci// 261cb0ef41Sopenharmony_ci// Disabling hook1 271cb0ef41Sopenharmony_ci// 281cb0ef41Sopenharmony_ci// Since we registered the "before" callback for hook2 it will execute it 291cb0ef41Sopenharmony_ci// right before `onsecondImmediate` is called. 301cb0ef41Sopenharmony_ci// At that point we disable hook1 which is why it will miss all invocations 311cb0ef41Sopenharmony_ci// afterwards and thus won't include the second "after" as well as the 321cb0ef41Sopenharmony_ci// "destroy" invocations 331cb0ef41Sopenharmony_ci// 341cb0ef41Sopenharmony_ci// This shows that a hook can disable another hook inside a life time event 351cb0ef41Sopenharmony_ci// callback. 361cb0ef41Sopenharmony_ci// 371cb0ef41Sopenharmony_ci// Disabling hook3 381cb0ef41Sopenharmony_ci// 391cb0ef41Sopenharmony_ci// When the second "after" invocation occurs (after onsecondImmediate), hook3 401cb0ef41Sopenharmony_ci// disables itself. 411cb0ef41Sopenharmony_ci// As a result it will not receive the "destroy" invocation. 421cb0ef41Sopenharmony_ci// 431cb0ef41Sopenharmony_ci// This shows that a hook can disable itself inside a life time event callback. 441cb0ef41Sopenharmony_ci// 451cb0ef41Sopenharmony_ci// Sample Test Log 461cb0ef41Sopenharmony_ci// =============== 471cb0ef41Sopenharmony_ci// 481cb0ef41Sopenharmony_ci// - setting up first Immediate 491cb0ef41Sopenharmony_ci// hook1.init.uid-5 501cb0ef41Sopenharmony_ci// hook3.init.uid-5 511cb0ef41Sopenharmony_ci// - finished setting first Immediate 521cb0ef41Sopenharmony_ci// 531cb0ef41Sopenharmony_ci// hook1.before.uid-5 541cb0ef41Sopenharmony_ci// hook3.before.uid-5 551cb0ef41Sopenharmony_ci// - enabled hook2 561cb0ef41Sopenharmony_ci// - entering onfirstImmediate 571cb0ef41Sopenharmony_ci// 581cb0ef41Sopenharmony_ci// - setting up second Immediate 591cb0ef41Sopenharmony_ci// hook1.init.uid-6 601cb0ef41Sopenharmony_ci// hook3.init.uid-6 611cb0ef41Sopenharmony_ci// hook2.init.uid-6 621cb0ef41Sopenharmony_ci// - finished setting second Immediate 631cb0ef41Sopenharmony_ci// 641cb0ef41Sopenharmony_ci// - exiting onfirstImmediate 651cb0ef41Sopenharmony_ci// hook1.after.uid-5 661cb0ef41Sopenharmony_ci// hook3.after.uid-5 671cb0ef41Sopenharmony_ci// hook2.after.uid-5 681cb0ef41Sopenharmony_ci// hook1.destroy.uid-5 691cb0ef41Sopenharmony_ci// hook3.destroy.uid-5 701cb0ef41Sopenharmony_ci// hook2.destroy.uid-5 711cb0ef41Sopenharmony_ci// hook1.before.uid-6 721cb0ef41Sopenharmony_ci// hook3.before.uid-6 731cb0ef41Sopenharmony_ci// hook2.before.uid-6 741cb0ef41Sopenharmony_ci// - disabled hook1 751cb0ef41Sopenharmony_ci// - entering onsecondImmediate 761cb0ef41Sopenharmony_ci// - exiting onsecondImmediate 771cb0ef41Sopenharmony_ci// hook3.after.uid-6 781cb0ef41Sopenharmony_ci// - disabled hook3 791cb0ef41Sopenharmony_ci// hook2.after.uid-6 801cb0ef41Sopenharmony_ci// hook2.destroy.uid-6 811cb0ef41Sopenharmony_ci 821cb0ef41Sopenharmony_ci 831cb0ef41Sopenharmony_ci'use strict'; 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ciconst common = require('../common'); 861cb0ef41Sopenharmony_ciconst assert = require('assert'); 871cb0ef41Sopenharmony_ciconst tick = require('../common/tick'); 881cb0ef41Sopenharmony_ciconst initHooks = require('./init-hooks'); 891cb0ef41Sopenharmony_ciconst { checkInvocations } = require('./hook-checks'); 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_ciif (!common.isMainThread) 921cb0ef41Sopenharmony_ci common.skip('Worker bootstrapping works differently -> different timing'); 931cb0ef41Sopenharmony_ci 941cb0ef41Sopenharmony_ci// Include "Unknown"s because hook2 will not be able to identify 951cb0ef41Sopenharmony_ci// the type of the first Immediate since it will miss its `init` invocation. 961cb0ef41Sopenharmony_ciconst types = [ 'Immediate', 'Unknown' ]; 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_ci// 991cb0ef41Sopenharmony_ci// Initializing hooks 1001cb0ef41Sopenharmony_ci// 1011cb0ef41Sopenharmony_ciconst hook1 = initHooks(); 1021cb0ef41Sopenharmony_ciconst hook2 = initHooks({ onbefore: onhook2Before, allowNoInit: true }); 1031cb0ef41Sopenharmony_ciconst hook3 = initHooks({ onbefore: onhook3Before, onafter: onhook3After }); 1041cb0ef41Sopenharmony_ci 1051cb0ef41Sopenharmony_ci// 1061cb0ef41Sopenharmony_ci// Enabling hook1 and hook3 only, hook2 is still disabled 1071cb0ef41Sopenharmony_ci// 1081cb0ef41Sopenharmony_cihook1.enable(); 1091cb0ef41Sopenharmony_ci// Verify that the hook is enabled even if .enable() is called twice. 1101cb0ef41Sopenharmony_cihook1.enable(); 1111cb0ef41Sopenharmony_cihook3.enable(); 1121cb0ef41Sopenharmony_ci 1131cb0ef41Sopenharmony_ci// 1141cb0ef41Sopenharmony_ci// Enabling hook2 1151cb0ef41Sopenharmony_ci// 1161cb0ef41Sopenharmony_cilet enabledHook2 = false; 1171cb0ef41Sopenharmony_cifunction onhook3Before() { 1181cb0ef41Sopenharmony_ci if (enabledHook2) return; 1191cb0ef41Sopenharmony_ci hook2.enable(); 1201cb0ef41Sopenharmony_ci enabledHook2 = true; 1211cb0ef41Sopenharmony_ci} 1221cb0ef41Sopenharmony_ci 1231cb0ef41Sopenharmony_ci// 1241cb0ef41Sopenharmony_ci// Disabling hook1 1251cb0ef41Sopenharmony_ci// 1261cb0ef41Sopenharmony_cilet disabledHook3 = false; 1271cb0ef41Sopenharmony_cifunction onhook2Before() { 1281cb0ef41Sopenharmony_ci if (disabledHook3) return; 1291cb0ef41Sopenharmony_ci hook1.disable(); 1301cb0ef41Sopenharmony_ci // Verify that the hook is disabled even if .disable() is called twice. 1311cb0ef41Sopenharmony_ci hook1.disable(); 1321cb0ef41Sopenharmony_ci disabledHook3 = true; 1331cb0ef41Sopenharmony_ci} 1341cb0ef41Sopenharmony_ci 1351cb0ef41Sopenharmony_ci// 1361cb0ef41Sopenharmony_ci// Disabling hook3 during the second "after" invocations it sees 1371cb0ef41Sopenharmony_ci// 1381cb0ef41Sopenharmony_cilet count = 2; 1391cb0ef41Sopenharmony_cifunction onhook3After() { 1401cb0ef41Sopenharmony_ci if (!--count) { 1411cb0ef41Sopenharmony_ci hook3.disable(); 1421cb0ef41Sopenharmony_ci } 1431cb0ef41Sopenharmony_ci} 1441cb0ef41Sopenharmony_ci 1451cb0ef41Sopenharmony_cisetImmediate(common.mustCall(onfirstImmediate)); 1461cb0ef41Sopenharmony_ci 1471cb0ef41Sopenharmony_ci// 1481cb0ef41Sopenharmony_ci// onfirstImmediate is called after all "init" and "before" callbacks of the 1491cb0ef41Sopenharmony_ci// active hooks were invoked 1501cb0ef41Sopenharmony_ci// 1511cb0ef41Sopenharmony_cifunction onfirstImmediate() { 1521cb0ef41Sopenharmony_ci const as1 = hook1.activitiesOfTypes(types); 1531cb0ef41Sopenharmony_ci const as2 = hook2.activitiesOfTypes(types); 1541cb0ef41Sopenharmony_ci const as3 = hook3.activitiesOfTypes(types); 1551cb0ef41Sopenharmony_ci assert.strictEqual(as1.length, 1); 1561cb0ef41Sopenharmony_ci // hook2 was not enabled yet .. it is enabled after hook3's "before" completed 1571cb0ef41Sopenharmony_ci assert.strictEqual(as2.length, 0); 1581cb0ef41Sopenharmony_ci assert.strictEqual(as3.length, 1); 1591cb0ef41Sopenharmony_ci 1601cb0ef41Sopenharmony_ci // Check that hook1 and hook3 captured the same Immediate and that it is valid 1611cb0ef41Sopenharmony_ci const firstImmediate = as1[0]; 1621cb0ef41Sopenharmony_ci assert.strictEqual(as3[0].uid, as1[0].uid); 1631cb0ef41Sopenharmony_ci assert.strictEqual(firstImmediate.type, 'Immediate'); 1641cb0ef41Sopenharmony_ci assert.strictEqual(typeof firstImmediate.uid, 'number'); 1651cb0ef41Sopenharmony_ci assert.strictEqual(typeof firstImmediate.triggerAsyncId, 'number'); 1661cb0ef41Sopenharmony_ci checkInvocations(as1[0], { init: 1, before: 1 }, 1671cb0ef41Sopenharmony_ci 'hook1[0]: on first immediate'); 1681cb0ef41Sopenharmony_ci checkInvocations(as3[0], { init: 1, before: 1 }, 1691cb0ef41Sopenharmony_ci 'hook3[0]: on first immediate'); 1701cb0ef41Sopenharmony_ci 1711cb0ef41Sopenharmony_ci // Setup the second Immediate, note that now hook2 is enabled and thus 1721cb0ef41Sopenharmony_ci // will capture all lifetime events of this Immediate 1731cb0ef41Sopenharmony_ci setImmediate(common.mustCall(onsecondImmediate)); 1741cb0ef41Sopenharmony_ci} 1751cb0ef41Sopenharmony_ci 1761cb0ef41Sopenharmony_ci// 1771cb0ef41Sopenharmony_ci// Once we exit onfirstImmediate the "after" callbacks of the active hooks are 1781cb0ef41Sopenharmony_ci// invoked 1791cb0ef41Sopenharmony_ci// 1801cb0ef41Sopenharmony_ci 1811cb0ef41Sopenharmony_cilet hook1First, hook2First, hook3First; 1821cb0ef41Sopenharmony_cilet hook1Second, hook2Second, hook3Second; 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ci// 1851cb0ef41Sopenharmony_ci// onsecondImmediate is called after all "before" callbacks of the active hooks 1861cb0ef41Sopenharmony_ci// are invoked again 1871cb0ef41Sopenharmony_ci// 1881cb0ef41Sopenharmony_cifunction onsecondImmediate() { 1891cb0ef41Sopenharmony_ci const as1 = hook1.activitiesOfTypes(types); 1901cb0ef41Sopenharmony_ci const as2 = hook2.activitiesOfTypes(types); 1911cb0ef41Sopenharmony_ci const as3 = hook3.activitiesOfTypes(types); 1921cb0ef41Sopenharmony_ci assert.strictEqual(as1.length, 2); 1931cb0ef41Sopenharmony_ci assert.strictEqual(as2.length, 2); 1941cb0ef41Sopenharmony_ci assert.strictEqual(as3.length, 2); 1951cb0ef41Sopenharmony_ci 1961cb0ef41Sopenharmony_ci // Assign the info collected by each hook for each immediate for easier 1971cb0ef41Sopenharmony_ci // reference. 1981cb0ef41Sopenharmony_ci // hook2 saw the "init" of the second immediate before the 1991cb0ef41Sopenharmony_ci // "after" of the first which is why they are ordered the opposite way 2001cb0ef41Sopenharmony_ci hook1First = as1[0]; 2011cb0ef41Sopenharmony_ci hook1Second = as1[1]; 2021cb0ef41Sopenharmony_ci hook2First = as2[1]; 2031cb0ef41Sopenharmony_ci hook2Second = as2[0]; 2041cb0ef41Sopenharmony_ci hook3First = as3[0]; 2051cb0ef41Sopenharmony_ci hook3Second = as3[1]; 2061cb0ef41Sopenharmony_ci 2071cb0ef41Sopenharmony_ci // Check that all hooks captured the same Immediate and that it is valid 2081cb0ef41Sopenharmony_ci const secondImmediate = hook1Second; 2091cb0ef41Sopenharmony_ci assert.strictEqual(hook2Second.uid, hook3Second.uid); 2101cb0ef41Sopenharmony_ci assert.strictEqual(hook1Second.uid, hook3Second.uid); 2111cb0ef41Sopenharmony_ci assert.strictEqual(secondImmediate.type, 'Immediate'); 2121cb0ef41Sopenharmony_ci assert.strictEqual(typeof secondImmediate.uid, 'number'); 2131cb0ef41Sopenharmony_ci assert.strictEqual(typeof secondImmediate.triggerAsyncId, 'number'); 2141cb0ef41Sopenharmony_ci 2151cb0ef41Sopenharmony_ci checkInvocations(hook1First, { init: 1, before: 1, after: 1, destroy: 1 }, 2161cb0ef41Sopenharmony_ci 'hook1First: on second immediate'); 2171cb0ef41Sopenharmony_ci checkInvocations(hook1Second, { init: 1, before: 1 }, 2181cb0ef41Sopenharmony_ci 'hook1Second: on second immediate'); 2191cb0ef41Sopenharmony_ci // hook2 missed the "init" and "before" since it was enabled after they 2201cb0ef41Sopenharmony_ci // occurred 2211cb0ef41Sopenharmony_ci checkInvocations(hook2First, { after: 1, destroy: 1 }, 2221cb0ef41Sopenharmony_ci 'hook2First: on second immediate'); 2231cb0ef41Sopenharmony_ci checkInvocations(hook2Second, { init: 1, before: 1 }, 2241cb0ef41Sopenharmony_ci 'hook2Second: on second immediate'); 2251cb0ef41Sopenharmony_ci checkInvocations(hook3First, { init: 1, before: 1, after: 1, destroy: 1 }, 2261cb0ef41Sopenharmony_ci 'hook3First: on second immediate'); 2271cb0ef41Sopenharmony_ci checkInvocations(hook3Second, { init: 1, before: 1 }, 2281cb0ef41Sopenharmony_ci 'hook3Second: on second immediate'); 2291cb0ef41Sopenharmony_ci tick(1); 2301cb0ef41Sopenharmony_ci} 2311cb0ef41Sopenharmony_ci 2321cb0ef41Sopenharmony_ci// 2331cb0ef41Sopenharmony_ci// Once we exit onsecondImmediate the "after" callbacks of the active hooks are 2341cb0ef41Sopenharmony_ci// invoked again. 2351cb0ef41Sopenharmony_ci// During this second "after" invocation hook3 disables itself 2361cb0ef41Sopenharmony_ci// (see onhook3After). 2371cb0ef41Sopenharmony_ci// 2381cb0ef41Sopenharmony_ci 2391cb0ef41Sopenharmony_ciprocess.on('exit', onexit); 2401cb0ef41Sopenharmony_ci 2411cb0ef41Sopenharmony_cifunction onexit() { 2421cb0ef41Sopenharmony_ci hook1.disable(); 2431cb0ef41Sopenharmony_ci hook2.disable(); 2441cb0ef41Sopenharmony_ci hook3.disable(); 2451cb0ef41Sopenharmony_ci hook1.sanityCheck(); 2461cb0ef41Sopenharmony_ci hook2.sanityCheck(); 2471cb0ef41Sopenharmony_ci hook3.sanityCheck(); 2481cb0ef41Sopenharmony_ci 2491cb0ef41Sopenharmony_ci checkInvocations(hook1First, { init: 1, before: 1, after: 1, destroy: 1 }, 2501cb0ef41Sopenharmony_ci 'hook1First: when process exits'); 2511cb0ef41Sopenharmony_ci // hook1 was disabled during hook2's "before" of the second immediate 2521cb0ef41Sopenharmony_ci // and thus did not see "after" and "destroy" 2531cb0ef41Sopenharmony_ci checkInvocations(hook1Second, { init: 1, before: 1 }, 2541cb0ef41Sopenharmony_ci 'hook1Second: when process exits'); 2551cb0ef41Sopenharmony_ci // hook2 missed the "init" and "before" since it was enabled after they 2561cb0ef41Sopenharmony_ci // occurred 2571cb0ef41Sopenharmony_ci checkInvocations(hook2First, { after: 1, destroy: 1 }, 2581cb0ef41Sopenharmony_ci 'hook2First: when process exits'); 2591cb0ef41Sopenharmony_ci checkInvocations(hook2Second, { init: 1, before: 1, after: 1, destroy: 1 }, 2601cb0ef41Sopenharmony_ci 'hook2Second: when process exits'); 2611cb0ef41Sopenharmony_ci checkInvocations(hook3First, { init: 1, before: 1, after: 1, destroy: 1 }, 2621cb0ef41Sopenharmony_ci 'hook3First: when process exits'); 2631cb0ef41Sopenharmony_ci // We don't see a "destroy" invocation here since hook3 disabled itself 2641cb0ef41Sopenharmony_ci // during its "after" invocation 2651cb0ef41Sopenharmony_ci checkInvocations(hook3Second, { init: 1, before: 1, after: 1 }, 2661cb0ef41Sopenharmony_ci 'hook3Second: when process exits'); 2671cb0ef41Sopenharmony_ci} 268