1#! /usr/bin/env python3 2# 3# BitBake Toaster functional tests implementation 4# 5# Copyright (C) 2017 Intel Corporation 6# 7# SPDX-License-Identifier: GPL-2.0-only 8# 9 10import os 11import logging 12import subprocess 13import signal 14import re 15 16from tests.browser.selenium_helpers_base import SeleniumTestCaseBase 17from selenium.webdriver.common.by import By 18from selenium.common.exceptions import NoSuchElementException 19 20logger = logging.getLogger("toaster") 21toaster_processes = [] 22 23class SeleniumFunctionalTestCase(SeleniumTestCaseBase): 24 wait_toaster_time = 10 25 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': 31 raise RuntimeError("Please initialise django with the tests settings: " 32 "DJANGO_SETTINGS_MODULE='toastermain.settings_test'") 33 34 # 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 42 # start toaster 43 cmd = "bash -c 'source toaster start'" 44 start_process = subprocess.Popen( 45 cmd, 46 cwd=os.environ.get("BUILDDIR"), 47 shell=True) 48 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())) 63 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 71 global toaster_processes 72 73 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) 90 91 92 def get_URL(self): 93 rc=self.get_page_source() 94 project_url=re.search(r"(projectPageUrl\s:\s\")(.*)(\",)",rc) 95 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) 105 element = table_element.find_element(By.LINK_TEXT, link_text) 106 except NoSuchElementException: 107 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: 116 element = self.driver.find_element(By.XPATH, element_xpath) 117 except NoSuchElementException: 118 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: 126 element = self.driver.find_element(By.XPATH, element_xpath) 127 except NoSuchElementException: 128 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: 135 element = self.driver.find_element(By.XPATH, element_xpath) 136 except NoSuchElementException: 137 return False 138 return element 139