diff --git a/lib/js/src/manager/SdlManager.js b/lib/js/src/manager/SdlManager.js index b0343b04..ee1a1405 100644 --- a/lib/js/src/manager/SdlManager.js +++ b/lib/js/src/manager/SdlManager.js @@ -345,6 +345,11 @@ class SdlManager extends _SdlManagerBase { } this._lifecycleManager = new _LifecycleManager(this._lifecycleConfig, this._lifecycleListener); + if (this._managerListener) { + this._lifecycleManager.setOnSystemInfoReceived( + this._managerListener.onSystemInfoReceived.bind(this._managerListener) + ); + } /* FIXME: setSdlSecurity and related methods/classes do not exist if (this._sdlSecList.length > 0) { diff --git a/lib/js/src/manager/SdlManagerListener.js b/lib/js/src/manager/SdlManagerListener.js index ca27a450..043a884d 100644 --- a/lib/js/src/manager/SdlManagerListener.js +++ b/lib/js/src/manager/SdlManagerListener.js @@ -42,6 +42,7 @@ class SdlManagerListener { this._managerShouldUpdateLifecycle = (language) => { return null; }; + this._onSystemInfoReceived = null; } /** @@ -126,6 +127,28 @@ class SdlManagerListener { } return null; } + + /** + * Set the onSystemInfoReceived function. + * @param {function} listener - A function to be invoked when the event occurs. + * @returns {SdlManagerListener} - A reference to this instance to allow method chaining. + */ + setOnSystemInfoReceived (listener) { + this._onSystemInfoReceived = listener; + return this; + } + + /** + * Safely attempts to invoke the onSystemInfoReceived event. + * @param {SystemInfo} systemInfo - the system info of vehicle that this session is currently active on. + * @returns {Boolean} Return true if this session should continue, false if the session should end + */ + onSystemInfoReceived (systemInfo) { + if (typeof this._onSystemInfoReceived === 'function') { + return !!this._onSystemInfoReceived(systemInfo); + } + return true; + } } export { SdlManagerListener }; diff --git a/lib/js/src/manager/_SdlManagerBase.js b/lib/js/src/manager/_SdlManagerBase.js index 88574350..f27b715a 100644 --- a/lib/js/src/manager/_SdlManagerBase.js +++ b/lib/js/src/manager/_SdlManagerBase.js @@ -39,7 +39,7 @@ class _SdlManagerBase { * @class * @private * @param {AppConfig} appConfig - An instance of AppConfig describing the application's metadata and desired transport - * @param {ManagerListener} managerListener - An instance of ManagerListener to be used to listen for manager events + * @param {SdlManagerListener} managerListener - An instance of ManagerListener to be used to listen for manager events */ constructor (appConfig = null, managerListener = null) { this._state = -1; diff --git a/lib/js/src/manager/lifecycle/_LifecycleManager.js b/lib/js/src/manager/lifecycle/_LifecycleManager.js index 36e56e06..f919b881 100644 --- a/lib/js/src/manager/lifecycle/_LifecycleManager.js +++ b/lib/js/src/manager/lifecycle/_LifecycleManager.js @@ -42,6 +42,7 @@ import { _ArrayTools } from '../../util/_ArrayTools.js'; import { SdlMsgVersion } from '../../rpc/structs/SdlMsgVersion.js'; import { FunctionID } from '../../rpc/enums/FunctionID.js'; import { _ServiceType } from '../../protocol/enums/_ServiceType.js'; +import { SystemInfo } from '../../util/SystemInfo.js'; /** * This class should also be marked private and behind the SdlManager API @@ -90,6 +91,8 @@ class _LifecycleManager { this._systemCapabilityManager = new SystemCapabilityManager(this); this._encryptionLifecycleManager = null; this._registerAppInterfaceResponse = null; + + this._didCheckSystemInfo = false; } /** @@ -99,7 +102,7 @@ class _LifecycleManager { */ _createSessionListener () { const sessionListener = new _SdlSessionListener(); - sessionListener.setOnProtocolSessionStarted((serviceType, sessionID, version, correlationID, hashID, isEncrypted) => { + sessionListener.setOnProtocolSessionStarted((serviceType, sessionID, version, correlationID, hashID, isEncrypted, systemInfo) => { // Session has been started if (this._minimumProtocolVersion !== null && this._minimumProtocolVersion.isNewerThan(this.getProtocolVersion()) === 1) { console.warn(`Disconnecting from head unit, the configured minimum protocol version ${this._minimumProtocolVersion} is greater than the supported protocol version ${this.getProtocolVersion()}`); @@ -107,6 +110,15 @@ class _LifecycleManager { return this._cleanProxy(); } + if (systemInfo && !this._didCheckSystemInfo) { + this._didCheckSystemInfo = true; + if (!this.onSystemInfoReceived(systemInfo)) { + console.warn('Disconnecting from head unit, the system info was not accepted.'); + this._sdlSession.endService(serviceType, this._sdlSession.getSessionId()); + return this._cleanProxy(); + } + } + if (serviceType === _ServiceType.RPC) { if (this._lifecycleConfig !== null && this._lifecycleConfig !== undefined) { this.sendRpcResolve(this._createRegisterAppInterface()); @@ -406,6 +418,28 @@ class _LifecycleManager { return registerAppInterface; } + /** + * Set the onSystemInfoReceived function. + * @param {function} listener - A function to be invoked when the event occurs. + * @returns {_LifecycleManager} - A reference to this instance to allow method chaining. + */ + setOnSystemInfoReceived (listener) { + this._onSystemInfoReceived = listener; + return this; + } + + /** + * Safely attempts to invoke the onSystemInfoReceived event. + * @param {SystemInfo} systemInfo - the system info of vehicle that this session is currently active on. + * @returns {Boolean} Return true if this session should continue, false if the session should end + */ + onSystemInfoReceived (systemInfo) { + if (typeof this._onSystemInfoReceived === 'function') { + return !!this._onSystemInfoReceived(systemInfo); + } + return true; + } + /* ******************************************************************************************************* ********************************** INTERNAL - RPC LISTENERS !! START !! ********************************* @@ -484,6 +518,21 @@ class _LifecycleManager { this._cleanProxy(); } + if (!this._didCheckSystemInfo) { + this._didCheckSystemInfo = true; + const vehicleType = registerAppInterfaceResponse.getVehicleType(); + const systemSoftwareVersion = registerAppInterfaceResponse.getSystemSoftwareVersion(); + let systemInfo = null; + if (vehicleType || systemSoftwareVersion) { + systemInfo = new SystemInfo(vehicleType, systemSoftwareVersion); + } + if (systemInfo && !this.onSystemInfoReceived(systemInfo)) { + console.warn('Disconnecting from head unit, the system info was not accepted.'); + this.sendRpcResolve(new UnregisterAppInterface()); + this._cleanProxy(); + } + } + // parse RAI for system capabilities this._systemCapabilityManager._parseRaiResponse(registerAppInterfaceResponse); } diff --git a/lib/js/src/protocol/_SdlProtocolBase.js b/lib/js/src/protocol/_SdlProtocolBase.js index 4fbbe6d4..65c7d1ed 100644 --- a/lib/js/src/protocol/_SdlProtocolBase.js +++ b/lib/js/src/protocol/_SdlProtocolBase.js @@ -38,10 +38,10 @@ import { _FrameType } from './enums/_FrameType.js'; import { _MessageFrameAssembler } from './_MessageFrameAssembler.js'; import { _SdlPacket } from './_SdlPacket.js'; import { _ControlFrameTags } from './enums/_ControlFrameTags.js'; -import { _BitConverter } from './../util/_BitConverter.js'; +import { _BitConverter } from '../util/_BitConverter.js'; import { _SdlPacketFactory } from './_SdlPacketFactory.js'; -import { RpcCreator } from './../rpc/RpcCreator.js'; +import { RpcCreator } from '../rpc/RpcCreator.js'; import { ImageResolution } from '../rpc/structs/ImageResolution.js'; import { VideoStreamingFormat } from '../rpc/structs/VideoStreamingFormat.js'; @@ -462,7 +462,7 @@ class _SdlProtocolBase { } this._sdlProtocolListener.onProtocolSessionStarted(serviceType, - sdlPacket.getSessionID(), this._protocolVersion.getMajor(), '', this._hashID, sdlPacket.getEncryption()); + sdlPacket.getSessionID(), this._protocolVersion.getMajor(), '', this._hashID, sdlPacket.getEncryption(), sdlPacket); } /** @@ -588,6 +588,6 @@ _SdlProtocolBase.V3_V4_MTU_SIZE = 131072; /** * Max supported protocol version in this release of the library */ -_SdlProtocolBase.MAX_PROTOCOL_VERSION = new Version(5, 3, 0); +_SdlProtocolBase.MAX_PROTOCOL_VERSION = new Version(5, 4, 0); export { _SdlProtocolBase }; diff --git a/lib/js/src/protocol/_SdlProtocolListener.js b/lib/js/src/protocol/_SdlProtocolListener.js index 09a6499c..4c0cac2a 100644 --- a/lib/js/src/protocol/_SdlProtocolListener.js +++ b/lib/js/src/protocol/_SdlProtocolListener.js @@ -112,16 +112,17 @@ class _SdlProtocolListener { /** * Safely attempts to invoke the event listener. - * @param {ServiceType} serviceType - A ServiceType enum value. + * @param {_ServiceType} serviceType - A ServiceType enum value. * @param {Number} sessionId - A session ID. * @param {Number} version - A numeric version. * @param {String} correlationId - A correlation ID. * @param {Number} hashId - A hash ID. * @param {Boolean} isEncrypted - Whether or not it is encrypted. + * @param {_SdlPacket} sdlPacket - An _SdlPacket. */ - onProtocolSessionStarted (serviceType, sessionId, version, correlationId, hashId, isEncrypted) { + onProtocolSessionStarted (serviceType, sessionId, version, correlationId, hashId, isEncrypted, sdlPacket) { if (typeof this._onProtocolSessionStarted === 'function') { - this._onProtocolSessionStarted(serviceType, sessionId, version, correlationId, hashId, isEncrypted); + this._onProtocolSessionStarted(serviceType, sessionId, version, correlationId, hashId, isEncrypted, sdlPacket); } } @@ -137,7 +138,7 @@ class _SdlProtocolListener { /** * Safely attempts to invoke the event listener. - * @param {ServiceType} serviceType - A ServiceType enum value. + * @param {_ServiceType} serviceType - A ServiceType enum value. * @param {Number} sessionId - A Session ID. * @param {String} correlationId - A correlation ID. */ @@ -159,7 +160,7 @@ class _SdlProtocolListener { /** * Safely attempts to invoke the event listener. - * @param {ServiceType} serviceType - A ServiceType enum value. + * @param {_ServiceType} serviceType - A ServiceType enum value. * @param {Number} sessionId - A Session ID. * @param {String} correlationId - A correlation ID. */ @@ -254,4 +255,4 @@ class _SdlProtocolListener { } } -export { _SdlProtocolListener }; \ No newline at end of file +export { _SdlProtocolListener }; diff --git a/lib/js/src/protocol/enums/_ControlFrameTags.js b/lib/js/src/protocol/enums/_ControlFrameTags.js index 8e921135..51e2b6ed 100644 --- a/lib/js/src/protocol/enums/_ControlFrameTags.js +++ b/lib/js/src/protocol/enums/_ControlFrameTags.js @@ -72,6 +72,18 @@ _ControlFrameTags.RPC = Object.freeze({ VIDEO_SERVICE_TRANSPORTS: 'videoServiceTransports', /** Auth token to be used for log in into services **/ AUTH_TOKEN: 'authToken', + /** + * Vehicle info to describe connected device + */ + MAKE: 'make', + MODEL: 'model', + MODEL_YEAR: 'modelYear', + TRIM: 'trim', + /** + * System specifics for hardware and software versions of connected device + */ + SYSTEM_SOFTWARE_VERSION: 'systemSoftwareVersion', + SYSTEM_HARDWARE_VERSION: 'systemHardwareVersion', }, StartServiceACKBase, StartServiceProtocolVersion, StartServiceHashId), StartServiceNAK: NAKBase, @@ -128,4 +140,4 @@ _ControlFrameTags.Video = Object.freeze({ EndServiceNAK: NAKBase, }); -export { _ControlFrameTags }; \ No newline at end of file +export { _ControlFrameTags }; diff --git a/lib/js/src/rpc/messages/RegisterAppInterfaceResponse.js b/lib/js/src/rpc/messages/RegisterAppInterfaceResponse.js index 1d57b8a1..0216eb5c 100644 --- a/lib/js/src/rpc/messages/RegisterAppInterfaceResponse.js +++ b/lib/js/src/rpc/messages/RegisterAppInterfaceResponse.js @@ -1,6 +1,6 @@ /* eslint-disable camelcase */ /* -* Copyright (c) 2020, SmartDeviceLink Consortium, Inc. +* Copyright (c) 2021, SmartDeviceLink Consortium, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -334,6 +334,7 @@ class RegisterAppInterfaceResponse extends RpcResponse { /** * Set the VehicleType * @since SmartDeviceLink 2.0.0 + * @deprecated in SmartDeviceLink 7.1.0 * @param {VehicleType} type - Specifies the vehicle's type. See VehicleType. - The desired VehicleType. * @returns {RegisterAppInterfaceResponse} - The class instance for method chaining. */ @@ -414,6 +415,7 @@ class RegisterAppInterfaceResponse extends RpcResponse { /** * Set the SystemSoftwareVersion * @since SmartDeviceLink 3.0.0 + * @deprecated in SmartDeviceLink 7.1.0 * @param {String} version - The software version of the system that implements the SmartDeviceLink core. - The desired SystemSoftwareVersion. * {'string_min_length': 1, 'string_max_length': 100} * @returns {RegisterAppInterfaceResponse} - The class instance for method chaining. diff --git a/lib/js/src/session/_SdlSession.js b/lib/js/src/session/_SdlSession.js index a282056b..cee99c1b 100644 --- a/lib/js/src/session/_SdlSession.js +++ b/lib/js/src/session/_SdlSession.js @@ -35,6 +35,10 @@ import { _SdlProtocol } from '../protocol/_SdlProtocol.js'; import { _ServiceType } from '../protocol/enums/_ServiceType.js'; import { _ServiceListenerMap } from './_ServiceListenerMap.js'; import { _VideoStreamingParameters } from '../streaming/video/_VideoStreamingParameters.js'; +import { _ControlFrameTags } from '../protocol/enums/_ControlFrameTags.js'; +import { VehicleType } from '../rpc/structs/VehicleType.js'; +import { SystemInfo } from '../util/SystemInfo.js'; +import { Version } from '../util/Version.js'; /** @@ -84,6 +88,36 @@ class _SdlSession { return sdlProtocolListener; } + /** + * Extracts the SystemInfo out of a packet + * @param {_SdlPacket} sdlPacket - should be a StartServiceACK for the RPC service. + * @returns {SystemInfo|null} - an instance of SystemInfo if the information is available, null otherwise. + */ + _extractSystemInfo (sdlPacket) { + const make = sdlPacket.getTag(_ControlFrameTags.RPC.StartServiceACK.MAKE); + const model = sdlPacket.getTag(_ControlFrameTags.RPC.StartServiceACK.MODEL); + const modelYear = sdlPacket.getTag(_ControlFrameTags.RPC.StartServiceACK.MODEL_YEAR); + const trim = sdlPacket.getTag(_ControlFrameTags.RPC.StartServiceACK.TRIM); + + const systemHardwareVersion = sdlPacket.getTag(_ControlFrameTags.RPC.StartServiceACK.SYSTEM_HARDWARE_VERSION); + const systemSoftwareVersion = sdlPacket.getTag(_ControlFrameTags.RPC.StartServiceACK.SYSTEM_SOFTWARE_VERSION); + + let vehicleType = null; + if (make || model || modelYear || trim) { + vehicleType = new VehicleType({ + make, + model, + modelYear, + trim, + }); + } + if (!vehicleType && !systemSoftwareVersion && !systemHardwareVersion) { + return null; + } + + return new SystemInfo(vehicleType, systemSoftwareVersion, systemHardwareVersion); + } + /** * Starts up the SDL protocol class. It will kick off the transport manager and underlying transport. */ @@ -118,15 +152,21 @@ class _SdlSession { * @param {String} correlationId - A correlationID. * @param {Number} hashId - A hash ID. * @param {Boolean} isEncrypted - Whether or not it is encrypted. + * @param {_SdlPacket} sdlPacket - An _SdlPacket. */ - onProtocolSessionStarted (serviceType, sessionId, version, correlationId, hashId, isEncrypted) { + onProtocolSessionStarted (serviceType, sessionId, version, correlationId, hashId, isEncrypted, sdlPacket) { this._sessionId = sessionId; if (serviceType === _ServiceType.RPC) { this._sessionHashId = hashId; } - this._sdlSessionListener.onProtocolSessionStarted(serviceType, sessionId, version, correlationId, hashId, isEncrypted); + let systemInfo = null; + if (version && (this.getProtocolVersion().isNewerThan(new Version(5, 4, 0)) >= 0)) { + systemInfo = this._extractSystemInfo(sdlPacket); + } + + this._sdlSessionListener.onProtocolSessionStarted(serviceType, sessionId, version, correlationId, hashId, isEncrypted, systemInfo); this._serviceListeners.sendEventServiceStarted(this, serviceType, isEncrypted); } @@ -324,4 +364,4 @@ class _SdlSession { } } -export { _SdlSession }; \ No newline at end of file +export { _SdlSession }; diff --git a/lib/js/src/session/_SdlSessionListener.js b/lib/js/src/session/_SdlSessionListener.js index 41d85572..c6fdb30a 100644 --- a/lib/js/src/session/_SdlSessionListener.js +++ b/lib/js/src/session/_SdlSessionListener.js @@ -101,22 +101,23 @@ class _SdlSessionListener { /** * Safely attempts to invoke the onProtocolSessionStarted event. - * @param {ServiceType} serviceType - The ServiceType. + * @param {_ServiceType} serviceType - The ServiceType. * @param {Number} sessionID - represents a byte * @param {Number} version - represents a byte * @param {String} correlationID - The correlationID. * @param {Number} hashID - The hash ID. * @param {Boolean} isEncrypted - Whether or not it is encrypted. + * @param {SystemInfo} systemInfo - the system info of vehicle that this session is currently active on. */ - onProtocolSessionStarted (serviceType, sessionID, version, correlationID, hashID, isEncrypted) { + onProtocolSessionStarted (serviceType, sessionID, version, correlationID, hashID, isEncrypted, systemInfo) { if (typeof this._onProtocolSessionStarted === 'function') { - this._onProtocolSessionStarted(serviceType, sessionID, version, correlationID, hashID, isEncrypted); + this._onProtocolSessionStarted(serviceType, sessionID, version, correlationID, hashID, isEncrypted, systemInfo); } } /** * Safely attempts to invoke the onProtocolSessionEnded event. - * @param {ServiceType} serviceType - The ServiceType. + * @param {_ServiceType} serviceType - The ServiceType. * @param {Number} sessionID - represents a byte * @param {String} correlationID - The correlationID. */ @@ -128,7 +129,7 @@ class _SdlSessionListener { /** * Safely attempts to invoke the onProtocolSessionEndedNACKed event. - * @param {ServiceType} serviceType - The ServiceType. + * @param {_ServiceType} serviceType - The ServiceType. * @param {Number} sessionID - represents a byte * @param {String} correlationID - The correlationID. */ @@ -179,4 +180,4 @@ class _SdlSessionListener { } } -export { _SdlSessionListener }; \ No newline at end of file +export { _SdlSessionListener }; diff --git a/lib/js/src/util/SystemInfo.js b/lib/js/src/util/SystemInfo.js new file mode 100644 index 00000000..26dc72dc --- /dev/null +++ b/lib/js/src/util/SystemInfo.js @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021, SmartDeviceLink Consortium, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +class SystemInfo { + /** + * Initializes an instance of SystemInfo. + * @class + * @param {VehicleType} vehicleType - the type of vehicle + * @param {String} systemSoftwareVersion - the software version of the vehicle system + * @param {String} systemHardwareVersion - the hardware version of the vehicle system + */ + constructor ( + vehicleType = null, + systemSoftwareVersion = null, + systemHardwareVersion = null + ) { + this.setVehicleType(vehicleType); + this.setSystemSoftwareVersion(systemSoftwareVersion); + this.setSystemHardwareVersion(systemHardwareVersion); + } + + /** + * Set the type of vehicle. + * @param {VehicleType} vehicleType - The type of vehicle. + * @returns {SystemInfo} - A reference to the class instance for method chaining. + */ + setVehicleType (vehicleType) { + this._vehicleType = vehicleType; + return this; + } + + /** + * Get the type of vehicle + * @returns {VehicleType} - The type of vehicle. + */ + getVehicleType () { + return this._vehicleType; + } + + /** + * Set the software version of the vehicle system. + * @param {String} systemSoftwareVersion - The software version of the vehicle system. + * @returns {SystemInfo} - A reference to the class instance for method chaining. + */ + setSystemSoftwareVersion (systemSoftwareVersion) { + this._systemSoftwareVersion = systemSoftwareVersion; + return this; + } + + /** + * Get the software version of the vehicle system + * @returns {String} - The software version of the vehicle system. + */ + getSystemSoftwareVersion () { + return this._systemSoftwareVersion; + } + + /** + * Set the hardware version of the vehicle system. + * @param {String} systemHardwareVersion - The hardware version of the vehicle system. + * @returns {SystemInfo} - A reference to the class instance for method chaining. + */ + setSystemHardwareVersion (systemHardwareVersion) { + this._systemHardwareVersion = systemHardwareVersion; + return this; + } + + /** + * Get the hardware version of the vehicle system + * @returns {String} - The hardware version of the vehicle system. + */ + getSystemHardwareVersion () { + return this._systemHardwareVersion; + } +} + +export { SystemInfo }; \ No newline at end of file diff --git a/tests/managers/lifecycle/LifecycleManagerTests.js b/tests/managers/lifecycle/LifecycleManagerTests.js index 3727636e..630a1d1e 100644 --- a/tests/managers/lifecycle/LifecycleManagerTests.js +++ b/tests/managers/lifecycle/LifecycleManagerTests.js @@ -52,5 +52,22 @@ module.exports = function (appClient) { Validator.assertNotNullUndefined(version.getPatchVersion()); done(); }); + + it('testOnSystemInfoReceived', function (done) { + const mockSystemInfo = {}; + const defaultResult = true; + let actualResult = sdlManager._lifecycleManager.onSystemInfoReceived(mockSystemInfo); + Validator.assertEquals(actualResult, defaultResult); + + const testResult = false; + const testListener = function (mockSystemInfo) { + return testResult; + }; + sdlManager._lifecycleManager.setOnSystemInfoReceived(testListener); + actualResult = sdlManager._lifecycleManager.onSystemInfoReceived(mockSystemInfo); + Validator.assertEquals(actualResult, testResult); + + done(); + }); }); }; \ No newline at end of file