Skip to content

Commit 5f2708d

Browse files
takahiroxkainino0x
andauthored
Add earlyRejection parameter to testMapAsyncCall() (#2052)
* Add earlyRejection parameter to testMapAsyncCall() It allows to validate the immediate rejection and non-immediate rejection. For example, mapAsync() should immediately reject on buffers pending map while it should not immediately reject on already mapped buffers. The change allows to check them. * Apply suggestions from code review Co-authored-by: Kai Ninomiya <kainino1@gmail.com>
1 parent dcdcb33 commit 5f2708d

File tree

1 file changed

+29
-31
lines changed

1 file changed

+29
-31
lines changed

src/webgpu/api/validation/buffer/mapping.spec.ts

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { ValidationTest } from '../validation_test.js';
1212
class F extends ValidationTest {
1313
async testMapAsyncCall(
1414
success: boolean,
15+
earlyRejection: boolean,
1516
rejectName: string | null,
1617
buffer: GPUBuffer,
1718
mode: GPUMapModeFlags,
@@ -26,12 +27,26 @@ class F extends ValidationTest {
2627
this.expectValidationError(() => {
2728
p = buffer.mapAsync(mode, offset, size);
2829
});
30+
let caught = false;
31+
let rejectedEarly = false;
32+
// If mapAsync rejected early, microtask A will run before B.
33+
// If not, B will run before A.
34+
p!.catch(() => {
35+
// Microtask A
36+
caught = true;
37+
});
38+
queueMicrotask(() => {
39+
// Microtask B
40+
rejectedEarly = caught;
41+
});
2942
try {
43+
// This await will always complete after microtasks A and B are both done.
3044
await p!;
3145
assert(rejectName === null, 'mapAsync unexpectedly passed');
3246
} catch (ex) {
3347
assert(ex instanceof Error, 'mapAsync rejected with non-error');
3448
assert(rejectName === ex.name, `mapAsync rejected unexpectedly with: ${ex}`);
49+
assert(earlyRejection === rejectedEarly, 'mapAsync rejected at an unexpected timing');
3550
}
3651
}
3752
}
@@ -102,7 +117,7 @@ g.test('mapAsync,usage')
102117
});
103118

104119
const success = usage === validUsage;
105-
await t.testMapAsyncCall(success, 'OperationError', buffer, mapMode);
120+
await t.testMapAsyncCall(success, false, 'OperationError', buffer, mapMode);
106121
});
107122

108123
g.test('mapAsync,invalidBuffer')
@@ -111,7 +126,7 @@ g.test('mapAsync,invalidBuffer')
111126
.fn(async t => {
112127
const { mapMode } = t.params;
113128
const buffer = t.getErrorBuffer();
114-
await t.testMapAsyncCall(false, 'OperationError', buffer, mapMode);
129+
await t.testMapAsyncCall(false, false, 'OperationError', buffer, mapMode);
115130
});
116131

117132
g.test('mapAsync,state,destroyed')
@@ -126,7 +141,7 @@ g.test('mapAsync,state,destroyed')
126141
t.shouldReject('AbortError', buffer.mapAsync(mapMode));
127142

128143
buffer.destroy();
129-
await t.testMapAsyncCall(false, 'OperationError', buffer, mapMode);
144+
await t.testMapAsyncCall(false, false, 'OperationError', buffer, mapMode);
130145
});
131146

132147
g.test('mapAsync,state,mappedAtCreation')
@@ -146,10 +161,10 @@ g.test('mapAsync,state,mappedAtCreation')
146161
usage: validUsage,
147162
mappedAtCreation: true,
148163
});
149-
await t.testMapAsyncCall(false, 'OperationError', buffer, mapMode);
164+
await t.testMapAsyncCall(false, false, 'OperationError', buffer, mapMode);
150165

151166
buffer.unmap();
152-
await t.testMapAsyncCall(true, null, buffer, mapMode);
167+
await t.testMapAsyncCall(true, false, null, buffer, mapMode);
153168
});
154169

155170
g.test('mapAsync,state,mapped')
@@ -162,11 +177,11 @@ g.test('mapAsync,state,mapped')
162177
const { mapMode } = t.params;
163178

