commit 5f1d7e6644da1aa346b575a5bbb0a2753af016a6
parent 3e1a58787c7effe54ed6eda19a5b5acb2fb6fc94
Author: Alexandra Borovova <aborovova@mozilla.com>
Date: Fri, 17 Oct 2025 19:07:00 +0000
Bug 1994661 - Fix wpt-update for None values. r=jgraham
Differential Revision: https://phabricator.services.mozilla.com/D268842
Diffstat:
7 files changed, 86 insertions(+), 17 deletions(-)
diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/manifestupdate.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/manifestupdate.py
@@ -7,7 +7,7 @@ from math import ceil
from typing import Any, Callable, ClassVar, Dict, List, Optional
from .wptmanifest import serialize
-from .wptmanifest.node import (DataNode, ConditionalNode, BinaryExpressionNode,
+from .wptmanifest.node import (AtomExprNode, DataNode, ConditionalNode, BinaryExpressionNode,
BinaryOperatorNode, NumberNode, StringNode, VariableNode,
ValueNode, UnaryExpressionNode, UnaryOperatorNode,
ListNode)
@@ -919,6 +919,7 @@ def make_expr(prop_set, rhs):
UnaryOperatorNode("not"),
VariableNode(prop)
))
+
if len(expressions) > 1:
prev = expressions[-1]
for curr in reversed(expressions[:-1]):
@@ -938,7 +939,9 @@ def make_expr(prop_set, rhs):
def make_node(value):
- if isinstance(value, (int, float,)):
+ if value is None:
+ node = AtomExprNode(None)
+ elif isinstance(value, (int, float,)):
node = NumberNode(value)
elif isinstance(value, str):
node = StringNode(str(value))
diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/tests/test_manifestupdate.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/tests/test_manifestupdate.py
@@ -56,3 +56,38 @@ def test_unconditional_default_promotion():
TIMEOUT
""").encode())
assert manifest.node == wptmanifest.parse(contents_after)
+
+
+def test_none_value():
+ contents_before = io.BytesIO(
+ textwrap.dedent(
+ """\
+ [b.html]
+ """).encode())
+ manifest = manifestupdate.compile(
+ contents_before,
+ test_path='a/b.html',
+ url_base='/',
+ run_info_properties=(['display'], {}),
+ update_intermittent=True,
+ remove_intermittent=False)
+ test = manifest.get_test('/a/b.html')
+ test.set_result(
+ metadata.RunInfo({'display': "x11"}),
+ metadata.Result('PASS', [], 'PASS'))
+ test.set_result(
+ metadata.RunInfo({'display': "wayland"}),
+ metadata.Result('PASS', [], 'PASS'))
+ test.set_result(
+ metadata.RunInfo({'display': None}),
+ metadata.Result('FAIL', [], 'PASS'))
+ test.update(full_update=True, disable_intermittent=False)
+
+ contents_after = io.BytesIO(
+ textwrap.dedent(
+ """\
+ [b.html]
+ expected:
+ if display == @Null: FAIL
+ """).encode())
+ assert manifest.node == wptmanifest.parse(contents_after)
diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/wptmanifest/backends/base.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/wptmanifest/backends/base.py
@@ -42,7 +42,7 @@ class Compiler(NodeVisitor):
return self.data_cls_getter(None, None)(node, **kwargs)
def visit_DataNode(self, node):
- if node != self.tree:
+ if node is not self.tree:
output_parent = self.output_node
self.output_node = self.data_cls_getter(self.output_node, node)(node, **self._kwargs)
else:
@@ -103,6 +103,9 @@ class Compiler(NodeVisitor):
return data
return value
+ def visit_AtomExprNode(self, node):
+ return node.data
+
def visit_IndexNode(self, node):
assert len(node.children) == 1
return self.visit(node.children[0])
diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py
@@ -114,7 +114,7 @@ class Compiler(NodeVisitor):
return self.data_cls_getter(None, None)(node, **kwargs)
def visit_DataNode(self, node):
- if node != self.tree:
+ if node is not self.tree:
output_parent = self.output_node
self.output_node = self.data_cls_getter(self.output_node, node)(node)
else:
@@ -178,6 +178,9 @@ class Compiler(NodeVisitor):
return data
return value
+ def visit_AtomExprNode(self, node):
+ return lambda x: node.data
+
def visit_IndexNode(self, node):
assert len(node.children) == 1
return self.visit(node.children[0])
@@ -344,7 +347,7 @@ class ManifestItem:
def append(self, child):
self.children.append(child)
child.parent = self
- if child.node.parent != self.node:
+ if child.node.parent is not self.node:
self.node.append(child.node)
return child
diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/wptmanifest/node.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/wptmanifest/node.py
@@ -33,12 +33,12 @@ class Node:
return "\n".join(rv)
def __eq__(self, other):
- if not (self.__class__ == other.__class__ and
- self.data == other.data and
- len(self.children) == len(other.children)):
+ if (self.__class__ != other.__class__ or
+ self.data != other.data or
+ len(self.children) != len(other.children)):
return False
for child, other_child in zip(self.children, other.children):
- if not child == other_child:
+ if child != other_child:
return False
return True
@@ -171,3 +171,7 @@ class StringNode(Node):
class NumberNode(ValueNode):
pass
+
+
+class AtomExprNode(ValueNode):
+ pass
diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/wptmanifest/parser.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/wptmanifest/parser.py
@@ -15,7 +15,7 @@
from io import BytesIO
-from .node import (Node, AtomNode, BinaryExpressionNode, BinaryOperatorNode,
+from .node import (Node, AtomNode, AtomExprNode, BinaryExpressionNode, BinaryOperatorNode,
ConditionalNode, DataNode, IndexNode, KeyValueNode, ListNode,
NumberNode, StringNode, UnaryExpressionNode,
UnaryOperatorNode, ValueNode, VariableNode)
@@ -45,7 +45,8 @@ operators = ["==", "!=", "not", "and", "or"]
atoms = {"True": True,
"False": False,
- "Reset": object()}
+ "Reset": object(),
+ "Null": None}
def decode(s):
assert isinstance(s, str)
@@ -326,9 +327,7 @@ class Tokenizer:
yield (token_types.string, self.consume_string(quote_char))
self.state = self.line_end_state
elif c == "@":
- self.consume()
- for _, value in self.value_inner_state():
- yield token_types.atom, value
+ self.state = self.value_atom_state
elif c == "[":
self.state = self.list_start_state
else:
@@ -433,6 +432,8 @@ class Tokenizer:
self.state = self.operator_state
elif c in digits:
self.state = self.digit_state
+ elif c == "@":
+ self.state = self.expr_atom_state
else:
self.state = self.ident_state
@@ -499,6 +500,17 @@ class Tokenizer:
self.state = self.expr_state
yield (token_types.ident, self.line[index_0:self.index])
+ def value_atom_state(self):
+ self.consume()
+ _, value = next(self.ident_state())
+ self.state = self.line_end_state
+ yield (token_types.atom, value)
+
+ def expr_atom_state(self):
+ self.consume()
+ _, value = next(self.ident_state())
+ yield (token_types.atom, value)
+
def consume_escape(self):
assert self.char() == "\\"
self.consume()
@@ -755,7 +767,7 @@ class Parser:
elif self.token[0] == token_types.ident and self.token[1] in unary_operators:
self.expr_unary_op()
self.expr_operand()
- elif self.token[0] in [token_types.string, token_types.ident]:
+ elif self.token[0] in [token_types.string, token_types.ident, token_types.atom]:
self.expr_value()
elif self.token[0] == token_types.number:
self.expr_number()
@@ -778,8 +790,14 @@ class Parser:
def expr_value(self):
node_type = {token_types.string: StringNode,
- token_types.ident: VariableNode}[self.token[0]]
- self.expr_builder.push_operand(node_type(self.token[1]))
+ token_types.ident: VariableNode,
+ token_types.atom: AtomExprNode}[self.token[0]]
+ if self.token[0] == token_types.atom:
+ value = atoms[self.token[1]]
+ else:
+ value = self.token[1]
+
+ self.expr_builder.push_operand(node_type(value))
self.consume()
if self.token == (token_types.paren, "["):
self.consume()
diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/wptmanifest/serializer.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/wptmanifest/serializer.py
@@ -115,6 +115,9 @@ class ManifestSerializer(NodeVisitor):
def visit_NumberNode(self, node):
return [node.data]
+ def visit_AtomExprNode(self, node):
+ return [atom_names[node.data]]
+
def visit_VariableNode(self, node):
rv = escape(node.data)
for child in node.children: