1#! /usr/bin/env python3
2#
3# BitBake Toaster Implementation
4#
5# SPDX-License-Identifier: GPL-2.0-only
6#
7# Copyright (C) 2013-2016 Intel Corporation
8#
9
10from django.urls import reverse
11from django.utils import timezone
12from tests.browser.selenium_helpers import SeleniumTestCase
13from tests.browser.selenium_helpers_base import Wait
14from orm.models import Project, Build, Task, Recipe, Layer, Layer_Version
15from bldcontrol.models import BuildRequest
16
17class TestMostRecentBuildsStates(SeleniumTestCase):
18    """ Test states update correctly in most recent builds area """
19
20    def _create_build_request(self):
21        project = Project.objects.get_or_create_default_project()
22
23        now = timezone.now()
24
25        build = Build.objects.create(project=project, build_name='fakebuild',
26            started_on=now, completed_on=now)
27
28        return BuildRequest.objects.create(build=build, project=project,
29            state=BuildRequest.REQ_QUEUED)
30
31    def _create_recipe(self):
32        """ Add a recipe to the database and return it """
33        layer = Layer.objects.create()
34        layer_version = Layer_Version.objects.create(layer=layer)
35        return Recipe.objects.create(name='foo', layer_version=layer_version)
36
37    def _check_build_states(self, build_request):
38        recipes_to_parse = 10
39        url = reverse('all-builds')
40        self.get(url)
41
42        build = build_request.build
43        base_selector = '[data-latest-build-result="%s"] ' % build.id
44
45        # build queued; check shown as queued
46        selector = base_selector + '[data-build-state="Queued"]'
47        element = self.wait_until_visible(selector)
48        self.assertRegexpMatches(element.get_attribute('innerHTML'),
49            'Build queued', 'build should show queued status')
50
51        # waiting for recipes to be parsed
52        build.outcome = Build.IN_PROGRESS
53        build.recipes_to_parse = recipes_to_parse
54        build.recipes_parsed = 0
55
56        build_request.state = BuildRequest.REQ_INPROGRESS
57        build_request.save()
58
59        self.get(url)
60
61        selector = base_selector + '[data-build-state="Parsing"]'
62        element = self.wait_until_visible(selector)
63
64        bar_selector = '#recipes-parsed-percentage-bar-%s' % build.id
65        bar_element = element.find_element_by_css_selector(bar_selector)
66        self.assertEqual(bar_element.value_of_css_property('width'), '0px',
67            'recipe parse progress should be at 0')
68
69        # recipes being parsed; check parse progress
70        build.recipes_parsed = 5
71        build.save()
72
73        self.get(url)
74
75        element = self.wait_until_visible(selector)
76        bar_element = element.find_element_by_css_selector(bar_selector)
77        recipe_bar_updated = lambda driver: \
78            bar_element.get_attribute('style') == 'width: 50%;'
79        msg = 'recipe parse progress bar should update to 50%'
80        element = Wait(self.driver).until(recipe_bar_updated, msg)
81
82        # all recipes parsed, task started, waiting for first task to finish;
83        # check status is shown as "Tasks starting..."
84        build.recipes_parsed = recipes_to_parse
85        build.save()
86
87        recipe = self._create_recipe()
88        task1 = Task.objects.create(build=build, recipe=recipe,
89            task_name='Lionel')
90        task2 = Task.objects.create(build=build, recipe=recipe,
91            task_name='Jeffries')
92
93        self.get(url)
94
95        selector = base_selector + '[data-build-state="Starting"]'
96        element = self.wait_until_visible(selector)
97        self.assertRegexpMatches(element.get_attribute('innerHTML'),
98            'Tasks starting', 'build should show "tasks starting" status')
99
100        # first task finished; check tasks progress bar
101        task1.order = 1
102        task1.save()
103
104        self.get(url)
105
106        selector = base_selector + '[data-build-state="In Progress"]'
107        element = self.wait_until_visible(selector)
108
109        bar_selector = '#build-pc-done-bar-%s' % build.id
110        bar_element = element.find_element_by_css_selector(bar_selector)
111
112        task_bar_updated = lambda driver: \
113            bar_element.get_attribute('style') == 'width: 50%;'
114        msg = 'tasks progress bar should update to 50%'
115        element = Wait(self.driver).until(task_bar_updated, msg)
116
117        # last task finished; check tasks progress bar updates
118        task2.order = 2
119        task2.save()
120
121        self.get(url)
122
123        element = self.wait_until_visible(selector)
124        bar_element = element.find_element_by_css_selector(bar_selector)
125        task_bar_updated = lambda driver: \
126            bar_element.get_attribute('style') == 'width: 100%;'
127        msg = 'tasks progress bar should update to 100%'
128        element = Wait(self.driver).until(task_bar_updated, msg)
129
130    def test_states_to_success(self):
131        """
132        Test state transitions in the recent builds area for a build which
133        completes successfully.
134        """
135        build_request = self._create_build_request()
136
137        self._check_build_states(build_request)
138
139        # all tasks complete and build succeeded; check success state shown
140        build = build_request.build
141        build.outcome = Build.SUCCEEDED
142        build.save()
143
144        selector = '[data-latest-build-result="%s"] ' \
145            '[data-build-state="Succeeded"]' % build.id
146        element = self.wait_until_visible(selector)
147
148    def test_states_to_failure(self):
149        """
150        Test state transitions in the recent builds area for a build which
151        completes in a failure.
152        """
153        build_request = self._create_build_request()
154
155        self._check_build_states(build_request)
156
157        # all tasks complete and build succeeded; check fail state shown
158        build = build_request.build
159        build.outcome = Build.FAILED
160        build.save()
161
162        selector = '[data-latest-build-result="%s"] ' \
163            '[data-build-state="Failed"]' % build.id
164        element = self.wait_until_visible(selector)
165
166    def test_states_cancelling(self):
167        """
168        Test that most recent build area updates correctly for a build
169        which is cancelled.
170        """
171        url = reverse('all-builds')
172
173        build_request = self._create_build_request()
174        build = build_request.build
175
176        # cancel the build
177        build_request.state = BuildRequest.REQ_CANCELLING
178        build_request.save()
179
180        self.get(url)
181
182        # check cancelling state
183        selector = '[data-latest-build-result="%s"] ' \
184            '[data-build-state="Cancelling"]' % build.id
185        element = self.wait_until_visible(selector)
186        self.assertRegexpMatches(element.get_attribute('innerHTML'),
187            'Cancelling the build', 'build should show "cancelling" status')
188
189        # check cancelled state
190        build.outcome = Build.CANCELLED
191        build.save()
192
193        self.get(url)
194
195        selector = '[data-latest-build-result="%s"] ' \
196            '[data-build-state="Cancelled"]' % build.id
197        element = self.wait_until_visible(selector)
198        self.assertRegexpMatches(element.get_attribute('innerHTML'),
199            'Build cancelled', 'build should show "cancelled" status')
200