chromium/third_party/blink/web_tests/wpt_internal/task-tracking/track-settimeout.html

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Verify that setTimeout tasks can be properly tracked.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/task-ids.js"></script>
</head>
<body>
<script>
promise_test(() => {
  return new Promise(async (resolve, reject) => {
    const initialId = initializeTaskId();
    await new Promise(resolve => setTimeout(resolve, 0));
    queueMicrotask(() => {
      try {
        assert_equals(scheduler.taskId, initialId);
        resolve();
      } catch {
        reject("Not an ancestor");
      }
    });
  });
}, "An immediate setTimeout microtask is a descendant of the dispatching task");

promise_test(() => {
  return new Promise(async (resolve, reject) => {
    const initialId = initializeTaskId();
    await new Promise(internal_resolve => {
      window.resolve = internal_resolve;
      setTimeout("resolve()", 100);
    });
    queueMicrotask(() => {
      try {
        assert_equals(scheduler.taskId, initialId);
        resolve();
      } catch {
        reject("Not an ancestor");
      }
    });
  });
}, "A microtask queued after a long setTimeout task with text input is a " +
   "descendant of the dispatching task");

promise_test(async () => {
  window.initialId = scheduler.taskId;
  await new Promise((resolve, reject) => {
    window.resolve = resolve;
    window.reject = reject;
    setTimeout(`
     try {
       assert_equals(scheduler.taskId, initialId);
       resolve();
     } catch {
       reject("Not an ancestor");
     }`, 100);
  });
}, "A long setTimeout task with text input is a descendant of the dispatching task");

promise_test(() => {
  return new Promise(async (resolve, reject) => {
    const initialId = initializeTaskId();
    await new Promise(resolve => setTimeout(resolve, 100));
    queueMicrotask(() => {
      try {
        assert_equals(scheduler.taskId, initialId);
        resolve();
      } catch {
        reject("Not an ancestor");
      }
    });
  });
}, "A long setTimeout microtask is a descendant of the dispatching task");

promise_test(async () => {
  const initialId = initializeTaskId();
  await new Promise((resolve, reject) => setTimeout(() => {
    try {
      assert_equals(scheduler.taskId, initialId);
      resolve();
    } catch {
      reject("Initial task not identified as ancestor of first setTimeout");
    }
    resolve();
  }, 10));
  await new Promise((resolve, reject) => setTimeout(() => {
    try {
      assert_equals(scheduler.taskId, initialId);
      resolve();
    } catch {
      reject("Initial task not identified as ancestor of second setTimeout");
    }
  }, 10));
}, "An async chain of setTimeouts task properly track their ancestors");

promise_test(async () => {
  const initialId = initializeTaskId();
  return new Promise((resolve, reject) => {
    try {
      assert_equals(scheduler.taskId, initialId);
      resolve();
    } catch {
      reject("The task is not its own ancestor");
    }
  });
}, "A task is its own ancestor");

promise_test(async t => {
  let firstTaskParent;
  let firstTaskDescendent;

  const initialId = initializeTaskId();

  const first = new Promise((resolve, reject) => setTimeout(t.step_func(() => {
    assert_equals(scheduler.taskId, initialId);
    firstTaskParent = initializeTaskId();
    setTimeout(t.step_func(() => {
      firstTaskDescendent = scheduler.taskId;
      assert_equals(firstTaskDescendent, firstTaskParent);
      assert_not_equals(firstTaskDescendent, initialId);
      resolve();
    }), 0);
  }), 15));

  const second = new Promise((resolve, reject) => setTimeout(t.step_func(() => {
    assert_equals(scheduler.taskId, initialId);
    const secondTaskParent = initializeTaskId();
    setTimeout(t.step_func(() => {
      try {
        assert_equals(scheduler.taskId, secondTaskParent);
        assert_not_equals(scheduler.taskId, firstTaskDescendent);
        assert_not_equals(scheduler.taskId, initialId);
        resolve();
      } catch (e) {
        reject(e);
      }
    }), 0);
  }), 30));
  await Promise.all([first, second]);
}, "Two unrelated setTimeout tasks properly track their ancestors");

promise_test(async t => {
  window.initialId = initializeTaskId();

  const first = new Promise((resolve, reject) => setTimeout(t.step_func(() => {
    assert_equals(scheduler.taskId, initialId);
    window.firstTaskParent = initializeTaskId();
    window.resolve1 = resolve;
    window.reject1 = reject;
    setTimeout(`
      try {
        window.firstTaskDescendent = scheduler.taskId;
        assert_equals(window.firstTaskDescendent, firstTaskParent);
        assert_not_equals(window.firstTaskDescendent, initialId);
        resolve1();
      } catch {
        reject1("Initial task not identified as ancestor of first setTimeout");
      }
    `, 0);
  }), 15));
  const second = new Promise((resolve, reject) => setTimeout(t.step_func(() => {
    window.resolve2 = resolve;
    window.reject2 = reject;
    assert_equals(scheduler.taskId, initialId);
    window.secondTaskParent = initializeTaskId();
    setTimeout(`
      try {
        assert_equals(scheduler.taskId, window.secondTaskParent);
        assert_not_equals(scheduler.taskId, window.firstTaskDescendent);
        assert_not_equals(scheduler.taskId, window.initialId);
        resolve2();
      } catch {
        reject2("Initial task not identified as ancestor of second setTimeout");
      }
    `, 0);
  }), 30));
  await Promise.all([first, second]);
}, "Two unrelated setTimeout tasks (delivered as strings) properly track their ancestors");

promise_test(async t => {
  const initialId = initializeTaskId();
  let times = 0;
  const ITERATIONS = 15;

  await new Promise((resolve, reject) => setTimeout(t.step_func(() => {
    const timeout = () => {
      assert_equals(scheduler.taskId, initialId);
      if (++times < ITERATIONS) {
        setTimeout(t.step_func(timeout), 0);
      } else {
        resolve();
      }
    };
    setTimeout(t.step_func(timeout), 0);
  }), 0));
}, "Recursive setTimeout");

</script>
</body>
</html>