Browse Source

More command line tests.

Make nilmdb.cmdline a proper class
Fix various stuff
Add /dbpath command to get DB path


git-svn-id: https://bucket.mit.edu/svn/nilm/nilmdb@10677 ddd99763-3ecb-0310-9145-efcb8ce7c51f
tags/bxinterval-last
Jim Paris 10 years ago
parent
commit
277b0c1d00
7 changed files with 206 additions and 114 deletions
  1. +11
    -1
      nilmdb/client.py
  2. +94
    -83
      nilmdb/cmdline.py
  3. +4
    -1
      nilmdb/nilmdb.py
  4. +10
    -2
      nilmdb/server.py
  5. +1
    -0
      setup.cfg
  6. +77
    -26
      tests/test_cmdline.py
  7. +9
    -1
      tests/test_helpers.py

+ 11
- 1
nilmdb/client.py View File

@@ -14,6 +14,8 @@ import urllib
import pycurl
import cStringIO

version = "1.0"

class NilmCommError(Exception):
"""Base exception for both ClientError and ServerError responses"""
def __init__(self,
@@ -120,7 +122,7 @@ class MyCurl(object):
class Client(object):
"""Main client interface to the Nilm database."""

client_version = "1.0"
client_version = version
def __init__(self, url):
self.curl = MyCurl(url)
@@ -129,10 +131,18 @@ class Client(object):
"""Return compact json-encoded version of parameter"""
return json.dumps(data, separators=(',',':'))

def geturl(self):
"""Return the URL we're using"""
return self.curl.baseurl

def version(self):
"""Return server version"""
return self.curl.getjson("version")

def dbpath(self):
"""Return server database path"""
return self.curl.getjson("dbpath")

def stream_list(self, path = None, layout = None):
params = {}
if path is not None:


+ 94
- 83
nilmdb/cmdline.py View File

@@ -13,86 +13,97 @@ import urlparse
import argparse

version = "0.1"
version_string = sprintf("nilmtool %s, client library %s",
version, nilmdb.Client.client_version)

def parse_opts(argv):
formatter = argparse.ArgumentDefaultsHelpFormatter
parser = argparse.ArgumentParser(add_help = False,
formatter_class = formatter)

group = parser.add_argument_group("General options")
group.add_argument("-h", "--help", action='help',
help='show this help message and exit')
group.add_argument("-V", "--version", action="version",
version=version_string)
group.add_argument("-q", "--quiet", action="store_true",
help="suppress unnecessary console output")

group = parser.add_argument_group("Server")
group.add_argument("-u", "--url", action="store",
default="http://localhost:12380/",
help="NilmDB server URL (default: %(default)s)")

sub = parser.add_subparsers(title="Commands",
dest="command",
description="Specify --help after the "
"command for command-specific options.")
# info
cmd = sub.add_parser("info", help="Server information",
formatter_class = formatter,
description="""
List information about the server, like
version.
""")

# list
cmd = sub.add_parser("list", help="List streams",
formatter_class = formatter,
description="""
List streams available in the database,
optionally filtering by type or partial path.
""")
group = cmd.add_argument_group("Stream filtering")
group.add_argument("-t", "--type", metavar="GLOB", default="*",
help="Match only this stream type")
group.add_argument("-p", "--path", metavar="GLOB", default="*",
help="Match only this path")


# group.add_argument(
# parser.add_argument_group(group)

# group = OptionGroup(parser, "Stream Operations")
# group.add_argument("-l", "--list", action="store_true", default=False,
# action="store", dest="url",
# default="http://localhost:12380/",
# help="NilmDB server URL (default: %default)")
# parser.add_argument_group(group)

args = parser.parse_args(argv)

return args

def die(formatstr, *args):
fprintf(sys.stderr, formatstr, *args)
sys.exit(-1)

def run(argv):
args = parse_opts(argv)

client = nilmdb.Client(args.url)

try:
server_version = client.version()
except nilmdb.client.NilmCommError as e:
die("Error connecting to server: %s\n", str(e))

print server_version
print args.url
print args.command

# if not opt.quiet:
# printf("Server URL: %s\n", opt.url)

class Cmdline(object):

def __init__(self, argv):
self.argv = argv

def parse_opts(self):
version_string = sprintf("nilmtool %s, client library %s",
version, nilmdb.Client.client_version)

formatter = argparse.ArgumentDefaultsHelpFormatter
parser = argparse.ArgumentParser(add_help = False,
formatter_class = formatter)

group = parser.add_argument_group("General options")
group.add_argument("-h", "--help", action='help',
help='show this help message and exit')
group.add_argument("-V", "--version", action="version",
version=version_string)
group.add_argument("-q", "--quiet", action="store_true",
help="suppress unnecessary console output")

group = parser.add_argument_group("Server")
group.add_argument("-u", "--url", action="store",
default="http://localhost:12380/",
help="NilmDB server URL (default: %(default)s)")

sub = parser.add_subparsers(title="Commands",
dest="command",
description="Specify --help after the "
"command for command-specific options.")

# info
cmd = sub.add_parser("info", help="Server information",
formatter_class = formatter,
description="""
List information about the server, like
version.
""")

# list
cmd = sub.add_parser("list", help="List streams",
formatter_class = formatter,
description="""
List streams available in the database,
optionally filtering by type or partial path.
""")
group = cmd.add_argument_group("Stream filtering")
group.add_argument("-t", "--type", metavar="GLOB", default="*",
help="Match only this stream type")
group.add_argument("-p", "--path", metavar="GLOB", default="*",
help="Match only this path")

# group.add_argument(

# parser.add_argument_group(group)

# group = OptionGroup(parser, "Stream Operations")
# group.add_argument("-l", "--list", action="store_true", default=False,
# action="store", dest="url",
# default="http://localhost:12380/",
# help="NilmDB server URL (default: %default)")
# parser.add_argument_group(group)

self.args = parser.parse_args(self.argv)

def die(self, formatstr, *args):
fprintf(sys.stderr, formatstr, *args)
sys.exit(-1)

def run(self):
self.parse_opts()

self.client = nilmdb.Client(self.args.url)

# Make a test connection to make sure things work
try:
server_version = self.client.version()
except nilmdb.client.NilmCommError as e:
self.die("Error connecting to server: %s\n", str(e))

# Now dispatch client request to appropriate function. Parser
# should have ensured that we don't have any unknown commands
# here.
getattr(self,"cmd_" + self.args.command)()

def cmd_info(self):
printf("Client library version: %s\n", self.client.client_version)
printf("Server version: %s\n", self.client.version())
printf("Server URL: %s\n", self.client.geturl())
printf("Server database: %s\n", self.client.dbpath())

# if not opt.quiet:
# printf("Server URL: %s\n", opt.url)

+ 4
- 1
nilmdb/nilmdb.py View File

@@ -85,7 +85,7 @@ class NilmDB(object):

def __init__(self, basepath, sync=True):
# set up path
self.basepath = basepath.rstrip('/')
self.basepath = os.path.abspath(basepath.rstrip('/'))

# Create the database path if it doesn't exist
try:
@@ -121,6 +121,9 @@ class NilmDB(object):
"error: NilmDB.close() wasn't called, path %s",
self.basepath)

