diff velib_python/test/mock_gobject.py @ 8:9c0435a617db

Import velib_python
author Daniel O'Connor <darius@dons.net.au>
date Sun, 05 Dec 2021 14:35:36 +1030
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/velib_python/test/mock_gobject.py	Sun Dec 05 14:35:36 2021 +1030
@@ -0,0 +1,153 @@
+# This module contains mock functions for some of the functionality in gobject.
+# You can use this to create unit tests on code using gobject timers without having to wait for those timer.
+# Use the patch functions to replace the original gobject functions. The timer_manager object defined here
+# allows you to set a virtual time stamp, which will invoke all timers that would normally run in the 
+# specified interval.
+
+from datetime import datetime as dt
+import time
+
+class MockTimer(object):
+	def __init__(self, start, timeout, callback, *args, **kwargs):
+		self._timeout = timeout
+		self._next = start + timeout
+		self._callback = callback
+		self._args = args
+		self._kwargs = kwargs
+
+	def run(self):
+		self._next += self._timeout
+		return self._callback(*self._args, **self._kwargs)
+
+	@property
+	def next(self):
+		return self._next
+
+
+class MockTimerManager(object):
+	def __init__(self, start_time=None):
+		self._resources = []
+		self._time = 0
+		self._id = 0
+		self._timestamp = start_time or time.time()
+
+	def add_timer(self, timeout, callback, *args, **kwargs):
+		return self._add_resource(MockTimer(self._time, timeout, callback, *args, **kwargs))
+
+	def add_idle(self, callback, *args, **kwargs):
+		return self.add_timer(self._time, callback, *args, **kwargs)
+
+	def remove_resouce(self, id):
+		for rid, rr in self._resources:
+			if rid == id:
+				self._resources.remove((rid, rr))
+				return
+		raise Exception('Resource not found: {}'.format(id))
+
+	def _add_resource(self, resource):
+		self._id += 1
+		self._resources.append((self._id, resource))
+		return self._id
+
+	def _terminate(self):
+		raise StopIteration()
+
+	@property
+	def time(self):
+		return self._time
+
+	@property
+	def datetime(self):
+		return dt.fromtimestamp(self._timestamp + self._time / 1000.0)
+
+	def run(self, interval=None):
+		'''
+		Simulate the given interval. Starting from the current (mock) time until time + interval, all timers
+		will be triggered. The timers will be triggered in chronological order. Timer removal (calling
+		source_remove or a False/None return value) and addition within the callback function is supported.
+		If interval is None or not supplied, the function will run until there are no timers left.
+		'''
+		if interval != None:
+			self.add_timer(interval, self._terminate)
+		try:
+			while True:
+				next_timer = None
+				next_id = None
+				for id,t in self._resources:
+					if next_timer == None or t.next < next_timer.next:
+						next_timer = t
+						next_id = id
+				if next_timer == None:
+					return
+				self._time = next_timer.next
+				if not next_timer.run():
+					self._resources.remove((next_id, next_timer))
+		except StopIteration:
+			self._resources.remove((next_id, next_timer))
+			pass
+
+	def reset(self):
+		self._resources = []
+		self._time = 0
+
+
+timer_manager = MockTimerManager()
+
+
+def idle_add(callback, *args, **kwargs):
+	return timer_manager.add_idle(callback, *args, **kwargs)
+
+
+def timeout_add(timeout, callback, *args, **kwargs):
+	return timer_manager.add_timer(timeout, callback, *args, **kwargs)
+
+
+def timeout_add_seconds(timeout, callback, *args, **kwargs):
+	return timeout_add(timeout * 1000, callback, *args, **kwargs)
+
+
+class datetime(object):
+	@staticmethod
+	def now():
+		return timer_manager.datetime
+
+	@staticmethod
+	def strptime(*args, **kwargs):
+		return dt.strptime(*args, **kwargs)
+
+
+def source_remove(id):
+	timer_manager.remove_resouce(id)
+
+
+def test_function(m, name):
+	print(m.time, m.datetime, name)
+	return True
+
+
+def patch_gobject(dest):
+	'''
+	Use this function to replace the original gobject/GLib functions with the
+	mocked versions in this file.  Suppose your source files being tested uses
+	'from gi.repository import GLib' and the unit test uses 'import tested' you
+	should call path(tested.GLib).
+	'''
+	dest.timeout_add = timeout_add
+	dest.timeout_add_seconds = timeout_add_seconds
+	dest.idle_add = idle_add
+	dest.source_remove = source_remove
+
+
+def patch_datetime(dest):
+	dest.datetime = datetime
+
+
+if __name__ == '__main__':
+	m = MockTimerManager()
+	id1 = m.add_timer(100, test_function, m, 'F1')
+	id2 = m.add_timer(30, test_function, m, 'F2')
+	m.run(5000)
+	m.remove_resouce(id1)
+	m.run(2000)
+	m.remove_resouce(id2)
+	m.run(2000)