blob: 7c20437d148f53907c44d967fb895bf36529c3c6 [file] [log] [blame]
Brad Bishop96ff1982019-08-19 13:50:42 -04001#! /usr/bin/env python3
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002#
3# BitBake Toaster functional tests implementation
4#
5# Copyright (C) 2017 Intel Corporation
6#
Brad Bishopc342db32019-05-15 21:57:59 -04007# SPDX-License-Identifier: GPL-2.0-only
Brad Bishop6e60e8b2018-02-01 10:27:11 -05008#
Brad Bishop6e60e8b2018-02-01 10:27:11 -05009
10import os
11import logging
12import subprocess
13import signal
Brad Bishop6e60e8b2018-02-01 10:27:11 -050014import re
15
16from tests.browser.selenium_helpers_base import SeleniumTestCaseBase
Andrew Geissler20137392023-10-12 04:59:14 -060017from selenium.webdriver.common.by import By
18from selenium.common.exceptions import NoSuchElementException
Brad Bishop6e60e8b2018-02-01 10:27:11 -050019
20logger = logging.getLogger("toaster")
Patrick Williams169d7bc2024-01-05 11:33:25 -060021toaster_processes = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -050022
23class SeleniumFunctionalTestCase(SeleniumTestCaseBase):
Patrick Williams169d7bc2024-01-05 11:33:25 -060024 wait_toaster_time = 10
Brad Bishop6e60e8b2018-02-01 10:27:11 -050025
26 @classmethod
27 def setUpClass(cls):
28 # So that the buildinfo helper uses the test database'
29 if os.environ.get('DJANGO_SETTINGS_MODULE', '') != \
30 'toastermain.settings_test':
Patrick Williams169d7bc2024-01-05 11:33:25 -060031 raise RuntimeError("Please initialise django with the tests settings: "
Brad Bishop6e60e8b2018-02-01 10:27:11 -050032 "DJANGO_SETTINGS_MODULE='toastermain.settings_test'")
33
Patrick Williams169d7bc2024-01-05 11:33:25 -060034 # Wait for any known toaster processes to exit
35 global toaster_processes
36 for toaster_process in toaster_processes:
37 try:
38 os.waitpid(toaster_process, os.WNOHANG)
39 except ChildProcessError:
40 pass
41
Brad Bishop6e60e8b2018-02-01 10:27:11 -050042 # start toaster
43 cmd = "bash -c 'source toaster start'"
Patrick Williams169d7bc2024-01-05 11:33:25 -060044 start_process = subprocess.Popen(
Brad Bishop6e60e8b2018-02-01 10:27:11 -050045 cmd,
46 cwd=os.environ.get("BUILDDIR"),
47 shell=True)
Patrick Williams169d7bc2024-01-05 11:33:25 -060048 toaster_processes = [start_process.pid]
49 if start_process.wait() != 0:
50 port_use = os.popen("lsof -i -P -n | grep '8000 (LISTEN)'").read().strip()
51 message = ''
52 if port_use:
53 process_id = port_use.split()[1]
54 process = os.popen(f"ps -o cmd= -p {process_id}").read().strip()
55 message = f"Port 8000 occupied by {process}"
56 raise RuntimeError(f"Can't initialize toaster. {message}")
57
58 builddir = os.environ.get("BUILDDIR")
59 with open(os.path.join(builddir, '.toastermain.pid'), 'r') as f:
60 toaster_processes.append(int(f.read()))
61 with open(os.path.join(builddir, '.runbuilds.pid'), 'r') as f:
62 toaster_processes.append(int(f.read()))
Brad Bishop6e60e8b2018-02-01 10:27:11 -050063
64 super(SeleniumFunctionalTestCase, cls).setUpClass()
65 cls.live_server_url = 'http://localhost:8000/'
66
67 @classmethod
68 def tearDownClass(cls):
69 super(SeleniumFunctionalTestCase, cls).tearDownClass()
70
Patrick Williams169d7bc2024-01-05 11:33:25 -060071 global toaster_processes
Brad Bishop6e60e8b2018-02-01 10:27:11 -050072
Patrick Williams169d7bc2024-01-05 11:33:25 -060073 cmd = "bash -c 'source toaster stop'"
74 stop_process = subprocess.Popen(
75 cmd,
76 cwd=os.environ.get("BUILDDIR"),
77 shell=True)
78 # Toaster stop has been known to hang in these tests so force kill if it stalls
79 try:
80 if stop_process.wait(cls.wait_toaster_time) != 0:
81 raise Exception('Toaster stop process failed')
82 except Exception as e:
83 if e is subprocess.TimeoutExpired:
84 print('Toaster stop process took too long. Force killing toaster...')
85 else:
86 print('Toaster stop process failed. Force killing toaster...')
87 stop_process.kill()
88 for toaster_process in toaster_processes:
89 os.kill(toaster_process, signal.SIGTERM)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050090
91
92 def get_URL(self):
93 rc=self.get_page_source()
Patrick Williams169d7bc2024-01-05 11:33:25 -060094 project_url=re.search(r"(projectPageUrl\s:\s\")(.*)(\",)",rc)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050095 return project_url.group(2)
96
97
98 def find_element_by_link_text_in_table(self, table_id, link_text):
99 """
100 Assume there're multiple suitable "find_element_by_link_text".
101 In this circumstance we need to specify "table".
102 """
103 try:
104 table_element = self.get_table_element(table_id)
Andrew Geissler20137392023-10-12 04:59:14 -0600105 element = table_element.find_element(By.LINK_TEXT, link_text)
106 except NoSuchElementException:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500107 print('no element found')
108 raise
109 return element
110
111 def get_table_element(self, table_id, *coordinate):
112 if len(coordinate) == 0:
113#return whole-table element
114 element_xpath = "//*[@id='" + table_id + "']"
115 try:
Andrew Geissler20137392023-10-12 04:59:14 -0600116 element = self.driver.find_element(By.XPATH, element_xpath)
117 except NoSuchElementException:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500118 raise
119 return element
120 row = coordinate[0]
121
122 if len(coordinate) == 1:
123#return whole-row element
124 element_xpath = "//*[@id='" + table_id + "']/tbody/tr[" + str(row) + "]"
125 try:
Andrew Geissler20137392023-10-12 04:59:14 -0600126 element = self.driver.find_element(By.XPATH, element_xpath)
127 except NoSuchElementException:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500128 return False
129 return element
130#now we are looking for an element with specified X and Y
131 column = coordinate[1]
132
133 element_xpath = "//*[@id='" + table_id + "']/tbody/tr[" + str(row) + "]/td[" + str(column) + "]"
134 try:
Andrew Geissler20137392023-10-12 04:59:14 -0600135 element = self.driver.find_element(By.XPATH, element_xpath)
136 except NoSuchElementException:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500137 return False
138 return element