Browse Source

Merge pull request #61 from RiveraGroup/feature/mongo_2.6.5_expressions_SetUnion

Feature/mongo 2.6.5 expressions set union
Kyle P Davis 11 years ago
parent
commit
df1501df0c

+ 0 - 25
lib/pipeline/expressions/Helpers.js

@@ -1,25 +0,0 @@
-var Helpers = module.exports = { //jshint ignore:line
-
-	arrayToSet: function arrayToSet(val) { //NOTE: DEVIATION FROM MONGO: we return an Object of JSON-String to Values
-		if (!(val instanceof Array)) throw new Error("Assertion failure");
-		var array = val;
-
-		var set = {};
-		for (var i = 0, l = array.length; i < l; i++) {
-			var item = array[i],
-				itemHash = JSON.stringify(array[i]);
-			set[itemHash] = item;
-		}
-
-		return set;
-	},
-
-	setToArray: function setToArray(set) {	//TODO: used??
-		var array = [];
-		for (var key in set) {
-			array.push(set[key]);
-		}
-		return array;
-	},
-
-};

+ 4 - 6
lib/pipeline/expressions/SetDifferenceExpression.js

@@ -14,7 +14,7 @@ var SetDifferenceExpression = module.exports = function SetDifferenceExpression(
 
 
 var Value = require("../Value"),
 var Value = require("../Value"),
 	Expression = require("./Expression"),
 	Expression = require("./Expression"),
-	Helpers = require("./Helpers");
+	ValueSet = require("./ValueSet");
 
 
 proto.evaluateInternal = function evaluateInternal(vars) {
 proto.evaluateInternal = function evaluateInternal(vars) {
 	var lhs = this.operands[0].evaluateInternal(vars),
 	var lhs = this.operands[0].evaluateInternal(vars),
@@ -31,16 +31,14 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 		throw new Error("both operands of " + this.getOpName() + " must be arrays. Second " +
 		throw new Error("both operands of " + this.getOpName() + " must be arrays. Second " +
 			"argument is of type: " + Value.getType(rhs) + "; uassert code 17049");
 			"argument is of type: " + Value.getType(rhs) + "; uassert code 17049");
 
 
-	var rhsSet = Helpers.arrayToSet(rhs),
+	var rhsSet = new ValueSet(rhs),
 		lhsArray = lhs,
 		lhsArray = lhs,
 		returnVec = [];
 		returnVec = [];
 	for (var i = 0, l = lhsArray.length; i < l; ++i) {
 	for (var i = 0, l = lhsArray.length; i < l; ++i) {
 		// rhsSet serves the dual role of filtering out elements that were originally present
 		// rhsSet serves the dual role of filtering out elements that were originally present
 		// in RHS and of eleminating duplicates from LHS
 		// in RHS and of eleminating duplicates from LHS
-		var it = lhsArray[i],
-			itHash = JSON.stringify(it);
-		if (!(itHash in rhsSet)) {
-			rhsSet[itHash] = it;
+		var it = lhsArray[i];
+		if (rhsSet.insert(it) !== undefined) {
 			returnVec.push(it);
 			returnVec.push(it);
 		}
 		}
 	}
 	}

+ 4 - 4
lib/pipeline/expressions/SetEqualsExpression.js

@@ -14,7 +14,7 @@ var SetEqualsExpression = module.exports = function SetEqualsExpression() {
 
 
 var Value = require("../Value"),
 var Value = require("../Value"),
 	Expression = require("./Expression"),
 	Expression = require("./Expression"),
-	Helpers = require("./Helpers");
+	ValueSet = require("./ValueSet");
 
 
 proto.validateArguments = function validateArguments(args) {
 proto.validateArguments = function validateArguments(args) {
 	if (args.length < 2)
 	if (args.length < 2)
@@ -33,10 +33,10 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 				"argument is of type: " + Value.getType(nextEntry) + "; uassert code 17044");
 				"argument is of type: " + Value.getType(nextEntry) + "; uassert code 17044");
 
 
 		if (i === 0) {
 		if (i === 0) {
-			lhs = Helpers.arrayToSet(nextEntry);
+			lhs = new ValueSet(nextEntry);
 		} else {
 		} else {
-			var rhs = Helpers.arrayToSet(nextEntry);
-			if (Object.keys(lhs).join() !== Object.keys(rhs).join()) {
+			var rhs = new ValueSet(nextEntry);
+			if (!lhs.equals(rhs)) {
 				return false;
 				return false;
 			}
 			}
 		}
 		}

+ 6 - 6
lib/pipeline/expressions/SetIsSubsetExpression.js

@@ -17,18 +17,18 @@ var Value = require("../Value"),
 	Expression = require("./Expression"),
 	Expression = require("./Expression"),
 	NaryExpression = require("./NaryExpression"),
 	NaryExpression = require("./NaryExpression"),
 	ConstantExpression = require("./ConstantExpression"),
 	ConstantExpression = require("./ConstantExpression"),
-	Helpers = require("./Helpers");
+	ValueSet = require("./ValueSet");
 
 
-var setIsSubsetHelper = function setIsSubsetHelper(lhs, rhs) { //NOTE: vector<Value> &lhs, ValueSet &rhs
+function setIsSubsetHelper(lhs, rhs) { //NOTE: vector<Value> &lhs, ValueSet &rhs
 	// do not shortcircuit when lhs.size() > rhs.size()
 	// do not shortcircuit when lhs.size() > rhs.size()
 	// because lhs can have redundant entries
 	// because lhs can have redundant entries
 	for (var i = 0; i < lhs.length; i++) {
 	for (var i = 0; i < lhs.length; i++) {
-		if (!(JSON.stringify(lhs[i]) in rhs)) {
+		if (!rhs.has(lhs[i])) {
 			return false;
 			return false;
 		}
 		}
 	}
 	}
 	return true;
 	return true;
-};
+}
 
 
 proto.evaluateInternal = function evaluateInternal(vars) {
 proto.evaluateInternal = function evaluateInternal(vars) {
 	var lhs = this.operands[0].evaluateInternal(vars),
 	var lhs = this.operands[0].evaluateInternal(vars),
@@ -41,7 +41,7 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 		throw new Error("both operands of " + this.getOpName() + ": must be arrays. Second " +
 		throw new Error("both operands of " + this.getOpName() + ": must be arrays. Second " +
 			"argument is of type " + Value.getType(rhs) + "; code 17042");
 			"argument is of type " + Value.getType(rhs) + "; code 17042");
 
 
-	return setIsSubsetHelper(lhs, Helpers.arrayToSet(rhs));
+	return setIsSubsetHelper(lhs, new ValueSet(rhs));
 };
 };
 
 
 
 
@@ -83,7 +83,7 @@ proto.optimize = function optimize(cachedRhsSet, operands) { //jshint ignore:lin
 			throw new Error("both operands of " + this.getOpName() + " must be arrays. Second " +
 			throw new Error("both operands of " + this.getOpName() + " must be arrays. Second " +
 				"argument is of type " + Value.getType(rhs) + "; uassert code 17311");
 				"argument is of type " + Value.getType(rhs) + "; uassert code 17311");
 
 
-		return new Optimized(Helpers.arrayToSet(rhs), this.operands);
+		return new Optimized(new ValueSet(rhs), this.operands);
 	}
 	}
 
 
 	return optimized;
 	return optimized;

+ 25 - 27
lib/pipeline/expressions/SetUnionExpression.js

@@ -2,45 +2,43 @@
 
 
 /**
 /**
  * A $setunion pipeline expression.
  * A $setunion pipeline expression.
- * @see evaluateInternal
  * @class SetUnionExpression
  * @class SetUnionExpression
  * @namespace mungedb-aggregate.pipeline.expressions
  * @namespace mungedb-aggregate.pipeline.expressions
  * @module mungedb-aggregate
  * @module mungedb-aggregate
  * @constructor
  * @constructor
- **/
+ */
 var SetUnionExpression = module.exports = function SetUnionExpression() {
 var SetUnionExpression = module.exports = function SetUnionExpression() {
+	if (arguments.length !== 0) throw new Error(klass.name + ": no args expected");
 	base.call(this);
 	base.call(this);
-}, klass = SetUnionExpression, base = require("./NaryBaseExpressionT")(SetUnionExpression), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
+}, klass = SetUnionExpression, base = require("./VariadicExpressionT")(SetUnionExpression), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
 
-// DEPENDENCIES
 var Value = require("../Value"),
 var Value = require("../Value"),
-	Expression = require("./Expression");
+	Expression = require("./Expression"),
+	ValueSet = require("./ValueSet");
 
 
-// PROTOTYPE MEMBERS
-proto.getOpName = function getOpName() {
-	return "$setUnion";
-};
-
-/**
- * Takes 2 objects. Unions the objects
- * @method evaluateInternal
- **/
 proto.evaluateInternal = function evaluateInternal(vars) {
 proto.evaluateInternal = function evaluateInternal(vars) {
-	var object1 = this.operands[0].evaluateInternal(vars),
-		object2 = this.operands[1].evaluateInternal(vars);
-	if (object1 instanceof Array) throw new Error(this.getOpName() + ": object 1 must be an object");
-	if (object2 instanceof Array) throw new Error(this.getOpName() + ": object 2 must be an object");
+	var unionedSet = new ValueSet(),
+		n = this.operands.length;
+	for (var i = 0; i < n; i++){
+		var newEntries = this.operands[i].evaluateInternal(vars);
+		if (newEntries === undefined || newEntries === null){
+			return null;
+		}
+		if (!(newEntries instanceof Array))
+			throw new Error("All operands of " + this.getOpName() + "must be arrays. One argument" +
+				" is of type: " + Value.getType(newEntries) + "; uassert code 17043");
 
 
-	var object3 = {};
-	for (var attrname1 in object1) {
-		object3[attrname1] = object1[attrname1];
-	}
-	for (var attrname2 in object2) {
-		object3[attrname2] = object2[attrname2];
+		unionedSet.insertRange(newEntries);
 	}
 	}
-
-	return object3;
+	return unionedSet.values();
 };
 };
 
 
-/** Register Expression */
 Expression.registerExpression("$setUnion", base.parse);
 Expression.registerExpression("$setUnion", base.parse);
+
+proto.getOpName = function getOpName() {
+	return "$setUnion";
+};
+
+proto.isAssociativeAndCommutative = function isAssociativeAndCommutative() {
+	return true;
+};

+ 50 - 0
lib/pipeline/expressions/ValueSet.js

@@ -0,0 +1,50 @@
+var ValueSet = module.exports = function ValueSet(vals) {
+	this.set = {};
+	if (vals instanceof Array)
+		this.insertRange(vals);
+}, klass = ValueSet, proto = klass.prototype;
+
+proto._getKey = JSON.stringify;
+
+proto.hasKey = function hasKey(key) {
+	return key in this.set;
+};
+
+proto.has = function has(val) {
+	return this._getKey(val) in this.set;
+};
+
+proto.insert = function insert(val) {
+	var valKey = this._getKey(val);
+	if (!this.hasKey(valKey)) {
+		this.set[valKey] = val;
+		return valKey;
+	}
+	return undefined;
+};
+
+proto.insertRange = function insertRange(vals) {
+	var results = [];
+	for (var i = 0, l = vals.length; i < l; i++)
+		results.push(this.insert(vals[i]));
+	return results;
+};
+
+proto.equals = function equals(other) {
+	for (var key in this.set) {
+		if (!other.hasKey(key))
+			return false;
+	}
+	for (var otherKey in other.set) {
+		if (!this.hasKey(otherKey))
+			return false;
+	}
+	return true;
+};
+
+proto.values = function values() {
+	var vals = [];
+	for (var key in this.set)
+		vals.push(this.set[key]);
+	return vals;
+};

+ 2 - 2
test/lib/pipeline/expressions/SetDifferenceExpression.js

@@ -20,7 +20,7 @@ exports.SetDifferenceExpression = {
 			assert.throws(function() {
 			assert.throws(function() {
 				new SetDifferenceExpression("someArg");
 				new SetDifferenceExpression("someArg");
 			});
 			});
-		}
+		},
 
 
 	},
 	},
 
 
