1*9880d681SAndroid Build Coastguard Workerimport os 2*9880d681SAndroid Build Coastguard Workerfrom xml.sax.saxutils import escape 3*9880d681SAndroid Build Coastguard Workerfrom json import JSONEncoder 4*9880d681SAndroid Build Coastguard Worker 5*9880d681SAndroid Build Coastguard Worker# Test result codes. 6*9880d681SAndroid Build Coastguard Worker 7*9880d681SAndroid Build Coastguard Workerclass ResultCode(object): 8*9880d681SAndroid Build Coastguard Worker """Test result codes.""" 9*9880d681SAndroid Build Coastguard Worker 10*9880d681SAndroid Build Coastguard Worker # We override __new__ and __getnewargs__ to ensure that pickling still 11*9880d681SAndroid Build Coastguard Worker # provides unique ResultCode objects in any particular instance. 12*9880d681SAndroid Build Coastguard Worker _instances = {} 13*9880d681SAndroid Build Coastguard Worker def __new__(cls, name, isFailure): 14*9880d681SAndroid Build Coastguard Worker res = cls._instances.get(name) 15*9880d681SAndroid Build Coastguard Worker if res is None: 16*9880d681SAndroid Build Coastguard Worker cls._instances[name] = res = super(ResultCode, cls).__new__(cls) 17*9880d681SAndroid Build Coastguard Worker return res 18*9880d681SAndroid Build Coastguard Worker def __getnewargs__(self): 19*9880d681SAndroid Build Coastguard Worker return (self.name, self.isFailure) 20*9880d681SAndroid Build Coastguard Worker 21*9880d681SAndroid Build Coastguard Worker def __init__(self, name, isFailure): 22*9880d681SAndroid Build Coastguard Worker self.name = name 23*9880d681SAndroid Build Coastguard Worker self.isFailure = isFailure 24*9880d681SAndroid Build Coastguard Worker 25*9880d681SAndroid Build Coastguard Worker def __repr__(self): 26*9880d681SAndroid Build Coastguard Worker return '%s%r' % (self.__class__.__name__, 27*9880d681SAndroid Build Coastguard Worker (self.name, self.isFailure)) 28*9880d681SAndroid Build Coastguard Worker 29*9880d681SAndroid Build Coastguard WorkerPASS = ResultCode('PASS', False) 30*9880d681SAndroid Build Coastguard WorkerFLAKYPASS = ResultCode('FLAKYPASS', False) 31*9880d681SAndroid Build Coastguard WorkerXFAIL = ResultCode('XFAIL', False) 32*9880d681SAndroid Build Coastguard WorkerFAIL = ResultCode('FAIL', True) 33*9880d681SAndroid Build Coastguard WorkerXPASS = ResultCode('XPASS', True) 34*9880d681SAndroid Build Coastguard WorkerUNRESOLVED = ResultCode('UNRESOLVED', True) 35*9880d681SAndroid Build Coastguard WorkerUNSUPPORTED = ResultCode('UNSUPPORTED', False) 36*9880d681SAndroid Build Coastguard WorkerTIMEOUT = ResultCode('TIMEOUT', True) 37*9880d681SAndroid Build Coastguard Worker 38*9880d681SAndroid Build Coastguard Worker# Test metric values. 39*9880d681SAndroid Build Coastguard Worker 40*9880d681SAndroid Build Coastguard Workerclass MetricValue(object): 41*9880d681SAndroid Build Coastguard Worker def format(self): 42*9880d681SAndroid Build Coastguard Worker """ 43*9880d681SAndroid Build Coastguard Worker format() -> str 44*9880d681SAndroid Build Coastguard Worker 45*9880d681SAndroid Build Coastguard Worker Convert this metric to a string suitable for displaying as part of the 46*9880d681SAndroid Build Coastguard Worker console output. 47*9880d681SAndroid Build Coastguard Worker """ 48*9880d681SAndroid Build Coastguard Worker raise RuntimeError("abstract method") 49*9880d681SAndroid Build Coastguard Worker 50*9880d681SAndroid Build Coastguard Worker def todata(self): 51*9880d681SAndroid Build Coastguard Worker """ 52*9880d681SAndroid Build Coastguard Worker todata() -> json-serializable data 53*9880d681SAndroid Build Coastguard Worker 54*9880d681SAndroid Build Coastguard Worker Convert this metric to content suitable for serializing in the JSON test 55*9880d681SAndroid Build Coastguard Worker output. 56*9880d681SAndroid Build Coastguard Worker """ 57*9880d681SAndroid Build Coastguard Worker raise RuntimeError("abstract method") 58*9880d681SAndroid Build Coastguard Worker 59*9880d681SAndroid Build Coastguard Workerclass IntMetricValue(MetricValue): 60*9880d681SAndroid Build Coastguard Worker def __init__(self, value): 61*9880d681SAndroid Build Coastguard Worker self.value = value 62*9880d681SAndroid Build Coastguard Worker 63*9880d681SAndroid Build Coastguard Worker def format(self): 64*9880d681SAndroid Build Coastguard Worker return str(self.value) 65*9880d681SAndroid Build Coastguard Worker 66*9880d681SAndroid Build Coastguard Worker def todata(self): 67*9880d681SAndroid Build Coastguard Worker return self.value 68*9880d681SAndroid Build Coastguard Worker 69*9880d681SAndroid Build Coastguard Workerclass RealMetricValue(MetricValue): 70*9880d681SAndroid Build Coastguard Worker def __init__(self, value): 71*9880d681SAndroid Build Coastguard Worker self.value = value 72*9880d681SAndroid Build Coastguard Worker 73*9880d681SAndroid Build Coastguard Worker def format(self): 74*9880d681SAndroid Build Coastguard Worker return '%.4f' % self.value 75*9880d681SAndroid Build Coastguard Worker 76*9880d681SAndroid Build Coastguard Worker def todata(self): 77*9880d681SAndroid Build Coastguard Worker return self.value 78*9880d681SAndroid Build Coastguard Worker 79*9880d681SAndroid Build Coastguard Workerclass JSONMetricValue(MetricValue): 80*9880d681SAndroid Build Coastguard Worker """ 81*9880d681SAndroid Build Coastguard Worker JSONMetricValue is used for types that are representable in the output 82*9880d681SAndroid Build Coastguard Worker but that are otherwise uninterpreted. 83*9880d681SAndroid Build Coastguard Worker """ 84*9880d681SAndroid Build Coastguard Worker def __init__(self, value): 85*9880d681SAndroid Build Coastguard Worker # Ensure the value is a serializable by trying to encode it. 86*9880d681SAndroid Build Coastguard Worker # WARNING: The value may change before it is encoded again, and may 87*9880d681SAndroid Build Coastguard Worker # not be encodable after the change. 88*9880d681SAndroid Build Coastguard Worker try: 89*9880d681SAndroid Build Coastguard Worker e = JSONEncoder() 90*9880d681SAndroid Build Coastguard Worker e.encode(value) 91*9880d681SAndroid Build Coastguard Worker except TypeError: 92*9880d681SAndroid Build Coastguard Worker raise 93*9880d681SAndroid Build Coastguard Worker self.value = value 94*9880d681SAndroid Build Coastguard Worker 95*9880d681SAndroid Build Coastguard Worker def format(self): 96*9880d681SAndroid Build Coastguard Worker e = JSONEncoder(indent=2, sort_keys=True) 97*9880d681SAndroid Build Coastguard Worker return e.encode(self.value) 98*9880d681SAndroid Build Coastguard Worker 99*9880d681SAndroid Build Coastguard Worker def todata(self): 100*9880d681SAndroid Build Coastguard Worker return self.value 101*9880d681SAndroid Build Coastguard Worker 102*9880d681SAndroid Build Coastguard Workerdef toMetricValue(value): 103*9880d681SAndroid Build Coastguard Worker if isinstance(value, MetricValue): 104*9880d681SAndroid Build Coastguard Worker return value 105*9880d681SAndroid Build Coastguard Worker elif isinstance(value, int): 106*9880d681SAndroid Build Coastguard Worker return IntMetricValue(value) 107*9880d681SAndroid Build Coastguard Worker elif isinstance(value, float): 108*9880d681SAndroid Build Coastguard Worker return RealMetricValue(value) 109*9880d681SAndroid Build Coastguard Worker else: 110*9880d681SAndroid Build Coastguard Worker # 'long' is only present in python2 111*9880d681SAndroid Build Coastguard Worker try: 112*9880d681SAndroid Build Coastguard Worker if isinstance(value, long): 113*9880d681SAndroid Build Coastguard Worker return IntMetricValue(value) 114*9880d681SAndroid Build Coastguard Worker except NameError: 115*9880d681SAndroid Build Coastguard Worker pass 116*9880d681SAndroid Build Coastguard Worker 117*9880d681SAndroid Build Coastguard Worker # Try to create a JSONMetricValue and let the constructor throw 118*9880d681SAndroid Build Coastguard Worker # if value is not a valid type. 119*9880d681SAndroid Build Coastguard Worker return JSONMetricValue(value) 120*9880d681SAndroid Build Coastguard Worker 121*9880d681SAndroid Build Coastguard Worker 122*9880d681SAndroid Build Coastguard Worker# Test results. 123*9880d681SAndroid Build Coastguard Worker 124*9880d681SAndroid Build Coastguard Workerclass Result(object): 125*9880d681SAndroid Build Coastguard Worker """Wrapper for the results of executing an individual test.""" 126*9880d681SAndroid Build Coastguard Worker 127*9880d681SAndroid Build Coastguard Worker def __init__(self, code, output='', elapsed=None): 128*9880d681SAndroid Build Coastguard Worker # The result code. 129*9880d681SAndroid Build Coastguard Worker self.code = code 130*9880d681SAndroid Build Coastguard Worker # The test output. 131*9880d681SAndroid Build Coastguard Worker self.output = output 132*9880d681SAndroid Build Coastguard Worker # The wall timing to execute the test, if timing. 133*9880d681SAndroid Build Coastguard Worker self.elapsed = elapsed 134*9880d681SAndroid Build Coastguard Worker # The metrics reported by this test. 135*9880d681SAndroid Build Coastguard Worker self.metrics = {} 136*9880d681SAndroid Build Coastguard Worker 137*9880d681SAndroid Build Coastguard Worker def addMetric(self, name, value): 138*9880d681SAndroid Build Coastguard Worker """ 139*9880d681SAndroid Build Coastguard Worker addMetric(name, value) 140*9880d681SAndroid Build Coastguard Worker 141*9880d681SAndroid Build Coastguard Worker Attach a test metric to the test result, with the given name and list of 142*9880d681SAndroid Build Coastguard Worker values. It is an error to attempt to attach the metrics with the same 143*9880d681SAndroid Build Coastguard Worker name multiple times. 144*9880d681SAndroid Build Coastguard Worker 145*9880d681SAndroid Build Coastguard Worker Each value must be an instance of a MetricValue subclass. 146*9880d681SAndroid Build Coastguard Worker """ 147*9880d681SAndroid Build Coastguard Worker if name in self.metrics: 148*9880d681SAndroid Build Coastguard Worker raise ValueError("result already includes metrics for %r" % ( 149*9880d681SAndroid Build Coastguard Worker name,)) 150*9880d681SAndroid Build Coastguard Worker if not isinstance(value, MetricValue): 151*9880d681SAndroid Build Coastguard Worker raise TypeError("unexpected metric value: %r" % (value,)) 152*9880d681SAndroid Build Coastguard Worker self.metrics[name] = value 153*9880d681SAndroid Build Coastguard Worker 154*9880d681SAndroid Build Coastguard Worker# Test classes. 155*9880d681SAndroid Build Coastguard Worker 156*9880d681SAndroid Build Coastguard Workerclass TestSuite: 157*9880d681SAndroid Build Coastguard Worker """TestSuite - Information on a group of tests. 158*9880d681SAndroid Build Coastguard Worker 159*9880d681SAndroid Build Coastguard Worker A test suite groups together a set of logically related tests. 160*9880d681SAndroid Build Coastguard Worker """ 161*9880d681SAndroid Build Coastguard Worker 162*9880d681SAndroid Build Coastguard Worker def __init__(self, name, source_root, exec_root, config): 163*9880d681SAndroid Build Coastguard Worker self.name = name 164*9880d681SAndroid Build Coastguard Worker self.source_root = source_root 165*9880d681SAndroid Build Coastguard Worker self.exec_root = exec_root 166*9880d681SAndroid Build Coastguard Worker # The test suite configuration. 167*9880d681SAndroid Build Coastguard Worker self.config = config 168*9880d681SAndroid Build Coastguard Worker 169*9880d681SAndroid Build Coastguard Worker def getSourcePath(self, components): 170*9880d681SAndroid Build Coastguard Worker return os.path.join(self.source_root, *components) 171*9880d681SAndroid Build Coastguard Worker 172*9880d681SAndroid Build Coastguard Worker def getExecPath(self, components): 173*9880d681SAndroid Build Coastguard Worker return os.path.join(self.exec_root, *components) 174*9880d681SAndroid Build Coastguard Worker 175*9880d681SAndroid Build Coastguard Workerclass Test: 176*9880d681SAndroid Build Coastguard Worker """Test - Information on a single test instance.""" 177*9880d681SAndroid Build Coastguard Worker 178*9880d681SAndroid Build Coastguard Worker def __init__(self, suite, path_in_suite, config, file_path = None): 179*9880d681SAndroid Build Coastguard Worker self.suite = suite 180*9880d681SAndroid Build Coastguard Worker self.path_in_suite = path_in_suite 181*9880d681SAndroid Build Coastguard Worker self.config = config 182*9880d681SAndroid Build Coastguard Worker self.file_path = file_path 183*9880d681SAndroid Build Coastguard Worker # A list of conditions under which this test is expected to fail. These 184*9880d681SAndroid Build Coastguard Worker # can optionally be provided by test format handlers, and will be 185*9880d681SAndroid Build Coastguard Worker # honored when the test result is supplied. 186*9880d681SAndroid Build Coastguard Worker self.xfails = [] 187*9880d681SAndroid Build Coastguard Worker # The test result, once complete. 188*9880d681SAndroid Build Coastguard Worker self.result = None 189*9880d681SAndroid Build Coastguard Worker 190*9880d681SAndroid Build Coastguard Worker def setResult(self, result): 191*9880d681SAndroid Build Coastguard Worker if self.result is not None: 192*9880d681SAndroid Build Coastguard Worker raise ArgumentError("test result already set") 193*9880d681SAndroid Build Coastguard Worker if not isinstance(result, Result): 194*9880d681SAndroid Build Coastguard Worker raise ArgumentError("unexpected result type") 195*9880d681SAndroid Build Coastguard Worker 196*9880d681SAndroid Build Coastguard Worker self.result = result 197*9880d681SAndroid Build Coastguard Worker 198*9880d681SAndroid Build Coastguard Worker # Apply the XFAIL handling to resolve the result exit code. 199*9880d681SAndroid Build Coastguard Worker if self.isExpectedToFail(): 200*9880d681SAndroid Build Coastguard Worker if self.result.code == PASS: 201*9880d681SAndroid Build Coastguard Worker self.result.code = XPASS 202*9880d681SAndroid Build Coastguard Worker elif self.result.code == FAIL: 203*9880d681SAndroid Build Coastguard Worker self.result.code = XFAIL 204*9880d681SAndroid Build Coastguard Worker 205*9880d681SAndroid Build Coastguard Worker def getFullName(self): 206*9880d681SAndroid Build Coastguard Worker return self.suite.config.name + ' :: ' + '/'.join(self.path_in_suite) 207*9880d681SAndroid Build Coastguard Worker 208*9880d681SAndroid Build Coastguard Worker def getFilePath(self): 209*9880d681SAndroid Build Coastguard Worker if self.file_path: 210*9880d681SAndroid Build Coastguard Worker return self.file_path 211*9880d681SAndroid Build Coastguard Worker return self.getSourcePath() 212*9880d681SAndroid Build Coastguard Worker 213*9880d681SAndroid Build Coastguard Worker def getSourcePath(self): 214*9880d681SAndroid Build Coastguard Worker return self.suite.getSourcePath(self.path_in_suite) 215*9880d681SAndroid Build Coastguard Worker 216*9880d681SAndroid Build Coastguard Worker def getExecPath(self): 217*9880d681SAndroid Build Coastguard Worker return self.suite.getExecPath(self.path_in_suite) 218*9880d681SAndroid Build Coastguard Worker 219*9880d681SAndroid Build Coastguard Worker def isExpectedToFail(self): 220*9880d681SAndroid Build Coastguard Worker """ 221*9880d681SAndroid Build Coastguard Worker isExpectedToFail() -> bool 222*9880d681SAndroid Build Coastguard Worker 223*9880d681SAndroid Build Coastguard Worker Check whether this test is expected to fail in the current 224*9880d681SAndroid Build Coastguard Worker configuration. This check relies on the test xfails property which by 225*9880d681SAndroid Build Coastguard Worker some test formats may not be computed until the test has first been 226*9880d681SAndroid Build Coastguard Worker executed. 227*9880d681SAndroid Build Coastguard Worker """ 228*9880d681SAndroid Build Coastguard Worker 229*9880d681SAndroid Build Coastguard Worker # Check if any of the xfails match an available feature or the target. 230*9880d681SAndroid Build Coastguard Worker for item in self.xfails: 231*9880d681SAndroid Build Coastguard Worker # If this is the wildcard, it always fails. 232*9880d681SAndroid Build Coastguard Worker if item == '*': 233*9880d681SAndroid Build Coastguard Worker return True 234*9880d681SAndroid Build Coastguard Worker 235*9880d681SAndroid Build Coastguard Worker # If this is an exact match for one of the features, it fails. 236*9880d681SAndroid Build Coastguard Worker if item in self.config.available_features: 237*9880d681SAndroid Build Coastguard Worker return True 238*9880d681SAndroid Build Coastguard Worker 239*9880d681SAndroid Build Coastguard Worker # If this is a part of the target triple, it fails. 240*9880d681SAndroid Build Coastguard Worker if item and item in self.suite.config.target_triple: 241*9880d681SAndroid Build Coastguard Worker return True 242*9880d681SAndroid Build Coastguard Worker 243*9880d681SAndroid Build Coastguard Worker return False 244*9880d681SAndroid Build Coastguard Worker 245*9880d681SAndroid Build Coastguard Worker def isEarlyTest(self): 246*9880d681SAndroid Build Coastguard Worker """ 247*9880d681SAndroid Build Coastguard Worker isEarlyTest() -> bool 248*9880d681SAndroid Build Coastguard Worker 249*9880d681SAndroid Build Coastguard Worker Check whether this test should be executed early in a particular run. 250*9880d681SAndroid Build Coastguard Worker This can be used for test suites with long running tests to maximize 251*9880d681SAndroid Build Coastguard Worker parallelism or where it is desirable to surface their failures early. 252*9880d681SAndroid Build Coastguard Worker """ 253*9880d681SAndroid Build Coastguard Worker return self.suite.config.is_early 254*9880d681SAndroid Build Coastguard Worker 255*9880d681SAndroid Build Coastguard Worker def getJUnitXML(self): 256*9880d681SAndroid Build Coastguard Worker test_name = self.path_in_suite[-1] 257*9880d681SAndroid Build Coastguard Worker test_path = self.path_in_suite[:-1] 258*9880d681SAndroid Build Coastguard Worker safe_test_path = [x.replace(".","_") for x in test_path] 259*9880d681SAndroid Build Coastguard Worker safe_name = self.suite.name.replace(".","-") 260*9880d681SAndroid Build Coastguard Worker 261*9880d681SAndroid Build Coastguard Worker if safe_test_path: 262*9880d681SAndroid Build Coastguard Worker class_name = safe_name + "." + "/".join(safe_test_path) 263*9880d681SAndroid Build Coastguard Worker else: 264*9880d681SAndroid Build Coastguard Worker class_name = safe_name + "." + safe_name 265*9880d681SAndroid Build Coastguard Worker 266*9880d681SAndroid Build Coastguard Worker xml = "<testcase classname='" + class_name + "' name='" + \ 267*9880d681SAndroid Build Coastguard Worker test_name + "'" 268*9880d681SAndroid Build Coastguard Worker xml += " time='%.2f'" % (self.result.elapsed,) 269*9880d681SAndroid Build Coastguard Worker if self.result.code.isFailure: 270*9880d681SAndroid Build Coastguard Worker xml += ">\n\t<failure >\n" + escape(self.result.output) 271*9880d681SAndroid Build Coastguard Worker xml += "\n\t</failure>\n</testcase>" 272*9880d681SAndroid Build Coastguard Worker else: 273*9880d681SAndroid Build Coastguard Worker xml += "/>" 274*9880d681SAndroid Build Coastguard Worker return xml 275