start PDB unittest
This commit is contained in:
16
Makefile
16
Makefile
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
0
devsupApp/src/devsup/test/__init__.py
Normal file
0
devsupApp/src/devsup/test/__init__.py
Normal file
82
devsupApp/src/devsup/test/test_db.py
Normal file
82
devsupApp/src/devsup/test/test_db.py
Normal 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)
|
@ -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
121
devsupApp/src/utest.c
Normal 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;
|
||||||
|
}
|
Reference in New Issue
Block a user