You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

255 lines
9.0 KiB

  1. # -*- coding: utf-8 -*-
  2. import nilmdb
  3. from nilmdb.utils.printf import *
  4. from nose.tools import *
  5. from nose.tools import assert_raises
  6. import distutils.version
  7. import itertools
  8. import os
  9. import shutil
  10. import sys
  11. import cherrypy
  12. import threading
  13. import urllib2
  14. from urllib2 import urlopen, HTTPError
  15. import Queue
  16. import cStringIO
  17. import random
  18. import unittest
  19. from testutil.helpers import *
  20. from nilmdb.layout import *
  21. class TestLayouts(object):
  22. # Some nilmdb.layout tests. Not complete, just fills in missing
  23. # coverage.
  24. def test_layouts(self):
  25. x = nilmdb.layout.get_named("PrepData")
  26. y = nilmdb.layout.get_named("float32_8")
  27. eq_(x.count, y.count)
  28. eq_(x.datatype, y.datatype)
  29. y = nilmdb.layout.get_named("float32_7")
  30. ne_(x.count, y.count)
  31. eq_(x.datatype, y.datatype)
  32. def test_parsing(self):
  33. self.real_t_parsing("PrepData", "RawData", "RawNotchedData")
  34. self.real_t_parsing("float32_8", "uint16_6", "uint16_9")
  35. def real_t_parsing(self, name_prep, name_raw, name_rawnotch):
  36. # invalid layouts
  37. with assert_raises(TypeError) as e:
  38. parser = Parser("NoSuchLayout")
  39. with assert_raises(TypeError) as e:
  40. parser = Parser("float32")
  41. # too little data
  42. parser = Parser(name_prep)
  43. data = ( "1234567890.000000 1.1 2.2 3.3 4.4 5.5\n" +
  44. "1234567890.100000 1.1 2.2 3.3 4.4 5.5\n")
  45. with assert_raises(ParserError) as e:
  46. parser.parse(data)
  47. in_("error", str(e.exception))
  48. # too much data
  49. parser = Parser(name_prep)
  50. data = ( "1234567890.000000 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9\n" +
  51. "1234567890.100000 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9\n")
  52. with assert_raises(ParserError) as e:
  53. parser.parse(data)
  54. in_("error", str(e.exception))
  55. # just right
  56. parser = Parser(name_prep)
  57. data = ( "1234567890.000000 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8\n" +
  58. "1234567890.100000 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8\n")
  59. parser.parse(data)
  60. eq_(parser.min_timestamp, 1234567890.0)
  61. eq_(parser.max_timestamp, 1234567890.1)
  62. eq_(parser.data, [[1234567890.0,1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8],
  63. [1234567890.1,1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8]])
  64. # try RawData too, with clamping
  65. parser = Parser(name_raw)
  66. data = ( "1234567890.000000 1 2 3 4 5 6\n" +
  67. "1234567890.100000 1 2 3 4 5 6\n" )
  68. parser.parse(data)
  69. eq_(parser.data, [[1234567890.0,1,2,3,4,5,6],
  70. [1234567890.1,1,2,3,4,5,6]])
  71. # pass an instantiated class
  72. parser = Parser(get_named(name_rawnotch))
  73. data = ( "1234567890.000000 1 2 3 4 5 6 7 8 9\n" +
  74. "1234567890.100000 1 2 3 4 5 6 7 8 9\n" )
  75. parser.parse(data)
  76. # non-monotonic
  77. parser = Parser(name_raw)
  78. data = ( "1234567890.100000 1 2 3 4 5 6\n" +
  79. "1234567890.000000 1 2 3 4 5 6\n" )
  80. with assert_raises(ParserError) as e:
  81. parser.parse(data)
  82. in_("not monotonically increasing", str(e.exception))
  83. # RawData with values out of bounds
  84. parser = Parser(name_raw)
  85. data = ( "1234567890.000000 1 2 3 4 500000 6\n" +
  86. "1234567890.100000 1 2 3 4 5 6\n" )
  87. with assert_raises(ParserError) as e:
  88. parser.parse(data)
  89. in_("value out of range", str(e.exception))
  90. # Empty data should work but is useless
  91. parser = Parser(name_raw)
  92. data = ""
  93. parser.parse(data)
  94. assert(parser.min_timestamp is None)
  95. assert(parser.max_timestamp is None)
  96. def test_formatting(self):
  97. self.real_t_formatting("PrepData", "RawData", "RawNotchedData")
  98. self.real_t_formatting("float32_8", "uint16_6", "uint16_9")
  99. def real_t_formatting(self, name_prep, name_raw, name_rawnotch):
  100. # invalid layout
  101. with assert_raises(TypeError) as e:
  102. formatter = Formatter("NoSuchLayout")
  103. # too little data
  104. formatter = Formatter(name_prep)
  105. data = [ [ 1234567890.000000, 1.1, 2.2, 3.3, 4.4, 5.5 ],
  106. [ 1234567890.100000, 1.1, 2.2, 3.3, 4.4, 5.5 ] ]
  107. with assert_raises(FormatterError) as e:
  108. formatter.format(data)
  109. in_("error", str(e.exception))
  110. # too much data
  111. formatter = Formatter(name_prep)
  112. data = [ [ 1234567890.000000, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
  113. [ 1234567890.100000, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ]
  114. with assert_raises(FormatterError) as e:
  115. formatter.format(data)
  116. in_("error", str(e.exception))
  117. # just right
  118. formatter = Formatter(name_prep)
  119. data = [ [ 1234567890.000000, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 ],
  120. [ 1234567890.100000, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 ] ]
  121. text = formatter.format(data)
  122. eq_(text,
  123. "1234567890.000000 1.100000 2.200000 3.300000 4.400000 " +
  124. "5.500000 6.600000 7.700000 8.800000\n" +
  125. "1234567890.100000 1.100000 2.200000 3.300000 4.400000 " +
  126. "5.500000 6.600000 7.700000 8.800000\n")
  127. # try RawData too
  128. formatter = Formatter(name_raw)
  129. data = [ [ 1234567890.000000, 1, 2, 3, 4, 5, 6 ],
  130. [ 1234567890.100000, 1, 2, 3, 4, 5, 6 ] ]
  131. text = formatter.format(data)
  132. eq_(text,
  133. "1234567890.000000 1 2 3 4 5 6\n" +
  134. "1234567890.100000 1 2 3 4 5 6\n")
  135. # pass an instantiated class
  136. formatter = Formatter(get_named(name_rawnotch))
  137. data = [ [ 1234567890.000000, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
  138. [ 1234567890.100000, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ]
  139. text = formatter.format(data)
  140. eq_(text,
  141. "1234567890.000000 1 2 3 4 5 6 7 8 9\n" +
  142. "1234567890.100000 1 2 3 4 5 6 7 8 9\n")
  143. # Empty data should work but is useless
  144. formatter = Formatter(name_raw)
  145. data = []
  146. text = formatter.format(data)
  147. eq_(text, "")
  148. def test_roundtrip(self):
  149. self.real_t_roundtrip("PrepData", "RawData", "RawNotchedData")
  150. self.real_t_roundtrip("float32_8", "uint16_6", "uint16_9")
  151. def real_t_roundtrip(self, name_prep, name_raw, name_rawnotch):
  152. # Verify that textual data passed into the Parser, and then
  153. # back through the Formatter, then back into the Parser,
  154. # gives identical parsed representations
  155. random.seed(12345)
  156. def do_roundtrip(layout, datagen):
  157. for i in range(100):
  158. rows = random.randint(1,100)
  159. data = ""
  160. ts = 1234567890
  161. for r in range(rows):
  162. ts += random.uniform(0,1)
  163. row = sprintf("%f", ts) + " "
  164. row += " ".join(datagen())
  165. row += "\n"
  166. data += row
  167. parser1 = Parser(layout)
  168. formatter = Formatter(layout)
  169. parser2 = Parser(layout)
  170. parser1.parse(data)
  171. parser2.parse(formatter.format(parser1.data))
  172. eq_(parser1.data, parser2.data)
  173. def datagen():
  174. return [ sprintf("%f", random.uniform(-1000,1000))
  175. for x in range(8) ]
  176. do_roundtrip(name_prep, datagen)
  177. def datagen():
  178. return [ sprintf("%d", random.randint(0,65535))
  179. for x in range(6) ]
  180. do_roundtrip(name_raw, datagen)
  181. def datagen():
  182. return [ sprintf("%d", random.randint(0,65535))
  183. for x in range(9) ]
  184. do_roundtrip(name_rawnotch, datagen)
  185. class TestLayoutSpeed:
  186. @unittest.skip("this is slow")
  187. def test_layout_speed(self):
  188. import time
  189. random.seed(54321)
  190. def do_speedtest(layout, datagen, rows = 5000, times = 100):
  191. # Build data once
  192. data = ""
  193. ts = 1234567890
  194. for r in range(rows):
  195. ts += random.uniform(0,1)
  196. row = sprintf("%f", ts) + " "
  197. row += " ".join(datagen())
  198. row += "\n"
  199. data += row
  200. # Do lots of roundtrips
  201. start = time.time()
  202. for i in range(times):
  203. parser = Parser(layout)
  204. formatter = Formatter(layout)
  205. parser.parse(data)
  206. data = formatter.format(parser.data)
  207. elapsed = time.time() - start
  208. printf("roundtrip %s: %d ms, %.1f μs/row, %d rows/sec\n",
  209. layout,
  210. elapsed * 1e3,
  211. (elapsed * 1e6) / (rows * times),
  212. (rows * times) / elapsed)
  213. print ""
  214. def datagen():
  215. return [ sprintf("%f", random.uniform(-1000,1000))
  216. for x in range(10) ]
  217. do_speedtest("float32_10", datagen)
  218. def datagen():
  219. return [ sprintf("%d", random.randint(0,65535))
  220. for x in range(10) ]
  221. do_speedtest("uint16_10", datagen)