hardpy 0.15.2__py3-none-any.whl → 0.17.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 (64) hide show
  1. hardpy/__init__.py +9 -6
  2. hardpy/cli/cli.py +16 -10
  3. hardpy/cli/template.py +5 -4
  4. hardpy/common/config.py +83 -50
  5. hardpy/{pytest_hardpy/utils → common}/singleton.py +1 -1
  6. hardpy/hardpy_panel/api.py +44 -13
  7. hardpy/hardpy_panel/frontend/dist/assets/{allPaths-CV5wjLMB.js → allPaths-COgYwK8M.js} +1 -1
  8. hardpy/hardpy_panel/frontend/dist/assets/{allPathsLoader-JIzW_pSb.js → allPathsLoader-B8vBKA-e.js} +2 -2
  9. hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-16-Bfs1BwbR.ttf → blueprint-icons-16-B2twAPZE.ttf} +0 -0
  10. hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-16-RCDSkC4W.eot → blueprint-icons-16-C0Unyq1d.eot} +0 -0
  11. hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-16-CzsyEoPG.svg → blueprint-icons-16-CVy9qFng.svg} +249 -3
  12. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-Ck1ifK4A.woff +0 -0
  13. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-DwWyHYRo.woff2 +0 -0
  14. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-9zitLjlL.woff2 +0 -0
  15. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-CjKGIKxE.woff +0 -0
  16. hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-20-DyVnGNfQ.svg → blueprint-icons-20-DQ09GSQq.svg} +249 -3
  17. hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-20-BGGGsqDJ.ttf → blueprint-icons-20-DmR755bS.ttf} +0 -0
  18. hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-20-Doom1bSH.eot → blueprint-icons-20-p9MhBXD8.eot} +0 -0
  19. hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-CVrkrold.js +2 -0
  20. hardpy/hardpy_panel/frontend/dist/assets/index-B-fsa5Ru.js +1 -0
  21. hardpy/hardpy_panel/frontend/dist/assets/index-B3wEgxl0.js +4673 -0
  22. hardpy/hardpy_panel/frontend/dist/assets/index-B7T9xvaW.css +1 -0
  23. hardpy/hardpy_panel/frontend/dist/assets/index-DLOviMB1.js +1 -0
  24. hardpy/hardpy_panel/frontend/dist/assets/{splitPathsBySizeLoader-DkZadBcn.js → splitPathsBySizeLoader-CLCw9W9y.js} +1 -1
  25. hardpy/hardpy_panel/frontend/dist/index.html +2 -2
  26. hardpy/hardpy_panel/frontend/dist/locales/de/translation.json +22 -7
  27. hardpy/hardpy_panel/frontend/dist/locales/en/translation.json +22 -7
  28. hardpy/hardpy_panel/frontend/dist/locales/es/translation.json +22 -7
  29. hardpy/hardpy_panel/frontend/dist/locales/fr/translation.json +22 -7
  30. hardpy/hardpy_panel/frontend/dist/locales/ja/translation.json +16 -1
  31. hardpy/hardpy_panel/frontend/dist/locales/ru/translation.json +22 -7
  32. hardpy/hardpy_panel/frontend/dist/locales/zh/translation.json +16 -1
  33. hardpy/pytest_hardpy/db/__init__.py +12 -0
  34. hardpy/pytest_hardpy/db/base_store.py +31 -4
  35. hardpy/pytest_hardpy/db/runstore.py +1 -1
  36. hardpy/pytest_hardpy/db/schema/v1.py +8 -6
  37. hardpy/pytest_hardpy/db/statestore.py +1 -1
  38. hardpy/pytest_hardpy/plugin.py +38 -25
  39. hardpy/pytest_hardpy/pytest_call.py +74 -29
  40. hardpy/pytest_hardpy/pytest_wrapper.py +9 -7
  41. hardpy/pytest_hardpy/reporter/base.py +21 -1
  42. hardpy/pytest_hardpy/reporter/runner_reporter.py +1 -1
  43. hardpy/pytest_hardpy/result/report_loader/stand_cloud_loader.py +35 -4
  44. hardpy/pytest_hardpy/utils/__init__.py +0 -16
  45. hardpy/pytest_hardpy/utils/dialog_box.py +3 -0
  46. {hardpy-0.15.2.dist-info → hardpy-0.17.0.dist-info}/METADATA +17 -2
  47. hardpy-0.17.0.dist-info/RECORD +83 -0
  48. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-Btb8d-Hu.woff +0 -0
  49. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-DrH54W_x.woff2 +0 -0
  50. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-D9WO2FSG.woff2 +0 -0
  51. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-ZW-9JnPf.woff +0 -0
  52. hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-CccdstaD.js +0 -2
  53. hardpy/hardpy_panel/frontend/dist/assets/index-6RIgWzcZ.js +0 -790
  54. hardpy/hardpy_panel/frontend/dist/assets/index-BMEat_ws.js +0 -1
  55. hardpy/hardpy_panel/frontend/dist/assets/index-BwCQzehg.css +0 -1
  56. hardpy/hardpy_panel/frontend/dist/assets/index-xb4M2ucX.js +0 -1
  57. hardpy/pytest_hardpy/db/base_connector.py +0 -31
  58. hardpy/pytest_hardpy/db/base_server.py +0 -14
  59. hardpy/pytest_hardpy/utils/connection_data.py +0 -13
  60. hardpy-0.15.2.dist-info/RECORD +0 -86
  61. /hardpy/pytest_hardpy/{utils → db}/stand_type.py +0 -0
  62. {hardpy-0.15.2.dist-info → hardpy-0.17.0.dist-info}/WHEEL +0 -0
  63. {hardpy-0.15.2.dist-info → hardpy-0.17.0.dist-info}/entry_points.txt +0 -0
  64. {hardpy-0.15.2.dist-info → hardpy-0.17.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-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};
