1#! /usr/bin/env python3
2#
3# BitBake Toaster Implementation
4#
5# Copyright (C) 2016 Intel Corporation
6#
7# SPDX-License-Identifier: GPL-2.0-only
8#
9# Tests were part of openembedded-core oe selftest Authored by: Lucian Musat
10# Ionut Chisanovici, Paul Eggleton and Cristian Iorga
11
12import os
13import pytest
14
15from django.db.models import Q
16
17from orm.models import Target_Image_File, Target_Installed_Package, Task
18from orm.models import Package_Dependency, Recipe_Dependency, Build
19from orm.models import Task_Dependency, Package, Target, Recipe
20from orm.models import CustomImagePackage
21
22from tests.builds.buildtest import BuildTest
23
24@pytest.mark.order(4)
25@pytest.mark.django_db(True)
26class BuildCoreImageMinimal(BuildTest):
27    """Build core-image-minimal and test the results"""
28
29    def setUp(self):
30        self.completed_build = self.target_already_built("core-image-minimal")
31
32    # Check if build name is unique - tc_id=795
33    def test_Build_Unique_Name(self):
34        all_builds = Build.objects.all().count()
35        distinct_builds = Build.objects.values('id').distinct().count()
36        self.assertEqual(distinct_builds,
37                         all_builds,
38                         msg='Build name is not unique')
39
40    # Check if build cooker log path is unique - tc_id=819
41    def test_Build_Unique_Cooker_Log_Path(self):
42        distinct_path = Build.objects.values(
43            'cooker_log_path').distinct().count()
44        total_builds = Build.objects.values('id').count()
45        self.assertEqual(distinct_path,
46                         total_builds,
47                         msg='Build cooker log path is not unique')
48
49    # Check task order sequence for one build - tc=825
50    def test_Task_Order_Sequence(self):
51        cnt_err = []
52        tasks = Task.objects.filter(
53            Q(build=self.completed_build),
54            ~Q(order=None),
55            ~Q(task_name__contains='_setscene')
56        ).values('id', 'order').order_by("order")
57
58        cnt_tasks = 0
59        for task in tasks:
60            cnt_tasks += 1
61            if (task['order'] != cnt_tasks):
62                cnt_err.append(task['id'])
63        self.assertEqual(
64            len(cnt_err), 0, msg='Errors for task id: %s' % cnt_err)
65
66    # Check if disk_io matches the difference between EndTimeIO and
67    # StartTimeIO in build stats - tc=828
68    # def test_Task_Disk_IO_TC828(self):
69
70    # Check if outcome = 2 (SSTATE) then sstate_result must be 3 (RESTORED) -
71    # tc=832
72    def test_Task_If_Outcome_2_Sstate_Result_Must_Be_3(self):
73        tasks = Task.objects.filter(outcome=2).values('id', 'sstate_result')
74        cnt_err = []
75        for task in tasks:
76            if (task['sstate_result'] != 3):
77                cnt_err.append(task['id'])
78
79        self.assertEqual(len(cnt_err),
80                         0,
81                         msg='Errors for task id: %s' % cnt_err)
82
83    # Check if outcome = 1 (COVERED) or 3 (EXISTING) then sstate_result must
84    # be 0 (SSTATE_NA) - tc=833
85    def test_Task_If_Outcome_1_3_Sstate_Result_Must_Be_0(self):
86        tasks = Task.objects.filter(
87            outcome__in=(Task.OUTCOME_COVERED,
88                         Task.OUTCOME_PREBUILT)).values('id',
89                                                        'task_name',
90                                                        'sstate_result')
91        cnt_err = []
92        for task in tasks:
93            if (task['sstate_result'] != Task.SSTATE_NA and
94                    task['sstate_result'] != Task.SSTATE_MISS):
95                cnt_err.append({'id': task['id'],
96                                'name': task['task_name'],
97                                'sstate_result': task['sstate_result']})
98
99        self.assertEqual(len(cnt_err),
100                         0,
101                         msg='Errors for task id: %s' % cnt_err)
102
103    # Check if outcome is 0 (SUCCESS) or 4 (FAILED) then sstate_result must be
104    # 0 (NA), 1 (MISS) or 2 (FAILED) - tc=834
105    def test_Task_If_Outcome_0_4_Sstate_Result_Must_Be_0_1_2(self):
106        tasks = Task.objects.filter(
107            outcome__in=(0, 4)).values('id', 'sstate_result')
108        cnt_err = []
109
110        for task in tasks:
111            if (task['sstate_result'] not in [0, 1, 2]):
112                cnt_err.append(task['id'])
113
114        self.assertEqual(len(cnt_err),
115                         0,
116                         msg='Errors for task id: %s' % cnt_err)
117
118    # Check if task_executed = TRUE (1), script_type must be 0 (CODING_NA), 2
119    # (CODING_PYTHON), 3 (CODING_SHELL) - tc=891
120    def test_Task_If_Task_Executed_True_Script_Type_0_2_3(self):
121        tasks = Task.objects.filter(
122            task_executed=1).values('id', 'script_type')
123        cnt_err = []
124
125        for task in tasks:
126            if (task['script_type'] not in [0, 2, 3]):
127                cnt_err.append(task['id'])
128        self.assertEqual(len(cnt_err),
129                         0,
130                         msg='Errors for task id: %s' % cnt_err)
131
132    # Check if task_executed = TRUE (1), outcome must be 0 (SUCCESS) or 4
133    # (FAILED) - tc=836
134    def test_Task_If_Task_Executed_True_Outcome_0_4(self):
135        tasks = Task.objects.filter(task_executed=1).values('id', 'outcome')
136        cnt_err = []
137
138        for task in tasks:
139            if (task['outcome'] not in [0, 4]):
140                cnt_err.append(task['id'])
141
142        self.assertEqual(len(cnt_err),
143                         0,
144                         msg='Errors for task id: %s' % cnt_err)
145
146    # Check if task_executed = FALSE (0), script_type must be 0 - tc=890
147    def test_Task_If_Task_Executed_False_Script_Type_0(self):
148        tasks = Task.objects.filter(
149            task_executed=0).values('id', 'script_type')
150        cnt_err = []
151
152        for task in tasks:
153            if (task['script_type'] != 0):
154                cnt_err.append(task['id'])
155
156        self.assertEqual(len(cnt_err),
157                         0,
158                         msg='Errors for task id: %s' % cnt_err)
159
160    # Check if task_executed = FALSE (0) and build outcome = SUCCEEDED (0),
161    # task outcome must be 1 (COVERED), 2 (CACHED), 3 (PREBUILT), 5 (EMPTY) -
162    # tc=837
163    def test_Task_If_Task_Executed_False_Outcome_1_2_3_5(self):
164        builds = Build.objects.filter(outcome=0).values('id')
165        cnt_err = []
166        for build in builds:
167            tasks = Task.objects.filter(
168                build=build['id'], task_executed=0).values('id', 'outcome')
169            for task in tasks:
170                if (task['outcome'] not in [1, 2, 3, 5]):
171                    cnt_err.append(task['id'])
172
173        self.assertEqual(len(cnt_err),
174                         0,
175                         msg='Errors for task id: %s' % cnt_err)
176
177    # Key verification - tc=888
178    def test_Target_Installed_Package(self):
179        rows = Target_Installed_Package.objects.values('id',
180                                                       'target_id',
181                                                       'package_id')
182        cnt_err = []
183
184        for row in rows:
185            target = Target.objects.filter(id=row['target_id']).values('id')
186            package = Package.objects.filter(id=row['package_id']).values('id')
187            if (not target or not package):
188                cnt_err.append(row['id'])
189        self.assertEqual(len(cnt_err),
190                         0,
191                         msg='Errors for target installed package id: %s' %
192                         cnt_err)
193
194    # Key verification - tc=889
195    def test_Task_Dependency(self):
196        rows = Task_Dependency.objects.values('id',
197                                              'task_id',
198                                              'depends_on_id')
199        cnt_err = []
200        for row in rows:
201            task_id = Task.objects.filter(id=row['task_id']).values('id')
202            depends_on_id = Task.objects.filter(
203                id=row['depends_on_id']).values('id')
204            if (not task_id or not depends_on_id):
205                cnt_err.append(row['id'])
206        self.assertEqual(len(cnt_err),
207                         0,
208                         msg='Errors for task dependency id: %s' % cnt_err)
209
210    # Check if build target file_name is populated only if is_image=true AND
211    # orm_build.outcome=0 then if the file exists and its size matches
212    # the file_size value. Need to add the tc in the test run
213    def test_Target_File_Name_Populated(self):
214        cnt_err = []
215        builds = Build.objects.filter(outcome=0).values('id')
216        for build in builds:
217            targets = Target.objects.filter(
218                build_id=build['id'], is_image=1).values('id')
219            for target in targets:
220                target_files = Target_Image_File.objects.filter(
221                    target_id=target['id']).values('id',
222                                                   'file_name',
223                                                   'file_size')
224                for file_info in target_files:
225                    target_id = file_info['id']
226                    target_file_name = file_info['file_name']
227                    target_file_size = file_info['file_size']
228                    if (not target_file_name or not target_file_size):
229                        cnt_err.append(target_id)
230                    else:
231                        if (not os.path.exists(target_file_name)):
232                            cnt_err.append(target_id)
233                        else:
234                            if (os.path.getsize(target_file_name) !=
235                                    target_file_size):
236                                cnt_err.append(target_id)
237            self.assertEqual(len(cnt_err), 0,
238                             msg='Errors for target image file id: %s' %
239                             cnt_err)
240
241    # Key verification - tc=884
242    def test_Package_Dependency(self):
243        cnt_err = []
244        deps = Package_Dependency.objects.values(
245            'id', 'package_id', 'depends_on_id')
246        for dep in deps:
247            if (dep['package_id'] == dep['depends_on_id']):
248                cnt_err.append(dep['id'])
249        self.assertEqual(len(cnt_err), 0,
250                         msg='Errors for package dependency id: %s' % cnt_err)
251
252    # Recipe key verification, recipe name does not depends on a recipe having
253    # the same name - tc=883
254    def test_Recipe_Dependency(self):
255        deps = Recipe_Dependency.objects.values(
256            'id', 'recipe_id', 'depends_on_id')
257        cnt_err = []
258        for dep in deps:
259            if (not dep['recipe_id'] or not dep['depends_on_id']):
260                cnt_err.append(dep['id'])
261            else:
262                name = Recipe.objects.filter(
263                    id=dep['recipe_id']).values('name')
264                dep_name = Recipe.objects.filter(
265                    id=dep['depends_on_id']).values('name')
266                if (name == dep_name):
267                    cnt_err.append(dep['id'])
268        self.assertEqual(len(cnt_err), 0,
269                         msg='Errors for recipe dependency id: %s' % cnt_err)
270
271    # Check if package name does not start with a number (0-9) - tc=846
272    def test_Package_Name_For_Number(self):
273        packages = Package.objects.filter(~Q(size=-1)).values('id', 'name')
274        cnt_err = []
275        for package in packages:
276            if (package['name'][0].isdigit() is True):
277                cnt_err.append(package['id'])
278        self.assertEqual(
279            len(cnt_err), 0, msg='Errors for package id: %s' % cnt_err)
280
281    # Check if package version starts with a number (0-9) - tc=847
282    def test_Package_Version_Starts_With_Number(self):
283        packages = Package.objects.filter(
284            ~Q(size=-1)).values('id', 'version')
285        cnt_err = []
286        for package in packages:
287            if (package['version'][0].isdigit() is False):
288                cnt_err.append(package['id'])
289        self.assertEqual(
290            len(cnt_err), 0, msg='Errors for package id: %s' % cnt_err)
291
292    # Check if package revision starts with 'r' - tc=848
293    def test_Package_Revision_Starts_With_r(self):
294        packages = Package.objects.filter(
295            ~Q(size=-1)).values('id', 'revision')
296        cnt_err = []
297        for package in packages:
298            if (package['revision'][0].startswith("r") is False):
299                cnt_err.append(package['id'])
300        self.assertEqual(
301            len(cnt_err), 0, msg='Errors for package id: %s' % cnt_err)
302
303    # Check the validity of the package build_id
304    # TC must be added in test run
305    def test_Package_Build_Id(self):
306        packages = Package.objects.filter(
307            ~Q(size=-1)).values('id', 'build_id')
308        cnt_err = []
309        for package in packages:
310            build_id = Build.objects.filter(
311                id=package['build_id']).values('id')
312            if (not build_id):
313                # They have no build_id but if they are
314                # CustomImagePackage that's expected
315                try:
316                    CustomImagePackage.objects.get(pk=package['id'])
317                except CustomImagePackage.DoesNotExist:
318                    cnt_err.append(package['id'])
319
320        self.assertEqual(len(cnt_err),
321                         0,
322                         msg="Errors for package id: %s they have no build"
323                         "associated with them" % cnt_err)
324
325    # Check the validity of package recipe_id
326    # TC must be added in test run
327    def test_Package_Recipe_Id(self):
328        packages = Package.objects.filter(
329            ~Q(size=-1)).values('id', 'recipe_id')
330        cnt_err = []
331        for package in packages:
332            recipe_id = Recipe.objects.filter(
333                id=package['recipe_id']).values('id')
334            if (not recipe_id):
335                cnt_err.append(package['id'])
336        self.assertEqual(
337            len(cnt_err), 0, msg='Errors for package id: %s' % cnt_err)
338
339    # Check if package installed_size field is not null
340    # TC must be aded in test run
341    def test_Package_Installed_Size_Not_NULL(self):
342        packages = Package.objects.filter(
343            installed_size__isnull=True).values('id')
344        cnt_err = []
345        for package in packages:
346            cnt_err.append(package['id'])
347        self.assertEqual(
348            len(cnt_err), 0, msg='Errors for package id: %s' % cnt_err)
349
350    def test_custom_packages_generated(self):
351        """Test if there is a corresponding generated CustomImagePackage"""
352        """ for each of the packages generated"""
353        missing_packages = []
354
355        for package in Package.objects.all():
356            try:
357                CustomImagePackage.objects.get(name=package.name)
358            except CustomImagePackage.DoesNotExist:
359                missing_packages.append(package.name)
360
361        self.assertEqual(len(missing_packages), 0,
362                         "Some package were created from the build but their"
363                         " corresponding CustomImagePackage was not found")
364