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