Acceptance tests: Extract _console_interaction()
[qemu/ar7.git] / tests / acceptance / avocado_qemu / __init__.py
blob0a50fcf2be6537cbec325c977fd1a2261c4b0e03
1 # Test class and utilities for functional tests
3 # Copyright (c) 2018 Red Hat, Inc.
5 # Author:
6 # Cleber Rosa <crosa@redhat.com>
8 # This work is licensed under the terms of the GNU GPL, version 2 or
9 # later. See the COPYING file in the top-level directory.
11 import logging
12 import os
13 import sys
14 import uuid
15 import tempfile
17 import avocado
19 SRC_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..', '..')
20 sys.path.append(os.path.join(SRC_ROOT_DIR, 'python'))
22 from qemu.machine import QEMUMachine
24 def is_readable_executable_file(path):
25 return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK)
28 def pick_default_qemu_bin(arch=None):
29 """
30 Picks the path of a QEMU binary, starting either in the current working
31 directory or in the source tree root directory.
33 :param arch: the arch to use when looking for a QEMU binary (the target
34 will match the arch given). If None (the default), arch
35 will be the current host system arch (as given by
36 :func:`os.uname`).
37 :type arch: str
38 :returns: the path to the default QEMU binary or None if one could not
39 be found
40 :rtype: str or None
41 """
42 if arch is None:
43 arch = os.uname()[4]
44 # qemu binary path does not match arch for powerpc, handle it
45 if 'ppc64le' in arch:
46 arch = 'ppc64'
47 qemu_bin_relative_path = os.path.join("%s-softmmu" % arch,
48 "qemu-system-%s" % arch)
49 if is_readable_executable_file(qemu_bin_relative_path):
50 return qemu_bin_relative_path
52 qemu_bin_from_src_dir_path = os.path.join(SRC_ROOT_DIR,
53 qemu_bin_relative_path)
54 if is_readable_executable_file(qemu_bin_from_src_dir_path):
55 return qemu_bin_from_src_dir_path
58 def _console_interaction(test, success_message, failure_message,
59 send_string):
60 console = test.vm.console_socket.makefile()
61 console_logger = logging.getLogger('console')
62 while True:
63 if send_string:
64 test.vm.console_socket.sendall(send_string.encode())
65 send_string = None # send only once
66 msg = console.readline().strip()
67 if not msg:
68 continue
69 console_logger.debug(msg)
70 if success_message in msg:
71 break
72 if failure_message and failure_message in msg:
73 console.close()
74 fail = 'Failure message found in console: %s' % failure_message
75 test.fail(fail)
77 def wait_for_console_pattern(test, success_message, failure_message=None):
78 """
79 Waits for messages to appear on the console, while logging the content
81 :param test: an Avocado test containing a VM that will have its console
82 read and probed for a success or failure message
83 :type test: :class:`avocado_qemu.Test`
84 :param success_message: if this message appears, test succeeds
85 :param failure_message: if this message appears, test fails
86 """
87 _console_interaction(test, success_message, failure_message, None)
89 def exec_command_and_wait_for_pattern(test, command,
90 success_message, failure_message=None):
91 """
92 Send a command to a console (appending CRLF characters), then wait
93 for success_message to appear on the console, while logging the.
94 content. Mark the test as failed if failure_message is found instead.
96 :param test: an Avocado test containing a VM that will have its console
97 read and probed for a success or failure message
98 :type test: :class:`avocado_qemu.Test`
99 :param command: the command to send
100 :param success_message: if this message appears, test succeeds
101 :param failure_message: if this message appears, test fails
103 _console_interaction(test, success_message, failure_message, command + '\r')
105 class Test(avocado.Test):
106 def _get_unique_tag_val(self, tag_name):
108 Gets a tag value, if unique for a key
110 vals = self.tags.get(tag_name, [])
111 if len(vals) == 1:
112 return vals.pop()
113 return None
115 def setUp(self):
116 self._vms = {}
118 self.arch = self.params.get('arch',
119 default=self._get_unique_tag_val('arch'))
121 self.machine = self.params.get('machine',
122 default=self._get_unique_tag_val('machine'))
124 default_qemu_bin = pick_default_qemu_bin(arch=self.arch)
125 self.qemu_bin = self.params.get('qemu_bin',
126 default=default_qemu_bin)
127 if self.qemu_bin is None:
128 self.cancel("No QEMU binary defined or found in the source tree")
130 def _new_vm(self, *args):
131 vm = QEMUMachine(self.qemu_bin, sock_dir=tempfile.mkdtemp())
132 if args:
133 vm.add_args(*args)
134 return vm
136 @property
137 def vm(self):
138 return self.get_vm(name='default')
140 def get_vm(self, *args, name=None):
141 if not name:
142 name = str(uuid.uuid4())
143 if self._vms.get(name) is None:
144 self._vms[name] = self._new_vm(*args)
145 if self.machine is not None:
146 self._vms[name].set_machine(self.machine)
147 return self._vms[name]
149 def tearDown(self):
150 for vm in self._vms.values():
151 vm.shutdown()