addon-sdk/source/test/test-traits-core.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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 ERR_CONFLICT = 'Remaining conflicting property: ',
     7       ERR_REQUIRED = 'Missing required property: ';
     9 function assertSametrait(assert, trait1, trait2) {
    10   let names1 = Object.getOwnPropertyNames(trait1),
    11       names2 = Object.getOwnPropertyNames(trait2);
    13   assert.equal(
    14     names1.length,
    15     names2.length,
    16     'equal traits must have same amount of properties'
    17   );
    19   for (let i = 0; i < names1.length; i++) {
    20     let name = names1[i];
    21     assert.notEqual(
    22       -1,
    23       names2.indexOf(name),
    24       'equal traits must contain same named properties: ' + name
    25     );
    26     assertSameDescriptor(assert, name, trait1[name], trait2[name]);
    27   }
    28 }
    30 function assertSameDescriptor(assert, name, desc1, desc2) {
    31   if (desc1.conflict || desc2.conflict) {
    32     assert.equal(
    33       desc1.conflict,
    34       desc2.conflict,
    35       'if one of same descriptors has `conflict` another must have it: '
    36         + name
    37     );
    38   }
    39   else if (desc1.required || desc2.required) {
    40     assert.equal(
    41       desc1.required,
    42       desc2.required,
    43       'if one of same descriptors is has `required` another must have it: '
    44         + name
    45     );
    46   }
    47   else {
    48     assert.equal(
    49       desc1.get,
    50       desc2.get,
    51       'get must be the same on both descriptors: ' + name
    52     );
    53     assert.equal(
    54       desc1.set,
    55       desc2.set,
    56       'set must be the same on both descriptors: ' + name
    57     );
    58     assert.equal(
    59       desc1.value,
    60       desc2.value,
    61       'value must be the same on both descriptors: ' + name
    62     );
    63     assert.equal(
    64       desc1.enumerable,
    65       desc2.enumerable,
    66       'enumerable must be the same on both descriptors: ' + name
    67     );
    68     assert.equal(
    69       desc1.required,
    70       desc2.required,
    71       'value must be the same on both descriptors: ' + name
    72     );
    73   }
    74 }
    76 function Data(value, enumerable, confligurable, writable) {
    77   return {
    78     value: value,
    79     enumerable: false !== enumerable,
    80     confligurable: false !== confligurable,
    81     writable: false !== writable
    82   };
    83 }
    85 function Method(method, enumerable, confligurable, writable) {
    86   return {
    87     value: method,
    88     enumerable: false !== enumerable,
    89     confligurable: false !== confligurable,
    90     writable: false !== writable
    91   };
    92 }
    94 function Accessor(get, set, enumerable, confligurable) {
    95   return {
    96     get: get,
    97     set: set,
    98     enumerable: false !== enumerable,
    99     confligurable: false !== confligurable,
   100   };
   101 }
   103 function Required(name) {
   104   function required() { throw new Error(ERR_REQUIRED + name) }
   105   return {
   106     get: required,
   107     set: required,
   108     required: true
   109   };
   110 }
   112 function Conflict(name) {
   113   function conflict() { throw new Error(ERR_CONFLICT + name) }
   114   return {
   115     get: conflict,
   116     set: conflict,
   117     conflict: true
   118   };
   119 }
   121 function testMethod() {};
   123 const { trait, compose, resolve, required, override, create } =
   124   require('sdk/deprecated/traits/core');
   127 exports['test:empty trait'] = function(assert) {
   128   assertSametrait(
   129     assert,
   130     trait({}),
   131     {}
   132   );
   133 };
   135 exports['test:simple trait'] = function(assert) {
   136   assertSametrait(
   137     assert,
   138     trait({
   139       a: 0,
   140       b: testMethod
   141     }),
   142     {
   143       a: Data(0, true, true, true),
   144       b: Method(testMethod, true, true, true)
   145     }
   146   );
   147 };
   149 exports['test:simple trait with required prop'] = function(assert) {
   150   assertSametrait(
   151     assert,
   152     trait({
   153       a: required,
   154       b: 1
   155     }),
   156     {
   157       a: Required('a'),
   158       b: Data(1)
   159     }
   160   );
   161 };
   163 exports['test:ordering of trait properties is irrelevant'] = function(assert) {
   164   assertSametrait(
   165     assert,
   166     trait({ a: 0, b: 1, c: required }),
   167     trait({ b: 1, c: required, a: 0 })
   168   );
   169 };
   171 exports['test:trait with accessor property'] = function(assert) {
   172   let record = { get a() {}, set a(v) {} };
   173   let get = Object.getOwnPropertyDescriptor(record,'a').get;
   174   let set = Object.getOwnPropertyDescriptor(record,'a').set;
   175   assertSametrait(assert,
   176     trait(record),
   177     { a: Accessor(get, set ) }
   178   );
   179 };
   181 exports['test:simple composition'] = function(assert) {
   182   assertSametrait(
   183     assert,
   184     compose(
   185       trait({ a: 0, b: 1 }),
   186       trait({ c: 2, d: testMethod })
   187     ),
   188     {
   189       a: Data(0),
   190       b: Data(1),
   191       c: Data(2),
   192       d: Method(testMethod)
   193     }
   194   );
   195 };
   197 exports['test:composition with conflict'] = function(assert) {
   198   assertSametrait(
   199     assert,
   200     compose(
   201       trait({ a: 0, b: 1 }),
   202       trait({ a: 2, c: testMethod })
   203     ),
   204     {
   205       a: Conflict('a'),
   206       b: Data(1),
   207       c: Method(testMethod)
   208     }
   209   );
   210 };
   212 exports['test:composition of identical props does not cause conflict'] =
   213 function(assert) {
   214   assertSametrait(assert,
   215     compose(
   216       trait({ a: 0, b: 1 }),
   217       trait({ a: 0, c: testMethod })
   218     ),
   219     {
   220       a: Data(0),
   221       b: Data(1),
   222       c: Method(testMethod) }
   223   )
   224 };
   226 exports['test:composition with identical required props'] =
   227 function(assert) {
   228   assertSametrait(assert,
   229     compose(
   230       trait({ a: required, b: 1 }),
   231       trait({ a: required, c: testMethod })
   232     ),
   233     {
   234       a: Required(),
   235       b: Data(1),
   236       c: Method(testMethod)
   237     }
   238   );
   239 };
   241 exports['test:composition satisfying a required prop'] = function (assert) {
   242   assertSametrait(assert,
   243     compose(
   244       trait({ a: required, b: 1 }),
   245       trait({ a: testMethod })
   246     ),
   247     {
   248       a: Method(testMethod),
   249       b: Data(1)
   250     }
   251   );
   252 };
   254 exports['test:compose is neutral wrt conflicts'] = function (assert) {
   255   assertSametrait(assert,
   256     compose(
   257       compose(
   258         trait({ a: 1 }),
   259         trait({ a: 2 })
   260       ),
   261       trait({ b: 0 })
   262     ),
   263     {
   264       a: Conflict('a'),
   265       b: Data(0)
   266     }
   267   );
   268 };
   270 exports['test:conflicting prop overrides required prop'] = function (assert) {
   271   assertSametrait(assert,
   272     compose(
   273       compose(
   274         trait({ a: 1 }),
   275         trait({ a: 2 })
   276       ),
   277       trait({ a: required })
   278     ),
   279     {
   280       a: Conflict('a')
   281     }
   282   );
   283 };
   285 exports['test:compose is commutative'] = function (assert) {
   286   assertSametrait(assert,
   287     compose(
   288       trait({ a: 0, b: 1 }),
   289       trait({ c: 2, d: testMethod })
   290     ),
   291     compose(
   292       trait({ c: 2, d: testMethod }),
   293       trait({ a: 0, b: 1 })
   294     )
   295   );
   296 };
   298 exports['test:compose is commutative, also for required/conflicting props'] =
   299 function (assert) {
   300   assertSametrait(assert,
   301     compose(
   302       trait({ a: 0, b: 1, c: 3, e: required }),
   303       trait({ c: 2, d: testMethod })
   304     ),
   305     compose(
   306       trait({ c: 2, d: testMethod }),
   307       trait({ a: 0, b: 1, c: 3, e: required })
   308     )
   309   );
   310 };
   311 exports['test:compose is associative'] = function (assert) {
   312   assertSametrait(assert,
   313     compose(
   314       trait({ a: 0, b: 1, c: 3, d: required }),
   315       compose(
   316         trait({ c: 3, d: required }),
   317         trait({ c: 2, d: testMethod, e: 'foo' })
   318       )
   319     ),
   320     compose(
   321       compose(
   322         trait({ a: 0, b: 1, c: 3, d: required }),
   323         trait({ c: 3, d: required })
   324       ),
   325       trait({ c: 2, d: testMethod, e: 'foo' })
   326     )
   327   );
   328 };
   330 exports['test:diamond import of same prop does not generate conflict'] =
   331 function (assert) {
   332   assertSametrait(assert,
   333     compose(
   334       compose(
   335         trait({ b: 2 }),
   336         trait({ a: 1 })
   337       ),
   338       compose(
   339         trait({ c: 3 }),
   340         trait({ a: 1 })
   341       ),
   342       trait({ d: 4 })
   343     ),
   344     {
   345       a: Data(1),
   346       b: Data(2),
   347       c: Data(3),
   348       d: Data(4)
   349     }
   350   );
   351 };
   353 exports['test:resolve with empty resolutions has no effect'] =
   354 function (assert) {
   355   assertSametrait(assert, resolve({}, trait({
   356     a: 1,
   357     b: required,
   358     c: testMethod
   359   })), {
   360     a: Data(1),
   361     b: Required(),
   362     c: Method(testMethod)
   363   });
   364 };
   366 exports['test:resolve: renaming'] = function (assert) {
   367   assertSametrait(assert,
   368     resolve(
   369       { a: 'A', c: 'C' },
   370       trait({ a: 1, b: required, c: testMethod })
   371     ),
   372     {
   373       A: Data(1),
   374       b: Required(),
   375       C: Method(testMethod),
   376       a: Required(),
   377       c: Required()
   378     }
   379   );
   380 };
   382 exports['test:resolve: renaming to conflicting name causes conflict, order 1']
   383 = function (assert) {
   384   assertSametrait(assert,
   385     resolve(
   386       { a: 'b'},
   387       trait({ a: 1, b: 2 })
   388     ),
   389     {
   390       b: Conflict('b'),
   391       a: Required()
   392     }
   393   );
   394 };
   396 exports['test:resolve: renaming to conflicting name causes conflict, order 2']
   397 = function (assert) {
   398   assertSametrait(assert,
   399     resolve(
   400       { a: 'b' },
   401       trait({ b: 2, a: 1 })
   402     ),
   403     {
   404       b: Conflict('b'),
   405       a: Required()
   406     }
   407   );
   408 };
   410 exports['test:resolve: simple exclusion'] = function (assert) {
   411   assertSametrait(assert,
   412     resolve(
   413       { a: undefined },
   414       trait({ a: 1, b: 2 })
   415     ),
   416     {
   417       a: Required(),
   418       b: Data(2)
   419     }
   420   );
   421 };
   423 exports['test:resolve: exclusion to "empty" trait'] = function (assert) {
   424   assertSametrait(assert,
   425     resolve(
   426       { a: undefined, b: undefined },
   427       trait({ a: 1, b: 2 })
   428     ),
   429     {
   430       a: Required(),
   431       b: Required()
   432     }
   433   );
   434 };
   436 exports['test:resolve: exclusion and renaming of disjoint props'] =
   437 function (assert) {
   438   assertSametrait(assert,
   439     resolve(
   440       { a: undefined, b: 'c' },
   441       trait({ a: 1, b: 2 })
   442     ),
   443     {
   444       a: Required(),
   445       c: Data(2),
   446       b: Required()
   447     }
   448   );
   449 };
   451 exports['test:resolve: exclusion and renaming of overlapping props'] =
   452 function (assert) {
   453   assertSametrait(assert,
   454     resolve(
   455       { a: undefined, b: 'a' },
   456       trait({ a: 1, b: 2 })
   457     ),
   458     {
   459       a: Data(2),
   460       b: Required()
   461     }
   462   );
   463 };
   465 exports['test:resolve: renaming to a common alias causes conflict'] =
   466 function (assert) {
   467   assertSametrait(assert,
   468     resolve(
   469       { a: 'c', b: 'c' },
   470       trait({ a: 1, b: 2 })
   471     ),
   472     {
   473       c: Conflict('c'),
   474       a: Required(),
   475       b: Required()
   476     }
   477   );
   478 };
   480 exports['test:resolve: renaming overrides required target'] =
   481 function (assert) {
   482   assertSametrait(assert,
   483     resolve(
   484       { b: 'a' },
   485       trait({ a: required, b: 2 })
   486     ),
   487     {
   488       a: Data(2),
   489       b: Required()
   490     }
   491   );
   492 };
   494 exports['test:resolve: renaming required properties has no effect'] =
   495 function (assert) {
   496   assertSametrait(assert,
   497     resolve(
   498       { b: 'a' },
   499       trait({ a: 2, b: required })
   500     ),
   501     {
   502       a: Data(2),
   503       b: Required()
   504     }
   505   );
   506 };
   508 exports['test:resolve: renaming of non-existent props has no effect'] =
   509 function (assert) {
   510   assertSametrait(assert,
   511     resolve(
   512       { a: 'c', d: 'c' },
   513       trait({ a: 1, b: 2 })
   514     ),
   515     {
   516       c: Data(1),
   517       b: Data(2),
   518       a: Required()
   519     }
   520   );
   521 };
   523 exports['test:resolve: exclusion of non-existent props has no effect'] =
   524 function (assert) {
   525   assertSametrait(assert,
   526     resolve(
   527       { b: undefined },
   528       trait({ a: 1 })
   529     ),
   530     {
   531       a: Data(1)
   532     }
   533   );
   534 };
   536 exports['test:resolve is neutral w.r.t. required properties'] =
   537 function (assert) {
   538   assertSametrait(assert,
   539     resolve(
   540       { a: 'c', b: undefined },
   541       trait({ a: required, b: required, c: 'foo', d: 1 })
   542     ),
   543     {
   544       a: Required(),
   545       b: Required(),
   546       c: Data('foo'),
   547       d: Data(1)
   548     }
   549   );
   550 };
   552 exports['test:resolve supports swapping of property names, ordering 1'] =
   553 function (assert) {
   554   assertSametrait(assert,
   555     resolve(
   556       { a: 'b', b: 'a' },
   557       trait({ a: 1, b: 2 })
   558     ),
   559     {
   560       a: Data(2),
   561       b: Data(1)
   562     }
   563   );
   564 };
   566 exports['test:resolve supports swapping of property names, ordering 2'] =
   567 function (assert) {
   568   assertSametrait(assert,
   569     resolve(
   570       { b: 'a', a: 'b' },
   571       trait({ a: 1, b: 2 })
   572     ),
   573     {
   574       a: Data(2),
   575       b: Data(1)
   576     }
   577   );
   578 };
   580 exports['test:resolve supports swapping of property names, ordering 3'] =
   581 function (assert) {
   582   assertSametrait(assert,
   583     resolve(
   584       { b: 'a', a: 'b' },
   585       trait({ b: 2, a: 1 })
   586     ),
   587     {
   588       a: Data(2),
   589       b: Data(1)
   590     }
   591   );
   592 };
   594 exports['test:resolve supports swapping of property names, ordering 4'] =
   595 function (assert) {
   596   assertSametrait(assert,
   597     resolve(
   598       { a: 'b', b: 'a' },
   599       trait({ b: 2, a: 1 })
   600     ),
   601     {
   602       a: Data(2),
   603       b: Data(1)
   604     }
   605   );
   606 };
   608 exports['test:override of mutually exclusive traits'] = function (assert) {
   609   assertSametrait(assert,
   610     override(
   611       trait({ a: 1, b: 2 }),
   612       trait({ c: 3, d: testMethod })
   613     ),
   614     {
   615       a: Data(1),
   616       b: Data(2),
   617       c: Data(3),
   618       d: Method(testMethod)
   619     }
   620   );
   621 };
   623 exports['test:override of mutually exclusive traits is compose'] =
   624 function (assert) {
   625   assertSametrait(assert,
   626     override(
   627       trait({ a: 1, b: 2 }),
   628       trait({ c: 3, d: testMethod })
   629     ),
   630     compose(
   631       trait({ d: testMethod, c: 3 }),
   632       trait({ b: 2, a: 1 })
   633     )
   634   );
   635 };
   637 exports['test:override of overlapping traits'] = function (assert) {
   638   assertSametrait(assert,
   639     override(
   640       trait({ a: 1, b: 2 }),
   641       trait({ a: 3, c: testMethod })
   642     ),
   643     {
   644       a: Data(1),
   645       b: Data(2),
   646       c: Method(testMethod)
   647     }
   648   );
   649 };
   651 exports['test:three-way override of overlapping traits'] = function (assert) {
   652   assertSametrait(assert,
   653     override(
   654       trait({ a: 1, b: 2 }),
   655       trait({ b: 4, c: 3 }),
   656       trait({ a: 3, c: testMethod, d: 5 })
   657     ),
   658     {
   659       a: Data(1),
   660       b: Data(2),
   661       c: Data(3),
   662       d: Data(5)
   663     }
   664   );
   665 };
   667 exports['test:override replaces required properties'] = function (assert) {
   668   assertSametrait(assert,
   669     override(
   670       trait({ a: required, b: 2 }),
   671       trait({ a: 1, c: testMethod })
   672     ),
   673     {
   674       a: Data(1),
   675       b: Data(2),
   676       c: Method(testMethod)
   677     }
   678   );
   679 };
   681 exports['test:override is not commutative'] = function (assert) {
   682   assertSametrait(assert,
   683     override(
   684       trait({ a: 1, b: 2 }),
   685       trait({ a: 3, c: 4 })
   686     ),
   687     {
   688       a: Data(1),
   689       b: Data(2),
   690       c: Data(4)
   691     }
   692   );
   694   assertSametrait(assert,
   695     override(
   696       trait({ a: 3, c: 4 }),
   697       trait({ a: 1, b: 2 })
   698     ),
   699     {
   700       a: Data(3),
   701       b: Data(2),
   702       c: Data(4)
   703     }
   704   );
   705 };
   707 exports['test:override is associative'] = function (assert) {
   708   assertSametrait(assert,
   709     override(
   710       override(
   711         trait({ a: 1, b: 2 }),
   712         trait({ a: 3, c: 4, d: 5 })
   713       ),
   714       trait({ a: 6, c: 7, e: 8 })
   715     ),
   716     override(
   717       trait({ a: 1, b: 2 }),
   718       override(
   719         trait({ a: 3, c: 4, d: 5 }),
   720         trait({ a: 6, c: 7, e: 8 })
   721       )
   722     )
   723   );
   724 };
   726 exports['test:create simple'] = function(assert) {
   727   let o1 = create(
   728     Object.prototype,
   729     trait({ a: 1, b: function() { return this.a; } })
   730   );
   732   assert.equal(
   733     Object.prototype,
   734     Object.getPrototypeOf(o1),
   735     'o1 prototype'
   736   );
   737   assert.equal(1, o1.a, 'o1.a');
   738   assert.equal(1, o1.b(), 'o1.b()');
   739   assert.equal(
   740     2,
   741     Object.getOwnPropertyNames(o1).length,
   742     'Object.keys(o1).length === 2'
   743   );
   744 };
   746 exports['test:create with Array.prototype'] = function(assert) {
   747   let o2 = create(Array.prototype, trait({}));
   748   assert.equal(
   749     Array.prototype,
   750     Object.getPrototypeOf(o2),
   751     "o2 prototype"
   752   );
   753 };
   755 exports['test:exception for incomplete required properties'] =
   756 function(assert) {
   757   try {
   758     create(Object.prototype, trait({ foo: required }));
   759     assert.fail('expected create to complain about missing required props');
   760   }
   761   catch(e) {
   762     assert.equal(
   763       'Error: Missing required property: foo',
   764       e.toString(),
   765       'required prop error'
   766     );
   767   }
   768 };
   770 exports['test:exception for unresolved conflicts'] = function(assert) {
   771   try {
   772     create({}, compose(trait({ a: 0 }), trait({ a: 1 })));
   773     assert.fail('expected create to complain about unresolved conflicts');
   774   }
   775   catch(e) {
   776     assert.equal(
   777       'Error: Remaining conflicting property: a',
   778       e.toString(),
   779       'conflicting prop error'
   780     );
   781   }
   782 };
   784 exports['test:verify that required properties are present but undefined'] =
   785 function(assert) {
   786   try {
   787     let o4 = Object.create(Object.prototype, trait({ foo: required }));
   788     assert.equal(true, 'foo' in o4, 'required property present');
   789     try {
   790       let foo = o4.foo;
   791       assert.fail('access to required property must throw');
   792     }
   793     catch(e) {
   794       assert.equal(
   795         'Error: Missing required property: foo',
   796         e.toString(),
   797         'required prop error'
   798       )
   799     }
   800   }
   801   catch(e) {
   802     assert.fail('did not expect create to complain about required props');
   803   }
   804 };
   806 exports['test:verify that conflicting properties are present'] =
   807 function(assert) {
   808   try {
   809     let o5 = Object.create(
   810       Object.prototype,
   811       compose(trait({ a: 0 }), trait({ a: 1 }))
   812     );
   813     assert.equal(true, 'a' in o5, 'conflicting property present');
   814     try {
   815       let a = o5.a; // accessors or data prop
   816       assert.fail('expected conflicting prop to cause exception');
   817     }
   818     catch (e) {
   819       assert.equal(
   820         'Error: Remaining conflicting property: a',
   821         e.toString(),
   822         'conflicting prop access error'
   823       );
   824     }
   825   }
   826   catch(e) {
   827     assert.fail('did not expect create to complain about conflicting props');
   828   }
   829 };
   831 exports['test diamond with conflicts'] = function(assert) {
   832   function makeT1(x) trait({ m: function() { return x; } })
   833   function makeT2(x) compose(trait({ t2: 'foo' }), makeT1(x))
   834   function makeT3(x) compose(trait({ t3: 'bar' }), makeT1(x))
   836   let T4 = compose(makeT2(5), makeT3(5));
   837   try {
   838     let o = create(Object.prototype, T4);
   839     assert.fail('expected diamond prop to cause exception');
   840   }
   841   catch(e) {
   842     assert.equal(
   843       'Error: Remaining conflicting property: m',
   844       e.toString(),
   845       'diamond prop conflict'
   846     );
   847   }
   848 };
   850 require('sdk/test').run(exports);

mercurial