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.
 
 
 

369 lines
12 KiB

  1. import nilmdb
  2. from nilmdb.printf import *
  3. import nilmdb.cmdline
  4. from nose.tools import *
  5. from nose.tools import assert_raises
  6. import itertools
  7. import datetime_tz
  8. import os
  9. import shutil
  10. import sys
  11. import threading
  12. import urllib2
  13. from urllib2 import urlopen, HTTPError
  14. import Queue
  15. import cStringIO
  16. import shlex
  17. from test_helpers import *
  18. testdb = "tests/cmdline-testdb"
  19. def server_start(response_size = None):
  20. global test_server, test_db
  21. # Start web app on a custom port
  22. test_db = nilmdb.NilmDB(testdb, sync = False, response_size = response_size)
  23. test_server = nilmdb.Server(test_db, host = "127.0.0.1",
  24. port = 12380, stoppable = False,
  25. fast_shutdown = True,
  26. force_traceback = False)
  27. test_server.start(blocking = False)
  28. def server_stop():
  29. global test_server, test_db
  30. # Close web app
  31. test_server.stop()
  32. test_db.close()
  33. def setup_module():
  34. global test_server, test_db
  35. # Clear out DB
  36. recursive_unlink(testdb)
  37. server_start()
  38. def teardown_module():
  39. server_stop()
  40. class TestCmdline(object):
  41. def run(self, arg_string, infile=None, outfile=None):
  42. """Run a cmdline client with the specified argument string,
  43. passing the given input. Returns a tuple with the output and
  44. exit code"""
  45. class stdio_wrapper:
  46. def __init__(self, stdin, stdout, stderr):
  47. self.io = (stdin, stdout, stderr)
  48. def __enter__(self):
  49. self.saved = ( sys.stdin, sys.stdout, sys.stderr )
  50. ( sys.stdin, sys.stdout, sys.stderr ) = self.io
  51. def __exit__(self, type, value, traceback):
  52. ( sys.stdin, sys.stdout, sys.stderr ) = self.saved
  53. # Empty input if none provided
  54. if infile is None:
  55. infile = cStringIO.StringIO("")
  56. # Capture stderr
  57. errfile = cStringIO.StringIO()
  58. if outfile is None:
  59. # If no output file, capture stdout with stderr
  60. outfile = errfile
  61. with stdio_wrapper(infile, outfile, errfile) as s:
  62. try:
  63. nilmdb.cmdline.Cmdline(shlex.split(arg_string)).run()
  64. sys.exit(0)
  65. except SystemExit as e:
  66. exitcode = e.code
  67. captured = outfile.getvalue()
  68. self.captured = captured
  69. self.exitcode = exitcode
  70. def ok(self, arg_string, infile = None):
  71. self.run(arg_string, infile)
  72. if self.exitcode != 0:
  73. self.dump()
  74. eq_(self.exitcode, 0)
  75. def fail(self, arg_string, infile = None):
  76. self.run(arg_string, infile)
  77. if self.exitcode == 0:
  78. self.dump()
  79. ne_(self.exitcode, 0)
  80. def contain(self, checkstring):
  81. in_(checkstring, self.captured)
  82. def match(self, checkstring):
  83. eq_(checkstring, self.captured)
  84. def dump(self):
  85. printf("-----dump start-----\n%s-----dump end-----\n", self.captured)
  86. def test_cmdline_1_basic(self):
  87. # help
  88. self.ok("--help")
  89. self.contain("usage:")
  90. # fail for no args
  91. self.fail("")
  92. # fail for no such option
  93. self.fail("--nosuchoption")
  94. # fail for bad command
  95. self.fail("badcommand")
  96. # try some URL constructions
  97. self.fail("--url http://nosuchurl/ info")
  98. self.contain("Couldn't resolve host 'nosuchurl'")
  99. self.fail("--url nosuchurl info")
  100. self.contain("Couldn't resolve host 'nosuchurl'")
  101. self.fail("-u nosuchurl/foo info")
  102. self.contain("Couldn't resolve host 'nosuchurl'")
  103. self.fail("-u localhost:0 info")
  104. self.contain("couldn't connect to host")
  105. self.ok("-u localhost:12380 info")
  106. self.ok("info")
  107. def test_cmdline_2_info(self):
  108. self.ok("info")
  109. self.contain("Server URL: http://localhost:12380/")
  110. self.contain("Server version: " + test_server.version)
  111. def test_cmdline_3_createlist(self):
  112. # Basic stream tests, like those in test_client.
  113. # No streams
  114. self.ok("list")
  115. self.match("")
  116. # Bad paths
  117. self.fail("create foo/bar/baz PrepData")
  118. self.contain("paths must start with /")
  119. self.fail("create /foo PrepData")
  120. self.contain("invalid path")
  121. # Bad layout type
  122. self.fail("create /newton/prep NoSuchLayout")
  123. self.contain("no such layout")
  124. # Create a few streams
  125. self.ok("create /newton/prep PrepData")
  126. self.ok("create /newton/raw RawData")
  127. self.ok("create /newton/zzz/rawnotch RawNotchedData")
  128. # Verify we got those 3 streams
  129. self.ok("list")
  130. self.match("/newton/prep PrepData\n"
  131. "/newton/raw RawData\n"
  132. "/newton/zzz/rawnotch RawNotchedData\n")
  133. # Match just one type or one path
  134. self.ok("list --path /newton/raw")
  135. self.match("/newton/raw RawData\n")
  136. self.ok("list --layout RawData")
  137. self.match("/newton/raw RawData\n")
  138. # Wildcard matches
  139. self.ok("list --layout Raw*")
  140. self.match("/newton/raw RawData\n"
  141. "/newton/zzz/rawnotch RawNotchedData\n")
  142. self.ok("list --path *zzz* --layout Raw*")
  143. self.match("/newton/zzz/rawnotch RawNotchedData\n")
  144. self.ok("list --path *zzz* --layout Prep*")
  145. self.match("")
  146. def test_cmdline_4_metadata(self):
  147. # Set / get metadata
  148. self.fail("metadata")
  149. self.fail("metadata --get")
  150. self.ok("metadata /newton/prep")
  151. self.match("")
  152. self.ok("metadata /newton/raw --get")
  153. self.match("")
  154. self.ok("metadata /newton/prep --set "
  155. "'description=The Data' "
  156. "v_scale=1.234")
  157. self.ok("metadata /newton/raw --update "
  158. "'description=The Data'")
  159. self.ok("metadata /newton/raw --update "
  160. "v_scale=1.234")
  161. # various parsing tests
  162. self.ok("metadata /newton/raw --update foo=")
  163. self.fail("metadata /newton/raw --update =bar")
  164. self.fail("metadata /newton/raw --update foo==bar")
  165. self.fail("metadata /newton/raw --update foo;bar")
  166. # errors
  167. self.fail("metadata /newton/nosuchstream foo=bar")
  168. self.contain("unrecognized arguments")
  169. self.fail("metadata /newton/nosuchstream")
  170. self.contain("No stream at path")
  171. self.fail("metadata /newton/nosuchstream --set foo=bar")
  172. self.contain("No stream at path")
  173. self.ok("metadata /newton/prep")
  174. self.match("description=The Data\nv_scale=1.234\n")
  175. self.ok("metadata /newton/prep --get")
  176. self.match("description=The Data\nv_scale=1.234\n")
  177. self.ok("metadata /newton/prep --get descr")
  178. self.match("descr=\n")
  179. self.ok("metadata /newton/prep --get description")
  180. self.match("description=The Data\n")
  181. self.ok("metadata /newton/prep --get description v_scale")
  182. self.match("description=The Data\nv_scale=1.234\n")
  183. self.ok("metadata /newton/prep --set "
  184. "'description=The Data'")
  185. self.ok("metadata /newton/prep --get")
  186. self.match("description=The Data\n")
  187. self.fail("metadata /newton/nosuchpath")
  188. self.contain("No stream at path /newton/nosuchpath")
  189. def test_cmdline_5_parsetime(self):
  190. cmd = nilmdb.cmdline.Cmdline(None)
  191. test = datetime_tz.datetime_tz.now()
  192. eq_(cmd.parse_time(str(test)), test)
  193. test = datetime_tz.datetime_tz.smartparse("20120405 1400-0400")
  194. eq_(cmd.parse_time("hi there 20120405 1400-0400 testing! 123"), test)
  195. eq_(cmd.parse_time("20120405 1800 UTC"), test)
  196. eq_(cmd.parse_time("20120405 1400-0400 UTC"), test)
  197. with assert_raises(ValueError):
  198. print cmd.parse_time("20120405 1400-9999")
  199. with assert_raises(ValueError):
  200. print cmd.parse_time("hello")
  201. with assert_raises(ValueError):
  202. print cmd.parse_time("-")
  203. with assert_raises(ValueError):
  204. print cmd.parse_time("")
  205. with assert_raises(ValueError):
  206. print cmd.parse_time("14:00")
  207. eq_(cmd.parse_time("snapshot-20120405-140000.raw.gz"), test)
  208. eq_(cmd.parse_time("prep-20120405T1400"), test)
  209. def test_cmdline_6_insert(self):
  210. self.ok("insert --help")
  211. self.fail("insert /foo/bar baz qwer")
  212. self.contain("Error getting stream info")
  213. self.fail("insert /newton/prep baz qwer")
  214. self.match("Error opening input file baz\n")
  215. self.fail("insert /newton/prep")
  216. self.contain("Error extracting time")
  217. self.fail("insert --start 19801205 /newton/prep 1 2 3 4")
  218. self.contain("--start can only be used with one input file")
  219. # insert pre-timestamped data, from stdin
  220. os.environ['TZ'] = "UTC"
  221. with open("tests/data/prep-20120323T1004-timestamped") as input:
  222. self.ok("insert --none /newton/prep", input)
  223. # insert data with normal timestamper from filename
  224. os.environ['TZ'] = "UTC"
  225. self.ok("insert /newton/prep "
  226. "tests/data/prep-20120323T1000 "
  227. "tests/data/prep-20120323T1002")
  228. # overlap
  229. os.environ['TZ'] = "UTC"
  230. self.fail("insert /newton/prep "
  231. "tests/data/prep-20120323T1004")
  232. self.contain("overlap")
  233. # Just to help test more situations -- stop and restart
  234. # the server now. This tests nilmdb's interval caching,
  235. # at the very least.
  236. server_stop()
  237. server_start()
  238. # still an overlap if we specify a different start
  239. os.environ['TZ'] = "America/New_York"
  240. self.fail("insert --start '03/23/2012 06:05:00' /newton/prep "
  241. "tests/data/prep-20120323T1004")
  242. self.contain("overlap")
  243. # wrong format
  244. os.environ['TZ'] = "UTC"
  245. self.fail("insert /newton/raw "
  246. "tests/data/prep-20120323T1004")
  247. self.contain("Error parsing input data")
  248. # empty data does nothing
  249. self.ok("insert --start '03/23/2012 06:05:00' /newton/prep "
  250. "/dev/null")
  251. # bad start time
  252. self.fail("insert --start 'whatever' /newton/prep /dev/null")
  253. def test_cmdline_7_detail(self):
  254. # Just count the number of lines, it's probably fine
  255. self.ok("list --detail")
  256. eq_(self.captured.count('\n'), 11)
  257. self.ok("list --detail --path *prep")
  258. eq_(self.captured.count('\n'), 7)
  259. self.ok("list --detail --path *prep --start='23 Mar 2012 10:02'")
  260. eq_(self.captured.count('\n'), 5)
  261. self.ok("list --detail --path *prep --start='23 Mar 2012 10:05'")
  262. eq_(self.captured.count('\n'), 3)
  263. self.ok("list --detail --path *prep --start='23 Mar 2012 10:05:15'")
  264. eq_(self.captured.count('\n'), 2)
  265. self.contain("10:05:15.000")
  266. self.ok("list --detail --path *prep --start='23 Mar 2012 10:05:15.50'")
  267. eq_(self.captured.count('\n'), 2)
  268. self.contain("10:05:15.500")
  269. self.ok("list --detail --path *prep --start='23 Mar 2012 19:05:15.50'")
  270. eq_(self.captured.count('\n'), 2)
  271. self.contain("no intervals")
  272. self.ok("list --detail --path *prep --start='23 Mar 2012 10:05:15.50'"
  273. + " --end='23 Mar 2012 10:05:15.50'")
  274. eq_(self.captured.count('\n'), 2)
  275. self.contain("10:05:15.500")
  276. self.ok("list --detail")
  277. eq_(self.captured.count('\n'), 11)
  278. def test_cmdline_8_extract(self):
  279. # nonexistent stream
  280. self.fail("extract /no/such/foo --start 2000-01-01 --end 2020-01-01")
  281. self.contain("Error getting stream info")
  282. # full dump
  283. #self.ok("extract -a /newton/prep --start 2000-01-01 --end 2020-01-01")
  284. #self.dump()
  285. def test_cmdline_9_truncated(self):
  286. # Test truncated responses by overriding the nilmdb response_size
  287. server_stop()
  288. server_start(response_size = 300)
  289. self.ok("list --detail")
  290. eq_(self.captured.count('\n'), 11)