# SPDX-License-Identifier: LicenseRef-CSSL-1.0
#---------------------------------------------------------------------
# Python for CI of OAI-eNB + COTS-UE
#
# Required Python Version
# Python 3.x
#
# Required Python Package
# pexpect
#---------------------------------------------------------------------
#-----------------------------------------------------------
# Import
#-----------------------------------------------------------
import re # reg
import fileinput
import logging
import os
import time
import subprocess
import constants as CONST
#-----------------------------------------------------------
# Class Declaration
#-----------------------------------------------------------
class HTMLManagement():
def __init__(self):
self.htmlFile = ''
self.htmlHeaderCreated = False
self.htmlFooterCreated = False
self.ranRepository = ''
self.ranBranch = ''
self.ranCommitID = ''
self.ranAllowMerge = False
self.ranTargetBranch = ''
self.nbTestXMLfiles = 0
self.htmlTabRefs = []
self.htmlTabNames = []
self.htmlTabIcons = []
self.testXMLfiles = []
self.htmleNBFailureMsg = ''
self.htmlUEFailureMsg = ''
self.startTime = int(round(time.time() * 1000))
self.testCaseIdx = ''
self.desc = ''
#-----------------------------------------------------------
# HTML structure creation functions
#-----------------------------------------------------------
def CreateHtmlHeader(self):
if (not self.htmlHeaderCreated):
logging.info('\u001B[1m----------------------------------------\u001B[0m')
logging.info('\u001B[1m Creating HTML header \u001B[0m')
logging.info('\u001B[1m----------------------------------------\u001B[0m')
self.htmlFile = open('test_results.html', 'w')
self.htmlFile.write('\n')
self.htmlFile.write('\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
TEMPLATE_STAGE_NAME
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
self.htmlFile.write(' | Build Start Time (UTC) | \n')
self.htmlFile.write(' TEMPLATE_BUILD_TIME | \n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
self.htmlFile.write(' | GIT Repository | \n')
self.htmlFile.write(' ' + self.ranRepository + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
self.htmlFile.write(' | Job Trigger | \n')
if (self.ranAllowMerge):
self.htmlFile.write(' Merge-Request | \n')
else:
self.htmlFile.write(' Push to Branch | \n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
if (self.ranAllowMerge):
self.htmlFile.write(' | Source Branch | \n')
else:
self.htmlFile.write(' Branch | \n')
self.htmlFile.write(' ' + self.ranBranch + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
if (self.ranAllowMerge):
self.htmlFile.write(' | Source Commit ID | \n')
else:
self.htmlFile.write(' Commit ID | \n')
self.htmlFile.write(' ' + self.ranCommitID + ' | \n')
self.htmlFile.write('
\n')
if self.ranAllowMerge != '' and self.ranCommitID != 'develop':
commit_message = subprocess.check_output("git log -n1 --pretty=format:\"%s\" " + self.ranCommitID, shell=True, universal_newlines=True)
commit_message = commit_message.strip()
self.htmlFile.write(' \n')
if (self.ranAllowMerge):
self.htmlFile.write(' | Source Commit Message | \n')
else:
self.htmlFile.write(' Commit Message | \n')
self.htmlFile.write(' ' + commit_message + ' | \n')
self.htmlFile.write('
\n')
if (self.ranAllowMerge):
self.htmlFile.write(' \n')
self.htmlFile.write(' | Target Branch | \n')
if (self.ranTargetBranch == ''):
self.htmlFile.write(' develop | \n')
else:
self.htmlFile.write(' ' + self.ranTargetBranch + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.close()
def CreateHtmlTabHeader(self):
if (not self.htmlHeaderCreated):
if (not os.path.isfile('test_results.html')):
self.CreateHtmlHeader('none')
self.htmlFile = open('test_results.html', 'a')
if (self.nbTestXMLfiles == 1):
self.htmlFile.write('
\n')
self.htmlFile.write('
Test Summary for ' + self.testXMLfiles[0] + '
\n')
else:
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
self.htmlFile.write(' | Relative Time (s) | \n')
self.htmlFile.write(' Test Index | \n')
self.htmlFile.write(' Test Desc | \n')
self.htmlFile.write(' Test Options | \n')
self.htmlFile.write(' Test Status | \n')
self.htmlFile.write(' Info | \n')
self.htmlFile.write('
\n')
self.htmlFile.close()
self.htmlHeaderCreated = True
def CreateHtmlTabFooter(self, passStatus):
if ((not self.htmlFooterCreated) and (self.htmlHeaderCreated)):
self.htmlFile = open('test_results.html', 'a')
self.htmlFile.write(' \n')
self.htmlFile.write(' | Final Tab Status | \n')
if passStatus:
self.htmlFile.write(' PASS | \n')
else:
self.htmlFile.write(' FAIL | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.close()
if passStatus:
cmd = "sed -i -e 's/__STATE_" + self.htmlTabNames[0] + "__//' test_results.html"
subprocess.run(cmd, shell=True)
else:
cmd = "sed -i -e 's/__STATE_" + self.htmlTabNames[0] + r"__/
<\/span>/' test_results.html"
subprocess.run(cmd, shell=True)
self.htmlFooterCreated = False
def CreateHtmlFooter(self, passStatus):
if (os.path.isfile('test_results.html')):
# Tagging the 1st tab as active so it is automatically opened.
firstTabFound = False
for line in fileinput.FileInput("test_results.html", inplace=1):
if re.search('tab-pane fade', line) and not firstTabFound:
firstTabFound = True
print(line.replace('tab-pane fade', 'tab-pane fade in active'), end ='')
else:
print(line, end ='')
self.htmlFile = open('test_results.html', 'a')
self.htmlFile.write('\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
self.htmlFile.write(' | Final Status | \n')
if passStatus:
self.htmlFile.write(' PASS | \n')
else:
self.htmlFile.write(' FAIL | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('\n')
self.htmlFile.close()
def CreateHtmlTestRow(self, options, status, processesStatus, machine='eNB'):
if (self.htmlFooterCreated or (not self.htmlHeaderCreated)):
return
self.htmlFile = open('test_results.html', 'a')
currentTime = int(round(time.time() * 1000)) - self.startTime
self.htmlFile.write('
\n')
self.htmlFile.write(' | ' + format(currentTime / 1000, '.1f') + ' | \n')
self.htmlFile.write(' ' + self.testCaseIdx + ' | \n')
self.htmlFile.write(' ' + self.desc + ' | \n')
self.htmlFile.write(' ' + str(options) + ' | \n')
if (str(status) == 'OK'):
self.htmlFile.write(' ' + str(status) + ' | \n')
elif (str(status) == 'KO'):
if (processesStatus == 0):
self.htmlFile.write(' ' + str(status) + ' | \n')
elif (processesStatus == CONST.ENB_PROCESS_FAILED):
self.htmlFile.write(' KO - eNB process not found | \n')
elif (processesStatus == CONST.OAI_UE_PROCESS_FAILED):
self.htmlFile.write(' KO - OAI UE process not found | \n')
elif (processesStatus == CONST.ENB_PROCESS_SEG_FAULT) or (processesStatus == CONST.OAI_UE_PROCESS_SEG_FAULT):
self.htmlFile.write(' KO - ' + machine + ' process ended in Segmentation Fault | \n')
elif (processesStatus == CONST.ENB_PROCESS_ASSERTION) or (processesStatus == CONST.OAI_UE_PROCESS_ASSERTION):
self.htmlFile.write(' KO - ' + machine + ' process ended in Assertion | \n')
elif (processesStatus == CONST.ENB_PROCESS_REALTIME_ISSUE):
self.htmlFile.write(' KO - ' + machine + ' process faced Real Time issue(s) | \n')
elif (processesStatus == CONST.ENB_PROCESS_NOLOGFILE_TO_ANALYZE) or (processesStatus == CONST.OAI_UE_PROCESS_NOLOGFILE_TO_ANALYZE):
self.htmlFile.write(' OK? | \n')
elif (processesStatus == CONST.ENB_PROCESS_SLAVE_RRU_NOT_SYNCED):
self.htmlFile.write(' KO - ' + machine + ' Slave RRU could not synch | \n')
elif (processesStatus == CONST.OAI_UE_PROCESS_COULD_NOT_SYNC):
self.htmlFile.write(' KO - UE could not sync | \n')
elif (processesStatus == CONST.HSS_PROCESS_FAILED):
self.htmlFile.write(' KO - HSS process not found | \n')
elif (processesStatus == CONST.MME_PROCESS_FAILED):
self.htmlFile.write(' KO - MME process not found | \n')
elif (processesStatus == CONST.SPGW_PROCESS_FAILED):
self.htmlFile.write(' KO - SPGW process not found | \n')
elif (processesStatus == CONST.UE_IP_ADDRESS_ISSUE):
self.htmlFile.write(' KO - Could not retrieve UE IP address | \n')
elif (processesStatus == CONST.PHYSIM_IMAGE_ABSENT):
self.htmlFile.write(' KO - No such image oai-physim | \n')
elif (processesStatus == CONST.OC_LOGIN_FAIL):
self.htmlFile.write(' KO - Could not log onto cluster | \n')
elif (processesStatus == CONST.OC_PROJECT_FAIL):
self.htmlFile.write(' KO - Could not register into cluster project | \n')
elif (processesStatus == CONST.OC_IS_FAIL):
self.htmlFile.write(' KO - Could not create Image Stream | \n')
elif (processesStatus == CONST.OC_PHYSIM_DEPLOY_FAIL):
self.htmlFile.write(' KO - Could not properly deploy physim on cluster | \n')
else:
self.htmlFile.write(' ' + str(status) + ' | \n')
else:
self.htmlFile.write(' ' + str(status) + ' | \n')
if (len(str(self.htmleNBFailureMsg)) > 2):
cellBgColor = 'white'
result = re.search('ended with|faced real time issues', self.htmleNBFailureMsg)
if result is not None:
cellBgColor = 'red'
else:
result = re.search('showed|Reestablishment|Could not copy eNB logfile', self.htmleNBFailureMsg)
if result is not None:
cellBgColor = 'orange'
self.htmlFile.write(' ' + self.htmleNBFailureMsg + ' | \n')
self.htmleNBFailureMsg = ''
elif (len(str(self.htmlUEFailureMsg)) > 2):
cellBgColor = 'white'
result = re.search('ended with|faced real time issues', self.htmlUEFailureMsg)
if result is not None:
cellBgColor = 'red'
else:
result = re.search('showed|Could not copy UE logfile|oaitun_ue1 interface is either NOT mounted or NOT configured', self.htmlUEFailureMsg)
if result is not None:
cellBgColor = 'orange'
self.htmlFile.write(' ' + self.htmlUEFailureMsg + ' | \n')
self.htmlUEFailureMsg = ''
else:
self.htmlFile.write(' - | \n')
self.htmlFile.write('
\n')
self.htmlFile.close()
#for the moment it is limited to 4 columns, to be made generic later
def CreateHtmlDataLogTable(self, DataLog):
if (self.htmlFooterCreated or (not self.htmlHeaderCreated)):
return
self.htmlFile = open('test_results.html', 'a')
# TabHeader
self.htmlFile.write('
\n')
self.htmlFile.write(' | ---- ' + DataLog['Title'] + ' ---- | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | '+ DataLog['ColNames'][0] +' | \n')
self.htmlFile.write(' ' + DataLog['ColNames'][1] + ' | \n')
self.htmlFile.write(' '+ DataLog['ColNames'][2] +' | \n')
self.htmlFile.write('
\n')
for k in DataLog['Data']:
# TestRow
avg = DataLog['Data'][k][0]
maxval = DataLog['Data'][k][1]
count = DataLog['Data'][k][2]
valnorm = float(DataLog['Data'][k][3])
dev = DataLog['DeviationThreshold'][k]
ref = DataLog['Ref'][k]
self.htmlFile.write('
\n')
self.htmlFile.write(' | ' + k + ' | \n')
self.htmlFile.write(f' {avg}; {maxval}; {count} | \n')
if valnorm > 1.0 + dev or valnorm < 1.0 - dev:
self.htmlFile.write(f' {valnorm} (Avg over Ref = {avg} over {ref}; max allowed deviation = {dev}) | \n')
else:
self.htmlFile.write(f' {valnorm} (Avg over Ref = {avg} over {ref}; max allowed deviation = {dev}) | \n')
self.htmlFile.write('
\n')
self.htmlFile.close()
def CreateHtmlTestRowQueue(self, options, status, infoList):
if ((not self.htmlFooterCreated) and (self.htmlHeaderCreated)):
self.htmlFile = open('test_results.html', 'a')
currentTime = int(round(time.time() * 1000)) - self.startTime
addOrangeBK = False
self.htmlFile.write('
\n')
self.htmlFile.write(' | ' + format(currentTime / 1000, '.1f') + ' | \n')
self.htmlFile.write(' ' + self.testCaseIdx + ' | \n')
self.htmlFile.write(' ' + self.desc + ' | \n')
self.htmlFile.write(' ' + str(options) + ' | \n')
if (str(status) == 'OK'):
self.htmlFile.write(f' {status} | \n')
elif (str(status) == 'KO'):
self.htmlFile.write(f' {status} | \n')
elif str(status) == 'SKIP':
self.htmlFile.write(f' {status} | \n')
else:
addOrangeBK = True
self.htmlFile.write(f' {status} | \n')
if (addOrangeBK):
self.htmlFile.write(' ')
else:
self.htmlFile.write(' | ')
for i in infoList: # add custom style to have elements side-by-side to reduce need for vertical space
self.htmlFile.write(f' {i}')
self.htmlFile.write(' | ')
self.htmlFile.write('
\n')
self.htmlFile.close()
def CreateHtmlTestRowCppCheckResults(self, CCR):
if (self.htmlFooterCreated or (not self.htmlHeaderCreated)):
return
self.htmlFile = open('test_results.html', 'a')
vId = 0
for version in CCR.versions:
self.htmlFile.write('
\n')
self.htmlFile.write(' | Results for cppcheck v ' + version + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' NB ERRORS | \n')
if CCR.nbErrors[vId] == 0:
myColor = 'lightgreen'
elif CCR.nbErrors[vId] < 20:
myColor = 'orange'
else:
myColor = 'lightcoral'
self.htmlFile.write(' ' + str(CCR.nbErrors[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' NB WARNINGS | \n')
if CCR.nbWarnings[vId] == 0:
myColor = 'lightgreen'
elif CCR.nbWarnings[vId] < 20:
myColor = 'orange'
else:
myColor = 'lightcoral'
self.htmlFile.write(' ' + str(CCR.nbWarnings[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | ----------------- | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Memory leak | \n')
self.htmlFile.write(' ' + str(CCR.nbMemLeaks[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Possible null pointer deference | \n')
self.htmlFile.write(' ' + str(CCR.nbNullPtrs[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Uninitialized variable | \n')
self.htmlFile.write(' ' + str(CCR.nbUninitVars[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Undefined behaviour shifting | \n')
self.htmlFile.write(' ' + str(CCR.nbTooManyBitsShift[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Signed integer overflow | \n')
self.htmlFile.write(' ' + str(CCR.nbIntegerOverflow[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Printf formatting issues | \n')
self.htmlFile.write(' ' + str(CCR.nbInvalidPrintf[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Modulo result is predetermined | \n')
self.htmlFile.write(' ' + str(CCR.nbModuloAlways[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Opposite Condition -> dead code | \n')
self.htmlFile.write(' ' + str(CCR.nbOppoInnerCondition[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Wrong Scanf Nb Args | \n')
self.htmlFile.write(' ' + str(CCR.nbWrongScanfArg[vId]) + ' | \n')
self.htmlFile.write('
\n')
vId += 1
def CreateHtmlTestRowPhySimTestResult(self, testSummary, testResult):
if (self.htmlFooterCreated or (not self.htmlHeaderCreated)):
return
self.htmlFile = open('test_results.html', 'a')
if bool(testResult) == False and bool(testSummary) == False:
self.htmlFile.write('
\n')
self.htmlFile.write(' | ----PHYSIM TESTING FAILED - Unable to recover the test logs ---- | \n')
self.htmlFile.write('
\n')
else:
# Tab header
self.htmlFile.write('
\n')
self.htmlFile.write(' | ---- PHYSIM TEST SUMMARY---- | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | LogFile Name | \n')
self.htmlFile.write(' Nb Tests | \n')
self.htmlFile.write(' Nb Failure | \n')
self.htmlFile.write(' Nb Pass | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | physim_log.txt | \n')
self.htmlFile.write(' ' + str(testSummary['Nbtests']) + ' | \n')
if testSummary['Nbfail'] == 0:
self.htmlFile.write(' ' + str(testSummary['Nbfail']) + ' | \n')
else:
self.htmlFile.write(' ' + str(testSummary['Nbfail']) + ' | \n')
self.htmlFile.write(' ' + str(testSummary['Nbpass']) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | ---- PHYSIM TEST DETAIL INFO---- | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | Test Name | \n')
self.htmlFile.write(' Test Description | \n')
self.htmlFile.write(' Test Status | \n')
self.htmlFile.write(' Info | \n')
self.htmlFile.write('
\n')
y = ''
for key, value in testResult.items():
x = key.split(".")
if x[2] != y:
self.htmlFile.write('
\n')
self.htmlFile.write(' | "' + x[2] + '" series | \n')
self.htmlFile.write('
\n')
y = x[2]
self.htmlFile.write('
\n')
self.htmlFile.write(' | ' + key + ' | \n')
self.htmlFile.write(' ' + value[0] + ' | \n')
if 'PASS' in value:
self.htmlFile.write(' ' + value[2] + ' | \n')
else:
self.htmlFile.write(' ' + value[2] + ' | \n')
self.htmlFile.write(f' {value[1]} | \n')
self.htmlFile.close()