diff options
4 files changed, 191 insertions, 7 deletions
diff --git a/bitbake/lib/toaster/tests/browser/selenium_helpers.py b/bitbake/lib/toaster/tests/browser/selenium_helpers.py index 54db2e8cf2..be1f037825 100644 --- a/bitbake/lib/toaster/tests/browser/selenium_helpers.py +++ b/bitbake/lib/toaster/tests/browser/selenium_helpers.py | |||
@@ -158,6 +158,13 @@ class SeleniumTestCase(StaticLiveServerTestCase): | |||
158 | """ Find all elements matching CSS selector """ | 158 | """ Find all elements matching CSS selector """ |
159 | return self.driver.find_elements_by_css_selector(selector) | 159 | return self.driver.find_elements_by_css_selector(selector) |
160 | 160 | ||
161 | def element_exists(self, selector): | ||
162 | """ | ||
163 | Return True if one element matching selector exists, | ||
164 | False otherwise | ||
165 | """ | ||
166 | return len(self.find_all(selector)) == 1 | ||
167 | |||
161 | def focused_element(self): | 168 | def focused_element(self): |
162 | """ Return the element which currently has focus on the page """ | 169 | """ Return the element which currently has focus on the page """ |
163 | return self.driver.switch_to.active_element | 170 | return self.driver.switch_to.active_element |
diff --git a/bitbake/lib/toaster/tests/browser/test_builddashboard_page_artifacts.py b/bitbake/lib/toaster/tests/browser/test_builddashboard_page_artifacts.py new file mode 100644 index 0000000000..18e4475711 --- /dev/null +++ b/bitbake/lib/toaster/tests/browser/test_builddashboard_page_artifacts.py | |||
@@ -0,0 +1,177 @@ | |||
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 | |||
22 | from django.core.urlresolvers import reverse | ||
23 | from django.utils import timezone | ||
24 | |||
25 | from tests.browser.selenium_helpers import SeleniumTestCase | ||
26 | |||
27 | from orm.models import Project, Release, BitbakeVersion, Build, Target | ||
28 | from orm.models import Target_Image_File, TargetSDKFile, TargetKernelFile | ||
29 | |||
30 | class TestBuildDashboardPageArtifacts(SeleniumTestCase): | ||
31 | """ Tests for artifacts on the build dashboard /build/X """ | ||
32 | |||
33 | def setUp(self): | ||
34 | bbv = BitbakeVersion.objects.create(name='bbv1', giturl='/tmp/', | ||
35 | branch='master', dirpath="") | ||
36 | release = Release.objects.create(name='release1', | ||
37 | bitbake_version=bbv) | ||
38 | self.project = Project.objects.create_project(name='test project', | ||
39 | release=release) | ||
40 | |||
41 | def _get_build_dashboard(self, build): | ||
42 | """ | ||
43 | Navigate to the build dashboard for build | ||
44 | """ | ||
45 | url = reverse('builddashboard', args=(build.id,)) | ||
46 | self.get(url) | ||
47 | |||
48 | def _has_build_artifacts_heading(self): | ||
49 | """ | ||
50 | Check whether the "Build artifacts" heading is visible (True if it | ||
51 | is, False otherwise). | ||
52 | """ | ||
53 | return self.element_exists('[data-heading="build-artifacts"]') | ||
54 | |||
55 | def _has_images_menu_option(self): | ||
56 | """ | ||
57 | Try to get the "Images" list element from the left-hand menu in the | ||
58 | build dashboard, and return True if it is present, False otherwise. | ||
59 | """ | ||
60 | return self.element_exists('li.nav-header[data-menu-heading="images"]') | ||
61 | |||
62 | def test_no_artifacts(self): | ||
63 | """ | ||
64 | If a build produced no artifacts, the artifacts heading and images | ||
65 | menu option shouldn't show. | ||
66 | """ | ||
67 | now = timezone.now() | ||
68 | build = Build.objects.create(project=self.project, | ||
69 | started_on=now, completed_on=now, outcome=Build.SUCCEEDED) | ||
70 | |||
71 | Target.objects.create(is_image=False, build=build, task='', | ||
72 | target='mpfr-native') | ||
73 | |||
74 | self._get_build_dashboard(build) | ||
75 | |||
76 | # check build artifacts heading | ||
77 | msg = 'Build artifacts heading should not be displayed for non-image' \ | ||
78 | 'builds' | ||
79 | self.assertFalse(self._has_build_artifacts_heading(), msg) | ||
80 | |||
81 | # check "Images" option in left-hand menu (should not be there) | ||
82 | msg = 'Images option should not be shown in left-hand menu' | ||
83 | self.assertFalse(self._has_images_menu_option(), msg) | ||
84 | |||
85 | def test_sdk_artifacts(self): | ||
86 | """ | ||
87 | If a build produced SDK artifacts, they should be shown, but the section | ||
88 | for image files and the images menu option should be hidden. | ||
89 | """ | ||
90 | now = timezone.now() | ||
91 | build = Build.objects.create(project=self.project, | ||
92 | started_on=now, completed_on=timezone.now(), | ||
93 | outcome=Build.SUCCEEDED) | ||
94 | |||
95 | target = Target.objects.create(is_image=True, build=build, | ||
96 | task='populate_sdk', target='core-image-minimal') | ||
97 | |||
98 | sdk_file1 = TargetSDKFile.objects.create(target=target, | ||
99 | file_size=100000, | ||
100 | file_name='/home/foo/core-image-minimal.toolchain.sh') | ||
101 | |||
102 | sdk_file2 = TargetSDKFile.objects.create(target=target, | ||
103 | file_size=120000, | ||
104 | file_name='/home/foo/x86_64.toolchain.sh') | ||
105 | |||
106 | self._get_build_dashboard(build) | ||
107 | |||
108 | # check build artifacts heading | ||
109 | msg = 'Build artifacts heading should be displayed for SDK ' \ | ||
110 | 'builds which generate artifacts' | ||
111 | self.assertTrue(self._has_build_artifacts_heading(), msg) | ||
112 | |||
113 | # check "Images" option in left-hand menu (should not be there) | ||
114 | msg = 'Images option should not be shown in left-hand menu for ' \ | ||
115 | 'builds which didn\'t generate an image file' | ||
116 | self.assertFalse(self._has_images_menu_option(), msg) | ||
117 | |||
118 | # check links to SDK artifacts | ||
119 | sdk_artifact_links = self.find_all('[data-links="sdk-artifacts"] li') | ||
120 | self.assertEqual(len(sdk_artifact_links), 2, | ||
121 | 'should be links to 2 SDK artifacts') | ||
122 | |||
123 | def test_image_artifacts(self): | ||
124 | """ | ||
125 | If a build produced image files, kernel artifacts, and manifests, | ||
126 | they should all be shown, as well as the image link in the left-hand | ||
127 | menu. | ||
128 | """ | ||
129 | now = timezone.now() | ||
130 | build = Build.objects.create(project=self.project, | ||
131 | started_on=now, completed_on=timezone.now(), | ||
132 | outcome=Build.SUCCEEDED) | ||
133 | |||
134 | target = Target.objects.create(is_image=True, build=build, | ||
135 | task='', target='core-image-minimal', | ||
136 | license_manifest_path='/home/foo/license.manifest', | ||
137 | package_manifest_path='/home/foo/package.manifest') | ||
138 | |||
139 | image_file = Target_Image_File.objects.create(target=target, | ||
140 | file_name='/home/foo/core-image-minimal.ext4', file_size=9000) | ||
141 | |||
142 | kernel_file1 = TargetKernelFile.objects.create(target=target, | ||
143 | file_name='/home/foo/bzImage', file_size=2000) | ||
144 | |||
145 | kernel_file2 = TargetKernelFile.objects.create(target=target, | ||
146 | file_name='/home/foo/bzImage', file_size=2000) | ||
147 | |||
148 | self._get_build_dashboard(build) | ||
149 | |||
150 | # check build artifacts heading | ||
151 | msg = 'Build artifacts heading should be displayed for image ' \ | ||
152 | 'builds' | ||
153 | self.assertTrue(self._has_build_artifacts_heading(), msg) | ||
154 | |||
155 | # check "Images" option in left-hand menu (should be there) | ||
156 | msg = 'Images option should be shown in left-hand menu for image builds' | ||
157 | self.assertTrue(self._has_images_menu_option(), msg) | ||
158 | |||
159 | # check link to image file | ||
160 | selector = '[data-links="image-artifacts"] li' | ||
161 | self.assertTrue(self.element_exists(selector), | ||
162 | 'should be a link to the image file (selector %s)' % selector) | ||
163 | |||
164 | # check links to kernel artifacts | ||
165 | kernel_artifact_links = \ | ||
166 | self.find_all('[data-links="kernel-artifacts"] li') | ||
167 | self.assertEqual(len(kernel_artifact_links), 2, | ||
168 | 'should be links to 2 kernel artifacts') | ||
169 | |||
170 | # check manifest links | ||
171 | selector = 'a[data-link="license-manifest"]' | ||
172 | self.assertTrue(self.element_exists(selector), | ||
173 | 'should be a link to the license manifest (selector %s)' % selector) | ||
174 | |||
175 | selector = 'a[data-link="package-manifest"]' | ||
176 | self.assertTrue(self.element_exists(selector), | ||
177 | 'should be a link to the package manifest (selector %s)' % selector) | ||
diff --git a/bitbake/lib/toaster/toastergui/templates/basebuildpage.html b/bitbake/lib/toaster/toastergui/templates/basebuildpage.html index eb709bbd43..44749bf49a 100644 --- a/bitbake/lib/toaster/toastergui/templates/basebuildpage.html +++ b/bitbake/lib/toaster/toastergui/templates/basebuildpage.html | |||
@@ -63,7 +63,7 @@ | |||
63 | <a href="{% url 'builddashboard' build.pk %}">Build summary</a> | 63 | <a href="{% url 'builddashboard' build.pk %}">Build summary</a> |
64 | </li> | 64 | </li> |
65 | {% if build.has_images and build.outcome == build.SUCCEEDED %} | 65 | {% if build.has_images and build.outcome == build.SUCCEEDED %} |
66 | <li class="nav-header">Images</li> | 66 | <li class="nav-header" data-menu-heading="images">Images</li> |
67 | {% block nav-target %} | 67 | {% block nav-target %} |
68 | {% for t in build.get_sorted_target_list %} | 68 | {% for t in build.get_sorted_target_list %} |
69 | {% if t.has_images %} | 69 | {% if t.has_images %} |
diff --git a/bitbake/lib/toaster/toastergui/templates/builddashboard.html b/bitbake/lib/toaster/toastergui/templates/builddashboard.html index 36c28b7d6a..bc41e23622 100644 --- a/bitbake/lib/toaster/toastergui/templates/builddashboard.html +++ b/bitbake/lib/toaster/toastergui/templates/builddashboard.html | |||
@@ -70,7 +70,7 @@ | |||
70 | {%if build.outcome == build.SUCCEEDED%} | 70 | {%if build.outcome == build.SUCCEEDED%} |
71 | <!-- built images --> | 71 | <!-- built images --> |
72 | {% if hasArtifacts %} | 72 | {% if hasArtifacts %} |
73 | <h2>Build artifacts</h2> | 73 | <h2 data-heading="build-artifacts">Build artifacts</h2> |
74 | {% for target in targets %} | 74 | {% for target in targets %} |
75 | {% if target.target.is_image %} | 75 | {% if target.target.is_image %} |
76 | <div class="well well-transparent dashboard-section" data-artifacts-for-target="{{target.target.pk}}"> | 76 | <div class="well well-transparent dashboard-section" data-artifacts-for-target="{{target.target.pk}}"> |
@@ -88,12 +88,12 @@ | |||
88 | </dt> | 88 | </dt> |
89 | 89 | ||
90 | <dd> | 90 | <dd> |
91 | <a href="{% url 'build_artifact' build.pk 'licensemanifest' target.target.pk %}">License manifest</a> | 91 | <a data-link="license-manifest" href="{% url 'build_artifact' build.pk 'licensemanifest' target.target.pk %}">License manifest</a> |
92 | </dd> | 92 | </dd> |
93 | 93 | ||
94 | {% if target.target.package_manifest_path %} | 94 | {% if target.target.package_manifest_path %} |
95 | <dd> | 95 | <dd> |
96 | <a href="{% url 'build_artifact' build.pk 'packagemanifest' target.target.pk %}">Package manifest</a> | 96 | <a data-link="package-manifest" href="{% url 'build_artifact' build.pk 'packagemanifest' target.target.pk %}">Package manifest</a> |
97 | </dd> | 97 | </dd> |
98 | {% endif %} | 98 | {% endif %} |
99 | </dl> | 99 | </dl> |
@@ -104,7 +104,7 @@ | |||
104 | Image files | 104 | Image files |
105 | </dt> | 105 | </dt> |
106 | <dd> | 106 | <dd> |
107 | <ul class="list-unstyled"> | 107 | <ul class="list-unstyled" data-links="image-artifacts"> |
108 | {% for i in target.imageFiles|dictsort:"suffix" %} | 108 | {% for i in target.imageFiles|dictsort:"suffix" %} |
109 | <li> | 109 | <li> |
110 | <a href="{% url 'build_artifact' build.pk 'imagefile' i.id %}"> | 110 | <a href="{% url 'build_artifact' build.pk 'imagefile' i.id %}"> |
@@ -119,7 +119,7 @@ | |||
119 | Kernel artifacts | 119 | Kernel artifacts |
120 | </dt> | 120 | </dt> |
121 | <dd> | 121 | <dd> |
122 | <ul class="list-unstyled"> | 122 | <ul class="list-unstyled" data-links="kernel-artifacts"> |
123 | {% for artifact in target.target_kernel_artifacts|dictsort:"basename" %} | 123 | {% for artifact in target.target_kernel_artifacts|dictsort:"basename" %} |
124 | <li> | 124 | <li> |
125 | <a href="{% url 'build_artifact' build.id 'targetkernelartifact' artifact.id %}">{{artifact.basename}}</a> | 125 | <a href="{% url 'build_artifact' build.id 'targetkernelartifact' artifact.id %}">{{artifact.basename}}</a> |
@@ -136,7 +136,7 @@ | |||
136 | SDK artifacts | 136 | SDK artifacts |
137 | </dt> | 137 | </dt> |
138 | <dd> | 138 | <dd> |
139 | <ul class="list-unstyled"> | 139 | <ul class="list-unstyled" data-links="sdk-artifacts"> |
140 | {% for artifact in target.target_sdk_artifacts|dictsort:"basename" %} | 140 | {% for artifact in target.target_sdk_artifacts|dictsort:"basename" %} |
141 | <li> | 141 | <li> |
142 | <a href="{% url 'build_artifact' build.id 'targetsdkartifact' artifact.id %}">{{artifact.basename}}</a> | 142 | <a href="{% url 'build_artifact' build.id 'targetsdkartifact' artifact.id %}">{{artifact.basename}}</a> |