start PDB unittest

This commit is contained in:
Michael Davidsaver
2018-11-03 14:39:57 -07:00
parent f428d2a4e4
commit a3524c5aa0
9 changed files with 264 additions and 25 deletions

View File

@ -18,8 +18,16 @@ include $(TOP)/configure/RULES_TOP
UNINSTALL_DIRS += $(wildcard $(INSTALL_LOCATION)/python*) UNINSTALL_DIRS += $(wildcard $(INSTALL_LOCATION)/python*)
#useful targets includ: doc-html and doc-clean # jump to a sub-directory where CONFIG_PY has been included
doc-%: # can't include CONFIG_PY here as it may not exist yet
PYTHONPATH=$$PWD/python$(PY_VER)/$(EPICS_HOST_ARCH) $(MAKE) -C documentation $* nose sphinx sh ipython: all
$(MAKE) -C devsupApp/src/O.$(EPICS_HOST_ARCH) $@ PYTHON=$(PYTHON)
doc: doc-html sphinx-clean:
$(MAKE) -C documentation clean PYTHON=$(PYTHON)
sphinx-commit: sphinx
touch documentation/_build/html/.nojekyll
./commit-gh.sh documentation/_build/html
.PHONY: nose sphinx sphinx-commit sphinx-clean

View File

@ -39,5 +39,7 @@ PY_VER=2.7
# Python interpreter # Python interpreter
PYTHON ?= python$(PY_VER) PYTHON ?= python$(PY_VER)
USR_CPPFLAGS += -DUSE_TYPED_RSET
-include $(TOP)/configure/CONFIG_SITE.local -include $(TOP)/configure/CONFIG_SITE.local
-include $(TOP)/../CONFIG_SITE.local -include $(TOP)/../CONFIG_SITE.local

View File

@ -24,9 +24,12 @@ _dbapi_SRCS += dbapi.c
_dbapi_SRCS += dbrec.c _dbapi_SRCS += dbrec.c
_dbapi_SRCS += dbfield.c _dbapi_SRCS += dbfield.c
_dbapi_SRCS += dbdset.c _dbapi_SRCS += dbdset.c
_dbapi_SRCS += utest.c
_dbapi_SRCS += pyDevSupCommon_registerRecordDeviceDriver.cpp _dbapi_SRCS += pyDevSupCommon_registerRecordDeviceDriver.cpp
_dbapi_LIBS += $(EPICS_BASE_IOC_LIBS)
PY += devsup/__init__.py PY += devsup/__init__.py
PY += devsup/_nullapi.py PY += devsup/_nullapi.py
PY += devsup/db.py PY += devsup/db.py
@ -37,6 +40,9 @@ PY += devsup/util.py
PY += devsup/disect.py PY += devsup/disect.py
PY += devsup/ptable.py PY += devsup/ptable.py
PY += devsup/test/__init__.py
PY += devsup/test/test_db.py
#=========================== #===========================
include $(TOP)/configure/RULES include $(TOP)/configure/RULES
@ -52,3 +58,20 @@ pyconfig:
@echo "Library path: $(PY_LIBDIRS)" @echo "Library path: $(PY_LIBDIRS)"
@echo "USR_CPPFLAGS: $(USR_CPPFLAGS)" @echo "USR_CPPFLAGS: $(USR_CPPFLAGS)"
@echo "USR_LDFLAGS: $(USR_LDFLAGS)" @echo "USR_LDFLAGS: $(USR_LDFLAGS)"
ifneq (,$(T_A))
nose:
PYTHONPATH="${PYTHONPATH}:$(abspath $(TOP))/python$(PY_LD_VER)/$(EPICS_HOST_ARCH)" $(PYTHON) -m nose -P devsup $(NOSEFLAGS)
# bounce back down to the sphinx generated Makefile
# aren't Makefiles fun...
sphinx:
PYTHONPATH="${PYTHONPATH}:$(abspath $(TOP))/python$(PY_LD_VER)/$(EPICS_HOST_ARCH)" $(MAKE) -C $(TOP)/documentation html
sh:
echo "export PYTHONPATH=\$${PYTHONPATH}:$(abspath $(TOP))/python$(PY_LD_VER)/$(EPICS_HOST_ARCH)" > $(OUTPUT)
ipython:
PYTHONPATH="${PYTHONPATH}:$(abspath $(TOP))/python$(PY_LD_VER)/$(EPICS_HOST_ARCH)" $(PYTHON) -c "import sys; sys.argv[0] = '$(PYTHON)'; from IPython.terminal.ipapp import launch_new_instance; launch_new_instance()"
endif

