diff options
Diffstat (limited to 'meta/lib/oeqa/selftest')
-rw-r--r-- | meta/lib/oeqa/selftest/__init__.py | 2 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/_sstatetests_noauto.py | 95 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/_toaster.py | 445 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/base.py | 131 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/bblayers.py | 43 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/bbtests.py | 178 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/buildhistory.py | 45 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/buildoptions.py | 120 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/oescripts.py | 54 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/prservice.py | 121 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/sstate.py | 53 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/sstatetests.py | 204 |
12 files changed, 1491 insertions, 0 deletions
diff --git a/meta/lib/oeqa/selftest/__init__.py b/meta/lib/oeqa/selftest/__init__.py new file mode 100644 index 0000000000..3ad9513f40 --- /dev/null +++ b/meta/lib/oeqa/selftest/__init__.py | |||
@@ -0,0 +1,2 @@ | |||
1 | from pkgutil import extend_path | ||
2 | __path__ = extend_path(__path__, __name__) | ||
diff --git a/meta/lib/oeqa/selftest/_sstatetests_noauto.py b/meta/lib/oeqa/selftest/_sstatetests_noauto.py new file mode 100644 index 0000000000..fc9ae7efb9 --- /dev/null +++ b/meta/lib/oeqa/selftest/_sstatetests_noauto.py | |||
@@ -0,0 +1,95 @@ | |||
1 | import datetime | ||
2 | import unittest | ||
3 | import os | ||
4 | import re | ||
5 | import shutil | ||
6 | |||
7 | import oeqa.utils.ftools as ftools | ||
8 | from oeqa.selftest.base import oeSelfTest | ||
9 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer | ||
10 | from oeqa.selftest.sstate import SStateBase | ||
11 | |||
12 | |||
13 | class RebuildFromSState(SStateBase): | ||
14 | |||
15 | @classmethod | ||
16 | def setUpClass(self): | ||
17 | self.builddir = os.path.join(os.environ.get('BUILDDIR')) | ||
18 | |||
19 | def get_dep_targets(self, primary_targets): | ||
20 | found_targets = [] | ||
21 | bitbake("-g " + ' '.join(map(str, primary_targets))) | ||
22 | with open(os.path.join(self.builddir, 'pn-buildlist'), 'r') as pnfile: | ||
23 | found_targets = pnfile.read().splitlines() | ||
24 | return found_targets | ||
25 | |||
26 | def configure_builddir(self, builddir): | ||
27 | os.mkdir(builddir) | ||
28 | self.track_for_cleanup(builddir) | ||
29 | os.mkdir(os.path.join(builddir, 'conf')) | ||
30 | shutil.copyfile(os.path.join(os.environ.get('BUILDDIR'), 'conf/local.conf'), os.path.join(builddir, 'conf/local.conf')) | ||
31 | config = {} | ||
32 | config['default_sstate_dir'] = "SSTATE_DIR ?= \"${TOPDIR}/sstate-cache\"" | ||
33 | config['null_sstate_mirrors'] = "SSTATE_MIRRORS = \"\"" | ||
34 | config['default_tmp_dir'] = "TMPDIR = \"${TOPDIR}/tmp\"" | ||
35 | for key in config: | ||
36 | ftools.append_file(os.path.join(builddir, 'conf/selftest.inc'), config[key]) | ||
37 | shutil.copyfile(os.path.join(os.environ.get('BUILDDIR'), 'conf/bblayers.conf'), os.path.join(builddir, 'conf/bblayers.conf')) | ||
38 | try: | ||
39 | shutil.copyfile(os.path.join(os.environ.get('BUILDDIR'), 'conf/auto.conf'), os.path.join(builddir, 'conf/auto.conf')) | ||
40 | except: | ||
41 | pass | ||
42 | |||
43 | def hardlink_tree(self, src, dst): | ||
44 | os.mkdir(dst) | ||
45 | self.track_for_cleanup(dst) | ||
46 | for root, dirs, files in os.walk(src): | ||
47 | if root == src: | ||
48 | continue | ||
49 | os.mkdir(os.path.join(dst, root.split(src)[1][1:])) | ||
50 | for sstate_file in files: | ||
51 | os.link(os.path.join(root, sstate_file), os.path.join(dst, root.split(src)[1][1:], sstate_file)) | ||
52 | |||
53 | def run_test_sstate_rebuild(self, primary_targets, relocate=False, rebuild_dependencies=False): | ||
54 | buildA = os.path.join(self.builddir, 'buildA') | ||
55 | if relocate: | ||
56 | buildB = os.path.join(self.builddir, 'buildB') | ||
57 | else: | ||
58 | buildB = buildA | ||
59 | |||
60 | if rebuild_dependencies: | ||
61 | rebuild_targets = self.get_dep_targets(primary_targets) | ||
62 | else: | ||
63 | rebuild_targets = primary_targets | ||
64 | |||
65 | self.configure_builddir(buildA) | ||
66 | runCmd((". %s/oe-init-build-env %s && " % (get_bb_var('COREBASE'), buildA)) + 'bitbake ' + ' '.join(map(str, primary_targets)), shell=True, executable='/bin/bash') | ||
67 | self.hardlink_tree(os.path.join(buildA, 'sstate-cache'), os.path.join(self.builddir, 'sstate-cache-buildA')) | ||
68 | shutil.rmtree(buildA) | ||
69 | |||
70 | failed_rebuild = [] | ||
71 | failed_cleansstate = [] | ||
72 | for target in rebuild_targets: | ||
73 | self.configure_builddir(buildB) | ||
74 | self.hardlink_tree(os.path.join(self.builddir, 'sstate-cache-buildA'), os.path.join(buildB, 'sstate-cache')) | ||
75 | |||
76 | result_cleansstate = runCmd((". %s/oe-init-build-env %s && " % (get_bb_var('COREBASE'), buildB)) + 'bitbake -ccleansstate ' + target, ignore_status=True, shell=True, executable='/bin/bash') | ||
77 | if not result_cleansstate.status == 0: | ||
78 | failed_cleansstate.append(target) | ||
79 | shutil.rmtree(buildB) | ||
80 | continue | ||
81 | |||
82 | result_build = runCmd((". %s/oe-init-build-env %s && " % (get_bb_var('COREBASE'), buildB)) + 'bitbake ' + target, ignore_status=True, shell=True, executable='/bin/bash') | ||
83 | if not result_build.status == 0: | ||
84 | failed_rebuild.append(target) | ||
85 | |||
86 | shutil.rmtree(buildB) | ||
87 | |||
88 | self.assertFalse(failed_rebuild, msg="The following recipes have failed to rebuild: %s" % ' '.join(map(str, failed_rebuild))) | ||
89 | self.assertFalse(failed_cleansstate, msg="The following recipes have failed cleansstate(all others have passed both cleansstate and rebuild from sstate tests): %s" % ' '.join(map(str, failed_cleansstate))) | ||
90 | |||
91 | def test_sstate_relocation(self): | ||
92 | self.run_test_sstate_rebuild(['core-image-sato-sdk'], relocate=True, rebuild_dependencies=True) | ||
93 | |||
94 | def test_sstate_rebuild(self): | ||
95 | self.run_test_sstate_rebuild(['core-image-sato-sdk'], relocate=False, rebuild_dependencies=True) | ||
diff --git a/meta/lib/oeqa/selftest/_toaster.py b/meta/lib/oeqa/selftest/_toaster.py new file mode 100644 index 0000000000..1cf28a0144 --- /dev/null +++ b/meta/lib/oeqa/selftest/_toaster.py | |||
@@ -0,0 +1,445 @@ | |||
1 | import unittest | ||
2 | import os | ||
3 | import sys | ||
4 | import shlex, subprocess | ||
5 | import urllib, commands, time, getpass, re, json, shlex | ||
6 | |||
7 | import oeqa.utils.ftools as ftools | ||
8 | from oeqa.selftest.base import oeSelfTest | ||
9 | from oeqa.utils.commands import runCmd | ||
10 | |||
11 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../', 'bitbake/lib/toaster'))) | ||
12 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "toastermain.settings") | ||
13 | |||
14 | import toastermain.settings | ||
15 | from django.db.models import Q | ||
16 | from orm.models import * | ||
17 | from oeqa.utils.decorators import testcase | ||
18 | |||
19 | class ToasterSetup(oeSelfTest): | ||
20 | |||
21 | def recipe_parse(self, file_path, var): | ||
22 | for line in open(file_path,'r'): | ||
23 | if line.find(var) > -1: | ||
24 | val = line.split(" = ")[1].replace("\"", "").strip() | ||
25 | return val | ||
26 | |||
27 | def fix_file_path(self, file_path): | ||
28 | if ":" in file_path: | ||
29 | file_path=file_path.split(":")[2] | ||
30 | return file_path | ||
31 | |||
32 | class Toaster_DB_Tests(ToasterSetup): | ||
33 | |||
34 | # Check if build name is unique - tc_id=795 | ||
35 | @testcase(795) | ||
36 | def test_Build_Unique_Name(self): | ||
37 | all_builds = Build.objects.all().count() | ||
38 | distinct_builds = Build.objects.values('id').distinct().count() | ||
39 | self.assertEqual(distinct_builds, all_builds, msg = 'Build name is not unique') | ||
40 | |||
41 | # Check if build coocker log path is unique - tc_id=819 | ||
42 | @testcase(819) | ||
43 | def test_Build_Unique_Cooker_Log_Path(self): | ||
44 | distinct_path = Build.objects.values('cooker_log_path').distinct().count() | ||
45 | total_builds = Build.objects.values('id').count() | ||
46 | self.assertEqual(distinct_path, total_builds, msg = 'Build coocker log path is not unique') | ||
47 | |||
48 | # Check if the number of errors matches the number of orm_logmessage.level entries with value 2 - tc_id=820 | ||
49 | @testcase(820) | ||
50 | def test_Build_Errors_No(self): | ||
51 | builds = Build.objects.values('id', 'errors_no') | ||
52 | cnt_err = [] | ||
53 | for build in builds: | ||
54 | log_mess_err_no = LogMessage.objects.filter(build = build['id'], level = 2).count() | ||
55 | if (build['errors_no'] != log_mess_err_no): | ||
56 | cnt_err.append(build['id']) | ||
57 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err) | ||
58 | |||
59 | # Check if the number of warnings matches the number of orm_logmessage.level entries with value 1 - tc=821 | ||
60 | @testcase(821) | ||
61 | def test_Build_Warnings_No(self): | ||
62 | builds = Build.objects.values('id', 'warnings_no') | ||
63 | cnt_err = [] | ||
64 | for build in builds: | ||
65 | log_mess_warn_no = LogMessage.objects.filter(build = build['id'], level = 1).count() | ||
66 | if (build['warnings_no'] != log_mess_warn_no): | ||
67 | cnt_err.append(build['id']) | ||
68 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err) | ||
69 | |||
70 | # Check if the build succeeded then the errors_no is 0 - tc_id=822 | ||
71 | @testcase(822) | ||
72 | def test_Build_Suceeded_Errors_No(self): | ||
73 | builds = Build.objects.filter(outcome = 0).values('id', 'errors_no') | ||
74 | cnt_err = [] | ||
75 | for build in builds: | ||
76 | if (build['errors_no'] != 0): | ||
77 | cnt_err.append(build['id']) | ||
78 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err) | ||
79 | |||
80 | # Check if task order is unique for one build - tc=824 | ||
81 | @testcase(824) | ||
82 | def test_Task_Unique_Order(self): | ||
83 | builds = Build.objects.values('id') | ||
84 | cnt_err = [] | ||
85 | for build in builds: | ||
86 | total_task_order = Task.objects.filter(build = build['id']).values('order').count() | ||
87 | distinct_task_order = Task.objects.filter(build = build['id']).values('order').distinct().count() | ||
88 | if (total_task_order != distinct_task_order): | ||
89 | cnt_err.append(build['id']) | ||
90 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err) | ||
91 | |||
92 | # Check task order sequence for one build - tc=825 | ||
93 | @testcase(825) | ||
94 | def test_Task_Order_Sequence(self): | ||
95 | builds = builds = Build.objects.values('id') | ||
96 | cnt_err = [] | ||
97 | for build in builds: | ||
98 | tasks = Task.objects.filter(Q(build = build['id']), ~Q(order = None), ~Q(task_name__contains = '_setscene')).values('id', 'order').order_by("order") | ||
99 | cnt_tasks = 0 | ||
100 | for task in tasks: | ||
101 | cnt_tasks += 1 | ||
102 | if (task['order'] != cnt_tasks): | ||
103 | cnt_err.append(task['id']) | ||
104 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err) | ||
105 | |||
106 | # Check if disk_io matches the difference between EndTimeIO and StartTimeIO in build stats - tc=828 | ||
107 | ### this needs to be updated ### | ||
108 | #def test_Task_Disk_IO_TC828(self): | ||
109 | |||
110 | # Check if outcome = 2 (SSTATE) then sstate_result must be 3 (RESTORED) - tc=832 | ||
111 | @testcase(832) | ||
112 | def test_Task_If_Outcome_2_Sstate_Result_Must_Be_3(self): | ||
113 | tasks = Task.objects.filter(outcome = 2).values('id', 'sstate_result') | ||
114 | cnt_err = [] | ||
115 | for task in tasks: | ||
116 | if (row['sstate_result'] != 3): | ||
117 | cnt_err.append(task['id']) | ||
118 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err) | ||
119 | |||
120 | # Check if outcome = 1 (COVERED) or 3 (EXISTING) then sstate_result must be 0 (SSTATE_NA) - tc=833 | ||
121 | @testcase(833) | ||
122 | def test_Task_If_Outcome_1_3_Sstate_Result_Must_Be_0(self): | ||
123 | tasks = Task.objects.filter(outcome__in = (1, 3)).values('id', 'sstate_result') | ||
124 | cnt_err = [] | ||
125 | for task in tasks: | ||
126 | if (task['sstate_result'] != 0): | ||
127 | cnt_err.append(task['id']) | ||
128 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err) | ||
129 | |||
130 | # Check if outcome is 0 (SUCCESS) or 4 (FAILED) then sstate_result must be 0 (NA), 1 (MISS) or 2 (FAILED) - tc=834 | ||
131 | @testcase(834) | ||
132 | def test_Task_If_Outcome_0_4_Sstate_Result_Must_Be_0_1_2(self): | ||
133 | tasks = Task.objects.filter(outcome__in = (0, 4)).values('id', 'sstate_result') | ||
134 | cnt_err = [] | ||
135 | for task in tasks: | ||
136 | if (task['sstate_result'] not in [0, 1, 2]): | ||
137 | cnt_err.append(task['id']) | ||
138 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err) | ||
139 | |||
140 | # Check if task_executed = TRUE (1), script_type must be 0 (CODING_NA), 2 (CODING_PYTHON), 3 (CODING_SHELL) - tc=891 | ||
141 | @testcase(891) | ||
142 | def test_Task_If_Task_Executed_True_Script_Type_0_2_3(self): | ||
143 | tasks = Task.objects.filter(task_executed = 1).values('id', 'script_type') | ||
144 | cnt_err = [] | ||
145 | for task in tasks: | ||
146 | if (task['script_type'] not in [0, 2, 3]): | ||
147 | cnt_err.append(task['id']) | ||
148 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err) | ||
149 | |||
150 | # Check if task_executed = TRUE (1), outcome must be 0 (SUCCESS) or 4 (FAILED) - tc=836 | ||
151 | @testcase(836) | ||
152 | def test_Task_If_Task_Executed_True_Outcome_0_4(self): | ||
153 | tasks = Task.objects.filter(task_executed = 1).values('id', 'outcome') | ||
154 | cnt_err = [] | ||
155 | for task in tasks: | ||
156 | if (task['outcome'] not in [0, 4]): | ||
157 | cnt_err.append(task['id']) | ||
158 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err) | ||
159 | |||
160 | # Check if task_executed = FALSE (0), script_type must be 0 - tc=890 | ||
161 | @testcase(890) | ||
162 | def test_Task_If_Task_Executed_False_Script_Type_0(self): | ||
163 | tasks = Task.objects.filter(task_executed = 0).values('id', 'script_type') | ||
164 | cnt_err = [] | ||
165 | for task in tasks: | ||
166 | if (task['script_type'] != 0): | ||
167 | cnt_err.append(task['id']) | ||
168 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err) | ||
169 | |||
170 | # Check if task_executed = FALSE (0) and build outcome = SUCCEEDED (0), task outcome must be 1 (COVERED), 2 (CACHED), 3 (PREBUILT), 5 (EMPTY) - tc=837 | ||
171 | @testcase(837) | ||
172 | def test_Task_If_Task_Executed_False_Outcome_1_2_3_5(self): | ||
173 | builds = Build.objects.filter(outcome = 0).values('id') | ||
174 | cnt_err = [] | ||
175 | for build in builds: | ||
176 | tasks = Task.objects.filter(build = build['id'], task_executed = 0).values('id', 'outcome') | ||
177 | for task in tasks: | ||
178 | if (task['outcome'] not in [1, 2, 3, 5]): | ||
179 | cnt_err.append(task['id']) | ||
180 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err) | ||
181 | |||
182 | # Key verification - tc=888 | ||
183 | @testcase(888) | ||
184 | def test_Target_Installed_Package(self): | ||
185 | rows = Target_Installed_Package.objects.values('id', 'target_id', 'package_id') | ||
186 | cnt_err = [] | ||
187 | for row in rows: | ||
188 | target = Target.objects.filter(id = row['target_id']).values('id') | ||
189 | package = Package.objects.filter(id = row['package_id']).values('id') | ||
190 | if (not target or not package): | ||
191 | cnt_err.append(row['id']) | ||
192 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for target installed package id: %s' % cnt_err) | ||
193 | |||
194 | # Key verification - tc=889 | ||
195 | @testcase(889) | ||
196 | def test_Task_Dependency(self): | ||
197 | rows = Task_Dependency.objects.values('id', 'task_id', 'depends_on_id') | ||
198 | cnt_err = [] | ||
199 | for row in rows: | ||
200 | task_id = Task.objects.filter(id = row['task_id']).values('id') | ||
201 | depends_on_id = Task.objects.filter(id = row['depends_on_id']).values('id') | ||
202 | if (not task_id or not depends_on_id): | ||
203 | cnt_err.append(row['id']) | ||
204 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for task dependency id: %s' % cnt_err) | ||
205 | |||
206 | # Check if build target file_name is populated only if is_image=true AND orm_build.outcome=0 then if the file exists and its size matches the file_size value | ||
207 | ### Need to add the tc in the test run | ||
208 | @testcase(1037) | ||
209 | def test_Target_File_Name_Populated(self): | ||
210 | builds = Build.objects.filter(outcome = 0).values('id') | ||
211 | for build in builds: | ||
212 | targets = Target.objects.filter(build_id = build['id'], is_image = 1).values('id') | ||
213 | for target in targets: | ||
214 | target_files = Target_Image_File.objects.filter(target_id = target['id']).values('id', 'file_name', 'file_size') | ||
215 | cnt_err = [] | ||
216 | for file_info in target_files: | ||
217 | target_id = file_info['id'] | ||
218 | target_file_name = file_info['file_name'] | ||
219 | target_file_size = file_info['file_size'] | ||
220 | if (not target_file_name or not target_file_size): | ||
221 | cnt_err.append(target_id) | ||
222 | else: | ||
223 | if (not os.path.exists(target_file_name)): | ||
224 | cnt_err.append(target_id) | ||
225 | else: | ||
226 | if (os.path.getsize(target_file_name) != target_file_size): | ||
227 | cnt_err.append(target_id) | ||
228 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for target image file id: %s' % cnt_err) | ||
229 | |||
230 | # Key verification - tc=884 | ||
231 | @testcase(884) | ||
232 | def test_Package_Dependency(self): | ||
233 | cnt_err = [] | ||
234 | deps = Package_Dependency.objects.values('id', 'package_id', 'depends_on_id') | ||
235 | for dep in deps: | ||
236 | if (dep['package_id'] == dep['depends_on_id']): | ||
237 | cnt_err.append(dep['id']) | ||
238 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for package dependency id: %s' % cnt_err) | ||
239 | |||
240 | # Check if recipe name does not start with a number (0-9) - tc=838 | ||
241 | @testcase(838) | ||
242 | def test_Recipe_Name(self): | ||
243 | recipes = Recipe.objects.values('id', 'name') | ||
244 | cnt_err = [] | ||
245 | for recipe in recipes: | ||
246 | if (recipe['name'][0].isdigit() is True): | ||
247 | cnt_err.append(recipe['id']) | ||
248 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err) | ||
249 | |||
250 | # Check if recipe section matches the content of the SECTION variable (if set) in file_path - tc=839 | ||
251 | @testcase(839) | ||
252 | def test_Recipe_DB_Section_Match_Recipe_File_Section(self): | ||
253 | recipes = Recipe.objects.values('id', 'section', 'file_path') | ||
254 | cnt_err = [] | ||
255 | for recipe in recipes: | ||
256 | file_path = self.fix_file_path(recipe['file_path']) | ||
257 | file_exists = os.path.isfile(file_path) | ||
258 | if (not file_path or (file_exists is False)): | ||
259 | cnt_err.append(recipe['id']) | ||
260 | else: | ||
261 | file_section = self.recipe_parse(file_path, "SECTION = ") | ||
262 | db_section = recipe['section'] | ||
263 | if file_section: | ||
264 | if (db_section != file_section): | ||
265 | cnt_err.append(recipe['id']) | ||
266 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err) | ||
267 | |||
268 | # Check if recipe license matches the content of the LICENSE variable (if set) in file_path - tc=840 | ||
269 | @testcase(840) | ||
270 | def test_Recipe_DB_License_Match_Recipe_File_License(self): | ||
271 | recipes = Recipe.objects.values('id', 'license', 'file_path') | ||
272 | cnt_err = [] | ||
273 | for recipe in recipes: | ||
274 | file_path = self.fix_file_path(recipe['file_path']) | ||
275 | file_exists = os.path.isfile(file_path) | ||
276 | if (not file_path or (file_exists is False)): | ||
277 | cnt_err.append(recipe['id']) | ||
278 | else: | ||
279 | file_license = self.recipe_parse(file_path, "LICENSE = ") | ||
280 | db_license = recipe['license'] | ||
281 | if file_license: | ||
282 | if (db_license != file_license): | ||
283 | cnt_err.append(recipe['id']) | ||
284 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err) | ||
285 | |||
286 | # Check if recipe homepage matches the content of the HOMEPAGE variable (if set) in file_path - tc=841 | ||
287 | @testcase(841) | ||
288 | def test_Recipe_DB_Homepage_Match_Recipe_File_Homepage(self): | ||
289 | recipes = Recipe.objects.values('id', 'homepage', 'file_path') | ||
290 | cnt_err = [] | ||
291 | for recipe in recipes: | ||
292 | file_path = self.fix_file_path(recipe['file_path']) | ||
293 | file_exists = os.path.isfile(file_path) | ||
294 | if (not file_path or (file_exists is False)): | ||
295 | cnt_err.append(recipe['id']) | ||
296 | else: | ||
297 | file_homepage = self.recipe_parse(file_path, "HOMEPAGE = ") | ||
298 | db_homepage = recipe['homepage'] | ||
299 | if file_homepage: | ||
300 | if (db_homepage != file_homepage): | ||
301 | cnt_err.append(recipe['id']) | ||
302 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err) | ||
303 | |||
304 | # Check if recipe bugtracker matches the content of the BUGTRACKER variable (if set) in file_path - tc=842 | ||
305 | @testcase(842) | ||
306 | def test_Recipe_DB_Bugtracker_Match_Recipe_File_Bugtracker(self): | ||
307 | recipes = Recipe.objects.values('id', 'bugtracker', 'file_path') | ||
308 | cnt_err = [] | ||
309 | for recipe in recipes: | ||
310 | file_path = self.fix_file_path(recipe['file_path']) | ||
311 | file_exists = os.path.isfile(file_path) | ||
312 | if (not file_path or (file_exists is False)): | ||
313 | cnt_err.append(recipe['id']) | ||
314 | else: | ||
315 | file_bugtracker = self.recipe_parse(file_path, "BUGTRACKER = ") | ||
316 | db_bugtracker = recipe['bugtracker'] | ||
317 | if file_bugtracker: | ||
318 | if (db_bugtracker != file_bugtracker): | ||
319 | cnt_err.append(recipe['id']) | ||
320 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err) | ||
321 | |||
322 | # Recipe key verification, recipe name does not depends on a recipe having the same name - tc=883 | ||
323 | @testcase(883) | ||
324 | def test_Recipe_Dependency(self): | ||
325 | deps = Recipe_Dependency.objects.values('id', 'recipe_id', 'depends_on_id') | ||
326 | cnt_err = [] | ||
327 | for dep in deps: | ||
328 | if (not dep['recipe_id'] or not dep['depends_on_id']): | ||
329 | cnt_err.append(dep['id']) | ||
330 | else: | ||
331 | name = Recipe.objects.filter(id = dep['recipe_id']).values('name') | ||
332 | dep_name = Recipe.objects.filter(id = dep['depends_on_id']).values('name') | ||
333 | if (name == dep_name): | ||
334 | cnt_err.append(dep['id']) | ||
335 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe dependency id: %s' % cnt_err) | ||
336 | |||
337 | # Check if package name does not start with a number (0-9) - tc=846 | ||
338 | @testcase(846) | ||
339 | def test_Package_Name_For_Number(self): | ||
340 | packages = Package.objects.filter(~Q(size = -1)).values('id', 'name') | ||
341 | cnt_err = [] | ||
342 | for package in packages: | ||
343 | if (package['name'][0].isdigit() is True): | ||
344 | cnt_err.append(package['id']) | ||
345 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err) | ||
346 | |||
347 | # Check if package version starts with a number (0-9) - tc=847 | ||
348 | @testcase(847) | ||
349 | def test_Package_Version_Starts_With_Number(self): | ||
350 | packages = Package.objects.filter(~Q(size = -1)).values('id', 'version') | ||
351 | cnt_err = [] | ||
352 | for package in packages: | ||
353 | if (package['version'][0].isdigit() is False): | ||
354 | cnt_err.append(package['id']) | ||
355 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err) | ||
356 | |||
357 | # Check if package revision starts with 'r' - tc=848 | ||
358 | @testcase(848) | ||
359 | def test_Package_Revision_Starts_With_r(self): | ||
360 | packages = Package.objects.filter(~Q(size = -1)).values('id', 'revision') | ||
361 | cnt_err = [] | ||
362 | for package in packages: | ||
363 | if (package['revision'][0].startswith("r") is False): | ||
364 | cnt_err.append(package['id']) | ||
365 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err) | ||
366 | |||
367 | # Check the validity of the package build_id | ||
368 | ### TC must be added in test run | ||
369 | @testcase(1038) | ||
370 | def test_Package_Build_Id(self): | ||
371 | packages = Package.objects.filter(~Q(size = -1)).values('id', 'build_id') | ||
372 | cnt_err = [] | ||
373 | for package in packages: | ||
374 | build_id = Build.objects.filter(id = package['build_id']).values('id') | ||
375 | if (not build_id): | ||
376 | cnt_err.append(package['id']) | ||
377 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err) | ||
378 | |||
379 | # Check the validity of package recipe_id | ||
380 | ### TC must be added in test run | ||
381 | @testcase(1039) | ||
382 | def test_Package_Recipe_Id(self): | ||
383 | packages = Package.objects.filter(~Q(size = -1)).values('id', 'recipe_id') | ||
384 | cnt_err = [] | ||
385 | for package in packages: | ||
386 | recipe_id = Recipe.objects.filter(id = package['recipe_id']).values('id') | ||
387 | if (not recipe_id): | ||
388 | cnt_err.append(package['id']) | ||
389 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err) | ||
390 | |||
391 | # Check if package installed_size field is not null | ||
392 | ### TC must be aded in test run | ||
393 | @testcase(1040) | ||
394 | def test_Package_Installed_Size_Not_NULL(self): | ||
395 | packages = Package.objects.filter(installed_size__isnull = True).values('id') | ||
396 | cnt_err = [] | ||
397 | for package in packages: | ||
398 | cnt_err.append(package['id']) | ||
399 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err) | ||
400 | |||
401 | # Check if all layers requests return exit code is 200 - tc=843 | ||
402 | @testcase(843) | ||
403 | def test_Layers_Requests_Exit_Code(self): | ||
404 | layers = Layer.objects.values('id', 'layer_index_url') | ||
405 | cnt_err = [] | ||
406 | for layer in layers: | ||
407 | resp = urllib.urlopen(layer['layer_index_url']) | ||
408 | if (resp.getcode() != 200): | ||
409 | cnt_err.append(layer['id']) | ||
410 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for layer id: %s' % cnt_err) | ||
411 | |||
412 | # Check if the output of bitbake-layers show_layers matches the info from database - tc=895 | ||
413 | @testcase(895) | ||
414 | def test_Layers_Show_Layers(self): | ||
415 | layers = Layer.objects.values('id', 'name', 'local_path') | ||
416 | cmd = commands.getoutput('bitbake-layers show_layers') | ||
417 | cnt_err = [] | ||
418 | for layer in layers: | ||
419 | if (layer['name'] or layer['local_path']) not in cmd: | ||
420 | cnt_err.append(layer['id']) | ||
421 | self.assertEqual(len(cnt_err), 0, msg = 'Errors for layer id: %s' % cnt_err) | ||
422 | |||
423 | # Check if django server starts regardless of the timezone set on the machine - tc=905 | ||
424 | @testcase(905) | ||
425 | def test_Start_Django_Timezone(self): | ||
426 | current_path = os.getcwd() | ||
427 | zonefilelist = [] | ||
428 | ZONEINFOPATH = '/usr/share/zoneinfo/' | ||
429 | os.chdir("../bitbake/lib/toaster/") | ||
430 | cnt_err = 0 | ||
431 | for filename in os.listdir(ZONEINFOPATH): | ||
432 | if os.path.isfile(os.path.join(ZONEINFOPATH, filename)): | ||
433 | zonefilelist.append(filename) | ||
434 | for k in range(len(zonefilelist)): | ||
435 | if k <= 5: | ||
436 | files = zonefilelist[k] | ||
437 | os.system("export TZ="+str(files)+"; python manage.py runserver > /dev/null 2>&1 &") | ||
438 | time.sleep(3) | ||
439 | pid = subprocess.check_output("ps aux | grep '[/u]sr/bin/python manage.py runserver' | awk '{print $2}'", shell = True) | ||
440 | if pid: | ||
441 | os.system("kill -9 "+str(pid)) | ||
442 | else: | ||
443 | cnt_err.append(zonefilelist[k]) | ||
444 | self.assertEqual(cnt_err, 0, msg = 'Errors django server does not start with timezone: %s' % cnt_err) | ||
445 | os.chdir(current_path) | ||
diff --git a/meta/lib/oeqa/selftest/base.py b/meta/lib/oeqa/selftest/base.py new file mode 100644 index 0000000000..80b9b4b312 --- /dev/null +++ b/meta/lib/oeqa/selftest/base.py | |||
@@ -0,0 +1,131 @@ | |||
1 | # Copyright (c) 2013 Intel Corporation | ||
2 | # | ||
3 | # Released under the MIT license (see COPYING.MIT) | ||
4 | |||
5 | |||
6 | # DESCRIPTION | ||
7 | # Base class inherited by test classes in meta/lib/selftest | ||
8 | |||
9 | import unittest | ||
10 | import os | ||
11 | import sys | ||
12 | import shutil | ||
13 | import logging | ||
14 | import errno | ||
15 | |||
16 | import oeqa.utils.ftools as ftools | ||
17 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer | ||
18 | from oeqa.utils.decorators import LogResults | ||
19 | |||
20 | @LogResults | ||
21 | class oeSelfTest(unittest.TestCase): | ||
22 | |||
23 | log = logging.getLogger("selftest.base") | ||
24 | longMessage = True | ||
25 | |||
26 | def __init__(self, methodName="runTest"): | ||
27 | self.builddir = os.environ.get("BUILDDIR") | ||
28 | self.localconf_path = os.path.join(self.builddir, "conf/local.conf") | ||
29 | self.testinc_path = os.path.join(self.builddir, "conf/selftest.inc") | ||
30 | self.testlayer_path = oeSelfTest.testlayer_path | ||
31 | self._extra_tear_down_commands = [] | ||
32 | self._track_for_cleanup = [] | ||
33 | super(oeSelfTest, self).__init__(methodName) | ||
34 | |||
35 | def setUp(self): | ||
36 | os.chdir(self.builddir) | ||
37 | # we don't know what the previous test left around in config or inc files | ||
38 | # if it failed so we need a fresh start | ||
39 | try: | ||
40 | os.remove(self.testinc_path) | ||
41 | except OSError as e: | ||
42 | if e.errno != errno.ENOENT: | ||
43 | raise | ||
44 | for root, _, files in os.walk(self.testlayer_path): | ||
45 | for f in files: | ||
46 | if f == 'test_recipe.inc': | ||
47 | os.remove(os.path.join(root, f)) | ||
48 | # tests might need their own setup | ||
49 | # but if they overwrite this one they have to call | ||
50 | # super each time, so let's give them an alternative | ||
51 | self.setUpLocal() | ||
52 | |||
53 | def setUpLocal(self): | ||
54 | pass | ||
55 | |||
56 | def tearDown(self): | ||
57 | if self._extra_tear_down_commands: | ||
58 | failed_extra_commands = [] | ||
59 | for command in self._extra_tear_down_commands: | ||
60 | result = runCmd(command, ignore_status=True) | ||
61 | if not result.status == 0: | ||
62 | failed_extra_commands.append(command) | ||
63 | if failed_extra_commands: | ||
64 | self.log.warning("tearDown commands have failed: %s" % ', '.join(map(str, failed_extra_commands))) | ||
65 | self.log.debug("Trying to move on.") | ||
66 | self._extra_tear_down_commands = [] | ||
67 | |||
68 | if self._track_for_cleanup: | ||
69 | for path in self._track_for_cleanup: | ||
70 | if os.path.isdir(path): | ||
71 | shutil.rmtree(path) | ||
72 | if os.path.isfile(path): | ||
73 | os.remove(path) | ||
74 | self._track_for_cleanup = [] | ||
75 | |||
76 | self.tearDownLocal() | ||
77 | |||
78 | def tearDownLocal(self): | ||
79 | pass | ||
80 | |||
81 | # add test specific commands to the tearDown method. | ||
82 | def add_command_to_tearDown(self, command): | ||
83 | self.log.debug("Adding command '%s' to tearDown for this test." % command) | ||
84 | self._extra_tear_down_commands.append(command) | ||
85 | # add test specific files or directories to be removed in the tearDown method | ||
86 | def track_for_cleanup(self, path): | ||
87 | self.log.debug("Adding path '%s' to be cleaned up when test is over" % path) | ||
88 | self._track_for_cleanup.append(path) | ||
89 | |||
90 | # write to <builddir>/conf/selftest.inc | ||
91 | def write_config(self, data): | ||
92 | self.log.debug("Writing to: %s\n%s\n" % (self.testinc_path, data)) | ||
93 | ftools.write_file(self.testinc_path, data) | ||
94 | |||
95 | # append to <builddir>/conf/selftest.inc | ||
96 | def append_config(self, data): | ||
97 | self.log.debug("Appending to: %s\n%s\n" % (self.testinc_path, data)) | ||
98 | ftools.append_file(self.testinc_path, data) | ||
99 | |||
100 | # remove data from <builddir>/conf/selftest.inc | ||
101 | def remove_config(self, data): | ||
102 | self.log.debug("Removing from: %s\n\%s\n" % (self.testinc_path, data)) | ||
103 | ftools.remove_from_file(self.testinc_path, data) | ||
104 | |||
105 | # write to meta-sefltest/recipes-test/<recipe>/test_recipe.inc | ||
106 | def write_recipeinc(self, recipe, data): | ||
107 | inc_file = os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc') | ||
108 | self.log.debug("Writing to: %s\n%s\n" % (inc_file, data)) | ||
109 | ftools.write_file(inc_file, data) | ||
110 | |||
111 | # append data to meta-sefltest/recipes-test/<recipe>/test_recipe.inc | ||
112 | def append_recipeinc(self, recipe, data): | ||
113 | inc_file = os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc') | ||
114 | self.log.debug("Appending to: %s\n%s\n" % (inc_file, data)) | ||
115 | ftools.append_file(inc_file, data) | ||
116 | |||
117 | # remove data from meta-sefltest/recipes-test/<recipe>/test_recipe.inc | ||
118 | def remove_recipeinc(self, recipe, data): | ||
119 | inc_file = os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc') | ||
120 | self.log.debug("Removing from: %s\n%s\n" % (inc_file, data)) | ||
121 | ftools.remove_from_file(inc_file, data) | ||
122 | |||
123 | # delete meta-sefltest/recipes-test/<recipe>/test_recipe.inc file | ||
124 | def delete_recipeinc(self, recipe): | ||
125 | inc_file = os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc') | ||
126 | self.log.debug("Deleting file: %s" % inc_file) | ||
127 | try: | ||
128 | os.remove(inc_file) | ||
129 | except OSError as e: | ||
130 | if e.errno != errno.ENOENT: | ||
131 | raise | ||
diff --git a/meta/lib/oeqa/selftest/bblayers.py b/meta/lib/oeqa/selftest/bblayers.py new file mode 100644 index 0000000000..1ead8e8671 --- /dev/null +++ b/meta/lib/oeqa/selftest/bblayers.py | |||
@@ -0,0 +1,43 @@ | |||
1 | import unittest | ||
2 | import os | ||
3 | import logging | ||
4 | import re | ||
5 | import shutil | ||
6 | |||
7 | import oeqa.utils.ftools as ftools | ||
8 | from oeqa.selftest.base import oeSelfTest | ||
9 | from oeqa.utils.commands import runCmd | ||
10 | from oeqa.utils.decorators import testcase | ||
11 | |||
12 | class BitbakeLayers(oeSelfTest): | ||
13 | |||
14 | @testcase(756) | ||
15 | def test_bitbakelayers_showcrossdepends(self): | ||
16 | result = runCmd('bitbake-layers show-cross-depends') | ||
17 | self.assertTrue('aspell' in result.output) | ||
18 | |||
19 | @testcase(83) | ||
20 | def test_bitbakelayers_showlayers(self): | ||
21 | result = runCmd('bitbake-layers show_layers') | ||
22 | self.assertTrue('meta-selftest' in result.output) | ||
23 | |||
24 | @testcase(93) | ||
25 | def test_bitbakelayers_showappends(self): | ||
26 | result = runCmd('bitbake-layers show_appends') | ||
27 | self.assertTrue('xcursor-transparent-theme_0.1.1.bbappend' in result.output, msg='xcursor-transparent-theme_0.1.1.bbappend file was not recognised') | ||
28 | |||
29 | @testcase(90) | ||
30 | def test_bitbakelayers_showoverlayed(self): | ||
31 | result = runCmd('bitbake-layers show_overlayed') | ||
32 | self.assertTrue('aspell' in result.output, msg='xcursor-transparent-theme_0.1.1.bbappend file was not recognised') | ||
33 | |||
34 | @testcase(95) | ||
35 | def test_bitbakelayers_flatten(self): | ||
36 | self.assertFalse(os.path.isdir(os.path.join(self.builddir, 'test'))) | ||
37 | result = runCmd('bitbake-layers flatten test') | ||
38 | bb_file = os.path.join(self.builddir, 'test/recipes-graphics/xcursor-transparent-theme/xcursor-transparent-theme_0.1.1.bb') | ||
39 | self.assertTrue(os.path.isfile(bb_file)) | ||
40 | contents = ftools.read_file(bb_file) | ||
41 | find_in_contents = re.search("##### bbappended from meta-selftest #####\n(.*\n)*include test_recipe.inc", contents) | ||
42 | shutil.rmtree(os.path.join(self.builddir, 'test')) | ||
43 | self.assertTrue(find_in_contents) | ||
diff --git a/meta/lib/oeqa/selftest/bbtests.py b/meta/lib/oeqa/selftest/bbtests.py new file mode 100644 index 0000000000..68f97bd8e3 --- /dev/null +++ b/meta/lib/oeqa/selftest/bbtests.py | |||
@@ -0,0 +1,178 @@ | |||
1 | import unittest | ||
2 | import os | ||
3 | import logging | ||
4 | import re | ||
5 | import shutil | ||
6 | |||
7 | import oeqa.utils.ftools as ftools | ||
8 | from oeqa.selftest.base import oeSelfTest | ||
9 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var | ||
10 | from oeqa.utils.decorators import testcase | ||
11 | |||
12 | class BitbakeTests(oeSelfTest): | ||
13 | |||
14 | @testcase(789) | ||
15 | def test_run_bitbake_from_dir_1(self): | ||
16 | os.chdir(os.path.join(self.builddir, 'conf')) | ||
17 | bitbake('-e') | ||
18 | |||
19 | @testcase(790) | ||
20 | def test_run_bitbake_from_dir_2(self): | ||
21 | my_env = os.environ.copy() | ||
22 | my_env['BBPATH'] = my_env['BUILDDIR'] | ||
23 | os.chdir(os.path.dirname(os.environ['BUILDDIR'])) | ||
24 | bitbake('-e', env=my_env) | ||
25 | |||
26 | @testcase(806) | ||
27 | def test_event_handler(self): | ||
28 | self.write_config("INHERIT += \"test_events\"") | ||
29 | result = bitbake('m4-native') | ||
30 | find_build_started = re.search("NOTE: Test for bb\.event\.BuildStarted(\n.*)*NOTE: Preparing runqueue", result.output) | ||
31 | find_build_completed = re.search("Tasks Summary:.*(\n.*)*NOTE: Test for bb\.event\.BuildCompleted", result.output) | ||
32 | self.assertTrue(find_build_started, msg = "Match failed in:\n%s" % result.output) | ||
33 | self.assertTrue(find_build_completed, msg = "Match failed in:\n%s" % result.output) | ||
34 | self.assertFalse('Test for bb.event.InvalidEvent' in result.output) | ||
35 | |||
36 | @testcase(103) | ||
37 | def test_local_sstate(self): | ||
38 | bitbake('m4-native -ccleansstate') | ||
39 | bitbake('m4-native') | ||
40 | bitbake('m4-native -cclean') | ||
41 | result = bitbake('m4-native') | ||
42 | find_setscene = re.search("m4-native.*do_.*_setscene", result.output) | ||
43 | self.assertTrue(find_setscene) | ||
44 | |||
45 | @testcase(105) | ||
46 | def test_bitbake_invalid_recipe(self): | ||
47 | result = bitbake('-b asdf', ignore_status=True) | ||
48 | self.assertTrue("ERROR: Unable to find any recipe file matching 'asdf'" in result.output) | ||
49 | |||
50 | @testcase(107) | ||
51 | def test_bitbake_invalid_target(self): | ||
52 | result = bitbake('asdf', ignore_status=True) | ||
53 | self.assertTrue("ERROR: Nothing PROVIDES 'asdf'" in result.output) | ||
54 | |||
55 | @testcase(106) | ||
56 | def test_warnings_errors(self): | ||
57 | result = bitbake('-b asdf', ignore_status=True) | ||
58 | find_warnings = re.search("Summary: There w.{2,3}? [1-9][0-9]* WARNING messages* shown", result.output) | ||
59 | find_errors = re.search("Summary: There w.{2,3}? [1-9][0-9]* ERROR messages* shown", result.output) | ||
60 | self.assertTrue(find_warnings, msg="Did not find the mumber of warnings at the end of the build:\n" + result.output) | ||
61 | self.assertTrue(find_errors, msg="Did not find the mumber of errors at the end of the build:\n" + result.output) | ||
62 | |||
63 | @testcase(108) | ||
64 | def test_invalid_patch(self): | ||
65 | self.write_recipeinc('man', 'SRC_URI += "file://man-1.5h1-make.patch"') | ||
66 | result = bitbake('man -c patch', ignore_status=True) | ||
67 | self.delete_recipeinc('man') | ||
68 | bitbake('-cclean man') | ||
69 | self.assertTrue("ERROR: Function failed: patch_do_patch" in result.output) | ||
70 | |||
71 | @testcase(163) | ||
72 | def test_force_task(self): | ||
73 | bitbake('m4-native') | ||
74 | result = bitbake('-C compile m4-native') | ||
75 | look_for_tasks = ['do_compile', 'do_install', 'do_populate_sysroot'] | ||
76 | for task in look_for_tasks: | ||
77 | find_task = re.search("m4-native.*%s" % task, result.output) | ||
78 | self.assertTrue(find_task) | ||
79 | |||
80 | @testcase(167) | ||
81 | def test_bitbake_g(self): | ||
82 | result = bitbake('-g core-image-full-cmdline') | ||
83 | self.assertTrue('NOTE: PN build list saved to \'pn-buildlist\'' in result.output) | ||
84 | self.assertTrue('openssh' in ftools.read_file(os.path.join(self.builddir, 'pn-buildlist'))) | ||
85 | for f in ['pn-buildlist', 'pn-depends.dot', 'package-depends.dot', 'task-depends.dot']: | ||
86 | os.remove(f) | ||
87 | |||
88 | @testcase(899) | ||
89 | def test_image_manifest(self): | ||
90 | bitbake('core-image-minimal') | ||
91 | deploydir = get_bb_var("DEPLOY_DIR_IMAGE", target="core-image-minimal") | ||
92 | imagename = get_bb_var("IMAGE_LINK_NAME", target="core-image-minimal") | ||
93 | manifest = os.path.join(deploydir, imagename + ".manifest") | ||
94 | self.assertTrue(os.path.islink(manifest), msg="No manifest file created for image") | ||
95 | |||
96 | @testcase(168) | ||
97 | def test_invalid_recipe_src_uri(self): | ||
98 | data = 'SRC_URI = "file://invalid"' | ||
99 | self.write_recipeinc('man', data) | ||
100 | bitbake('-ccleanall man') | ||
101 | result = bitbake('-c fetch man', ignore_status=True) | ||
102 | bitbake('-ccleanall man') | ||
103 | self.delete_recipeinc('man') | ||
104 | self.assertEqual(result.status, 1, msg='Command succeded when it should have failed') | ||
105 | self.assertTrue('Fetcher failure: Unable to find file file://invalid anywhere. The paths that were searched were:' in result.output) | ||
106 | self.assertTrue('ERROR: Function failed: Fetcher failure for URL: \'file://invalid\'. Unable to fetch URL from any source.' in result.output) | ||
107 | |||
108 | @testcase(171) | ||
109 | def test_rename_downloaded_file(self): | ||
110 | data = 'SRC_URI_append = ";downloadfilename=test-aspell.tar.gz"' | ||
111 | self.write_recipeinc('aspell', data) | ||
112 | bitbake('-ccleanall aspell') | ||
113 | result = bitbake('-c fetch aspell', ignore_status=True) | ||
114 | self.delete_recipeinc('aspell') | ||
115 | self.assertEqual(result.status, 0) | ||
116 | self.assertTrue(os.path.isfile(os.path.join(get_bb_var("DL_DIR"), 'test-aspell.tar.gz'))) | ||
117 | self.assertTrue(os.path.isfile(os.path.join(get_bb_var("DL_DIR"), 'test-aspell.tar.gz.done'))) | ||
118 | bitbake('-ccleanall aspell') | ||
119 | |||
120 | @testcase(1028) | ||
121 | def test_environment(self): | ||
122 | self.append_config("TEST_ENV=\"localconf\"") | ||
123 | result = runCmd('bitbake -e | grep TEST_ENV=') | ||
124 | self.assertTrue('localconf' in result.output) | ||
125 | self.remove_config("TEST_ENV=\"localconf\"") | ||
126 | |||
127 | @testcase(1029) | ||
128 | def test_dry_run(self): | ||
129 | result = runCmd('bitbake -n m4-native') | ||
130 | self.assertEqual(0, result.status) | ||
131 | |||
132 | @testcase(1030) | ||
133 | def test_just_parse(self): | ||
134 | result = runCmd('bitbake -p') | ||
135 | self.assertEqual(0, result.status) | ||
136 | |||
137 | @testcase(1031) | ||
138 | def test_version(self): | ||
139 | result = runCmd('bitbake -s | grep wget') | ||
140 | find = re.search("wget *:([0-9a-zA-Z\.\-]+)", result.output) | ||
141 | self.assertTrue(find) | ||
142 | |||
143 | @testcase(1032) | ||
144 | def test_prefile(self): | ||
145 | preconf = os.path.join(self.builddir, 'conf/prefile.conf') | ||
146 | self.track_for_cleanup(preconf) | ||
147 | ftools.write_file(preconf ,"TEST_PREFILE=\"prefile\"") | ||
148 | result = runCmd('bitbake -r conf/prefile.conf -e | grep TEST_PREFILE=') | ||
149 | self.assertTrue('prefile' in result.output) | ||
150 | self.append_config("TEST_PREFILE=\"localconf\"") | ||
151 | result = runCmd('bitbake -r conf/prefile.conf -e | grep TEST_PREFILE=') | ||
152 | self.assertTrue('localconf' in result.output) | ||
153 | self.remove_config("TEST_PREFILE=\"localconf\"") | ||
154 | |||
155 | @testcase(1033) | ||
156 | def test_postfile(self): | ||
157 | postconf = os.path.join(self.builddir, 'conf/postfile.conf') | ||
158 | self.track_for_cleanup(postconf) | ||
159 | ftools.write_file(postconf , "TEST_POSTFILE=\"postfile\"") | ||
160 | self.append_config("TEST_POSTFILE=\"localconf\"") | ||
161 | result = runCmd('bitbake -R conf/postfile.conf -e | grep TEST_POSTFILE=') | ||
162 | self.assertTrue('postfile' in result.output) | ||
163 | self.remove_config("TEST_POSTFILE=\"localconf\"") | ||
164 | |||
165 | @testcase(1034) | ||
166 | def test_checkuri(self): | ||
167 | result = runCmd('bitbake -c checkuri m4') | ||
168 | self.assertEqual(0, result.status) | ||
169 | |||
170 | @testcase(1035) | ||
171 | def test_continue(self): | ||
172 | self.write_recipeinc('man',"\ndo_fail_task () {\nexit 1 \n}\n\naddtask do_fail_task before do_fetch\n" ) | ||
173 | runCmd('bitbake -c cleanall man xcursor-transparent-theme') | ||
174 | result = runCmd('bitbake man xcursor-transparent-theme -k', ignore_status=True) | ||
175 | errorpos = result.output.find('ERROR: Function failed: do_fail_task') | ||
176 | manver = re.search("NOTE: recipe xcursor-transparent-theme-(.*?): task do_unpack: Started", result.output) | ||
177 | continuepos = result.output.find('NOTE: recipe xcursor-transparent-theme-%s: task do_unpack: Started' % manver.group(1)) | ||
178 | self.assertLess(errorpos,continuepos) | ||
diff --git a/meta/lib/oeqa/selftest/buildhistory.py b/meta/lib/oeqa/selftest/buildhistory.py new file mode 100644 index 0000000000..d8cae4664b --- /dev/null +++ b/meta/lib/oeqa/selftest/buildhistory.py | |||
@@ -0,0 +1,45 @@ | |||
1 | import unittest | ||
2 | import os | ||
3 | import re | ||
4 | import shutil | ||
5 | import datetime | ||
6 | |||
7 | import oeqa.utils.ftools as ftools | ||
8 | from oeqa.selftest.base import oeSelfTest | ||
9 | from oeqa.utils.commands import Command, runCmd, bitbake, get_bb_var, get_test_layer | ||
10 | |||
11 | |||
12 | class BuildhistoryBase(oeSelfTest): | ||
13 | |||
14 | def config_buildhistory(self, tmp_bh_location=False): | ||
15 | if (not 'buildhistory' in get_bb_var('USER_CLASSES')) and (not 'buildhistory' in get_bb_var('INHERIT')): | ||
16 | add_buildhistory_config = 'INHERIT += "buildhistory"\nBUILDHISTORY_COMMIT = "1"' | ||
17 | self.append_config(add_buildhistory_config) | ||
18 | |||
19 | if tmp_bh_location: | ||
20 | # Using a temporary buildhistory location for testing | ||
21 | tmp_bh_dir = os.path.join(self.builddir, "tmp_buildhistory_%s" % datetime.datetime.now().strftime('%Y%m%d%H%M%S')) | ||
22 | buildhistory_dir_config = "BUILDHISTORY_DIR = \"%s\"" % tmp_bh_dir | ||
23 | self.append_config(buildhistory_dir_config) | ||
24 | self.track_for_cleanup(tmp_bh_dir) | ||
25 | |||
26 | def run_buildhistory_operation(self, target, global_config='', target_config='', change_bh_location=False, expect_error=False, error_regex=''): | ||
27 | if change_bh_location: | ||
28 | tmp_bh_location = True | ||
29 | else: | ||
30 | tmp_bh_location = False | ||
31 | self.config_buildhistory(tmp_bh_location) | ||
32 | |||
33 | self.append_config(global_config) | ||
34 | self.append_recipeinc(target, target_config) | ||
35 | bitbake("-cclean %s" % target) | ||
36 | result = bitbake(target, ignore_status=True) | ||
37 | self.remove_config(global_config) | ||
38 | self.remove_recipeinc(target, target_config) | ||
39 | |||
40 | if expect_error: | ||
41 | self.assertEqual(result.status, 1, msg="Error expected for global config '%s' and target config '%s'" % (global_config, target_config)) | ||
42 | search_for_error = re.search(error_regex, result.output) | ||
43 | self.assertTrue(search_for_error, msg="Could not find desired error in output: %s" % error_regex) | ||
44 | else: | ||
45 | self.assertEqual(result.status, 0, msg="Command 'bitbake %s' has failed unexpectedly: %s" % (target, result.output)) | ||
diff --git a/meta/lib/oeqa/selftest/buildoptions.py b/meta/lib/oeqa/selftest/buildoptions.py new file mode 100644 index 0000000000..a250cae0e1 --- /dev/null +++ b/meta/lib/oeqa/selftest/buildoptions.py | |||
@@ -0,0 +1,120 @@ | |||
1 | import unittest | ||
2 | import os | ||
3 | import logging | ||
4 | import re | ||
5 | |||
6 | from oeqa.selftest.base import oeSelfTest | ||
7 | from oeqa.selftest.buildhistory import BuildhistoryBase | ||
8 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var | ||
9 | import oeqa.utils.ftools as ftools | ||
10 | from oeqa.utils.decorators import testcase | ||
11 | |||
12 | class ImageOptionsTests(oeSelfTest): | ||
13 | |||
14 | @testcase(761) | ||
15 | def test_incremental_image_generation(self): | ||
16 | bitbake("-c cleanall core-image-minimal") | ||
17 | self.write_config('INC_RPM_IMAGE_GEN = "1"') | ||
18 | self.append_config('IMAGE_FEATURES += "ssh-server-openssh"') | ||
19 | bitbake("core-image-minimal") | ||
20 | res = runCmd("grep 'Installing openssh-sshd' %s" % (os.path.join(get_bb_var("WORKDIR", "core-image-minimal"), "temp/log.do_rootfs")), ignore_status=True) | ||
21 | self.remove_config('IMAGE_FEATURES += "ssh-server-openssh"') | ||
22 | self.assertEqual(0, res.status, msg="No match for openssh-sshd in log.do_rootfs") | ||
23 | bitbake("core-image-minimal") | ||
24 | res = runCmd("grep 'Removing openssh-sshd' %s" %(os.path.join(get_bb_var("WORKDIR", "core-image-minimal"), "temp/log.do_rootfs")),ignore_status=True) | ||
25 | self.assertEqual(0, res.status, msg="openssh-sshd was not removed from image") | ||
26 | |||
27 | @testcase(925) | ||
28 | def test_rm_old_image(self): | ||
29 | bitbake("core-image-minimal") | ||
30 | deploydir = get_bb_var("DEPLOY_DIR_IMAGE", target="core-image-minimal") | ||
31 | imagename = get_bb_var("IMAGE_LINK_NAME", target="core-image-minimal") | ||
32 | deploydir_files = os.listdir(deploydir) | ||
33 | track_original_files = [] | ||
34 | for image_file in deploydir_files: | ||
35 | if imagename in image_file and os.path.islink(os.path.join(deploydir, image_file)): | ||
36 | track_original_files.append(os.path.realpath(os.path.join(deploydir, image_file))) | ||
37 | self.append_config("RM_OLD_IMAGE = \"1\"") | ||
38 | bitbake("-C rootfs core-image-minimal") | ||
39 | deploydir_files = os.listdir(deploydir) | ||
40 | remaining_not_expected = [path for path in track_original_files if os.path.basename(path) in deploydir_files] | ||
41 | self.assertFalse(remaining_not_expected, msg="\nThe following image files ware not removed: %s" % ', '.join(map(str, remaining_not_expected))) | ||
42 | |||
43 | @testcase(286) | ||
44 | def test_ccache_tool(self): | ||
45 | bitbake("ccache-native") | ||
46 | self.assertTrue(os.path.isfile(os.path.join(get_bb_var('STAGING_BINDIR_NATIVE', 'ccache-native'), "ccache"))) | ||
47 | self.write_config('INHERIT += "ccache"') | ||
48 | bitbake("m4 -c cleansstate") | ||
49 | bitbake("m4 -c compile") | ||
50 | res = runCmd("grep ccache %s" % (os.path.join(get_bb_var("WORKDIR","m4"),"temp/log.do_compile")), ignore_status=True) | ||
51 | self.assertEqual(0, res.status, msg="No match for ccache in m4 log.do_compile") | ||
52 | bitbake("ccache-native -ccleansstate") | ||
53 | |||
54 | |||
55 | class DiskMonTest(oeSelfTest): | ||
56 | |||
57 | @testcase(277) | ||
58 | def test_stoptask_behavior(self): | ||
59 | self.write_config('BB_DISKMON_DIRS = "STOPTASKS,${TMPDIR},100000G,100K"') | ||
60 | res = bitbake("m4", ignore_status = True) | ||
61 | self.assertTrue('ERROR: No new tasks can be executed since the disk space monitor action is "STOPTASKS"!' in res.output) | ||
62 | self.assertEqual(res.status, 1) | ||
63 | self.write_config('BB_DISKMON_DIRS = "ABORT,${TMPDIR},100000G,100K"') | ||
64 | res = bitbake("m4", ignore_status = True) | ||
65 | self.assertTrue('ERROR: Immediately abort since the disk space monitor action is "ABORT"!' in res.output) | ||
66 | self.assertEqual(res.status, 1) | ||
67 | self.write_config('BB_DISKMON_DIRS = "WARN,${TMPDIR},100000G,100K"') | ||
68 | res = bitbake("m4") | ||
69 | self.assertTrue('WARNING: The free space' in res.output) | ||
70 | |||
71 | class SanityOptionsTest(oeSelfTest): | ||
72 | |||
73 | @testcase(927) | ||
74 | def test_options_warnqa_errorqa_switch(self): | ||
75 | bitbake("xcursor-transparent-theme -ccleansstate") | ||
76 | |||
77 | if "packages-list" not in get_bb_var("ERROR_QA"): | ||
78 | self.write_config("ERROR_QA_append = \" packages-list\"") | ||
79 | |||
80 | self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"') | ||
81 | res = bitbake("xcursor-transparent-theme", ignore_status=True) | ||
82 | self.delete_recipeinc('xcursor-transparent-theme') | ||
83 | self.assertTrue("ERROR: QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors." in res.output, msg=res.output) | ||
84 | self.assertEqual(res.status, 1) | ||
85 | self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"') | ||
86 | self.append_config('ERROR_QA_remove = "packages-list"') | ||
87 | self.append_config('WARN_QA_append = " packages-list"') | ||
88 | bitbake("xcursor-transparent-theme -ccleansstate") | ||
89 | res = bitbake("xcursor-transparent-theme") | ||
90 | self.delete_recipeinc('xcursor-transparent-theme') | ||
91 | self.assertTrue("WARNING: QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors." in res.output, msg=res.output) | ||
92 | |||
93 | @testcase(278) | ||
94 | def test_sanity_userspace_dependency(self): | ||
95 | self.append_config('WARN_QA_append = " unsafe-references-in-binaries unsafe-references-in-scripts"') | ||
96 | bitbake("-ccleansstate gzip nfs-utils") | ||
97 | res = bitbake("gzip nfs-utils") | ||
98 | self.assertTrue("WARNING: QA Issue: gzip" in res.output) | ||
99 | self.assertTrue("WARNING: QA Issue: nfs-utils" in res.output) | ||
100 | |||
101 | class BuildhistoryTests(BuildhistoryBase): | ||
102 | |||
103 | @testcase(293) | ||
104 | def test_buildhistory_basic(self): | ||
105 | self.run_buildhistory_operation('xcursor-transparent-theme') | ||
106 | self.assertTrue(os.path.isdir(get_bb_var('BUILDHISTORY_DIR'))) | ||
107 | |||
108 | @testcase(294) | ||
109 | def test_buildhistory_buildtime_pr_backwards(self): | ||
110 | self.add_command_to_tearDown('cleanup-workdir') | ||
111 | target = 'xcursor-transparent-theme' | ||
112 | error = "ERROR: QA Issue: Package version for package %s went backwards which would break package feeds from (.*-r1 to .*-r0)" % target | ||
113 | self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True) | ||
114 | self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True, error_regex=error) | ||
115 | |||
116 | |||
117 | |||
118 | |||
119 | |||
120 | |||
diff --git a/meta/lib/oeqa/selftest/oescripts.py b/meta/lib/oeqa/selftest/oescripts.py new file mode 100644 index 0000000000..31cd50809c --- /dev/null +++ b/meta/lib/oeqa/selftest/oescripts.py | |||
@@ -0,0 +1,54 @@ | |||
1 | import datetime | ||
2 | import unittest | ||
3 | import os | ||
4 | import re | ||
5 | import shutil | ||
6 | |||
7 | import oeqa.utils.ftools as ftools | ||
8 | from oeqa.selftest.base import oeSelfTest | ||
9 | from oeqa.selftest.buildhistory import BuildhistoryBase | ||
10 | from oeqa.utils.commands import Command, runCmd, bitbake, get_bb_var, get_test_layer | ||
11 | from oeqa.utils.decorators import testcase | ||
12 | |||
13 | class TestScripts(oeSelfTest): | ||
14 | |||
15 | @testcase(300) | ||
16 | def test_cleanup_workdir(self): | ||
17 | path = os.path.dirname(get_bb_var('WORKDIR', 'gzip')) | ||
18 | old_version_recipe = os.path.join(get_bb_var('COREBASE'), 'meta/recipes-extended/gzip/gzip_1.3.12.bb') | ||
19 | old_version = '1.3.12' | ||
20 | bitbake("-ccleansstate gzip") | ||
21 | bitbake("-ccleansstate -b %s" % old_version_recipe) | ||
22 | if os.path.exists(get_bb_var('WORKDIR', "-b %s" % old_version_recipe)): | ||
23 | shutil.rmtree(get_bb_var('WORKDIR', "-b %s" % old_version_recipe)) | ||
24 | if os.path.exists(get_bb_var('WORKDIR', 'gzip')): | ||
25 | shutil.rmtree(get_bb_var('WORKDIR', 'gzip')) | ||
26 | |||
27 | if os.path.exists(path): | ||
28 | initial_contents = os.listdir(path) | ||
29 | else: | ||
30 | initial_contents = [] | ||
31 | |||
32 | bitbake('gzip') | ||
33 | intermediary_contents = os.listdir(path) | ||
34 | bitbake("-b %s" % old_version_recipe) | ||
35 | runCmd('cleanup-workdir') | ||
36 | remaining_contents = os.listdir(path) | ||
37 | |||
38 | expected_contents = [x for x in intermediary_contents if x not in initial_contents] | ||
39 | remaining_not_expected = [x for x in remaining_contents if x not in expected_contents] | ||
40 | self.assertFalse(remaining_not_expected, msg="Not all necessary content has been deleted from %s: %s" % (path, ', '.join(map(str, remaining_not_expected)))) | ||
41 | expected_not_remaining = [x for x in expected_contents if x not in remaining_contents] | ||
42 | self.assertFalse(expected_not_remaining, msg="The script removed extra contents from %s: %s" % (path, ', '.join(map(str, expected_not_remaining)))) | ||
43 | |||
44 | class BuildhistoryDiffTests(BuildhistoryBase): | ||
45 | |||
46 | @testcase(295) | ||
47 | def test_buildhistory_diff(self): | ||
48 | self.add_command_to_tearDown('cleanup-workdir') | ||
49 | target = 'xcursor-transparent-theme' | ||
50 | self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True) | ||
51 | self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True) | ||
52 | result = runCmd("buildhistory-diff -p %s" % get_bb_var('BUILDHISTORY_DIR')) | ||
53 | expected_output = 'PR changed from "r1" to "r0"' | ||
54 | self.assertTrue(expected_output in result.output, msg="Did not find expected output: %s" % result.output) | ||
diff --git a/meta/lib/oeqa/selftest/prservice.py b/meta/lib/oeqa/selftest/prservice.py new file mode 100644 index 0000000000..fb6d68d3bf --- /dev/null +++ b/meta/lib/oeqa/selftest/prservice.py | |||
@@ -0,0 +1,121 @@ | |||
1 | import unittest | ||
2 | import os | ||
3 | import logging | ||
4 | import re | ||
5 | import shutil | ||
6 | import datetime | ||
7 | |||
8 | import oeqa.utils.ftools as ftools | ||
9 | from oeqa.selftest.base import oeSelfTest | ||
10 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var | ||
11 | from oeqa.utils.decorators import testcase | ||
12 | |||
13 | class BitbakePrTests(oeSelfTest): | ||
14 | |||
15 | def get_pr_version(self, package_name): | ||
16 | pkgdata_dir = get_bb_var('PKGDATA_DIR') | ||
17 | package_data_file = os.path.join(pkgdata_dir, 'runtime', package_name) | ||
18 | package_data = ftools.read_file(package_data_file) | ||
19 | find_pr = re.search("PKGR: r[0-9]+\.([0-9]+)", package_data) | ||
20 | self.assertTrue(find_pr) | ||
21 | return int(find_pr.group(1)) | ||
22 | |||
23 | def get_task_stamp(self, package_name, recipe_task): | ||
24 | stampdata = get_bb_var('STAMP', target=package_name).split('/') | ||
25 | prefix = stampdata[-1] | ||
26 | package_stamps_path = "/".join(stampdata[:-1]) | ||
27 | stamps = [] | ||
28 | for stamp in os.listdir(package_stamps_path): | ||
29 | find_stamp = re.match("%s\.%s\.([a-z0-9]{32})" % (prefix, recipe_task), stamp) | ||
30 | if find_stamp: | ||
31 | stamps.append(find_stamp.group(1)) | ||
32 | self.assertFalse(len(stamps) == 0, msg="Cound not find stamp for task %s for recipe %s" % (recipe_task, package_name)) | ||
33 | self.assertFalse(len(stamps) > 1, msg="Found multiple %s stamps for the %s recipe in the %s directory." % (recipe_task, package_name, package_stamps_path)) | ||
34 | return str(stamps[0]) | ||
35 | |||
36 | def increment_package_pr(self, package_name): | ||
37 | inc_data = "do_package_append() {\nbb.build.exec_func('do_test_prserv', d)\n}\ndo_test_prserv() {\necho \"The current date is: %s\"\n}" % datetime.datetime.now() | ||
38 | self.write_recipeinc(package_name, inc_data) | ||
39 | bitbake("-ccleansstate %s" % package_name) | ||
40 | res = bitbake(package_name, ignore_status=True) | ||
41 | self.delete_recipeinc(package_name) | ||
42 | self.assertEqual(res.status, 0, msg=res.output) | ||
43 | self.assertTrue("NOTE: Started PRServer with DBfile" in res.output, msg=res.output) | ||
44 | |||
45 | def config_pr_tests(self, package_name, package_type='rpm', pr_socket='localhost:0'): | ||
46 | config_package_data = 'PACKAGE_CLASSES = "package_%s"' % package_type | ||
47 | self.write_config(config_package_data) | ||
48 | config_server_data = 'PRSERV_HOST = "%s"' % pr_socket | ||
49 | self.append_config(config_server_data) | ||
50 | |||
51 | def run_test_pr_service(self, package_name, package_type='rpm', track_task='do_package', pr_socket='localhost:0'): | ||
52 | self.config_pr_tests(package_name, package_type, pr_socket) | ||
53 | |||
54 | self.increment_package_pr(package_name) | ||
55 | pr_1 = self.get_pr_version(package_name) | ||
56 | stamp_1 = self.get_task_stamp(package_name, track_task) | ||
57 | |||
58 | self.increment_package_pr(package_name) | ||
59 | pr_2 = self.get_pr_version(package_name) | ||
60 | stamp_2 = self.get_task_stamp(package_name, track_task) | ||
61 | |||
62 | bitbake("-ccleansstate %s" % package_name) | ||
63 | self.assertTrue(pr_2 - pr_1 == 1) | ||
64 | self.assertTrue(stamp_1 != stamp_2) | ||
65 | |||
66 | def run_test_pr_export_import(self, package_name, replace_current_db=True): | ||
67 | self.config_pr_tests(package_name) | ||
68 | |||
69 | self.increment_package_pr(package_name) | ||
70 | pr_1 = self.get_pr_version(package_name) | ||
71 | |||
72 | exported_db_path = os.path.join(self.builddir, 'export.inc') | ||
73 | export_result = runCmd("bitbake-prserv-tool export %s" % exported_db_path, ignore_status=True) | ||
74 | self.assertEqual(export_result.status, 0, msg="PR Service database export failed: %s" % export_result.output) | ||
75 | |||
76 | if replace_current_db: | ||
77 | current_db_path = os.path.join(get_bb_var('PERSISTENT_DIR'), 'prserv.sqlite3') | ||
78 | self.assertTrue(os.path.exists(current_db_path), msg="Path to current PR Service database is invalid: %s" % current_db_path) | ||
79 | os.remove(current_db_path) | ||
80 | |||
81 | import_result = runCmd("bitbake-prserv-tool import %s" % exported_db_path, ignore_status=True) | ||
82 | os.remove(exported_db_path) | ||
83 | self.assertEqual(import_result.status, 0, msg="PR Service database import failed: %s" % import_result.output) | ||
84 | |||
85 | self.increment_package_pr(package_name) | ||
86 | pr_2 = self.get_pr_version(package_name) | ||
87 | |||
88 | bitbake("-ccleansstate %s" % package_name) | ||
89 | self.assertTrue(pr_2 - pr_1 == 1) | ||
90 | |||
91 | @testcase(930) | ||
92 | def test_import_export_replace_db(self): | ||
93 | self.run_test_pr_export_import('m4') | ||
94 | |||
95 | @testcase(931) | ||
96 | def test_import_export_override_db(self): | ||
97 | self.run_test_pr_export_import('m4', replace_current_db=False) | ||
98 | |||
99 | @testcase(932) | ||
100 | def test_pr_service_rpm_arch_dep(self): | ||
101 | self.run_test_pr_service('m4', 'rpm', 'do_package') | ||
102 | |||
103 | @testcase(934) | ||
104 | def test_pr_service_deb_arch_dep(self): | ||
105 | self.run_test_pr_service('m4', 'deb', 'do_package') | ||
106 | |||
107 | @testcase(933) | ||
108 | def test_pr_service_ipk_arch_dep(self): | ||
109 | self.run_test_pr_service('m4', 'ipk', 'do_package') | ||
110 | |||
111 | @testcase(935) | ||
112 | def test_pr_service_rpm_arch_indep(self): | ||
113 | self.run_test_pr_service('xcursor-transparent-theme', 'rpm', 'do_package') | ||
114 | |||
115 | @testcase(937) | ||
116 | def test_pr_service_deb_arch_indep(self): | ||
117 | self.run_test_pr_service('xcursor-transparent-theme', 'deb', 'do_package') | ||
118 | |||
119 | @testcase(936) | ||
120 | def test_pr_service_ipk_arch_indep(self): | ||
121 | self.run_test_pr_service('xcursor-transparent-theme', 'ipk', 'do_package') | ||
diff --git a/meta/lib/oeqa/selftest/sstate.py b/meta/lib/oeqa/selftest/sstate.py new file mode 100644 index 0000000000..5989724432 --- /dev/null +++ b/meta/lib/oeqa/selftest/sstate.py | |||
@@ -0,0 +1,53 @@ | |||
1 | import datetime | ||
2 | import unittest | ||
3 | import os | ||
4 | import re | ||
5 | import shutil | ||
6 | |||
7 | import oeqa.utils.ftools as ftools | ||
8 | from oeqa.selftest.base import oeSelfTest | ||
9 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer | ||
10 | |||
11 | |||
12 | class SStateBase(oeSelfTest): | ||
13 | |||
14 | def setUpLocal(self): | ||
15 | self.temp_sstate_location = None | ||
16 | self.sstate_path = get_bb_var('SSTATE_DIR') | ||
17 | self.distro = get_bb_var('NATIVELSBSTRING') | ||
18 | self.distro_specific_sstate = os.path.join(self.sstate_path, self.distro) | ||
19 | |||
20 | # Creates a special sstate configuration with the option to add sstate mirrors | ||
21 | def config_sstate(self, temp_sstate_location=False, add_local_mirrors=[]): | ||
22 | self.temp_sstate_location = temp_sstate_location | ||
23 | |||
24 | if self.temp_sstate_location: | ||
25 | temp_sstate_path = os.path.join(self.builddir, "temp_sstate_%s" % datetime.datetime.now().strftime('%Y%m%d%H%M%S')) | ||
26 | config_temp_sstate = "SSTATE_DIR = \"%s\"" % temp_sstate_path | ||
27 | self.append_config(config_temp_sstate) | ||
28 | self.track_for_cleanup(temp_sstate_path) | ||
29 | self.sstate_path = get_bb_var('SSTATE_DIR') | ||
30 | self.distro = get_bb_var('NATIVELSBSTRING') | ||
31 | self.distro_specific_sstate = os.path.join(self.sstate_path, self.distro) | ||
32 | |||
33 | if add_local_mirrors: | ||
34 | config_set_sstate_if_not_set = 'SSTATE_MIRRORS ?= ""' | ||
35 | self.append_config(config_set_sstate_if_not_set) | ||
36 | for local_mirror in add_local_mirrors: | ||
37 | self.assertFalse(os.path.join(local_mirror) == os.path.join(self.sstate_path), msg='Cannot add the current sstate path as a sstate mirror') | ||
38 | config_sstate_mirror = "SSTATE_MIRRORS += \"file://.* file:///%s/PATH\"" % local_mirror | ||
39 | self.append_config(config_sstate_mirror) | ||
40 | |||
41 | # Returns a list containing sstate files | ||
42 | def search_sstate(self, filename_regex, distro_specific=True, distro_nonspecific=True): | ||
43 | result = [] | ||
44 | for root, dirs, files in os.walk(self.sstate_path): | ||
45 | if distro_specific and re.search("%s/[a-z0-9]{2}$" % self.distro, root): | ||
46 | for f in files: | ||
47 | if re.search(filename_regex, f): | ||
48 | result.append(f) | ||
49 | if distro_nonspecific and re.search("%s/[a-z0-9]{2}$" % self.sstate_path, root): | ||
50 | for f in files: | ||
51 | if re.search(filename_regex, f): | ||
52 | result.append(f) | ||
53 | return result | ||
diff --git a/meta/lib/oeqa/selftest/sstatetests.py b/meta/lib/oeqa/selftest/sstatetests.py new file mode 100644 index 0000000000..d578ddd489 --- /dev/null +++ b/meta/lib/oeqa/selftest/sstatetests.py | |||
@@ -0,0 +1,204 @@ | |||
1 | import datetime | ||
2 | import unittest | ||
3 | import os | ||
4 | import re | ||
5 | import shutil | ||
6 | |||
7 | import oeqa.utils.ftools as ftools | ||
8 | from oeqa.selftest.base import oeSelfTest | ||
9 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer | ||
10 | from oeqa.selftest.sstate import SStateBase | ||
11 | from oeqa.utils.decorators import testcase | ||
12 | |||
13 | class SStateTests(SStateBase): | ||
14 | |||
15 | # Test sstate files creation and their location | ||
16 | def run_test_sstate_creation(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True, should_pass=True): | ||
17 | self.config_sstate(temp_sstate_location) | ||
18 | |||
19 | if self.temp_sstate_location: | ||
20 | bitbake(['-cclean'] + targets) | ||
21 | else: | ||
22 | bitbake(['-ccleansstate'] + targets) | ||
23 | |||
24 | bitbake(targets) | ||
25 | file_tracker = self.search_sstate('|'.join(map(str, targets)), distro_specific, distro_nonspecific) | ||
26 | if should_pass: | ||
27 | self.assertTrue(file_tracker , msg="Could not find sstate files for: %s" % ', '.join(map(str, targets))) | ||
28 | else: | ||
29 | self.assertTrue(not file_tracker , msg="Found sstate files in the wrong place for: %s" % ', '.join(map(str, targets))) | ||
30 | |||
31 | @testcase(975) | ||
32 | def test_sstate_creation_distro_specific_pass(self): | ||
33 | targetarch = get_bb_var('TUNE_ARCH') | ||
34 | self.run_test_sstate_creation(['binutils-cross-'+ targetarch, 'binutils-native'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True) | ||
35 | |||
36 | @testcase(975) | ||
37 | def test_sstate_creation_distro_specific_fail(self): | ||
38 | targetarch = get_bb_var('TUNE_ARCH') | ||
39 | self.run_test_sstate_creation(['binutils-cross-'+ targetarch, 'binutils-native'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True, should_pass=False) | ||
40 | |||
41 | @testcase(976) | ||
42 | def test_sstate_creation_distro_nonspecific_pass(self): | ||
43 | self.run_test_sstate_creation(['glibc-initial'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True) | ||
44 | |||
45 | @testcase(976) | ||
46 | def test_sstate_creation_distro_nonspecific_fail(self): | ||
47 | self.run_test_sstate_creation(['glibc-initial'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True, should_pass=False) | ||
48 | |||
49 | |||
50 | # Test the sstate files deletion part of the do_cleansstate task | ||
51 | def run_test_cleansstate_task(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True): | ||
52 | self.config_sstate(temp_sstate_location) | ||
53 | |||
54 | bitbake(['-ccleansstate'] + targets) | ||
55 | |||
56 | bitbake(targets) | ||
57 | tgz_created = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific, distro_nonspecific) | ||
58 | self.assertTrue(tgz_created, msg="Could not find sstate .tgz files for: %s" % ', '.join(map(str, targets))) | ||
59 | |||
60 | siginfo_created = self.search_sstate('|'.join(map(str, [s + '.*?\.siginfo$' for s in targets])), distro_specific, distro_nonspecific) | ||
61 | self.assertTrue(siginfo_created, msg="Could not find sstate .siginfo files for: %s" % ', '.join(map(str, targets))) | ||
62 | |||
63 | bitbake(['-ccleansstate'] + targets) | ||
64 | tgz_removed = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific, distro_nonspecific) | ||
65 | self.assertTrue(not tgz_removed, msg="do_cleansstate didn't remove .tgz sstate files for: %s" % ', '.join(map(str, targets))) | ||
66 | |||
67 | @testcase(977) | ||
68 | def test_cleansstate_task_distro_specific_nonspecific(self): | ||
69 | targetarch = get_bb_var('TUNE_ARCH') | ||
70 | self.run_test_cleansstate_task(['binutils-cross-' + targetarch, 'binutils-native', 'glibc-initial'], distro_specific=True, distro_nonspecific=True, temp_sstate_location=True) | ||
71 | |||
72 | @testcase(977) | ||
73 | def test_cleansstate_task_distro_nonspecific(self): | ||
74 | self.run_test_cleansstate_task(['glibc-initial'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True) | ||
75 | |||
76 | @testcase(977) | ||
77 | def test_cleansstate_task_distro_specific(self): | ||
78 | targetarch = get_bb_var('TUNE_ARCH') | ||
79 | self.run_test_cleansstate_task(['binutils-cross-'+ targetarch, 'binutils-native', 'glibc-initial'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True) | ||
80 | |||
81 | |||
82 | # Test rebuilding of distro-specific sstate files | ||
83 | def run_test_rebuild_distro_specific_sstate(self, targets, temp_sstate_location=True): | ||
84 | self.config_sstate(temp_sstate_location) | ||
85 | |||
86 | bitbake(['-ccleansstate'] + targets) | ||
87 | |||
88 | bitbake(targets) | ||
89 | self.assertTrue(self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=False, distro_nonspecific=True) == [], msg="Found distro non-specific sstate for: %s" % ', '.join(map(str, targets))) | ||
90 | file_tracker_1 = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=True, distro_nonspecific=False) | ||
91 | self.assertTrue(len(file_tracker_1) >= len(targets), msg = "Not all sstate files ware created for: %s" % ', '.join(map(str, targets))) | ||
92 | |||
93 | self.track_for_cleanup(self.distro_specific_sstate + "_old") | ||
94 | shutil.copytree(self.distro_specific_sstate, self.distro_specific_sstate + "_old") | ||
95 | shutil.rmtree(self.distro_specific_sstate) | ||
96 | |||
97 | bitbake(['-cclean'] + targets) | ||
98 | bitbake(targets) | ||
99 | file_tracker_2 = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=True, distro_nonspecific=False) | ||
100 | self.assertTrue(len(file_tracker_2) >= len(targets), msg = "Not all sstate files ware created for: %s" % ', '.join(map(str, targets))) | ||
101 | |||
102 | not_recreated = [x for x in file_tracker_1 if x not in file_tracker_2] | ||
103 | self.assertTrue(not_recreated == [], msg="The following sstate files ware not recreated: %s" % ', '.join(map(str, not_recreated))) | ||
104 | |||
105 | created_once = [x for x in file_tracker_2 if x not in file_tracker_1] | ||
106 | self.assertTrue(created_once == [], msg="The following sstate files ware created only in the second run: %s" % ', '.join(map(str, created_once))) | ||
107 | |||
108 | @testcase(175) | ||
109 | def test_rebuild_distro_specific_sstate_cross_native_targets(self): | ||
110 | targetarch = get_bb_var('TUNE_ARCH') | ||
111 | self.run_test_rebuild_distro_specific_sstate(['binutils-cross-' + targetarch, 'binutils-native'], temp_sstate_location=True) | ||
112 | |||
113 | @testcase(175) | ||
114 | def test_rebuild_distro_specific_sstate_cross_target(self): | ||
115 | targetarch = get_bb_var('TUNE_ARCH') | ||
116 | self.run_test_rebuild_distro_specific_sstate(['binutils-cross-' + targetarch], temp_sstate_location=True) | ||
117 | |||
118 | @testcase(175) | ||
119 | def test_rebuild_distro_specific_sstate_native_target(self): | ||
120 | self.run_test_rebuild_distro_specific_sstate(['binutils-native'], temp_sstate_location=True) | ||
121 | |||
122 | |||
123 | # Test the sstate-cache-management script. Each element in the global_config list is used with the corresponding element in the target_config list | ||
124 | # global_config elements are expected to not generate any sstate files that would be removed by sstate-cache-management.sh (such as changing the value of MACHINE) | ||
125 | def run_test_sstate_cache_management_script(self, target, global_config=[''], target_config=[''], ignore_patterns=[]): | ||
126 | self.assertTrue(global_config) | ||
127 | self.assertTrue(target_config) | ||
128 | self.assertTrue(len(global_config) == len(target_config), msg='Lists global_config and target_config should have the same number of elements') | ||
129 | self.config_sstate(temp_sstate_location=True, add_local_mirrors=[self.sstate_path]) | ||
130 | |||
131 | # If buildhistory is enabled, we need to disable version-going-backwards QA checks for this test. It may report errors otherwise. | ||
132 | if ('buildhistory' in get_bb_var('USER_CLASSES')) or ('buildhistory' in get_bb_var('INHERIT')): | ||
133 | remove_errors_config = 'ERROR_QA_remove = "version-going-backwards"' | ||
134 | self.append_config(remove_errors_config) | ||
135 | |||
136 | # For not this only checks if random sstate tasks are handled correctly as a group. | ||
137 | # In the future we should add control over what tasks we check for. | ||
138 | |||
139 | sstate_archs_list = [] | ||
140 | expected_remaining_sstate = [] | ||
141 | for idx in range(len(target_config)): | ||
142 | self.append_config(global_config[idx]) | ||
143 | self.append_recipeinc(target, target_config[idx]) | ||
144 | sstate_arch = get_bb_var('SSTATE_PKGARCH', target) | ||
145 | if not sstate_arch in sstate_archs_list: | ||
146 | sstate_archs_list.append(sstate_arch) | ||
147 | if target_config[idx] == target_config[-1]: | ||
148 | target_sstate_before_build = self.search_sstate(target + '.*?\.tgz$') | ||
149 | bitbake("-cclean %s" % target) | ||
150 | result = bitbake(target, ignore_status=True) | ||
151 | if target_config[idx] == target_config[-1]: | ||
152 | target_sstate_after_build = self.search_sstate(target + '.*?\.tgz$') | ||
153 | expected_remaining_sstate += [x for x in target_sstate_after_build if x not in target_sstate_before_build if not any(pattern in x for pattern in ignore_patterns)] | ||
154 | self.remove_config(global_config[idx]) | ||
155 | self.remove_recipeinc(target, target_config[idx]) | ||
156 | self.assertEqual(result.status, 0) | ||
157 | |||
158 | runCmd("sstate-cache-management.sh -y --cache-dir=%s --remove-duplicated --extra-archs=%s" % (self.sstate_path, ','.join(map(str, sstate_archs_list)))) | ||
159 | actual_remaining_sstate = [x for x in self.search_sstate(target + '.*?\.tgz$') if not any(pattern in x for pattern in ignore_patterns)] | ||
160 | |||
161 | actual_not_expected = [x for x in actual_remaining_sstate if x not in expected_remaining_sstate] | ||
162 | self.assertFalse(actual_not_expected, msg="Files should have been removed but ware not: %s" % ', '.join(map(str, actual_not_expected))) | ||
163 | expected_not_actual = [x for x in expected_remaining_sstate if x not in actual_remaining_sstate] | ||
164 | self.assertFalse(expected_not_actual, msg="Extra files ware removed: %s" ', '.join(map(str, expected_not_actual))) | ||
165 | |||
166 | @testcase(973) | ||
167 | def test_sstate_cache_management_script_using_pr_1(self): | ||
168 | global_config = [] | ||
169 | target_config = [] | ||
170 | global_config.append('') | ||
171 | target_config.append('PR = "0"') | ||
172 | self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic']) | ||
173 | |||
174 | @testcase(978) | ||
175 | def test_sstate_cache_management_script_using_pr_2(self): | ||
176 | global_config = [] | ||
177 | target_config = [] | ||
178 | global_config.append('') | ||
179 | target_config.append('PR = "0"') | ||
180 | global_config.append('') | ||
181 | target_config.append('PR = "1"') | ||
182 | self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic']) | ||
183 | |||
184 | @testcase(979) | ||
185 | def test_sstate_cache_management_script_using_pr_3(self): | ||
186 | global_config = [] | ||
187 | target_config = [] | ||
188 | global_config.append('MACHINE = "qemux86-64"') | ||
189 | target_config.append('PR = "0"') | ||
190 | global_config.append(global_config[0]) | ||
191 | target_config.append('PR = "1"') | ||
192 | global_config.append('MACHINE = "qemux86"') | ||
193 | target_config.append('PR = "1"') | ||
194 | self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic']) | ||
195 | |||
196 | @testcase(974) | ||
197 | def test_sstate_cache_management_script_using_machine(self): | ||
198 | global_config = [] | ||
199 | target_config = [] | ||
200 | global_config.append('MACHINE = "qemux86-64"') | ||
201 | target_config.append('') | ||
202 | global_config.append('MACHINE = "qemux86"') | ||
203 | target_config.append('') | ||
204 | self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic']) | ||