ragger 1.37.1__tar.gz → 1.39.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 (166) hide show
  1. {ragger-1.37.1 → ragger-1.39.0}/.github/workflows/build_and_tests.yml +0 -7
  2. {ragger-1.37.1 → ragger-1.39.0}/.gitignore +0 -1
  3. {ragger-1.37.1 → ragger-1.39.0}/CHANGELOG.md +16 -0
  4. {ragger-1.37.1/src/ragger.egg-info → ragger-1.39.0}/PKG-INFO +2 -2
  5. {ragger-1.37.1 → ragger-1.39.0}/README.md +1 -1
  6. ragger-1.39.0/ledger_app.toml +5 -0
  7. {ragger-1.37.1 → ragger-1.39.0}/pyproject.toml +1 -0
  8. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/__version__.py +2 -2
  9. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/backend/interface.py +7 -7
  10. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/backend/ledgercomm.py +2 -2
  11. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/backend/ledgerwallet.py +2 -2
  12. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/conftest/base_conftest.py +67 -6
  13. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/conftest/configuration.py +24 -19
  14. {ragger-1.37.1 → ragger-1.39.0/src/ragger.egg-info}/PKG-INFO +2 -2
  15. {ragger-1.37.1 → ragger-1.39.0}/src/ragger.egg-info/SOURCES.txt +1 -0
  16. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/conftests/test_base_conftest.py +19 -33
  17. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/utils/test_misc.py +0 -16
  18. {ragger-1.37.1 → ragger-1.39.0}/.codeql-config.yaml +0 -0
  19. {ragger-1.37.1 → ragger-1.39.0}/.gitattributes +0 -0
  20. {ragger-1.37.1 → ragger-1.39.0}/.github/workflows/codeql-analysis.yml +0 -0
  21. {ragger-1.37.1 → ragger-1.39.0}/.github/workflows/documentation.yml +0 -0
  22. {ragger-1.37.1 → ragger-1.39.0}/.github/workflows/fast-checks.yml +0 -0
  23. {ragger-1.37.1 → ragger-1.39.0}/.github/workflows/force-rebase.yml +0 -0
  24. {ragger-1.37.1 → ragger-1.39.0}/LICENSE +0 -0
  25. {ragger-1.37.1 → ragger-1.39.0}/MANIFEST.in +0 -0
  26. {ragger-1.37.1 → ragger-1.39.0}/doc/Makefile +0 -0
  27. {ragger-1.37.1 → ragger-1.39.0}/doc/_static/layout.css +0 -0
  28. {ragger-1.37.1 → ragger-1.39.0}/doc/_templates/layout.html +0 -0
  29. {ragger-1.37.1 → ragger-1.39.0}/doc/architecture.rst +0 -0
  30. {ragger-1.37.1 → ragger-1.39.0}/doc/conf.py +0 -0
  31. {ragger-1.37.1 → ragger-1.39.0}/doc/faq.rst +0 -0
  32. {ragger-1.37.1 → ragger-1.39.0}/doc/glossary.rst +0 -0
  33. {ragger-1.37.1 → ragger-1.39.0}/doc/images/navigate.draw +0 -0
  34. {ragger-1.37.1 → ragger-1.39.0}/doc/images/navigate.svg +0 -0
  35. {ragger-1.37.1 → ragger-1.39.0}/doc/images/ragger.png +0 -0
  36. {ragger-1.37.1 → ragger-1.39.0}/doc/images/stax_infos.png +0 -0
  37. {ragger-1.37.1 → ragger-1.39.0}/doc/images/stax_welcome.png +0 -0
  38. {ragger-1.37.1 → ragger-1.39.0}/doc/images/usage.draw +0 -0
  39. {ragger-1.37.1 → ragger-1.39.0}/doc/images/usage.svg +0 -0
  40. {ragger-1.37.1 → ragger-1.39.0}/doc/index.rst +0 -0
  41. {ragger-1.37.1 → ragger-1.39.0}/doc/installation.rst +0 -0
  42. {ragger-1.37.1 → ragger-1.39.0}/doc/rationale.rst +0 -0
  43. {ragger-1.37.1 → ragger-1.39.0}/doc/source.rst +0 -0
  44. {ragger-1.37.1 → ragger-1.39.0}/doc/tutorial.rst +0 -0
  45. {ragger-1.37.1 → ragger-1.39.0}/doc/tutorial_conftest.rst +0 -0
  46. {ragger-1.37.1 → ragger-1.39.0}/doc/tutorial_installation.rst +0 -0
  47. {ragger-1.37.1 → ragger-1.39.0}/doc/tutorial_screen.rst +0 -0
  48. {ragger-1.37.1 → ragger-1.39.0}/setup.cfg +0 -0
  49. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/__init__.py +0 -0
  50. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/backend/__init__.py +0 -0
  51. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/backend/physical_backend.py +0 -0
  52. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/backend/speculos.py +0 -0
  53. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/backend/stub.py +0 -0
  54. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/bip/__init__.py +0 -0
  55. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/bip/path.py +0 -0
  56. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/bip/seed.py +0 -0
  57. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/conftest/__init__.py +0 -0
  58. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/error.py +0 -0
  59. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/firmware/__init__.py +0 -0
  60. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/firmware/structs.py +0 -0
  61. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/firmware/touch/__init__.py +0 -0
  62. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/firmware/touch/element.py +0 -0
  63. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/firmware/touch/layouts.py +0 -0
  64. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/firmware/touch/positions.py +0 -0
  65. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/firmware/touch/screen.py +0 -0
  66. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/firmware/touch/use_cases.py +0 -0
  67. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/__init__.py +0 -0
  68. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/nanos_body.png +0 -0
  69. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/nanos_leftbutton.png +0 -0
  70. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/nanos_rightbutton.png +0 -0
  71. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/nanosp_body.png +0 -0
  72. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/nanosp_leftbutton.png +0 -0
  73. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/nanosp_rightbutton.png +0 -0
  74. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/nanox_body.png +0 -0
  75. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/nanox_leftbutton.png +0 -0
  76. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/nanox_rightbutton.png +0 -0
  77. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/stax_body.png +0 -0
  78. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/swipe_left_action.png +0 -0
  79. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/swipe_right_action.png +0 -0
  80. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/assets/touch_action.png +0 -0
  81. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/interface.py +0 -0
  82. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/gui/process.py +0 -0
  83. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/logger.py +0 -0
  84. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/navigator/__init__.py +0 -0
  85. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/navigator/instruction.py +0 -0
  86. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/navigator/nano_navigator.py +0 -0
  87. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/navigator/navigation_scenario.py +0 -0
  88. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/navigator/navigator.py +0 -0
  89. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/navigator/touch_navigator.py +0 -0
  90. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/py.typed +0 -0
  91. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/utils/__init__.py +0 -0
  92. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/utils/misc.py +0 -0
  93. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/utils/packing.py +0 -0
  94. {ragger-1.37.1 → ragger-1.39.0}/src/ragger/utils/structs.py +0 -0
  95. {ragger-1.37.1 → ragger-1.39.0}/src/ragger.egg-info/dependency_links.txt +0 -0
  96. {ragger-1.37.1 → ragger-1.39.0}/src/ragger.egg-info/requires.txt +0 -0
  97. {ragger-1.37.1 → ragger-1.39.0}/src/ragger.egg-info/top_level.txt +0 -0
  98. {ragger-1.37.1 → ragger-1.39.0}/template/.dispatch_to_your_test_folder +0 -0
  99. {ragger-1.37.1 → ragger-1.39.0}/template/conftest.py +0 -0
  100. {ragger-1.37.1 → ragger-1.39.0}/template/usage.md +0 -0
  101. {ragger-1.37.1 → ragger-1.39.0}/tests/__init__.py +0 -0
  102. {ragger-1.37.1 → ragger-1.39.0}/tests/functional/__init__.py +0 -0
  103. {ragger-1.37.1 → ragger-1.39.0}/tests/functional/backend/__init__.py +0 -0
  104. {ragger-1.37.1 → ragger-1.39.0}/tests/functional/backend/test_speculos.py +0 -0
  105. {ragger-1.37.1 → ragger-1.39.0}/tests/functional/conftest.py +0 -0
  106. {ragger-1.37.1 → ragger-1.39.0}/tests/functional/navigator/__init__.py +0 -0
  107. {ragger-1.37.1 → ragger-1.39.0}/tests/functional/navigator/test_navigator.py +0 -0
  108. {ragger-1.37.1 → ragger-1.39.0}/tests/functional/test_boilerplate.py +0 -0
  109. {ragger-1.37.1 → ragger-1.39.0}/tests/pytest.ini +0 -0
  110. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/flex/waiting_screen/00000.png +0 -0
  111. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/flex/waiting_screen/00001.png +0 -0
  112. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/flex/waiting_screen/00002.png +0 -0
  113. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/flex/waiting_screen/00003.png +0 -0
  114. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/flex/waiting_screen/00004.png +0 -0
  115. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/generic/00000.png +0 -0
  116. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/generic/00001.png +0 -0
  117. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/generic/00002.png +0 -0
  118. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/generic/00003.png +0 -0
  119. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/generic/00004.png +0 -0
  120. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_and_compare/00000.png +0 -0
  121. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_and_compare/00001.png +0 -0
  122. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_and_compare/00002.png +0 -0
  123. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_and_compare/00003.png +0 -0
  124. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_and_compare/00004.png +0 -0
  125. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_and_compare/00005.png +0 -0
  126. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_and_compare/00006.png +0 -0
  127. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_and_compare/00007.png +0 -0
  128. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_and_compare_no_golden/00000.png +0 -0
  129. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_and_compare_wrong_golden/00000.png +0 -0
  130. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_and_compare_wrong_golden/00001.png +0 -0
  131. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_until_text_and_compare/00000.png +0 -0
  132. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_until_text_and_compare/00001.png +0 -0
  133. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_until_text_and_compare/00002.png +0 -0
  134. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/nanos/test_navigate_until_text_and_compare/00003.png +0 -0
  135. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/stax/waiting_screen/00000.png +0 -0
  136. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/stax/waiting_screen/00001.png +0 -0
  137. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/stax/waiting_screen/00002.png +0 -0
  138. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/stax/waiting_screen/00003.png +0 -0
  139. {ragger-1.37.1 → ragger-1.39.0}/tests/snapshots/stax/waiting_screen/00004.png +0 -0
  140. {ragger-1.37.1 → ragger-1.39.0}/tests/stubs.py +0 -0
  141. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/__init__.py +0 -0
  142. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/backend/__init__.py +0 -0
  143. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/backend/test_interface.py +0 -0
  144. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/backend/test_ledgercomm.py +0 -0
  145. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/backend/test_ledgerwallet.py +0 -0
  146. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/backend/test_physical_backend.py +0 -0
  147. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/backend/test_speculos.py +0 -0
  148. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/backend/test_stub.py +0 -0
  149. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/bip/test_path.py +0 -0
  150. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/bip/test_seed.py +0 -0
  151. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/conftests/__init__.py +0 -0
  152. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/firmware/test_structs_Firmware.py +0 -0
  153. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/firmware/touch/test_element.py +0 -0
  154. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/firmware/touch/test_screen_FullScreen.py +0 -0
  155. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/firmware/touch/test_screen_MetaScreen.py +0 -0
  156. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/helpers.py +0 -0
  157. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/navigator/__init__.py +0 -0
  158. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/navigator/test_nano_navigator.py +0 -0
  159. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/navigator/test_navigation_scenario.py +0 -0
  160. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/navigator/test_navigator.py +0 -0
  161. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/navigator/test_touch_navigator.py +0 -0
  162. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/test_error_ApplicationError.py +0 -0
  163. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/utils/__init__.py +0 -0
  164. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/utils/test_packing.py +0 -0
  165. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/utils/test_path.py +0 -0
  166. {ragger-1.37.1 → ragger-1.39.0}/tests/unit/utils/test_structs.py +0 -0
