blob: e4dde6ef7f0e4f9ebe43c8546d098ab1273283ca [file] [log] [blame]
adminbae64d82013-08-01 10:50:15 -07001#!/usr/bin/env python
2'''
3Created on 22-Oct-2012
Jeremy Songsterae01bba2016-07-11 15:39:17 -07004Modified 2016 by ON.Lab
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
Jon Hall714eeba2015-09-29 17:53:10 -070038module = new.module( "test" )
adminbae64d82013-08-01 10:50:15 -070039import openspeak
Hari Krishnabe4b97b2015-07-15 12:19:43 -070040import subprocess
Jon Hall714eeba2015-09-29 17:53:10 -070041global path, drivers_path, core_path, tests_path, logs_path
Jon Hall44506242015-07-29 17:40:26 -070042location = os.path.abspath( os.path.dirname( __file__ ) )
43path = re.sub( "(core|bin)$", "", location )
Jon Hall714eeba2015-09-29 17:53:10 -070044drivers_path = path + "drivers"
45core_path = path + "core"
46tests_path = path + "tests"
47logs_path = path + "logs/"
adminbae64d82013-08-01 10:50:15 -070048config_path = path + "config/"
Jon Hall44506242015-07-29 17:40:26 -070049sys.path.append( path )
50sys.path.append( drivers_path )
51sys.path.append( core_path )
52sys.path.append( tests_path )
adminbae64d82013-08-01 10:50:15 -070053
54from core.utilities import Utilities
kelvin-onlabfb521662015-02-27 09:52:40 -080055from core.Thread import Thread
adminbae64d82013-08-01 10:50:15 -070056
adminbae64d82013-08-01 10:50:15 -070057class TestON:
58 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -070059 TestON will initiate the specified test.
Jon Hall714eeba2015-09-29 17:53:10 -070060 The main tasks are:
kelvin-onlabf70fd542015-05-07 18:41:40 -070061 * Initiate the required Component handles for the test.
adminbae64d82013-08-01 10:50:15 -070062 * Create Log file Handles.
adminbae64d82013-08-01 10:50:15 -070063 '''
Jon Hall714eeba2015-09-29 17:53:10 -070064 def __init__( self, options ):
adminbae64d82013-08-01 10:50:15 -070065 '''
Jon Hall714eeba2015-09-29 17:53:10 -070066 Initialise the component handles specified in the topology file of
67 the specified test.
adminbae64d82013-08-01 10:50:15 -070068 '''
69 # Initialization of the variables.
70 __builtin__.main = self
adminbae64d82013-08-01 10:50:15 -070071 __builtin__.path = path
72 __builtin__.utilities = Utilities()
73 self.TRUE = 1
74 self.FALSE = 0
75 self.ERROR = -1
kelvin-onlabf70fd542015-05-07 18:41:40 -070076 self.NORESULT = 2
adminbae64d82013-08-01 10:50:15 -070077 self.FAIL = False
78 self.PASS = True
kelvin-onlabf70fd542015-05-07 18:41:40 -070079 self.CASERESULT = self.ERROR
80 self.STEPRESULT = self.NORESULT
adminbae64d82013-08-01 10:50:15 -070081 self.init_result = self.TRUE
82 self.testResult = "Summary"
kelvin-onlabf70fd542015-05-07 18:41:40 -070083 self.stepName = ""
84 self.stepCache = ""
Jon Halld61331b2015-02-17 16:35:47 -080085 self.EXPERIMENTAL_MODE = False
adminbae64d82013-08-01 10:50:15 -070086 self.test_target = None
87 self.lastcommand = None
Jon Halld61331b2015-02-17 16:35:47 -080088 self.testDir = tests_path
89 self.configFile = config_path + "teston.cfg"
adminbae64d82013-08-01 10:50:15 -070090 self.parsingClass = "xmlparser"
91 self.parserPath = core_path + "/xmlparser"
92 self.loggerPath = core_path + "/logger"
93 self.loggerClass = "Logger"
94 self.logs_path = logs_path
95 self.driver = ''
kelvin-onlabfb521662015-02-27 09:52:40 -080096 self.Thread = Thread
Jon Hall5b586732015-06-11 11:39:39 -070097 self.cleanupFlag = False
98 self.cleanupLock = threading.Lock()
Jon Hall0fc0d452015-07-14 09:49:58 -070099 self.initiated = False
Jon Hall65844a32015-03-09 19:09:37 -0700100
Jon Hall25079782015-10-13 13:54:39 -0700101 self.config = self.configparser()
Jon Hall714eeba2015-09-29 17:53:10 -0700102 verifyOptions( options )
adminbae64d82013-08-01 10:50:15 -0700103 load_logger()
104 self.componentDictionary = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700105 self.componentDictionary = self.topology['COMPONENT']
106 self.driversList = []
107 if isinstance( self.componentDictionary, str):
108 self.componentDictionary = dict( self.componentDictionary )
Jon Hall65844a32015-03-09 19:09:37 -0700109
Jon Hall714eeba2015-09-29 17:53:10 -0700110 for component in self.componentDictionary:
111 self.driversList.append( self.componentDictionary[component]['type'] )
Jon Hall65844a32015-03-09 19:09:37 -0700112
Jon Hall714eeba2015-09-29 17:53:10 -0700113 self.driversList = list( set( self.driversList ) ) # Removing duplicates.
adminbae64d82013-08-01 10:50:15 -0700114 # Checking the test_target option set for the component or not
Jon Hall714eeba2015-09-29 17:53:10 -0700115 if isinstance( self.componentDictionary, dict ):
adminbae64d82013-08-01 10:50:15 -0700116 for component in self.componentDictionary.keys():
117 if 'test_target' in self.componentDictionary[component].keys():
118 self.test_target = component
Jon Hall65844a32015-03-09 19:09:37 -0700119
Jon Halld61331b2015-02-17 16:35:47 -0800120 # Checking for the openspeak file and test script
Jon Hall714eeba2015-09-29 17:53:10 -0700121 self.logger.initlog( self )
adminbae64d82013-08-01 10:50:15 -0700122
123 # Creating Drivers Handles
Jon Hall714eeba2015-09-29 17:53:10 -0700124 initString = "\n" + "*" * 30 + "\n CASE INIT \n" + "*" * 30 + "\n"
125 self.log.exact( initString )
adminbae64d82013-08-01 10:50:15 -0700126 self.driverObject = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700127 self.random_order = 111 # Random order id to connect the components
adminbae64d82013-08-01 10:50:15 -0700128 components_connect_order = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700129 if isinstance( self.componentDictionary, dict ):
adminbae64d82013-08-01 10:50:15 -0700130 for component in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700131 if 'connect_order' not in self.componentDictionary[component].keys():
132 self.componentDictionary[component]['connect_order'] = str( self.get_random() )
133 components_connect_order[component] = eval( self.componentDictionary[component]['connect_order'] )
134 # Ordering components based on the connect order.
135 ordered_component_list = sorted( components_connect_order,
136 key=lambda key: components_connect_order[key] )
adminbae64d82013-08-01 10:50:15 -0700137 print ordered_component_list
adminbae64d82013-08-01 10:50:15 -0700138 for component in ordered_component_list:
Jon Hall714eeba2015-09-29 17:53:10 -0700139 self.componentInit( component )
adminbae64d82013-08-01 10:50:15 -0700140
Jon Hall714eeba2015-09-29 17:53:10 -0700141 def configparser( self ):
adminbae64d82013-08-01 10:50:15 -0700142 '''
143 It will parse the config file (teston.cfg) and return as dictionary
144 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700145 matchFileName = re.match( r'(.*)\.cfg', self.configFile, re.M | re.I )
adminbae64d82013-08-01 10:50:15 -0700146 if matchFileName:
Jon Hall714eeba2015-09-29 17:53:10 -0700147 xml = open( self.configFile ).read()
148 try:
149 self.configDict = xmldict.xml_to_dict( xml )
adminbae64d82013-08-01 10:50:15 -0700150 return self.configDict
Jon Hall1306a562015-09-04 11:21:24 -0700151 except IOError:
adminbae64d82013-08-01 10:50:15 -0700152 print "There is no such file to parse " + self.configFile
Jon Hall1306a562015-09-04 11:21:24 -0700153 else:
154 print "There is no such file to parse " + self.configFile
kelvin-onlabf70fd542015-05-07 18:41:40 -0700155
Jon Hall714eeba2015-09-29 17:53:10 -0700156 def componentInit( self, component ):
adminbae64d82013-08-01 10:50:15 -0700157 '''
158 This method will initialize specified component
159 '''
160 global driver_options
Jon Hall0fc0d452015-07-14 09:49:58 -0700161 self.initiated = False
Jon Hall714eeba2015-09-29 17:53:10 -0700162 self.log.info( "Creating component Handle: " + component )
Jon Halld61331b2015-02-17 16:35:47 -0800163 driver_options = {}
adminbae64d82013-08-01 10:50:15 -0700164 if 'COMPONENTS' in self.componentDictionary[component].keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700165 driver_options = dict( self.componentDictionary[component]['COMPONENTS'] )
Jon Hall714eeba2015-09-29 17:53:10 -0700166 driver_options['name'] = component
adminbae64d82013-08-01 10:50:15 -0700167 driverName = self.componentDictionary[component]['type']
Jon Hall714eeba2015-09-29 17:53:10 -0700168 driver_options['type'] = driverName
kelvin-onlabf70fd542015-05-07 18:41:40 -0700169
Jon Hall714eeba2015-09-29 17:53:10 -0700170 classPath = self.getDriverPath( driverName.lower() )
171 driverModule = importlib.import_module( classPath )
172 driverClass = getattr( driverModule, driverName )
adminbae64d82013-08-01 10:50:15 -0700173 driverObject = driverClass()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700174
Jon Hall714eeba2015-09-29 17:53:10 -0700175 if "OCN" in self.componentDictionary[component]['host'] and\
176 main.onoscell:
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700177 self.componentDictionary[component]['host'] = main.mnIP
178
Jon Hall714eeba2015-09-29 17:53:10 -0700179 user_name = self.componentDictionary[component].get( 'user',
180 getpass.getuser() )
181 ip_address = self.componentDictionary[component].get( 'host',
182 'localhost' )
183 pwd = self.componentDictionary[component].get( 'password',
184 'changeme' )
185 port = self.componentDictionary[component].get( 'port' )
186 connect_result = driverObject.connect( user_name=user_name,
187 ip_address=ip_address,
188 pwd=pwd,
189 port=port,
190 options=driver_options)
cameron@onlab.us5cc6a372015-05-11 17:18:07 -0700191
adminbae64d82013-08-01 10:50:15 -0700192 if not connect_result:
Jon Hall714eeba2015-09-29 17:53:10 -0700193 self.log.error( "Exiting from the test execution because connecting to the " +
194 component + " component failed." )
Jon Halld61331b2015-02-17 16:35:47 -0800195 self.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700196
Jon Hall714eeba2015-09-29 17:53:10 -0700197 vars( self )[component] = driverObject
Jon Hall0fc0d452015-07-14 09:49:58 -0700198 self.initiated = True
kelvin-onlabf70fd542015-05-07 18:41:40 -0700199
Jon Hall714eeba2015-09-29 17:53:10 -0700200 def run( self ):
adminbae64d82013-08-01 10:50:15 -0700201 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700202 The Execution of the test script's cases listed in the Test params
203 file will be done here then update each test case result.
204 This method will return main.TRUE if it executed all the test cases
205 successfully, else will retun main.FALSE
adminbae64d82013-08-01 10:50:15 -0700206 '''
adminbae64d82013-08-01 10:50:15 -0700207 self.testCaseResult = {}
Jon Halla1185982014-09-15 14:55:10 -0700208 self.TOTAL_TC = 0
adminbae64d82013-08-01 10:50:15 -0700209 self.TOTAL_TC_RUN = 0
Jon Halld61331b2015-02-17 16:35:47 -0800210 self.TOTAL_TC_PLANNED = 0
adminbae64d82013-08-01 10:50:15 -0700211 self.TOTAL_TC_NORESULT = 0
212 self.TOTAL_TC_FAIL = 0
213 self.TOTAL_TC_PASS = 0
Jon Halla1185982014-09-15 14:55:10 -0700214 self.TEST_ITERATION = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700215
216 # NOTE: number of main.step statements in the
217 # outer most level of the test case. used to
218 # execute code in smaller steps
219 self.stepCount = 0
kelvin-onlabf70fd542015-05-07 18:41:40 -0700220 self.CASERESULT = self.NORESULT
221
Jon Halld61331b2015-02-17 16:35:47 -0800222 import testparser
Jon Hall53c5e662016-04-13 16:06:56 -0700223 test = testparser.TestParser( main.testFile )
adminbae64d82013-08-01 10:50:15 -0700224 self.testscript = test.testscript
225 self.code = test.getStepCode()
Jon Hall714eeba2015-09-29 17:53:10 -0700226 repeat = int( self.params.get( 'repeat', 1 ) )
227 self.TOTAL_TC_PLANNED = len( self.testcases_list ) * repeat
kelvin-onlabf70fd542015-05-07 18:41:40 -0700228
adminbae64d82013-08-01 10:50:15 -0700229 result = self.TRUE
Jon Hall714eeba2015-09-29 17:53:10 -0700230 while repeat:
Jon Halla1185982014-09-15 14:55:10 -0700231 for self.CurrentTestCaseNumber in self.testcases_list:
Jon Hall714eeba2015-09-29 17:53:10 -0700232 result = self.runCase( self.CurrentTestCaseNumber )
233 repeat -= 1
adminbae64d82013-08-01 10:50:15 -0700234 return result
kelvin-onlabf70fd542015-05-07 18:41:40 -0700235
Jon Halle234cc42015-08-31 15:26:47 -0700236 def runCase( self, testCaseNumber ):
adminbae64d82013-08-01 10:50:15 -0700237 self.CurrentTestCaseNumber = testCaseNumber
kelvin-onlabf70fd542015-05-07 18:41:40 -0700238 self.CurrentTestCase = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700239
240 # List of step results in a case. ANDed together to get the result
241 self.stepResultsList = []
kelvin-onlabf70fd542015-05-07 18:41:40 -0700242 self.stepName = ""
Jon Hall783bbf92015-07-23 14:33:19 -0700243 self.caseExplanation = ""
adminbae64d82013-08-01 10:50:15 -0700244 result = self.TRUE
Jon Hall714eeba2015-09-29 17:53:10 -0700245
246 # NOTE: number of main.step statements in the
247 # outer most level of the test case. used to
248 # execute code in smaller steps
249 self.stepCount = 0
250
251 # NOTE: This is the current number of main.step()'s executed
252 # in a case. Used for logging.
253 self.stepNumber = 0
adminbae64d82013-08-01 10:50:15 -0700254 self.EXPERIMENTAL_MODE = self.FALSE
255 self.addCaseHeader()
Jon Halle234cc42015-08-31 15:26:47 -0700256 self.testCaseNumber = str( testCaseNumber )
257 self.CASERESULT = self.NORESULT
adminbae64d82013-08-01 10:50:15 -0700258 stopped = False
Jon Hall5a72b712015-09-28 12:20:59 -0700259 try:
260 self.code[self.testCaseNumber]
Jon Halld61331b2015-02-17 16:35:47 -0800261 except KeyError:
Jon Halle234cc42015-08-31 15:26:47 -0700262 self.log.error( "There is no Test-Case " + self.testCaseNumber )
Jon Hallfebb1c72015-03-05 13:30:09 -0800263 return self.FALSE
adminbae64d82013-08-01 10:50:15 -0700264 self.stepCount = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700265 while self.stepCount < len( self.code[self.testCaseNumber].keys() ):
266 result = self.runStep( self.code, self.testCaseNumber )
Jon Hallfebb1c72015-03-05 13:30:09 -0800267 if result == self.FALSE:
adminbae64d82013-08-01 10:50:15 -0700268 break
Jon Hallfebb1c72015-03-05 13:30:09 -0800269 elif result == self.TRUE:
adminbae64d82013-08-01 10:50:15 -0700270 continue
Jon Hall5a72b712015-09-28 12:20:59 -0700271 # stepResults format: ( stepNo[], stepName[], stepResult[], onFail[] )
272 stepResults = self.stepResultsList
Jon Halle234cc42015-08-31 15:26:47 -0700273 if not stopped:
274 if self.CASERESULT == self.TRUE or self.CASERESULT == self.FALSE:
Jon Hall714eeba2015-09-29 17:53:10 -0700275 # Result was already explitily set somewhere else like
276 # in skipCase()
Jon Halle234cc42015-08-31 15:26:47 -0700277 pass
Jon Hall5a72b712015-09-28 12:20:59 -0700278 elif all( self.TRUE == i for i in stepResults ):
kelvin-onlabf70fd542015-05-07 18:41:40 -0700279 # ALL PASSED
280 self.CASERESULT = self.TRUE
Jon Hall5a72b712015-09-28 12:20:59 -0700281 elif self.FALSE in stepResults:
kelvin-onlabf70fd542015-05-07 18:41:40 -0700282 # AT LEAST ONE FAILED
283 self.CASERESULT = self.FALSE
Jon Hall5a72b712015-09-28 12:20:59 -0700284 elif self.TRUE in stepResults:
kelvin-onlabf70fd542015-05-07 18:41:40 -0700285 # AT LEAST ONE PASSED
286 self.CASERESULT = self.TRUE
287 else:
288 self.CASERESULT = self.NORESULT
Jon Hall714eeba2015-09-29 17:53:10 -0700289 self.testCaseResult[str( self.CurrentTestCaseNumber )] = self.CASERESULT
290 self.logger.updateCaseResults( self )
Jon Hall783bbf92015-07-23 14:33:19 -0700291 self.log.wiki( "<p>" + self.caseExplanation + "</p>" )
292 self.log.summary( self.caseExplanation )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700293 self.log.wiki( "<ul>" )
acsmarse2d1ed12015-10-05 13:51:17 -0700294 subcaseMessage = False
kelvin-onlabf70fd542015-05-07 18:41:40 -0700295 for line in self.stepCache.splitlines():
acsmarse2d1ed12015-10-05 13:51:17 -0700296 if re.search( "[0-9]\.[0-9]", line ): # Step
297 if subcaseMessage: # End of Failure Message Printout
298 self.log.wiki( "</ul>\n" )
299 subcaseMessage = False
300 if re.search( " - PASS$", line ):
301 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"tick\" /></li>\n" )
302 elif re.search( " - FAIL$", line ):
303 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"cross\" /></li>\n" )
304 elif re.search( " - No Result$", line ):
305 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"warning\" /></li>\n" )
306 else: # Substep
307 if not subcaseMessage: # Open Failure Message Printout
308 self.log.wiki( "<ul><li>" + line + "</li>\n" )
309 subcaseMessage = True
310 else: # Add to Failure Message Printout
311 self.log.wiki( "<li>" + line + "</li>\n" )
acsmars27e62dd2015-10-06 11:35:47 -0700312 if subcaseMessage: # End of Failure Message Printout for last item
313 self.log.wiki( "</ul>\n" )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700314 self.log.wiki( "</ul>" )
315 self.log.summary( self.stepCache )
316 self.stepCache = ""
adminbae64d82013-08-01 10:50:15 -0700317 return result
kelvin-onlabf70fd542015-05-07 18:41:40 -0700318
Jon Hall714eeba2015-09-29 17:53:10 -0700319 def runStep( self, code, testCaseNumber ):
adminbae64d82013-08-01 10:50:15 -0700320 if not cli.pause:
Jon Hall5a72b712015-09-28 12:20:59 -0700321 try:
322 step = self.stepCount
323 # stepResults format: ( stepNo, stepName, stepResult, onFail )
324 # NOTE: This is needed to catch results of main.step()'s
325 # called inside functions or loops
326 self.stepResults = ( [], [], [], [] )
adminbae64d82013-08-01 10:50:15 -0700327 exec code[testCaseNumber][step] in module.__dict__
328 self.stepCount = self.stepCount + 1
Jon Hall96b816f2015-11-03 12:00:56 -0800329 self.parseStepResults( testCaseNumber )
Jon Halle234cc42015-08-31 15:26:47 -0700330 except StopIteration: # Raised in self.skipCase()
331 self.log.warn( "Skipping the rest of CASE" +
332 str( testCaseNumber ) )
Jon Hall96b816f2015-11-03 12:00:56 -0800333 self.parseStepResults( testCaseNumber )
Jon Hall714eeba2015-09-29 17:53:10 -0700334 self.stepResultsList.append( self.STEPRESULT )
Jon Halle234cc42015-08-31 15:26:47 -0700335 self.stepCache += "\t\t" + self.onFailMsg + "\n"
336 self.stepCount = self.stepCount + 1
337 return self.FALSE
Jon Hallc1606352015-10-06 14:51:36 -0700338 except StandardError as e:
339 try:
340 stepNo = self.stepResults[0][ self.stepNumber - 1 ]
341 except IndexError:
342 stepNo = "<IndexError>"
343 main.log.warn( "Error trying to get step number. " +
344 "It is likely between step " +
Jon Hall6e709752016-02-01 13:38:46 -0800345 str( self.stepNumber ) + " and step " +
Jon Hallc1606352015-10-06 14:51:36 -0700346 str( self.stepNumber + 1 ) )
347 try:
348 stepName = self.stepResults[1][ self.stepNumber - 1 ]
349 except IndexError:
350 stepName = "<IndexError>"
351 self.log.error( "\nException in the following section of" +
352 " code: " + str( testCaseNumber ) + "." +
353 str( stepNo ) + ": " + stepName )
Jeremyd9e4eb12016-04-13 12:09:06 -0700354 self.log.error( str( e.__class__ ) + str( e.message ) )
adminbae64d82013-08-01 10:50:15 -0700355 self.stepCount = self.stepCount + 1
Jon Hall714eeba2015-09-29 17:53:10 -0700356 self.logger.updateCaseResults( self )
357 # WIKI results
kelvin-onlabf70fd542015-05-07 18:41:40 -0700358 self.log.wiki( "<ul>" )
359 for line in self.stepCache.splitlines():
360 if re.search( " - PASS$", line ):
361 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"tick\" /></li>\n" )
362 elif re.search( " - FAIL$", line ):
363 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"cross\" /></li>\n" )
364 elif re.search( " - No Result$", line ):
365 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"warning\" /></li>\n" )
Jon Hall90627612015-06-09 14:57:02 -0700366 else: # Should only be on fail message
367 self.log.wiki( "<ul><li>" + line + "</li></ul>\n" )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700368 self.log.wiki( "</ul>" )
Jon Hall714eeba2015-09-29 17:53:10 -0700369 # summary results
kelvin-onlabf70fd542015-05-07 18:41:40 -0700370 self.log.summary( self.stepCache )
371 self.stepCache = ""
shahshreya957feaa2015-03-23 16:08:29 -0700372 self.cleanup()
Jon Hall00539b12015-04-03 13:55:46 -0700373 self.exit()
Jon Halle234cc42015-08-31 15:26:47 -0700374 return self.TRUE
adminbae64d82013-08-01 10:50:15 -0700375 if cli.stop:
376 cli.stop = False
adminbae64d82013-08-01 10:50:15 -0700377 self.TOTAL_TC_NORESULT = self.TOTAL_TC_NORESULT + 1
Jon Hall714eeba2015-09-29 17:53:10 -0700378 self.testCaseResult[str( self.CurrentTestCaseNumber )] = "Stopped"
379 self.logger.updateCaseResults( self )
adminbae64d82013-08-01 10:50:15 -0700380 result = self.cleanup()
Jon Halle234cc42015-08-31 15:26:47 -0700381 return self.FALSE
382
Jon Hall96b816f2015-11-03 12:00:56 -0800383 def parseStepResults( self, testCaseNumber ):
384 """
385 Parse throught the step results for the wiki
386 """
387 try:
388 # Iterate through each of the steps and print them
389 for index in range( len( self.stepResults[0] ) ):
390 # stepResults = ( stepNo, stepName, stepResult, onFail )
391 stepNo = self.stepResults[0][ index ]
392 stepName = self.stepResults[1][ index ]
393 stepResult = self.stepResults[2][ index ]
394 onFail = self.stepResults[3][ index ]
395 self.stepCache += "\t" + str( testCaseNumber ) + "."
396 self.stepCache += str( stepNo ) + " "
397 self.stepCache += stepName + " - "
398 if stepResult == self.TRUE:
399 self.stepCache += "PASS\n"
400 elif stepResult == self.FALSE:
401 self.stepCache += "FAIL\n"
402 self.stepCache += "\t\t" + onFail + "\n"
403 else:
404 self.stepCache += "No Result\n"
405 self.stepResultsList.append( stepResult )
406 except Exception:
407 self.log.exception( "Error parsing step results" )
408
Jon Halle234cc42015-08-31 15:26:47 -0700409 def skipCase( self, result="DEFAULT", msg=None ):
410 """
411 Will skip the rest of the code in a test case. The case results will be
412 determined as normal based on completed assertions unless the result
413 argument is given.
414
415 Optional Arguments:
Jon Hall7c4f4302016-07-15 14:39:02 -0700416 result: Case insensitive string. Can be 'PASS' or 'FAIL' and will set
Jon Halle234cc42015-08-31 15:26:47 -0700417 the case result accordingly.
418 msg: Message to be printed when the case is skipped in the reports.
419 """
420 result = result.upper().strip()
421 if result == "PASS":
422 self.CASERESULT = self.TRUE
423 elif result == "FAIL":
424 self.CASERESULT = self.FALSE
425 self.onFailMsg = "Skipping the rest of this case. "
426 if msg:
427 self.onFailMsg += str( msg )
428 raise StopIteration
kelvin-onlabf70fd542015-05-07 18:41:40 -0700429
Jon Hall714eeba2015-09-29 17:53:10 -0700430 def addCaseHeader( self ):
431 caseHeader = "\n" + "*" * 30 + "\n Result summary for Testcase" +\
432 str( self.CurrentTestCaseNumber ) + "\n" + "*" * 30 + "\n"
433 self.log.exact( caseHeader )
434 caseHeader = "\n" + "*" * 40 + "\nStart of Test Case" +\
435 str( self.CurrentTestCaseNumber ) + " : "
adminbae64d82013-08-01 10:50:15 -0700436 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700437 vars( self )[driver + 'log'].info( caseHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700438
Jon Hall714eeba2015-09-29 17:53:10 -0700439 def addCaseFooter( self ):
Jon Hall5a72b712015-09-28 12:20:59 -0700440 stepNo = self.stepResults[0][-2]
Jon Hall714eeba2015-09-29 17:53:10 -0700441 if stepNo > 0:
442 previousStep = " " + str( self.CurrentTestCaseNumber ) + "." +\
443 str( stepNo ) + ": " + str( self.stepName )
444 stepHeader = "\n" + "*" * 40 + "\nEnd of Step " + previousStep +\
445 "\n" + "*" * 40 + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700446
Jon Hall714eeba2015-09-29 17:53:10 -0700447 caseFooter = "\n" + "*" * 40 + "\nEnd of Test case " +\
448 str( self.CurrentTestCaseNumber ) + "\n" + "*" * 40 + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700449
adminbae64d82013-08-01 10:50:15 -0700450 for driver in self.driversList:
Jon Hall714eeba2015-09-29 17:53:10 -0700451 vars( self )[driver].write( stepHeader + "\n" + caseFooter )
adminbae64d82013-08-01 10:50:15 -0700452
Jon Hall714eeba2015-09-29 17:53:10 -0700453 def cleanup( self ):
adminbae64d82013-08-01 10:50:15 -0700454 '''
Jon Hall5b586732015-06-11 11:39:39 -0700455 Print a summary of the current test's results then attempt to release
456 all the component handles and the close opened file handles.
adminbae64d82013-08-01 10:50:15 -0700457
Jon Hall5b586732015-06-11 11:39:39 -0700458 This function shouldbe threadsafe such that cleanup will only be
459 executed once per test.
460
461 This will return TRUE if all the component handles and log handles
462 closed properly, else return FALSE.
adminbae64d82013-08-01 10:50:15 -0700463 '''
464 result = self.TRUE
Jon Hall5b586732015-06-11 11:39:39 -0700465 lock = self.cleanupLock
466 if lock.acquire( False ):
467 try:
468 if self.cleanupFlag is False: # First thread to run this
469 self.cleanupFlag = True
Jon Hall0fc0d452015-07-14 09:49:58 -0700470 if self.initiated:
Jon Hall714eeba2015-09-29 17:53:10 -0700471 self.logger.testSummary( self )
Jon Hall892818c2015-10-20 17:58:34 -0700472 components = self.componentDictionary
473 for component in sorted( components,
474 key=lambda item: components[item]['connect_order'],
475 reverse=True ):
Jon Hall714eeba2015-09-29 17:53:10 -0700476 try:
477 tempObject = vars( self )[component]
478 print "Disconnecting from " + str( tempObject.name ) +\
479 ": " + str( tempObject.__class__)
Jon Hall5b586732015-06-11 11:39:39 -0700480 tempObject.disconnect()
Jon Hall1306a562015-09-04 11:21:24 -0700481 except KeyboardInterrupt:
482 pass
483 except KeyError:
484 # Component not created yet
485 self.log.warn( "Could not find the component " +
486 str( component ) )
487 except StandardError:
Jon Hall5b586732015-06-11 11:39:39 -0700488 self.log.exception( "Exception while disconnecting from " +
489 str( component ) )
490 result = self.FALSE
491 # Closing all the driver's session files
492 for driver in self.componentDictionary.keys():
493 try:
Jon Hall714eeba2015-09-29 17:53:10 -0700494 vars( self )[driver].close_log_handles()
Jon Hall1306a562015-09-04 11:21:24 -0700495 except KeyboardInterrupt:
496 pass
497 except KeyError:
498 # Component not created yet
499 self.log.warn( "Could not find the component " +
500 str( driver ) + " while trying to" +
501 " close log file" )
502 except StandardError:
Jon Hall5b586732015-06-11 11:39:39 -0700503 self.log.exception( "Exception while closing log files for " +
504 str( driver ) )
505 result = self.FALSE
506 else:
507 pass # Someone else already ran through this function
508 finally:
509 lock.release()
510 else: # Someone already has a lock
511 # NOTE: This could cause problems if we don't release the lock
512 # correctly
513 lock.acquire() # Wait for the other thread to finish
514 # NOTE: If we don't wait, exit could be called while the thread
515 # with the lock is still cleaning up
516 lock.release()
adminbae64d82013-08-01 10:50:15 -0700517 return result
Jon Halld61331b2015-02-17 16:35:47 -0800518
Jon Hall714eeba2015-09-29 17:53:10 -0700519 def pause( self ):
adminbae64d82013-08-01 10:50:15 -0700520 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700521 This function will pause the test's execution, and will continue after
522 user provide 'resume' command.
adminbae64d82013-08-01 10:50:15 -0700523 '''
524 __builtin__.testthread.pause()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700525
Jon Hall714eeba2015-09-29 17:53:10 -0700526 def onfail( self, *components ):
adminbae64d82013-08-01 10:50:15 -0700527 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -0700528 When test step failed, calling all the components onfail.
adminbae64d82013-08-01 10:50:15 -0700529 '''
adminbae64d82013-08-01 10:50:15 -0700530 if not components:
Jon Hall714eeba2015-09-29 17:53:10 -0700531 try:
adminbae64d82013-08-01 10:50:15 -0700532 for component in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700533 tempObject = vars( self )[component]
adminbae64d82013-08-01 10:50:15 -0700534 result = tempObject.onfail()
Jon Hall1306a562015-09-04 11:21:24 -0700535 except StandardError as e:
Jon Hall714eeba2015-09-29 17:53:10 -0700536 print str( e )
adminbae64d82013-08-01 10:50:15 -0700537 result = self.FALSE
adminbae64d82013-08-01 10:50:15 -0700538 else:
Jon Hall714eeba2015-09-29 17:53:10 -0700539 try:
adminbae64d82013-08-01 10:50:15 -0700540 for component in components:
Jon Hall714eeba2015-09-29 17:53:10 -0700541 tempObject = vars( self )[component]
adminbae64d82013-08-01 10:50:15 -0700542 result = tempObject.onfail()
Jon Hall1306a562015-09-04 11:21:24 -0700543 except StandardError as e:
Jon Hall714eeba2015-09-29 17:53:10 -0700544 print str( e )
adminbae64d82013-08-01 10:50:15 -0700545 result = self.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700546
Jon Hall714eeba2015-09-29 17:53:10 -0700547 def getDriverPath( self, driverName ):
adminbae64d82013-08-01 10:50:15 -0700548 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700549 Based on the component 'type' specified in the params , this method
550 will find the absolute path, by recursively searching the name of
551 the component.
552
553 NOTE: This function requires the linux 'find' command.
adminbae64d82013-08-01 10:50:15 -0700554 '''
555 import commands
556
Jon Hall714eeba2015-09-29 17:53:10 -0700557 cmd = "find " + drivers_path + " -name " + driverName + ".py"
558 result = commands.getoutput( cmd )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700559
Jon Hall714eeba2015-09-29 17:53:10 -0700560 result_array = str( result ).split( '\n' )
adminbae64d82013-08-01 10:50:15 -0700561 result_count = 0
kelvin-onlabf70fd542015-05-07 18:41:40 -0700562
adminbae64d82013-08-01 10:50:15 -0700563 for drivers_list in result_array:
Jon Hall714eeba2015-09-29 17:53:10 -0700564 result_count = result_count + 1
565 if result_count > 1:
566 print "Found " + driverName + " " + str( result_count ) + " times:"
567 print str( result_array )
adminbae64d82013-08-01 10:50:15 -0700568 self.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700569
Jon Hall714eeba2015-09-29 17:53:10 -0700570 result = re.sub( "(.*)drivers", "", result )
571 result = re.sub( "\/\/", "/", result )
572 result = re.sub( "\.py", "", result )
573 result = re.sub( "\.pyc", "", result )
574 result = re.sub( "\/", ".", result )
575 result = "drivers" + result
adminbae64d82013-08-01 10:50:15 -0700576 return result
adminbae64d82013-08-01 10:50:15 -0700577
Jon Hall714eeba2015-09-29 17:53:10 -0700578 def step( self, stepDesc ):
adminbae64d82013-08-01 10:50:15 -0700579 '''
580 The step information of the test-case will append to the logs.
581 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700582 previousStep = " " + str( self.CurrentTestCaseNumber ) + "." +\
583 str( self.stepNumber ) + ": " + str( self.stepName )
adminbae64d82013-08-01 10:50:15 -0700584 self.stepName = stepDesc
Jon Hall5a72b712015-09-28 12:20:59 -0700585 self.stepNumber += 1
586 self.stepResults[0].append( self.stepNumber )
587 self.stepResults[1].append( stepDesc )
588 self.stepResults[2].append( self.NORESULT )
589 self.stepResults[3].append( "No on fail message given" )
adminbae64d82013-08-01 10:50:15 -0700590
Jon Hall714eeba2015-09-29 17:53:10 -0700591 stepName = " " + str( self.CurrentTestCaseNumber ) + "." +\
592 str( self.stepNumber ) + ": " + str( stepDesc )
adminbae64d82013-08-01 10:50:15 -0700593 self.log.step(stepName)
594 stepHeader = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700595 line = "\n" + "-" * 45 + "\n"
596 if self.stepNumber > 1:
597 stepHeader = line + "End of Step " + previousStep + line
598 stepHeader += line + "Start of Step" + stepName + line
adminbae64d82013-08-01 10:50:15 -0700599 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700600 vars( self )[driver + 'log'].info( stepHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700601
Jon Hall714eeba2015-09-29 17:53:10 -0700602 def case( self, testCaseName ):
adminbae64d82013-08-01 10:50:15 -0700603 '''
604 Test's each test-case information will append to the logs.
605 '''
Jon Halld61331b2015-02-17 16:35:47 -0800606 self.CurrentTestCase = testCaseName
Jon Hall714eeba2015-09-29 17:53:10 -0700607 testCaseName = " " + str( testCaseName )
608 self.log.case( testCaseName )
609 caseHeader = testCaseName + "\n" + "*" * 40 + "\n"
adminbae64d82013-08-01 10:50:15 -0700610 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700611 vars( self )[driver + 'log'].info( caseHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700612
Jon Hall714eeba2015-09-29 17:53:10 -0700613 def testDesc( self, description ):
adminbae64d82013-08-01 10:50:15 -0700614 '''
615 Test description will append to the logs.
616 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700617 description = "Test Description : " + str( description )
618 self.log.info( description )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700619
Jon Hall714eeba2015-09-29 17:53:10 -0700620 def _getTest( self ):
adminbae64d82013-08-01 10:50:15 -0700621 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700622 This method will parse the test script to find required test
623 information.
adminbae64d82013-08-01 10:50:15 -0700624 '''
Jon Hall53c5e662016-04-13 16:06:56 -0700625 testFileHandler = open( main.testFile, 'r' )
adminbae64d82013-08-01 10:50:15 -0700626 testFileList = testFileHandler.readlines()
627 testFileHandler.close()
adminbae64d82013-08-01 10:50:15 -0700628 counter = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700629 for index in range( len( testFileList ) ):
630 lineMatch = re.match( '\s+def CASE(\d+)(.*):',
631 testFileList[index],
632 0 )
adminbae64d82013-08-01 10:50:15 -0700633 if lineMatch:
Jon Hall714eeba2015-09-29 17:53:10 -0700634 counter = counter + 1
635 self.TC_PLANNED = len( self.testcases_list )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700636
Jon Hall714eeba2015-09-29 17:53:10 -0700637 def response_parser( self, response, return_format ):
adminbae64d82013-08-01 10:50:15 -0700638 ''' It will load the default response parser '''
639 response_dict = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700640 response_dict = self.response_to_dict( response, return_format )
641 return_format_string = self.dict_to_return_format( response,
642 return_format,
643 response_dict )
adminbae64d82013-08-01 10:50:15 -0700644 return return_format_string
kelvin-onlabf70fd542015-05-07 18:41:40 -0700645
Jon Hall714eeba2015-09-29 17:53:10 -0700646 def response_to_dict( self, response, return_format ):
adminbae64d82013-08-01 10:50:15 -0700647 response_dict = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700648 json_match = re.search( '^\s*{', response )
649 xml_match = re.search( '^\s*\<', response )
650 ini_match = re.search( '^\s*\[', response )
651 if json_match:
652 self.log.info( "Response is in 'JSON' format, converting to '" +
653 return_format + "' format" )
Jon Halld61331b2015-02-17 16:35:47 -0800654 # Formatting the json string
Jon Hall714eeba2015-09-29 17:53:10 -0700655 response = re.sub( r"{\s*'?(\w)", r'{"\1', response )
656 response = re.sub( r",\s*'?(\w)", r',"\1', response )
657 response = re.sub( r"(\w)'?\s*:", r'\1":', response )
658 response = re.sub( r":\s*'(\w)'\s*([,}])", r':"\1"\2', response )
659 try:
adminbae64d82013-08-01 10:50:15 -0700660 import json
Jon Hall714eeba2015-09-29 17:53:10 -0700661 response_dict = json.loads( response )
Jon Hall1306a562015-09-04 11:21:24 -0700662 except StandardError:
Jon Hall2a5002c2015-08-21 16:49:11 -0700663 self.log.exception( "Json Parser is unable to parse the string" )
adminbae64d82013-08-01 10:50:15 -0700664 return response_dict
Jon Hall714eeba2015-09-29 17:53:10 -0700665 elif ini_match:
666 self.log.info( "Response is in 'INI' format, converting to '" +
667 return_format + "' format" )
adminbae64d82013-08-01 10:50:15 -0700668 from configobj import ConfigObj
Jon Hall714eeba2015-09-29 17:53:10 -0700669 response_file = open( "respnse_file.temp", 'w' )
670 response_file.write( response )
Jon Halld61331b2015-02-17 16:35:47 -0800671 response_file.close()
Jon Hall714eeba2015-09-29 17:53:10 -0700672 response_dict = ConfigObj( "respnse_file.temp" )
adminbae64d82013-08-01 10:50:15 -0700673 return response_dict
Jon Hall714eeba2015-09-29 17:53:10 -0700674 elif xml_match:
675 self.log.info( "Response is in 'XML' format, converting to '" +
676 return_format + "' format" )
677 try:
678 response_dict = xmldict.xml_to_dict( "<response> " +
679 str( response ) +
680 " </response>" )
Jon Hall1306a562015-09-04 11:21:24 -0700681 except StandardError:
682 self.log.exception()
adminbae64d82013-08-01 10:50:15 -0700683 return response_dict
kelvin-onlabf70fd542015-05-07 18:41:40 -0700684
Jon Hall714eeba2015-09-29 17:53:10 -0700685 def dict_to_return_format( self, response, return_format, response_dict ):
686 if return_format == 'table':
adminbae64d82013-08-01 10:50:15 -0700687 ''' Will return in table format'''
688 to_do = "Call the table output formatter"
689 global response_table
690 response_table = '\n'
Jon Hall714eeba2015-09-29 17:53:10 -0700691 response_table = response_table + '\t'.join( response_dict ) + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700692
Jon Hall714eeba2015-09-29 17:53:10 -0700693 def get_table( value_to_convert ):
694 ''' This will parse the dictionary recusrsively and print as
695 table format'''
adminbae64d82013-08-01 10:50:15 -0700696 table_data = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700697 if isinstance( value_to_convert, dict ):
698 table_data = table_data + '\t'.join( value_to_convert ) +\
699 "\n"
700 for temp_val in value_to_convert.values():
701 table_data = table_data + get_table( temp_val )
702 else:
703 table_data = table_data + str( value_to_convert ) + "\t"
Jon Halld61331b2015-02-17 16:35:47 -0800704 return table_data
kelvin-onlabf70fd542015-05-07 18:41:40 -0700705
Jon Hall714eeba2015-09-29 17:53:10 -0700706 for value in response_dict.values():
707 response_table = response_table + get_table( value )
adminbae64d82013-08-01 10:50:15 -0700708 return response_table
kelvin-onlabf70fd542015-05-07 18:41:40 -0700709
Jon Hall714eeba2015-09-29 17:53:10 -0700710 elif return_format == 'config':
adminbae64d82013-08-01 10:50:15 -0700711 ''' Will return in config format'''
712 to_do = 'Call dict to config coverter'
Jon Hall714eeba2015-09-29 17:53:10 -0700713 response_string = str( response_dict )
adminbae64d82013-08-01 10:50:15 -0700714 print response_string
Jon Hall714eeba2015-09-29 17:53:10 -0700715 response_config = re.sub( ",", "\n\t", response_string )
716 response_config = re.sub( "u\'", "\'", response_config )
717 response_config = re.sub( "{", "", response_config )
718 response_config = re.sub( "}", "\n", response_config )
719 response_config = re.sub( ":", " =", response_config )
720 return "[response]\n\t " + response_config
adminbae64d82013-08-01 10:50:15 -0700721 elif return_format == 'xml':
722 ''' Will return in xml format'''
Jon Hall714eeba2015-09-29 17:53:10 -0700723 response_xml = xmldict.dict_to_xml( response_dict )
724 response_xml = re.sub( ">\s*<", ">\n<", response_xml )
725 return "\n" + response_xml
adminbae64d82013-08-01 10:50:15 -0700726 elif return_format == 'json':
727 ''' Will return in json format'''
728 to_do = 'Call dict to xml coverter'
729 import json
Jon Hall714eeba2015-09-29 17:53:10 -0700730 response_json = json.dumps( response_dict )
adminbae64d82013-08-01 10:50:15 -0700731 return response_json
kelvin-onlabf70fd542015-05-07 18:41:40 -0700732
Jon Hall714eeba2015-09-29 17:53:10 -0700733 def get_random( self ):
adminbae64d82013-08-01 10:50:15 -0700734 self.random_order = self.random_order + 1
735 return self.random_order
kelvin-onlabf70fd542015-05-07 18:41:40 -0700736
Jon Hall714eeba2015-09-29 17:53:10 -0700737 def exit( self ):
adminbae64d82013-08-01 10:50:15 -0700738 __builtin__.testthread = None
Jon Hall5b586732015-06-11 11:39:39 -0700739 for thread in threading.enumerate():
740 if thread.isAlive():
741 try:
742 thread._Thread__stop()
743 except:
Jon Hall1306a562015-09-04 11:21:24 -0700744 # NOTE: We should catch any exceptions while trying to
745 # close the thread so that we can try to close the other
746 # threads as well
Jon Hall714eeba2015-09-29 17:53:10 -0700747 print str( thread.getName() ) +\
748 ' could not be terminated'
adminbae64d82013-08-01 10:50:15 -0700749 sys.exit()
750
Jon Hallcd3d2a32015-10-01 11:07:28 -0700751 def stop( self, email=False ):
752 """
753 Stop the test until Ctrl-D is entered.
754 Ctrl-C will kill the test
Jon Hall25079782015-10-13 13:54:39 -0700755
756 Optional arguments:
757 email can either be a bool, or you can specify the email address
758 to send the email to
Jon Hallcd3d2a32015-10-01 11:07:28 -0700759 """
760 try:
761 if email:
Jon Hall25079782015-10-13 13:54:39 -0700762 if '@' in email:
763 main.mail = email
764 utilities.send_warning_email()
Jon Hallcd3d2a32015-10-01 11:07:28 -0700765 self.log.error( "Test execution suspended. Press Ctrl-D to "
766 "resume or Ctrl-C to exit the test" )
767 # NOTE: Ctrl-D needs to be entered on a new line
768 while True:
769 # TODO: we could give the user an interactive prompt where
770 # they could call functions
771 raw_input()
772 except EOFError:
773 return
774 # Pass all other exceptions up to caller
775
776
Jon Hall714eeba2015-09-29 17:53:10 -0700777def verifyOptions( options ):
adminbae64d82013-08-01 10:50:15 -0700778 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700779 This will verify the command line options and set to default values,
780 if any option not given in command line.
adminbae64d82013-08-01 10:50:15 -0700781 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700782 verifyTest( options )
783 verifyExample( options )
784 verifyTestScript( options )
YPZhang1c89e762016-06-29 10:43:58 -0700785 verifyParams( options )
Jon Hall714eeba2015-09-29 17:53:10 -0700786 verifyLogdir( options )
787 verifyMail( options )
788 verifyTestCases( options )
789 verifyOnosCell( options )
adminbae64d82013-08-01 10:50:15 -0700790
Jon Hall714eeba2015-09-29 17:53:10 -0700791def verifyTest( options ):
Jon Hall44506242015-07-29 17:40:26 -0700792 try:
793 if options.testname:
794 main.TEST = options.testname
Jon Hall714eeba2015-09-29 17:53:10 -0700795 main.classPath = "tests." + main.TEST + "." + main.TEST
Jon Hall44506242015-07-29 17:40:26 -0700796 main.tests_path = tests_path
797 elif options.example:
798 main.TEST = options.example
Jon Hall714eeba2015-09-29 17:53:10 -0700799 main.tests_path = path + "/examples/"
800 main.classPath = "examples." + main.TEST + "." + main.TEST
Jon Hall44506242015-07-29 17:40:26 -0700801 except AttributeError:
adminbae64d82013-08-01 10:50:15 -0700802 print "Test or Example not specified please specify the --test <test name > or --example <example name>"
Jon Hall5b586732015-06-11 11:39:39 -0700803 main.exit()
adminbae64d82013-08-01 10:50:15 -0700804
Jon Hall714eeba2015-09-29 17:53:10 -0700805def verifyExample( options ):
adminbae64d82013-08-01 10:50:15 -0700806 if options.example:
Jon Hall714eeba2015-09-29 17:53:10 -0700807 main.testDir = path + '/examples/'
808 main.tests_path = path + "/examples/"
809 main.classPath = "examples." + main.TEST + "." + main.TEST
kelvin-onlabf70fd542015-05-07 18:41:40 -0700810
Jon Hall714eeba2015-09-29 17:53:10 -0700811def verifyLogdir( options ):
Jon Hall88e498c2015-03-06 09:54:35 -0800812 # Verifying Log directory option
adminbae64d82013-08-01 10:50:15 -0700813 if options.logdir:
814 main.logdir = options.logdir
Jon Hall714eeba2015-09-29 17:53:10 -0700815 else:
Jon Halld61331b2015-02-17 16:35:47 -0800816 main.logdir = main.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700817
Jon Hall714eeba2015-09-29 17:53:10 -0700818def verifyMail( options ):
Jon Hall25079782015-10-13 13:54:39 -0700819 # Mail-To: field
820 if options.mail: # Test run specific
adminbae64d82013-08-01 10:50:15 -0700821 main.mail = options.mail
Jon Hall25079782015-10-13 13:54:39 -0700822 elif main.params.get('mail'): # Test suite specific
823 main.mail = main.params.get( 'mail' )
824 else: # TestON specific
825 main.mail = main.config['config'].get( 'mail_to' )
826 # Mail-From: field
827 main.sender = main.config['config'].get( 'mail_from' )
828 # Mail smtp server
829 main.smtp = main.config['config'].get( 'mail_server' )
830 # Mail-From account password
831 main.senderPwd = main.config['config'].get( 'mail_pass' )
832
Devin Lim2df68a12017-06-30 15:39:05 -0700833def evalTestCase( tempList ):
834 tList = []
835 for tcase in tempList:
836 if isinstance( tcase, list ):
837 tList.extend( evalTestCase( tcase ) )
838 else:
839 tList.extend( [tcase] )
840 return tList
adminbae64d82013-08-01 10:50:15 -0700841
Jon Hall714eeba2015-09-29 17:53:10 -0700842def verifyTestCases( options ):
Jon Hall88e498c2015-03-06 09:54:35 -0800843 # Getting Test cases list
adminbae64d82013-08-01 10:50:15 -0700844 if options.testcases:
Jon Hallfebb1c72015-03-05 13:30:09 -0800845 testcases_list = options.testcases
Jon Hall88e498c2015-03-06 09:54:35 -0800846 # sys.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700847 testcases_list = re.sub( "(\[|\])", "", options.testcases )
848 main.testcases_list = eval( testcases_list + "," )
849 else:
adminbae64d82013-08-01 10:50:15 -0700850 if 'testcases' in main.params.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700851 temp = eval( main.params['testcases'] + "," )
Devin Lim2df68a12017-06-30 15:39:05 -0700852 main.testcases_list = evalTestCase( list( temp ) )
Jon Hall714eeba2015-09-29 17:53:10 -0700853 else:
854 print "Testcases not specifed in params, please provide in " +\
855 "params file or 'testcases' commandline argument"
Jon Halld61331b2015-02-17 16:35:47 -0800856 sys.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700857
Jon Hall714eeba2015-09-29 17:53:10 -0700858def verifyOnosCell( options ):
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700859 # Verifying onoscell option
Hari Krishna03f530e2015-07-10 17:28:27 -0700860 if options.onoscell:
861 main.onoscell = options.onoscell
Devin Lim58046fa2017-07-05 16:55:00 -0700862 main.ONOSip = []
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700863 main.mnIP = ""
Devin Lim58046fa2017-07-05 16:55:00 -0700864 cellCMD = ". ~/onos/tools/dev/bash_profile; cell " + main.onoscell
Jon Hall714eeba2015-09-29 17:53:10 -0700865 output = subprocess.check_output( ["bash", '-c', cellCMD] )
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700866 splitOutput = output.splitlines()
Devin Lim58046fa2017-07-05 16:55:00 -0700867 main.apps = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700868 for i in range( len( splitOutput ) ):
869 if re.match( "OCN", splitOutput[i] ):
870 mnNode = splitOutput[i].split( "=" )
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700871 main.mnIP = mnNode[1]
Jon Hall714eeba2015-09-29 17:53:10 -0700872 # cell already sorts OC variables in bash, so no need to
873 # sort in TestON
Devin Lim58046fa2017-07-05 16:55:00 -0700874 elif re.match( "OC[1-9]", splitOutput[i] ):
Jon Hall714eeba2015-09-29 17:53:10 -0700875 onosNodes = splitOutput[i].split( "=" )
Devin Lim58046fa2017-07-05 16:55:00 -0700876 main.ONOSip.append( onosNodes[1] )
877 elif re.match( "ONOS_APPS", splitOutput[i] ):
878 main.apps = ( splitOutput[i].split( "=" ) )[1]
Jon Hall714eeba2015-09-29 17:53:10 -0700879 else:
Hari Krishna03f530e2015-07-10 17:28:27 -0700880 main.onoscell = main.FALSE
881
Jon Hall714eeba2015-09-29 17:53:10 -0700882def verifyTestScript( options ):
adminbae64d82013-08-01 10:50:15 -0700883 '''
884 Verifyies test script.
885 '''
Jon Halld61331b2015-02-17 16:35:47 -0800886 main.openspeak = openspeak.OpenSpeak()
Jon Hall53c5e662016-04-13 16:06:56 -0700887 directory = main.testDir + "/" + main.TEST
888 if os.path.exists( directory ):
889 pass
890 else:
891 directory = ""
892 for root, dirs, files in os.walk( main.testDir, topdown=True):
893 if not directory:
894 for name in dirs:
895 if name == main.TEST:
896 directory = ( os.path.join( root, name ) )
897 index = directory.find( "/tests/" ) + 1
898 main.classPath = directory[index:].replace( '/', '.' ) + "." + main.TEST
899 break
900 openspeakfile = directory + "/" + main.TEST + ".ospk"
901 main.testFile = directory + "/" + main.TEST + ".py"
Jon Hall714eeba2015-09-29 17:53:10 -0700902 if os.path.exists( openspeakfile ):
Jon Hall44506242015-07-29 17:40:26 -0700903 # Openspeak file found, compiling to python
Jon Hall714eeba2015-09-29 17:53:10 -0700904 main.openspeak.compiler( openspeakfile=openspeakfile, writetofile=1 )
Jon Hall53c5e662016-04-13 16:06:56 -0700905 elif os.path.exists( main.testFile ):
Jon Hall44506242015-07-29 17:40:26 -0700906 # No openspeak found, using python file instead
907 pass
adminbae64d82013-08-01 10:50:15 -0700908 else:
Jon Hall714eeba2015-09-29 17:53:10 -0700909 print "\nThere is no \"" + main.TEST + "\" test script.\nPlease provide a " +\
Jon Hall44506242015-07-29 17:40:26 -0700910 "Python or OpenSpeak test script in the tests folder: " +\
Jon Hall714eeba2015-09-29 17:53:10 -0700911 main.testDir + "/" + main.TEST + "/"
adminbae64d82013-08-01 10:50:15 -0700912 __builtin__.testthread = None
913 main.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700914 try:
915 testModule = __import__( main.classPath,
916 globals(),
917 locals(),
918 [main.TEST],
919 -1 )
Jon Hall1306a562015-09-04 11:21:24 -0700920 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -0700921 print "There was an import error, it might mean that there is " +\
922 "no test named " + main.TEST
Jon Halld61331b2015-02-17 16:35:47 -0800923 main.exit()
adminbae64d82013-08-01 10:50:15 -0700924
Jon Hall714eeba2015-09-29 17:53:10 -0700925 testClass = getattr( testModule, main.TEST )
adminbae64d82013-08-01 10:50:15 -0700926 main.testObject = testClass()
927 load_parser()
Jon Hall714eeba2015-09-29 17:53:10 -0700928 main.params = main.parser.parseParams( main.classPath )
929 main.topology = main.parser.parseTopology( main.classPath )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700930
YPZhang1c89e762016-06-29 10:43:58 -0700931def verifyParams( options ):
Jon Hall714eeba2015-09-29 17:53:10 -0700932 try:
adminbae64d82013-08-01 10:50:15 -0700933 main.params = main.params['PARAMS']
Jon Hall1306a562015-09-04 11:21:24 -0700934 except KeyError:
Jon Hall714eeba2015-09-29 17:53:10 -0700935 print "Error with the params file: Either the file not specified " +\
936 "or the format is not correct"
Jon Halld61331b2015-02-17 16:35:47 -0800937 main.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700938 try:
adminbae64d82013-08-01 10:50:15 -0700939 main.topology = main.topology['TOPOLOGY']
Jon Hall1306a562015-09-04 11:21:24 -0700940 except KeyError:
Jon Hall714eeba2015-09-29 17:53:10 -0700941 print "Error with the Topology file: Either the file not specified " +\
942 "or the format is not correct"
adminbae64d82013-08-01 10:50:15 -0700943 main.exit()
YPZhang1c89e762016-06-29 10:43:58 -0700944 # Overwrite existing params variables if they are specified from command line
945 if len(options.params) > 0:
946 # Some params variables are specified from command line
947 for param in options.params:
948 if not re.search( ".=.", param ):
949 print( "Error when parsing params: params should follow key=value format" )
950 continue
Jon Hall7c4f4302016-07-15 14:39:02 -0700951 # Split the param string to catch nested keys and the value
YPZhang1c89e762016-06-29 10:43:58 -0700952 [ keys, value ] = param.split( "=" )
953 # Split the nested keys according to its hierarchy
954 keyList = keys.split( "/" )
955 # Get the outermost dictionary
956 paramDict = main.params
957 # Get the innermost dictionary
958 try:
959 while len( keyList ) > 1:
960 key = keyList.pop(0)
961 assert isinstance( paramDict[ key ], dict )
962 paramDict = paramDict[ key ]
963 except KeyError:
964 print( "Error when parsing params: key \"" + key + "\" not found in main.params" )
965 main.exit()
966 except AssertionError:
967 print( "Error when parsing params: \"" + key + "\" is already the innermost level in main.params" )
968 main.exit()
969 # Change the value
970 if not paramDict.has_key( keyList[0] ):
971 print( "Error when parsing params: key \"" + keyList[0] + "\" not found in main.params" )
972 main.exit()
973 elif isinstance( paramDict[ keyList[0] ], dict ):
974 print( "Error when parsing params: more levels under key \"" + keyList[0] + "\" in main.params" )
975 main.exit()
976 else:
977 paramDict[ keyList[0] ] = value
kelvin-onlabf70fd542015-05-07 18:41:40 -0700978
Jon Hall714eeba2015-09-29 17:53:10 -0700979def load_parser():
adminbae64d82013-08-01 10:50:15 -0700980 '''
981 It facilitates the loading customised parser for topology and params file.
982 It loads parser mentioned in tab named parser of teston.cfg file.
Jon Hall714eeba2015-09-29 17:53:10 -0700983 It also loads default xmlparser if no parser have specified in teston.cfg
984 file.
adminbae64d82013-08-01 10:50:15 -0700985
986 '''
987 confighash = main.configDict
Jon Hall714eeba2015-09-29 17:53:10 -0700988 if 'file' in confighash['config']['parser'] and\
989 'class' in confighash['config']['parser']:
Jon Hall44506242015-07-29 17:40:26 -0700990 path = confighash['config']['parser']['file']
Jon Hall714eeba2015-09-29 17:53:10 -0700991 if path is not None or\
992 confighash['config']['parser']['class'] is not None:
Jon Hall44506242015-07-29 17:40:26 -0700993 try:
994 module = re.sub( r".py\s*$", "", path )
adminbae64d82013-08-01 10:50:15 -0700995 moduleList = module.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -0700996 newModule = ".".join( moduleList[-2:] )
Jon Hall44506242015-07-29 17:40:26 -0700997 parsingClass = confighash['config']['parser']['class']
Jon Hall714eeba2015-09-29 17:53:10 -0700998 parsingModule = __import__( newModule,
999 globals(),
1000 locals(),
1001 [parsingClass],
1002 -1 )
1003 parsingClass = getattr( parsingModule, parsingClass )
Jon Hall44506242015-07-29 17:40:26 -07001004 main.parser = parsingClass()
Jon Hall714eeba2015-09-29 17:53:10 -07001005 if hasattr( main.parser, "parseParams" ) and\
1006 hasattr( main.parser, "parseTopology" ) and\
1007 hasattr( main.parser, "parse" ):
Jon Hall44506242015-07-29 17:40:26 -07001008 pass
1009 else:
1010 print "Invalid parser format"
adminbae64d82013-08-01 10:50:15 -07001011 main.exit()
Jon Hall44506242015-07-29 17:40:26 -07001012 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -07001013 print "Could not find the file " + path +\
1014 " using default parser."
Jon Halld61331b2015-02-17 16:35:47 -08001015 load_defaultParser()
Jon Hall714eeba2015-09-29 17:53:10 -07001016 elif confighash['config']['parser']['file'] is None or\
1017 confighash['config']['parser']['class'] is None:
Jon Halld61331b2015-02-17 16:35:47 -08001018 load_defaultParser()
adminbae64d82013-08-01 10:50:15 -07001019 else:
1020 load_defaultParser()
1021
1022def load_defaultParser():
1023 '''
Jon Hall714eeba2015-09-29 17:53:10 -07001024 It will load the default parser which is xml parser to parse the params and
1025 topology file.
adminbae64d82013-08-01 10:50:15 -07001026 '''
1027 moduleList = main.parserPath.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -07001028 newModule = ".".join( moduleList[-2:] )
1029 try:
Jon Halld61331b2015-02-17 16:35:47 -08001030 parsingClass = main.parsingClass
Jon Hall714eeba2015-09-29 17:53:10 -07001031 parsingModule = __import__( newModule,
1032 globals(),
1033 locals(),
1034 [parsingClass],
1035 -1 )
1036 parsingClass = getattr( parsingModule, parsingClass )
adminbae64d82013-08-01 10:50:15 -07001037 main.parser = parsingClass()
Jon Hall714eeba2015-09-29 17:53:10 -07001038 if hasattr( main.parser, "parseParams" ) and\
1039 hasattr( main.parser, "parseTopology" ) and\
1040 hasattr( main.parser, "parse" ):
adminbae64d82013-08-01 10:50:15 -07001041 pass
1042 else:
1043 main.exit()
adminbae64d82013-08-01 10:50:15 -07001044 except ImportError:
1045 print sys.exc_info()[1]
1046
Jon Hall714eeba2015-09-29 17:53:10 -07001047def load_logger():
adminbae64d82013-08-01 10:50:15 -07001048 '''
1049 It facilitates the loading customised parser for topology and params file.
1050 It loads parser mentioned in tab named parser of teston.cfg file.
Jon Hall714eeba2015-09-29 17:53:10 -07001051 It also loads default xmlparser if no parser have specified in teston.cfg
1052 file.
adminbae64d82013-08-01 10:50:15 -07001053 '''
1054 confighash = main.configDict
Jon Hall714eeba2015-09-29 17:53:10 -07001055 if 'file' in confighash['config']['logger'] and\
1056 'class' in confighash['config']['logger']:
Jon Hall44506242015-07-29 17:40:26 -07001057 path = confighash['config']['logger']['file']
Jon Hall714eeba2015-09-29 17:53:10 -07001058 if path is not None or\
1059 confighash['config']['logger']['class'] is not None:
Jon Hall44506242015-07-29 17:40:26 -07001060 try:
1061 module = re.sub( r".py\s*$", "", path )
Jon Hall714eeba2015-09-29 17:53:10 -07001062 moduleList = module.split( "/" )
1063 newModule = ".".join( moduleList[-2:] )
Jon Hall44506242015-07-29 17:40:26 -07001064 loggerClass = confighash['config']['logger']['class']
Jon Hall714eeba2015-09-29 17:53:10 -07001065 loggerModule = __import__( newModule,
1066 globals(),
1067 locals(),
1068 [loggerClass],
1069 -1 )
1070 loggerClass = getattr( loggerModule, loggerClass )
Jon Hall44506242015-07-29 17:40:26 -07001071 main.logger = loggerClass()
Jon Hall44506242015-07-29 17:40:26 -07001072 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -07001073 print "Could not find the file " + path +\
1074 " using default logger."
adminbae64d82013-08-01 10:50:15 -07001075 load_defaultlogger()
Jon Hall714eeba2015-09-29 17:53:10 -07001076 elif confighash['config']['parser']['file'] is None or\
1077 confighash['config']['parser']['class'] is None:
Jon Halld61331b2015-02-17 16:35:47 -08001078 load_defaultlogger()
adminbae64d82013-08-01 10:50:15 -07001079 else:
1080 load_defaultlogger()
1081
1082def load_defaultlogger():
1083 '''
Jon Hall714eeba2015-09-29 17:53:10 -07001084 It will load the default parser which is xml parser to parse the params and
1085 topology file.
adminbae64d82013-08-01 10:50:15 -07001086 '''
1087 moduleList = main.loggerPath.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -07001088 newModule = ".".join( moduleList[-2:] )
1089 try:
Jon Halld61331b2015-02-17 16:35:47 -08001090 loggerClass = main.loggerClass
Jon Hall714eeba2015-09-29 17:53:10 -07001091 loggerModule = __import__( newModule,
1092 globals(),
1093 locals(),
1094 [loggerClass],
1095 -1 )
1096 loggerClass = getattr( loggerModule, loggerClass )
adminbae64d82013-08-01 10:50:15 -07001097 main.logger = loggerClass()
1098
1099 except ImportError:
1100 print sys.exc_info()[1]
Jon Halld61331b2015-02-17 16:35:47 -08001101 main.exit()
adminbae64d82013-08-01 10:50:15 -07001102
Jon Hall714eeba2015-09-29 17:53:10 -07001103def _echo( self ):
adminbae64d82013-08-01 10:50:15 -07001104 print "THIS IS ECHO"