Start cleaning up unit tests by introducing new class/type-based comparators lookup table.

This commit is contained in:
Jürg Lehni 2014-12-28 14:33:22 +01:00
parent 28538d8a43
commit e95e17826e
7 changed files with 122 additions and 106 deletions

View file

@ -27,42 +27,70 @@ QUnit.jsDump.setParser('object', function (obj, stack) {
: objectParser).call(this, obj, stack); : objectParser).call(this, obj, stack);
}); });
function getFunctionBody(func) { var comparators = {
return func.toString().match( Number: function(actual, expected, message, options) {
// Compare with a default tolerance of Numerical.TOLERANCE:
var ok = Math.abs(actual - expected)
<= Base.pick(options && options.tolerance, Numerical.TOLERANCE);
QUnit.push(ok, ok ? expected : actual, expected, message);
},
Array: function(actual, expected, message, options) {
equals(actual.length, expected.length, (message || '') + ' length',
options);
for (var i = 0, l = actual.length; i < l; i++) {
equals(actual[i], expected[i], (message || '') + ' [' + i + ']',
options);
}
}
};
function getClass(object) {
return typeof object === 'number' && 'Number'
|| Array.isArray(object) && 'Array'
|| object && object._class;
}
function getFunctionMessage(func) {
var message = func.toString().match(
/^\s*function[^\{]*\{([\s\S]*)\}\s*$/)[1] /^\s*function[^\{]*\{([\s\S]*)\}\s*$/)[1]
.replace(/ /g, '') .replace(/ /g, '')
.replace(/^\s+|\s+$/g, ''); .replace(/^\s+|\s+$/g, '');
if (/^return /.test(message)) {
message = message
.replace(/^return /, '')
.replace(/;$/, '');
}
return message;
} }
// Override equals to convert functions to message and execute them as tests() // Override equals to convert functions to message and execute them as tests()
function equals(actual, expected, message, tolerance) { function equals(actual, expected, message, options) {
// Allow the use of functions for actual, which will get called and their // Allow the use of functions for actual, which will get called and their
// source content extracted for readable reports. // source content extracted for readable reports.
if (typeof actual === 'function') { if (typeof actual === 'function') {
if (!message) { if (!message)
message = getFunctionBody(actual); message = getFunctionMessage(actual);
if (/^return /.test(message)) {
message = message
.replace(/^return /, '')
.replace(/;$/, '');
}
}
actual = actual(); actual = actual();
} }
if (typeof expected === 'number') { if (actual != null) {
// Compare with a default tolerance of Numerical.TOLERANCE: var comparator = comparators[getClass(actual)];
var ok = Math.abs(actual - expected) if (comparator)
<= Base.pick(tolerance, Numerical.TOLERANCE); return comparator(actual, expected, message, options);
return QUnit.push(ok, ok ? expected : actual, expected, message); // Support calling of #equals() on the actual or expected value.
} else if (actual && actual.equals) { if (actual.equals)
// Support calling of #equals() on the actual or expected value, and return QUnit.push(actual.equals(expected),
// automatically convert displayed values to strings. actual, expected, message);
return QUnit.push(actual.equals(expected), actual, expected, message);
} else if (expected && expected.equals) {
return QUnit.push(expected.equals(actual), actual, expected, message);
} }
// Let's be strict if (expected != null) {
return strictEqual(actual, expected, message); var comparator = comparators[getClass(expected)];
if (comparator)
return comparator(actual, expected, message, options);
if (expected.equals)
return QUnit.push(expected.equals(actual),
actual, expected, message);
}
QUnit.push(actual === expected, actual, expected, message);
} }
function test(testName, expected) { function test(testName, expected) {
@ -83,42 +111,36 @@ function asyncTest(testName, expected) {
}); });
} }
function compareArrays(array1, array2, message, tolerance) { function comparePoints(point1, point2, message, options) {
equals(array1.length, array2.length, (message || '') + ' length'); equals(point1.x, point2.x, (message || '') + ' x', options);
for (var i = 0, l = array1.length; i < l; i++) { equals(point1.y, point2.y, (message || '') + ' y', options);
equals(array1[i], array2[i], (message || '') + ' [' + i + ']',
tolerance);
}
} }
function comparePoints(point1, point2, message, tolerance) { function compareSize(size1, size2, message, options) {
equals(point1.x, point2.x, (message || '') + ' x', tolerance); equals(size1.width, size2.width, (message || '') + ' width', options);
equals(point1.y, point2.y, (message || '') + ' y', tolerance); equals(size1.height, size2.height, (message || '') + ' height', options);
} }
function compareSize(size1, size2, message, tolerance) { function compareRectangles(rect1, rect2, message, options) {
equals(size1.width, size2.width, (message || '') + ' width', tolerance); comparePoints(rect1, rect2, message, options);
equals(size1.height, size2.height, (message || '') + ' height', tolerance); compareSize(rect1, rect2, message, options);
} }
function compareRectangles(rect1, rect2, message, tolerance) { function compareColors(color1, color2, message, options) {
comparePoints(rect1, rect2, message, tolerance);
compareSize(rect1, rect2, message, tolerance);
}
function compareColors(color1, color2, message, tolerance) {
color1 = color1 && new Color(color1); color1 = color1 && new Color(color1);
color2 = color2 && new Color(color2); color2 = color2 && new Color(color2);
if (color1 && color2) { if (color1 && color2) {
equals(color1.type, color2.type, (message || '') + ' type'); equals(color1.type, color2.type,
compareArrays(color1.components, color2.components, (message || '') + ' type', options);
(message || '') + ' components', tolerance); equals(color1.components, color2.components,
(message || '') + ' components', options);
} else { } else {
equals(color1, color2, message, tolerance); equals(color1, color2, message, options);
} }
} }
function compareStyles(style, style2, checkIdentity) { function compareStyles(style, style2, options) {
var checkIdentity = options && options.checkIdentity;
if (checkIdentity) { if (checkIdentity) {
equals(function() { equals(function() {
return style !== style2; return style !== style2;
@ -150,38 +172,30 @@ function compareStyles(style, style2, checkIdentity) {
} }
}); });
compareObjects('Style', ['strokeCap', 'strokeJoin', 'dashArray', compareObjects(['strokeCap', 'strokeJoin', 'dashArray', 'dashOffset',
'dashOffset', 'miterLimit', 'strokeOverprint', 'fillOverprint', 'miterLimit', 'strokeOverprint', 'fillOverprint',
'fontSize', 'font', 'leading', 'justification'], 'fontSize', 'font', 'leading', 'justification'],
style, style2, checkIdentity); style, style2, 'Compare Style', options);
} }
function compareObjects(name, keys, obj, obj2, checkIdentity) { function compareObjects(keys, obj, obj2, message, options) {
if (checkIdentity) { if (options && options.checkIdentity) {
equals(function() { equals(function() {
return obj !== obj2; return obj !== obj2;
}, true); }, true);
} }
Base.each(keys, function(key) { Base.each(keys, function(key) {
var val = obj[key], val2 = obj2[key], equals(obj[key], obj2[key], message + '#' + key, options);
message = 'Compare ' + name + '#' + key;
if (typeof val === 'number') {
equals(val, val2, message);
} else if (Array.isArray(val)) {
compareArrays(val, val2, message);
} else {
equals(val, val2, message);
}
}); });
} }
function compareSegmentPoints(segmentPoint, segmentPoint2, checkIdentity) { function compareSegmentPoints(segmentPoint, segmentPoint2, options) {
compareObjects('SegmentPoint', ['x', 'y', 'selected'], compareObjects(['x', 'y', 'selected'], segmentPoint, segmentPoint2,
segmentPoint, segmentPoint2, checkIdentity); 'Compare SegmentPoint', options);
} }
function compareSegments(segment, segment2, checkIdentity) { function compareSegments(segment, segment2, options) {
if (checkIdentity) { if (options.checkIdentity) {
equals(function() { equals(function() {
return segment !== segment2; return segment !== segment2;
}, true); }, true);
@ -194,7 +208,8 @@ function compareSegments(segment, segment2, checkIdentity) {
}); });
} }
function compareSegmentLists(segmentList, segmentList2, checkIdentity) { function compareSegmentLists(segmentList, segmentList2, options) {
var checkIdentity = options && options.checkIdentity;
if (checkIdentity) { if (checkIdentity) {
equals(function() { equals(function() {
return segmentList !== segmentList2; return segmentList !== segmentList2;
@ -206,12 +221,13 @@ function compareSegmentLists(segmentList, segmentList2, checkIdentity) {
for (var i = 0, l = segmentList.length; i < l; i++) { for (var i = 0, l = segmentList.length; i < l; i++) {
var segment = segmentList[i], var segment = segmentList[i],
segment2 = segmentList2[i]; segment2 = segmentList2[i];
compareSegments(segment, segment2, checkIdentity); compareSegments(segment, segment2, options);
} }
} }
} }
function compareItems(item, item2, cloned, checkIdentity, dontShareProject) { function compareItems(item, item2, options) {
var checkIdentity = options && options.checkIdentity;
if (checkIdentity) { if (checkIdentity) {
equals(function() { equals(function() {
return item !== item2; return item !== item2;
@ -232,7 +248,7 @@ function compareItems(item, item2, cloned, checkIdentity, dontShareProject) {
var value = item[key]; var value = item[key];
// When item was cloned and had a name, the name will be versioned // When item was cloned and had a name, the name will be versioned
equals( equals(
key == 'name' && cloned && value key == 'name' && options && options.cloned && value
? value + ' 1' ? value + ' 1'
: value, : value,
item2[key], item2[key],
@ -279,7 +295,7 @@ function compareItems(item, item2, cloned, checkIdentity, dontShareProject) {
equals(item[key], item2[key], 'Compare Path#' + key); equals(item[key], item2[key], 'Compare Path#' + key);
} }
equals(item.length, item2.length, 'Compare Path#length'); equals(item.length, item2.length, 'Compare Path#length');
compareSegmentLists(item.segments, item2.segments, checkIdentity); compareSegmentLists(item.segments, item2.segments, options);
} }
// Shape specific // Shape specific
@ -301,7 +317,7 @@ function compareItems(item, item2, cloned, checkIdentity, dontShareProject) {
// Layer specific // Layer specific
if (item instanceof Layer) { if (item instanceof Layer) {
equals(function() { equals(function() {
return dontShareProject return options && options.dontShareProject
? item.project != item2.project ? item.project != item2.project
: item.project == item2.project; : item.project == item2.project;
}, true); }, true);
@ -309,9 +325,9 @@ function compareItems(item, item2, cloned, checkIdentity, dontShareProject) {
// PlacedSymbol specific // PlacedSymbol specific
if (item instanceof PlacedSymbol) { if (item instanceof PlacedSymbol) {
if (dontShareProject) { if (options.dontShareProject) {
compareItems(item.symbol.definition, item2.symbol.definition, compareItems(item.symbol.definition, item2.symbol.definition,
cloned, checkIdentity, dontShareProject, options,
'Compare Symbol#definition'); 'Compare Symbol#definition');
} else { } else {
equals(function() { equals(function() {
@ -331,7 +347,7 @@ function compareItems(item, item2, cloned, checkIdentity, dontShareProject) {
'Compare Raster#ppi'); 'Compare Raster#ppi');
equals(item.source, item2.source, 'Compare Raster#source'); equals(item.source, item2.source, 'Compare Raster#source');
if (checkIdentity) { if (options.checkIdentity) {
equals(item.image, item2.image, 'Compare Raster#image'); equals(item.image, item2.image, 'Compare Raster#image');
} }
equals(item.size.toString(), item2.size.toString(), equals(item.size.toString(), item2.size.toString(),
@ -347,7 +363,7 @@ function compareItems(item, item2, cloned, checkIdentity, dontShareProject) {
// PointText specific: // PointText specific:
if (item instanceof PointText) { if (item instanceof PointText) {
if (checkIdentity) { if (options.checkIdentity) {
equals(function() { equals(function() {
return item.point !== item2.point; return item.point !== item2.point;
}, true); }, true);
@ -358,7 +374,7 @@ function compareItems(item, item2, cloned, checkIdentity, dontShareProject) {
if (item.style) { if (item.style) {
// Style // Style
compareStyles(item.style, item2.style, checkIdentity); compareStyles(item.style, item2.style, options);
} }
// Check length of children and recursively compare them: // Check length of children and recursively compare them:
@ -367,8 +383,7 @@ function compareItems(item, item2, cloned, checkIdentity, dontShareProject) {
return item.children.length == item2.children.length; return item.children.length == item2.children.length;
}, true); }, true);
for (var i = 0, l = item.children.length; i < l; i++) { for (var i = 0, l = item.children.length; i < l; i++) {
compareItems(item.children[i], item2.children[i], cloned, compareItems(item.children[i], item2.children[i], options);
checkIdentity, dontShareProject);
} }
} }
} }
@ -381,7 +396,7 @@ function compareProjects(project, project2) {
for (var i = 0, l = project.symbols.length; i < l; i++) { for (var i = 0, l = project.symbols.length; i < l; i++) {
var definition1 = project.symbols[i].definition; var definition1 = project.symbols[i].definition;
var definition2 = project2.symbols[i].definition; var definition2 = project2.symbols[i].definition;
compareItems(definition1, definition2, false, false, true, compareItems(definition1, definition2, { dontShareProject: true },
'Compare Symbol#definition'); 'Compare Symbol#definition');
} }
@ -390,7 +405,8 @@ function compareProjects(project, project2) {
return project.layers.length == project2.layers.length; return project.layers.length == project2.layers.length;
}, true); }, true);
for (var i = 0, l = project.layers.length; i < l; i++) { for (var i = 0, l = project.layers.length; i < l; i++) {
compareItems(project.layers[i], project2.layers[i], false, false, true); compareItems(project.layers[i], project2.layers[i],
{ dontShareProject: true });
} }
} }

View file

@ -71,5 +71,5 @@ test('text.bounds', function() {
var text = new PointText(new Point(50, 100)); var text = new PointText(new Point(50, 100));
text.fillColor = 'black'; text.fillColor = 'black';
text.content = 'This is a test'; text.content = 'This is a test';
compareRectangles(text.bounds, { x: 50, y: 89.2, width: 67, height: 14.4 } , 'text.bounds', 0.5); compareRectangles(text.bounds, { x: 50, y: 89.2, width: 67, height: 14.4 } , 'text.bounds', { tolerance: 0.5 });
}); });

View file

@ -24,7 +24,7 @@ function cloneAndCompare(item) {
return copy.parent.children[copy.name] == copy; return copy.parent.children[copy.name] == copy;
}, true); }, true);
} }
compareItems(item, copy, true, true); compareItems(item, copy, { cloned: true, checkIdentity: true });
// Remove the cloned item to restore the document: // Remove the cloned item to restore the document:
copy.remove(); copy.remove();
} }

View file

@ -64,30 +64,30 @@ test('Item#insertAbove(item) / Item#insertBelow(item)', function() {
item1 = new Group(); item1 = new Group();
item2 = new Group(); item2 = new Group();
command(); command();
var str = getFunctionBody(command); var str = getFunctionMessage(command);
equals(item0.index, indexes[0], str + ': item0.index'); equals(item0.index, indexes[0], str + ': item0.index');
equals(item1.index, indexes[1], str + ': item1.index'); equals(item1.index, indexes[1], str + ': item1.index');
equals(item2.index, indexes[2], str + ': item2.index'); equals(item2.index, indexes[2], str + ': item2.index');
} }
testMove(function() { item0.insertBelow(item0) }, [0,1,2]); testMove(function() { item0.insertBelow(item0); }, [0,1,2]);
testMove(function() { item0.insertBelow(item1) }, [0,1,2]); testMove(function() { item0.insertBelow(item1); }, [0,1,2]);
testMove(function() { item0.insertBelow(item2) }, [1,0,2]); testMove(function() { item0.insertBelow(item2); }, [1,0,2]);
testMove(function() { item1.insertBelow(item0) }, [1,0,2]); testMove(function() { item1.insertBelow(item0); }, [1,0,2]);
testMove(function() { item1.insertBelow(item1) }, [0,1,2]); testMove(function() { item1.insertBelow(item1); }, [0,1,2]);
testMove(function() { item1.insertBelow(item2) }, [0,1,2]); testMove(function() { item1.insertBelow(item2); }, [0,1,2]);
testMove(function() { item2.insertBelow(item0) }, [1,2,0]); testMove(function() { item2.insertBelow(item0); }, [1,2,0]);
testMove(function() { item2.insertBelow(item1) }, [0,2,1]); testMove(function() { item2.insertBelow(item1); }, [0,2,1]);
testMove(function() { item2.insertBelow(item2) }, [0,1,2]); testMove(function() { item2.insertBelow(item2); }, [0,1,2]);
testMove(function() { item0.insertAbove(item0) }, [0,1,2]); testMove(function() { item0.insertAbove(item0); }, [0,1,2]);
testMove(function() { item0.insertAbove(item1) }, [1,0,2]); testMove(function() { item0.insertAbove(item1); }, [1,0,2]);
testMove(function() { item0.insertAbove(item2) }, [2,0,1]); testMove(function() { item0.insertAbove(item2); }, [2,0,1]);
testMove(function() { item1.insertAbove(item0) }, [0,1,2]); testMove(function() { item1.insertAbove(item0); }, [0,1,2]);
testMove(function() { item1.insertAbove(item1) }, [0,1,2]); testMove(function() { item1.insertAbove(item1); }, [0,1,2]);
testMove(function() { item1.insertAbove(item2) }, [0,2,1]); testMove(function() { item1.insertAbove(item2); }, [0,2,1]);
testMove(function() { item2.insertAbove(item0) }, [0,2,1]); testMove(function() { item2.insertAbove(item0); }, [0,2,1]);
testMove(function() { item2.insertAbove(item1) }, [0,1,2]); testMove(function() { item2.insertAbove(item1); }, [0,1,2]);
testMove(function() { item2.insertAbove(item2) }, [0,1,2]); testMove(function() { item2.insertAbove(item2); }, [0,1,2]);
}); });

View file

@ -131,7 +131,7 @@ test('Raster#getAverageColor(path)', function() {
}); });
var raster = paper.project.activeLayer.rasterize(72); var raster = paper.project.activeLayer.rasterize(72);
circle.scale(0.9); circle.scale(0.9);
compareColors(raster.getAverageColor(circle), circle.fillColor, null, 3); compareColors(raster.getAverageColor(circle), circle.fillColor);
}); });
test('Raster#getAverageColor(path) with compound path', function() { test('Raster#getAverageColor(path) with compound path', function() {
@ -153,5 +153,5 @@ test('Raster#getAverageColor(path) with compound path', function() {
var raster = paper.project.activeLayer.rasterize(72); var raster = paper.project.activeLayer.rasterize(72);
path.scale(0.9); path.scale(0.9);
path2.scale(1.1); path2.scale(1.1);
compareColors(raster.getAverageColor(compoundPath), new Color(1, 0, 0), null, 3); compareColors(raster.getAverageColor(compoundPath), new Color(1, 0, 0), null, { tolerance: 10e-4 });
}); });

View file

@ -15,7 +15,7 @@ module('SVGImport');
test('Import complex CompoundPath and clone', function() { test('Import complex CompoundPath and clone', function() {
var svg = createSVG('<path id="path" fill="red" d="M4,14h20v-2H4V14z M15,26h7v-2h-7V26z M15,22h9v-2h-9V22z M15,18h9v-2h-9V18z M4,26h9V16H4V26z M28,10V6H0v22c0,0,0,4,4,4 h25c0,0,3-0.062,3-4V10H28z M4,30c-2,0-2-2-2-2V8h24v20c0,0.921,0.284,1.558,0.676,2H4z"/>;'); var svg = createSVG('<path id="path" fill="red" d="M4,14h20v-2H4V14z M15,26h7v-2h-7V26z M15,22h9v-2h-9V22z M15,18h9v-2h-9V18z M4,26h9V16H4V26z M28,10V6H0v22c0,0,0,4,4,4 h25c0,0,3-0.062,3-4V10H28z M4,30c-2,0-2-2-2-2V8h24v20c0,0.921,0.284,1.558,0.676,2H4z"/>;');
var item = paper.project.importSVG(svg.getElementById('path')); var item = paper.project.importSVG(svg.getElementById('path'));
compareItems(item, item.clone(), true, true); compareItems(item, item.clone(), { cloned: true, checkIdentity: true });
}); });
test('make an svg line', function() { test('make an svg line', function() {

View file

@ -22,7 +22,7 @@ test('PointText', function() {
compareColors(text.fillColor, new Color(0, 0, 0), 'text.fillColor should be black by default'); compareColors(text.fillColor, new Color(0, 0, 0), 'text.fillColor should be black by default');
comparePoints(text.point, { x: 100, y: 100 }, 'text.point'); comparePoints(text.point, { x: 100, y: 100 }, 'text.point');
comparePoints(text.bounds.point, { x: 100, y: 87.4 }, 'text.bounds.point'); comparePoints(text.bounds.point, { x: 100, y: 87.4 }, 'text.bounds.point');
compareSize(text.bounds.size, { width: 77, height: 16.8 }, 'text.bounds.size', 1.0); compareSize(text.bounds.size, { width: 77, height: 16.8 }, 'text.bounds.size', { tolerance: 1.0 });
equals(function() { equals(function() {
return text.hitTest(text.bounds.center) != null; return text.hitTest(text.bounds.center) != null;
}, true); }, true);