michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: let { seq, iterate, filter, map, reductions, reduce, count, michael@0: isEmpty, every, isEvery, some, take, takeWhile, drop, michael@0: dropWhile, concat, first, rest, nth, last, dropLast, michael@0: distinct, remove, mapcat, fromEnumerator, string, michael@0: object, pairs, keys, values, each michael@0: } = require("sdk/util/sequence"); michael@0: michael@0: const boom = () => { throw new Error("Boom!"); }; michael@0: const broken = seq(function*() { michael@0: yield 1; michael@0: throw new Error("Boom!"); michael@0: }); michael@0: michael@0: exports["test seq"] = assert => { michael@0: let xs = seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: }); michael@0: michael@0: assert.deepEqual([...seq(null)], [], "seq of null is empty"); michael@0: assert.deepEqual([...seq(void(0))], [], "seq of void is empty"); michael@0: assert.deepEqual([...xs], [1, 2, 3], "seq of 1 2 3"); michael@0: assert.deepEqual([...seq(xs)], [1, 2, 3], "seq of seq is seq"); michael@0: michael@0: assert.deepEqual([...seq([])], [], "seq of emtpy array is empty"); michael@0: assert.deepEqual([...seq([1])], [1], "seq of lonly array is single element"); michael@0: assert.deepEqual([...seq([1, 2, 3])], [1, 2, 3], "seq of array is it's elements"); michael@0: michael@0: assert.deepEqual([...seq("")], [], "seq of emtpy string is empty"); michael@0: assert.deepEqual([...seq("o")], ["o"], "seq of char is single char seq"); michael@0: assert.deepEqual([...seq("hello")], ["h", "e", "l", "l", "o"], michael@0: "seq of string are chars"); michael@0: michael@0: assert.deepEqual([...seq(new Set())], [], "seq of emtpy set is empty"); michael@0: assert.deepEqual([...seq(new Set([1]))], [1], "seq of lonely set is single"); michael@0: assert.deepEqual([...seq(new Set([1, 2, 3]))], [1, 2, 3], "seq of lonely set is single"); michael@0: michael@0: assert.deepEqual([...seq(new Map())], [], "seq of emtpy map is empty"); michael@0: assert.deepEqual([...seq(new Map([[1, 2]]))], [[1, 2]], "seq single mapping is that mapping"); michael@0: assert.deepEqual([...seq(new Map([[1, 2], [3, 4], [5, 6]]))], michael@0: [[1, 2], [3, 4], [5, 6]], michael@0: "seq of map is key value mappings"); michael@0: michael@0: [function(){}, 1, /foo/, true].forEach(x => { michael@0: assert.throws(() => seq(x), "Type is not seq-able"); michael@0: }); michael@0: michael@0: assert.throws(() => [...broken], michael@0: /Boom/, michael@0: "broken sequence errors propagate"); michael@0: }; michael@0: michael@0: exports["test seq casting"] = assert => { michael@0: const xs = seq(function*() { yield 1; yield 2; yield 3; }); michael@0: const ys = seq(function*() { yield 1; }); michael@0: const zs = seq(function*() {}); michael@0: const kvs = seq(function*() { yield ["a", 1]; yield ["b", 2]; }); michael@0: const kv = seq(function*() { yield ["a", 1]; }); michael@0: michael@0: assert.deepEqual([...xs], [1, 2, 3], "cast to array"); michael@0: assert.deepEqual([...ys], [1], "cast to of one element"); michael@0: assert.deepEqual([...zs], [], "cast empty array"); michael@0: michael@0: assert.deepEqual(string(...xs), "123", "cast to string"); michael@0: assert.deepEqual(string(...ys), "1", "cast to char"); michael@0: assert.deepEqual(string(...zs), "", "cast to empty string"); michael@0: michael@0: assert.deepEqual(new Set(xs), new Set([1, 2, 3]), michael@0: "cast to set of items"); michael@0: assert.deepEqual(new Set(ys), new Set([1]), michael@0: "cast to set of one item"); michael@0: assert.deepEqual(new Set(zs), new Set(), michael@0: "cast to set of one item"); michael@0: michael@0: assert.deepEqual(new Map(kvs), new Map([["a", 1], ["b", 2]]), michael@0: "cast to map"); michael@0: assert.deepEqual(new Map(kv), new Map([["a", 1]]), michael@0: "cast to single mapping"); michael@0: assert.deepEqual(new Map(zs), new Map(), michael@0: "cast to empty map"); michael@0: michael@0: assert.deepEqual(object(...kvs), {a: 1, b: 2}, michael@0: "cast to object"); michael@0: assert.deepEqual(object(...kv), {a: 1}, michael@0: "cast to single pair"); michael@0: assert.deepEqual(object(...zs), {}, michael@0: "cast to empty object"); michael@0: }; michael@0: michael@0: exports["test pairs"] = assert => { michael@0: assert.deepEqual([...pairs(null)], [], "pairs on null is empty"); michael@0: assert.deepEqual([...pairs(void(0))], [], "pairs on void is empty"); michael@0: assert.deepEqual([...pairs({})], [], "empty sequence"); michael@0: assert.deepEqual([...pairs({a: 1})], [["a", 1]], "single pair"); michael@0: assert.deepEqual([...pairs({a: 1, b: 2, c: 3})].sort(), michael@0: [["a", 1], ["b", 2], ["c", 3]], michael@0: "creates pairs"); michael@0: let items = []; michael@0: for (let [key, value] of pairs({a: 1, b: 2, c: 3})) michael@0: items.push([key, value]); michael@0: michael@0: assert.deepEqual(items.sort(), michael@0: [["a", 1], ["b", 2], ["c", 3]], michael@0: "for of works on pairs"); michael@0: michael@0: michael@0: assert.deepEqual([...pairs([])], [], "pairs on empty array is empty"); michael@0: assert.deepEqual([...pairs([1])], [[0, 1]], "pairs on array is [index, element]"); michael@0: assert.deepEqual([...pairs([1, 2, 3])], michael@0: [[0, 1], [1, 2], [2, 3]], michael@0: "for arrays it pair of [index, element]"); michael@0: michael@0: assert.deepEqual([...pairs("")], [], "pairs on empty string is empty"); michael@0: assert.deepEqual([...pairs("a")], [[0, "a"]], "pairs on char is [0, char]"); michael@0: assert.deepEqual([...pairs("hello")], michael@0: [[0, "h"], [1, "e"], [2, "l"], [3, "l"], [4, "o"]], michael@0: "for strings it's pair of [index, char]"); michael@0: michael@0: assert.deepEqual([...pairs(new Map())], michael@0: [], michael@0: "pairs on empty map is empty"); michael@0: assert.deepEqual([...pairs(new Map([[1, 3]]))], michael@0: [[1, 3]], michael@0: "pairs on single mapping single mapping"); michael@0: assert.deepEqual([...pairs(new Map([[1, 2], [3, 4]]))], michael@0: [[1, 2], [3, 4]], michael@0: "pairs on map returs key vaule pairs"); michael@0: michael@0: assert.throws(() => pairs(new Set()), michael@0: "can't pair set"); michael@0: michael@0: assert.throws(() => pairs(4), michael@0: "can't pair number"); michael@0: michael@0: assert.throws(() => pairs(true), michael@0: "can't pair boolean"); michael@0: }; michael@0: michael@0: exports["test keys"] = assert => { michael@0: assert.deepEqual([...keys(null)], [], "keys on null is empty"); michael@0: assert.deepEqual([...keys(void(0))], [], "keys on void is empty"); michael@0: assert.deepEqual([...keys({})], [], "empty sequence"); michael@0: assert.deepEqual([...keys({a: 1})], ["a"], "single key"); michael@0: assert.deepEqual([...keys({a: 1, b: 2, c: 3})].sort(), michael@0: ["a", "b", "c"], michael@0: "all keys"); michael@0: michael@0: let items = []; michael@0: for (let key of keys({a: 1, b: 2, c: 3})) michael@0: items.push(key); michael@0: michael@0: assert.deepEqual(items.sort(), michael@0: ["a", "b", "c"], michael@0: "for of works on keys"); michael@0: michael@0: michael@0: assert.deepEqual([...keys([])], [], "keys on empty array is empty"); michael@0: assert.deepEqual([...keys([1])], [0], "keys on array is indexes"); michael@0: assert.deepEqual([...keys([1, 2, 3])], michael@0: [0, 1, 2], michael@0: "keys on arrays returns indexes"); michael@0: michael@0: assert.deepEqual([...keys("")], [], "keys on empty string is empty"); michael@0: assert.deepEqual([...keys("a")], [0], "keys on char is 0"); michael@0: assert.deepEqual([...keys("hello")], michael@0: [0, 1, 2, 3, 4], michael@0: "keys on strings is char indexes"); michael@0: michael@0: assert.deepEqual([...keys(new Map())], michael@0: [], michael@0: "keys on empty map is empty"); michael@0: assert.deepEqual([...keys(new Map([[1, 3]]))], michael@0: [1], michael@0: "keys on single mapping single mapping is single key"); michael@0: assert.deepEqual([...keys(new Map([[1, 2], [3, 4]]))], michael@0: [1, 3], michael@0: "keys on map is keys from map"); michael@0: michael@0: assert.throws(() => keys(new Set()), michael@0: "can't keys set"); michael@0: michael@0: assert.throws(() => keys(4), michael@0: "can't keys number"); michael@0: michael@0: assert.throws(() => keys(true), michael@0: "can't keys boolean"); michael@0: }; michael@0: michael@0: exports["test values"] = assert => { michael@0: assert.deepEqual([...values({})], [], "empty sequence"); michael@0: assert.deepEqual([...values({a: 1})], [1], "single value"); michael@0: assert.deepEqual([...values({a: 1, b: 2, c: 3})].sort(), michael@0: [1, 2, 3], michael@0: "all values"); michael@0: michael@0: let items = []; michael@0: for (let value of values({a: 1, b: 2, c: 3})) michael@0: items.push(value); michael@0: michael@0: assert.deepEqual(items.sort(), michael@0: [1, 2, 3], michael@0: "for of works on values"); michael@0: michael@0: assert.deepEqual([...values([])], [], "values on empty array is empty"); michael@0: assert.deepEqual([...values([1])], [1], "values on array elements"); michael@0: assert.deepEqual([...values([1, 2, 3])], michael@0: [1, 2, 3], michael@0: "values on arrays returns elements"); michael@0: michael@0: assert.deepEqual([...values("")], [], "values on empty string is empty"); michael@0: assert.deepEqual([...values("a")], ["a"], "values on char is char"); michael@0: assert.deepEqual([...values("hello")], michael@0: ["h", "e", "l", "l", "o"], michael@0: "values on strings is chars"); michael@0: michael@0: assert.deepEqual([...values(new Map())], michael@0: [], michael@0: "values on empty map is empty"); michael@0: assert.deepEqual([...values(new Map([[1, 3]]))], michael@0: [3], michael@0: "keys on single mapping single mapping is single key"); michael@0: assert.deepEqual([...values(new Map([[1, 2], [3, 4]]))], michael@0: [2, 4], michael@0: "values on map is values from map"); michael@0: michael@0: assert.deepEqual([...values(new Set())], [], "values on empty set is empty"); michael@0: assert.deepEqual([...values(new Set([1]))], [1], "values on set is it's items"); michael@0: assert.deepEqual([...values(new Set([1, 2, 3]))], michael@0: [1, 2, 3], michael@0: "values on set is it's items"); michael@0: michael@0: michael@0: assert.throws(() => values(4), michael@0: "can't values number"); michael@0: michael@0: assert.throws(() => values(true), michael@0: "can't values boolean"); michael@0: }; michael@0: michael@0: exports["test fromEnumerator"] = assert => { michael@0: const { Cc, Ci } = require("chrome"); michael@0: const { enumerateObservers, michael@0: addObserver, michael@0: removeObserver } = Cc["@mozilla.org/observer-service;1"]. michael@0: getService(Ci.nsIObserverService); michael@0: michael@0: michael@0: const topic = "sec:" + Math.random().toString(32).substr(2); michael@0: const [a, b, c] = [{wrappedJSObject: {}}, michael@0: {wrappedJSObject: {}}, michael@0: {wrappedJSObject: {}}]; michael@0: const unwrap = x => x.wrappedJSObject; michael@0: michael@0: [a, b, c].forEach(x => addObserver(x, topic, false)); michael@0: michael@0: const xs = fromEnumerator(() => enumerateObservers(topic)); michael@0: const ys = map(unwrap, xs); michael@0: michael@0: assert.deepEqual([...ys], [a, b, c].map(unwrap), michael@0: "all observers are there"); michael@0: michael@0: removeObserver(b, topic); michael@0: michael@0: assert.deepEqual([...ys], [a, c].map(unwrap), michael@0: "b was removed"); michael@0: michael@0: removeObserver(a, topic); michael@0: michael@0: assert.deepEqual([...ys], [c].map(unwrap), michael@0: "a was removed"); michael@0: michael@0: removeObserver(c, topic); michael@0: michael@0: assert.deepEqual([...ys], [], michael@0: "c was removed, now empty"); michael@0: michael@0: addObserver(a, topic, false); michael@0: michael@0: assert.deepEqual([...ys], [a].map(unwrap), michael@0: "a was added"); michael@0: michael@0: removeObserver(a, topic); michael@0: michael@0: assert.deepEqual([...ys], [].map(unwrap), michael@0: "a was removed, now empty"); michael@0: michael@0: }; michael@0: michael@0: exports["test filter"] = assert => { michael@0: const isOdd = x => x % 2; michael@0: const odds = seq(function*() { yield 1; yield 3; yield 5; }); michael@0: const evens = seq(function*() { yield 2; yield 4; yield 6; }); michael@0: const mixed = seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: yield 4; michael@0: }); michael@0: michael@0: assert.deepEqual([...filter(isOdd, mixed)], [1, 3], michael@0: "filtered odds"); michael@0: assert.deepEqual([...filter(isOdd, odds)], [1, 3, 5], michael@0: "kept all"); michael@0: assert.deepEqual([...filter(isOdd, evens)], [], michael@0: "kept none"); michael@0: michael@0: michael@0: let xs = filter(boom, mixed); michael@0: assert.throws(() => [...xs], /Boom/, "errors propagate"); michael@0: michael@0: assert.throws(() => [...filter(isOdd, broken)], /Boom/, michael@0: "sequence errors propagate"); michael@0: }; michael@0: michael@0: exports["test filter array"] = assert => { michael@0: let isOdd = x => x % 2; michael@0: let xs = filter(isOdd, [1, 2, 3, 4]); michael@0: let ys = filter(isOdd, [1, 3, 5]); michael@0: let zs = filter(isOdd, [2, 4, 6]); michael@0: michael@0: assert.deepEqual([...xs], [1, 3], "filteres odds"); michael@0: assert.deepEqual([...ys], [1, 3, 5], "kept all"); michael@0: assert.deepEqual([...zs], [], "kept none"); michael@0: assert.ok(!Array.isArray(xs)); michael@0: }; michael@0: michael@0: exports["test filter set"] = assert => { michael@0: let isOdd = x => x % 2; michael@0: let xs = filter(isOdd, new Set([1, 2, 3, 4])); michael@0: let ys = filter(isOdd, new Set([1, 3, 5])); michael@0: let zs = filter(isOdd, new Set([2, 4, 6])); michael@0: michael@0: assert.deepEqual([...xs], [1, 3], "filteres odds"); michael@0: assert.deepEqual([...ys], [1, 3, 5], "kept all"); michael@0: assert.deepEqual([...zs], [], "kept none"); michael@0: }; michael@0: michael@0: exports["test filter string"] = assert => { michael@0: let isUpperCase = x => x.toUpperCase() === x; michael@0: let xs = filter(isUpperCase, "aBcDe"); michael@0: let ys = filter(isUpperCase, "ABC"); michael@0: let zs = filter(isUpperCase, "abcd"); michael@0: michael@0: assert.deepEqual([...xs], ["B", "D"], "filteres odds"); michael@0: assert.deepEqual([...ys], ["A", "B", "C"], "kept all"); michael@0: assert.deepEqual([...zs], [], "kept none"); michael@0: }; michael@0: michael@0: exports["test filter lazy"] = assert => { michael@0: const x = 1; michael@0: let y = 2; michael@0: michael@0: const xy = seq(function*() { yield x; yield y; }); michael@0: const isOdd = x => x % 2; michael@0: const actual = filter(isOdd, xy); michael@0: michael@0: assert.deepEqual([...actual], [1], "only one odd number"); michael@0: y = 3; michael@0: assert.deepEqual([...actual], [1, 3], "filter is lazy"); michael@0: }; michael@0: michael@0: exports["test filter non sequences"] = assert => { michael@0: const False = _ => false; michael@0: assert.throws(() => [...filter(False, 1)], michael@0: "can't iterate number"); michael@0: assert.throws(() => [...filter(False, {a: 1, b:2})], michael@0: "can't iterate object"); michael@0: }; michael@0: michael@0: exports["test map"] = assert => { michael@0: let inc = x => x + 1; michael@0: let xs = seq(function*() { yield 1; yield 2; yield 3; }); michael@0: let ys = map(inc, xs); michael@0: michael@0: assert.deepEqual([...ys], [2, 3, 4], "incremented each item"); michael@0: michael@0: assert.deepEqual([...map(inc, null)], [], "mapping null is empty"); michael@0: assert.deepEqual([...map(inc, void(0))], [], "mapping void is empty"); michael@0: assert.deepEqual([...map(inc, new Set([1, 2, 3]))], [2, 3, 4], "maps set items"); michael@0: }; michael@0: michael@0: exports["test map two inputs"] = assert => { michael@0: let sum = (x, y) => x + y; michael@0: let xs = seq(function*() { yield 1; yield 2; yield 3; }); michael@0: let ys = seq(function*() { yield 4; yield 5; yield 6; }); michael@0: michael@0: let zs = map(sum, xs, ys); michael@0: michael@0: assert.deepEqual([...zs], [5, 7, 9], "summed numbers"); michael@0: }; michael@0: michael@0: exports["test map diff sized inputs"] = assert => { michael@0: let sum = (x, y) => x + y; michael@0: let xs = seq(function*() { yield 1; yield 2; yield 3; }); michael@0: let ys = seq(function*() { yield 4; yield 5; yield 6; yield 7; yield 8; }); michael@0: michael@0: let zs = map(sum, xs, ys); michael@0: michael@0: assert.deepEqual([...zs], [5, 7, 9], "summed numbers"); michael@0: assert.deepEqual([...map(sum, ys, xs)], [5, 7, 9], michael@0: "index of exhasting input is irrelevant"); michael@0: }; michael@0: michael@0: exports["test map multi"] = assert => { michael@0: let sum = (x, y, z, w) => x + y + z + w; michael@0: let xs = seq(function*() { yield 1; yield 2; yield 3; yield 4; }); michael@0: let ys = seq(function*() { yield 4; yield 5; yield 6; yield 7; yield 8; }); michael@0: let zs = seq(function*() { yield 10; yield 11; yield 12; }); michael@0: let ws = seq(function*() { yield 0; yield 20; yield 40; yield 60; }); michael@0: michael@0: let actual = map(sum, xs, ys, zs, ws); michael@0: michael@0: assert.deepEqual([...actual], [15, 38, 61], "summed numbers"); michael@0: }; michael@0: michael@0: exports["test map errors"] = assert => { michael@0: assert.deepEqual([...map(boom, [])], [], michael@0: "won't throw if empty"); michael@0: michael@0: const xs = map(boom, [1, 2, 4]); michael@0: michael@0: assert.throws(() => [...xs], /Boom/, "propagates errors"); michael@0: michael@0: assert.throws(() => [...map(x => x, broken)], /Boom/, michael@0: "sequence errors propagate"); michael@0: }; michael@0: michael@0: exports["test reductions"] = assert => { michael@0: let sum = (...xs) => xs.reduce((x, y) => x + y, 0); michael@0: michael@0: assert.deepEqual([...reductions(sum, [1, 1, 1, 1])], michael@0: [1, 2, 3, 4], michael@0: "works with arrays"); michael@0: assert.deepEqual([...reductions(sum, 5, [1, 1, 1, 1])], michael@0: [5, 6, 7, 8, 9], michael@0: "array with initial"); michael@0: michael@0: assert.deepEqual([...reductions(sum, seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: }))], michael@0: [1, 3, 6], michael@0: "works with sequences"); michael@0: michael@0: assert.deepEqual([...reductions(sum, 10, seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: }))], michael@0: [10, 11, 13, 16], michael@0: "works with sequences"); michael@0: michael@0: assert.deepEqual([...reductions(sum, [])], [0], michael@0: "invokes accumulator with no args"); michael@0: michael@0: assert.throws(() => [...reductions(boom, 1, [1])], michael@0: /Boom/, michael@0: "arg errors errors propagate"); michael@0: assert.throws(() => [...reductions(sum, 1, broken)], michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: }; michael@0: michael@0: exports["test reduce"] = assert => { michael@0: let sum = (...xs) => xs.reduce((x, y) => x + y, 0); michael@0: michael@0: assert.deepEqual(reduce(sum, [1, 2, 3, 4, 5]), michael@0: 15, michael@0: "works with arrays"); michael@0: michael@0: assert.deepEqual(reduce(sum, seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: })), michael@0: 6, michael@0: "works with sequences"); michael@0: michael@0: assert.deepEqual(reduce(sum, 10, [1, 2, 3, 4, 5]), michael@0: 25, michael@0: "works with array & initial"); michael@0: michael@0: assert.deepEqual(reduce(sum, 5, seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: })), michael@0: 11, michael@0: "works with sequences & initial"); michael@0: michael@0: assert.deepEqual(reduce(sum, []), 0, "reduce with no args"); michael@0: assert.deepEqual(reduce(sum, "a", []), "a", "reduce with initial"); michael@0: assert.deepEqual(reduce(sum, 1, [1]), 2, "reduce with single & initial"); michael@0: michael@0: assert.throws(() => [...reduce(boom, 1, [1])], michael@0: /Boom/, michael@0: "arg errors errors propagate"); michael@0: assert.throws(() => [...reduce(sum, 1, broken)], michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: }; michael@0: michael@0: exports["test each"] = assert => { michael@0: const collect = xs => { michael@0: let result = []; michael@0: each((...etc) => result.push(...etc), xs); michael@0: return result; michael@0: }; michael@0: michael@0: assert.deepEqual(collect(null), [], "each ignores null"); michael@0: assert.deepEqual(collect(void(0)), [], "each ignores void"); michael@0: michael@0: assert.deepEqual(collect([]), [], "each ignores empty"); michael@0: assert.deepEqual(collect([1]), [1], "each works on single item arrays"); michael@0: assert.deepEqual(collect([1, 2, 3, 4, 5]), michael@0: [1, 2, 3, 4, 5], michael@0: "works with arrays"); michael@0: michael@0: assert.deepEqual(collect(seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: })), michael@0: [1, 2, 3], michael@0: "works with sequences"); michael@0: michael@0: assert.deepEqual(collect(""), [], "ignores empty strings"); michael@0: assert.deepEqual(collect("a"), ["a"], "works on chars"); michael@0: assert.deepEqual(collect("hello"), ["h", "e", "l", "l", "o"], michael@0: "works on strings"); michael@0: michael@0: assert.deepEqual(collect(new Set()), [], "ignores empty sets"); michael@0: assert.deepEqual(collect(new Set(["a"])), ["a"], michael@0: "works on single item sets"); michael@0: assert.deepEqual(collect(new Set([1, 2, 3])), [1, 2, 3], michael@0: "works on muti item tests"); michael@0: michael@0: assert.deepEqual(collect(new Map()), [], "ignores empty maps"); michael@0: assert.deepEqual(collect(new Map([["a", 1]])), [["a", 1]], michael@0: "works on single mapping maps"); michael@0: assert.deepEqual(collect(new Map([[1, 2], [3, 4], [5, 6]])), michael@0: [[1, 2], [3, 4], [5, 6]], michael@0: "works on muti mapping maps"); michael@0: michael@0: assert.throws(() => collect({}), "objects arn't supported"); michael@0: assert.throws(() => collect(1), "numbers arn't supported"); michael@0: assert.throws(() => collect(true), "booleas arn't supported"); michael@0: }; michael@0: michael@0: exports["test count"] = assert => { michael@0: assert.equal(count(null), 0, "null counts to 0"); michael@0: assert.equal(count(), 0, "undefined counts to 0"); michael@0: assert.equal(count([]), 0, "empty array"); michael@0: assert.equal(count([1, 2, 3]), 3, "non-empty array"); michael@0: assert.equal(count(""), 0, "empty string"); michael@0: assert.equal(count("hello"), 5, "non-empty string"); michael@0: assert.equal(count(new Map()), 0, "empty map"); michael@0: assert.equal(count(new Map([[1, 2], [2, 3]])), 2, "non-empty map"); michael@0: assert.equal(count(new Set()), 0, "empty set"); michael@0: assert.equal(count(new Set([1, 2, 3, 4])), 4, "non-empty set"); michael@0: assert.equal(count(seq(function*() {})), 0, "empty sequence"); michael@0: assert.equal(count(seq(function*() { yield 1; yield 2; })), 2, michael@0: "non-empty sequence"); michael@0: michael@0: assert.throws(() => count(broken), michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: }; michael@0: michael@0: exports["test isEmpty"] = assert => { michael@0: assert.equal(isEmpty(null), true, "null is empty"); michael@0: assert.equal(isEmpty(), true, "undefined is empty"); michael@0: assert.equal(isEmpty([]), true, "array is array"); michael@0: assert.equal(isEmpty([1, 2, 3]), false, "array isn't empty"); michael@0: assert.equal(isEmpty(""), true, "string is empty"); michael@0: assert.equal(isEmpty("hello"), false, "non-empty string"); michael@0: assert.equal(isEmpty(new Map()), true, "empty map"); michael@0: assert.equal(isEmpty(new Map([[1, 2], [2, 3]])), false, "non-empty map"); michael@0: assert.equal(isEmpty(new Set()), true, "empty set"); michael@0: assert.equal(isEmpty(new Set([1, 2, 3, 4])), false , "non-empty set"); michael@0: assert.equal(isEmpty(seq(function*() {})), true, "empty sequence"); michael@0: assert.equal(isEmpty(seq(function*() { yield 1; yield 2; })), false, michael@0: "non-empty sequence"); michael@0: michael@0: assert.equal(isEmpty(broken), false, "hasn't reached error"); michael@0: }; michael@0: michael@0: exports["test isEvery"] = assert => { michael@0: let isOdd = x => x % 2; michael@0: let isTrue = x => x === true; michael@0: let isFalse = x => x === false; michael@0: michael@0: assert.equal(isEvery(isOdd, seq(function*() { michael@0: yield 1; michael@0: yield 3; michael@0: yield 5; michael@0: })), true, "all are odds"); michael@0: michael@0: assert.equal(isEvery(isOdd, seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: })), false, "contains even"); michael@0: michael@0: assert.equal(isEvery(isTrue, seq(function*() {})), true, "true if empty"); michael@0: assert.equal(isEvery(isFalse, seq(function*() {})), true, "true if empty"); michael@0: michael@0: assert.equal(isEvery(isTrue, null), true, "true for null"); michael@0: assert.equal(isEvery(isTrue, undefined), true, "true for undefined"); michael@0: michael@0: assert.throws(() => isEvery(boom, [1, 2]), michael@0: /Boom/, michael@0: "arg errors errors propagate"); michael@0: assert.throws(() => isEvery(x => true, broken), michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: michael@0: assert.equal(isEvery(x => false, broken), false, michael@0: "hasn't reached error"); michael@0: }; michael@0: michael@0: exports["test some"] = assert => { michael@0: let isOdd = x => x % 2; michael@0: let isTrue = x => x === true; michael@0: let isFalse = x => x === false; michael@0: michael@0: assert.equal(some(isOdd, seq(function*() { michael@0: yield 2; michael@0: yield 4; michael@0: yield 6; michael@0: })), null, "all are even"); michael@0: michael@0: assert.equal(some(isOdd, seq(function*() { michael@0: yield 2; michael@0: yield 3; michael@0: yield 4; michael@0: })), true, "contains odd"); michael@0: michael@0: assert.equal(some(isTrue, seq(function*() {})), null, michael@0: "null if empty") michael@0: assert.equal(some(isFalse, seq(function*() {})), null, michael@0: "null if empty") michael@0: michael@0: assert.equal(some(isTrue, null), null, "null for null"); michael@0: assert.equal(some(isTrue, undefined), null, "null for undefined"); michael@0: michael@0: assert.throws(() => some(boom, [1, 2]), michael@0: /Boom/, michael@0: "arg errors errors propagate"); michael@0: assert.throws(() => some(x => false, broken), michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: michael@0: assert.equal(some(x => true, broken), true, michael@0: "hasn't reached error"); michael@0: }; michael@0: michael@0: exports["test take"] = assert => { michael@0: let xs = seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: yield 4; michael@0: yield 5; michael@0: yield 6; michael@0: }); michael@0: michael@0: assert.deepEqual([...take(3, xs)], [1, 2, 3], "took 3 items"); michael@0: assert.deepEqual([...take(3, [1, 2, 3, 4, 5])], [1, 2, 3], michael@0: "took 3 from array"); michael@0: michael@0: let ys = seq(function*() { yield 1; yield 2; }); michael@0: assert.deepEqual([...take(3, ys)], [1, 2], "takes at max n"); michael@0: assert.deepEqual([...take(3, [1, 2])], [1, 2], michael@0: "takes at max n from arary"); michael@0: michael@0: let empty = seq(function*() {}); michael@0: assert.deepEqual([...take(5, empty)], [], "nothing to take"); michael@0: michael@0: assert.throws(() => [...take(3, broken)], michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: michael@0: assert.deepEqual([...take(1, broken)], [1], michael@0: "hasn't reached error"); michael@0: }; michael@0: michael@0: exports["test iterate"] = assert => { michael@0: let inc = x => x + 1; michael@0: let nums = iterate(inc, 0); michael@0: michael@0: assert.deepEqual([...take(5, nums)], [0, 1, 2, 3, 4], "took 5"); michael@0: assert.deepEqual([...take(10, nums)], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "took 10"); michael@0: michael@0: let xs = iterate(x => x * 3, 2); michael@0: assert.deepEqual([...take(4, xs)], [2, 6, 18, 54], "took 4"); michael@0: michael@0: assert.throws(() => [...iterate(boom, 0)], michael@0: /Boom/, michael@0: "function exceptions propagate"); michael@0: }; michael@0: michael@0: exports["test takeWhile"] = assert => { michael@0: let isNegative = x => x < 0; michael@0: let xs = seq(function*() { michael@0: yield -2; michael@0: yield -1; michael@0: yield 0; michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: }); michael@0: michael@0: assert.deepEqual([...takeWhile(isNegative, xs)], [-2, -1], michael@0: "took until 0"); michael@0: michael@0: let ys = seq(function*() {}); michael@0: assert.deepEqual([...takeWhile(isNegative, ys)], [], michael@0: "took none"); michael@0: michael@0: let zs = seq(function*() { michael@0: yield 0; michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: }); michael@0: michael@0: assert.deepEqual([...takeWhile(isNegative, zs)], [], michael@0: "took none"); michael@0: michael@0: assert.throws(() => [...takeWhile(boom, zs)], michael@0: /Boom/, michael@0: "function errors errors propagate"); michael@0: assert.throws(() => [...takeWhile(x => true, broken)], michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: michael@0: assert.deepEqual([...takeWhile(x => false, broken)], michael@0: [], michael@0: "hasn't reached error"); michael@0: }; michael@0: michael@0: exports["test drop"] = assert => { michael@0: let testDrop = xs => { michael@0: assert.deepEqual([...drop(2, xs)], michael@0: [3, 4], michael@0: "dropped two elements"); michael@0: michael@0: assert.deepEqual([...drop(1, xs)], michael@0: [2, 3, 4], michael@0: "dropped one"); michael@0: michael@0: assert.deepEqual([...drop(0, xs)], michael@0: [1, 2, 3, 4], michael@0: "dropped 0"); michael@0: michael@0: assert.deepEqual([...drop(-2, xs)], michael@0: [1, 2, 3, 4], michael@0: "dropped 0 on negative `n`"); michael@0: michael@0: assert.deepEqual([...drop(5, xs)], michael@0: [], michael@0: "dropped all items"); michael@0: }; michael@0: michael@0: testDrop([1, 2, 3, 4]); michael@0: testDrop(seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: yield 4; michael@0: })); michael@0: michael@0: assert.throws(() => [...drop(1, broken)], michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: }; michael@0: michael@0: michael@0: exports["test dropWhile"] = assert => { michael@0: let isNegative = x => x < 0; michael@0: let True = _ => true; michael@0: let False = _ => false; michael@0: michael@0: let test = xs => { michael@0: assert.deepEqual([...dropWhile(isNegative, xs)], michael@0: [0, 1, 2], michael@0: "dropped negative"); michael@0: michael@0: assert.deepEqual([...dropWhile(True, xs)], michael@0: [], michael@0: "drop all"); michael@0: michael@0: assert.deepEqual([...dropWhile(False, xs)], michael@0: [-2, -1, 0, 1, 2], michael@0: "keep all"); michael@0: }; michael@0: michael@0: test([-2, -1, 0, 1, 2]); michael@0: test(seq(function*() { michael@0: yield -2; michael@0: yield -1; michael@0: yield 0; michael@0: yield 1; michael@0: yield 2; michael@0: })); michael@0: michael@0: assert.throws(() => [...dropWhile(boom, [1, 2, 3])], michael@0: /Boom/, michael@0: "function errors errors propagate"); michael@0: assert.throws(() => [...dropWhile(x => true, broken)], michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: }; michael@0: michael@0: michael@0: exports["test concat"] = assert => { michael@0: let test = (a, b, c, d) => { michael@0: assert.deepEqual([...concat()], michael@0: [], michael@0: "nothing to concat"); michael@0: assert.deepEqual([...concat(a)], michael@0: [1, 2, 3], michael@0: "concat with nothing returns same as first"); michael@0: assert.deepEqual([...concat(a, b)], michael@0: [1, 2, 3, 4, 5], michael@0: "concat items from both"); michael@0: assert.deepEqual([...concat(a, b, a)], michael@0: [1, 2, 3, 4, 5, 1, 2, 3], michael@0: "concat itself"); michael@0: assert.deepEqual([...concat(c)], michael@0: [], michael@0: "concat of empty is empty"); michael@0: assert.deepEqual([...concat(a, c)], michael@0: [1, 2, 3], michael@0: "concat with empty"); michael@0: assert.deepEqual([...concat(c, c, c)], michael@0: [], michael@0: "concat of empties is empty"); michael@0: assert.deepEqual([...concat(c, b)], michael@0: [4, 5], michael@0: "empty can be in front"); michael@0: assert.deepEqual([...concat(d)], michael@0: [7], michael@0: "concat singular"); michael@0: assert.deepEqual([...concat(d, d)], michael@0: [7, 7], michael@0: "concat singulars"); michael@0: michael@0: assert.deepEqual([...concat(a, a, b, c, d, c, d, d)], michael@0: [1, 2, 3, 1, 2, 3, 4, 5, 7, 7, 7], michael@0: "many concats"); michael@0: michael@0: let ab = concat(a, b); michael@0: let abcd = concat(ab, concat(c, d)); michael@0: let cdabcd = concat(c, d, abcd); michael@0: michael@0: assert.deepEqual([...cdabcd], michael@0: [7, 1, 2, 3, 4, 5, 7], michael@0: "nested concats"); michael@0: }; michael@0: michael@0: test([1, 2, 3], michael@0: [4, 5], michael@0: [], michael@0: [7]); michael@0: michael@0: test(seq(function*() { yield 1; yield 2; yield 3; }), michael@0: seq(function*() { yield 4; yield 5; }), michael@0: seq(function*() { }), michael@0: seq(function*() { yield 7; })); michael@0: michael@0: assert.throws(() => [...concat(broken, [1, 2, 3])], michael@0: /Boom/, michael@0: "function errors errors propagate"); michael@0: }; michael@0: michael@0: michael@0: exports["test first"] = assert => { michael@0: let test = (xs, empty) => { michael@0: assert.equal(first(xs), 1, "returns first"); michael@0: assert.equal(first(empty), null, "returns null empty"); michael@0: }; michael@0: michael@0: test("1234", ""); michael@0: test([1, 2, 3], []); michael@0: test([1, 2, 3], null); michael@0: test([1, 2, 3], undefined); michael@0: test(seq(function*() { yield 1; yield 2; yield 3; }), michael@0: seq(function*() { })); michael@0: assert.equal(first(broken), 1, "did not reached error"); michael@0: }; michael@0: michael@0: exports["test rest"] = assert => { michael@0: let test = (xs, x, nil) => { michael@0: assert.deepEqual([...rest(xs)], ["b", "c"], michael@0: "rest items"); michael@0: assert.deepEqual([...rest(x)], [], michael@0: "empty when singular"); michael@0: assert.deepEqual([...rest(nil)], [], michael@0: "empty when empty"); michael@0: }; michael@0: michael@0: test("abc", "a", ""); michael@0: test(["a", "b", "c"], ["d"], []); michael@0: test(seq(function*() { yield "a"; yield "b"; yield "c"; }), michael@0: seq(function*() { yield "d"; }), michael@0: seq(function*() {})); michael@0: test(["a", "b", "c"], ["d"], null); michael@0: test(["a", "b", "c"], ["d"], undefined); michael@0: michael@0: assert.throws(() => [...rest(broken)], michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: }; michael@0: michael@0: michael@0: exports["test nth"] = assert => { michael@0: let notFound = {}; michael@0: let test = xs => { michael@0: assert.equal(nth(xs, 0), "h", "first"); michael@0: assert.equal(nth(xs, 1), "e", "second"); michael@0: assert.equal(nth(xs, 5), void(0), "out of bound"); michael@0: assert.equal(nth(xs, 5, notFound), notFound, "out of bound"); michael@0: assert.equal(nth(xs, -1), void(0), "out of bound"); michael@0: assert.equal(nth(xs, -1, notFound), notFound, "out of bound"); michael@0: assert.equal(nth(xs, 4), "o", "5th"); michael@0: }; michael@0: michael@0: let testEmpty = xs => { michael@0: assert.equal(nth(xs, 0), void(0), "no first in empty"); michael@0: assert.equal(nth(xs, 5), void(0), "no 5th in empty"); michael@0: assert.equal(nth(xs, 0, notFound), notFound, "notFound on out of bound"); michael@0: }; michael@0: michael@0: test("hello"); michael@0: test(["h", "e", "l", "l", "o"]); michael@0: test(seq(function*() { michael@0: yield "h"; michael@0: yield "e"; michael@0: yield "l"; michael@0: yield "l"; michael@0: yield "o"; michael@0: })); michael@0: testEmpty(null); michael@0: testEmpty(undefined); michael@0: testEmpty([]); michael@0: testEmpty(""); michael@0: testEmpty(seq(function*() {})); michael@0: michael@0: michael@0: assert.throws(() => nth(broken, 1), michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: assert.equal(nth(broken, 0), 1, "have not reached error"); michael@0: }; michael@0: michael@0: michael@0: exports["test last"] = assert => { michael@0: assert.equal(last(null), null, "no last in null"); michael@0: assert.equal(last(void(0)), null, "no last in undefined"); michael@0: assert.equal(last([]), null, "no last in []"); michael@0: assert.equal(last(""), null, "no last in ''"); michael@0: assert.equal(last(seq(function*() { })), null, "no last in empty"); michael@0: michael@0: assert.equal(last("hello"), "o", "last from string"); michael@0: assert.equal(last([1, 2, 3]), 3, "last from array"); michael@0: assert.equal(last([1]), 1, "last from singular"); michael@0: assert.equal(last(seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: })), 3, "last from sequence"); michael@0: michael@0: assert.throws(() => last(broken), michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: }; michael@0: michael@0: michael@0: exports["test dropLast"] = assert => { michael@0: let test = xs => { michael@0: assert.deepEqual([...dropLast(xs)], michael@0: [1, 2, 3, 4], michael@0: "dropped last"); michael@0: assert.deepEqual([...dropLast(0, xs)], michael@0: [1, 2, 3, 4, 5], michael@0: "dropped none on 0"); michael@0: assert.deepEqual([...dropLast(-3, xs)], michael@0: [1, 2, 3, 4, 5], michael@0: "drop none on negative"); michael@0: assert.deepEqual([...dropLast(3, xs)], michael@0: [1, 2], michael@0: "dropped given number"); michael@0: assert.deepEqual([...dropLast(5, xs)], michael@0: [], michael@0: "dropped all"); michael@0: }; michael@0: michael@0: let testEmpty = xs => { michael@0: assert.deepEqual([...dropLast(xs)], michael@0: [], michael@0: "nothing to drop"); michael@0: assert.deepEqual([...dropLast(0, xs)], michael@0: [], michael@0: "dropped none on 0"); michael@0: assert.deepEqual([...dropLast(-3, xs)], michael@0: [], michael@0: "drop none on negative"); michael@0: assert.deepEqual([...dropLast(3, xs)], michael@0: [], michael@0: "nothing to drop"); michael@0: }; michael@0: michael@0: test([1, 2, 3, 4, 5]); michael@0: test(seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: yield 4; michael@0: yield 5; michael@0: })); michael@0: testEmpty([]); michael@0: testEmpty(""); michael@0: testEmpty(seq(function*() {})); michael@0: michael@0: assert.throws(() => [...dropLast(broken)], michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: }; michael@0: michael@0: michael@0: exports["test distinct"] = assert => { michael@0: let test = (xs, message) => { michael@0: assert.deepEqual([...distinct(xs)], michael@0: [1, 2, 3, 4, 5], michael@0: message); michael@0: }; michael@0: michael@0: test([1, 2, 1, 3, 1, 4, 1, 5], "works with arrays"); michael@0: test(seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 1; michael@0: yield 3; michael@0: yield 1; michael@0: yield 4; michael@0: yield 1; michael@0: yield 5; michael@0: }), "works with sequences"); michael@0: test(new Set([1, 2, 1, 3, 1, 4, 1, 5]), michael@0: "works with sets"); michael@0: test(seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 2; michael@0: yield 2; michael@0: yield 1; michael@0: yield 3; michael@0: yield 1; michael@0: yield 4; michael@0: yield 4; michael@0: yield 4; michael@0: yield 1; michael@0: yield 5; michael@0: }), "works with multiple repeatitions"); michael@0: test([1, 2, 3, 4, 5], "work with distinct arrays"); michael@0: test(seq(function*() { michael@0: yield 1; michael@0: yield 2; michael@0: yield 3; michael@0: yield 4; michael@0: yield 5; michael@0: }), "works with distinct seqs"); michael@0: }; michael@0: michael@0: michael@0: exports["test remove"] = assert => { michael@0: let isPositive = x => x > 0; michael@0: let test = xs => { michael@0: assert.deepEqual([...remove(isPositive, xs)], michael@0: [-2, -1, 0], michael@0: "removed positives"); michael@0: }; michael@0: michael@0: test([1, -2, 2, -1, 3, 7, 0]); michael@0: test(seq(function*() { michael@0: yield 1; michael@0: yield -2; michael@0: yield 2; michael@0: yield -1; michael@0: yield 3; michael@0: yield 7; michael@0: yield 0; michael@0: })); michael@0: michael@0: assert.throws(() => [...distinct(broken)], michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: }; michael@0: michael@0: michael@0: exports["test mapcat"] = assert => { michael@0: let upto = n => seq(function* () { michael@0: let index = 0; michael@0: while (index < n) { michael@0: yield index; michael@0: index = index + 1; michael@0: } michael@0: }); michael@0: michael@0: assert.deepEqual([...mapcat(upto, [1, 2, 3, 4])], michael@0: [0, 0, 1, 0, 1, 2, 0, 1, 2, 3], michael@0: "expands given sequence"); michael@0: michael@0: assert.deepEqual([...mapcat(upto, [0, 1, 2, 0])], michael@0: [0, 0, 1], michael@0: "expands given sequence"); michael@0: michael@0: assert.deepEqual([...mapcat(upto, [0, 0, 0])], michael@0: [], michael@0: "expands given sequence"); michael@0: michael@0: assert.deepEqual([...mapcat(upto, [])], michael@0: [], michael@0: "nothing to expand"); michael@0: michael@0: assert.deepEqual([...mapcat(upto, null)], michael@0: [], michael@0: "nothing to expand"); michael@0: michael@0: assert.deepEqual([...mapcat(upto, void(0))], michael@0: [], michael@0: "nothing to expand"); michael@0: michael@0: let xs = seq(function*() { michael@0: yield 0; michael@0: yield 1; michael@0: yield 0; michael@0: yield 2; michael@0: yield 0; michael@0: }); michael@0: michael@0: assert.deepEqual([...mapcat(upto, xs)], michael@0: [0, 0, 1], michael@0: "expands given sequence"); michael@0: michael@0: assert.throws(() => [...mapcat(boom, xs)], michael@0: /Boom/, michael@0: "function errors errors propagate"); michael@0: assert.throws(() => [...mapcat(upto, broken)], michael@0: /Boom/, michael@0: "sequence errors propagate"); michael@0: }; michael@0: michael@0: require("sdk/test").run(exports);