1# -*- coding: utf-8 -*- 2 3import sys 4import random 5import string 6import subprocess 7from optparse import OptionParser 8 9def all (results, predicate): 10 for result in results: 11 if not predicate(result): 12 return False 13 return True 14 15def any (results, predicate): 16 for result in results: 17 if predicate(result): 18 return True 19 return False 20 21class FilterRule: 22 def __init__ (self, name, description, filters): 23 self.name = name 24 self.description = description 25 self.filters = filters 26 27class TestCaseResult: 28 def __init__ (self, name, results): 29 self.name = name 30 self.results = results 31 32class Group: 33 def __init__ (self, name): 34 self.name = name 35 self.cases = [] 36 37def readCaseList (filename): 38 f = open(filename, 'rb') 39 cases = [] 40 for line in f: 41 if line[:6] == "TEST: ": 42 case = line[6:].strip() 43 if len(case) > 0: 44 cases.append(case) 45 return cases 46 47def toResultList (caselist): 48 results = [] 49 for case in caselist: 50 results.append(TestCaseResult(case, [])) 51 return results 52 53def addResultsToCaseList (caselist, results): 54 resultMap = {} 55 caseListRes = toResultList(caselist) 56 57 for res in caseListRes: 58 resultMap[res.name] = res 59 60 for result in results: 61 if result.name in resultMap: 62 resultMap[result.name].results += result.results 63 64 return caseListRes 65 66def readTestResults (filename): 67 f = open(filename, 'rb') 68 csvData = f.read() 69 csvLines = csvData.splitlines() 70 results = [] 71 72 f.close() 73 74 for line in csvLines[1:]: 75 args = line.split(',') 76 if len(args) == 1: 77 continue # Ignore 78 79 results.append(TestCaseResult(args[0], args[1:])) 80 81 if len(results) == 0: 82 raise Exception("Empty result list") 83 84 # Sanity check for results 85 numResultItems = len(results[0].results) 86 seenResults = set() 87 for result in results: 88 if result.name in seenResults: 89 raise Exception("Duplicate result row for test case '%s'" % result.name) 90 if len(result.results) != numResultItems: 91 raise Exception("Found %d results for test case '%s', expected %d" % (len(result.results), result.name, numResultItems)) 92 seenResults.add(result.name) 93 94 return results 95 96def readGroupList (filename): 97 f = open(filename, 'rb') 98 groups = [] 99 for line in f: 100 group = line.strip() 101 if group != "": 102 groups.append(group) 103 return groups 104 105def createGroups (results, groupNames): 106 groups = [] 107 matched = set() 108 109 for groupName in groupNames: 110 group = Group(groupName) 111 groups.append(group) 112 113 prefix = groupName + "." 114 prefixLen = len(prefix) 115 for case in results: 116 if case.name[:prefixLen] == prefix: 117 if case in matched: 118 die("Case '%s' matched by multiple groups (when processing '%s')" % (case.name, group.name)) 119 group.cases.append(case) 120 matched.add(case) 121 122 return groups 123 124def createLeafGroups (results): 125 groups = [] 126 groupMap = {} 127 128 for case in results: 129 parts = case.name.split('.') 130 groupName = string.join(parts[:-1], ".") 131 132 if not groupName in groupMap: 133 group = Group(groupName) 134 groups.append(group) 135 groupMap[groupName] = group 136 else: 137 group = groupMap[groupName] 138 139 group.cases.append(case) 140 141 return groups 142 143def filterList (results, condition): 144 filtered = [] 145 for case in results: 146 if condition(case.results): 147 filtered.append(case) 148 return filtered 149 150def getFilter (list, name): 151 for filter in list: 152 if filter.name == name: 153 return filter 154 return None 155 156def getNumCasesInGroups (groups): 157 numCases = 0 158 for group in groups: 159 numCases += len(group.cases) 160 return numCases 161 162def getCasesInSet (results, caseSet): 163 filtered = [] 164 for case in results: 165 if case in caseSet: 166 filtered.append(case) 167 return filtered 168 169def selectCasesInGroups (results, groups): 170 casesInGroups = set() 171 for group in groups: 172 for case in group.cases: 173 casesInGroups.add(case) 174 return getCasesInSet(results, casesInGroups) 175 176def selectRandomSubset (results, groups, limit, seed): 177 selectedCases = set() 178 numSelect = min(limit, getNumCasesInGroups(groups)) 179 180 random.seed(seed) 181 random.shuffle(groups) 182 183 groupNdx = 0 184 while len(selectedCases) < numSelect: 185 group = groups[groupNdx] 186 if len(group.cases) == 0: 187 del groups[groupNdx] 188 if groupNdx == len(groups): 189 groupNdx -= 1 190 continue # Try next 191 192 selected = random.choice(group.cases) 193 selectedCases.add(selected) 194 group.cases.remove(selected) 195 196 groupNdx = (groupNdx + 1) % len(groups) 197 198 return getCasesInSet(results, selectedCases) 199 200def die (msg): 201 print msg 202 sys.exit(-1) 203 204# Named filter lists 205FILTER_RULES = [ 206 FilterRule("all", "No filtering", []), 207 FilterRule("all-pass", "All results must be 'Pass'", [lambda l: all(l, lambda r: r == 'Pass')]), 208 FilterRule("any-pass", "Any of results is 'Pass'", [lambda l: any(l, lambda r: r == 'Pass')]), 209 FilterRule("any-fail", "Any of results is not 'Pass' or 'NotSupported'", [lambda l: not all(l, lambda r: r == 'Pass' or r == 'NotSupported')]), 210 FilterRule("prev-failing", "Any except last result is failure", [lambda l: l[-1] == 'Pass' and not all(l[:-1], lambda r: r == 'Pass')]), 211 FilterRule("prev-passing", "Any except last result is 'Pass'", [lambda l: l[-1] != 'Pass' and any(l[:-1], lambda r: r == 'Pass')]) 212] 213 214if __name__ == "__main__": 215 parser = OptionParser(usage = "usage: %prog [options] [caselist] [result csv file]") 216 parser.add_option("-f", "--filter", dest="filter", default="all", help="filter rule name") 217 parser.add_option("-l", "--list", action="store_true", dest="list", default=False, help="list available rules") 218 parser.add_option("-n", "--num", dest="limit", default=0, help="limit number of cases") 219 parser.add_option("-s", "--seed", dest="seed", default=0, help="use selected seed for random selection") 220 parser.add_option("-g", "--groups", dest="groups_file", default=None, help="select cases based on group list file") 221 222 (options, args) = parser.parse_args() 223 224 if options.list: 225 print "Available filter rules:" 226 for filter in FILTER_RULES: 227 print " %s: %s" % (filter.name, filter.description) 228 sys.exit(0) 229 230 if len(args) == 0: 231 die("No input files specified") 232 elif len(args) > 2: 233 die("Too many arguments") 234 235 # Fetch filter 236 filter = getFilter(FILTER_RULES, options.filter) 237 if filter == None: 238 die("Unknown filter '%s'" % options.filter) 239 240 # Read case list 241 caselist = readCaseList(args[0]) 242 if len(args) > 1: 243 results = readTestResults(args[1]) 244 results = addResultsToCaseList(caselist, results) 245 else: 246 results = toResultList(caselist) 247 248 # Execute filters for results 249 for rule in filter.filters: 250 results = filterList(results, rule) 251 252 if options.limit != 0: 253 if options.groups_file != None: 254 groups = createGroups(results, readGroupList(options.groups_file)) 255 else: 256 groups = createLeafGroups(results) 257 results = selectRandomSubset(results, groups, int(options.limit), int(options.seed)) 258 elif options.groups_file != None: 259 groups = createGroups(results, readGroupList(options.groups_file)) 260 results = selectCasesInGroups(results, groups) 261 262 # Print test set 263 for result in results: 264 print result.name 265