| |
@@ -10,6 +10,7 @@
|
| |
from os.path import dirname
|
| |
import re
|
| |
import sched
|
| |
+ import signal
|
| |
from random import randint
|
| |
from shutil import copyfile
|
| |
|
| |
@@ -83,6 +84,16 @@
|
| |
return stop_condition
|
| |
|
| |
|
| |
+ def make_trapped_stop_condition(stop_condition):
|
| |
+ def trapped_stop_condition(message):
|
| |
+ if events.EventTrap.get_exception():
|
| |
+ return True
|
| |
+
|
| |
+ return stop_condition(message)
|
| |
+
|
| |
+ return trapped_stop_condition
|
| |
+
|
| |
+
|
| |
def main(initial_messages, stop_condition):
|
| |
""" Run the consumer until some condition is met.
|
| |
|
| |
@@ -108,18 +119,42 @@
|
| |
|
| |
consumers = [module_build_service.scheduler.consumer.MBSConsumer]
|
| |
|
| |
- # Note that the hub we kick off here cannot send any message. You
|
| |
- # should use fedmsg.publish(...) still for that.
|
| |
- moksha.hub.main(
|
| |
- # Pass in our config dict
|
| |
- options=config,
|
| |
- # Only run the specified consumers if any are so specified.
|
| |
- consumers=consumers,
|
| |
- # Do not run default producers.
|
| |
- producers=[],
|
| |
- # Tell moksha to quiet its logging.
|
| |
- framework=False,
|
| |
- )
|
| |
+ old_run = moksha.hub.reactor.reactor.run
|
| |
+ old_sigint_handler = signal.getsignal(signal.SIGINT)
|
| |
+
|
| |
+ def trap_sigint(self, *args):
|
| |
+ try:
|
| |
+ raise KeyboardInterrupt()
|
| |
+ except KeyboardInterrupt as e:
|
| |
+ events.EventTrap.set_exception(e)
|
| |
+
|
| |
+ def set_signals_and_run(*args, **kwargs):
|
| |
+ signal.signal(signal.SIGINT, trap_sigint)
|
| |
+ try:
|
| |
+ old_run(*args, **kwargs)
|
| |
+ finally:
|
| |
+ signal.signal(signal.SIGINT, old_sigint_handler)
|
| |
+
|
| |
+ with patch('moksha.hub.reactor.reactor.run', set_signals_and_run):
|
| |
+ # The events.EventTrap context handler allows us to detect exceptions
|
| |
+ # in event handlers and re-raise them here so that tests fail usefully
|
| |
+ # rather than just hang.
|
| |
+ with events.EventTrap() as trap:
|
| |
+ # Note that the hub we kick off here cannot send any message. You
|
| |
+ # should use fedmsg.publish(...) still for that.
|
| |
+ moksha.hub.main(
|
| |
+ # Pass in our config dict
|
| |
+ options=config,
|
| |
+ # Only run the specified consumers if any are so specified.
|
| |
+ consumers=consumers,
|
| |
+ # Do not run default producers.
|
| |
+ producers=[],
|
| |
+ # Tell moksha to quiet its logging.
|
| |
+ framework=False,
|
| |
+ )
|
| |
+
|
| |
+ if trap.exception:
|
| |
+ raise trap.exception
|
| |
|
| |
|
| |
class FakeSCM(object):
|
| |
@@ -388,9 +423,10 @@
|
| |
class BaseTestBuild:
|
| |
|
| |
def run_scheduler(self, msgs=None, stop_condition=None):
|
| |
+ stop_condition = stop_condition or make_simple_stop_condition()
|
| |
main(
|
| |
msgs or [],
|
| |
- stop_condition or make_simple_stop_condition()
|
| |
+ make_trapped_stop_condition(stop_condition)
|
| |
)
|
| |
|
| |
|
| |
This PR has two related patches to fix problems with tests/test_build. The first one makes exceptions occurred during test_build not hang the tests - this made the intermittent failures from the problem fixed in #1670 particularly annoying. The second makes Control-C work during and after the test_build tests.
test_build: exit rather than hanging on event handler exception
Event handlers decorated with @celery_app_task don't raise an exception - they just log the exception, leaving the Moksha hub running. This meant that any failures in test_build would result in the test suite hanging rather than failing usefully.
We solve this by adding a new class EventTrap which acts as a context manager. Any exceptions that occur in event handlers are set on the current EventTrap, and the test_build tests re-raise the exception.
test_build: leave Control-C working
Two problems occurred with the moksha/twisted handling of SIGINT:
Fix that by using mock.patch to override moksha's signal handler with our own signal handler that stores the KeyboardInterrupt in the current EventTrap, and restores the default signal handler after the loop ends.
Note that since the KeyboardInterrupt is always handled in the main thread, we don't get a useful backtrace from the child thread.