summaryrefslogtreecommitdiffstats
path: root/bitbake/lib
diff options
context:
space:
mode:
authorAlassane Yattara <alassane.yattara@savoirfairelinux.com>2023-12-14 23:11:32 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2023-12-15 14:37:28 +0000
commit81a0110ca532fb4f60b3ea4cd6a977e9c967ac62 (patch)
tree306c4128b720ab8b0dc06ada4384109f87f71196 /bitbake/lib
parentc8382b35e843591a34f867c5eb34a24cc17634e2 (diff)
downloadpoky-81a0110ca532fb4f60b3ea4cd6a977e9c967ac62.tar.gz
bitbake: toaster/tests: Bug-Fix testcase functional/test_project_page_tab_config.py
All issues and failures stemmed from a specific test case: test_project_config_tab_right_section in the file bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py. This test was designed to verify whether the "Most built recipes" section on the project page correctly displays the latest and oldest recipes built by the user, irrespective of the build outcome (failed, cancelled, succeeded, or errored). The errors and failures arose because the build process did not terminate as expected, particularly when attempting to build recipe images such as "core-image-minimal" or "bash." It was discovered that building a real recipe/image was unnecessary for the test's purpose. Instead, building a fake recipe like "foo" provided a reliable way to ensure the build would fail or be interrupted. (Bitbake rev: 5162db5305826235c09d9fcd38b5fb48ded31622) Signed-off-by: Alassane Yattara <alassane.yattara@savoirfairelinux.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib')
-rw-r--r--bitbake/lib/toaster/tests/functional/test_project_page.py2
-rw-r--r--bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py186
2 files changed, 95 insertions, 93 deletions
diff --git a/bitbake/lib/toaster/tests/functional/test_project_page.py b/bitbake/lib/toaster/tests/functional/test_project_page.py
index 077badb0c2..82dca442f9 100644
--- a/bitbake/lib/toaster/tests/functional/test_project_page.py
+++ b/bitbake/lib/toaster/tests/functional/test_project_page.py
@@ -461,7 +461,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
461 '//td[@class="add-del-layers"]//a[1]' 461 '//td[@class="add-del-layers"]//a[1]'
462 ) 462 )
463 build_btn.click() 463 build_btn.click()
464 build_state = wait_until_build(self, 'parsing starting cloning queued') 464 build_state = wait_until_build(self, 'queued cloning starting parsing failed')
465 lastest_builds = self.driver.find_elements( 465 lastest_builds = self.driver.find_elements(
466 By.XPATH, 466 By.XPATH,
467 '//div[@id="latest-builds"]/div' 467 '//div[@id="latest-builds"]/div'
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 d911ff00d4..4dbf5aeba4 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
@@ -12,7 +12,7 @@ import pytest
12from django.urls import reverse 12from django.urls import reverse
13from selenium.webdriver import Keys 13from selenium.webdriver import Keys
14from selenium.webdriver.support.select import Select 14from selenium.webdriver.support.select import Select
15from selenium.common.exceptions import TimeoutException 15from selenium.common.exceptions import NoSuchElementException, TimeoutException
16from orm.models import Project 16from orm.models import Project
17from tests.functional.functional_helpers import SeleniumFunctionalTestCase 17from tests.functional.functional_helpers import SeleniumFunctionalTestCase
18from selenium.webdriver.common.by import By 18from selenium.webdriver.common.by import By
@@ -26,17 +26,18 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
26 PROJECT_NAME = 'TestProjectConfigTab' 26 PROJECT_NAME = 'TestProjectConfigTab'
27 project_id = None 27 project_id = None
28 28
29 def _create_project(self, project_name): 29 def _create_project(self, project_name, **kwargs):
30 """ Create/Test new project using: 30 """ Create/Test new project using:
31 - Project Name: Any string 31 - Project Name: Any string
32 - Release: Any string 32 - Release: Any string
33 - Merge Toaster settings: True or False 33 - Merge Toaster settings: True or False
34 """ 34 """
35 release = kwargs.get('release', '3')
35 self.get(reverse('newproject')) 36 self.get(reverse('newproject'))
36 self.wait_until_visible('#new-project-name') 37 self.wait_until_visible('#new-project-name')
37 self.find("#new-project-name").send_keys(project_name) 38 self.find("#new-project-name").send_keys(project_name)
38 select = Select(self.find("#projectversion")) 39 select = Select(self.find("#projectversion"))
39 select.select_by_value('3') 40 select.select_by_value(release)
40 41
41 # check merge toaster settings 42 # check merge toaster settings
42 checkbox = self.find('.checkbox-mergeattr') 43 checkbox = self.find('.checkbox-mergeattr')
@@ -50,7 +51,7 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
50 self.find("#create-project-button").click() 51 self.find("#create-project-button").click()
51 52
52 try: 53 try:
53 self.wait_until_visible('#hint-error-project-name') 54 self.wait_until_visible('#hint-error-project-name', poll=3)
54 url = reverse('project', args=(TestProjectConfigTab.project_id, )) 55 url = reverse('project', args=(TestProjectConfigTab.project_id, ))
55 self.get(url) 56 self.get(url)
56 self.wait_until_visible('#config-nav', poll=3) 57 self.wait_until_visible('#config-nav', poll=3)
@@ -67,7 +68,8 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
67 if TestProjectConfigTab.project_id is None: 68 if TestProjectConfigTab.project_id is None:
68 self._create_project(project_name=self._random_string(10)) 69 self._create_project(project_name=self._random_string(10))
69 current_url = self.driver.current_url 70 current_url = self.driver.current_url
70 TestProjectConfigTab.project_id = get_projectId_from_url(current_url) 71 TestProjectConfigTab.project_id = get_projectId_from_url(
72 current_url)
71 else: 73 else:
72 url = reverse('project', args=(TestProjectConfigTab.project_id,)) 74 url = reverse('project', args=(TestProjectConfigTab.project_id,))
73 self.get(url) 75 self.get(url)
@@ -76,27 +78,30 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
76 def _create_builds(self): 78 def _create_builds(self):
77 # check search box can be use to build recipes 79 # check search box can be use to build recipes
78 search_box = self.find('#build-input') 80 search_box = self.find('#build-input')
79 search_box.send_keys('core-image-minimal') 81 search_box.send_keys('foo')
80 self.find('#build-button').click() 82 self.find('#build-button').click()
81 self.wait_until_visible('#latest-builds') 83 self.wait_until_present('#latest-builds')
82 # loop until reach the parsing state 84 # loop until reach the parsing state
83 build_state = wait_until_build(self, 'parsing starting cloning') 85 wait_until_build(self, 'queued cloning starting parsing failed')
84 lastest_builds = self.driver.find_elements( 86 lastest_builds = self.driver.find_elements(
85 By.XPATH, 87 By.XPATH,
86 '//div[@id="latest-builds"]/div', 88 '//div[@id="latest-builds"]/div',
87 ) 89 )
88 last_build = lastest_builds[0] 90 last_build = lastest_builds[0]
89 self.assertTrue( 91 self.assertTrue(
90 'core-image-minimal' in str(last_build.text) 92 'foo' in str(last_build.text)
91 ) 93 )
92 cancel_button = last_build.find_element( 94 last_build = lastest_builds[0]
93 By.XPATH, 95 try:
94 '//span[@class="cancel-build-btn pull-right alert-link"]', 96 cancel_button = last_build.find_element(
95 ) 97 By.XPATH,
96 cancel_button.click() 98 '//span[@class="cancel-build-btn pull-right alert-link"]',
97 if 'starting' not in build_state: # change build state when cancelled in starting state 99 )
98 wait_until_build_cancelled(self) 100 cancel_button.click()
99 return build_state 101 except NoSuchElementException:
102 # Skip if the build is already cancelled
103 pass
104 wait_until_build_cancelled(self)
100 105
101 def _get_tabs(self): 106 def _get_tabs(self):
102 # tabs links list 107 # tabs links list
@@ -126,6 +131,7 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
126 - Delete project 131 - Delete project
127 """ 132 """
128 self._navigate_to_project_page() 133 self._navigate_to_project_page()
134
129 def _get_config_nav_item(index): 135 def _get_config_nav_item(index):
130 config_nav = self.find('#config-nav') 136 config_nav = self.find('#config-nav')
131 return config_nav.find_elements(By.TAG_NAME, 'li')[index] 137 return config_nav.find_elements(By.TAG_NAME, 'li')[index]
@@ -152,13 +158,27 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
152 self.assertTrue("actions" in str(actions.text).lower()) 158 self.assertTrue("actions" in str(actions.text).lower())
153 159
154 conf_nav_list = [ 160 conf_nav_list = [
155 [0, 'Configuration', f"/toastergui/project/{TestProjectConfigTab.project_id}"], # config 161 # config
156 [2, 'Custom images', f"/toastergui/project/{TestProjectConfigTab.project_id}/customimages"], # custom images 162 [0, 'Configuration',
157 [3, 'Image recipes', f"/toastergui/project/{TestProjectConfigTab.project_id}/images"], # image recipes 163 f"/toastergui/project/{TestProjectConfigTab.project_id}"],
158 [4, 'Software recipes', f"/toastergui/project/{TestProjectConfigTab.project_id}/softwarerecipes"], # software recipes 164 # custom images
159 [5, 'Machines', f"/toastergui/project/{TestProjectConfigTab.project_id}/machines"], # machines 165 [2, 'Custom images',
160 [6, 'Layers', f"/toastergui/project/{TestProjectConfigTab.project_id}/layers"], # layers 166 f"/toastergui/project/{TestProjectConfigTab.project_id}/customimages"],
161 [7, 'Distros', f"/toastergui/project/{TestProjectConfigTab.project_id}/distros"], # distro 167 # image recipes
168 [3, 'Image recipes',
169 f"/toastergui/project/{TestProjectConfigTab.project_id}/images"],
170 # software recipes
171 [4, 'Software recipes',
172 f"/toastergui/project/{TestProjectConfigTab.project_id}/softwarerecipes"],
173 # machines
174 [5, 'Machines',
175 f"/toastergui/project/{TestProjectConfigTab.project_id}/machines"],
176 # layers
177 [6, 'Layers',
178 f"/toastergui/project/{TestProjectConfigTab.project_id}/layers"],
179 # distro
180 [7, 'Distros',
181 f"/toastergui/project/{TestProjectConfigTab.project_id}/distros"],
162 # [9, 'BitBake variables', f"/toastergui/project/{TestProjectConfigTab.project_id}/configuration"], # bitbake variables 182 # [9, 'BitBake variables', f"/toastergui/project/{TestProjectConfigTab.project_id}/configuration"], # bitbake variables
163 ] 183 ]
164 for index, item_name, url in conf_nav_list: 184 for index, item_name, url in conf_nav_list:
@@ -281,15 +301,10 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
281 # Create a new project for this test 301 # Create a new project for this test
282 project_name = self._random_string(10) 302 project_name = self._random_string(10)
283 self._create_project(project_name=project_name) 303 self._create_project(project_name=project_name)
284 current_url = self.driver.current_url
285 TestProjectConfigTab.project_id = get_projectId_from_url(current_url)
286 url = current_url.split('?')[0]
287 # check if the menu is displayed 304 # check if the menu is displayed
288 self.wait_until_visible('#project-page') 305 self.wait_until_visible('#project-page')
289 block_l = self.driver.find_element( 306 block_l = self.driver.find_element(
290 By.XPATH, '//*[@id="project-page"]/div[2]') 307 By.XPATH, '//*[@id="project-page"]/div[2]')
291 most_built_recipes = self.driver.find_element(
292 By.XPATH, '//*[@id="project-page"]/div[1]/div[3]')
293 project_release = self.driver.find_element( 308 project_release = self.driver.find_element(
294 By.XPATH, '//*[@id="project-page"]/div[1]/div[4]') 309 By.XPATH, '//*[@id="project-page"]/div[1]/div[4]')
295 layers = block_l.find_element(By.ID, 'layer-container') 310 layers = block_l.find_element(By.ID, 'layer-container')
@@ -315,26 +330,6 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
315 f'You have changed the {item_name} to: {new_item_name}' in change_notification.text 330 f'You have changed the {item_name} to: {new_item_name}' in change_notification.text
316 ) 331 )
317 332
318 def rebuild_from_most_build_recipes(recipe_list_items):
319 checkbox = recipe_list_items[0].find_element(By.TAG_NAME, 'input')
320 checkbox.click()
321 build_btn = self.find('#freq-build-btn')
322 build_btn.click()
323 self.wait_until_visible('#latest-builds')
324 build_state = wait_until_build(self, 'parsing starting cloning queued')
325 lastest_builds = self.driver.find_elements(
326 By.XPATH,
327 '//div[@id="latest-builds"]/div'
328 )
329 last_build = lastest_builds[0]
330 self.assertTrue(len(lastest_builds) >= 2)
331 cancel_button = last_build.find_element(
332 By.XPATH,
333 '//span[@class="cancel-build-btn pull-right alert-link"]',
334 )
335 cancel_button.click()
336 if 'starting' not in build_state: # change build state when cancelled in starting state
337 wait_until_build_cancelled(self)
338 # Machine 333 # Machine
339 check_machine_distro(self, 'machine', 'qemux86-64', 'machine-section') 334 check_machine_distro(self, 'machine', 'qemux86-64', 'machine-section')
340 # Distro 335 # Distro
@@ -374,32 +369,61 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
374 layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li') 369 layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li')
375 self.assertTrue(len(layers_list_items) == 4) 370 self.assertTrue(len(layers_list_items) == 4)
376 371
377 # Most built recipes 372 def test_most_build_recipes(self):
378 title = most_built_recipes.find_element(By.TAG_NAME, 'h3') 373 """ Test most build recipes block contains"""
379 self.assertTrue("Most built recipes" in title.text) 374 def rebuild_from_most_build_recipes(recipe_list_items):
375 checkbox = recipe_list_items[0].find_element(By.TAG_NAME, 'input')
376 checkbox.click()
377 build_btn = self.find('#freq-build-btn')
378 build_btn.click()
379 self.wait_until_present('#latest-builds')
380 wait_until_build(self, 'queued cloning starting parsing failed')
381 lastest_builds = self.driver.find_elements(
382 By.XPATH,
383 '//div[@id="latest-builds"]/div'
384 )
385 self.assertTrue(len(lastest_builds) >= 2)
386 last_build = lastest_builds[0]
387 try:
388 cancel_button = last_build.find_element(
389 By.XPATH,
390 '//span[@class="cancel-build-btn pull-right alert-link"]',
391 )
392 cancel_button.click()
393 except NoSuchElementException:
394 # Skip if the build is already cancelled
395 pass
396 wait_until_build_cancelled(self)
397 # Create a new project for remaining asserts
398 project_name = self._random_string(10)
399 self._create_project(project_name=project_name, release='2')
400 current_url = self.driver.current_url
401 TestProjectConfigTab.project_id = get_projectId_from_url(current_url)
402 url = current_url.split('?')[0]
403
380 # Create a new builds 404 # Create a new builds
381 build_state = self._create_builds() 405 self._create_builds()
382 406
383 # Refresh the page 407 # back to project page
384 self.driver.get(url) 408 self.driver.get(url)
385 409
386 self.wait_until_visible('#project-page', poll=3) 410 self.wait_until_visible('#project-page', poll=3)
387 # check can select a recipe and build it 411
412 # Most built recipes
388 most_built_recipes = self.driver.find_element( 413 most_built_recipes = self.driver.find_element(
389 By.XPATH, '//*[@id="project-page"]/div[1]/div[3]') 414 By.XPATH, '//*[@id="project-page"]/div[1]/div[3]')
390 recipe_list = most_built_recipes.find_element(By.ID, 'freq-build-list') 415 title = most_built_recipes.find_element(By.TAG_NAME, 'h3')
416 self.assertTrue("Most built recipes" in title.text)
417 # check can select a recipe and build it
418 self.wait_until_visible('#freq-build-list', poll=3)
419 recipe_list = self.find('#freq-build-list')
391 recipe_list_items = recipe_list.find_elements(By.TAG_NAME, 'li') 420 recipe_list_items = recipe_list.find_elements(By.TAG_NAME, 'li')
392 if 'starting' not in build_state: # Build will not appear in the list if canceled in starting state 421 self.assertTrue(
393 self.assertTrue( 422 len(recipe_list_items) > 0,
394 len(recipe_list_items) > 0, 423 msg="Any recipes found in the most built recipes list",
395 msg="No recipes found in the most built recipes list", 424 )
396 ) 425 rebuild_from_most_build_recipes(recipe_list_items)
397 rebuild_from_most_build_recipes(recipe_list_items) 426 TestProjectConfigTab.project_id = None # reset project id
398 else:
399 self.assertTrue(
400 len(recipe_list_items) == 0,
401 msg="Recipes found in the most built recipes list",
402 )
403 427
404 def test_project_page_tab_importlayer(self): 428 def test_project_page_tab_importlayer(self):
405 """ Test project page tab import layer """ 429 """ Test project page tab import layer """
@@ -461,10 +485,9 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
461 div_empty_msg = self.find('#empty-state-customimagestable') 485 div_empty_msg = self.find('#empty-state-customimagestable')
462 link_create_custom_image = div_empty_msg.find_element( 486 link_create_custom_image = div_empty_msg.find_element(
463 By.TAG_NAME, 'a') 487 By.TAG_NAME, 'a')
464 last_project_id = Project.objects.get(name=project_name).id 488 self.assertTrue(TestProjectConfigTab.project_id is not None)
465 self.assertTrue(last_project_id is not None)
466 self.assertTrue( 489 self.assertTrue(
467 f"/toastergui/project/{last_project_id}/newcustomimage" in str( 490 f"/toastergui/project/{TestProjectConfigTab.project_id}/newcustomimage" in str(
468 link_create_custom_image.get_attribute('href') 491 link_create_custom_image.get_attribute('href')
469 ) 492 )
470 ) 493 )
@@ -473,6 +496,7 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
473 link_create_custom_image.text 496 link_create_custom_image.text
474 ) 497 )
475 ) 498 )
499 TestProjectConfigTab.project_id = None # reset project id
476 500
477 def test_project_page_image_recipe(self): 501 def test_project_page_image_recipe(self):
478 """ Test project page section images 502 """ Test project page section images
@@ -497,25 +521,3 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
497 self.wait_until_visible('#imagerecipestable tbody tr') 521 self.wait_until_visible('#imagerecipestable tbody tr')
498 rows = self.find_all('#imagerecipestable tbody tr') 522 rows = self.find_all('#imagerecipestable tbody tr')
499 self.assertTrue(len(rows) > 0) 523 self.assertTrue(len(rows) > 0)
500
501 # Test build button
502 image_to_build = rows[0]
503 build_btn = image_to_build.find_element(
504 By.XPATH,
505 '//td[@class="add-del-layers"]'
506 )
507 build_btn.click()
508 build_state = wait_until_build(self, 'parsing starting cloning queued')
509 lastest_builds = self.driver.find_elements(
510 By.XPATH,
511 '//div[@id="latest-builds"]/div'
512 )
513 self.assertTrue(len(lastest_builds) > 0)
514 last_build = lastest_builds[0]
515 cancel_button = last_build.find_element(
516 By.XPATH,
517 '//span[@class="cancel-build-btn pull-right alert-link"]',
518 )
519 cancel_button.click()
520 if 'starting' not in build_state: # change build state when cancelled in starting state
521 wait_until_build_cancelled(self)