summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/streams/readable-streams/patched-global.any.js
blob: a64a054a97f1f5f133ec961a02f75207b2cb4eee (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
// META: global=window,worker
'use strict';

// Tests which patch the global environment are kept separate to avoid
// interfering with other tests.

const ReadableStream_prototype_locked_get =
      Object.getOwnPropertyDescriptor(ReadableStream.prototype, 'locked').get;

// Verify that |rs| passes the brand check as a readable stream.
function isReadableStream(rs) {
  try {
    ReadableStream_prototype_locked_get.call(rs);
    return true;
  } catch (e) {
    return false;
  }
}

test(t => {
  const rs = new ReadableStream();

  const trappedProperties = ['highWaterMark', 'size', 'start', 'type', 'mode'];
  for (const property of trappedProperties) {
    // eslint-disable-next-line no-extend-native, accessor-pairs
    Object.defineProperty(Object.prototype, property, {
      get() { throw new Error(`${property} getter called`); },
      configurable: true
    });
  }
  t.add_cleanup(() => {
    for (const property of trappedProperties) {
      delete Object.prototype[property];
    }
  });

  const [branch1, branch2] = rs.tee();
  assert_true(isReadableStream(branch1), 'branch1 should be a ReadableStream');
  assert_true(isReadableStream(branch2), 'branch2 should be a ReadableStream');
}, 'ReadableStream tee() should not touch Object.prototype properties');

test(t => {
  const rs = new ReadableStream();

  const oldReadableStream = self.ReadableStream;

  self.ReadableStream = function() {
    throw new Error('ReadableStream called on global object');
  };

  t.add_cleanup(() => {
    self.ReadableStream = oldReadableStream;
  });

  const [branch1, branch2] = rs.tee();

  assert_true(isReadableStream(branch1), 'branch1 should be a ReadableStream');
  assert_true(isReadableStream(branch2), 'branch2 should be a ReadableStream');
}, 'ReadableStream tee() should not call the global ReadableStream');

promise_test(async t => {
  const rs = new ReadableStream({
    start(c) {
      c.enqueue(1);
      c.enqueue(2);
      c.enqueue(3);
      c.close();
    }
  });

  const oldReadableStreamGetReader = ReadableStream.prototype.getReader;

  const ReadableStreamDefaultReader = (new ReadableStream()).getReader().constructor;
  const oldDefaultReaderRead = ReadableStreamDefaultReader.prototype.read;
  const oldDefaultReaderCancel = ReadableStreamDefaultReader.prototype.cancel;
  const oldDefaultReaderReleaseLock = ReadableStreamDefaultReader.prototype.releaseLock;

  self.ReadableStream.prototype.getReader = function() {
    throw new Error('patched getReader() called');
  };

  ReadableStreamDefaultReader.prototype.read = function() {
    throw new Error('patched read() called');
  };
  ReadableStreamDefaultReader.prototype.cancel = function() {
    throw new Error('patched cancel() called');
  };
  ReadableStreamDefaultReader.prototype.releaseLock = function() {
    throw new Error('patched releaseLock() called');
  };

  t.add_cleanup(() => {
    self.ReadableStream.prototype.getReader = oldReadableStreamGetReader;

    ReadableStreamDefaultReader.prototype.read = oldDefaultReaderRead;
    ReadableStreamDefaultReader.prototype.cancel = oldDefaultReaderCancel;
    ReadableStreamDefaultReader.prototype.releaseLock = oldDefaultReaderReleaseLock;
  });

  // read the first chunk, then cancel
  for await (const chunk of rs) {
    break;
  }

  // should be able to acquire a new reader
  const reader = oldReadableStreamGetReader.call(rs);
  // stream should be cancelled
  await reader.closed;
}, 'ReadableStream async iterator should use the original values of getReader() and ReadableStreamDefaultReader ' +
   'methods');

test(t => {
  const oldPromiseThen = Promise.prototype.then;
  Promise.prototype.then = () => {
    throw new Error('patched then() called');
  };
  t.add_cleanup(() => {
    Promise.prototype.then = oldPromiseThen;
  });
  const [branch1, branch2] = new ReadableStream().tee();
  assert_true(isReadableStream(branch1), 'branch1 should be a ReadableStream');
  assert_true(isReadableStream(branch2), 'branch2 should be a ReadableStream');
}, 'tee() should not call Promise.prototype.then()');

test(t => {
  const oldPromiseThen = Promise.prototype.then;
  Promise.prototype.then = () => {
    throw new Error('patched then() called');
  };
  t.add_cleanup(() => {
    Promise.prototype.then = oldPromiseThen;
  });
  let readableController;
  const rs = new ReadableStream({
    start(c) {
      readableController = c;
    }
  });
  const ws = new WritableStream();
  rs.pipeTo(ws);
  readableController.close();
}, 'pipeTo() should not call Promise.prototype.then()');