addon-sdk/source/test/test-traits.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/. */
     5 "use strict";
     7 const { Trait } = require('sdk/deprecated/traits');
     9 exports['test:simple compose'] = function(assert) {
    10   let List = Trait.compose({
    11     _list: null,
    12     constructor: function List() {
    13       this._list = [];
    14     },
    15     list: function list() this._list.slice(0),
    16     add: function add(item) this._list.push(item),
    17     remove: function remove(item) {
    18       let list = this._list;
    19       let index = list.indexOf(item);
    20       if (0 <= index) list.slice(index, 1);
    21     }
    22   });
    24   assert.notEqual(undefined, List, 'should not be undefined');
    25   assert.equal('function', typeof List, 'type should be function');
    26   assert.equal(
    27     Trait.compose,
    28     List.compose,
    29     'should inherit static compose'
    30   );
    31   assert.equal(
    32     Trait.override,
    33     List.override,
    34     'should inherit static override'
    35   );
    36   assert.equal(
    37     Trait.required,
    38     List.required,
    39     'should inherit static required'
    40   );
    41   assert.equal(
    42     Trait.resolve,
    43     List.resolve,
    44     'should inherit static resolve'
    45   );
    47   assert.ok(
    48     !('_list' in List.prototype),
    49     'should not expose private API'
    50   );
    51 }
    52 exports['test: compose trait instance and create instance'] = function(assert) {
    53   let List = Trait.compose({
    54     constructor: function List(options) {
    55       this._list = [];
    56       this._public.publicMember = options.publicMember;
    57     },
    58     _privateMember: true,
    59     get privateMember() this._privateMember,
    60     get list() this._list.slice(0),
    61     add: function add(item) this._list.push(item),
    62     remove: function remove(item) {
    63       let list = this._list
    64       let index = list.indexOf(item)
    65       if (0 <= index) list.slice(index, 1)
    66     }
    67   });
    68   let list = List({ publicMember: true });
    70   assert.equal('object', typeof list, 'should return an object')
    71   assert.equal(
    72     true,
    73     list instanceof List,
    74     'should be instance of a List'
    75   );
    77   assert.equal(
    78     undefined,
    79     list._privateMember,
    80     'instance should not expose private API'
    81   );
    83   assert.equal(
    84     true,
    85     list.privateMember,
    86     'privates are accessible by  public API'
    87   );
    89   list._privateMember = false;
    91   assert.equal(
    92     true,
    93     list.privateMember,
    94     'property changes on instance must not affect privates'
    95   );
    97   assert.ok(
    98     !('_list' in list),
    99     'instance should not expose private members'
   100   );
   102   assert.equal(
   103     true,
   104     list.publicMember,
   105     'public members are exposed'
   106   )
   107   assert.equal(
   108     'function',
   109     typeof list.add,
   110     'should be function'
   111   )
   112   assert.equal(
   113     'function',
   114     typeof list.remove,
   115     'should be function'
   116   );
   118   list.add(1);
   119   assert.equal(
   120     1,
   121     list.list[0],
   122     'exposed public API should be able of modifying privates'
   123   )
   124 };
   127 exports['test:instances must not be hackable'] = function(assert) {
   128   let SECRET = 'There is no secret!',
   129       secret = null;
   131   let Class = Trait.compose({
   132     _secret: null,
   133     protect: function(data) this._secret = data
   134   });
   136   let i1 = Class();
   137   i1.protect(SECRET);
   139   assert.equal(
   140     undefined,
   141     (function() this._secret).call(i1),
   142     'call / apply can\'t access private state'
   143   );
   145   let proto = Object.getPrototypeOf(i1);
   146   try {
   147     proto.reveal = function() this._secret;
   148     secret = i1.reveal();
   149   } catch(e) {}
   150   assert.notEqual(
   151     SECRET,
   152     secret,
   153     'public __proto__ changes should not affect privates'
   154   );
   155   secret = null;
   157   let Class2 = Trait.compose({
   158     _secret: null,
   159     protect: function(data) this._secret = data
   160   });
   161   let i2 = Class2();
   162   i2.protect(SECRET);
   163   try {
   164     Object.prototype.reveal = function() this._secret;
   165     secret = i2.reveal();
   166   } catch(e) {}
   167   assert.notEqual(
   168     SECRET,
   169     secret,
   170     'Object.prototype changes must not affect instances'
   171   );
   172 }
   174 exports['test:instanceof'] = function(assert) {
   175   const List = Trait.compose({
   176     // private API:
   177     _list: null,
   178     // public API
   179     constructor: function List() {
   180       this._list = []
   181     },
   182     get length() this._list.length,
   183     add: function add(item) this._list.push(item),
   184     remove: function remove(item) {
   185       let list = this._list;
   186       let index = list.indexOf(item);
   187       if (0 <= index) list.slice(index, 1);
   188     }
   189   });
   191   assert.ok(List() instanceof List, 'Must be instance of List');
   192   assert.ok(new List() instanceof List, 'Must be instance of List');
   193 };
   195 exports['test:privates are unaccessible'] = function(assert) {
   196   const List = Trait.compose({
   197     // private API:
   198     _list: null,
   199     // public API
   200     constructor: function List() {
   201       this._list = [];
   202     },
   203     get length() this._list.length,
   204     add: function add(item) this._list.push(item),
   205     remove: function remove(item) {
   206       let list = this._list;
   207       let index = list.indexOf(item);
   208       if (0 <= index) list.slice(index, 1);
   209     }
   210   });
   212   let list = List();
   213   assert.ok(!('_list' in list), 'no privates on instance');
   214   assert.ok(
   215     !('_list' in List.prototype),
   216     'no privates on prototype'
   217   );
   218 };
   220 exports['test:public API can access private API'] = function(assert) {
   221   const List = Trait.compose({
   222     // private API:
   223     _list: null,
   224     // public API
   225     constructor: function List() {
   226       this._list = [];
   227     },
   228     get length() this._list.length,
   229     add: function add(item) this._list.push(item),
   230     remove: function remove(item) {
   231       let list = this._list;
   232       let index = list.indexOf(item);
   233       if (0 <= index) list.slice(index, 1);
   234     }
   235   });
   236   let list = List();
   238   list.add('test');
   240   assert.equal(
   241     1,
   242     list.length,
   243     'should be able to add element and access it from public getter'
   244   );
   245 };
   247 exports['test:required'] = function(assert) {
   248   const Enumerable = Trait.compose({
   249     list: Trait.required,
   250     forEach: function forEach(consumer) {
   251       return this.list.forEach(consumer);
   252     }
   253   });
   255   try {
   256     let i = Enumerable();
   257     assert.fail('should throw when creating instance with required properties');
   258   } catch(e) {
   259     assert.equal(
   260       'Error: Missing required property: list',
   261       e.toString(),
   262       'required prop error'
   263     );
   264   }
   265 };
   267 exports['test:compose with required'] = function(assert) {
   268   const List = Trait.compose({
   269     // private API:
   270     _list: null,
   271     // public API
   272     constructor: function List() {
   273       this._list = [];
   274     },
   275     get length() this._list.length,
   276     add: function add(item) this._list.push(item),
   277     remove: function remove(item) {
   278       let list = this._list;
   279       let index = list.indexOf(item);
   280       if (0 <= index) list.slice(index, 1);
   281     }
   282   });
   284   const Enumerable = Trait.compose({
   285     list: Trait.required,
   286     forEach: function forEach(consumer) {
   287       return this.list.forEach(consumer);
   288     }
   289   });
   291   const EnumerableList = Enumerable.compose({
   292     get list() this._list.slice(0)
   293   }, List);
   295   let array = [1,2, 'ab']
   296   let l = EnumerableList(array);
   297   array.forEach(function(element) l.add(element));
   298   let number = 0;
   299   l.forEach(function(element, index) {
   300     number ++;
   301     assert.equal(array[index], element, 'should mach array element')
   302   });
   303   assert.equal(
   304     array.length,
   305     number,
   306     'should perform as many asserts as elements in array'
   307   );
   308 };
   310 exports['test:resolve'] = function(assert) {
   311   const List = Trait.compose({
   312     // private API:
   313     _list: null,
   314     // public API
   315     constructor: function List() {
   316       this._list = [];
   317     },
   318     get length() this._list.length,
   319     add: function add(item) this._list.push(item),
   320     remove: function remove(item) {
   321       let list = this._list;
   322       let index = list.indexOf(item);
   323       if (0 <= index) list.slice(index, 1);
   324     }
   325   });
   327   const Range = List.resolve({
   328     constructor: null,
   329     add: '_add',
   330   }).compose({
   331     min: null,
   332     max: null,
   333     get list() this._list.slice(0),
   334     constructor: function Range(min, max) {
   335       this.min = min;
   336       this.max = max;
   337       this._list = [];
   338     },
   339     add: function(item) {
   340       if (item <= this.max && item >= this.min)
   341         this._add(item)
   342     }
   343   });
   345   let r = Range(0, 10);
   347   assert.equal(
   348     0,
   349     r.min,
   350     'constructor must have set min'
   351   );
   352   assert.equal(
   353     10,
   354     r.max,
   355     'constructor must have set max'
   356   );
   358   assert.equal(
   359     0,
   360     r.length,
   361     'should not contain any elements'
   362   );
   364   r.add(5);
   366   assert.equal(
   367     1,
   368     r.length,
   369     'should add `5` to list'
   370   );
   372   r.add(12);
   374   assert.equal(
   375     1,
   376     r.length,
   377     'should not add `12` to list'
   378   );
   379 };
   381 exports['test:custom iterator'] = function(assert) {
   382   let Sub = Trait.compose({
   383     foo: "foo",
   384     bar: "bar",
   385     baz: "baz",
   386     __iterator__: function() {
   387       yield 1;
   388       yield 2;
   389       yield 3;
   390     }
   391   });
   393   let (i = 0, sub = Sub()) {
   394     for (let item in sub)
   395     assert.equal(++i, item, "iterated item has the right value");
   396   };
   397 };
   399 require('sdk/test').run(exports);

mercurial