1
+ import{_ as o,a as _,b as i,p as c,I as u}from"./index-B3wEgxl0.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-DLOviMB1.js").then(t=>t.I),[])];case 1:return r=e.sent(),[3,4];case 2:return[4,i(()=>import("./index-B-fsa5Ru.js").then(t=>t.I),[])];case 3:r=e.sent(),e.label=4;case 4:return[2,r[a]]}})})};export{p as splitPathsBySizeLoader};
@@ -25,8 +25,8 @@
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-6RIgWzcZ.js"></script>
29
- <link rel="stylesheet" crossorigin href="/assets/index-BwCQzehg.css">
28
+ <script type="module" crossorigin src="/assets/index-B3wEgxl0.js"></script>
29
+ <link rel="stylesheet" crossorigin href="/assets/index-B7T9xvaW.css">
30
30
  </head>
31
31
  <body>
32
32
  <noscript>You need to enable JavaScript to run this app.</noscript>
@@ -11,24 +11,39 @@
11
11
  "connection": "Verbindung wird hergestellt... 🧐🔎",
12
12
  "dbError": "Datenbankverbindungsfehler. 🙅🏽‍♀️🚫",
13
13
  "noEntries": "Keine Einträge in der Datenbank 🙅🏽‍♀️🚫",
14
+ "stoppedTestCase": "Angehaltener Testfall",
15
+ "failedTestCases": "Fehlgeschlagene Testfälle",
16
+ "modalResultDismissHint": "Klicken Sie irgendwo oder drücken Sie eine Taste, um zu schließen",
17
+ "modalResultAutoDismissHint": "Automatisches Schließen in {{seconds}} Sekunden...",
14
18
  "status": {
15
- "ready": "bereit",
16
- "run": "läuft",
17
- "passed": "bestanden",
18
- "failed": "fehlgeschlagen",
19
- "stopped": "gestoppt",
20
- "unknown": "unbekannt"
19
+ "ready": "Bereit",
20
+ "run": "Läuft",
21
+ "passed": "Bestanden",
22
+ "failed": "Fehlgeschlagen",
23
+ "stopped": "Gestoppt",
24
+ "unknown": "Unbekannt"
21
25
  }
22
26
  },
23
27
  "button": {
24
28
  "start": "Starten",
25
29
  "stop": "Stoppen",
26
- "confirm": "Bestätigen"
30
+ "confirm": "Bestätigen",
31
+ "pass": "Bestanden",
32
+ "fail": "Fehlgeschlagen"
27
33
  },
28
34
  "error": {
29
35
  "dbConnectionTitle": "Datenbankverbindungsfehler",
30
36
  "dbConnectionMessage": "Verbindung zur Datenbank konnte nicht hergestellt werden"
31
37
  },
