Browse Source

Strictly enforce (start < end) for all intervals.

Previously, we allowed start == end, but this doesn't make sense with
half-open intervals.
tags/nilmdb-1.1^2
Jim Paris 9 years ago
parent
commit
a49c655816
8 changed files with 60 additions and 56 deletions
  1. +2
    -2
      nilmdb/cmdline/list.py
  2. +1
    -7
      nilmdb/cmdline/remove.py
  3. +1
    -1
      nilmdb/server/interval.pyx
  4. +11
    -8
      nilmdb/server/server.py
  5. +5
    -5
      tests/test_client.py
  6. +29
    -29
      tests/test_cmdline.py
  7. +4
    -4
      tests/test_interval.py
  8. +7
    -0
      tests/test_nilmdb.py

+ 2
- 2
nilmdb/cmdline/list.py View File

@@ -47,8 +47,8 @@ def cmd_list_verify(self):
self.args.path = self.args.path_positional

if self.args.start is not None and self.args.end is not None:
if self.args.start > self.args.end:
self.parser.error("start is after end")
if self.args.start >= self.args.end:
self.parser.error("start must precede end")

def cmd_list(self):
"""List available streams"""


+ 1
- 7
nilmdb/cmdline/remove.py View File

@@ -8,8 +8,7 @@ def setup(self, sub):
Remove all data from a specified time range within a
stream.
""")
cmd.set_defaults(verify = cmd_remove_verify,
handler = cmd_remove)
cmd.set_defaults(handler = cmd_remove)

group = cmd.add_argument_group("Data selection")
group.add_argument("path",
@@ -25,11 +24,6 @@ def setup(self, sub):
group.add_argument("-c", "--count", action="store_true",
help="Output number of data points removed")

def cmd_remove_verify(self):
if self.args.start is not None and self.args.end is not None:
if self.args.start > self.args.end:
self.parser.error("start is after end")

def cmd_remove(self):
try:
count = self.client.stream_remove(self.args.path,


+ 1
- 1
nilmdb/server/interval.pyx View File

@@ -36,7 +36,7 @@ cdef class Interval:
"""
'start' and 'end' are arbitrary floats that represent time
"""
if start > end:
if start >= end:
# Explicitly disallow zero-width intervals (since they're half-open)
raise IntervalError("start %s must precede end %s" % (start, end))
self.start = float(start)


+ 11
- 8
nilmdb/server/server.py View File

@@ -229,11 +229,14 @@ class Stream(NilmApp):
# Check limits
start = float(start)
end = float(end)
if parser.min_timestamp < start:
if start >= end:
raise cherrypy.HTTPError("400 Bad Request",
"start must precede end")
if parser.min_timestamp is not None and parser.min_timestamp < start:
raise cherrypy.HTTPError("400 Bad Request", "Data timestamp " +
repr(parser.min_timestamp) +
" < start time " + repr(start))
if parser.max_timestamp >= end:
if parser.max_timestamp is not None and parser.max_timestamp >= end:
raise cherrypy.HTTPError("400 Bad Request", "Data timestamp " +
repr(parser.max_timestamp) +
" >= end time " + repr(end))
@@ -263,9 +266,9 @@ class Stream(NilmApp):
if end is not None:
end = float(end)
if start is not None and end is not None:
if end < start:
if start >= end:
raise cherrypy.HTTPError("400 Bad Request",
"end before start")
"start must precede end")
return self.db.stream_remove(path, start, end)

# /stream/intervals?path=/newton/prep
@@ -290,9 +293,9 @@ class Stream(NilmApp):
end = float(end)

if start is not None and end is not None:
if end < start:
if start >= end:
raise cherrypy.HTTPError("400 Bad Request",
"end before start")
"start must precede end")

streams = self.db.stream_list(path = path)
if len(streams) != 1:
@@ -330,9 +333,9 @@ class Stream(NilmApp):

# Check parameters
if start is not None and end is not None:
if end < start:
if start >= end:
raise cherrypy.HTTPError("400 Bad Request",
"end before start")
"start must precede end")

# Check path and get layout
streams = self.db.stream_list(path = path)


+ 5
- 5
tests/test_client.py View File

@@ -194,7 +194,7 @@ class TestClient(object):
client.http.put("stream/insert", "", { "path": "/newton/prep",
"start": 0, "end": 0 })
in_("400 Bad Request", str(e.exception))
in_("no data provided", str(e.exception))
in_("start must precede end", str(e.exception))

# Specify start/end (starts too late)
data = timestamper.TimestamperRate(testfile, start, 120)
@@ -239,7 +239,7 @@ class TestClient(object):
# Misc tests for extract and remove. Most of them are in test_cmdline.
client = nilmdb.Client(url = testurl)

for x in client.stream_extract("/newton/prep", 123, 123):
for x in client.stream_extract("/newton/prep", 999123, 999124):
raise AssertionError("shouldn't be any data for this request")

with assert_raises(ClientError) as e:
@@ -270,7 +270,7 @@ class TestClient(object):
start.totimestamp(),
end.totimestamp()).next()
in_("400 Bad Request", str(e.exception))
in_("end before start", str(e.exception))
in_("start must precede end", str(e.exception))

# Trigger a curl error in generator
with assert_raises(ServerError) as e:
@@ -296,7 +296,7 @@ class TestClient(object):
{ "path": "/newton/prep",
"start": 0, "end": 0 }).next()
in_("400 Bad Request", str(e.exception))
in_("no data provided", str(e.exception))
in_("start must precede end", str(e.exception))

# Check 404 for missing streams
for function in [ client.stream_intervals, client.stream_extract ]:
@@ -333,7 +333,7 @@ class TestClient(object):
x = http.get("stream/extract",
{ "path": "/newton/prep",
"start": "123",
"end": "123" }, retjson=False)
"end": "124" }, retjson=False)
if "Transfer-Encoding: chunked" not in http._headers:
warnings.warn("Non-chunked HTTP response for /stream/extract")
if "Content-Type: text/plain;charset=utf-8" not in http._headers:


+ 29
- 29
tests/test_cmdline.py View File

@@ -274,7 +274,7 @@ class TestCmdline(object):

# reversed range
self.fail("list /newton/prep --start 2020-01-01 --end 2000-01-01")
self.contain("start is after end")
self.contain("start must precede end")

def test_04_metadata(self):
# Set / get metadata
@@ -444,7 +444,7 @@ class TestCmdline(object):
self.contain("no intervals")

self.ok("list --detail --path *prep --start='23 Mar 2012 10:05:15.50'"
+ " --end='23 Mar 2012 10:05:15.50'")
+ " --end='23 Mar 2012 10:05:15.51'")
lines_(self.captured, 2)
self.contain("10:05:15.500")

@@ -473,29 +473,29 @@ class TestCmdline(object):

# empty ranges return error 2
self.fail("extract -a /newton/prep " +
"--start '23 Mar 2012 10:00:30' " +
"--end '23 Mar 2012 10:00:30'",
"--start '23 Mar 2012 20:00:30' " +
"--end '23 Mar 2012 20:00:31'",
exitcode = 2, require_error = False)
self.contain("no data")
self.fail("extract -a /newton/prep " +
"--start '23 Mar 2012 10:00:30.000001' " +
"--end '23 Mar 2012 10:00:30.000001'",
"--start '23 Mar 2012 20:00:30.000001' " +
"--end '23 Mar 2012 20:00:30.000002'",
exitcode = 2, require_error = False)
self.contain("no data")
self.fail("extract -a /newton/prep " +
"--start '23 Mar 2022 10:00:30' " +
"--end '23 Mar 2022 10:00:30'",
"--end '23 Mar 2022 10:00:31'",
exitcode = 2, require_error = False)
self.contain("no data")

# but are ok if we're just counting results
self.ok("extract --count /newton/prep " +
"--start '23 Mar 2012 10:00:30' " +
"--end '23 Mar 2012 10:00:30'")
"--start '23 Mar 2012 20:00:30' " +
"--end '23 Mar 2012 20:00:31'")
self.match("0\n")
self.ok("extract -c /newton/prep " +
"--start '23 Mar 2012 10:00:30.000001' " +
"--end '23 Mar 2012 10:00:30.000001'")
"--start '23 Mar 2012 20:00:30.000001' " +
"--end '23 Mar 2012 20:00:30.000002'")
self.match("0\n")

# Check various dumps against stored copies of how they should appear
@@ -542,31 +542,31 @@ class TestCmdline(object):
self.fail("remove /no/such/foo --start 2000-01-01 --end 2020-01-01")
self.contain("No stream at path")

# empty or backward ranges return errors
self.fail("remove /newton/prep --start 2020-01-01 --end 2000-01-01")
self.contain("start is after end")
self.contain("start must precede end")

# empty ranges return success, backwards ranges return error
self.ok("remove /newton/prep " +
"--start '23 Mar 2012 10:00:30' " +
"--end '23 Mar 2012 10:00:30'")
self.match("")
self.ok("remove /newton/prep " +
"--start '23 Mar 2012 10:00:30.000001' " +
"--end '23 Mar 2012 10:00:30.000001'")
self.match("")
self.ok("remove /newton/prep " +
"--start '23 Mar 2022 10:00:30' " +
"--end '23 Mar 2022 10:00:30'")
self.match("")
self.fail("remove /newton/prep " +
"--start '23 Mar 2012 10:00:30' " +
"--end '23 Mar 2012 10:00:30'")
self.contain("start must precede end")
self.fail("remove /newton/prep " +
"--start '23 Mar 2012 10:00:30.000001' " +
"--end '23 Mar 2012 10:00:30.000001'")
self.contain("start must precede end")
self.fail("remove /newton/prep " +
"--start '23 Mar 2022 10:00:30' " +
"--end '23 Mar 2022 10:00:30'")
self.contain("start must precede end")

# Verbose
self.ok("remove -c /newton/prep " +
"--start '23 Mar 2012 10:00:30' " +
"--end '23 Mar 2012 10:00:30'")
"--start '23 Mar 2022 20:00:30' " +
"--end '23 Mar 2022 20:00:31'")
self.match("0\n")
self.ok("remove --count /newton/prep " +
"--start '23 Mar 2012 10:00:30' " +
"--end '23 Mar 2012 10:00:30'")
"--start '23 Mar 2022 20:00:30' " +
"--end '23 Mar 2022 20:00:31'")
self.match("0\n")

# Make sure we have the data we expect


+ 4
- 4
tests/test_interval.py View File

@@ -55,7 +55,7 @@ class TestInterval:
for x in [ "03/24/2012", "03/25/2012", "03/26/2012" ] ]

# basic construction
i = Interval(d1, d1)
i = Interval(d1, d2)
i = Interval(d1, d3)
eq_(i.start, d1)
eq_(i.end, d3)
@@ -77,8 +77,8 @@ class TestInterval:
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) > Interval(d1, d3))
assert(Interval(d3, d3) == Interval(d3, d3))
assert(Interval(d2, d2+0.01) > Interval(d1, d3))
assert(Interval(d3, d3+0.01) == Interval(d3, d3+0.01))
#with assert_raises(TypeError): # was AttributeError, that's wrong
# x = (i == 123)

@@ -293,7 +293,7 @@ class TestIntervalDB:
# 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, 150, 100, 200, 10000, 20000)
c = DBInterval(150, 160, 100, 200, 10000, 20000)

# Make a set of DBIntervals
iseta = IntervalSet([a, b])


+ 7
- 0
tests/test_nilmdb.py View File

@@ -93,6 +93,13 @@ class Test00Nilmdb(object): # named 00 so it runs first
eq_(db.stream_get_metadata("/newton/prep"), meta1)
eq_(db.stream_get_metadata("/newton/raw"), meta1)

# fill in some test coverage for start >= end
with assert_raises(nilmdb.server.NilmDBError):
db.stream_remove("/newton/prep", 0, 0)
with assert_raises(nilmdb.server.NilmDBError):
db.stream_remove("/newton/prep", 1, 0)
db.stream_remove("/newton/prep", 0, 1)

db.close()

class TestBlockingServer(object):


Loading…
Cancel
Save