def get_basepath(self):
return self.basepath

def close(self):
if self.con:
self.con.commit()


+ 10
- 2
nilmdb/server.py View File

@@ -24,6 +24,8 @@ class NilmApp(object):
def __init__(self, db):
self.db = db

version = "1.1"
class Root(NilmApp):
"""Root application for NILM database"""

@@ -47,6 +49,12 @@ class Root(NilmApp):
def version(self):
return self.server_version

# /dbpath
@cherrypy.expose
@cherrypy.tools.json_out()
def dbpath(self):
return self.db.get_basepath()

class Stream(NilmApp):
"""Stream-specific operations"""

@@ -186,14 +194,14 @@ class Exiter(object):
index._cp_config = { 'response.stream': True }

class Server(object):
version = "1.0"
def __init__(self, db, host = '127.0.0.1', port = 8080,
stoppable = False, # whether /exit URL exists
embedded = True, # hide diagnostics and output, etc
fast_shutdown = False, # don't wait for clients to disconn.
force_traceback = False # include traceback in all errors
):
self.version = version
# Need to wrap DB object in a serializer because we'll call
# into it from separate threads.
self.embedded = embedded


+ 1
- 0
setup.cfg View File

@@ -10,6 +10,7 @@ cover-erase=
##cover-branches= # need nose 1.1.3 for this
stop=
verbosity=2
tests=tests/test_cmdline.py
#tests=tests/test_layout.py
#tests=tests/test_interval.py
#tests=tests/test_client.py


