hardware_testbench: use plain subprocess to start controllers

This commit is contained in:
Robert Jördens 2016-01-28 15:31:01 -07:00
parent 1b7020dff3
commit 982fbb0670
4 changed files with 41 additions and 76 deletions

View File

@ -5,14 +5,14 @@ import os
import sys import sys
import unittest import unittest
import logging import logging
import asyncio import subprocess
import threading import shlex
import time
from artiq.master.databases import DeviceDB, DatasetDB from artiq.master.databases import DeviceDB, DatasetDB
from artiq.master.worker_db import DeviceManager, DatasetManager from artiq.master.worker_db import DeviceManager, DatasetManager
from artiq.coredevice.core import CompileError from artiq.coredevice.core import CompileError
from artiq.frontend.artiq_run import DummyScheduler from artiq.frontend.artiq_run import DummyScheduler
from artiq.devices.ctlmgr import Controllers
artiq_root = os.getenv("ARTIQ_ROOT") artiq_root = os.getenv("ARTIQ_ROOT")
@ -21,75 +21,38 @@ logger = logging.getLogger(__name__)
@unittest.skipUnless(artiq_root, "no ARTIQ_ROOT") @unittest.skipUnless(artiq_root, "no ARTIQ_ROOT")
class ControllerCase(unittest.TestCase): class ControllerCase(unittest.TestCase):
host_filter = "::1"
timeout = 2
def setUp(self): def setUp(self):
if os.name == "nt":
self.loop = asyncio.ProactorEventLoop()
else:
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self.addCleanup(self.loop.close)
self.loop_thread = threading.Thread(target=self.loop.run_forever)
self.addCleanup(self.loop_thread.join)
self.addCleanup(self.loop.call_soon_threadsafe, self.loop.stop)
self.controllers = Controllers()
self.controllers.host_filter = self.host_filter
self.addCleanup(self._stop_controllers)
self.device_db = DeviceDB(os.path.join(artiq_root, "device_db.pyon")) self.device_db = DeviceDB(os.path.join(artiq_root, "device_db.pyon"))
self.device_mgr = DeviceManager(self.device_db) self.device_mgr = DeviceManager(self.device_db)
self.addCleanup(self.device_mgr.close_devices) self.addCleanup(self.device_mgr.close_devices)
self.controllers = {}
self.loop_thread.start() def tearDown(self):
self.stop_controllers()
def _stop_controllers(self): def start_controller(self, name, sleep=1):
fut = asyncio.run_coroutine_threadsafe(self.controllers.shutdown(), try:
self.loop) entry = self.device_db.get(name)
fut.result() except KeyError:
raise unittest.SkipTest(
"controller `{}` not found".format(name))
entry["command"] = entry["command"].format(
name=name, bind=entry["host"], port=entry["port"])
proc = subprocess.Popen(shlex.split(entry["command"]))
self.controllers[name] = entry, proc
time.sleep(sleep)
async def _start(self, *names): def stop_controllers(self):
print("FOOO started") for entry, proc in self.controllers.values():
l = asyncio.get_event_loop_policy() proc.terminate()
l = l.get_event_loop() for name in list(self.controllers):
print("FOOO started") entry, proc = self.controllers[name]
for name in names:
try: try:
self.controllers[name] = self.device_db.get(name) proc.wait(entry.get("term_timeout"))
except KeyError: except TimeoutError:
raise unittest.SkipTest( proc.kill()
"controller `{}` not found".format(name)) proc.wait(entry.get("term_timeout"))
await self.controllers.queue.join() del self.controllers[name]
await asyncio.wait([self._wait_for_ping(name)
for name in names])
async def _wait_for_ping(self, name, retries=5):
t = self.timeout
dt = t/retries
while t > 0:
try:
ok = await asyncio.wait_for(
self.controllers.active[name].call("ping"), dt)
if not ok:
raise ValueError("unexcepted ping() response from "
"controller `{}`: `{}`".format(name, ok))
return ok
except asyncio.TimeoutError:
t -= dt
except (ConnectionAbortedError, ConnectionError,
ConnectionRefusedError, ConnectionResetError):
await asyncio.sleep(dt)
t -= dt
raise asyncio.TimeoutError
def start_controllers(self, *names):
fut = asyncio.run_coroutine_threadsafe(self._start(*names), self.loop)
fut.result()
@unittest.skipUnless(artiq_root, "no ARTIQ_ROOT") @unittest.skipUnless(artiq_root, "no ARTIQ_ROOT")

