hardpy 0.12.1__py3-none-any.whl → 0.14.0__py3-none-any.whl

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 (33) hide show
  1. hardpy/cli/cli.py +18 -11
  2. hardpy/common/config.py +5 -0
  3. hardpy/common/stand_cloud/connector.py +142 -135
  4. hardpy/common/stand_cloud/oauth2.py +88 -0
  5. hardpy/common/stand_cloud/registration.py +50 -211
  6. hardpy/common/stand_cloud/token_manager.py +119 -0
  7. hardpy/common/stand_cloud/utils.py +33 -0
  8. hardpy/hardpy_panel/frontend/dist/assets/{allPaths-B26356fZ.js → allPaths-CV5wjLMB.js} +1 -1
  9. hardpy/hardpy_panel/frontend/dist/assets/{allPathsLoader-0BeGWuiy.js → allPathsLoader-JIzW_pSb.js} +2 -2
  10. hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-CccdstaD.js +2 -0
  11. hardpy/hardpy_panel/frontend/dist/assets/index-6RIgWzcZ.js +790 -0
  12. hardpy/hardpy_panel/frontend/dist/assets/{splitPathsBySizeLoader-BEs5IL5-.js → splitPathsBySizeLoader-DkZadBcn.js} +1 -1
  13. hardpy/hardpy_panel/frontend/dist/index.html +1 -1
  14. hardpy/hardpy_panel/frontend/dist/locales/de/translation.json +60 -0
  15. hardpy/hardpy_panel/frontend/dist/locales/en/translation.json +60 -0
  16. hardpy/hardpy_panel/frontend/dist/locales/es/translation.json +60 -0
  17. hardpy/hardpy_panel/frontend/dist/locales/fr/translation.json +60 -0
  18. hardpy/hardpy_panel/frontend/dist/locales/ja/translation.json +60 -0
  19. hardpy/hardpy_panel/frontend/dist/locales/ru/translation.json +60 -0
  20. hardpy/hardpy_panel/frontend/dist/locales/zh/translation.json +60 -0
  21. hardpy/pytest_hardpy/plugin.py +47 -28
  22. hardpy/pytest_hardpy/pytest_call.py +7 -3
  23. hardpy/pytest_hardpy/reporter/base.py +8 -0
  24. hardpy/pytest_hardpy/reporter/hook_reporter.py +25 -8
  25. hardpy/pytest_hardpy/result/report_loader/stand_cloud_loader.py +8 -2
  26. {hardpy-0.12.1.dist-info → hardpy-0.14.0.dist-info}/METADATA +2 -1
  27. {hardpy-0.12.1.dist-info → hardpy-0.14.0.dist-info}/RECORD +30 -21
  28. hardpy/common/stand_cloud/oauth_callback.py +0 -95
  29. hardpy/common/stand_cloud/token_storage.py +0 -27
  30. hardpy/hardpy_panel/frontend/dist/assets/index-Bl_IX0Up.js +0 -790
  31. {hardpy-0.12.1.dist-info → hardpy-0.14.0.dist-info}/WHEEL +0 -0
  32. {hardpy-0.12.1.dist-info → hardpy-0.14.0.dist-info}/entry_points.txt +0 -0
  33. {hardpy-0.12.1.dist-info → hardpy-0.14.0.dist-info}/licenses/LICENSE +0 -0
@@ -1 +1 @@
1
- import{_ as o,a as _,b as i,p as c,I as u}from"./index-Bl_IX0Up.js";var p=function(n,s){return o(void 0,void 0,void 0,function(){var a,r;return _(this,function(e){switch(e.label){case 0:return a=c(n),s!==u.STANDARD?[3,2]:[4,i(()=>import("./index-xb4M2ucX.js").then(t=>t.I),[])];case 1:return r=e.sent(),[3,4];case 2:return[4,i(()=>import("./index-BMEat_ws.js").then(t=>t.I),[])];case 3:r=e.sent(),e.label=4;case 4:return[2,r[a]]}})})};export{p as splitPathsBySizeLoader};
1
+ import{_ as o,a as _,b as i,p as c,I as u}from"./index-6RIgWzcZ.js";var p=function(n,s){return o(void 0,void 0,void 0,function(){var a,r;return _(this,function(e){switch(e.label){case 0:return a=c(n),s!==u.STANDARD?[3,2]:[4,i(()=>import("./index-xb4M2ucX.js").then(t=>t.I),[])];case 1:return r=e.sent(),[3,4];case 2:return[4,i(()=>import("./index-BMEat_ws.js").then(t=>t.I),[])];case 3:r=e.sent(),e.label=4;case 4:return[2,r[a]]}})})};export{p as splitPathsBySizeLoader};
@@ -25,7 +25,7 @@
25
25
  Learn how to configure a non-root public URL by running `npm run build`.
