# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------
# drawElements Quality Program utilities
# --------------------------------------
#
# Copyright 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#-------------------------------------------------------------------------
import sys
import random
import string
import subprocess
from optparse import OptionParser
def all (results, predicate):
for result in results:
if not predicate(result):
return False
return True
def any (results, predicate):
for result in results:
if predicate(result):
return True
return False
class FilterRule:
def __init__ (self, name, description, filters):
self.name = name
self.description = description
self.filters = filters
class TestCaseResult:
def __init__ (self, name, results):
self.name = name
self.results = results
class Group:
def __init__ (self, name):
self.name = name
self.cases = []
def readCaseList (filename):
f = open(filename, 'rb')
cases = []
for line in f:
if line[:6] == "TEST: ":
case = line[6:].strip()
if len(case) > 0:
cases.append(case)
return cases
def toResultList (caselist):
results = []
for case in caselist:
results.append(TestCaseResult(case, []))
return results
def addResultsToCaseList (caselist, results):
resultMap = {}
caseListRes = toResultList(caselist)
for res in caseListRes:
resultMap[res.name] = res
for result in results:
if result.name in resultMap:
resultMap[result.name].results += result.results
return caseListRes
def readTestResults (filename):
f = open(filename, 'rb')
csvData = f.read()
csvLines = csvData.splitlines()
results = []
f.close()
for line in csvLines[1:]:
args = line.split(',')
if len(args) == 1:
continue # Ignore
results.append(TestCaseResult(args[0], args[1:]))
if len(results) == 0:
raise Exception("Empty result list")
# Sanity check for results
numResultItems = len(results[0].results)
seenResults = set()
for result in results:
if result.name in seenResults:
raise Exception("Duplicate result row for test case '%s'" % result.name)
if len(result.results) != numResultItems:
raise Exception("Found %d results for test case '%s', expected %d" % (len(result.results), result.name, numResultItems))
seenResults.add(result.name)
return results
def readGroupList (filename):
f = open(filename, 'rb')
groups = []
for line in f:
group = line.strip()
if group != "":
groups.append(group)
return groups
def createGroups (results, groupNames):
groups = []
matched = set()
for groupName in groupNames:
group = Group(groupName)
groups.append(group)
prefix = groupName + "."
prefixLen = len(prefix)
for case in results:
if case.name[:prefixLen] == prefix:
if case in matched:
die("Case '%s' matched by multiple groups (when processing '%s')" % (case.name, group.name))
group.cases.append(case)
matched.add(case)
return groups
def createLeafGroups (results):
groups = []
groupMap = {}
for case in results:
parts = case.name.split('.')
groupName = string.join(parts[:-1], ".")
if not groupName in groupMap:
group = Group(groupName)
groups.append(group)
groupMap[groupName] = group
else:
group = groupMap[groupName]
group.cases.append(case)
return groups
def filterList (results, condition):
filtered = []
for case in results:
if condition(case.results):
filtered.append(case)
return filtered
def getFilter (list, name):
for filter in list:
if filter.name == name:
return filter
return None
def getNumCasesInGroups (groups):
numCases = 0
for group in groups:
numCases += len(group.cases)
return numCases
def getCasesInSet (results, caseSet):
filtered = []
for case in results:
if case in caseSet:
filtered.append(case)
return filtered
def selectCasesInGroups (results, groups):
casesInGroups = set()
for group in groups:
for case in group.cases:
casesInGroups.add(case)
return getCasesInSet(results, casesInGroups)
def selectRandomSubset (results, groups, limit, seed):
selectedCases = set()
numSelect = min(limit, getNumCasesInGroups(groups))
random.seed(seed)
random.shuffle(groups)
groupNdx = 0
while len(selectedCases) < numSelect:
group = groups[groupNdx]
if len(group.cases) == 0:
del groups[groupNdx]
if groupNdx == len(groups):
groupNdx -= 1
continue # Try next
selected = random.choice(group.cases)
selectedCases.add(selected)
group.cases.remove(selected)
groupNdx = (groupNdx + 1) % len(groups)
return getCasesInSet(results, selectedCases)
def die (msg):
print msg
sys.exit(-1)
# Named filter lists
FILTER_RULES = [
FilterRule("all", "No filtering", []),
FilterRule("all-pass", "All results must be 'Pass'", [lambda l: all(l, lambda r: r == 'Pass')]),
FilterRule("any-pass", "Any of results is 'Pass'", [lambda l: any(l, lambda r: r == 'Pass')]),
FilterRule("any-fail", "Any of results is not 'Pass' or 'NotSupported'", [lambda l: not all(l, lambda r: r == 'Pass' or r == 'NotSupported')]),
FilterRule("prev-failing", "Any except last result is failure", [lambda l: l[-1] == 'Pass' and not all(l[:-1], lambda r: r == 'Pass')]),
FilterRule("prev-passing", "Any except last result is 'Pass'", [lambda l: l[-1] != 'Pass' and any(l[:-1], lambda r: r == 'Pass')])
]
if __name__ == "__main__":
parser = OptionParser(usage = "usage: %prog [options] [caselist] [result csv file]")
parser.add_option("-f", "--filter", dest="filter", default="all", help="filter rule name")
parser.add_option("-l", "--list", action="store_true", dest="list", default=False, help="list available rules")
parser.add_option("-n", "--num", dest="limit", default=0, help="limit number of cases")
parser.add_option("-s", "--seed", dest="seed", default=0, help="use selected seed for random selection")
parser.add_option("-g", "--groups", dest="groups_file", default=None, help="select cases based on group list file")
(options, args) = parser.parse_args()
if options.list:
print "Available filter rules:"
for filter in FILTER_RULES:
print " %s: %s" % (filter.name, filter.description)
sys.exit(0)
if len(args) == 0:
die("No input files specified")
elif len(args) > 2:
die("Too many arguments")
# Fetch filter
filter = getFilter(FILTER_RULES, options.filter)
if filter == None:
die("Unknown filter '%s'" % options.filter)
# Read case list
caselist = readCaseList(args[0])
if len(args) > 1:
results = readTestResults(args[1])
results = addResultsToCaseList(caselist, results)
else:
results = toResultList(caselist)
# Execute filters for results
for rule in filter.filters:
results = filterList(results, rule)
if options.limit != 0:
if options.groups_file != None:
groups = createGroups(results, readGroupList(options.groups_file))
else:
groups = createLeafGroups(results)
results = selectRandomSubset(results, groups, int(options.limit), int(options.seed))
elif options.groups_file != None:
groups = createGroups(results, readGroupList(options.groups_file))
results = selectCasesInGroups(results, groups)
# Print test set
for result in results:
print result.name