pytest_fatfsgen_example.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. # SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  2. # SPDX-License-Identifier: Unlicense OR CC0-1.0
  3. import os
  4. import re
  5. import shutil
  6. import sys
  7. from datetime import datetime
  8. from subprocess import STDOUT, run
  9. from typing import List
  10. import pytest
  11. from pytest_embedded import Dut
  12. idf_path = os.environ['IDF_PATH'] # get value of IDF_PATH from environment
  13. parttool_dir = os.path.join(idf_path, 'components', 'partition_table')
  14. sys.path.append(parttool_dir)
  15. from parttool import PartitionName, ParttoolTarget # noqa E402 # pylint: disable=C0413
  16. def file_(x: str, content_: str = 'hey this is a test') -> dict:
  17. return {
  18. 'type': 'file',
  19. 'name': x,
  20. 'content': content_
  21. }
  22. def generate_local_folder_structure(structure_: dict, path_: str) -> None:
  23. if structure_['type'] == 'folder':
  24. new_path_ = os.path.join(path_, structure_['name'])
  25. os.makedirs(new_path_)
  26. for item_ in structure_['content']:
  27. generate_local_folder_structure(item_, new_path_)
  28. else:
  29. new_path_ = os.path.join(path_, structure_['name'])
  30. with open(new_path_, 'w') as f_:
  31. f_.write(structure_['content'])
  32. def compare_folders(fp1: str, fp2: str) -> bool:
  33. if os.path.isdir(fp1) != os.path.isdir(fp2):
  34. return False
  35. if os.path.isdir(fp1):
  36. if set(os.listdir(fp1)) != set(os.listdir(fp2)):
  37. return False
  38. return all([compare_folders(os.path.join(fp1, path_), os.path.join(fp2, path_)) for path_ in os.listdir(fp1)])
  39. with open(fp1, 'rb') as f1_, open(fp2, 'rb') as f2_:
  40. return f1_.read() == f2_.read()
  41. # Example_GENERIC
  42. @pytest.mark.esp32
  43. @pytest.mark.parametrize('config', ['test_read_only_partition_gen',
  44. 'test_read_only_partition_gen_default_dt',
  45. 'test_read_only_partition_gen_ln',
  46. 'test_read_only_partition_gen_ln_default_dt',
  47. 'test_read_write_partition_gen',
  48. 'test_read_write_partition_gen_default_dt',
  49. 'test_read_write_partition_gen_ln',
  50. 'test_read_write_partition_gen_ln_default_dt',
  51. ], indirect=True)
  52. def test_examples_fatfsgen(config: str, dut: Dut) -> None:
  53. # Expects list of strings sequentially
  54. def expect_all(msg_list: List[str], to: int) -> None:
  55. for msg in msg_list:
  56. dut.expect(msg, timeout=to)
  57. # Expects prefix string followed by date in the format 'yyyy-mm-dd'
  58. def expect_date(prefix: str, to: int) -> datetime:
  59. expect_str = prefix + '(\\d+)-(\\d+)-(\\d+)'
  60. match_ = dut.expect(re.compile(str.encode(expect_str)), timeout=to)
  61. year_ = int(match_[1].decode())
  62. month_ = int(match_[2].decode())
  63. day_ = int(match_[3].decode())
  64. return datetime(year_, month_, day_)
  65. # Calculates absolute difference in days between date_reference and date_actual.
  66. # Raises exception if difference exceeds tolerance
  67. def evaluate_dates(date_reference: datetime, date_actual: datetime, days_tolerance: int) -> None:
  68. td = date_actual - date_reference
  69. if abs(td.days) > days_tolerance:
  70. raise Exception(f'Too big date difference. Actual: {date_actual}, reference: {date_reference}, tolerance: {days_tolerance} day(s)')
  71. # Expect timeout
  72. timeout = 20
  73. # We tolerate 30 days difference between actual file creation and date when test was executed.
  74. tolerance = 30
  75. filename_ln = 'sublongnames/testlongfilenames.txt'
  76. filename_sn = 'sub/test.txt'
  77. date_modified = datetime.today()
  78. date_default = datetime(1980, 1, 1)
  79. fatfs_parser_path = os.path.join(idf_path, 'components', 'fatfs', 'fatfsparse.py')
  80. if config in ['test_read_write_partition_gen', 'test_read_write_partition_gen_default_dt']:
  81. filename = filename_sn
  82. filename_expected = f'/spiflash/{filename}'
  83. date_ref = date_default if config == 'test_read_write_partition_gen_default_dt' else date_modified
  84. expect_all(['example: Mounting FAT filesystem',
  85. 'example: Opening file',
  86. 'example: File written',
  87. 'example: Reading file',
  88. 'example: Read from file: \'This is written by the device\'',
  89. 'example: Reading file'], timeout)
  90. date_act = expect_date(f'The file \'{filename_expected}\' was modified at date: ', timeout)
  91. evaluate_dates(date_ref, date_act, tolerance)
  92. expect_all(['example: Read from file: \'This is generated on the host\'',
  93. 'example: Unmounting FAT filesystem',
  94. 'example: Done'], timeout)
  95. target = ParttoolTarget(dut.port)
  96. target.read_partition(PartitionName('storage'), 'temp.img')
  97. run(['python', fatfs_parser_path, '--wear-leveling', 'temp.img'], stderr=STDOUT)
  98. folder_ = {
  99. 'type': 'folder',
  100. 'name': 'SUB',
  101. 'content': [
  102. file_('TEST.TXT', content_='this is test\n'),
  103. ]
  104. }
  105. struct_: dict = {
  106. 'type': 'folder',
  107. 'name': 'testf',
  108. 'content': [
  109. file_('HELLO.TXT', content_='This is generated on the host\n'),
  110. file_('INNER.TXT', content_='This is written by the device'),
  111. folder_
  112. ]
  113. }
  114. generate_local_folder_structure(struct_, path_='.')
  115. try:
  116. assert compare_folders('testf', 'Espressif')
  117. finally:
  118. shutil.rmtree('Espressif', ignore_errors=True)
  119. shutil.rmtree('testf', ignore_errors=True)
  120. elif config in ['test_read_only_partition_gen', 'test_read_only_partition_gen_default_dt']:
  121. filename = filename_sn
  122. filename_expected = f'/spiflash/{filename}'
  123. date_ref = date_default if config == 'test_read_only_partition_gen_default_dt' else date_modified
  124. expect_all(['example: Mounting FAT filesystem',
  125. 'example: Reading file'], timeout)
  126. date_act = expect_date(f'The file \'{filename_expected}\' was modified at date: ', timeout)
  127. evaluate_dates(date_ref, date_act, tolerance)
  128. expect_all(['example: Read from file: \'this is test\'',
  129. 'example: Unmounting FAT filesystem',
  130. 'example: Done'], timeout)
  131. target = ParttoolTarget(dut.port)
  132. target.read_partition(PartitionName('storage'), 'temp.img')
  133. run(['python', fatfs_parser_path, '--long-name-support', 'temp.img'], stderr=STDOUT)
  134. folder_ = {
  135. 'type': 'folder',
  136. 'name': 'sublongnames',
  137. 'content': [
  138. file_('testlongfilenames.txt', content_='this is test; long name it has\n'),
  139. ]
  140. }
  141. struct_ = {
  142. 'type': 'folder',
  143. 'name': 'testf',
  144. 'content': [
  145. file_('hellolongname.txt', content_='This is generated on the host; long name it has\n'),
  146. folder_
  147. ]
  148. }
  149. generate_local_folder_structure(struct_, path_='.')
  150. try:
  151. assert compare_folders('testf', 'Espressif')
  152. finally:
  153. shutil.rmtree('Espressif', ignore_errors=True)
  154. shutil.rmtree('testf', ignore_errors=True)
  155. elif config in ['test_read_write_partition_gen_ln', 'test_read_write_partition_gen_ln_default_dt']:
  156. filename = filename_ln
  157. filename_expected = f'/spiflash/{filename}'
  158. date_ref = date_default if config == 'test_read_write_partition_gen_ln_default_dt' else date_modified
  159. expect_all(['example: Mounting FAT filesystem',
  160. 'example: Opening file',
  161. 'example: File written',
  162. 'example: Reading file',
  163. 'example: Read from file: \'This is written by the device\'',
  164. 'example: Reading file'], timeout)
  165. date_act = expect_date(f'The file \'{filename_expected}\' was modified at date: ', timeout)
  166. evaluate_dates(date_ref, date_act, tolerance)
  167. expect_all(['example: Read from file: \'This is generated on the host; long name it has\'',
  168. 'example: Unmounting FAT filesystem',
  169. 'example: Done'], timeout)
  170. elif config in ['test_read_only_partition_gen_ln', 'test_read_only_partition_gen_ln_default_dt']:
  171. filename = filename_ln
  172. filename_expected = f'/spiflash/{filename}'
  173. date_ref = date_default if config == 'test_read_only_partition_gen_ln_default_dt' else date_modified
  174. expect_all(['example: Mounting FAT filesystem',
  175. 'example: Reading file'], timeout)
  176. date_act = expect_date(f'The file \'{filename_expected}\' was modified at date: ', timeout)
  177. evaluate_dates(date_ref, date_act, tolerance)
  178. expect_all(['example: Read from file: \'this is test; long name it has\'',
  179. 'example: Unmounting FAT filesystem',
  180. 'example: Done'], timeout)
  181. target = ParttoolTarget(dut.port)
  182. target.read_partition(PartitionName('storage'), 'temp.img')
  183. run(['python', fatfs_parser_path, 'temp.img'], stderr=STDOUT)
  184. folder_ = {
  185. 'type': 'folder',
  186. 'name': 'SUB',
  187. 'content': [
  188. file_('TEST.TXT', content_='this is test\n'),
  189. ]
  190. }
  191. struct_ = {
  192. 'type': 'folder',
  193. 'name': 'testf',
  194. 'content': [
  195. file_('HELLO.TXT', content_='This is generated on the host\n'),
  196. folder_
  197. ]
  198. }
  199. generate_local_folder_structure(struct_, path_='.')
  200. try:
  201. assert compare_folders('testf', 'Espressif')
  202. finally:
  203. shutil.rmtree('Espressif', ignore_errors=True)
  204. shutil.rmtree('testf', ignore_errors=True)