@@ -28,7 +28,7 @@ exports.SetDifferenceExpression = {
 
 
 		"should return the correct op name; $setDifference": function() {
 		"should return the correct op name; $setDifference": function() {
 			assert.equal(new SetDifferenceExpression().getOpName(), "$setDifference");
 			assert.equal(new SetDifferenceExpression().getOpName(), "$setDifference");
-		}
+		},
 
 
 	},
 	},
 
 

+ 2 - 2
test/lib/pipeline/expressions/SetEqualsExpression.js

@@ -20,7 +20,7 @@ exports.SetEqualsExpression = {
 			assert.throws(function() {
 			assert.throws(function() {
 				new SetEqualsExpression("someArg");
 				new SetEqualsExpression("someArg");
 			});
 			});
-		}
+		},
 
 
 	},
 	},
 
 
@@ -28,7 +28,7 @@ exports.SetEqualsExpression = {
 
 
 		"should return the correct op name; $setEquals": function() {
 		"should return the correct op name; $setEquals": function() {
 			assert.equal(new SetEqualsExpression().getOpName(), "$setEquals");
 			assert.equal(new SetEqualsExpression().getOpName(), "$setEquals");
-		}
+		},
 
 
 	},
 	},
 
 

+ 1 - 1
test/lib/pipeline/expressions/SetExpectedResultBase.js

