Browse Source

Integrated CherryPy server into tree

Added testing framework for cherrypy server.
Added tests for basic server functionality.


git-svn-id: https://bucket.mit.edu/svn/nilm/nilmdb-new@10331 ddd99763-3ecb-0310-9145-efcb8ce7c51f
tags/bxinterval-last
Jim Paris 12 years ago
parent
commit
acdea5f00b
4 changed files with 238 additions and 40 deletions
  1. +1
    -0
      nilmdb/__init__.py
  2. +84
    -0
      nilmdb/server.py
  3. +53
    -0
      tests/server.py
  4. +100
    -40
      tests/test_nilmdb.py

+ 1
- 0
nilmdb/__init__.py View File

@@ -1,3 +1,4 @@
# empty
from nilmdb import NilmDB
from server import Server
from layout import *

+ 84
- 0
nilmdb/server.py View File

@@ -0,0 +1,84 @@
"""CherryPy-based server for accessing NILM database via HTTP"""

import nilmdb
import cherrypy
import sys

try:
import cherrypy
cherrypy.tools.json_out
except: # pragma: no cover
sys.stderr.write("Cherrypy 3.2+ required\n")
sys.exit(1)

class NilmApp(object):
def __init__(self, db):
self.db = db

class Root(NilmApp):
"""Root application for NILM database"""

def __init__(self, db, version):
super(Root, self).__init__(db)
self.server_version = version

@cherrypy.expose
def index(self):
raise cherrypy.NotFound()

@cherrypy.expose
def favicon_ico(self):
raise cherrypy.NotFound()

@cherrypy.expose
@cherrypy.tools.json_out()
def version(self):
return self.server_version

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

@cherrypy.expose
@cherrypy.tools.json_out()
def list(self):
return [ "hello world!! TODO" ]

class Exiter(object):
"""App that exits the server, for testing"""
@cherrypy.expose
def index(self):
cherrypy.response.headers['Content-Type'] = 'text/plain'
def content():
yield 'Exiting by request'
raise SystemExit
return content()
index._cp_config = { 'response.stream': True }

class Server(object):
version = "1.0"
def __init__(self, filename, host = '127.0.0.1', port = 8080, stoppable = False):
self.db = nilmdb.NilmDB(filename)
cherrypy.config.update({
'server.socket_host': host,
'server.socket_port': port,
'engine.autoreload_on': False,
'environment': 'embedded',
})
cherrypy.tree.apps = {}
cherrypy.tree.mount(Root(self.db, self.version), "/")
cherrypy.tree.mount(Stream(self.db), "/stream")
if stoppable:
cherrypy.tree.mount(Exiter(), "/exit")

def start(self, blocking = False, event = None):
cherrypy.engine.start()
if event is not None:
event.set()
if blocking:
cherrypy.engine.wait(cherrypy.engine.states.EXITING,
interval = 0.1, channel = 'main')

def stop(self):
cherrypy.engine.exit()
self.db.close()

+ 53
- 0
tests/server.py View File

@@ -0,0 +1,53 @@
import sys
import tables
import nilmdb

try:
import cherrypy
cherrypy.tools.json_out
except:
sys.stderr.write("Cherrypy 3.2+ required\n")
sys.exit(1)

class NilmApp:
def __init__(self, db):
self.db = db

class Root(NilmApp):
"""NILM Database"""

server_version = "1.0"

@cherrypy.expose
def index(self):
raise cherrypy.NotFound()

@cherrypy.expose
def favicon_ico(self):
raise cherrypy.NotFound()

@cherrypy.expose
@cherrypy.tools.json_out()
def version(self):
return self.server_version

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

@cherrypy.expose
@cherrypy.tools.json_out()
def list(self):
return
cherrypy.config.update({
'server.socket_host': '127.0.0.1',
'server.socket_port': 8080
})

db = nilmdb.nilmdb()
cherrypy.tree.mount(Root(db), "/")
cherrypy.tree.mount(Stream(db), "/stream")

if __name__ == "__main__":
cherrypy.engine.start()
cherrypy.engine.block()

+ 100
- 40
tests/test_nilmdb.py View File

@@ -1,11 +1,15 @@
import nilmdb
from nilmdb import NilmDB

from nose.tools import *
from nose.tools import assert_raises
from distutils.version import StrictVersion as V
import json
import itertools
import os
import sys
import atexit
import cherrypy
import threading
import urllib2

testdb = "tests/test.db"

