blob: 79862e044c079f0695d1083f261bbb449fc26eff [file] [log] [blame]
'use strict';
// Helpers for tests that constructors perform getting and validation of properties in the standard order.
// See ../readable-streams/constructor.js for an example of how to use them.
// Describes an operation on a property. |type| is "get", "validate" or "tonumber". |name| is the name of the property
// in question. |side| is usually undefined, but is used by TransformStream to distinguish between the readable and
// writable strategies.
class Op {
constructor(type, name, side) {
this.type = type;
this.name = name;
this.side = side;
}
toString() {
return this.side === undefined ? `${this.type} on ${this.name}` : `${this.type} on ${this.name} (${this.side})`;
}
equals(otherOp) {
return this.type === otherOp.type && this.name === otherOp.name && this.side === otherOp.side;
}
}
// Provides a concise syntax to create an Op object. |side| is used by TransformStream to distinguish between the two
// strategies.
function op(type, name, side = undefined) {
return new Op(type, name, side);
}
// Records a sequence of operations. Also checks each operation against |failureOp| to see if it should fail.
class OpRecorder {
constructor(failureOp) {
this.ops = [];
this.failureOp = failureOp;
this.matched = false;
}
// Record an operation. Returns true if this operation should fail.
recordAndCheck(type, name, side = undefined) {
const recordedOp = op(type, name, side);
this.ops.push(recordedOp);
return this.failureOp.equals(recordedOp);
}
// Returns true if validation of this property should fail.
check(name, side = undefined) {
return this.failureOp.equals(op('validate', name, side));
}
// Returns the sequence of recorded operations as a string.
actual() {
return this.ops.toString();
}
}
// Creates an object with the list of properties named in |properties|. Every property access will be recorded in
// |record|, which will also be used to determine whether a particular property access should fail, or whether it should
// return an invalid value that will fail validation.
function createRecordingObjectWithProperties(record, properties) {
const recordingObject = {};
for (const property of properties) {
defineCheckedProperty(record, recordingObject, property, () => record.check(property) ? 'invalid' : undefined);
}
return recordingObject;
}
// Add a getter to |object| named |property| which throws if op('get', property) should fail, and otherwise calls
// getter() to get the return value.
function defineCheckedProperty(record, object, property, getter) {
Object.defineProperty(object, property, {
get() {
if (record.recordAndCheck('get', property)) {
throw new Error(`intentional failure of get ${property}`);
}
return getter();
}
});
}
// Similar to createRecordingObjectWithProperties(), but with specific functionality for "highWaterMark" so that numeric
// conversion can be recorded. Permits |side| to be specified so that TransformStream can distinguish between its two
// strategies.
function createRecordingStrategy(record, side = undefined) {
return {
get size() {
if (record.recordAndCheck('get', 'size', side)) {
throw new Error(`intentional failure of get size`);
}
return record.check('size', side) ? 'invalid' : undefined;
},
get highWaterMark() {
if (record.recordAndCheck('get', 'highWaterMark', side)) {
throw new Error(`intentional failure of get highWaterMark`);
}
return createRecordingNumberObject(record, 'highWaterMark', side);
}
};
}
// Creates an object which will record when it is converted to a number. It will assert if the conversion is to some
// other type, and will fail if op('tonumber', property, side) is set as the failure step. The object will convert to -1
// if 'validate' is set as the failure step, and 1 otherwise.
function createRecordingNumberObject(record, property, side = undefined) {
return {
[Symbol.toPrimitive](hint) {
assert_equals(hint, 'number', `hint for ${property} should be 'number'`);
if (record.recordAndCheck('tonumber', property, side)) {
throw new Error(`intentional failure of ${op('tonumber', property, side)}`);
}
return record.check(property, side) ? -1 : 1;
}
};
}
// Creates a string from everything in |operations| up to and including |failureOp|. "validate" steps are excluded from
// the output, as we cannot record them except by making them fail.
function expectedAsString(operations, failureOp) {
const expected = [];
for (const step of operations) {
if (step.type !== 'validate') {
expected.push(step);
}
if (step.equals(failureOp)) {
break;
}
}
return expected.toString();
}