test_common.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. # SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  2. # SPDX-License-Identifier: Apache-2.0
  3. import logging
  4. import os
  5. import re
  6. import shutil
  7. import stat
  8. import subprocess
  9. import sys
  10. import textwrap
  11. from pathlib import Path
  12. from typing import List
  13. import pytest
  14. from _pytest.monkeypatch import MonkeyPatch
  15. from test_build_system_helpers import IdfPyFunc, find_python, get_snapshot, replace_in_file, run_idf_py
  16. def get_subdirs_absolute_paths(path: Path) -> List[str]:
  17. """
  18. Get a list of files with absolute path in a given `path` folder
  19. """
  20. return [
  21. '{}/{}'.format(dir_path, file_name)
  22. for dir_path, _, file_names in os.walk(path)
  23. for file_name in file_names
  24. ]
  25. @pytest.mark.usefixtures('test_app_copy')
  26. @pytest.mark.test_app_copy('examples/get-started/blink')
  27. def test_compile_commands_json_updated_by_reconfigure(idf_py: IdfPyFunc) -> None:
  28. output = idf_py('reconfigure')
  29. assert 'Building ESP-IDF components for target esp32' in output.stdout
  30. snapshot_1 = get_snapshot(['build/compile_commands.json'])
  31. snapshot_2 = get_snapshot(['build/compile_commands.json'])
  32. snapshot_2.assert_same(snapshot_1)
  33. idf_py('reconfigure')
  34. snapshot_3 = get_snapshot(['build/compile_commands.json'])
  35. snapshot_3.assert_different(snapshot_2)
  36. @pytest.mark.usefixtures('test_app_copy')
  37. def test_of_test_app_copy(idf_py: IdfPyFunc) -> None:
  38. p = Path('main/idf_component.yml')
  39. p.write_text('syntax_error\n')
  40. try:
  41. with (pytest.raises(subprocess.CalledProcessError)) as exc_info:
  42. idf_py('reconfigure')
  43. assert 'ERROR: Unknown format of the manifest file:' in exc_info.value.stderr
  44. finally:
  45. p.unlink()
  46. @pytest.mark.usefixtures('test_app_copy')
  47. def test_hints_no_color_output_when_noninteractive(idf_py: IdfPyFunc) -> None:
  48. """Check that idf.py hints don't include color escape codes in non-interactive builds"""
  49. # make the build fail in such a way that idf.py shows a hint
  50. replace_in_file('main/build_test_app.c', '// placeholder_inside_main',
  51. 'esp_chip_info_t chip_info; esp_chip_info(&chip_info);')
  52. with (pytest.raises(subprocess.CalledProcessError)) as exc_info:
  53. idf_py('build')
  54. # Should not actually include a color escape sequence!
  55. # Change the assert to the correct value once the bug is fixed.
  56. assert '\x1b[0;33mHINT: esp_chip_info.h' in exc_info.value.stderr
  57. @pytest.mark.usefixtures('test_app_copy')
  58. def test_idf_copy(idf_copy: Path, idf_py: IdfPyFunc) -> None:
  59. # idf_copy is the temporary IDF copy.
  60. # For example, we can check if idf.py build can work without the .git directory:
  61. shutil.rmtree(idf_copy / '.git', ignore_errors=True)
  62. idf_py('build')
  63. def test_idf_build_with_env_var_sdkconfig_defaults(
  64. test_app_copy: Path,
  65. idf_py: IdfPyFunc,
  66. monkeypatch: MonkeyPatch,
  67. ) -> None:
  68. with open(test_app_copy / 'sdkconfig.test', 'w') as fw:
  69. fw.write('CONFIG_BT_ENABLED=y')
  70. monkeypatch.setenv('SDKCONFIG_DEFAULTS', 'sdkconfig.test')
  71. idf_py('build')
  72. with open(test_app_copy / 'sdkconfig') as fr:
  73. assert 'CONFIG_BT_ENABLED=y' in fr.read()
  74. @pytest.mark.usefixtures('test_app_copy')
  75. @pytest.mark.test_app_copy('examples/system/efuse')
  76. def test_efuse_symmary_cmake_functions(
  77. idf_py: IdfPyFunc,
  78. monkeypatch: MonkeyPatch
  79. ) -> None:
  80. monkeypatch.setenv('IDF_CI_BUILD', '1')
  81. output = idf_py('efuse-summary')
  82. assert 'FROM_CMAKE: MAC: 00:00:00:00:00:00' in output.stdout
  83. assert 'FROM_CMAKE: WR_DIS: 0' in output.stdout
  84. def test_custom_build_folder(test_app_copy: Path, idf_py: IdfPyFunc) -> None:
  85. idf_py('-BBuiLDdiR', 'build')
  86. assert (test_app_copy / 'BuiLDdiR').is_dir()
  87. def test_python_clean(idf_py: IdfPyFunc) -> None:
  88. logging.info('Cleaning Python bytecode')
  89. idf_path = Path(os.environ['IDF_PATH'])
  90. abs_paths = get_subdirs_absolute_paths(idf_path)
  91. abs_paths_suffix = [path for path in abs_paths if path.endswith(('.pyc', '.pyo'))]
  92. assert len(abs_paths_suffix) != 0
  93. idf_py('python-clean')
  94. abs_paths = get_subdirs_absolute_paths(idf_path)
  95. abs_paths_suffix = [path for path in abs_paths if path.endswith(('.pyc', '.pyo'))]
  96. assert len(abs_paths_suffix) == 0
  97. @pytest.mark.usefixtures('test_app_copy')
  98. def test_partition_table(idf_py: IdfPyFunc) -> None:
  99. logging.info('Displays partition table when executing target partition_table')
  100. output = idf_py('partition-table')
  101. assert re.search('# ESP-IDF.+Partition Table', output.stdout)
  102. @pytest.mark.skipif(sys.platform == 'win32', reason='Windows does not support executing bash script')
  103. def test_python_interpreter_unix(test_app_copy: Path) -> None:
  104. logging.info("Make sure idf.py never runs '/usr/bin/env python' or similar")
  105. env_dict = dict(**os.environ)
  106. python = find_python(env_dict['PATH'])
  107. (test_app_copy / 'python').write_text(textwrap.dedent("""#!/bin/sh
  108. echo "idf.py has executed '/usr/bin/env python' or similar"
  109. exit 1
  110. """))
  111. st = os.stat(test_app_copy / 'python')
  112. # equivalent to 'chmod +x ./python'
  113. os.chmod((test_app_copy / 'python'), st.st_mode | stat.S_IEXEC)
  114. env_dict['PATH'] = str(test_app_copy) + os.pathsep + env_dict['PATH']
  115. # python is loaded from env:$PATH, but since false interpreter is provided there, python needs to be specified as argument
  116. # if idf.py is reconfigured during it's execution, it would load a false interpreter
  117. run_idf_py('reconfigure', env=env_dict, python=python)
  118. @pytest.mark.skipif(sys.platform != 'win32', reason='Linux does not support executing .exe files')
  119. def test_python_interpreter_win(test_app_copy: Path) -> None:
  120. logging.info('Make sure idf.py never runs false python interpreter')
  121. env_dict = dict(**os.environ)
  122. python = find_python(env_dict['PATH'])
  123. # on windows python interpreter has compiled code '.exe' format, so this false file can be empty
  124. (test_app_copy / 'python.exe').write_text('')
  125. env_dict['PATH'] = str(test_app_copy) + os.pathsep + env_dict['PATH']
  126. # python is loaded from env:$PATH, but since false interpreter is provided there, python needs to be specified as argument
  127. # if idf.py is reconfigured during it's execution, it would load a false interpreter
  128. run_idf_py('reconfigure', env=env_dict, python=python)