1#! /usr/bin/env python 2# ex:ts=4:sw=4:sts=4:et 3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- 4# 5# BitBake Toaster Implementation 6# 7# Copyright (C) 2013-2016 Intel Corporation 8# 9# This program is free software; you can redistribute it and/or modify 10# it under the terms of the GNU General Public License version 2 as 11# published by the Free Software Foundation. 12# 13# This program is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License along 19# with this program; if not, write to the Free Software Foundation, Inc., 20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 22import re 23 24from django.core.urlresolvers import reverse 25from django.utils import timezone 26from tests.browser.selenium_helpers import SeleniumTestCase 27 28from orm.models import BitbakeVersion, Release, Project, Build, Target 29 30 31class TestAllBuildsPage(SeleniumTestCase): 32 """ Tests for all builds page /builds/ """ 33 34 PROJECT_NAME = 'test project' 35 CLI_BUILDS_PROJECT_NAME = 'command line builds' 36 37 def setUp(self): 38 bbv = BitbakeVersion.objects.create(name='bbv1', giturl='/tmp/', 39 branch='master', dirpath='') 40 release = Release.objects.create(name='release1', 41 bitbake_version=bbv) 42 self.project1 = Project.objects.create_project(name=self.PROJECT_NAME, 43 release=release) 44 self.default_project = Project.objects.create_project( 45 name=self.CLI_BUILDS_PROJECT_NAME, 46 release=release 47 ) 48 self.default_project.is_default = True 49 self.default_project.save() 50 51 # parameters for builds to associate with the projects 52 now = timezone.now() 53 54 self.project1_build_success = { 55 'project': self.project1, 56 'started_on': now, 57 'completed_on': now, 58 'outcome': Build.SUCCEEDED 59 } 60 61 self.project1_build_failure = { 62 'project': self.project1, 63 'started_on': now, 64 'completed_on': now, 65 'outcome': Build.FAILED 66 } 67 68 self.default_project_build_success = { 69 'project': self.default_project, 70 'started_on': now, 71 'completed_on': now, 72 'outcome': Build.SUCCEEDED 73 } 74 75 def _get_build_time_element(self, build): 76 """ 77 Return the HTML element containing the build time for a build 78 in the recent builds area 79 """ 80 selector = 'div[data-latest-build-result="%s"] ' \ 81 '[data-role="data-recent-build-buildtime-field"]' % build.id 82 83 # because this loads via Ajax, wait for it to be visible 84 self.wait_until_present(selector) 85 86 build_time_spans = self.find_all(selector) 87 88 self.assertEqual(len(build_time_spans), 1) 89 90 return build_time_spans[0] 91 92 def _get_row_for_build(self, build): 93 """ Get the table row for the build from the all builds table """ 94 self.wait_until_present('#allbuildstable') 95 96 rows = self.find_all('#allbuildstable tr') 97 98 # look for the row with a download link on the recipe which matches the 99 # build ID 100 url = reverse('builddashboard', args=(build.id,)) 101 selector = 'td.target a[href="%s"]' % url 102 103 found_row = None 104 for row in rows: 105 106 outcome_links = row.find_elements_by_css_selector(selector) 107 if len(outcome_links) == 1: 108 found_row = row 109 break 110 111 self.assertNotEqual(found_row, None) 112 113 return found_row 114 115 def test_show_tasks_with_suffix(self): 116 """ Task should be shown as suffix on build name """ 117 build = Build.objects.create(**self.project1_build_success) 118 target = 'bash' 119 task = 'clean' 120 Target.objects.create(build=build, target=target, task=task) 121 122 url = reverse('all-builds') 123 self.get(url) 124 self.wait_until_present('td[class="target"]') 125 126 cell = self.find('td[class="target"]') 127 content = cell.get_attribute('innerHTML') 128 expected_text = '%s:%s' % (target, task) 129 130 self.assertTrue(re.search(expected_text, content), 131 '"target" cell should contain text %s' % expected_text) 132 133 def test_rebuild_buttons(self): 134 """ 135 Test 'Rebuild' buttons in recent builds section 136 137 'Rebuild' button should not be shown for command-line builds, 138 but should be shown for other builds 139 """ 140 build1 = Build.objects.create(**self.project1_build_success) 141 default_build = Build.objects.create(**self.default_project_build_success) 142 143 url = reverse('all-builds') 144 self.get(url) 145 146 # shouldn't see a rebuild button for command-line builds 147 selector = 'div[data-latest-build-result="%s"] .rebuild-btn' % default_build.id 148 run_again_button = self.find_all(selector) 149 self.assertEqual(len(run_again_button), 0, 150 'should not see a rebuild button for cli builds') 151 152 # should see a rebuild button for non-command-line builds 153 selector = 'div[data-latest-build-result="%s"] .rebuild-btn' % build1.id 154 run_again_button = self.find_all(selector) 155 self.assertEqual(len(run_again_button), 1, 156 'should see a rebuild button for non-cli builds') 157 158 def test_tooltips_on_project_name(self): 159 """ 160 Test tooltips shown next to project name in the main table 161 162 A tooltip should be present next to the command line 163 builds project name in the all builds page, but not for 164 other projects 165 """ 166 Build.objects.create(**self.project1_build_success) 167 Build.objects.create(**self.default_project_build_success) 168 169 url = reverse('all-builds') 170 self.get(url) 171 172 # get the project name cells from the table 173 cells = self.find_all('#allbuildstable td[class="project"]') 174 175 selector = 'span.get-help' 176 177 for cell in cells: 178 content = cell.get_attribute('innerHTML') 179 help_icons = cell.find_elements_by_css_selector(selector) 180 181 if re.search(self.PROJECT_NAME, content): 182 # no help icon next to non-cli project name 183 msg = 'should not be a help icon for non-cli builds name' 184 self.assertEqual(len(help_icons), 0, msg) 185 elif re.search(self.CLI_BUILDS_PROJECT_NAME, content): 186 # help icon next to cli project name 187 msg = 'should be a help icon for cli builds name' 188 self.assertEqual(len(help_icons), 1, msg) 189 else: 190 msg = 'found unexpected project name cell in all builds table' 191 self.fail(msg) 192 193 def test_builds_time_links(self): 194 """ 195 Successful builds should have links on the time column and in the 196 recent builds area; failed builds should not have links on the time column, 197 or in the recent builds area 198 """ 199 build1 = Build.objects.create(**self.project1_build_success) 200 build2 = Build.objects.create(**self.project1_build_failure) 201 202 # add some targets to these builds so they have recipe links 203 # (and so we can find the row in the ToasterTable corresponding to 204 # a particular build) 205 Target.objects.create(build=build1, target='foo') 206 Target.objects.create(build=build2, target='bar') 207 208 url = reverse('all-builds') 209 self.get(url) 210 211 # test recent builds area for successful build 212 element = self._get_build_time_element(build1) 213 links = element.find_elements_by_css_selector('a') 214 msg = 'should be a link on the build time for a successful recent build' 215 self.assertEquals(len(links), 1, msg) 216 217 # test recent builds area for failed build 218 element = self._get_build_time_element(build2) 219 links = element.find_elements_by_css_selector('a') 220 msg = 'should not be a link on the build time for a failed recent build' 221 self.assertEquals(len(links), 0, msg) 222 223 # test the time column for successful build 224 build1_row = self._get_row_for_build(build1) 225 links = build1_row.find_elements_by_css_selector('td.time a') 226 msg = 'should be a link on the build time for a successful build' 227 self.assertEquals(len(links), 1, msg) 228 229 # test the time column for failed build 230 build2_row = self._get_row_for_build(build2) 231 links = build2_row.find_elements_by_css_selector('td.time a') 232 msg = 'should not be a link on the build time for a failed build' 233 self.assertEquals(len(links), 0, msg) 234