test_idf_py.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. #!/usr/bin/env python
  2. #
  3. # SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
  4. # SPDX-License-Identifier: Apache-2.0
  5. import json
  6. import os
  7. import subprocess
  8. import sys
  9. from unittest import TestCase, main, mock
  10. import jsonschema
  11. try:
  12. from StringIO import StringIO
  13. except ImportError:
  14. from io import StringIO
  15. try:
  16. import idf
  17. except ImportError:
  18. sys.path.append('..')
  19. import idf
  20. current_dir = os.path.dirname(os.path.realpath(__file__))
  21. idf_py_path = os.path.join(current_dir, '..', 'idf.py')
  22. extension_path = os.path.join(current_dir, 'test_idf_extensions', 'test_ext')
  23. link_path = os.path.join(current_dir, '..', 'idf_py_actions', 'test_ext')
  24. class TestWithoutExtensions(TestCase):
  25. @classmethod
  26. def setUpClass(cls):
  27. # Disable the component manager and extra extensions for these tests
  28. cls.env_patcher = mock.patch.dict(os.environ, {
  29. 'IDF_COMPONENT_MANAGER': '0',
  30. 'IDF_EXTRA_ACTIONS_PATH': '',
  31. })
  32. cls.env_patcher.start()
  33. super().setUpClass()
  34. class TestExtensions(TestWithoutExtensions):
  35. def test_extension_loading(self):
  36. try:
  37. os.symlink(extension_path, link_path)
  38. os.environ['IDF_EXTRA_ACTIONS_PATH'] = os.path.join(current_dir, 'extra_path')
  39. output = subprocess.check_output([sys.executable, idf_py_path, '--help'],
  40. env=os.environ).decode('utf-8', 'ignore')
  41. self.assertIn('--test-extension-option', output)
  42. self.assertIn('test_subcommand', output)
  43. self.assertIn('--some-extension-option', output)
  44. self.assertIn('extra_subcommand', output)
  45. finally:
  46. os.remove(link_path)
  47. def test_extension_execution(self):
  48. try:
  49. os.symlink(extension_path, link_path)
  50. os.environ['IDF_EXTRA_ACTIONS_PATH'] = ';'.join([os.path.join(current_dir, 'extra_path')])
  51. output = subprocess.check_output(
  52. [sys.executable, idf_py_path, '--some-extension-option=awesome', 'test_subcommand', 'extra_subcommand'],
  53. env=os.environ).decode('utf-8', 'ignore')
  54. self.assertIn('!!! From some global callback: awesome', output)
  55. self.assertIn('!!! From some subcommand', output)
  56. self.assertIn('!!! From test global callback: test', output)
  57. self.assertIn('!!! From some subcommand', output)
  58. finally:
  59. os.remove(link_path)
  60. def test_hidden_commands(self):
  61. try:
  62. os.symlink(extension_path, link_path)
  63. os.environ['IDF_EXTRA_ACTIONS_PATH'] = ';'.join([os.path.join(current_dir, 'extra_path')])
  64. output = subprocess.check_output([sys.executable, idf_py_path, '--help'],
  65. env=os.environ).decode('utf-8', 'ignore')
  66. self.assertIn('test_subcommand', output)
  67. self.assertNotIn('hidden_one', output)
  68. finally:
  69. os.remove(link_path)
  70. class TestDependencyManagement(TestWithoutExtensions):
  71. def test_dependencies(self):
  72. result = idf.init_cli()(
  73. args=['--dry-run', 'flash'],
  74. standalone_mode=False,
  75. )
  76. self.assertEqual(['flash'], list(result.keys()))
  77. def test_order_only_dependencies(self):
  78. result = idf.init_cli()(
  79. args=['--dry-run', 'build', 'fullclean', 'all'],
  80. standalone_mode=False,
  81. )
  82. self.assertEqual(['fullclean', 'all'], list(result.keys()))
  83. def test_repeated_dependencies(self):
  84. result = idf.init_cli()(
  85. args=['--dry-run', 'fullclean', 'app', 'fullclean', 'fullclean'],
  86. standalone_mode=False,
  87. )
  88. self.assertEqual(['fullclean', 'app'], list(result.keys()))
  89. def test_complex_case(self):
  90. result = idf.init_cli()(
  91. args=['--dry-run', 'clean', 'monitor', 'clean', 'fullclean', 'flash'],
  92. standalone_mode=False,
  93. )
  94. self.assertEqual(['fullclean', 'clean', 'flash', 'monitor'], list(result.keys()))
  95. def test_dupplicated_commands_warning(self):
  96. capturedOutput = StringIO()
  97. sys.stderr = capturedOutput
  98. idf.init_cli()(
  99. args=['--dry-run', 'clean', 'monitor', 'build', 'clean', 'fullclean', 'all'],
  100. standalone_mode=False,
  101. )
  102. sys.stderr = sys.__stderr__
  103. self.assertIn(
  104. 'WARNING: Commands "all", "clean" are found in the list of commands more than once.',
  105. capturedOutput.getvalue())
  106. sys.stderr = capturedOutput
  107. idf.init_cli()(
  108. args=['--dry-run', 'clean', 'clean'],
  109. standalone_mode=False,
  110. )
  111. sys.stderr = sys.__stderr__
  112. self.assertIn(
  113. 'WARNING: Command "clean" is found in the list of commands more than once.', capturedOutput.getvalue())
  114. class TestVerboseFlag(TestWithoutExtensions):
  115. def test_verbose_messages(self):
  116. output = subprocess.check_output(
  117. [
  118. sys.executable,
  119. idf_py_path,
  120. '-C%s' % current_dir,
  121. '-v',
  122. 'test-verbose',
  123. ], env=os.environ).decode('utf-8', 'ignore')
  124. self.assertIn('Verbose mode on', output)
  125. def test_verbose_messages_not_shown_by_default(self):
  126. output = subprocess.check_output(
  127. [
  128. sys.executable,
  129. idf_py_path,
  130. '-C%s' % current_dir,
  131. 'test-verbose',
  132. ], env=os.environ).decode('utf-8', 'ignore')
  133. self.assertIn('Output from test-verbose', output)
  134. self.assertNotIn('Verbose mode on', output)
  135. class TestGlobalAndSubcommandParameters(TestWithoutExtensions):
  136. def test_set_twice_same_value(self):
  137. """Can set -D twice: globally and for subcommand if values are the same"""
  138. idf.init_cli()(
  139. args=['--dry-run', '-DAAA=BBB', '-DCCC=EEE', 'build', '-DAAA=BBB', '-DCCC=EEE'],
  140. standalone_mode=False,
  141. )
  142. def test_set_twice_different_values(self):
  143. """Cannot set -D twice: for command and subcommand of idf.py (with different values)"""
  144. with self.assertRaises(idf.FatalError):
  145. idf.init_cli()(
  146. args=['--dry-run', '-DAAA=BBB', 'build', '-DAAA=EEE', '-DCCC=EEE'],
  147. standalone_mode=False,
  148. )
  149. class TestDeprecations(TestWithoutExtensions):
  150. def test_exit_with_error_for_subcommand(self):
  151. try:
  152. subprocess.check_output(
  153. [sys.executable, idf_py_path, '-C%s' % current_dir, 'test-2'], env=os.environ, stderr=subprocess.STDOUT)
  154. except subprocess.CalledProcessError as e:
  155. self.assertIn('Error: Command "test-2" is deprecated and was removed.', e.output.decode('utf-8', 'ignore'))
  156. def test_exit_with_error_for_option(self):
  157. try:
  158. subprocess.check_output(
  159. [sys.executable, idf_py_path, '-C%s' % current_dir, '--test-5=asdf'],
  160. env=os.environ,
  161. stderr=subprocess.STDOUT)
  162. except subprocess.CalledProcessError as e:
  163. self.assertIn(
  164. 'Error: Option "test_5" is deprecated since v2.0 and was removed in v3.0.',
  165. e.output.decode('utf-8', 'ignore'))
  166. def test_deprecation_messages(self):
  167. output = subprocess.check_output(
  168. [
  169. sys.executable,
  170. idf_py_path,
  171. '-C%s' % current_dir,
  172. '--test-0=a',
  173. '--test-1=b',
  174. '--test-2=c',
  175. '--test-3=d',
  176. 'test-0',
  177. '--test-sub-0=sa',
  178. '--test-sub-1=sb',
  179. 'ta',
  180. 'test-1',
  181. ],
  182. env=os.environ,
  183. stderr=subprocess.STDOUT).decode('utf-8', 'ignore')
  184. self.assertIn('Warning: Option "test_sub_1" is deprecated and will be removed in future versions.', output)
  185. self.assertIn(
  186. 'Warning: Command "test-1" is deprecated and will be removed in future versions. '
  187. 'Please use alternative command.', output)
  188. self.assertIn('Warning: Option "test_1" is deprecated and will be removed in future versions.', output)
  189. self.assertIn(
  190. 'Warning: Option "test_2" is deprecated and will be removed in future versions. '
  191. 'Please update your parameters.', output)
  192. self.assertIn('Warning: Option "test_3" is deprecated and will be removed in future versions.', output)
  193. self.assertNotIn('"test-0" is deprecated', output)
  194. self.assertNotIn('"test_0" is deprecated', output)
  195. class TestHelpOutput(TestWithoutExtensions):
  196. def test_output(self):
  197. def action_test(commands, schema):
  198. output_file = 'idf_py_help_output.json'
  199. with open(output_file, 'w') as outfile:
  200. subprocess.run(commands, env=os.environ, stdout=outfile)
  201. with open(output_file, 'r') as outfile:
  202. help_obj = json.load(outfile)
  203. self.assertIsNone(jsonschema.validate(help_obj, schema))
  204. with open(os.path.join(current_dir, 'idf_py_help_schema.json'), 'r') as schema_file:
  205. schema_json = json.load(schema_file)
  206. action_test(['idf.py', 'help', '--json'], schema_json)
  207. action_test(['idf.py', 'help', '--json', '--add-options'], schema_json)
  208. if __name__ == '__main__':
  209. main()