diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py
index 269b6868e29..3ff7e89a759 100644
--- a/src/etc/htmldocck.py
+++ b/src/etc/htmldocck.py
@@ -94,6 +94,10 @@ There are a number of supported commands:
in the specified file. The number of occurrences must match the given
count.
+* `@count PATH XPATH TEXT COUNT` checks for the occurrence of the given XPath
+ with the given text in the specified file. The number of occurrences must
+ match the given count.
+
* `@snapshot NAME PATH XPATH` creates a snapshot test named NAME.
A snapshot test captures a subtree of the DOM, at the location
determined by the XPath, and compares it to a pre-recorded value
@@ -382,9 +386,10 @@ def check_tree_attr(tree, path, attr, pat, regexp):
return ret
-def check_tree_text(tree, path, pat, regexp):
+# Returns the number of occurences matching the regex (`regexp`) and the text (`pat`).
+def check_tree_text(tree, path, pat, regexp, stop_at_first):
path = normalize_xpath(path)
- ret = False
+ match_count = 0
try:
for e in tree.findall(path):
try:
@@ -392,13 +397,14 @@ def check_tree_text(tree, path, pat, regexp):
except KeyError:
continue
else:
- ret = check_string(value, pat, regexp)
- if ret:
- break
+ if check_string(value, pat, regexp):
+ match_count += 1
+ if stop_at_first:
+ break
except Exception:
print('Failed to get path "{}"'.format(path))
raise
- return ret
+ return match_count
def get_tree_count(tree, path):
@@ -516,6 +522,19 @@ def print_err(lineno, context, err, message=None):
stderr("\t{}".format(context))
+def get_nb_matching_elements(cache, c, regexp, stop_at_first):
+ tree = cache.get_tree(c.args[0])
+ pat, sep, attr = c.args[1].partition('/@')
+ if sep: # attribute
+ tree = cache.get_tree(c.args[0])
+ return check_tree_attr(tree, pat, attr, c.args[2], False)
+ else: # normalized text
+ pat = c.args[1]
+ if pat.endswith('/text()'):
+ pat = pat[:-7]
+ return check_tree_text(cache.get_tree(c.args[0]), pat, c.args[2], regexp, stop_at_first)
+
+
ERR_COUNT = 0
@@ -536,16 +555,7 @@ def check_command(c, cache):
ret = check_string(cache.get_file(c.args[0]), c.args[1], regexp)
elif len(c.args) == 3: # @has/matches = XML tree test
cerr = "`XPATH PATTERN` did not match"
- tree = cache.get_tree(c.args[0])
- pat, sep, attr = c.args[1].partition('/@')
- if sep: # attribute
- tree = cache.get_tree(c.args[0])
- ret = check_tree_attr(tree, pat, attr, c.args[2], regexp)
- else: # normalized text
- pat = c.args[1]
- if pat.endswith('/text()'):
- pat = pat[:-7]
- ret = check_tree_text(cache.get_tree(c.args[0]), pat, c.args[2], regexp)
+ ret = get_nb_matching_elements(cache, c, regexp, True) != 0
else:
raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
@@ -555,6 +565,11 @@ def check_command(c, cache):
found = get_tree_count(cache.get_tree(c.args[0]), c.args[1])
cerr = "Expected {} occurrences but found {}".format(expected, found)
ret = expected == found
+ elif len(c.args) == 4: # @count = count test
+ expected = int(c.args[3])
+ found = get_nb_matching_elements(cache, c, False, False)
+ cerr = "Expected {} occurrences but found {}".format(expected, found)
+ ret = found == expected
else:
raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))