@@ -13,41 +17,97 @@ testdb = "tests/test.db"
#def cleanup():
# os.unlink(testdb)

def test_NilmDB():
try:
os.unlink(testdb)
except:
pass

with assert_raises(IOError):
nilmdb.NilmDB("/nonexistant-db/foo")

db = nilmdb.NilmDB(testdb)
db.close()
db = nilmdb.NilmDB(testdb)

def test_stream():
db = nilmdb.NilmDB(testdb)
assert(db.stream_list() == [])

# Bad path
with assert_raises(ValueError):
db.stream_create("/foo", nilmdb.layout.PrepData)
# Bad index columns
with assert_raises(KeyError):
db.stream_create("/newton/prep", nilmdb.layout.PrepData, ["nonexistant"])
db.stream_create("/newton/prep", nilmdb.layout.PrepData)
db.stream_create("/newton/raw", nilmdb.layout.RawData)
db.stream_create("/newton/zzz/rawnotch", nilmdb.layout.RawNotchedData)

# Verify we got 3 streams
assert(db.stream_list() == [ "/newton/prep",
"/newton/raw",
"/newton/zzz/rawnotch" ])

# Verify that columns were made right
assert(len(db.h5file.getNode("/newton/prep").cols) == 9)
assert(len(db.h5file.getNode("/newton/raw").cols) == 7)
assert(len(db.h5file.getNode("/newton/zzz/rawnotch").cols) == 10)
assert(db.h5file.getNode("/newton/prep").colindexed["timestamp"])
assert(not db.h5file.getNode("/newton/prep").colindexed["p1"])
class TestNilmdb(object):
def test_NilmDB(self):
try:
os.unlink(testdb)
except:
pass

with assert_raises(IOError):
nilmdb.NilmDB("/nonexistant-db/foo")

db = nilmdb.NilmDB(testdb)
db.close()
db = nilmdb.NilmDB(testdb)
db.close()

def test_stream(self):
db = nilmdb.NilmDB(testdb)
assert(db.stream_list() == [])

# Bad path
with assert_raises(ValueError):
db.stream_create("/foo", nilmdb.layout.PrepData)
# Bad index columns
with assert_raises(KeyError):
db.stream_create("/newton/prep", nilmdb.layout.PrepData, ["nonexistant"])
db.stream_create("/newton/prep", nilmdb.layout.PrepData)
db.stream_create("/newton/raw", nilmdb.layout.RawData)
db.stream_create("/newton/zzz/rawnotch", nilmdb.layout.RawNotchedData)

# Verify we got 3 streams
assert(db.stream_list() == [ "/newton/prep",
"/newton/raw",
"/newton/zzz/rawnotch" ])

# Verify that columns were made right
assert(len(db.h5file.getNode("/newton/prep").cols) == 9)
assert(len(db.h5file.getNode("/newton/raw").cols) == 7)
assert(len(db.h5file.getNode("/newton/zzz/rawnotch").cols) == 10)
assert(db.h5file.getNode("/newton/prep").colindexed["timestamp"])
assert(not db.h5file.getNode("/newton/prep").colindexed["p1"])
db.close()

class TestBlockingServer(object):
def test_blocking_server(self):
# Start web app on a custom port
self.server = nilmdb.Server(testdb, host = "127.0.0.1",
port = 12380, stoppable = True)
# Run it
event = threading.Event()
def run_server():
self.server.start(blocking = True, event = event)
thread = threading.Thread(target = run_server)
thread.start()
event.wait()
# Send request to exit.
req = urllib2.urlopen("http://127.0.0.1:12380/exit/")

# Wait for it
thread.join()
def geturl(path):
req = urllib2.urlopen("http://127.0.0.1:12380" + path)
return req.read()

def getjson(path):
return json.loads(geturl(path))

class TestServer(object):

def setUp(self):
# Start web app on a custom port
self.server = nilmdb.Server(testdb, host = "127.0.0.1",
port = 12380, stoppable = False)
self.server.start(blocking = False)

def tearDown(self):
# Close web app
self.server.stop()

def test_server(self):
# Make sure we can't force an exit, and test other 404 errors
for url in [ "/exit", "/", "/favicon.ico" ]:
with assert_raises(urllib2.HTTPError) as e:
geturl(url)
eq_(e.exception.code, 404)

# Check version
eq_(V(getjson("/version")), V(self.server.version))

def test_stream(self):
# List
streams = getjson("/stream/list")
print streams

Loading…
Cancel
Save