blob: 0f45e04fe3cfa49e050129f2ce8e96bd0c141cd3 [file] [log] [blame]
adminbae64d82013-08-01 10:50:15 -07001#!/usr/bin/env python
2'''
3Created on 22-Oct-2012
Jeremy Ronquillob27ce4c2017-07-17 12:41:28 -07004Copyright 2012 Open Networking Foundation (ONF)
Jon Hall65844a32015-03-09 19:09:37 -07005
Jeremy Songsterae01bba2016-07-11 15:39:17 -07006Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
7the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
8or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
adminbae64d82013-08-01 10:50:15 -07009
10 TestON is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 2 of the License, or
13 (at your option) any later version.
14
15 TestON is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
Jon Hall65844a32015-03-09 19:09:37 -070021 along with TestON. If not, see <http://www.gnu.org/licenses/>.
adminbae64d82013-08-01 10:50:15 -070022
23
24
25teston is the main module.
26
27'''
28
29import sys
30import getpass
31import os
32import re
33import __builtin__
34import new
35import xmldict
Jon Hall30b82fa2015-03-04 17:15:43 -080036import importlib
Jon Hall5b586732015-06-11 11:39:39 -070037import threading
Jeremy Ronquillo15ff1072017-07-17 10:55:46 -070038import pdb
Jon Hall714eeba2015-09-29 17:53:10 -070039module = new.module( "test" )
adminbae64d82013-08-01 10:50:15 -070040import openspeak
Hari Krishnabe4b97b2015-07-15 12:19:43 -070041import subprocess
Jon Hall714eeba2015-09-29 17:53:10 -070042global path, drivers_path, core_path, tests_path, logs_path
Jon Hall44506242015-07-29 17:40:26 -070043location = os.path.abspath( os.path.dirname( __file__ ) )
44path = re.sub( "(core|bin)$", "", location )
Jon Hall714eeba2015-09-29 17:53:10 -070045drivers_path = path + "drivers"
46core_path = path + "core"
47tests_path = path + "tests"
48logs_path = path + "logs/"
adminbae64d82013-08-01 10:50:15 -070049config_path = path + "config/"
Jon Hall44506242015-07-29 17:40:26 -070050sys.path.append( path )
51sys.path.append( drivers_path )
52sys.path.append( core_path )
53sys.path.append( tests_path )
adminbae64d82013-08-01 10:50:15 -070054
55from core.utilities import Utilities
kelvin-onlabfb521662015-02-27 09:52:40 -080056from core.Thread import Thread
adminbae64d82013-08-01 10:50:15 -070057
Jon Hallca319892017-06-15 15:25:22 -070058class SkipCase( Exception ):
59 pass
60
adminbae64d82013-08-01 10:50:15 -070061class TestON:
62 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -070063 TestON will initiate the specified test.
Jon Hall714eeba2015-09-29 17:53:10 -070064 The main tasks are:
kelvin-onlabf70fd542015-05-07 18:41:40 -070065 * Initiate the required Component handles for the test.
adminbae64d82013-08-01 10:50:15 -070066 * Create Log file Handles.
adminbae64d82013-08-01 10:50:15 -070067 '''
Jon Hall714eeba2015-09-29 17:53:10 -070068 def __init__( self, options ):
adminbae64d82013-08-01 10:50:15 -070069 '''
Jon Hall714eeba2015-09-29 17:53:10 -070070 Initialise the component handles specified in the topology file of
71 the specified test.
adminbae64d82013-08-01 10:50:15 -070072 '''
73 # Initialization of the variables.
74 __builtin__.main = self
adminbae64d82013-08-01 10:50:15 -070075 __builtin__.path = path
76 __builtin__.utilities = Utilities()
77 self.TRUE = 1
78 self.FALSE = 0
79 self.ERROR = -1
kelvin-onlabf70fd542015-05-07 18:41:40 -070080 self.NORESULT = 2
adminbae64d82013-08-01 10:50:15 -070081 self.FAIL = False
82 self.PASS = True
kelvin-onlabf70fd542015-05-07 18:41:40 -070083 self.CASERESULT = self.ERROR
84 self.STEPRESULT = self.NORESULT
adminbae64d82013-08-01 10:50:15 -070085 self.init_result = self.TRUE
86 self.testResult = "Summary"
kelvin-onlabf70fd542015-05-07 18:41:40 -070087 self.stepName = ""
88 self.stepCache = ""
Jon Halld61331b2015-02-17 16:35:47 -080089 self.EXPERIMENTAL_MODE = False
adminbae64d82013-08-01 10:50:15 -070090 self.test_target = None
91 self.lastcommand = None
Jon Halld61331b2015-02-17 16:35:47 -080092 self.testDir = tests_path
93 self.configFile = config_path + "teston.cfg"
adminbae64d82013-08-01 10:50:15 -070094 self.parsingClass = "xmlparser"
95 self.parserPath = core_path + "/xmlparser"
96 self.loggerPath = core_path + "/logger"
97 self.loggerClass = "Logger"
98 self.logs_path = logs_path
99 self.driver = ''
kelvin-onlabfb521662015-02-27 09:52:40 -0800100 self.Thread = Thread
Jon Hall5b586732015-06-11 11:39:39 -0700101 self.cleanupFlag = False
102 self.cleanupLock = threading.Lock()
Jon Hall0fc0d452015-07-14 09:49:58 -0700103 self.initiated = False
Jon Hall65844a32015-03-09 19:09:37 -0700104
Jon Hall25079782015-10-13 13:54:39 -0700105 self.config = self.configparser()
Jon Hall714eeba2015-09-29 17:53:10 -0700106 verifyOptions( options )
adminbae64d82013-08-01 10:50:15 -0700107 load_logger()
108 self.componentDictionary = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700109 self.componentDictionary = self.topology['COMPONENT']
110 self.driversList = []
111 if isinstance( self.componentDictionary, str):
112 self.componentDictionary = dict( self.componentDictionary )
Jon Hall65844a32015-03-09 19:09:37 -0700113
Jon Hall714eeba2015-09-29 17:53:10 -0700114 for component in self.componentDictionary:
115 self.driversList.append( self.componentDictionary[component]['type'] )
Jon Hall65844a32015-03-09 19:09:37 -0700116
Jon Hall714eeba2015-09-29 17:53:10 -0700117 self.driversList = list( set( self.driversList ) ) # Removing duplicates.
adminbae64d82013-08-01 10:50:15 -0700118 # Checking the test_target option set for the component or not
Jon Hall714eeba2015-09-29 17:53:10 -0700119 if isinstance( self.componentDictionary, dict ):
adminbae64d82013-08-01 10:50:15 -0700120 for component in self.componentDictionary.keys():
121 if 'test_target' in self.componentDictionary[component].keys():
122 self.test_target = component
Jon Hall65844a32015-03-09 19:09:37 -0700123
Jon Halld61331b2015-02-17 16:35:47 -0800124 # Checking for the openspeak file and test script
Jon Hall714eeba2015-09-29 17:53:10 -0700125 self.logger.initlog( self )
adminbae64d82013-08-01 10:50:15 -0700126
127 # Creating Drivers Handles
Jon Hall714eeba2015-09-29 17:53:10 -0700128 initString = "\n" + "*" * 30 + "\n CASE INIT \n" + "*" * 30 + "\n"
129 self.log.exact( initString )
adminbae64d82013-08-01 10:50:15 -0700130 self.driverObject = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700131 self.random_order = 111 # Random order id to connect the components
adminbae64d82013-08-01 10:50:15 -0700132 components_connect_order = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700133 if isinstance( self.componentDictionary, dict ):
adminbae64d82013-08-01 10:50:15 -0700134 for component in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700135 if 'connect_order' not in self.componentDictionary[component].keys():
136 self.componentDictionary[component]['connect_order'] = str( self.get_random() )
137 components_connect_order[component] = eval( self.componentDictionary[component]['connect_order'] )
138 # Ordering components based on the connect order.
139 ordered_component_list = sorted( components_connect_order,
140 key=lambda key: components_connect_order[key] )
adminbae64d82013-08-01 10:50:15 -0700141 print ordered_component_list
adminbae64d82013-08-01 10:50:15 -0700142 for component in ordered_component_list:
Jon Hall714eeba2015-09-29 17:53:10 -0700143 self.componentInit( component )
adminbae64d82013-08-01 10:50:15 -0700144
Jon Hall714eeba2015-09-29 17:53:10 -0700145 def configparser( self ):
adminbae64d82013-08-01 10:50:15 -0700146 '''
147 It will parse the config file (teston.cfg) and return as dictionary
148 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700149 matchFileName = re.match( r'(.*)\.cfg', self.configFile, re.M | re.I )
adminbae64d82013-08-01 10:50:15 -0700150 if matchFileName:
Jon Hall714eeba2015-09-29 17:53:10 -0700151 xml = open( self.configFile ).read()
152 try:
153 self.configDict = xmldict.xml_to_dict( xml )
adminbae64d82013-08-01 10:50:15 -0700154 return self.configDict
Jon Hall1306a562015-09-04 11:21:24 -0700155 except IOError:
adminbae64d82013-08-01 10:50:15 -0700156 print "There is no such file to parse " + self.configFile
Jon Hall1306a562015-09-04 11:21:24 -0700157 else:
158 print "There is no such file to parse " + self.configFile
kelvin-onlabf70fd542015-05-07 18:41:40 -0700159
Jon Hall714eeba2015-09-29 17:53:10 -0700160 def componentInit( self, component ):
adminbae64d82013-08-01 10:50:15 -0700161 '''
162 This method will initialize specified component
163 '''
164 global driver_options
Jon Hall0fc0d452015-07-14 09:49:58 -0700165 self.initiated = False
Jon Hall714eeba2015-09-29 17:53:10 -0700166 self.log.info( "Creating component Handle: " + component )
Jon Halld61331b2015-02-17 16:35:47 -0800167 driver_options = {}
adminbae64d82013-08-01 10:50:15 -0700168 if 'COMPONENTS' in self.componentDictionary[component].keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700169 driver_options = dict( self.componentDictionary[component]['COMPONENTS'] )
Jon Hall714eeba2015-09-29 17:53:10 -0700170 driver_options['name'] = component
adminbae64d82013-08-01 10:50:15 -0700171 driverName = self.componentDictionary[component]['type']
Jon Hall714eeba2015-09-29 17:53:10 -0700172 driver_options['type'] = driverName
kelvin-onlabf70fd542015-05-07 18:41:40 -0700173
Jon Hall714eeba2015-09-29 17:53:10 -0700174 classPath = self.getDriverPath( driverName.lower() )
175 driverModule = importlib.import_module( classPath )
176 driverClass = getattr( driverModule, driverName )
adminbae64d82013-08-01 10:50:15 -0700177 driverObject = driverClass()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700178
Jon Hall714eeba2015-09-29 17:53:10 -0700179 if "OCN" in self.componentDictionary[component]['host'] and\
180 main.onoscell:
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700181 self.componentDictionary[component]['host'] = main.mnIP
182
Jon Hall714eeba2015-09-29 17:53:10 -0700183 user_name = self.componentDictionary[component].get( 'user',
184 getpass.getuser() )
185 ip_address = self.componentDictionary[component].get( 'host',
186 'localhost' )
187 pwd = self.componentDictionary[component].get( 'password',
188 'changeme' )
189 port = self.componentDictionary[component].get( 'port' )
190 connect_result = driverObject.connect( user_name=user_name,
191 ip_address=ip_address,
192 pwd=pwd,
193 port=port,
194 options=driver_options)
cameron@onlab.us5cc6a372015-05-11 17:18:07 -0700195
adminbae64d82013-08-01 10:50:15 -0700196 if not connect_result:
Jon Hall714eeba2015-09-29 17:53:10 -0700197 self.log.error( "Exiting from the test execution because connecting to the " +
198 component + " component failed." )
Jon Halld61331b2015-02-17 16:35:47 -0800199 self.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700200
Jon Hall714eeba2015-09-29 17:53:10 -0700201 vars( self )[component] = driverObject
Jon Hall0fc0d452015-07-14 09:49:58 -0700202 self.initiated = True
Jon Hallca319892017-06-15 15:25:22 -0700203 return driverObject
kelvin-onlabf70fd542015-05-07 18:41:40 -0700204
Jon Hall714eeba2015-09-29 17:53:10 -0700205 def run( self ):
adminbae64d82013-08-01 10:50:15 -0700206 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700207 The Execution of the test script's cases listed in the Test params
208 file will be done here then update each test case result.
209 This method will return main.TRUE if it executed all the test cases
210 successfully, else will retun main.FALSE
adminbae64d82013-08-01 10:50:15 -0700211 '''
adminbae64d82013-08-01 10:50:15 -0700212 self.testCaseResult = {}
Jon Halla1185982014-09-15 14:55:10 -0700213 self.TOTAL_TC = 0
adminbae64d82013-08-01 10:50:15 -0700214 self.TOTAL_TC_RUN = 0
Jon Halld61331b2015-02-17 16:35:47 -0800215 self.TOTAL_TC_PLANNED = 0
adminbae64d82013-08-01 10:50:15 -0700216 self.TOTAL_TC_NORESULT = 0
217 self.TOTAL_TC_FAIL = 0
218 self.TOTAL_TC_PASS = 0
Jon Halla1185982014-09-15 14:55:10 -0700219 self.TEST_ITERATION = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700220
221 # NOTE: number of main.step statements in the
222 # outer most level of the test case. used to
223 # execute code in smaller steps
224 self.stepCount = 0
kelvin-onlabf70fd542015-05-07 18:41:40 -0700225 self.CASERESULT = self.NORESULT
226
Jon Halld61331b2015-02-17 16:35:47 -0800227 import testparser
Jon Hall53c5e662016-04-13 16:06:56 -0700228 test = testparser.TestParser( main.testFile )
adminbae64d82013-08-01 10:50:15 -0700229 self.testscript = test.testscript
230 self.code = test.getStepCode()
Jon Hall714eeba2015-09-29 17:53:10 -0700231 repeat = int( self.params.get( 'repeat', 1 ) )
232 self.TOTAL_TC_PLANNED = len( self.testcases_list ) * repeat
kelvin-onlabf70fd542015-05-07 18:41:40 -0700233
adminbae64d82013-08-01 10:50:15 -0700234 result = self.TRUE
Jon Hall714eeba2015-09-29 17:53:10 -0700235 while repeat:
Jon Halla1185982014-09-15 14:55:10 -0700236 for self.CurrentTestCaseNumber in self.testcases_list:
Jon Hall714eeba2015-09-29 17:53:10 -0700237 result = self.runCase( self.CurrentTestCaseNumber )
238 repeat -= 1
adminbae64d82013-08-01 10:50:15 -0700239 return result
kelvin-onlabf70fd542015-05-07 18:41:40 -0700240
Jon Halle234cc42015-08-31 15:26:47 -0700241 def runCase( self, testCaseNumber ):
adminbae64d82013-08-01 10:50:15 -0700242 self.CurrentTestCaseNumber = testCaseNumber
kelvin-onlabf70fd542015-05-07 18:41:40 -0700243 self.CurrentTestCase = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700244
245 # List of step results in a case. ANDed together to get the result
246 self.stepResultsList = []
kelvin-onlabf70fd542015-05-07 18:41:40 -0700247 self.stepName = ""
Jon Hall783bbf92015-07-23 14:33:19 -0700248 self.caseExplanation = ""
adminbae64d82013-08-01 10:50:15 -0700249 result = self.TRUE
Jon Hall714eeba2015-09-29 17:53:10 -0700250
251 # NOTE: number of main.step statements in the
252 # outer most level of the test case. used to
253 # execute code in smaller steps
254 self.stepCount = 0
255
256 # NOTE: This is the current number of main.step()'s executed
257 # in a case. Used for logging.
258 self.stepNumber = 0
adminbae64d82013-08-01 10:50:15 -0700259 self.EXPERIMENTAL_MODE = self.FALSE
260 self.addCaseHeader()
Jon Halle234cc42015-08-31 15:26:47 -0700261 self.testCaseNumber = str( testCaseNumber )
262 self.CASERESULT = self.NORESULT
adminbae64d82013-08-01 10:50:15 -0700263 stopped = False
Jon Hall5a72b712015-09-28 12:20:59 -0700264 try:
265 self.code[self.testCaseNumber]
Jon Halld61331b2015-02-17 16:35:47 -0800266 except KeyError:
Jon Halle234cc42015-08-31 15:26:47 -0700267 self.log.error( "There is no Test-Case " + self.testCaseNumber )
Jon Hallfebb1c72015-03-05 13:30:09 -0800268 return self.FALSE
adminbae64d82013-08-01 10:50:15 -0700269 self.stepCount = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700270 while self.stepCount < len( self.code[self.testCaseNumber].keys() ):
271 result = self.runStep( self.code, self.testCaseNumber )
Jon Hallfebb1c72015-03-05 13:30:09 -0800272 if result == self.FALSE:
adminbae64d82013-08-01 10:50:15 -0700273 break
Jon Hallfebb1c72015-03-05 13:30:09 -0800274 elif result == self.TRUE:
adminbae64d82013-08-01 10:50:15 -0700275 continue
Jon Hall5a72b712015-09-28 12:20:59 -0700276 # stepResults format: ( stepNo[], stepName[], stepResult[], onFail[] )
277 stepResults = self.stepResultsList
Jon Halle234cc42015-08-31 15:26:47 -0700278 if not stopped:
279 if self.CASERESULT == self.TRUE or self.CASERESULT == self.FALSE:
Jon Hall714eeba2015-09-29 17:53:10 -0700280 # Result was already explitily set somewhere else like
281 # in skipCase()
Jon Halle234cc42015-08-31 15:26:47 -0700282 pass
Jon Hall5a72b712015-09-28 12:20:59 -0700283 elif all( self.TRUE == i for i in stepResults ):
kelvin-onlabf70fd542015-05-07 18:41:40 -0700284 # ALL PASSED
285 self.CASERESULT = self.TRUE
Jon Hall5a72b712015-09-28 12:20:59 -0700286 elif self.FALSE in stepResults:
kelvin-onlabf70fd542015-05-07 18:41:40 -0700287 # AT LEAST ONE FAILED
288 self.CASERESULT = self.FALSE
Jon Hall5a72b712015-09-28 12:20:59 -0700289 elif self.TRUE in stepResults:
kelvin-onlabf70fd542015-05-07 18:41:40 -0700290 # AT LEAST ONE PASSED
291 self.CASERESULT = self.TRUE
292 else:
293 self.CASERESULT = self.NORESULT
Jon Hall714eeba2015-09-29 17:53:10 -0700294 self.testCaseResult[str( self.CurrentTestCaseNumber )] = self.CASERESULT
295 self.logger.updateCaseResults( self )
Jon Hall783bbf92015-07-23 14:33:19 -0700296 self.log.wiki( "<p>" + self.caseExplanation + "</p>" )
297 self.log.summary( self.caseExplanation )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700298 self.log.wiki( "<ul>" )
acsmarse2d1ed12015-10-05 13:51:17 -0700299 subcaseMessage = False
kelvin-onlabf70fd542015-05-07 18:41:40 -0700300 for line in self.stepCache.splitlines():
acsmarse2d1ed12015-10-05 13:51:17 -0700301 if re.search( "[0-9]\.[0-9]", line ): # Step
302 if subcaseMessage: # End of Failure Message Printout
303 self.log.wiki( "</ul>\n" )
304 subcaseMessage = False
305 if re.search( " - PASS$", line ):
306 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"tick\" /></li>\n" )
307 elif re.search( " - FAIL$", line ):
308 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"cross\" /></li>\n" )
309 elif re.search( " - No Result$", line ):
310 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"warning\" /></li>\n" )
311 else: # Substep
312 if not subcaseMessage: # Open Failure Message Printout
313 self.log.wiki( "<ul><li>" + line + "</li>\n" )
314 subcaseMessage = True
315 else: # Add to Failure Message Printout
316 self.log.wiki( "<li>" + line + "</li>\n" )
acsmars27e62dd2015-10-06 11:35:47 -0700317 if subcaseMessage: # End of Failure Message Printout for last item
318 self.log.wiki( "</ul>\n" )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700319 self.log.wiki( "</ul>" )
320 self.log.summary( self.stepCache )
321 self.stepCache = ""
adminbae64d82013-08-01 10:50:15 -0700322 return result
kelvin-onlabf70fd542015-05-07 18:41:40 -0700323
Jon Hall714eeba2015-09-29 17:53:10 -0700324 def runStep( self, code, testCaseNumber ):
adminbae64d82013-08-01 10:50:15 -0700325 if not cli.pause:
Jon Hall5a72b712015-09-28 12:20:59 -0700326 try:
327 step = self.stepCount
328 # stepResults format: ( stepNo, stepName, stepResult, onFail )
329 # NOTE: This is needed to catch results of main.step()'s
330 # called inside functions or loops
331 self.stepResults = ( [], [], [], [] )
adminbae64d82013-08-01 10:50:15 -0700332 exec code[testCaseNumber][step] in module.__dict__
333 self.stepCount = self.stepCount + 1
Jon Hall96b816f2015-11-03 12:00:56 -0800334 self.parseStepResults( testCaseNumber )
Jon Hallca319892017-06-15 15:25:22 -0700335 except SkipCase: # Raised in self.skipCase()
Jon Halle234cc42015-08-31 15:26:47 -0700336 self.log.warn( "Skipping the rest of CASE" +
337 str( testCaseNumber ) )
Jon Hall96b816f2015-11-03 12:00:56 -0800338 self.parseStepResults( testCaseNumber )
Jon Hall714eeba2015-09-29 17:53:10 -0700339 self.stepResultsList.append( self.STEPRESULT )
Jon Halle234cc42015-08-31 15:26:47 -0700340 self.stepCache += "\t\t" + self.onFailMsg + "\n"
341 self.stepCount = self.stepCount + 1
342 return self.FALSE
Jon Hallc1606352015-10-06 14:51:36 -0700343 except StandardError as e:
344 try:
345 stepNo = self.stepResults[0][ self.stepNumber - 1 ]
346 except IndexError:
347 stepNo = "<IndexError>"
348 main.log.warn( "Error trying to get step number. " +
349 "It is likely between step " +
Jon Hall6e709752016-02-01 13:38:46 -0800350 str( self.stepNumber ) + " and step " +
Jon Hallc1606352015-10-06 14:51:36 -0700351 str( self.stepNumber + 1 ) )
352 try:
353 stepName = self.stepResults[1][ self.stepNumber - 1 ]
354 except IndexError:
355 stepName = "<IndexError>"
356 self.log.error( "\nException in the following section of" +
357 " code: " + str( testCaseNumber ) + "." +
358 str( stepNo ) + ": " + stepName )
Jeremyd9e4eb12016-04-13 12:09:06 -0700359 self.log.error( str( e.__class__ ) + str( e.message ) )
adminbae64d82013-08-01 10:50:15 -0700360 self.stepCount = self.stepCount + 1
Jon Hall714eeba2015-09-29 17:53:10 -0700361 self.logger.updateCaseResults( self )
362 # WIKI results
kelvin-onlabf70fd542015-05-07 18:41:40 -0700363 self.log.wiki( "<ul>" )
364 for line in self.stepCache.splitlines():
365 if re.search( " - PASS$", line ):
366 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"tick\" /></li>\n" )
367 elif re.search( " - FAIL$", line ):
368 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"cross\" /></li>\n" )
369 elif re.search( " - No Result$", line ):
370 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"warning\" /></li>\n" )
Jon Hall90627612015-06-09 14:57:02 -0700371 else: # Should only be on fail message
372 self.log.wiki( "<ul><li>" + line + "</li></ul>\n" )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700373 self.log.wiki( "</ul>" )
Jon Hall714eeba2015-09-29 17:53:10 -0700374 # summary results
kelvin-onlabf70fd542015-05-07 18:41:40 -0700375 self.log.summary( self.stepCache )
376 self.stepCache = ""
shahshreya957feaa2015-03-23 16:08:29 -0700377 self.cleanup()
Jon Hall00539b12015-04-03 13:55:46 -0700378 self.exit()
Jon Halle234cc42015-08-31 15:26:47 -0700379 return self.TRUE
adminbae64d82013-08-01 10:50:15 -0700380 if cli.stop:
381 cli.stop = False
adminbae64d82013-08-01 10:50:15 -0700382 self.TOTAL_TC_NORESULT = self.TOTAL_TC_NORESULT + 1
Jon Hall714eeba2015-09-29 17:53:10 -0700383 self.testCaseResult[str( self.CurrentTestCaseNumber )] = "Stopped"
384 self.logger.updateCaseResults( self )
adminbae64d82013-08-01 10:50:15 -0700385 result = self.cleanup()
Jon Halle234cc42015-08-31 15:26:47 -0700386 return self.FALSE
387
Jon Hall96b816f2015-11-03 12:00:56 -0800388 def parseStepResults( self, testCaseNumber ):
389 """
390 Parse throught the step results for the wiki
391 """
392 try:
393 # Iterate through each of the steps and print them
394 for index in range( len( self.stepResults[0] ) ):
395 # stepResults = ( stepNo, stepName, stepResult, onFail )
396 stepNo = self.stepResults[0][ index ]
397 stepName = self.stepResults[1][ index ]
398 stepResult = self.stepResults[2][ index ]
399 onFail = self.stepResults[3][ index ]
400 self.stepCache += "\t" + str( testCaseNumber ) + "."
401 self.stepCache += str( stepNo ) + " "
402 self.stepCache += stepName + " - "
403 if stepResult == self.TRUE:
404 self.stepCache += "PASS\n"
405 elif stepResult == self.FALSE:
406 self.stepCache += "FAIL\n"
407 self.stepCache += "\t\t" + onFail + "\n"
408 else:
409 self.stepCache += "No Result\n"
410 self.stepResultsList.append( stepResult )
411 except Exception:
412 self.log.exception( "Error parsing step results" )
413
Jon Halle234cc42015-08-31 15:26:47 -0700414 def skipCase( self, result="DEFAULT", msg=None ):
415 """
416 Will skip the rest of the code in a test case. The case results will be
417 determined as normal based on completed assertions unless the result
418 argument is given.
419
420 Optional Arguments:
Jon Hall7c4f4302016-07-15 14:39:02 -0700421 result: Case insensitive string. Can be 'PASS' or 'FAIL' and will set
Jon Halle234cc42015-08-31 15:26:47 -0700422 the case result accordingly.
423 msg: Message to be printed when the case is skipped in the reports.
424 """
425 result = result.upper().strip()
426 if result == "PASS":
427 self.CASERESULT = self.TRUE
428 elif result == "FAIL":
429 self.CASERESULT = self.FALSE
430 self.onFailMsg = "Skipping the rest of this case. "
431 if msg:
432 self.onFailMsg += str( msg )
Jon Hallca319892017-06-15 15:25:22 -0700433 raise SkipCase
kelvin-onlabf70fd542015-05-07 18:41:40 -0700434
Jon Hall714eeba2015-09-29 17:53:10 -0700435 def addCaseHeader( self ):
436 caseHeader = "\n" + "*" * 30 + "\n Result summary for Testcase" +\
437 str( self.CurrentTestCaseNumber ) + "\n" + "*" * 30 + "\n"
438 self.log.exact( caseHeader )
439 caseHeader = "\n" + "*" * 40 + "\nStart of Test Case" +\
440 str( self.CurrentTestCaseNumber ) + " : "
adminbae64d82013-08-01 10:50:15 -0700441 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700442 vars( self )[driver + 'log'].info( caseHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700443
Jon Hall714eeba2015-09-29 17:53:10 -0700444 def addCaseFooter( self ):
Jon Hall5a72b712015-09-28 12:20:59 -0700445 stepNo = self.stepResults[0][-2]
Jon Hall714eeba2015-09-29 17:53:10 -0700446 if stepNo > 0:
447 previousStep = " " + str( self.CurrentTestCaseNumber ) + "." +\
448 str( stepNo ) + ": " + str( self.stepName )
449 stepHeader = "\n" + "*" * 40 + "\nEnd of Step " + previousStep +\
450 "\n" + "*" * 40 + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700451
Jon Hall714eeba2015-09-29 17:53:10 -0700452 caseFooter = "\n" + "*" * 40 + "\nEnd of Test case " +\
453 str( self.CurrentTestCaseNumber ) + "\n" + "*" * 40 + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700454
adminbae64d82013-08-01 10:50:15 -0700455 for driver in self.driversList:
Jon Hall714eeba2015-09-29 17:53:10 -0700456 vars( self )[driver].write( stepHeader + "\n" + caseFooter )
adminbae64d82013-08-01 10:50:15 -0700457
Jon Hall714eeba2015-09-29 17:53:10 -0700458 def cleanup( self ):
adminbae64d82013-08-01 10:50:15 -0700459 '''
Jon Hall5b586732015-06-11 11:39:39 -0700460 Print a summary of the current test's results then attempt to release
461 all the component handles and the close opened file handles.
adminbae64d82013-08-01 10:50:15 -0700462
Jon Hall5b586732015-06-11 11:39:39 -0700463 This function shouldbe threadsafe such that cleanup will only be
464 executed once per test.
465
466 This will return TRUE if all the component handles and log handles
467 closed properly, else return FALSE.
adminbae64d82013-08-01 10:50:15 -0700468 '''
469 result = self.TRUE
Jon Hall5b586732015-06-11 11:39:39 -0700470 lock = self.cleanupLock
471 if lock.acquire( False ):
472 try:
473 if self.cleanupFlag is False: # First thread to run this
474 self.cleanupFlag = True
Jon Hall0fc0d452015-07-14 09:49:58 -0700475 if self.initiated:
Jon Hall714eeba2015-09-29 17:53:10 -0700476 self.logger.testSummary( self )
Jon Hall892818c2015-10-20 17:58:34 -0700477 components = self.componentDictionary
478 for component in sorted( components,
479 key=lambda item: components[item]['connect_order'],
480 reverse=True ):
Jon Hall714eeba2015-09-29 17:53:10 -0700481 try:
482 tempObject = vars( self )[component]
483 print "Disconnecting from " + str( tempObject.name ) +\
484 ": " + str( tempObject.__class__)
Jon Hall5b586732015-06-11 11:39:39 -0700485 tempObject.disconnect()
Jon Hall1306a562015-09-04 11:21:24 -0700486 except KeyboardInterrupt:
487 pass
488 except KeyError:
489 # Component not created yet
490 self.log.warn( "Could not find the component " +
491 str( component ) )
492 except StandardError:
Jon Hall5b586732015-06-11 11:39:39 -0700493 self.log.exception( "Exception while disconnecting from " +
494 str( component ) )
495 result = self.FALSE
496 # Closing all the driver's session files
497 for driver in self.componentDictionary.keys():
498 try:
Jon Hall714eeba2015-09-29 17:53:10 -0700499 vars( self )[driver].close_log_handles()
Jon Hall1306a562015-09-04 11:21:24 -0700500 except KeyboardInterrupt:
501 pass
502 except KeyError:
503 # Component not created yet
504 self.log.warn( "Could not find the component " +
505 str( driver ) + " while trying to" +
506 " close log file" )
507 except StandardError:
Jon Hall5b586732015-06-11 11:39:39 -0700508 self.log.exception( "Exception while closing log files for " +
509 str( driver ) )
510 result = self.FALSE
511 else:
512 pass # Someone else already ran through this function
513 finally:
514 lock.release()
515 else: # Someone already has a lock
516 # NOTE: This could cause problems if we don't release the lock
517 # correctly
518 lock.acquire() # Wait for the other thread to finish
519 # NOTE: If we don't wait, exit could be called while the thread
520 # with the lock is still cleaning up
521 lock.release()
adminbae64d82013-08-01 10:50:15 -0700522 return result
Jon Halld61331b2015-02-17 16:35:47 -0800523
Jon Hall714eeba2015-09-29 17:53:10 -0700524 def pause( self ):
adminbae64d82013-08-01 10:50:15 -0700525 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700526 This function will pause the test's execution, and will continue after
527 user provide 'resume' command.
adminbae64d82013-08-01 10:50:15 -0700528 '''
529 __builtin__.testthread.pause()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700530
Jon Hall714eeba2015-09-29 17:53:10 -0700531 def onfail( self, *components ):
adminbae64d82013-08-01 10:50:15 -0700532 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -0700533 When test step failed, calling all the components onfail.
adminbae64d82013-08-01 10:50:15 -0700534 '''
adminbae64d82013-08-01 10:50:15 -0700535 if not components:
Jon Hall714eeba2015-09-29 17:53:10 -0700536 try:
adminbae64d82013-08-01 10:50:15 -0700537 for component in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700538 tempObject = vars( self )[component]
adminbae64d82013-08-01 10:50:15 -0700539 result = tempObject.onfail()
Jon Hall1306a562015-09-04 11:21:24 -0700540 except StandardError as e:
Jon Hall714eeba2015-09-29 17:53:10 -0700541 print str( e )
adminbae64d82013-08-01 10:50:15 -0700542 result = self.FALSE
adminbae64d82013-08-01 10:50:15 -0700543 else:
Jon Hall714eeba2015-09-29 17:53:10 -0700544 try:
adminbae64d82013-08-01 10:50:15 -0700545 for component in components:
Jon Hall714eeba2015-09-29 17:53:10 -0700546 tempObject = vars( self )[component]
adminbae64d82013-08-01 10:50:15 -0700547 result = tempObject.onfail()
Jon Hall1306a562015-09-04 11:21:24 -0700548 except StandardError as e:
Jon Hall714eeba2015-09-29 17:53:10 -0700549 print str( e )
adminbae64d82013-08-01 10:50:15 -0700550 result = self.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700551
Jon Hall714eeba2015-09-29 17:53:10 -0700552 def getDriverPath( self, driverName ):
adminbae64d82013-08-01 10:50:15 -0700553 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700554 Based on the component 'type' specified in the params , this method
555 will find the absolute path, by recursively searching the name of
556 the component.
557
558 NOTE: This function requires the linux 'find' command.
adminbae64d82013-08-01 10:50:15 -0700559 '''
560 import commands
561
Jon Hall714eeba2015-09-29 17:53:10 -0700562 cmd = "find " + drivers_path + " -name " + driverName + ".py"
563 result = commands.getoutput( cmd )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700564
Jon Hall714eeba2015-09-29 17:53:10 -0700565 result_array = str( result ).split( '\n' )
adminbae64d82013-08-01 10:50:15 -0700566 result_count = 0
kelvin-onlabf70fd542015-05-07 18:41:40 -0700567
adminbae64d82013-08-01 10:50:15 -0700568 for drivers_list in result_array:
Jon Hall714eeba2015-09-29 17:53:10 -0700569 result_count = result_count + 1
570 if result_count > 1:
571 print "Found " + driverName + " " + str( result_count ) + " times:"
572 print str( result_array )
adminbae64d82013-08-01 10:50:15 -0700573 self.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700574
Jon Hall714eeba2015-09-29 17:53:10 -0700575 result = re.sub( "(.*)drivers", "", result )
576 result = re.sub( "\/\/", "/", result )
577 result = re.sub( "\.py", "", result )
578 result = re.sub( "\.pyc", "", result )
579 result = re.sub( "\/", ".", result )
580 result = "drivers" + result
adminbae64d82013-08-01 10:50:15 -0700581 return result
adminbae64d82013-08-01 10:50:15 -0700582
Jon Hall714eeba2015-09-29 17:53:10 -0700583 def step( self, stepDesc ):
adminbae64d82013-08-01 10:50:15 -0700584 '''
585 The step information of the test-case will append to the logs.
586 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700587 previousStep = " " + str( self.CurrentTestCaseNumber ) + "." +\
588 str( self.stepNumber ) + ": " + str( self.stepName )
adminbae64d82013-08-01 10:50:15 -0700589 self.stepName = stepDesc
Jon Hall5a72b712015-09-28 12:20:59 -0700590 self.stepNumber += 1
591 self.stepResults[0].append( self.stepNumber )
592 self.stepResults[1].append( stepDesc )
593 self.stepResults[2].append( self.NORESULT )
594 self.stepResults[3].append( "No on fail message given" )
adminbae64d82013-08-01 10:50:15 -0700595
Jon Hall714eeba2015-09-29 17:53:10 -0700596 stepName = " " + str( self.CurrentTestCaseNumber ) + "." +\
597 str( self.stepNumber ) + ": " + str( stepDesc )
adminbae64d82013-08-01 10:50:15 -0700598 self.log.step(stepName)
599 stepHeader = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700600 line = "\n" + "-" * 45 + "\n"
601 if self.stepNumber > 1:
602 stepHeader = line + "End of Step " + previousStep + line
603 stepHeader += line + "Start of Step" + stepName + line
adminbae64d82013-08-01 10:50:15 -0700604 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700605 vars( self )[driver + 'log'].info( stepHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700606
Jon Hall714eeba2015-09-29 17:53:10 -0700607 def case( self, testCaseName ):
adminbae64d82013-08-01 10:50:15 -0700608 '''
609 Test's each test-case information will append to the logs.
610 '''
Jon Halld61331b2015-02-17 16:35:47 -0800611 self.CurrentTestCase = testCaseName
Jon Hall714eeba2015-09-29 17:53:10 -0700612 testCaseName = " " + str( testCaseName )
613 self.log.case( testCaseName )
614 caseHeader = testCaseName + "\n" + "*" * 40 + "\n"
adminbae64d82013-08-01 10:50:15 -0700615 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700616 vars( self )[driver + 'log'].info( caseHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700617
Jon Hall714eeba2015-09-29 17:53:10 -0700618 def testDesc( self, description ):
adminbae64d82013-08-01 10:50:15 -0700619 '''
620 Test description will append to the logs.
621 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700622 description = "Test Description : " + str( description )
623 self.log.info( description )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700624
Jon Hall714eeba2015-09-29 17:53:10 -0700625 def _getTest( self ):
adminbae64d82013-08-01 10:50:15 -0700626 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700627 This method will parse the test script to find required test
628 information.
adminbae64d82013-08-01 10:50:15 -0700629 '''
Jon Hall53c5e662016-04-13 16:06:56 -0700630 testFileHandler = open( main.testFile, 'r' )
adminbae64d82013-08-01 10:50:15 -0700631 testFileList = testFileHandler.readlines()
632 testFileHandler.close()
adminbae64d82013-08-01 10:50:15 -0700633 counter = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700634 for index in range( len( testFileList ) ):
635 lineMatch = re.match( '\s+def CASE(\d+)(.*):',
636 testFileList[index],
637 0 )
adminbae64d82013-08-01 10:50:15 -0700638 if lineMatch:
Jon Hall714eeba2015-09-29 17:53:10 -0700639 counter = counter + 1
640 self.TC_PLANNED = len( self.testcases_list )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700641
Jon Hall714eeba2015-09-29 17:53:10 -0700642 def response_parser( self, response, return_format ):
adminbae64d82013-08-01 10:50:15 -0700643 ''' It will load the default response parser '''
644 response_dict = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700645 response_dict = self.response_to_dict( response, return_format )
646 return_format_string = self.dict_to_return_format( response,
647 return_format,
648 response_dict )
adminbae64d82013-08-01 10:50:15 -0700649 return return_format_string
kelvin-onlabf70fd542015-05-07 18:41:40 -0700650
Jon Hall714eeba2015-09-29 17:53:10 -0700651 def response_to_dict( self, response, return_format ):
adminbae64d82013-08-01 10:50:15 -0700652 response_dict = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700653 json_match = re.search( '^\s*{', response )
654 xml_match = re.search( '^\s*\<', response )
655 ini_match = re.search( '^\s*\[', response )
656 if json_match:
657 self.log.info( "Response is in 'JSON' format, converting to '" +
658 return_format + "' format" )
Jon Halld61331b2015-02-17 16:35:47 -0800659 # Formatting the json string
Jon Hall714eeba2015-09-29 17:53:10 -0700660 response = re.sub( r"{\s*'?(\w)", r'{"\1', response )
661 response = re.sub( r",\s*'?(\w)", r',"\1', response )
662 response = re.sub( r"(\w)'?\s*:", r'\1":', response )
663 response = re.sub( r":\s*'(\w)'\s*([,}])", r':"\1"\2', response )
664 try:
adminbae64d82013-08-01 10:50:15 -0700665 import json
Jon Hall714eeba2015-09-29 17:53:10 -0700666 response_dict = json.loads( response )
Jon Hall1306a562015-09-04 11:21:24 -0700667 except StandardError:
Jon Hall2a5002c2015-08-21 16:49:11 -0700668 self.log.exception( "Json Parser is unable to parse the string" )
adminbae64d82013-08-01 10:50:15 -0700669 return response_dict
Jon Hall714eeba2015-09-29 17:53:10 -0700670 elif ini_match:
671 self.log.info( "Response is in 'INI' format, converting to '" +
672 return_format + "' format" )
adminbae64d82013-08-01 10:50:15 -0700673 from configobj import ConfigObj
Jon Hall714eeba2015-09-29 17:53:10 -0700674 response_file = open( "respnse_file.temp", 'w' )
675 response_file.write( response )
Jon Halld61331b2015-02-17 16:35:47 -0800676 response_file.close()
Jon Hall714eeba2015-09-29 17:53:10 -0700677 response_dict = ConfigObj( "respnse_file.temp" )
adminbae64d82013-08-01 10:50:15 -0700678 return response_dict
Jon Hall714eeba2015-09-29 17:53:10 -0700679 elif xml_match:
680 self.log.info( "Response is in 'XML' format, converting to '" +
681 return_format + "' format" )
682 try:
683 response_dict = xmldict.xml_to_dict( "<response> " +
684 str( response ) +
685 " </response>" )
Jon Hall1306a562015-09-04 11:21:24 -0700686 except StandardError:
687 self.log.exception()
adminbae64d82013-08-01 10:50:15 -0700688 return response_dict
kelvin-onlabf70fd542015-05-07 18:41:40 -0700689
Jon Hall714eeba2015-09-29 17:53:10 -0700690 def dict_to_return_format( self, response, return_format, response_dict ):
691 if return_format == 'table':
adminbae64d82013-08-01 10:50:15 -0700692 ''' Will return in table format'''
693 to_do = "Call the table output formatter"
694 global response_table
695 response_table = '\n'
Jon Hall714eeba2015-09-29 17:53:10 -0700696 response_table = response_table + '\t'.join( response_dict ) + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700697
Jon Hall714eeba2015-09-29 17:53:10 -0700698 def get_table( value_to_convert ):
699 ''' This will parse the dictionary recusrsively and print as
700 table format'''
adminbae64d82013-08-01 10:50:15 -0700701 table_data = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700702 if isinstance( value_to_convert, dict ):
703 table_data = table_data + '\t'.join( value_to_convert ) +\
704 "\n"
705 for temp_val in value_to_convert.values():
706 table_data = table_data + get_table( temp_val )
707 else:
708 table_data = table_data + str( value_to_convert ) + "\t"
Jon Halld61331b2015-02-17 16:35:47 -0800709 return table_data
kelvin-onlabf70fd542015-05-07 18:41:40 -0700710
Jon Hall714eeba2015-09-29 17:53:10 -0700711 for value in response_dict.values():
712 response_table = response_table + get_table( value )
adminbae64d82013-08-01 10:50:15 -0700713 return response_table
kelvin-onlabf70fd542015-05-07 18:41:40 -0700714
Jon Hall714eeba2015-09-29 17:53:10 -0700715 elif return_format == 'config':
adminbae64d82013-08-01 10:50:15 -0700716 ''' Will return in config format'''
717 to_do = 'Call dict to config coverter'
Jon Hall714eeba2015-09-29 17:53:10 -0700718 response_string = str( response_dict )
adminbae64d82013-08-01 10:50:15 -0700719 print response_string
Jon Hall714eeba2015-09-29 17:53:10 -0700720 response_config = re.sub( ",", "\n\t", response_string )
721 response_config = re.sub( "u\'", "\'", response_config )
722 response_config = re.sub( "{", "", response_config )
723 response_config = re.sub( "}", "\n", response_config )
724 response_config = re.sub( ":", " =", response_config )
725 return "[response]\n\t " + response_config
adminbae64d82013-08-01 10:50:15 -0700726 elif return_format == 'xml':
727 ''' Will return in xml format'''
Jon Hall714eeba2015-09-29 17:53:10 -0700728 response_xml = xmldict.dict_to_xml( response_dict )
729 response_xml = re.sub( ">\s*<", ">\n<", response_xml )
730 return "\n" + response_xml
adminbae64d82013-08-01 10:50:15 -0700731 elif return_format == 'json':
732 ''' Will return in json format'''
733 to_do = 'Call dict to xml coverter'
734 import json
Jon Hall714eeba2015-09-29 17:53:10 -0700735 response_json = json.dumps( response_dict )
adminbae64d82013-08-01 10:50:15 -0700736 return response_json
kelvin-onlabf70fd542015-05-07 18:41:40 -0700737
Jon Hall714eeba2015-09-29 17:53:10 -0700738 def get_random( self ):
adminbae64d82013-08-01 10:50:15 -0700739 self.random_order = self.random_order + 1
740 return self.random_order
kelvin-onlabf70fd542015-05-07 18:41:40 -0700741
Jon Hall714eeba2015-09-29 17:53:10 -0700742 def exit( self ):
adminbae64d82013-08-01 10:50:15 -0700743 __builtin__.testthread = None
Jon Hall5b586732015-06-11 11:39:39 -0700744 for thread in threading.enumerate():
745 if thread.isAlive():
746 try:
747 thread._Thread__stop()
748 except:
Jon Hall1306a562015-09-04 11:21:24 -0700749 # NOTE: We should catch any exceptions while trying to
750 # close the thread so that we can try to close the other
751 # threads as well
Jon Hall714eeba2015-09-29 17:53:10 -0700752 print str( thread.getName() ) +\
753 ' could not be terminated'
Jeremy Ronquillo15ff1072017-07-17 10:55:46 -0700754 os.system( "stty sane" ) # fix format if necessary
adminbae64d82013-08-01 10:50:15 -0700755 sys.exit()
756
Devin Lim44075962017-08-11 10:56:37 -0700757 def cleanAndExit( self ):
758 """
759 It will set the testcase result to be FAILED and update it
760 before cleaning up and exitting the test.
761 :return:
762 """
763 if self.CurrentTestCaseNumber:
764 self.testCaseResult[ str( self.CurrentTestCaseNumber ) ] = self.FALSE
765 self.logger.updateCaseResults( self )
766 self.cleanup()
767 self.exit()
Jeremy Ronquillo15ff1072017-07-17 10:55:46 -0700768
Jon Hallcd3d2a32015-10-01 11:07:28 -0700769 def stop( self, email=False ):
770 """
771 Stop the test until Ctrl-D is entered.
772 Ctrl-C will kill the test
Jon Hall25079782015-10-13 13:54:39 -0700773
774 Optional arguments:
775 email can either be a bool, or you can specify the email address
776 to send the email to
Jon Hallcd3d2a32015-10-01 11:07:28 -0700777 """
778 try:
779 if email:
Jon Hall25079782015-10-13 13:54:39 -0700780 if '@' in email:
781 main.mail = email
782 utilities.send_warning_email()
Jeremy Ronquillo15ff1072017-07-17 10:55:46 -0700783 self.log.debug( "Test execution suspended. \n"
784 "- Type 'c' to resume the test.\n"
785 "- Type Ctrl-C to exit the test.\n"
786 "- Enter interactive python interpreter commands.\n"
787 "\t- ie: main.Mininet1.pingall()\n"
788 "- Type 'help' for help with pdb." )
789 pdb.set_trace()
Jon Hallcd3d2a32015-10-01 11:07:28 -0700790 except EOFError:
791 return
792 # Pass all other exceptions up to caller
793
794
Jon Hall714eeba2015-09-29 17:53:10 -0700795def verifyOptions( options ):
adminbae64d82013-08-01 10:50:15 -0700796 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700797 This will verify the command line options and set to default values,
798 if any option not given in command line.
adminbae64d82013-08-01 10:50:15 -0700799 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700800 verifyTest( options )
801 verifyExample( options )
802 verifyTestScript( options )
YPZhang1c89e762016-06-29 10:43:58 -0700803 verifyParams( options )
Jon Hall714eeba2015-09-29 17:53:10 -0700804 verifyLogdir( options )
805 verifyMail( options )
806 verifyTestCases( options )
807 verifyOnosCell( options )
adminbae64d82013-08-01 10:50:15 -0700808
Jon Hall714eeba2015-09-29 17:53:10 -0700809def verifyTest( options ):
Jon Hall44506242015-07-29 17:40:26 -0700810 try:
811 if options.testname:
812 main.TEST = options.testname
Jon Hall714eeba2015-09-29 17:53:10 -0700813 main.classPath = "tests." + main.TEST + "." + main.TEST
Jon Hall44506242015-07-29 17:40:26 -0700814 main.tests_path = tests_path
815 elif options.example:
816 main.TEST = options.example
Jon Hall714eeba2015-09-29 17:53:10 -0700817 main.tests_path = path + "/examples/"
818 main.classPath = "examples." + main.TEST + "." + main.TEST
Jon Hall44506242015-07-29 17:40:26 -0700819 except AttributeError:
adminbae64d82013-08-01 10:50:15 -0700820 print "Test or Example not specified please specify the --test <test name > or --example <example name>"
Jon Hall5b586732015-06-11 11:39:39 -0700821 main.exit()
adminbae64d82013-08-01 10:50:15 -0700822
Jon Hall714eeba2015-09-29 17:53:10 -0700823def verifyExample( options ):
adminbae64d82013-08-01 10:50:15 -0700824 if options.example:
Jon Hall714eeba2015-09-29 17:53:10 -0700825 main.testDir = path + '/examples/'
826 main.tests_path = path + "/examples/"
827 main.classPath = "examples." + main.TEST + "." + main.TEST
kelvin-onlabf70fd542015-05-07 18:41:40 -0700828
Jon Hall714eeba2015-09-29 17:53:10 -0700829def verifyLogdir( options ):
Jon Hall88e498c2015-03-06 09:54:35 -0800830 # Verifying Log directory option
adminbae64d82013-08-01 10:50:15 -0700831 if options.logdir:
832 main.logdir = options.logdir
Jon Hall714eeba2015-09-29 17:53:10 -0700833 else:
Jon Halld61331b2015-02-17 16:35:47 -0800834 main.logdir = main.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700835
Jon Hall714eeba2015-09-29 17:53:10 -0700836def verifyMail( options ):
Jon Hall25079782015-10-13 13:54:39 -0700837 # Mail-To: field
838 if options.mail: # Test run specific
adminbae64d82013-08-01 10:50:15 -0700839 main.mail = options.mail
Jon Hall25079782015-10-13 13:54:39 -0700840 elif main.params.get('mail'): # Test suite specific
841 main.mail = main.params.get( 'mail' )
842 else: # TestON specific
843 main.mail = main.config['config'].get( 'mail_to' )
844 # Mail-From: field
845 main.sender = main.config['config'].get( 'mail_from' )
846 # Mail smtp server
847 main.smtp = main.config['config'].get( 'mail_server' )
848 # Mail-From account password
849 main.senderPwd = main.config['config'].get( 'mail_pass' )
850
Devin Lim2df68a12017-06-30 15:39:05 -0700851def evalTestCase( tempList ):
852 tList = []
853 for tcase in tempList:
854 if isinstance( tcase, list ):
855 tList.extend( evalTestCase( tcase ) )
856 else:
857 tList.extend( [tcase] )
858 return tList
adminbae64d82013-08-01 10:50:15 -0700859
Jon Hall714eeba2015-09-29 17:53:10 -0700860def verifyTestCases( options ):
Jon Hall88e498c2015-03-06 09:54:35 -0800861 # Getting Test cases list
adminbae64d82013-08-01 10:50:15 -0700862 if options.testcases:
Jon Hallfebb1c72015-03-05 13:30:09 -0800863 testcases_list = options.testcases
Jon Hall88e498c2015-03-06 09:54:35 -0800864 # sys.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700865 testcases_list = re.sub( "(\[|\])", "", options.testcases )
866 main.testcases_list = eval( testcases_list + "," )
867 else:
adminbae64d82013-08-01 10:50:15 -0700868 if 'testcases' in main.params.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700869 temp = eval( main.params['testcases'] + "," )
Devin Lim2df68a12017-06-30 15:39:05 -0700870 main.testcases_list = evalTestCase( list( temp ) )
Jon Hall714eeba2015-09-29 17:53:10 -0700871 else:
872 print "Testcases not specifed in params, please provide in " +\
873 "params file or 'testcases' commandline argument"
Jon Halld61331b2015-02-17 16:35:47 -0800874 sys.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700875
Jon Hall714eeba2015-09-29 17:53:10 -0700876def verifyOnosCell( options ):
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700877 # Verifying onoscell option
Hari Krishna03f530e2015-07-10 17:28:27 -0700878 if options.onoscell:
879 main.onoscell = options.onoscell
Devin Lim58046fa2017-07-05 16:55:00 -0700880 main.ONOSip = []
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700881 main.mnIP = ""
Devin Lim58046fa2017-07-05 16:55:00 -0700882 cellCMD = ". ~/onos/tools/dev/bash_profile; cell " + main.onoscell
Jon Hall714eeba2015-09-29 17:53:10 -0700883 output = subprocess.check_output( ["bash", '-c', cellCMD] )
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700884 splitOutput = output.splitlines()
Devin Lim58046fa2017-07-05 16:55:00 -0700885 main.apps = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700886 for i in range( len( splitOutput ) ):
887 if re.match( "OCN", splitOutput[i] ):
888 mnNode = splitOutput[i].split( "=" )
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700889 main.mnIP = mnNode[1]
Jon Hall714eeba2015-09-29 17:53:10 -0700890 # cell already sorts OC variables in bash, so no need to
891 # sort in TestON
Devin Lim58046fa2017-07-05 16:55:00 -0700892 elif re.match( "OC[1-9]", splitOutput[i] ):
Jon Hall714eeba2015-09-29 17:53:10 -0700893 onosNodes = splitOutput[i].split( "=" )
Devin Lim58046fa2017-07-05 16:55:00 -0700894 main.ONOSip.append( onosNodes[1] )
895 elif re.match( "ONOS_APPS", splitOutput[i] ):
896 main.apps = ( splitOutput[i].split( "=" ) )[1]
Jon Hall714eeba2015-09-29 17:53:10 -0700897 else:
Hari Krishna03f530e2015-07-10 17:28:27 -0700898 main.onoscell = main.FALSE
899
Jon Hall714eeba2015-09-29 17:53:10 -0700900def verifyTestScript( options ):
adminbae64d82013-08-01 10:50:15 -0700901 '''
902 Verifyies test script.
903 '''
Jon Halld61331b2015-02-17 16:35:47 -0800904 main.openspeak = openspeak.OpenSpeak()
Jon Hall53c5e662016-04-13 16:06:56 -0700905 directory = main.testDir + "/" + main.TEST
906 if os.path.exists( directory ):
907 pass
908 else:
909 directory = ""
910 for root, dirs, files in os.walk( main.testDir, topdown=True):
911 if not directory:
912 for name in dirs:
913 if name == main.TEST:
914 directory = ( os.path.join( root, name ) )
915 index = directory.find( "/tests/" ) + 1
916 main.classPath = directory[index:].replace( '/', '.' ) + "." + main.TEST
917 break
918 openspeakfile = directory + "/" + main.TEST + ".ospk"
919 main.testFile = directory + "/" + main.TEST + ".py"
Jon Hall714eeba2015-09-29 17:53:10 -0700920 if os.path.exists( openspeakfile ):
Jon Hall44506242015-07-29 17:40:26 -0700921 # Openspeak file found, compiling to python
Jon Hall714eeba2015-09-29 17:53:10 -0700922 main.openspeak.compiler( openspeakfile=openspeakfile, writetofile=1 )
Jon Hall53c5e662016-04-13 16:06:56 -0700923 elif os.path.exists( main.testFile ):
Jon Hall44506242015-07-29 17:40:26 -0700924 # No openspeak found, using python file instead
925 pass
adminbae64d82013-08-01 10:50:15 -0700926 else:
Jon Hall714eeba2015-09-29 17:53:10 -0700927 print "\nThere is no \"" + main.TEST + "\" test script.\nPlease provide a " +\
Jon Hall44506242015-07-29 17:40:26 -0700928 "Python or OpenSpeak test script in the tests folder: " +\
Jon Hall714eeba2015-09-29 17:53:10 -0700929 main.testDir + "/" + main.TEST + "/"
adminbae64d82013-08-01 10:50:15 -0700930 __builtin__.testthread = None
931 main.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700932 try:
933 testModule = __import__( main.classPath,
934 globals(),
935 locals(),
936 [main.TEST],
937 -1 )
Jon Hall1306a562015-09-04 11:21:24 -0700938 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -0700939 print "There was an import error, it might mean that there is " +\
940 "no test named " + main.TEST
Jon Halld61331b2015-02-17 16:35:47 -0800941 main.exit()
adminbae64d82013-08-01 10:50:15 -0700942
Jon Hall714eeba2015-09-29 17:53:10 -0700943 testClass = getattr( testModule, main.TEST )
adminbae64d82013-08-01 10:50:15 -0700944 main.testObject = testClass()
945 load_parser()
Jon Hall714eeba2015-09-29 17:53:10 -0700946 main.params = main.parser.parseParams( main.classPath )
947 main.topology = main.parser.parseTopology( main.classPath )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700948
YPZhang1c89e762016-06-29 10:43:58 -0700949def verifyParams( options ):
Jon Hall714eeba2015-09-29 17:53:10 -0700950 try:
adminbae64d82013-08-01 10:50:15 -0700951 main.params = main.params['PARAMS']
Jon Hall1306a562015-09-04 11:21:24 -0700952 except KeyError:
Jon Hall714eeba2015-09-29 17:53:10 -0700953 print "Error with the params file: Either the file not specified " +\
954 "or the format is not correct"
Jon Halld61331b2015-02-17 16:35:47 -0800955 main.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700956 try:
adminbae64d82013-08-01 10:50:15 -0700957 main.topology = main.topology['TOPOLOGY']
Jon Hall1306a562015-09-04 11:21:24 -0700958 except KeyError:
Jon Hall714eeba2015-09-29 17:53:10 -0700959 print "Error with the Topology file: Either the file not specified " +\
960 "or the format is not correct"
adminbae64d82013-08-01 10:50:15 -0700961 main.exit()
YPZhang1c89e762016-06-29 10:43:58 -0700962 # Overwrite existing params variables if they are specified from command line
963 if len(options.params) > 0:
964 # Some params variables are specified from command line
965 for param in options.params:
966 if not re.search( ".=.", param ):
967 print( "Error when parsing params: params should follow key=value format" )
968 continue
Jon Hall7c4f4302016-07-15 14:39:02 -0700969 # Split the param string to catch nested keys and the value
YPZhang1c89e762016-06-29 10:43:58 -0700970 [ keys, value ] = param.split( "=" )
971 # Split the nested keys according to its hierarchy
972 keyList = keys.split( "/" )
973 # Get the outermost dictionary
974 paramDict = main.params
975 # Get the innermost dictionary
976 try:
977 while len( keyList ) > 1:
978 key = keyList.pop(0)
979 assert isinstance( paramDict[ key ], dict )
980 paramDict = paramDict[ key ]
981 except KeyError:
982 print( "Error when parsing params: key \"" + key + "\" not found in main.params" )
983 main.exit()
984 except AssertionError:
985 print( "Error when parsing params: \"" + key + "\" is already the innermost level in main.params" )
986 main.exit()
987 # Change the value
988 if not paramDict.has_key( keyList[0] ):
989 print( "Error when parsing params: key \"" + keyList[0] + "\" not found in main.params" )
990 main.exit()
991 elif isinstance( paramDict[ keyList[0] ], dict ):
992 print( "Error when parsing params: more levels under key \"" + keyList[0] + "\" in main.params" )
993 main.exit()
994 else:
995 paramDict[ keyList[0] ] = value
kelvin-onlabf70fd542015-05-07 18:41:40 -0700996
Jon Hall714eeba2015-09-29 17:53:10 -0700997def load_parser():
adminbae64d82013-08-01 10:50:15 -0700998 '''
999 It facilitates the loading customised parser for topology and params file.
1000 It loads parser mentioned in tab named parser of teston.cfg file.
Jon Hall714eeba2015-09-29 17:53:10 -07001001 It also loads default xmlparser if no parser have specified in teston.cfg
1002 file.
adminbae64d82013-08-01 10:50:15 -07001003
1004 '''
1005 confighash = main.configDict
Jon Hall714eeba2015-09-29 17:53:10 -07001006 if 'file' in confighash['config']['parser'] and\
1007 'class' in confighash['config']['parser']:
Jon Hall44506242015-07-29 17:40:26 -07001008 path = confighash['config']['parser']['file']
Jon Hall714eeba2015-09-29 17:53:10 -07001009 if path is not None or\
1010 confighash['config']['parser']['class'] is not None:
Jon Hall44506242015-07-29 17:40:26 -07001011 try:
1012 module = re.sub( r".py\s*$", "", path )
adminbae64d82013-08-01 10:50:15 -07001013 moduleList = module.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -07001014 newModule = ".".join( moduleList[-2:] )
Jon Hall44506242015-07-29 17:40:26 -07001015 parsingClass = confighash['config']['parser']['class']
Jon Hall714eeba2015-09-29 17:53:10 -07001016 parsingModule = __import__( newModule,
1017 globals(),
1018 locals(),
1019 [parsingClass],
1020 -1 )
1021 parsingClass = getattr( parsingModule, parsingClass )
Jon Hall44506242015-07-29 17:40:26 -07001022 main.parser = parsingClass()
Jon Hall714eeba2015-09-29 17:53:10 -07001023 if hasattr( main.parser, "parseParams" ) and\
1024 hasattr( main.parser, "parseTopology" ) and\
1025 hasattr( main.parser, "parse" ):
Jon Hall44506242015-07-29 17:40:26 -07001026 pass
1027 else:
1028 print "Invalid parser format"
adminbae64d82013-08-01 10:50:15 -07001029 main.exit()
Jon Hall44506242015-07-29 17:40:26 -07001030 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -07001031 print "Could not find the file " + path +\
1032 " using default parser."
Jon Halld61331b2015-02-17 16:35:47 -08001033 load_defaultParser()
Jon Hall714eeba2015-09-29 17:53:10 -07001034 elif confighash['config']['parser']['file'] is None or\
1035 confighash['config']['parser']['class'] is None:
Jon Halld61331b2015-02-17 16:35:47 -08001036 load_defaultParser()
adminbae64d82013-08-01 10:50:15 -07001037 else:
1038 load_defaultParser()
1039
1040def load_defaultParser():
1041 '''
Jon Hall714eeba2015-09-29 17:53:10 -07001042 It will load the default parser which is xml parser to parse the params and
1043 topology file.
adminbae64d82013-08-01 10:50:15 -07001044 '''
1045 moduleList = main.parserPath.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -07001046 newModule = ".".join( moduleList[-2:] )
1047 try:
Jon Halld61331b2015-02-17 16:35:47 -08001048 parsingClass = main.parsingClass
Jon Hall714eeba2015-09-29 17:53:10 -07001049 parsingModule = __import__( newModule,
1050 globals(),
1051 locals(),
1052 [parsingClass],
1053 -1 )
1054 parsingClass = getattr( parsingModule, parsingClass )
adminbae64d82013-08-01 10:50:15 -07001055 main.parser = parsingClass()
Jon Hall714eeba2015-09-29 17:53:10 -07001056 if hasattr( main.parser, "parseParams" ) and\
1057 hasattr( main.parser, "parseTopology" ) and\
1058 hasattr( main.parser, "parse" ):
adminbae64d82013-08-01 10:50:15 -07001059 pass
1060 else:
1061 main.exit()
adminbae64d82013-08-01 10:50:15 -07001062 except ImportError:
1063 print sys.exc_info()[1]
1064
Jon Hall714eeba2015-09-29 17:53:10 -07001065def load_logger():
adminbae64d82013-08-01 10:50:15 -07001066 '''
1067 It facilitates the loading customised parser for topology and params file.
1068 It loads parser mentioned in tab named parser of teston.cfg file.
Jon Hall714eeba2015-09-29 17:53:10 -07001069 It also loads default xmlparser if no parser have specified in teston.cfg
1070 file.
adminbae64d82013-08-01 10:50:15 -07001071 '''
1072 confighash = main.configDict
Jon Hall714eeba2015-09-29 17:53:10 -07001073 if 'file' in confighash['config']['logger'] and\
1074 'class' in confighash['config']['logger']:
Jon Hall44506242015-07-29 17:40:26 -07001075 path = confighash['config']['logger']['file']
Jon Hall714eeba2015-09-29 17:53:10 -07001076 if path is not None or\
1077 confighash['config']['logger']['class'] is not None:
Jon Hall44506242015-07-29 17:40:26 -07001078 try:
1079 module = re.sub( r".py\s*$", "", path )
Jon Hall714eeba2015-09-29 17:53:10 -07001080 moduleList = module.split( "/" )
1081 newModule = ".".join( moduleList[-2:] )
Jon Hall44506242015-07-29 17:40:26 -07001082 loggerClass = confighash['config']['logger']['class']
Jon Hall714eeba2015-09-29 17:53:10 -07001083 loggerModule = __import__( newModule,
1084 globals(),
1085 locals(),
1086 [loggerClass],
1087 -1 )
1088 loggerClass = getattr( loggerModule, loggerClass )
Jon Hall44506242015-07-29 17:40:26 -07001089 main.logger = loggerClass()
Jon Hall44506242015-07-29 17:40:26 -07001090 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -07001091 print "Could not find the file " + path +\
1092 " using default logger."
adminbae64d82013-08-01 10:50:15 -07001093 load_defaultlogger()
Jon Hall714eeba2015-09-29 17:53:10 -07001094 elif confighash['config']['parser']['file'] is None or\
1095 confighash['config']['parser']['class'] is None:
Jon Halld61331b2015-02-17 16:35:47 -08001096 load_defaultlogger()
adminbae64d82013-08-01 10:50:15 -07001097 else:
1098 load_defaultlogger()
1099
1100def load_defaultlogger():
1101 '''
Jon Hall714eeba2015-09-29 17:53:10 -07001102 It will load the default parser which is xml parser to parse the params and
1103 topology file.
adminbae64d82013-08-01 10:50:15 -07001104 '''
1105 moduleList = main.loggerPath.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -07001106 newModule = ".".join( moduleList[-2:] )
1107 try:
Jon Halld61331b2015-02-17 16:35:47 -08001108 loggerClass = main.loggerClass
Jon Hall714eeba2015-09-29 17:53:10 -07001109 loggerModule = __import__( newModule,
1110 globals(),
1111 locals(),
1112 [loggerClass],
1113 -1 )
1114 loggerClass = getattr( loggerModule, loggerClass )
adminbae64d82013-08-01 10:50:15 -07001115 main.logger = loggerClass()
1116
1117 except ImportError:
1118 print sys.exc_info()[1]
Jon Halld61331b2015-02-17 16:35:47 -08001119 main.exit()
adminbae64d82013-08-01 10:50:15 -07001120
Jon Hall714eeba2015-09-29 17:53:10 -07001121def _echo( self ):
adminbae64d82013-08-01 10:50:15 -07001122 print "THIS IS ECHO"