38
+ "chart": {
39
+ "dataChart": "Diagrammdaten",
40
+ "xAxis": "X-Achse",
41
+ "yAxis": "Y-Achse",
42
+ "chart": "Diagramm",
43
+ "showChart": "Diagramm anzeigen {{title}}",
44
+ "fullscreenButton": "Diagramm im Vollbildmodus öffnen",
45
+ "series": "Reihe {{number}}"
46
+ },
32
47
  "operatorDialog": {
33
48
  "defaultTitle": "Nachricht",
34
49
  "imageAlt": "Operator-Nachrichtenbild",
@@ -11,24 +11,39 @@
11
11
  "connection": "Establishing a connection... 🧐🔎",
12
12
  "dbError": "Database connection error. 🙅🏽‍♀️🚫",
13
13
  "noEntries": "No entries in the database 🙅🏽‍♀️🚫",
14
+ "stoppedTestCase": "Stopped Test Case",
15
+ "failedTestCases": "Failed Test Cases",
16
+ "modalResultDismissHint": "Click anywhere or press any key to dismiss",
17
+ "modalResultAutoDismissHint": "Auto-dismissing in {{seconds}} seconds...",
14
18
  "status": {
15
- "ready": "ready",
16
- "run": "run",
17
- "passed": "pass",
18
- "failed": "fail",
19
- "stopped": "stopped",
20
- "unknown": "unknown"
19
+ "ready": "Ready",
20
+ "run": "Run",
21
+ "passed": "Pass",
22
+ "failed": "Fail",
23
+ "stopped": "Stopped",
24
+ "unknown": "Unknown"
21
25
  }
22
26
  },
23
27
  "button": {
24
28
  "start": "Start",
25
29
  "stop": "Stop",
26
- "confirm": "Confirm"
30
+ "confirm": "Confirm",
31
+ "pass": "Pass",
32
+ "fail": "Fail"
27
33
  },
28
34
  "error": {
29
35
  "dbConnectionTitle": "Database Connection Error",
30
36
  "dbConnectionMessage": "Failed to establish connection with the database"
31
37
  },
38
+ "chart": {
39
+ "dataChart": "Chart Data",
40
+ "xAxis": "X Axis",
41
+ "yAxis": "Y Axis",
42
+ "chart": "Chart",
43
+ "showChart": "Show chart {{title}}",
44
+ "fullscreenButton": "Open chart in full screen",
45
+ "series": "Series {{number}}"
46
+ },
32
47
  "operatorDialog": {
33
48
  "defaultTitle": "Message",
34
49
  "imageAlt": "Operator message image",
@@ -11,24 +11,39 @@
11
11
  "connection": "Estableciendo conexión... 🧐🔎",
12
12
  "dbError": "Error de conexión a la base de datos. 🙅🏽‍♀️🚫",
13
13
  "noEntries": "No hay entradas en la base de datos 🙅🏽‍♀️🚫",
14
+ "stoppedTestCase": "Caso de prueba detenido",
15
+ "failedTestCases": "Casos de prueba fallidos",
16
+ "modalResultDismissHint": "Haga clic en cualquier lugar o presione cualquier tecla para cerrar",
17
+ "modalResultAutoDismissHint": "Cierre automático en {{seconds}} segundos...",
14
18
  "status": {
15
- "ready": "listo",
16
- "run": "ejecutando",
17
- "passed": "aprobado",
18
- "failed": "fallado",
19
- "stopped": "detenido",
20
- "unknown": "desconocido"
19
+ "ready": "Listo",
20
+ "run": "Ejecutando",
21
+ "passed": "Aprobado",
22
+ "failed": "Fallado",
23
+ "stopped": "Detenido",
24
+ "unknown": "Desconocido"
21
25
  }
22
26
  },
23
27
  "button": {
24
28
  "start": "Iniciar",
25
29
  "stop": "Detener",
26
- "confirm": "Confirmar"
30
+ "confirm": "Confirmar",
31
+ "pass": "Aprobado",
32
+ "fail": "Fallado"
27
33
  },
28
34
  "error": {
29
35
  "dbConnectionTitle": "Error de conexión a la base de datos",
30
36
  "dbConnectionMessage": "No se pudo establecer la conexión con la base de datos"
31
37
  },