+ 77
- 26
tests/test_cmdline.py View File

@@ -13,31 +13,35 @@ import threading
import urllib2
from urllib2 import urlopen, HTTPError
import Queue
import StringIO
import cStringIO
import shlex

from test_helpers import *

testdb = "tests/cmdline-testdb"

class TestCmdline(object):
def setup_module():
global test_server, test_db
# Clear out DB
recursive_unlink(testdb)

def setUp(self):
# Clear out DB
recursive_unlink(testdb)
# Start web app on a custom port
test_db = nilmdb.NilmDB(testdb, sync = False)
test_server = nilmdb.Server(test_db, host = "127.0.0.1",
port = 12380, stoppable = False,
fast_shutdown = True,
force_traceback = False)
test_server.start(blocking = False)

# Start web app on a custom port
self.db = nilmdb.NilmDB(testdb, sync=False)
self.server = nilmdb.Server(self.db, host = "127.0.0.1",
port = 12380, stoppable = False)
self.server.start(blocking = False)
def teardown_module():
global test_server, test_db
# Close web app
test_server.stop()
test_db.close()

def tearDown(self):
# Close web app
self.server.stop()
self.db.close()
class TestCmdline(object):

def run(self, arg_string, input_string = None):
def run(self, arg_string, input_string = ""):
"""Run a cmdline client with the specified argument string,
passing the given input. Returns a tuple with the output and
exit code"""
@@ -49,24 +53,71 @@ class TestCmdline(object):
( sys.stdin, sys.stdout, sys.stderr ) = self.io
def __exit__(self, type, value, traceback):
( sys.stdin, sys.stdout, sys.stderr ) = self.saved
infile = StringIO.StringIO(input_string)
outfile = StringIO.StringIO()
infile = cStringIO.StringIO(input_string)
outfile = cStringIO.StringIO()
with stdio_wrapper(infile, outfile, outfile) as s:
try:
nilmdb.cmdline.run(shlex.split(arg_string))
nilmdb.cmdline.Cmdline(shlex.split(arg_string)).run()
sys.exit(0)
except SystemExit as e:
exitcode = e.code
output = outfile.getvalue()
return (output, exitcode)
self.output = output
self.exitcode = exitcode

def ok(self, arg_string, input_string = ""):
self.run(arg_string, input_string)
if self.exitcode != 0:
self.dump()
eq_(self.exitcode, 0)

def fail(self, arg_string, input_string = ""):
self.run(arg_string, input_string)
if self.exitcode == 0:
self.dump()
ne_(self.exitcode, 0)

def check(self, checkstring):
in_(checkstring, self.output)

def dump(self):
print '-----dump start-----'
print self.output[:-1]
print '-----dump end-----'

def test_cmdline_basic(self):
(output, exitcode) = self.run("--help")
assert("usage:" in output)
eq_(exitcode, 0)
# help
self.ok("--help")
self.check("usage:")

# fail for no args
self.fail("")

# fail for no such option
self.fail("--nosuchoption")

# fail for bad command
self.fail("badcommand")

# try some URL constructions
self.fail("--url http://nosuchurl/ info")
self.check("Couldn't resolve host 'nosuchurl'")

self.fail("--url nosuchurl info")
self.check("Couldn't resolve host 'nosuchurl'")

self.fail("-u nosuchurl/foo info")
self.check("Couldn't resolve host 'nosuchurl'")

self.fail("-u localhost:0 info")
self.check("couldn't connect to host")

self.ok("-u localhost:12380 info")
self.ok("info")

(output, exitcode) = self.run("--nosuchoption")
ne_(exitcode, 0)
def test_cmdline_info(self):
self.ok("info")
self.check("Server URL: http://localhost:12380/")
self.check("Server version: " + test_server.version)

# (output, exitcode) = self.run("--url http://localhost:12380/")
# eq_(exitcode, 0)

+ 9
- 1
tests/test_helpers.py View File

@@ -8,7 +8,15 @@ def eq_(a, b):

def in_(a, b):
if a not in b:
raise AssertionError("%r not in %r" % (a, b))
if not isinstance(a, basestring):
a = repr(a)
else:
a = '"' + a + '"'
if not isinstance(b, basestring):
b = repr(b)
else:
b = '"' + b + '"'
raise AssertionError("%s not in %s" % (a, b))

def ne_(a, b):
if not a != b:


Loading…
Cancel
Save