xref: /rk3399_rockchip-uboot/test/py/u_boot_utils.py (revision 76b4693928920d7c30fa935b3c46a02b637a29e1)
1*76b46939SStephen Warren# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
2*76b46939SStephen Warren#
3*76b46939SStephen Warren# SPDX-License-Identifier: GPL-2.0
4*76b46939SStephen Warren
5*76b46939SStephen Warren# Utility code shared across multiple tests.
6*76b46939SStephen Warren
7*76b46939SStephen Warrenimport hashlib
8*76b46939SStephen Warrenimport os
9*76b46939SStephen Warrenimport os.path
10*76b46939SStephen Warrenimport sys
11*76b46939SStephen Warrenimport time
12*76b46939SStephen Warren
13*76b46939SStephen Warrendef md5sum_data(data):
14*76b46939SStephen Warren    '''Calculate the MD5 hash of some data.
15*76b46939SStephen Warren
16*76b46939SStephen Warren    Args:
17*76b46939SStephen Warren        data: The data to hash.
18*76b46939SStephen Warren
19*76b46939SStephen Warren    Returns:
20*76b46939SStephen Warren        The hash of the data, as a binary string.
21*76b46939SStephen Warren    '''
22*76b46939SStephen Warren
23*76b46939SStephen Warren    h = hashlib.md5()
24*76b46939SStephen Warren    h.update(data)
25*76b46939SStephen Warren    return h.digest()
26*76b46939SStephen Warren
27*76b46939SStephen Warrendef md5sum_file(fn, max_length=None):
28*76b46939SStephen Warren    '''Calculate the MD5 hash of the contents of a file.
29*76b46939SStephen Warren
30*76b46939SStephen Warren    Args:
31*76b46939SStephen Warren        fn: The filename of the file to hash.
32*76b46939SStephen Warren        max_length: The number of bytes to hash. If the file has more
33*76b46939SStephen Warren            bytes than this, they will be ignored. If None or omitted, the
34*76b46939SStephen Warren            entire file will be hashed.
35*76b46939SStephen Warren
36*76b46939SStephen Warren    Returns:
37*76b46939SStephen Warren        The hash of the file content, as a binary string.
38*76b46939SStephen Warren    '''
39*76b46939SStephen Warren
40*76b46939SStephen Warren    with open(fn, 'rb') as fh:
41*76b46939SStephen Warren        if max_length:
42*76b46939SStephen Warren            params = [max_length]
43*76b46939SStephen Warren        else:
44*76b46939SStephen Warren            params = []
45*76b46939SStephen Warren        data = fh.read(*params)
46*76b46939SStephen Warren    return md5sum_data(data)
47*76b46939SStephen Warren
48*76b46939SStephen Warrenclass PersistentRandomFile(object):
49*76b46939SStephen Warren    '''Generate and store information about a persistent file containing
50*76b46939SStephen Warren    random data.'''
51*76b46939SStephen Warren
52*76b46939SStephen Warren    def __init__(self, u_boot_console, fn, size):
53*76b46939SStephen Warren        '''Create or process the persistent file.
54*76b46939SStephen Warren
55*76b46939SStephen Warren        If the file does not exist, it is generated.
56*76b46939SStephen Warren
57*76b46939SStephen Warren        If the file does exist, its content is hashed for later comparison.
58*76b46939SStephen Warren
59*76b46939SStephen Warren        These files are always located in the "persistent data directory" of
60*76b46939SStephen Warren        the current test run.
61*76b46939SStephen Warren
62*76b46939SStephen Warren        Args:
63*76b46939SStephen Warren            u_boot_console: A console connection to U-Boot.
64*76b46939SStephen Warren            fn: The filename (without path) to create.
65*76b46939SStephen Warren            size: The desired size of the file in bytes.
66*76b46939SStephen Warren
67*76b46939SStephen Warren        Returns:
68*76b46939SStephen Warren            Nothing.
69*76b46939SStephen Warren        '''
70*76b46939SStephen Warren
71*76b46939SStephen Warren        self.fn = fn
72*76b46939SStephen Warren
73*76b46939SStephen Warren        self.abs_fn = u_boot_console.config.persistent_data_dir + '/' + fn
74*76b46939SStephen Warren
75*76b46939SStephen Warren        if os.path.exists(self.abs_fn):
76*76b46939SStephen Warren            u_boot_console.log.action('Persistent data file ' + self.abs_fn +
77*76b46939SStephen Warren                ' already exists')
78*76b46939SStephen Warren            self.content_hash = md5sum_file(self.abs_fn)
79*76b46939SStephen Warren        else:
80*76b46939SStephen Warren            u_boot_console.log.action('Generating ' + self.abs_fn +
81*76b46939SStephen Warren                ' (random, persistent, %d bytes)' % size)
82*76b46939SStephen Warren            data = os.urandom(size)
83*76b46939SStephen Warren            with open(self.abs_fn, 'wb') as fh:
84*76b46939SStephen Warren                fh.write(data)
85*76b46939SStephen Warren            self.content_hash = md5sum_data(data)
86*76b46939SStephen Warren
87*76b46939SStephen Warrendef attempt_to_open_file(fn):
88*76b46939SStephen Warren    '''Attempt to open a file, without throwing exceptions.
89*76b46939SStephen Warren
90*76b46939SStephen Warren    Any errors (exceptions) that occur during the attempt to open the file
91*76b46939SStephen Warren    are ignored. This is useful in order to test whether a file (in
92*76b46939SStephen Warren    particular, a device node) exists and can be successfully opened, in order
93*76b46939SStephen Warren    to poll for e.g. USB enumeration completion.
94*76b46939SStephen Warren
95*76b46939SStephen Warren    Args:
96*76b46939SStephen Warren        fn: The filename to attempt to open.
97*76b46939SStephen Warren
98*76b46939SStephen Warren    Returns:
99*76b46939SStephen Warren        An open file handle to the file, or None if the file could not be
100*76b46939SStephen Warren            opened.
101*76b46939SStephen Warren    '''
102*76b46939SStephen Warren
103*76b46939SStephen Warren    try:
104*76b46939SStephen Warren        return open(fn, 'rb')
105*76b46939SStephen Warren    except:
106*76b46939SStephen Warren        return None
107*76b46939SStephen Warren
108*76b46939SStephen Warrendef wait_until_open_succeeds(fn):
109*76b46939SStephen Warren    '''Poll until a file can be opened, or a timeout occurs.
110*76b46939SStephen Warren
111*76b46939SStephen Warren    Continually attempt to open a file, and return when this succeeds, or
112*76b46939SStephen Warren    raise an exception after a timeout.
113*76b46939SStephen Warren
114*76b46939SStephen Warren    Args:
115*76b46939SStephen Warren        fn: The filename to attempt to open.
116*76b46939SStephen Warren
117*76b46939SStephen Warren    Returns:
118*76b46939SStephen Warren        An open file handle to the file.
119*76b46939SStephen Warren    '''
120*76b46939SStephen Warren
121*76b46939SStephen Warren    for i in xrange(100):
122*76b46939SStephen Warren        fh = attempt_to_open_file(fn)
123*76b46939SStephen Warren        if fh:
124*76b46939SStephen Warren            return fh
125*76b46939SStephen Warren        time.sleep(0.1)
126*76b46939SStephen Warren    raise Exception('File could not be opened')
127*76b46939SStephen Warren
128*76b46939SStephen Warrendef wait_until_file_open_fails(fn, ignore_errors):
129*76b46939SStephen Warren    '''Poll until a file cannot be opened, or a timeout occurs.
130*76b46939SStephen Warren
131*76b46939SStephen Warren    Continually attempt to open a file, and return when this fails, or
132*76b46939SStephen Warren    raise an exception after a timeout.
133*76b46939SStephen Warren
134*76b46939SStephen Warren    Args:
135*76b46939SStephen Warren        fn: The filename to attempt to open.
136*76b46939SStephen Warren        ignore_errors: Indicate whether to ignore timeout errors. If True, the
137*76b46939SStephen Warren            function will simply return if a timeout occurs, otherwise an
138*76b46939SStephen Warren            exception will be raised.
139*76b46939SStephen Warren
140*76b46939SStephen Warren    Returns:
141*76b46939SStephen Warren        Nothing.
142*76b46939SStephen Warren    '''
143*76b46939SStephen Warren
144*76b46939SStephen Warren    for i in xrange(100):
145*76b46939SStephen Warren        fh = attempt_to_open_file(fn)
146*76b46939SStephen Warren        if not fh:
147*76b46939SStephen Warren            return
148*76b46939SStephen Warren        fh.close()
149*76b46939SStephen Warren        time.sleep(0.1)
150*76b46939SStephen Warren    if ignore_errors:
151*76b46939SStephen Warren        return
152*76b46939SStephen Warren    raise Exception('File can still be opened')
153*76b46939SStephen Warren
154*76b46939SStephen Warrendef run_and_log(u_boot_console, cmd, ignore_errors=False):
155*76b46939SStephen Warren    '''Run a command and log its output.
156*76b46939SStephen Warren
157*76b46939SStephen Warren    Args:
158*76b46939SStephen Warren        u_boot_console: A console connection to U-Boot.
159*76b46939SStephen Warren        cmd: The command to run, as an array of argv[].
160*76b46939SStephen Warren        ignore_errors: Indicate whether to ignore errors. If True, the function
161*76b46939SStephen Warren            will simply return if the command cannot be executed or exits with
162*76b46939SStephen Warren            an error code, otherwise an exception will be raised if such
163*76b46939SStephen Warren            problems occur.
164*76b46939SStephen Warren
165*76b46939SStephen Warren    Returns:
166*76b46939SStephen Warren        Nothing.
167*76b46939SStephen Warren    '''
168*76b46939SStephen Warren
169*76b46939SStephen Warren    runner = u_boot_console.log.get_runner(cmd[0], sys.stdout)
170*76b46939SStephen Warren    runner.run(cmd, ignore_errors=ignore_errors)
171*76b46939SStephen Warren    runner.close()
172