// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
// This file tests that JS can correctly encode optional numerics (by binding
// to an interface implemented in C++) and that JS can correctly decode optional
// numerics params (by binding to a interface implemented in JS).
//
// These tests can be imported as a module or added as a script to tests lite
// bindings.
class ParamsJsImpl {
constructor() {
const sendNullMethods = [
'sendNullBool',
'sendNullUint8',
'sendNullInt8',
'sendNullUint16',
'sendNullInt16',
'sendNullUint32',
'sendNullInt32',
'sendNullUint64',
'sendNullInt64',
'sendNullFloat',
'sendNullDouble',
'sendNullEnum',
'sendNullBools',
'sendNullInt16s',
'sendNullUint32s',
'sendNullDoubles',
'sendNullEnums',
'sendNullBoolMap',
'sendNullDoubleMap',
'sendNullEnumMap',
];
for (const method of sendNullMethods) {
this[method] = this.sendNull;
}
const sendOptionalMethods = [
'sendOptionalBool',
'sendOptionalUint8',
'sendOptionalInt8',
'sendOptionalUint16',
'sendOptionalInt16',
'sendOptionalUint32',
'sendOptionalInt32',
'sendOptionalUint64',
'sendOptionalInt64',
'sendOptionalFloat',
'sendOptionalDouble',
'sendOptionalEnum',
'sendOptionalBools',
'sendOptionalInt16s',
'sendOptionalUint32s',
'sendOptionalDoubles',
'sendOptionalEnums',
'sendOptionalBoolMap',
'sendOptionalDoubleMap',
'sendOptionalEnumMap',
];
for (const method of sendOptionalMethods) {
this[method] = this.sendOptional;
}
this.receiver = new OptionalNumericsParamsReceiver(this);
}
async sendNull(value) {
assert_equals(value, null);
}
async sendOptional(value) {
return {value: value};
}
async sendNullStructWithOptionalNumerics(s) {
assert_equals(s, null);
}
async sendStructWithNullOptionalNumerics(s) {
assert_equals(s.optionalBool, null);
assert_equals(s.optionalUint8, null);
assert_equals(s.optionalInt8, null);
assert_equals(s.optionalUint16, null);
assert_equals(s.optionalInt16, null);
assert_equals(s.optionalUint32, null);
assert_equals(s.optionalInt32, null);
assert_equals(s.optionalUint64, null);
assert_equals(s.optionalInt64, null);
assert_equals(s.optionalFloat, null);
assert_equals(s.optionalDouble, null);
assert_equals(s.optionalEnum, null);
}
async sendStructWithOptionalNumerics(s) {
return {
optionalBool: s.optionalBool,
optionalUint8: s.optionalUint8,
optionalInt8: s.optionalInt8,
optionalUint16: s.optionalUint16,
optionalInt16: s.optionalInt16,
optionalUint32: s.optionalUint32,
optionalInt32: s.optionalInt32,
optionalUint64: s.optionalUint64,
optionalInt64: s.optionalInt64,
optionalFloat: s.optionalFloat,
optionalDouble: s.optionalDouble,
optionalEnum: s.optionalEnum,
};
}
}
const cpp = new OptionalNumericsParamsRemote();
cpp.$.bindNewPipeAndPassReceiver().bindInBrowser('process');
const jsImpl = new ParamsJsImpl();
const js = jsImpl.receiver.$.bindNewPipeAndPassRemote();
function assert_empty_response(response) {
assert_equals(Object.keys(response).length, 0);
};
const testNullMethods = [{
method: 'sendNullBool',
numericalType: 'bool'
}, {
method: 'sendNullUint8',
numericalType: 'uint8'
}, {
method: 'sendNullInt8',
numericalType: 'int8'
}, {
method: 'sendNullUint16',
numericalType: 'uint16'
}, {
method: 'sendNullInt16',
numericalType: 'int16'
}, {
method: 'sendNullUint32',
numericalType: 'uint32'
}, {
method: 'sendNullInt32',
numericalType: 'int32'
}, {
method: 'sendNullUint64',
numericalType: 'uint64'
}, {
method: 'sendNullInt64',
numericalType: 'int64'
}, {
method: 'sendNullFloat',
numericalType: 'float'
}, {
method: 'sendNullDouble',
numericalType: 'double'
}, {
method: 'sendNullEnum',
numericalType: 'enum'
}];
for (const {method, numericalType} of testNullMethods) {
promise_test(async () => {
assert_empty_response(await cpp[method]());
assert_empty_response(await cpp[method](null));
assert_empty_response(await cpp[method](undefined));
}, `JS encoding and C++ decoding of null ${numericalType}.`);
promise_test(async () => {
assert_empty_response(await js[method]());
assert_empty_response(await js[method](null));
assert_empty_response(await js[method](undefined));
}, `JS decoding of null ${numericalType} param.`);
}
promise_test(async () => {
assert_empty_response(await cpp.sendNullBools([null, null]));
assert_empty_response(await cpp.sendNullInt16s([null, null]));
assert_empty_response(await cpp.sendNullUint32s([null, null]));
assert_empty_response(await cpp.sendNullDoubles([null, null]));
assert_empty_response(await cpp.sendNullEnums([null, null]));
});
promise_test(async () => {
assert_empty_response(await cpp.sendNullBoolMap({0: null}));
assert_empty_response(await cpp.sendNullDoubleMap({1: null}));
assert_empty_response(await cpp.sendNullEnumMap({2: null}));
});
promise_test(async () => {
assert_empty_response(await cpp.sendNullStructWithOptionalNumerics(null));
}, `JS encoding and C++ decoding of null struct with optional numerics.`);
promise_test(async () => {
assert_empty_response(await js.sendNullStructWithOptionalNumerics(null));
}, `JS decoding of null struct with optional numerics.`);
function assert_value_equals(response, expectedValue) {
assert_equals(response.value, expectedValue);
}
const testMethods = [{
method: 'sendOptionalBool',
valueToUse: true,
numericalType: 'bool',
}, {
method: 'sendOptionalUint8',
valueToUse: 8,
numericalType: 'uint8',
}, {
method: 'sendOptionalInt8',
valueToUse: -8,
numericalType: 'int8',
}, {
method: 'sendOptionalUint16',
valueToUse: 16,
numericalType: 'uint16',
}, {
method: 'sendOptionalInt16',
valueToUse: -16,
numericalType: 'int16',
}, {
method: 'sendOptionalUint32',
valueToUse: 32,
numericalType: 'uint32',
}, {
method: 'sendOptionalInt32',
valueToUse: -32,
numericalType: 'int32',
}, {
method: 'sendOptionalUint64',
valueToUse: BigInt("64"),
numericalType: 'uint64',
}, {
method: 'sendOptionalInt64',
valueToUse: BigInt("-64"),
numericalType: 'int64',
}, {
method: 'sendOptionalFloat',
valueToUse: -0.5,
numericalType: 'float',
}, {
method: 'sendOptionalDouble',
valueToUse: 0.25,
numericalType: 'double',
}, {
method: 'sendOptionalEnum',
valueToUse: OptionalNumericsRegularEnum.kBar,
numericalType: 'enum',
}];
for (const {method, valueToUse, numericalType} of testMethods) {
promise_test(async () => {
assert_value_equals(await cpp[method](valueToUse), valueToUse);
}, `JS encoding and C++ decoding of optional ${numericalType}.`);
}
const testArrayMethods = [{
method: 'sendOptionalBools',
valuesToUse: [true, null, false, true],
expected: [true, false, true],
}, {
method: 'sendOptionalInt16s',
valuesToUse: [3, null, 2, 1],
expected: [3, 2, 1],
}, {
method: 'sendOptionalUint32s',
valuesToUse: [null, 1],
expected: [1],
}, {
method: 'sendOptionalDoubles',
valuesToUse: [6.66, 9.99],
expected: [6.66, 9.99],
}, {
method: 'sendOptionalEnums',
valuesToUse: [null, OptionalNumericsRegularEnum.kBar, null, null],
expected: [OptionalNumericsRegularEnum.kBar],
}];
for (const {method, valuesToUse, expected} of testArrayMethods) {
promise_test(async() => {
const response = await cpp[method](valuesToUse);
assert_array_equals(response.values, expected, `JS encoding and C++ decoding of: ${method}`);
});
}
const testMapMethods = [{
method: 'sendOptionalBoolMap',
valuesToUse: {0: true, 1: null, 2:false},
expected: {0: true, 2: false},
}, {
method: 'sendOptionalDoubleMap',
valuesToUse: {3: 3.33, 4: null},
expected: {3: 3.33},
}, {
method: 'sendOptionalEnumMap',
valuesToUse: {5: null, 6: OptionalNumericsRegularEnum.kBar},
expected: {6: OptionalNumericsRegularEnum.kBar},
}];
for (const {method, valuesToUse, expected} of testMapMethods) {
promise_test(async() => {
const response = await cpp[method](valuesToUse);
assert_object_equals(response.values, expected, `JS encoding and C++ decoding of: ${method}`);
});
}
const structFields = [
'optionalBool',
'optionalUint8',
'optionalInt8',
'optionalUint16',
'optionalInt16',
'optionalUint32',
'optionalInt32',
'optionalUint64',
'optionalInt64',
'optionalFloat',
'optionalDouble',
'optionalEnum',
];
promise_test(async () => {
assert_empty_response(await cpp.sendStructWithNullOptionalNumerics({}));
const structWithNullFields = {};
const structWithUndefinedFields = {};
for (const field of structFields) {
structWithNullFields[field] = null;
structWithUndefinedFields[field] = undefined;
}
assert_empty_response(await cpp.sendStructWithNullOptionalNumerics(
structWithNullFields));
assert_empty_response(await cpp.sendStructWithNullOptionalNumerics(
structWithUndefinedFields));
}, 'JS encoding and C++ decoding of struct with null optional numerics.');
promise_test(async () => {
assert_empty_response(await cpp.sendStructWithNullOptionalNumerics({}));
const structWithNullFields = {};
const structWithUndefinedFields = {};
for (const field of structFields) {
structWithNullFields[field] = null;
structWithUndefinedFields[field] = undefined;
}
assert_empty_response(await js.sendStructWithNullOptionalNumerics(
structWithNullFields));
assert_empty_response(await js.sendStructWithNullOptionalNumerics(
structWithUndefinedFields));
}, 'JS decoding of struct param with null optional numerics.');
const testStructFields = [{
name: 'optionalBool',
value: true,
responseArgName: 'boolValue'
}, {
name: 'optionalUint8',
value: 8,
responseArgName: 'uint8Value'
}, {
name: 'optionalInt8',
value: -8,
responseArgName: 'int8Value'
}, {
name: 'optionalUint16',
value: 16,
responseArgName: 'uint16Value'
}, {
name: 'optionalInt16',
value: -16,
responseArgName: 'int16Value'
}, {
name: 'optionalUint32',
value: 32,
responseArgName: 'uint32Value'
}, {
name: 'optionalInt32',
value: -32,
responseArgName: 'int32Value'
}, {
name: 'optionalUint64',
value: BigInt("64"),
responseArgName: 'uint64Value'
}, {
name: 'optionalInt64',
value: BigInt("-64"),
responseArgName: 'int64Value'
}, {
name: 'optionalFloat',
value: -0.5,
responseArgName: 'floatValue'
}, {
name: 'optionalDouble',
value: 0.25,
responseArgName: 'doubleValue'
}, {
name: 'optionalEnum',
value: OptionalNumericsRegularEnum.kFoo,
responseArgName: 'enumValue'
}];
promise_test(async () => {
const testStruct = {};
for (const {name, value} of testStructFields) {
testStruct[name] = value;
}
const response = await cpp.sendStructWithOptionalNumerics(testStruct);
for (const {name, value, responseArgName} of testStructFields) {
assert_equals(response[responseArgName], value, `${name}:`);
}
}, 'JS encoding and C++ decoding of struct with optional numerics.');
promise_test(async () => {
const testStruct = {};
for (const {name, value} of testStructFields) {
testStruct[name] = value;
}
const response = await cpp.sendStructWithOptionalNumerics(testStruct);
for (const {name, value, responseArgName} of testStructFields) {
assert_equals(response[responseArgName], value, `${name}:`);
}
}, 'JS decoding of struct param with optional numerics.');