| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| 'use strict'; |
| |
| require('jasmine-expect'); |
| const JasmineReporters = require('jasmine-reporters'); |
| |
| const psTree = require('ps-tree'); |
| const Util = require('util'); |
| const path = require('path'); |
| const fs = require('fs'); |
| const child_process = require('child_process'); |
| const config = require('./config'); |
| const LogReader = require('./LogReader'); |
| const {IgniteClient, IgniteClientConfiguration, Errors, EnumItem, Timestamp, Decimal, BinaryObject, ObjectType} = require('apache-ignite-client'); |
| |
| const TIMEOUT_MS = 60000; |
| |
| jasmine.getEnv().addReporter(new JasmineReporters.TeamCityReporter()); |
| |
| const dateComparator = (date1, date2) => { return !date1 && !date2 || date1.value === date2.value; }; |
| const floatComparator = (date1, date2) => { return Math.abs(date1 - date2) < 0.00001; }; |
| const defaultComparator = (value1, value2) => { return value1 === value2; }; |
| const enumComparator = (value1, value2) => { |
| return value1.getTypeId() === value2.getTypeId() && |
| value1.getOrdinal() === value2.getOrdinal(); }; |
| const decimalComparator = (value1, value2) => { |
| return value1 === null && value2 === null || |
| value1.equals(value2); |
| }; |
| const timestampComparator = (value1, value2) => { |
| return value1 === null && value2 === null || |
| dateComparator(value1.getTime(), value2.getTime()) && |
| value1.getNanos() === value2.getNanos(); }; |
| |
| const numericValueModificator = (data) => { return data > 0 ? data - 10 : data + 10; }; |
| const charValueModificator = (data) => { return String.fromCharCode(data.charCodeAt(0) + 5); }; |
| const booleanValueModificator = (data) => { return !data; }; |
| const stringValueModificator = (data) => { return data + 'xxx'; }; |
| const dateValueModificator = (data) => { return new Date(data.getTime() + 12345); }; |
| const UUIDValueModificator = (data) => { return data.reverse(); }; |
| const enumValueModificator = (data) => { return new EnumItem(data.getTypeId(), data.getOrdinal() + 1); }; |
| const decimalValueModificator = (data) => { return data.add(12345); }; |
| const timestampValueModificator = (data) => { return new Timestamp(new Date(data.getTime() + 12345), data.getNanos() + 123); }; |
| |
| const primitiveValues = { |
| [ObjectType.PRIMITIVE_TYPE.BYTE] : { |
| values : [-128, 0, 127], |
| isMapKey : true, |
| modificator : numericValueModificator |
| }, |
| [ObjectType.PRIMITIVE_TYPE.SHORT] : { |
| values : [-32768, 0, 32767], |
| isMapKey : true, |
| modificator : numericValueModificator |
| }, |
| [ObjectType.PRIMITIVE_TYPE.INTEGER] : { |
| values : [12345, 0, -54321], |
| isMapKey : true, |
| modificator : numericValueModificator |
| }, |
| [ObjectType.PRIMITIVE_TYPE.LONG] : { |
| values : [12345678912345, 0, -98765432112345], |
| isMapKey : true, |
| modificator : numericValueModificator |
| }, |
| [ObjectType.PRIMITIVE_TYPE.FLOAT] : { |
| values : [-1.155, 0, 123e-5], |
| isMapKey : false, |
| modificator : numericValueModificator |
| }, |
| [ObjectType.PRIMITIVE_TYPE.DOUBLE] : { |
| values : [-123e5, 0, 0.0001], |
| typeOptional : true, |
| isMapKey : false, |
| modificator : numericValueModificator |
| }, |
| [ObjectType.PRIMITIVE_TYPE.CHAR] : { |
| values : ['a', String.fromCharCode(0x1234)], |
| isMapKey : true, |
| modificator : charValueModificator |
| }, |
| [ObjectType.PRIMITIVE_TYPE.BOOLEAN] : { |
| values : [true, false], |
| isMapKey : true, |
| typeOptional : true, |
| modificator : booleanValueModificator |
| }, |
| [ObjectType.PRIMITIVE_TYPE.STRING] : { |
| values : ['abc', ''], |
| isMapKey : true, |
| typeOptional : true, |
| modificator : stringValueModificator |
| }, |
| [ObjectType.PRIMITIVE_TYPE.UUID] : { |
| values : [ |
| [ 18, 70, 2, 119, 154, 254, 198, 254, 195, 146, 33, 60, 116, 230, 0, 146 ], |
| [ 141, 77, 31, 194, 127, 36, 184, 255, 192, 4, 118, 57, 253, 209, 111, 147 ] |
| ], |
| isMapKey : false, |
| modificator : UUIDValueModificator |
| }, |
| [ObjectType.PRIMITIVE_TYPE.DATE] : { |
| values : [new Date(), new Date('1995-12-17T03:24:00'), new Date(0)], |
| typeOptional : true, |
| isMapKey : false, |
| modificator : dateValueModificator |
| }, |
| // [ObjectType.PRIMITIVE_TYPE.ENUM] : { |
| // values : [new EnumItem(12345, 7), new EnumItem(0, 0)], |
| // typeOptional : true, |
| // isMapKey : false, |
| // modificator : enumValueModificator |
| // }, |
| [ObjectType.PRIMITIVE_TYPE.DECIMAL] : { |
| values : [new Decimal('123456789.6789345'), new Decimal(0), new Decimal('-98765.4321e15')], |
| typeOptional : true, |
| isMapKey : false, |
| modificator : decimalValueModificator |
| }, |
| [ObjectType.PRIMITIVE_TYPE.TIMESTAMP] : { |
| values : [new Timestamp(new Date().getTime(), 12345), new Timestamp(new Date('1995-12-17T03:24:00').getTime(), 543), new Timestamp(0, 0)], |
| typeOptional : true, |
| isMapKey : false, |
| modificator : timestampValueModificator |
| }, |
| [ObjectType.PRIMITIVE_TYPE.TIME] : { |
| values : [new Date(), new Date('1995-12-17T03:24:00'), new Date(123)], |
| isMapKey : false, |
| modificator : dateValueModificator |
| } |
| }; |
| |
| const arrayValues = { |
| [ObjectType.PRIMITIVE_TYPE.BYTE_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.BYTE }, |
| [ObjectType.PRIMITIVE_TYPE.SHORT_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.SHORT }, |
| [ObjectType.PRIMITIVE_TYPE.INTEGER_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.INTEGER }, |
| [ObjectType.PRIMITIVE_TYPE.LONG_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.LONG }, |
| [ObjectType.PRIMITIVE_TYPE.FLOAT_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.FLOAT }, |
| [ObjectType.PRIMITIVE_TYPE.DOUBLE_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.DOUBLE, typeOptional : true }, |
| [ObjectType.PRIMITIVE_TYPE.CHAR_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.CHAR }, |
| [ObjectType.PRIMITIVE_TYPE.BOOLEAN_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.BOOLEAN, typeOptional : true }, |
| [ObjectType.PRIMITIVE_TYPE.STRING_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.STRING, typeOptional : true }, |
| [ObjectType.PRIMITIVE_TYPE.UUID_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.UUID }, |
| [ObjectType.PRIMITIVE_TYPE.DATE_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.DATE, typeOptional : true }, |
| //[ObjectType.PRIMITIVE_TYPE.ENUM_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.ENUM, typeOptional : true }, |
| [ObjectType.PRIMITIVE_TYPE.DECIMAL_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.DECIMAL, typeOptional : true }, |
| [ObjectType.PRIMITIVE_TYPE.TIMESTAMP_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.TIMESTAMP, typeOptional : true }, |
| [ObjectType.PRIMITIVE_TYPE.TIME_ARRAY] : { elemType : ObjectType.PRIMITIVE_TYPE.TIME } |
| }; |
| |
| // Helper class for testing apache-ignite-client library. |
| // Contains common methods for testing environment initialization and cleanup. |
| class TestingHelper { |
| static get TIMEOUT() { |
| return TIMEOUT_MS; |
| } |
| |
| static get primitiveValues() { |
| return primitiveValues; |
| } |
| |
| static get arrayValues() { |
| return arrayValues; |
| } |
| |
| // Initializes only cluster |
| static async initClusterOnly(serversNum = 1, needLogging = false) { |
| jasmine.DEFAULT_TIMEOUT_INTERVAL = TIMEOUT_MS; |
| |
| await TestingHelper.startTestServers(needLogging, serversNum); |
| } |
| |
| // Create test client instance |
| static makeClient() { |
| const client = new IgniteClient(); |
| client.setDebug(config.debug); |
| return client; |
| } |
| |
| // Initializes testing environment: creates and starts the library client, sets default jasmine test timeout. |
| // Should be called from any test suite beforeAll method. |
| static async init(partitionAwareness = config.partitionAwareness, serversNum = 1, needLogging = false, endpoints) { |
| jasmine.DEFAULT_TIMEOUT_INTERVAL = TIMEOUT_MS; |
| |
| if (!endpoints) |
| endpoints = TestingHelper.getEndpoints(serversNum); |
| |
| await TestingHelper.startTestServers(needLogging, serversNum); |
| |
| TestingHelper._igniteClient = TestingHelper.makeClient(); |
| await TestingHelper._igniteClient.connect(new IgniteClientConfiguration(...endpoints). |
| setConnectionOptions(false, null, partitionAwareness)); |
| } |
| |
| // Cleans up testing environment. |
| // Should be called from any test suite afterAll method. |
| static async cleanUp() { |
| try { |
| if (TestingHelper._igniteClient) { |
| await TestingHelper._igniteClient.disconnect(); |
| delete TestingHelper._igniteClient; |
| } |
| |
| if (TestingHelper._logReaders) |
| delete TestingHelper._logReaders; |
| } |
| finally { |
| await TestingHelper.stopTestServers(); |
| } |
| } |
| |
| static get igniteClient() { |
| return TestingHelper._igniteClient; |
| } |
| |
| static async destroyCache(cacheName, done) { |
| try { |
| await TestingHelper.igniteClient.destroyCache(cacheName); |
| } |
| catch (err) { |
| TestingHelper.checkOperationError(err, done); |
| } |
| } |
| |
| static getEndpoints(serversNum) { |
| if (serversNum < 1) |
| throw 'Wrong number of nodes: ' + serversNum; |
| |
| let res = []; |
| for (let i = 1; i < serversNum + 1; ++i) |
| res.push('127.0.0.1:' + (10800 + i)); |
| |
| return res; |
| } |
| |
| static isWindows() { |
| return process.platform === 'win32'; |
| } |
| |
| static getNodeRunner() { |
| if (!config.igniteHome) |
| throw 'Can not start node: ' + |
| 'IGNITE_HOME is not set'; |
| |
| const ext = TestingHelper.isWindows() ? '.bat' : '.sh'; |
| const runner = path.join(config.igniteHome, 'bin', 'ignite' + ext); |
| if (!fs.existsSync(runner)) |
| throw 'Can not find ' + runner + '. Please, check your IGNITE_HOME environment variable'; |
| |
| return runner; |
| } |
| |
| static getConfigPath(needLogging, idx = 1) { |
| if (!needLogging) |
| return path.join(__dirname, 'configs', 'ignite-config-default.xml'); |
| |
| return path.join(__dirname, 'configs', Util.format('ignite-config-%d.xml', idx)); |
| } |
| |
| static async sleep(milliseconds) { |
| return new Promise(resolve => setTimeout(resolve, milliseconds)); |
| } |
| |
| static async waitForCondition(cond, timeout) { |
| const startTime = Date.now(); |
| let now = startTime; |
| do { |
| const ok = await cond(); |
| if (ok) |
| return true; |
| |
| await TestingHelper.sleep(100); |
| now = Date.now(); |
| } while ((now - startTime) < timeout); |
| |
| return await cond(); |
| } |
| |
| static async waitForConditionOrThrow(cond, timeout) { |
| const startTime = Date.now(); |
| |
| while (!await cond()) { |
| if (Date.now() - startTime > timeout) { |
| throw 'Failed to achive condition within timeout ' + timeout; |
| } |
| |
| await TestingHelper.sleep(100); |
| } |
| } |
| |
| static async tryConnectClient(idx = 1, debug = false) { |
| const endPoint = Util.format('127.0.0.1:%d', 10800 + idx); |
| |
| TestingHelper.logDebug('Checking endpoint: ' + endPoint); |
| |
| let cli = new IgniteClient(); |
| cli.setDebug(debug); |
| |
| return await cli.connect(new IgniteClientConfiguration(endPoint). |
| setConnectionOptions(false, null, false)). |
| then(() => { |
| TestingHelper.logDebug('Successfully connected'); |
| cli.disconnect(); |
| return true; |
| }). |
| catch(error => { |
| TestingHelper.logDebug('Error while connecting: ' + error.toString()); |
| return false; |
| }); |
| } |
| |
| static async startTestServers(needLogging, serversNum) { |
| TestingHelper.logDebug('Starting ' + serversNum + ' node[s]'); |
| if (serversNum < 0) |
| throw 'Wrong number of servers to start: ' + serversNum; |
| |
| for (let i = 1; i < serversNum + 1; ++i) |
| await TestingHelper.startTestServer(needLogging, i); |
| } |
| |
| static async startTestServer(needLogging, idx) { |
| if (!TestingHelper._servers) |
| TestingHelper._servers = []; |
| |
| if (!TestingHelper._logReaders) |
| TestingHelper._logReaders = new Map(); |
| |
| TestingHelper._servers.push(await TestingHelper._startNode(needLogging, idx)); |
| |
| const logs = TestingHelper.getLogFiles(idx); |
| if (!needLogging && logs.length > 0) |
| throw 'Unexpected log file for node ' + idx; |
| |
| if (needLogging) { |
| if (logs.length != 1) |
| throw 'Unexpected number of log files for node ' + idx + ': ' + logs.length; |
| |
| TestingHelper._logReaders.set(idx, new LogReader(logs[0])); |
| } |
| } |
| |
| static async stopTestServers() { |
| if (TestingHelper._servers) { |
| for (let server of TestingHelper._servers) { |
| await TestingHelper.killNodeAndWait(server); |
| } |
| |
| delete TestingHelper._servers; |
| } |
| } |
| |
| static async killNodeByIdAndWait(idx) { |
| if (!TestingHelper._servers || idx < 0 || idx > TestingHelper._servers.length) |
| throw 'Invalid index'; |
| |
| const srv = TestingHelper._servers[idx - 1]; |
| if (srv) |
| await TestingHelper.killNodeAndWait(srv); |
| } |
| |
| static async killNodeAndWait(proc) { |
| const ProcessExists = require('process-exists'); |
| |
| const pid = proc.pid; |
| TestingHelper.killNode(proc); |
| |
| await TestingHelper.waitForConditionOrThrow(async () => { |
| return !(await ProcessExists(pid)); |
| }, 5000); |
| } |
| |
| static killNode(proc) { |
| if (TestingHelper.isWindows()) { |
| child_process.spawnSync('taskkill', ['/F', '/T', '/PID', proc.pid.toString()]) |
| } |
| psTree(proc.pid, function (err, children) { |
| children.map((p) => { |
| try { |
| process.kill(p.PID, 'SIGKILL'); |
| } |
| catch (_error) { |
| // No-op. |
| } |
| }); |
| }); |
| } |
| |
| // Make sure that topology is stable, version won't change and partition map is up-to-date for the given cache. |
| static async ensureStableTopology(igniteClient, cache, key = 1, skipLogs=false, timeout=5000) { |
| let oldTopVer = igniteClient._router._affinityTopologyVer; |
| |
| await cache.get(key); |
| |
| let newTopVer = igniteClient._router._affinityTopologyVer; |
| |
| while (newTopVer !== oldTopVer) { |
| oldTopVer = newTopVer; |
| await cache.get(key); |
| newTopVer = igniteClient._router._affinityTopologyVer; |
| } |
| |
| // Now when topology stopped changing, let's ensure we received distribution map. |
| let ok = await TestingHelper.waitForCondition(async () => { |
| await cache.get(key); |
| return await TestingHelper._waitMapObtained(igniteClient, cache, 1000); |
| }, timeout); |
| |
| if (!ok) |
| throw 'getting of partition map timed out'; |
| |
| if (skipLogs) |
| await TestingHelper.getRequestGridIdx(); |
| } |
| |
| // Waiting for distribution map to be obtained. |
| static async _waitMapObtained(igniteClient, cache, timeout) { |
| return await TestingHelper.waitForCondition(() => { |
| return igniteClient._router._distributionMap.has(cache._cacheId); |
| }, timeout); |
| } |
| |
| static async readLogFile(idx) { |
| const reader = TestingHelper._logReaders.get(idx); |
| if (!reader) { |
| TestingHelper.logDebug('WARNING: Reader is null'); |
| return null; |
| } |
| |
| return await reader.nextRequest(); |
| } |
| |
| static async getRequestGridIdx(message='Get') { |
| if (!TestingHelper._logReaders) |
| throw 'Logs are not enabled for the cluster'; |
| |
| let res = -1 |
| for(let [id, logReader] of TestingHelper._logReaders) { |
| if (!logReader) |
| continue; |
| |
| let req = null; |
| do { |
| req = await logReader.nextRequest(); |
| TestingHelper.logDebug('Node' + id +': Got ' + req + ', looking for ' + message); |
| if (req === message) |
| res = id; |
| } while (req != null); |
| } |
| |
| TestingHelper.logDebug('Request "' + message + '" node: ' + res); |
| |
| return res; |
| } |
| |
| static getLogFiles(idx) { |
| const glob = require('glob'); |
| // glob package only works with slashes so no need in 'path' here. |
| const logsPattern = Util.format('./logs/ignite-log-%d*.txt', idx); |
| const res = glob.sync(logsPattern); |
| return res; |
| } |
| |
| static clearLogs(idx) { |
| for (const f of TestingHelper.getLogFiles(idx)) |
| fs.unlinkSync(f); |
| } |
| |
| static async _startNode(needLogging, idx = 1) { |
| TestingHelper.clearLogs(idx); |
| |
| const runner = TestingHelper.getNodeRunner(); |
| |
| let nodeEnv = {}; |
| for (const ev in process.env) |
| nodeEnv[ev] = process.env[ev]; |
| |
| if (config.debug) { |
| nodeEnv['JVM_OPTS'] = '-Djava.net.preferIPv4Stack=true -Xdebug -Xnoagent -Djava.compiler=NONE \ |
| -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=' + (5005 + idx); |
| } |
| |
| const nodeCfg = TestingHelper.getConfigPath(needLogging, idx); |
| TestingHelper.logDebug('Trying to start node using following command: ' + runner + ' ' + nodeCfg); |
| |
| const srv = child_process.spawn(runner, [nodeCfg], {env: nodeEnv}); |
| |
| srv.on('error', (error) => { |
| jasmine.fail('Failed to start node: ' + error); |
| throw 'Failed to start node: ' + error; |
| }); |
| |
| srv.stdout.on('data', (data) => { |
| if (config.nodeDebug) |
| console.log(data.toString()); |
| }); |
| |
| srv.stderr.on('data', (data) => { |
| if (config.nodeDebug) |
| console.error(data.toString()); |
| }); |
| |
| const started = await TestingHelper.waitForCondition(async () => |
| TestingHelper.tryConnectClient(idx), 10000); |
| |
| if (!started) { |
| await TestingHelper.killNodeAndWait(srv); |
| throw 'Failed to start Node: timeout while trying to connect'; |
| } |
| |
| return srv; |
| } |
| |
| static executeExample(name, outputChecker) { |
| return new Promise((resolve, reject) => { |
| child_process.exec('node ' + name, (error, stdout, stderr) => { |
| TestingHelper.logDebug(stdout); |
| resolve(stdout); |
| }) |
| }). |
| then(output => { |
| expect(output).not.toMatch('ERROR:'); |
| expect(output).toMatch('Client is started'); |
| }); |
| } |
| |
| static checkOperationError(error, done) { |
| TestingHelper.checkError(error, Errors.OperationError, done) |
| } |
| |
| static checkIllegalArgumentError(error, done) { |
| TestingHelper.checkError(error, Errors.IgniteClientError, done) |
| } |
| |
| static checkEnumItemSerializationError(error, done) { |
| if (!(error instanceof Errors.IgniteClientError) || |
| error.message.indexOf('Enum item can not be serialized') < 0) { |
| done.fail('unexpected error: ' + error); |
| } |
| } |
| |
| static checkError(error, errorType, done) { |
| if (!(error instanceof errorType)) { |
| done.fail('unexpected error: ' + error); |
| } |
| } |
| |
| static logDebug(message) { |
| if (config.debug) { |
| console.log(message); |
| } |
| } |
| |
| static printValue(value) { |
| const val = Util.inspect(value, false, null); |
| const length = 500; |
| return val.length > length ? val.substr(0, length) + '...' : val; |
| } |
| |
| static async compare(value1, value2) { |
| TestingHelper.logDebug(Util.format('compare: %s and %s', TestingHelper.printValue(value1), TestingHelper.printValue(value2))); |
| if (value1 === undefined || value2 === undefined) { |
| TestingHelper.logDebug(Util.format('compare: unexpected "undefined" value')); |
| return false; |
| } |
| if (value1 === null && value2 === null) { |
| return true; |
| } |
| if (value1 === null && value2 !== null || value1 !== null && value2 === null) { |
| return false; |
| } |
| if (typeof value1 !== typeof value2) { |
| TestingHelper.logDebug(Util.format('compare: value types are different: %s and %s', |
| typeof value1, typeof value2)); |
| return false; |
| } |
| if (typeof value1 === 'number') { |
| return floatComparator(value1, value2); |
| } |
| else if (typeof value1 !== 'object') { |
| return defaultComparator(value1, value2); |
| } |
| else if (value1.constructor.name !== value2.constructor.name && !value2 instanceof BinaryObject) { |
| TestingHelper.logDebug(Util.format('compare: value types are different: %s and %s', |
| value1.constructor.name, value2.constructor.name)); |
| return false; |
| } |
| else if (value1 instanceof Date && value2 instanceof Date) { |
| return dateComparator(value1, value2); |
| } |
| else if (value1 instanceof EnumItem && value2 instanceof EnumItem) { |
| return enumComparator(value1, value2); |
| } |
| else if (value1 instanceof Decimal && value2 instanceof Decimal) { |
| return decimalComparator(value1, value2); |
| } |
| else if (value1 instanceof Timestamp && value2 instanceof Timestamp) { |
| return timestampComparator(value1, value2); |
| } |
| else if (value1 instanceof Array && value2 instanceof Array) { |
| if (value1.length !== value2.length) { |
| TestingHelper.logDebug(Util.format('compare: array lengths are different')); |
| return false; |
| } |
| for (var i = 0; i < value1.length; i++) { |
| if (!await TestingHelper.compare(value1[i], value2[i])) { |
| TestingHelper.logDebug(Util.format('compare: array elements are different: %s, %s', |
| TestingHelper.printValue(value1[i]), TestingHelper.printValue(value2[i]))); |
| return false; |
| } |
| } |
| return true; |
| } |
| else if (value1 instanceof Map && value2 instanceof Map) { |
| if (value1.size !== value2.size) { |
| TestingHelper.logDebug(Util.format('compare: map sizes are different')); |
| return false; |
| } |
| for (var [key, val] of value1) { |
| if (!value2.has(key)) { |
| TestingHelper.logDebug(Util.format('compare: maps are different: %s key is absent', TestingHelper.printValue(key))); |
| return false; |
| } |
| if (!(await TestingHelper.compare(val, value2.get(key)))) { |
| TestingHelper.logDebug(Util.format('compare: map values are different: %s, %s', |
| TestingHelper.printValue(val), TestingHelper.printValue(value2.get(key)))); |
| return false; |
| } |
| } |
| return true; |
| } |
| else if (value1 instanceof Set && value2 instanceof Set) { |
| if (value1.size !== value2.size) { |
| TestingHelper.logDebug(Util.format('compare: set sizes are different')); |
| return false; |
| } |
| const value1Arr = [...value1].sort(); |
| const value2Arr = [...value2].sort(); |
| if (!await TestingHelper.compare(value1Arr, value2Arr)) { |
| TestingHelper.logDebug(Util.format('compare: sets are different: %s and %s', |
| TestingHelper.printValue(value1Arr), TestingHelper.printValue(value2Arr))); |
| return false; |
| } |
| return true; |
| } |
| else if (value2 instanceof BinaryObject) { |
| if (value1 instanceof BinaryObject) { |
| if (value1.getTypeName() !== value2.getTypeName()) { |
| TestingHelper.logDebug(Util.format('compare: binary object type names are different')); |
| return false; |
| } |
| if (!await TestingHelper.compare(value1.getFieldNames(), value2.getFieldNames())) { |
| TestingHelper.logDebug(Util.format('compare: binary object field names are different')); |
| return false; |
| } |
| for (let fieldName of value1.getFieldNames()) { |
| if (!value1.hasField(fieldName) || !value2.hasField(fieldName) || |
| !await TestingHelper.compare(await value1.getField(fieldName), await value1.getField(fieldName))) { |
| TestingHelper.logDebug(Util.format('compare: binary objects field "%s" values are different', fieldName)); |
| return false; |
| } |
| } |
| return true; |
| } |
| else { |
| let value; |
| for (let key of Object.keys(value1)) { |
| value = await value2.getField(key); |
| if (!(await TestingHelper.compare(value1[key], value))) { |
| TestingHelper.logDebug(Util.format('compare: binary object values for key %s are different: %s and %s', |
| TestingHelper.printValue(key), TestingHelper.printValue(value1[key]), TestingHelper.printValue(value))); |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| else { |
| for (let key of Object.keys(value1)) { |
| if (!(await TestingHelper.compare(value1[key], value2[key]))) { |
| TestingHelper.logDebug(Util.format('compare: object values for key %s are different: %s and %s', |
| TestingHelper.printValue(key), TestingHelper.printValue(value1[key]), TestingHelper.printValue(value2[key]))); |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| } |
| |
| module.exports = TestingHelper; |