164179
const buffer = t.createMappableBuffer(mapMode, 16);
165-
await t.testMapAsyncCall(true, null, buffer, mapMode);
166-
await t.testMapAsyncCall(false, 'OperationError', buffer, mapMode);
180+
await t.testMapAsyncCall(true, false, null, buffer, mapMode);
181+
await t.testMapAsyncCall(false, false, 'OperationError', buffer, mapMode);
167182

168183
buffer.unmap();
169-
await t.testMapAsyncCall(true, null, buffer, mapMode);
184+
await t.testMapAsyncCall(true, false, null, buffer, mapMode);
170185
});
171186

172187
g.test('mapAsync,state,mappingPending')
@@ -193,7 +208,7 @@ g.test('mapAsync,state,mappingPending')
193208

194209
// Unmap the first mapping. It should now be possible to successfully call mapAsync
195210
buffer.unmap();
196-
await t.testMapAsyncCall(true, null, buffer, mapMode);
211+
await t.testMapAsyncCall(true, false, null, buffer, mapMode);
197212
});
198213

199214
g.test('mapAsync,sizeUnspecifiedOOB')
@@ -224,7 +239,7 @@ g.test('mapAsync,sizeUnspecifiedOOB')
224239
const buffer = t.createMappableBuffer(mapMode, bufferSize);
225240

226241
const success = offset <= bufferSize;
227-
await t.testMapAsyncCall(success, 'OperationError', buffer, mapMode, offset);
242+
await t.testMapAsyncCall(success, false, 'OperationError', buffer, mapMode, offset);
228243
});
229244

230245
g.test('mapAsync,offsetAndSizeAlignment')
@@ -240,7 +255,7 @@ g.test('mapAsync,offsetAndSizeAlignment')
240255
const buffer = t.createMappableBuffer(mapMode, 16);
241256

242257
const success = offset % kOffsetAlignment === 0 && size % kSizeAlignment === 0;
243-
await t.testMapAsyncCall(success, 'OperationError', buffer, mapMode, offset, size);
258+
await t.testMapAsyncCall(success, false, 'OperationError', buffer, mapMode, offset, size);
244259
});
245260

246261
g.test('mapAsync,offsetAndSizeOOB')
@@ -282,7 +297,7 @@ g.test('mapAsync,offsetAndSizeOOB')
282297
const buffer = t.createMappableBuffer(mapMode, bufferSize);
283298

284299
const success = offset + size <= bufferSize;
285-
await t.testMapAsyncCall(success, 'OperationError', buffer, mapMode, offset, size);
300+
await t.testMapAsyncCall(success, false, 'OperationError', buffer, mapMode, offset, size);
286301
});
287302

288303
g.test('mapAsync,earlyRejection')
@@ -296,26 +311,9 @@ g.test('mapAsync,earlyRejection')
296311
const offset1 = 0;
297312

298313
const buffer = t.createMappableBuffer(mapMode, bufferSize);
299-
300314
const p1 = buffer.mapAsync(mapMode, offset1, mapSize); // succeeds
301-
let success = false;
302-
{
303-
let caught = false;
304-
// should be already rejected
305-
const p2 = buffer.mapAsync(mapMode, offset2, mapSize);
306-
// queues a microtask catching the rejection
307-
p2.catch(() => {
308-
caught = true;
309-
});
310-
// queues a second microtask to capture the state immediately after the catch.
311-
// Test fails if p2 isn't rejected before the second microtask is fired or
312-
// p1 is resolved.
313-
queueMicrotask(() => {
314-
success = caught;
315-
});
316-
}
315+
await t.testMapAsyncCall(false, true, 'OperationError', buffer, mapMode, offset2, mapSize);
317316
await p1; // ensure the original map still succeeds
318-
assert(success);
319317
});
320318

321319
g.test('getMappedRange,state,mapped')
@@ -410,7 +408,7 @@ g.test('getMappedRange,state,mappedAgain')
410408
await buffer.mapAsync(mapMode);
411409

412410
// call mapAsync again on already mapped buffer should fail
413-
await t.testMapAsyncCall(false, 'OperationError', buffer, mapMode);
411+
await t.testMapAsyncCall(false, false, 'OperationError', buffer, mapMode);
414412

415413
// getMapppedRange should still success
416414
t.testGetMappedRangeCall(true, buffer);

0 commit comments

Comments
 (0)