summaryrefslogtreecommitdiffstats
path: root/yocto-poky/bitbake/lib/toaster/toastergui/tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'yocto-poky/bitbake/lib/toaster/toastergui/tests.py')
-rw-r--r--yocto-poky/bitbake/lib/toaster/toastergui/tests.py859
1 files changed, 213 insertions, 646 deletions
diff --git a/yocto-poky/bitbake/lib/toaster/toastergui/tests.py b/yocto-poky/bitbake/lib/toaster/toastergui/tests.py
index 9e6c46a25..a4cab5848 100644
--- a/yocto-poky/bitbake/lib/toaster/toastergui/tests.py
+++ b/yocto-poky/bitbake/lib/toaster/toastergui/tests.py
@@ -30,90 +30,45 @@ from orm.models import Project, Release, BitbakeVersion, Package, LogMessage
from orm.models import ReleaseLayerSourcePriority, LayerSource, Layer, Build
from orm.models import Layer_Version, Recipe, Machine, ProjectLayer, Target
from orm.models import CustomImageRecipe, ProjectVariable
-from orm.models import Branch
+from orm.models import Branch, CustomImagePackage
import toastermain
+import inspect
+import toastergui
from toastergui.tables import SoftwareRecipesTable
import json
+from datetime import timedelta
from bs4 import BeautifulSoup
import re
+import string
+import json
PROJECT_NAME = "test project"
+PROJECT_NAME2 = "test project 2"
CLI_BUILDS_PROJECT_NAME = 'Command line builds'
-# by default, tests are run in build mode; to run in analysis mode,
-# set this to False in individual test cases
-toastermain.settings.BUILD_MODE = True
-
class ViewTests(TestCase):
"""Tests to verify view APIs."""
- def setUp(self):
- bbv = BitbakeVersion.objects.create(name="test bbv", giturl="/tmp/",
- branch="master", dirpath="")
- release = Release.objects.create(name="test release",
- branch_name="master",
- bitbake_version=bbv)
- self.project = Project.objects.create_project(name=PROJECT_NAME,
- release=release)
- now = timezone.now()
-
- build = Build.objects.create(project=self.project,
- started_on=now,
- completed_on=now)
-
- layersrc = LayerSource.objects.create(sourcetype=LayerSource.TYPE_IMPORTED)
- self.priority = ReleaseLayerSourcePriority.objects.create(release=release,
- layer_source=layersrc)
- layer = Layer.objects.create(name="base-layer", layer_source=layersrc,
- vcs_url="/tmp/")
-
- branch = Branch.objects.create(name="master", layer_source=layersrc)
-
- lver = Layer_Version.objects.create(layer=layer, project=self.project,
- layer_source=layersrc, commit="master",
- up_branch=branch)
-
- self.recipe1 = Recipe.objects.create(layer_source=layersrc,
- name="base-recipe",
- version="1.2",
- summary="one recipe",
- description="recipe",
- layer_version=lver)
-
- Machine.objects.create(layer_version=lver, name="wisk",
- description="wisking machine")
-
- ProjectLayer.objects.create(project=self.project, layercommit=lver)
-
-
- self.customr = CustomImageRecipe.objects.create(\
- name="custom recipe", project=self.project,
- base_recipe=self.recipe1)
-
- self.package = Package.objects.create(name='pkg1', recipe=self.recipe1,
- build=build)
+ fixtures = ['toastergui-unittest-data']
+ def setUp(self):
- # recipe with project for testing AvailableRecipe table
- self.recipe2 = Recipe.objects.create(layer_source=layersrc,
- name="fancy-recipe",
- version="1.4",
- summary="a fancy recipe",
- description="fancy recipe",
- layer_version=lver,
- file_path='/home/foo')
-
- self.assertTrue(lver in self.project.compatible_layerversions())
+ self.project = Project.objects.first()
+ self.recipe1 = Recipe.objects.get(pk=2)
+ self.recipe2 = Recipe.objects.last()
+ self.customr = CustomImageRecipe.objects.first()
+ self.cust_package = CustomImagePackage.objects.first()
+ self.package = Package.objects.first()
+ self.lver = Layer_Version.objects.first()
def test_get_base_call_returns_html(self):
"""Basic test for all-projects view"""
response = self.client.get(reverse('all-projects'), follow=True)
self.assertEqual(response.status_code, 200)
self.assertTrue(response['Content-Type'].startswith('text/html'))
- self.assertTemplateUsed(response, "projects.html")
- self.assertTrue(PROJECT_NAME in response.content)
+ self.assertTemplateUsed(response, "projects-toastertable.html")
def test_get_json_call_returns_json(self):
"""Test for all projects output in json format"""
@@ -128,16 +83,9 @@ class ViewTests(TestCase):
self.assertEqual(data["error"], "ok")
self.assertTrue("rows" in data)
- self.assertTrue(PROJECT_NAME in [x["name"] for x in data["rows"]])
+ self.assertTrue(self.project.name in [x["name"] for x in data["rows"]])
self.assertTrue("id" in data["rows"][0])
- self.assertEqual(sorted(data["rows"][0]),
- ['bitbake_version_id', 'created', 'id',
- 'is_default', 'layersTypeAheadUrl', 'name',
- 'num_builds', 'projectBuildsUrl', 'projectPageUrl',
- 'recipesTypeAheadUrl', 'release_id',
- 'short_description', 'updated', 'user_id'])
-
def test_typeaheads(self):
"""Test typeahead ReST API"""
layers_url = reverse('xhr_layerstypeahead', args=(self.project.id,))
@@ -181,7 +129,6 @@ class ViewTests(TestCase):
return False
- import string
for url in urls:
results = False
@@ -198,15 +145,17 @@ class ViewTests(TestCase):
def test_xhr_import_layer(self):
"""Test xhr_importlayer API"""
+ LayerSource.objects.create(sourcetype=LayerSource.TYPE_IMPORTED)
#Test for importing an already existing layer
args = {'vcs_url' : "git://git.example.com/test",
'name' : "base-layer",
'git_ref': "c12b9596afd236116b25ce26dbe0d793de9dc7ce",
- 'project_id': 1, 'dir_path' : "/path/in/repository"}
+ 'project_id': self.project.id,
+ 'dir_path' : "/path/in/repository"}
response = self.client.post(reverse('xhr_importlayer'), args)
data = json.loads(response.content)
self.assertEqual(response.status_code, 200)
- self.assertNotEqual(data["error"], "ok")
+ self.assertEqual(data["error"], "ok")
#Test to verify import of a layer successful
args['name'] = "meta-oe"
@@ -272,13 +221,12 @@ class ViewTests(TestCase):
def test_xhr_custom_details(self):
"""Test getting custom recipe details"""
- name = "custom recipe"
url = reverse('xhr_customrecipe_id', args=(self.customr.id,))
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
expected = {"error": "ok",
"info": {'id': self.customr.id,
- 'name': name,
+ 'name': self.customr.name,
'base_recipe_id': self.recipe1.id,
'project_id': self.project.id,
}
@@ -290,7 +238,9 @@ class ViewTests(TestCase):
name = "to be deleted"
recipe = CustomImageRecipe.objects.create(\
name=name, project=self.project,
- base_recipe=self.recipe1)
+ base_recipe=self.recipe1,
+ file_path="/tmp/testing",
+ layer_version=self.customr.layer_version)
url = reverse('xhr_customrecipe_id', args=(recipe.id,))
response = self.client.delete(url)
self.assertEqual(response.status_code, 200)
@@ -303,20 +253,34 @@ class ViewTests(TestCase):
def test_xhr_custom_packages(self):
"""Test adding and deleting package to a custom recipe"""
- url = reverse('xhr_customrecipe_packages',
- args=(self.customr.id, self.package.id))
- # add self.package1 to recipe
- response = self.client.put(url)
+ # add self.package to recipe
+ response = self.client.put(reverse('xhr_customrecipe_packages',
+ args=(self.customr.id,
+ self.cust_package.id)))
+
self.assertEqual(response.status_code, 200)
- self.assertEqual(json.loads(response.content), {"error": "ok"})
- self.assertEqual(self.customr.packages.all()[0].id, self.package.id)
+ self.assertEqual(json.loads(response.content),
+ {"error": "ok"})
+ self.assertEqual(self.customr.appends_set.first().name,
+ self.cust_package.name)
# delete it
- response = self.client.delete(url)
+ to_delete = self.customr.appends_set.first().pk
+ del_url = reverse('xhr_customrecipe_packages',
+ args=(self.customr.id, to_delete))
+
+ response = self.client.delete(del_url)
self.assertEqual(response.status_code, 200)
self.assertEqual(json.loads(response.content), {"error": "ok"})
- self.assertFalse(self.customr.packages.all())
- # delete it again to test error condition
- response = self.client.delete(url)
+ all_packages = self.customr.get_all_packages().values_list('pk',
+ flat=True)
+
+ self.assertFalse(to_delete in all_packages)
+ # delete invalid package to test error condition
+ del_url = reverse('xhr_customrecipe_packages',
+ args=(self.customr.id,
+ 99999))
+
+ response = self.client.delete(del_url)
self.assertEqual(response.status_code, 200)
self.assertNotEqual(json.loads(response.content)["error"], "ok")
@@ -332,6 +296,18 @@ class ViewTests(TestCase):
self.assertNotEqual(json.loads(response.content),
{"error": "ok"})
+ def test_download_custom_recipe(self):
+ """Download the recipe file generated for the custom image"""
+
+ # Create a dummy recipe file for the custom image generation to read
+ open("/tmp/a_recipe.bb", 'wa').close()
+ response = self.client.get(reverse('customrecipedownload',
+ args=(self.project.id,
+ self.customr.id)))
+
+ self.assertEqual(response.status_code, 200)
+
+
def test_software_recipes_table(self):
"""Test structure returned for Software RecipesTable"""
table = SoftwareRecipesTable()
@@ -344,7 +320,6 @@ class ViewTests(TestCase):
row2 = next(x for x in rows if x['name'] == self.recipe2.name)
self.assertEqual(response.status_code, 200, 'should be 200 OK status')
- self.assertEqual(len(rows), 2, 'should be 2 recipes')
# check other columns have been populated correctly
self.assertEqual(row1['name'], self.recipe1.name)
@@ -360,568 +335,160 @@ class ViewTests(TestCase):
self.assertEqual(row2['layer_version__layer__name'],
self.recipe2.layer_version.layer.name)
-class LandingPageTests(TestCase):
- """ Tests for redirects on the landing page """
- # disable bogus pylint message error:
- # "Instance of 'WSGIRequest' has no 'url' member (no-member)"
- # (see https://github.com/landscapeio/pylint-django/issues/42)
- # pylint: disable=E1103
-
- LANDING_PAGE_TITLE = 'This is Toaster'
-
- def setUp(self):
- """ Add default project manually """
- self.project = Project.objects.create_project('foo', None)
- self.project.is_default = True
- self.project.save()
-
- def test_only_default_project(self):
- """
- No projects except default
- => get the landing page
- """
- response = self.client.get(reverse('landing'))
- self.assertTrue(self.LANDING_PAGE_TITLE in response.content)
-
- def test_default_project_has_build(self):
- """
- Default project has a build, no other projects
- => get the builds page
- """
- now = timezone.now()
- build = Build.objects.create(project=self.project,
- started_on=now,
- completed_on=now)
- build.save()
-
- response = self.client.get(reverse('landing'))
- self.assertEqual(response.status_code, 302,
- 'response should be a redirect')
- self.assertTrue('/builds' in response.url,
- 'should redirect to builds')
-
- def test_user_project_exists(self):
- """
- User has added a project (without builds)
- => get the projects page
- """
- user_project = Project.objects.create_project('foo', None)
- user_project.save()
-
- response = self.client.get(reverse('landing'))
- self.assertEqual(response.status_code, 302,
- 'response should be a redirect')
- self.assertTrue('/projects' in response.url,
- 'should redirect to projects')
-
- def test_user_project_has_build(self):
- """
- User has added a project (with builds)
- => get the builds page
- """
- user_project = Project.objects.create_project('foo', None)
- user_project.save()
-
- now = timezone.now()
- build = Build.objects.create(project=user_project,
- started_on=now,
- completed_on=now)
- build.save()
-
- response = self.client.get(reverse('landing'))
- self.assertEqual(response.status_code, 302,
- 'response should be a redirect')
- self.assertTrue('/builds' in response.url,
- 'should redirect to builds')
-
-class AllProjectsPageTests(TestCase):
- """ Tests for projects page /projects/ """
-
- MACHINE_NAME = 'delorean'
-
- def setUp(self):
- """ Add default project manually """
- project = Project.objects.create_project(CLI_BUILDS_PROJECT_NAME, None)
- self.default_project = project
- self.default_project.is_default = True
- self.default_project.save()
-
- # this project is only set for some of the tests
- self.project = None
-
- self.release = None
-
- def _add_build_to_default_project(self):
- """ Add a build to the default project (not used in all tests) """
- now = timezone.now()
- build = Build.objects.create(project=self.default_project,
- started_on=now,
- completed_on=now)
- build.save()
-
- def _add_non_default_project(self):
- """ Add another project """
- bbv = BitbakeVersion.objects.create(name="test bbv", giturl="/tmp/",
- branch="master", dirpath="")
- self.release = Release.objects.create(name="test release",
- branch_name="master",
- bitbake_version=bbv)
- self.project = Project.objects.create_project(PROJECT_NAME, self.release)
- self.project.is_default = False
- self.project.save()
-
- # fake the MACHINE variable
- project_var = ProjectVariable.objects.create(project=self.project,
- name='MACHINE',
- value=self.MACHINE_NAME)
- project_var.save()
-
- def test_default_project_hidden(self):
- """ The default project should be hidden if it has no builds """
- params = {"count": 10, "orderby": "updated:-", "page": 1}
- response = self.client.get(reverse('all-projects'), params)
-
- self.assertTrue(not('tr class="data"' in response.content),
- 'should be no project rows in the page')
- self.assertTrue(not(CLI_BUILDS_PROJECT_NAME in response.content),
- 'default project "cli builds" should not be in page')
-
- def test_default_project_has_build(self):
- """ The default project should be shown if it has builds """
- self._add_build_to_default_project()
-
- params = {"count": 10, "orderby": "updated:-", "page": 1}
- response = self.client.get(reverse('all-projects'), params)
-
- self.assertTrue('tr class="data"' in response.content,
- 'should be a project row in the page')
- self.assertTrue(CLI_BUILDS_PROJECT_NAME in response.content,
- 'default project "cli builds" should be in page')
-
- def test_default_project_release(self):
- """
- The release for the default project should display as
- 'Not applicable'
- """
- # need a build, otherwise project doesn't display at all
- self._add_build_to_default_project()
-
- # another project to test, which should show release
- self._add_non_default_project()
-
- response = self.client.get(reverse('all-projects'), follow=True)
- soup = BeautifulSoup(response.content)
-
- # check the release cell for the default project
- attrs = {'data-project': str(self.default_project.id)}
- rows = soup.find_all('tr', attrs=attrs)
- self.assertEqual(len(rows), 1, 'should be one row for default project')
- cells = rows[0].find_all('td', attrs={'data-project-field': 'release'})
- self.assertEqual(len(cells), 1, 'should be one release cell')
- text = cells[0].select('span.muted')[0].text
- self.assertEqual(text, 'Not applicable',
- 'release should be not applicable for default project')
-
- # check the link in the release cell for the other project
- attrs = {'data-project': str(self.project.id)}
- rows = soup.find_all('tr', attrs=attrs)
- cells = rows[0].find_all('td', attrs={'data-project-field': 'release'})
- text = cells[0].select('a')[0].text
- self.assertEqual(text, self.release.name,
- 'release name should be shown for non-default project')
-
- def test_default_project_machine(self):
- """
- The machine for the default project should display as
- 'Not applicable'
- """
- # need a build, otherwise project doesn't display at all
- self._add_build_to_default_project()
-
- # another project to test, which should show machine
- self._add_non_default_project()
-
- response = self.client.get(reverse('all-projects'), follow=True)
- soup = BeautifulSoup(response.content)
-
- # check the machine cell for the default project
- attrs = {'data-project': str(self.default_project.id)}
- rows = soup.find_all('tr', attrs=attrs)
- self.assertEqual(len(rows), 1, 'should be one row for default project')
- cells = rows[0].find_all('td', attrs={'data-project-field': 'machine'})
- self.assertEqual(len(cells), 1, 'should be one machine cell')
- text = cells[0].select('span.muted')[0].text
- self.assertEqual(text, 'Not applicable',
- 'machine should be not applicable for default project')
-
- # check the link in the machine cell for the other project
- attrs = {'data-project': str(self.project.id)}
- rows = soup.find_all('tr', attrs=attrs)
- cells = rows[0].find_all('td', attrs={'data-project-field': 'machine'})
- text = cells[0].select('a')[0].text
- self.assertEqual(text, self.MACHINE_NAME,
- 'machine name should be shown for non-default project')
-
- def test_project_page_links(self):
- """
- Test that links for the default project point to the builds
- page /projects/X/builds for that project, and that links for
- other projects point to their configuration pages /projects/X/
- """
-
- # need a build, otherwise project doesn't display at all
- self._add_build_to_default_project()
-
- # another project to test, which should show machine
- self._add_non_default_project()
-
- response = self.client.get(reverse('all-projects'), follow=True)
- soup = BeautifulSoup(response.content)
-
- # link for default project
- row = soup.find('tr', attrs={'data-project': self.default_project.id})
- cell = row.find('td', attrs={'data-project-field': 'name'})
- expected_url = reverse('projectbuilds', args=(self.default_project.id,))
- self.assertEqual(cell.find('a')['href'], expected_url,
- 'link on default project name should point to builds')
-
- # link for other project
- row = soup.find('tr', attrs={'data-project': self.project.id})
- cell = row.find('td', attrs={'data-project-field': 'name'})
- expected_url = reverse('project', args=(self.project.id,))
- self.assertEqual(cell.find('a')['href'], expected_url,
- 'link on project name should point to configuration')
+ def test_toaster_tables(self):
+ """Test all ToasterTables instances"""
+ current_recipes = self.project.get_available_recipes()
+
+ def get_data(table, options={}):
+ """Send a request and parse the json response"""
+ options['format'] = "json"
+ options['nocache'] = "true"
+ request = RequestFactory().get('/', options)
+
+ # This is the image recipe needed for a package list for
+ # PackagesTable do this here to throw a non exist exception
+ image_recipe = Recipe.objects.get(pk=4)
+
+ # Add any kwargs that are needed by any of the possible tables
+ args = {'pid': self.project.id,
+ 'layerid': self.lver.pk,
+ 'recipeid': self.recipe1.pk,
+ 'recipe_id': image_recipe.pk,
+ 'custrecipeid': self.customr.pk
+ }
-class ProjectBuildsPageTests(TestCase):
- """ Test data at /project/X/builds is displayed correctly """
+ response = table.get(request, **args)
+ return json.loads(response.content)
+
+ # Get a list of classes in tables module
+ tables = inspect.getmembers(toastergui.tables, inspect.isclass)
+
+ for name, table_cls in tables:
+ # Filter out the non ToasterTables from the tables module
+ if not issubclass(table_cls, toastergui.widgets.ToasterTable) or \
+ table_cls == toastergui.widgets.ToasterTable:
+ continue
+
+ # Get the table data without any options, this also does the
+ # initialisation of the table i.e. setup_columns,
+ # setup_filters and setup_queryset that we can use later
+ table = table_cls()
+ all_data = get_data(table)
+
+ self.assertTrue(len(all_data['rows']) > 1,
+ "Cannot test on a %s table with < 1 row" % name)
+
+ if table.default_orderby:
+ row_one = all_data['rows'][0][table.default_orderby.strip("-")]
+ row_two = all_data['rows'][1][table.default_orderby.strip("-")]
+
+ if '-' in table.default_orderby:
+ self.assertTrue(row_one >= row_two,
+ "Default ordering not working on %s"
+ " '%s' should be >= '%s'" %
+ (name, row_one, row_two))
+ else:
+ self.assertTrue(row_one <= row_two,
+ "Default ordering not working on %s"
+ " '%s' should be <= '%s'" %
+ (name, row_one, row_two))
+
+ # Test the column ordering and filtering functionality
+ for column in table.columns:
+ if column['orderable']:
+ # If a column is orderable test it in both order
+ # directions ordering on the columns field_name
+ ascending = get_data(table_cls(),
+ {"orderby" : column['field_name']})
+
+ row_one = ascending['rows'][0][column['field_name']]
+ row_two = ascending['rows'][1][column['field_name']]
+
+ self.assertTrue(row_one <= row_two,
+ "Ascending sort applied but row 0 is less "
+ "than row 1 %s %s " %
+ (column['field_name'], name))
+
+
+ descending = get_data(table_cls(),
+ {"orderby" :
+ '-'+column['field_name']})
+
+ row_one = descending['rows'][0][column['field_name']]
+ row_two = descending['rows'][1][column['field_name']]
+
+ self.assertTrue(row_one >= row_two,
+ "Descending sort applied but row 0 is "
+ "greater than row 1 %s %s" %
+ (column['field_name'], name))
+
+ # If the two start rows are the same we haven't actually
+ # changed the order
+ self.assertNotEqual(ascending['rows'][0],
+ descending['rows'][0],
+ "An orderby %s has not changed the "
+ "order of the data in table %s" %
+ (column['field_name'], name))
+
+ if column['filter_name']:
+ # If a filter is available for the column get the filter
+ # info. This contains what filter actions are defined.
+ filter_info = get_data(table_cls(),
+ {"cmd": "filterinfo",
+ "name": column['filter_name']})
+ self.assertTrue(len(filter_info['filter_actions']) > 0,
+ "Filter %s was defined but no actions "
+ "added to it" % column['filter_name'])
+
+ for filter_action in filter_info['filter_actions']:
+ # filter string to pass as the option
+ # This is the name of the filter:action
+ # e.g. project_filter:not_in_project
+ filter_string = "%s:%s" % (column['filter_name'],
+ filter_action['action_name'])
+ # Now get the data with the filter applied
+ filtered_data = get_data(table_cls(),
+ {"filter" : filter_string})
+
+ # date range filter actions can't specify the
+ # number of results they return, so their count is 0
+ if filter_action['count'] != None:
+ self.assertEqual(len(filtered_data['rows']),
+ int(filter_action['count']),
+ "We added a table filter for %s but "
+ "the number of rows returned was not "
+ "what the filter info said there "
+ "would be" % name)
+
+
+ # Test search functionality on the table
+ something_found = False
+ for search in list(string.ascii_letters):
+ search_data = get_data(table_cls(), {'search' : search})
+
+ if len(search_data['rows']) > 0:
+ something_found = True
+ break
- def setUp(self):
- bbv = BitbakeVersion.objects.create(name="bbv1", giturl="/tmp/",
- branch="master", dirpath="")
- release = Release.objects.create(name="release1",
- bitbake_version=bbv)
- self.project1 = Project.objects.create_project(name=PROJECT_NAME,
- release=release)
- self.project1.save()
-
- self.project2 = Project.objects.create_project(name=PROJECT_NAME,
- release=release)
- self.project2.save()
-
- self.default_project = Project.objects.create_project(
- name=CLI_BUILDS_PROJECT_NAME,
- release=release
- )
- self.default_project.is_default = True
- self.default_project.save()
-
- # parameters for builds to associate with the projects
- now = timezone.now()
-
- self.project1_build_success = {
- "project": self.project1,
- "started_on": now,
- "completed_on": now,
- "outcome": Build.SUCCEEDED
- }
-
- self.project1_build_in_progress = {
- "project": self.project1,
- "started_on": now,
- "completed_on": now,
- "outcome": Build.IN_PROGRESS
- }
-
- self.project2_build_success = {
- "project": self.project2,
- "started_on": now,
- "completed_on": now,
- "outcome": Build.SUCCEEDED
- }
-
- self.project2_build_in_progress = {
- "project": self.project2,
- "started_on": now,
- "completed_on": now,
- "outcome": Build.IN_PROGRESS
- }
-
- def _get_rows_for_project(self, project_id):
- """ Helper to retrieve HTML rows for a project """
- url = reverse("projectbuilds", args=(project_id,))
- response = self.client.get(url, follow=True)
- soup = BeautifulSoup(response.content)
- return soup.select('tr[class="data"]')
-
- def test_show_builds_for_project(self):
- """ Builds for a project should be displayed """
- Build.objects.create(**self.project1_build_success)
- Build.objects.create(**self.project1_build_success)
- build_rows = self._get_rows_for_project(self.project1.id)
- self.assertEqual(len(build_rows), 2)
-
- def test_show_builds_project_only(self):
- """ Builds for other projects should be excluded """
- Build.objects.create(**self.project1_build_success)
- Build.objects.create(**self.project1_build_success)
- Build.objects.create(**self.project1_build_success)
-
- # shouldn't see these two
- Build.objects.create(**self.project2_build_success)
- Build.objects.create(**self.project2_build_in_progress)
-
- build_rows = self._get_rows_for_project(self.project1.id)
- self.assertEqual(len(build_rows), 3)
-
- def test_builds_exclude_in_progress(self):
- """ "in progress" builds should not be shown """
- Build.objects.create(**self.project1_build_success)
- Build.objects.create(**self.project1_build_success)
-
- # shouldn't see this one
- Build.objects.create(**self.project1_build_in_progress)
-
- # shouldn't see these two either, as they belong to a different project
- Build.objects.create(**self.project2_build_success)
- Build.objects.create(**self.project2_build_in_progress)
-
- build_rows = self._get_rows_for_project(self.project1.id)
- self.assertEqual(len(build_rows), 2)
-
- def test_tasks_in_projectbuilds(self):
- """ Task should be shown as suffix on build name """
- build = Build.objects.create(**self.project1_build_success)
- Target.objects.create(build=build, target='bash', task='clean')
- url = reverse("projectbuilds", args=(self.project1.id,))
- response = self.client.get(url, follow=True)
- result = re.findall('^ +bash:clean$', response.content, re.MULTILINE)
- self.assertEqual(len(result), 2)
-
- def test_cli_builds_hides_tabs(self):
- """
- Display for command line builds should hide tabs;
- note that the latest builds section is already tested in
- AllBuildsPageTests, as the template is the same
- """
- url = reverse("projectbuilds", args=(self.default_project.id,))
- response = self.client.get(url, follow=True)
- soup = BeautifulSoup(response.content)
- tabs = soup.select('#project-topbar')
- self.assertEqual(len(tabs), 0,
- 'should be no top bar shown for command line builds')
-
- def test_non_cli_builds_has_tabs(self):
- """
- Non-command-line builds projects should show the tabs
- """
- url = reverse("projectbuilds", args=(self.project1.id,))
- response = self.client.get(url, follow=True)
- soup = BeautifulSoup(response.content)
- tabs = soup.select('#project-topbar')
- self.assertEqual(len(tabs), 1,
- 'should be a top bar shown for non-command-line builds')
-
-class AllBuildsPageTests(TestCase):
- """ Tests for all builds page /builds/ """
+ self.assertTrue(something_found,
+ "We went through the whole alphabet and nothing"
+ " was found for the search of table %s" % name)
- def setUp(self):
- bbv = BitbakeVersion.objects.create(name="bbv1", giturl="/tmp/",
- branch="master", dirpath="")
- release = Release.objects.create(name="release1",
- bitbake_version=bbv)
- self.project1 = Project.objects.create_project(name=PROJECT_NAME,
- release=release)
- self.default_project = Project.objects.create_project(
- name=CLI_BUILDS_PROJECT_NAME,
- release=release
- )
- self.default_project.is_default = True
- self.default_project.save()
-
- # parameters for builds to associate with the projects
- now = timezone.now()
-
- self.project1_build_success = {
- "project": self.project1,
- "started_on": now,
- "completed_on": now,
- "outcome": Build.SUCCEEDED
- }
-
- self.default_project_build_success = {
- "project": self.default_project,
- "started_on": now,
- "completed_on": now,
- "outcome": Build.SUCCEEDED
- }
-
- def test_show_tasks_in_allbuilds(self):
- """ Task should be shown as suffix on build name """
- build = Build.objects.create(**self.project1_build_success)
- Target.objects.create(build=build, target='bash', task='clean')
- url = reverse('all-builds')
- response = self.client.get(url, follow=True)
- result = re.findall('bash:clean', response.content, re.MULTILINE)
- self.assertEqual(len(result), 3)
-
- def test_no_run_again_for_cli_build(self):
- """ "Run again" button should not be shown for command-line builds """
- build = Build.objects.create(**self.default_project_build_success)
- url = reverse('all-builds')
- response = self.client.get(url, follow=True)
- soup = BeautifulSoup(response.content)
-
- attrs = {'data-latest-build-result': build.id}
- result = soup.find('div', attrs=attrs)
-
- # shouldn't see a run again button for command-line builds
- run_again_button = result.select('button')
- self.assertEqual(len(run_again_button), 0)
-
- # should see a help icon for command-line builds
- help_icon = result.select('i.get-help-green')
- self.assertEqual(len(help_icon), 1)
-
- def test_tooltips_on_project_name(self):
- """
- A tooltip should be present next to the command line
- builds project name in the all builds page, but not for
- other projects
- """
- build1 = Build.objects.create(**self.project1_build_success)
- default_build = Build.objects.create(**self.default_project_build_success)
-
- url = reverse('all-builds')
- response = self.client.get(url, follow=True)
- soup = BeautifulSoup(response.content)
-
- # no help icon on non-default project name
- result = soup.find('tr', attrs={'data-table-build-result': build1.id})
- name = result.select('td.project-name')[0]
- icons = name.select('i.get-help')
- self.assertEqual(len(icons), 0,
- 'should not be a help icon for non-cli builds name')
-
- # help icon on default project name
- result = soup.find('tr', attrs={'data-table-build-result': default_build.id})
- name = result.select('td.project-name')[0]
- icons = name.select('i.get-help')
- self.assertEqual(len(icons), 1,
- 'should be a help icon for cli builds name')
-
-class ProjectPageTests(TestCase):
- """ Test project data at /project/X/ is displayed correctly """
- CLI_BUILDS_PROJECT_NAME = 'Command line builds'
-
- def test_command_line_builds_in_progress(self):
- """
- In progress builds should not cause an error to be thrown
- when navigating to "command line builds" project page;
- see https://bugzilla.yoctoproject.org/show_bug.cgi?id=8277
- """
-
- # add the "command line builds" default project; this mirrors what
- # we do in migration 0026_set_default_project.py
- default_project = Project.objects.create_project(self.CLI_BUILDS_PROJECT_NAME, None)
- default_project.is_default = True
- default_project.save()
-
- # add an "in progress" build for the default project
- now = timezone.now()
- build = Build.objects.create(project=default_project,
- started_on=now,
- completed_on=now,
- outcome=Build.IN_PROGRESS)
-
- # navigate to the project page for the default project
- url = reverse("project", args=(default_project.id,))
- response = self.client.get(url, follow=True)
+ # Test the limit functionality on the table
+ limited_data = get_data(table_cls(), {'limit' : "1"})
+ self.assertEqual(len(limited_data['rows']),
+ 1,
+ "Limit 1 set on table %s but not 1 row returned"
+ % name)
- self.assertEqual(response.status_code, 200)
+ # Test the pagination functionality on the table
+ page_one_data = get_data(table_cls(), {'limit' : "1",
+ "page": "1"})['rows'][0]
-class BuildDashboardTests(TestCase):
- """ Tests for the build dashboard /build/X """
+ page_two_data = get_data(table_cls(), {'limit' : "1",
+ "page": "2"})['rows'][0]
- def setUp(self):
- bbv = BitbakeVersion.objects.create(name="bbv1", giturl="/tmp/",
- branch="master", dirpath="")
- release = Release.objects.create(name="release1",
- bitbake_version=bbv)
- project = Project.objects.create_project(name=PROJECT_NAME,
- release=release)
-
- now = timezone.now()
-
- self.build1 = Build.objects.create(project=project,
- started_on=now,
- completed_on=now)
-
- # exception
- msg1 = 'an exception was thrown'
- self.exception_message = LogMessage.objects.create(
- build=self.build1,
- level=LogMessage.EXCEPTION,
- message=msg1
- )
-
- # critical
- msg2 = 'a critical error occurred'
- self.critical_message = LogMessage.objects.create(
- build=self.build1,
- level=LogMessage.CRITICAL,
- message=msg2
- )
-
- def _get_build_dashboard_errors(self):
- """
- Get a list of HTML fragments representing the errors on the
- build dashboard
- """
- url = reverse('builddashboard', args=(self.build1.id,))
- response = self.client.get(url)
- soup = BeautifulSoup(response.content)
- return soup.select('#errors div.alert-error')
-
- def _check_for_log_message(self, log_message):
- """
- Check whether the LogMessage instance <log_message> is
- represented as an HTML error in the build dashboard page
- """
- errors = self._get_build_dashboard_errors()
- self.assertEqual(len(errors), 2)
-
- expected_text = log_message.message
- expected_id = str(log_message.id)
-
- found = False
- for error in errors:
- error_text = error.find('pre').text
- text_matches = (error_text == expected_text)
-
- error_id = error['data-error']
- id_matches = (error_id == expected_id)
-
- if text_matches and id_matches:
- found = True
- break
-
- template_vars = (expected_text, error_text,
- expected_id, error_id)
- assertion_error_msg = 'exception not found as error: ' \
- 'expected text "%s" and got "%s"; ' \
- 'expected ID %s and got %s' % template_vars
- self.assertTrue(found, assertion_error_msg)
-
- def test_exceptions_show_as_errors(self):
- """
- LogMessages with level EXCEPTION should display in the errors
- section of the page
- """
- self._check_for_log_message(self.exception_message)
-
- def test_criticals_show_as_errors(self):
- """
- LogMessages with level CRITICAL should display in the errors
- section of the page
- """
- self._check_for_log_message(self.critical_message)
+ self.assertNotEqual(page_one_data,
+ page_two_data,
+ "Changed page on table %s but first row is the "
+ "same as the previous page" % name)
OpenPOWER on IntegriCloud