View File

@ -20,7 +20,7 @@ class GenericLdaTest:
class TestLda(ControllerCase, GenericLdaTest): class TestLda(ControllerCase, GenericLdaTest):
def setUp(self): def setUp(self):
ControllerCase.setUp(self) ControllerCase.setUp(self)
self.start_controllers("lda") self.start_controller("lda")
self.cont = self.device_mgr.get("lda") self.cont = self.device_mgr.get("lda")

View File

@ -1,7 +1,7 @@
import unittest import unittest
from artiq.devices.novatech409b.driver import Novatech409B from artiq.devices.novatech409b.driver import Novatech409B
from artiq.test.hardware_testbench import ControllerCase, with_controllers from artiq.test.hardware_testbench import ControllerCase
class GenericNovatech409BTest: class GenericNovatech409BTest:
@ -21,10 +21,10 @@ class GenericNovatech409BTest:
class TestNovatech409B(GenericNovatech409BTest, ControllerCase): class TestNovatech409B(GenericNovatech409BTest, ControllerCase):
@with_controllers("novatech409b") def setUp(self):
def test_parameters_readback(self): ControllerCase.setUp(self)
self.start_controller("novatech409b")
self.driver = self.device_mgr.get("novatech409b") self.driver = self.device_mgr.get("novatech409b")
GenericNovatech409BTest.test_parameters_readback(self)
class TestNovatech409BSim(GenericNovatech409BTest, unittest.TestCase): class TestNovatech409BSim(GenericNovatech409BTest, unittest.TestCase):

View File

@ -3,7 +3,7 @@ import time
from artiq.devices.thorlabs_tcube.driver import Tdc, Tpz, TdcSim, TpzSim from artiq.devices.thorlabs_tcube.driver import Tdc, Tpz, TdcSim, TpzSim
from artiq.language.units import V from artiq.language.units import V
from artiq.test.hardware_testbench import get_from_ddb from artiq.test.hardware_testbench import ControllerCase
class GenericTdcTest: class GenericTdcTest:
@ -131,10 +131,11 @@ class GenericTpzTest:
self.assertEqual(test_vector, self.cont.get_tpz_io_settings()) self.assertEqual(test_vector, self.cont.get_tpz_io_settings())
class TestTdc(unittest.TestCase, GenericTdcTest): class TestTdc(ControllerCase, GenericTdcTest):
def setUp(self): def setUp(self):
tdc_serial = get_from_ddb("tdc", "device") ControllerCase.setUp(self)
self.cont = Tdc(serial_dev=tdc_serial) self.start_controller("tdc")
self.cont = self.device_mgr.get("tdc")
class TestTdcSim(unittest.TestCase, GenericTdcTest): class TestTdcSim(unittest.TestCase, GenericTdcTest):
@ -142,10 +143,11 @@ class TestTdcSim(unittest.TestCase, GenericTdcTest):
self.cont = TdcSim() self.cont = TdcSim()
class TestTpz(unittest.TestCase, GenericTpzTest): class TestTpz(ControllerCase, GenericTpzTest):
def setUp(self): def setUp(self):
tpz_serial = get_from_ddb("tpz", "device") ControllerCase.setUp(self)
self.cont = Tpz(serial_dev=tpz_serial) self.start_controller("tpz")
self.cont = self.device_mgr.get("tpz")
class TestTpzSim(unittest.TestCase, GenericTpzTest): class TestTpzSim(unittest.TestCase, GenericTpzTest):