blob: 8971a88149bd6195aa98e9fade5a69573bb1df3a [file] [log] [blame]
mblighe8819cd2008-02-15 16:48:40 +00001"""\
2Logic for control file generation.
3"""
4
5__author__ = 'showard@google.com (Steve Howard)'
6
showard9ca52702008-06-02 21:14:49 +00007import re, os
mbligh120351e2009-01-24 01:40:45 +00008
9import common
10from autotest_lib.frontend.afe import model_logic
Michael Tang84a2ecf2016-06-07 15:10:53 -070011from autotest_lib.frontend.afe import site_rpc_interface
mblighe8819cd2008-02-15 16:48:40 +000012import frontend.settings
13
14AUTOTEST_DIR = os.path.abspath(os.path.join(
15 os.path.dirname(frontend.settings.__file__), '..'))
16
mblighc86113b2009-04-28 18:32:51 +000017EMPTY_TEMPLATE = 'def step_init():\n'
mblighe8819cd2008-02-15 16:48:40 +000018
mblighf5fdfab2008-06-16 23:57:25 +000019CLIENT_STEP_TEMPLATE = " job.next_step('step%d')\n"
mblighc86113b2009-04-28 18:32:51 +000020SERVER_STEP_TEMPLATE = ' step%d()\n'
showard9ca52702008-06-02 21:14:49 +000021
showard232b7ae2009-11-10 00:46:48 +000022
Richard Barnette8e33b4e2016-05-21 12:12:26 -070023def _read_control_file(test):
Michael Tang84a2ecf2016-06-07 15:10:53 -070024 """Reads the test control file from local disk.
25
26 @param test The test name.
27
28 @return The test control file string.
29 """
jadmanski0afbb632008-06-06 21:10:57 +000030 control_file = open(os.path.join(AUTOTEST_DIR, test.path))
31 control_contents = control_file.read()
32 control_file.close()
33 return control_contents
mblighe8819cd2008-02-15 16:48:40 +000034
35
Richard Barnette8e33b4e2016-05-21 12:12:26 -070036def _add_boilerplate_to_nested_steps(lines):
Michael Tang84a2ecf2016-06-07 15:10:53 -070037 """Adds boilerplate magic.
38
39 @param lines The string of lines.
40
41 @returns The string lines.
42 """
jadmanski0afbb632008-06-06 21:10:57 +000043 # Look for a line that begins with 'def step_init():' while
44 # being flexible on spacing. If it's found, this will be
45 # a nested set of steps, so add magic to make it work.
46 # See client/bin/job.py's step_engine for more info.
47 if re.search(r'^(.*\n)*def\s+step_init\s*\(\s*\)\s*:', lines):
48 lines += '\nreturn locals() '
49 lines += '# Boilerplate magic for nested sets of steps'
50 return lines
showard9ca52702008-06-02 21:14:49 +000051
52
Richard Barnette8e33b4e2016-05-21 12:12:26 -070053def _format_step(item, lines):
Michael Tang84a2ecf2016-06-07 15:10:53 -070054 """Format a line item.
55 @param item The item number.
56 @param lines The string of lines.
57
58 @returns The string lines.
59 """
Richard Barnette8e33b4e2016-05-21 12:12:26 -070060 lines = _indent_text(lines, ' ')
jadmanski0afbb632008-06-06 21:10:57 +000061 lines = 'def step%d():\n%s' % (item, lines)
62 return lines
showard9ca52702008-06-02 21:14:49 +000063
jadmanski0afbb632008-06-06 21:10:57 +000064
Richard Barnette8e33b4e2016-05-21 12:12:26 -070065def _get_tests_stanza(tests, is_server, prepend=None, append=None,
Michael Tang84a2ecf2016-06-07 15:10:53 -070066 client_control_file='', test_source_build=None):
mbligh120351e2009-01-24 01:40:45 +000067 """ Constructs the control file test step code from a list of tests.
mbligh12eafff2008-11-05 23:42:42 +000068
mbligh120351e2009-01-24 01:40:45 +000069 @param tests A sequence of test control files to run.
70 @param is_server bool, Is this a server side test?
71 @param prepend A list of steps to prepend to each client test.
72 Defaults to [].
73 @param append A list of steps to append to each client test.
74 Defaults to [].
75 @param client_control_file If specified, use this text as the body of a
76 final client control file to run after tests. is_server must be False.
Michael Tang84a2ecf2016-06-07 15:10:53 -070077 @param test_source_build: Build to be used to retrieve test code. Default
78 to None.
mbligh120351e2009-01-24 01:40:45 +000079
80 @returns The control file test code to be run.
mbligh12eafff2008-11-05 23:42:42 +000081 """
mbligh120351e2009-01-24 01:40:45 +000082 assert not (client_control_file and is_server)
mbligh12eafff2008-11-05 23:42:42 +000083 if not prepend:
84 prepend = []
85 if not append:
86 append = []
Michael Tang84a2ecf2016-06-07 15:10:53 -070087 if test_source_build:
88 raw_control_files = site_rpc_interface.get_test_control_files_by_build(
89 tests, test_source_build)
90 else:
Richard Barnette8e33b4e2016-05-21 12:12:26 -070091 raw_control_files = [_read_control_file(test) for test in tests]
mbligh120351e2009-01-24 01:40:45 +000092 if client_control_file:
Richard Barnette8e33b4e2016-05-21 12:12:26 -070093 # 'return locals()' is always appended in case the user forgot, it
mbligh120351e2009-01-24 01:40:45 +000094 # is necessary to allow for nested step engine execution to work.
95 raw_control_files.append(client_control_file + '\nreturn locals()')
Richard Barnette8e33b4e2016-05-21 12:12:26 -070096 raw_steps = prepend + [_add_boilerplate_to_nested_steps(step)
jadmanski0afbb632008-06-06 21:10:57 +000097 for step in raw_control_files] + append
Richard Barnette8e33b4e2016-05-21 12:12:26 -070098 steps = [_format_step(index, step)
jadmanski0afbb632008-06-06 21:10:57 +000099 for index, step in enumerate(raw_steps)]
mblighc86113b2009-04-28 18:32:51 +0000100 if is_server:
101 step_template = SERVER_STEP_TEMPLATE
102 footer = '\n\nstep_init()\n'
103 else:
104 step_template = CLIENT_STEP_TEMPLATE
105 footer = ''
106
107 header = ''.join(step_template % i for i in xrange(len(steps)))
108 return header + '\n' + '\n\n'.join(steps) + footer
mblighe8819cd2008-02-15 16:48:40 +0000109
110
Richard Barnette8e33b4e2016-05-21 12:12:26 -0700111def _indent_text(text, indent):
showardd2624152009-04-29 21:29:01 +0000112 """Indent given lines of python code avoiding indenting multiline
Michael Tang84a2ecf2016-06-07 15:10:53 -0700113 quoted content (only for triple " and ' quoting for now).
114
115 @param text The string of lines.
116 @param indent The indent string.
117
118 @return The indented string.
119 """
showardd2624152009-04-29 21:29:01 +0000120 regex = re.compile('(\\\\*)("""|\'\'\')')
121
122 res = []
123 in_quote = None
124 for line in text.splitlines():
125 # if not within a multinline quote indent the line contents
126 if in_quote:
127 res.append(line)
128 else:
129 res.append(indent + line)
130
131 while line:
132 match = regex.search(line)
133 if match:
134 # for an even number of backslashes before the triple quote
135 if len(match.group(1)) % 2 == 0:
136 if not in_quote:
137 in_quote = match.group(2)[0]
138 elif in_quote == match.group(2)[0]:
139 # if we found a matching end triple quote
140 in_quote = None
141 line = line[match.end():]
142 else:
143 break
144
145 return '\n'.join(res)
mblighe8819cd2008-02-15 16:48:40 +0000146
147
showard91f85102009-10-12 20:34:52 +0000148def _get_profiler_commands(profilers, is_server, profile_only):
showard2b9a88b2008-06-13 20:55:03 +0000149 prepend, append = [], []
showard91f85102009-10-12 20:34:52 +0000150 if profile_only is not None:
mblighfbf73ae2009-12-19 05:22:42 +0000151 prepend.append("job.default_profile_only = %r" % profile_only)
showard2b9a88b2008-06-13 20:55:03 +0000152 for profiler in profilers:
153 prepend.append("job.profilers.add('%s')" % profiler.name)
154 append.append("job.profilers.delete('%s')" % profiler.name)
155 return prepend, append
156
157
Richard Barnette8e33b4e2016-05-21 12:12:26 -0700158def _sanity_check_generate_control(is_server, client_control_file):
mbligh12eafff2008-11-05 23:42:42 +0000159 """
mbligh120351e2009-01-24 01:40:45 +0000160 Sanity check some of the parameters to generate_control().
161
162 This exists as its own function so that site_control_file may call it as
163 well from its own generate_control().
164
165 @raises ValidationError if any of the parameters do not make sense.
166 """
167 if is_server and client_control_file:
168 raise model_logic.ValidationError(
169 {'tests' : 'You cannot run server tests at the same time '
170 'as directly supplying a client-side control file.'})
171
172
Richard Barnette8e33b4e2016-05-21 12:12:26 -0700173def generate_control(tests, is_server=False, profilers=(),
174 client_control_file='', profile_only=None,
175 test_source_build=None):
mbligh120351e2009-01-24 01:40:45 +0000176 """
177 Generate a control file for a sequence of tests.
178
179 @param tests A sequence of test control files to run.
mbligh120351e2009-01-24 01:40:45 +0000180 @param is_server bool, Is this a server control file rather than a client?
181 @param profilers A list of profiler objects to enable during the tests.
182 @param client_control_file Contents of a client control file to run as the
mbligha3c58d22009-08-24 22:01:51 +0000183 last test after everything in tests. Requires is_server=False.
showard91f85102009-10-12 20:34:52 +0000184 @param profile_only bool, should this control file run all tests in
185 profile_only mode by default
Michael Tang84a2ecf2016-06-07 15:10:53 -0700186 @param test_source_build: Build to be used to retrieve test code. Default
187 to None.
mbligh120351e2009-01-24 01:40:45 +0000188
189 @returns The control file text as a string.
190 """
Richard Barnette8e33b4e2016-05-21 12:12:26 -0700191 _sanity_check_generate_control(is_server=is_server,
192 client_control_file=client_control_file)
193 control_file_text = EMPTY_TEMPLATE
showard91f85102009-10-12 20:34:52 +0000194 prepend, append = _get_profiler_commands(profilers, is_server, profile_only)
Richard Barnette8e33b4e2016-05-21 12:12:26 -0700195 control_file_text += _get_tests_stanza(tests, is_server, prepend, append,
196 client_control_file,
197 test_source_build)
jadmanski0afbb632008-06-06 21:10:57 +0000198 return control_file_text