123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- // Smart comparison of three.js objects.
- // Identifies significant differences between two objects.
- // Performs deep comparison.
- // Comparison stops after the first difference is found.
- // Provides an explanation for the failure.
- function SmartComparer() {
- 'use strict';
- // Diagnostic message, when comparison fails.
- var message;
- // Keys to skip during object comparison.
- var omitKeys = [ 'id', 'uuid' ];
- return {
- areEqual: areEqual,
- getDiagnostic: function() {
- return message;
- }
- };
- // val1 - first value to compare (typically the actual value)
- // val2 - other value to compare (typically the expected value)
- function areEqual( val1, val2 ) {
- // Values are strictly equal.
- if ( val1 === val2 ) return true;
- // Null or undefined values.
- /* jshint eqnull:true */
- if ( val1 == null || val2 == null ) {
- if ( val1 != val2 ) {
- return makeFail( 'One value is undefined or null', val1, val2 );
- }
- // Both null / undefined.
- return true;
- }
- // Don't compare functions.
- if ( _.isFunction( val1 ) && _.isFunction( val2 ) ) return true;
- // Array comparison.
- var arrCmp = compareArrays( val1, val2 );
- if ( arrCmp !== undefined ) return arrCmp;
- // Has custom equality comparer.
- if ( val1.equals ) {
- if ( val1.equals( val2 ) ) return true;
- return makeFail( 'Comparison with .equals method returned false' );
- }
- // Object comparison.
- var objCmp = compareObjects( val1, val2 );
- if ( objCmp !== undefined ) return objCmp;
- // if (JSON.stringify( val1 ) == JSON.stringify( val2 ) ) return true;
- // Continue with default comparison.
- if ( _.isEqual( val1, val2 ) ) return true;
- // Object differs (unknown reason).
- return makeFail( 'Values differ', val1, val2 );
- }
- function compareArrays( val1, val2 ) {
- var isArr1 = Array.isArray( val1 );
- var isArr2 = Array.isArray( val2 );
- // Compare type.
- if ( isArr1 !== isArr2 ) return makeFail( 'Values are not both arrays' );
- // Not arrays. Continue.
- if ( !isArr1 ) return undefined;
- // Compare length.
- var N1 = val1.length;
- var N2 = val2.length;
- if ( N1 !== val2.length ) return makeFail( 'Array length differs', N1, N2 );
- // Compare content at each index.
- for ( var i = 0; i < N1; i ++ ) {
- var cmp = areEqual( val1[ i ], val2[ i ] );
- if ( !cmp ) return addContext( 'array index "' + i + '"' );
- }
- // Arrays are equal.
- return true;
- }
- function compareObjects( val1, val2 ) {
- var isObj1 = _.isObject( val1 );
- var isObj2 = _.isObject( val2 );
- // Compare type.
- if ( isObj1 !== isObj2 ) return makeFail( 'Values are not both objects' );
- // Not objects. Continue.
- if ( !isObj1 ) return undefined;
- // Compare keys.
- var keys1 = _( val1 ).keys().difference( omitKeys ).value();
- var keys2 = _( val2 ).keys().difference( omitKeys ).value();
- var missingActual = _.difference( keys1, keys2 );
- if ( missingActual.length !== 0 ) {
- return makeFail( 'Property "' + missingActual[0] + '" is unexpected.' );
- }
- var missingExpected = _.difference( keys2, keys1 );
- if ( missingExpected.length !== 0 ) {
- return makeFail( 'Property "' + missingExpected[0] + '" is missing.' );
- }
- // Keys are the same. For each key, compare content until a difference is found.
- var hadDifference = _.any( keys1, function ( key ) {
- var prop1 = val1[key];
- var prop2 = val2[key];
- // Compare property content.
- var eq = areEqual( prop1, prop2 );
- // In case of failure, an message should already be set.
- // Add context to low level message.
- if ( !eq ) addContext( 'property "' + key + '"' );
- return !eq;
- });
- return ! hadDifference;
- }
- function makeFail( msg, val1, val2 ) {
- message = msg;
- if ( arguments.length > 1) message += " (" + val1 + " vs " + val2 + ")";
- return false;
- }
- function addContext( msg ) {
- // There should already be a validation message. Add more context to it.
- message = message || "Error";
- message += ", at " + msg;
- return false;
- }
- }
|