sbsv2/raptor/test/common/run_tests.py
changeset 3 e1eecf4d390d
child 9 b211d87c390a
equal deleted inserted replaced
1:be27ed110b50 3:e1eecf4d390d
       
     1 #
       
     2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 # All rights reserved.
       
     4 # This component and the accompanying materials are made available
       
     5 # under the terms of the License "Eclipse Public License v1.0"
       
     6 # which accompanies this distribution, and is available
       
     7 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 #
       
     9 # Initial Contributors:
       
    10 # Nokia Corporation - initial contribution.
       
    11 #
       
    12 # Contributors:
       
    13 #
       
    14 # Description: 
       
    15 #
       
    16 # Runs the specified suite of raptor tests
       
    17 
       
    18 import os
       
    19 import sys
       
    20 import re
       
    21 import imp
       
    22 import datetime
       
    23 import traceback
       
    24 raptor_tests = imp.load_source("raptor_tests", "common/raptor_tests.py")
       
    25 
       
    26 # Command line options ########################################################
       
    27 from optparse import OptionParser
       
    28 
       
    29 parser = OptionParser(
       
    30 		prog = "run",
       
    31 		usage = "%prog [Options]")
       
    32 
       
    33 parser.add_option("-u", "--upload", action = "store", type = "string",
       
    34 		dest = "upload", default = None,
       
    35 		help = "Path for uploading results (Can be UNC path)")
       
    36 parser.add_option("-b", "--branch", action = "store", type = "choice",
       
    37 		dest = "branch", choices = ["master", "m", "fix", "f", "wip", "w"],
       
    38 		help = "string indicating which branch is being tested:\n" + \
       
    39 		"master, fix or wip. Default is 'fix'")
       
    40 parser.add_option("-s", "--suite", action = "store", type = "string",
       
    41 		dest = "suite", help = "regex to use for selecting test suites")
       
    42 parser.add_option("-t", "--tests", action = "store", type = "string",
       
    43 		dest = "tests", help = "regex to use for selecting tests")
       
    44 parser.add_option("-d", "--debug", action = "store_true", dest = "debug_mode",
       
    45 		default = False, help = "Turns on debug-mode")
       
    46 parser.add_option("--test-home", action = "store", type = "string",
       
    47 		dest = "test_home",
       
    48 		help = "Location of custom .sbs_init.xml (name of directory in " +
       
    49 		"'custom_options'): test/custom_options/<test_home>/.sbs_init.xml")
       
    50 parser.add_option("--what-failed", action = "store_true", dest = "what_failed",
       
    51 		help = "Re-run all the tests that failed in the previous test run")
       
    52 parser.add_option("--clean", action = "store_true", dest = "clean",
       
    53 		help = "Clean EPOCROOT after each test is run")
       
    54 
       
    55 
       
    56 (options, args) = parser.parse_args()
       
    57 
       
    58 # Check for --what-failed and override '-s' and '-t' (including flagless regex)
       
    59 if options.what_failed:
       
    60 	try:
       
    61 		what_failed_file = open("what_failed", "r")
       
    62 		what_failed = what_failed_file.readline()
       
    63 		what_failed_file.close()
       
    64 		print "Running: run " + what_failed
       
    65 		
       
    66 		first = what_failed.find('"')
       
    67 		second = what_failed.find('"', (first + 1))
       
    68 		options.suite = what_failed[(first + 1):second]
       
    69 		
       
    70 		first = what_failed.find('"', (second + 1))
       
    71 		second = what_failed.find('"', (first + 1))
       
    72 		options.tests = what_failed[(first + 1):second]
       
    73 	except:
       
    74 		# If no file exists, nothing failed, so run as usual
       
    75 		pass
       
    76 
       
    77 # Allow flagless test regex
       
    78 if (options.tests == None) and (len(args) > 0):
       
    79 	options.tests = args[len(args) - 1]
       
    80 	
       
    81 if options.upload != None:
       
    82 	if options.branch != None:
       
    83 		if options.branch == "m":
       
    84 			branch = "master"
       
    85 		elif options.branch == "f":
       
    86 			branch = "fix"
       
    87 		elif options.branch == "w":
       
    88 			branch = "wip"
       
    89 		else:
       
    90 			branch = options.branch
       
    91 	else:
       
    92 		print "Warning: Test branch not set - Use " + \
       
    93 				"'-b [master|fix|wip]'\n Using default of 'Fix'..."
       
    94 		branch = "fix"
       
    95 
       
    96 if options.debug_mode:
       
    97 	raptor_tests.activate_debug()
       
    98 
       
    99 
       
   100 # Set $HOME environment variable for finding a custom .sbs_init.xml 
       
   101 if options.test_home != None:
       
   102 	home_dir = options.test_home
       
   103 	if home_dir in os.listdir("./custom_options"):
       
   104 		os.environ["HOME"] = os.environ["SBS_HOME"] + "/test/custom_options/" \
       
   105 				+ home_dir + "/"
       
   106 	else:
       
   107 		print "Warning: Path to custom .sbs_init.xml file not found (" + \
       
   108 				home_dir + ")\nUsing defaults..."
       
   109 		options.test_home = None
       
   110 
       
   111 
       
   112 def format_milliseconds(microseconds):
       
   113 	""" format a microsecond time in milliseconds """
       
   114 	milliseconds = (microseconds / 1000)
       
   115 	if milliseconds == 0:
       
   116 		return "000"
       
   117 	elif milliseconds < 10:
       
   118 		return "00" + str(milliseconds)
       
   119 	elif milliseconds < 100:
       
   120 		return "0" + str(milliseconds)
       
   121 	return milliseconds
       
   122 
       
   123 
       
   124 
       
   125 class TestRun(object):
       
   126 	"""Represents any series of tests"""
       
   127 	def __init__(self):
       
   128 		self.test_set = []
       
   129 		self.failed_tests = []
       
   130 		self.error_tests = []
       
   131 		self.pass_total = 0
       
   132 		self.fail_total = 0
       
   133 		self.skip_total = 0
       
   134 		self.exception_total = 0
       
   135 		self.test_total = 0
       
   136 		# For --what-failed:
       
   137 		self.suites_failed = []
       
   138 		self.tests_failed = []
       
   139 
       
   140 	def aggregate(self, atestrun):
       
   141 		""" Aggregate other test results into this one """
       
   142 		self.test_set.append(atestrun)
       
   143 		self.test_total += len(atestrun.test_set)
       
   144 
       
   145 	def show(self):
       
   146 		for test_set in self.test_set:
       
   147 			print "\n\n" + str(test_set.suite_dir) + ":\n"
       
   148 			
       
   149 			# If a suite has failed/erroneous tests, add it to what_failed
       
   150 			if (test_set.fail_total + test_set.exception_total) > 0:
       
   151 				self.suites_failed.append(test_set.suite_dir)
       
   152 				
       
   153 			if len(test_set.test_set) < 1:
       
   154 				print "No tests run"
       
   155 			else:
       
   156 				print "PASSED: " + str(test_set.pass_total)
       
   157 				print "FAILED: " + str(test_set.fail_total)
       
   158 				if test_set.skip_total > 0:
       
   159 					print "SKIPPED: " + str(test_set.skip_total)
       
   160 				if test_set.exception_total > 0:
       
   161 					print "EXCEPTIONS: " + str(test_set.exception_total)
       
   162 		
       
   163 				if test_set.fail_total > 0:
       
   164 					print "\nFAILED TESTS:"
       
   165 					
       
   166 					# Add each failed test to what_failed and print it
       
   167 					for test in test_set.failed_tests:
       
   168 						self.tests_failed.append("^" + test + ".py")
       
   169 						print "\t", test
       
   170 		
       
   171 				if test_set.exception_total > 0:
       
   172 					print "\nERRONEOUS TESTS:"
       
   173 					
       
   174 					# Add each erroneous test to what_failed and print it
       
   175 					for test in test_set.error_tests:
       
   176 						first = test.find("'")
       
   177 						second = test.find("'", (first + 1))
       
   178 						self.tests_failed.append("^" +
       
   179 								test[(first + 1):second] + ".py")
       
   180 						print "\t", test
       
   181 						
       
   182 	def what_failed(self):
       
   183 		"Create the file for --what-failed if there were failing tests"
       
   184 		if len(self.suites_failed) > 0:
       
   185 			self.what_failed = open("what_failed", "w")
       
   186 			# Add the suites and tests to the file as command-line options
       
   187 			self.what_failed.write('-s "')
       
   188 			loop_number = 0
       
   189 			for suite in self.suites_failed:
       
   190 				loop_number += 1
       
   191 				self.what_failed.write(suite)
       
   192 				
       
   193 				# If this is not the last suite, prepare to add another
       
   194 				if loop_number < len(self.suites_failed):
       
   195 					self.what_failed.write("|")
       
   196 					
       
   197 			self.what_failed.write('" -t "')
       
   198 			loop_number = 0
       
   199 			for test in self.tests_failed:
       
   200 				loop_number += 1
       
   201 				self.what_failed.write(test)
       
   202 				
       
   203 				# If this is not the last test, prepare to add another
       
   204 				if loop_number < len(self.tests_failed):
       
   205 					self.what_failed.write("|")
       
   206 			self.what_failed.write('"')
       
   207 			self.what_failed.close()
       
   208 			
       
   209 		else:
       
   210 			# If there were no failing tests this time, remove any previous file
       
   211 			try:
       
   212 				os.remove("what_failed")
       
   213 			except:
       
   214 				try:
       
   215 					os.chmod("what_failed", stat.S_IRWXU)
       
   216 					os.remove("what_failed")
       
   217 				except:
       
   218 					pass
       
   219 					
       
   220 
       
   221 class Suite(TestRun):
       
   222 	"""A test suite"""
       
   223 
       
   224 	python_file_regex = re.compile("(.*)\.py$", re.I)
       
   225 
       
   226 	def __init__(self, dir, parent):
       
   227 		TestRun.__init__(self)
       
   228 		self.suite_dir = dir
       
   229 
       
   230 		# Upload directory (if set)
       
   231 		self.upload_location = parent.upload_location
       
   232 
       
   233 		# Regex for searching for tests
       
   234 
       
   235 		self.test_file_regex = parent.test_file_regex
       
   236 		self.test_pattern = parent.testpattern
       
   237 		
       
   238 
       
   239 	def run(self):
       
   240 		"""run the suite"""
       
   241 
       
   242 		self.time_stamp = datetime.datetime.now()
       
   243 		self.results = {}
       
   244 		self.start_times = {}
       
   245 		self.end_times = {}
       
   246 		
       
   247 		print "\n\nRunning " + str(self.suite_dir) + "..."
       
   248 
       
   249 		# Iterate through all files in specified directory
       
   250 		for test in os.listdir(self.suite_dir):
       
   251 			# Only check '*.py' files
       
   252 			name_match = self.python_file_regex.match(test)
       
   253 			if name_match is not None:
       
   254 				if self.test_file_regex is not None:
       
   255 					# Each file that matches -t input is imported if any
       
   256 					name_match = self.test_file_regex.match(test)
       
   257 				else:
       
   258 					name_match = 1
       
   259 				if name_match is not None:
       
   260 					import_name = test[:-3]
       
   261 					try:
       
   262 						self.test_set.append(imp.load_source(import_name,
       
   263 								(raptor_tests.ReplaceEnvs(self.suite_dir
       
   264 								+ "/" + test))))
       
   265 					except:
       
   266 						print "\n", (sys.exc_type.__name__ + ":"), \
       
   267 								sys.exc_value, "\n", \
       
   268 								traceback.print_tb(sys.exc_traceback)
       
   269 	
       
   270 		test_number = 0
       
   271 		test_total = len(self.test_set)
       
   272 		if test_total < 1:
       
   273 			print "No tests in suite "+self.suite_dir+" matched by specification '"+self.test_pattern+"' (regex: /.*"+self.test_pattern+".*/)\n";
       
   274 		# Run each test, capturing all its details and its results
       
   275 		for test in self.test_set:
       
   276 			test_number += 1
       
   277 			# Save start/end times and save in dictionary for TMS
       
   278 			start_time = datetime.datetime.now()
       
   279 			try:
       
   280 				print "\n\nTEST " + str(test_number) + "/" + \
       
   281 						str(test_total) + ":\n",
       
   282 				test_object = test.run()
       
   283 				
       
   284 				end_time = datetime.datetime.now()
       
   285 				
       
   286 				# Add leading 0s
       
   287 				test_object.id = raptor_tests.fix_id(test_object.id)
       
   288 
       
   289 				# No millisecond function, so need to use microseconds/1000
       
   290 				start_milliseconds = start_time.microsecond
       
   291 				end_milliseconds = end_time.microsecond
       
   292 		
       
   293 				# Add trailing 0's if required
       
   294 				start_milliseconds = \
       
   295 						format_milliseconds(start_milliseconds)
       
   296 				end_milliseconds = \
       
   297 						format_milliseconds(end_milliseconds)
       
   298 		
       
   299 				self.start_times[test_object.id] = \
       
   300 						start_time.strftime("%H:%M:%S:" +
       
   301 						str(start_milliseconds))
       
   302 				self.end_times[test_object.id] = \
       
   303 						end_time.strftime("%H:%M:%S:" + \
       
   304 						str(end_milliseconds))
       
   305 				
       
   306 				run_time = (end_time - start_time)
       
   307 				
       
   308 				run_time_seconds = (str(run_time.seconds) + "." + \
       
   309 						str(format_milliseconds(run_time.microseconds)))
       
   310 				print ("RunTime: " + run_time_seconds + "s")
       
   311 				# Add to pass/fail count and save result to dictionary
       
   312 				if test_object.result == raptor_tests.SmokeTest.PASS:
       
   313 					self.pass_total += 1
       
   314 					self.results[test_object.id] = "Passed"
       
   315 				elif test_object.result == raptor_tests.SmokeTest.FAIL:
       
   316 					self.fail_total += 1
       
   317 					self.results[test_object.id] = "Failed"
       
   318 					self.failed_tests.append(test_object.name)
       
   319 				elif test_object.result == raptor_tests.SmokeTest.SKIP:
       
   320 					self.skip_total += 1
       
   321 				# Clean epocroot after running each test if --clean option is specified
       
   322 				if options.clean:
       
   323 					print "\nCLEANING TEST RESULTS..."
       
   324 					raptor_tests.clean_epocroot()
       
   325 					
       
   326 			except:
       
   327 				print "\nTEST ERROR:"
       
   328 				print (sys.exc_type.__name__ + ":"), \
       
   329 						sys.exc_value, "\n", \
       
   330 						traceback.print_tb(sys.exc_traceback)
       
   331 				self.exception_total += 1
       
   332 				self.error_tests.append(str(self.test_set[test_number - 1]))
       
   333 				
       
   334 		if self.upload_location != None:
       
   335 			self.create_csv()
       
   336 
       
   337 		end_time_stamp = datetime.datetime.now()
       
   338 			
       
   339 		runtime = end_time_stamp - self.time_stamp
       
   340 		seconds = (str(runtime.seconds) + "." + \
       
   341 				str(format_milliseconds(runtime.microseconds)))
       
   342 		if options.upload:
       
   343 			self.create_tri(seconds)
       
   344 
       
   345 		print ("\n" + str(self.suite_dir) + " RunTime: " + seconds + "s")
       
   346 
       
   347 	def create_csv(self):
       
   348 		"""
       
   349 		This method will create a CSV file with the smoke test's output
       
   350 				in order to successfully upload results to TMS QC
       
   351 		"""
       
   352 		
       
   353 		# This sorts the dictionaries by their key values (Test IDs)
       
   354 		id_list = run_tests.sort_dict(self.results)
       
   355 		
       
   356 		self.test_file_name = (self.suite_dir + "_" + \
       
   357 				self.time_stamp.strftime("%Y-%m-%d_%H-%M-%S") + "_" +
       
   358 				branch + "_results.csv")
       
   359 		# This is the path for file-creation on the server. Includes
       
   360 		self.test_path = (self.upload_location + "/csv/" + self.suite_dir + "/"
       
   361 				+ self.test_file_name)
       
   362 		
       
   363 		try:
       
   364 		
       
   365 			if not os.path.isdir(self.upload_location + "/csv/" +
       
   366 					self.suite_dir):
       
   367 				os.makedirs(self.upload_location + "/csv/" + self.suite_dir)
       
   368 
       
   369 			csv_file = \
       
   370 					open(raptor_tests.ReplaceEnvs(os.path.normpath(self.test_path)),
       
   371 					"w")
       
   372 			csv_file.write("TestCaseID,StartTime,EndTime,Result\n")
       
   373 			
       
   374 			for test_id in id_list:
       
   375 				csv_file.write("PCT-SBSV2-" + self.suite_dir + "-" + test_id + \
       
   376 						"," + str(self.start_times[test_id]) + "," + \
       
   377 						str(self.end_times[test_id]) + "," + \
       
   378 						self.results[test_id] + "\n")
       
   379 			csv_file.close()
       
   380 			
       
   381 		except OSError, e:
       
   382 			print "SBS_TESTS: Error:", e
       
   383 			
       
   384 			
       
   385 	def create_tri(self, overall_seconds):
       
   386 		"""
       
   387 		This method will create a TRI (xml) file containing the location of the
       
   388 				CSV file in order to successfully upload results to TMS QC
       
   389 		"""
       
   390 		# Path for the tri file
       
   391 		tri_path = (self.upload_location + "/new/" + self.suite_dir + \
       
   392 				"_" + self.time_stamp.strftime("%Y-%m-%d_%H-%M-%S") + ".xml")
       
   393 		run_name_timestamp = self.time_stamp.strftime(self.suite_dir + \
       
   394 				"%Y-%m-%d_%H-%M-%S")
       
   395 		date_time_timestamp = self.time_stamp.strftime("%d.%m.%Y %H:%M:%S")
       
   396 		test_set_name = "Root\\Product Creation Tools\\Regression\\" + \
       
   397 				"SBS v2 (Raptor)\\" + self.suite_dir + "_"
       
   398 		if sys.platform.startswith("win"):
       
   399 			test_set_name += ("WinXP_" + branch)
       
   400 		else:
       
   401 			test_set_name += ("Linux_" + branch)
       
   402 		
       
   403 		# /mnt/ -> // Fixes the difference in paths for lon-rhdev mounts vs. win
       
   404 		if not sys.platform.startswith("win"):
       
   405 			if self.test_path.startswith("/mnt/"):
       
   406 				self.test_path = self.test_path.replace("mnt", "", 1)
       
   407 		
       
   408 		try:
       
   409 			tri_file = \
       
   410 					open(raptor_tests.ReplaceEnvs(os.path.normpath(tri_path)), \
       
   411 					"w")
       
   412 			tri_file.write(
       
   413 					"<TestRunInfo>\n" + \
       
   414 						"\t<RunName>\n\t\t" + \
       
   415 							run_name_timestamp + \
       
   416 						"\n\t</RunName>\n" + \
       
   417 						"\t<TestGroup>\n" + \
       
   418 							"\t\tSBSv2 (Non-SITK)\n" + \
       
   419 						"\t</TestGroup>\n" + \
       
   420 						"\t<DateTime>\n\t\t" + \
       
   421 							date_time_timestamp + \
       
   422 						"\n\t</DateTime>\n" + \
       
   423 						"\t<RunDuration>\n\t\t" + \
       
   424 							overall_seconds + \
       
   425 						"\n\t</RunDuration>\n" + \
       
   426 						'\t<TestSet name="' + test_set_name + '">\n' + \
       
   427 							"\t\t<TestResults>\n\t\t\t" + \
       
   428 								self.test_path + \
       
   429 							"\n\t\t</TestResults>\n" + \
       
   430 						"\t</TestSet>\n" + \
       
   431 					"</TestRunInfo>")
       
   432 			tri_file.close()
       
   433 			print "Tests uploaded to '" + self.upload_location + "' (" + \
       
   434 					branch + ")"
       
   435 		except OSError, e:
       
   436 			print "SBS_TESTS: Error:", e
       
   437 
       
   438 class SuiteRun(TestRun):
       
   439 	""" Represents a 'run' of a number of test suites """
       
   440 
       
   441 	def __init__(self, suitepattern = None, testpattern = None,
       
   442 			upload_location = None):
       
   443 		TestRun.__init__(self)
       
   444 		
       
   445 		# Add common directory to list of paths to search for modules
       
   446 		sys.path.append(raptor_tests.ReplaceEnvs("$(SBS_HOME)/test/common"))
       
   447 		
       
   448 		
       
   449 		if suitepattern:
       
   450 			self.suite_regex = re.compile(".*" + suitepattern + ".*", re.I)
       
   451 		else:
       
   452 			self.suite_regex = re.compile(".*\_suite$", re.I)
       
   453 
       
   454 		if testpattern:
       
   455 			self.test_file_regex = re.compile(".*" + testpattern + ".*",
       
   456 					re.I)
       
   457 		else:
       
   458 			self.test_file_regex = None
       
   459 
       
   460 		self.suitepattern = suitepattern
       
   461 		self.testpattern = testpattern
       
   462 		self.upload_location = upload_location
       
   463 		
       
   464 
       
   465 
       
   466 	def run_tests(self):
       
   467 		"""
       
   468 		Run all the tests in the specified suite (directory)
       
   469 		"""
       
   470 	
       
   471 		suites = []
       
   472 		for dir in os.listdir("."):
       
   473 			name_match = self.suite_regex.match(dir)
       
   474 			# Each folder that matches the suite pattern will be looked into
       
   475 			# Also checks to make sure the found entry is actually a directory
       
   476 			if name_match is not None and os.path.isdir(dir):
       
   477 				s = Suite(dir, self)
       
   478 				s.run()
       
   479 				self.aggregate(s)
       
   480 				suites.append(dir)
       
   481 		
       
   482 		# Print which options were used
       
   483 		if options.test_home == None:
       
   484 			options_dir = "defaults)"
       
   485 		else:
       
   486 			options_dir = "'" + options.test_home + "' options file)"
       
   487 		print "\n(Tests run using %s" %options_dir
       
   488 
       
   489 		# Summarise the entire test run
       
   490 		if self.suitepattern and (self.test_total < 1):
       
   491 			print "\nNo suites matched specification '" + self.suitepattern + \
       
   492 					"'\n"
       
   493 		else:
       
   494 			print "Overall summary (%d suites, %d tests):" \
       
   495 					%(len(suites), self.test_total)
       
   496 			self.show()
       
   497 			self.what_failed()
       
   498 	        
       
   499 
       
   500 	def sort_dict(self, input_dict):
       
   501 		"""
       
   502 		This method sorts values in a dictionary
       
   503 		"""
       
   504 		keys = input_dict.keys()
       
   505 		keys.sort()
       
   506 		return keys
       
   507 
       
   508 
       
   509 # Clean epocroot before running tests
       
   510 raptor_tests.clean_epocroot()
       
   511 run_tests = SuiteRun(suitepattern = options.suite, testpattern = options.tests,
       
   512 		upload_location = options.upload)
       
   513 run_tests.run_tests()
       
   514