summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Purdie <richard.purdie@linuxfoundation.org>2024-10-18 22:37:27 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2024-10-24 11:24:03 +0100
commitbac01b075611065a087aeb6efe1f85ee751737d8 (patch)
tree9317ee2918ded19d99356a17dcae9c660889ed8e
parentd0c0c00f6cc693a70ba4cb1e0834a1f75a7fef75 (diff)
downloadpoky-bac01b075611065a087aeb6efe1f85ee751737d8.tar.gz
bitbake: toaster/tests/functiona/project_page_tab_config: Switch to using library create_project function
Switch this test module to use the common project creation code which contains race fixes. That code requires the database access wrapper be dropped and we no longer have ordering constraints. There is one test that does require database access. Move this to a separate class and allow database access there. Use ordering constraints to allow them to run after the main code. They depend on the project creation from the other class which isn't ideal but good enough for now. (Bitbake rev: fa10ba2a8749415d8f06cfc15c228c6eb7df1bcf) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py213
1 files changed, 90 insertions, 123 deletions
diff --git a/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py b/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py
index 5da9706b29..daf00d8f1d 100644
--- a/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py
+++ b/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py
@@ -7,7 +7,6 @@
7# 7#
8 8
9import string 9import string
10import random
11import pytest 10import pytest
12from django.urls import reverse 11from django.urls import reverse
13from selenium.webdriver import Keys 12from selenium.webdriver import Keys
@@ -19,60 +18,16 @@ from selenium.webdriver.common.by import By
19 18
20from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled 19from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled
21 20
22 21class TestProjectConfigTabBase(SeleniumFunctionalTestCase):
23@pytest.mark.django_db
24@pytest.mark.order("last")
25class TestProjectConfigTab(SeleniumFunctionalTestCase):
26 PROJECT_NAME = 'TestProjectConfigTab' 22 PROJECT_NAME = 'TestProjectConfigTab'
27 project_id = None 23 project_id = None
28 24
29 def _create_project(self, project_name, **kwargs):
30 """ Create/Test new project using:
31 - Project Name: Any string
32 - Release: Any string
33 - Merge Toaster settings: True or False
34 """
35 release = kwargs.get('release', '3')
36 self.get(reverse('newproject'))
37 self.wait_until_visible('#new-project-name')
38 self.find("#new-project-name").send_keys(project_name)
39 select = Select(self.find("#projectversion"))
40 select.select_by_value(release)
41
42 # check merge toaster settings
43 checkbox = self.find('.checkbox-mergeattr')
44 if not checkbox.is_selected():
45 checkbox.click()
46
47 if self.PROJECT_NAME != 'TestProjectConfigTab':
48 # Reset project name if it's not the default one
49 self.PROJECT_NAME = 'TestProjectConfigTab'
50
51 self.find("#create-project-button").click()
52
53 try:
54 self.wait_until_visible('#hint-error-project-name', poll=3)
55 url = reverse('project', args=(TestProjectConfigTab.project_id, ))
56 self.get(url)
57 self.wait_until_visible('#config-nav', poll=3)
58 except TimeoutException:
59 self.wait_until_visible('#config-nav', poll=3)
60
61 def _random_string(self, length):
62 return ''.join(
63 random.choice(string.ascii_letters) for _ in range(length)
64 )
65
66 def _navigate_to_project_page(self): 25 def _navigate_to_project_page(self):
67 # Navigate to project page 26 # Navigate to project page
68 if TestProjectConfigTab.project_id is None: 27 if TestProjectConfigTabBase.project_id is None:
69 self._create_project(project_name=self._random_string(10)) 28 TestProjectConfigTabBase.project_id = self.create_new_project(self.PROJECT_NAME, '3', None, True)
70 current_url = self.driver.current_url 29 url = reverse('project', args=(TestProjectConfigTabBase.project_id,))
71 TestProjectConfigTab.project_id = get_projectId_from_url( 30 self.get(url)
72 current_url)
73 else:
74 url = reverse('project', args=(TestProjectConfigTab.project_id,))
75 self.get(url)
76 self.wait_until_visible('#config-nav') 31 self.wait_until_visible('#config-nav')
77 32
78 def _create_builds(self): 33 def _create_builds(self):
@@ -114,6 +69,8 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
114 config_nav = self.find('#config-nav') 69 config_nav = self.find('#config-nav')
115 return config_nav.find_elements(By.TAG_NAME, 'li')[index] 70 return config_nav.find_elements(By.TAG_NAME, 'li')[index]
116 71
72class TestProjectConfigTab(TestProjectConfigTabBase):
73
117 def test_project_config_nav(self): 74 def test_project_config_nav(self):
118 """ Test project config tab navigation: 75 """ Test project config tab navigation:
119 - Check if the menu is displayed and contains the right elements: 76 - Check if the menu is displayed and contains the right elements:
@@ -160,26 +117,26 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
160 conf_nav_list = [ 117 conf_nav_list = [
161 # config 118 # config
162 [0, 'Configuration', 119 [0, 'Configuration',
163 f"/toastergui/project/{TestProjectConfigTab.project_id}"], 120 f"/toastergui/project/{TestProjectConfigTabBase.project_id}"],
164 # custom images 121 # custom images
165 [2, 'Custom images', 122 [2, 'Custom images',
166 f"/toastergui/project/{TestProjectConfigTab.project_id}/customimages"], 123 f"/toastergui/project/{TestProjectConfigTabBase.project_id}/customimages"],
167 # image recipes 124 # image recipes
168 [3, 'Image recipes', 125 [3, 'Image recipes',
169 f"/toastergui/project/{TestProjectConfigTab.project_id}/images"], 126 f"/toastergui/project/{TestProjectConfigTabBase.project_id}/images"],
170 # software recipes 127 # software recipes
171 [4, 'Software recipes', 128 [4, 'Software recipes',
172 f"/toastergui/project/{TestProjectConfigTab.project_id}/softwarerecipes"], 129 f"/toastergui/project/{TestProjectConfigTabBase.project_id}/softwarerecipes"],
173 # machines 130 # machines
174 [5, 'Machines', 131 [5, 'Machines',
175 f"/toastergui/project/{TestProjectConfigTab.project_id}/machines"], 132 f"/toastergui/project/{TestProjectConfigTabBase.project_id}/machines"],
176 # layers 133 # layers
177 [6, 'Layers', 134 [6, 'Layers',
178 f"/toastergui/project/{TestProjectConfigTab.project_id}/layers"], 135 f"/toastergui/project/{TestProjectConfigTabBase.project_id}/layers"],
179 # distro 136 # distro
180 [7, 'Distros', 137 [7, 'Distros',
181 f"/toastergui/project/{TestProjectConfigTab.project_id}/distros"], 138 f"/toastergui/project/{TestProjectConfigTabBase.project_id}/distros"],
182 # [9, 'BitBake variables', f"/toastergui/project/{TestProjectConfigTab.project_id}/configuration"], # bitbake variables 139 # [9, 'BitBake variables', f"/toastergui/project/{TestProjectConfigTabBase.project_id}/configuration"], # bitbake variables
183 ] 140 ]
184 for index, item_name, url in conf_nav_list: 141 for index, item_name, url in conf_nav_list:
185 item = _get_config_nav_item(index) 142 item = _get_config_nav_item(index)
@@ -299,9 +256,11 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
299 - meta-poky 256 - meta-poky
300 - meta-yocto-bsp 257 - meta-yocto-bsp
301 """ 258 """
302 # Create a new project for this test 259 project_id = self.create_new_project(self.PROJECT_NAME + "-ST", '3', None, True)
303 project_name = self._random_string(10) 260 url = reverse('project', args=(project_id,))
304 self._create_project(project_name=project_name) 261 self.get(url)
262 self.wait_until_visible('#config-nav')
263
305 # check if the menu is displayed 264 # check if the menu is displayed
306 self.wait_until_visible('#project-page') 265 self.wait_until_visible('#project-page')
307 block_l = self.driver.find_element( 266 block_l = self.driver.find_element(
@@ -374,61 +333,6 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
374 layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li') 333 layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li')
375 self.assertEqual(len(layers_list_items), 4) 334 self.assertEqual(len(layers_list_items), 4)
376 335
377 def test_most_build_recipes(self):
378 """ Test most build recipes block contains"""
379 def rebuild_from_most_build_recipes(recipe_list_items):
380 checkbox = recipe_list_items[0].find_element(By.TAG_NAME, 'input')
381 checkbox.click()
382 build_btn = self.find('#freq-build-btn')
383 build_btn.click()
384 self.wait_until_visible('#latest-builds')
385 wait_until_build(self, 'queued cloning starting parsing failed')
386 lastest_builds = self.driver.find_elements(
387 By.XPATH,
388 '//div[@id="latest-builds"]/div'
389 )
390 self.assertTrue(len(lastest_builds) >= 2)
391 last_build = lastest_builds[0]
392 try:
393 cancel_button = last_build.find_element(
394 By.XPATH,
395 '//span[@class="cancel-build-btn pull-right alert-link"]',
396 )
397 cancel_button.click()
398 except NoSuchElementException:
399 # Skip if the build is already cancelled
400 pass
401 wait_until_build_cancelled(self)
402 # Create a new project for remaining asserts
403 project_name = self._random_string(10)
404 self._create_project(project_name=project_name, release='2')
405 current_url = self.driver.current_url
406 TestProjectConfigTab.project_id = get_projectId_from_url(current_url)
407 url = current_url.split('?')[0]
408
409 # Create a new builds
410 self._create_builds()
411
412 # back to project page
413 self.driver.get(url)
414
415 self.wait_until_visible('#project-page', poll=3)
416
417 # Most built recipes
418 most_built_recipes = self.driver.find_element(
419 By.XPATH, '//*[@id="project-page"]/div[1]/div[3]')
420 title = most_built_recipes.find_element(By.TAG_NAME, 'h3')
421 self.assertIn("Most built recipes", title.text)
422 # check can select a recipe and build it
423 self.wait_until_visible('#freq-build-list', poll=3)
424 recipe_list = self.find('#freq-build-list')
425 recipe_list_items = recipe_list.find_elements(By.TAG_NAME, 'li')
426 self.assertTrue(
427 len(recipe_list_items) > 0,
428 msg="No recipes found in the most built recipes list",
429 )
430 rebuild_from_most_build_recipes(recipe_list_items)
431 TestProjectConfigTab.project_id = None # reset project id
432 336
433 def test_project_page_tab_importlayer(self): 337 def test_project_page_tab_importlayer(self):
434 """ Test project page tab import layer """ 338 """ Test project page tab import layer """
@@ -472,10 +376,11 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
472 376
473 def test_project_page_custom_image_no_image(self): 377 def test_project_page_custom_image_no_image(self):
474 """ Test project page tab "New custom image" when no custom image """ 378 """ Test project page tab "New custom image" when no custom image """
475 project_name = self._random_string(10) 379 project_id = self.create_new_project(self.PROJECT_NAME + "-CustomImage", '3', None, True)
476 self._create_project(project_name=project_name) 380 url = reverse('project', args=(project_id,))
477 current_url = self.driver.current_url 381 self.get(url)
478 TestProjectConfigTab.project_id = get_projectId_from_url(current_url) 382 self.wait_until_visible('#config-nav')
383
479 # navigate to "Custom image" tab 384 # navigate to "Custom image" tab
480 custom_image_section = self._get_config_nav_item(2) 385 custom_image_section = self._get_config_nav_item(2)
481 custom_image_section.click() 386 custom_image_section.click()
@@ -490,9 +395,9 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
490 div_empty_msg = self.find('#empty-state-customimagestable') 395 div_empty_msg = self.find('#empty-state-customimagestable')
491 link_create_custom_image = div_empty_msg.find_element( 396 link_create_custom_image = div_empty_msg.find_element(
492 By.TAG_NAME, 'a') 397 By.TAG_NAME, 'a')
493 self.assertTrue(TestProjectConfigTab.project_id is not None) 398 self.assertTrue(TestProjectConfigTabBase.project_id is not None)
494 self.assertIn( 399 self.assertIn(
495 f"/toastergui/project/{TestProjectConfigTab.project_id}/newcustomimage", str( 400 f"/toastergui/project/{project_id}/newcustomimage", str(
496 link_create_custom_image.get_attribute('href') 401 link_create_custom_image.get_attribute('href')
497 ) 402 )
498 ) 403 )
@@ -501,7 +406,6 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
501 link_create_custom_image.text 406 link_create_custom_image.text
502 ) 407 )
503 ) 408 )
504 TestProjectConfigTab.project_id = None # reset project id
505 409
506 def test_project_page_image_recipe(self): 410 def test_project_page_image_recipe(self):
507 """ Test project page section images 411 """ Test project page section images
@@ -526,3 +430,66 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
526 self.wait_until_visible('#imagerecipestable tbody tr') 430 self.wait_until_visible('#imagerecipestable tbody tr')
527 rows = self.find_all('#imagerecipestable tbody tr') 431 rows = self.find_all('#imagerecipestable tbody tr')
528 self.assertTrue(len(rows) > 0) 432 self.assertTrue(len(rows) > 0)
433
434@pytest.mark.django_db
435@pytest.mark.order("last")
436class TestProjectConfigTabDB(TestProjectConfigTabBase):
437
438 def test_most_build_recipes(self):
439 """ Test most build recipes block contains"""
440 def rebuild_from_most_build_recipes(recipe_list_items):
441 checkbox = recipe_list_items[0].find_element(By.TAG_NAME, 'input')
442 checkbox.click()
443 build_btn = self.find('#freq-build-btn')
444 build_btn.click()
445 self.wait_until_visible('#latest-builds')
446 wait_until_build(self, 'queued cloning starting parsing failed')
447 lastest_builds = self.driver.find_elements(
448 By.XPATH,
449 '//div[@id="latest-builds"]/div'
450 )
451 self.assertTrue(len(lastest_builds) >= 2)
452 last_build = lastest_builds[0]
453 try:
454 cancel_button = last_build.find_element(
455 By.XPATH,
456 '//span[@class="cancel-build-btn pull-right alert-link"]',
457 )
458 cancel_button.click()
459 except NoSuchElementException:
460 # Skip if the build is already cancelled
461 pass
462 wait_until_build_cancelled(self)
463
464 # Create a new project for remaining asserts
465 project_id = self.create_new_project(self.PROJECT_NAME + "-MostBuilt", '2', None, True)
466 url = reverse('project', args=(project_id,))
467 self.get(url)
468 self.wait_until_visible('#config-nav')
469
470 current_url = self.driver.current_url
471 url = current_url.split('?')[0]
472
473 # Create a new builds
474 self._create_builds()
475
476 # back to project page
477 self.driver.get(url)
478
479 self.wait_until_visible('#project-page', poll=3)
480
481 # Most built recipes
482 most_built_recipes = self.driver.find_element(
483 By.XPATH, '//*[@id="project-page"]/div[1]/div[3]')
484 title = most_built_recipes.find_element(By.TAG_NAME, 'h3')
485 self.assertIn("Most built recipes", title.text)
486 # check can select a recipe and build it
487 self.wait_until_visible('#freq-build-list', poll=3)
488 recipe_list = self.find('#freq-build-list')
489 recipe_list_items = recipe_list.find_elements(By.TAG_NAME, 'li')
490 self.assertTrue(
491 len(recipe_list_items) > 0,
492 msg="No recipes found in the most built recipes list",
493 )
494 rebuild_from_most_build_recipes(recipe_list_items)
495