# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this file, # You can obtain one at http://mozilla.org/MPL/2.0/.
def sort_paths_keyfn(k):
sort_paths_keyfn.rx = getattr(sort_paths_keyfn, "rx", None) # static if sort_paths_keyfn.rx isNone:
sort_paths_keyfn.rx = re.compile(FILENAME_REGEX)
name = str(k) if name == DEFAULT_SECTION: return""
m = sort_paths_keyfn.rx.findall(name) if len(m) == 1and len(m[0]) == 5:
prefix = m[0][0] # text before "Bug"
bug = m[0][1] # the word "Bug"
underbar = m[0][2] # underbar or dash (optional)
num = m[0][3] # the bug id
suffix = m[0][4] # text after the bug id
name = f"{prefix}{bug.lower()}{underbar}{int(num):09d}{suffix}" return name return name
def sort_paths(paths): """
Returns a list of paths (tests) in a manifest in alphabetical order.
Ensures DEFAULT is first and filenames with a bug number are in the proper order. """ return sorted(paths, key=sort_paths_keyfn)
def parse_toml_str(contents): """
Parse TOML contents using rtoml (fastest), tomllib, or toml """ try: import rtoml
TOMLDecodeError = ValueError # rtoml raises ValueError on parse errors
TOMLloads = rtoml.loads except ImportError: try: from tomllib import TOMLDecodeError from tomllib import loads as TOMLloads except ImportError: from toml import TomlDecodeError as TOMLDecodeError from toml import loads as TOMLloads
def read_toml(
fp,
defaults=None,
default=DEFAULT_SECTION,
_comments=None,
_separators=None,
strict=True,
handle_defaults=True,
document=False,
add_line_no=False,
): """
read a .toml file andreturn a list of [(section, values)]
- fp : file pointer or path to read
- defaults : default set of variables
- default : name of the section for the default section
- comments : characters that if they start a line denote a comment
- separators : strings that denote key, value separation in order
- strict : whether to be strict about parsing
- handle_defaults : whether to incorporate defaults into each section
- document: read TOML with tomlkit andreturn source in test["document"]
- add_line_no: add the line number where the test name appears in the file to the source. Also, the document variable must be set to Truefor this flag to work. (This is used only to generate the documentation) """
if document: # Use tomlkit to parse the file contents
error, manifest = parse_tomlkit_str(contents) else:
error, manifest = parse_toml_str(contents) if error: raise OSError(f"Error parsing TOML manifest file {filename}: {error}")
# handle each section of the manifest for section in manifest.keys():
current_section = {} for key in manifest[section].keys():
val = manifest[section][key] if isinstance(val, bool): # must coerce to lowercase string if val:
val = "true" else:
val = "false" elif isinstance(val, list):
new_vals = "" for v in val: if len(new_vals) > 0:
new_vals += os.linesep
new_val = str(v).strip() # coerce to str
comment_found = inline_comment_rx.search(new_val) if comment_found:
new_val = new_val[0 : comment_found.span()[0]] if" = "in new_val: raise Exception(
f"Should not assign in {key} list condition for {section}"
)
new_vals += new_val
val = new_vals else:
val = str(val).strip() # coerce to str
comment_found = inline_comment_rx.search(val) if comment_found:
val = val[0 : comment_found.span()[0]] if" = "in val: raise Exception(
f"Should not assign in {key} condition for {section}"
)
current_section[key] = val if section.lower() == default.lower():
default_section = current_section # DEFAULT does NOT appear in the output else:
sections.append((section, current_section))
# merge global defaults with the DEFAULT section
defaults = combine_fields(defaults, default_section) if handle_defaults: # merge combined defaults into each section
sections = [(i, combine_fields(defaults, j)) for i, j in sections]
if document and add_line_no: # Take the line where the test name appears in the file. for i, _ in enumerate(sections):
line = contents.split(sections[i][0])[0].count(os.linesep) + 1
manifest.setdefault(sections[i][0], {})["lineno"] = str(line) elifnot document:
manifest = None
return sections, defaults, manifest
def unused_condition(condition: str) -> bool: """true if condition does not exist in the CI infrastructure"""
def idiomatic_condition(cond: str, condition: str) -> TupleOptStrListStr: """
Given the cond (skip-if, run-if)...
Converts a manifest condtiion into an idiomatic form
cond isNoneif the condition should be removed
Returns (condition, new_conds) where
- condition is the revised condition
- new_conds are NEW conditions to add """
if"verify"in condition: return (condition, new_conds) # do not edit with verify* # Get values for i in range(len(ops)):
op: str = ops[i]
op = op.strip() if EQUAL in op:
kv = op.split(EQUAL) if len(kv) != 2:
single_ops.append(op) continue
k, v = kv
k = k.strip(" '\"")
vv = v.strip()
v = vv.strip("'\"") if k in k_seen: # duplicate key => ignore continue
k_seen[k] = True if k == "android_version":
android_version = v
op = f"android_version == '{android_version}'" elif k == "arch":
arch = v
op = f"arch == '{arch}'" elif k == "display":
display = v
op = f"display == '{display}'" elif k == "os":
os = v
op = f"os == '{os}'" elif k == "os_version":
os_version = v
op = f"os_version == '{os_version}'" elif v == vv: # not quoted if v == "false": if k == "debug":
permutations.append(["asan", "opt", "tsan"]) else:
single_ops.append("!" + k)
op = None elif v == "true":
single_ops.append(k)
op = None else:
op = f"{k} == {v}" else:
op = f"{k} == '{v}'" if op isnotNone:
new_ops.append(op) elif NOT_EQUAL in op or GREATER_EQUAL in op:
new_ops.append(op) elif op == "apple_catalina": if os isNone:
os = "mac"
op = f"os == '{os}'"
new_ops.insert(0, op) if os_version isNone:
os_version = "10.15"
op = f"os_version == '{os_version}'"
new_ops.insert(1, op) if arch isNone:
arch = "x86_64"
op = f"arch == '{arch}'"
new_ops.insert(2, op) elif op == "apple_silicon": if os isNone:
os = "mac"
op = f"os == '{os}'"
new_ops.insert(0, op) if os_version isNone:
os_version = "15.30"
op = f"os_version == '{os_version}'"
new_ops.insert(1, op) if arch isNone:
arch = "aarch64"
op = f"arch == '{arch}'"
new_ops.insert(2, op) elif op == "win10_2009": if os isNone:
os = "win"
op = f"os == '{os}'"
new_ops.insert(0, op) if os_version isNone:
os_version = "10.2009"
op = f"os_version == '{os_version}'"
new_ops.insert(1, op) if arch isNone:
arch = "x86_64"
op = f"arch == '{arch}'"
new_ops.insert(2, op) elif op == "win11_2009": # no longer used return (None, new_conds) elif op == "!debug":
permutations.append(["asan", "opt", "tsan"]) else:
single_ops.append(op)
# place single operands after equality/comparison operands
ops = [] for op in new_ops:
ops.append(op)
single_ops.sort() for op in single_ops:
ops.append(op)
# Handle special cases if android_version isnotNone: from mozinfo.platforminfo import android_api_to_os_version
v = android_api_to_os_version(android_version) if os isNone:
os = "android"
op = f"os == '{os}'"
i = 0
ops.insert(i, op) else:
i = ops.index(f"os == '{os}'") if os_version isNone:
op = f"os_version == '{v}'"
i += 1
ops.insert(i, op) elif v != os_version: raise Exception(
f"android_version == '{android_version}' conflicts with os_version == '{os_version}'"
) if os isnotNone:
i = ops.index(f"os == '{os}'") if os == "linux": if os_version isNone: if cond == "skip-if":
new_cond = "os == 'linux' && os_version == '22.04' && arch == 'x86_64' && display == 'wayland'" for single_op in single_ops:
new_cond += " && " + single_op
new_conds.append(new_cond)
os_version = "24.04"
op = f"os_version == '{os_version}'"
i += 1
ops.insert(i, op) else:
i = ops.index(f"os_version == '{os_version}'") if arch isNone: if cond == "skip-if":
arch = "x86_64"
op = f"arch == '{arch}'"
i += 1
ops.insert(i, op) else:
i = ops.index(f"arch == '{arch}'") if display isNone: if cond == "skip-if": if os_version == "22.04":
display = "wayland" else:
display = "x11"
op = f"display == '{display}'"
i += 1
ops.insert(i, op) elif os_version == "24.04"and display == "wayland":
i = ops.index(f"display == '{display}'")
display = "x11"
op = f"display == '{display}'"
ops[i] = op elif os == "mac": if os_version isNone: # do NOT guess os_version on mac if cond == "skip-if":
new_cond = ( "os == 'mac' && os_version == '10.15' && arch == 'x86_64'"
) for single_op in single_ops:
new_cond += " && " + single_op
new_conds.append(new_cond)
new_cond = ( "os == 'mac' && os_version == '14.70' && arch == 'x86_64'"
) for single_op in single_ops:
new_cond += " && " + single_op
new_conds.append(new_cond)
os_version = "15.30"
op = f"os_version == '{os_version}'"
i += 1
ops.insert(i, op) elif os_version == "11.20": # no longer used return (None, new_conds) else:
i = ops.index(f"os_version == '{os_version}'") if arch isNoneand cond == "skip-if": if os_version in ["10.15", "14.70"]:
arch = "x86_64" else:
arch = "aarch64"
op = f"arch == '{arch}'"
i += 1
ops.insert(i, op) elif os == "win": if os_version isNone: pass# do NOT guess os_version on win elif os_version == "11.2009": # no longer used return (None, new_conds)
# Fix values
new_ops = [] for op in ops:
new_op = op if EQUAL in op:
kv = op.split(EQUAL) if len(kv) == 2:
k, v = kv
k = k.strip(" '\"")
v = v.strip(" '\"") if k == "android_version":
new_op = None elif k == "bits": if arch isNone:
arch = "x86_64" if v == "32":
arch = "x86" elif (
os isnotNone and os == "mac" and os_version isnotNone and os_version notin ["10.15", "14.70"]
):
arch = "aarch64"
new_op = f"arch == '{arch}'" else:
new_op = None if new_op isnotNone:
new_ops.append(new_op)
condition = " && ".join(new_ops)
new_conds.insert(0, condition) if len(permutations) > 0:
perm_conds: ListStr = [] for perm in permutations: # for each set of permutations for condition in new_conds: # for all the conditions for p in perm:
c = condition if c != "":
c += " && "
c += p
perm_conds.append(c)
new_conds = perm_conds
perm_conds = []
i = 0 while i < len(new_conds):
condition = new_conds[i] if unused_condition(condition): del new_conds[i] else:
i += 1 if len(new_conds) > 0:
condition = new_conds.pop(0) ifnot condition:
condition = None else:
condition = None return (condition, new_conds)
def add_unique_condition(
conds: OptConditions, condition: str, comment: str
) -> OptConditions: """only add a condition if it is unique"""
ignore: bool = False for c in conds: if _should_ignore_new_condition(c[0], condition):
ignore = True ifnot ignore:
conds.append([condition, comment]) return conds
def alphabetize_toml_str(manifest, fix: bool = False): """
Will take a TOMLkit manifest document (i.e. from a previous invocation
of read_toml(..., document=True) and accessing the document from mp.source_documents[filename]) andreturn it as a string in sorted order by section (i.e. test file name, taking bug ids into consideration). If fix then fix non-idiomatic conditions """
from tomlkit import array, document, dumps, table from tomlkit.items import Comment, String
for k, v in manifest.body: if k isNone:
preamble += v.as_string() continue ifnot isinstance(v, Table): raise Exception(f"MP TOML illegal keyval in preamble: {k} = {v}")
section = None ifnot first_section: if k == DEFAULT_SECTION:
new_manifest.add(k, v) else:
new_manifest.add(DEFAULT_SECTION, table())
first_section = True else:
values = v.items() if len(values) == 1: for kk, vv in values: if isinstance(vv, Table): # unquoted, dotted key
section = f"{k}.{kk}"
sections[section] = vv if section isNone:
section = str(k).strip("'\"")
sections[section] = v if fix:
new_conds: ListStr = [] # conditions to add
del_conds: ListStr = [] # conditions to remove
keyvals: Dict = sections[section] # noqa UP006 for cond, skip_if in keyvals.items():
conds: OptConditions = []
first: OptStr = None# first skip-if condition
first_comment: str = ""# first skip-if comment
e_cond: OptStr = None# existing skip-if condition
e_comment: str = ""# existing skip-if comment
comment1: str = ""
ifnot cond.endswith("-if"): continue # handle the first condition uniquely to perserve whitespace if len(skip_if) == 1: for e in skip_if._iter_items(): if isinstance(e, String): if first isNone: if first_comment: # comment before condition
first_comment = _simplify_comment(first_comment)
conds = add_unique_condition(
conds, " comment", first_comment
)
comment1 = first_comment
first_comment = ""
first = e.as_string().strip('"') else:
c: str = e.as_string().strip() if c and c != ",":
first_comment += c if skip_if.trivia isnotNone:
first_comment += skip_if.trivia.comment
skip_if.trivia.indent = ""
skip_if.trivia.comment_ws = ""
skip_if.trivia.comment = ""# remove comment after array
first_comment = _simplify_comment(first_comment) if first isnotNone:
e_cond, new_conds = idiomatic_condition(cond, first)
e_comment = first_comment
# loop over all skip-if conditions for e in skip_if._iter_items(): if isinstance(e, String): if e_cond isnotNone:
conds = add_unique_condition(conds, e_cond, e_comment) for new_cond in new_conds:
conds = add_unique_condition(conds, new_cond, e_comment)
def _simplify_comment(comment): """Remove any leading #, but preserve leading whitespace in comment""" if comment isNone: returnNone
length = len(comment)
i = 0
j = -1# remove exactly one space while i < length and comment[i] in" \n#":
i += 1 if i < length and comment[i] == " ":
j += 1
comment = comment[i:] if j > 0:
comment = " " * j + comment return comment.rstrip()
def _should_keep_existing_condition(
existing_condition: str, new_condition: str
) -> bool: """
Checks the new condition is equal ornot simpler than the existing one """ return (
existing_condition == new_condition ornot existing_condition.startswith(new_condition) ornot existing_condition.endswith(new_condition)
)
def _should_ignore_new_condition(existing_condition: str, new_condition: str) -> bool: """
Checks if the new condition is equal or more complex than an existing one
Avoid ignoring conditions like "os == 'win' && !debug"if the
existing condition is"debug" (etc.) """ return (
existing_condition == new_condition or new_condition.startswith(existing_condition) or new_condition.endswith(existing_condition)
)
class Mode: "Skipfails mode of operation"
NORMAL: int = 0
CARRYOVER: int = 1
KNOWN_INTERMITTENT: int = 2
NEW_FAILURE: int = 3
REPLACE_TBD: int = 4
CARRYOVER_FILED: int = 5
KNOWN_INTERMITTENT_FILED: int = 6
NEW_FAILURE_FILED: int = 7
class Carry: "Helper class for add_skip_if to call is_carryover()"
def test_index(self) -> int: "Returns expected relative index number of next test"
self.text_index += 1 return self.text_index
def initialize(self): if self.opt_rx isNone:
self.os_rx = re.compile(r"os == ['\"]([a-z]+)['\"]") # pre = Either start of string ^, # Or a preceding character that is not a letter, digit, !, or _.
pre = r"(?:(?<![A-Za-z0-9!_])|^)" # post = Either whitespace, # Or a non-alphanumeric, non-underscore character, # Or end of string.
post = r"(?:(?=\s)|(?=[^A-Za-z0-9_])|$)"
self.opt_rx = re.compile(pre + "opt" + post)
self.not_opt_rx = re.compile(pre + "!opt" + post)
self.debug_rx = re.compile(pre + "debug" + post)
self.not_debug_rx = re.compile(pre + "!debug" + post)
self.asan_rx = re.compile(pre + "asan" + post)
self.tsan_rx = re.compile(pre + "tsan" + post)
self.ccov_rx = re.compile(pre + "ccov" + post)
def os(self, s: str) -> str:
self.initialize()
m = self.os_rx.search(s) if m isnotNone: return m[0] return""
def is_carryover(self, existing_condition: str, new_condition: str) -> bool: """
Checks if the new condition isis a carryover from the existing
condition. See ./test/SKIP-FAILS.txt and Bug 197161 """
self.initialize() # Does the OS match?
e_os: str = self.os(existing_condition)
os: str = self.os(new_condition) if e_os: ifnot os or e_os != os: returnFalse# os mismatch
e_opt: bool = self.opt(existing_condition)
e_not_opt: bool = self.not_opt(existing_condition)
e_debug: bool = self.debug(existing_condition)
e_not_debug: bool = self.not_debug(existing_condition)
e_asan: bool = self.asan(existing_condition)
e_tsan: bool = self.tsan(existing_condition)
e_ccov: bool = self.ccov(existing_condition)
opt: bool = self.opt(new_condition)
not_opt: bool = self.not_opt(new_condition)
debug: bool = self.debug(new_condition)
not_debug: bool = self.not_debug(new_condition)
asan: bool = self.asan(new_condition)
tsan: bool = self.tsan(new_condition)
ccov: bool = self.ccov(new_condition) # NOTE: the sum of bool in Python is the most efficient way to # determine how many vars are true (as bool is represented at 0 or 1) if sum( # existing_condition has NO build type
[e_opt, e_not_opt, e_debug, e_not_debug, e_asan, e_tsan, e_ccov]
) == 0and (e_os and os and e_os == os): returnTrue# build type not present and os explicit match if (e_opt or e_not_debug) and (opt or not_debug): returnTrue# both optimized if (e_debug and sum([asan, tsan, ccov]) == 1) or (
sum([e_asan, e_tsan, e_ccov]) == 1and debug
): returnFalse# debug != [asan, tsan, ccov] if ( # both conditions have a compatible build type
sum([e_not_opt, e_debug, e_asan, e_tsan, e_ccov]) == 1 and sum([not_opt, debug, asan, tsan, ccov]) == 1
): returnTrue returnFalse
def add_skip_if(
manifest: TOMLDocument,
filename: str,
condition: str,
bug_reference: OptStr = None,
create_bug_lambda: CreateBug = None,
mode: Mode = Mode.NORMAL,
) -> TupleStrBoolStr: """
Will take a TOMLkit manifest document (i.e. from a previous invocation
of read_toml(..., document=True) and accessing the document from mp.source_documents[filename]) and mutate it in sorted order by section (i.e. test file name, taking bug ids into consideration).
Determine if this condition is a carryover (see ./test/SKIP-FAILS.txt and Bug 1971610) In carryover mode only consider carryover edits (do not create bugs) Else when notin carryover mode and create_bug_lambda isnotNone
then invoke create_bug_lambda to create new bug
Returns (additional_comment, carryover) where
additional_comment is the empty string (used in other manifest add_skip_if functions)
carryover isTrueif this condition is carried over from an existing condition
bug_reference is returned asNoneif the skip-if was ignored """
from tomlkit import array from tomlkit.items import Comment, String, Whitespace
carry = Carry()
additional_comment: str = ""
carryover: bool = False# not carried over from a previous skip-if if filename notin manifest: raise Exception(f"TOML manifest does not contain section: {filename}")
keyvals: Dict = manifest[filename] # noqa UP006
first: OptStr = None
first_comment: str = ""
skip_if: OptArray = None
ignore_cond: bool = False# this condition should not be added if"skip-if"in keyvals:
skip_if = keyvals["skip-if"] if len(skip_if) == 1: for e in skip_if._iter_items(): ifnot first: ifnot isinstance(e, Whitespace):
first = e.as_string().strip('"') else:
c = e.as_string() if c != ",":
first_comment += c if skip_if.trivia isnotNone:
first_comment += skip_if.trivia.comment
mp_array: Array = array() if skip_if isNone: # add the first one line entry to the table if mode != Mode.CARRYOVER: if create_bug_lambda isnotNone:
bug = create_bug_lambda() if bug isnotNone:
bug_reference = f"Bug {bug.id}"
mp_array.add_line(condition, indent=" ", comment=bug_reference)
mp_array.add_line("", indent="") # fixed in write_toml_str
skip_if = {"skip-if": mp_array}
keyvals.update(skip_if) else: # We store the conditions in a regular python array so we can sort them before # dumping them in the TOML
conds: OptConditions = [] if first isnotNone: if _should_ignore_new_condition(first, condition):
ignore_cond = True if first_comment:
first_comment = _simplify_comment(first_comment) if _should_keep_existing_condition(first, condition):
conds.append([first, first_comment]) if ( not ignore_cond and mode == Mode.CARRYOVER and carry.is_carryover(first, condition)
):
carryover = True
bug_reference = first_comment elif bug_reference isNoneand create_bug_lambda isNone:
bug_reference = first_comment if len(skip_if) > 1:
e_cond = None
e_comment = None for e in skip_if._iter_items(): if isinstance(e, String): if e_cond isnotNone: if _should_keep_existing_condition(e_cond, condition):
conds.append([e_cond, e_comment]) if ( not ignore_cond and mode == Mode.CARRYOVER and carry.is_carryover(e_cond, condition)
):
carryover = True
bug_reference = e_comment elif bug_reference isNoneand create_bug_lambda isNone:
bug_reference = e_comment
e_comment = None
e_cond = None if len(e) > 0:
e_cond = e.as_string().strip('"') if _should_ignore_new_condition(e_cond, condition):
ignore_cond = True elif isinstance(e, Comment):
e_comment = _simplify_comment(e.as_string()) if e_cond isnotNone: if _should_keep_existing_condition(e_cond, condition):
conds.append([e_cond, e_comment]) if ( not ignore_cond and mode == Mode.CARRYOVER and carry.is_carryover(e_cond, condition)
):
carryover = True
bug_reference = e_comment elif bug_reference isNoneand create_bug_lambda isNone:
bug_reference = e_comment if ignore_cond:
carryover = False
bug_reference = None else: if mode == Mode.NORMAL and create_bug_lambda isnotNone:
bug = create_bug_lambda() if bug isnotNone:
bug_reference = f"Bug {bug.id}"
conds.append([condition, bug_reference])
conds.sort() for c in conds:
mp_array.add_line(c[0], indent=" ", comment=c[1])
mp_array.add_line("", indent="") # fixed in write_toml_str
skip_if = {"skip-if": mp_array} del keyvals["skip-if"]
keyvals.update(skip_if) return (additional_comment, carryover, bug_reference)
def _should_remove_cond(
condition: str,
os_name: OptStr = None,
os_version: OptStr = None,
processor: OptStr = None,
):
to_ignore = [os_name, os_version, processor] for part in to_ignore: if part isnotNoneand part notin condition: returnFalse returnTrue
if os_name isNoneand os_version isNoneand processor isNone: raise ValueError("Needs at least os name, version or processor to be set.")
has_removed_items = False
for filename in manifest:
key_values = manifest[filename] if isinstance(key_values, Table) and"skip-if"in key_values:
condition_array = key_values["skip-if"] if isinstance(condition_array, Array):
new_conditions: Array = array()
condition = None
comment = None
conditions_to_add: OptConditions = [] for item in condition_array._iter_items(): if isinstance(item, String): if condition isnotNone: ifnot _should_remove_cond(
condition, os_name, os_version, processor
):
conditions_to_add.append((condition, comment)) else:
has_removed_items = True
condition = None
comment = None if len(item) > 0:
condition = item.as_string().strip('"') elif isinstance(item, Comment):
comment = _simplify_comment(item.as_string()) if condition isnotNone: ifnot _should_remove_cond(
condition, os_name, os_version, processor
):
conditions_to_add.append((condition, comment)) else:
has_removed_items = True
if len(conditions_to_add) > 0: for condition, comment in conditions_to_add:
new_conditions.add_line(condition, comment=comment, indent=" ") if len(new_conditions) > 0:
new_conditions.add_line("", indent="")
key_values.update({"skip-if": new_conditions}) else: del key_values["skip-if"]
return has_removed_items
def replace_tbd_skip_if(
manifest: TOMLDocument, filename: str, condition: str, bugid: str
) -> bool: """
Edits the test ["filename"] in manifest with the given condition
that has a bug reference `# Bug TBD` with the actual bugid
returns Trueif properly updated """ from tomlkit import array from tomlkit.items import Comment, String, Whitespace
updated: bool = False# was the TBD found and updated with bugid?
BUG_TBD: str = "Bug TBD"# string we are looking for if filename notin manifest: raise Exception(f"TOML manifest does not contain section: {filename}")
keyvals: Dict = manifest[filename] # noqa UP006 ifnot"skip-if"in keyvals: raise Exception(
f"TOML manifest for section: {filename} does not contain a skip-if condition"
)
skip_if: Array = keyvals["skip-if"]
mp_array: Array = array()
conds: OptConditions = []
first: OptStr = None# first skip-if condition
first_comment: str = ""# first skip-if comment
e_cond: OptStr = None# existing skip-if condition
e_comment: str = ""# existing skip-if comment
# handle the first condition uniquely to properly perserve whitespace if len(skip_if) == 1: for e in skip_if._iter_items(): if first isNone: ifnot isinstance(e, Whitespace):
first = e.as_string().strip('"') else:
c = e.as_string() if c != ",":
first_comment += c if skip_if.trivia isnotNone:
first_comment += skip_if.trivia.comment if first isnotNone: if first_comment:
first_comment = _simplify_comment(first_comment) if first == condition and first_comment.endswith(BUG_TBD):
i: int = max(first_comment.find(BUG_TBD), 0)
first_comment = f"{' ' * i}Bug {bugid}"
updated = True
e_cond = first
e_comment = first_comment
# loop over all skip-if conditions to find BUG_TBD for e in skip_if._iter_items(): if isinstance(e, String): if e_cond isnotNone:
conds.append([e_cond, e_comment])
e_cond = None
e_comment = "" if len(e) > 0:
e_cond = e.as_string().strip('"') if e_cond == first:
e_cond = None# don't repeat first elif isinstance(e, Comment):
e_comment = _simplify_comment(e.as_string()) if e_cond == condition and e_comment.endswith(BUG_TBD):
i: int = max(e_comment.find(BUG_TBD), 0)
e_comment = f"{' ' * i}Bug {bugid}"
updated = True if e_cond isnotNone:
conds.append([e_cond, e_comment])
# Update TOML document for the test with the updated skip-if comment
conds.sort() for c in conds:
mp_array.add_line(c[0], indent=" ", comment=c[1])
mp_array.add_line("", indent="") # fixed in write_toml_str
skip_if = {"skip-if": mp_array} del keyvals["skip-if"]
keyvals.update(skip_if)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.