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