blob: ebe8c1d80386324dfaf7472ae59478656d947700 [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 -070057
58class TestON:
59 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -070060 TestON will initiate the specified test.
Jon Hall714eeba2015-09-29 17:53:10 -070061 The main tasks are:
kelvin-onlabf70fd542015-05-07 18:41:40 -070062 * Initiate the required Component handles for the test.
adminbae64d82013-08-01 10:50:15 -070063 * Create Log file Handles.
adminbae64d82013-08-01 10:50:15 -070064 '''
Jon Hall714eeba2015-09-29 17:53:10 -070065 def __init__( self, options ):
adminbae64d82013-08-01 10:50:15 -070066 '''
Jon Hall714eeba2015-09-29 17:53:10 -070067 Initialise the component handles specified in the topology file of
68 the specified test.
adminbae64d82013-08-01 10:50:15 -070069 '''
70 # Initialization of the variables.
71 __builtin__.main = self
adminbae64d82013-08-01 10:50:15 -070072 __builtin__.path = path
73 __builtin__.utilities = Utilities()
74 self.TRUE = 1
75 self.FALSE = 0
76 self.ERROR = -1
kelvin-onlabf70fd542015-05-07 18:41:40 -070077 self.NORESULT = 2
adminbae64d82013-08-01 10:50:15 -070078 self.FAIL = False
79 self.PASS = True
kelvin-onlabf70fd542015-05-07 18:41:40 -070080 self.CASERESULT = self.ERROR
81 self.STEPRESULT = self.NORESULT
adminbae64d82013-08-01 10:50:15 -070082 self.init_result = self.TRUE
83 self.testResult = "Summary"
kelvin-onlabf70fd542015-05-07 18:41:40 -070084 self.stepName = ""
85 self.stepCache = ""
Jon Halld61331b2015-02-17 16:35:47 -080086 self.EXPERIMENTAL_MODE = False
adminbae64d82013-08-01 10:50:15 -070087 self.test_target = None
88 self.lastcommand = None
Jon Halld61331b2015-02-17 16:35:47 -080089 self.testDir = tests_path
90 self.configFile = config_path + "teston.cfg"
adminbae64d82013-08-01 10:50:15 -070091 self.parsingClass = "xmlparser"
92 self.parserPath = core_path + "/xmlparser"
93 self.loggerPath = core_path + "/logger"
94 self.loggerClass = "Logger"
95 self.logs_path = logs_path
96 self.driver = ''
kelvin-onlabfb521662015-02-27 09:52:40 -080097 self.Thread = Thread
Jon Hall5b586732015-06-11 11:39:39 -070098 self.cleanupFlag = False
99 self.cleanupLock = threading.Lock()
Jon Hall0fc0d452015-07-14 09:49:58 -0700100 self.initiated = False
Jon Hall65844a32015-03-09 19:09:37 -0700101
Jon Hall25079782015-10-13 13:54:39 -0700102 self.config = self.configparser()
Jon Hall714eeba2015-09-29 17:53:10 -0700103 verifyOptions( options )
adminbae64d82013-08-01 10:50:15 -0700104 load_logger()
105 self.componentDictionary = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700106 self.componentDictionary = self.topology['COMPONENT']
107 self.driversList = []
108 if isinstance( self.componentDictionary, str):
109 self.componentDictionary = dict( self.componentDictionary )
Jon Hall65844a32015-03-09 19:09:37 -0700110
Jon Hall714eeba2015-09-29 17:53:10 -0700111 for component in self.componentDictionary:
112 self.driversList.append( self.componentDictionary[component]['type'] )
Jon Hall65844a32015-03-09 19:09:37 -0700113
Jon Hall714eeba2015-09-29 17:53:10 -0700114 self.driversList = list( set( self.driversList ) ) # Removing duplicates.
adminbae64d82013-08-01 10:50:15 -0700115 # Checking the test_target option set for the component or not
Jon Hall714eeba2015-09-29 17:53:10 -0700116 if isinstance( self.componentDictionary, dict ):
adminbae64d82013-08-01 10:50:15 -0700117 for component in self.componentDictionary.keys():
118 if 'test_target' in self.componentDictionary[component].keys():
119 self.test_target = component
Jon Hall65844a32015-03-09 19:09:37 -0700120
Jon Halld61331b2015-02-17 16:35:47 -0800121 # Checking for the openspeak file and test script
Jon Hall714eeba2015-09-29 17:53:10 -0700122 self.logger.initlog( self )
adminbae64d82013-08-01 10:50:15 -0700123
124 # Creating Drivers Handles
Jon Hall714eeba2015-09-29 17:53:10 -0700125 initString = "\n" + "*" * 30 + "\n CASE INIT \n" + "*" * 30 + "\n"
126 self.log.exact( initString )
adminbae64d82013-08-01 10:50:15 -0700127 self.driverObject = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700128 self.random_order = 111 # Random order id to connect the components
adminbae64d82013-08-01 10:50:15 -0700129 components_connect_order = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700130 if isinstance( self.componentDictionary, dict ):
adminbae64d82013-08-01 10:50:15 -0700131 for component in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700132 if 'connect_order' not in self.componentDictionary[component].keys():
133 self.componentDictionary[component]['connect_order'] = str( self.get_random() )
134 components_connect_order[component] = eval( self.componentDictionary[component]['connect_order'] )
135 # Ordering components based on the connect order.
136 ordered_component_list = sorted( components_connect_order,
137 key=lambda key: components_connect_order[key] )
adminbae64d82013-08-01 10:50:15 -0700138 print ordered_component_list
adminbae64d82013-08-01 10:50:15 -0700139 for component in ordered_component_list:
Jon Hall714eeba2015-09-29 17:53:10 -0700140 self.componentInit( component )
adminbae64d82013-08-01 10:50:15 -0700141
Jon Hall714eeba2015-09-29 17:53:10 -0700142 def configparser( self ):
adminbae64d82013-08-01 10:50:15 -0700143 '''
144 It will parse the config file (teston.cfg) and return as dictionary
145 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700146 matchFileName = re.match( r'(.*)\.cfg', self.configFile, re.M | re.I )
adminbae64d82013-08-01 10:50:15 -0700147 if matchFileName:
Jon Hall714eeba2015-09-29 17:53:10 -0700148 xml = open( self.configFile ).read()
149 try:
150 self.configDict = xmldict.xml_to_dict( xml )
adminbae64d82013-08-01 10:50:15 -0700151 return self.configDict
Jon Hall1306a562015-09-04 11:21:24 -0700152 except IOError:
adminbae64d82013-08-01 10:50:15 -0700153 print "There is no such file to parse " + self.configFile
Jon Hall1306a562015-09-04 11:21:24 -0700154 else:
155 print "There is no such file to parse " + self.configFile
kelvin-onlabf70fd542015-05-07 18:41:40 -0700156
Jon Hall714eeba2015-09-29 17:53:10 -0700157 def componentInit( self, component ):
adminbae64d82013-08-01 10:50:15 -0700158 '''
159 This method will initialize specified component
160 '''
161 global driver_options
Jon Hall0fc0d452015-07-14 09:49:58 -0700162 self.initiated = False
Jon Hall714eeba2015-09-29 17:53:10 -0700163 self.log.info( "Creating component Handle: " + component )
Jon Halld61331b2015-02-17 16:35:47 -0800164 driver_options = {}
adminbae64d82013-08-01 10:50:15 -0700165 if 'COMPONENTS' in self.componentDictionary[component].keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700166 driver_options = dict( self.componentDictionary[component]['COMPONENTS'] )
Jon Hall714eeba2015-09-29 17:53:10 -0700167 driver_options['name'] = component
adminbae64d82013-08-01 10:50:15 -0700168 driverName = self.componentDictionary[component]['type']
Jon Hall714eeba2015-09-29 17:53:10 -0700169 driver_options['type'] = driverName
kelvin-onlabf70fd542015-05-07 18:41:40 -0700170
Jon Hall714eeba2015-09-29 17:53:10 -0700171 classPath = self.getDriverPath( driverName.lower() )
172 driverModule = importlib.import_module( classPath )
173 driverClass = getattr( driverModule, driverName )
adminbae64d82013-08-01 10:50:15 -0700174 driverObject = driverClass()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700175
Jon Hall714eeba2015-09-29 17:53:10 -0700176 if "OCN" in self.componentDictionary[component]['host'] and\
177 main.onoscell:
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700178 self.componentDictionary[component]['host'] = main.mnIP
179
Jon Hall714eeba2015-09-29 17:53:10 -0700180 user_name = self.componentDictionary[component].get( 'user',
181 getpass.getuser() )
182 ip_address = self.componentDictionary[component].get( 'host',
183 'localhost' )
184 pwd = self.componentDictionary[component].get( 'password',
185 'changeme' )
186 port = self.componentDictionary[component].get( 'port' )
187 connect_result = driverObject.connect( user_name=user_name,
188 ip_address=ip_address,
189 pwd=pwd,
190 port=port,
191 options=driver_options)
cameron@onlab.us5cc6a372015-05-11 17:18:07 -0700192
adminbae64d82013-08-01 10:50:15 -0700193 if not connect_result:
Jon Hall714eeba2015-09-29 17:53:10 -0700194 self.log.error( "Exiting from the test execution because connecting to the " +
195 component + " component failed." )
Jon Halld61331b2015-02-17 16:35:47 -0800196 self.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700197
Jon Hall714eeba2015-09-29 17:53:10 -0700198 vars( self )[component] = driverObject
Jon Hall0fc0d452015-07-14 09:49:58 -0700199 self.initiated = True
kelvin-onlabf70fd542015-05-07 18:41:40 -0700200
Jon Hall714eeba2015-09-29 17:53:10 -0700201 def run( self ):
adminbae64d82013-08-01 10:50:15 -0700202 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700203 The Execution of the test script's cases listed in the Test params
204 file will be done here then update each test case result.
205 This method will return main.TRUE if it executed all the test cases
206 successfully, else will retun main.FALSE
adminbae64d82013-08-01 10:50:15 -0700207 '''
adminbae64d82013-08-01 10:50:15 -0700208 self.testCaseResult = {}
Jon Halla1185982014-09-15 14:55:10 -0700209 self.TOTAL_TC = 0
adminbae64d82013-08-01 10:50:15 -0700210 self.TOTAL_TC_RUN = 0
Jon Halld61331b2015-02-17 16:35:47 -0800211 self.TOTAL_TC_PLANNED = 0
adminbae64d82013-08-01 10:50:15 -0700212 self.TOTAL_TC_NORESULT = 0
213 self.TOTAL_TC_FAIL = 0
214 self.TOTAL_TC_PASS = 0
Jon Halla1185982014-09-15 14:55:10 -0700215 self.TEST_ITERATION = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700216
217 # NOTE: number of main.step statements in the
218 # outer most level of the test case. used to
219 # execute code in smaller steps
220 self.stepCount = 0
kelvin-onlabf70fd542015-05-07 18:41:40 -0700221 self.CASERESULT = self.NORESULT
222
Jon Halld61331b2015-02-17 16:35:47 -0800223 import testparser
Jon Hall53c5e662016-04-13 16:06:56 -0700224 test = testparser.TestParser( main.testFile )
adminbae64d82013-08-01 10:50:15 -0700225 self.testscript = test.testscript
226 self.code = test.getStepCode()
Jon Hall714eeba2015-09-29 17:53:10 -0700227 repeat = int( self.params.get( 'repeat', 1 ) )
228 self.TOTAL_TC_PLANNED = len( self.testcases_list ) * repeat
kelvin-onlabf70fd542015-05-07 18:41:40 -0700229
adminbae64d82013-08-01 10:50:15 -0700230 result = self.TRUE
Jon Hall714eeba2015-09-29 17:53:10 -0700231 while repeat:
Jon Halla1185982014-09-15 14:55:10 -0700232 for self.CurrentTestCaseNumber in self.testcases_list:
Jon Hall714eeba2015-09-29 17:53:10 -0700233 result = self.runCase( self.CurrentTestCaseNumber )
234 repeat -= 1
adminbae64d82013-08-01 10:50:15 -0700235 return result
kelvin-onlabf70fd542015-05-07 18:41:40 -0700236
Jon Halle234cc42015-08-31 15:26:47 -0700237 def runCase( self, testCaseNumber ):
adminbae64d82013-08-01 10:50:15 -0700238 self.CurrentTestCaseNumber = testCaseNumber
kelvin-onlabf70fd542015-05-07 18:41:40 -0700239 self.CurrentTestCase = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700240
241 # List of step results in a case. ANDed together to get the result
242 self.stepResultsList = []
kelvin-onlabf70fd542015-05-07 18:41:40 -0700243 self.stepName = ""
Jon Hall783bbf92015-07-23 14:33:19 -0700244 self.caseExplanation = ""
adminbae64d82013-08-01 10:50:15 -0700245 result = self.TRUE
Jon Hall714eeba2015-09-29 17:53:10 -0700246
247 # NOTE: number of main.step statements in the
248 # outer most level of the test case. used to
249 # execute code in smaller steps
250 self.stepCount = 0
251
252 # NOTE: This is the current number of main.step()'s executed
253 # in a case. Used for logging.
254 self.stepNumber = 0
adminbae64d82013-08-01 10:50:15 -0700255 self.EXPERIMENTAL_MODE = self.FALSE
256 self.addCaseHeader()
Jon Halle234cc42015-08-31 15:26:47 -0700257 self.testCaseNumber = str( testCaseNumber )
258 self.CASERESULT = self.NORESULT
adminbae64d82013-08-01 10:50:15 -0700259 stopped = False
Jon Hall5a72b712015-09-28 12:20:59 -0700260 try:
261 self.code[self.testCaseNumber]
Jon Halld61331b2015-02-17 16:35:47 -0800262 except KeyError:
Jon Halle234cc42015-08-31 15:26:47 -0700263 self.log.error( "There is no Test-Case " + self.testCaseNumber )
Jon Hallfebb1c72015-03-05 13:30:09 -0800264 return self.FALSE
adminbae64d82013-08-01 10:50:15 -0700265 self.stepCount = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700266 while self.stepCount < len( self.code[self.testCaseNumber].keys() ):
267 result = self.runStep( self.code, self.testCaseNumber )
Jon Hallfebb1c72015-03-05 13:30:09 -0800268 if result == self.FALSE:
adminbae64d82013-08-01 10:50:15 -0700269 break
Jon Hallfebb1c72015-03-05 13:30:09 -0800270 elif result == self.TRUE:
adminbae64d82013-08-01 10:50:15 -0700271 continue
Jon Hall5a72b712015-09-28 12:20:59 -0700272 # stepResults format: ( stepNo[], stepName[], stepResult[], onFail[] )
273 stepResults = self.stepResultsList
Jon Halle234cc42015-08-31 15:26:47 -0700274 if not stopped:
275 if self.CASERESULT == self.TRUE or self.CASERESULT == self.FALSE:
Jon Hall714eeba2015-09-29 17:53:10 -0700276 # Result was already explitily set somewhere else like
277 # in skipCase()
Jon Halle234cc42015-08-31 15:26:47 -0700278 pass
Jon Hall5a72b712015-09-28 12:20:59 -0700279 elif all( self.TRUE == i for i in stepResults ):
kelvin-onlabf70fd542015-05-07 18:41:40 -0700280 # ALL PASSED
281 self.CASERESULT = self.TRUE
Jon Hall5a72b712015-09-28 12:20:59 -0700282 elif self.FALSE in stepResults:
kelvin-onlabf70fd542015-05-07 18:41:40 -0700283 # AT LEAST ONE FAILED
284 self.CASERESULT = self.FALSE
Jon Hall5a72b712015-09-28 12:20:59 -0700285 elif self.TRUE in stepResults:
kelvin-onlabf70fd542015-05-07 18:41:40 -0700286 # AT LEAST ONE PASSED
287 self.CASERESULT = self.TRUE
288 else:
289 self.CASERESULT = self.NORESULT
Jon Hall714eeba2015-09-29 17:53:10 -0700290 self.testCaseResult[str( self.CurrentTestCaseNumber )] = self.CASERESULT
291 self.logger.updateCaseResults( self )
Jon Hall783bbf92015-07-23 14:33:19 -0700292 self.log.wiki( "<p>" + self.caseExplanation + "</p>" )
293 self.log.summary( self.caseExplanation )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700294 self.log.wiki( "<ul>" )
acsmarse2d1ed12015-10-05 13:51:17 -0700295 subcaseMessage = False
kelvin-onlabf70fd542015-05-07 18:41:40 -0700296 for line in self.stepCache.splitlines():
acsmarse2d1ed12015-10-05 13:51:17 -0700297 if re.search( "[0-9]\.[0-9]", line ): # Step
298 if subcaseMessage: # End of Failure Message Printout
299 self.log.wiki( "</ul>\n" )
300 subcaseMessage = False
301 if re.search( " - PASS$", line ):
302 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"tick\" /></li>\n" )
303 elif re.search( " - FAIL$", line ):
304 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"cross\" /></li>\n" )
305 elif re.search( " - No Result$", line ):
306 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"warning\" /></li>\n" )
307 else: # Substep
308 if not subcaseMessage: # Open Failure Message Printout
309 self.log.wiki( "<ul><li>" + line + "</li>\n" )
310 subcaseMessage = True
311 else: # Add to Failure Message Printout
312 self.log.wiki( "<li>" + line + "</li>\n" )
acsmars27e62dd2015-10-06 11:35:47 -0700313 if subcaseMessage: # End of Failure Message Printout for last item
314 self.log.wiki( "</ul>\n" )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700315 self.log.wiki( "</ul>" )
316 self.log.summary( self.stepCache )
317 self.stepCache = ""
adminbae64d82013-08-01 10:50:15 -0700318 return result
kelvin-onlabf70fd542015-05-07 18:41:40 -0700319
Jon Hall714eeba2015-09-29 17:53:10 -0700320 def runStep( self, code, testCaseNumber ):
adminbae64d82013-08-01 10:50:15 -0700321 if not cli.pause:
Jon Hall5a72b712015-09-28 12:20:59 -0700322 try:
323 step = self.stepCount
324 # stepResults format: ( stepNo, stepName, stepResult, onFail )
325 # NOTE: This is needed to catch results of main.step()'s
326 # called inside functions or loops
327 self.stepResults = ( [], [], [], [] )
adminbae64d82013-08-01 10:50:15 -0700328 exec code[testCaseNumber][step] in module.__dict__
329 self.stepCount = self.stepCount + 1
Jon Hall96b816f2015-11-03 12:00:56 -0800330 self.parseStepResults( testCaseNumber )
Jon Halle234cc42015-08-31 15:26:47 -0700331 except StopIteration: # Raised in self.skipCase()
332 self.log.warn( "Skipping the rest of CASE" +
333 str( testCaseNumber ) )
Jon Hall96b816f2015-11-03 12:00:56 -0800334 self.parseStepResults( testCaseNumber )
Jon Hall714eeba2015-09-29 17:53:10 -0700335 self.stepResultsList.append( self.STEPRESULT )
Jon Halle234cc42015-08-31 15:26:47 -0700336 self.stepCache += "\t\t" + self.onFailMsg + "\n"
337 self.stepCount = self.stepCount + 1
338 return self.FALSE
Jon Hallc1606352015-10-06 14:51:36 -0700339 except StandardError as e:
340 try:
341 stepNo = self.stepResults[0][ self.stepNumber - 1 ]
342 except IndexError:
343 stepNo = "<IndexError>"
344 main.log.warn( "Error trying to get step number. " +
345 "It is likely between step " +
Jon Hall6e709752016-02-01 13:38:46 -0800346 str( self.stepNumber ) + " and step " +
Jon Hallc1606352015-10-06 14:51:36 -0700347 str( self.stepNumber + 1 ) )
348 try:
349 stepName = self.stepResults[1][ self.stepNumber - 1 ]
350 except IndexError:
351 stepName = "<IndexError>"
352 self.log.error( "\nException in the following section of" +
353 " code: " + str( testCaseNumber ) + "." +
354 str( stepNo ) + ": " + stepName )
Jeremyd9e4eb12016-04-13 12:09:06 -0700355 self.log.error( str( e.__class__ ) + str( e.message ) )
adminbae64d82013-08-01 10:50:15 -0700356 self.stepCount = self.stepCount + 1
Jon Hall714eeba2015-09-29 17:53:10 -0700357 self.logger.updateCaseResults( self )
358 # WIKI results
kelvin-onlabf70fd542015-05-07 18:41:40 -0700359 self.log.wiki( "<ul>" )
360 for line in self.stepCache.splitlines():
361 if re.search( " - PASS$", line ):
362 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"tick\" /></li>\n" )
363 elif re.search( " - FAIL$", line ):
364 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"cross\" /></li>\n" )
365 elif re.search( " - No Result$", line ):
366 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"warning\" /></li>\n" )
Jon Hall90627612015-06-09 14:57:02 -0700367 else: # Should only be on fail message
368 self.log.wiki( "<ul><li>" + line + "</li></ul>\n" )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700369 self.log.wiki( "</ul>" )
Jon Hall714eeba2015-09-29 17:53:10 -0700370 # summary results
kelvin-onlabf70fd542015-05-07 18:41:40 -0700371 self.log.summary( self.stepCache )
372 self.stepCache = ""
shahshreya957feaa2015-03-23 16:08:29 -0700373 self.cleanup()
Jon Hall00539b12015-04-03 13:55:46 -0700374 self.exit()
Jon Halle234cc42015-08-31 15:26:47 -0700375 return self.TRUE
adminbae64d82013-08-01 10:50:15 -0700376 if cli.stop:
377 cli.stop = False
adminbae64d82013-08-01 10:50:15 -0700378 self.TOTAL_TC_NORESULT = self.TOTAL_TC_NORESULT + 1
Jon Hall714eeba2015-09-29 17:53:10 -0700379 self.testCaseResult[str( self.CurrentTestCaseNumber )] = "Stopped"
380 self.logger.updateCaseResults( self )
adminbae64d82013-08-01 10:50:15 -0700381 result = self.cleanup()
Jon Halle234cc42015-08-31 15:26:47 -0700382 return self.FALSE
383
Jon Hall96b816f2015-11-03 12:00:56 -0800384 def parseStepResults( self, testCaseNumber ):
385 """
386 Parse throught the step results for the wiki
387 """
388 try:
389 # Iterate through each of the steps and print them
390 for index in range( len( self.stepResults[0] ) ):
391 # stepResults = ( stepNo, stepName, stepResult, onFail )
392 stepNo = self.stepResults[0][ index ]
393 stepName = self.stepResults[1][ index ]
394 stepResult = self.stepResults[2][ index ]
395 onFail = self.stepResults[3][ index ]
396 self.stepCache += "\t" + str( testCaseNumber ) + "."
397 self.stepCache += str( stepNo ) + " "
398 self.stepCache += stepName + " - "
399 if stepResult == self.TRUE:
400 self.stepCache += "PASS\n"
401 elif stepResult == self.FALSE:
402 self.stepCache += "FAIL\n"
403 self.stepCache += "\t\t" + onFail + "\n"
404 else:
405 self.stepCache += "No Result\n"
406 self.stepResultsList.append( stepResult )
407 except Exception:
408 self.log.exception( "Error parsing step results" )
409
Jon Halle234cc42015-08-31 15:26:47 -0700410 def skipCase( self, result="DEFAULT", msg=None ):
411 """
412 Will skip the rest of the code in a test case. The case results will be
413 determined as normal based on completed assertions unless the result
414 argument is given.
415
416 Optional Arguments:
Jon Hall7c4f4302016-07-15 14:39:02 -0700417 result: Case insensitive string. Can be 'PASS' or 'FAIL' and will set
Jon Halle234cc42015-08-31 15:26:47 -0700418 the case result accordingly.
419 msg: Message to be printed when the case is skipped in the reports.
420 """
421 result = result.upper().strip()
422 if result == "PASS":
423 self.CASERESULT = self.TRUE
424 elif result == "FAIL":
425 self.CASERESULT = self.FALSE
426 self.onFailMsg = "Skipping the rest of this case. "
427 if msg:
428 self.onFailMsg += str( msg )
429 raise StopIteration
kelvin-onlabf70fd542015-05-07 18:41:40 -0700430
Jon Hall714eeba2015-09-29 17:53:10 -0700431 def addCaseHeader( self ):
432 caseHeader = "\n" + "*" * 30 + "\n Result summary for Testcase" +\
433 str( self.CurrentTestCaseNumber ) + "\n" + "*" * 30 + "\n"
434 self.log.exact( caseHeader )
435 caseHeader = "\n" + "*" * 40 + "\nStart of Test Case" +\
436 str( self.CurrentTestCaseNumber ) + " : "
adminbae64d82013-08-01 10:50:15 -0700437 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700438 vars( self )[driver + 'log'].info( caseHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700439
Jon Hall714eeba2015-09-29 17:53:10 -0700440 def addCaseFooter( self ):
Jon Hall5a72b712015-09-28 12:20:59 -0700441 stepNo = self.stepResults[0][-2]
Jon Hall714eeba2015-09-29 17:53:10 -0700442 if stepNo > 0:
443 previousStep = " " + str( self.CurrentTestCaseNumber ) + "." +\
444 str( stepNo ) + ": " + str( self.stepName )
445 stepHeader = "\n" + "*" * 40 + "\nEnd of Step " + previousStep +\
446 "\n" + "*" * 40 + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700447
Jon Hall714eeba2015-09-29 17:53:10 -0700448 caseFooter = "\n" + "*" * 40 + "\nEnd of Test case " +\
449 str( self.CurrentTestCaseNumber ) + "\n" + "*" * 40 + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700450
adminbae64d82013-08-01 10:50:15 -0700451 for driver in self.driversList:
Jon Hall714eeba2015-09-29 17:53:10 -0700452 vars( self )[driver].write( stepHeader + "\n" + caseFooter )
adminbae64d82013-08-01 10:50:15 -0700453
Jon Hall714eeba2015-09-29 17:53:10 -0700454 def cleanup( self ):
adminbae64d82013-08-01 10:50:15 -0700455 '''
Jon Hall5b586732015-06-11 11:39:39 -0700456 Print a summary of the current test's results then attempt to release
457 all the component handles and the close opened file handles.
adminbae64d82013-08-01 10:50:15 -0700458
Jon Hall5b586732015-06-11 11:39:39 -0700459 This function shouldbe threadsafe such that cleanup will only be
460 executed once per test.
461
462 This will return TRUE if all the component handles and log handles
463 closed properly, else return FALSE.
adminbae64d82013-08-01 10:50:15 -0700464 '''
465 result = self.TRUE
Jon Hall5b586732015-06-11 11:39:39 -0700466 lock = self.cleanupLock
467 if lock.acquire( False ):
468 try:
469 if self.cleanupFlag is False: # First thread to run this
470 self.cleanupFlag = True
Jon Hall0fc0d452015-07-14 09:49:58 -0700471 if self.initiated:
Jon Hall714eeba2015-09-29 17:53:10 -0700472 self.logger.testSummary( self )
Jon Hall892818c2015-10-20 17:58:34 -0700473 components = self.componentDictionary
474 for component in sorted( components,
475 key=lambda item: components[item]['connect_order'],
476 reverse=True ):
Jon Hall714eeba2015-09-29 17:53:10 -0700477 try:
478 tempObject = vars( self )[component]
479 print "Disconnecting from " + str( tempObject.name ) +\
480 ": " + str( tempObject.__class__)
Jon Hall5b586732015-06-11 11:39:39 -0700481 tempObject.disconnect()
Jon Hall1306a562015-09-04 11:21:24 -0700482 except KeyboardInterrupt:
483 pass
484 except KeyError:
485 # Component not created yet
486 self.log.warn( "Could not find the component " +
487 str( component ) )
488 except StandardError:
Jon Hall5b586732015-06-11 11:39:39 -0700489 self.log.exception( "Exception while disconnecting from " +
490 str( component ) )
491 result = self.FALSE
492 # Closing all the driver's session files
493 for driver in self.componentDictionary.keys():
494 try:
Jon Hall714eeba2015-09-29 17:53:10 -0700495 vars( self )[driver].close_log_handles()
Jon Hall1306a562015-09-04 11:21:24 -0700496 except KeyboardInterrupt:
497 pass
498 except KeyError:
499 # Component not created yet
500 self.log.warn( "Could not find the component " +
501 str( driver ) + " while trying to" +
502 " close log file" )
503 except StandardError:
Jon Hall5b586732015-06-11 11:39:39 -0700504 self.log.exception( "Exception while closing log files for " +
505 str( driver ) )
506 result = self.FALSE
507 else:
508 pass # Someone else already ran through this function
509 finally:
510 lock.release()
511 else: # Someone already has a lock
512 # NOTE: This could cause problems if we don't release the lock
513 # correctly
514 lock.acquire() # Wait for the other thread to finish
515 # NOTE: If we don't wait, exit could be called while the thread
516 # with the lock is still cleaning up
517 lock.release()
adminbae64d82013-08-01 10:50:15 -0700518 return result
Jon Halld61331b2015-02-17 16:35:47 -0800519
Jon Hall714eeba2015-09-29 17:53:10 -0700520 def pause( self ):
adminbae64d82013-08-01 10:50:15 -0700521 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700522 This function will pause the test's execution, and will continue after
523 user provide 'resume' command.
adminbae64d82013-08-01 10:50:15 -0700524 '''
525 __builtin__.testthread.pause()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700526
Jon Hall714eeba2015-09-29 17:53:10 -0700527 def onfail( self, *components ):
adminbae64d82013-08-01 10:50:15 -0700528 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -0700529 When test step failed, calling all the components onfail.
adminbae64d82013-08-01 10:50:15 -0700530 '''
adminbae64d82013-08-01 10:50:15 -0700531 if not components:
Jon Hall714eeba2015-09-29 17:53:10 -0700532 try:
adminbae64d82013-08-01 10:50:15 -0700533 for component in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700534 tempObject = vars( self )[component]
adminbae64d82013-08-01 10:50:15 -0700535 result = tempObject.onfail()
Jon Hall1306a562015-09-04 11:21:24 -0700536 except StandardError as e:
Jon Hall714eeba2015-09-29 17:53:10 -0700537 print str( e )
adminbae64d82013-08-01 10:50:15 -0700538 result = self.FALSE
adminbae64d82013-08-01 10:50:15 -0700539 else:
Jon Hall714eeba2015-09-29 17:53:10 -0700540 try:
adminbae64d82013-08-01 10:50:15 -0700541 for component in components:
Jon Hall714eeba2015-09-29 17:53:10 -0700542 tempObject = vars( self )[component]
adminbae64d82013-08-01 10:50:15 -0700543 result = tempObject.onfail()
Jon Hall1306a562015-09-04 11:21:24 -0700544 except StandardError as e:
Jon Hall714eeba2015-09-29 17:53:10 -0700545 print str( e )
adminbae64d82013-08-01 10:50:15 -0700546 result = self.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700547
Jon Hall714eeba2015-09-29 17:53:10 -0700548 def getDriverPath( self, driverName ):
adminbae64d82013-08-01 10:50:15 -0700549 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700550 Based on the component 'type' specified in the params , this method
551 will find the absolute path, by recursively searching the name of
552 the component.
553
554 NOTE: This function requires the linux 'find' command.
adminbae64d82013-08-01 10:50:15 -0700555 '''
556 import commands
557
Jon Hall714eeba2015-09-29 17:53:10 -0700558 cmd = "find " + drivers_path + " -name " + driverName + ".py"
559 result = commands.getoutput( cmd )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700560
Jon Hall714eeba2015-09-29 17:53:10 -0700561 result_array = str( result ).split( '\n' )
adminbae64d82013-08-01 10:50:15 -0700562 result_count = 0
kelvin-onlabf70fd542015-05-07 18:41:40 -0700563
adminbae64d82013-08-01 10:50:15 -0700564 for drivers_list in result_array:
Jon Hall714eeba2015-09-29 17:53:10 -0700565 result_count = result_count + 1
566 if result_count > 1:
567 print "Found " + driverName + " " + str( result_count ) + " times:"
568 print str( result_array )
adminbae64d82013-08-01 10:50:15 -0700569 self.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700570
Jon Hall714eeba2015-09-29 17:53:10 -0700571 result = re.sub( "(.*)drivers", "", result )
572 result = re.sub( "\/\/", "/", result )
573 result = re.sub( "\.py", "", result )
574 result = re.sub( "\.pyc", "", result )
575 result = re.sub( "\/", ".", result )
576 result = "drivers" + result
adminbae64d82013-08-01 10:50:15 -0700577 return result
adminbae64d82013-08-01 10:50:15 -0700578
Jon Hall714eeba2015-09-29 17:53:10 -0700579 def step( self, stepDesc ):
adminbae64d82013-08-01 10:50:15 -0700580 '''
581 The step information of the test-case will append to the logs.
582 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700583 previousStep = " " + str( self.CurrentTestCaseNumber ) + "." +\
584 str( self.stepNumber ) + ": " + str( self.stepName )
adminbae64d82013-08-01 10:50:15 -0700585 self.stepName = stepDesc
Jon Hall5a72b712015-09-28 12:20:59 -0700586 self.stepNumber += 1
587 self.stepResults[0].append( self.stepNumber )
588 self.stepResults[1].append( stepDesc )
589 self.stepResults[2].append( self.NORESULT )
590 self.stepResults[3].append( "No on fail message given" )
adminbae64d82013-08-01 10:50:15 -0700591
Jon Hall714eeba2015-09-29 17:53:10 -0700592 stepName = " " + str( self.CurrentTestCaseNumber ) + "." +\
593 str( self.stepNumber ) + ": " + str( stepDesc )
adminbae64d82013-08-01 10:50:15 -0700594 self.log.step(stepName)
595 stepHeader = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700596 line = "\n" + "-" * 45 + "\n"
597 if self.stepNumber > 1:
598 stepHeader = line + "End of Step " + previousStep + line
599 stepHeader += line + "Start of Step" + stepName + line
adminbae64d82013-08-01 10:50:15 -0700600 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700601 vars( self )[driver + 'log'].info( stepHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700602
Jon Hall714eeba2015-09-29 17:53:10 -0700603 def case( self, testCaseName ):
adminbae64d82013-08-01 10:50:15 -0700604 '''
605 Test's each test-case information will append to the logs.
606 '''
Jon Halld61331b2015-02-17 16:35:47 -0800607 self.CurrentTestCase = testCaseName
Jon Hall714eeba2015-09-29 17:53:10 -0700608 testCaseName = " " + str( testCaseName )
609 self.log.case( testCaseName )
610 caseHeader = testCaseName + "\n" + "*" * 40 + "\n"
adminbae64d82013-08-01 10:50:15 -0700611 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700612 vars( self )[driver + 'log'].info( caseHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700613
Jon Hall714eeba2015-09-29 17:53:10 -0700614 def testDesc( self, description ):
adminbae64d82013-08-01 10:50:15 -0700615 '''
616 Test description will append to the logs.
617 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700618 description = "Test Description : " + str( description )
619 self.log.info( description )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700620
Jon Hall714eeba2015-09-29 17:53:10 -0700621 def _getTest( self ):
adminbae64d82013-08-01 10:50:15 -0700622 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700623 This method will parse the test script to find required test
624 information.
adminbae64d82013-08-01 10:50:15 -0700625 '''
Jon Hall53c5e662016-04-13 16:06:56 -0700626 testFileHandler = open( main.testFile, 'r' )
adminbae64d82013-08-01 10:50:15 -0700627 testFileList = testFileHandler.readlines()
628 testFileHandler.close()
adminbae64d82013-08-01 10:50:15 -0700629 counter = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700630 for index in range( len( testFileList ) ):
631 lineMatch = re.match( '\s+def CASE(\d+)(.*):',
632 testFileList[index],
633 0 )
adminbae64d82013-08-01 10:50:15 -0700634 if lineMatch:
Jon Hall714eeba2015-09-29 17:53:10 -0700635 counter = counter + 1
636 self.TC_PLANNED = len( self.testcases_list )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700637
Jon Hall714eeba2015-09-29 17:53:10 -0700638 def response_parser( self, response, return_format ):
adminbae64d82013-08-01 10:50:15 -0700639 ''' It will load the default response parser '''
640 response_dict = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700641 response_dict = self.response_to_dict( response, return_format )
642 return_format_string = self.dict_to_return_format( response,
643 return_format,
644 response_dict )
adminbae64d82013-08-01 10:50:15 -0700645 return return_format_string
kelvin-onlabf70fd542015-05-07 18:41:40 -0700646
Jon Hall714eeba2015-09-29 17:53:10 -0700647 def response_to_dict( self, response, return_format ):
adminbae64d82013-08-01 10:50:15 -0700648 response_dict = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700649 json_match = re.search( '^\s*{', response )
650 xml_match = re.search( '^\s*\<', response )
651 ini_match = re.search( '^\s*\[', response )
652 if json_match:
653 self.log.info( "Response is in 'JSON' format, converting to '" +
654 return_format + "' format" )
Jon Halld61331b2015-02-17 16:35:47 -0800655 # Formatting the json string
Jon Hall714eeba2015-09-29 17:53:10 -0700656 response = re.sub( r"{\s*'?(\w)", r'{"\1', response )
657 response = re.sub( r",\s*'?(\w)", r',"\1', response )
658 response = re.sub( r"(\w)'?\s*:", r'\1":', response )
659 response = re.sub( r":\s*'(\w)'\s*([,}])", r':"\1"\2', response )
660 try:
adminbae64d82013-08-01 10:50:15 -0700661 import json
Jon Hall714eeba2015-09-29 17:53:10 -0700662 response_dict = json.loads( response )
Jon Hall1306a562015-09-04 11:21:24 -0700663 except StandardError:
Jon Hall2a5002c2015-08-21 16:49:11 -0700664 self.log.exception( "Json Parser is unable to parse the string" )
adminbae64d82013-08-01 10:50:15 -0700665 return response_dict
Jon Hall714eeba2015-09-29 17:53:10 -0700666 elif ini_match:
667 self.log.info( "Response is in 'INI' format, converting to '" +
668 return_format + "' format" )
adminbae64d82013-08-01 10:50:15 -0700669 from configobj import ConfigObj
Jon Hall714eeba2015-09-29 17:53:10 -0700670 response_file = open( "respnse_file.temp", 'w' )
671 response_file.write( response )
Jon Halld61331b2015-02-17 16:35:47 -0800672 response_file.close()
Jon Hall714eeba2015-09-29 17:53:10 -0700673 response_dict = ConfigObj( "respnse_file.temp" )
adminbae64d82013-08-01 10:50:15 -0700674 return response_dict
Jon Hall714eeba2015-09-29 17:53:10 -0700675 elif xml_match:
676 self.log.info( "Response is in 'XML' format, converting to '" +
677 return_format + "' format" )
678 try:
679 response_dict = xmldict.xml_to_dict( "<response> " +
680 str( response ) +
681 " </response>" )
Jon Hall1306a562015-09-04 11:21:24 -0700682 except StandardError:
683 self.log.exception()
adminbae64d82013-08-01 10:50:15 -0700684 return response_dict
kelvin-onlabf70fd542015-05-07 18:41:40 -0700685
Jon Hall714eeba2015-09-29 17:53:10 -0700686 def dict_to_return_format( self, response, return_format, response_dict ):
687 if return_format == 'table':
adminbae64d82013-08-01 10:50:15 -0700688 ''' Will return in table format'''
689 to_do = "Call the table output formatter"
690 global response_table
691 response_table = '\n'
Jon Hall714eeba2015-09-29 17:53:10 -0700692 response_table = response_table + '\t'.join( response_dict ) + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700693
Jon Hall714eeba2015-09-29 17:53:10 -0700694 def get_table( value_to_convert ):
695 ''' This will parse the dictionary recusrsively and print as
696 table format'''
adminbae64d82013-08-01 10:50:15 -0700697 table_data = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700698 if isinstance( value_to_convert, dict ):
699 table_data = table_data + '\t'.join( value_to_convert ) +\
700 "\n"
701 for temp_val in value_to_convert.values():
702 table_data = table_data + get_table( temp_val )
703 else:
704 table_data = table_data + str( value_to_convert ) + "\t"
Jon Halld61331b2015-02-17 16:35:47 -0800705 return table_data
kelvin-onlabf70fd542015-05-07 18:41:40 -0700706
Jon Hall714eeba2015-09-29 17:53:10 -0700707 for value in response_dict.values():
708 response_table = response_table + get_table( value )
adminbae64d82013-08-01 10:50:15 -0700709 return response_table
kelvin-onlabf70fd542015-05-07 18:41:40 -0700710
Jon Hall714eeba2015-09-29 17:53:10 -0700711 elif return_format == 'config':
adminbae64d82013-08-01 10:50:15 -0700712 ''' Will return in config format'''
713 to_do = 'Call dict to config coverter'
Jon Hall714eeba2015-09-29 17:53:10 -0700714 response_string = str( response_dict )
adminbae64d82013-08-01 10:50:15 -0700715 print response_string
Jon Hall714eeba2015-09-29 17:53:10 -0700716 response_config = re.sub( ",", "\n\t", response_string )
717 response_config = re.sub( "u\'", "\'", response_config )
718 response_config = re.sub( "{", "", response_config )
719 response_config = re.sub( "}", "\n", response_config )
720 response_config = re.sub( ":", " =", response_config )
721 return "[response]\n\t " + response_config
adminbae64d82013-08-01 10:50:15 -0700722 elif return_format == 'xml':
723 ''' Will return in xml format'''
Jon Hall714eeba2015-09-29 17:53:10 -0700724 response_xml = xmldict.dict_to_xml( response_dict )
725 response_xml = re.sub( ">\s*<", ">\n<", response_xml )
726 return "\n" + response_xml
adminbae64d82013-08-01 10:50:15 -0700727 elif return_format == 'json':
728 ''' Will return in json format'''
729 to_do = 'Call dict to xml coverter'
730 import json
Jon Hall714eeba2015-09-29 17:53:10 -0700731 response_json = json.dumps( response_dict )
adminbae64d82013-08-01 10:50:15 -0700732 return response_json
kelvin-onlabf70fd542015-05-07 18:41:40 -0700733
Jon Hall714eeba2015-09-29 17:53:10 -0700734 def get_random( self ):
adminbae64d82013-08-01 10:50:15 -0700735 self.random_order = self.random_order + 1
736 return self.random_order
kelvin-onlabf70fd542015-05-07 18:41:40 -0700737
Jon Hall714eeba2015-09-29 17:53:10 -0700738 def exit( self ):
adminbae64d82013-08-01 10:50:15 -0700739 __builtin__.testthread = None
Jon Hall5b586732015-06-11 11:39:39 -0700740 for thread in threading.enumerate():
741 if thread.isAlive():
742 try:
743 thread._Thread__stop()
744 except:
Jon Hall1306a562015-09-04 11:21:24 -0700745 # NOTE: We should catch any exceptions while trying to
746 # close the thread so that we can try to close the other
747 # threads as well
Jon Hall714eeba2015-09-29 17:53:10 -0700748 print str( thread.getName() ) +\
749 ' could not be terminated'
adminbae64d82013-08-01 10:50:15 -0700750 sys.exit()
751
Jon Hallcd3d2a32015-10-01 11:07:28 -0700752 def stop( self, email=False ):
753 """
754 Stop the test until Ctrl-D is entered.
755 Ctrl-C will kill the test
Jon Hall25079782015-10-13 13:54:39 -0700756
757 Optional arguments:
758 email can either be a bool, or you can specify the email address
759 to send the email to
Jon Hallcd3d2a32015-10-01 11:07:28 -0700760 """
761 try:
762 if email:
Jon Hall25079782015-10-13 13:54:39 -0700763 if '@' in email:
764 main.mail = email
765 utilities.send_warning_email()
Jon Hallcd3d2a32015-10-01 11:07:28 -0700766 self.log.error( "Test execution suspended. Press Ctrl-D to "
767 "resume or Ctrl-C to exit the test" )
768 # NOTE: Ctrl-D needs to be entered on a new line
769 while True:
770 # TODO: we could give the user an interactive prompt where
771 # they could call functions
772 raw_input()
773 except EOFError:
774 return
775 # Pass all other exceptions up to caller
776
777
Jon Hall714eeba2015-09-29 17:53:10 -0700778def verifyOptions( options ):
adminbae64d82013-08-01 10:50:15 -0700779 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700780 This will verify the command line options and set to default values,
781 if any option not given in command line.
adminbae64d82013-08-01 10:50:15 -0700782 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700783 verifyTest( options )
784 verifyExample( options )
785 verifyTestScript( options )
YPZhang1c89e762016-06-29 10:43:58 -0700786 verifyParams( options )
Jon Hall714eeba2015-09-29 17:53:10 -0700787 verifyLogdir( options )
788 verifyMail( options )
789 verifyTestCases( options )
790 verifyOnosCell( options )
adminbae64d82013-08-01 10:50:15 -0700791
Jon Hall714eeba2015-09-29 17:53:10 -0700792def verifyTest( options ):
Jon Hall44506242015-07-29 17:40:26 -0700793 try:
794 if options.testname:
795 main.TEST = options.testname
Jon Hall714eeba2015-09-29 17:53:10 -0700796 main.classPath = "tests." + main.TEST + "." + main.TEST
Jon Hall44506242015-07-29 17:40:26 -0700797 main.tests_path = tests_path
798 elif options.example:
799 main.TEST = options.example
Jon Hall714eeba2015-09-29 17:53:10 -0700800 main.tests_path = path + "/examples/"
801 main.classPath = "examples." + main.TEST + "." + main.TEST
Jon Hall44506242015-07-29 17:40:26 -0700802 except AttributeError:
adminbae64d82013-08-01 10:50:15 -0700803 print "Test or Example not specified please specify the --test <test name > or --example <example name>"
Jon Hall5b586732015-06-11 11:39:39 -0700804 main.exit()
adminbae64d82013-08-01 10:50:15 -0700805
Jon Hall714eeba2015-09-29 17:53:10 -0700806def verifyExample( options ):
adminbae64d82013-08-01 10:50:15 -0700807 if options.example:
Jon Hall714eeba2015-09-29 17:53:10 -0700808 main.testDir = path + '/examples/'
809 main.tests_path = path + "/examples/"
810 main.classPath = "examples." + main.TEST + "." + main.TEST
kelvin-onlabf70fd542015-05-07 18:41:40 -0700811
Jon Hall714eeba2015-09-29 17:53:10 -0700812def verifyLogdir( options ):
Jon Hall88e498c2015-03-06 09:54:35 -0800813 # Verifying Log directory option
adminbae64d82013-08-01 10:50:15 -0700814 if options.logdir:
815 main.logdir = options.logdir
Jon Hall714eeba2015-09-29 17:53:10 -0700816 else:
Jon Halld61331b2015-02-17 16:35:47 -0800817 main.logdir = main.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700818
Jon Hall714eeba2015-09-29 17:53:10 -0700819def verifyMail( options ):
Jon Hall25079782015-10-13 13:54:39 -0700820 # Mail-To: field
821 if options.mail: # Test run specific
adminbae64d82013-08-01 10:50:15 -0700822 main.mail = options.mail
Jon Hall25079782015-10-13 13:54:39 -0700823 elif main.params.get('mail'): # Test suite specific
824 main.mail = main.params.get( 'mail' )
825 else: # TestON specific
826 main.mail = main.config['config'].get( 'mail_to' )
827 # Mail-From: field
828 main.sender = main.config['config'].get( 'mail_from' )
829 # Mail smtp server
830 main.smtp = main.config['config'].get( 'mail_server' )
831 # Mail-From account password
832 main.senderPwd = main.config['config'].get( 'mail_pass' )
833
Devin Lim2df68a12017-06-30 15:39:05 -0700834def evalTestCase( tempList ):
835 tList = []
836 for tcase in tempList:
837 if isinstance( tcase, list ):
838 tList.extend( evalTestCase( tcase ) )
839 else:
840 tList.extend( [tcase] )
841 return tList
adminbae64d82013-08-01 10:50:15 -0700842
Jon Hall714eeba2015-09-29 17:53:10 -0700843def verifyTestCases( options ):
Jon Hall88e498c2015-03-06 09:54:35 -0800844 # Getting Test cases list
adminbae64d82013-08-01 10:50:15 -0700845 if options.testcases:
Jon Hallfebb1c72015-03-05 13:30:09 -0800846 testcases_list = options.testcases
Jon Hall88e498c2015-03-06 09:54:35 -0800847 # sys.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700848 testcases_list = re.sub( "(\[|\])", "", options.testcases )
849 main.testcases_list = eval( testcases_list + "," )
850 else:
adminbae64d82013-08-01 10:50:15 -0700851 if 'testcases' in main.params.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700852 temp = eval( main.params['testcases'] + "," )
Devin Lim2df68a12017-06-30 15:39:05 -0700853 main.testcases_list = evalTestCase( list( temp ) )
Jon Hall714eeba2015-09-29 17:53:10 -0700854 else:
855 print "Testcases not specifed in params, please provide in " +\
856 "params file or 'testcases' commandline argument"
Jon Halld61331b2015-02-17 16:35:47 -0800857 sys.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700858
Jon Hall714eeba2015-09-29 17:53:10 -0700859def verifyOnosCell( options ):
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700860 # Verifying onoscell option
Hari Krishna03f530e2015-07-10 17:28:27 -0700861 if options.onoscell:
862 main.onoscell = options.onoscell
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700863 main.onosIPs = []
864 main.mnIP = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700865 cellCMD = ". ~/.profile; cell " + main.onoscell
866 output = subprocess.check_output( ["bash", '-c', cellCMD] )
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700867 splitOutput = output.splitlines()
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
874 if re.match( "OC[1-9]", splitOutput[i] ):
875 onosNodes = splitOutput[i].split( "=" )
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700876 main.onosIPs.append( onosNodes[1] )
Jon Hall714eeba2015-09-29 17:53:10 -0700877 else:
Hari Krishna03f530e2015-07-10 17:28:27 -0700878 main.onoscell = main.FALSE
879
Jon Hall714eeba2015-09-29 17:53:10 -0700880def verifyTestScript( options ):
adminbae64d82013-08-01 10:50:15 -0700881 '''
882 Verifyies test script.
883 '''
Jon Halld61331b2015-02-17 16:35:47 -0800884 main.openspeak = openspeak.OpenSpeak()
Jon Hall53c5e662016-04-13 16:06:56 -0700885 directory = main.testDir + "/" + main.TEST
886 if os.path.exists( directory ):
887 pass
888 else:
889 directory = ""
890 for root, dirs, files in os.walk( main.testDir, topdown=True):
891 if not directory:
892 for name in dirs:
893 if name == main.TEST:
894 directory = ( os.path.join( root, name ) )
895 index = directory.find( "/tests/" ) + 1
896 main.classPath = directory[index:].replace( '/', '.' ) + "." + main.TEST
897 break
898 openspeakfile = directory + "/" + main.TEST + ".ospk"
899 main.testFile = directory + "/" + main.TEST + ".py"
Jon Hall714eeba2015-09-29 17:53:10 -0700900 if os.path.exists( openspeakfile ):
Jon Hall44506242015-07-29 17:40:26 -0700901 # Openspeak file found, compiling to python
Jon Hall714eeba2015-09-29 17:53:10 -0700902 main.openspeak.compiler( openspeakfile=openspeakfile, writetofile=1 )
Jon Hall53c5e662016-04-13 16:06:56 -0700903 elif os.path.exists( main.testFile ):
Jon Hall44506242015-07-29 17:40:26 -0700904 # No openspeak found, using python file instead
905 pass
adminbae64d82013-08-01 10:50:15 -0700906 else:
Jon Hall714eeba2015-09-29 17:53:10 -0700907 print "\nThere is no \"" + main.TEST + "\" test script.\nPlease provide a " +\
Jon Hall44506242015-07-29 17:40:26 -0700908 "Python or OpenSpeak test script in the tests folder: " +\
Jon Hall714eeba2015-09-29 17:53:10 -0700909 main.testDir + "/" + main.TEST + "/"
adminbae64d82013-08-01 10:50:15 -0700910 __builtin__.testthread = None
911 main.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700912 try:
913 testModule = __import__( main.classPath,
914 globals(),
915 locals(),
916 [main.TEST],
917 -1 )
Jon Hall1306a562015-09-04 11:21:24 -0700918 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -0700919 print "There was an import error, it might mean that there is " +\
920 "no test named " + main.TEST
Jon Halld61331b2015-02-17 16:35:47 -0800921 main.exit()
adminbae64d82013-08-01 10:50:15 -0700922
Jon Hall714eeba2015-09-29 17:53:10 -0700923 testClass = getattr( testModule, main.TEST )
adminbae64d82013-08-01 10:50:15 -0700924 main.testObject = testClass()
925 load_parser()
Jon Hall714eeba2015-09-29 17:53:10 -0700926 main.params = main.parser.parseParams( main.classPath )
927 main.topology = main.parser.parseTopology( main.classPath )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700928
YPZhang1c89e762016-06-29 10:43:58 -0700929def verifyParams( options ):
Jon Hall714eeba2015-09-29 17:53:10 -0700930 try:
adminbae64d82013-08-01 10:50:15 -0700931 main.params = main.params['PARAMS']
Jon Hall1306a562015-09-04 11:21:24 -0700932 except KeyError:
Jon Hall714eeba2015-09-29 17:53:10 -0700933 print "Error with the params file: Either the file not specified " +\
934 "or the format is not correct"
Jon Halld61331b2015-02-17 16:35:47 -0800935 main.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700936 try:
adminbae64d82013-08-01 10:50:15 -0700937 main.topology = main.topology['TOPOLOGY']
Jon Hall1306a562015-09-04 11:21:24 -0700938 except KeyError:
Jon Hall714eeba2015-09-29 17:53:10 -0700939 print "Error with the Topology file: Either the file not specified " +\
940 "or the format is not correct"
adminbae64d82013-08-01 10:50:15 -0700941 main.exit()
YPZhang1c89e762016-06-29 10:43:58 -0700942 # Overwrite existing params variables if they are specified from command line
943 if len(options.params) > 0:
944 # Some params variables are specified from command line
945 for param in options.params:
946 if not re.search( ".=.", param ):
947 print( "Error when parsing params: params should follow key=value format" )
948 continue
Jon Hall7c4f4302016-07-15 14:39:02 -0700949 # Split the param string to catch nested keys and the value
YPZhang1c89e762016-06-29 10:43:58 -0700950 [ keys, value ] = param.split( "=" )
951 # Split the nested keys according to its hierarchy
952 keyList = keys.split( "/" )
953 # Get the outermost dictionary
954 paramDict = main.params
955 # Get the innermost dictionary
956 try:
957 while len( keyList ) > 1:
958 key = keyList.pop(0)
959 assert isinstance( paramDict[ key ], dict )
960 paramDict = paramDict[ key ]
961 except KeyError:
962 print( "Error when parsing params: key \"" + key + "\" not found in main.params" )
963 main.exit()
964 except AssertionError:
965 print( "Error when parsing params: \"" + key + "\" is already the innermost level in main.params" )
966 main.exit()
967 # Change the value
968 if not paramDict.has_key( keyList[0] ):
969 print( "Error when parsing params: key \"" + keyList[0] + "\" not found in main.params" )
970 main.exit()
971 elif isinstance( paramDict[ keyList[0] ], dict ):
972 print( "Error when parsing params: more levels under key \"" + keyList[0] + "\" in main.params" )
973 main.exit()
974 else:
975 paramDict[ keyList[0] ] = value
kelvin-onlabf70fd542015-05-07 18:41:40 -0700976
Jon Hall714eeba2015-09-29 17:53:10 -0700977def load_parser():
adminbae64d82013-08-01 10:50:15 -0700978 '''
979 It facilitates the loading customised parser for topology and params file.
980 It loads parser mentioned in tab named parser of teston.cfg file.
Jon Hall714eeba2015-09-29 17:53:10 -0700981 It also loads default xmlparser if no parser have specified in teston.cfg
982 file.
adminbae64d82013-08-01 10:50:15 -0700983
984 '''
985 confighash = main.configDict
Jon Hall714eeba2015-09-29 17:53:10 -0700986 if 'file' in confighash['config']['parser'] and\
987 'class' in confighash['config']['parser']:
Jon Hall44506242015-07-29 17:40:26 -0700988 path = confighash['config']['parser']['file']
Jon Hall714eeba2015-09-29 17:53:10 -0700989 if path is not None or\
990 confighash['config']['parser']['class'] is not None:
Jon Hall44506242015-07-29 17:40:26 -0700991 try:
992 module = re.sub( r".py\s*$", "", path )
adminbae64d82013-08-01 10:50:15 -0700993 moduleList = module.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -0700994 newModule = ".".join( moduleList[-2:] )
Jon Hall44506242015-07-29 17:40:26 -0700995 parsingClass = confighash['config']['parser']['class']
Jon Hall714eeba2015-09-29 17:53:10 -0700996 parsingModule = __import__( newModule,
997 globals(),
998 locals(),
999 [parsingClass],
1000 -1 )
1001 parsingClass = getattr( parsingModule, parsingClass )
Jon Hall44506242015-07-29 17:40:26 -07001002 main.parser = parsingClass()
Jon Hall714eeba2015-09-29 17:53:10 -07001003 if hasattr( main.parser, "parseParams" ) and\
1004 hasattr( main.parser, "parseTopology" ) and\
1005 hasattr( main.parser, "parse" ):
Jon Hall44506242015-07-29 17:40:26 -07001006 pass
1007 else:
1008 print "Invalid parser format"
adminbae64d82013-08-01 10:50:15 -07001009 main.exit()
Jon Hall44506242015-07-29 17:40:26 -07001010 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -07001011 print "Could not find the file " + path +\
1012 " using default parser."
Jon Halld61331b2015-02-17 16:35:47 -08001013 load_defaultParser()
Jon Hall714eeba2015-09-29 17:53:10 -07001014 elif confighash['config']['parser']['file'] is None or\
1015 confighash['config']['parser']['class'] is None:
Jon Halld61331b2015-02-17 16:35:47 -08001016 load_defaultParser()
adminbae64d82013-08-01 10:50:15 -07001017 else:
1018 load_defaultParser()
1019
1020def load_defaultParser():
1021 '''
Jon Hall714eeba2015-09-29 17:53:10 -07001022 It will load the default parser which is xml parser to parse the params and
1023 topology file.
adminbae64d82013-08-01 10:50:15 -07001024 '''
1025 moduleList = main.parserPath.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -07001026 newModule = ".".join( moduleList[-2:] )
1027 try:
Jon Halld61331b2015-02-17 16:35:47 -08001028 parsingClass = main.parsingClass
Jon Hall714eeba2015-09-29 17:53:10 -07001029 parsingModule = __import__( newModule,
1030 globals(),
1031 locals(),
1032 [parsingClass],
1033 -1 )
1034 parsingClass = getattr( parsingModule, parsingClass )
adminbae64d82013-08-01 10:50:15 -07001035 main.parser = parsingClass()
Jon Hall714eeba2015-09-29 17:53:10 -07001036 if hasattr( main.parser, "parseParams" ) and\
1037 hasattr( main.parser, "parseTopology" ) and\
1038 hasattr( main.parser, "parse" ):
adminbae64d82013-08-01 10:50:15 -07001039 pass
1040 else:
1041 main.exit()
adminbae64d82013-08-01 10:50:15 -07001042 except ImportError:
1043 print sys.exc_info()[1]
1044
Jon Hall714eeba2015-09-29 17:53:10 -07001045def load_logger():
adminbae64d82013-08-01 10:50:15 -07001046 '''
1047 It facilitates the loading customised parser for topology and params file.
1048 It loads parser mentioned in tab named parser of teston.cfg file.
Jon Hall714eeba2015-09-29 17:53:10 -07001049 It also loads default xmlparser if no parser have specified in teston.cfg
1050 file.
adminbae64d82013-08-01 10:50:15 -07001051 '''
1052 confighash = main.configDict
Jon Hall714eeba2015-09-29 17:53:10 -07001053 if 'file' in confighash['config']['logger'] and\
1054 'class' in confighash['config']['logger']:
Jon Hall44506242015-07-29 17:40:26 -07001055 path = confighash['config']['logger']['file']
Jon Hall714eeba2015-09-29 17:53:10 -07001056 if path is not None or\
1057 confighash['config']['logger']['class'] is not None:
Jon Hall44506242015-07-29 17:40:26 -07001058 try:
1059 module = re.sub( r".py\s*$", "", path )
Jon Hall714eeba2015-09-29 17:53:10 -07001060 moduleList = module.split( "/" )
1061 newModule = ".".join( moduleList[-2:] )
Jon Hall44506242015-07-29 17:40:26 -07001062 loggerClass = confighash['config']['logger']['class']
Jon Hall714eeba2015-09-29 17:53:10 -07001063 loggerModule = __import__( newModule,
1064 globals(),
1065 locals(),
1066 [loggerClass],
1067 -1 )
1068 loggerClass = getattr( loggerModule, loggerClass )
Jon Hall44506242015-07-29 17:40:26 -07001069 main.logger = loggerClass()
Jon Hall44506242015-07-29 17:40:26 -07001070 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -07001071 print "Could not find the file " + path +\
1072 " using default logger."
adminbae64d82013-08-01 10:50:15 -07001073 load_defaultlogger()
Jon Hall714eeba2015-09-29 17:53:10 -07001074 elif confighash['config']['parser']['file'] is None or\
1075 confighash['config']['parser']['class'] is None:
Jon Halld61331b2015-02-17 16:35:47 -08001076 load_defaultlogger()
adminbae64d82013-08-01 10:50:15 -07001077 else:
1078 load_defaultlogger()
1079
1080def load_defaultlogger():
1081 '''
Jon Hall714eeba2015-09-29 17:53:10 -07001082 It will load the default parser which is xml parser to parse the params and
1083 topology file.
adminbae64d82013-08-01 10:50:15 -07001084 '''
1085 moduleList = main.loggerPath.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -07001086 newModule = ".".join( moduleList[-2:] )
1087 try:
Jon Halld61331b2015-02-17 16:35:47 -08001088 loggerClass = main.loggerClass
Jon Hall714eeba2015-09-29 17:53:10 -07001089 loggerModule = __import__( newModule,
1090 globals(),
1091 locals(),
1092 [loggerClass],
1093 -1 )
1094 loggerClass = getattr( loggerModule, loggerClass )
adminbae64d82013-08-01 10:50:15 -07001095 main.logger = loggerClass()
1096
1097 except ImportError:
1098 print sys.exc_info()[1]
Jon Halld61331b2015-02-17 16:35:47 -08001099 main.exit()
adminbae64d82013-08-01 10:50:15 -07001100
Jon Hall714eeba2015-09-29 17:53:10 -07001101def _echo( self ):
adminbae64d82013-08-01 10:50:15 -07001102 print "THIS IS ECHO"