addon-sdk/source/test/test-functional.js

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     4 "use strict";
     6 const { setTimeout } = require('sdk/timers');
     7 const utils = require('sdk/lang/functional');
     8 const { invoke, defer, partial, compose, memoize, once, is, isnt,
     9   delay, wrap, curry, chainable, field, query, isInstance, debounce, throttle
    10 } = utils;
    11 const { LoaderWithHookedConsole } = require('sdk/test/loader');
    13 exports['test forwardApply'] = function(assert) {
    14   function sum(b, c) { return this.a + b + c; }
    15   assert.equal(invoke(sum, [2, 3], { a: 1 }), 6,
    16                'passed arguments and pseoude-variable are used');
    18   assert.equal(invoke(sum.bind({ a: 2 }), [2, 3], { a: 1 }), 7,
    19                'bounded `this` pseoudo variable is used');
    20 };
    22 exports['test deferred function'] = function(assert, done) {
    23   let nextTurn = false;
    24   function sum(b, c) {
    25     assert.ok(nextTurn, 'enqueued is called in next turn of event loop');
    26     assert.equal(this.a + b + c, 6,
    27                  'passed arguments an pseoude-variable are used');
    28     done();
    29   }
    31   let fixture = { a: 1, method: defer(sum) };
    32   fixture.method(2, 3);
    33   nextTurn = true;
    34 };
    36 exports['test partial function'] = function(assert) {
    37   function sum(b, c) { return this.a + b + c; }
    39   let foo = { a : 5 };
    41   foo.sum7 = partial(sum, 7);
    42   foo.sum8and4 = partial(sum, 8, 4);
    44   assert.equal(foo.sum7(2), 14, 'partial one arguments works');
    46   assert.equal(foo.sum8and4(), 17, 'partial both arguments works');
    47 };
    49 exports["test curry defined numeber of arguments"] = function(assert) {
    50   var sum = curry(function(a, b, c) {
    51     return a + b + c;
    52   });
    54   assert.equal(sum(2, 2, 1), 5, "sum(2, 2, 1) => 5");
    55   assert.equal(sum(2, 4)(1), 7, "sum(2, 4)(1) => 7");
    56   assert.equal(sum(2)(4, 2), 8, "sum(2)(4, 2) => 8");
    57   assert.equal(sum(2)(4)(3), 9, "sum(2)(4)(3) => 9");
    58 };
    60 exports['test compose'] = function(assert) {
    61   let greet = function(name) { return 'hi: ' + name; };
    62   let exclaim = function(sentence) { return sentence + '!'; };
    64   assert.equal(compose(exclaim, greet)('moe'), 'hi: moe!',
    65                'can compose a function that takes another');
    67   assert.equal(compose(greet, exclaim)('moe'), 'hi: moe!',
    68                'in this case, the functions are also commutative');
    70   let target = {
    71     name: 'Joe',
    72     greet: compose(function exclaim(sentence) {
    73       return sentence + '!';
    74     }, function(title) {
    75       return 'hi : ' + title + ' ' + this.name;
    76     })
    77   };
    79   assert.equal(target.greet('Mr'), 'hi : Mr Joe!',
    80                'this can be passed in');
    81   assert.equal(target.greet.call({ name: 'Alex' }, 'Dr'), 'hi : Dr Alex!',
    82                'this can be applied');
    84   let single = compose(function(value) {
    85     return value + ':suffix';
    86   });
    88   assert.equal(single('text'), 'text:suffix', 'works with single function');
    90   let identity = compose();
    91   assert.equal(identity('bla'), 'bla', 'works with zero functions');
    92 };
    94 exports['test wrap'] = function(assert) {
    95   let greet = function(name) { return 'hi: ' + name; };
    96   let backwards = wrap(greet, function(f, name) {
    97     return f(name) + ' ' + name.split('').reverse().join('');
    98   });
   100   assert.equal(backwards('moe'), 'hi: moe eom',
   101                'wrapped the saluation function');
   103   let inner = function () { return 'Hello '; };
   104   let target = {
   105     name: 'Matteo',
   106     hi: wrap(inner, function(f) { return f() + this.name; })
   107   };
   109   assert.equal(target.hi(), 'Hello Matteo', 'works with this');
   111   function noop() { }
   112   let wrapped = wrap(noop, function(f) {
   113     return Array.slice(arguments);
   114   });
   116   let actual = wrapped([ 'whats', 'your' ], 'vector', 'victor');
   117   assert.deepEqual(actual, [ noop, ['whats', 'your'], 'vector', 'victor' ],
   118                    'works with fancy stuff');
   119 };
   121 exports['test memoize'] = function(assert) {
   122   const fib = n => n < 2 ? n : fib(n - 1) + fib(n - 2);
   123   let fibnitro = memoize(fib);
   125   assert.equal(fib(10), 55,
   126         'a memoized version of fibonacci produces identical results');
   127   assert.equal(fibnitro(10), 55,
   128         'a memoized version of fibonacci produces identical results');
   130   function o(key, value) { return value; }
   131   let oo = memoize(o), v1 = {}, v2 = {};
   134   assert.equal(oo(1, v1), v1, 'returns value back');
   135   assert.equal(oo(1, v2), v1, 'memoized by a first argument');
   136   assert.equal(oo(2, v2), v2, 'returns back value if not memoized');
   137   assert.equal(oo(2), v2, 'memoized new value');
   138   assert.notEqual(oo(1), oo(2), 'values do not override');
   139   assert.equal(o(3, v2), oo(2, 3), 'returns same value as un-memoized');
   141   let get = memoize(function(attribute) { return this[attribute]; });
   142   let target = { name: 'Bob', get: get };
   144   assert.equal(target.get('name'), 'Bob', 'has correct `this`');
   145   assert.equal(target.get.call({ name: 'Jack' }, 'name'), 'Bob',
   146                'name is memoized');
   147   assert.equal(get('name'), 'Bob', 'once memoized can be called without this');
   148 };
   150 exports['test delay'] = function(assert, done) {
   151   let delayed = false;
   152   delay(function() {
   153     assert.ok(delayed, 'delayed the function');
   154     done();
   155   }, 1);
   156   delayed = true;
   157 };
   159 exports['test delay with this'] = function(assert, done) {
   160   let context = {};
   161   delay.call(context, function(name) {
   162     assert.equal(this, context, 'this was passed in');
   163     assert.equal(name, 'Tom', 'argument was passed in');
   164     done();
   165   }, 10, 'Tom');
   166 };
   168 exports['test once'] = function(assert) {
   169   let n = 0;
   170   let increment = once(function() { n ++; });
   172   increment();
   173   increment();
   175   assert.equal(n, 1, 'only incremented once');
   177   let target = {
   178     state: 0,
   179     update: once(function() {
   180       return this.state ++;
   181     })
   182   };
   184   target.update();
   185   target.update();
   187   assert.equal(target.state, 1, 'this was passed in and called only once');
   188 };
   190 exports['test once with argument'] = function(assert) {
   191   let n = 0;
   192   let increment = once(a => n++);
   194   increment();
   195   increment('foo');
   197   assert.equal(n, 1, 'only incremented once');
   199   increment();
   200   increment('foo');
   202   assert.equal(n, 1, 'only incremented once');
   203 };
   205 exports['test complement'] = assert => {
   206   let { complement } = require("sdk/lang/functional");
   208   let isOdd = x => Boolean(x % 2);
   210   assert.equal(isOdd(1), true);
   211   assert.equal(isOdd(2), false);
   213   let isEven = complement(isOdd);
   215   assert.equal(isEven(1), false);
   216   assert.equal(isEven(2), true);
   218   let foo = {};
   219   let isFoo = function() { return this === foo; };
   220   let insntFoo = complement(isFoo);
   222   assert.equal(insntFoo.call(foo), false);
   223   assert.equal(insntFoo.call({}), true);
   224 };
   226 exports['test constant'] = assert => {
   227   let { constant } = require("sdk/lang/functional");
   229   let one = constant(1);
   231   assert.equal(one(1), 1);
   232   assert.equal(one(2), 1);
   233 };
   235 exports['test apply'] = assert => {
   236   let { apply } = require("sdk/lang/functional");
   238   let dashify = (...args) => args.join("-");
   240   assert.equal(apply(dashify, 1, [2, 3]), "1-2-3");
   241   assert.equal(apply(dashify, "a"), "a");
   242   assert.equal(apply(dashify, ["a", "b"]), "a-b");
   243   assert.equal(apply(dashify, ["a", "b"], "c"), "a,b-c");
   244   assert.equal(apply(dashify, [1, 2], [3, 4]), "1,2-3-4");
   245 };
   247 exports['test flip'] = assert => {
   248   let { flip } = require("sdk/lang/functional");
   250   let append = (left, right) => left + " " + right;
   251   let prepend = flip(append);
   253   assert.equal(append("hello", "world"), "hello world");
   254   assert.equal(prepend("hello", "world"), "world hello");
   256   let wrap = function(left, right) {
   257     return left + " " + this + " " + right;
   258   };
   259   let invertWrap = flip(wrap);
   261   assert.equal(wrap.call("@", "hello", "world"), "hello @ world");
   262   assert.equal(invertWrap.call("@", "hello", "world"), "world @ hello");
   264   let reverse = flip((...args) => args);
   266   assert.deepEqual(reverse(1, 2, 3, 4), [4, 3, 2, 1]);
   267   assert.deepEqual(reverse(1), [1]);
   268   assert.deepEqual(reverse(), []);
   270   // currying still works
   271   let prependr = curry(prepend);
   273   assert.equal(prependr("hello", "world"), "world hello");
   274   assert.equal(prependr("hello")("world"), "world hello");
   275 };
   277 exports["test when"] = assert => {
   278   let { when } = require("sdk/lang/functional");
   280   let areNums = (...xs) => xs.every(x => typeof(x) === "number");
   282   let sum = when(areNums, (...xs) => xs.reduce((y, x) => x + y, 0));
   284   assert.equal(sum(1, 2, 3), 6);
   285   assert.equal(sum(1, 2, "3"), undefined);
   287   let multiply = when(areNums,
   288                       (...xs) => xs.reduce((y, x) => x * y, 1),
   289                       (...xs) => xs);
   291   assert.equal(multiply(2), 2);
   292   assert.equal(multiply(2, 3), 6);
   293   assert.deepEqual(multiply(2, "4"), [2, "4"]);
   295   function Point(x, y) {
   296     this.x = x;
   297     this.y = y;
   298   }
   300   let isPoint = x => x instanceof Point;
   302   let inc = when(isPoint, ({x, y}) => new Point(x + 1, y + 1));
   304   assert.equal(inc({}), undefined);
   305   assert.deepEqual(inc(new Point(0, 0)), { x: 1, y: 1 });
   307   let axis = when(isPoint,
   308                   ({ x, y }) => [x, y],
   309                   _ => [0, 0]);
   311   assert.deepEqual(axis(new Point(1, 4)), [1, 4]);
   312   assert.deepEqual(axis({ foo: "bar" }), [0, 0]);
   313 };
   315 exports["test chainable"] = function(assert) {
   316   let Player = function () { this.volume = 5; };
   317   Player.prototype = {
   318     setBand: chainable(function (band) { return (this.band = band); }),
   319     incVolume: chainable(function () { return this.volume++; })
   320   };
   321   let player = new Player();
   322   player
   323     .setBand('Animals As Leaders')
   324     .incVolume().incVolume().incVolume().incVolume().incVolume().incVolume();
   326   assert.equal(player.band, 'Animals As Leaders', 'passes arguments into chained');
   327   assert.equal(player.volume, 11, 'accepts no arguments in chain');
   328 };
   330 exports["test field"] = assert => {
   331   let Num = field("constructor", 0);
   332   assert.equal(Num.name, Number.name);
   333   assert.ok(typeof(Num), "function");
   335   let x = field("x");
   337   [
   338     [field("foo", { foo: 1 }), 1],
   339     [field("foo")({ foo: 1 }), 1],
   340     [field("bar", {}), undefined],
   341     [field("bar")({}), undefined],
   342     [field("hey", undefined), undefined],
   343     [field("hey")(undefined), undefined],
   344     [field("how", null), null],
   345     [field("how")(null), null],
   346     [x(1), undefined],
   347     [x(undefined), undefined],
   348     [x(null), null],
   349     [x({ x: 1 }), 1],
   350     [x({ x: 2 }), 2],
   351   ].forEach(([actual, expected]) => assert.equal(actual, expected));
   352 };
   354 exports["test query"] = assert => {
   355   let Num = query("constructor", 0);
   356   assert.equal(Num.name, Number.name);
   357   assert.ok(typeof(Num), "function");
   359   let x = query("x");
   360   let xy = query("x.y");
   362   [
   363     [query("foo", { foo: 1 }), 1],
   364     [query("foo")({ foo: 1 }), 1],
   365     [query("foo.bar", { foo: { bar: 2 } }), 2],
   366     [query("foo.bar")({ foo: { bar: 2 } }), 2],
   367     [query("foo.bar", { foo: 1 }), undefined],
   368     [query("foo.bar")({ foo: 1 }), undefined],
   369     [x(1), undefined],
   370     [x(undefined), undefined],
   371     [x(null), null],
   372     [x({ x: 1 }), 1],
   373     [x({ x: 2 }), 2],
   374     [xy(1), undefined],
   375     [xy(undefined), undefined],
   376     [xy(null), null],
   377     [xy({ x: 1 }), undefined],
   378     [xy({ x: 2 }), undefined],
   379     [xy({ x: { y: 1 } }), 1],
   380     [xy({ x: { y: 2 } }), 2]
   381   ].forEach(([actual, expected]) => assert.equal(actual, expected));
   382 };
   384 exports["test isInstance"] = assert => {
   385   function X() {}
   386   function Y() {}
   387   let isX = isInstance(X);
   389   [
   390     isInstance(X, new X()),
   391     isInstance(X)(new X()),
   392     !isInstance(X, new Y()),
   393     !isInstance(X)(new Y()),
   394     isX(new X()),
   395     !isX(new Y())
   396   ].forEach(x => assert.ok(x));
   397 };
   399 exports["test is"] = assert => {
   401   assert.deepEqual([ 1, 0, 1, 0, 1 ].map(is(1)),
   402                    [ true, false, true, false, true ],
   403                    "is can be partially applied");
   405   assert.ok(is(1, 1));
   406   assert.ok(!is({}, {}));
   407   assert.ok(is()(1)()(1), "is is curried");
   408   assert.ok(!is()(1)()(2));
   409 };
   411 exports["test isnt"] = assert => {
   413   assert.deepEqual([ 1, 0, 1, 0, 1 ].map(isnt(0)),
   414                    [ true, false, true, false, true ],
   415                    "is can be partially applied");
   417   assert.ok(!isnt(1, 1));
   418   assert.ok(isnt({}, {}));
   419   assert.ok(!isnt()(1)()(1));
   420   assert.ok(isnt()(1)()(2));
   421 };
   423 exports["test debounce"] = (assert, done) => {
   424   let counter = 0;
   425   let fn = debounce(() => counter++, 100);
   427   new Array(10).join(0).split("").forEach(fn);
   429   assert.equal(counter, 0, "debounce does not fire immediately");
   430   setTimeout(() => {
   431     assert.equal(counter, 1, "function called after wait time");
   432     fn();
   433     setTimeout(() => {
   434       assert.equal(counter, 2, "function able to be called again after wait");
   435       done();
   436     }, 150);
   437   }, 200);
   438 };
   440 exports["test throttle"] = (assert, done) => {
   441   let called = 0;
   442   let attempt = 0;
   443   let atleast100ms = false;
   444   let throttledFn = throttle(() => {
   445     called++;
   446     if (called === 2) {
   447       assert.equal(attempt, 10, "called twice, but attempted 10 times");
   448       fn();
   449     }
   450     if (called === 3) {
   451       assert.ok(atleast100ms, "atleast 100ms have passed");
   452       assert.equal(attempt, 11, "called third, waits for delay to happen");
   453       done();
   454     }
   455   }, 200);
   456   let fn = () => ++attempt && throttledFn();
   458   setTimeout(() => atleast100ms = true, 100);
   460   new Array(11).join(0).split("").forEach(fn);
   461 };
   463 require('test').run(exports);

mercurial