mite 2.0.2__tar.gz → 2.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. {mite-2.0.2 → mite-2.1.0}/PKG-INFO +1 -1
  2. {mite-2.0.2 → mite-2.1.0}/docker_compose.yml +0 -1
  3. {mite-2.0.2 → mite-2.1.0}/docker_compose_monitoring.yml +0 -1
  4. {mite-2.0.2 → mite-2.1.0}/mite/__main__.py +39 -35
  5. {mite-2.0.2 → mite-2.1.0}/mite/cli/test.py +55 -1
  6. {mite-2.0.2 → mite-2.1.0}/mite/logoutput.py +65 -8
  7. {mite-2.0.2 → mite-2.1.0}/mite.egg-info/PKG-INFO +1 -1
  8. {mite-2.0.2 → mite-2.1.0}/mite_http/__init__.py +1 -0
  9. {mite-2.0.2 → mite-2.1.0}/.dockerignore +0 -0
  10. {mite-2.0.2 → mite-2.1.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  11. {mite-2.0.2 → mite-2.1.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  12. {mite-2.0.2 → mite-2.1.0}/.github/pull_request_template.md +0 -0
  13. {mite-2.0.2 → mite-2.1.0}/.gitignore +0 -0
  14. {mite-2.0.2 → mite-2.1.0}/.isort.cfg +0 -0
  15. {mite-2.0.2 → mite-2.1.0}/.pre-commit-config.yaml +0 -0
  16. {mite-2.0.2 → mite-2.1.0}/.travis.yml +0 -0
  17. {mite-2.0.2 → mite-2.1.0}/CODE_OF_CONDUCT.md +0 -0
  18. {mite-2.0.2 → mite-2.1.0}/CONTRIBUTING.md +0 -0
  19. {mite-2.0.2 → mite-2.1.0}/DEV.md +0 -0
  20. {mite-2.0.2 → mite-2.1.0}/Dockerfile +0 -0
  21. {mite-2.0.2 → mite-2.1.0}/Dockerfile.perftest +0 -0
  22. {mite-2.0.2 → mite-2.1.0}/LICENSE +0 -0
  23. {mite-2.0.2 → mite-2.1.0}/MANIFEST.in +0 -0
  24. {mite-2.0.2 → mite-2.1.0}/README.md +0 -0
  25. {mite-2.0.2 → mite-2.1.0}/cd-scripts/cdBuild-1.sh +0 -0
  26. {mite-2.0.2 → mite-2.1.0}/cd-scripts/cdRelease.py +0 -0
  27. {mite-2.0.2 → mite-2.1.0}/cd-scripts/cdTagRelease.sh +0 -0
  28. {mite-2.0.2 → mite-2.1.0}/cd-scripts/runTests.sh +0 -0
  29. {mite-2.0.2 → mite-2.1.0}/cd.yml +0 -0
  30. {mite-2.0.2 → mite-2.1.0}/dev-requirements.txt +0 -0
  31. {mite-2.0.2 → mite-2.1.0}/docs/Makefile +0 -0
  32. {mite-2.0.2 → mite-2.1.0}/docs/components.rst +0 -0
  33. {mite-2.0.2 → mite-2.1.0}/docs/conf.py +0 -0
  34. {mite-2.0.2 → mite-2.1.0}/docs/config.rst +0 -0
  35. {mite-2.0.2 → mite-2.1.0}/docs/design-deployment.rst +0 -0
  36. {mite-2.0.2 → mite-2.1.0}/docs/index.rst +0 -0
  37. {mite-2.0.2 → mite-2.1.0}/docs/journeys.rst +0 -0
  38. {mite-2.0.2 → mite-2.1.0}/docs/notes.txt +0 -0
  39. {mite-2.0.2 → mite-2.1.0}/docs/prometheus.rst +0 -0
  40. {mite-2.0.2 → mite-2.1.0}/docs/stats.rst +0 -0
  41. {mite-2.0.2 → mite-2.1.0}/docs/volume-model.rst +0 -0
  42. {mite-2.0.2 → mite-2.1.0}/grafana/Dockerfile +0 -0
  43. {mite-2.0.2 → mite-2.1.0}/grafana/config.ini +0 -0
  44. {mite-2.0.2 → mite-2.1.0}/grafana/provisioning/dashboards/dashboards.yml +0 -0
  45. {mite-2.0.2 → mite-2.1.0}/grafana/provisioning/dashboards/grafana_dashboard_template.json +0 -0
  46. {mite-2.0.2 → mite-2.1.0}/grafana/provisioning/dashboards/mite_docker.json +0 -0
  47. {mite-2.0.2 → mite-2.1.0}/grafana/provisioning/datasources/datasources.yml +0 -0
  48. {mite-2.0.2 → mite-2.1.0}/local/Makefile +0 -0
  49. {mite-2.0.2 → mite-2.1.0}/local/README.md +0 -0
  50. {mite-2.0.2 → mite-2.1.0}/local/demo.py +0 -0
  51. {mite-2.0.2 → mite-2.1.0}/local/run_mite.sh +0 -0
  52. {mite-2.0.2 → mite-2.1.0}/mite/__init__.py +0 -0
  53. {mite-2.0.2 → mite-2.1.0}/mite/cli/__init__.py +0 -0
  54. {mite-2.0.2 → mite-2.1.0}/mite/cli/cat.py +0 -0
  55. {mite-2.0.2 → mite-2.1.0}/mite/cli/collector.py +0 -0
  56. {mite-2.0.2 → mite-2.1.0}/mite/cli/common.py +0 -0
  57. {mite-2.0.2 → mite-2.1.0}/mite/cli/duplicator.py +0 -0
  58. {mite-2.0.2 → mite-2.1.0}/mite/cli/receiver.py +0 -0
  59. {mite-2.0.2 → mite-2.1.0}/mite/cli/stats.py +0 -0
  60. {mite-2.0.2 → mite-2.1.0}/mite/collector.py +0 -0
  61. {mite-2.0.2 → mite-2.1.0}/mite/config.py +0 -0
  62. {mite-2.0.2 → mite-2.1.0}/mite/context.py +0 -0
  63. {mite-2.0.2 → mite-2.1.0}/mite/controller.py +0 -0
  64. {mite-2.0.2 → mite-2.1.0}/mite/datapools.py +0 -0
  65. {mite-2.0.2 → mite-2.1.0}/mite/example.py +0 -0
  66. {mite-2.0.2 → mite-2.1.0}/mite/exceptions.py +0 -0
  67. {mite-2.0.2 → mite-2.1.0}/mite/har_to_mite.py +0 -0
  68. {mite-2.0.2 → mite-2.1.0}/mite/nanomsg.py +0 -0
  69. {mite-2.0.2 → mite-2.1.0}/mite/recorder.py +0 -0
  70. {mite-2.0.2 → mite-2.1.0}/mite/runner.py +0 -0
  71. {mite-2.0.2 → mite-2.1.0}/mite/scenario.py +0 -0
  72. {mite-2.0.2 → mite-2.1.0}/mite/stats.py +0 -0
  73. {mite-2.0.2 → mite-2.1.0}/mite/test.py +0 -0
  74. {mite-2.0.2 → mite-2.1.0}/mite/utils.py +0 -0
  75. {mite-2.0.2 → mite-2.1.0}/mite/volume_model.py +0 -0
  76. {mite-2.0.2 → mite-2.1.0}/mite/web/__init__.py +0 -0
  77. {mite-2.0.2 → mite-2.1.0}/mite/web/prometheus.py +0 -0
  78. {mite-2.0.2 → mite-2.1.0}/mite/zmq.py +0 -0
  79. {mite-2.0.2 → mite-2.1.0}/mite.egg-info/SOURCES.txt +0 -0
  80. {mite-2.0.2 → mite-2.1.0}/mite.egg-info/dependency_links.txt +0 -0
  81. {mite-2.0.2 → mite-2.1.0}/mite.egg-info/entry_points.txt +0 -0
  82. {mite-2.0.2 → mite-2.1.0}/mite.egg-info/requires.txt +0 -0
  83. {mite-2.0.2 → mite-2.1.0}/mite.egg-info/top_level.txt +0 -0
  84. {mite-2.0.2 → mite-2.1.0}/mite_amqp/__init__.py +0 -0
  85. {mite-2.0.2 → mite-2.1.0}/mite_browser/__init__.py +0 -0
  86. {mite-2.0.2 → mite-2.1.0}/mite_finagle/README.md +0 -0
  87. {mite-2.0.2 → mite-2.1.0}/mite_finagle/__init__.py +0 -0
  88. {mite-2.0.2 → mite-2.1.0}/mite_finagle/mux.py +0 -0
  89. {mite-2.0.2 → mite-2.1.0}/mite_finagle/stats.py +0 -0
  90. {mite-2.0.2 → mite-2.1.0}/mite_finagle/tests/__init__.py +0 -0
  91. {mite-2.0.2 → mite-2.1.0}/mite_finagle/tests/conftest.py +0 -0
  92. {mite-2.0.2 → mite-2.1.0}/mite_finagle/tests/foo_service/Foo-remote +0 -0
  93. {mite-2.0.2 → mite-2.1.0}/mite_finagle/tests/foo_service/Foo.py +0 -0
  94. {mite-2.0.2 → mite-2.1.0}/mite_finagle/tests/foo_service/__init__.py +0 -0
  95. {mite-2.0.2 → mite-2.1.0}/mite_finagle/tests/foo_service/constants.py +0 -0
  96. {mite-2.0.2 → mite-2.1.0}/mite_finagle/tests/foo_service/ttypes.py +0 -0
  97. {mite-2.0.2 → mite-2.1.0}/mite_finagle/tests/foo_service.thrift +0 -0
  98. {mite-2.0.2 → mite-2.1.0}/mite_finagle/tests/test_integration.py +0 -0
  99. {mite-2.0.2 → mite-2.1.0}/mite_finagle/tests/test_mux.py +0 -0
  100. {mite-2.0.2 → mite-2.1.0}/mite_finagle/tests/test_thrift.py +0 -0
  101. {mite-2.0.2 → mite-2.1.0}/mite_finagle/thrift.py +0 -0
  102. {mite-2.0.2 → mite-2.1.0}/mite_http/stats.py +0 -0
  103. {mite-2.0.2 → mite-2.1.0}/mite_kafka/__init__.py +0 -0
  104. {mite-2.0.2 → mite-2.1.0}/mite_kafka/kafka_test_scenario.py +0 -0
  105. {mite-2.0.2 → mite-2.1.0}/mite_kafka/stats.py +0 -0
  106. {mite-2.0.2 → mite-2.1.0}/mite_selenium/__init__.py +0 -0
  107. {mite-2.0.2 → mite-2.1.0}/mite_selenium/stats.py +0 -0
  108. {mite-2.0.2 → mite-2.1.0}/mite_websocket/__init__.py +0 -0
  109. {mite-2.0.2 → mite-2.1.0}/prometheus/Dockerfile +0 -0
  110. {mite-2.0.2 → mite-2.1.0}/prometheus/prometheus_config_docker.yml +0 -0
  111. {mite-2.0.2 → mite-2.1.0}/prometheus/prometheus_config_template.yml +0 -0
  112. {mite-2.0.2 → mite-2.1.0}/pyproject.toml +0 -0
  113. {mite-2.0.2 → mite-2.1.0}/requirements.txt +0 -0
  114. {mite-2.0.2 → mite-2.1.0}/setup.cfg +0 -0
  115. {mite-2.0.2 → mite-2.1.0}/setup.py +0 -0
  116. {mite-2.0.2 → mite-2.1.0}/test/legacy.py +0 -0
  117. {mite-2.0.2 → mite-2.1.0}/test/mocks/mock_collector.py +0 -0
  118. {mite-2.0.2 → mite-2.1.0}/test/mocks/mock_context.py +0 -0
  119. {mite-2.0.2 → mite-2.1.0}/test/mocks/mock_direct_receiver.py +0 -0
  120. {mite-2.0.2 → mite-2.1.0}/test/mocks/mock_direct_runner_transport.py +0 -0
  121. {mite-2.0.2 → mite-2.1.0}/test/mocks/mock_recorder_msg_http.py +0 -0
  122. {mite-2.0.2 → mite-2.1.0}/test/mocks/mock_selenium.py +0 -0
  123. {mite-2.0.2 → mite-2.1.0}/test/mocks/mock_sender.py +0 -0
  124. {mite-2.0.2 → mite-2.1.0}/test/perf/.gitignore +0 -0
  125. {mite-2.0.2 → mite-2.1.0}/test/perf/http_server.py +0 -0
  126. {mite-2.0.2 → mite-2.1.0}/test/perf/http_server_aiohttp.py +0 -0
  127. {mite-2.0.2 → mite-2.1.0}/test/perf/mite_perftest.py +0 -0
  128. {mite-2.0.2 → mite-2.1.0}/test/perf/perftest.py +0 -0
  129. {mite-2.0.2 → mite-2.1.0}/test/perf/run-perftest.sh +0 -0
  130. {mite-2.0.2 → mite-2.1.0}/test/perf/test-requirements.txt +0 -0
  131. {mite-2.0.2 → mite-2.1.0}/test/test.har +0 -0
  132. {mite-2.0.2 → mite-2.1.0}/test/test_collector.py +0 -0
  133. {mite-2.0.2 → mite-2.1.0}/test/test_config.py +0 -0
  134. {mite-2.0.2 → mite-2.1.0}/test/test_context.py +0 -0
  135. {mite-2.0.2 → mite-2.1.0}/test/test_controller.py +0 -0
  136. {mite-2.0.2 → mite-2.1.0}/test/test_datapools.py +0 -0
  137. {mite-2.0.2 → mite-2.1.0}/test/test_har_to_mite.py +0 -0
  138. {mite-2.0.2 → mite-2.1.0}/test/test_logging.py +0 -0
  139. {mite-2.0.2 → mite-2.1.0}/test/test_mite_amqp.py +0 -0
  140. {mite-2.0.2 → mite-2.1.0}/test/test_mite_browser.py +0 -0
  141. {mite-2.0.2 → mite-2.1.0}/test/test_mite_http.py +0 -0
  142. {mite-2.0.2 → mite-2.1.0}/test/test_mite_kafka.py +0 -0
  143. {mite-2.0.2 → mite-2.1.0}/test/test_mite_utils.py +0 -0
  144. {mite-2.0.2 → mite-2.1.0}/test/test_mite_websockets.py +0 -0
  145. {mite-2.0.2 → mite-2.1.0}/test/test_prometheus_mite.py +0 -0
  146. {mite-2.0.2 → mite-2.1.0}/test/test_recorder.py +0 -0
  147. {mite-2.0.2 → mite-2.1.0}/test/test_runner.py +0 -0
  148. {mite-2.0.2 → mite-2.1.0}/test/test_runner_tracker.py +0 -0
  149. {mite-2.0.2 → mite-2.1.0}/test/test_scenario.py +0 -0
  150. {mite-2.0.2 → mite-2.1.0}/test/test_stats.py +0 -0
  151. {mite-2.0.2 → mite-2.1.0}/test/test_volume_model.py +0 -0
  152. {mite-2.0.2 → mite-2.1.0}/test/test_webdriver.py +0 -0
  153. {mite-2.0.2 → mite-2.1.0}/test/test_work_tracker.py +0 -0
  154. {mite-2.0.2 → mite-2.1.0}/test-requirements.txt +0 -0
  155. {mite-2.0.2 → mite-2.1.0}/test.har +0 -0
  156. {mite-2.0.2 → mite-2.1.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mite
3
- Version: 2.0.2
3
+ Version: 2.1.0
4
4
  Summary: A Python Performance Testing Framework
5
5
  Home-page: https://github.com/sky-uk/mite/
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -1,4 +1,3 @@
1
- version: '3.9'
2
1
  services:
3
2
  apiserver:
4
3
  image: python:3.11-alpine3.16
@@ -1,4 +1,3 @@
1
- version: '3.9'
2
1
  services:
3
2
  prometheus:
4
3
  image: "prom/prometheus:latest"
@@ -56,41 +56,45 @@ Examples:
56
56
 
57
57
 
58
58
  Options:
59
- -h --help Show this screen
60
- --version Show version
61
- --debugging Drop into debugger (pdb) on journey error and exit. Select debugger with PYTHONBREAKPOINT and PYTHONPOSTMORTEM
62
- --memory-tracing Print heap alloc diffs every 60 seconds
63
- --log-level=LEVEL Set logger level, one of DEBUG, INFO, WARNING, ERROR, CRITICAL [default: INFO]
64
- --config=CONFIG_SPEC Set a config loader to a callable loaded via a spec [default: mite.config:default_config_loader]
65
- --add-to-config=NEW_VALUE Add a key:value to the config map, in addition to what's loaded from a file
66
- --spawn-rate=NUM_PER_SECOND Maximum spawn rate [default: 1000]
67
- --max-loop-delay=SECONDS Runner internal loop delay maximum [default: 1]
68
- --min-loop-delay=SECONDS Runner internal loop delay minimum [default: 0]
69
- --runner-max-journeys=NUMBER Max number of concurrent journeys a runner can run
70
- --controller-socket=SOCKET Controller socket [default: tcp://127.0.0.1:14301]
71
- --message-socket=SOCKET Message socket [default: tcp://127.0.0.1:14302]
72
- --collector-socket=SOCKET Socket [default: tcp://127.0.0.1:14303]
73
- --stats-in-socket=SOCKET Socket [default: tcp://127.0.0.1:14304]
74
- --stats-out-socket=SOCKET Socket [default: tcp://127.0.0.1:14305]
75
- --stats-include-processors=PROCESSORS Stats processor names to include, as a comma separated list (no spaces)
76
- --stats-exclude-processors=PROCESSORS Stats processor names to exclude, as a comma separated list (no spaces)
77
- --recorder-socket=SOCKET Socket [default: tcp://127.0.0.1:14306]
78
- --delay-start-seconds=DELAY Delay start allowing others to connect [default: 0]
79
- --volume=VOLUME Volume to run journey at [default: 1]
80
- --web-address=HOST_PORT Web bind address [default: 127.0.0.1:9301]
81
- --message-backend=BACKEND Backend to transport messages over [default: ZMQ]
82
- --exclude-working-directory By default mite puts the current directory on the python path
83
- --collector-dir=DIRECTORY Set the collectors output directory [default: collector_data]
84
- --collector-filter=SPEC Function spec to filter messages collected by the collector
85
- --collector-roll=NUM_LINES How many lines per collector output file [default: 100000]
86
- --collector-use-json Output in json format rather than msgpack
87
- --recorder-dir=DIRECTORY Set the recorders output directory [default: recorder_data]
88
- --sleep-time=SLEEP Set the second to await between each request [default: 1]
89
- --logging-webhook=URL URL of an HTTP server to log test runs to
90
- --message-processors=PROCESSORS Classes to connect to the message bus for local testing [default: mite.logoutput:HttpStatsOutput,mite.logoutput:MsgOutput]
91
- --prettify-timestamps Reformat unix timestamps to human readable dates
92
- --journey-logging Log errors on a per journey basis
93
- --max-errors-threshold=THRESHOLD Set the maximum number of errors accepted before setting exit status to 1 [default: 0]
59
+ -h --help Show this screen
60
+ --version Show version
61
+ --debugging Drop into debugger (pdb) on journey error and exit. Select debugger with PYTHONBREAKPOINT and PYTHONPOSTMORTEM
62
+ --memory-tracing Print heap alloc diffs every 60 seconds
63
+ --log-level=LEVEL Set logger level, one of DEBUG, INFO, WARNING, ERROR, CRITICAL [default: INFO]
64
+ --config=CONFIG_SPEC Set a config loader to a callable loaded via a spec [default: mite.config:default_config_loader]
65
+ --add-to-config=NEW_VALUE Add a key:value to the config map, in addition to what's loaded from a file
66
+ --spawn-rate=NUM_PER_SECOND Maximum spawn rate [default: 1000]
67
+ --max-loop-delay=SECONDS Runner internal loop delay maximum [default: 1]
68
+ --min-loop-delay=SECONDS Runner internal loop delay minimum [default: 0]
69
+ --runner-max-journeys=NUMBER Max number of concurrent journeys a runner can run
70
+ --controller-socket=SOCKET Controller socket [default: tcp://127.0.0.1:14301]
71
+ --message-socket=SOCKET Message socket [default: tcp://127.0.0.1:14302]
72
+ --collector-socket=SOCKET Socket [default: tcp://127.0.0.1:14303]
73
+ --stats-in-socket=SOCKET Socket [default: tcp://127.0.0.1:14304]
74
+ --stats-out-socket=SOCKET Socket [default: tcp://127.0.0.1:14305]
75
+ --stats-include-processors=PROCESSORS Stats processor names to include, as a comma separated list (no spaces)
76
+ --stats-exclude-processors=PROCESSORS Stats processor names to exclude, as a comma separated list (no spaces)
77
+ --recorder-socket=SOCKET Socket [default: tcp://127.0.0.1:14306]
78
+ --delay-start-seconds=DELAY Delay start allowing others to connect [default: 0]
79
+ --volume=VOLUME Volume to run journey at [default: 1]
80
+ --web-address=HOST_PORT Web bind address [default: 127.0.0.1:9301]
81
+ --message-backend=BACKEND Backend to transport messages over [default: ZMQ]
82
+ --exclude-working-directory By default mite puts the current directory on the python path
83
+ --collector-dir=DIRECTORY Set the collectors output directory [default: collector_data]
84
+ --collector-filter=SPEC Function spec to filter messages collected by the collector
85
+ --collector-roll=NUM_LINES How many lines per collector output file [default: 100000]
86
+ --collector-use-json Output in json format rather than msgpack
87
+ --recorder-dir=DIRECTORY Set the recorders output directory [default: recorder_data]
88
+ --sleep-time=SLEEP Set the second to await between each request [default: 1]
89
+ --logging-webhook=URL URL of an HTTP server to log test runs to
90
+ --message-processors=PROCESSORS Classes to connect to the message bus for local testing [default: mite.logoutput:HttpStatsOutput,mite.logoutput:MsgOutput]
91
+ --prettify-timestamps Reformat unix timestamps to human readable dates
92
+ --journey-logging Log errors on a per journey basis
93
+ --report Report stats at the end of the test
94
+ --hide-constant-logs Hide logs that are printed every 2 seconds
95
+ --max-errors-threshold=THRESHOLD Set the maximum number of errors accepted before setting exit status to 1 [default: 0]
96
+ --max-response-time-threshold=THRESHOLD Set the maximum response time accepted before setting exit status to 1 [default: 0]
97
+ --mean-response-time-threshold=THRESHOLD Set the mean response time accepted before setting exit status to 1 [default: 0]
94
98
  """
95
99
  import asyncio
96
100
  import logging
@@ -2,6 +2,7 @@ import asyncio
2
2
  import gc
3
3
  import logging
4
4
  import sys
5
+ import time
5
6
  import tracemalloc
6
7
 
7
8
  from mite.datapools import SingleRunDataPoolWrapper
@@ -142,7 +143,7 @@ def test_scenarios(test_name, opts, scenarios, config_manager):
142
143
 
143
144
  tasks = [
144
145
  loop.create_task(controller_report(controller, receiver)),
145
- loop.create_task(_create_runner(opts, transport, receiver.recieve).run())
146
+ loop.create_task(_create_runner(opts, transport, receiver.recieve).run()),
146
147
  ]
147
148
 
148
149
  if opts["--memory-tracing"]:
@@ -152,12 +153,28 @@ def test_scenarios(test_name, opts, scenarios, config_manager):
152
153
 
153
154
  loop.run_until_complete(asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED))
154
155
 
156
+ http_stats_output._scenarios_completed_time = time.time()
157
+
155
158
  # Run one last report before exiting
156
159
  controller.report(receiver.recieve)
157
160
  has_error = http_stats_output is not None and http_stats_output.error_total > int(
158
161
  opts.get("--max-errors-threshold")
159
162
  )
160
163
 
164
+ if opts.get("--max-response-time-threshold") != "0":
165
+ max_response_time = http_stats_output._resp_time_max * 1000
166
+ if max_response_time > int(opts["--max-response-time-threshold"]):
167
+ has_error = True
168
+ logging.error("Max response time exceeded: %sms", max_response_time)
169
+ if opts.get("--mean-response-time-threshold") != "0":
170
+ mean_response_time = http_stats_output.mean_resp_time * 1000
171
+ if mean_response_time > int(opts["--mean-response-time-threshold"]):
172
+ has_error = True
173
+ logging.error("Mean response time exceeded: %sms", mean_response_time)
174
+
175
+ if opts.get("--report"):
176
+ benchmark_report(opts, http_stats_output)
177
+
161
178
  # Ensure any open files get closed
162
179
  del receiver._raw_listeners
163
180
  del receiver._listeners
@@ -165,6 +182,43 @@ def test_scenarios(test_name, opts, scenarios, config_manager):
165
182
  sys.exit(int(has_error))
166
183
 
167
184
 
185
+ def human_readable_bytes(size):
186
+ for unit in ["B", "KB", "MB", "GB", "TB"]:
187
+ if size < 1024.0:
188
+ return size, unit
189
+ size /= 1024.0
190
+ return size, "PB"
191
+
192
+
193
+ def benchmark_report(opts, http_stats_output):
194
+ report_output = """
195
+
196
+ \t\tAvg\t\tMin\t\tMax
197
+ Latency\t\t{mean_resp_time:.2f}ms\t\t{min_resp_time:.2f}ms\t\t{max_resp_time:.2f}ms
198
+ Req/Sec\t\t{req_per_sec_mean:.2f}\t\t{min_req_per_sec:.2f}\t\t{max_req_per_sec:.2f}
199
+
200
+ {total_reqs} requests in {total_time:.2f}s, {data_transfer:.2f}{data_unit} data transfered
201
+ """
202
+
203
+ data_transfer, data_unit = human_readable_bytes(http_stats_output._data_transferred)
204
+
205
+ print(
206
+ report_output.format(
207
+ mean_resp_time=http_stats_output.mean_resp_time * 1000,
208
+ min_resp_time=http_stats_output._resp_time_min * 1000,
209
+ max_resp_time=http_stats_output._resp_time_max * 1000,
210
+ req_per_sec_mean=http_stats_output.req_sec_mean,
211
+ min_req_per_sec=http_stats_output._req_sec_min,
212
+ max_req_per_sec=http_stats_output._req_sec_max,
213
+ total_reqs=http_stats_output._req_total,
214
+ total_time=http_stats_output._scenarios_completed_time
215
+ - http_stats_output._init_time,
216
+ data_transfer=data_transfer,
217
+ data_unit=data_unit,
218
+ )
219
+ )
220
+
221
+
168
222
  def scenario_test_cmd(opts):
169
223
  scenario_spec = opts["SCENARIO_SPEC"]
170
224
  config_manager = _create_config_manager(opts)
@@ -42,13 +42,23 @@ class GenericStatsOutput:
42
42
  self._journey_logging = opts.get("--journey-logging", False)
43
43
  if self._journey_logging is not False:
44
44
  self._journey_logging = True
45
+ self._hide_constant_logs = opts.get("--hide-constant-logs")
45
46
  self._logger = logging.getLogger(f"{self.log_name} Stats")
47
+ self._init_time = time.time()
46
48
  self._start_t = None
47
49
  self._req_total = 0
48
50
  self._req_recent = 0
49
51
  self._error_total = 0
50
52
  self._error_recent = 0
51
53
  self._resp_time_recent = []
54
+ self._resp_time_min = 0
55
+ self._resp_time_max = 0
56
+ self._resp_time_mean = 0
57
+ self._resp_time_mean_store = []
58
+ self._req_sec_min = 0
59
+ self._req_sec_max = 0
60
+ self._scenarios_completed_time = 0
61
+ self._data_transferred = 0
52
62
  self._error_journeys = defaultdict(int)
53
63
 
54
64
  def _pct(self, percentile):
@@ -80,18 +90,20 @@ class GenericStatsOutput:
80
90
  def print_output(self, t):
81
91
  dt = t - self._start_t
82
92
  self._resp_time_recent.sort()
83
- self._logger.info(f"Total> #Reqs:{self._req_total} #Errs:{self._error_total}")
93
+ if not self._hide_constant_logs:
94
+ self._logger.info(f"Total> #Reqs:{self._req_total} #Errs:{self._error_total}")
84
95
  # only output journey logs if the --journey_logging switch is set
85
96
  if self._journey_logging:
86
97
  for k, v in self._error_journeys.items():
87
98
  self._logger.info(f"Total errors for {k} :{v}")
88
- self._logger.info(
89
- f"Last {self._period} Secs> #Reqs:{self._req_recent} #Errs:{self._error_recent} "
90
- + f"Req/S:{self._req_recent / dt:.1f} min:{self._pct(0)} "
91
- + f"25%:{self._pct(25)} 50%:{self._pct(50)} 75%:{self._pct(75)} "
92
- + f"90%:{self._pct(90)} 99%:{self._pct(99)} 99.9%:{self._pct(99.9)} "
93
- + f"max:{self._pct(100)}",
94
- )
99
+ if not self._hide_constant_logs:
100
+ self._logger.info(
101
+ f"Last {self._period} Secs> #Reqs:{self._req_recent} #Errs:{self._error_recent} "
102
+ + f"Req/S:{self._req_recent / dt:.1f} min:{self._pct(0)} "
103
+ + f"25%:{self._pct(25)} 50%:{self._pct(50)} 75%:{self._pct(75)} "
104
+ + f"90%:{self._pct(90)} 99%:{self._pct(99)} 99.9%:{self._pct(99.9)} "
105
+ + f"max:{self._pct(100)}",
106
+ )
95
107
  self._start_t = t
96
108
  del self._resp_time_recent[:]
97
109
  self._req_recent = 0
@@ -105,9 +117,32 @@ class GenericStatsOutput:
105
117
  if self._start_t is None:
106
118
  self._start_t = t
107
119
  if self._start_t + self._period < t:
120
+
121
+ if self._resp_time_recent:
122
+ if self._resp_time_min == 0:
123
+ self._resp_time_min = self.min_resp_time
124
+ else:
125
+ self._resp_time_min = min(self.min_resp_time, self._resp_time_min)
126
+
127
+ self._resp_time_max = max(self.max_resp_time_recent, self._resp_time_max)
128
+
129
+ self._resp_time_mean_store.append(
130
+ sum(self._resp_time_recent) / len(self._resp_time_recent)
131
+ )
132
+
133
+ if self._req_recent:
134
+ req_sec = self._req_recent / (t - self._start_t)
135
+ if self._req_sec_min == 0:
136
+ self._req_sec_min = req_sec
137
+ else:
138
+ self._req_sec_min = min(req_sec, self._req_sec_min)
139
+
140
+ self._req_sec_max = max(req_sec, self._req_sec_max)
141
+
108
142
  self.print_output(t)
109
143
  if msg_type in self.message_types:
110
144
  self._resp_time_recent.append(message["total_time"])
145
+ self._data_transferred += message.get("download_size", 0)
111
146
  self._req_total += 1
112
147
  self._req_recent += 1
113
148
  elif msg_type in ("error", "exception"):
@@ -117,6 +152,28 @@ class GenericStatsOutput:
117
152
  journey_name = message.get("journey")
118
153
  self._error_journeys[journey_name] += 1
119
154
 
155
+ @property
156
+ def mean_resp_time(self):
157
+ if not self._resp_time_mean_store:
158
+ return 0
159
+ return sum(self._resp_time_mean_store) / len(self._resp_time_mean_store)
160
+
161
+ @property
162
+ def max_resp_time_recent(self):
163
+ if not self._resp_time_recent:
164
+ return 0
165
+ return max(self._resp_time_recent)
166
+
167
+ @property
168
+ def min_resp_time(self):
169
+ if not self._resp_time_recent:
170
+ return 0
171
+ return min(self._resp_time_recent)
172
+
173
+ @property
174
+ def req_sec_mean(self):
175
+ return self._req_total / (self._scenarios_completed_time - self._init_time)
176
+
120
177
 
121
178
  class HttpStatsOutput(GenericStatsOutput):
122
179
  message_types = {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mite
3
- Version: 2.0.2
3
+ Version: 2.1.0
4
4
  Summary: A Python Performance Testing Framework
5
5
  Home-page: https://github.com/sky-uk/mite/
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -82,6 +82,7 @@ class SessionPool:
82
82
  total_time=r.total_time,
83
83
  primary_ip=r.primary_ip,
84
84
  method=r.request.method,
85
+ download_size=r.download_size,
85
86
  **session_wrapper.additional_metrics,
86
87
  )
87
88
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes