blob: 9d243e047b19e1b1071b3036d6ad97a20b67b3a4 [file] [log] [blame]
Stephen Warrenef824f52016-01-22 12:30:12 -07001# 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
7import hashlib
8import os
9import os.path
Heiko Schocher56549012016-05-09 10:08:24 +020010import pytest
Stephen Warrenef824f52016-01-22 12:30:12 -070011import sys
12import time
13
14def md5sum_data(data):
Stephen Warren75e731e2016-01-26 13:41:30 -070015 """Calculate the MD5 hash of some data.
Stephen Warrenef824f52016-01-22 12:30:12 -070016
17 Args:
18 data: The data to hash.
19
20 Returns:
21 The hash of the data, as a binary string.
Stephen Warren75e731e2016-01-26 13:41:30 -070022 """
Stephen Warrenef824f52016-01-22 12:30:12 -070023
24 h = hashlib.md5()
25 h.update(data)
26 return h.digest()
27
28def md5sum_file(fn, max_length=None):
Stephen Warren75e731e2016-01-26 13:41:30 -070029 """Calculate the MD5 hash of the contents of a file.
Stephen Warrenef824f52016-01-22 12:30:12 -070030
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.
Stephen Warren75e731e2016-01-26 13:41:30 -070039 """
Stephen Warrenef824f52016-01-22 12:30:12 -070040
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
49class PersistentRandomFile(object):
Stephen Warren75e731e2016-01-26 13:41:30 -070050 """Generate and store information about a persistent file containing
51 random data."""
Stephen Warrenef824f52016-01-22 12:30:12 -070052
53 def __init__(self, u_boot_console, fn, size):
Stephen Warren75e731e2016-01-26 13:41:30 -070054 """Create or process the persistent file.
Stephen Warrenef824f52016-01-22 12:30:12 -070055
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.
Stephen Warren75e731e2016-01-26 13:41:30 -070070 """
Stephen Warrenef824f52016-01-22 12:30:12 -070071
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
88def attempt_to_open_file(fn):
Stephen Warren75e731e2016-01-26 13:41:30 -070089 """Attempt to open a file, without throwing exceptions.
Stephen Warrenef824f52016-01-22 12:30:12 -070090
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.
Stephen Warren75e731e2016-01-26 13:41:30 -0700102 """
Stephen Warrenef824f52016-01-22 12:30:12 -0700103
104 try:
105 return open(fn, 'rb')
106 except:
107 return None
108
109def wait_until_open_succeeds(fn):
Stephen Warren75e731e2016-01-26 13:41:30 -0700110 """Poll until a file can be opened, or a timeout occurs.
Stephen Warrenef824f52016-01-22 12:30:12 -0700111
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.
Stephen Warren75e731e2016-01-26 13:41:30 -0700120 """
Stephen Warrenef824f52016-01-22 12:30:12 -0700121
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
129def wait_until_file_open_fails(fn, ignore_errors):
Stephen Warren75e731e2016-01-26 13:41:30 -0700130 """Poll until a file cannot be opened, or a timeout occurs.
Stephen Warrenef824f52016-01-22 12:30:12 -0700131
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.
Stephen Warren75e731e2016-01-26 13:41:30 -0700143 """
Stephen Warrenef824f52016-01-22 12:30:12 -0700144
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
155def run_and_log(u_boot_console, cmd, ignore_errors=False):
Stephen Warren75e731e2016-01-26 13:41:30 -0700156 """Run a command and log its output.
Stephen Warrenef824f52016-01-22 12:30:12 -0700157
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.
Stephen Warren75e731e2016-01-26 13:41:30 -0700168 """
Stephen Warrenef824f52016-01-22 12:30:12 -0700169
170 runner = u_boot_console.log.get_runner(cmd[0], sys.stdout)
171 runner.run(cmd, ignore_errors=ignore_errors)
172 runner.close()
Stephen Warrenf7743ce2016-01-21 16:05:30 -0700173
174ram_base = None
175def find_ram_base(u_boot_console):
Stephen Warren75e731e2016-01-26 13:41:30 -0700176 """Find the running U-Boot's RAM location.
Stephen Warrenf7743ce2016-01-21 16:05:30 -0700177
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.
Stephen Warren75e731e2016-01-26 13:41:30 -0700190 """
Stephen Warrenf7743ce2016-01-21 16:05:30 -0700191
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