Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame^] | 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-2015 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 | """Test cases for Toaster GUI and ReST.""" |
| 23 | |
| 24 | from django.test import TestCase |
| 25 | from django.core.urlresolvers import reverse |
| 26 | from django.utils import timezone |
| 27 | from orm.models import Project, Release, BitbakeVersion, Build |
| 28 | from orm.models import ReleaseLayerSourcePriority, LayerSource, Layer |
| 29 | from orm.models import Layer_Version, Recipe, Machine, ProjectLayer |
| 30 | import json |
| 31 | |
| 32 | PROJECT_NAME = "test project" |
| 33 | |
| 34 | class ViewTests(TestCase): |
| 35 | """Tests to verify view APIs.""" |
| 36 | |
| 37 | def setUp(self): |
| 38 | bbv = BitbakeVersion.objects.create(name="test bbv", giturl="/tmp/", |
| 39 | branch="master", dirpath="") |
| 40 | release = Release.objects.create(name="test release", |
| 41 | bitbake_version=bbv) |
| 42 | self.project = Project.objects.create_project(name=PROJECT_NAME, |
| 43 | release=release) |
| 44 | |
| 45 | layersrc = LayerSource.objects.create(sourcetype=LayerSource.TYPE_IMPORTED) |
| 46 | self.priority = ReleaseLayerSourcePriority.objects.create(release=release, |
| 47 | layer_source=layersrc) |
| 48 | layer = Layer.objects.create(name="base-layer", layer_source=layersrc, |
| 49 | vcs_url="/tmp/") |
| 50 | |
| 51 | lver = Layer_Version.objects.create(layer=layer, project=self.project, |
| 52 | layer_source=layersrc, commit="master") |
| 53 | |
| 54 | Recipe.objects.create(layer_source=layersrc, name="base-recipe", |
| 55 | version="1.2", summary="one recipe", |
| 56 | description="recipe", layer_version=lver) |
| 57 | |
| 58 | Machine.objects.create(layer_version=lver, name="wisk", |
| 59 | description="wisking machine") |
| 60 | |
| 61 | ProjectLayer.objects.create(project=self.project, layercommit=lver) |
| 62 | |
| 63 | self.assertTrue(lver in self.project.compatible_layerversions()) |
| 64 | |
| 65 | def test_get_base_call_returns_html(self): |
| 66 | """Basic test for all-projects view""" |
| 67 | response = self.client.get(reverse('all-projects'), follow=True) |
| 68 | self.assertEqual(response.status_code, 200) |
| 69 | self.assertTrue(response['Content-Type'].startswith('text/html')) |
| 70 | self.assertTemplateUsed(response, "projects.html") |
| 71 | self.assertTrue(PROJECT_NAME in response.content) |
| 72 | |
| 73 | def test_get_json_call_returns_json(self): |
| 74 | """Test for all projects output in json format""" |
| 75 | url = reverse('all-projects') |
| 76 | response = self.client.get(url, {"format": "json"}, follow=True) |
| 77 | self.assertEqual(response.status_code, 200) |
| 78 | self.assertTrue(response['Content-Type'].startswith('application/json')) |
| 79 | |
| 80 | data = json.loads(response.content) |
| 81 | |
| 82 | self.assertTrue("error" in data) |
| 83 | self.assertEqual(data["error"], "ok") |
| 84 | self.assertTrue("rows" in data) |
| 85 | |
| 86 | self.assertTrue(PROJECT_NAME in [x["name"] for x in data["rows"]]) |
| 87 | self.assertTrue("id" in data["rows"][0]) |
| 88 | |
| 89 | self.assertEqual(sorted(data["rows"][0]), |
| 90 | ['bitbake_version_id', 'created', 'id', |
| 91 | 'is_default', 'layersTypeAheadUrl', 'name', |
| 92 | 'num_builds', 'projectBuildsUrl', 'projectPageUrl', |
| 93 | 'recipesTypeAheadUrl', 'release_id', |
| 94 | 'short_description', 'updated', 'user_id']) |
| 95 | |
| 96 | def test_typeaheads(self): |
| 97 | """Test typeahead ReST API""" |
| 98 | layers_url = reverse('xhr_layerstypeahead', args=(self.project.id,)) |
| 99 | prj_url = reverse('xhr_projectstypeahead') |
| 100 | |
| 101 | urls = [layers_url, |
| 102 | prj_url, |
| 103 | reverse('xhr_recipestypeahead', args=(self.project.id,)), |
| 104 | reverse('xhr_machinestypeahead', args=(self.project.id,)), |
| 105 | ] |
| 106 | |
| 107 | def basic_reponse_check(response, url): |
| 108 | """Check data structure of http response.""" |
| 109 | self.assertEqual(response.status_code, 200) |
| 110 | self.assertTrue(response['Content-Type'].startswith('application/json')) |
| 111 | |
| 112 | data = json.loads(response.content) |
| 113 | |
| 114 | self.assertTrue("error" in data) |
| 115 | self.assertEqual(data["error"], "ok") |
| 116 | self.assertTrue("results" in data) |
| 117 | |
| 118 | # We got a result so now check the fields |
| 119 | if len(data['results']) > 0: |
| 120 | result = data['results'][0] |
| 121 | |
| 122 | self.assertTrue(len(result['name']) > 0) |
| 123 | self.assertTrue("detail" in result) |
| 124 | self.assertTrue(result['id'] > 0) |
| 125 | |
| 126 | # Special check for the layers typeahead's extra fields |
| 127 | if url == layers_url: |
| 128 | self.assertTrue(len(result['layerdetailurl']) > 0) |
| 129 | self.assertTrue(len(result['vcs_url']) > 0) |
| 130 | self.assertTrue(len(result['vcs_reference']) > 0) |
| 131 | # Special check for project typeahead extra fields |
| 132 | elif url == prj_url: |
| 133 | self.assertTrue(len(result['projectPageUrl']) > 0) |
| 134 | |
| 135 | return True |
| 136 | |
| 137 | return False |
| 138 | |
| 139 | import string |
| 140 | |
| 141 | for url in urls: |
| 142 | results = False |
| 143 | |
| 144 | for typeing in list(string.ascii_letters): |
| 145 | response = self.client.get(url, {'search': typeing}) |
| 146 | results = basic_reponse_check(response, url) |
| 147 | if results: |
| 148 | break |
| 149 | |
| 150 | # After "typeing" the alpabet we should have result true |
| 151 | # from each of the urls |
| 152 | self.assertTrue(results) |
| 153 | |
| 154 | def test_xhr_import_layer(self): |
| 155 | """Test xhr_importlayer API""" |
| 156 | #Test for importing an already existing layer |
| 157 | args = {'vcs_url' : "git://git.example.com/test", |
| 158 | 'name' : "base-layer", |
| 159 | 'git_ref': "c12b9596afd236116b25ce26dbe0d793de9dc7ce", |
| 160 | 'project_id': 1, 'dir_path' : "/path/in/repository"} |
| 161 | response = self.client.post(reverse('xhr_importlayer'), args) |
| 162 | data = json.loads(response.content) |
| 163 | self.assertEqual(response.status_code, 200) |
| 164 | self.assertNotEqual(data["error"], "ok") |
| 165 | |
| 166 | #Test to verify import of a layer successful |
| 167 | args['name'] = "meta-oe" |
| 168 | response = self.client.post(reverse('xhr_importlayer'), args) |
| 169 | data = json.loads(response.content) |
| 170 | self.assertTrue(data["error"], "ok") |
| 171 | |
| 172 | #Test for html tag in the data |
| 173 | args['<'] = "testing html tag" |
| 174 | response = self.client.post(reverse('xhr_importlayer'), args) |
| 175 | data = json.loads(response.content) |
| 176 | self.assertNotEqual(data["error"], "ok") |
| 177 | |
| 178 | #Empty data passed |
| 179 | args = {} |
| 180 | response = self.client.post(reverse('xhr_importlayer'), args) |
| 181 | data = json.loads(response.content) |
| 182 | self.assertNotEqual(data["error"], "ok") |
| 183 | |
| 184 | class LandingPageTests(TestCase): |
| 185 | """ Tests for redirects on the landing page """ |
| 186 | # disable bogus pylint message error: |
| 187 | # "Instance of 'WSGIRequest' has no 'url' member (no-member)" |
| 188 | # (see https://github.com/landscapeio/pylint-django/issues/42) |
| 189 | # pylint: disable=E1103 |
| 190 | |
| 191 | LANDING_PAGE_TITLE = 'This is Toaster' |
| 192 | |
| 193 | def setUp(self): |
| 194 | """ Add default project manually """ |
| 195 | self.project = Project.objects.create_project('foo', None) |
| 196 | self.project.is_default = True |
| 197 | self.project.save() |
| 198 | |
| 199 | def test_only_default_project(self): |
| 200 | """ |
| 201 | No projects except default |
| 202 | => get the landing page |
| 203 | """ |
| 204 | response = self.client.get(reverse('landing')) |
| 205 | self.assertTrue(self.LANDING_PAGE_TITLE in response.content) |
| 206 | |
| 207 | def test_default_project_has_build(self): |
| 208 | """ |
| 209 | Default project has a build, no other projects |
| 210 | => get the builds page |
| 211 | """ |
| 212 | now = timezone.now() |
| 213 | build = Build.objects.create(project=self.project, |
| 214 | started_on=now, |
| 215 | completed_on=now) |
| 216 | build.save() |
| 217 | |
| 218 | response = self.client.get(reverse('landing')) |
| 219 | self.assertEqual(response.status_code, 302, |
| 220 | 'response should be a redirect') |
| 221 | self.assertTrue('/builds' in response.url, |
| 222 | 'should redirect to builds') |
| 223 | |
| 224 | def test_user_project_exists(self): |
| 225 | """ |
| 226 | User has added a project (without builds) |
| 227 | => get the projects page |
| 228 | """ |
| 229 | user_project = Project.objects.create_project('foo', None) |
| 230 | user_project.save() |
| 231 | |
| 232 | response = self.client.get(reverse('landing')) |
| 233 | self.assertEqual(response.status_code, 302, |
| 234 | 'response should be a redirect') |
| 235 | self.assertTrue('/projects' in response.url, |
| 236 | 'should redirect to projects') |
| 237 | |
| 238 | def test_user_project_has_build(self): |
| 239 | """ |
| 240 | User has added a project (with builds) |
| 241 | => get the builds page |
| 242 | """ |
| 243 | user_project = Project.objects.create_project('foo', None) |
| 244 | user_project.save() |
| 245 | |
| 246 | now = timezone.now() |
| 247 | build = Build.objects.create(project=user_project, |
| 248 | started_on=now, |
| 249 | completed_on=now) |
| 250 | build.save() |
| 251 | |
| 252 | response = self.client.get(reverse('landing')) |
| 253 | self.assertEqual(response.status_code, 302, |
| 254 | 'response should be a redirect') |
| 255 | self.assertTrue('/builds' in response.url, |
| 256 | 'should redirect to builds') |
| 257 | |
| 258 | class ProjectsPageTests(TestCase): |
| 259 | """ Tests for projects page """ |
| 260 | |
| 261 | PROJECT_NAME = 'cli builds' |
| 262 | |
| 263 | def setUp(self): |
| 264 | """ Add default project manually """ |
| 265 | project = Project.objects.create_project(self.PROJECT_NAME, None) |
| 266 | self.default_project = project |
| 267 | self.default_project.is_default = True |
| 268 | self.default_project.save() |
| 269 | |
| 270 | def test_default_project_hidden(self): |
| 271 | """ The default project should be hidden if it has no builds """ |
| 272 | params = {"count": 10, "orderby": "updated:-", "page": 1} |
| 273 | response = self.client.get(reverse('all-projects'), params) |
| 274 | |
| 275 | self.assertTrue(not('tr class="data"' in response.content), |
| 276 | 'should be no project rows in the page') |
| 277 | self.assertTrue(not(self.PROJECT_NAME in response.content), |
| 278 | 'default project "cli builds" should not be in page') |
| 279 | |
| 280 | def test_default_project_has_build(self): |
| 281 | """ The default project should be shown if it has builds """ |
| 282 | now = timezone.now() |
| 283 | build = Build.objects.create(project=self.default_project, |
| 284 | started_on=now, |
| 285 | completed_on=now) |
| 286 | build.save() |
| 287 | |
| 288 | params = {"count": 10, "orderby": "updated:-", "page": 1} |
| 289 | response = self.client.get(reverse('all-projects'), params) |
| 290 | |
| 291 | self.assertTrue('tr class="data"' in response.content, |
| 292 | 'should be a project row in the page') |
| 293 | self.assertTrue(self.PROJECT_NAME in response.content, |
| 294 | 'default project "cli builds" should be in page') |