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