1 # SPDX-License-Identifier: GPL-2.0
2 # Copyright (c) 2015 Stephen Warren
3 # Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
5 # Test operation of shell commands relating to environment variables.
9 from subprocess import call, check_call, CalledProcessError
14 # FIXME: This might be useful for other tests;
15 # perhaps refactor it into ConsoleBase or some other state object?
16 class StateTestEnv(object):
17 """Container that represents the state of all U-Boot environment variables.
18 This enables quick determination of existant/non-existant variable
22 def __init__(self, u_boot_console):
23 """Initialize a new StateTestEnv object.
26 u_boot_console: A U-Boot console.
32 self.u_boot_console = u_boot_console
34 self.set_var = self.get_non_existent_var()
37 """Read all current environment variables from U-Boot.
46 if self.u_boot_console.config.buildconfig.get(
47 'config_version_variable', 'n') == 'y':
48 with self.u_boot_console.disable_check('main_signon'):
49 response = self.u_boot_console.run_command('printenv')
51 response = self.u_boot_console.run_command('printenv')
53 for l in response.splitlines():
56 (var, value) = l.split('=', 1)
59 def get_existent_var(self):
60 """Return the name of an environment variable that exists.
66 The name of an environment variable.
72 def get_non_existent_var(self):
73 """Return the name of an environment variable that does not exist.
79 The name of an environment variable.
84 var = 'test_env_' + str(n)
85 if var not in self.env:
90 @pytest.fixture(scope='function')
91 def state_test_env(u_boot_console):
92 """pytest fixture to provide a StateTestEnv object to tests."""
96 ste = StateTestEnv(u_boot_console)
99 def unset_var(state_test_env, var):
100 """Unset an environment variable.
102 This both executes a U-Boot shell command and updates a StateTestEnv
106 state_test_env: The StateTestEnv object to update.
107 var: The variable name to unset.
113 state_test_env.u_boot_console.run_command('setenv %s' % var)
114 if var in state_test_env.env:
115 del state_test_env.env[var]
117 def set_var(state_test_env, var, value):
118 """Set an environment variable.
120 This both executes a U-Boot shell command and updates a StateTestEnv
124 state_test_env: The StateTestEnv object to update.
125 var: The variable name to set.
126 value: The value to set the variable to.
132 bc = state_test_env.u_boot_console.config.buildconfig
133 if bc.get('config_hush_parser', None):
138 pytest.skip('Space in variable value on non-Hush shell')
140 state_test_env.u_boot_console.run_command(
141 'setenv %s %s%s%s' % (var, quote, value, quote))
142 state_test_env.env[var] = value
144 def validate_empty(state_test_env, var):
145 """Validate that a variable is not set, using U-Boot shell commands.
148 var: The variable name to test.
154 response = state_test_env.u_boot_console.run_command('echo $%s' % var)
155 assert response == ''
157 def validate_set(state_test_env, var, value):
158 """Validate that a variable is set, using U-Boot shell commands.
161 var: The variable name to test.
162 value: The value the variable is expected to have.
168 # echo does not preserve leading, internal, or trailing whitespace in the
169 # value. printenv does, and hence allows more complete testing.
170 response = state_test_env.u_boot_console.run_command('printenv %s' % var)
171 assert response == ('%s=%s' % (var, value))
173 def test_env_echo_exists(state_test_env):
174 """Test echoing a variable that exists."""
176 var = state_test_env.get_existent_var()
177 value = state_test_env.env[var]
178 validate_set(state_test_env, var, value)
180 @pytest.mark.buildconfigspec('cmd_echo')
181 def test_env_echo_non_existent(state_test_env):
182 """Test echoing a variable that doesn't exist."""
184 var = state_test_env.set_var
185 validate_empty(state_test_env, var)
187 def test_env_printenv_non_existent(state_test_env):
188 """Test printenv error message for non-existant variables."""
190 var = state_test_env.set_var
191 c = state_test_env.u_boot_console
192 with c.disable_check('error_notification'):
193 response = c.run_command('printenv %s' % var)
194 assert(response == '## Error: "%s" not defined' % var)
196 @pytest.mark.buildconfigspec('cmd_echo')
197 def test_env_unset_non_existent(state_test_env):
198 """Test unsetting a nonexistent variable."""
200 var = state_test_env.get_non_existent_var()
201 unset_var(state_test_env, var)
202 validate_empty(state_test_env, var)
204 def test_env_set_non_existent(state_test_env):
205 """Test set a non-existant variable."""
207 var = state_test_env.set_var
209 set_var(state_test_env, var, value)
210 validate_set(state_test_env, var, value)
212 def test_env_set_existing(state_test_env):
213 """Test setting an existant variable."""
215 var = state_test_env.set_var
217 set_var(state_test_env, var, value)
218 validate_set(state_test_env, var, value)
220 @pytest.mark.buildconfigspec('cmd_echo')
221 def test_env_unset_existing(state_test_env):
222 """Test unsetting a variable."""
224 var = state_test_env.set_var
225 unset_var(state_test_env, var)
226 validate_empty(state_test_env, var)
228 def test_env_expansion_spaces(state_test_env):
229 """Test expanding a variable that contains a space in its value."""
234 var_space = state_test_env.get_non_existent_var()
235 set_var(state_test_env, var_space, ' ')
237 var_test = state_test_env.get_non_existent_var()
238 value = ' 1${%(var_space)s}${%(var_space)s} 2 ' % locals()
239 set_var(state_test_env, var_test, value)
241 validate_set(state_test_env, var_test, value)
244 unset_var(state_test_env, var_space)
246 unset_var(state_test_env, var_test)
248 @pytest.mark.buildconfigspec('cmd_importenv')
249 def test_env_import_checksum_no_size(state_test_env):
250 """Test that omitted ('-') size parameter with checksum validation fails the
253 c = state_test_env.u_boot_console
254 ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
255 addr = '%08x' % ram_base
257 with c.disable_check('error_notification'):
258 response = c.run_command('env import -c %s -' % addr)
259 assert(response == '## Error: external checksum format must pass size')
261 @pytest.mark.buildconfigspec('cmd_importenv')
262 def test_env_import_whitelist_checksum_no_size(state_test_env):
263 """Test that omitted ('-') size parameter with checksum validation fails the
264 env import function when variables are passed as parameters.
266 c = state_test_env.u_boot_console
267 ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
268 addr = '%08x' % ram_base
270 with c.disable_check('error_notification'):
271 response = c.run_command('env import -c %s - foo1 foo2 foo4' % addr)
272 assert(response == '## Error: external checksum format must pass size')
274 @pytest.mark.buildconfigspec('cmd_exportenv')
275 @pytest.mark.buildconfigspec('cmd_importenv')
276 def test_env_import_whitelist(state_test_env):
277 """Test importing only a handful of env variables from an environment."""
278 c = state_test_env.u_boot_console
279 ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
280 addr = '%08x' % ram_base
282 set_var(state_test_env, 'foo1', 'bar1')
283 set_var(state_test_env, 'foo2', 'bar2')
284 set_var(state_test_env, 'foo3', 'bar3')
286 c.run_command('env export %s' % addr)
288 unset_var(state_test_env, 'foo1')
289 set_var(state_test_env, 'foo2', 'test2')
290 set_var(state_test_env, 'foo4', 'bar4')
292 # no foo1 in current env, foo2 overridden, foo3 should be of the value
293 # before exporting and foo4 should be of the value before importing.
294 c.run_command('env import %s - foo1 foo2 foo4' % addr)
296 validate_set(state_test_env, 'foo1', 'bar1')
297 validate_set(state_test_env, 'foo2', 'bar2')
298 validate_set(state_test_env, 'foo3', 'bar3')
299 validate_set(state_test_env, 'foo4', 'bar4')
301 # Cleanup test environment
302 unset_var(state_test_env, 'foo1')
303 unset_var(state_test_env, 'foo2')
304 unset_var(state_test_env, 'foo3')
305 unset_var(state_test_env, 'foo4')
307 @pytest.mark.buildconfigspec('cmd_exportenv')
308 @pytest.mark.buildconfigspec('cmd_importenv')
309 def test_env_import_whitelist_delete(state_test_env):
311 """Test importing only a handful of env variables from an environment, with.
312 deletion if a var A that is passed to env import is not in the
313 environment to be imported.
315 c = state_test_env.u_boot_console
316 ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
317 addr = '%08x' % ram_base
319 set_var(state_test_env, 'foo1', 'bar1')
320 set_var(state_test_env, 'foo2', 'bar2')
321 set_var(state_test_env, 'foo3', 'bar3')
323 c.run_command('env export %s' % addr)
325 unset_var(state_test_env, 'foo1')
326 set_var(state_test_env, 'foo2', 'test2')
327 set_var(state_test_env, 'foo4', 'bar4')
329 # no foo1 in current env, foo2 overridden, foo3 should be of the value
330 # before exporting and foo4 should be empty.
331 c.run_command('env import -d %s - foo1 foo2 foo4' % addr)
333 validate_set(state_test_env, 'foo1', 'bar1')
334 validate_set(state_test_env, 'foo2', 'bar2')
335 validate_set(state_test_env, 'foo3', 'bar3')
336 validate_empty(state_test_env, 'foo4')
338 # Cleanup test environment
339 unset_var(state_test_env, 'foo1')
340 unset_var(state_test_env, 'foo2')
341 unset_var(state_test_env, 'foo3')
342 unset_var(state_test_env, 'foo4')
344 @pytest.mark.buildconfigspec('cmd_nvedit_info')
345 def test_env_info(state_test_env):
347 """Test 'env info' command with all possible options.
349 c = state_test_env.u_boot_console
351 response = c.run_command('env info')
353 for l in response.split('\n'):
354 if 'env_valid = ' in l:
355 assert '= invalid' in l or '= valid' in l or '= redundant' in l
357 elif 'env_ready =' in l or 'env_use_default =' in l:
358 assert '= true' in l or '= false' in l
364 response = c.run_command('env info -p -d')
365 assert 'Default environment is used' in response or "Environment was loaded from persistent storage" in response
366 assert 'Environment can be persisted' in response or "Environment cannot be persisted" in response
368 response = c.run_command('env info -p -d -q')
369 assert response == ""
371 response = c.run_command('env info -p -q')
372 assert response == ""
374 response = c.run_command('env info -d -q')
375 assert response == ""
377 @pytest.mark.boardspec('sandbox')
378 @pytest.mark.buildconfigspec('cmd_nvedit_info')
379 @pytest.mark.buildconfigspec('cmd_echo')
380 def test_env_info_sandbox(state_test_env):
381 """Test 'env info' command result with several options on sandbox
382 with a known ENV configuration: ready & default & persistent
384 c = state_test_env.u_boot_console
386 response = c.run_command('env info')
387 assert 'env_ready = true' in response
388 assert 'env_use_default = true' in response
390 response = c.run_command('env info -p -d')
391 assert 'Default environment is used' in response
392 assert 'Environment cannot be persisted' in response
394 response = c.run_command('env info -d -q')
395 response = c.run_command('echo $?')
396 assert response == "0"
398 response = c.run_command('env info -p -q')
399 response = c.run_command('echo $?')
400 assert response == "1"
402 response = c.run_command('env info -d -p -q')
403 response = c.run_command('echo $?')
404 assert response == "1"
406 def mk_env_ext4(state_test_env):
408 """Create a empty ext4 file system volume."""
409 c = state_test_env.u_boot_console
410 filename = 'env.ext4.img'
411 persistent = c.config.persistent_data_dir + '/' + filename
412 fs_img = c.config.result_dir + '/' + filename
414 if os.path.exists(persistent):
415 c.log.action('Disk image file ' + persistent + ' already exists')
418 u_boot_utils.run_and_log(c, 'dd if=/dev/zero of=%s bs=1M count=16' % persistent)
419 u_boot_utils.run_and_log(c, 'mkfs.ext4 -O ^metadata_csum %s' % persistent)
420 except CalledProcessError:
421 call('rm -f %s' % persistent, shell=True)
424 u_boot_utils.run_and_log(c, ['cp', '-f', persistent, fs_img])
427 @pytest.mark.boardspec('sandbox')
428 @pytest.mark.buildconfigspec('cmd_echo')
429 @pytest.mark.buildconfigspec('cmd_nvedit_info')
430 @pytest.mark.buildconfigspec('cmd_nvedit_load')
431 @pytest.mark.buildconfigspec('cmd_nvedit_select')
432 @pytest.mark.buildconfigspec('env_is_in_ext4')
433 def test_env_ext4(state_test_env):
435 """Test ENV in EXT4 on sandbox."""
436 c = state_test_env.u_boot_console
439 fs_img = mk_env_ext4(state_test_env)
441 c.run_command('host bind 0 %s' % fs_img)
443 response = c.run_command('ext4ls host 0:0')
444 assert 'uboot.env' not in response
446 # force env location: EXT4 (prio 1 in sandbox)
447 response = c.run_command('env select EXT4')
448 assert 'Select Environment on EXT4: OK' in response
450 response = c.run_command('env save')
451 assert 'Saving Environment to EXT4' in response
453 response = c.run_command('env load')
454 assert 'Loading Environment from EXT4... OK' in response
456 response = c.run_command('ext4ls host 0:0')
457 assert '8192 uboot.env' in response
459 response = c.run_command('env info')
460 assert 'env_valid = valid' in response
461 assert 'env_ready = true' in response
462 assert 'env_use_default = false' in response
464 response = c.run_command('env info -p -d')
465 assert 'Environment was loaded from persistent storage' in response
466 assert 'Environment can be persisted' in response
468 response = c.run_command('env info -d -q')
469 assert response == ""
470 response = c.run_command('echo $?')
471 assert response == "1"
473 response = c.run_command('env info -p -q')
474 assert response == ""
475 response = c.run_command('echo $?')
476 assert response == "0"
478 # restore env location: NOWHERE (prio 0 in sandbox)
479 response = c.run_command('env select nowhere')
480 assert 'Select Environment on nowhere: OK' in response
482 response = c.run_command('env load')
483 assert 'Loading Environment from nowhere... OK' in response
485 response = c.run_command('env info')
486 assert 'env_valid = invalid' in response
487 assert 'env_ready = true' in response
488 assert 'env_use_default = true' in response
490 response = c.run_command('env info -p -d')
491 assert 'Default environment is used' in response
492 assert 'Environment cannot be persisted' in response
496 call('rm -f %s' % fs_img, shell=True)