|
- # -*- coding: utf-8 -*-
-
- import nilmdb
- from nilmdb.utils.printf import *
- import datetime_tz
-
- from nose.tools import *
- from nose.tools import assert_raises
- import itertools
-
- from nilmdb.utils.interval import IntervalError
- from nilmdb.server.interval import Interval, DBInterval, IntervalSet
-
- # so we can test them separately
- from nilmdb.utils.interval import Interval as UtilsInterval
-
- from testutil.helpers import *
- import unittest
-
- # set to False to skip live renders
- do_live_renders = False
- def render(iset, description = "", live = True):
- import testutil.renderdot as renderdot
- r = renderdot.RBTreeRenderer(iset.tree)
- return r.render(description, live and do_live_renders)
-
- def makeset(string):
- """Build an IntervalSet from a string, for testing purposes
-
- Each character is 1 second
- [ = interval start
- | = interval end + next start
- ] = interval end
- . = zero-width interval (identical start and end)
- anything else is ignored
- """
- iset = IntervalSet()
- for i, c in enumerate(string):
- day = i + 10000
- if (c == "["):
- start = day
- elif (c == "|"):
- iset += Interval(start, day)
- start = day
- elif (c == ")"):
- iset += Interval(start, day)
- del start
- elif (c == "."):
- iset += Interval(day, day)
- return iset
-
- class TestInterval:
- def test_client_interval(self):
- # Run interval tests against the Python version of Interval.
- global Interval
- NilmdbInterval = Interval
- Interval = UtilsInterval
- self.test_interval()
- self.test_interval_intersect()
- Interval = NilmdbInterval
-
- # Other helpers in nilmdb.utils.interval
- i = [ UtilsInterval(1,2), UtilsInterval(2,3), UtilsInterval(4,5) ]
- eq_(list(nilmdb.utils.interval.optimize(i)),
- [ UtilsInterval(1,3), UtilsInterval(4,5) ])
- eq_(UtilsInterval(1234567890123456, 1234567890654321).human_string(),
- "[ Fri, 13 Feb 2009 18:31:30.123456 -0500 -> " +
- "Fri, 13 Feb 2009 18:31:30.654321 -0500 ]")
-
- def test_interval(self):
- # Test Interval class
- os.environ['TZ'] = "America/New_York"
- datetime_tz._localtz = None
- (d1, d2, d3) = [ nilmdb.utils.time.parse_time(x)
- for x in [ "03/24/2012", "03/25/2012", "03/26/2012" ] ]
-
- # basic construction
- i = Interval(d1, d2)
- i = Interval(d1, d3)
- eq_(i.start, d1)
- eq_(i.end, d3)
-
- # assignment is allowed, but not verified
- i.start = d2
- #with assert_raises(IntervalError):
- # i.end = d1
- i.start = d1
- i.end = d2
-
- # end before start
- with assert_raises(IntervalError):
- i = Interval(d3, d1)
-
- # compare
- assert(Interval(d1, d2) == Interval(d1, d2))
- assert(Interval(d1, d2) < Interval(d1, d3))
- assert(Interval(d1, d3) > Interval(d1, d2))
- assert(Interval(d1, d2) < Interval(d2, d3))
- assert(Interval(d1, d3) < Interval(d2, d3))
- assert(Interval(d2, d2+1) > Interval(d1, d3))
- assert(Interval(d3, d3+1) == Interval(d3, d3+1))
- #with assert_raises(TypeError): # was AttributeError, that's wrong
- # x = (i == 123)
-
- # subset
- eq_(Interval(d1, d3).subset(d1, d2), Interval(d1, d2))
- with assert_raises(IntervalError):
- x = Interval(d2, d3).subset(d1, d2)
-
- # big integers, negative integers
- x = Interval(5000111222000000, 6000111222000000)
- eq_(str(x), "[5000111222000000 -> 6000111222000000)")
- x = Interval(-5000111222000000, -4000111222000000)
- eq_(str(x), "[-5000111222000000 -> -4000111222000000)")
-
- # misc
- i = Interval(d1, d2)
- eq_(repr(i), repr(eval(repr(i))))
- eq_(str(i), "[1332561600000000 -> 1332648000000000)")
-
- def test_interval_intersect(self):
- # Test Interval intersections
- dates = [ 100, 200, 300, 400 ]
- perm = list(itertools.permutations(dates, 2))
- prod = list(itertools.product(perm, perm))
- should_intersect = {
- False: [4, 5, 8, 20, 48, 56, 60, 96, 97, 100],
- True: [0, 1, 2, 12, 13, 14, 16, 17, 24, 25, 26, 28, 29,
- 32, 49, 50, 52, 53, 61, 62, 64, 65, 68, 98, 101, 104]
- }
- for i,((a,b),(c,d)) in enumerate(prod):
- try:
- i1 = Interval(a, b)
- i2 = Interval(c, d)
- eq_(i1.intersects(i2), i2.intersects(i1))
- in_(i, should_intersect[i1.intersects(i2)])
- except IntervalError:
- assert(i not in should_intersect[True] and
- i not in should_intersect[False])
- with assert_raises(TypeError):
- x = i1.intersects(1234)
-
- def test_intervalset_construct(self):
- # Test IntervalSet construction
- dates = [ 100, 200, 300, 400 ]
-
- a = Interval(dates[0], dates[1])
- b = Interval(dates[1], dates[2])
- c = Interval(dates[0], dates[2])
- d = Interval(dates[2], dates[3])
-
- iseta = IntervalSet(a)
- isetb = IntervalSet([a, b])
- isetc = IntervalSet([a])
- ne_(iseta, isetb)
- eq_(iseta, isetc)
- with assert_raises(TypeError):
- x = iseta != 3
- ne_(IntervalSet(a), IntervalSet(b))
-
- # Note that assignment makes a new reference (not a copy)
- isetd = IntervalSet(isetb)
- isete = isetd
- eq_(isetd, isetb)
- eq_(isetd, isete)
- isetd -= a
- ne_(isetd, isetb)
- eq_(isetd, isete)
-
- # test iterator
- for interval in iseta:
- pass
-
- # overlap
- with assert_raises(IntervalError):
- x = IntervalSet([a, b, c])
-
- # bad types
- with assert_raises(Exception):
- x = IntervalSet([1, 2])
-
- iset = IntervalSet(isetb) # test iterator
- eq_(iset, isetb)
- eq_(len(iset), 2)
- eq_(len(IntervalSet()), 0)
-
- # Test adding
- iset = IntervalSet(a)
- iset += IntervalSet(b)
- eq_(iset, IntervalSet([a, b]))
-
- iset = IntervalSet(a)
- iset += b
- eq_(iset, IntervalSet([a, b]))
-
- iset = IntervalSet(a)
- iset.iadd_nocheck(b)
- eq_(iset, IntervalSet([a, b]))
-
- iset = IntervalSet(a) + IntervalSet(b)
- eq_(iset, IntervalSet([a, b]))
-
- iset = IntervalSet(b) + a
- eq_(iset, IntervalSet([a, b]))
-
- # A set consisting of [0-1],[1-2] should match a set consisting of [0-2]
- eq_(IntervalSet([a,b]), IntervalSet([c]))
- # Etc
- ne_(IntervalSet([a,d]), IntervalSet([c]))
- ne_(IntervalSet([c]), IntervalSet([a,d]))
- ne_(IntervalSet([c,d]), IntervalSet([b,d]))
-
- # misc
- eq_(repr(iset), repr(eval(repr(iset))))
- eq_(str(iset),
- "[[100 -> 200), [200 -> 300)]")
-
- def test_intervalset_geniset(self):
- # Test basic iset construction
- eq_(makeset(" [----) "),
- makeset(" [-|--) "))
-
- eq_(makeset("[) [--) ") +
- makeset(" [) [--)"),
- makeset("[|) [-----)"))
-
- eq_(makeset(" [-------)"),
- makeset(" [-|-----|"))
-
-
- def test_intervalset_intersect_difference(self):
- # Test intersection (&)
- with assert_raises(TypeError): # was AttributeError
- x = makeset("[--)") & 1234
-
- def do_test(a, b, c, d):
- # a & b == c (using nilmdb.server.interval)
- ab = IntervalSet()
- for x in b:
- for i in (a & x):
- ab += i
- eq_(ab,c)
-
- # a & b == c (using nilmdb.utils.interval)
- eq_(IntervalSet(nilmdb.utils.interval.intersection(a,b)), c)
-
- # a \ b == d
- eq_(IntervalSet(nilmdb.utils.interval.set_difference(a,b)), d)
-
- # Intersection with intervals
- do_test(makeset("[---|---)[)"),
- makeset(" [------) "),
- makeset(" [-----) "), # intersection
- makeset("[-) [)")) # difference
-
- do_test(makeset("[---------)"),
- makeset(" [---) "),
- makeset(" [---) "), # intersection
- makeset("[) [----)")) # difference
-
- do_test(makeset(" [---) "),
- makeset("[---------)"),
- makeset(" [---) "), # intersection
- makeset(" ")) # difference
-
- do_test(makeset(" [-----)"),
- makeset(" [-----) "),
- makeset(" [--) "), # intersection
- makeset(" [--)")) # difference
-
- do_test(makeset(" [--) [--)"),
- makeset(" [------) "),
- makeset(" [-) [-) "), # intersection
- makeset(" [) [)")) # difference
-
- do_test(makeset(" [---)"),
- makeset(" [--) "),
- makeset(" "), # intersection
- makeset(" [---)")) # difference
-
- do_test(makeset(" [-|---)"),
- makeset(" [-----|-) "),
- makeset(" [----) "), # intersection
- makeset(" [)")) # difference
-
- do_test(makeset(" [-|-) "),
- makeset(" [-|--|--) "),
- makeset(" [---) "), # intersection
- makeset(" ")) # difference
-
- do_test(makeset("[-)[-)[-)[)"),
- makeset(" [) [|)[) "),
- makeset(" [) [) "), # intersection
- makeset("[) [-) [)[)")) # difference
-
- # Border cases -- will give different results if intervals are
- # half open or fully closed. In nilmdb, they are half open.
- do_test(makeset(" [---)"),
- makeset(" [----) "),
- makeset(" "), # intersection
- makeset(" [---)")) # difference
-
- do_test(makeset(" [----)[--)"),
- makeset("[-) [--) [)"),
- makeset(" [) [-) [)"), # intersection
- makeset(" [-) [-) ")) # difference
-
- # Set difference with bounds
- a = makeset(" [----)[--)")
- b = makeset("[-) [--) [)")
- c = makeset("[----) ")
- d = makeset(" [-) ")
- eq_(nilmdb.utils.interval.set_difference(
- a.intersection(list(c)[0]), b.intersection(list(c)[0])), d)
-
- # Fill out test coverage for non-subsets
- def diff2(a,b, subset):
- return nilmdb.utils.interval._interval_math_helper(
- a, b, (lambda a, b: b and not a), subset=subset)
- with assert_raises(nilmdb.utils.interval.IntervalError):
- list(diff2(a,b,True))
- list(diff2(a,b,False))
-
- # Empty second set
- eq_(nilmdb.utils.interval.set_difference(a, IntervalSet()), a)
-
- # Empty second set
- eq_(nilmdb.utils.interval.set_difference(a, IntervalSet()), a)
-
- class TestIntervalDB:
- def test_dbinterval(self):
- # Test DBInterval class
- i = DBInterval(100, 200, 100, 200, 10000, 20000)
- eq_(i.start, 100)
- eq_(i.end, 200)
- eq_(i.db_start, 100)
- eq_(i.db_end, 200)
- eq_(i.db_startpos, 10000)
- eq_(i.db_endpos, 20000)
- eq_(repr(i), repr(eval(repr(i))))
-
- # end before start
- with assert_raises(IntervalError):
- i = DBInterval(200, 100, 100, 200, 10000, 20000)
-
- # db_start too late
- with assert_raises(IntervalError):
- i = DBInterval(100, 200, 150, 200, 10000, 20000)
-
- # db_end too soon
- with assert_raises(IntervalError):
- i = DBInterval(100, 200, 100, 150, 10000, 20000)
-
- # actual start, end can be a subset
- a = DBInterval(150, 200, 100, 200, 10000, 20000)
- b = DBInterval(100, 150, 100, 200, 10000, 20000)
- c = DBInterval(150, 160, 100, 200, 10000, 20000)
-
- # Make a set of DBIntervals
- iseta = IntervalSet([a, b])
- isetc = IntervalSet(c)
- assert(iseta.intersects(a))
- assert(iseta.intersects(b))
-
- # Test subset
- with assert_raises(IntervalError):
- x = a.subset(150, 250)
-
- # Subset of those IntervalSets should still contain DBIntervals
- for i in IntervalSet(iseta.intersection(Interval(125,250))):
- assert(isinstance(i, DBInterval))
-
- class TestIntervalTree:
-
- def test_interval_tree(self):
- import random
- random.seed(1234)
-
- # make a set of 100 intervals
- iset = IntervalSet()
- j = 100
- for i in random.sample(range(j),j):
- interval = Interval(i, i+1)
- iset += interval
- render(iset, "Random Insertion")
-
- # remove about half of them
- for i in random.sample(range(j),j):
- if random.randint(0,1):
- iset -= Interval(i, i+1)
-
- # try removing an interval that doesn't exist
- with assert_raises(IntervalError):
- iset -= Interval(1234,5678)
- render(iset, "Random Insertion, deletion")
-
- # make a set of 100 intervals, inserted in order
- iset = IntervalSet()
- j = 100
- for i in range(j):
- interval = Interval(i, i+1)
- iset += interval
- render(iset, "In-order insertion")
-
- class TestIntervalSpeed:
- @unittest.skip("this is slow")
- def test_interval_speed(self):
- import yappi
- import time
- import random
- import math
-
- print()
- yappi.start()
- speeds = {}
- limit = 22 # was 20
- for j in [ 2**x for x in range(5,limit) ]:
- start = time.time()
- iset = IntervalSet()
- for i in random.sample(range(j),j):
- interval = Interval(i, i+1)
- iset += interval
- speed = (time.time() - start) * 1000000.0
- printf("%d: %g μs (%g μs each, O(n log n) ratio %g)\n",
- j,
- speed,
- speed/j,
- speed / (j*math.log(j))) # should be constant
- speeds[j] = speed
- yappi.stop()
- stats = yappi.get_func_stats()
- stats.sort("ttot")
- stats.print_all()
|