@@ -76,13 +76,6 @@ jobs:
76
76
  name: boilerplate_binaries_nanos
77
77
  path: ./build/
78
78
 
79
- - name: Create boilerplate manifest
80
- run: |
81
- echo """[app]
82
- build_directory = \"./\"
83
- sdk = \"C\"
84
- devices = [\"nanos\", \"nanos+\", \"nanox\", \"stax\", \"flex\"]""" > ledger_app.toml
85
-
86
79
  - name: Check the downloaded files
87
80
  run: tree .
88
81
 
@@ -9,7 +9,6 @@ __version__.py
9
9
  .python-version
10
10
 
11
11
  tests/snapshots-tmp/
12
- ledger_app.toml
13
12
 
14
13
  # doc
15
14
  doc/images/*generated*
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.39.0] - 2025-07-22
9
+
10
+ ### Added
11
+
12
+ - Align libraries loading for swap standalone tests with library loading for eth plugin tests
13
+
14
+ ## [1.38.0] - 2025-07-16
15
+
16
+ ### Changed
17
+
18
+ - `log_apdu_file` option allows to set the filename pattern when executing multiple tests
19
+
20
+ ### Fixed
21
+
22
+ - yield true on async to notify the action has been completed (physical backends)
23
+
8
24
  ## [1.37.1] - 2025-05-16
9
25
 
10
26
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ragger
3
- Version: 1.37.1
3
+ Version: 1.39.0
4
4
  Summary: Testing framework using Speculos and LedgerComm as backends
5
5
  Author-email: Ledger <hello@ledger.fr>
6
6
  Project-URL: Homepage, https://github.com/LedgerHQ/ragger
@@ -79,7 +79,7 @@ It mainly consists of an interface which is implemented by three backends:
79
79
 
80
80
  - two physical backends (although technically they are agnostic, but the
81
81
  `SpeculosClient` is superior with an emulator), `LedgerCommBackend` and
82
- `LedgerWalletbackend`, which use respectively the
82
+ `LedgerWalletBackend`, which use respectively the
83
83
  [`LedgerComm` library](https://github.com/LedgerHQ/ledgercomm) or the
84
84
  [`LedgerWallet` library](https://github.com/LedgerHQ/ledgerctl/) to discuss
85
85
  with a physical device. In these cases, the physical device must be started,
@@ -17,7 +17,7 @@ It mainly consists of an interface which is implemented by three backends:
17
17
 
18
18
  - two physical backends (although technically they are agnostic, but the
19
19
  `SpeculosClient` is superior with an emulator), `LedgerCommBackend` and
20
- `LedgerWalletbackend`, which use respectively the
20
+ `LedgerWalletBackend`, which use respectively the
21
21
  [`LedgerComm` library](https://github.com/LedgerHQ/ledgercomm) or the
22
22
  [`LedgerWallet` library](https://github.com/LedgerHQ/ledgerctl/) to discuss
23
23
  with a physical device. In these cases, the physical device must be started,
@@ -0,0 +1,5 @@
1
+ # Fake boilerplate application manifest for ragger tests
2
+ [app]
3
+ build_directory = "./"
4
+ sdk = "C"
5
+ devices = ["nanos", "nanos+", "nanox", "stax", "flex"]
@@ -118,3 +118,4 @@ exclude_lines = [
118
118
 
119
119
  [tool.flake8]
120
120
  max-line-length = 120
121
+ extend-ignore = "E502"
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '1.37.1'
21
- __version_tuple__ = version_tuple = (1, 37, 1)
20
+ __version__ = version = '1.39.0'
21
+ __version_tuple__ = version_tuple = (1, 39, 0)
@@ -144,8 +144,8 @@ class BackendInterface(ABC):
144
144
  :type ins: int
145
145
  :param p1: First instruction parameter, defaults to 0
146
146
  :type p1: int
147
- :param p1: Second instruction parameter, defaults to 0
148
- :type p1: int
147
+ :param p2: Second instruction parameter, defaults to 0
148
+ :type p2: int
149
149
  :param data: Command data
150
150
  :type data: bytes
151
151
 
@@ -205,8 +205,8 @@ class BackendInterface(ABC):
205
205
  :type ins: int
206
206
  :param p1: First instruction parameter, defaults to 0
207
207
  :type p1: int
208
- :param p1: Second instruction parameter, defaults to 0
209
- :type p1: int
208
+ :param p2: Second instruction parameter, defaults to 0
209
+ :type p2: int
210
210
  :param data: Command data
211
211
  :type data: bytes
212
212
 
@@ -262,8 +262,8 @@ class BackendInterface(ABC):
262
262
  :type ins: int
263
263
  :param p1: First instruction parameter, defaults to 0
264
264
  :type p1: int
265
- :param p1: Second instruction parameter, defaults to 0
266
- :type p1: int
265
+ :param p2: Second instruction parameter, defaults to 0
266
+ :type p2: int
267
267
  :param data: Command data
268
268
  :type data: bytes
269
269
 
@@ -292,7 +292,7 @@ class BackendInterface(ABC):
292
292
 
293
293
  :raises ExceptionRAPDU: If the `raises` attribute is True, this method
294
294
  will raise if the backend returns a status code
295
- not registered a a `valid_statuses`
295
+ not registered as a `valid_statuses`
296
296
 
297
297
  :return: None
298
298
  :rtype: NoneType
@@ -106,7 +106,7 @@ class LedgerCommBackend(PhysicalBackend):
106
106
  return result
107
107
 
108
108
  @contextmanager
109
- def exchange_async_raw(self, data: bytes = b"") -> Generator[None, None, None]:
109
+ def exchange_async_raw(self, data: bytes = b"") -> Generator[bool, None, None]:
110
110
  self.send_raw(data)
111
- yield
111
+ yield True
112
112
  self._last_async_response = self.receive()
@@ -98,7 +98,7 @@ class LedgerWalletBackend(PhysicalBackend):
98
98
  return result
99
99
 
100
100
  @contextmanager
101
- def exchange_async_raw(self, data: bytes = b"") -> Generator[None, None, None]:
101
+ def exchange_async_raw(self, data: bytes = b"") -> Generator[bool, None, None]:
102
102
  self.send_raw(data)
103
- yield
103
+ yield True
104
104
  self._last_async_response = self.receive()
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import pytest
2
3
  import logging
3
4
  from dataclasses import fields
@@ -39,7 +40,12 @@ def pytest_addoption(parser):
39
40
  action="store_true",
40
41
  default=False,
41
42
  help="Have Speculos accept prod PKI certificates instead of test")
42
- parser.addoption("--log_apdu_file", action="store", default=None, help="Log the APDU in a file")
43
+ parser.addoption("--log_apdu_file",
44
+ action="store",
45
+ default=None,
46
+ nargs="?",
47
+ const="apdu.log",
48
+ help="Log the APDU in a file. If no pattern provided, uses 'apdu_xxx.log'.")
43
49
  parser.addoption("--seed", action="store", default=None, help="Set a custom seed")
44
50
  # Always allow "default" even if application conftest does not define it
45
51
  allowed_setups = conf.OPTIONAL.ALLOWED_SETUPS
@@ -72,10 +78,23 @@ def golden_run(pytestconfig):
72
78
  return pytestconfig.getoption("golden_run")
73
79
 
74
80
 
75
- @pytest.fixture(scope="session")
76
- def log_apdu_file(pytestconfig):
81
+ @pytest.fixture(scope=conf.OPTIONAL.BACKEND_SCOPE)
82
+ def log_apdu_file(request, pytestconfig, full_test_name: str):
77
83
  filename = pytestconfig.getoption("log_apdu_file")
78
- return Path(filename).resolve() if filename is not None else None
84
+ if filename is None:
85
+ return None
86
+
87
+ path = Path(filename)
88
+ # Get nb of collected tests to be executed
89
+ collected_items = len(request.session.items)
90
+ if collected_items == 1:
91
+ # Single test - use filename directly
92
+ return path.resolve()
93
+ # Multiple tests - create a file per test
94
+ MAX_TEST_NAME_LENGTH = 100 # Define a safe maximum length for test names
95
+ truncated_test_name = full_test_name[:MAX_TEST_NAME_LENGTH] # Truncate if necessary
96
+ new_filename = f"{path.stem}_{truncated_test_name}{path.suffix}"
97
+ return Path(new_filename).resolve()
79
98
 
80
99
 
81
100
  @pytest.fixture(scope="session")
@@ -112,6 +131,37 @@ def default_screenshot_path(root_pytest_dir: Path) -> Path:
112
131
  return root_pytest_dir
113
132
 
114
133
 
134
+ @pytest.fixture(scope=conf.OPTIONAL.BACKEND_SCOPE)
135
+ def full_test_name(request) -> str:
136
+ # Get the name of current pytest test
137
+ test_name = request.node.name
138
+
139
+ if '[' in test_name:
140
+ # Split all parameters
141
+ base_name, params = test_name.rsplit('[', 1)
142
+ params = params.rstrip(']')
143
+
144
+ # Split parameters by '-' and filter out device names
145
+ param_list = [p for p in params.split('-') if p not in DEVICES]
146
+
147
+ # Rebuild test name with remaining parameters
148
+ if param_list:
149
+ test_name = f"{base_name}[{'-'.join(param_list)}]"
150
+ else:
151
+ test_name = base_name
152
+
153
+ # Clean up for filename friendliness by replacing special characters with underscores
154
+ translation_table = str.maketrans({
155
+ '[': '_',
156
+ ']': '',
157
+ '-': '_',
158
+ '/': '_',
159
+ "'": '',
160
+ })
161
+ clean_name = test_name.translate(translation_table)
162
+ return clean_name
163
+
164
+
115
165
  @pytest.fixture
116
166
  def test_name(request) -> str:
117
167
  # Get the name of current pytest test
@@ -225,15 +275,16 @@ def prepare_speculos_args(root_pytest_dir: Path,
225
275
  main_app_path = find_application(project_root_dir / manifest.app.build_directory,
226
276
  device_name, manifest.app.sdk)
227
277
 
228
- # Find all libraries that have to be sideloaded
278
+ # If this repository does not hold the main app, then we need to load this repository's application as a library
229
279
  if conf.OPTIONAL.MAIN_APP_DIR is not None:
230
280
  # This repo holds the library, not the standalone app: search in root_dir/build
231
281
  lib_path = find_application(project_root_dir, device_name, manifest.app.sdk)
232
282
  speculos_args.append(f"-l{lib_path}")
233
283
 
284
+ # Legacy lib method, remove once exchange is ported
234
285
  if len(conf.OPTIONAL.SIDELOADED_APPS) != 0:
235
286
  # We are testing a a standalone app that needs libraries: search in SIDELOADED_APPS_DIR
236
- if conf.OPTIONAL.SIDELOADED_APPS_DIR == "":
287
+ if conf.OPTIONAL.SIDELOADED_APPS_DIR is None:
237
288
  raise ValueError("Configuration \"SIDELOADED_APPS_DIR\" is mandatory if "
238
289
  "\"SIDELOADED_APPS\" is used")
239
290
  libs_dir = Path(project_root_dir / conf.OPTIONAL.SIDELOADED_APPS_DIR)
@@ -241,6 +292,16 @@ def prepare_speculos_args(root_pytest_dir: Path,
241
292
  for coin_name, lib_name in conf.OPTIONAL.SIDELOADED_APPS.items():
242
293
  lib_path = find_library_application(libs_dir, coin_name, device_name)
243
294
  speculos_args.append(f"-l{lib_name}:{lib_path}")
295
+ else:
296
+ # Keep this method instead
297
+ # Find all external libraries that have to be sideloaded
298
+ if conf.OPTIONAL.SIDELOADED_APPS_DIR is not None:
299
+ sideloaded_dir = project_root_dir / conf.OPTIONAL.SIDELOADED_APPS_DIR
300
+ subdirs = sorted(
301
+ filter(lambda d: (sideloaded_dir / d).is_dir(), os.listdir(sideloaded_dir)))
302
+ for subdir in subdirs:
303
+ lib_path = find_application(sideloaded_dir / subdir, device_name, manifest.app.sdk)
304
+ speculos_args.append(f"-l{lib_path}")
244
305
 
245
306
  # Check if custom user seed has been provided through CLI or optional configuration.
246
307
  # CLI user seed has priority over the optional configuration seed.
@@ -7,37 +7,39 @@ class OptionalOptions:
7
7
  APP_NAME: str
8
8
  MAIN_APP_DIR: Optional[str]
9
9
  SIDELOADED_APPS: dict
10
- SIDELOADED_APPS_DIR: str
10
+ SIDELOADED_APPS_DIR: Optional[str]
11
11
  BACKEND_SCOPE: str
12
12
  CUSTOM_SEED: str
13
13
  ALLOWED_SETUPS: List[str]
14
14
 
15
15
 
16
16
  OPTIONAL = OptionalOptions(
17
- # Use this parameter if you want to make sure the right app is started upon backend instantiation.
18
- # This is only used for LedgerWallet and LedgerComm backend.
17
+ # Use this parameter if you want physical Ragger backends (LedgerWallet and LedgerComm) to start
18
+ # your application from the Dashboard at test start.
19
19
  APP_NAME=str(),
20
20
 
21
- # If not None, the app being tested with Ragger should be loaded as a library and not as a standalone
22
- # app. This parameter points to the repository holding the "main app", i.e the app started by the
23
- # backend, which will then use the "local app" as a library.
24
- # Use cases : your app is an Ethereum plugin, which will be used as a library by the Ethereum app.
25
- # MAIN_APP_DIR contains the directory where the Ethereum app binaries are stored.
26
- # example: "./tests/"
21
+ # If not None, the application being tested with Ragger should be loaded as a library and not as
22
+ # a standalone application. This parameter points to the repository holding the "main app", i.e
23
+ # the application started from the Dashboard, which will then use the "local app" as a library.
24
+ # Example use case: your application is an Ethereum plugin, which will be used as a library by
25
+ # the Ethereum application.
26
+ # MAIN_APP_DIR is the directory where the Ethereum application is cloned.
27
+ #
28
+ # example: configuration.OPTIONAL.MAIN_APP_DIR = "tests/.test_dependencies/"
29
+ # Speculos will then start "tests/.test_dependencies/ethereum/build/<device>/bin/app.elf"
30
+ # There must be exactly one application cloned inside this directory.
27
31
  MAIN_APP_DIR=None,
28
32
 
29
- # use this parameter if you want Speculos to sideload libraries being installed on the device
30
- # Useful for Ethereum main app and exchange app, for example
31
- # example: {"bitcoin": "Bitcoin", "ethereum": "Ethereum"}
32
- # this would result in Speculos being launched with -lbitcoin:/path/to/bitcoin_device.elf
33
- # Mutually exclusive with LOAD_MAIN_APP_AS_LIBRARY
33
+ # Deprecated
34
34
  SIDELOADED_APPS=dict(),
35
35
 
36
- # Mandatory in case SIDELOADED_APPS is used
37
- # Relative path to the directory that will store your libs, from the top repository of your application
38
- # example: "test/lib_binaries/"
39
- # place your compiled application inside with naming "name_device.elf"
40
- SIDELOADED_APPS_DIR=str(),
36
+ # Relative path to the directory that will store library applications needed for the test.
37
+ # They will be sideloaded by Speculos and can then be called by the main application. This
38
+ # emulates the applications being installed on the device alongside the main application.
39
+ #
40
+ # example: configuration.OPTIONAL.SIDELOADED_APPS_DIR = "tests/.test_dependencies/libraries"
41
+ # Speculos will then sideload "tests/.test_dependencies/libraries/*/build/<device>/bin/app.elf"
42
+ SIDELOADED_APPS_DIR=None,
41
43
 
