Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Changeset 1691

Show
Ignore:
Timestamp:
06/24/10 00:38:40 (14 years ago)
Author:
dsimcha
Message:

Start improving std.range unittests. Lots of bugs found already.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/phobos/std/range.d

    r1690 r1691  
    2424import std.contracts; 
    2525import std.traits; 
    2626import std.typecons; 
    2727import std.typetuple; 
    2828import std.algorithm; 
    2929import std.functional; 
    3030import std.conv; 
    3131version(unittest) 
    3232{ 
    3333    import std.container, std.conv, std.math, std.stdio; 
     34 
     35    // Used with the dummy ranges for testing higher order ranges. 
     36    enum RangeType { 
     37        Input, 
     38        Forward, 
     39        Bidirectional, 
     40        Random 
     41    } 
     42 
     43    enum Length { 
     44        Yes, 
     45        No 
     46    } 
     47 
     48    enum ReturnBy { 
     49        Reference, 
     50        Value 
     51    } 
     52 
     53    // Range that's useful for testing other higher order ranges, 
     54    // can be parametrized with attributes.  It just dumbs down an array of 
     55    // numbers 1..10. 
     56    struct DummyRange(ReturnBy _r, Length _l, RangeType _rt) { 
     57        // These enums are so that the template params are visible outside 
     58        // this instantiation. 
     59        enum r = _r; 
     60        enum l = _l; 
     61        enum rt = _rt; 
     62 
     63        uint[] arr = [1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]; 
     64 
     65        void reinit() { 
     66            // Workaround for DMD bug 4378 
     67            arr = [1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]; 
     68        } 
     69 
     70        void popFront() { 
     71            arr = arr[1..$]; 
     72        } 
     73 
     74        @property bool empty() { 
     75            return arr.length == 0; 
     76        } 
     77 
     78        static if(r == ReturnBy.Reference) { 
     79            @property ref uint front() { 
     80                return arr[0]; 
     81            } 
     82        } else { 
     83            @property uint front() { 
     84                return arr[0]; 
     85            } 
     86        } 
     87 
     88        static if(rt >= RangeType.Forward) { 
     89            @property typeof(this) save() { 
     90                return this; 
     91            } 
     92        } 
     93 
     94        static if(rt >= RangeType.Bidirectional) { 
     95            void popBack() { 
     96                arr = arr[0..$ - 1]; 
     97            } 
     98 
     99            static if(r == ReturnBy.Reference) { 
     100                @property ref uint back() { 
     101                    return arr[$ - 1]; 
     102                } 
     103            } else { 
     104                @property uint back() { 
     105                    return arr[$ - 1]; 
     106                } 
     107            } 
     108        } 
     109 
     110        static if(rt >= RangeType.Random) { 
     111            static if(r == ReturnBy.Reference) { 
     112                @property ref uint opIndex(size_t index) { 
     113                    return arr[index]; 
     114                } 
     115            } else { 
     116                @property uint opIndex(size_t index) { 
     117                    return arr[index]; 
     118                } 
     119            } 
     120        } 
     121 
     122        static if(l == Length.Yes) { 
     123            @property size_t length() { 
     124                return arr.length; 
     125            } 
     126        } 
     127    } 
     128 
     129    alias TypeTuple!( 
     130        DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input), 
     131        DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), 
     132        DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), 
     133        DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), 
     134        DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input), 
     135        DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward), 
     136        DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional), 
     137        DummyRange!(ReturnBy.Reference, Length.No, RangeType.Random), 
     138        DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input), 
     139        DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward), 
     140        DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional), 
     141        DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random), 
     142        DummyRange!(ReturnBy.Value, Length.No, RangeType.Input), 
     143        DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward), 
     144        DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional), 
     145        DummyRange!(ReturnBy.Value, Length.No, RangeType.Random) 
     146    ) AllDummyRanges; 
     147 
     148    alias TypeTuple!(1,2,3,4,5,6,7,8,9,0,11,12,13,14,15,16) DummyIndices; 
    34149} 
    35150 
    36151/** 
    37152Returns $(D true) if $(D R) is an input range. An input range must 
    38153define the primitives $(D empty), $(D popFront), and $(D front). The 
    39154following code should compile for any input range. 
    40155 
    41156---- 
    42157R r;             // can define a range object 
    43158if (r.empty) {}  // can test for empty 
     
    619734        assert(r.front == witness.front); 
    620735        assert(r.back == witness.back); 
    621736        assert(equal(r, witness)); 
    622737    } 
    623738    test([ 1 ], [ 1 ]); 
    624739    test([ 1, 2 ], [ 2, 1 ]); 
    625740    test([ 1, 2, 3 ], [ 3, 2, 1 ]); 
    626741    test([ 1, 2, 3, 4 ], [ 4, 3, 2, 1 ]); 
    627742    test([ 1, 2, 3, 4, 5 ], [ 5, 4, 3, 2, 1 ]); 
    628743    test([ 1, 2, 3, 4, 5, 6 ], [ 6, 5, 4, 3, 2, 1 ]); 
     744 
     745    foreach(DummyType; AllDummyRanges) { 
     746        static if(!isBidirectionalRange!DummyType) { 
     747            static assert(!__traits(compiles, Retro!DummyType)); 
     748        } else { 
     749            DummyType dummyRange; 
     750            dummyRange.reinit(); 
     751 
     752            auto myRetro = retro(dummyRange); 
     753            assert(myRetro.front == 10); 
     754            assert(myRetro.back == 1); 
     755 
     756            static if(isRandomAccessRange!DummyType && hasLength!DummyType) { 
     757                assert(myRetro[0] == myRetro.front); 
     758 
     759                static if(DummyType.r == ReturnBy.Reference) { 
     760                    myRetro[9]++; 
     761                    assert(dummyRange[0] == 2); 
     762                    myRetro.front++; 
     763                    assert(myRetro.front == 11); 
     764                    myRetro.back++; 
     765                    assert(myRetro.back == 3); 
     766                } 
     767            } 
     768        } 
     769    } 
    629770} 
    630771 
    631772/** 
    632773Iterates range $(D r) with stride $(D n). If the range is a 
    633774random-access range, moves by indexing into the range; otehrwise, 
    634775moves by successive calls to $(D popFront). 
    635776 
    636777Example: 
    637778---- 
    638779int[] a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]; 
     
    655796        _n = n; 
    656797        static if (hasLength!(R)) 
    657798        { 
    658799            auto slack = _input.length % _n; 
    659800            if (slack) slack--; 
    660801            if (!slack) return; 
    661802            static if (isRandomAccessRange!(R) && hasSlicing!(R)) 
    662803            { 
    663804                _input = _input[0 .. _input.length - slack]; 
    664805            } 
    665             else 
     806            else static if(isBidirectionalRange!(R)) 
    666807            { 
    667808                foreach (i; 0 .. slack) 
    668809                { 
    669810                    if (_input.empty) break; 
    670811                    _input.popBack; 
    671812                } 
    672813            } 
    673814        } 
    674815    } 
    675816 
    676817/** 
    677818Returns $(D this). 
    678819 */ 
    679     @property Stride save() 
    680     { 
    681         return Stride(_input.save, _n); 
     820    static if(isForwardRange!(R)) 
     821    { 
     822        @property Stride save() 
     823        { 
     824            return Stride(_input.save, _n); 
     825        } 
    682826    } 
    683827 
    684828/** 
    685829Forwards to $(D _input.empty). 
    686830 */ 
    687831    bool empty() 
    688832    { 
    689833        return _input.empty; 
    690834    } 
    691835 
     
    704848            foreach (i; 0 .. _n) 
    705849            { 
    706850                _input.popFront; 
    707851                if (_input.empty) break; 
    708852            } 
    709853    } 
    710854 
    711855/** 
    712856Forwards to $(D _input.popFront). 
    713857 */ 
    714     static if (hasLength!(R)) 
     858    static if (isBidirectionalRange!(R) && hasLength!(R)) 
    715859        void popBack() 
    716860        { 
    717861            enforce(_input.length >= _n); 
    718862            static if (isRandomAccessRange!(R) && hasSlicing!(R)) 
    719863            { 
    720864                _input = _input[0 .. _input.length - _n]; 
    721865            } 
    722866            else 
    723867            { 
    724868                foreach (i; 0 .. _n) 
     
    733877Forwards to $(D _input.front). 
    734878 */ 
    735879    ref ElementType!(R) front() 
    736880    { 
    737881        return _input.front; 
    738882    } 
    739883 
    740884/** 
    741885Forwards to $(D _input.back) after getting rid of any slack items. 
    742886 */ 
    743     ref ElementType!(R) back() 
    744     { 
    745         return _input.back; 
     887    static if(isBidirectionalRange!(R) && hasLength!(R)) 
     888    { 
     889        ref ElementType!(R) back() 
     890        { 
     891            return _input.back; 
     892        } 
    746893    } 
    747894 
    748895/** 
    749896Forwards to $(D _input[_input.length - n + 1]). Defined only if $(D R) 
    750897is a random access range and if $(D R) defines $(D R.length). 
    751898 */ 
    752899    static if (isRandomAccessRange!(R) && hasLength!(R)) 
    753900        ref ElementType!(R) opIndex(uint n) 
    754901        { 
    755902            return _input[_n * n]; 
     
    781928    void test(size_t n, int[] input, int[] witness) 
    782929    { 
    783930        assert(equal(stride(input, n), witness)); 
    784931    } 
    785932    test(1, [], []); 
    786933    int[] arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; 
    787934    test(1, arr, arr); 
    788935    test(2, arr, [1, 3, 5, 7, 9]); 
    789936    test(3, arr, [1, 4, 7, 10]); 
    790937    test(4, arr, [1, 5, 9]); 
     938 
     939    foreach(DummyType; AllDummyRanges) { 
     940        static if(DummyType.r == ReturnBy.Reference) { 
     941            // Doesn't work yet w/o ref returns, see DMD bug 3294. 
     942            DummyType dummyRange; 
     943            dummyRange.reinit(); 
     944 
     945            auto myStride = stride(dummyRange, 4); 
     946            assert(myStride.front == 1); 
     947            assert(equal(myStride, [1, 5, 9])); 
     948 
     949            static if(hasLength!DummyType) { 
     950                assert(myStride.length == 3); 
     951            } 
     952 
     953            static if(isBidirectionalRange!DummyType && hasLength!DummyType) { 
     954                assert(myStride.back == 9); 
     955            } 
     956 
     957            static if(isRandomAccessRange!DummyType && hasLength!DummyType) { 
     958                assert(myStride[0] == 1); 
     959                assert(myStride[1] == 5); 
     960                assert(myStride[2] == 9); 
     961            } 
     962        } 
     963    } 
    791964} 
    792965 
    793966/** 
    794967Spans multiple ranges in sequence. The function $(D chain) takes any 
    795968number of ranges and returns a $(D Chain!(R1, R2,...)) object. The 
    796969ranges may be different, but they must have the same element type. The 
    797970result is a range that offers the $(D front), $(D popFront), and $(D empty) 
    798971primitives. If all input ranges offer random access and $(D length), 
    799972$(D Chain) offers them as well. 
    800973 
     
    16311804/// Ditto 
    16321805Cycle!(R) cycle(R)(ref R input, size_t index = 0) if (isStaticArray!R) 
    16331806{ 
    16341807    return Cycle!(R)(input, index); 
    16351808} 
    16361809 
    16371810unittest 
    16381811{ 
    16391812    assert(equal(take(cycle([1, 2][]), 5), [ 1, 2, 1, 2, 1 ][])); 
    16401813    static assert(isForwardRange!(Cycle!(uint[]))); 
    1641  
    1642     struct DummyRange { 
    1643         uint num; 
    1644         ref uint front() { return num;  } 
    1645         void popFront() { num++;   } 
    1646         @property bool empty() { return num >= 10; } 
    1647         typeof(this) save() { return this; } 
    1648     } 
    1649     static assert(isForwardRange!(Cycle!(DummyRange))); 
    16501814 
    16511815    int[3] a = [ 1, 2, 3 ]; 
    16521816    static assert(isStaticArray!(typeof(a))); 
    16531817    auto c = cycle(a); 
    16541818    assert(a.ptr == c._ptr); 
    16551819    assert(equal(take(cycle(a), 5), [ 1, 2, 3, 1, 2 ][])); 
    16561820    static assert(isForwardRange!(typeof(c))); 
    16571821} 
    16581822 
    16591823unittest // For infinite ranges