26
26
  -->
27
27
  <title>HardPy Operator Panel</title>
28
- <script type="module" crossorigin src="/assets/index-Bl_IX0Up.js"></script>
28
+ <script type="module" crossorigin src="/assets/index-6RIgWzcZ.js"></script>
29
29
  <link rel="stylesheet" crossorigin href="/assets/index-BwCQzehg.css">
30
30
  </head>
31
31
  <body>
@@ -0,0 +1,60 @@
1
+ {
2
+ "app": {
3
+ "title": "HardPy Bedienfeld",
4
+ "lastLaunch": "Letzter Start:",
5
+ "duration": "Dauer",
6
+ "seconds": "s",
7
+ "soundOn": "Ton einschalten",
8
+ "soundOff": "Ton ausschalten",
9
+ "debugOn": "Debug-Modus einschalten",
10
+ "debugOff": "Debug-Modus ausschalten",
11
+ "connection": "Verbindung wird hergestellt... 🧐🔎",
12
+ "dbError": "Datenbankverbindungsfehler. 🙅🏽‍♀️🚫",
13
+ "noEntries": "Keine Einträge in der Datenbank 🙅🏽‍♀️🚫",
14
+ "status": {
15
+ "ready": "bereit",
16
+ "run": "läuft",
17
+ "passed": "bestanden",
18
+ "failed": "fehlgeschlagen",
19
+ "stopped": "gestoppt",
20
+ "unknown": "unbekannt"
21
+ }
22
+ },
23
+ "button": {
24
+ "start": "Starten",
25
+ "stop": "Stoppen",
26
+ "confirm": "Bestätigen"
27
+ },
28
+ "error": {
29
+ "dbConnectionTitle": "Datenbankverbindungsfehler",
30
+ "dbConnectionMessage": "Verbindung zur Datenbank konnte nicht hergestellt werden"
31
+ },
32
+ "operatorDialog": {
33
+ "defaultTitle": "Nachricht",
34
+ "imageAlt": "Operator-Nachrichtenbild",
35
+ "htmlCodeTitle": "HTML-Code",
36
+ "htmlLinkTitle": "HTML-Link",
37
+ "enterAnswer": "Antwort eingeben",
38
+ "fieldNotEmpty": "Das Feld darf nicht leer sein",
39
+ "notificationTitle": "Benachrichtigung",
40
+ "notificationDesc": "Das Fenster wurde geschlossen. Tests gestoppt.",
41
+ "numericInputError": "Bitte geben Sie eine Zahl ein",
42
+ "radioButtonError": "Bitte wählen Sie eine Option",
43
+ "checkboxError": "Bitte wählen Sie mindestens eine Option"
44
+ },
45
+ "suiteList": {
46
+ "loadingTests": "Tests werden geladen... 🤔",
47
+ "refreshHint": "Versuchen Sie, die Seite zu aktualisieren.",
48
+ "standName": "Standortname",
49
+ "status": "Status",
50
+ "startTime": "Startzeit",
51
+ "finishTime": "Endzeit",
52
+ "alert": "Warnung"
53
+ },
54
+ "testSuite": {
55
+ "nameColumn": "Name",
56
+ "dataColumn": "Daten",
57
+ "loading": "Wird geladen...",
58
+ "stubName": "Testgruppe"
59
+ }
60
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "app": {
3
+ "title": "HardPy Operator Panel",
4
+ "lastLaunch": "Last launch:",
5
+ "duration": "Duration",
6
+ "seconds": "s",
7
+ "soundOn": "Turn on the sound",
8
+ "soundOff": "Turn off the sound",
9
+ "debugOn": "Turn on the debug mode",
10
+ "debugOff": "Turn off the debug mode",
11
+ "connection": "Establishing a connection... 🧐🔎",
12
+ "dbError": "Database connection error. 🙅🏽‍♀️🚫",
13
+ "noEntries": "No entries in the database 🙅🏽‍♀️🚫",
14
+ "status": {
15
+ "ready": "ready",
16
+ "run": "run",
17
+ "passed": "pass",
18
+ "failed": "fail",
19
+ "stopped": "stopped",
20
+ "unknown": "unknown"
21
+ }
22
+ },
23
+ "button": {
24
+ "start": "Start",
25
+ "stop": "Stop",
26
+ "confirm": "Confirm"
27
+ },
28
+ "error": {
29
+ "dbConnectionTitle": "Database Connection Error",
30
+ "dbConnectionMessage": "Failed to establish connection with the database"
31
+ },
32
+ "operatorDialog": {
33
+ "defaultTitle": "Message",
34
+ "imageAlt": "Operator message image",
35
+ "htmlCodeTitle": "HTML Code",
36
+ "htmlLinkTitle": "HTML Link",
37
+ "enterAnswer": "Enter answer",
38
+ "fieldNotEmpty": "The field must not be empty",
39
+ "notificationTitle": "Notification",
40
+ "notificationDesc": "The window was closed. Tests stopped.",
41
+ "numericInputError": "Please enter a number",
42
+ "radioButtonError": "Please select one option",
43
+ "checkboxError": "Please select at least one option"
44
+ },
45
+ "suiteList": {
46
+ "loadingTests": "Loading tests... 🤔",
47
+ "refreshHint": "Try refreshing the page.",
48
+ "standName": "Stand name",
49
+ "status": "Status",
50
+ "startTime": "Start time",
51
+ "finishTime": "Finish time",
52
+ "alert": "Alert"
53
+ },
54
+ "testSuite": {
55
+ "nameColumn": "Name",
56
+ "dataColumn": "Data",
57
+ "loading": "Loading...",
58
+ "stubName": "Test Suite"
59
+ }
60
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "app": {
3
+ "title": "Panel de operador HardPy",
4
+ "lastLaunch": "Última lanzamiento:",
5
+ "duration": "Duración",
6
+ "seconds": "s",
7
+ "soundOn": "Activar sonido",
8
+ "soundOff": "Desactivar sonido",
9
+ "debugOn": "Activar modo de depuración",
10
+ "debugOff": "Desactivar modo de depuración",
11
+ "connection": "Estableciendo conexión... 🧐🔎",
12
+ "dbError": "Error de conexión a la base de datos. 🙅🏽‍♀️🚫",
13
+ "noEntries": "No hay entradas en la base de datos 🙅🏽‍♀️🚫",
14
+ "status": {
15
+ "ready": "listo",
16
+ "run": "ejecutando",
17
+ "passed": "aprobado",
18
+ "failed": "fallado",
19
+ "stopped": "detenido",
20
+ "unknown": "desconocido"
21
+ }
22
+ },
23
+ "button": {
24
+ "start": "Iniciar",
25
+ "stop": "Detener",
26
+ "confirm": "Confirmar"
27
+ },
28
+ "error": {
29
+ "dbConnectionTitle": "Error de conexión a la base de datos",
30
+ "dbConnectionMessage": "No se pudo establecer la conexión con la base de datos"
31
+ },
32
+ "operatorDialog": {
33
+ "defaultTitle": "Mensaje",
34
+ "imageAlt": "Imagen del mensaje del operador",
35
+ "htmlCodeTitle": "Código HTML",
36
+ "htmlLinkTitle": "Enlace HTML",
37
+ "enterAnswer": "Ingrese respuesta",
38
+ "fieldNotEmpty": "El campo no debe estar vacío",
39
+ "notificationTitle": "Notificación",
40
+ "notificationDesc": "La ventana fue cerrada. Pruebas detenidas.",
41
+ "numericInputError": "Por favor ingrese un número",
42
+ "radioButtonError": "Por favor seleccione una opción",
43
+ "checkboxError": "Por favor seleccione al menos una opción"
44
+ },
45
+ "suiteList": {
46
+ "loadingTests": "Cargando pruebas... 🤔",
47
+ "refreshHint": "Intente actualizar la página.",
48
+ "standName": "Nombre del puesto",
49
+ "status": "Estado",
50
+ "startTime": "Hora de inicio",
51
+ "finishTime": "Hora de finalización",
52
+ "alert": "Alerta"
53
+ },
54
+ "testSuite": {
55
+ "nameColumn": "Nombre",
56
+ "dataColumn": "Datos",
57
+ "loading": "Cargando...",
58
+ "stubName": "Conjunto de pruebas"
59
+ }
60
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "app": {
3
+ "title": "Panneau opérateur HardPy",
4
+ "lastLaunch": "Dernière lancement:",
5
+ "duration": "Durée",
6
+ "seconds": "s",
7
+ "soundOn": "Activer le son",
8
+ "soundOff": "Désactiver le son",
9
+ "debugOn": "Activer le mode débogage",
10
+ "debugOff": "Désactiver le mode débogage",
11
+ "connection": "Établissement de la connexion... 🧐🔎",
12
+ "dbError": "Erreur de connexion à la base de données. 🙅🏽‍♀️🚫",
13
+ "noEntries": "Aucune entrée dans la base de données 🙅🏽‍♀️🚫",
14
+ "status": {
15
+ "ready": "prêt",
16
+ "run": "en cours",
17
+ "passed": "réussi",
18
+ "failed": "échoué",
19
+ "stopped": "arrêté",
20
+ "unknown": "inconnu"
21
+ }
22
+ },
23
+ "button": {
24
+ "start": "Commencer",
25
+ "stop": "Arrêter",
26
+ "confirm": "Confirmer"
27
+ },
28
+ "error": {
29
+ "dbConnectionTitle": "Erreur de connexion à la base de données",
30
+ "dbConnectionMessage": "Échec de la connexion à la base de données"
31
+ },
32
+ "operatorDialog": {
33
+ "defaultTitle": "Message",
34
+ "imageAlt": "Image du message de l'opérateur",
35
+ "htmlCodeTitle": "Code HTML",
36
+ "htmlLinkTitle": "Lien HTML",
37
+ "enterAnswer": "Entrez la réponse",
38
+ "fieldNotEmpty": "Le champ ne doit pas être vide",
39
+ "notificationTitle": "Notification",
40
+ "notificationDesc": "La fenêtre a été fermée. Tests arrêtés.",
41
+ "numericInputError": "Veuillez entrer un nombre",
42
+ "radioButtonError": "Veuillez sélectionner une option",
43
+ "checkboxError": "Veuillez sélectionner au moins une option"
44
+ },
45
+ "suiteList": {
46
+ "loadingTests": "Chargement des tests... 🤔",
47
+ "refreshHint": "Essayez de rafraîchir la page.",
48
+ "standName": "Nom du stand",
49
+ "status": "Statut",
50
+ "startTime": "Heure de début",
51
+ "finishTime": "Heure de fin",
52
+ "alert": "Alerte"
53
+ },
54
+ "testSuite": {
55
+ "nameColumn": "Nom",
56
+ "dataColumn": "Données",
57
+ "loading": "Chargement...",
58
+ "stubName": "Suite de tests"
59
+ }
60
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "app": {
3
+ "title": "HardPy オペレーターパネル",
4
+ "lastLaunch": "最終起動:",
5
+ "duration": "期間",
6
+ "seconds": "秒",
7
+ "soundOn": "音をオンにする",
8
+ "soundOff": "音をオフにする",
9
+ "debugOn": "デバッグモードをオンにする",
10
+ "debugOff": "デバッグモードをオフにする",
11
+ "connection": "接続を確立しています... 🧐🔎",
12
+ "dbError": "データベース接続エラー. 🙅🏽‍♀️🚫",
13
+ "noEntries": "データベースにエントリがありません 🙅🏽‍♀️🚫",
14
+ "status": {
15
+ "ready": "準備完了",
16
+ "run": "実行中",
17
+ "passed": "合格",
18
+ "failed": "不合格",
19
+ "stopped": "停止",
20
+ "unknown": "不明"
21
+ }
22
+ },
23
+ "button": {
24
+ "start": "開始",
25
+ "stop": "停止",
26
+ "confirm": "確認"
27
+ },
28
+ "error": {
29
+ "dbConnectionTitle": "データベース接続エラー",
30
+ "dbConnectionMessage": "データベースへの接続に失敗しました"
31
+ },
32
+ "operatorDialog": {
33
+ "defaultTitle": "メッセージ",
34
+ "imageAlt": "オペレーターメッセージ画像",
35
+ "htmlCodeTitle": "HTMLコード",
36
+ "htmlLinkTitle": "HTMLリンク",
37
+ "enterAnswer": "回答を入力",
38
+ "fieldNotEmpty": "フィールドは空にできません",
39
+ "notificationTitle": "通知",
40
+ "notificationDesc": "ウィンドウが閉じられました。テストを停止しました。",
41
+ "numericInputError": "数字を入力してください",
42
+ "radioButtonError": "いずれかのオプションを選択してください",
43
+ "checkboxError": "少なくとも1つのオプションを選択してください"
44
+ },
45
+ "suiteList": {
46
+ "loadingTests": "テストを読み込み中... 🤔",
47
+ "refreshHint": "ページを更新してみてください。",
48
+ "standName": "スタンド名",
49
+ "status": "ステータス",
50
+ "startTime": "開始時間",
51
+ "finishTime": "終了時間",
52
+ "alert": "警告"
53
+ },
54
+ "testSuite": {
55
+ "nameColumn": "名前",
56
+ "dataColumn": "データ",
57
+ "loading": "読み込み中...",
58
+ "stubName": "テストスイート"
59
+ }
60
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "app": {
3
+ "title": "Панель оператора HardPy",
4
+ "lastLaunch": "Последний запуск:",
5
+ "duration": "Длительность",
6
+ "seconds": "с",
7
+ "soundOn": "Включить звук",
8
+ "soundOff": "Выключить звук",
9
+ "debugOn": "Включить режим отладки",
10
+ "debugOff": "Выключить режим отладки",
11
+ "connection": "Установка соединения... 🧐🔎",
12
+ "dbError": "Ошибка подключения к базе данных. 🙅🏽‍♀️🚫",
13
+ "noEntries": "Нет записей в базе данных 🙅🏽‍♀️🚫",
14
+ "status": {
15
+ "ready": "готов",
16
+ "run": "выполнение",
17
+ "passed": "успех",
18
+ "failed": "провалено",
19
+ "stopped": "остановлено",
20
+ "unknown": "неизвестно"
21
+ }
22
+ },
23
+ "button": {
24
+ "start": "Старт",
25
+ "stop": "Стоп",
26
+ "confirm": "Подтвердить"
27
+ },
28
+ "error": {
29
+ "dbConnectionTitle": "Ошибка подключения к базе данных",
30
+ "dbConnectionMessage": "Не удалось установить соединение с базой данных"
31
+ },
32
+ "operatorDialog": {
33
+ "defaultTitle": "Сообщение",
34
+ "imageAlt": "Изображение в сообщении оператора",
35
+ "htmlCodeTitle": "HTML код",
36
+ "htmlLinkTitle": "HTML ссылка",
37
+ "enterAnswer": "Введите ответ",
38
+ "fieldNotEmpty": "Поле не должно быть пустым",
39
+ "notificationTitle": "Уведомление",
40
+ "notificationDesc": "Окно было закрыто. Тесты остановлены.",
41
+ "numericInputError": "Пожалуйста, введите число",
42
+ "radioButtonError": "Пожалуйста, выберите один вариант",
43
+ "checkboxError": "Пожалуйста, выберите хотя бы один вариант"
44
+ },
45
+ "suiteList": {
46
+ "loadingTests": "Загрузка тестов... 🤔",
47
+ "refreshHint": "Попробуйте обновить страницу.",
48
+ "standName": "Название стенда",
49
+ "status": "Статус",
50
+ "startTime": "Время начала",
51
+ "finishTime": "Время завершения",
52
+ "alert": "Предупреждение"
53
+ },
54
+ "testSuite": {
55
+ "nameColumn": "Название",
56
+ "dataColumn": "Данные",
57
+ "loading": "Загрузка...",
58
+ "stubName": "Тестовый набор"
59
+ }
60
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "app": {
3
+ "title": "HardPy 操作面板",
4
+ "lastLaunch": "上次启动:",
5
+ "duration": "持续时间",
6
+ "seconds": "秒",
7
+ "soundOn": "开启声音",
8
+ "soundOff": "关闭声音",
9
+ "debugOn": "开启调试模式",
10
+ "debugOff": "关闭调试模式",
11
+ "connection": "正在建立连接... 🧐🔎",
12
+ "dbError": "数据库连接错误. 🙅🏽‍♀️🚫",
13
+ "noEntries": "数据库中没有条目 🙅🏽‍♀️🚫",
14
+ "status": {
15
+ "ready": "就绪",
16
+ "run": "运行中",
17
+ "passed": "通过",
18
+ "failed": "失败",
19
+ "stopped": "已停止",
20
+ "unknown": "未知"
21
+ }
22
+ },
23
+ "button": {
24
+ "start": "开始",
25
+ "stop": "停止",
26
+ "confirm": "确认"
27
+ },
28
+ "error": {
29
+ "dbConnectionTitle": "数据库连接错误",
30
+ "dbConnectionMessage": "无法建立数据库连接"
31
+ },
32
+ "operatorDialog": {
33
+ "defaultTitle": "消息",
34
+ "imageAlt": "操作员消息图片",
35
+ "htmlCodeTitle": "HTML代码",
36
+ "htmlLinkTitle": "HTML链接",
37
+ "enterAnswer": "输入答案",
38
+ "fieldNotEmpty": "字段不能为空",
39
+ "notificationTitle": "通知",
40
+ "notificationDesc": "窗口已关闭。测试已停止。",
41
+ "numericInputError": "请输入数字",
42
+ "radioButtonError": "请选择一个选项",
43
+ "checkboxError": "请至少选择一个选项"
44
+ },
45
+ "suiteList": {
46
+ "loadingTests": "正在加载测试... 🤔",
47
+ "refreshHint": "尝试刷新页面。",
48
+ "standName": "测试台名称",
49
+ "status": "状态",
50
+ "startTime": "开始时间",
51
+ "finishTime": "完成时间",
52
+ "alert": "警报"
53
+ },
54
+ "testSuite": {
55
+ "nameColumn": "名称",
56
+ "dataColumn": "数据",
57
+ "loading": "加载中...",
58
+ "stubName": "测试套件"
59
+ }
60
+ }
@@ -175,6 +175,8 @@ class HardpyPlugin:
175
175
  if "--collect-only" in session.config.invocation_params.args:
