test_env.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. # SPDX-License-Identifier: GPL-2.0
  2. # Copyright (c) 2015 Stephen Warren
  3. # Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
  4. # Test operation of shell commands relating to environment variables.
  5. import os
  6. import os.path
  7. from subprocess import call, check_call, CalledProcessError
  8. import pytest
  9. import u_boot_utils
  10. # FIXME: This might be useful for other tests;
  11. # perhaps refactor it into ConsoleBase or some other state object?
  12. class StateTestEnv(object):
  13. """Container that represents the state of all U-Boot environment variables.
  14. This enables quick determination of existant/non-existant variable
  15. names.
  16. """
  17. def __init__(self, u_boot_console):
  18. """Initialize a new StateTestEnv object.
  19. Args:
  20. u_boot_console: A U-Boot console.
  21. Returns:
  22. Nothing.
  23. """
  24. self.u_boot_console = u_boot_console
  25. self.get_env()
  26. self.set_var = self.get_non_existent_var()
  27. def get_env(self):
  28. """Read all current environment variables from U-Boot.
  29. Args:
  30. None.
  31. Returns:
  32. Nothing.
  33. """
  34. if self.u_boot_console.config.buildconfig.get(
  35. 'config_version_variable', 'n') == 'y':
  36. with self.u_boot_console.disable_check('main_signon'):
  37. response = self.u_boot_console.run_command('printenv')
  38. else:
  39. response = self.u_boot_console.run_command('printenv')
  40. self.env = {}
  41. for l in response.splitlines():
  42. if not '=' in l:
  43. continue
  44. (var, value) = l.split('=', 1)
  45. self.env[var] = value
  46. def get_existent_var(self):
  47. """Return the name of an environment variable that exists.
  48. Args:
  49. None.
  50. Returns:
  51. The name of an environment variable.
  52. """
  53. for var in self.env:
  54. return var
  55. def get_non_existent_var(self):
  56. """Return the name of an environment variable that does not exist.
  57. Args:
  58. None.
  59. Returns:
  60. The name of an environment variable.
  61. """
  62. n = 0
  63. while True:
  64. var = 'test_env_' + str(n)
  65. if var not in self.env:
  66. return var
  67. n += 1
  68. ste = None
  69. @pytest.fixture(scope='function')
  70. def state_test_env(u_boot_console):
  71. """pytest fixture to provide a StateTestEnv object to tests."""
  72. global ste
  73. if not ste:
  74. ste = StateTestEnv(u_boot_console)
  75. return ste
  76. def unset_var(state_test_env, var):
  77. """Unset an environment variable.
  78. This both executes a U-Boot shell command and updates a StateTestEnv
  79. object.
  80. Args:
  81. state_test_env: The StateTestEnv object to update.
  82. var: The variable name to unset.
  83. Returns:
  84. Nothing.
  85. """
  86. state_test_env.u_boot_console.run_command('setenv %s' % var)
  87. if var in state_test_env.env:
  88. del state_test_env.env[var]
  89. def set_var(state_test_env, var, value):
  90. """Set an environment variable.
  91. This both executes a U-Boot shell command and updates a StateTestEnv
  92. object.
  93. Args:
  94. state_test_env: The StateTestEnv object to update.
  95. var: The variable name to set.
  96. value: The value to set the variable to.
  97. Returns:
  98. Nothing.
  99. """
  100. bc = state_test_env.u_boot_console.config.buildconfig
  101. if bc.get('config_hush_parser', None):
  102. quote = '"'
  103. else:
  104. quote = ''
  105. if ' ' in value:
  106. pytest.skip('Space in variable value on non-Hush shell')
  107. state_test_env.u_boot_console.run_command(
  108. 'setenv %s %s%s%s' % (var, quote, value, quote))
  109. state_test_env.env[var] = value
  110. def validate_empty(state_test_env, var):
  111. """Validate that a variable is not set, using U-Boot shell commands.
  112. Args:
  113. var: The variable name to test.
  114. Returns:
  115. Nothing.
  116. """
  117. response = state_test_env.u_boot_console.run_command('echo ${%s}' % var)
  118. assert response == ''
  119. def validate_set(state_test_env, var, value):
  120. """Validate that a variable is set, using U-Boot shell commands.
  121. Args:
  122. var: The variable name to test.
  123. value: The value the variable is expected to have.
  124. Returns:
  125. Nothing.
  126. """
  127. # echo does not preserve leading, internal, or trailing whitespace in the
  128. # value. printenv does, and hence allows more complete testing.
  129. response = state_test_env.u_boot_console.run_command('printenv %s' % var)
  130. assert response == ('%s=%s' % (var, value))
  131. def test_env_echo_exists(state_test_env):
  132. """Test echoing a variable that exists."""
  133. var = state_test_env.get_existent_var()
  134. value = state_test_env.env[var]
  135. validate_set(state_test_env, var, value)
  136. @pytest.mark.buildconfigspec('cmd_echo')
  137. def test_env_echo_non_existent(state_test_env):
  138. """Test echoing a variable that doesn't exist."""
  139. var = state_test_env.set_var
  140. validate_empty(state_test_env, var)
  141. def test_env_printenv_non_existent(state_test_env):
  142. """Test printenv error message for non-existant variables."""
  143. var = state_test_env.set_var
  144. c = state_test_env.u_boot_console
  145. with c.disable_check('error_notification'):
  146. response = c.run_command('printenv %s' % var)
  147. assert(response == '## Error: "%s" not defined' % var)
  148. @pytest.mark.buildconfigspec('cmd_echo')
  149. def test_env_unset_non_existent(state_test_env):
  150. """Test unsetting a nonexistent variable."""
  151. var = state_test_env.get_non_existent_var()
  152. unset_var(state_test_env, var)
  153. validate_empty(state_test_env, var)
  154. def test_env_set_non_existent(state_test_env):
  155. """Test set a non-existant variable."""
  156. var = state_test_env.set_var
  157. value = 'foo'
  158. set_var(state_test_env, var, value)
  159. validate_set(state_test_env, var, value)
  160. def test_env_set_existing(state_test_env):
  161. """Test setting an existant variable."""
  162. var = state_test_env.set_var
  163. value = 'bar'
  164. set_var(state_test_env, var, value)
  165. validate_set(state_test_env, var, value)
  166. @pytest.mark.buildconfigspec('cmd_echo')
  167. def test_env_unset_existing(state_test_env):
  168. """Test unsetting a variable."""
  169. var = state_test_env.set_var
  170. unset_var(state_test_env, var)
  171. validate_empty(state_test_env, var)
  172. def test_env_expansion_spaces(state_test_env):
  173. """Test expanding a variable that contains a space in its value."""
  174. var_space = None
  175. var_test = None
  176. try:
  177. var_space = state_test_env.get_non_existent_var()
  178. set_var(state_test_env, var_space, ' ')
  179. var_test = state_test_env.get_non_existent_var()
  180. value = ' 1${%(var_space)s}${%(var_space)s} 2 ' % locals()
  181. set_var(state_test_env, var_test, value)
  182. value = ' 1 2 '
  183. validate_set(state_test_env, var_test, value)
  184. finally:
  185. if var_space:
  186. unset_var(state_test_env, var_space)
  187. if var_test:
  188. unset_var(state_test_env, var_test)
  189. @pytest.mark.buildconfigspec('cmd_importenv')
  190. def test_env_import_checksum_no_size(state_test_env):
  191. """Test that omitted ('-') size parameter with checksum validation fails the
  192. env import function.
  193. """
  194. c = state_test_env.u_boot_console
  195. ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
  196. addr = '%08x' % ram_base
  197. with c.disable_check('error_notification'):
  198. response = c.run_command('env import -c %s -' % addr)
  199. assert(response == '## Error: external checksum format must pass size')
  200. @pytest.mark.buildconfigspec('cmd_importenv')
  201. def test_env_import_whitelist_checksum_no_size(state_test_env):
  202. """Test that omitted ('-') size parameter with checksum validation fails the
  203. env import function when variables are passed as parameters.
  204. """
  205. c = state_test_env.u_boot_console
  206. ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
  207. addr = '%08x' % ram_base
  208. with c.disable_check('error_notification'):
  209. response = c.run_command('env import -c %s - foo1 foo2 foo4' % addr)
  210. assert(response == '## Error: external checksum format must pass size')
  211. @pytest.mark.buildconfigspec('cmd_exportenv')
  212. @pytest.mark.buildconfigspec('cmd_importenv')
  213. def test_env_import_whitelist(state_test_env):
  214. """Test importing only a handful of env variables from an environment."""
  215. c = state_test_env.u_boot_console
  216. ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
  217. addr = '%08x' % ram_base
  218. set_var(state_test_env, 'foo1', 'bar1')
  219. set_var(state_test_env, 'foo2', 'bar2')
  220. set_var(state_test_env, 'foo3', 'bar3')
  221. c.run_command('env export %s' % addr)
  222. unset_var(state_test_env, 'foo1')
  223. set_var(state_test_env, 'foo2', 'test2')
  224. set_var(state_test_env, 'foo4', 'bar4')
  225. # no foo1 in current env, foo2 overridden, foo3 should be of the value
  226. # before exporting and foo4 should be of the value before importing.
  227. c.run_command('env import %s - foo1 foo2 foo4' % addr)
  228. validate_set(state_test_env, 'foo1', 'bar1')
  229. validate_set(state_test_env, 'foo2', 'bar2')
  230. validate_set(state_test_env, 'foo3', 'bar3')
  231. validate_set(state_test_env, 'foo4', 'bar4')
  232. # Cleanup test environment
  233. unset_var(state_test_env, 'foo1')
  234. unset_var(state_test_env, 'foo2')
  235. unset_var(state_test_env, 'foo3')
  236. unset_var(state_test_env, 'foo4')
  237. @pytest.mark.buildconfigspec('cmd_exportenv')
  238. @pytest.mark.buildconfigspec('cmd_importenv')
  239. def test_env_import_whitelist_delete(state_test_env):
  240. """Test importing only a handful of env variables from an environment, with.
  241. deletion if a var A that is passed to env import is not in the
  242. environment to be imported.
  243. """
  244. c = state_test_env.u_boot_console
  245. ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
  246. addr = '%08x' % ram_base
  247. set_var(state_test_env, 'foo1', 'bar1')
  248. set_var(state_test_env, 'foo2', 'bar2')
  249. set_var(state_test_env, 'foo3', 'bar3')
  250. c.run_command('env export %s' % addr)
  251. unset_var(state_test_env, 'foo1')
  252. set_var(state_test_env, 'foo2', 'test2')
  253. set_var(state_test_env, 'foo4', 'bar4')
  254. # no foo1 in current env, foo2 overridden, foo3 should be of the value
  255. # before exporting and foo4 should be empty.
  256. c.run_command('env import -d %s - foo1 foo2 foo4' % addr)
  257. validate_set(state_test_env, 'foo1', 'bar1')
  258. validate_set(state_test_env, 'foo2', 'bar2')
  259. validate_set(state_test_env, 'foo3', 'bar3')
  260. validate_empty(state_test_env, 'foo4')
  261. # Cleanup test environment
  262. unset_var(state_test_env, 'foo1')
  263. unset_var(state_test_env, 'foo2')
  264. unset_var(state_test_env, 'foo3')
  265. unset_var(state_test_env, 'foo4')
  266. @pytest.mark.buildconfigspec('cmd_nvedit_info')
  267. def test_env_info(state_test_env):
  268. """Test 'env info' command with all possible options.
  269. """
  270. c = state_test_env.u_boot_console
  271. response = c.run_command('env info')
  272. nb_line = 0
  273. for l in response.split('\n'):
  274. if 'env_valid = ' in l:
  275. assert '= invalid' in l or '= valid' in l or '= redundant' in l
  276. nb_line += 1
  277. elif 'env_ready =' in l or 'env_use_default =' in l:
  278. assert '= true' in l or '= false' in l
  279. nb_line += 1
  280. else:
  281. assert true
  282. assert nb_line == 3
  283. response = c.run_command('env info -p -d')
  284. assert 'Default environment is used' in response or "Environment was loaded from persistent storage" in response
  285. assert 'Environment can be persisted' in response or "Environment cannot be persisted" in response
  286. response = c.run_command('env info -p -d -q')
  287. assert response == ""
  288. response = c.run_command('env info -p -q')
  289. assert response == ""
  290. response = c.run_command('env info -d -q')
  291. assert response == ""
  292. @pytest.mark.boardspec('sandbox')
  293. @pytest.mark.buildconfigspec('cmd_nvedit_info')
  294. @pytest.mark.buildconfigspec('cmd_echo')
  295. def test_env_info_sandbox(state_test_env):
  296. """Test 'env info' command result with several options on sandbox
  297. with a known ENV configuration: ready & default & persistent
  298. """
  299. c = state_test_env.u_boot_console
  300. response = c.run_command('env info')
  301. assert 'env_ready = true' in response
  302. assert 'env_use_default = true' in response
  303. response = c.run_command('env info -p -d')
  304. assert 'Default environment is used' in response
  305. assert 'Environment cannot be persisted' in response
  306. response = c.run_command('env info -d -q')
  307. response = c.run_command('echo $?')
  308. assert response == "0"
  309. response = c.run_command('env info -p -q')
  310. response = c.run_command('echo $?')
  311. assert response == "1"
  312. response = c.run_command('env info -d -p -q')
  313. response = c.run_command('echo $?')
  314. assert response == "1"
  315. def mk_env_ext4(state_test_env):
  316. """Create a empty ext4 file system volume."""
  317. c = state_test_env.u_boot_console
  318. filename = 'env.ext4.img'
  319. persistent = c.config.persistent_data_dir + '/' + filename
  320. fs_img = c.config.result_dir + '/' + filename
  321. if os.path.exists(persistent):
  322. c.log.action('Disk image file ' + persistent + ' already exists')
  323. else:
  324. try:
  325. u_boot_utils.run_and_log(c, 'dd if=/dev/zero of=%s bs=1M count=16' % persistent)
  326. u_boot_utils.run_and_log(c, 'mkfs.ext4 %s' % persistent)
  327. sb_content = u_boot_utils.run_and_log(c, 'tune2fs -l %s' % persistent)
  328. if 'metadata_csum' in sb_content:
  329. u_boot_utils.run_and_log(c, 'tune2fs -O ^metadata_csum %s' % persistent)
  330. except CalledProcessError:
  331. call('rm -f %s' % persistent, shell=True)
  332. raise
  333. u_boot_utils.run_and_log(c, ['cp', '-f', persistent, fs_img])
  334. return fs_img
  335. @pytest.mark.boardspec('sandbox')
  336. @pytest.mark.buildconfigspec('cmd_echo')
  337. @pytest.mark.buildconfigspec('cmd_nvedit_info')
  338. @pytest.mark.buildconfigspec('cmd_nvedit_load')
  339. @pytest.mark.buildconfigspec('cmd_nvedit_select')
  340. @pytest.mark.buildconfigspec('env_is_in_ext4')
  341. def test_env_ext4(state_test_env):
  342. """Test ENV in EXT4 on sandbox."""
  343. c = state_test_env.u_boot_console
  344. fs_img = ''
  345. try:
  346. fs_img = mk_env_ext4(state_test_env)
  347. c.run_command('host bind 0 %s' % fs_img)
  348. response = c.run_command('ext4ls host 0:0')
  349. assert 'uboot.env' not in response
  350. # force env location: EXT4 (prio 1 in sandbox)
  351. response = c.run_command('env select EXT4')
  352. assert 'Select Environment on EXT4: OK' in response
  353. response = c.run_command('env save')
  354. assert 'Saving Environment to EXT4' in response
  355. response = c.run_command('env load')
  356. assert 'Loading Environment from EXT4... OK' in response
  357. response = c.run_command('ext4ls host 0:0')
  358. assert '8192 uboot.env' in response
  359. response = c.run_command('env info')
  360. assert 'env_valid = valid' in response
  361. assert 'env_ready = true' in response
  362. assert 'env_use_default = false' in response
  363. response = c.run_command('env info -p -d')
  364. assert 'Environment was loaded from persistent storage' in response
  365. assert 'Environment can be persisted' in response
  366. response = c.run_command('env info -d -q')
  367. assert response == ""
  368. response = c.run_command('echo $?')
  369. assert response == "1"
  370. response = c.run_command('env info -p -q')
  371. assert response == ""
  372. response = c.run_command('echo $?')
  373. assert response == "0"
  374. response = c.run_command('env erase')
  375. assert 'OK' in response
  376. response = c.run_command('env load')
  377. assert 'Loading Environment from EXT4... ' in response
  378. assert 'bad CRC, using default environment' in response
  379. response = c.run_command('env info')
  380. assert 'env_valid = invalid' in response
  381. assert 'env_ready = true' in response
  382. assert 'env_use_default = true' in response
  383. response = c.run_command('env info -p -d')
  384. assert 'Default environment is used' in response
  385. assert 'Environment can be persisted' in response
  386. # restore env location: NOWHERE (prio 0 in sandbox)
  387. response = c.run_command('env select nowhere')
  388. assert 'Select Environment on nowhere: OK' in response
  389. response = c.run_command('env load')
  390. assert 'Loading Environment from nowhere... OK' in response
  391. response = c.run_command('env info')
  392. assert 'env_valid = invalid' in response
  393. assert 'env_ready = true' in response
  394. assert 'env_use_default = true' in response
  395. response = c.run_command('env info -p -d')
  396. assert 'Default environment is used' in response
  397. assert 'Environment cannot be persisted' in response
  398. finally:
  399. if fs_img:
  400. call('rm -f %s' % fs_img, shell=True)