Add various common utility functions. These will be used by a forthcoming re-written UMS test, and a brand-new DFU test. Signed-off-by: Stephen Warren <swarren@nvidia.com> Acked-by: Simon Glass <sjg@chromium.org>master
parent
3f2faf7327
commit
76b4693928
@ -0,0 +1,171 @@ |
||||
# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0 |
||||
|
||||
# Utility code shared across multiple tests. |
||||
|
||||
import hashlib |
||||
import os |
||||
import os.path |
||||
import sys |
||||
import time |
||||
|
||||
def md5sum_data(data): |
||||
'''Calculate the MD5 hash of some data. |
||||
|
||||
Args: |
||||
data: The data to hash. |
||||
|
||||
Returns: |
||||
The hash of the data, as a binary string. |
||||
''' |
||||
|
||||
h = hashlib.md5() |
||||
h.update(data) |
||||
return h.digest() |
||||
|
||||
def md5sum_file(fn, max_length=None): |
||||
'''Calculate the MD5 hash of the contents of a file. |
||||
|
||||
Args: |
||||
fn: The filename of the file to hash. |
||||
max_length: The number of bytes to hash. If the file has more |
||||
bytes than this, they will be ignored. If None or omitted, the |
||||
entire file will be hashed. |
||||
|
||||
Returns: |
||||
The hash of the file content, as a binary string. |
||||
''' |
||||
|
||||
with open(fn, 'rb') as fh: |
||||
if max_length: |
||||
params = [max_length] |
||||
else: |
||||
params = [] |
||||
data = fh.read(*params) |
||||
return md5sum_data(data) |
||||
|
||||
class PersistentRandomFile(object): |
||||
'''Generate and store information about a persistent file containing |
||||
random data.''' |
||||
|
||||
def __init__(self, u_boot_console, fn, size): |
||||
'''Create or process the persistent file. |
||||
|
||||
If the file does not exist, it is generated. |
||||
|
||||
If the file does exist, its content is hashed for later comparison. |
||||
|
||||
These files are always located in the "persistent data directory" of |
||||
the current test run. |
||||
|
||||
Args: |
||||
u_boot_console: A console connection to U-Boot. |
||||
fn: The filename (without path) to create. |
||||
size: The desired size of the file in bytes. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
''' |
||||
|
||||
self.fn = fn |
||||
|
||||
self.abs_fn = u_boot_console.config.persistent_data_dir + '/' + fn |
||||
|
||||
if os.path.exists(self.abs_fn): |
||||
u_boot_console.log.action('Persistent data file ' + self.abs_fn + |
||||
' already exists') |
||||
self.content_hash = md5sum_file(self.abs_fn) |
||||
else: |
||||
u_boot_console.log.action('Generating ' + self.abs_fn + |
||||
' (random, persistent, %d bytes)' % size) |
||||
data = os.urandom(size) |
||||
with open(self.abs_fn, 'wb') as fh: |
||||
fh.write(data) |
||||
self.content_hash = md5sum_data(data) |
||||
|
||||
def attempt_to_open_file(fn): |
||||
'''Attempt to open a file, without throwing exceptions. |
||||
|
||||
Any errors (exceptions) that occur during the attempt to open the file |
||||
are ignored. This is useful in order to test whether a file (in |
||||
particular, a device node) exists and can be successfully opened, in order |
||||
to poll for e.g. USB enumeration completion. |
||||
|
||||
Args: |
||||
fn: The filename to attempt to open. |
||||
|
||||
Returns: |
||||
An open file handle to the file, or None if the file could not be |
||||
opened. |
||||
''' |
||||
|
||||
try: |
||||
return open(fn, 'rb') |
||||
except: |
||||
return None |
||||
|
||||
def wait_until_open_succeeds(fn): |
||||
'''Poll until a file can be opened, or a timeout occurs. |
||||
|
||||
Continually attempt to open a file, and return when this succeeds, or |
||||
raise an exception after a timeout. |
||||
|
||||
Args: |
||||
fn: The filename to attempt to open. |
||||
|
||||
Returns: |
||||
An open file handle to the file. |
||||
''' |
||||
|
||||
for i in xrange(100): |
||||
fh = attempt_to_open_file(fn) |
||||
if fh: |
||||
return fh |
||||
time.sleep(0.1) |
||||
raise Exception('File could not be opened') |
||||
|
||||
def wait_until_file_open_fails(fn, ignore_errors): |
||||
'''Poll until a file cannot be opened, or a timeout occurs. |
||||
|
||||
Continually attempt to open a file, and return when this fails, or |
||||
raise an exception after a timeout. |
||||
|
||||
Args: |
||||
fn: The filename to attempt to open. |
||||
ignore_errors: Indicate whether to ignore timeout errors. If True, the |
||||
function will simply return if a timeout occurs, otherwise an |
||||
exception will be raised. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
''' |
||||
|
||||
for i in xrange(100): |
||||
fh = attempt_to_open_file(fn) |
||||
if not fh: |
||||
return |
||||
fh.close() |
||||
time.sleep(0.1) |
||||
if ignore_errors: |
||||
return |
||||
raise Exception('File can still be opened') |
||||
|
||||
def run_and_log(u_boot_console, cmd, ignore_errors=False): |
||||
'''Run a command and log its output. |
||||
|
||||
Args: |
||||
u_boot_console: A console connection to U-Boot. |
||||
cmd: The command to run, as an array of argv[]. |
||||
ignore_errors: Indicate whether to ignore errors. If True, the function |
||||
will simply return if the command cannot be executed or exits with |
||||
an error code, otherwise an exception will be raised if such |
||||
problems occur. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
''' |
||||
|
||||
runner = u_boot_console.log.get_runner(cmd[0], sys.stdout) |
||||
runner.run(cmd, ignore_errors=ignore_errors) |
||||
runner.close() |
Loading…
Reference in new issue