summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/web-locks/query-ordering.https.html
blob: d5e722baf75b6b4a77bf0943ddecd620499dc03a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<!DOCTYPE html>
<meta charset=utf-8>
<title>Web Locks API: navigator.locks.query ordering</title>
<link rel=help href="https://w3c.github.io/web-locks/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/helpers.js"></script>
<style>iframe { display: none; }</style>
<script>
'use strict';

// Grab a lock and hold until a release function is called. Resolves
// to a release function.
function getLockAndHoldUntilReleased(name, options) {
  let release;
  const promise = new Promise(resolve => { release = resolve; });
  return new Promise(resolve => {
    navigator.locks.request(name, options || {}, lock => {
      resolve(release);
      return promise;
    }).catch(_ => {});
  });
}

// Returns a promise resolved by the next message event.
function nextMessage() {
  return new Promise(resolve => {
    window.addEventListener('message', event => {
      resolve(event.data);
    }, {once: true});
  });
}

// Tests the ordering constraints on the requested lock state returned by
// navigator.locks.query(). Three separate iframes are instantiated to make
// lock requests on the same resource, first in one order and then in another,
// different order. For each set of requests, it is verified that the requests
// appear in the result of navigator.locks.query() in the same order in which
// they were made.
//
// It is necessary to use separate iframes here so that the lock requests have
// distinguishable client_ids (otherwise it would not be possible to
// distinguish the requests and thus impossible to verify ordering).
promise_test(async testCase => {
  assert_implements(navigator.locks);
  const resourceName = uniqueName(testCase);

  // Set up clients.
  const frame1 = await iframe('resources/iframe.html');
  const frame2 = await iframe('resources/iframe.html');
  const frame3 = await iframe('resources/iframe.html');
  testCase.add_cleanup(() => { frame1.remove(); });
  testCase.add_cleanup(() => { frame2.remove(); });
  testCase.add_cleanup(() => { frame3.remove(); });

  // Collect the client ids.
  const clientId1 =
      (await postToFrameAndWait(frame1, {op: 'client_id',
                                         name: resourceName})).client_id;
  const clientId2 =
      (await postToFrameAndWait(frame2, {op: 'client_id',
                                         name: resourceName})).client_id;
  const clientId3 =
      (await postToFrameAndWait(frame3, {op: 'client_id',
                                         name: resourceName})).client_id;

  // Preemptively take the lock.
  const firstRequestGroupReleaseFunction =
      await getLockAndHoldUntilReleased(resourceName);

  // Queue the first group of lock requests from the different clients. These
  // will be blocked until firstRequestGroupReleaseFunction() is called.
  let lockId1;
  let lockId2;
  const lockPromise1 =
      postToFrameAndWait(frame1, {op: 'request', name: resourceName})
          .then(val => {lockId1 = val.lock_id;});
  const lockPromise2 =
      postToFrameAndWait(frame2, {op: 'request', name: resourceName})
          .then(val => {lockId2 = val.lock_id;});

  // This third request will later be granted and held in order to block a
  // second group of requests to test a different client ordering. It is not
  // meant to be released.
  postToFrameAndWait(frame3, {op: 'request', name: resourceName});

  // Request and wait for the release of a separate lock to ensure all previous
  // requests are processed.
  const checkpointName = uniqueName(testCase, 'checkpoint');
  const checkpointId = (await postToFrameAndWait(
                                  frame3,
                                  {op: 'request', name: checkpointName})).lock_id;
  await postToFrameAndWait(frame3, {op: 'release', lock_id: checkpointId});

  // Query the state and test the ordering of requested locks.
  const state = await navigator.locks.query();
  const relevant_pending_ids = state.pending
      .filter(lock => [clientId1, clientId2, clientId3].includes(lock.clientId))
      .map(lock => lock.clientId);
  assert_array_equals(
      [clientId1, clientId2, clientId3],
      relevant_pending_ids,
      'Querying the state should return requested locks in the order they were '
      + 'requested.');

  // Add the second group of requests from the clients in a new order.
  postToFrameAndWait(frame3, {op: 'request', name: resourceName});
  postToFrameAndWait(frame1, {op: 'request', name: resourceName});
  postToFrameAndWait(frame2, {op: 'request', name: resourceName});

  // Release locks such that only the newly added locks are requested. This
  // acts like a checkpoint for the newly queued requests.
  firstRequestGroupReleaseFunction();
  await lockPromise1;
  await postToFrameAndWait(frame1, {op: 'release', lock_id: lockId1});
  await lockPromise2;
  await postToFrameAndWait(frame2, {op: 'release', lock_id: lockId2});

  // Query the state and test the new ordering.
  const state2 = await navigator.locks.query();
  const relevant_pending_ids2 = state2.pending
      .filter(lock => [clientId1, clientId2, clientId3].includes(lock.clientId))
      .map(lock => lock.clientId);
  assert_array_equals(
      [clientId3, clientId1, clientId2],
      relevant_pending_ids2,
      'Querying the state should return requested locks in the order they were '
      + 'requested.');

}, 'Requests appear in state in order made.');
</script>