38
+ "chart": {
39
+ "dataChart": "Datos del gráfico",
40
+ "xAxis": "Eje X",
41
+ "yAxis": "Eje Y",
42
+ "chart": "Gráfico",
43
+ "showChart": "Mostrar gráfico {{title}}",
44
+ "fullscreenButton": "Abrir gráfico en pantalla completa",
45
+ "series": "Serie {{number}}"
46
+ },
32
47
  "operatorDialog": {
33
48
  "defaultTitle": "Mensaje",
34
49
  "imageAlt": "Imagen del mensaje del operador",
@@ -11,24 +11,39 @@
11
11
  "connection": "Établissement de la connexion... 🧐🔎",
12
12
  "dbError": "Erreur de connexion à la base de données. 🙅🏽‍♀️🚫",
13
13
  "noEntries": "Aucune entrée dans la base de données 🙅🏽‍♀️🚫",
14
+ "stoppedTestCase": "Cas de test arrêté",
15
+ "failedTestCases": "Cas de test échoués",
16
+ "modalResultDismissHint": "Cliquez n'importe où ou appuyez sur n'importe quelle touche pour fermer",
17
+ "modalResultAutoDismissHint": "Fermeture automatique dans {{seconds}} secondes...",
14
18
  "status": {
15
- "ready": "prêt",
16
- "run": "en cours",
17
- "passed": "réussi",
18
- "failed": "échoué",
19
- "stopped": "arrêté",
20
- "unknown": "inconnu"
19
+ "ready": "Prêt",
20
+ "run": "En cours",
21
+ "passed": "Réussi",
22
+ "failed": "Échoué",
23
+ "stopped": "Arrêté",
24
+ "unknown": "Inconnu"
21
25
  }
22
26
  },
23
27
  "button": {
24
28
  "start": "Commencer",
25
29
  "stop": "Arrêter",
26
- "confirm": "Confirmer"
30
+ "confirm": "Confirmer",
31
+ "pass": "Réussi",
32
+ "fail": "Échoué"
27
33
  },
28
34
  "error": {
29
35
  "dbConnectionTitle": "Erreur de connexion à la base de données",
30
36
  "dbConnectionMessage": "Échec de la connexion à la base de données"
31
37
  },
