1################################################################ 2# 3# Licensed to the Apache Software Foundation (ASF) under one 4# or more contributor license agreements. See the NOTICE file 5# distributed with this work for additional information 6# regarding copyright ownership. The ASF licenses this file 7# to you under the Apache License, Version 2.0 (the 8# "License"); you may not use this file except in compliance 9# with the License. You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, 14# software distributed under the License is distributed on an 15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16# KIND, either express or implied. See the License for the 17# specific language governing permissions and limitations 18# under the License. 19# 20################################################################ 21 22"""Known deviations as data: rows of test_name<TAB>kind<TAB>reason in data/expect/*.tsv, applied as pytest marks. 23 24test_name is the pytest item name including its parameter ids, e.g. test_formula_result[DCOUNT.zero.crit]; 25a '*' matches any characters. kind is xfail (the item is expected to fail; it is reported XPASS when it starts to pass, and 26fails then under --strict-expect), flaky (it fails in some runs; never strict) or skip. The reason must say what is known, not guess.""" 27import re 28 29from aootest.paths import DATA 30 31EXPECT_DIR = DATA / "expect" 32KINDS = ("xfail", "flaky", "skip") 33 34 35class Row: 36 def __init__(self, pattern, kind, reason, origin): 37 self.pattern, self.kind, self.reason, self.origin = pattern, kind, reason, origin 38 self.regex = re.compile("".join(".*" if c == "*" else re.escape(c) for c in pattern)) 39 self.used = 0 40 41 42def load(directory=EXPECT_DIR): 43 rows = [] 44 for path in sorted(directory.glob("*.tsv")): 45 for n, line in enumerate(open(path, encoding="utf-8"), 1): 46 if not line.strip() or line.startswith("#"): 47 continue 48 parts = line.rstrip("\n").split("\t") 49 if len(parts) != 3 or parts[1] not in KINDS or not parts[2].strip(): 50 raise ValueError("%s:%d: expected test_name<TAB>xfail|flaky|skip<TAB>reason" % (path.name, n)) 51 rows.append(Row(parts[0], parts[1], parts[2], "%s:%d" % (path.name, n))) 52 return rows 53 54 55def match(rows, name): 56 """The first row that matches the item name, or None.""" 57 for r in rows: 58 if r.regex.fullmatch(name): 59 return r 60 return None 61