@@ -33,7 +33,7 @@ var ExpectedResultBase = module.exports = (function() { //jshint ignore:line
 				}
 				}
 				var errMsg = "for expression " + fieldFirst +
 				var errMsg = "for expression " + fieldFirst +
 					" with argument " + JSON.stringify(args) +
 					" with argument " + JSON.stringify(args) +
-					" full tree " + JSON.stringify(expr.serialize(false)) +
+					" full tree: " + JSON.stringify(expr.serialize(false)) +
 					" expected: " + JSON.stringify(expected) +
 					" expected: " + JSON.stringify(expected) +
 					" but got: " + JSON.stringify(result);
 					" but got: " + JSON.stringify(result);
 				assert.deepEqual(result, expected, errMsg);
 				assert.deepEqual(result, expected, errMsg);

+ 2 - 2
test/lib/pipeline/expressions/SetIsSubsetExpression.js

@@ -20,7 +20,7 @@ exports.SetIsSubsetExpression = {
 			assert.throws(function() {
 			assert.throws(function() {
 				new SetIsSubsetExpression("someArg");
 				new SetIsSubsetExpression("someArg");
 			});
 			});
-		}
+		},
 
 
 	},
 	},
 
 
@@ -28,7 +28,7 @@ exports.SetIsSubsetExpression = {
 
 
 		"should return the correct op name; $setIsSubset": function() {
 		"should return the correct op name; $setIsSubset": function() {
 			assert.equal(new SetIsSubsetExpression().getOpName(), "$setIsSubset");
 			assert.equal(new SetIsSubsetExpression().getOpName(), "$setIsSubset");
-		}
+		},
 
 
 	},
 	},
 
 

