blob: 236262f349f50d185ffa0fb3476e589cc635b794 [file] [log] [blame]
'use strict';
// Wraps callback and calls reject_func if callback throws an error.
class CallbackWrapper {
constructor(callback, reject_func) {
this.wrapper_func_ = (args) => {
try {
callback(args);
} catch(e) {
reject_func();
}
}
}
get callback() {
return this.wrapper_func_;
}
}
function sensor_mocks(mojo) {
return define('Generic Sensor API mocks', [
'mojo/public/js/core',
'mojo/public/js/bindings',
'mojo/public/js/connection',
'device/generic_sensor/public/interfaces/sensor_provider.mojom',
'device/generic_sensor/public/interfaces/sensor.mojom',
], (core, bindings, connection, sensor_provider, sensor) => {
// Helper function that returns resolved promise with result.
function sensorResponse(success) {
return Promise.resolve({success});
}
// Class that mocks Sensor interface defined in sensor.mojom
class MockSensor {
constructor(stub, handle, offset, size, reportingMode) {
this.client_ = null;
this.stub_ = stub;
this.expects_modified_reading_ = false;
this.start_should_fail_ = false;
this.reporting_mode_ = reportingMode;
this.sensor_reading_timer_id_ = null;
this.update_reading_function_ = null;
this.reading_updates_count_ = 0;
this.suspend_called_ = null;
this.resume_called_ = null;
this.add_configuration_called_ = null;
this.remove_configuration_called_ = null;
this.active_sensor_configurations_ = [];
let rv = core.mapBuffer(handle, offset, size,
core.MAP_BUFFER_FLAG_NONE);
assert_equals(rv.result, core.RESULT_OK, "Failed to map shared buffer");
this.buffer_array_ = rv.buffer;
this.buffer_ = new Float64Array(this.buffer_array_);
this.resetBuffer();
bindings.StubBindings(this.stub_).delegate = this;
bindings.StubBindings(this.stub_).connectionErrorHandler = () => {
this.reset();
};
}
// Returns default configuration.
getDefaultConfiguration() {
return Promise.resolve({frequency: 5});
}
reading_updates_count() {
return this.reading_updates_count_;
}
// Adds configuration for the sensor and starts reporting fake data
// through update_reading_function_ callback.
addConfiguration(configuration) {
assert_not_equals(configuration, null, "Invalid sensor configuration.");
this.active_sensor_configurations_.push(configuration);
// Sort using descending order.
this.active_sensor_configurations_.sort(
(first, second) => { return second.frequency - first.frequency });
if (!this.start_should_fail_ )
this.startReading();
if (this.add_configuration_called_ != null)
this.add_configuration_called_(this);
return sensorResponse(!this.start_should_fail_);
}
// Removes sensor configuration from the list of active configurations and
// stops notification about sensor reading changes if
// active_sensor_configurations_ is empty.
removeConfiguration(configuration) {
if (this.remove_configuration_called_ != null) {
this.remove_configuration_called_(this);
}
let index = this.active_sensor_configurations_.indexOf(configuration);
if (index !== -1) {
this.active_sensor_configurations_.splice(index, 1);
} else {
return sensorResponse(false);
}
if (this.active_sensor_configurations_.length === 0)
this.stopReading();
return sensorResponse(true);
}
// Suspends sensor.
suspend() {
this.stopReading();
if (this.suspend_called_ != null) {
this.suspend_called_(this);
}
}
// Resumes sensor.
resume() {
assert_equals(this.sensor_reading_timer_id_, null);
this.startReading();
if (this.resume_called_ != null) {
this.resume_called_(this);
}
}
// Mock functions
// Resets mock Sensor state.
reset() {
this.stopReading();
this.expects_modified_reading_ = false;
this.reading_updates_count_ = 0;
this.start_should_fail_ = false;
this.update_reading_function_ = null;
this.active_sensor_configurations_ = [];
this.suspend_called_ = null;
this.resume_called_ = null;
this.add_configuration_called_ = null;
this.remove_configuration_called_ = null;
this.resetBuffer();
core.unmapBuffer(this.buffer_array_);
this.buffer_array_ = null;
bindings.StubBindings(this.stub_).close();
}
// Zeroes shared buffer.
resetBuffer() {
for (let i = 0; i < this.buffer_.length; ++i) {
this.buffer_[i] = 0;
}
}
// Sets callback that is used to deliver sensor reading updates.
setUpdateSensorReadingFunction(update_reading_function) {
this.update_reading_function_ = update_reading_function;
return Promise.resolve(this);
}
// Sets flag that forces sensor to fail when addConfiguration is invoked.
setStartShouldFail(should_fail) {
this.start_should_fail_ = should_fail;
}
// Sets flags that asks for a modified reading values at each iteration
// to initiate 'onchange' event broadcasting.
setExpectsModifiedReading(expects_modified_reading) {
this.expects_modified_reading_ = expects_modified_reading;
}
// Returns resolved promise if suspend() was called, rejected otherwise.
suspendCalled() {
return new Promise((resolve, reject) => {
this.suspend_called_ = resolve;
});
}
// Returns resolved promise if resume() was called, rejected otherwise.
resumeCalled() {
return new Promise((resolve, reject) => {
this.resume_called_ = resolve;
});
}
// Resolves promise when addConfiguration() is called.
addConfigurationCalled() {
return new Promise((resolve, reject) => {
this.add_configuration_called_ = resolve;
});
}
// Resolves promise when removeConfiguration() is called.
removeConfigurationCalled() {
return new Promise((resolve, reject) => {
this.remove_configuration_called_ = resolve;
});
}
startReading() {
if (this.update_reading_function_ != null) {
this.stopReading();
let max_frequency_used =
this.active_sensor_configurations_[0].frequency;
let timeout = (1 / max_frequency_used) * 1000;
this.sensor_reading_timer_id_ = window.setInterval(() => {
if (this.update_reading_function_) {
this.update_reading_function_(this.buffer_,
this.expects_modified_reading_,
this.reading_updates_count_);
this.reading_updates_count_++;
}
if (this.reporting_mode_ === sensor.ReportingMode.ON_CHANGE) {
this.client_.sensorReadingChanged();
}
}, timeout);
}
}
stopReading() {
if (this.sensor_reading_timer_id_ != null) {
window.clearInterval(this.sensor_reading_timer_id_);
this.sensor_reading_timer_id_ = null;
}
}
}
// Helper function that returns resolved promise for getSensor() function.
function getSensorResponse(init_params, client_request) {
return Promise.resolve({init_params, client_request});
}
// Class that mocks SensorProvider interface defined in
// sensor_provider.mojom
class MockSensorProvider {
constructor() {
this.reading_size_in_bytes_ =
sensor_provider.SensorInitParams.kReadBufferSizeForTests;
this.shared_buffer_size_in_bytes_ = this.reading_size_in_bytes_ *
sensor.SensorType.LAST;
let rv =
core.createSharedBuffer(
this.shared_buffer_size_in_bytes_,
core.CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE);
assert_equals(rv.result, core.RESULT_OK, "Failed to create buffer");
this.shared_buffer_handle_ = rv.handle;
this.active_sensor_ = null;
this.get_sensor_should_fail_ = false;
this.resolve_func_ = null;
this.is_continuous_ = false;
this.max_frequency_ = 60;
}
// Returns initialized Sensor proxy to the client.
getSensor(type, request) {
if (this.get_sensor_should_fail_) {
var ignored = new sensor.SensorClientPtr();
return getSensorResponse(null, bindings.makeRequest(ignored));
}
let offset =
(sensor.SensorType.LAST - type) * this.reading_size_in_bytes_;
let reporting_mode = sensor.ReportingMode.ON_CHANGE;
if (this.is_continuous_) {
reporting_mode = sensor.ReportingMode.CONTINUOUS;
}
if (this.active_sensor_ == null) {
var stub = connection.bindHandleToStub(request.handle, sensor.Sensor);
let mockSensor = new MockSensor(stub, this.shared_buffer_handle_,
offset, this.reading_size_in_bytes_, reporting_mode);
this.active_sensor_ = mockSensor;
}
let rv =
core.duplicateBufferHandle(
this.shared_buffer_handle_,
core.DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE);
assert_equals(rv.result, core.RESULT_OK);
let default_config = {frequency: 5};
let init_params =
new sensor_provider.SensorInitParams(
{ memory: rv.handle,
buffer_offset: offset,
mode: reporting_mode,
default_configuration: default_config,
maximum_frequency: this.max_frequency_});
if (this.resolve_func_ !== null) {
this.resolve_func_(this.active_sensor_);
}
var client_request = new bindings.InterfaceRequest(
connection.bindProxy(proxy => {
this.active_sensor_.client_ = proxy;
}, sensor.SensorClient));
return getSensorResponse(init_params, client_request);
}
// Binds object to mojo message pipe
bindToPipe(pipe) {
this.stub_ = connection.bindHandleToStub(
pipe, sensor_provider.SensorProvider);
bindings.StubBindings(this.stub_).delegate = this;
bindings.StubBindings(this.stub_).connectionErrorHandler = () => {
this.reset();
};
}
// Mock functions
// Resets state of mock SensorProvider between test runs.
reset() {
if (this.active_sensor_ != null) {
this.active_sensor_.reset();
this.active_sensor_ = null;
}
this.get_sensor_should_fail_ = false;
this.resolve_func_ = null;
this.max_frequency_ = 60;
this.is_continuous_ = false;
if (this.stub_)
bindings.StubBindings(this.stub_).close();
}
// Sets flag that forces mock SensorProvider to fail when getSensor() is
// invoked.
setGetSensorShouldFail(should_fail) {
this.get_sensor_should_fail_ = should_fail;
}
// Returns mock sensor that was created in getSensor to the layout test.
getCreatedSensor() {
if (this.active_sensor_ != null) {
return Promise.resolve(this.active_sensor_);
}
return new Promise((resolve, reject) => {
this.resolve_func_ = resolve;
});
}
// Forces sensor to use |reporting_mode| as an update mode.
setContinuousReportingMode() {
this.is_continuous_ = true;
}
// Sets the maximum frequency for a concrete sensor.
setMaximumSupportedFrequency(frequency) {
this.max_frequency_ = frequency;
}
}
let mockSensorProvider = new MockSensorProvider;
mojo.frameInterfaces.addInterfaceOverrideForTesting(
sensor_provider.SensorProvider.name,
pipe => {
mockSensorProvider.bindToPipe(pipe);
});
return Promise.resolve({
mockSensorProvider: mockSensorProvider,
});
});
}
function sensor_test(func, name, properties) {
mojo_test(mojo => sensor_mocks(mojo).then(sensor => {
// Clean up and reset mock sensor stubs asynchronously, so that the blink
// side closes its proxies and notifies JS sensor objects before new test is
// started.
let onSuccess = () => {
sensor.mockSensorProvider.reset();
return new Promise((resolve, reject) => { setTimeout(resolve, 0); });
};
let onFailure = () => {
sensor.mockSensorProvider.reset();
return new Promise((resolve, reject) => { setTimeout(reject, 0); });
};
return Promise.resolve(func(sensor)).then(onSuccess, onFailure);
}), name, properties);
}