SOURCE CODE: Uize.Test.Uize.Data.Matches (view docs)

/*______________
|       ______  |   U I Z E    J A V A S C R I P T    F R A M E W O R K
|     /      /  |   ---------------------------------------------------
|    /    O /   |    MODULE : Uize.Test.Uize.Data.Matches Class
|   /    / /    |
|  /    / /  /| |    ONLINE : http://www.uize.com
| /____/ /__/_| | COPYRIGHT : (c)2012-2016 UIZE
|          /___ |   LICENSE : Available under MIT License or GNU General Public License
|_______________|             http://www.uize.com/license.html
*/

/* Module Meta Data
  type: Test
  importance: 1
  codeCompleteness: 100
  docCompleteness: 100
*/

/*?
  Introduction
    The =Uize.Test.Uize.Data.Matches= module defines a suite of unit tests for the =Uize.Data.Matches= module.

    *DEVELOPERS:* `Chris van Rensburg`
*/

Uize.module ({
  name:'Uize.Test.Uize.Data.Matches',
  builder:function () {
    'use strict';

    function _geSparselyPopulatedArray () {
      var _result = [];
      _result [0] = undefined;
      _result [3] = 5;
      _result [5] = undefined;
      _result [8] = 'foo';
      _result.length = 10;
      return _result;
    }

    function _forEachMaxMatchesTest (_title,_maxMatches) {
      return {
        title:_title,
        test:function () {
          var
            _source = ['','hey','','there','foo','','','bar'],
            _allMatchValues = ['hey','there','foo','bar'],
            _matchValues = []
          ;
          Uize.Data.Matches.forEach (
            _source,
            function (_value) {return !!_value},
            function (_value) {_matchValues.push (_value)},
            _maxMatches
          );
          return this.expect (
            _allMatchValues.slice (0,_maxMatches == undefined ? Infinity : Math.max (_maxMatches,0)),
            _matchValues
          );
        }
      };
    }

    function _forEachMatcherTest (_title,_source,_matcher,_expectedIteratorValues) {
      return {
        title:_title,
        test:function () {
          var _actualIteratorValues = [];
          Uize.Data.Matches.forEach (
            _source,
            _matcher,
            function (_value,_key) {_actualIteratorValues.push (_value)}
          );
          return this.expect (_expectedIteratorValues,_actualIteratorValues);
        }
      }
    }

    function _forEachIteratorTest (_title,_source,_expectedIteratorValues,_expectedIteratorKeys) {
      return {
        title:_title,
        test:function () {
          var
            _actualIteratorValues = [],
            _actualIteratorKeys = []
          ;
          Uize.Data.Matches.forEach (
            _source,
            function (_value) {return !!_value},
            function (_value,_key) {
              _actualIteratorValues.push (_value);
              _actualIteratorKeys.push (_key);
            }
          );
          return (
            this.expect (_expectedIteratorValues,_actualIteratorValues) &&
            this.expect (_expectedIteratorKeys,_actualIteratorKeys)
          );
        }
      }
    }

    function _sourceTest (_methodName) {
      function _basicSourceTest (_title,_source,_expectedMatchValues,_expectedMatchKeys) {
        return {
          title:_title,
          test:function () {
            var
              _matchValues = [],
              _matchKeys = []
            ;
            Uize.Data.Matches [_methodName] (
              _source,
              function (_value,_key) {
                _matchValues.push (_value);
                _matchKeys.push (_key);
              }
            );
            return (
              this.expect (_expectedMatchValues,_matchValues) &&
              this.expect (_expectedMatchKeys,_matchKeys)
            );
          }
        };
      }
      return {
        title:'Different types of sources are supported',
        test:[
          _basicSourceTest (
            'The source can be an array',
            ['','hey','','there','foo','','','bar'],
            ['','hey','','there','foo','','','bar'],
            [0,1,2,3,4,5,6,7]
          ),
          _basicSourceTest (
            'The source can be an object',
            {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
            ['','hey','','there','foo','','','bar'],
            ['prop0','prop1','prop2','prop3','prop4','prop5','prop6','prop7']
          ),
          _basicSourceTest (
            'Specifying the value null for the source results in no iterations being performed',
            null,
            [],
            []
          ),
          _basicSourceTest (
            'Specifying the value undefined for the source results in no iterations being performed',
            undefined,
            [],
            []
          ),
          _basicSourceTest (
            'The source can be a number',
            5,
            [0,1,2,3,4],
            [0,1,2,3,4]
          )
        ]
      }
    }

    function _matcherTest (_methodName) {
      var
        _global = Uize.global (),
        _dummyGlobalTestMethodName = 'Uize_Test_Uize_Data_Matches_dummyTestMethod'
      ;
      function _matcherCallSignatureTest (
        _title,_source,_expectedMatchValues,_expectedMatchKeys,_useStringMatcher
      ) {
        return {
          title:_title,
          test:function () {
            var
              _valuesSeenByMatcher = [],
              _keysSeenByMatcher = []
            ;
            function _matcher (_value,_key) {
              _valuesSeenByMatcher.push (_value);
              _keysSeenByMatcher.push (_key);
            }
            if (_useStringMatcher)
              _global [_dummyGlobalTestMethodName] = _matcher
            ;
            Uize.Data.Matches [_methodName] (
              _source,
              _useStringMatcher ? _dummyGlobalTestMethodName + ' (value,key)' : _matcher
            );
            if (_useStringMatcher)
              delete _global [_dummyGlobalTestMethodName]
            ;
            return (
              this.expect (_expectedMatchValues,_valuesSeenByMatcher) &&
              this.expect (_expectedMatchKeys,_keysSeenByMatcher)
            );
          }
        };
      }
      return {
        title:'A matcher parameter is supported',
        test:[
          /*** test call signature for matcher function ***/
            _matcherCallSignatureTest (
              'The matcher function receives value and index for each element of an array source as parameters',
              ['','hey','','there','foo','','','bar'],
              ['','hey','','there','foo','','','bar'],
              [0,1,2,3,4,5,6,7]
            ),
            _matcherCallSignatureTest (
              'The matcher function receives value and key for each property of an object source as parameters',
              {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
              ['','hey','','there','foo','','','bar'],
              ['prop0','prop1','prop2','prop3','prop4','prop5','prop6','prop7']
            ),
            _matcherCallSignatureTest (
              'The matcher function receives value and index for each value of a range source as parameters',
              5,
              [0,1,2,3,4],
              [0,1,2,3,4]
            ),

          /*** test support for string matcher expression ***/
            _matcherCallSignatureTest (
              'A matcher expression string receives value and key values for each element of an array source',
              ['','hey','','there','foo','','','bar'],
              ['','hey','','there','foo','','','bar'],
              [0,1,2,3,4,5,6,7],
              true
            ),
            _matcherCallSignatureTest (
              'A matcher expression string receives value and key values for each property of an object source',
              {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
              ['','hey','','there','foo','','','bar'],
              ['prop0','prop1','prop2','prop3','prop4','prop5','prop6','prop7'],
              true
            ),
            _matcherCallSignatureTest (
              'A matcher expression string receives value and key values for each value of a range source',
              5,
              [0,1,2,3,4],
              [0,1,2,3,4],
              true
            )
        ]
      }
    }

    function _targetTestForRemoveOrRetainMethod (_methodName) {
      var
        _matcherPrefix = _methodName == 'retain' ? '!' : '',
        _matcher = _matcherPrefix + '!value',
        _matcherForNumberSource = _matcherPrefix + '(value % 2)'
      ;
      return {
        title:'An optional target parameter can be specified',
        test:[
          {
            title:'When the source is an object, the optional target parameter has a specific behavior',
            test:[
              {
                title:'When the source is an object and the value true is specified for the target parameter, the result is returned in a fresh object',
                test:function () {
                  var
                    _source = {prop0:'',prop1:'hey',prop2:'',prop3:'there'},
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,true)
                  ;
                  return (
                    this.expect (false,_source === _result) &&
                    this.expect ({prop1:'hey',prop3:'there'},_result)
                  );
                }
              },
              {
                title:'When the source is an object and the value false is specified for the target parameter, the source object is modified and returned',
                test:function () {
                  var
                    _source = {prop0:'',prop1:'hey',prop2:'',prop3:'there'},
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,false)
                  ;
                  return (
                    this.expectSameAs (_source,_result) &&
                    this.expect ({prop1:'hey',prop3:'there'},_result)
                  );
                }
              },
              {
                title:'When the source is an object and the source is specified for the target parameter, the source is modified and returned',
                test:function () {
                  var
                    _source = {prop0:'',prop1:'hey',prop2:'',prop3:'there'},
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,_source)
                  ;
                  return (
                    this.expectSameAs (_source,_result) &&
                    this.expect ({prop1:'hey',prop3:'there'},_result)
                  );
                }
              },
              {
                title:'When the source is an object and an empty object is specified for the target parameter, the target object is modified and returned',
                test:function () {
                  var
                    _source = {prop0:'',prop1:'hey',prop2:'',prop3:'there'},
                    _target = {},
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,_target)
                  ;
                  return (
                    this.expectSameAs (_target,_result) &&
                    this.expect ({prop1:'hey',prop3:'there'},_result)
                  );
                }
              },
              {
                title:'When the source is an object and a non-empty object is specified for the target parameter, the target object\'s contents is replaced with the result and returned',
                test:function () {
                  var
                    _source = {prop0:'',prop1:'hey',prop2:'',prop3:'there'},
                    _target = {foo:'bar'},
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,_target)
                  ;
                  return (
                    this.expectSameAs (_target,_result) &&
                    this.expect ({prop1:'hey',prop3:'there'},_result)
                  );
                }
              },
              {
                title:'When the source is an object and an empty array is specified for the target parameter, the target array remains empty but is extended with custom properties from the source object',
                test:function () {
                  var
                    _source = {prop0:'',prop1:'hey',prop2:'',prop3:'there'},
                    _target = [],
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,_target)
                  ;
                  return (
                    this.expectSameAs (_target,_result) &&
                    this.expect (0,_result.length) &&
                    this.expect (false,'prop0' in _target) &&
                    this.expect (false,'prop2' in _target) &&
                    this.expect ('hey',_target.prop1) &&
                    this.expect ('there',_target.prop3)
                  );
                }
              },
              {
                title:'When the source is an object and a non-empty array is specified for the target parameter, the target array is emptied and extended with custom properties from the source object',
                test:function () {
                  var
                    _source = {prop0:'',prop1:'hey',prop2:'',prop3:'there'},
                    _target = ['foo','bar'],
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,_target)
                  ;
                  return (
                    this.expectSameAs (_target,_result) &&
                    this.expect (0,_result.length) &&
                    this.expect (false,'prop0' in _target) &&
                    this.expect (false,'prop2' in _target) &&
                    this.expect ('hey',_target.prop1) &&
                    this.expect ('there',_target.prop3)
                  );
                }
              }
            ]
          },
          {
            title:'When the source is an array, the optional target parameter has a specific behavior',
            test:[
              {
                title:'When the source is an array and the value true is specified for the target parameter, the result is returned in a fresh array',
                test:function () {
                  var
                    _source = ['','hey','','there'],
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,true)
                  ;
                  return (
                    this.expect (false,_source === _result) &&
                    this.expect (['hey','there'],_result)
                  );
                }
              },
              {
                title:'When the source is an array and the value false is specified for the target parameter, the source array is modified and returned',
                test:function () {
                  var
                    _source = ['','hey','','there'],
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,false)
                  ;
                  return (
                    this.expectSameAs (_source,_result) &&
                    this.expect (['hey','there'],_result)
                  );
                }
              },
              {
                title:'When the source is an array and the source is specified for the target parameter, the source is modified and returned',
                test:function () {
                  var
                    _source = ['','hey','','there'],
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,_source)
                  ;
                  return (
                    this.expectSameAs (_source,_result) &&
                    this.expect (['hey','there'],_result)
                  );
                }
              },
              {
                title:'When the source is an array and an empty object is specified for the target parameter, the target object is modified to be a list and returned',
                test:function () {
                  var
                    _source = ['','hey','','there'],
                    _target = {},
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,_target)
                  ;
                  return (
                    this.expectSameAs (_target,_result) &&
                    this.expect ({0:'hey',1:'there',length:2},_result)
                  );
                }
              },
              {
                title:'When the source is an array and a non-empty object is specified for the target parameter, the target object is emptied, modified to be a list, and returned',
                test:function () {
                  var
                    _source = ['','hey','','there'],
                    _target = {foo:'bar'},
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,_target)
                  ;
                  return (
                    this.expectSameAs (_target,_result) &&
                    this.expect ({0:'hey',1:'there',length:2},_result)
                  );
                }
              },
              {
                title:'When the source is an array and an empty array is specified for the target parameter, the target array is modified and returned',
                test:function () {
                  var
                    _source = ['','hey','','there'],
                    _target = [],
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,_target)
                  ;
                  return (
                    this.expectSameAs (_target,_result) &&
                    this.expect (['hey','there'],_result)
                  );
                }
              },
              {
                title:'When the source is an array and a non-empty array is specified for the target parameter, the target array\'s contents is replaced with the result and returned',
                test:function () {
                  var
                    _source = ['','hey','','there'],
                    _target = ['foo','bar'],
                    _result = Uize.Data.Matches [_methodName] (_source,_matcher,null,_target)
                  ;
                  return (
                    this.expectSameAs (_target,_result) &&
                    this.expect (['hey','there'],_result)
                  );
                }
              }
            ]
          },
          {
            title:'When the source is a number, the optional target parameter has a specific behavior',
            test:[
              {
                title:'When the source is a number and the value true is specified for the target parameter, the result is returned in a fresh array',
                test:function () {
                  return this.expect (
                    [0,2,4],
                    Uize.Data.Matches [_methodName] (5,_matcherForNumberSource,null,true)
                  );
                }
              },
              {
                title:'When the source is a number and the value false is specified for the target parameter, the result is returned in a fresh array',
                test:function () {
                  return this.expect (
                    [0,2,4],
                    Uize.Data.Matches [_methodName] (5,_matcherForNumberSource,null,false)
                  );
                }
              },
              {
                title:'When the source is a number and a number is specified for the target parameter, the result is returned in a fresh array',
                test:function () {
                  return this.expect (
                    [0,2,4],
                    Uize.Data.Matches [_methodName] (5,_matcherForNumberSource,null,10)
                  );
                }
              },
              {
                title:'When the source is a number and an empty array is specified for the target parameter, the target array is modified and returned',
                test:function () {
                  var
                    _target = [],
                    _result = Uize.Data.Matches [_methodName] (5,_matcherForNumberSource,null,_target)
                  ;
                  return (
                    this.expectSameAs (_target,_result) &&
                    this.expect ([0,2,4],_result)
                  );
                }
              },
              {
                title:'When the source is a number and a non-empty array is specified for the target parameter, the target array\'s contents is replaced with the result and returned',
                test:function () {
                  var
                    _target = ['foo','bar'],
                    _result = Uize.Data.Matches [_methodName] (5,_matcherForNumberSource,null,_target)
                  ;
                  return (
                    this.expectSameAs (_target,_result) &&
                    this.expect ([0,2,4],_result)
                  );
                }
              },
              {
                title:'When the source is a number and an empty object is specified for the target parameter, the target object is modified to be a list and returned',
                test:function () {
                  var
                    _target = {},
                    _result = Uize.Data.Matches [_methodName] (5,_matcherForNumberSource,null,_target)
                  ;
                  return (
                    this.expectSameAs (_target,_result) &&
                    this.expect ({0:0,1:2,2:4,length:3},_result)
                  );
                }
              },
              {
                title:'When the source is a number and a non-empty object is specified for the target parameter, the target object is emptied, modified to be a list, and returned',
                test:function () {
                  var
                    _target = {foo:'bar'},
                    _result = Uize.Data.Matches [_methodName] (5,_matcherForNumberSource,null,_target)
                  ;
                  return (
                    this.expectSameAs (_target,_result) &&
                    this.expect ({0:0,1:2,2:4,length:3},_result)
                  );
                }
              }
            ]
          }
        ]
      }
    }

    return Uize.Test.resolve ({
      title:'Uize.Data.Matches Module Test',
      test:[
        Uize.Test.requiredModulesTest ('Uize.Data.Matches'),
        Uize.Test.staticMethodsTest ([
          ['Uize.Data.Matches.forEach',[
            /*** test support for different types of sources ***/
              _sourceTest ('forEach'),

            /*** test support for matcher ***/
              _matcherTest ('forEach'),

              /*** test when true is specified for matcher ***/
                _forEachMatcherTest (
                  'When the value true is specified for the matcher, all elements of an array source are iterated over',
                  ['','hey','','there','foo','','','bar'],
                  true,
                  ['','hey','','there','foo','','','bar']
                ),
                _forEachMatcherTest (
                  'When the value true is specified for the matcher, all properties of an object source are iterated over',
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  true,
                  ['','hey','','there','foo','','','bar']
                ),
                _forEachMatcherTest (
                  'When the value true is specified for the matcher, all values of a range source are iterated over',
                  5,
                  true,
                  [0,1,2,3,4]
                ),

              /*** test when false is specified for matcher ***/
                _forEachMatcherTest (
                  'When the value false is specified for the matcher, no elements of an array source are iterated over',
                  ['','hey','','there','foo','','','bar'],
                  false,
                  []
                ),
                _forEachMatcherTest (
                  'When the value false is specified for the matcher, no properties of an object source are iterated over',
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  false,
                  []
                ),
                _forEachMatcherTest (
                  'When the value false is specified for the matcher, no values of a range source are iterated over',
                  5,
                  false,
                  []
                ),

              /*** test when null is specified for matcher ***/
                _forEachMatcherTest (
                  'When the value null is specified for the matcher, all elements of an array source are iterated over',
                  ['','hey','','there','foo','','','bar'],
                  true,
                  ['','hey','','there','foo','','','bar']
                ),
                _forEachMatcherTest (
                  'When the value null is specified for the matcher, all properties of an object source are iterated over',
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  true,
                  ['','hey','','there','foo','','','bar']
                ),
                _forEachMatcherTest (
                  'When the value null is specified for the matcher, all values of a range source are iterated over',
                  5,
                  true,
                  [0,1,2,3,4]
                ),

              /*** test when undefined is specified for matcher ***/
                _forEachMatcherTest (
                  'When the value undefined is specified for the matcher, all elements of an array source are iterated over',
                  ['','hey','','there','foo','','','bar'],
                  true,
                  ['','hey','','there','foo','','','bar']
                ),
                _forEachMatcherTest (
                  'When the value undefined is specified for the matcher, all properties of an object source are iterated over',
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  true,
                  ['','hey','','there','foo','','','bar']
                ),
                _forEachMatcherTest (
                  'When the value undefined is specified for the matcher, all values of a range source are iterated over',
                  5,
                  true,
                  [0,1,2,3,4]
                ),

              /*** test when regular expression is specified for matcher ***/
                _forEachMatcherTest (
                  'When a regular expression is specified for the matcher, only elements of an array source whose values match the regular expression are iterated over',
                  ['boo','1234','hey','hi42','6','number 6',''],
                  /^\d+$/,
                  ['1234','6']
                ),
                _forEachMatcherTest (
                  'When a regular expression is specified for the matcher, only properties of an object source whose values match the regular expression are iterated over',
                  {prop0:'1234',prop1:'hey',prop2:'hi42',prop3:'6',prop4:'number 6',prop5:''},
                  /^\d+$/,
                  ['1234','6']
                ),
                _forEachMatcherTest (
                  'When a regular expression is specified for the matcher, only values of a range source that match the regular expression are iterated over',
                  100,
                  /^\d*1$/,
                  [1,11,21,31,41,51,61,71,81,91]
                ),

            /*** test call signature for iterator function ***/
              _forEachIteratorTest (
                'The iterator function receives value and index as parameters for each matched element of an array source as parameters',
                ['','hey','','there','foo','','','bar'],
                ['hey','there','foo','bar'],
                [1,3,4,7]
              ),
              _forEachIteratorTest (
                'The iterator function receives value and index as parameters for each matched element of an object source as parameters',
                {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                ['hey','there','foo','bar'],
                ['prop1','prop3','prop4','prop7']
              ),

            /*** test support for the maxMatches parameter ***/
              _forEachMaxMatchesTest (
                'Specifying the value 0 for the maxMatches parameter results in the iteration handler never being called, even if there are matches',
                0
              ),
              _forEachMaxMatchesTest (
                'Specifying a negative value for the maxMatches parameter results in the iteration handler never being called, even if there are matches',
                -10
              ),
              _forEachMaxMatchesTest (
                'Specifying a number for the maxMatches parameter that is lower than the number of actual matches results in the iteration handler being called only for matches before the max has been reached',
                2
              ),
              _forEachMaxMatchesTest (
                'Specifying a number for the maxMatches parameter that is higher than the number of actual matches results in the iteration handler being called for all the matches',
                10
              ),
              _forEachMaxMatchesTest (
                'Specifying the value null for the maxMatches parameter results in it being defaulted to Infinity',
                null
              ),
              _forEachMaxMatchesTest (
                'Specifying the value undefined for the maxMatches parameter results in it being defaulted to Infinity',
                undefined
              ),

            /*** miscellaneous tests ***/
              {
                title:'Unassigned elements of a sparsely populated array source are iterated over',
                test:function () {
                  var
                    _matchValues = [],
                    _matchKeys = []
                  ;
                  Uize.Data.Matches.forEach (
                    _geSparselyPopulatedArray (),
                    function (_value) {return _value === undefined},
                    function (_value,_key) {
                      _matchValues.push (_value);
                      _matchKeys.push (_key);
                    }
                  );
                  return (
                    this.expect (
                      [undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined],
                      _matchValues
                    ) &&
                    this.expect ([0,1,2,4,5,6,7,9],_matchKeys)
                  );
                }
              }
          ]],
          ['Uize.Data.Matches.remove',
            [
              /*** test support for different types of sources ***/
                _sourceTest ('remove'),
                /*** further tests for array source ***/
                  ['When a matcher matches some elements of an array source, those matching elements are removed',
                    [
                      ['','hey','','there','foo','','','bar'],
                      function (_value) {return !_value}
                    ],
                    ['hey','there','foo','bar']
                  ],
                  ['When a matcher matches no elements of an array source, no elements are removed',
                    [
                      ['','hey','','there','foo','','','bar'],
                      Uize.returnFalse
                    ],
                    ['','hey','','there','foo','','','bar']
                  ],
                  ['When a matcher matches all elements of an array source, all elements are removed',
                    [
                      ['','hey','','there','foo','','','bar'],
                      Uize.returnTrue
                    ],
                    []
                  ],

                /*** further tests for object source ***/
                  ['When a matcher matches some properties of an object source, those matching properties are removed',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      function (_value) {return !_value}
                    ],
                    {prop1:'hey',prop3:'there',prop4:'foo',prop7:'bar'}
                  ],
                  ['When a matcher matches no properties of an object source, no properties are removed',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      Uize.returnFalse
                    ],
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'}
                  ],
                  ['When a matcher matches all properties of an object source, all properties are removed',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      Uize.returnTrue
                    ],
                    {}
                  ],

                /*** tests for a range sounrce ***/
                  ['When a matcher matches some values of a range source, then an array of the range values with the matching values removed is returned',
                    [6,function (_value) {return _value % 2}],
                    [0,2,4]
                  ],
                  ['When a matcher matches all values of a range source, then an empty array is returned',
                    [6,Uize.returnTrue],
                    []
                  ],
                  ['When a matcher matches no values of a range source, then an array containing all values in the range is returned',
                    [6,Uize.returnFalse],
                    [0,1,2,3,4,5]
                  ],

              /*** test support for matcher ***/
                _matcherTest ('remove'),

                /*** test when no matcher is specified ***/
                  ['When no matcher is specified, all elements of an array source are removed',
                    [['','hey','','there','foo','','','bar']],
                    []
                  ],
                  ['When no matcher is specified, all properties of an object source are removed',
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    {}
                  ],
                  ['When no matcher is specified, all values of a range source are removed',
                    5,
                    []
                  ],

                /*** test when true is specified for matcher ***/
                  ['When the value true is specified for the matcher, all elements of an array source are removed',
                    [
                      ['','hey','','there','foo','','','bar'],
                      true
                    ],
                    []
                  ],
                  ['When the value true is specified for the matcher, all properties of an object source are removed',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      true
                    ],
                    {}
                  ],
                  ['When the value true is specified for the matcher, all values of a range source are removed',
                    [5,true],
                    []
                  ],

                /*** test when false is specified for matcher ***/
                  ['When the value false is specified for the matcher, no elements of an array source are removed',
                    [
                      ['','hey','','there','foo','','','bar'],
                      false
                    ],
                    ['','hey','','there','foo','','','bar']
                  ],
                  ['When the value false is specified for the matcher, no properties of an object source are removed',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      false
                    ],
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'}
                  ],
                  ['When the value false is specified for the matcher, no values of a range source are iterated over',
                    [5,false],
                    [0,1,2,3,4]
                  ],

                /*** test when null is specified for matcher ***/
                  ['When the value null is specified for the matcher, all elements of an array source are removed',
                    [
                      ['','hey','','there','foo','','','bar'],
                      true
                    ],
                    []
                  ],
                  ['When the value null is specified for the matcher, all properties of an object source are removed',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      true
                    ],
                    {}
                  ],
                  ['When the value null is specified for the matcher, all values of a range source are removed',
                    [5,true],
                    []
                  ],

                /*** test when undefined is specified for matcher ***/
                  ['When the value undefined is specified for the matcher, all elements of an array source are removed',
                    [
                      ['','hey','','there','foo','','','bar'],
                      true
                    ],
                    []
                  ],
                  ['When the value undefined is specified for the matcher, all properties of an object source are removed',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      true
                    ],
                    {}
                  ],
                  ['When the value undefined is specified for the matcher, all values of a range source are removed',
                    [5,true],
                    []
                  ],

                /*** test when regular expression is specified for matcher ***/
                  ['When a regular expression is specified for the matcher, only elements of an array source whose values match the regular expression are removed',
                    [
                      ['boo','1234','hey','hi42','6','number 6',''],
                      /^\d+$/
                    ],
                    ['boo','hey','hi42','number 6','']
                  ],
                  ['When a regular expression is specified for the matcher, only properties of an object source whose values match the regular expression are removed',
                    [
                      {prop0:'1234',prop1:'hey',prop2:'hi42',prop3:'6',prop4:'number 6',prop5:''},
                      /^\d+$/
                    ],
                    {prop1:'hey',prop2:'hi42',prop4:'number 6',prop5:''}
                  ],
                  ['When a regular expression is specified for the matcher, only values of a range source that match the regular expression are removed',
                    [30,/1/],
                    [0,2,3,4,5,6,7,8,9,20,22,23,24,25,26,27,28,29]
                  ],

                /*** test when lookup object is specified for matcher ***/
                  ['When a lookup object is specified for the matcher, only elements of an array source whose values are mapped to a truthy value in the lookup are removed',
                    [['foo','bar','baz','qux'],{bar:1,baz:1}],
                    ['foo','qux']
                  ],
                  ['When a lookup object is specified for the matcher, only properties of an object source whose values are mapped to a truthy value in the lookup are removed',
                    [{foo:'foo',bar:'bar',baz:'baz',qux:'qux'},{bar:1,baz:1}],
                    {foo:'foo',qux:'qux'}
                  ],
                  ['When a lookup object is specified for the matcher, only values of a range source that are mapped to a truthy value in the lookup are removed',
                    [10,{3:true,5:true,9:true}],
                    [0,1,2,4,6,7,8]
                  ],

              /*** test support for the maxMatches parameter ***/
                ['Specifying the value 0 for the maxMatches parameter results in no elements of an array source being removed, even if there are matches',
                  [
                    ['','hey','','there','foo','','','bar'],
                    function (_value) {return !!_value},
                    0
                  ],
                  ['','hey','','there','foo','','','bar']
                ],
                ['Specifying a negative value for the maxMatches parameter results in no elements of an array source being removed, even if there are matches',
                  [
                    ['','hey','','there','foo','','','bar'],
                    function (_value) {return !!_value},
                    -10
                  ],
                  ['','hey','','there','foo','','','bar']
                ],
                ['Specifying a non-zero number for the maxMatches parameter that is lower than the number of actual matches results in only the specified maximum number of matches being removed from a source array',
                  [
                    ['','hey','','there','foo','','','bar'],
                    function (_value) {return !!_value},
                    2
                  ],
                  ['','','foo','','','bar']
                ],
                ['Specifying a number for the maxMatches parameter that is higher than the number of actual matches in a source array results in all matching elements being removed',
                  [
                    ['','hey','','there','foo','','','bar'],
                    function (_value) {return !!_value},
                    10
                  ],
                  ['','','','']
                ],
                ['Specifying the value null for the maxMatches parameter results in it being defaulted to Infinity',
                  [
                    ['','hey','','there','foo','','','bar'],
                    function (_value) {return !!_value},
                    null
                  ],
                  ['','','','']
                ],
                ['Specifying the value undefined for the maxMatches parameter results in it being defaulted to Infinity',
                  [
                    ['','hey','','there','foo','','','bar'],
                    function (_value) {return !!_value},
                    undefined
                  ],
                  ['','','','']
                ],

              /*** test support for target parameter ***/
                _targetTestForRemoveOrRetainMethod ('remove')
            ],
            null,
            {cloneArguments:true}
          ],
          ['Uize.Data.Matches.retain',
            [
              /*** test support for different types of sources ***/
                _sourceTest ('retain'),
                /*** further tests for array source ***/
                  ['When a matcher matches some elements of an array source, only those matching elements are retained',
                    [
                      ['','hey','','there','foo','','','bar'],
                      Uize.returnX
                    ],
                    ['hey','there','foo','bar']
                  ],
                  ['When a matcher matches no elements of an array source, no elements are retained',
                    [
                      ['','hey','','there','foo','','','bar'],
                      Uize.returnFalse
                    ],
                    []
                  ],
                  ['When a matcher matches all elements of an array source, all elements are retained',
                    [
                      ['','hey','','there','foo','','','bar'],
                      Uize.returnTrue
                    ],
                    ['','hey','','there','foo','','','bar']
                  ],

                /*** further tests for object source ***/
                  ['When a matcher matches some properties of an object source, only those matching properties are retained',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      Uize.returnX
                    ],
                    {prop1:'hey',prop3:'there',prop4:'foo',prop7:'bar'}
                  ],
                  ['When a matcher matches no properties of an object source, no properties are retained',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      Uize.returnFalse
                    ],
                    {}
                  ],
                  ['When a matcher matches all properties of an object source, all properties are retained',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      Uize.returnTrue
                    ],
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'}
                  ],

                /*** tests for a range sounrce ***/
                  ['When a matcher matches some values of a range source, then an array of only those matching range values is returned',
                    [6,function (_value) {return _value % 2}],
                    [1,3,5]
                  ],
                  ['When a matcher matches all values of a range source, then an array containing all values in the range is returned',
                    [6,Uize.returnTrue],
                    [0,1,2,3,4,5]
                  ],
                  ['When a matcher matches no values of a range source, then an empty array is returned',
                    [6,Uize.returnFalse],
                    []
                  ],

              /*** test support for matcher ***/
                _matcherTest ('retain'),

                /*** test when no matcher is specified ***/
                  ['When no matcher is specified, all elements of an array source are retained',
                    [['','hey','','there','foo','','','bar']],
                    ['','hey','','there','foo','','','bar']
                  ],
                  ['When no matcher is specified, all properties of an object source are retained',
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'}
                  ],
                  ['When no matcher is specified, all values of a range source are retained',
                    5,
                    [0,1,2,3,4]
                  ],

                /*** test when true is specified for matcher ***/
                  ['When the value true is specified for the matcher, all elements of an array source are retained',
                    [
                      ['','hey','','there','foo','','','bar'],
                      true
                    ],
                    ['','hey','','there','foo','','','bar']
                  ],
                  ['When the value true is specified for the matcher, all properties of an object source are retained',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      true
                    ],
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'}
                  ],
                  ['When the value true is specified for the matcher, all values of a range source are retained',
                    [5,true],
                    [0,1,2,3,4]
                  ],

                /*** test when false is specified for matcher ***/
                  ['When the value false is specified for the matcher, no elements of an array source are retained',
                    [
                      ['','hey','','there','foo','','','bar'],
                      false
                    ],
                    []
                  ],
                  ['When the value false is specified for the matcher, no properties of an object source are retained',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      false
                    ],
                    {}
                  ],
                  ['When the value false is specified for the matcher, no values of a range source are iterated over',
                    [5,false],
                    []
                  ],

                /*** test when null is specified for matcher ***/
                  ['When the value null is specified for the matcher, all elements of an array source are retained',
                    [
                      ['','hey','','there','foo','','','bar'],
                      true
                    ],
                    ['','hey','','there','foo','','','bar']
                  ],
                  ['When the value null is specified for the matcher, all properties of an object source are retained',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      true
                    ],
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'}
                  ],
                  ['When the value null is specified for the matcher, all values of a range source are retained',
                    [5,true],
                    [0,1,2,3,4]
                  ],

                /*** test when undefined is specified for matcher ***/
                  ['When the value undefined is specified for the matcher, all elements of an array source are retained',
                    [
                      ['','hey','','there','foo','','','bar'],
                      true
                    ],
                    ['','hey','','there','foo','','','bar']
                  ],
                  ['When the value undefined is specified for the matcher, all properties of an object source are retained',
                    [
                      {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                      true
                    ],
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'}
                  ],
                  ['When the value undefined is specified for the matcher, all values of a range source are retained',
                    [5,true],
                    [0,1,2,3,4]
                  ],

                /*** test when regular expression is specified for matcher ***/
                  ['When a regular expression is specified for the matcher, only elements of an array source whose values match the regular expression are retained',
                    [
                      ['boo','1234','hey','hi42','6','number 6',''],
                      /^\d+$/
                    ],
                    ['1234','6']
                  ],
                  ['When a regular expression is specified for the matcher, only properties of an object source whose values match the regular expression are retained',
                    [
                      {prop0:'1234',prop1:'hey',prop2:'hi42',prop3:'6',prop4:'number 6',prop5:''},
                      /^\d+$/
                    ],
                    {prop0:'1234',prop3:'6'}
                  ],
                  ['When a regular expression is specified for the matcher, only values of a range source that match the regular expression are retained',
                    [100,/^\d*1$/],
                    [1,11,21,31,41,51,61,71,81,91]
                  ],

              /*** test support for the maxMatches parameter ***/
                ['Specifying the value 0 for the maxMatches parameter results in no elements of an array source being retained, even if there are matches',
                  [
                    ['','hey','','there','foo','','','bar'],
                    function (_value) {return !!_value},
                    0
                  ],
                  []
                ],
                ['Specifying a negative value for the maxMatches parameter results in no elements of an array source being retained, even if there are matches',
                  [
                    ['','hey','','there','foo','','','bar'],
                    function (_value) {return !!_value},
                    -10
                  ],
                  []
                ],
                ['Specifying a non-zero number for the maxMatches parameter that is lower than the number of actual matches results in only the specified maximum number of matches being retained from a source array',
                  [
                    ['','hey','','there','foo','','','bar'],
                    function (_value) {return !!_value},
                    2
                  ],
                  ['hey','there']
                ],
                ['Specifying a number for the maxMatches parameter that is higher than the number of actual matches in a source array results in all matching elements being retained',
                  [
                    ['','hey','','there','foo','','','bar'],
                    function (_value) {return !!_value},
                    10
                  ],
                  ['hey','there','foo','bar']
                ],
                ['Specifying the value null for the maxMatches parameter results in it being defaulted to Infinity',
                  [
                    ['','hey','','there','foo','','','bar'],
                    function (_value) {return !!_value},
                    null
                  ],
                  ['hey','there','foo','bar']
                ],
                ['Specifying the value undefined for the maxMatches parameter results in it being defaulted to Infinity',
                  [
                    ['','hey','','there','foo','','','bar'],
                    function (_value) {return !!_value},
                    undefined
                  ],
                  ['hey','there','foo','bar']
                ],

              /*** test support for target parameter ***/
                _targetTestForRemoveOrRetainMethod ('retain')
            ],
            null,
            {cloneArguments:true}
          ],
          ['Uize.Data.Matches.count',[
            /*** test support for different types of sources ***/
              _sourceTest ('count'),
              ['When the source is an array, the number of matching elements is returned',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return !!_value}
                ],
                4
              ],
              ['When the source is an object, the number of matching properties is returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value}
                ],
                4
              ],
              ['When the source is a range, the number of matching values in the range is returned',
                [
                  5,
                  function (_value) {return _value % 2}
                ],
                2
              ],

            /*** test support for matcher ***/
              _matcherTest ('count'),

              /*** test when no matcher is specified ***/
                ['When no matcher is specified, all elements of an array source are counted',
                  [['','hey','','there','foo','','','bar']],
                  8
                ],
                ['When no matcher is specified, all properties of an object source are counted',
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  8
                ],
                ['When no matcher is specified, all values of a range source are counted',
                  5,
                  5
                ],

              /*** test when true is specified for matcher ***/
                ['When the value true is specified for the matcher, all elements of an array source are counted',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  8
                ],
                ['When the value true is specified for the matcher, all properties of an object source are counted',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  8
                ],
                ['When the value true is specified for the matcher, all values of a range source are counted',
                  [5,true],
                  5
                ],

              /*** test when false is specified for matcher ***/
                ['When the value false is specified for the matcher, no elements of an array source are counted',
                  [
                    ['','hey','','there','foo','','','bar'],
                    false
                  ],
                  0
                ],
                ['When the value false is specified for the matcher, no properties of an object source are counted',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    false
                  ],
                  0
                ],
                ['When the value false is specified for the matcher, no values of a range source are iterated over',
                  [5,false],
                  0
                ],

              /*** test when null is specified for matcher ***/
                ['When the value null is specified for the matcher, all elements of an array source are counted',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  8
                ],
                ['When the value null is specified for the matcher, all properties of an object source are counted',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  8
                ],
                ['When the value null is specified for the matcher, all values of a range source are counted',
                  [5,true],
                  5
                ],

              /*** test when undefined is specified for matcher ***/
                ['When the value undefined is specified for the matcher, all elements of an array source are counted',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  8
                ],
                ['When the value undefined is specified for the matcher, all properties of an object source are counted',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  8
                ],
                ['When the value undefined is specified for the matcher, all values of a range source are counted',
                  [5,true],
                  5
                ],

              /*** test when regular expression is specified for matcher ***/
                ['When a regular expression is specified for the matcher, only elements of an array source whose values match the regular expression are counted',
                  [
                    ['boo','1234','hey','hi42','6','number 6',''],
                    /^\d+$/
                  ],
                  2
                ],
                ['When a regular expression is specified for the matcher, only properties of an object source whose values match the regular expression are counted',
                  [
                    {prop0:'1234',prop1:'hey',prop2:'hi42',prop3:'6',prop4:'number 6',prop5:''},
                    /^\d+$/
                  ],
                  2
                ],
                ['When a regular expression is specified for the matcher, only values of a range source that match the regular expression are counted',
                  [100,/^\d*1$/],
                  10
                ],

            /*** test that method returns numbers of matches ***/
              ['When there are matches, the number of matches is returned',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return !!_value}
                ],
                4
              ],
              ['When there are no matches, the value 0 is returned',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return _value == 'bat'}
                ],
                0
              ],

            /*** test support for matcher expression string ***/
              ['The matcher can be specified in the form of an expression string',
                [['','hey','','there','foo','','','bar'],'!value'],
                4
              ],
              ['The index of each element for an array source is provided to a matcher expression',
                [['','hey','','there','foo','','','bar'],'key > 2'],
                5
              ],
              ['The key of each property for an object source is provided to a matcher expression',
                [{foo0:1,bar0:1,bar1:1,foo1:1,bar2:1},'key.slice (0,3) == "foo"'],
                2
              ],

            /*** miscellaneous tests ***/
              {
                title:'Unassigned elements of an array source are iterated over',
                test:function () {
                  return this.expect (
                    8,
                    Uize.Data.Matches.count (
                      _geSparselyPopulatedArray (),
                      function (_value) {return _value === undefined}
                    )
                  )
                }
              },

            /*** test support for the maxMatches parameter ***/
              ['Specifying the value 0 for the maxMatches parameter produces the result 0, even if there are matches',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return !!_value},
                  0
                ],
                0
              ],
              ['Specifying a negative value for the maxMatches parameter produces the result 0, even if there are matches',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return !!_value},
                  -10
                ],
                0
              ],
              ['Specifying a non-zero number for the maxMatches parameter that is lower than the number of actual matches results in the maxMatches value being returned',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return !!_value},
                  2
                ],
                2
              ],
              ['Specifying a number for the maxMatches parameter that is higher than the number of actual matches results in the number of matches being returned',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return !!_value},
                  10
                ],
                4
              ],
              ['Specifying the value null for the maxMatches parameter results in it being defaulted to Infinity',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return !!_value},
                  null
                ],
                4
              ],
              ['Specifying the value undefined for the maxMatches parameter results in it being defaulted to Infinity',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return !!_value},
                  undefined
                ],
                4
              ]
          ]],
          ['Uize.Data.Matches.keys',[
            /*** test support for different types of sources ***/
              _sourceTest ('keys'),
              ['When the source is an array, the indexes of matching elements are returned',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return !!_value}
                ],
                [1,3,4,7]
              ],
              ['When the source is an object, the keys of matching properties are returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value}
                ],
                ['prop1','prop3','prop4','prop7']
              ],
              ['When the source is a range, the indexes of matching values in the range are returned',
                [
                  5,
                  function (_value) {return _value % 2}
                ],
                [1,3]
              ],

            /*** test support for matcher ***/
              _matcherTest ('keys'),

              /*** test when no matcher is specified ***/
                ['When no matcher is specified, all indexes of an array source are returned',
                  [['','hey','','there','foo','','','bar']],
                  [0,1,2,3,4,5,6,7]
                ],
                ['When no matcher is specified, all keys of an object source are returned',
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  ['prop0','prop1','prop2','prop3','prop4','prop5','prop6','prop7']
                ],
                ['When no matcher is specified, all keys of a range source are returned',
                  5,
                  [0,1,2,3,4]
                ],

              /*** test when true is specified for matcher ***/
                ['When the value true is specified for the matcher, all indexes of an array source are returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  [0,1,2,3,4,5,6,7]
                ],
                ['When the value true is specified for the matcher, all keys of an object source are returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  ['prop0','prop1','prop2','prop3','prop4','prop5','prop6','prop7']
                ],
                ['When the value true is specified for the matcher, all keys of a range source are returned',
                  [5,true],
                  [0,1,2,3,4]
                ],

              /*** test when false is specified for matcher ***/
                ['When the value false is specified for the matcher, no indexes of an array source are returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    false
                  ],
                  []
                ],
                ['When the value false is specified for the matcher, no keys of an object source are returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    false
                  ],
                  []
                ],
                ['When the value false is specified for the matcher, no keys of a range source are iterated over',
                  [5,false],
                  []
                ],

              /*** test when null is specified for matcher ***/
                ['When the value null is specified for the matcher, all indexes of an array source are returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  [0,1,2,3,4,5,6,7]
                ],
                ['When the value null is specified for the matcher, all keys of an object source are returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  ['prop0','prop1','prop2','prop3','prop4','prop5','prop6','prop7']
                ],
                ['When the value null is specified for the matcher, all keys of a range source are returned',
                  [5,true],
                  [0,1,2,3,4]
                ],

              /*** test when undefined is specified for matcher ***/
                ['When the value undefined is specified for the matcher, all indexes of an array source are returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  [0,1,2,3,4,5,6,7]
                ],
                ['When the value undefined is specified for the matcher, all keys of an object source are returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  ['prop0','prop1','prop2','prop3','prop4','prop5','prop6','prop7']
                ],
                ['When the value undefined is specified for the matcher, all keys of a range source are returned',
                  [5,true],
                  [0,1,2,3,4]
                ],

              /*** test when regular expression is specified for matcher ***/
                ['When a regular expression is specified for the matcher, only indexes of an array source whose values match the regular expression are returned',
                  [
                    ['boo','1234','hey','hi42','6','number 6',''],
                    /^\d+$/
                  ],
                  [1,4]
                ],
                ['When a regular expression is specified for the matcher, only keys of an object source\'s properties whose values match the regular expression are returned',
                  [
                    {prop0:'1234',prop1:'hey',prop2:'hi42',prop3:'6',prop4:'number 6',prop5:''},
                    /^\d+$/
                  ],
                  ['prop0','prop3']
                ],
                ['When a regular expression is specified for the matcher, only keys for the values of a range source that match the regular expression are returned',
                  [100,/^\d*1$/],
                  [1,11,21,31,41,51,61,71,81,91]
                ],

            /*** test support for maxMatches parameter ***/
              ['When the value for the maxMatches parameter is greater than the number of matches, the keys for all matches are returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  10
                ],
                ['prop1','prop3','prop4','prop7']
              ],
              ['When the value for the maxMatches parameter is less than the number of matches, the keys for the specified maximum number of matches are returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  3
                ],
                ['prop1','prop3','prop4']
              ],
              ['When the value Infinity is specified for the maxMatches parameter, the keys for all matches are returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  Infinity
                ],
                ['prop1','prop3','prop4','prop7']
              ],
              ['When the value null is specified for the maxMatches parameter, the keys for all matches are returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  null
                ],
                ['prop1','prop3','prop4','prop7']
              ],
              ['When the value undefined is specified for the maxMatches parameter, the keys for all matches are returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  undefined
                ],
                ['prop1','prop3','prop4','prop7']
              ],
              ['When the value 0 is specified for the maxMatches parameter, an array of zero length is returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  0
                ],
                []
              ],
              ['When a negative value is specified for the maxMatches parameter, an array of zero length is returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  0
                ],
                []
              ]
          ]],
          ['Uize.Data.Matches.values',[
            /*** test support for different types of sources ***/
              _sourceTest ('values'),
              ['When the source is an array, the values of matching elements are returned',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return !!_value}
                ],
                ['hey','there','foo','bar']
              ],
              ['When the source is an object, the values of matching properties are returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value}
                ],
                ['hey','there','foo','bar']
              ],
              ['When the source is a range, the matching values in the range are returned',
                [
                  5,
                  function (_value) {return _value % 2}
                ],
                [1,3]
              ],

            /*** test support for matcher ***/
              _matcherTest ('values'),

              /*** test when no matcher is specified ***/
                ['When no matcher is specified, all values of an array source are returned',
                  [['','hey','','there','foo','','','bar']],
                  ['','hey','','there','foo','','','bar']
                ],
                ['When no matcher is specified, all values of an object source are returned',
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  ['','hey','','there','foo','','','bar']
                ],
                ['When no matcher is specified, all values of a range source are returned',
                  5,
                  [0,1,2,3,4]
                ],

              /*** test when true is specified for matcher ***/
                ['When the value true is specified for the matcher, all values of an array source are returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  ['','hey','','there','foo','','','bar']
                ],
                ['When the value true is specified for the matcher, all values of an object source are returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  ['','hey','','there','foo','','','bar']
                ],
                ['When the value true is specified for the matcher, all values of a range source are returned',
                  [5,true],
                  [0,1,2,3,4]
                ],

              /*** test when false is specified for matcher ***/
                ['When the value false is specified for the matcher, no values of an array source are returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    false
                  ],
                  []
                ],
                ['When the value false is specified for the matcher, no values of an object source are returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    false
                  ],
                  []
                ],
                ['When the value false is specified for the matcher, no values of a range source are iterated over',
                  [5,false],
                  []
                ],

              /*** test when null is specified for matcher ***/
                ['When the value null is specified for the matcher, all values of an array source are returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  ['','hey','','there','foo','','','bar']
                ],
                ['When the value null is specified for the matcher, all values of an object source are returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  ['','hey','','there','foo','','','bar']
                ],
                ['When the value null is specified for the matcher, all values of a range source are returned',
                  [5,true],
                  [0,1,2,3,4]
                ],

              /*** test when undefined is specified for matcher ***/
                ['When the value undefined is specified for the matcher, all values of an array source are returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  ['','hey','','there','foo','','','bar']
                ],
                ['When the value undefined is specified for the matcher, all values of an object source are returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  ['','hey','','there','foo','','','bar']
                ],
                ['When the value undefined is specified for the matcher, all values of a range source are returned',
                  [5,true],
                  [0,1,2,3,4]
                ],

              /*** test when regular expression is specified for matcher ***/
                ['When a regular expression is specified for the matcher, only values of an array that match the regular expression are returned',
                  [
                    ['boo','1234','hey','hi42','6','number 6',''],
                    /^\d+$/
                  ],
                  ['1234','6']
                ],
                ['When a regular expression is specified for the matcher, only values of an object source\'s properties that match the regular expression are returned',
                  [
                    {prop0:'1234',prop1:'hey',prop2:'hi42',prop3:'6',prop4:'number 6',prop5:''},
                    /^\d+$/
                  ],
                  ['1234','6']
                ],
                ['When a regular expression is specified for the matcher, only values of a range source that match the regular expression are returned',
                  [100,/^\d*1$/],
                  [1,11,21,31,41,51,61,71,81,91]
                ],

            /*** test support for maxMatches parameter ***/
              ['When the value for the maxMatches parameter is greater than the number of matches, the values for all matches are returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  10
                ],
                ['hey','there','foo','bar']
              ],
              ['When the value for the maxMatches parameter is less than the number of matches, the values for the specified maximum number of matches are returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  3
                ],
                ['hey','there','foo']
              ],
              ['When the value Infinity is specified for the maxMatches parameter, the values for all matches are returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  Infinity
                ],
                ['hey','there','foo','bar']
              ],
              ['When the value null is specified for the maxMatches parameter, the values for all matches are returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  null
                ],
                ['hey','there','foo','bar']
              ],
              ['When the value undefined is specified for the maxMatches parameter, the values for all matches are returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  undefined
                ],
                ['hey','there','foo','bar']
              ],
              ['When the value 0 is specified for the maxMatches parameter, an array of zero length is returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  0
                ],
                []
              ],
              ['When a negative value is specified for the maxMatches parameter, an array of zero length is returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value},
                  0
                ],
                []
              ]
          ]],
          ['Uize.Data.Matches.firstKey',[
            /*** test support for different types of sources ***/
              _sourceTest ('firstKey'),
              ['When the source is an array, the index of the first matching element is returned',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return !!_value}
                ],
                1
              ],
              ['When the source is an object, the key of the first matching property is returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value}
                ],
                'prop1'
              ],
              ['When the source is a range, the index of the first matching value in the range is returned',
                [
                  5,
                  function (_value) {return _value % 2}
                ],
                1
              ],

            /*** test support for matcher ***/
              _matcherTest ('firstKey'),
              ['When a matcher matches a property of an object source, the key of the first matching property is returned',
                [
                  {foo1:'hello',hello:'bar1',foo2:'bar2',foo3:'bar3'},
                  function (_value,_key) {return _key.slice (0,3) == 'foo' && _value.slice (0,3) == 'bar'}
                ],
                'foo2'
              ],
              ['When a matcher doesn\'t match any properties of an object source, the value undefined is returned',
                [
                  {foo1:'hello',hello:'bar1',foo2:'bar2',foo3:'bar3'},
                  function () {return false}
                ],
                undefined
              ],
              ['The matcher can be specified in the form of an expression string',
                [
                  {foo1:'hello',hello:'bar1',foo2:'bar2',foo3:'bar3'},
                  'key.slice (0,3) == "foo" && value.slice (0,3) == "bar"'
                ],
                'foo2'
              ],

              /*** test when no matcher is specified ***/
                ['When no matcher is specified, the index of the first element of an array source is returned',
                  [['','hey','','there','foo','','','bar']],
                  0
                ],
                ['When no matcher is specified, the key of the first property of an object source is returned',
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  'prop0'
                ],
                ['When no matcher is specified, the index of the first value of a range source is returned',
                  5,
                  0
                ],

              /*** test when true is specified for matcher ***/
                ['When the value true is specified for the matcher, the index for the first element of an array source is returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  0
                ],
                ['When the value true is specified for the matcher, the key of the first property of an object source is returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  'prop0'
                ],
                ['When the value true is specified for the matcher, the index of the first value of a range source is returned',
                  [5,true],
                  0
                ],

              /*** test when false is specified for matcher ***/
                ['When the value false is specified for the matcher with an array source, the value undefined is returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    false
                  ],
                  undefined
                ],
                ['When the value false is specified for the matcher with an object source, the value undefined is returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    false
                  ],
                  undefined
                ],
                ['When the value false is specified for the matcher with a range source, the value undefined is returned',
                  [5,false],
                  undefined
                ],

              /*** test when null is specified for matcher ***/
                ['When the value null is specified for the matcher, the index of the first element of an array source is returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  0
                ],
                ['When the value null is specified for the matcher, the key of the first property of an object source is returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  'prop0'
                ],
                ['When the value null is specified for the matcher, the index of the first value of a range source is returned',
                  [5,true],
                  0
                ],

              /*** test when undefined is specified for matcher ***/
                ['When the value undefined is specified for the matcher, the index of the first element of an array source is returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  0
                ],
                ['When the value undefined is specified for the matcher, the key of the first property of an object source is returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  'prop0'
                ],
                ['When the value undefined is specified for the matcher, the index of the first value of a range source is returned',
                  [5,true],
                  0
                ],

              /*** test when regular expression is specified for matcher ***/
                ['When a regular expression is specified for the matcher, the index for the first matching element of an array source is returned',
                  [
                    ['boo','1234','hey','hi42','6','number 6',''],
                    /^\d+$/
                  ],
                  1
                ],
                ['When a regular expression is specified for the matcher, the key for the first maching property is returned',
                  [
                    {prop0:'1234',prop1:'hey',prop2:'hi42',prop3:'6',prop4:'number 6',prop5:''},
                    /^\d+$/
                  ],
                  'prop0'
                ],
                ['When a regular expression is specified for the matcher, the index for the first matching value of a range source is returned',
                  [100,/^\d1$/],
                  11
                ]
          ]],
          ['Uize.Data.Matches.firstValue',[
            /*** test support for different types of sources ***/
              _sourceTest ('firstValue'),
              ['When the source is an array, the value of the first matching element is returned',
                [
                  ['','hey','','there','foo','','','bar'],
                  function (_value) {return !!_value}
                ],
                'hey'
              ],
              ['When the source is an object, the value of the first matching property is returned',
                [
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  function (_value) {return !!_value}
                ],
                'hey'
              ],
              ['When the source is a range, the first matching value in the range is returned',
                [
                  5,
                  function (_value) {return _value % 2}
                ],
                1
              ],

            /*** test support for matcher ***/
              _matcherTest ('firstValue'),
              ['When a matcher matches a property of an object source, the value of the first matching property is returned',
                [
                  {foo1:'hello',hello:'bar1',foo2:'bar2',foo3:'bar3'},
                  function (_value,_key) {return _key.slice (0,3) == 'foo' && _value.slice (0,3) == 'bar'}
                ],
                'bar2'
              ],
              ['When a matcher doesn\'t match any properties of an object source, the value undefined is returned',
                [
                  {foo1:'hello',hello:'bar1',foo2:'bar2',foo3:'bar3'},
                  function () {return false}
                ],
                undefined
              ],
              ['The matcher can be specified in the form of an expression string',
                [
                  {foo1:'hello',hello:'bar1',foo2:'bar2',foo3:'bar3'},
                  'key.slice (0,3) == "foo" && value.slice (0,3) == "bar"'
                ],
                'bar2'
              ],

              /*** test when no matcher is specified ***/
                ['When no matcher is specified, the value of the first element of an array source is returned',
                  [['','hey','','there','foo','','','bar']],
                  ''
                ],
                ['When no matcher is specified, the value of the first property of an object source is returned',
                  {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                  ''
                ],
                ['When no matcher is specified, the first value of a range source is returned',
                  5,
                  0
                ],

              /*** test when true is specified for matcher ***/
                ['When the value true is specified for the matcher, the value for the first element of an array source is returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  ''
                ],
                ['When the value true is specified for the matcher, the value of the first property of an object source is returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  ''
                ],
                ['When the value true is specified for the matcher, the first value of a range source is returned',
                  [5,true],
                  0
                ],

              /*** test when false is specified for matcher ***/
                ['When the value false is specified for the matcher with an array source, the value undefined is returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    false
                  ],
                  undefined
                ],
                ['When the value false is specified for the matcher with an object source, the value undefined is returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    false
                  ],
                  undefined
                ],
                ['When the value false is specified for the matcher with a range source, the value undefined is returned',
                  [5,false],
                  undefined
                ],

              /*** test when null is specified for matcher ***/
                ['When the value null is specified for the matcher, the value of the first element of an array source is returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  ''
                ],
                ['When the value null is specified for the matcher, the value of the first property of an object source is returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  ''
                ],
                ['When the value null is specified for the matcher, the first value of a range source is returned',
                  [5,true],
                  0
                ],

              /*** test when undefined is specified for matcher ***/
                ['When the value undefined is specified for the matcher, the value of the first element of an array source is returned',
                  [
                    ['','hey','','there','foo','','','bar'],
                    true
                  ],
                  ''
                ],
                ['When the value undefined is specified for the matcher, the value of the first property of an object source is returned',
                  [
                    {prop0:'',prop1:'hey',prop2:'',prop3:'there',prop4:'foo',prop5:'',prop6:'',prop7:'bar'},
                    true
                  ],
                  ''
                ],
                ['When the value undefined is specified for the matcher, the first value of a range source is returned',
                  [5,true],
                  0
                ],

              /*** test when regular expression is specified for matcher ***/
                ['When a regular expression is specified for the matcher, the value for the first matching element of an array source is returned',
                  [
                    ['boo','1234','hey','hi42','6','number 6',''],
                    /^\d+$/
                  ],
                  '1234'
                ],
                ['When a regular expression is specified for the matcher, the value for the first maching property is returned',
                  [
                    {prop0:'1234',prop1:'hey',prop2:'hi42',prop3:'6',prop4:'number 6',prop5:''},
                    /^\d+$/
                  ],
                  '1234'
                ],
                ['When a regular expression is specified for the matcher, the first matching value of a range source is returned',
                  [100,/^\d1$/],
                  11
                ]
          ]]
        ])
      ]
    });
  }
});