614 lines
20 KiB
Python
614 lines
20 KiB
Python
|
import datetime
|
||
|
from decimal import Decimal
|
||
|
|
||
|
from django.db import connection
|
||
|
from hypothesis import given
|
||
|
from hypothesis.extra.django import TestCase
|
||
|
from hypothesis.strategies import (
|
||
|
dates,
|
||
|
datetimes,
|
||
|
integers,
|
||
|
none,
|
||
|
one_of,
|
||
|
sampled_from,
|
||
|
tuples,
|
||
|
)
|
||
|
from psycopg2.extras import DateRange, DateTimeRange, NumericRange, Range
|
||
|
|
||
|
from .range_fields import array_subtract_all, normalise, safe_subtract
|
||
|
|
||
|
|
||
|
def valid_range(obj):
|
||
|
return obj[0] is None or obj[1] is None or obj[0] <= obj[1]
|
||
|
|
||
|
|
||
|
BOUNDS = sampled_from(["[]", "()", "[)", "(]"])
|
||
|
BIGINT = one_of(
|
||
|
integers(min_value=-9223372036854775807, max_value=+9223372036854775806), none()
|
||
|
)
|
||
|
DATES = one_of(dates(), none())
|
||
|
DATETIMES = one_of(datetimes(), none())
|
||
|
|
||
|
num_range = tuples(BIGINT, BIGINT, BOUNDS).filter(valid_range)
|
||
|
date_range = tuples(DATES, DATES, BOUNDS).filter(valid_range)
|
||
|
datetime_range = tuples(DATETIMES, DATETIMES, BOUNDS).filter(valid_range)
|
||
|
|
||
|
|
||
|
class TestRange__and__(TestCase):
|
||
|
def test_normalised(self):
|
||
|
self.assertTrue(normalise(NumericRange(0, 1, "()")).isempty)
|
||
|
self.assertFalse(
|
||
|
normalise(
|
||
|
DateRange(datetime.date(2000, 1, 9), datetime.date(2000, 1, 10), "(]")
|
||
|
).isempty
|
||
|
)
|
||
|
self.assertTrue(normalise(NumericRange(2, 2, "()")).isempty)
|
||
|
|
||
|
@given(num_range)
|
||
|
def test_normalise_hypothesis(self, a):
|
||
|
a = NumericRange(*a)
|
||
|
cursor = connection.cursor()
|
||
|
cursor.execute("SELECT %s::int8range", [a])
|
||
|
self.assertEqual(cursor.fetchone()[0], normalise(a), a)
|
||
|
|
||
|
@given(date_range)
|
||
|
def test_normalise_hypothesis_daterange(self, a):
|
||
|
a = DateRange(*a)
|
||
|
cursor = connection.cursor()
|
||
|
cursor.execute("SELECT %s::daterange", [a])
|
||
|
self.assertEqual(cursor.fetchone()[0], normalise(a), a)
|
||
|
|
||
|
@given(datetime_range)
|
||
|
def test_normalise_hypothesis_tsrange(self, a):
|
||
|
a = DateTimeRange(*a)
|
||
|
cursor = connection.cursor()
|
||
|
cursor.execute("SELECT %s::tsrange", [a])
|
||
|
self.assertEqual(cursor.fetchone()[0], normalise(a), a)
|
||
|
|
||
|
def test_can_query_db(self):
|
||
|
cursor = connection.cursor()
|
||
|
cursor.execute(
|
||
|
"SELECT %s::int8range && %s::int8range", [NumericRange(), NumericRange()]
|
||
|
)
|
||
|
self.assertTrue(cursor.fetchone()[0])
|
||
|
|
||
|
def test_may_not_compare_different_range_types(self):
|
||
|
with self.assertRaises(TypeError):
|
||
|
NumericRange() & DateRange()
|
||
|
|
||
|
def test_empty_ranges_do_not_overlap(self):
|
||
|
self.assertFalse(NumericRange(0, 0, "()") & NumericRange())
|
||
|
self.assertFalse(NumericRange(0, 1, "()") & NumericRange(None, None, "[]"))
|
||
|
|
||
|
def test_two_full_ranges_overlap(self):
|
||
|
self.assertTrue(NumericRange() & NumericRange())
|
||
|
self.assertTrue(NumericRange(None, None, "[]") & NumericRange(None, None, "[]"))
|
||
|
|
||
|
def test_full_range_overlaps_non_full_range(self):
|
||
|
self.assertTrue(NumericRange() & NumericRange(-12, 55))
|
||
|
self.assertTrue(NumericRange(-12, 55) & NumericRange())
|
||
|
|
||
|
def test_ends_touch(self):
|
||
|
self.assertFalse(NumericRange(10, 20) & NumericRange(20, 30))
|
||
|
self.assertTrue(NumericRange(10, 20, "[]") & NumericRange(20, 30))
|
||
|
self.assertFalse(NumericRange(20, 30) & NumericRange(10, 20))
|
||
|
self.assertTrue(NumericRange(20, 30) & NumericRange(10, 20, "[]"))
|
||
|
|
||
|
self.assertFalse(NumericRange(10, 20) & NumericRange(20, None))
|
||
|
self.assertTrue(NumericRange(10, 20, "[]") & NumericRange(20, None))
|
||
|
self.assertFalse(NumericRange(20, None) & NumericRange(10, 20))
|
||
|
self.assertTrue(NumericRange(20, None) & NumericRange(10, 20, "[]"))
|
||
|
|
||
|
self.assertFalse(NumericRange(None, 20) & NumericRange(20, 30))
|
||
|
self.assertTrue(NumericRange(None, 20, "[]") & NumericRange(20, 30))
|
||
|
self.assertFalse(NumericRange(20, 30) & NumericRange(None, 20))
|
||
|
self.assertTrue(NumericRange(20, 30) & NumericRange(None, 20, "[]"))
|
||
|
|
||
|
self.assertFalse(NumericRange(None, 20) & NumericRange(20, None))
|
||
|
self.assertTrue(NumericRange(None, 20, "[]") & NumericRange(20, None))
|
||
|
self.assertFalse(NumericRange(20, None) & NumericRange(None, 20))
|
||
|
self.assertTrue(NumericRange(20, None) & NumericRange(None, 20, "[]"))
|
||
|
|
||
|
self.assertFalse(NumericRange(0, 2, "()") & NumericRange(0, 0, "[]"))
|
||
|
|
||
|
def test_both_upper_None(self):
|
||
|
self.assertTrue(NumericRange(1, None), NumericRange(100, None))
|
||
|
|
||
|
@given(num_range, num_range)
|
||
|
def test_with_hypothesis(self, a, b):
|
||
|
a = NumericRange(*a)
|
||
|
b = NumericRange(*b)
|
||
|
cursor = connection.cursor()
|
||
|
cursor.execute("SELECT %s::int8range && %s::int8range", [a, b])
|
||
|
self.assertEqual(cursor.fetchone()[0], a & b, "{} && {}".format(a, b))
|
||
|
|
||
|
@given(date_range, date_range)
|
||
|
def test_with_hypothesis_dates(self, a, b):
|
||
|
a = DateRange(*a)
|
||
|
b = DateRange(*b)
|
||
|
cursor = connection.cursor()
|
||
|
cursor.execute("SELECT %s::daterange && %s::daterange", [a, b])
|
||
|
self.assertEqual(cursor.fetchone()[0], a & b, "{} && {}".format(a, b))
|
||
|
|
||
|
@given(datetime_range, datetime_range)
|
||
|
def test_with_hypothesis_datetimes(self, a, b):
|
||
|
a = DateTimeRange(*a)
|
||
|
b = DateTimeRange(*b)
|
||
|
cursor = connection.cursor()
|
||
|
cursor.execute("SELECT %s::tsrange && %s::tsrange", [a, b])
|
||
|
self.assertEqual(cursor.fetchone()[0], a & b, "{} && {}".format(a, b))
|
||
|
|
||
|
def test_with_values_found_by_hypothesis(self):
|
||
|
self.assertEqual(
|
||
|
NumericRange(None, 1, "()"), normalise(NumericRange(None, 0, "[]"))
|
||
|
)
|
||
|
self.assertFalse(NumericRange(0, None, "()") & NumericRange(None, 1, "()"))
|
||
|
|
||
|
@given(datetime_range)
|
||
|
def test_equality(self, a):
|
||
|
self.assertNotEqual(a, None)
|
||
|
self.assertNotEqual(a, 1)
|
||
|
self.assertNotEqual(a, [])
|
||
|
self.assertEqual(a, a)
|
||
|
|
||
|
def test_manual_equality(self):
|
||
|
self.assertFalse(NumericRange(0, 2, "[]") is None)
|
||
|
|
||
|
def test_timedelta_ranges(self):
|
||
|
a = Range(datetime.timedelta(0), datetime.timedelta(1))
|
||
|
b = Range(datetime.timedelta(hours=5), datetime.timedelta(hours=9))
|
||
|
|
||
|
self.assertTrue(a & b)
|
||
|
self.assertTrue(b & a)
|
||
|
self.assertTrue(b.lower in a)
|
||
|
self.assertTrue(b.upper in a)
|
||
|
self.assertFalse(a.lower in b)
|
||
|
self.assertFalse(a.upper in b)
|
||
|
|
||
|
|
||
|
class TestRangeContains(TestCase):
|
||
|
def test_out_of_bounds(self):
|
||
|
self.assertFalse(2 in Range(7, 12))
|
||
|
self.assertFalse(6 in Range(1, 4))
|
||
|
self.assertFalse(2 in Range(6, None))
|
||
|
self.assertFalse(22 in Range(None, 20))
|
||
|
|
||
|
self.assertFalse(Decimal("8.01") in Range(0, 8, "[]"))
|
||
|
self.assertFalse(Decimal(8) in Range(0, 8, "[)"))
|
||
|
|
||
|
def test_in_bounds(self):
|
||
|
self.assertTrue(4 in Range(0, 8))
|
||
|
self.assertTrue(4 in Range(None, 8))
|
||
|
self.assertTrue(4 in Range(0, None))
|
||
|
self.assertTrue(4 in Range(None, None))
|
||
|
|
||
|
def test_in_on_lower_bounds_inclusive(self):
|
||
|
self.assertTrue(2 in Range(2, 7))
|
||
|
self.assertTrue(2 in Range(2, None))
|
||
|
|
||
|
def test_out_on_lower_bounds_exclusive(self):
|
||
|
self.assertFalse(2 in Range(2, 7, "()"))
|
||
|
self.assertFalse(2 in Range(2, None, "()"))
|
||
|
self.assertFalse(2 in Range(2, 7, "(]"))
|
||
|
|
||
|
def test_in_on_upper_bounds_inclusive(self):
|
||
|
self.assertTrue(10 in Range(0, 10, "[]"))
|
||
|
self.assertTrue(10 in Range(0, 10, "(]"))
|
||
|
self.assertTrue(10 in Range(None, 10, "(]"))
|
||
|
|
||
|
def test_out_on_upper_bounds_exclusive(self):
|
||
|
self.assertFalse(10 in Range(0, 10, "[)"))
|
||
|
self.assertFalse(10 in Range(None, 10, "()"))
|
||
|
self.assertFalse(10 in Range(0, 10, "()"))
|
||
|
|
||
|
def test_no_overlap(self):
|
||
|
self.assertFalse(Range(2, 4) in Range(8, 12))
|
||
|
|
||
|
def test_partial_overlap(self):
|
||
|
self.assertFalse(Range(2, 10) in Range(8, 12))
|
||
|
|
||
|
def test_in_is_larger(self):
|
||
|
self.assertFalse(Range(2, 14) in Range(8, 12))
|
||
|
|
||
|
def test_match(self):
|
||
|
self.assertTrue(Range(2, 4) in Range(2, 4))
|
||
|
self.assertTrue(Range(2, 4, "[)") in Range(2, 4, "[]"))
|
||
|
self.assertFalse(Range(2, 4, "[]") in Range(2, 4, "[)"))
|
||
|
|
||
|
|
||
|
class TestRangeMerge(TestCase):
|
||
|
def test_contained(self):
|
||
|
self.assertEqual(Range(1, 12) + Range(2, 5), Range(1, 12))
|
||
|
self.assertEqual(Range(2, 5) + Range(1, 12), Range(1, 12))
|
||
|
self.assertEqual(Range(None, None) + Range(2, 44), Range(None, None))
|
||
|
self.assertEqual(Range(2, 44) + Range(None, None), Range(None, None))
|
||
|
self.assertEqual(Range(None, 44) + Range(None, None), Range(None, None))
|
||
|
|
||
|
def test_intersect(self):
|
||
|
self.assertEqual(Range(None, 5) + Range(2, None), Range(None, None))
|
||
|
|
||
|
def test_adjacent(self):
|
||
|
self.assertEqual(Range(2, 22, "[]") + Range(23, 44), Range(2, 44))
|
||
|
|
||
|
def test_distinct(self):
|
||
|
with self.assertRaises(ValueError):
|
||
|
Range(2, 6) + Range(8, 12)
|
||
|
|
||
|
|
||
|
class TestRangeIntersect(TestCase):
|
||
|
def test_intersects(self):
|
||
|
self.assertEqual(Range(22, 25, "[]") * Range(23, 28, "[]"), Range(23, 25, "[]"))
|
||
|
self.assertEqual(
|
||
|
Range(None, 25, "(]") * Range(23, None, "[)"), Range(23, 25, "[]")
|
||
|
)
|
||
|
|
||
|
|
||
|
class TestRangeSubtract(TestCase):
|
||
|
def test_source_within_subtract(self):
|
||
|
"""
|
||
|
[ source )
|
||
|
[ subtract )
|
||
|
[ subtract ]
|
||
|
( subtract )
|
||
|
( subtract ]
|
||
|
"""
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "[)"), Range(0, 44, "[)")))
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "[)"), Range(0, 44, "(]")))
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "[)"), Range(0, 44, "()")))
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "[)"), Range(0, 44, "[]")))
|
||
|
|
||
|
"""
|
||
|
(source)
|
||
|
[ subtract ]
|
||
|
... etc
|
||
|
"""
|
||
|
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "()"), Range(0, 44, "[)")))
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "()"), Range(0, 44, "(]")))
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "()"), Range(0, 44, "()")))
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "()"), Range(0, 44, "[]")))
|
||
|
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "[]"), Range(0, 44, "[)")))
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "[]"), Range(0, 44, "(]")))
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "[]"), Range(0, 44, "()")))
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "[]"), Range(0, 44, "[]")))
|
||
|
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "(]"), Range(0, 44, "[)")))
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "(]"), Range(0, 44, "(]")))
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "(]"), Range(0, 44, "()")))
|
||
|
self.assertEqual([], safe_subtract(Range(11, 16, "(]"), Range(0, 44, "[]")))
|
||
|
|
||
|
def test_subtract_upper_bound_matches_source_lower_bound(self):
|
||
|
"""
|
||
|
|
||
|
[ source )
|
||
|
[subtract]
|
||
|
|
||
|
"""
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "()")], safe_subtract(Range(4, 7, "[)"), Range(0, 4, "[]"))
|
||
|
)
|
||
|
|
||
|
def test_subtract_lower_bound_below_bounds_only(self):
|
||
|
"""
|
||
|
|
||
|
[source)
|
||
|
[subtract)
|
||
|
(subtract)
|
||
|
|
||
|
"""
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[)")], safe_subtract(Range(4, 7, "[)"), Range(0, 4, "[)"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[)")], safe_subtract(Range(4, 7, "[)"), Range(0, 4, "()"))
|
||
|
)
|
||
|
|
||
|
"""
|
||
|
(source)
|
||
|
(subtract]
|
||
|
"""
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "()")], safe_subtract(Range(4, 7, "()"), Range(0, 4, "[]"))
|
||
|
)
|
||
|
|
||
|
def test_subtract_lower_bound_below_completely(self):
|
||
|
"""
|
||
|
[source)
|
||
|
[subtract]
|
||
|
[subtract)
|
||
|
[subtract]
|
||
|
(subtract)
|
||
|
|
||
|
"""
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[)")], safe_subtract(Range(4, 7, "[)"), Range(0, 3, "[)"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[)")], safe_subtract(Range(4, 7, "[)"), Range(0, 3, "()"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[)")], safe_subtract(Range(4, 7, "[)"), Range(0, 3, "[]"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[)")], safe_subtract(Range(4, 7, "[)"), Range(0, 3, "(]"))
|
||
|
)
|
||
|
|
||
|
# Other source bounds types
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[]")], safe_subtract(Range(4, 7, "[]"), Range(0, 3, "[)"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[]")], safe_subtract(Range(4, 7, "[]"), Range(0, 3, "()"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[]")], safe_subtract(Range(4, 7, "[]"), Range(0, 3, "[]"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[]")], safe_subtract(Range(4, 7, "[]"), Range(0, 3, "(]"))
|
||
|
)
|
||
|
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "(]")], safe_subtract(Range(4, 7, "(]"), Range(0, 3, "[)"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "(]")], safe_subtract(Range(4, 7, "(]"), Range(0, 3, "()"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "(]")], safe_subtract(Range(4, 7, "(]"), Range(0, 3, "[]"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "(]")], safe_subtract(Range(4, 7, "(]"), Range(0, 3, "(]"))
|
||
|
)
|
||
|
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "()")], safe_subtract(Range(4, 7, "()"), Range(0, 3, "[)"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "()")], safe_subtract(Range(4, 7, "()"), Range(0, 3, "()"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "()")], safe_subtract(Range(4, 7, "()"), Range(0, 3, "[]"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "()")], safe_subtract(Range(4, 7, "()"), Range(0, 3, "(]"))
|
||
|
)
|
||
|
|
||
|
def test_upper_bound_above_bounds_only(self):
|
||
|
"""
|
||
|
|
||
|
[source)
|
||
|
[subtract]
|
||
|
|
||
|
[source]
|
||
|
(subtract)
|
||
|
"""
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[)")], safe_subtract(Range(4, 7, "[)"), Range(7, 10, "[)"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[]")], safe_subtract(Range(4, 7, "[]"), Range(7, 10, "()"))
|
||
|
)
|
||
|
|
||
|
"""
|
||
|
[source]
|
||
|
[subtract]
|
||
|
"""
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[)")], safe_subtract(Range(4, 7, "[]"), Range(7, 12, "[]"))
|
||
|
)
|
||
|
|
||
|
def test_upper_bound_above_completely(self):
|
||
|
"""
|
||
|
|
||
|
[source)
|
||
|
[subtract]
|
||
|
(subtract)
|
||
|
[subtract)
|
||
|
(subtract]
|
||
|
|
||
|
"""
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[)")], safe_subtract(Range(4, 7, "[)"), Range(10, 14, "[]"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[)")], safe_subtract(Range(4, 7, "[)"), Range(10, 14, "()"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[)")], safe_subtract(Range(4, 7, "[)"), Range(10, 14, "[)"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "[)")], safe_subtract(Range(4, 7, "[)"), Range(10, 14, "(]"))
|
||
|
)
|
||
|
|
||
|
def test_intersects_lower_bounds(self):
|
||
|
"""
|
||
|
|
||
|
[source)
|
||
|
[subtract]
|
||
|
[subtract]
|
||
|
[subtract)
|
||
|
(subtract)
|
||
|
|
||
|
"""
|
||
|
self.assertEqual(
|
||
|
[Range(4, 7, "()")], safe_subtract(Range(4, 7, "[)"), Range(0, 4, "[]"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(5, 7, "()")], safe_subtract(Range(4, 7, "[)"), Range(0, 5, "[]"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(5, 7, "[)")], safe_subtract(Range(4, 7, "[)"), Range(0, 5, "[)"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(5, 7, "()")], safe_subtract(Range(4, 7, "[)"), Range(0, 5, "[]"))
|
||
|
)
|
||
|
|
||
|
def test_lower_bounds_same(self):
|
||
|
"""
|
||
|
|
||
|
[source )
|
||
|
|
||
|
[subtract]
|
||
|
[subtract)
|
||
|
(subtract)
|
||
|
(subtract]
|
||
|
( subtract )
|
||
|
( subtract ]
|
||
|
"""
|
||
|
|
||
|
self.assertEqual(
|
||
|
[Range(6, 8, "()")], safe_subtract(Range(4, 8), Range(4, 6, "[]"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(6, 8, "[)")], safe_subtract(Range(4, 8), Range(4, 6, "[)"))
|
||
|
)
|
||
|
|
||
|
self.assertEqual(
|
||
|
[Range(4, 4, "[]"), Range(6, 8, "[)")],
|
||
|
safe_subtract(Range(4, 8), Range(4, 6, "()")),
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 4, "[]"), Range(6, 8, "()")],
|
||
|
safe_subtract(Range(4, 8), Range(4, 6, "(]")),
|
||
|
)
|
||
|
|
||
|
def test_lower_bound_inclusive_difference_only(self):
|
||
|
"""
|
||
|
[source )
|
||
|
(subtract )
|
||
|
(subtract ]
|
||
|
"""
|
||
|
self.assertEqual(
|
||
|
[Range(4, 4, "[]")], safe_subtract(Range(4, 8, "[)"), Range(4, 8, "(]"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 4, "[]")], safe_subtract(Range(4, 8, "[)"), Range(4, 8, "()"))
|
||
|
)
|
||
|
|
||
|
def test_intersects_upper_bound(self):
|
||
|
"""
|
||
|
[source)
|
||
|
|
||
|
[subtract]
|
||
|
(subtract)
|
||
|
"""
|
||
|
self.assertEqual(
|
||
|
[Range(4, 6, "[)")], safe_subtract(Range(4, 8), Range(6, 12, "[]"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 6, "[]")], safe_subtract(Range(4, 8), Range(6, 12, "()"))
|
||
|
)
|
||
|
|
||
|
def test_exact_match(self):
|
||
|
"""
|
||
|
|
||
|
[ source )
|
||
|
[ subtract )
|
||
|
|
||
|
|
||
|
( source )
|
||
|
( subtract )
|
||
|
|
||
|
[ source ]
|
||
|
[ subtract ]
|
||
|
|
||
|
( source ]
|
||
|
( subtract ]
|
||
|
|
||
|
"""
|
||
|
self.assertEqual([], safe_subtract(Range(4, 8, "[)"), Range(4, 8, "[)")))
|
||
|
self.assertEqual([], safe_subtract(Range(4, 8, "[]"), Range(4, 8, "[]")))
|
||
|
self.assertEqual([], safe_subtract(Range(4, 8, "()"), Range(4, 8, "()")))
|
||
|
self.assertEqual([], safe_subtract(Range(4, 8, "(]"), Range(4, 8, "(]")))
|
||
|
|
||
|
def test_upper_bounds_match(self):
|
||
|
"""
|
||
|
|
||
|
[ source )
|
||
|
[subtract)
|
||
|
(subtract)
|
||
|
"""
|
||
|
|
||
|
self.assertEqual(
|
||
|
[Range(4, 5, "[)")], safe_subtract(Range(4, 8, "[)"), Range(5, 8, "[)"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 5, "[]")], safe_subtract(Range(4, 8, "[)"), Range(5, 8, "()"))
|
||
|
)
|
||
|
|
||
|
"""
|
||
|
[ source )
|
||
|
[subtract]
|
||
|
(subtract]
|
||
|
"""
|
||
|
|
||
|
self.assertEqual(
|
||
|
[Range(4, 5, "[)")], safe_subtract(Range(4, 8, "[)"), Range(5, 8, "[]"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 5, "[]")], safe_subtract(Range(4, 8, "[)"), Range(5, 8, "(]"))
|
||
|
)
|
||
|
|
||
|
def test_subtract_within(self):
|
||
|
"""
|
||
|
[ source )
|
||
|
[subtract]
|
||
|
(subtract)
|
||
|
[subtract)
|
||
|
(subtract]
|
||
|
"""
|
||
|
|
||
|
self.assertEqual(
|
||
|
[Range(4, 5, "[)"), Range(7, 8, "()")],
|
||
|
safe_subtract(Range(4, 8, "[)"), Range(5, 7, "[]")),
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 5, "[]"), Range(7, 8, "[)")],
|
||
|
safe_subtract(Range(4, 8, "[)"), Range(5, 7, "()")),
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 5, "[)"), Range(7, 8, "[)")],
|
||
|
safe_subtract(Range(4, 8, "[)"), Range(5, 7, "[)")),
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 5, "[]"), Range(7, 8, "()")],
|
||
|
safe_subtract(Range(4, 8, "[)"), Range(5, 7, "(]")),
|
||
|
)
|
||
|
|
||
|
def test_bounds_only_differ(self):
|
||
|
"""
|
||
|
[ source ]
|
||
|
(subtract)
|
||
|
[subtract)
|
||
|
(subtract]
|
||
|
"""
|
||
|
self.assertEqual(
|
||
|
[Range(4, 4, "[]"), Range(8, 8, "[]")],
|
||
|
safe_subtract(Range(4, 8, "[]"), Range(4, 8, "()")),
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(8, 8, "[]")], safe_subtract(Range(4, 8, "[]"), Range(4, 8, "[)"))
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
[Range(4, 4, "[]")], safe_subtract(Range(4, 8, "[]"), Range(4, 8, "(]"))
|
||
|
)
|
||
|
|
||
|
"""
|
||
|
( source )
|
||
|
[subtract]
|
||
|
[subtract)
|
||
|
(subtract]
|
||
|
"""
|
||
|
self.assertEqual([], safe_subtract(Range(4, 8, "()"), Range(4, 8, "[]")))
|
||
|
self.assertEqual([], safe_subtract(Range(4, 8, "()"), Range(4, 8, "(]")))
|
||
|
self.assertEqual([], safe_subtract(Range(4, 8, "()"), Range(4, 8, "[)")))
|
||
|
|
||
|
def test_subtract_ranges(self):
|
||
|
self.assertEqual(
|
||
|
[Range(2, 6), Range(8, 12)],
|
||
|
array_subtract_all(
|
||
|
[Range(0, 6), Range(7, 18)], [Range(0, 2), Range(6, 8), Range(12, None)]
|
||
|
),
|
||
|
)
|