Merge pull request #1189 from forslund/test/event-scheduler

Test/event scheduler
pull/1206/head
Michael Nguyen 2017-11-07 16:17:54 -06:00 committed by GitHub
commit 15ea88a8ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 139 additions and 24 deletions

View File

@ -25,6 +25,14 @@ from mycroft.util.log import LOG
class EventScheduler(Thread):
def __init__(self, emitter, schedule_file='/opt/mycroft/schedule.json'):
"""
Create an event scheduler thread. Will send messages at a
predetermined time to the registered targets.
Args:
emitter: event emitter to use to send messages
schedule_file: File to store pending events to on shutdown
"""
super(EventScheduler, self).__init__()
self.events = {}
self.emitter = emitter
@ -51,11 +59,12 @@ class EventScheduler(Thread):
Load json data with active events from json file.
"""
if isfile(self.schedule_file):
json_data = {}
with open(self.schedule_file) as f:
try:
json_data = json.load(f)
except Exception as e:
LOG.error(e)
LOG.error(e.message)
current_time = time.time()
for key in json_data:
event_list = json_data[key]
@ -97,31 +106,37 @@ class EventScheduler(Thread):
def run(self):
while self.isRunning:
# Remove events
self.remove_events()
# Fetch newly scheduled events
self.fetch_new_events()
# Update events
self.update_events()
# Check all events
for event in self.events:
current_time = time.time()
e = self.events[event]
# Get scheduled times that has passed
passed = [(t, r, d) for (t, r, d) in e if t <= current_time]
# and remaining times that we're still waiting for
remaining = [(t, r, d) for t, r, d in e if t > current_time]
# Trigger registered methods
for sched_time, repeat, data in passed:
self.emitter.emit(Message(event, data))
# if this is a repeated event add a new trigger time
if repeat:
remaining.append((sched_time + repeat, repeat, data))
# update list of events
self.events[event] = remaining
self.check_state()
time.sleep(0.5)
def check_state(self):
"""
Check if an event should be triggered.
"""
# Remove events
self.remove_events()
# Fetch newly scheduled events
self.fetch_new_events()
# Update events
self.update_events()
# Check all events
for event in self.events:
current_time = time.time()
e = self.events[event]
# Get scheduled times that has passed
passed = [(t, r, d) for (t, r, d) in e if t <= current_time]
# and remaining times that we're still waiting for
remaining = [(t, r, d) for t, r, d in e if t > current_time]
# Trigger registered methods
for sched_time, repeat, data in passed:
self.emitter.emit(Message(event, data))
# if this is a repeated event add a new trigger time
if repeat:
remaining.append((sched_time + repeat, repeat, data))
# update list of events
self.events[event] = remaining
def schedule_event(self, event, sched_time, repeat=None, data=None):
""" Send event to thread using thread safe queue. """
data = data or {}

View File

@ -0,0 +1,100 @@
"""
Test cases regarding the event scheduler.
"""
import unittest
import mock
import time
from mycroft.skills.event_scheduler import EventScheduler
class TestEventScheduler(unittest.TestCase):
@mock.patch('threading.Thread')
@mock.patch('json.load')
@mock.patch('json.dump')
@mock.patch('mycroft.skills.event_scheduler.open')
def test_create(self, mock_open, mock_json_dump, mock_load, mock_thread):
"""
Test creating and shutting down event_scheduler.
"""
mock_load.return_value = ''
mock_open.return_value = mock.MagicMock(spec=file)
emitter = mock.MagicMock()
es = EventScheduler(emitter)
es.shutdown()
self.assertEquals(mock_json_dump.call_args[0][0], {})
@mock.patch('threading.Thread')
@mock.patch('json.load')
@mock.patch('json.dump')
@mock.patch('mycroft.skills.event_scheduler.open')
def test_add_remove(self, mock_open, mock_json_dump,
mock_load, mock_thread):
"""
Test add an event and then remove it.
"""
# Thread start is mocked so will not actually run the thread loop
mock_load.return_value = ''
mock_open.return_value = mock.MagicMock(spec=file)
emitter = mock.MagicMock()
es = EventScheduler(emitter)
# 900000000000 should be in the future for a long time
es.schedule_event('test', 90000000000, None)
es.schedule_event('test-2', 90000000000, None)
es.check_state() # run one cycle
self.assertTrue('test' in es.events)
self.assertTrue('test-2' in es.events)
es.remove_event('test')
es.check_state() # run one cycle
self.assertTrue('test' not in es.events)
self.assertTrue('test-2' in es.events)
es.shutdown()
@mock.patch('threading.Thread')
@mock.patch('json.load')
@mock.patch('json.dump')
@mock.patch('mycroft.skills.event_scheduler.open')
def test_save(self, mock_open, mock_dump, mock_load, mock_thread):
"""
Test save functionality.
"""
mock_load.return_value = ''
mock_open.return_value = mock.MagicMock(spec=file)
emitter = mock.MagicMock()
es = EventScheduler(emitter)
# 900000000000 should be in the future for a long time
es.schedule_event('test', 900000000000, None)
es.schedule_event('test-repeat', 910000000000, 60)
es.check_state()
es.shutdown()
# Make sure the dump method wasn't called with test-repeat
self.assertEquals(mock_dump.call_args[0][0],
{'test': [(900000000000, None, {})]})
@mock.patch('threading.Thread')
@mock.patch('json.load')
@mock.patch('json.dump')
@mock.patch('mycroft.skills.event_scheduler.open')
def test_send_event(self, mock_open, mock_dump, mock_load, mock_thread):
"""
Test save functionality.
"""
mock_load.return_value = ''
mock_open.return_value = mock.MagicMock(spec=file)
emitter = mock.MagicMock()
es = EventScheduler(emitter)
# 0 should be in the future for a long time
es.schedule_event('test', time.time(), None)
es.check_state()
self.assertEquals(emitter.emit.call_args[0][0].type, 'test')
self.assertEquals(emitter.emit.call_args[0][0].data, {})
es.shutdown()