PageRenderTime 59ms CodeModel.GetById 36ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

/test_apiary/span.py

https://bitbucket.org/lindenlab/apiary/
Python | 219 lines | 153 code | 37 blank | 29 comment | 56 complexity | 4055c0206356934118a603f005c96760 MD5 | raw file
  1#
  2# $LicenseInfo:firstyear=2010&license=mit$
  3# 
  4# Copyright (c) 2010, Linden Research, Inc.
  5# 
  6# Permission is hereby granted, free of charge, to any person obtaining a copy
  7# of this software and associated documentation files (the "Software"), to deal
  8# in the Software without restriction, including without limitation the rights
  9# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10# copies of the Software, and to permit persons to whom the Software is
 11# furnished to do so, subject to the following conditions:
 12# 
 13# The above copyright notice and this permission notice shall be included in
 14# all copies or substantial portions of the Software.
 15# 
 16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22# THE SOFTWARE.
 23# $/LicenseInfo$
 24#
 25
 26import unittest
 27from apiary.tools.span import Span, SpanSequence, SlidingWindowSequence
 28    
 29
 30class SpanTests (unittest.TestCase):
 31    def testInvalidRangeConstruction(self):
 32        # *FIX: We allow point spans for now for the convenience of a demo:
 33        #badspans = [(0, 0), # A Span cannot be a point.
 34        #            (1, 0)]
 35        badspans = [(1, 0)]
 36        
 37        for badspan in badspans:
 38            l, r = badspan
 39            try:
 40                span = Span(l, r)
 41            except AssertionError:
 42                return # Success
 43            self.fail('Invalid span range %r successfully constructed span %r.' % (badspan, span))
 44        
 45    def testStartAndEndInvariants(self):
 46        li, ri = 0, 1
 47        span = Span(li, ri)
 48        self.failUnlessEqual(li, span.start)
 49        self.failUnlessEqual(ri, span.end)
 50        lo, ro = span
 51        self.failUnlessEqual(li, lo)
 52        self.failUnlessEqual(ri, ro)
 53        self.failUnlessEqual(span.start, lo)
 54        self.failUnlessEqual(span.end, ro)
 55
 56    def testMagnitudeInvariants(self):
 57        for (l, r) in [(0, 1), (0, 10), (-100, 100)]:
 58            span = Span(l, r)
 59            self.failUnlessEqual(span.magnitude, r-l)
 60            
 61    def testStartAndEndReadonly(self):
 62        span = Span(0, 1)
 63        err = None
 64        try:
 65            span.start = 42
 66        except AttributeError, e:
 67            err = e
 68        self.failIfEqual(None, err)
 69        err = None
 70        try:
 71            span.end = 43
 72        except AttributeError, e:
 73            err = e
 74        self.failIfEqual(None, err)
 75
 76    def testPositiveContains(self):
 77        span = Span(0, 10)
 78        for i in range(span.start, span.end):
 79            self.failUnless(span.contains(i),
 80                            '%r should contain %r but .contains() returns False.' % (span, i))
 81
 82    def testNegativeContains(self):
 83        span = Span(0, 10)
 84        for badpt in [span.end, span.start - 0.1, 2**33, -2**33]:
 85            self.failIf(span.contains(badpt),
 86                        '%r should not contain %r but .contains() returns True.' % (span, badpt))
 87        
 88    def testPositiveOverlaps(self):
 89        span = Span(0, 10)
 90        for (s, e) in [(9.9, 10), span, (-42, 0.1), (9.99999, 43), (-10, 20), (5, 6)]:
 91            olap = Span(s, e)
 92            self.failUnless(span.overlaps(olap),
 93                            '%r should overlap %r, but .overlaps returns False.' % (span, olap))
 94
 95    def testNegativeOverlaps(self):
 96        span = Span(0, 10)
 97        for (s, e) in [(-10, 0), (10, 11), (-42, -0.000001), (10, 43)]:
 98            olap = Span(s, e)
 99            self.failIf(span.overlaps(olap),
100                        '%r should not overlap %r, but .overlaps returns True.' % (span, olap))
101
102
103def make_span(tup):
104    '''Make a Span from a tuple and associate an arbitrary but instance-specific data item.'''
105    (s, e) = tup
106    return Span(s, e, data=('somedata', s, e))
107
108
109class SpanSequenceTests (unittest.TestCase):
110    empty = []
111    
112    contiguous = map(make_span,
113                     [(0, 1),
114                      (1, 2),
115                      (2, 3)])
116
117    discontiguous = map(make_span,
118                        [(0, 1),
119                         (2, 3),
120                         (4, 5)])
121
122    overlapping = map(make_span,
123                      [(0, 2),
124                       (1, 3),
125                       (1, 4),
126                       (2, 5)])
127
128    all_sequences = [empty, contiguous, discontiguous, overlapping]
129
130
131    def testData(self):
132        for seq in self.all_sequences:
133            seq = SpanSequence(seq)
134            for span in seq:
135                self.failUnlessEqual(span.data, ('somedata', span.start, span.end))
136        
137    def testOrdering(self):
138        for seq in self.all_sequences:
139            seq = SpanSequence(seq)
140            lastspan = None
141            for span in seq:
142                if lastspan is None:
143                    lastspan = span.start
144                else:
145                    self.failUnless(lastspan < span)
146                    lastspan = span
147
148    def testPositiveAppend(self):
149        for spans in self.all_sequences:
150            seq = SpanSequence()
151            for span in spans:
152                seq.append(span)
153                    
154    def testNegativeAppend(self):
155        for spans in self.all_sequences:
156            seq = SpanSequence()
157            for span in reversed(spans):
158                try:
159                    seq.append(span)
160                except AssertionError:
161                    pass # Successfully detected out-of-order append.
162        
163    def testInsertAndAppendEquivalence(self):
164        for spans in self.all_sequences:
165            ins = SpanSequence(spans)
166            app = SpanSequence()
167            [app.append(s) for s in spans]
168            self.failUnlessEqual(ins, app)
169        
170    def testLength(self):
171        for spans in self.all_sequences:
172            seq = SpanSequence(spans)
173            self.failUnlessEqual(len(seq), len(spans))
174
175    def testDisjointBins(self):
176        for spans in [self.empty, self.contiguous, self.discontiguous]: # Exclude overlapping for the test condition:
177            if spans:
178                disjointbinwidth = min([s.magnitude for s in spans])
179            else:
180                disjointbinwidth = 1 # The value does not matter, so long as positive.
181            seq = SpanSequence(spans)
182            for window, subseq in seq.as_bins(disjointbinwidth):
183                if len(subseq) not in (0, 1):
184                    self.fail('Disjoint bin over %r contains more than one span: %r' % (window, list(subseq)))
185
186    def testDisjointUniformBins(self):
187        '''In this test, every bin should have exactly one span.'''
188        seq = SpanSequence(self.contiguous)
189        for window, subseq in seq.as_bins(self.contiguous[0].magnitude):
190            self.failUnlessEqual(1, len(subseq),
191                                 'Uniform disjoint bin over %r does not contain a single span:: %r' % (window, list(subseq)))  
192
193    def testConcurrencyVectorTimesIncreaseMonotonically(self):
194        decreasing_endings = map(make_span, [(0, 2), (0, 1), (3, 4)])
195        last = None
196        for (t, span, spans) in SpanSequence(decreasing_endings).concurrency_vector():
197            if last is not None:
198                self.failIf(last > t,
199                            'Overlap vector is not monotonically increasing.')
200            last = t
201                        
202        
203
204
205class SlidingWindowSequenceTests (unittest.TestCase):
206    def testDiscontinuousWindow(self):
207        for spans in [SpanSequenceTests.contiguous,
208                      SpanSequenceTests.discontiguous]:
209
210            slwin = SlidingWindowSequence(spans[1].start - spans[0].start)
211            for span in spans:
212                slwin.append(span)
213                self.failUnlessEqual(1, len(slwin),
214                                     'Sliding window should encompass exactly one span, not: %r' % (slwin._spans,))
215            
216
217        
218if __name__ == '__main__':
219    unittest.main()