Previously, we allowed start == end, but this doesn't make sense with half-open intervals.tags/nilmdb-1.1^2
@@ -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""" | |||
@@ -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, | |||
@@ -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) | |||
@@ -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) | |||
@@ -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: | |||
@@ -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 | |||
@@ -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]) | |||
@@ -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): | |||