You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

125 lines
5.2 KiB

2 years ago
  1. import re
  2. import signal
  3. import subprocess
  4. import sys
  5. import unittest
  6. import os
  7. import time
  8. import wx
  9. from gooey.gui import events, processor
  10. from gooey.gui.pubsub import pub
  11. from gooey.gui.processor import ProcessController
  12. class TestProcessor(unittest.TestCase):
  13. def test_extract_progress(self):
  14. # should pull out a number based on the supplied
  15. # regex and expression
  16. processor = ProcessController(r"^progress: (\d+)%$", None, False, 'utf-8')
  17. self.assertEqual(processor._extract_progress(b'progress: 50%'), 50)
  18. processor = ProcessController(r"total: (\d+)%$", None, False, 'utf-8')
  19. self.assertEqual(processor._extract_progress(b'my cool total: 100%'), 100)
  20. def test_extract_progress_returns_none_if_no_regex_supplied(self):
  21. processor = ProcessController(None, None, False, 'utf-8')
  22. self.assertIsNone(processor._extract_progress(b'Total progress: 100%'))
  23. def test_extract_progress_returns_none_if_no_match_found(self):
  24. processor = ProcessController(r'(\d+)%$', None, False, 'utf-8')
  25. self.assertIsNone(processor._extract_progress(b'No match in dis string'))
  26. def test_eval_progress(self):
  27. # given a match in the string, should eval the result
  28. regex = r'(\d+)/(\d+)$'
  29. processor = ProcessController(regex, r'x[0] / x[1]', False,False, 'utf-8')
  30. match = re.search(regex, '50/50')
  31. self.assertEqual(processor._eval_progress(match), 1.0)
  32. def test_eval_progress_returns_none_on_failure(self):
  33. # given a match in the string, should eval the result
  34. regex = r'(\d+)/(\d+)$'
  35. processor = ProcessController(regex, r'x[0] *^/* x[1]', False, False,'utf-8')
  36. match = re.search(regex, '50/50')
  37. self.assertIsNone(processor._eval_progress(match))
  38. def test_all_interrupts_halt_process(self):
  39. """
  40. TODO: These tests are hella flaky. I'm confident that the feature works. However, getting
  41. signals, subprocesses and unittest to all play together reliably is proving tricky. It
  42. primarily seems to come down to how long the time.sleep() is before sending the shutdown
  43. signal.
  44. """
  45. cmd = 'python ' + os.path.join(os.getcwd(), 'files', 'infinite_loop.py')
  46. try:
  47. import _winapi
  48. signals = [signal.SIGTERM, signal.CTRL_BREAK_EVENT, signal.CTRL_C_EVENT]
  49. except ModuleNotFoundError:
  50. signals = [signal.SIGTERM, signal.SIGINT]
  51. try:
  52. for sig in signals:
  53. print('sig', sig)
  54. processor = ProcessController(None, None, False, 'utf-8', True, shutdown_signal=sig)
  55. processor.run(cmd)
  56. self.assertTrue(processor.running())
  57. # super-duper important sleep so that the
  58. # signal is actually received by the child process
  59. # see: https://stackoverflow.com/questions/32023719/how-to-simulate-a-terminal-ctrl-c-event-from-a-unittest
  60. time.sleep(1)
  61. processor.stop()
  62. max_wait = time.time() + 4
  63. while processor.running() and time.time() < max_wait:
  64. time.sleep(0.1)
  65. self.assertFalse(processor.running())
  66. except KeyboardInterrupt:
  67. pass
  68. def test_ignore_sigint_family_signals(self):
  69. try:
  70. import _winapi
  71. signals = [signal.CTRL_BREAK_EVENT, signal.CTRL_C_EVENT]
  72. programs = ['ignore_break.py', 'ignore_interrupt.py']
  73. except ModuleNotFoundError:
  74. signals = [signal.SIGINT]
  75. programs = ['ignore_interrupt.py']
  76. for program, sig in zip(programs, signals):
  77. cmd = sys.executable + ' ' + os.path.join(os.getcwd(), 'files', program)
  78. process = processor = ProcessController(None, None, False, 'utf-8', True, shutdown_signal=sig, testmode=True)
  79. process.run(cmd)
  80. # super-duper important sleep so that the
  81. # signal is actually received by the child process
  82. # see: https://stackoverflow.com/questions/32023719/how-to-simulate-a-terminal-ctrl-c-event-from-a-unittest
  83. time.sleep(1)
  84. process.send_shutdown_signal()
  85. # wait to give stdout enough time to write
  86. time.sleep(1)
  87. # now our signal should have been received, but rejected.
  88. self.assertTrue(processor.running())
  89. # so we sigterm to actually shut down the process.
  90. process._send_signal(signal.SIGTERM)
  91. # sanity wait
  92. max_wait = time.time() + 2
  93. while processor.running() and time.time() < max_wait:
  94. time.sleep(0.1)
  95. # now we should be shut down due to killing the process.
  96. self.assertFalse(processor.running())
  97. # and we'll see in the stdout out from the process that our
  98. # interrupt was received
  99. output = process._process.stdout.read().decode('utf-8')
  100. self.assertIn("INTERRUPT", str(output))
  101. # but indeed ignored. It continued running and writing to stdout after
  102. # receiving the signal
  103. self.assertTrue(output.index("INTERRUPT") < len(output))