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