176
176
  return
177
177
  status = self._get_run_status(exitstatus)
178
+ if status == TestStatus.STOPPED:
179
+ self._stop_tests()
178
180
  self._reporter.finish(status)
179
181
  self._reporter.update_db_by_doc()
180
182
  self._reporter.compact_all()
@@ -237,7 +239,8 @@ class HardpyPlugin:
237
239
 
238
240
  con_data = ConnectionData()
239
241
 
240
- if con_data.sc_connection_only: # check
242
+ # running tests depends on a connection to StandCloud
243
+ if con_data.sc_connection_only:
241
244
  try:
242
245
  sc_connector = StandCloudConnector(addr=con_data.sc_address)
243
246
  except StandCloudError as exc:
@@ -407,45 +410,61 @@ class HardpyPlugin:
407
410
  case ExitCode.TESTS_FAILED:
408
411
  return TestStatus.FAILED
409
412
  case ExitCode.INTERRUPTED:
410
- self._stop_tests()
411
413
  return TestStatus.STOPPED
412
414
  case _:
413
415
  return TestStatus.FAILED
414
416
 
415
417
  def _stop_tests(self) -> None:
416
- """Update module and case statuses from READY or RUN to STOPPED."""
418
+ """Update module and case statuses to stopped and skipped."""
419
+ is_module_stopped = False
420
+ is_case_stopped = False
421
+ valid_statuses = {TestStatus.PASSED, TestStatus.FAILED, TestStatus.SKIPPED}
417
422
  for module_id, module_data in self._results.items():
