4 # Copyright (c) 2018 Vojtech Horky
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
11 # - Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # - Redistributions in binary form must reproduce the above copyright
14 # notice, this list of conditions and the following disclaimer in the
15 # documentation and/or other materials provided with the distribution.
16 # - The name of the author may not be used to endorse or promote products
17 # derived from this software without specific prior written permission.
19 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 from htest
.utils
import retries
37 Base class for individual tasks that are executed in a scenario.
40 def __init__(self
, name
):
42 Set @name to the task name (call from subclass).
47 self
.fail_message
= ''
48 self
.logger
= logging
.getLogger(name
)
50 def check_required_argument(self
, args
, name
):
52 To be used by subclasses to check that arguments
56 raise Exception("Required argument {} missing.".format(name
))
58 def is_vm_launcher(self
):
60 Whether this task starts a new VM.
67 def set_machine(self
, machine
):
69 Set machine responsible for executing this task.
71 self
.machine
= machine
75 Actually execute this task.
79 class ScenarioTaskBoot(ScenarioTask
):
81 Brings the machine up.
83 def __init__(self
, args
):
84 ScenarioTask
.__init
__(self
, 'boot')
87 def is_vm_launcher(self
):
93 class ScenarioTaskCommand(ScenarioTask
):
95 Run a command in vterm.
98 def __init__(self
, args
):
99 ScenarioTask
.__init
__(self
, 'command')
100 if type(args
) is str:
101 args
= { 'args': args
}
102 self
.check_required_argument(args
, 'args')
103 self
.command
= args
['args']
104 self
.ignore_abort
= False
105 if 'ignoreabort' in args
:
106 self
.ignore_abort
= args
['ignoreabort']
109 def _find_in_lines(self
, text
, lines
):
111 if l
.find(text
) != -1:
116 self
.logger
.info("Typing '{}' into {}.".format(self
.command
, self
.machine
.name
))
118 # Capture the screen before typing the command.
119 self
.machine
.capture_vterm()
121 self
.machine
.type(self
.command
)
123 # Wait until the command is fully displayed on the screen.
124 # That is needed to properly detect the newly displayed lines.
125 # FIXME: this will not work for long commands spanning multiple lines
126 for xxx
in retries(timeout
=60, interval
=2, name
="vterm-type", message
="Failed to type command"):
127 self
.machine
.vterm
= []
128 self
.machine
.capture_vterm()
129 lines
= self
.machine
.vterm
132 line
= lines
[0].strip()
133 if line
.endswith("_"):
135 if line
.endswith(self
.command
.strip()):
138 self
.machine
.vterm
= []
139 self
.machine
.type('\n')
141 # Read output of the command.
142 # We wait until prompt reappears or we find some text that is not
143 # supposed to be there.
144 for xxx
in retries(timeout
=60, interval
=2, name
="vterm-run", message
="Failed to run command"):
145 self
.logger
.debug("self.vterm = {}".format(self
.machine
.vterm
))
146 self
.machine
.capture_vterm()
147 lines
= self
.machine
.vterm
148 self
.logger
.debug("Read lines {}".format(lines
))
149 self
.machine
.vterm
= []
150 if not self
.ignore_abort
:
151 if self
._find
_in
_lines
('Cannot spawn', lines
) or self
._find
_in
_lines
('Command failed', lines
):
152 raise Exception('Failed to run command')
153 if 'negassert' in self
.args
:
154 if self
._find
_in
_lines
(self
.args
['negassert'], lines
):
155 raise Exception('Found forbidden text {} ...'.format(self
.args
['negassert']))
156 if self
._find
_in
_lines
('# _', lines
):
157 if 'assert' in self
.args
:
158 if not self
._find
_in
_lines
(self
.args
['assert'], lines
):
159 raise Exception('Missing expected text {} ...'.format(self
.args
['assert']))
161 self
.logger
.info("Command '{}' done.".format(self
.command
))
163 class ScenarioTaskCls(ScenarioTask
):
168 def __init__(self
, args
):
169 ScenarioTask
.__init
__(self
, 'vterm-cls')
172 self
.logger
.info("Clearing the screen.")
175 self
.machine
.type('\n')