Bug 1561129 [wpt PR 17443] - KV Storage: update to match IDL, a=testonly

Automatic update from web-platform-tests
KV Storage: update to match IDL

Along with https://chromium-review.googlesource.com/c/chromium/src/+/1670572, this aligns the KV Storage implementation with https://github.com/WICG/kv-storage/pull/68, which uses Web IDL to define the API. The observable changes are to:

* Enumerability of methods
* Adding @@toStringTag (affecting Object.prototype.toString.call)

This includes web platform tests that abuse the current idlharness.js infrastructure, plus some ad-hoc hand-written tests that we expect to be generated by future versions of idlharness.js once the relevant Web IDL pull requests are merged. It removes the existing API surface tests and helpers in favor of idlharness.js.

Bug: 931263
Change-Id: I9205d1a8b3040617cbb6200f825ba9ad250e61c5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1672137
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Reviewed-by: Joshua Bell <jsbell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#671975}

--

wpt-commits: 72da3e36e3657a358c4e74b495e582262b40a016
wpt-pr: 17443
This commit is contained in:
Domenic Denicola 2019-07-19 18:13:27 +00:00 коммит произвёл James Graham
Родитель 7f1c86ef60
Коммит e6a4c3bbfd
9 изменённых файлов: 236 добавлений и 239 удалений

Просмотреть файл

@ -1,70 +0,0 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>KV Storage: API surface</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script type="module">
import { storage, StorageArea } from "std:kv-storage";
import * as classAssert from "./helpers/class-assert.js";
import { testWithArea } from "./helpers/kvs-tests.js";
test(() => {
classAssert.isConstructor(StorageArea);
classAssert.functionName(StorageArea, "StorageArea");
classAssert.functionLength(StorageArea, 1);
classAssert.hasClassPrototype(StorageArea);
classAssert.hasPrototypeConstructorLink(StorageArea);
classAssert.propertyKeys(StorageArea, ["length", "prototype", "name"], []);
}, "StorageArea constructor");
test(() => {
classAssert.propertyKeys(StorageArea.prototype, [
"constructor", "set", "get", "delete", "clear",
"keys", "values", "entries", "backingStore"
], [
Symbol.asyncIterator
]);
classAssert.methods(StorageArea.prototype, {
set: 2,
get: 1,
delete: 1,
clear: 0,
keys: 0,
values: 0,
entries: 0
});
classAssert.accessors(StorageArea.prototype, {
backingStore: ["get"]
});
}, "StorageArea.prototype methods and properties");
test(() => {
assert_equals(StorageArea.prototype[Symbol.asyncIterator], StorageArea.prototype.entries);
}, "[Symbol.asyncIterator]() and entries() must be the same function");
testWithArea(async area => {
classAssert.propertyKeys(area, [], []);
}, "Instances don't have any properties");
test(() => {
assert_equals(storage instanceof StorageArea, true, "instanceof");
assert_equals(storage.constructor, StorageArea, ".constructor property");
assert_equals(Object.getPrototypeOf(storage), StorageArea.prototype, "[[Prototype]]");
}, "Built-in storage export is a StorageArea");
testWithArea(async area => {
assert_false(Symbol.toStringTag in StorageArea.prototype,
"Symbol.toStringTag must not be in the prototype");
assert_equals(Object.prototype.toString.call(StorageArea.prototype), "[object Object]",
"The prototype must not be customized");
assert_equals(Object.prototype.toString.call(area), "[object Object]",
"A constructed StorageArea must not be customized");
assert_equals(Object.prototype.toString.call(storage), "[object Object]",
"The default storage area must not be customized");
}, "No custom toStringTag");
</script>

Просмотреть файл

