blob: 9bed2f48d77e674a2b4b062bdb15cbb18e7e966a [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0
Stephen Warren5ab097a2016-01-15 11:15:26 -07002# Copyright (c) 2015 Stephen Warren
3# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
Stephen Warren5ab097a2016-01-15 11:15:26 -07004
5# Test operation of shell commands relating to environment variables.
6
Patrick Delaunayad045762020-07-28 11:51:24 +02007import os
8import os.path
9from subprocess import call, check_call, CalledProcessError
10
Stephen Warren5ab097a2016-01-15 11:15:26 -070011import pytest
Quentin Schulzb7a7c412018-07-09 19:16:30 +020012import u_boot_utils
Stephen Warren5ab097a2016-01-15 11:15:26 -070013
14# FIXME: This might be useful for other tests;
15# perhaps refactor it into ConsoleBase or some other state object?
16class StateTestEnv(object):
Stephen Warrene8debf32016-01-26 13:41:30 -070017 """Container that represents the state of all U-Boot environment variables.
Stephen Warren5ab097a2016-01-15 11:15:26 -070018 This enables quick determination of existant/non-existant variable
19 names.
Stephen Warrene8debf32016-01-26 13:41:30 -070020 """
Stephen Warren5ab097a2016-01-15 11:15:26 -070021
22 def __init__(self, u_boot_console):
Stephen Warrene8debf32016-01-26 13:41:30 -070023 """Initialize a new StateTestEnv object.
Stephen Warren5ab097a2016-01-15 11:15:26 -070024
25 Args:
26 u_boot_console: A U-Boot console.
27
28 Returns:
29 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -070030 """
Stephen Warren5ab097a2016-01-15 11:15:26 -070031
32 self.u_boot_console = u_boot_console
33 self.get_env()
34 self.set_var = self.get_non_existent_var()
35
36 def get_env(self):
Stephen Warrene8debf32016-01-26 13:41:30 -070037 """Read all current environment variables from U-Boot.
Stephen Warren5ab097a2016-01-15 11:15:26 -070038
39 Args:
40 None.
41
42 Returns:
43 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -070044 """
Stephen Warren5ab097a2016-01-15 11:15:26 -070045
Stephen Warren7a8f8862016-06-16 12:59:34 -060046 if self.u_boot_console.config.buildconfig.get(
47 'config_version_variable', 'n') == 'y':
Heiko Schocherda37f002016-06-07 08:31:15 +020048 with self.u_boot_console.disable_check('main_signon'):
49 response = self.u_boot_console.run_command('printenv')
50 else:
51 response = self.u_boot_console.run_command('printenv')
Stephen Warren5ab097a2016-01-15 11:15:26 -070052 self.env = {}
53 for l in response.splitlines():
54 if not '=' in l:
55 continue
Stephen Warrenfc1a3bf2019-12-18 11:37:21 -070056 (var, value) = l.split('=', 1)
Stephen Warren5ab097a2016-01-15 11:15:26 -070057 self.env[var] = value
58
59 def get_existent_var(self):
Stephen Warrene8debf32016-01-26 13:41:30 -070060 """Return the name of an environment variable that exists.
Stephen Warren5ab097a2016-01-15 11:15:26 -070061
62 Args:
63 None.
64
65 Returns:
66 The name of an environment variable.
Stephen Warrene8debf32016-01-26 13:41:30 -070067 """
Stephen Warren5ab097a2016-01-15 11:15:26 -070068
69 for var in self.env:
70 return var
71
72 def get_non_existent_var(self):
Stephen Warrene8debf32016-01-26 13:41:30 -070073 """Return the name of an environment variable that does not exist.
Stephen Warren5ab097a2016-01-15 11:15:26 -070074
75 Args:
76 None.
77
78 Returns:
79 The name of an environment variable.
Stephen Warrene8debf32016-01-26 13:41:30 -070080 """
Stephen Warren5ab097a2016-01-15 11:15:26 -070081
82 n = 0
83 while True:
84 var = 'test_env_' + str(n)
85 if var not in self.env:
86 return var
87 n += 1
88
Stephen Warren636f38d2016-01-22 12:30:08 -070089ste = None
90@pytest.fixture(scope='function')
Stephen Warren5ab097a2016-01-15 11:15:26 -070091def state_test_env(u_boot_console):
Stephen Warrene8debf32016-01-26 13:41:30 -070092 """pytest fixture to provide a StateTestEnv object to tests."""
Stephen Warren5ab097a2016-01-15 11:15:26 -070093
Stephen Warren636f38d2016-01-22 12:30:08 -070094 global ste
95 if not ste:
96 ste = StateTestEnv(u_boot_console)
97 return ste
Stephen Warren5ab097a2016-01-15 11:15:26 -070098
99def unset_var(state_test_env, var):
Stephen Warrene8debf32016-01-26 13:41:30 -0700100 """Unset an environment variable.
Stephen Warren5ab097a2016-01-15 11:15:26 -0700101
102 This both executes a U-Boot shell command and updates a StateTestEnv
103 object.
104
105 Args:
Stephen Warrendb261f02016-01-28 10:18:03 -0700106 state_test_env: The StateTestEnv object to update.
Stephen Warren5ab097a2016-01-15 11:15:26 -0700107 var: The variable name to unset.
108
109 Returns:
110 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700111 """
Stephen Warren5ab097a2016-01-15 11:15:26 -0700112
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]
116
117def set_var(state_test_env, var, value):
Stephen Warrene8debf32016-01-26 13:41:30 -0700118 """Set an environment variable.
Stephen Warren5ab097a2016-01-15 11:15:26 -0700119
120 This both executes a U-Boot shell command and updates a StateTestEnv
121 object.
122
123 Args:
Stephen Warrendb261f02016-01-28 10:18:03 -0700124 state_test_env: The StateTestEnv object to update.
Stephen Warren5ab097a2016-01-15 11:15:26 -0700125 var: The variable name to set.
126 value: The value to set the variable to.
127
128 Returns:
129 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700130 """
Stephen Warren5ab097a2016-01-15 11:15:26 -0700131
Stephen Warren3e229a82017-11-10 11:59:15 +0100132 bc = state_test_env.u_boot_console.config.buildconfig
133 if bc.get('config_hush_parser', None):
134 quote = '"'
135 else:
136 quote = ''
137 if ' ' in value:
138 pytest.skip('Space in variable value on non-Hush shell')
139
140 state_test_env.u_boot_console.run_command(
141 'setenv %s %s%s%s' % (var, quote, value, quote))
Stephen Warren5ab097a2016-01-15 11:15:26 -0700142 state_test_env.env[var] = value
143
144def validate_empty(state_test_env, var):
Stephen Warrene8debf32016-01-26 13:41:30 -0700145 """Validate that a variable is not set, using U-Boot shell commands.
Stephen Warren5ab097a2016-01-15 11:15:26 -0700146
147 Args:
148 var: The variable name to test.
149
150 Returns:
151 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700152 """
Stephen Warren5ab097a2016-01-15 11:15:26 -0700153
Heinrich Schuchardt185440f2020-09-10 12:09:03 +0200154 response = state_test_env.u_boot_console.run_command('echo ${%s}' % var)
Stephen Warren5ab097a2016-01-15 11:15:26 -0700155 assert response == ''
156
157def validate_set(state_test_env, var, value):
Stephen Warrene8debf32016-01-26 13:41:30 -0700158 """Validate that a variable is set, using U-Boot shell commands.
Stephen Warren5ab097a2016-01-15 11:15:26 -0700159
160 Args:
161 var: The variable name to test.
162 value: The value the variable is expected to have.
163
164 Returns:
165 Nothing.
Stephen Warrene8debf32016-01-26 13:41:30 -0700166 """
Stephen Warren5ab097a2016-01-15 11:15:26 -0700167
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))
172
173def test_env_echo_exists(state_test_env):
Stephen Warrene8debf32016-01-26 13:41:30 -0700174 """Test echoing a variable that exists."""
Stephen Warren5ab097a2016-01-15 11:15:26 -0700175
176 var = state_test_env.get_existent_var()
177 value = state_test_env.env[var]
178 validate_set(state_test_env, var, value)
179
Michal Simek6b83c382017-05-15 14:29:02 +0200180@pytest.mark.buildconfigspec('cmd_echo')
Stephen Warren5ab097a2016-01-15 11:15:26 -0700181def test_env_echo_non_existent(state_test_env):
Stephen Warrene8debf32016-01-26 13:41:30 -0700182 """Test echoing a variable that doesn't exist."""
Stephen Warren5ab097a2016-01-15 11:15:26 -0700183
184 var = state_test_env.set_var
185 validate_empty(state_test_env, var)
186
187def test_env_printenv_non_existent(state_test_env):
Stephen Warrene8debf32016-01-26 13:41:30 -0700188 """Test printenv error message for non-existant variables."""
Stephen Warren5ab097a2016-01-15 11:15:26 -0700189
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)
195
Michal Simek6b83c382017-05-15 14:29:02 +0200196@pytest.mark.buildconfigspec('cmd_echo')
Stephen Warren5ab097a2016-01-15 11:15:26 -0700197def test_env_unset_non_existent(state_test_env):
Stephen Warrene8debf32016-01-26 13:41:30 -0700198 """Test unsetting a nonexistent variable."""
Stephen Warren5ab097a2016-01-15 11:15:26 -0700199
200 var = state_test_env.get_non_existent_var()
201 unset_var(state_test_env, var)
202 validate_empty(state_test_env, var)
203
204def test_env_set_non_existent(state_test_env):
Stephen Warrene8debf32016-01-26 13:41:30 -0700205 """Test set a non-existant variable."""
Stephen Warren5ab097a2016-01-15 11:15:26 -0700206
207 var = state_test_env.set_var
208 value = 'foo'
209 set_var(state_test_env, var, value)
210 validate_set(state_test_env, var, value)
211
212def test_env_set_existing(state_test_env):
Stephen Warrene8debf32016-01-26 13:41:30 -0700213 """Test setting an existant variable."""
Stephen Warren5ab097a2016-01-15 11:15:26 -0700214
215 var = state_test_env.set_var
216 value = 'bar'
217 set_var(state_test_env, var, value)
218 validate_set(state_test_env, var, value)
219
Michal Simek6b83c382017-05-15 14:29:02 +0200220@pytest.mark.buildconfigspec('cmd_echo')
Stephen Warren5ab097a2016-01-15 11:15:26 -0700221def test_env_unset_existing(state_test_env):
Stephen Warrene8debf32016-01-26 13:41:30 -0700222 """Test unsetting a variable."""
Stephen Warren5ab097a2016-01-15 11:15:26 -0700223
224 var = state_test_env.set_var
225 unset_var(state_test_env, var)
226 validate_empty(state_test_env, var)
227
228def test_env_expansion_spaces(state_test_env):
Stephen Warrene8debf32016-01-26 13:41:30 -0700229 """Test expanding a variable that contains a space in its value."""
Stephen Warren5ab097a2016-01-15 11:15:26 -0700230
231 var_space = None
232 var_test = None
233 try:
234 var_space = state_test_env.get_non_existent_var()
235 set_var(state_test_env, var_space, ' ')
236
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)
240 value = ' 1 2 '
241 validate_set(state_test_env, var_test, value)
242 finally:
243 if var_space:
244 unset_var(state_test_env, var_space)
245 if var_test:
246 unset_var(state_test_env, var_test)
Quentin Schulzb7a7c412018-07-09 19:16:30 +0200247
248@pytest.mark.buildconfigspec('cmd_importenv')
249def test_env_import_checksum_no_size(state_test_env):
250 """Test that omitted ('-') size parameter with checksum validation fails the
251 env import function.
252 """
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
256
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')
260
261@pytest.mark.buildconfigspec('cmd_importenv')
262def 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.
265 """
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
269
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')
273
274@pytest.mark.buildconfigspec('cmd_exportenv')
275@pytest.mark.buildconfigspec('cmd_importenv')
276def 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
281
282 set_var(state_test_env, 'foo1', 'bar1')
283 set_var(state_test_env, 'foo2', 'bar2')
284 set_var(state_test_env, 'foo3', 'bar3')
285
286 c.run_command('env export %s' % addr)
287
288 unset_var(state_test_env, 'foo1')
289 set_var(state_test_env, 'foo2', 'test2')
290 set_var(state_test_env, 'foo4', 'bar4')
291
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)
295
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')
300
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')
306
307@pytest.mark.buildconfigspec('cmd_exportenv')
308@pytest.mark.buildconfigspec('cmd_importenv')
309def test_env_import_whitelist_delete(state_test_env):
310
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.
314 """
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
318
319 set_var(state_test_env, 'foo1', 'bar1')
320 set_var(state_test_env, 'foo2', 'bar2')
321 set_var(state_test_env, 'foo3', 'bar3')
322
323 c.run_command('env export %s' % addr)
324
325 unset_var(state_test_env, 'foo1')
326 set_var(state_test_env, 'foo2', 'test2')
327 set_var(state_test_env, 'foo4', 'bar4')
328
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)
332
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')
337
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')
Patrick Delaunayacbf93b2020-06-19 14:03:37 +0200343
344@pytest.mark.buildconfigspec('cmd_nvedit_info')
345def test_env_info(state_test_env):
346
347 """Test 'env info' command with all possible options.
348 """
349 c = state_test_env.u_boot_console
350
351 response = c.run_command('env info')
352 nb_line = 0
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
356 nb_line += 1
357 elif 'env_ready =' in l or 'env_use_default =' in l:
358 assert '= true' in l or '= false' in l
359 nb_line += 1
360 else:
361 assert true
362 assert nb_line == 3
363
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
367
368 response = c.run_command('env info -p -d -q')
369 assert response == ""
370
371 response = c.run_command('env info -p -q')
372 assert response == ""
373
374 response = c.run_command('env info -d -q')
375 assert response == ""
376
377@pytest.mark.boardspec('sandbox')
378@pytest.mark.buildconfigspec('cmd_nvedit_info')
379@pytest.mark.buildconfigspec('cmd_echo')
380def test_env_info_sandbox(state_test_env):
Patrick Delaunayacbf93b2020-06-19 14:03:37 +0200381 """Test 'env info' command result with several options on sandbox
382 with a known ENV configuration: ready & default & persistent
383 """
384 c = state_test_env.u_boot_console
385
386 response = c.run_command('env info')
387 assert 'env_ready = true' in response
388 assert 'env_use_default = true' in response
389
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
393
394 response = c.run_command('env info -d -q')
395 response = c.run_command('echo $?')
396 assert response == "0"
397
398 response = c.run_command('env info -p -q')
399 response = c.run_command('echo $?')
400 assert response == "1"
401
402 response = c.run_command('env info -d -p -q')
403 response = c.run_command('echo $?')
404 assert response == "1"
Patrick Delaunayad045762020-07-28 11:51:24 +0200405
406def mk_env_ext4(state_test_env):
407
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
413
414 if os.path.exists(persistent):
415 c.log.action('Disk image file ' + persistent + ' already exists')
416 else:
Andy Shevchenko9262fe12021-02-11 16:40:09 +0200417 # Some distributions do not add /sbin to the default PATH, where mkfs.ext4 lives
418 os.environ["PATH"] += os.pathsep + '/sbin'
Patrick Delaunayad045762020-07-28 11:51:24 +0200419 try:
420 u_boot_utils.run_and_log(c, 'dd if=/dev/zero of=%s bs=1M count=16' % persistent)
Stephen Warrencc886252020-08-04 11:28:33 -0600421 u_boot_utils.run_and_log(c, 'mkfs.ext4 %s' % persistent)
422 sb_content = u_boot_utils.run_and_log(c, 'tune2fs -l %s' % persistent)
423 if 'metadata_csum' in sb_content:
424 u_boot_utils.run_and_log(c, 'tune2fs -O ^metadata_csum %s' % persistent)
Patrick Delaunayad045762020-07-28 11:51:24 +0200425 except CalledProcessError:
426 call('rm -f %s' % persistent, shell=True)
427 raise
428
429 u_boot_utils.run_and_log(c, ['cp', '-f', persistent, fs_img])
430 return fs_img
431
432@pytest.mark.boardspec('sandbox')
433@pytest.mark.buildconfigspec('cmd_echo')
434@pytest.mark.buildconfigspec('cmd_nvedit_info')
435@pytest.mark.buildconfigspec('cmd_nvedit_load')
436@pytest.mark.buildconfigspec('cmd_nvedit_select')
437@pytest.mark.buildconfigspec('env_is_in_ext4')
438def test_env_ext4(state_test_env):
439
440 """Test ENV in EXT4 on sandbox."""
441 c = state_test_env.u_boot_console
442 fs_img = ''
443 try:
444 fs_img = mk_env_ext4(state_test_env)
445
446 c.run_command('host bind 0 %s' % fs_img)
447
448 response = c.run_command('ext4ls host 0:0')
449 assert 'uboot.env' not in response
450
451 # force env location: EXT4 (prio 1 in sandbox)
452 response = c.run_command('env select EXT4')
453 assert 'Select Environment on EXT4: OK' in response
454
455 response = c.run_command('env save')
456 assert 'Saving Environment to EXT4' in response
457
458 response = c.run_command('env load')
459 assert 'Loading Environment from EXT4... OK' in response
460
461 response = c.run_command('ext4ls host 0:0')
462 assert '8192 uboot.env' in response
463
464 response = c.run_command('env info')
465 assert 'env_valid = valid' in response
466 assert 'env_ready = true' in response
467 assert 'env_use_default = false' in response
468
469 response = c.run_command('env info -p -d')
470 assert 'Environment was loaded from persistent storage' in response
471 assert 'Environment can be persisted' in response
472
473 response = c.run_command('env info -d -q')
474 assert response == ""
475 response = c.run_command('echo $?')
476 assert response == "1"
477
478 response = c.run_command('env info -p -q')
479 assert response == ""
480 response = c.run_command('echo $?')
481 assert response == "0"
482
Patrick Delaunayef5cc2e2020-07-28 11:51:27 +0200483 response = c.run_command('env erase')
484 assert 'OK' in response
485
486 response = c.run_command('env load')
487 assert 'Loading Environment from EXT4... ' in response
488 assert 'bad CRC, using default environment' in response
489
490 response = c.run_command('env info')
491 assert 'env_valid = invalid' in response
492 assert 'env_ready = true' in response
493 assert 'env_use_default = true' in response
494
495 response = c.run_command('env info -p -d')
496 assert 'Default environment is used' in response
497 assert 'Environment can be persisted' in response
498
Patrick Delaunayad045762020-07-28 11:51:24 +0200499 # restore env location: NOWHERE (prio 0 in sandbox)
500 response = c.run_command('env select nowhere')
501 assert 'Select Environment on nowhere: OK' in response
502
503 response = c.run_command('env load')
504 assert 'Loading Environment from nowhere... OK' in response
505
506 response = c.run_command('env info')
507 assert 'env_valid = invalid' in response
508 assert 'env_ready = true' in response
509 assert 'env_use_default = true' in response
510
511 response = c.run_command('env info -p -d')
512 assert 'Default environment is used' in response
513 assert 'Environment cannot be persisted' in response
514
515 finally:
516 if fs_img:
517 call('rm -f %s' % fs_img, shell=True)