+ 319 - 113
test/lib/pipeline/expressions/SetUnionExpression.js

@@ -1,119 +1,325 @@
 "use strict";
 "use strict";
 var assert = require("assert"),
 var assert = require("assert"),
-		SetUnionExpression = require("../../../../lib/pipeline/expressions/SetUnionExpression"),
-		Expression = require("../../../../lib/pipeline/expressions/Expression");
-
-
-module.exports = {
-
-		"SetUnionExpression": {
-
-				"constructor()": {
-
-						"should throw Error when constructing without args": function testConstructor() {
-								assert.throws(function() {
-										new SetUnionExpression();
-								});
-						}
-
-				},
-
-				"#getOpName()": {
-
-						"should return the correct op name; $setunion": function testOpName() {
-								assert.equal(new SetUnionExpression([1, 2, 3], [4, 5, 6]).getOpName(), "$setunion");
-						}
-
-				},
-
-				"#evaluateInternal()": {
-
-						"Should fail if array1 is not an array": function testArg1() {
-								var array1 = "not an array",
-										array2 = [6, 7, 8, 9];
-								assert.throws(function() {
-										Expression.parseOperand({
-												$setunion: ["$array1", "$array2"]
-										}).evaluateInternal({
-												array1: array1,
-												array2: array2
-										});
-								});
-						},
-
-						"Should fail if array2 is not an array": function testArg2() {
-								var array1 = [1, 2, 3, 4],
-										array2 = "not an array";
-								assert.throws(function() {
-										Expression.parseOperand({
-												$setunion: ["$array1", "$array2"]
-										}).evaluateInternal({
-												array1: array1,
-												array2: array2
-										});
-								});
-						},
-
-						"Should fail if both are not an array": function testArg1andArg2() {
-								var array1 = "not an array",
-										array2 = "not an array";
-								assert.throws(function() {
-										Expression.parseOperand({
-												$setunion: ["$array1", "$array2"]
-										}).evaluateInternal({
-												array1: array1,
-												array2: array2
-										});
-								});
-						},
-
-						"Should pass and return a unioned set1": function testBasicAssignment() {
-								var array1 = {
-										"a": "3",
-										"c": "4"
-								},
-										array2 = {
-												"a": "3",
-												"b": "3"
-										};
-								assert.strictEqual(Expression.parseOperand({
-										$setunion: ["$array1", "$array2"]
-								}).evaluateInternal({
-										array1: array1,
-										array2: array2
-								}), {
-										"a": "3",
-										"c": "4",
-										"b": "3"
-								});
-						},
-
-						"Should pass and return a unioned set": function testBasicAssignment() {
-								var array1 = [1, 2, 3, 4, 5],
-										array2 = [2, 3, 6, 7, 8];
-								assert.strictEqual(Expression.parseOperand({
-										$setunion: ["$array1", "$array2"]
-								}).evaluateInternal({
-										array1: array1,
-										array2: array2
-								}), [1, 2, 3, 4, 5, 6, 7, 8]);
-						},
-
-						"Should pass and return unioned set": function testBasicAssignment() {
-								var array1 = [1, 2, 3, 4, 5],
-										array2 = [7, 8, 9];
-								assert.strictEqual(Expression.parseOperand({
-										$setunion: ["$array1", "$array2"]
-								}).evaluateInternal({
-										array1: array1,
-										array2: array2
-								}), [1, 2, 3, 4, 5, 7, 8, 9]);
-						},
-
-				}
+	SetUnionExpression = require("../../../../lib/pipeline/expressions/SetUnionExpression"),
+	ExpectedResultBase = require("./SetExpectedResultBase");
 
 
+// Mocha one-liner to make these tests self-hosted
+if(!module.parent)return(require.cache[__filename]=null,(new(require("mocha"))({ui:"exports",reporter:"spec",grep:process.env.TEST_GREP})).addFile(__filename).run(process.exit));
+
+exports.SetUnionExpression = {
+
+	"constructor()": {
+
+		"should not throw Error when constructing without args": function() {
+			assert.doesNotThrow(function() {
+				new SetUnionExpression();
+			});
+		},
+
+		"should throw Error when constructing with args": function() {
+			assert.throws(function() {
+				new SetUnionExpression("someArg");
+			});
+		},
+
+	},
+
+	"#getOpName()": {
+
+		"should return the correct op name; $setUnion": function() {
+			assert.equal(new SetUnionExpression().getOpName(), "$setUnion");
 		}
 		}
 
 
-};
+	},
+
+	"#evaluate()": {
+
+		"should handle when sets are the same": function Same(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 2], [1, 2]],
+					expected: {
+						$setIsSubset: true,
+						// $setEquals: true,
+						// $setIntersection: [1, 2],
+						// $setUnion: [1, 2],
+						// $setDifference: [],
+					},
+				},
+			}).run();
+		},
+
+		"should handle when the 2nd set has redundant items": function Redundant(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 2], [1, 2, 2]],
+					expected: {
+						$setIsSubset: true,
+						// $setEquals: true,
+						// $setIntersection: [1, 2],
+						// $setUnion: [1, 2],
+						// $setDifference: [],
+					},
+				},
+			}).run();
+		},
+
+		"should handle when the both sets have redundant items": function DoubleRedundant(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 1, 2], [1, 2, 2]],
+					expected: {
+						$setIsSubset: true,
+						// $setEquals: true,
+						// $setIntersection: [1, 2],
+						// $setUnion: [1, 2],
+						// $setDifference: [],
+					},
+				},
+			}).run();
+		},
+
+		"should handle when the 1st set is a superset": function Super(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 2], [1]],
+					expected: {
+						$setIsSubset: false,
+						// $setEquals: false,
+						// $setIntersection: [1],
+						// $setUnion: [1, 2],
+						// $setDifference: [2],
+					},
+				},
+			}).run();
+		},
+
+		"should handle when the 2nd set is a superset and has redundant items": function SuperWithRedundant(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 2, 2], [1]],
+					expected: {
+						$setIsSubset: false,
+						// $setEquals: false,
+						// $setIntersection: [1],
+						// $setUnion: [1, 2],
+						// $setDifference: [2],
+					},
+				},
+			}).run();
+		},
+
+		"should handle when the 1st set is a subset": function Sub(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1], [1, 2]],
+					expected: {
+						$setIsSubset: true,
+						// $setEquals: false,
+						// $setIntersection: [1],
+						// $setUnion: [1, 2],
+						// $setDifference: [],
+					},
+				},
+			}).run();
+		},
 
 
-if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);
+		"should handle when the sets are the same but backwards": function SameBackwards(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 2], [2, 1]],
+					expected: {
+						$setIsSubset: true,
+						// $setEquals: true,
+						// $setIntersection: [1, 2],
+						// $setUnion: [1, 2],
+						// $setDifference: [],
+					},
+				},
+			}).run();
+		},
+
+		"should handle when the sets do not overlap": function NoOverlap(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 2], [8, 4]],
+					expected: {
+						$setIsSubset: false,
+						// $setEquals: false,
+						// $setIntersection: [],
+						// $setUnion: [1, 2, 4, 8],
+						// $setDifference: [1, 2],
+					},
+				},
+			}).run();
+		},
+
+		"should handle when the sets do overlap": function Overlap(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 2], [8, 2, 4]],
+					expected: {
+						$setIsSubset: false,
+						// $setEquals: false,
+						// $setIntersection: [2],
+						// $setUnion: [1, 2, 4, 8],
+						// $setDifference: [1],
+					},
+				},
+			}).run();
+		},
+
+		"should handle when the 2nd set is null": function LastNull(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 2], null],
+					expected: {
+						// $setIntersection: null,
+						// $setUnion: null,
+						// $setDifference: null,
+					},
+					error: [
+						// "$setEquals"
+						"$setIsSubset"
+					],
+				},
+			}).run();
+		},
+
+		"should handle when the 1st set is null": function FirstNull(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [null, [1, 2]],
+					expected: {
+						// $setIntersection: null,
+						// $setUnion: null,
+						// $setDifference: null,
+					},
+					error: [
+						// "$setEquals"
+						"$setIsSubset"
+					],
+				},
+			}).run();
+		},
+
+		"should handle when the input has no args": function NoArg(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [],
+					expected: {
+						// $setIntersection: [],
+						// $setUnion: [],
+					},
+					error: [
+						// "$setEquals"
+						"$setIsSubset"
+						// "$setDifference"
+					],
+				},
+			}).run();
+		},
+
+		"should handle when the input has one arg": function OneArg(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 2]],
+					expected: {
+						// $setIntersection: [1, 2],
+						// $setUnion: [1, 2],
+					},
+					error: [
+						// "$setEquals"
+						"$setIsSubset"
+						// "$setDifference"
+					],
+				},
+			}).run();
+		},
+
+		"should handle when the input has empty arg": function EmptyArg(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 2]],
+					expected: {
+						// $setIntersection: [1, 2],
+						// $setUnion: [1, 2],
+					},
+					error: [
+						// "$setEquals"
+						"$setIsSubset"
+						// "$setDifference"
+					],
+				},
+			}).run();
+		},
+
+		"should handle when the input has empty left arg": function LeftArgEmpty(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[]],
+					expected: {
+						// $setIntersection: [],
+						// $setUnion: [],
+					},
+					error: [
+						// "$setEquals"
+						"$setIsSubset"
+						// "$setDifference"
+					],
+				},
+			}).run();
+		},
+
+		"should handle when the input has empty right arg": function RightArgEmpty(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 2], []],
+					expected: {
+						// $setIntersection: [],
+						// $setUnion: [1, 2],
+						$setIsSubset: false,
+						// $setEquals: false,
+						// $setDifference: [1, 2],
+					},
+				},
+			}).run();
+		},
+
+		"should handle when the input has many args": function ManyArgs(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[8, 3], ["asdf", "foo"], [80.3, 34], [], [80.3, "foo", 11, "yay"]],
+					expected: {
+						// $setIntersection: [],
+						// $setEquals: false,
+						// $setUnion: [3, 8, 11, 34, 80.3, "asdf", "foo", "yay"],
+					},
+					error: [
+						"$setIsSubset",
+						// "$setDifference",
+					],
+				},
+			}).run();
+		},
+
+		"should handle when the input has many args that are equal sets": function ManyArgsEqual(){
+			new ExpectedResultBase({
+				getSpec: {
+					input: [[1, 2, 4], [1, 2, 2, 4], [4, 1, 2], [2, 1, 1, 4]],
+					expected: {
+						// $setIntersection: [1, 2, 4],
+						// $setEquals: true,
+						// $setUnion: [1, 2, 4],
+					},
+					error: [
+						"$setIsSubset",
+						// "$setDifference",
+					],
+				},
+			}).run();
+		},
+
+	},
+
+};