@ -7,7 +7,7 @@
<script type="module">
import { testWithArea } from "./helpers/kvs-tests.js";
import * as classAssert from "./helpers/class-assert.js";
import * as iterAssert from "./helpers/iter-assert.js";
import {
assertAsyncIteratorEquals,
assertAsyncIteratorCustomEquals,
@ -76,7 +76,7 @@ testWithArea(async area => {
await iter.next()
];
classAssert.iterResultsCustom(
iterAssert.iterResultsCustom(
iterResults,
[
[[1, "value 1"], false],
@ -106,7 +106,7 @@ testWithArea(async area => {
];
const iterResults = await Promise.all(promises);
classAssert.iterResultsCustom(
iterAssert.iterResultsCustom(
iterResults,
[
[[1, "value 1"], false],

Просмотреть файл

@ -1,139 +0,0 @@
export function isConstructor(o) {
assert_equals(typeof o, "function", "Must be a function according to typeof");
assert_true(isConstructorTest(o), "Must be a constructor according to the meta-object protocol");
assert_throws(new TypeError(), () => o(), "Attempting to call (not construct) must throw");
}
export function functionLength(o, expected, label) {
const lengthExpected = { writable: false, enumerable: false, configurable: true };
const { value } = propertyDescriptor(o, "length", lengthExpected);
assert_equals(value, expected, `${formatLabel(label)}length value`);
}
export function functionName(o, expected, label) {
const lengthExpected = { writable: false, enumerable: false, configurable: true };
const { value } = propertyDescriptor(o, "name", lengthExpected);
assert_equals(value, expected, `${formatLabel(label)}name value`);
}
export function hasClassPrototype(o) {
const prototypeExpected = { writable: false, enumerable: false, configurable: false };
const { value } = propertyDescriptor(o, "prototype", prototypeExpected);
assert_equals(typeof value, "object", "prototype must be an object");
assert_not_equals(value, null, "prototype must not be null");
}
export function hasPrototypeConstructorLink(klass) {
const constructorExpected = { writable: true, enumerable: false, configurable: true };
const { value } = propertyDescriptor(klass.prototype, "constructor", constructorExpected);
assert_equals(value, klass, "constructor property must match");
}
export function propertyKeys(o, expectedNames, expectedSymbols, label) {
label = formatLabel(label);
assert_array_equals(Object.getOwnPropertyNames(o), expectedNames, `${label}property names`);
assert_array_equals(Object.getOwnPropertySymbols(o), expectedSymbols,
`${label}property symbols`);
}
export function iterResultCustom(o, expectedValue, expectedDone, valueAsserter, label) {
label = formatLabel(label);
assert_equals(typeof expectedDone, "boolean",
`${label} iterResult assert usage check: expectedDone must be a boolean`);
propertyKeys(o, ["value", "done"], [], label);
assert_equals(Object.getPrototypeOf(o), Object.prototype, `${label}prototype must be Object.prototype`);
valueAsserter(o.value, expectedValue, `${label}value`);
assert_equals(o.done, expectedDone, `${label}done`);
}
export function iterResult(o, expectedValue, expectedDone, label) {
return iterResultCustom(o, expectedValue, expectedDone, assert_equals, label);
}
export function iterResultsCustom(actualArray, expectedArrayOfArrays, valueAsserter, label) {
label = formatLabel(label);
assert_equals(actualArray.length, expectedArrayOfArrays.length,
`${label} iterResults assert usage check: actual and expected must have the same length`);
for (let i = 0; i < actualArray.length; ++i) {
const [expectedValue, expectedDone] = expectedArrayOfArrays[i];
iterResultCustom(actualArray[i], expectedValue, expectedDone, valueAsserter, `${label}iter result ${i}`);
}
}
export function iterResults(actualArray, expectedArrayOfArrays, label) {
return iterResultsCustom(actualArray, expectedArrayOfArrays, assert_equals, label);
}
export function methods(o, expectedMethods) {
for (const [name, length] of Object.entries(expectedMethods)) {
method(o, name, length);
}
}
export function accessors(o, expectedAccessors) {
for (const [name, accessorTypes] of Object.entries(expectedAccessors)) {
accessor(o, name, accessorTypes);
}
}
function method(o, prop, length) {
const methodExpected = { writable: true, enumerable: false, configurable: true };
const { value } = propertyDescriptor(o, prop, methodExpected);
assert_equals(typeof value, "function", `${prop} method must be a function according to typeof`);
assert_false(isConstructorTest(value),
`${prop} method must not be a constructor according to the meta-object protocol`);
functionLength(value, length, prop);
functionName(value, prop, prop);
propertyKeys(value, ["length", "name"], [], prop);
}
function accessor(o, prop, expectedAccessorTypes) {
const accessorExpected = { enumerable: false, configurable: true };
const propDesc = propertyDescriptor(o, prop, accessorExpected);
for (const possibleType of ["get", "set"]) {
const accessorFunc = propDesc[possibleType];
if (expectedAccessorTypes.includes(possibleType)) {
const label = `${prop}'s ${possibleType}ter`;
assert_equals(typeof accessorFunc, "function",
`${label} must be a function according to typeof`);
assert_false(isConstructorTest(accessorFunc),
`${label} must not be a constructor according to the meta-object protocol`);
functionLength(accessorFunc, possibleType === "get" ? 0 : 1, label);
functionName(accessorFunc, `${possibleType} ${prop}`, label);
propertyKeys(accessorFunc, ["length", "name"], [], label);
} else {
assert_equals(accessorFunc, undefined, `${prop} must not have a ${possibleType}ter`);
}
}
}
function propertyDescriptor(obj, prop, mustMatch) {
const propDesc = Object.getOwnPropertyDescriptor(obj, prop);
for (const key in Object.keys(mustMatch)) {
assert_equals(propDesc[key], mustMatch[key], `${prop} ${key}`);
}
return propDesc;
}
function isConstructorTest(o) {
try {
new (new Proxy(o, {construct: () => ({})}));
return true;
} catch (e) {
return false;
}
}
function formatLabel(label) {
return label !== undefined ? `${label} ` : "";
}

Просмотреть файл

@ -0,0 +1,8 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Helper file to be loaded in an iframe that exposes a copy of StorageArea as a global</title>
<script type="module">
import { StorageArea } from "std:kv-storage";
window.StorageArea = StorageArea;
</script>

Просмотреть файл

@ -0,0 +1,42 @@
export function iterResultCustom(o, expectedValue, expectedDone, valueAsserter, label) {
label = formatLabel(label);
assert_equals(typeof expectedDone, "boolean",
`${label} iterResult assert usage check: expectedDone must be a boolean`);
propertyKeys(o, ["value", "done"], [], label);
assert_equals(Object.getPrototypeOf(o), Object.prototype, `${label}prototype must be Object.prototype`);
valueAsserter(o.value, expectedValue, `${label}value`);
assert_equals(o.done, expectedDone, `${label}done`);
}
export function iterResult(o, expectedValue, expectedDone, label) {
return iterResultCustom(o, expectedValue, expectedDone, assert_equals, label);
}
export function iterResultsCustom(actualArray, expectedArrayOfArrays, valueAsserter, label) {
label = formatLabel(label);
assert_equals(actualArray.length, expectedArrayOfArrays.length,
`${label} iterResults assert usage check: actual and expected must have the same length`);
for (let i = 0; i < actualArray.length; ++i) {
const [expectedValue, expectedDone] = expectedArrayOfArrays[i];
iterResultCustom(actualArray[i], expectedValue, expectedDone, valueAsserter, `${label}iter result ${i}`);
}
}
export function iterResults(actualArray, expectedArrayOfArrays, label) {
return iterResultsCustom(actualArray, expectedArrayOfArrays, assert_equals, label);
}
function propertyKeys(o, expectedNames, expectedSymbols, label) {
label = formatLabel(label);
assert_array_equals(Object.getOwnPropertyNames(o), expectedNames, `${label}property names`);
assert_array_equals(Object.getOwnPropertySymbols(o), expectedSymbols,
`${label}property symbols`);
}
function formatLabel(label) {
return label !== undefined ? `${label} ` : "";
}

Просмотреть файл

@ -0,0 +1,175 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>KV Storage: IDL interface tests</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/WebIDLParser.js"></script>
<script src="/resources/idlharness.js"></script>
<script type="module">
import { storage, StorageArea } from "std:kv-storage";
// Web IDL/idlharness.js do not yet have support for the spec's IDL, which uses module {},
// async_iterator, and some new extended attributes. This IDL is a mutated version to work with the
// current idlharness.js to get some coverage.
//
// When the relevant Web IDL PRs land and idlharness.js gets updated, we can replace this file with
// a normal-style idlharness test, with the IDL scraped from the spec.
//
// Other tests in this file are similarly ones that should be generatable from the IDL, in theory.
const idl = `
[Constructor(DOMString name)]
interface StorageArea {
Promise<void> set(any key, any value);
Promise<any> get(any key);
Promise<void> delete(any key);
Promise<void> clear();
// async_iterable<any, any>;
[SameObject] readonly attribute object backingStore;
};
`;
// Define a global property because idlharness.js only understands
// global properties, not modules.
Object.defineProperty(window, "StorageArea", {
configurable: true,
enumerable: false,
writable: true,
value: StorageArea
});
test(t => {
window.testStorageArea = storage;
t.add_cleanup(() => {
delete window.testStorageArea;
});
const idlArray = new IdlArray();
idlArray.add_idls(idl);
idlArray.add_objects({ StorageArea: ["window.testStorageArea"] });
idlArray.test();
}, "idlharness basic interface tests");
test(() => {
assert_equals(storage instanceof StorageArea, true, "instanceof");
assert_equals(storage.constructor, StorageArea, ".constructor property");
assert_equals(Object.getPrototypeOf(storage), StorageArea.prototype, "[[Prototype]]");
}, "Built-in storage export is a StorageArea");
// These should be auto-tested by idlharness eventually, similar to
// https://github.com/web-platform-tests/wpt/blob/3725067ef0328c998be2ba93dbaeb0579586fccd/resources/idlharness.js#L2452
// for sync iterators (although this tests more than that does).
test(() => {
for (const methodName of ["keys", "values", "entries"]) {
const descriptor = Object.getOwnPropertyDescriptor(StorageArea.prototype, methodName);
assert_true(descriptor.writable, `${methodName} property should be writable`);
assert_true(descriptor.configurable, `${methodName} property should be configurable`);
// May need updating if https://github.com/heycam/webidl/issues/738 changes the spec
assert_true(descriptor.enumerable, `${methodName} property should be enumerable`);
assert_equals(typeof descriptor.value, "function",
`${methodName} property should be a function`);
assert_equals(descriptor.value.length, 0, `${methodName} function object length should be 0`);
assert_equals(descriptor.value.name, methodName,
`${methodName} function object should have the right name`);
assert_throws(new TypeError(), () => descriptor.value.call(StorageArea.prototype),
`${methodName} should throw when called on the prototype directly`);
assert_throws(new TypeError(), () => descriptor.value.call({}),
`${methodName} should throw when called on an empty object`);
}
const AsyncIteratorPrototype =
Object.getPrototypeOf(Object.getPrototypeOf(async function*() {}).prototype);
const asyncIteratorProto = Object.getPrototypeOf(storage.keys());
assert_equals(Object.getPrototypeOf(storage.values()), asyncIteratorProto,
"keys() and values() return values must have the same prototype");
assert_equals(Object.getPrototypeOf(storage.entries()), asyncIteratorProto,
"keys() and entries() return values must have the same prototype");
assert_equals(Object.getPrototypeOf(asyncIteratorProto), AsyncIteratorPrototype,
"[[Prototype]] must be the async iterator prototype");
assert_array_equals(Object.getOwnPropertyNames(asyncIteratorProto), ["next"],
`async iterator prototype object must have a next method`);
const nextMethodDescriptor = Object.getOwnPropertyDescriptor(asyncIteratorProto, "next");
assert_true(nextMethodDescriptor.writable, `async iterator next property should be writable`);
assert_true(nextMethodDescriptor.configurable,
`async iterator next property should be configurable`);
// May need updating if https://github.com/heycam/webidl/issues/739 changes the spec
assert_true(nextMethodDescriptor.enumerable,
`async iterator next property should be enumerable`);
assert_equals(typeof nextMethodDescriptor.value, "function",
`async iterator next property should be a function`);
assert_equals(nextMethodDescriptor.value.length, 0,
`async iterator next function object length should be 0`);
assert_equals(nextMethodDescriptor.value.name, "next",
`async iterator next function object should have the right name`);
assert_array_equals(Object.getOwnPropertySymbols(asyncIteratorProto), [Symbol.toStringTag]);
const toStringTagDescriptor = Object.getOwnPropertyDescriptor(
asyncIteratorProto,
Symbol.toStringTag
);
assert_false(toStringTagDescriptor.writable,
`async iterator @@toStringTag property should be non-writable`);
assert_true(toStringTagDescriptor.configurable,
`async iterator @@toStringTag property should be configurable`);
assert_false(toStringTagDescriptor.enumerable,
`async iterator @@toStringTag property should be non-enumerable`);
assert_equals(toStringTagDescriptor.value, "StorageArea AsyncIterator",
`async iterator @@toStringTag property should have the right value`);
assert_equals(StorageArea.prototype[Symbol.asyncIterator], StorageArea.prototype.entries,
"@@asyncIterator method should be the same as entries");
}, "@@asyncIterator tests");
promise_test(async t => {
const iframe = document.createElement("iframe");
iframe.src = "helpers/expose-as-global.html";
document.body.append(iframe);
await frameLoadPromise(iframe);
const OtherStorageArea = iframe.contentWindow.StorageArea;
await promise_rejects(t, new TypeError(),
OtherStorageArea.prototype.set.call(storage, "testkey", "testvalue"),
`set() must reject cross-realm`);
await promise_rejects(t, new TypeError(),
OtherStorageArea.prototype.get.call(storage, "testkey"),
`get() must reject cross-realm`);
await promise_rejects(t, new TypeError(),
OtherStorageArea.prototype.delete.call(storage, "testkey"),
`delete() must reject cross-realm`);
await promise_rejects(t, new TypeError(), OtherStorageArea.prototype.clear.call(storage),
`clear() must reject cross-realm`);
assert_throws(new TypeError(), () => OtherStorageArea.prototype.keys.call(storage),
`keys() must throw cross-realm`);
assert_throws(new TypeError(), () => OtherStorageArea.prototype.values.call(storage),
`values() must throw cross-realm`);
assert_throws(new TypeError(), () => OtherStorageArea.prototype.entries.call(storage),
`entries() must throw cross-realm`);
const otherBackingStoreGetter =
Object.getOwnPropertyDescriptor(OtherStorageArea.prototype, "backingStore").get;
assert_throws(new TypeError(), () => otherBackingStoreGetter.call(storage),
`backingStore must throw cross-realm`);
}, "Same-realm brand checks");
function frameLoadPromise(frame) {
return new Promise((resolve, reject) => {
frame.onload = resolve;
frame.onerror = () => reject(new Error(`${frame.src} failed to load`));
});
}
</script>

Просмотреть файл

@ -12,30 +12,11 @@
<script type="module">
import { testWithArea } from "./helpers/kvs-tests.js";
import * as classAssert from "./helpers/class-assert.js";
import * as iterAssert from "./helpers/iter-assert.js";
import { assertAsyncIteratorEquals } from "./helpers/equality-asserters.js";
// Also uses some global functions included via support-promises.js.
const AsyncIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(async function*() {}).prototype);
testWithArea(async area => {
const keysProto = Object.getPrototypeOf(area.keys());
const valuesProto = Object.getPrototypeOf(area.values());
const entriesProto = Object.getPrototypeOf(area.entries());
assert_equals(keysProto, valuesProto, "keys() and values() return values' must have the same [[Prototype]]");
assert_equals(valuesProto, entriesProto, "values() and entries () return values' must have the same [[Prototype]]");
}, "keys()/values()/entries() all use the same prototype object");
for (const method of ["keys", "values", "entries"]) {
testWithArea(async area => {
const iter = area[method]();
const proto = Object.getPrototypeOf(iter);
assert_equals(Object.getPrototypeOf(proto), AsyncIteratorPrototype,
"[[Prototype]] must be the async iterator prototype");
classAssert.propertyKeys(proto, ["next"], [], "must only have a next() method");
}, `${method}() return value is an async iterator of the expected shape`);
testWithArea(async area => {
const iter = area[method]();
const promise = iter.next();
@ -47,7 +28,7 @@ for (const method of ["keys", "values", "entries"]) {
await iter.next()
];
classAssert.iterResults(iterResults, [
iterAssert.iterResults(iterResults, [
[undefined, true],
[undefined, true]
]);

Просмотреть файл

@ -7,7 +7,7 @@
<script type="module">
import { testWithArea } from "./helpers/kvs-tests.js";
import * as classAssert from "./helpers/class-assert.js";
import * as iterAssert from "./helpers/iter-assert.js";
import {
assertAsyncIteratorEquals,
assertAsyncIteratorCustomEquals,
@ -62,7 +62,7 @@ testWithArea(async area => {
await iter.next()
];
classAssert.iterResults(iterResults, [
iterAssert.iterResults(iterResults, [
[1, false],
[2, false],
[3, false],
@ -88,7 +88,7 @@ testWithArea(async area => {
];
const iterResults = await Promise.all(promises);
classAssert.iterResults(iterResults, [
iterAssert.iterResults(iterResults, [
[1, false],
[2, false],
[3, false],

Просмотреть файл

@ -7,7 +7,7 @@
<script type="module">
import { testWithArea } from "./helpers/kvs-tests.js";
import * as classAssert from "./helpers/class-assert.js";
import * as iterAssert from "./helpers/iter-assert.js";
import { assertAsyncIteratorEquals } from "./helpers/equality-asserters.js";
testWithArea(async area => {
@ -57,7 +57,7 @@ testWithArea(async area => {
await iter.next()
];
classAssert.iterResults(iterResults, [
iterAssert.iterResults(iterResults, [
["value 1", false],
["value 2", false],
["value 3", false],
@ -83,7 +83,7 @@ testWithArea(async area => {
];
const iterResults = await Promise.all(promises);
classAssert.iterResults(iterResults, [
iterAssert.iterResults(iterResults, [
["value 1", false],
["value 2", false],
["value 3", false],