1#!/usr/bin/env python 2from __future__ import absolute_import, print_function, unicode_literals 3from subprocess import call, check_output 4import os # For filesystem access 5import sys # For sys.exit() 6import argparse # For parsing command-line arguments 7import urllib # For downloading the ssget index 8import ssl 9import tarfile # For un-tar/unzipping matrix files 10import csv # For reading the ssget index 11import shutil # For using 'which' 12from distutils.spawn import find_executable # For using find_executable 13 14def main(): 15 # Check to make sure we're in the build directory - if not, exit 16 checkLocation() 17 18 # Parse the command-line arguments 19 args = parseArguments() 20 21 # Run tests if needed 22 if args.tests != "none": 23 runTests(args) 24 25 # If the coverage flag is on, run gcov (or similar) 26 if args.coverage or args.html_coverage: 27 runCoverageUtility(args) 28 29def runTests(args): 30 # Create or locate matrix temporary storage directory 31 matrix_dir = getMatrixDirectory(args) 32 33 # Download the matrix stats csv file 34 stats_file = downloadStatsFile(matrix_dir) 35 36 with open(stats_file, 'rb') as f: 37 reader = csv.reader(f) 38 39 # Matrix IDs are not listed in the stats file - we just have to keep count 40 matrix_id = 0 41 for row in reader: 42 43 if len(row) == 13: # Only rows with 13 elements represent matrix data 44 matrix_id += 1 45 46 # Check if the matrix ID is in the proper range and 47 # that the matrix is real and symmetric 48 isInBounds = ((matrix_id >= args.id_min) and (matrix_id <= args.id_max)) 49 isSquare = (row[2] == row[3]) 50 isReal = (row[5] == '1') 51 52 if (isInBounds and isSquare and isReal): 53 if args.ids is None or matrix_id in args.ids: 54 matrix_name = row[0] + '/' + row[1] + '.tar.gz' 55 gzip_path = matrix_dir + row[0] + '_' + row[1] + '.tar.gz' 56 matrix_path = matrix_dir + row[1] + '/' + row[1] + ".mtx" 57 58 matrix_exists = os.path.isfile(gzip_path) 59 if matrix_exists: 60 tar = tarfile.open(gzip_path, mode='r:gz') 61 matrix_files = tar.getnames() 62 else: 63 # Download matrix if it doesn't exist 64 print("Downloading " + matrix_name) 65 sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) 66 testfile = urllib.URLopener(context=sslcontext) 67 testfile.retrieve("https://sparse.tamu.edu/MM/" + matrix_name, gzip_path) 68 tar = tarfile.open(gzip_path, mode='r:gz') 69 tar.extractall(path=matrix_dir) # Extract the matrix from the tar.gz file 70 tar.close() 71 72 # Determine which test executables to run 73 if args.tests == 'all': 74 print("Calling ALL Tests...") 75 if args.tests == 'memory' or args.tests == 'all': 76 print("Calling Memory Test...") 77 status = call(["./tests/mongoose_test_memory", matrix_path]) 78 if status: 79 print("Error! Memory Test Failure") 80 cleanup(args, row, matrix_exists, gzip_path) 81 sys.exit(status) 82 if args.tests == 'valgrind' or args.tests == 'all': 83 print("Calling Valgrind Test...") 84 valgrind = find_executable('valgrind') 85 if valgrind: 86 status = call([valgrind + " --leak-check=full ./tests/mongoose_test_memory", matrix_path]) 87 if status: 88 print("Error! Valgrind Test Failure") 89 cleanup(args, row, matrix_exists, gzip_path) 90 sys.exit(status) 91 else: 92 print("\033[91mERROR!\033[0m Unable to find Valgrind. Skipping Valgrind Test...") 93 if args.tests == 'io' or args.tests == 'all': 94 print("Calling I/O Test...") 95 status = call(["./tests/mongoose_test_io", matrix_path, "1"]) 96 if status: 97 print("Error! I/O Test Failure") 98 cleanup(args, row, matrix_exists, gzip_path) 99 sys.exit(status) 100 if args.tests == 'edgesep' or args.tests == 'all': 101 print("Calling Edge Separator Test...") 102 target_split = args.target_split 103 status = call(["./tests/mongoose_test_edgesep", matrix_path, str(target_split)]) 104 if status: 105 print("Error! Edge Separator Test Failure") 106 cleanup(args, row, matrix_exists, gzip_path) 107 sys.exit(status) 108 if args.tests == 'performance' or args.tests == 'all': 109 print("Calling Performance Test...") 110 status = call(["./tests/mongoose_test_performance", matrix_path, row[0] + '_' + row[1] + '_performance.txt']) 111 if status: 112 print("Error! Performance Test Failure") 113 cleanup(args, row, matrix_exists, gzip_path) 114 sys.exit(status) 115 116 # Delete the matrix only if we downloaded it and the keep 117 # flag is off 118 cleanup(args, row, matrix_exists, gzip_path) 119 120 121 # Delete the ssstats.csv file now that we're done 122 os.remove(stats_file) 123 124def cleanup(args, row, matrix_exists, gzip_path): 125 matrix_dir = args.matrix_directory 126 127 if args.purge or not (args.keep or matrix_exists): 128 files = os.listdir(matrix_dir + row[1]) 129 for file in files: 130 os.remove(os.path.join(matrix_dir + row[1] + '/', file)) 131 os.rmdir(matrix_dir + row[1]) 132 os.remove(gzip_path) 133 134def getMatrixDirectory(args): 135 # Check if the supplied matrix download directory exists - if not, create it 136 matrix_dir = args.matrix_directory 137 if not os.path.exists(matrix_dir): 138 os.makedirs(matrix_dir) 139 140 # Make sure the directory ends with '/' 141 if not matrix_dir.endswith('/'): 142 matrix_dir = matrix_dir + '/' 143 144 return matrix_dir 145 146def downloadStatsFile(matrix_dir): 147 sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) 148 downloader = urllib.URLopener(context=sslcontext) 149 downloader.retrieve( 150 "https://sparse.tamu.edu/files/ssstats.csv", 151 matrix_dir + "/ssstats.csv") 152 153 stats_file = matrix_dir + "/ssstats.csv" 154 return stats_file 155 156def runCoverageUtility(args): 157 if args.gcov: 158 gcov = args.gcov 159 else: 160 gcov = find_executable('gcov') 161 162 if gcov: 163 # Determine if we are using GCC gcov or LLVM gcov 164 gcov_version = check_output([gcov, "--version"]) 165 if gcov_version.find('LLVM') == -1: 166 call([gcov + " -o ./CMakeFiles/mongoose_lib_dbg.dir/Source ../Source/*.cpp"], shell=True) 167 else: 168 call(gcov + " -o=./CMakeFiles/mongoose_lib_dbg.dir/Source ../Source/*.cpp", shell=True) 169 170 gcovr = find_executable('gcovr') 171 if gcovr: 172 if args.html_coverage: 173 print("Running gcovr with HTML generation") 174 call([gcovr, 175 "--html", 176 "--html-details", 177 "--output=coverage.html", 178 "--gcov-executable=" + gcov, 179 "--object-directory=CMakeFiles/mongoose_lib_dbg.dir/Source", 180 "--root=../Source/"]) 181 else: 182 print("Running gcovr without HTML generation") 183 call([gcovr, "--gcov-executable=" + gcov, "--object-directory=CMakeFiles/mongoose_lib_dbg.dir/Source", "--root=../Source/"]) 184 else: 185 print("\033[91mERROR!\033[0m Cannot generate HTML coverage report, gcovr not found!") 186 187def parseArguments(): 188 parser = argparse.ArgumentParser( 189 description='Run tests on the Mongoose library.') 190 parser.add_argument('-k', '--keep', 191 action='store_true', 192 help='do not remove downloaded files when test is complete') 193 parser.add_argument('-p', '--purge', 194 action='store_true', 195 help='force remove downloaded matrix files when complete') 196 parser.add_argument('-min', 197 action='store', 198 metavar='min_id', 199 type=int, 200 default=1, 201 dest='id_min', 202 help='minimum matrix ID to run tests on [default: 1]') 203 parser.add_argument('-max', 204 action='store', 205 metavar='max_id', 206 type=int, 207 default=2757, 208 dest='id_max', 209 help='maximum matrix ID to run tests on [default: 2757]') 210 parser.add_argument('-i', '--ids', 211 action='store', 212 nargs='+', 213 metavar='matrix_ID', 214 type=int, 215 help='list of matrix IDs to run tests on') 216 parser.add_argument('-t', '--tests', 217 choices=['all', 'memory', 'io', 'edgesep', 'performance', 'valgrind', 'none'], 218 default='none', 219 help='choice of which tests to run') 220 parser.add_argument('-s', '--split', 221 action='store', 222 metavar='target_split', 223 type=float, 224 dest='target_split', 225 default=0.5, 226 help='target split ratio for edge separator test [default: 0.5]') 227 parser.add_argument('-d', '--matrix-directory', 228 action='store', 229 metavar='matrix_dir', 230 default='../Matrix/', 231 help='download directory for Matrix Market data files') 232 parser.add_argument('-c', '--coverage', 233 action='store_true', 234 help='generate coverage information') 235 parser.add_argument('--html-coverage', 236 action='store_true', 237 help='generate html coverage pages if gcovr is available') 238 parser.add_argument('--gcov', 239 action='store', 240 metavar='gcov_path', 241 help='path to gcov tool') 242 243 return parser.parse_args() 244 245def checkLocation(): 246 # Check if we are in the right location - if not, exit 247 if not (os.path.isdir('./CMakeFiles/mongoose_lib_dbg.dir/Source') 248 and os.path.isdir('../Source')): 249 print( 250 "\n\033[91mERROR!\033[0m Looks like you might not be running this from " 251 "your build directory.\n\n" 252 "Make sure that... \n\n" 253 " * You are in your build directory (e.g. Mongoose/build) and\n" 254 " * You have built Mongoose ('cmake ..' followed by 'make')\n") 255 sys.exit() 256 257 # Check if Mongoose has been built - if not, exit 258 if not (os.path.exists('./CMakeFiles/mongoose_lib_dbg.dir/Source/Mongoose_Graph.o')): 259 print( 260 "\n\033[91mERROR!\033[0m Looks like you might not have built Mongoose " 261 "yet.\n\n" 262 "Make sure that... \n\n" 263 " * You are in your build directory (e.g. Mongoose/build) and\n" 264 " * You have built Mongoose ('cmake ..' followed by 'make')\n") 265 sys.exit() 266 267if __name__=="__main__": 268 main() 269