418
- module_status = module_data["module_status"]
419
-
420
- # skip not ready and running modules
421
- if module_status not in {TestStatus.READY, TestStatus.RUN}:
423
+ module_status = self._reporter.get_module_status(module_id)
424
+ if module_status in valid_statuses:
422
425
  continue
423
426
 
424
- # update module statuses
425
- self._results[module_id]["module_status"] = TestStatus.STOPPED
426
- self._reporter.set_module_status(module_id, TestStatus.STOPPED)
427
- if self._reporter.get_module_start_time(module_id):
428
- self._reporter.set_module_stop_time(module_id)
429
-
430
- # update case statuses
431
- for module_key, module_value in module_data.items():
432
- # module status is not a case_id
433
- if module_key == "module_status":
427
+ is_module_stopped = self._stop_module(module_id, is_module_stopped)
428
+ for module_data_key in module_data:
429
+ # skip module status
430
+ if module_data_key == "module_status":
434
431
  continue
435
- # case value is empty - case is not finished
436
- if module_value is None:
437
- case_id = module_key
438
- self._results[module_id][case_id] = TestStatus.STOPPED
439
- self._reporter.set_case_status(
440
- module_id,
441
- case_id,
442
- TestStatus.STOPPED,
443
- )
444
- if self._reporter.get_case_start_time(module_id, case_id):
445
- self._reporter.set_case_stop_time(module_id, case_id)
432
+ case_id = module_data_key
433
+ case_status = self._reporter.get_case_status(module_id, case_id)
434
+ if case_status in valid_statuses:
435
+ continue
436
+ is_case_stopped = self._stop_case(module_id, case_id, is_case_stopped)
446
437
 