38
+ "chart": {
39
+ "dataChart": "Données du graphique",
40
+ "xAxis": "Axe X",
41
+ "yAxis": "Axe Y",
42
+ "chart": "Graphique",
43
+ "showChart": "Afficher le graphique {{title}}",
44
+ "fullscreenButton": "Ouvrir le graphique en plein écran",
45
+ "series": "Série {{number}}"
46
+ },
32
47
  "operatorDialog": {
33
48
  "defaultTitle": "Message",
34
49
  "imageAlt": "Image du message de l'opérateur",
@@ -11,6 +11,10 @@
11
11
  "connection": "接続を確立しています... 🧐🔎",
12
12
  "dbError": "データベース接続エラー. 🙅🏽‍♀️🚫",
13
13
  "noEntries": "データベースにエントリがありません 🙅🏽‍♀️🚫",
14
+ "stoppedTestCase": "停止されたテストケース",
15
+ "failedTestCases": "失敗したテストケース",
16
+ "modalResultDismissHint": "どこかをクリックするか、任意のキーを押して閉じます",
17
+ "modalResultAutoDismissHint": "{{seconds}}秒後に自動的に閉じます...",
14
18
  "status": {
15
19
  "ready": "準備完了",
16
20
  "run": "実行中",
@@ -23,12 +27,23 @@
23
27
  "button": {
24
28
  "start": "開始",
25
29
  "stop": "停止",
26
- "confirm": "確認"
30
+ "confirm": "確認",
31
+ "pass": "合格",
32
+ "fail": "不合格"
27
33
  },
28
34
  "error": {
29
35
  "dbConnectionTitle": "データベース接続エラー",
30
36
  "dbConnectionMessage": "データベースへの接続に失敗しました"
31
37
  },
38
+ "chart": {
39
+ "dataChart": "チャートデータ",
40
+ "xAxis": "X軸",
41
+ "yAxis": "Y軸",
42
+ "chart": "グラフ",
43
+ "showChart": "グラフを表示 {{title}}",
44
+ "fullscreenButton": "グラフを全画面表示で開く",
45
+ "series": "シリーズ{{number}}"
46
+ },
32
47
  "operatorDialog": {
33
48
  "defaultTitle": "メッセージ",
34
49
  "imageAlt": "オペレーターメッセージ画像",
@@ -11,24 +11,39 @@
11
11
  "connection": "Установка соединения... 🧐🔎",
12
12
  "dbError": "Ошибка подключения к базе данных. 🙅🏽‍♀️🚫",
13
13
  "noEntries": "Нет записей в базе данных 🙅🏽‍♀️🚫",
14
+ "stoppedTestCase": "Остановленный тестовый случай",
15
+ "failedTestCases": "Проваленные тестовые случаи",
16
+ "modalResultDismissHint": "Нажмите на любое место на экране или нажмите любую клавишу для закрытия",
17
+ "modalResultAutoDismissHint": "Автоматическое закрытие через {{seconds}} секунд...",
14
18
  "status": {
15
- "ready": "готов",
16
- "run": "выполнение",
17
- "passed": "успех",
18
- "failed": "провалено",
19
- "stopped": "остановлено",
20
- "unknown": "неизвестно"
19
+ "ready": "Готов",
20
+ "run": "Выполнение",
21
+ "passed": "Успех",
22
+ "failed": "Провалено",
23
+ "stopped": "Остановлено",
24
+ "unknown": "Неизвестно"
21
25
  }
22
26
  },
23
27
  "button": {
24
28
  "start": "Старт",
25
29
  "stop": "Стоп",
26
- "confirm": "Подтвердить"
30
+ "confirm": "Подтвердить",
31
+ "pass": "Успех",
32
+ "fail": "Провалено"
27
33
  },
28
34
  "error": {
29
35
  "dbConnectionTitle": "Ошибка подключения к базе данных",
30
36
  "dbConnectionMessage": "Не удалось установить соединение с базой данных"
31
37
  },
38
+ "chart": {
39
+ "dataChart": "Данные графика",
40
+ "xAxis": "Ось X",
41
+ "yAxis": "Ось Y",
42
+ "chart": "График",
43
+ "showChart": "Показать график {{title}}",
44
+ "fullscreenButton": "Открыть график в полноэкранном режиме",
45
+ "series": "Серия {{number}}"
46
+ },
32
47
  "operatorDialog": {
33
48
  "defaultTitle": "Сообщение",
34
49
  "imageAlt": "Изображение в сообщении оператора",
@@ -11,6 +11,10 @@
11
11
  "connection": "正在建立连接... 🧐🔎",
12
12
  "dbError": "数据库连接错误. 🙅🏽‍♀️🚫",
13
13
  "noEntries": "数据库中没有条目 🙅🏽‍♀️🚫",
14
+ "stoppedTestCase": "已停止的测试用例",
15
+ "failedTestCases": "失败的测试用例",
16
+ "modalResultDismissHint": "点击任意位置或按任意键关闭",
17
+ "modalResultAutoDismissHint": "{{seconds}}秒后自动关闭...",
14
18
  "status": {
15
19
  "ready": "就绪",
16
20
  "run": "运行中",
@@ -23,12 +27,23 @@
23
27
  "button": {
24
28
  "start": "开始",
25
29
  "stop": "停止",
26
- "confirm": "确认"
30
+ "confirm": "确认",
31
+ "pass": "通过",
32
+ "fail": "失败"
27
33
  },
28
34
  "error": {
29
35
  "dbConnectionTitle": "数据库连接错误",
30
36
  "dbConnectionMessage": "无法建立数据库连接"
31
37
  },
38
+ "chart": {
39
+ "dataChart": "图表数据",
40
+ "xAxis": "X轴",
41
+ "yAxis": "Y轴",
42
+ "chart": "图表",
43
+ "showChart": "显示图表 {{title}}",
44
+ "fullscreenButton": "全屏显示图表",
45
+ "series": "系列{{number}}"
46
+ },
32
47
  "operatorDialog": {
33
48
  "defaultTitle": "消息",
34
49
  "imageAlt": "操作员消息图片",
@@ -5,13 +5,25 @@ from hardpy.pytest_hardpy.db.base_store import BaseStore
5
5
  from hardpy.pytest_hardpy.db.const import DatabaseField
6
6
  from hardpy.pytest_hardpy.db.runstore import RunStore
7
7
  from hardpy.pytest_hardpy.db.schema import ResultRunStore, ResultStateStore
8
+ from hardpy.pytest_hardpy.db.stand_type import (
9
+ Chart,
10
+ Instrument,
11
+ NumericMeasurement,
12
+ StringMeasurement,
13
+ SubUnit,
14
+ )
8
15
  from hardpy.pytest_hardpy.db.statestore import StateStore
9
16
 
10
17
  __all__ = [
11
18
  "BaseStore",
19
+ "Chart",
12
20
  "DatabaseField",
21
+ "Instrument",
22
+ "NumericMeasurement",
13
23
  "ResultRunStore",
14
24
  "ResultStateStore",
15
25
  "RunStore",
16
26
  "StateStore",
27
+ "StringMeasurement",
28
+ "SubUnit",
17
29
  ]
@@ -1,22 +1,31 @@
1
1
  # Copyright (c) 2024 Everypin
2
2
  # GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
3
3
 
4
+ from json import dumps
4
5
  from logging import getLogger
5
6
  from typing import Any
6
7
 
7
8
  from glom import assign, glom
8
- from pycouchdb.exceptions import Conflict, NotFound
9
+ from pycouchdb import Server as DbServer
10
+ from pycouchdb.client import Database
11
+ from pycouchdb.exceptions import Conflict, GenericError, NotFound
9
12
  from pydantic._internal._model_construction import ModelMetaclass
13
+ from requests.exceptions import ConnectionError # noqa: A004
10
14
 
11
- from hardpy.pytest_hardpy.db.base_connector import BaseConnector
15
+ from hardpy.common.config import ConfigManager
12
16
  from hardpy.pytest_hardpy.db.const import DatabaseField as DF # noqa: N817
13
17
 
14
18
 
15
- class BaseStore(BaseConnector):
19
+ class BaseStore:
16
20
  """HardPy base storage interface for CouchDB."""
17
21
 
18
22
  def __init__(self, db_name: str) -> None:
19
- super().__init__(db_name)
23
+ config_manager = ConfigManager()
24
+ config = config_manager.config
25
+ self._db_srv = DbServer(config.database.url)
26
+ self._db_name = db_name
27
+ self._db = self._init_db()
28
+ self._doc_id = config.database.doc_id
20
29
  self._log = getLogger(__name__)
21
30
  self._doc: dict = self._init_doc()
22
31
  self._schema: ModelMetaclass
@@ -47,6 +56,11 @@ class BaseStore(BaseConnector):
47
56
  key (str): document key
48
57
  value: document value
49
58
  """
59
+ try:
60
+ dumps(value)
61
+ except Exception: # noqa: BLE001
62
+ # serialize non-serializable objects as string
63
+ value = dumps(value, default=str)
50
64
  if "." in key:
51
65
  assign(self._doc, key, value)
52
66
  else:
@@ -83,6 +97,19 @@ class BaseStore(BaseConnector):
83
97
  self._log.debug("Database will be created for the first time")
84
98
  self._doc: dict = self._init_doc()
85
99
 
100
+ def _init_db(self) -> Database:
101
+ try:
102
+ return self._db_srv.create(self._db_name) # type: ignore
103
+ except Conflict:
104
+ # database is already created
105
+ return self._db_srv.database(self._db_name)
106
+ except GenericError as exc:
107
+ msg = f"Error initializing database {exc}"
108
+ raise RuntimeError(msg) from exc
109
+ except ConnectionError as exc:
110
+ msg = f"Error initializing database: {exc}"
111
+ raise RuntimeError(msg) from exc
112
+
86
113
  def _init_doc(self) -> dict:
87
114
  try:
88
115
  doc = self._db.get(self._doc_id)
@@ -5,9 +5,9 @@ from logging import getLogger
5
5
 
6
6
  from pycouchdb.exceptions import Conflict, NotFound
7
7
 
8
+ from hardpy.common.singleton import SingletonMeta
8
9
  from hardpy.pytest_hardpy.db.base_store import BaseStore
9
10
  from hardpy.pytest_hardpy.db.schema import ResultRunStore
10
- from hardpy.pytest_hardpy.utils import SingletonMeta
11
11
 
12
12
 
13
13
  class RunStore(BaseStore, metaclass=SingletonMeta):
@@ -8,7 +8,7 @@ from typing import ClassVar
8
8
 
9
9
  from pydantic import BaseModel, ConfigDict, Field
10
10
 
11
- from hardpy.pytest_hardpy.utils import (
11
+ from hardpy.pytest_hardpy.utils.const import (
12
12
  ChartType,
13
13
  ComparisonOperation as CompOp,
14
14
  Group,
@@ -78,7 +78,7 @@ class Dut(BaseModel):
78
78
  part_number: str | None = None
79
79
  revision: str | None = None
80
80
  sub_units: list[SubUnit] = []
81
- info: Mapping[str, str | int | float] = {}
81
+ info: Mapping[str, str | int | float | None] = {}
82
82
 
83
83
 
84
84
  class SubUnit(BaseModel):
@@ -91,7 +91,7 @@ class SubUnit(BaseModel):
91
91
  serial_number: str | None = None
92
92
  part_number: str | None = None
93
93
  revision: str | None = None
94
- info: Mapping[str, str | int | float] = {}
94
+ info: Mapping[str, str | int | float | None] = {}
95
95
 
96
96
 
97
97
  class Instrument(BaseModel):
@@ -101,9 +101,11 @@ class Instrument(BaseModel):
101
101
 
102
102
  name: str | None = None
103
103
  revision: str | None = None
104
+ serial_number: str | None = None
105
+ part_number: str | None = None
104
106
  number: int | None = None
105
107
  comment: str | None = None
106
- info: Mapping[str, str | int | float] = {}
108
+ info: Mapping[str, str | int | float | None] = {}
107
109
 
108
110
 
109
111
  class TestStand(BaseModel):
@@ -119,7 +121,7 @@ class TestStand(BaseModel):
119
121
  number: int | None = None
120
122
  drivers: dict = {} # deprecated, remove in v2
121
123
  instruments: list[Instrument] = []
122
- info: Mapping[str, str | int | float] = {}
124
+ info: Mapping[str, str | int | float | None] = {}
123
125
 
124
126
 
125
127
  class Process(BaseModel):
@@ -129,7 +131,7 @@ class Process(BaseModel):
129
131
 
130
132
  name: str | None = None
131
133
  number: int | None = None
132
- info: Mapping[str, str | int | float] = {}
134
+ info: Mapping[str, str | int | float | None] = {}
133
135
 
134
136
 
135
137
  class IBaseMeasurement(BaseModel, ABC):
@@ -3,9 +3,9 @@
3
3
 
4
4
  from logging import getLogger
5
5
 
6
+ from hardpy.common.singleton import SingletonMeta
6
7
  from hardpy.pytest_hardpy.db.base_store import BaseStore
7
8
  from hardpy.pytest_hardpy.db.schema import ResultStateStore
8
- from hardpy.pytest_hardpy.utils import SingletonMeta
9
9
 
10
10
 
11
11
  class StateStore(BaseStore, metaclass=SingletonMeta):
@@ -32,14 +32,10 @@ from pytest import (
32
32
  skip,
33
33
  )
34
34
 
35
+ from hardpy.common.config import ConfigManager, HardpyConfig
35
36
  from hardpy.common.stand_cloud.connector import StandCloudConnector, StandCloudError
36
37
  from hardpy.pytest_hardpy.reporter import HookReporter
37
- from hardpy.pytest_hardpy.utils import (
38
- ConnectionData,
39
- NodeInfo,
40
- ProgressCalculator,
41
- TestStatus,
42
- )
38
+ from hardpy.pytest_hardpy.utils import NodeInfo, ProgressCalculator, TestStatus
43
39
  from hardpy.pytest_hardpy.utils.node_info import TestDependencyInfo
44
40
 
45
41
  if __debug__:
@@ -51,11 +47,11 @@ if __debug__:
51
47
 
52
48
  def pytest_addoption(parser: Parser) -> None:
53
49
  """Register argparse-style options."""
54
- con_data = ConnectionData()
50
+ default_config = HardpyConfig()
55
51
  parser.addoption(
56
52
  "--hardpy-db-url",
57
53
  action="store",
58
- default=con_data.database_url,
54
+ default=default_config.database.url,
59
55
  help="database url",
60
56
  )
61
57
  parser.addoption(
@@ -90,13 +86,13 @@ def pytest_addoption(parser: Parser) -> None:
90
86
  parser.addoption(
91
87
  "--sc-address",
92
88
  action="store",
93
- default=con_data.sc_address,
89
+ default=default_config.stand_cloud.address,
94
90
  help="StandCloud address",
95
91
  )
96
92
  parser.addoption(
97
93
  "--sc-connection-only",
98
94
  action="store_true",
99
- default=con_data.sc_connection_only,
95
+ default=default_config.stand_cloud.connection_only,
100
96
  help="check StandCloud availability",
101
97
  )
102
98
  parser.addoption(
@@ -144,11 +140,15 @@ class HardpyPlugin:
144
140
 
145
141
  def pytest_configure(self, config: Config) -> None:
146
142
  """Configure pytest."""
147
- con_data = ConnectionData()
143
+ config_manager = ConfigManager()
144
+ hardpy_config = config_manager.read_config(Path(config.rootpath))
145
+
146
+ if not hardpy_config:
147
+ hardpy_config = HardpyConfig()
148
148
 
149
149
  database_url = config.getoption("--hardpy-db-url")
150
150
  if database_url:
151
- con_data.database_url = str(database_url) # type: ignore
151
+ hardpy_config.database.url = str(database_url) # type: ignore
152
152
 
153
153
  tests_name = config.getoption("--hardpy-tests-name")
154
154
  if tests_name:
@@ -160,11 +160,11 @@ class HardpyPlugin:
160
160
 
161
161
  sc_address = config.getoption("--sc-address")
162
162
  if sc_address:
163
- con_data.sc_address = str(sc_address) # type: ignore
163
+ hardpy_config.stand_cloud.address = str(sc_address) # type: ignore
164
164
 
165
165
  sc_connection_only = config.getoption("--sc-connection-only")
166
166
  if sc_connection_only:
167
- con_data.sc_connection_only = bool(sc_connection_only) # type: ignore
167
+ hardpy_config.stand_cloud.connection_only = bool(sc_connection_only) # type: ignore
168
168
 
169
169
  _args = config.getoption("--hardpy-start-arg") or []
170
170
  if _args:
@@ -181,7 +181,7 @@ class HardpyPlugin:
181
181
  # must be init after config data is set
182
182
  try:
183
183
  self._reporter = HookReporter(bool(is_clear_database))
184
- except RuntimeError as exc:
184
+ except Exception as exc: # noqa: BLE001
185
185
  exit(str(exc), ExitCode.INTERNAL_ERROR)
186
186
 
187
187
  def pytest_sessionfinish(self, session: Session, exitstatus: int) -> None:
@@ -246,25 +246,34 @@ class HardpyPlugin:
246
246
 
247
247
  def pytest_runtestloop(self, session: Session) -> bool | None:
248
248
  """Call at the start of test run."""
249
- self._progress.set_test_amount(session.testscollected)
249
+ try:
250
+ self._progress.set_test_amount(session.testscollected)
251
+ except ValueError:
252
+ msg = "No tests collected"
253
+ self._reporter.set_alert(msg)
254
+ self._reporter.update_db_by_doc()
255
+ exit(msg, ExitCode.NO_TESTS_COLLECTED)
250
256
  if session.config.option.collectonly:
251
257
  # ignore collect only mode
252
258
  return True
253
259
 
254
- con_data = ConnectionData()
260
+ config_manager = ConfigManager()
255
261
 
256
262
  # running tests depends on a connection to StandCloud
257
- if con_data.sc_connection_only:
263
+ if config_manager.config.stand_cloud.connection_only:
258
264
  try:
259
- sc_connector = StandCloudConnector(addr=con_data.sc_address)
265
+ sc_connector = StandCloudConnector(
266
+ addr=config_manager.config.stand_cloud.address,
267
+ )
260
268
  except StandCloudError as exc:
261
269
  msg = str(exc)
262
270
  self._reporter.set_alert(msg)
271
+ self._reporter.update_db_by_doc()
263
272
  exit(msg, ExitCode.INTERNAL_ERROR)
264
273
  try:
265
274
  sc_connector.healthcheck()
266
275
  except Exception: # noqa: BLE001
267
- addr = con_data.sc_address
276
+ addr = config_manager.config.stand_cloud.address
268
277
  msg = (
269
278
  f"StandCloud service at the address {addr} "
270
279
  "not available or HardPy user is not authorized"
@@ -337,24 +346,28 @@ class HardpyPlugin:
337
346
  self._reporter.clear_case_data(module_id, case_id)
338
347
  self._reporter.update_db_by_doc()
339
348
 
349
+ # clear the error code if there were no failed tests before
350
+ if caused_dut_failure_id is None:
351
+ self._reporter.clear_error_code()
352
+
340
353
  try:
341
354
  item.runtest()
342
355
  call.excinfo = None
343
356
  self._is_critical_not_passed = False
344
357
  is_dut_failure = False
345
358
  self._reporter.set_case_status(module_id, case_id, TestStatus.PASSED)
346
- # clear the error code if there were no failed tests before
347
- if caused_dut_failure_id is None:
348
- self._reporter.clear_error_code()
349
359
  break
350
360
  except AssertionError:
351
361
  self._reporter.set_case_status(module_id, case_id, TestStatus.FAILED)
352
362
  is_dut_failure = True
353
363
  if current_attempt == attempt:
354
364
  break
355
-
356
365
  # set the caused dut failure id only the first time
357
- if is_dut_failure and caused_dut_failure_id is None:
366
+ if (
367
+ is_dut_failure
368
+ and caused_dut_failure_id is None
369
+ and call.excinfo.typename != "Skipped"
370
+ ):
358
371
  self._reporter.set_caused_dut_failure_id(module_id, case_id)
359
372
 
360
373
  # Reporting hooks