PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/test_apiary/span.py

https://bitbucket.org/lindenlab/apiary/
Python | 219 lines | 153 code | 37 blank | 29 comment | 39 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. import unittest
  26. from apiary.tools.span import Span, SpanSequence, SlidingWindowSequence
  27. class SpanTests (unittest.TestCase):
  28. def testInvalidRangeConstruction(self):
  29. # *FIX: We allow point spans for now for the convenience of a demo:
  30. #badspans = [(0, 0), # A Span cannot be a point.
  31. # (1, 0)]
  32. badspans = [(1, 0)]
  33. for badspan in badspans:
  34. l, r = badspan
  35. try:
  36. span = Span(l, r)
  37. except AssertionError:
  38. return # Success
  39. self.fail('Invalid span range %r successfully constructed span %r.' % (badspan, span))
  40. def testStartAndEndInvariants(self):
  41. li, ri = 0, 1
  42. span = Span(li, ri)
  43. self.failUnlessEqual(li, span.start)
  44. self.failUnlessEqual(ri, span.end)
  45. lo, ro = span
  46. self.failUnlessEqual(li, lo)
  47. self.failUnlessEqual(ri, ro)
  48. self.failUnlessEqual(span.start, lo)
  49. self.failUnlessEqual(span.end, ro)
  50. def testMagnitudeInvariants(self):
  51. for (l, r) in [(0, 1), (0, 10), (-100, 100)]:
  52. span = Span(l, r)
  53. self.failUnlessEqual(span.magnitude, r-l)
  54. def testStartAndEndReadonly(self):
  55. span = Span(0, 1)
  56. err = None
  57. try:
  58. span.start = 42
  59. except AttributeError, e:
  60. err = e
  61. self.failIfEqual(None, err)
  62. err = None
  63. try:
  64. span.end = 43
  65. except AttributeError, e:
  66. err = e
  67. self.failIfEqual(None, err)
  68. def testPositiveContains(self):
  69. span = Span(0, 10)
  70. for i in range(span.start, span.end):
  71. self.failUnless(span.contains(i),
  72. '%r should contain %r but .contains() returns False.' % (span, i))
  73. def testNegativeContains(self):
  74. span = Span(0, 10)
  75. for badpt in [span.end, span.start - 0.1, 2**33, -2**33]:
  76. self.failIf(span.contains(badpt),
  77. '%r should not contain %r but .contains() returns True.' % (span, badpt))
  78. def testPositiveOverlaps(self):
  79. span = Span(0, 10)
  80. for (s, e) in [(9.9, 10), span, (-42, 0.1), (9.99999, 43), (-10, 20), (5, 6)]:
  81. olap = Span(s, e)
  82. self.failUnless(span.overlaps(olap),
  83. '%r should overlap %r, but .overlaps returns False.' % (span, olap))
  84. def testNegativeOverlaps(self):
  85. span = Span(0, 10)
  86. for (s, e) in [(-10, 0), (10, 11), (-42, -0.000001), (10, 43)]:
  87. olap = Span(s, e)
  88. self.failIf(span.overlaps(olap),
  89. '%r should not overlap %r, but .overlaps returns True.' % (span, olap))
  90. def make_span(tup):
  91. '''Make a Span from a tuple and associate an arbitrary but instance-specific data item.'''
  92. (s, e) = tup
  93. return Span(s, e, data=('somedata', s, e))
  94. class SpanSequenceTests (unittest.TestCase):
  95. empty = []
  96. contiguous = map(make_span,
  97. [(0, 1),
  98. (1, 2),
  99. (2, 3)])
  100. discontiguous = map(make_span,
  101. [(0, 1),
  102. (2, 3),
  103. (4, 5)])
  104. overlapping = map(make_span,
  105. [(0, 2),
  106. (1, 3),
  107. (1, 4),
  108. (2, 5)])
  109. all_sequences = [empty, contiguous, discontiguous, overlapping]
  110. def testData(self):
  111. for seq in self.all_sequences:
  112. seq = SpanSequence(seq)
  113. for span in seq:
  114. self.failUnlessEqual(span.data, ('somedata', span.start, span.end))
  115. def testOrdering(self):
  116. for seq in self.all_sequences:
  117. seq = SpanSequence(seq)
  118. lastspan = None
  119. for span in seq:
  120. if lastspan is None:
  121. lastspan = span.start
  122. else:
  123. self.failUnless(lastspan < span)
  124. lastspan = span
  125. def testPositiveAppend(self):
  126. for spans in self.all_sequences:
  127. seq = SpanSequence()
  128. for span in spans:
  129. seq.append(span)
  130. def testNegativeAppend(self):
  131. for spans in self.all_sequences:
  132. seq = SpanSequence()
  133. for span in reversed(spans):
  134. try:
  135. seq.append(span)
  136. except AssertionError:
  137. pass # Successfully detected out-of-order append.
  138. def testInsertAndAppendEquivalence(self):
  139. for spans in self.all_sequences:
  140. ins = SpanSequence(spans)
  141. app = SpanSequence()
  142. [app.append(s) for s in spans]
  143. self.failUnlessEqual(ins, app)
  144. def testLength(self):
  145. for spans in self.all_sequences:
  146. seq = SpanSequence(spans)
  147. self.failUnlessEqual(len(seq), len(spans))
  148. def testDisjointBins(self):
  149. for spans in [self.empty, self.contiguous, self.discontiguous]: # Exclude overlapping for the test condition:
  150. if spans:
  151. disjointbinwidth = min([s.magnitude for s in spans])
  152. else:
  153. disjointbinwidth = 1 # The value does not matter, so long as positive.
  154. seq = SpanSequence(spans)
  155. for window, subseq in seq.as_bins(disjointbinwidth):
  156. if len(subseq) not in (0, 1):
  157. self.fail('Disjoint bin over %r contains more than one span: %r' % (window, list(subseq)))
  158. def testDisjointUniformBins(self):
  159. '''In this test, every bin should have exactly one span.'''
  160. seq = SpanSequence(self.contiguous)
  161. for window, subseq in seq.as_bins(self.contiguous[0].magnitude):
  162. self.failUnlessEqual(1, len(subseq),
  163. 'Uniform disjoint bin over %r does not contain a single span:: %r' % (window, list(subseq)))
  164. def testConcurrencyVectorTimesIncreaseMonotonically(self):
  165. decreasing_endings = map(make_span, [(0, 2), (0, 1), (3, 4)])
  166. last = None
  167. for (t, span, spans) in SpanSequence(decreasing_endings).concurrency_vector():
  168. if last is not None:
  169. self.failIf(last > t,
  170. 'Overlap vector is not monotonically increasing.')
  171. last = t
  172. class SlidingWindowSequenceTests (unittest.TestCase):
  173. def testDiscontinuousWindow(self):
  174. for spans in [SpanSequenceTests.contiguous,
  175. SpanSequenceTests.discontiguous]:
  176. slwin = SlidingWindowSequence(spans[1].start - spans[0].start)
  177. for span in spans:
  178. slwin.append(span)
  179. self.failUnlessEqual(1, len(slwin),
  180. 'Sliding window should encompass exactly one span, not: %r' % (slwin._spans,))
  181. if __name__ == '__main__':
  182. unittest.main()