]> git.karo-electronics.de Git - karo-tx-uboot.git/blob - test/py/u_boot_utils.py
tests: py: fix NameError exception if bdi cmd is not supported
[karo-tx-uboot.git] / test / py / u_boot_utils.py
1 # Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
2 #
3 # SPDX-License-Identifier: GPL-2.0
4
5 # Utility code shared across multiple tests.
6
7 import hashlib
8 import os
9 import os.path
10 import pytest
11 import sys
12 import time
13
14 def md5sum_data(data):
15     """Calculate the MD5 hash of some data.
16
17     Args:
18         data: The data to hash.
19
20     Returns:
21         The hash of the data, as a binary string.
22     """
23
24     h = hashlib.md5()
25     h.update(data)
26     return h.digest()
27
28 def md5sum_file(fn, max_length=None):
29     """Calculate the MD5 hash of the contents of a file.
30
31     Args:
32         fn: The filename of the file to hash.
33         max_length: The number of bytes to hash. If the file has more
34             bytes than this, they will be ignored. If None or omitted, the
35             entire file will be hashed.
36
37     Returns:
38         The hash of the file content, as a binary string.
39     """
40
41     with open(fn, 'rb') as fh:
42         if max_length:
43             params = [max_length]
44         else:
45             params = []
46         data = fh.read(*params)
47     return md5sum_data(data)
48
49 class PersistentRandomFile(object):
50     """Generate and store information about a persistent file containing
51     random data."""
52
53     def __init__(self, u_boot_console, fn, size):
54         """Create or process the persistent file.
55
56         If the file does not exist, it is generated.
57
58         If the file does exist, its content is hashed for later comparison.
59
60         These files are always located in the "persistent data directory" of
61         the current test run.
62
63         Args:
64             u_boot_console: A console connection to U-Boot.
65             fn: The filename (without path) to create.
66             size: The desired size of the file in bytes.
67
68         Returns:
69             Nothing.
70         """
71
72         self.fn = fn
73
74         self.abs_fn = u_boot_console.config.persistent_data_dir + '/' + fn
75
76         if os.path.exists(self.abs_fn):
77             u_boot_console.log.action('Persistent data file ' + self.abs_fn +
78                 ' already exists')
79             self.content_hash = md5sum_file(self.abs_fn)
80         else:
81             u_boot_console.log.action('Generating ' + self.abs_fn +
82                 ' (random, persistent, %d bytes)' % size)
83             data = os.urandom(size)
84             with open(self.abs_fn, 'wb') as fh:
85                 fh.write(data)
86             self.content_hash = md5sum_data(data)
87
88 def attempt_to_open_file(fn):
89     """Attempt to open a file, without throwing exceptions.
90
91     Any errors (exceptions) that occur during the attempt to open the file
92     are ignored. This is useful in order to test whether a file (in
93     particular, a device node) exists and can be successfully opened, in order
94     to poll for e.g. USB enumeration completion.
95
96     Args:
97         fn: The filename to attempt to open.
98
99     Returns:
100         An open file handle to the file, or None if the file could not be
101             opened.
102     """
103
104     try:
105         return open(fn, 'rb')
106     except:
107         return None
108
109 def wait_until_open_succeeds(fn):
110     """Poll until a file can be opened, or a timeout occurs.
111
112     Continually attempt to open a file, and return when this succeeds, or
113     raise an exception after a timeout.
114
115     Args:
116         fn: The filename to attempt to open.
117
118     Returns:
119         An open file handle to the file.
120     """
121
122     for i in xrange(100):
123         fh = attempt_to_open_file(fn)
124         if fh:
125             return fh
126         time.sleep(0.1)
127     raise Exception('File could not be opened')
128
129 def wait_until_file_open_fails(fn, ignore_errors):
130     """Poll until a file cannot be opened, or a timeout occurs.
131
132     Continually attempt to open a file, and return when this fails, or
133     raise an exception after a timeout.
134
135     Args:
136         fn: The filename to attempt to open.
137         ignore_errors: Indicate whether to ignore timeout errors. If True, the
138             function will simply return if a timeout occurs, otherwise an
139             exception will be raised.
140
141     Returns:
142         Nothing.
143     """
144
145     for i in xrange(100):
146         fh = attempt_to_open_file(fn)
147         if not fh:
148             return
149         fh.close()
150         time.sleep(0.1)
151     if ignore_errors:
152         return
153     raise Exception('File can still be opened')
154
155 def run_and_log(u_boot_console, cmd, ignore_errors=False):
156     """Run a command and log its output.
157
158     Args:
159         u_boot_console: A console connection to U-Boot.
160         cmd: The command to run, as an array of argv[].
161         ignore_errors: Indicate whether to ignore errors. If True, the function
162             will simply return if the command cannot be executed or exits with
163             an error code, otherwise an exception will be raised if such
164             problems occur.
165
166     Returns:
167         Nothing.
168     """
169
170     runner = u_boot_console.log.get_runner(cmd[0], sys.stdout)
171     runner.run(cmd, ignore_errors=ignore_errors)
172     runner.close()
173
174 ram_base = None
175 def find_ram_base(u_boot_console):
176     """Find the running U-Boot's RAM location.
177
178     Probe the running U-Boot to determine the address of the first bank
179     of RAM. This is useful for tests that test reading/writing RAM, or
180     load/save files that aren't associated with some standard address
181     typically represented in an environment variable such as
182     ${kernel_addr_r}. The value is cached so that it only needs to be
183     actively read once.
184
185     Args:
186         u_boot_console: A console connection to U-Boot.
187
188     Returns:
189         The address of U-Boot's first RAM bank, as an integer.
190     """
191
192     global ram_base
193     if u_boot_console.config.buildconfig.get('config_cmd_bdi', 'n') != 'y':
194         pytest.skip('bdinfo command not supported')
195     if ram_base == -1:
196         pytest.skip('Previously failed to find RAM bank start')
197     if ram_base is not None:
198         return ram_base
199
200     with u_boot_console.log.section('find_ram_base'):
201         response = u_boot_console.run_command('bdinfo')
202         for l in response.split('\n'):
203             if '-> start' in l:
204                 ram_base = int(l.split('=')[1].strip(), 16)
205                 break
206         if ram_base is None:
207             ram_base = -1
208             raise Exception('Failed to find RAM bank start in `bdinfo`')
209
210     return ram_base