View File

@ -194,9 +194,9 @@ PyObject *py_dbReadDatabase(PyObject *unused, PyObject *args, PyObject *kws)
} }
Py_BEGIN_ALLOW_THREADS { Py_BEGIN_ALLOW_THREADS {
if(fname) if(fname) {
status = dbReadDatabase(&pdbbase, fname, path, sub); status = dbReadDatabase(&pdbbase, fname, path, sub);
else { } else {
FILE *ff = fdopen(fd, "r"); FILE *ff = fdopen(fd, "r");
status = dbReadDatabaseFP(&pdbbase, ff, path, sub); status = dbReadDatabaseFP(&pdbbase, ff, path, sub);
// dbReadDatabaseFP() has called fclose() // dbReadDatabaseFP() has called fclose()
@ -213,18 +213,31 @@ PyObject *py_dbReadDatabase(PyObject *unused, PyObject *args, PyObject *kws)
} }
static static
PyObject *py_iocInit(PyObject *unused) PyObject *py_iocInit(PyObject *unused, PyObject *args, PyObject *kws)
{ {
static char* names[] = {"isolate", NULL};
PyObject *pyisolate = Py_True;
int isolate, ret;
if(!PyArg_ParseTupleAndKeywords(args, kws, "|O", names, &pyisolate))
return NULL;
isolate = PyObject_IsTrue(pyisolate);
Py_BEGIN_ALLOW_THREADS { Py_BEGIN_ALLOW_THREADS {
iocInit(); ret = isolate ? iocBuildIsolated() : iocBuild();
if(!ret)
ret = iocRun();
} Py_END_ALLOW_THREADS } Py_END_ALLOW_THREADS
if(ret)
return PyErr_Format(PyExc_RuntimeError, "Error %d", ret);
Py_RETURN_NONE; Py_RETURN_NONE;
} }
static static
PyObject *py_pyDevSupCommon(PyObject *unused) PyObject *py_pyDevSupCommon(PyObject *unused)
{ {
static int once;
Py_BEGIN_ALLOW_THREADS { Py_BEGIN_ALLOW_THREADS {
pyDevSupCommon_registerRecordDeviceDriver(pdbbase); pyDevSupCommon_registerRecordDeviceDriver(pdbbase);
} Py_END_ALLOW_THREADS } Py_END_ALLOW_THREADS
@ -258,7 +271,7 @@ static struct PyModuleDef dbapimodule = {
PyMODINIT_FUNC init_dbapi(void) PyMODINIT_FUNC init_dbapi(void)
{ {
PyObject *mod = NULL, *hookdict, *vertup, *obj; PyObject *mod = NULL, *hookdict, *vertup;
pystate *st; pystate *st;
pyDevReasonID = epicsThreadPrivateCreate(); pyDevReasonID = epicsThreadPrivateCreate();
@ -354,19 +367,12 @@ PyMODINIT_FUNC init_dbapi(void)
if(vertup) if(vertup)
PyModule_AddObject(mod, "pydevver", vertup); PyModule_AddObject(mod, "pydevver", vertup);
#if PY_MAJOR_VERSION >= 3 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION>=7)
obj = PyCapsule_New(pdbbase, "pdbbase", NULL);
#else
obj = PyCObject_FromVoidPtrAndDesc(pdbbase, "pdbbase", NULL);
#endif
if(!obj)
goto fail;
PyModule_AddObject(mod, "pdbbase", obj);
if(pyField_prepare(mod)) if(pyField_prepare(mod))
goto fail; goto fail;
if(pyRecord_prepare(mod)) if(pyRecord_prepare(mod))
goto fail; goto fail;
if(pyUTest_prepare(mod))
goto fail;
MODINIT_RET(mod); MODINIT_RET(mod);

View File

@ -22,16 +22,11 @@ from ._dbapi import (EPICS_VERSION_STRING,
__all__ = [] __all__ = []
_ready = [False]
def _init(iocMain=False): def _init(iocMain=False):
if _ready[0]:
return
_ready[0] = True
if not iocMain: if not iocMain:
# we haven't read/register base.dbd # we haven't read/register base.dbd
_dbapi.dbReadDatabase(os.path.join(XEPICS_BASE, "dbd", "base.dbd")) _dbapi.dbReadDatabase(os.path.join(XEPICS_BASE, "dbd", "base.dbd"),
path=os.path.join(XEPICS_BASE, "dbd"))
_dbapi._dbd_rrd_base() _dbapi._dbd_rrd_base()
with tempfile.NamedTemporaryFile() as F: with tempfile.NamedTemporaryFile() as F:

View File

View File

@ -0,0 +1,82 @@
import os
import unittest
import tempfile
from ..db import getRecord
from .. import _dbapi
from .. import _init
# short-circuit warning from _dbapi._init()
os.environ['TOP'] = _dbapi.XPYDEV_BASE
class IOCHelper(unittest.TestCase):
db = None
autostart = running = False
def setUp(self):
print("testdbPrepare()")
_dbapi._UTest.testdbPrepare()
_init(iocMain=False) # load base.dbd
if self.db is not None:
with tempfile.NamedTemporaryFile() as F:
F.write(self.db)
F.flush()
_dbapi.dbReadDatabase(F.name)
if self.autostart:
self.iocInit()
def tearDown(self):
self.iocShutdown();
print("testdbCleanup()")
_dbapi._UTest.testdbCleanup()
def iocInit(self):
if not self.running:
print("testIocInitOk")
_dbapi._UTest.testIocInitOk()
self.running = True
def iocShutdown(self):
if self.running:
print("testIocShutdownOk")
_dbapi._UTest.testIocShutdownOk()
self.running = False
class TestIOC(IOCHelper):
def test_base(self):
pass
def test_start(self):
self.iocInit()
self.iocShutdown()
def test_db(self):
with tempfile.NamedTemporaryFile() as F:
F.write('record(longin, "test") {}\n')
F.flush()
_dbapi.dbReadDatabase(F.name)
rec = getRecord("test")
self.assertEqual(rec.VAL, 0)
rec.VAL = 5
self.assertEqual(rec.VAL, 5)
class TestScan(IOCHelper):
db = """
record(longout, src) {
field(OUT, "tgt PP")
}
record(longin, "tgt") {}
"""
autostart = True
def test_link(self):
src, tgt = getRecord('src'), getRecord('tgt')
src.VAL = 42
self.assertEqual(src.VAL, 42)
self.assertEqual(tgt.VAL, 0)
src.scan(sync=True)
self.assertEqual(tgt.VAL, 42)

View File

@ -20,6 +20,8 @@ initHookState pyInitLastState;
PyObject* pyDBD_setup(PyObject *unused); PyObject* pyDBD_setup(PyObject *unused);
PyObject* pyDBD_cleanup(PyObject *unused); PyObject* pyDBD_cleanup(PyObject *unused);
int pyUTest_prepare(PyObject *module);
int pyField_prepare(PyObject *module); int pyField_prepare(PyObject *module);
int pyRecord_prepare(PyObject *module); int pyRecord_prepare(PyObject *module);

121
devsupApp/src/utest.c Normal file
View File

@ -0,0 +1,121 @@
/* python has its own ideas about which version to support */
#undef _POSIX_C_SOURCE
#undef _XOPEN_SOURCE
#include <Python.h>
#include <dbUnitTest.h>
#include <dbEvent.h>
#include <iocInit.h>
#include <errlog.h>
#include "pydevsup.h"
static dbEventCtx testEvtCtx;
typedef struct {
PyObject_HEAD
} UTest;
static PyTypeObject UTest_type = {
#if PY_MAJOR_VERSION >= 3
PyVarObject_HEAD_INIT(NULL, 0)
#else
PyObject_HEAD_INIT(NULL)
0,
#endif
"_dbapi._UTest",
sizeof(UTest),
};
static PyObject* utest_prepare(PyObject *unused)
{
Py_BEGIN_ALLOW_THREADS {
//testdbPrepare(); doesn't do anything essential for us as of 7.0.2
} Py_END_ALLOW_THREADS
Py_RETURN_NONE;
}
static PyObject* utest_init(PyObject *unused)
{
int ret;
if(testEvtCtx)
return PyErr_Format(PyExc_RuntimeError, "Missing testIocShutdownOk()");
// like, testIocInitOk() without testAbort()
Py_BEGIN_ALLOW_THREADS {
eltc(0);
ret = iocBuildIsolated() || iocRun();
eltc(1);
} Py_END_ALLOW_THREADS
if(ret) {
return PyErr_Format(PyExc_RuntimeError, "iocInit fails with %d", ret);
}
Py_BEGIN_ALLOW_THREADS {
testEvtCtx=db_init_events();
} Py_END_ALLOW_THREADS
if(!testEvtCtx) {
iocShutdown();
return PyErr_Format(PyExc_RuntimeError, "iocInit fails create dbEvent context");
}
Py_BEGIN_ALLOW_THREADS {
ret = db_start_events(testEvtCtx, "CAS-test-py", NULL, NULL, epicsThreadPriorityCAServerLow);
} Py_END_ALLOW_THREADS
if(ret!=DB_EVENT_OK) {
db_close_events(testEvtCtx);
testEvtCtx = NULL;
iocShutdown();
return PyErr_Format(PyExc_RuntimeError, "db_start_events fails with %d", ret);
}
Py_RETURN_NONE;
}
static PyObject* utest_shutdown(PyObject *unused)
{
Py_BEGIN_ALLOW_THREADS {
//testIocShutdownOk();
db_close_events(testEvtCtx);
testEvtCtx = NULL;
iocShutdown();
} Py_END_ALLOW_THREADS
Py_RETURN_NONE;
}
static PyObject* utest_cleanup(PyObject *unused)
{
Py_BEGIN_ALLOW_THREADS {
testdbCleanup();
errlogFlush();
} Py_END_ALLOW_THREADS
Py_RETURN_NONE;
}
static PyMethodDef UTest_methods[] = {
{"testdbPrepare", (PyCFunction)&utest_prepare, METH_STATIC|METH_NOARGS, ""},
{"testIocInitOk", (PyCFunction)&utest_init, METH_STATIC|METH_NOARGS, ""},
{"testIocShutdownOk", (PyCFunction)&utest_shutdown, METH_STATIC|METH_NOARGS, ""},
{"testdbCleanup", (PyCFunction)&utest_cleanup, METH_STATIC|METH_NOARGS, ""},
{NULL}
};
int pyUTest_prepare(PyObject *module)
{
PyObject *typeobj=(PyObject*)&UTest_type;
UTest_type.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE;
UTest_type.tp_methods = UTest_methods;
if(PyType_Ready(&UTest_type)<0)
return -1;
Py_INCREF(typeobj);
if(PyModule_AddObject(module, "_UTest", typeobj)) {
Py_DECREF(typeobj);
return -1;
}
return 0;
}