4 from collections
import deque
5 from six
import binary_type
, PY3
6 from six
.moves
.urllib
.parse
import urljoin
7 from fnmatch
import fnmatch
11 # MYPY is set to True when run under Mypy.
12 from typing
import Any
13 from typing
import AnyStr
14 from typing
import BinaryIO
15 from typing
import Callable
16 from typing
import Deque
17 from typing
import Dict
18 from typing
import Iterable
19 from typing
import List
20 from typing
import Optional
21 from typing
import Pattern
22 from typing
import Set
23 from typing
import Text
24 from typing
import Tuple
25 from typing
import Union
26 from typing
import cast
29 from xml
.etree
import cElementTree
as ElementTree
31 from xml
.etree
import ElementTree
as ElementTree
# type: ignore
35 from . import XMLParser
36 from .item
import (ManifestItem
, ManualTest
, WebDriverSpecTest
, RefTest
, TestharnessTest
,
37 SupportFile
, CrashTest
, ConformanceCheckerTest
, VisualTest
)
38 from .utils
import ContextManagerBytesIO
, cached_property
41 js_meta_re
= re
.compile(br
"//\s*META:\s*(\w*)=(.*)$")
42 python_meta_re
= re
.compile(br
"#\s*META:\s*(\w*)=(.*)$")
44 reference_file_re
= re
.compile(r
'(^|[\-_])(not)?ref[0-9]*([\-_]|$)')
46 space_chars
= u
"".join(html5lib
.constants
.spaceCharacters
) # type: Text
48 def replace_end(s
, old
, new
):
49 # type: (Text, Text, Text) -> Text
51 Given a string `s` that ends with `old`, replace that occurrence of `old`
54 assert s
.endswith(old
)
55 return s
[:-len(old
)] + new
58 def read_script_metadata(f
, regexp
):
59 # type: (BinaryIO, Pattern[bytes]) -> Iterable[Tuple[bytes, bytes]]
61 Yields any metadata (pairs of bytestrings) from the file-like object `f`,
62 as specified according to a supplied regexp.
64 `regexp` - Regexp containing two groups containing the metadata name and
68 assert isinstance(line
, binary_type
), line
69 m
= regexp
.match(line
)
73 yield (m
.groups()[0], m
.groups()[1])
77 b
"default": {"longhand": {b
"window", b
"dedicatedworker"}},
78 b
"window": {"suffix": ".any.html"},
79 b
"serviceworker": {"force_https": True},
81 b
"dedicatedworker": {"suffix": ".any.worker.html"},
82 b
"worker": {"longhand": {b
"dedicatedworker", b
"sharedworker", b
"serviceworker"}},
83 b
"jsshell": {"suffix": ".any.js"},
84 } # type: Dict[bytes, Dict[str, Any]]
87 def get_any_variants(item
):
88 # type: (bytes) -> Set[bytes]
90 Returns a set of variants (bytestrings) defined by the given keyword.
92 assert isinstance(item
, binary_type
), item
93 assert not item
.startswith(b
"!"), item
95 variant
= _any_variants
.get(item
, None)
99 return variant
.get("longhand", {item}
)
102 def get_default_any_variants():
103 # type: () -> Set[bytes]
105 Returns a set of variants (bytestrings) that will be used by default.
107 return set(_any_variants
[b
"default"]["longhand"])
110 def parse_variants(value
):
111 # type: (bytes) -> Set[bytes]
113 Returns a set of variants (bytestrings) defined by a comma-separated value.
115 assert isinstance(value
, binary_type
), value
117 globals = get_default_any_variants()
119 for item
in value
.split(b
","):
121 if item
.startswith(b
"!"):
122 globals -= get_any_variants(item
[1:])
124 globals |
= get_any_variants(item
)
129 def global_suffixes(value
):
130 # type: (bytes) -> Set[Tuple[bytes, bool]]
132 Yields tuples of the relevant filename suffix (a string) and whether the
133 variant is intended to run in a JS shell, for the variants defined by the
134 given comma-separated value.
136 assert isinstance(value
, binary_type
), value
140 global_types
= parse_variants(value
)
141 for global_type
in global_types
:
142 variant
= _any_variants
[global_type
]
143 suffix
= variant
.get("suffix", ".any.%s.html" % global_type
.decode("utf-8"))
144 rv
.add((suffix
, global_type
== b
"jsshell"))
149 def global_variant_url(url
, suffix
):
150 # type: (Text, Text) -> Text
152 Returns a url created from the given url and suffix (all strings).
154 url
= url
.replace(".any.", ".")
155 # If the url must be loaded over https, ensure that it will have
156 # the form .https.any.js
157 if ".https." in url
and suffix
.startswith(".https."):
158 url
= url
.replace(".https.", ".")
159 elif ".h2." in url
and suffix
.startswith(".h2."):
160 url
= url
.replace(".h2.", ".")
161 return replace_end(url
, ".js", suffix
)
165 # type: (BinaryIO) -> ElementTree.ElementTree
166 doc
= html5lib
.parse(f
, treebuilder
="etree", useChardet
=False)
168 return cast(ElementTree
.ElementTree
, doc
)
172 # type: (BinaryIO) -> ElementTree.ElementTree
174 # raises ValueError with an unsupported encoding,
175 # ParseError when there's an undefined entity
176 return ElementTree
.parse(f
)
177 except (ValueError, ElementTree
.ParseError
):
179 return ElementTree
.parse(f
, XMLParser
.XMLParser()) # type: ignore
182 class SourceFile(object):
183 parsers
= {"html":_parse_html
,
185 "svg":_parse_xml
} # type: Dict[Text, Callable[[BinaryIO], ElementTree.ElementTree]]
187 root_dir_non_test
= {"common"}
189 dir_non_test
= {"resources",
193 dir_path_non_test
= {("css21", "archive"),
194 ("css", "CSS2", "archive"),
195 ("css", "common")} # type: Set[Tuple[bytes, ...]]
197 def __init__(self
, tests_root
, rel_path
, url_base
, hash=None, contents
=None):
198 # type: (AnyStr, AnyStr, Text, Optional[bytes], Optional[bytes]) -> None
199 """Object representing a file in a source tree.
201 :param tests_root: Path to the root of the source tree
202 :param rel_path: File path relative to tests_root
203 :param url_base: Base URL used when converting file paths to urls
204 :param contents: Byte array of the contents of the file or ``None``.
207 assert not os
.path
.isabs(rel_path
), rel_path
210 # do slash normalization on Windows
211 if isinstance(rel_path
, binary_type
):
212 rel_path
= rel_path
.replace(b
"/", b
"\\")
214 rel_path
= rel_path
.replace(u
"/", u
"\\")
216 dir_path
, filename
= os
.path
.split(rel_path
)
217 name
, ext
= os
.path
.splitext(filename
)
221 type_flag
= name
.rsplit("-", 1)[1].split(".")[0]
223 meta_flags
= name
.split(".")[1:]
225 self
.tests_root
= tests_root
# type: Union[bytes, Text]
226 self
.rel_path
= rel_path
# type: Union[bytes, Text]
227 self
.dir_path
= dir_path
# type: Union[bytes, Text]
228 self
.filename
= filename
# type: Union[bytes, Text]
229 self
.name
= name
# type: Union[bytes, Text]
230 self
.ext
= ext
# type: Union[bytes, Text]
231 self
.type_flag
= type_flag
# type: Optional[Union[bytes, Text]]
232 self
.meta_flags
= meta_flags
# type: Union[List[bytes], List[Text]]
233 self
.url_base
= url_base
234 self
.contents
= contents
235 self
.items_cache
= None # type: Optional[Tuple[Text, List[ManifestItem]]]
238 def __getstate__(self
):
239 # type: () -> Dict[str, Any]
240 # Remove computed properties if we pickle this class
241 rv
= self
.__dict
__.copy()
243 if "__cached_properties__" in rv
:
244 cached_properties
= rv
["__cached_properties__"]
245 for key
in rv
.keys():
246 if key
in cached_properties
:
248 del rv
["__cached_properties__"]
251 def name_prefix(self
, prefix
):
252 # type: (bytes) -> bool
253 """Check if the filename starts with a given prefix
255 :param prefix: The prefix to check"""
256 return self
.name
.startswith(prefix
)
260 """Return whether this file represents a directory."""
261 if self
.contents
is not None:
264 return os
.path
.isdir(self
.rel_path
)
267 # type: () -> BinaryIO
270 * the contents specified in the constructor, if any;
271 * a File object opened for reading the file contents.
274 if self
.contents
is not None:
275 wrapped
= ContextManagerBytesIO(self
.contents
)
277 file_obj
= cast(BinaryIO
, wrapped
)
281 file_obj
= open(self
.path
, 'rb')
286 # type: () -> Union[bytes, Text]
287 return os
.path
.join(self
.tests_root
, self
.rel_path
)
292 assert not os
.path
.isabs(self
.rel_path
), self
.rel_path
293 return self
.rel_path
.replace(os
.sep
, "/")
298 return urljoin(self
.url_base
, self
.rel_url
)
304 with self
.open() as f
:
307 data
= b
"".join((b
"blob ", b
"%d" % len(content
), b
"\0", content
))
308 hash_str
= hashlib
.sha1(data
).hexdigest() # type: str
310 self
._hash
= hash_str
.encode("ascii")
312 self
._hash
= hash_str
316 def in_non_test_dir(self
):
318 if self
.dir_path
== "":
321 parts
= self
.dir_path
.split(os
.path
.sep
)
323 if (parts
[0] in self
.root_dir_non_test
or
324 any(item
in self
.dir_non_test
for item
in parts
) or
325 any(parts
[:len(path
)] == list(path
) for path
in self
.dir_path_non_test
)):
329 def in_conformance_checker_dir(self
):
331 return (self
.dir_path
== "conformance-checkers" or
332 self
.dir_path
.startswith("conformance-checkers" + os
.path
.sep
))
335 def name_is_non_test(self
):
337 """Check if the file name matches the conditions for the file to
338 be a non-test file"""
339 return (self
.is_dir() or
340 self
.name_prefix("MANIFEST") or
341 self
.filename
== "META.yml" or
342 self
.filename
.startswith(".") or
343 self
.filename
.endswith(".headers") or
344 self
.filename
.endswith(".ini") or
345 self
.in_non_test_dir())
348 def name_is_conformance(self
):
350 return (self
.in_conformance_checker_dir() and
351 self
.type_flag
in ("is-valid", "no-valid"))
354 def name_is_conformance_support(self
):
356 return self
.in_conformance_checker_dir()
359 def name_is_manual(self
):
361 """Check if the file name matches the conditions for the file to
362 be a manual test file"""
363 return self
.type_flag
== "manual"
366 def name_is_visual(self
):
368 """Check if the file name matches the conditions for the file to
369 be a visual test file"""
370 return self
.type_flag
== "visual"
373 def name_is_multi_global(self
):
375 """Check if the file name matches the conditions for the file to
376 be a multi-global js test file"""
377 return "any" in self
.meta_flags
and self
.ext
== ".js"
380 def name_is_worker(self
):
382 """Check if the file name matches the conditions for the file to
383 be a worker js test file"""
384 return "worker" in self
.meta_flags
and self
.ext
== ".js"
387 def name_is_window(self
):
389 """Check if the file name matches the conditions for the file to
390 be a window js test file"""
391 return "window" in self
.meta_flags
and self
.ext
== ".js"
394 def name_is_webdriver(self
):
396 """Check if the file name matches the conditions for the file to
397 be a webdriver spec test file"""
398 # wdspec tests are in subdirectories of /webdriver excluding __init__.py
400 rel_dir_tree
= self
.rel_path
.split(os
.path
.sep
)
401 return (((rel_dir_tree
[0] == "webdriver" and len(rel_dir_tree
) > 1) or
402 (rel_dir_tree
[:2] == ["infrastructure", "webdriver"] and
403 len(rel_dir_tree
) > 2)) and
404 self
.filename
not in ("__init__.py", "conftest.py") and
405 fnmatch(self
.filename
, wd_pattern
))
408 def name_is_reference(self
):
410 """Check if the file name matches the conditions for the file to
411 be a reference file (not a reftest)"""
412 return "/reference/" in self
.url
or bool(reference_file_re
.search(self
.name
))
415 def name_is_crashtest(self
):
417 return self
.type_flag
== "crash" or "crashtests" in self
.dir_path
.split(os
.path
.sep
)
420 def markup_type(self
):
421 # type: () -> Optional[Text]
422 """Return the type of markup contained in a file, based on its extension,
423 or None if it doesn't contain markup"""
430 if ext
in ["html", "htm"]:
432 if ext
in ["xhtml", "xht", "xml"]:
440 # type: () -> Optional[Union[ElementTree.Element, ElementTree.ElementTree]]
441 """Return an ElementTree Element for the root node of the file if it contains
442 markup, or None if it does not"""
443 if not self
.markup_type
:
446 parser
= self
.parsers
[self
.markup_type
]
448 with self
.open() as f
:
454 if hasattr(tree
, "getroot"):
455 root
= tree
.getroot() # type: Union[ElementTree.Element, ElementTree.ElementTree]
462 def timeout_nodes(self
):
463 # type: () -> List[ElementTree.Element]
464 """List of ElementTree Elements corresponding to nodes in a test that
466 assert self
.root
is not None
467 return self
.root
.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='timeout']")
470 def script_metadata(self
):
471 # type: () -> Optional[List[Tuple[bytes, bytes]]]
472 if self
.name_is_worker
or self
.name_is_multi_global
or self
.name_is_window
:
474 elif self
.name_is_webdriver
:
475 regexp
= python_meta_re
479 with self
.open() as f
:
480 return list(read_script_metadata(f
, regexp
))
484 # type: () -> Optional[Text]
485 """The timeout of a test or reference file. "long" if the file has an extended timeout
487 if self
.script_metadata
:
488 if any(m
== (b
"timeout", b
"long") for m
in self
.script_metadata
):
491 if self
.root
is None:
494 if self
.timeout_nodes
:
495 timeout_str
= self
.timeout_nodes
[0].attrib
.get("content", None) # type: Optional[Text]
496 if timeout_str
and timeout_str
.lower() == "long":
502 def viewport_nodes(self
):
503 # type: () -> List[ElementTree.Element]
504 """List of ElementTree Elements corresponding to nodes in a test that
505 specify viewport sizes"""
506 assert self
.root
is not None
507 return self
.root
.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='viewport-size']")
510 def viewport_size(self
):
511 # type: () -> Optional[Text]
512 """The viewport size of a test or reference file"""
513 if self
.root
is None:
516 if not self
.viewport_nodes
:
519 return self
.viewport_nodes
[0].attrib
.get("content", None)
523 # type: () -> List[ElementTree.Element]
524 """List of ElementTree Elements corresponding to nodes in a test that
525 specify device pixel ratios"""
526 assert self
.root
is not None
527 return self
.root
.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='device-pixel-ratio']")
531 # type: () -> Optional[Text]
532 """The device pixel ratio of a test or reference file"""
533 if self
.root
is None:
536 if not self
.dpi_nodes
:
539 return self
.dpi_nodes
[0].attrib
.get("content", None)
542 def fuzzy_nodes(self
):
543 # type: () -> List[ElementTree.Element]
544 """List of ElementTree Elements corresponding to nodes in a test that
545 specify reftest fuzziness"""
546 assert self
.root
is not None
547 return self
.root
.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='fuzzy']")
551 # type: () -> Dict[Optional[Tuple[Text, Text, Text]], List[List[int]]]
552 rv
= {} # type: Dict[Optional[Tuple[Text, Text, Text]], List[List[int]]]
553 if self
.root
is None:
556 if not self
.fuzzy_nodes
:
559 args
= [u
"maxDifference", u
"totalPixels"]
561 for node
in self
.fuzzy_nodes
:
562 item
= node
.attrib
.get(u
"content", u
"") # type: Text
564 parts
= item
.rsplit(u
":", 1)
566 key
= None # type: Optional[Tuple[Text, Text, Text]]
569 key_part
= urljoin(self
.url
, parts
[0])
571 for ref
in self
.references
: # type: Tuple[Text, Text]
572 if ref
[0] == key_part
:
575 if reftype
not in (u
"==", u
"!="):
576 raise ValueError("Fuzzy key %s doesn't correspond to a references" % key_part
)
577 key
= (self
.url
, key_part
, reftype
)
579 ranges
= value
.split(u
";")
581 raise ValueError("Malformed fuzzy value %s" % item
)
582 arg_values
= {} # type: Dict[Text, List[int]]
583 positional_args
= deque() # type: Deque[List[int]]
584 for range_str_value
in ranges
: # type: Text
585 name
= None # type: Optional[Text]
586 if u
"=" in range_str_value
:
587 name
, range_str_value
= [part
.strip()
588 for part
in range_str_value
.split(u
"=", 1)]
590 raise ValueError("%s is not a valid fuzzy property" % name
)
591 if arg_values
.get(name
):
592 raise ValueError("Got multiple values for argument %s" % name
)
593 if u
"-" in range_str_value
:
594 range_min
, range_max
= range_str_value
.split(u
"-")
596 range_min
= range_str_value
597 range_max
= range_str_value
599 range_value
= [int(x
.strip()) for x
in (range_min
, range_max
)]
601 raise ValueError("Fuzzy value %s must be a range of integers" %
604 positional_args
.append(range_value
)
606 arg_values
[name
] = range_value
608 for arg_name
in args
:
609 if arg_values
.get(arg_name
):
610 arg_value
= arg_values
.pop(arg_name
)
612 arg_value
= positional_args
.popleft()
613 rv
[key
].append(arg_value
)
614 assert len(arg_values
) == 0 and len(positional_args
) == 0
618 def testharness_nodes(self
):
619 # type: () -> List[ElementTree.Element]
620 """List of ElementTree Elements corresponding to nodes representing a
621 testharness.js script"""
622 assert self
.root
is not None
623 return self
.root
.findall(".//{http://www.w3.org/1999/xhtml}script[@src='/resources/testharness.js']")
626 def content_is_testharness(self
):
627 # type: () -> Optional[bool]
628 """Boolean indicating whether the file content represents a
629 testharness.js test"""
630 if self
.root
is None:
632 return bool(self
.testharness_nodes
)
635 def variant_nodes(self
):
636 # type: () -> List[ElementTree.Element]
637 """List of ElementTree Elements corresponding to nodes representing a
639 assert self
.root
is not None
640 return self
.root
.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='variant']")
643 def test_variants(self
):
644 # type: () -> List[Text]
645 rv
= [] # type: List[Text]
646 if self
.ext
== ".js":
647 script_metadata
= self
.script_metadata
648 assert script_metadata
is not None
649 for (key
, value
) in script_metadata
:
650 if key
== b
"variant":
651 rv
.append(value
.decode("utf-8"))
653 for element
in self
.variant_nodes
:
654 if "content" in element
.attrib
:
655 variant
= element
.attrib
["content"] # type: Text
659 assert variant
== "" or variant
[0] in ["#", "?"], variant
667 def testdriver_nodes(self
):
668 # type: () -> List[ElementTree.Element]
669 """List of ElementTree Elements corresponding to nodes representing a
670 testdriver.js script"""
671 assert self
.root
is not None
672 return self
.root
.findall(".//{http://www.w3.org/1999/xhtml}script[@src='/resources/testdriver.js']")
675 def has_testdriver(self
):
676 # type: () -> Optional[bool]
677 """Boolean indicating whether the file content represents a
678 testharness.js test"""
679 if self
.root
is None:
681 return bool(self
.testdriver_nodes
)
684 def reftest_nodes(self
):
685 # type: () -> List[ElementTree.Element]
686 """List of ElementTree Elements corresponding to nodes representing a
687 to a reftest <link>"""
688 if self
.root
is None:
691 match_links
= self
.root
.findall(".//{http://www.w3.org/1999/xhtml}link[@rel='match']")
692 mismatch_links
= self
.root
.findall(".//{http://www.w3.org/1999/xhtml}link[@rel='mismatch']")
693 return match_links
+ mismatch_links
696 def references(self
):
697 # type: () -> List[Tuple[Text, Text]]
698 """List of (ref_url, relation) tuples for any reftest references specified in
700 rv
= [] # type: List[Tuple[Text, Text]]
701 rel_map
= {"match": "==", "mismatch": "!="}
702 for item
in self
.reftest_nodes
:
703 if "href" in item
.attrib
:
704 ref_url
= urljoin(self
.url
, item
.attrib
["href"].strip(space_chars
))
705 ref_type
= rel_map
[item
.attrib
["rel"]]
706 rv
.append((ref_url
, ref_type
))
710 def content_is_ref_node(self
):
712 """Boolean indicating whether the file is a non-leaf node in a reftest
713 graph (i.e. if it contains any <link rel=[mis]match>"""
714 return bool(self
.references
)
717 def css_flag_nodes(self
):
718 # type: () -> List[ElementTree.Element]
719 """List of ElementTree Elements corresponding to nodes representing a
721 if self
.root
is None:
723 return self
.root
.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='flags']")
727 # type: () -> Set[Text]
728 """Set of flags specified in the file"""
729 rv
= set() # type: Set[Text]
730 for item
in self
.css_flag_nodes
:
731 if "content" in item
.attrib
:
732 for flag
in item
.attrib
["content"].split():
737 def content_is_css_manual(self
):
738 # type: () -> Optional[bool]
739 """Boolean indicating whether the file content represents a
740 CSS WG-style manual test"""
741 if self
.root
is None:
743 # return True if the intersection between the two sets is non-empty
744 return bool(self
.css_flags
& {"animated", "font", "history", "interact", "paged", "speech", "userstyle"})
747 def spec_link_nodes(self
):
748 # type: () -> List[ElementTree.Element]
749 """List of ElementTree Elements corresponding to nodes representing a
750 <link rel=help>, used to point to specs"""
751 if self
.root
is None:
753 return self
.root
.findall(".//{http://www.w3.org/1999/xhtml}link[@rel='help']")
756 def spec_links(self
):
757 # type: () -> Set[Text]
758 """Set of spec links specified in the file"""
759 rv
= set() # type: Set[Text]
760 for item
in self
.spec_link_nodes
:
761 if "href" in item
.attrib
:
762 rv
.add(item
.attrib
["href"].strip(space_chars
))
766 def content_is_css_visual(self
):
767 # type: () -> Optional[bool]
768 """Boolean indicating whether the file content represents a
769 CSS WG-style visual test"""
770 if self
.root
is None:
772 return bool(self
.ext
in {'.xht', '.html', '.xhtml', '.htm', '.xml', '.svg'} and
778 rv
, _
= self
.manifest_items()
781 def manifest_items(self
):
782 # type: () -> Tuple[Text, List[ManifestItem]]
783 """List of manifest items corresponding to the file. There is typically one
784 per test, but in the case of reftests a node may have corresponding manifest
785 items without being a test itself."""
788 return self
.items_cache
790 if self
.name_is_non_test
:
795 )] # type: Tuple[Text, List[ManifestItem]]
797 elif self
.name_is_manual
:
798 rv
= ManualTest
.item_type
, [
806 elif self
.name_is_conformance
:
807 rv
= ConformanceCheckerTest
.item_type
, [
808 ConformanceCheckerTest(
815 elif self
.name_is_conformance_support
:
822 elif self
.name_is_visual
:
823 rv
= VisualTest
.item_type
, [
831 elif self
.name_is_crashtest
:
832 rv
= CrashTest
.item_type
, [
840 elif self
.name_is_multi_global
:
842 script_metadata
= self
.script_metadata
843 assert script_metadata
is not None
844 for (key
, value
) in script_metadata
:
854 global_variant_url(self
.rel_url
, suffix
) + variant
,
855 timeout
=self
.timeout
,
857 script_metadata
=self
.script_metadata
859 for (suffix
, jsshell
) in sorted(global_suffixes(globals))
860 for variant
in self
.test_variants
861 ] # type: List[ManifestItem]
862 rv
= TestharnessTest
.item_type
, tests
864 elif self
.name_is_worker
:
865 test_url
= replace_end(self
.rel_url
, ".worker.js", ".worker.html")
872 timeout
=self
.timeout
,
873 script_metadata
=self
.script_metadata
875 for variant
in self
.test_variants
877 rv
= TestharnessTest
.item_type
, tests
879 elif self
.name_is_window
:
880 test_url
= replace_end(self
.rel_url
, ".window.js", ".window.html")
887 timeout
=self
.timeout
,
888 script_metadata
=self
.script_metadata
890 for variant
in self
.test_variants
892 rv
= TestharnessTest
.item_type
, tests
894 elif self
.name_is_webdriver
:
895 rv
= WebDriverSpecTest
.item_type
, [
904 elif self
.content_is_css_manual
and not self
.name_is_reference
:
905 rv
= ManualTest
.item_type
, [
913 elif self
.content_is_testharness
:
914 rv
= TestharnessTest
.item_type
, []
915 testdriver
= self
.has_testdriver
916 for variant
in self
.test_variants
:
917 url
= self
.rel_url
+ variant
918 rv
[1].append(TestharnessTest(
923 timeout
=self
.timeout
,
924 testdriver
=testdriver
,
925 script_metadata
=self
.script_metadata
928 elif self
.content_is_ref_node
:
929 rv
= RefTest
.item_type
, [
935 references
=self
.references
,
936 timeout
=self
.timeout
,
937 viewport_size
=self
.viewport_size
,
942 elif self
.content_is_css_visual
and not self
.name_is_reference
:
943 rv
= VisualTest
.item_type
, [
958 assert len(rv
[1]) == len(set(rv
[1]))
960 self
.items_cache
= rv