new file mode 100644
@@ -0,0 +1,88 @@
+#! /usr/bin/env python3
+#
+# BitBake Toaster Implementation
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+"""Project pages with and without build mode (TOASTER_BUILDSERVER)."""
+
+import os
+from unittest import mock
+
+from django.test import TestCase
+from django.urls import reverse
+from django.utils import timezone
+
+from orm.models import Project, Build, Variable, Layer, Layer_Version, Recipe
+
+
+class ProjectPagesBase(TestCase):
+
+ def setUp(self):
+ self.project = Project.objects.create(name="test project")
+ self.builds_url = reverse('projectbuilds', args=(self.project.id,))
+ self.config_url = reverse('project', args=(self.project.id,))
+
+ # a started build of an image recipe
+ now = timezone.now()
+ self.build = Build.objects.create(project=self.project, started_on=now,
+ completed_on=now,
+ outcome=Build.SUCCEEDED)
+ Variable.objects.create(build=self.build, variable_name='MACHINE',
+ variable_value='qemux86-64')
+ layer_version = Layer_Version.objects.create(
+ build=self.build, layer=Layer.objects.create(name='meta-test'))
+ Recipe.objects.create(name='core-image-test', is_image=True,
+ layer_version=layer_version)
+ self.build_url = reverse('builddashboard', args=(self.build.id,))
+
+ def projects_table_names(self):
+ response = self.client.get(reverse('all-projects'), {'format': 'json'})
+ self.assertEqual(response.status_code, 200)
+ return [row['name'] for row in response.json()['rows']]
+
+
+@mock.patch('toastergui.views.project_enable', True)
+@mock.patch.dict(os.environ, {'TOASTER_BUILDSERVER': '1'})
+class BuildModeTests(ProjectPagesBase):
+
+ def test_projects_table_links_to_configuration(self):
+ names = self.projects_table_names()
+ self.assertTrue(any(self.config_url + '"' in n for n in names), names)
+
+ def test_build_page_offers_new_custom_image(self):
+ response = self.client.get(self.build_url)
+ self.assertContains(response, 'id="menu-new-custom-image"')
+
+
+@mock.patch('toastergui.views.project_enable', False)
+@mock.patch.dict(os.environ, {'TOASTER_BUILDSERVER': '0'})
+class NoBuildModeTests(ProjectPagesBase):
+
+ def test_projects_tab_shown(self):
+ response = self.client.get(reverse('all-builds'))
+ self.assertContains(response, 'id="navbar-all-projects"')
+
+ def test_projects_table_links_to_builds(self):
+ names = self.projects_table_names()
+ self.assertTrue(any(self.builds_url + '"' in n for n in names), names)
+ self.assertFalse(any(self.config_url + '"' in n for n in names), names)
+
+ def test_configuration_pages_redirect_to_builds(self):
+ for url in (self.config_url,
+ reverse('projectconf', args=(self.project.id,))):
+ response = self.client.get(url)
+ self.assertRedirects(response, self.builds_url,
+ fetch_redirect_response=False)
+
+ def test_builds_page_has_no_project_topbar(self):
+ response = self.client.get(self.builds_url)
+ self.assertEqual(response.status_code, 200)
+ self.assertNotContains(response, 'id="project-topbar"')
+
+ def test_build_page_has_no_custom_image_actions(self):
+ response = self.client.get(self.build_url)
+ self.assertContains(response, 'id="menu-download-build-log"')
+ self.assertNotContains(response, 'id="menu-new-custom-image"')
+ self.assertNotContains(response, 'id="menu-edit-custom-image"')
@@ -110,7 +110,6 @@
All builds
</a>
</li>
- {% if project_enable %}
<li id="navbar-all-projects"
{% if request.resolver_match.url_name == 'all-projects' %}
class="active"
@@ -120,7 +119,6 @@
All projects
</a>
</li>
- {% endif %}
{% endif %}
<li id="navbar-docs">
<a target="_blank" href="http://docs.yoctoproject.org/toaster-manual/index.html#toaster-user-manual">
@@ -148,6 +148,8 @@
</a>
</li>
+ <!-- custom images can only be built in build mode -->
+ {% if project_enable %}
{% with build.get_custom_image_recipes as custom_image_recipes %}
{% if custom_image_recipes.count > 0 %}
<!-- edit custom image built during this build -->
@@ -215,6 +217,7 @@
});
</script>
{% endif %}
+ {% endif %}
<li id="menu-delete-build">
<a href="#delete-build-modal" id="delete-build" data-toggle="modal" data-target="#delete-build-modal" class="text-danger">
@@ -41,7 +41,7 @@
</form>
</div>
- {% if not project.is_default %}
+ {% if project_enable and not project.is_default %}
<div id="project-topbar">
<ul class="nav nav-tabs">
<li id="topbar-configuration-tab">
@@ -2,6 +2,8 @@
# SPDX-License-Identifier: GPL-2.0-only
#
+import os
+
from django import template
from django.urls import reverse
@@ -10,8 +12,9 @@ register = template.Library()
def project_url(parser, token):
"""
Create a URL for a project's main page;
- for non-default projects, this is the configuration page;
- for the default project, this is the project builds page
+ for non-default projects in build mode, this is the configuration page;
+ for the default project, or without build mode, this is the project
+ builds page
"""
try:
tag_name, project = token.split_contents()
@@ -28,7 +31,8 @@ class ProjectUrlNode(template.Node):
def render(self, context):
try:
project = self.project.resolve(context)
- if project.is_default:
+ build_mode = os.environ.get('TOASTER_BUILDSERVER') == '1'
+ if project.is_default or not build_mode:
return reverse('projectbuilds', args=(project.id,))
else:
return reverse('project', args=(project.id,))
@@ -1522,6 +1522,10 @@ if True:
# Shows the edit project page
def project(request, pid):
+ # without build mode there is nothing to configure
+ if not project_enable:
+ return redirect('projectbuilds', pid=pid)
+
project = Project.objects.get(pk=pid)
if '1' == os.environ.get('TOASTER_PROJECTSPECIFIC'):
@@ -1815,6 +1819,8 @@ if True:
return(vars_managed,sorted(vars_fstypes),vars_disallowed)
def projectconf(request, pid):
+ if not project_enable:
+ return redirect('projectbuilds', pid=pid)
try:
prj = Project.objects.get(id = pid)