4 # Copyright (c) 2017 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.
35 from hbuild
.scheduler
import Task
36 from hbuild
.builders
.helenos
import HelenOSBuildWithHarboursTask
38 class GetTestListTask(Task
):
39 def __init__(self
, root_path
, test_filter
):
40 Task
.__init
__(self
, None)
41 self
.root_path
= os
.path
.abspath(os
.path
.join(root_path
, 'scenarios'))
42 self
.test_filter
= test_filter
44 def get_scenario_list(self
, root
, base
):
49 for name
in os
.listdir(root
):
50 path
= os
.path
.join(root
, name
)
51 if os
.path
.isdir(path
):
52 tmp
= self
.get_scenario_list(path
, base
+ name
+ '/')
55 elif os
.path
.isfile(path
):
56 xxx
, ext
= os
.path
.splitext(path
)
58 tests
.append(base
+ name
)
63 files
= self
.get_scenario_list(self
.root_path
, '')
65 if 'ALL' in self
.test_filter
:
66 self
.test_filter
= files
70 for pat
in self
.test_filter
:
71 if fnmatch
.fnmatch(fn
, pat
):
72 files_filtered
.append(fn
)
75 self
.ctl
.dprint('scenarios files: %s', files_filtered
)
77 'scenarios' : files_filtered
,
78 'scenario_dir': self
.root_path
82 class ScheduleTestsTask(Task
):
83 NEEDED_HARBOUR_PATTERN
= re
.compile('^[ ]*#[ ]*@needs[ ]+(?P<HARBOURS>.*)$')
84 SPLIT_HARBOURS_PATTERN
= re
.compile('\w+')
86 def __init__(self
, scheduler
, extra_builds
, base_path
, extra_tester_options
):
87 self
.scheduler
= scheduler
88 self
.testable_profiles
= [ 'ia32', 'amd64', 'arm32/integratorcp', 'ppc32' ]
89 self
.extra_builds
= extra_builds
90 self
.base_path
= base_path
91 self
.extra_tester_options
= extra_tester_options
92 Task
.__init
__(self
, None)
95 helenos_build_tasks
= self
.ctl
.get_dependency_data('helenos_tasks')
96 scenarios
= self
.ctl
.get_dependency_data('scenarios')
97 scenario_base_path
= self
.ctl
.get_dependency_data('scenario_dir')
98 harbour_tasks
= self
.ctl
.get_dependency_data('harbour_tasks')
100 self
.extra_builds
.set_dependent_tasks(helenos_build_tasks
, harbour_tasks
)
102 profiles_all
= helenos_build_tasks
.keys()
104 for p
in self
.testable_profiles
:
105 if p
in profiles_all
:
109 for scenario
in scenarios
:
110 for profile
in profiles
:
111 scenario_filename
= os
.path
.join(scenario_base_path
, scenario
)
112 dep_harbours
= self
.get_needed_harbours(scenario_filename
)
113 if len(dep_harbours
) > 0:
114 helenos_task
= self
.extra_builds
.build(profile
, dep_harbours
)
115 if helenos_task
is None:
116 # TODO: properly handle the error
119 helenos_task
= helenos_build_tasks
[profile
]
120 scenario_flat
= scenario
.replace('/', '-').replace('.', '-')
121 self
.scheduler
.submit("Testing {} on {}".format(scenario
, profile
),
122 'test-{}-{}'.format(profile
.replace('/', '-'), scenario_flat
),
123 TestRunTask(profile
, scenario
, scenario_filename
,
124 os
.path
.abspath(os
.path
.join(self
.base_path
, 'test-in-vm.py')), self
.extra_tester_options
),
130 # scenario_dependencies = {}
131 # for s in scenarios:
132 # scenario_dependencies[s] = {}
135 # scenario_dependencies[s][p] = [
136 # self.extra_builds.build(p, harbours)
140 # needed_harbours = {}
141 # for s in scenarios:
143 # needed_harbours[ s ] = {
144 # 'harbours': harbours,
145 # 'hash': '-'.join(harbours)
148 # self.ctl.dprint('Harbour tasks: {}'.format(harbour_tasks))
149 # self.ctl.dprint('Needed harbours: {}'.format(needed_harbours))
152 # special_images_tasks = {}
153 # for s in scenarios:
154 # self.ctl.dprint('{}/{}: special-tasks: {}'.format(p, s, special_images_tasks))
155 # s_flat = s.replace('/', '-').replace('.', '-')
156 # deps = [ helenos_build_tasks[p] ]
157 # if len(needed_harbours[s]['harbours']) > 0:
158 # if not needed_harbours[s]['hash'] in special_images_tasks:
159 # extra_deps = [ helenos_build_tasks[p] ]
160 # continue_outer = False
161 # for h in needed_harbours[s]['harbours']:
162 # if h not in harbour_tasks[p]:
163 # continue_outer = True
166 # extra_deps.append(harbour_tasks[p][h])
169 # task_name = 'extra-{}-with-{}'.format(p.replace('/', '-'), needed_harbours[s]['hash'])
170 # self.scheduler.submit("Special build of {} with {}".format(p, ','.join(needed_harbours[s]['harbours'])),
172 # HelenOSBuildWithHarboursTask(p, needed_harbours[s]['harbours']),
174 # [ 'extras-{}'.format(p) ]
176 # special_images_tasks[ needed_harbours[s]['hash'] ] = task_name
177 # deps.append(special_images_tasks[ needed_harbours[s]['hash'] ])
178 # self.scheduler.submit("Testing {} on {}".format(s, p),
179 # 'test-{}-{}'.format(p.replace('/', '-'), s_flat),
180 # TestRunTask(p, s, os.path.join(scenario_base_path, s)),
187 def get_needed_harbours(self
, scenario_filename
):
188 with
open(scenario_filename
) as f
:
191 scenario
= yaml
.load(f
)
192 if ('meta' in scenario
) and ('harbours' in scenario
['meta']):
193 res
= scenario
['meta']['harbours']
196 except Exception as ex
:
200 class TestRunTask(Task
):
201 def __init__(self
, profile
, scenario_name
, scenario_full_filename
, test_script_filename
, extra_test_script_options
):
202 self
.profile
= profile
203 self
.scenario_name
= scenario_name
204 self
.scenario
= scenario_full_filename
205 self
.tester
= os
.path
.abspath(test_script_filename
)
206 self
.tester_options
= extra_test_script_options
207 Task
.__init
__(self
, 'test', arch
=profile
, scenario
=scenario_name
)
210 os_image
= self
.ctl
.get_dependency_data('image')
211 my_dir
= self
.ctl
.get_dependency_data('dir')
214 # FIXME: this is probably not the best location for the files
215 vterm_dump
= os
.path
.join(my_dir
, 'dump.txt')
216 screenshot
= os
.path
.join(my_dir
, 'screenshot.png')
221 '--arch={}'.format(self
.profile
),
222 '--image={}'.format(os_image
),
223 '--vterm-dump={}'.format(vterm_dump
),
224 '--last-screenshot={}'.format(screenshot
),
226 for i
in self
.tester_options
:
228 command
.append('--scenario')
229 command
.append(self
.scenario
)
230 res
= self
.ctl
.run_command(command
)
234 profile_flat
= self
.profile
.replace("/", "-")
235 scenario_flat
= self
.scenario_name
.replace('.yml', '').replace('/', '-').replace('.', '-')
236 self
.ctl
.add_downloadable_file("Last screen", '{}/test-{}-screen.png'.format(profile_flat
, scenario_flat
), screenshot
)
237 self
.ctl
.add_downloadable_file("Terminal dump", '{}/test-{}-vterm.txt'.format(profile_flat
, scenario_flat
), vterm_dump
)