conftest.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. from py.xml import html
  2. import pytest
  3. import subprocess
  4. import xmlrpc
  5. import time
  6. stderr = list()
  7. stdout = list()
  8. apperrs = list()
  9. def pytest_html_results_summary(prefix, summary, postfix):
  10. prefix.extend([html.h3("stderr")])
  11. prefix.extend([html.pre("".join([line for line in stderr]))])
  12. prefix.extend([html.h3("stdout")])
  13. prefix.extend([html.pre("".join([line for line in stdout]))])
  14. prefix.extend([html.h3("App errors")])
  15. prefix.extend([html.pre("".join([line for line in apperrs]))])
  16. @pytest.fixture
  17. def process_alive(app, process, process_communicate):
  18. def func():
  19. try:
  20. global apperrs
  21. # We need to call something on the rpc server
  22. # the process might still be running but frozen or only almost dead
  23. apperrs += app.getErrors()
  24. except Exception as e:
  25. # Process probably died
  26. pass
  27. finally:
  28. process_communicate()
  29. exit_code = process.poll()
  30. if exit_code is not None:
  31. print(f"Process exited with {exit_code}")
  32. return exit_code is None
  33. yield func
  34. @pytest.fixture
  35. def process_communicate(process, report_summary):
  36. def func():
  37. try:
  38. output_b, errors_b = process.communicate(timeout=0.1)
  39. output = output_b.decode("utf-8")
  40. errors = errors_b.decode("utf-8")
  41. print(errors)
  42. print(output)
  43. report_summary[0].append(output)
  44. report_summary[1].append(errors)
  45. except subprocess.TimeoutExpired:
  46. pass
  47. yield func
  48. @pytest.fixture
  49. def report_summary():
  50. yield stdout, stderr
  51. @pytest.fixture
  52. def process():
  53. filenames = [
  54. "./output/bin/qfield",
  55. "./output/bin/Release/qfield.exe",
  56. "./output/bin/Debug/qfield.exe",
  57. "./output/bin/qfield.app/qfield.exe",
  58. "./output/bin/qfield.app/Contents/MacOS/qfield",
  59. ]
  60. for filename in filenames:
  61. try:
  62. process = subprocess.Popen(
  63. filename, stdout=subprocess.PIPE, stderr=subprocess.PIPE
  64. )
  65. yield process
  66. break
  67. except FileNotFoundError:
  68. pass
  69. else:
  70. assert False, f"No qfield executable found in {filenames}"
  71. @pytest.fixture
  72. def app(process, process_communicate):
  73. """
  74. Starts a qfield process and connects an xmlrpc client to it.
  75. Returns the xmlrpc client that can send commands to the running process.
  76. Will wait up to 30 seconds for the process to start and return as soon
  77. as either the process is started and QML initialized, the process exits
  78. or the timeout occurs.
  79. Also makes sure it cleans up properly after running the app.
  80. """
  81. app = xmlrpc.client.ServerProxy("http://localhost:9000")
  82. app.process = process
  83. start = time.time()
  84. while True:
  85. try:
  86. exit_code = process.poll()
  87. if exit_code is not None:
  88. process_communicate(process)
  89. assert exit_code is None
  90. app.existsAndVisible("mainWindow")
  91. yield app
  92. break
  93. except (ConnectionRefusedError, OSError) as e:
  94. if time.time() - start > 30:
  95. process_communicate(process)
  96. assert False # Could not start app after 30 seconds
  97. print(str(e))
  98. time.sleep(0.2)
  99. except Exception as e:
  100. process_communicate(process)
  101. assert False # Unexcpected exception while starting up app
  102. try:
  103. app.quit()
  104. app.quit()
  105. except Exception:
  106. print("Exception while trying to exit app. The process probably died.")
  107. process_communicate()
  108. raise
  109. timeout = 5
  110. try:
  111. process.wait(timeout)
  112. except subprocess.TimeoutExpired:
  113. print(f"Process did not quit after {timeout} second. Killing it.")
  114. process.kill()
  115. process_communicate()