/**
* @copyright Copyright 2016 Kevin Locke <kevin@kevinlocke.name>
* @license MIT
*/
'use strict';
/** Incrementally compares and reduces an Array-like property of the states
* using a given comparison function.
*
* @ template CompareResult
* @param {!StreamState} state1 First state to compare.
* @param {!StreamState} state2 Second state to compare.
* @param {string} propName Name of Array-like property to compare.
* @param {function(*, *): CompareResult} compare Comparison function to apply
* to the <code>propName</code> values from each state.
* @return {CompareResult} Result of comparing <code>propName</code> values, up
* to the minimum common length.
* @private
*/
function incrementalProp(state1, state2, propName, compare) {
const values1 = state1[propName];
const values2 = state2[propName];
let result;
// Note: Values may be undefined if no data was read.
if (((values1 && values1.length !== 0) || !state1.expectEvents)
&& ((values2 && values2.length !== 0) || !state2.expectEvents)) {
const minLen = Math.min(
values1 ? values1.length : 0,
values2 ? values2.length : 0,
);
if ((values1 && values1.length > minLen) && state2.expectEvents) {
result = compare(values1.slice(0, minLen), values2);
} else if ((values2 && values2.length > minLen) && state1.expectEvents) {
result = compare(values1, values2.slice(0, minLen));
} else {
result = compare(values1, values2);
}
if (minLen > 0 && (result === null || result === undefined)) {
state1[propName] = values1.slice(minLen);
state2[propName] = values2.slice(minLen);
}
}
return result;
}
/** Makes an incremental comparison and reduction function from a comparison
* function.
*
* Given a function which compares output data (e.g.
* <code>assert.deepStrictEqual</code>), this function returns an incremental
* comparison function which compares only the amount of data output by both
* streams (unless the stream has ended, in which case all remaining data is
* compared) and removes the compared data if no comparison result is
* returned/thrown.
*
* @ template CompareResult
* @param {function((string|Buffer|Array), (string|Buffer|Array)):
* CompareResult} compareData Data comparison function which will be called
* with data output by each stream.
* @param {?function(!Array<!{name:string,args:Array}>,
* !Array<!{name:string,args:Array}>): CompareResult=} compareEvents Events
* comparison function which will be called with the events output by each
* stream.
* @returns {function(!StreamState, !StreamState): CompareResult} Incremental
* comparison function which compares the stream states using
* <code>compareData</code> and <code>compareEvents</code>, and removes
* compared values if no result is reached.
* @alias makeIncremental
*/
module.exports = function makeIncremental(compareData, compareEvents) {
return function incremental(state1, state2) {
const dataResult = compareData
&& incrementalProp(state1, state2, 'data', compareData);
const eventsResult = compareEvents
&& incrementalProp(state1, state2, 'events', compareEvents);
return dataResult !== null && dataResult !== undefined
? dataResult
: eventsResult;
};
};