2012-08-08 17:58:09 -04:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2012-03-29 17:43:05 -04:00
|
|
|
import nilmdb
|
|
|
|
|
2012-12-31 15:52:28 -05:00
|
|
|
from nilmdb.utils.printf import *
|
2012-05-25 12:44:24 -04:00
|
|
|
|
2012-03-29 17:43:05 -04:00
|
|
|
from nose.tools import *
|
|
|
|
from nose.tools import assert_raises
|
|
|
|
import distutils.version
|
2012-04-24 17:00:26 -04:00
|
|
|
import itertools
|
2012-03-29 17:43:05 -04:00
|
|
|
import os
|
|
|
|
import sys
|
2012-05-25 12:44:24 -04:00
|
|
|
import random
|
|
|
|
import unittest
|
2012-03-29 17:43:05 -04:00
|
|
|
|
2013-01-05 15:00:34 -05:00
|
|
|
from testutil.helpers import *
|
2012-03-29 17:43:05 -04:00
|
|
|
|
2013-01-28 19:04:52 -05:00
|
|
|
from nilmdb.server.layout import *
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
class TestLayouts(object):
|
|
|
|
# Some nilmdb.layout tests. Not complete, just fills in missing
|
|
|
|
# coverage.
|
|
|
|
def test_layouts(self):
|
2013-03-03 13:34:09 -05:00
|
|
|
x = nilmdb.server.layout.get_named("float32_8")
|
2013-01-28 19:04:52 -05:00
|
|
|
y = nilmdb.server.layout.get_named("float32_8")
|
2012-12-21 16:45:39 -05:00
|
|
|
eq_(x.count, y.count)
|
|
|
|
eq_(x.datatype, y.datatype)
|
2013-01-28 19:04:52 -05:00
|
|
|
y = nilmdb.server.layout.get_named("float32_7")
|
2012-12-21 16:45:39 -05:00
|
|
|
ne_(x.count, y.count)
|
|
|
|
eq_(x.datatype, y.datatype)
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
def test_parsing(self):
|
2013-03-03 13:34:09 -05:00
|
|
|
self.real_t_parsing("float32_8", "uint16_6", "uint16_9")
|
2012-08-08 15:23:28 -04:00
|
|
|
self.real_t_parsing("float32_8", "uint16_6", "uint16_9")
|
|
|
|
def real_t_parsing(self, name_prep, name_raw, name_rawnotch):
|
|
|
|
# invalid layouts
|
2012-03-29 17:43:05 -04:00
|
|
|
with assert_raises(TypeError) as e:
|
|
|
|
parser = Parser("NoSuchLayout")
|
2012-08-08 15:23:28 -04:00
|
|
|
with assert_raises(TypeError) as e:
|
|
|
|
parser = Parser("float32")
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
# too little data
|
2012-08-08 15:23:28 -04:00
|
|
|
parser = Parser(name_prep)
|
2012-03-29 17:43:05 -04:00
|
|
|
data = ( "1234567890.000000 1.1 2.2 3.3 4.4 5.5\n" +
|
|
|
|
"1234567890.100000 1.1 2.2 3.3 4.4 5.5\n")
|
|
|
|
with assert_raises(ParserError) as e:
|
|
|
|
parser.parse(data)
|
2012-04-24 17:00:26 -04:00
|
|
|
in_("error", str(e.exception))
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
# too much data
|
2012-08-08 15:23:28 -04:00
|
|
|
parser = Parser(name_prep)
|
2012-03-29 17:43:05 -04:00
|
|
|
data = ( "1234567890.000000 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9\n" +
|
|
|
|
"1234567890.100000 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9\n")
|
|
|
|
with assert_raises(ParserError) as e:
|
|
|
|
parser.parse(data)
|
2012-04-24 17:00:26 -04:00
|
|
|
in_("error", str(e.exception))
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
# just right
|
2012-08-08 15:23:28 -04:00
|
|
|
parser = Parser(name_prep)
|
2012-03-29 17:43:05 -04:00
|
|
|
data = ( "1234567890.000000 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8\n" +
|
|
|
|
"1234567890.100000 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8\n")
|
|
|
|
parser.parse(data)
|
2012-04-24 17:00:26 -04:00
|
|
|
eq_(parser.min_timestamp, 1234567890.0)
|
|
|
|
eq_(parser.max_timestamp, 1234567890.1)
|
|
|
|
eq_(parser.data, [[1234567890.0,1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8],
|
|
|
|
[1234567890.1,1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8]])
|
|
|
|
|
2013-03-03 13:34:09 -05:00
|
|
|
# try uint16_6 too, with clamping
|
2012-08-08 15:23:28 -04:00
|
|
|
parser = Parser(name_raw)
|
2012-03-29 17:43:05 -04:00
|
|
|
data = ( "1234567890.000000 1 2 3 4 5 6\n" +
|
|
|
|
"1234567890.100000 1 2 3 4 5 6\n" )
|
|
|
|
parser.parse(data)
|
2012-04-24 17:00:26 -04:00
|
|
|
eq_(parser.data, [[1234567890.0,1,2,3,4,5,6],
|
|
|
|
[1234567890.1,1,2,3,4,5,6]])
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
# pass an instantiated class
|
2012-08-08 15:23:28 -04:00
|
|
|
parser = Parser(get_named(name_rawnotch))
|
2012-03-29 17:43:05 -04:00
|
|
|
data = ( "1234567890.000000 1 2 3 4 5 6 7 8 9\n" +
|
|
|
|
"1234567890.100000 1 2 3 4 5 6 7 8 9\n" )
|
|
|
|
parser.parse(data)
|
|
|
|
|
|
|
|
# non-monotonic
|
2012-08-08 15:23:28 -04:00
|
|
|
parser = Parser(name_raw)
|
2012-03-29 17:43:05 -04:00
|
|
|
data = ( "1234567890.100000 1 2 3 4 5 6\n" +
|
2013-01-22 18:35:18 -05:00
|
|
|
"1234567890.099999 1 2 3 4 5 6\n" )
|
2012-03-29 17:43:05 -04:00
|
|
|
with assert_raises(ParserError) as e:
|
|
|
|
parser.parse(data)
|
|
|
|
in_("not monotonically increasing", str(e.exception))
|
2012-03-30 17:29:22 -04:00
|
|
|
|
2013-01-22 18:35:18 -05:00
|
|
|
parser = Parser(name_raw)
|
|
|
|
data = ( "1234567890.100000 1 2 3 4 5 6\n" +
|
|
|
|
"1234567890.100000 1 2 3 4 5 6\n" )
|
|
|
|
with assert_raises(ParserError) as e:
|
|
|
|
parser.parse(data)
|
|
|
|
in_("not monotonically increasing", str(e.exception))
|
|
|
|
|
|
|
|
parser = Parser(name_raw)
|
|
|
|
data = ( "1234567890.100000 1 2 3 4 5 6\n" +
|
|
|
|
"1234567890.100001 1 2 3 4 5 6\n" )
|
|
|
|
parser.parse(data)
|
|
|
|
|
2013-03-03 13:34:09 -05:00
|
|
|
# uint16_6 with values out of bounds
|
2012-08-08 15:23:28 -04:00
|
|
|
parser = Parser(name_raw)
|
2012-03-30 17:29:22 -04:00
|
|
|
data = ( "1234567890.000000 1 2 3 4 500000 6\n" +
|
|
|
|
"1234567890.100000 1 2 3 4 5 6\n" )
|
|
|
|
with assert_raises(ParserError) as e:
|
|
|
|
parser.parse(data)
|
2012-04-24 17:00:26 -04:00
|
|
|
in_("value out of range", str(e.exception))
|
2012-03-30 17:29:22 -04:00
|
|
|
|
|
|
|
# Empty data should work but is useless
|
2012-08-08 15:23:28 -04:00
|
|
|
parser = Parser(name_raw)
|
2012-03-30 17:29:22 -04:00
|
|
|
data = ""
|
|
|
|
parser.parse(data)
|
|
|
|
assert(parser.min_timestamp is None)
|
|
|
|
assert(parser.max_timestamp is None)
|
2012-05-25 12:44:24 -04:00
|
|
|
|
|
|
|
def test_formatting(self):
|
2013-03-03 13:34:09 -05:00
|
|
|
self.real_t_formatting("float32_8", "uint16_6", "uint16_9")
|
2012-08-08 15:23:28 -04:00
|
|
|
self.real_t_formatting("float32_8", "uint16_6", "uint16_9")
|
|
|
|
def real_t_formatting(self, name_prep, name_raw, name_rawnotch):
|
2012-05-25 12:44:24 -04:00
|
|
|
# invalid layout
|
|
|
|
with assert_raises(TypeError) as e:
|
|
|
|
formatter = Formatter("NoSuchLayout")
|
|
|
|
|
|
|
|
# too little data
|
2012-08-08 15:23:28 -04:00
|
|
|
formatter = Formatter(name_prep)
|
2012-05-25 12:44:24 -04:00
|
|
|
data = [ [ 1234567890.000000, 1.1, 2.2, 3.3, 4.4, 5.5 ],
|
|
|
|
[ 1234567890.100000, 1.1, 2.2, 3.3, 4.4, 5.5 ] ]
|
|
|
|
with assert_raises(FormatterError) as e:
|
|
|
|
formatter.format(data)
|
|
|
|
in_("error", str(e.exception))
|
|
|
|
|
|
|
|
# too much data
|
2012-08-08 15:23:28 -04:00
|
|
|
formatter = Formatter(name_prep)
|
2012-05-25 12:44:24 -04:00
|
|
|
data = [ [ 1234567890.000000, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
|
|
|
|
[ 1234567890.100000, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ]
|
|
|
|
with assert_raises(FormatterError) as e:
|
|
|
|
formatter.format(data)
|
|
|
|
in_("error", str(e.exception))
|
|
|
|
|
|
|
|
# just right
|
2012-08-08 15:23:28 -04:00
|
|
|
formatter = Formatter(name_prep)
|
2012-05-25 12:44:24 -04:00
|
|
|
data = [ [ 1234567890.000000, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 ],
|
|
|
|
[ 1234567890.100000, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 ] ]
|
|
|
|
text = formatter.format(data)
|
|
|
|
eq_(text,
|
2013-03-03 21:50:29 -05:00
|
|
|
"1234567890.000000 1.100000e+00 2.200000e+00 3.300000e+00 "
|
|
|
|
"4.400000e+00 5.500000e+00 6.600000e+00 7.700000e+00 "
|
|
|
|
"8.800000e+00\n" +
|
|
|
|
"1234567890.100000 1.100000e+00 2.200000e+00 3.300000e+00 "
|
|
|
|
"4.400000e+00 5.500000e+00 6.600000e+00 7.700000e+00 "
|
|
|
|
"8.800000e+00\n")
|
2012-05-25 12:44:24 -04:00
|
|
|
|
2013-03-03 13:34:09 -05:00
|
|
|
# try uint16_6 too
|
2012-08-08 15:23:28 -04:00
|
|
|
formatter = Formatter(name_raw)
|
2012-05-25 12:44:24 -04:00
|
|
|
data = [ [ 1234567890.000000, 1, 2, 3, 4, 5, 6 ],
|
|
|
|
[ 1234567890.100000, 1, 2, 3, 4, 5, 6 ] ]
|
|
|
|
text = formatter.format(data)
|
|
|
|
eq_(text,
|
|
|
|
"1234567890.000000 1 2 3 4 5 6\n" +
|
|
|
|
"1234567890.100000 1 2 3 4 5 6\n")
|
|
|
|
|
|
|
|
# pass an instantiated class
|
2012-08-08 15:23:28 -04:00
|
|
|
formatter = Formatter(get_named(name_rawnotch))
|
2012-05-25 12:44:24 -04:00
|
|
|
data = [ [ 1234567890.000000, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
|
|
|
|
[ 1234567890.100000, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ]
|
|
|
|
text = formatter.format(data)
|
|
|
|
eq_(text,
|
|
|
|
"1234567890.000000 1 2 3 4 5 6 7 8 9\n" +
|
|
|
|
"1234567890.100000 1 2 3 4 5 6 7 8 9\n")
|
|
|
|
|
|
|
|
# Empty data should work but is useless
|
2012-08-08 15:23:28 -04:00
|
|
|
formatter = Formatter(name_raw)
|
2012-05-25 12:44:24 -04:00
|
|
|
data = []
|
|
|
|
text = formatter.format(data)
|
|
|
|
eq_(text, "")
|
|
|
|
|
|
|
|
def test_roundtrip(self):
|
2013-03-03 13:34:09 -05:00
|
|
|
self.real_t_roundtrip("float32_8", "uint16_6", "uint16_9")
|
2012-08-08 15:23:28 -04:00
|
|
|
self.real_t_roundtrip("float32_8", "uint16_6", "uint16_9")
|
|
|
|
def real_t_roundtrip(self, name_prep, name_raw, name_rawnotch):
|
2012-05-25 15:02:58 -04:00
|
|
|
# Verify that textual data passed into the Parser, and then
|
|
|
|
# back through the Formatter, then back into the Parser,
|
|
|
|
# gives identical parsed representations
|
2012-05-25 12:44:24 -04:00
|
|
|
random.seed(12345)
|
|
|
|
|
2012-05-25 15:02:58 -04:00
|
|
|
def do_roundtrip(layout, datagen):
|
|
|
|
for i in range(100):
|
|
|
|
rows = random.randint(1,100)
|
|
|
|
data = ""
|
|
|
|
ts = 1234567890
|
|
|
|
for r in range(rows):
|
|
|
|
ts += random.uniform(0,1)
|
|
|
|
row = sprintf("%f", ts) + " "
|
|
|
|
row += " ".join(datagen())
|
|
|
|
row += "\n"
|
|
|
|
data += row
|
|
|
|
parser1 = Parser(layout)
|
|
|
|
formatter = Formatter(layout)
|
|
|
|
parser2 = Parser(layout)
|
|
|
|
parser1.parse(data)
|
|
|
|
parser2.parse(formatter.format(parser1.data))
|
|
|
|
eq_(parser1.data, parser2.data)
|
|
|
|
|
|
|
|
def datagen():
|
2013-03-03 21:50:29 -05:00
|
|
|
return [ sprintf("%.6e", random.uniform(-1000,1000))
|
2012-05-25 15:02:58 -04:00
|
|
|
for x in range(8) ]
|
2012-08-08 15:23:28 -04:00
|
|
|
do_roundtrip(name_prep, datagen)
|
2012-05-25 15:02:58 -04:00
|
|
|
|
|
|
|
def datagen():
|
|
|
|
return [ sprintf("%d", random.randint(0,65535))
|
|
|
|
for x in range(6) ]
|
2012-08-08 15:23:28 -04:00
|
|
|
do_roundtrip(name_raw, datagen)
|
2012-05-25 15:02:58 -04:00
|
|
|
|
|
|
|
def datagen():
|
|
|
|
return [ sprintf("%d", random.randint(0,65535))
|
|
|
|
for x in range(9) ]
|
2012-08-08 15:23:28 -04:00
|
|
|
do_roundtrip(name_rawnotch, datagen)
|
|
|
|
|
|
|
|
class TestLayoutSpeed:
|
2012-08-09 13:53:01 -04:00
|
|
|
@unittest.skip("this is slow")
|
2012-08-08 15:23:28 -04:00
|
|
|
def test_layout_speed(self):
|
2012-08-08 17:58:09 -04:00
|
|
|
import time
|
|
|
|
|
|
|
|
random.seed(54321)
|
|
|
|
|
|
|
|
def do_speedtest(layout, datagen, rows = 5000, times = 100):
|
|
|
|
# Build data once
|
|
|
|
data = ""
|
|
|
|
ts = 1234567890
|
|
|
|
for r in range(rows):
|
|
|
|
ts += random.uniform(0,1)
|
|
|
|
row = sprintf("%f", ts) + " "
|
|
|
|
row += " ".join(datagen())
|
|
|
|
row += "\n"
|
|
|
|
data += row
|
|
|
|
|
|
|
|
# Do lots of roundtrips
|
|
|
|
start = time.time()
|
|
|
|
for i in range(times):
|
|
|
|
parser = Parser(layout)
|
|
|
|
formatter = Formatter(layout)
|
|
|
|
parser.parse(data)
|
2013-03-01 16:04:10 -05:00
|
|
|
formatter.format(parser.data)
|
2012-08-08 18:00:54 -04:00
|
|
|
elapsed = time.time() - start
|
|
|
|
printf("roundtrip %s: %d ms, %.1f μs/row, %d rows/sec\n",
|
|
|
|
layout,
|
|
|
|
elapsed * 1e3,
|
|
|
|
(elapsed * 1e6) / (rows * times),
|
|
|
|
(rows * times) / elapsed)
|
2012-08-08 17:58:09 -04:00
|
|
|
|
|
|
|
print ""
|
|
|
|
def datagen():
|
2013-03-03 21:50:29 -05:00
|
|
|
return [ sprintf("%.6e", random.uniform(-1000,1000))
|
2012-08-08 17:58:09 -04:00
|
|
|
for x in range(10) ]
|
|
|
|
do_speedtest("float32_10", datagen)
|
|
|
|
|
|
|
|
def datagen():
|
|
|
|
return [ sprintf("%d", random.randint(0,65535))
|
|
|
|
for x in range(10) ]
|
|
|
|
do_speedtest("uint16_10", datagen)
|
2013-03-01 16:04:10 -05:00
|
|
|
|
|
|
|
def datagen():
|
|
|
|
return [ sprintf("%d", random.randint(0,65535))
|
|
|
|
for x in range(6) ]
|
|
|
|
do_speedtest("uint16_6", datagen)
|