42
44
  # As the backend instantiation may take some time, Ragger supports multiple backend scopes.
43
45
  # You can choose to share the backend instance between {session / module / class / function}
@@ -48,8 +50,10 @@ OPTIONAL = OptionalOptions(
48
50
  # Use this parameter if you want speculos to use a custom seed instead of the default one.
49
51
  # This would result in speculos being launched with --seed <CUSTOM_SEED>
50
52
  # If a seed is provided through the "--seed" pytest command line option, it will override this one.
53
+ # /!\ DO NOT USE SEEDS WITH REAL FUNDS /!\
51
54
  CUSTOM_SEED=str(),
52
55
 
56
+ # /!\ DEPRECATED /!\
53
57
  # Use this parameter if you want ragger to handle running different test suites depending on setup
54
58
  # Useful when some tests need certain build options and other tests need other build options, or a
55
59
  # different Speculos command line
@@ -59,5 +63,6 @@ OPTIONAL = OptionalOptions(
59
63
  #
60
64
  # "default" setup is always allowed, all tests without explicit decoration depend on default
61
65
  # and the --setup option defaults to "default"
66
+ # /!\ DEPRECATED /!\
62
67
  ALLOWED_SETUPS=["default"],
63
68
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ragger
3
- Version: 1.37.1
3
+ Version: 1.39.0
4
4
  Summary: Testing framework using Speculos and LedgerComm as backends
5
5
  Author-email: Ledger <hello@ledger.fr>
6
6
  Project-URL: Homepage, https://github.com/LedgerHQ/ragger
@@ -79,7 +79,7 @@ It mainly consists of an interface which is implemented by three backends:
79
79
 
80
80
  - two physical backends (although technically they are agnostic, but the
81
81
  `SpeculosClient` is superior with an emulator), `LedgerCommBackend` and
82
- `LedgerWalletbackend`, which use respectively the
82
+ `LedgerWalletBackend`, which use respectively the
83
83
  [`LedgerComm` library](https://github.com/LedgerHQ/ledgercomm) or the
84
84
  [`LedgerWallet` library](https://github.com/LedgerHQ/ledgerctl/) to discuss
85
85
  with a physical device. In these cases, the physical device must be started,
@@ -5,6 +5,7 @@ CHANGELOG.md
5
5
  LICENSE
6
6
  MANIFEST.in
7
7
  README.md
8
+ ledger_app.toml
8
9
  pyproject.toml
9
10
  .github/workflows/build_and_tests.yml
10
11
  .github/workflows/codeql-analysis.yml
@@ -1,3 +1,4 @@
1
+ import os
1
2
  from dataclasses import dataclass
2
3
  from ledgered.devices import DeviceType, Devices
3
4
  from pathlib import Path
@@ -91,43 +92,28 @@ class TestBaseConftest(TestCase):
91
92
  self.assertEqual(result_app, dep_path)
92
93
  self.assertEqual(result_args, {"args": [f"-l{app_path}", "--seed", self.seed]})
93
94
 
94
- def test_prepare_speculos_args_sideloaded_apps_nok_no_dir(self):
95
- with temporary_directory() as temp_dir:
96
- prepare_base_dir(temp_dir)
97
- with patch("ragger.conftest.base_conftest.conf.OPTIONAL.SIDELOADED_APPS",
98
- ["more than 1 elt"]):
99
- with patch("ragger.conftest.base_conftest.Manifest", ManifestMock):
100
- with self.assertRaises(ValueError):
101
- bc.prepare_speculos_args(temp_dir, self.stax, False, False, self.seed, [])
102
-
103
95
  def test_prepare_speculos_args_sideloaded_apps_ok(self):
104
- lib1_bin, lib1_name, lib2_bin, lib2_name = "lib1", "name1", "lib2", "name2"
105
96
  with temporary_directory() as temp_dir:
106
97
  app_path, _ = prepare_base_dir(temp_dir)
107
98
  sideloaded_apps_dir = temp_dir / "here"
108
- sideloaded_apps_dir.mkdir()
109
- lib1_path = sideloaded_apps_dir / f"{lib1_bin}_stax.elf"
110
- lib2_path = sideloaded_apps_dir / f"{lib2_bin}_stax.elf"
111
- lib1_path.touch()
112
- lib2_path.touch()
113
- with patch("ragger.conftest.base_conftest.conf.OPTIONAL.SIDELOADED_APPS", {
114
- lib1_bin: lib1_name,
115
- lib2_bin: lib2_name
116
- }):
117
- with patch("ragger.conftest.base_conftest.conf.OPTIONAL.SIDELOADED_APPS_DIR",
118
- sideloaded_apps_dir):
119
-
120
- with patch("ragger.conftest.base_conftest.Manifest", ManifestMock):
121
- result_app, result_args = bc.prepare_speculos_args(
122
- temp_dir, self.stax, False, False, self.seed, [])
123
- self.assertEqual(result_app, app_path)
124
- self.assertEqual(
125
- result_args, {
126
- "args": [
127
- f"-l{lib1_name}:{lib1_path}", f"-l{lib2_name}:{lib2_path}",
128
- "--seed", self.seed
129
- ]
130
- })
99
+ lib1_path = sideloaded_apps_dir / "lib1/build/stax/bin/"
100
+ lib2_path = sideloaded_apps_dir / "lib2/build/stax/bin/"
101
+ os.makedirs(lib1_path)
102
+ os.makedirs(lib2_path)
103
+ lib1_exe = lib1_path / "app.elf"
104
+ lib2_exe = lib2_path / "app.elf"
105
+ lib1_exe.touch()
106
+ lib2_exe.touch()
107
+
108
+ with patch("ragger.conftest.base_conftest.conf.OPTIONAL.SIDELOADED_APPS_DIR",
109
+ sideloaded_apps_dir):
110
+
111
+ with patch("ragger.conftest.base_conftest.Manifest", ManifestMock):
112
+ result_app, result_args = bc.prepare_speculos_args(
113
+ temp_dir, self.stax, False, False, self.seed, [])
114
+ self.assertEqual(result_app, app_path)
115
+ self.assertEqual(result_args,
116
+ {"args": [f"-l{lib1_exe}", f"-l{lib2_exe}", "--seed", self.seed]})
131
117
 
132
118
  def test_create_backend_nok(self):
133
119
  with self.assertRaises(ValueError):
@@ -12,22 +12,6 @@ from ..helpers import temporary_directory
12
12
 
13
13
  class TestMisc(TestCase):
14
14
 
15
- def test_app_path_from_app_name_ok(self):
16
- with temporary_directory() as dir_path:
17
- app_name, device = "some_name", "some_device"
18
- expected = (dir_path / f"{app_name}_{device}.elf")
19
- expected.touch()
20
- self.assertEqual(expected, misc.find_library_application(dir_path, app_name, device))
21
-
22
- def test_app_path_from_app_name_nok_no_directory(self):
23
- with self.assertRaises(AssertionError):
24
- misc.find_library_application(Path("/this/does/not/exists/"), "a", "b")
25
-
26
- def test_app_path_from_app_name_nok_no_file(self):
27
- with temporary_directory() as dir_path:
28
- with self.assertRaises(AssertionError):
29
- misc.find_library_application(dir_path, "a", "b")
30
-
31
15
  def test_find_application_ok_c(self):
32
16
  device, sdk = "device", "sdk"
33
17
  with temporary_directory() as dir_path:
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