summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/streams/transferable/writable-stream.html
blob: 7e25dad94d4cfd8090e8becba0d5a4fe14ce6512 (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<!DOCTYPE html>
<meta charset="utf-8">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/helpers.js"></script>
<script src="../resources/test-utils.js"></script>
<script src="../resources/recording-streams.js"></script>
<script>
'use strict';

promise_test(t => {
  const orig = new WritableStream();
  const promise = new Promise(resolve => {
    addEventListener('message', t.step_func(evt => {
      const transferred = evt.data;
      assert_equals(transferred.constructor, WritableStream,
                    'transferred should be a WritableStream in this realm');
      assert_true(transferred instanceof WritableStream,
                  'instanceof check should pass');

      // Perform a brand-check on |transferred|.
      const writer = WritableStream.prototype.getWriter.call(transferred);
      resolve();
    }), {once: true});
  });
  postMessage(orig, '*', [orig]);
  assert_true(orig.locked, 'the original stream should be locked');
  return promise;
}, 'window.postMessage should be able to transfer a WritableStream');

test(() => {
  const ws = new WritableStream();
  const writer = ws.getWriter();
  assert_throws_dom('DataCloneError', () => postMessage(ws, '*', [ws]),
                    'postMessage should throw');
}, 'a locked WritableStream should not be transferable');

promise_test(t => {
  const {writable, readable} = new TransformStream();
  const promise = new Promise(resolve => {
    addEventListener('message', t.step_func(async evt => {
      const {writable, readable} = evt.data;
      const reader = readable.getReader();
      const writer = writable.getWriter();
      const writerPromises = Promise.all([
        writer.write('hi'),
        writer.close(),
      ]);
      const {value, done} = await reader.read();
      assert_false(done, 'we should not be done');
      assert_equals(value, 'hi', 'chunk should have been delivered');
      const readResult = await reader.read();
      assert_true(readResult.done, 'readable should be closed');
      await writerPromises;
      resolve();
    }), {once: true});
  });
  postMessage({writable, readable}, '*', [writable, readable]);
  return promise;
}, 'window.postMessage should be able to transfer a {readable, writable} pair');

function transfer(stream) {
  return new Promise(resolve => {
    addEventListener('message', evt => resolve(evt.data), { once: true });
    postMessage(stream, '*', [stream]);
  });
}

promise_test(async () => {
  const orig = new WritableStream(
    {}, new ByteLengthQueuingStrategy({ highWaterMark: 65536 }));
  const transferred = await transfer(orig);
  const writer = transferred.getWriter();
  assert_equals(writer.desiredSize, 1, 'desiredSize should be 1');
}, 'desiredSize for a newly-transferred stream should be 1');

promise_test(async () => {
  const orig = new WritableStream({
    write() {
      return new Promise(() => {});
    }
  });
  const transferred = await transfer(orig);
  const writer = transferred.getWriter();
  await writer.write('a');
  assert_equals(writer.desiredSize, 1, 'desiredSize should be 1');
}, 'effective queue size of a transferred writable should be 2');

promise_test(async () => {
  const [writeCalled, resolveWriteCalled] = makePromiseAndResolveFunc();
  let resolveWrite;
  const orig = new WritableStream({
    write() {
      resolveWriteCalled();
      return new Promise(resolve => {
        resolveWrite = resolve;
      });
    }
  });
  const transferred = await transfer(orig);
  const writer = transferred.getWriter();
  await writer.write('a');
  let writeDone = false;
  const writePromise = writer.write('b').then(() => {
    writeDone = true;
  });
  await writeCalled;
  assert_false(writeDone, 'second write should not have resolved yet');
  resolveWrite();
  await writePromise; // (makes sure this resolves)
}, 'second write should wait for first underlying write to complete');

async function transferredWritableStreamWithAbortPromise() {
  const [abortCalled, resolveAbortCalled] = makePromiseAndResolveFunc();
  const orig = recordingWritableStream({
    abort() {
      resolveAbortCalled();
    }
  });
  const transferred = await transfer(orig);
  return { orig, transferred, abortCalled };
}

promise_test(async t => {
  const { orig, transferred, abortCalled } = await transferredWritableStreamWithAbortPromise();
  transferred.abort('p');
  await abortCalled;
  assert_array_equals(orig.events, ['abort', 'p'],
                      'abort() should have been called');
}, 'abort() should work');

promise_test(async t => {
  const { orig, transferred, abortCalled } = await transferredWritableStreamWithAbortPromise();
  const writer = transferred.getWriter();
  // A WritableStream object cannot be cloned.
  await promise_rejects_dom(t, 'DataCloneError', writer.write(new WritableStream()),
                            'the write should reject');
  await promise_rejects_dom(t, 'DataCloneError', writer.closed,
                            'the stream should be errored');
  await abortCalled;
  assert_equals(orig.events.length, 2, 'abort should have been called');
  assert_equals(orig.events[0], 'abort', 'first event should be abort');
  assert_equals(orig.events[1].name, 'DataCloneError',
                'reason should be a DataCloneError');
}, 'writing a unclonable object should error the stream');
</script>