447
438
  self._reporter.update_db_by_doc()
448
439
 
440
+ def _stop_module(self, module_id: str, is_module_stopped: bool) -> bool:
441
+ # stopped status is set only once other statuses are skipped
442
+ if is_module_stopped is False:
443
+ module_status = TestStatus.STOPPED
444
+ is_module_stopped = True
445
+ if not self._reporter.get_module_start_time(module_id):
446
+ self._reporter.set_module_start_time(module_id)
447
+ self._reporter.set_module_stop_time(module_id)
448
+ else:
449
+ module_status = TestStatus.SKIPPED
450
+ self._results[module_id]["module_status"] = module_status
451
+ self._reporter.set_module_status(module_id, module_status)
452
+ return is_module_stopped
453
+
454
+ def _stop_case(self, module_id: str, case_id: str, is_case_stopped: bool) -> bool:
455
+ # stopped status is set only once other statuses are skipped
456
+ if is_case_stopped is False:
457
+ case_status = TestStatus.STOPPED
458
+ is_case_stopped = True
459
+ if not self._reporter.get_case_start_time(module_id, case_id):
460
+ self._reporter.set_case_start_time(module_id, case_id)
461
+ self._reporter.set_case_stop_time(module_id, case_id)
462
+ else:
463
+ case_status = TestStatus.SKIPPED
464
+ self._results[module_id][case_id] = case_status
465
+ self._reporter.set_case_status(module_id, case_id, case_status)
466
+ return is_case_stopped
467
+
449
468
  def _decode_assertion_msg(
450
469
  self,
451
470
  error: (
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from dataclasses import dataclass
6
+ from inspect import stack
6
7
  from os import environ
7
8
  from time import sleep
8
9
  from typing import Any
@@ -181,8 +182,8 @@ def set_message(msg: str, msg_key: str | None = None) -> None:
181
182
  If not specified, a random ID will be generated.
182
183
  """
183
184
  current_test = _get_current_test()
184
- reporter = RunnerReporter()
185
185
 
186
+ reporter = RunnerReporter()
186
187
  if msg_key is None:
187
188
  msg_key = str(uuid4())
188
189
 
@@ -323,8 +324,8 @@ def run_dialog_box(dialog_box_data: DialogBox) -> Any: # noqa: ANN401
323
324
  if not dialog_box_data.dialog_text:
324
325
  msg = "The 'dialog_text' argument cannot be empty."
325
326
  raise ValueError(msg)
326
- current_test = _get_current_test()
327
327
  reporter = RunnerReporter()
328
+ current_test = _get_current_test()
328
329
  key = reporter.generate_key(
329
330
  DF.MODULES,
330
331
  current_test.module_id,
@@ -420,7 +421,10 @@ def _get_current_test() -> CurrentTestInfo:
420
421
  current_node = environ.get("PYTEST_CURRENT_TEST")
421
422
 
422
423
  if current_node is None:
423
- msg = "PYTEST_CURRENT_TEST variable is not set"
424
+ reporter = RunnerReporter()
425
+ caller = stack()[1].function
426
+ msg = f"Function {caller} can't be called outside of the test."
427
+ reporter.set_alert(msg)
424
428
  raise RuntimeError(msg)
425
429
 
426
430
  module_delimiter = ".py::"
@@ -53,6 +53,14 @@ class BaseReporter:
53
53
  self._runstore.update_doc_value(key, value)
54
54
  self._statestore.update_doc_value(key, value)
55
55
 
56
+ def set_alert(self, alert: str) -> None:
57
+ """Set alert message.
58
+
59
+ Args:
60
+ alert (str): alert message
61
+ """
62
+ self.set_doc_value(DF.ALERT, alert, statestore_only=True)
63
+
56
64
  def update_db_by_doc(self) -> None:
57
65
  """Update database by current document."""
58
66
  self._statestore.update_db()
@@ -129,6 +129,19 @@ class HookReporter(BaseReporter):
129
129
  key = self.generate_key(DF.MODULES, module_id, DF.CASES, case_id, DF.STATUS)
130
130
  self.set_doc_value(key, status)
131
131
 
132
+ def get_case_status(self, module_id: str, case_id: str) -> str:
133
+ """Set test case status.
134
+
135
+ Args:
136
+ module_id (str): module id
137
+ case_id (str): case id
138
+
139
+ Returns:
140
+ str: test case status
141
+ """
142
+ key = self.generate_key(DF.MODULES, module_id, DF.CASES, case_id, DF.STATUS)
143
+ return self._statestore.get_field(key)
144
+
132
145
  def set_case_start_time(self, module_id: str, case_id: str) -> None:
133
146
  """Set test case start_time.
134
147
 
@@ -159,6 +172,18 @@ class HookReporter(BaseReporter):
159
172
  key = self.generate_key(DF.MODULES, module_id, DF.STATUS)
160
173
  self.set_doc_value(key, status)
161
174
 
175
+ def get_module_status(self, module_id: str) -> str:
176
+ """Get test module status.
177
+
178
+ Args:
179
+ module_id (str): module id
180
+
181
+ Returns:
182
+ str: test module status
183
+ """
184
+ key = self.generate_key(DF.MODULES, module_id, DF.STATUS)
185
+ return self._statestore.get_field(key)
186
+
162
187
  def set_module_start_time(self, module_id: str) -> None:
163
188
  """Set test module status.
164
189
 
@@ -194,14 +219,6 @@ class HookReporter(BaseReporter):
194
219
  )
195
220
  self.set_doc_value(key, attempt, statestore_only=True)
196
221
 
197
- def set_alert(self, alert: str) -> None:
198
- """Set alert message.
199
-
200
- Args:
201
- alert (str): alert message
202
- """
203
- self.set_doc_value(DF.ALERT, alert, statestore_only=True)
204
-
205
222
  def get_module_start_time(self, module_id: str) -> int:
206
223
  """Get module start time.
207
224