tests: Switch to pytest, remove devnull hack

This commit is contained in:
jaseg 2022-04-17 22:35:13 +02:00
parent 0cda09c628
commit 7459d3df04

View file

@ -16,58 +16,28 @@ import time
import io import io
import platform import platform
import ctypes import ctypes
from concurrent.futures import Future
import mpv import mpv
from xvfbwrapper import Xvfb from xvfbwrapper import Xvfb
# stdout magic to suppress useless libmpv warning messages in unittest output
# See https://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/
@contextmanager
def devnull_libmpv():
""" Redirect libmpv stdout into /dev/null while still allowing python to print to stdout as usual """
if platform.system() != 'Linux':
# This is only a linux-specific convenience function.
yield
return
libc = ctypes.CDLL("libc.so.6")
stderr_fd, stdout_fd = sys.stderr.fileno(), sys.stdout.fileno()
sys.stderr.flush()
sys.stdout.flush()
libc.fflush(None)
# Preserve a copy so python can continue printing
stderr_copy, stdout_copy = os.dup(stderr_fd), os.dup(stdout_fd)
sys.stderr = io.TextIOWrapper(open(stderr_copy, 'wb', closefd=False), write_through=True)
sys.stdout = io.TextIOWrapper(open(stdout_copy, 'wb', closefd=False))
with open(os.devnull, 'w') as devnull:
os.dup2(devnull.fileno(), stderr_fd) # closes old stderr
os.dup2(devnull.fileno(), stdout_fd) # closes old stdout
yield
sys.stderr.flush()
sys.stdout.flush()
libc.fflush(None)
os.dup2(stderr_copy, stderr_fd)
os.dup2(stdout_copy, stdout_fd)
os.close(stderr_copy)
os.close(stdout_copy)
sys.stderr = io.TextIOWrapper(open(stderr_fd, 'wb', closefd=False), write_through=True)
sys.stdout = io.TextIOWrapper(open(stdout_fd, 'wb', closefd=False))
TESTVID = os.path.join(os.path.dirname(__file__), 'test.webm') TESTVID = os.path.join(os.path.dirname(__file__), 'test.webm')
TESTSRT = os.path.join(os.path.dirname(__file__), 'sub_test.srt') TESTSRT = os.path.join(os.path.dirname(__file__), 'sub_test.srt')
MPV_ERRORS = [ l(ec) for ec, l in mpv.ErrorCode.EXCEPTION_DICT.items() if l ] MPV_ERRORS = [ l(ec) for ec, l in mpv.ErrorCode.EXCEPTION_DICT.items() if l ]
def timed_print():
start_time = time.time()
def do_print(level, prefix, text):
td = time.time() - start_time
print('{:.3f} [{}] {}: {}'.format(td, level, prefix, text), flush=True)
class MpvTestCase(unittest.TestCase): class MpvTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.disp = Xvfb() self.disp = Xvfb()
self.disp.start() self.disp.start()
self.m = mpv.MPV(vo='x11') self.m = mpv.MPV(vo='x11', loglevel='debug', log_handler=timed_print())
def tearDown(self): def tearDown(self):
self.m.terminate() self.m.terminate()
@ -85,7 +55,6 @@ class TestProperties(MpvTestCase):
else: else:
raise raise
@devnull_libmpv()
def test_read(self): def test_read(self):
self.m.loop = 'inf' self.m.loop = 'inf'
self.m.play(TESTVID) self.m.play(TESTVID)
@ -99,7 +68,6 @@ class TestProperties(MpvTestCase):
mpv.ErrorCode.PROPERTY_NOT_FOUND]): mpv.ErrorCode.PROPERTY_NOT_FOUND]):
getattr(self.m, name) getattr(self.m, name)
@devnull_libmpv()
def test_write(self): def test_write(self):
self.m.loop = 'inf' self.m.loop = 'inf'
self.m.play(TESTVID) self.m.play(TESTVID)
@ -222,7 +190,6 @@ class TestProperties(MpvTestCase):
# See comment in test_property_decoding_invalid_utf8 # See comment in test_property_decoding_invalid_utf8
self.m.osd.alang self.m.osd.alang
@devnull_libmpv()
def test_option_read(self): def test_option_read(self):
self.m.loop = 'inf' self.m.loop = 'inf'
self.m.play(TESTVID) self.m.play(TESTVID)
@ -239,7 +206,6 @@ class TestProperties(MpvTestCase):
class ObservePropertyTest(MpvTestCase): class ObservePropertyTest(MpvTestCase):
@devnull_libmpv()
def test_observe_property(self): def test_observe_property(self):
handler = mock.Mock() handler = mock.Mock()
@ -256,7 +222,6 @@ class ObservePropertyTest(MpvTestCase):
m.terminate() # needed for synchronization of event thread m.terminate() # needed for synchronization of event thread
handler.assert_has_calls([mock.call('vid', 'auto')]) handler.assert_has_calls([mock.call('vid', 'auto')])
@devnull_libmpv()
def test_property_observer_decorator(self): def test_property_observer_decorator(self):
handler = mock.Mock() handler = mock.Mock()
@ -454,7 +419,6 @@ class KeyBindingTest(MpvTestCase):
handler2.assert_has_calls([ mock.call() ]) handler2.assert_has_calls([ mock.call() ])
class TestStreams(unittest.TestCase): class TestStreams(unittest.TestCase):
@devnull_libmpv()
def test_python_stream(self): def test_python_stream(self):
handler = mock.Mock() handler = mock.Mock()
@ -589,27 +553,28 @@ class TestLifecycle(unittest.TestCase):
m.terminate() m.terminate()
handler.assert_not_called() handler.assert_not_called()
@devnull_libmpv()
def test_wait_for_property_negative(self): def test_wait_for_property_negative(self):
self.disp = Xvfb() self.disp = Xvfb()
self.disp.start() self.disp.start()
m = mpv.MPV() m = mpv.MPV()
m.play(TESTVID) m.play(TESTVID)
result = Future()
def run(): def run():
nonlocal self nonlocal self
result.set_running_or_notify_cancel()
try: try:
m.wait_for_property('mute') m.wait_for_property('mute')
self.fail() result.set_result(False)
except mpv.ShutdownError: except mpv.ShutdownError:
pass result.set_result(True)
t = threading.Thread(target=run, daemon=True) t = threading.Thread(target=run, daemon=True)
t.start() t.start()
time.sleep(1) time.sleep(1)
m.terminate() m.terminate()
t.join() t.join()
self.disp.stop() self.disp.stop()
assert result.result()
@devnull_libmpv()
def test_wait_for_property_positive(self): def test_wait_for_property_positive(self):
self.disp = Xvfb() self.disp = Xvfb()
self.disp.start() self.disp.start()
@ -629,28 +594,29 @@ class TestLifecycle(unittest.TestCase):
handler.assert_called() handler.assert_called()
self.disp.stop() self.disp.stop()
@devnull_libmpv()
def test_wait_for_event(self): def test_wait_for_event(self):
self.disp = Xvfb() self.disp = Xvfb()
self.disp.start() self.disp.start()
handler = mock.Mock() handler = mock.Mock()
m = mpv.MPV() m = mpv.MPV()
m.play(TESTVID) m.play(TESTVID)
result = Future()
def run(): def run():
nonlocal self nonlocal self
result.set_running_or_notify_cancel()
try: try:
m.wait_for_event('seek') m.wait_for_event('seek')
self.fail() result.set_result(False)
except mpv.ShutdownError: except mpv.ShutdownError:
pass result.set_result(True)
t = threading.Thread(target=run, daemon=True) t = threading.Thread(target=run, daemon=True)
t.start() t.start()
time.sleep(1) time.sleep(1)
m.terminate() m.terminate()
t.join() t.join()
self.disp.stop() self.disp.stop()
assert result.result()
@devnull_libmpv()
def test_wait_for_property_shutdown(self): def test_wait_for_property_shutdown(self):
self.disp = Xvfb() self.disp = Xvfb()
self.disp.start() self.disp.start()
@ -664,11 +630,9 @@ class TestLifecycle(unittest.TestCase):
m.terminate() m.terminate()
self.disp.stop() self.disp.stop()
@devnull_libmpv()
def test_wait_for_event_shutdown(self): def test_wait_for_event_shutdown(self):
self.disp = Xvfb() self.disp = Xvfb()
self.disp.start() self.disp.start()
handler = mock.Mock()
m = mpv.MPV() m = mpv.MPV()
m.play(TESTVID) m.play(TESTVID)
with self.assertRaises(mpv.ShutdownError): with self.assertRaises(mpv.ShutdownError):