def check_css_globally_unique(repo_root, paths): """ Checks that CSS filenames are sufficiently unique This groups files by path classifying them as "test", "reference", or "support". "test" files must have a unique name across files that share links to the same spec. "reference" and "support" files, on the other hand, must have globally unique names. :param repo_root: the repository root :param paths: list of all paths :returns: a list of errors found in ``paths`` """ test_files = defaultdict(set) ref_files = defaultdict(set) support_files = defaultdict(set) for path in paths: if os.name == "nt": path = path.replace("\\", "/") if not path.startswith("css/"): continue source_file = SourceFile(repo_root, path, "/") if source_file.name_is_non_test: # If we're name_is_non_test for a reason apart from support, ignore it. # We care about support because of the requirement all support files in css/ to be in # a support directory; see the start of check_parsed. offset = path.find("/support/") if offset == -1: continue parts = source_file.dir_path.split(os.path.sep) if (parts[0] in source_file.root_dir_non_test or any(item in source_file.dir_non_test - {"support"} for item in parts) or any(parts[:len(non_test_path)] == list(non_test_path) for non_test_path in source_file.dir_path_non_test)): continue name = path[offset + 1:] support_files[name].add(path) elif source_file.name_is_reference: ref_files[source_file.name].add(path) else: name = source_file.name.replace('-manual', '') test_files[name].add(path) errors = [] for name, colliding in iteritems(test_files): if len(colliding) > 1: if not _all_files_equal( [os.path.join(repo_root, x) for x in colliding]): # Only compute by_spec if there are prima-facie collisions because of cost by_spec = defaultdict(set) for path in colliding: source_file = SourceFile(repo_root, path, "/") for link in source_file.spec_links: for r in (drafts_csswg_re, w3c_tr_re, w3c_dev_re): m = r.match(link) if m: spec = m.group(1) break else: continue by_spec[spec].add(path) for spec, paths in iteritems(by_spec): if not _all_files_equal( [os.path.join(repo_root, x) for x in paths]): for x in paths: context = (name, spec, ", ".join(sorted(paths))) errors.append( rules.CSSCollidingTestName.error(x, context)) for rule_class, d in [(rules.CSSCollidingRefName, ref_files), (rules.CSSCollidingSupportName, support_files)]: for name, colliding in iteritems(d): if len(colliding) > 1: if not _all_files_equal( [os.path.join(repo_root, x) for x in colliding]): context = (name, ", ".join(sorted(colliding))) for x in colliding: errors.append(rule_class.error(x, context)) return errors
def check_parsed(repo_root, path, f): source_file = SourceFile(repo_root, path, "/", contents=f.read()) errors = [] if path.startswith("css/"): if (source_file.type == "support" and not source_file.name_is_non_test and not source_file.name_is_reference): return [("SUPPORT-WRONG-DIR", "Support file not in support directory", path, None)] if (source_file.type != "support" and not source_file.name_is_reference and not source_file.spec_links): return [("MISSING-LINK", "Testcase file must have a link to a spec", path, None)] if source_file.name_is_non_test or source_file.name_is_manual: return [] if source_file.markup_type is None: return [] if source_file.root is None: return [("PARSE-FAILED", "Unable to parse file", path, None)] if source_file.type == "manual" and not source_file.name_is_manual: return [("CONTENT-MANUAL", "Manual test whose filename doesn't end in '-manual'", path, None)] if source_file.type == "visual" and not source_file.name_is_visual: return [("CONTENT-VISUAL", "Visual test whose filename doesn't end in '-visual'", path, None)] for reftest_node in source_file.reftest_nodes: href = reftest_node.attrib.get("href", "").strip(space_chars) parts = urlsplit(href) if (parts.scheme or parts.netloc) and parts != urlsplit("about:blank"): errors.append(("ABSOLUTE-URL-REF", "Reference test with a reference file specified via an absolute URL: '%s'" % href, path, None)) continue ref_url = urljoin(source_file.url, href) ref_parts = urlsplit(ref_url) if source_file.url == ref_url: errors.append(("SAME-FILE-REF", "Reference test which points at itself as a reference", path, None)) continue assert ref_parts.path != "" reference_file = os.path.join(repo_root, ref_parts.path[1:]) reference_rel = reftest_node.attrib.get("rel", "") if not os.path.isfile(reference_file): errors.append(("NON-EXISTENT-REF", "Reference test with a non-existent '%s' relationship reference: '%s'" % (reference_rel, href), path, None)) if len(source_file.timeout_nodes) > 1: errors.append(("MULTIPLE-TIMEOUT", "More than one meta name='timeout'", path, None)) for timeout_node in source_file.timeout_nodes: timeout_value = timeout_node.attrib.get("content", "").lower() if timeout_value != "long": errors.append(("INVALID-TIMEOUT", "Invalid timeout value %s" % timeout_value, path, None)) if source_file.testharness_nodes: if len(source_file.testharness_nodes) > 1: errors.append(("MULTIPLE-TESTHARNESS", "More than one <script src='/resources/testharness.js'>", path, None)) testharnessreport_nodes = source_file.root.findall(".//{http://www.w3.org/1999/xhtml}script[@src='/resources/testharnessreport.js']") if not testharnessreport_nodes: errors.append(("MISSING-TESTHARNESSREPORT", "Missing <script src='/resources/testharnessreport.js'>", path, None)) else: if len(testharnessreport_nodes) > 1: errors.append(("MULTIPLE-TESTHARNESSREPORT", "More than one <script src='/resources/testharnessreport.js'>", path, None)) testharnesscss_nodes = source_file.root.findall(".//{http://www.w3.org/1999/xhtml}link[@href='/resources/testharness.css']") if testharnesscss_nodes: errors.append(("PRESENT-TESTHARNESSCSS", "Explicit link to testharness.css present", path, None)) for element in source_file.variant_nodes: if "content" not in element.attrib: errors.append(("VARIANT-MISSING", "<meta name=variant> missing 'content' attribute", path, None)) else: variant = element.attrib["content"] if variant != "" and variant[0] not in ("?", "#"): errors.append(("MALFORMED-VARIANT", "%s <meta name=variant> 'content' attribute must be the empty string or start with '?' or '#'" % path, None)) seen_elements = {"timeout": False, "testharness": False, "testharnessreport": False} required_elements = [key for key, value in {"testharness": True, "testharnessreport": len(testharnessreport_nodes) > 0, "timeout": len(source_file.timeout_nodes) > 0}.items() if value] for elem in source_file.root.iter(): if source_file.timeout_nodes and elem == source_file.timeout_nodes[0]: seen_elements["timeout"] = True if seen_elements["testharness"]: errors.append(("LATE-TIMEOUT", "<meta name=timeout> seen after testharness.js script", path, None)) elif elem == source_file.testharness_nodes[0]: seen_elements["testharness"] = True elif testharnessreport_nodes and elem == testharnessreport_nodes[0]: seen_elements["testharnessreport"] = True if not seen_elements["testharness"]: errors.append(("EARLY-TESTHARNESSREPORT", "testharnessreport.js script seen before testharness.js script", path, None)) if all(seen_elements[name] for name in required_elements): break if source_file.testdriver_nodes: if len(source_file.testdriver_nodes) > 1: errors.append(("MULTIPLE-TESTDRIVER", "More than one <script src='/resources/testdriver.js'>", path, None)) testdriver_vendor_nodes = source_file.root.findall(".//{http://www.w3.org/1999/xhtml}script[@src='/resources/testdriver-vendor.js']") if not testdriver_vendor_nodes: errors.append(("MISSING-TESTDRIVER-VENDOR", "Missing <script src='/resources/testdriver-vendor.js'>", path, None)) else: if len(testdriver_vendor_nodes) > 1: errors.append(("MULTIPLE-TESTDRIVER-VENDOR", "More than one <script src='/resources/testdriver-vendor.js'>", path, None)) for element in source_file.root.findall(".//{http://www.w3.org/1999/xhtml}script[@src]"): src = element.attrib["src"] for name in ["testharness", "testharnessreport", "testdriver", "testdriver-vendor"]: if "%s.js" % name == src or ("/%s.js" % name in src and src != "/resources/%s.js" % name): errors.append(("%s-PATH" % name.upper(), "%s.js script seen with incorrect path" % name, path, None)) return errors
def check_parsed(repo_root, path, f, css_mode): source_file = SourceFile(repo_root, path, "/", contents=f.read()) errors = [] if css_mode or path.startswith("css/"): if (source_file.type == "support" and not source_file.name_is_non_test and not source_file.name_is_reference): return [("SUPPORT-WRONG-DIR", "Support file not in support directory", path, None)] if source_file.name_is_non_test or source_file.name_is_manual: return [] if source_file.markup_type is None: return [] if source_file.root is None: return [("PARSE-FAILED", "Unable to parse file", path, None)] if source_file.type == "manual" and not source_file.name_is_manual: return [ ("CONTENT-MANUAL", "Manual test whose filename doesn't end in '-manual'", path, None) ] if source_file.type == "visual" and not source_file.name_is_visual: return [ ("CONTENT-VISUAL", "Visual test whose filename doesn't end in '-visual'", path, None) ] if len(source_file.timeout_nodes) > 1: errors.append(("MULTIPLE-TIMEOUT", "More than one meta name='timeout'", path, None)) for timeout_node in source_file.timeout_nodes: timeout_value = timeout_node.attrib.get("content", "").lower() if timeout_value != "long": errors.append( ("INVALID-TIMEOUT", "Invalid timeout value %s" % timeout_value, path, None)) if source_file.testharness_nodes: if len(source_file.testharness_nodes) > 1: errors.append( ("MULTIPLE-TESTHARNESS", "More than one <script src='/resources/testharness.js'>", path, None)) testharnessreport_nodes = source_file.root.findall( ".//{http://www.w3.org/1999/xhtml}script[@src='/resources/testharnessreport.js']" ) if not testharnessreport_nodes: errors.append( ("MISSING-TESTHARNESSREPORT", "Missing <script src='/resources/testharnessreport.js'>", path, None)) else: if len(testharnessreport_nodes) > 1: errors.append(( "MULTIPLE-TESTHARNESSREPORT", "More than one <script src='/resources/testharnessreport.js'>", path, None)) testharnesscss_nodes = source_file.root.findall( ".//{http://www.w3.org/1999/xhtml}link[@href='/resources/testharness.css']" ) if testharnesscss_nodes: errors.append( ("PRESENT-TESTHARNESSCSS", "Explicit link to testharness.css present", path, None)) for element in source_file.variant_nodes: if "content" not in element.attrib: errors.append( ("VARIANT-MISSING", "<meta name=variant> missing 'content' attribute", path, None)) else: variant = element.attrib["content"] if variant != "" and variant[0] not in ("?", "#"): errors.append(( "MALFORMED-VARIANT", "%s <meta name=variant> 'content' attribute must be the empty string or start with '?' or '#'" % path, None)) seen_elements = { "timeout": False, "testharness": False, "testharnessreport": False } required_elements = [ key for key, value in { "testharness": True, "testharnessreport": len(testharnessreport_nodes) > 0, "timeout": len(source_file.timeout_nodes) > 0 }.items() if value ] for elem in source_file.root.iter(): if source_file.timeout_nodes and elem == source_file.timeout_nodes[ 0]: seen_elements["timeout"] = True if seen_elements["testharness"]: errors.append(( "LATE-TIMEOUT", "<meta name=timeout> seen after testharness.js script", path, None)) elif elem == source_file.testharness_nodes[0]: seen_elements["testharness"] = True elif testharnessreport_nodes and elem == testharnessreport_nodes[0]: seen_elements["testharnessreport"] = True if not seen_elements["testharness"]: errors.append(( "EARLY-TESTHARNESSREPORT", "testharnessreport.js script seen before testharness.js script", path, None)) if all(seen_elements[name] for name in required_elements): break for element in source_file.root.findall( ".//{http://www.w3.org/1999/xhtml}script[@src]"): src = element.attrib["src"] for name in ["testharness", "testharnessreport"]: if "%s.js" % name == src or ("/%s.js" % name in src and src != "/resources/%s.js" % name): errors.append(("%s-PATH" % name.upper(), "%s.js script seen with incorrect path" % name, path, None)) return errors
def check_parsed(path, f): source_file = SourceFile(repo_root, path, "/") errors = [] if source_file.name_is_non_test or source_file.name_is_manual: return [] if source_file.markup_type is None: return [] if source_file.root is None: return [("PARSE-FAILED", "Unable to parse file %s" % path, None)] if len(source_file.timeout_nodes) > 1: errors.append(("MULTIPLE-TIMEOUT", "%s more than one meta name='timeout'" % path, None)) for timeout_node in source_file.timeout_nodes: timeout_value = timeout_node.attrib.get("content", "").lower() if timeout_value != "long": errors.append(("INVALID-TIMEOUT", "%s invalid timeout value %s" % (path, timeout_value), None)) if source_file.testharness_nodes: if len(source_file.testharness_nodes) > 1: errors.append(("MULTIPLE-TESTHARNESS", "%s more than one <script src='/resources/testharness.js'>" % path, None)) testharnessreport_nodes = source_file.root.findall(".//{http://www.w3.org/1999/xhtml}script[@src='/resources/testharnessreport.js']") if not testharnessreport_nodes: errors.append(("MISSING-TESTHARNESSREPORT", "%s missing <script src='/resources/testharnessreport.js'>" % path, None)) else: if len(testharnessreport_nodes) > 1: errors.append(("MULTIPLE-TESTHARNESSREPORT", "%s more than one <script src='/resources/testharnessreport.js'>" % path, None)) for element in source_file.variant_nodes: if "content" not in element.attrib: errors.append(("VARIANT-MISSING", "%s has <meta name=variant> missing 'content' attribute" % path, None)) else: variant = element.attrib["content"] if variant != "" and variant[0] not in ("?", "#"): errors.append(("MALFORMED-VARIANT", "%s <meta name=variant> 'content' attribute must be the empty string or start with '?' or '#'" % path, None)) seen_elements = {"timeout": False, "testharness": False, "testharnessreport": False} required_elements = [key for key, value in {"testharness": True, "testharnessreport": len(testharnessreport_nodes) > 0, "timeout": len(source_file.timeout_nodes) > 0}.iteritems() if value] for elem in source_file.root.iter(): if source_file.timeout_nodes and elem == source_file.timeout_nodes[0]: seen_elements["timeout"] = True if seen_elements["testharness"]: errors.append(("LATE-TIMEOUT", "%s <meta name=timeout> seen after testharness.js script" % path, None)) elif elem == source_file.testharness_nodes[0]: seen_elements["testharness"] = True elif testharnessreport_nodes and elem == testharnessreport_nodes[0]: seen_elements["testharnessreport"] = True if not seen_elements["testharness"]: errors.append(("EARLY-TESTHARNESSREPORT", "%s testharnessreport.js script seen before testharness.js script" % path, None)) if all(seen_elements[name] for name in required_elements): break return errors
def __init__(self, id, path, timeout=10, contents=testharness_test): self.id = id self.url = "/" + path self.item_type = "testharness" self.timeout = timeout self.source_file = SourceFile("/", path, "/", contents=contents)