commit e0caacd89193d0512f44e292a6889f8baf2a05d2
parent c51d10b724e24c02088756c57c60805e38c11147
Author: Alexandru Marc <amarc@mozilla.com>
Date: Tue, 21 Oct 2025 11:26:58 +0300
Revert "Bug 1995438 - Move rust tests for fragment directive parser into implementation and enable them for `./mach rusttests`. r=emilio" for causing rust build bustages
This reverts commit fe7c15268a75eb424e4be0bc47420a3da4d0c83f.
Diffstat:
4 files changed, 602 insertions(+), 599 deletions(-)
diff --git a/dom/base/fragmentdirectives/fragment_directive_impl.rs b/dom/base/fragmentdirectives/fragment_directive_impl.rs
@@ -335,601 +335,3 @@ pub fn create_text_directive_string(text_directive: &TextDirective) -> Option<St
None
}
}
-
-#[cfg(test)]
-mod tests {
- use super::{
- create_fragment_directive_string, parse_fragment_directive_and_remove_it_from_hash,
- TextDirective,
- };
-
- /// This test verifies that valid combinations of [prefix-,]start[,end][,-suffix] are parsed correctly.
- #[test]
- fn test_parse_fragment_directive_with_one_text_directive() {
- // U+2705 WHITE HEAVY CHECK MARK - UTF-8 percent encoding: %E2%9C%85
- let checkmark = String::from_utf8(vec![0xE2, 0x9C, 0x85]).unwrap();
- let test_cases = vec![
- (":~:text=start", (None, Some("start"), None, None)),
- (
- ":~:text=start,end",
- (None, Some("start"), Some("end"), None),
- ),
- (
- ":~:text=prefix-,start",
- (Some("prefix"), Some("start"), None, None),
- ),
- (
- ":~:text=prefix-,start,end",
- (Some("prefix"), Some("start"), Some("end"), None),
- ),
- (
- ":~:text=prefix-,start,end,-suffix",
- (Some("prefix"), Some("start"), Some("end"), Some("suffix")),
- ),
- (
- ":~:text=start,-suffix",
- (None, Some("start"), None, Some("suffix")),
- ),
- (
- ":~:text=start,end,-suffix",
- (None, Some("start"), Some("end"), Some("suffix")),
- ),
- (":~:text=text=", (None, Some("text="), None, None)),
- (":~:text=%25", (None, Some("%"), None, None)),
- (":~:text=%", (None, Some("%"), None, None)),
- (":~:text=%%", (None, Some("%%"), None, None)),
- (":~:text=%25%25F", (None, Some("%%F"), None, None)),
- (
- ":~:text=%E2%9C%85",
- (None, Some(checkmark.as_str()), None, None),
- ),
- (":~:text=#", (None, Some("#"), None, None)),
- (":~:text=:", (None, Some(":"), None, None)),
- (
- ":~:text=prefix--,start",
- (Some("prefix-"), Some("start"), None, None),
- ),
- (
- ":~:text=p-refix-,start",
- (Some("p-refix"), Some("start"), None, None),
- ),
- ];
- for (url, (prefix, start, end, suffix)) in test_cases {
- let (stripped_url, fragment_directive, result) =
- parse_fragment_directive_and_remove_it_from_hash(&url)
- .expect("The parser must find a result.");
- assert_eq!(
- fragment_directive,
- &url[3..],
- "The extracted fragment directive string
- should be unsanitized and therefore match the input string."
- );
- assert_eq!(result.len(), 1, "There must be one parsed text fragment.");
- assert_eq!(
- stripped_url, "",
- "The fragment directive must be removed from the url hash."
- );
- let text_directive = result.first().unwrap();
- if prefix.is_none() {
- assert!(
- text_directive.prefix().is_none(),
- "There must be no `prefix` token (test case `{}`).",
- url
- );
- } else {
- assert!(
- text_directive
- .prefix()
- .as_ref()
- .expect("There must be a `prefix` token.")
- .value()
- == prefix.unwrap(),
- "Wrong value for `prefix` (test case `{}`).",
- url
- );
- }
- if start.is_none() {
- assert!(
- text_directive.start().is_none(),
- "There must be no `start` token (test case `{}`).",
- url
- );
- } else {
- assert!(
- text_directive
- .start()
- .as_ref()
- .expect("There must be a `start` token.")
- .value()
- == start.unwrap(),
- "Wrong value for `start` (test case `{}`).",
- url
- );
- }
- if end.is_none() {
- assert!(
- text_directive.end().is_none(),
- "There must be no `end` token (test case `{}`).",
- url
- );
- } else {
- assert!(
- text_directive
- .end()
- .as_ref()
- .expect("There must be a `end` token.")
- .value()
- == end.unwrap(),
- "Wrong value for `end` (test case `{}`).",
- url
- );
- }
- if suffix.is_none() {
- assert!(
- text_directive.suffix().is_none(),
- "There must be no `suffix` token (test case `{}`).",
- url
- );
- } else {
- assert!(
- text_directive
- .suffix()
- .as_ref()
- .expect("There must be a `suffix` token.")
- .value()
- == suffix.unwrap(),
- "Wrong value for `suffix` (test case `{}`).",
- url
- );
- }
- }
- }
-
- /// This test verifies that a text fragment is parsed correctly if it is preceded
- /// or followed by a fragment (i.e. `#foo:~:text=bar`).
- #[test]
- fn test_parse_text_fragment_after_fragments() {
- let url = "foo:~:text=start";
- let (stripped_url, fragment_directive, result) =
- parse_fragment_directive_and_remove_it_from_hash(&url)
- .expect("The parser must find a result.");
- assert_eq!(
- result.len(),
- 1,
- "There must be exactly one parsed text fragment."
- );
- assert_eq!(
- stripped_url, "foo",
- "The fragment directive was not removed correctly."
- );
- assert_eq!(
- fragment_directive, "text=start",
- "The fragment directive was not extracted correctly."
- );
- let fragment = result.first().unwrap();
- assert!(fragment.prefix().is_none(), "There is no `prefix` token.");
- assert_eq!(
- fragment
- .start()
- .as_ref()
- .expect("There must be a `start` token.")
- .value(),
- "start"
- );
- assert!(fragment.end().is_none(), "There is no `end` token.");
- assert!(fragment.suffix().is_none(), "There is no `suffix` token.");
- }
-
- /// Ensure that multiple text fragments are parsed correctly.
- #[test]
- fn test_parse_multiple_text_fragments() {
- let url = ":~:text=prefix-,start,-suffix&text=foo&text=bar,-suffix";
- let (_, _, text_directives) = parse_fragment_directive_and_remove_it_from_hash(&url)
- .expect("The parser must find a result.");
- assert_eq!(
- text_directives.len(),
- 3,
- "There must be exactly two parsed text fragments."
- );
- let first_text_directive = &text_directives[0];
- assert_eq!(
- first_text_directive
- .prefix()
- .as_ref()
- .expect("There must be a `prefix` token.")
- .value(),
- "prefix"
- );
- assert_eq!(
- first_text_directive
- .start()
- .as_ref()
- .expect("There must be a `start` token.")
- .value(),
- "start"
- );
- assert!(
- first_text_directive.end().is_none(),
- "There is no `end` token."
- );
- assert_eq!(
- first_text_directive
- .suffix()
- .as_ref()
- .expect("There must be a `suffix` token.")
- .value(),
- "suffix"
- );
-
- let second_text_directive = &text_directives[1];
- assert!(
- second_text_directive.prefix().is_none(),
- "There is no `prefix` token."
- );
- assert_eq!(
- second_text_directive
- .start()
- .as_ref()
- .expect("There must be a `start` token.")
- .value(),
- "foo"
- );
- assert!(
- second_text_directive.end().is_none(),
- "There is no `end` token."
- );
- assert!(
- second_text_directive.suffix().is_none(),
- "There is no `suffix` token."
- );
- let third_text_directive = &text_directives[2];
- assert!(
- third_text_directive.prefix().is_none(),
- "There is no `prefix` token."
- );
- assert_eq!(
- third_text_directive
- .start()
- .as_ref()
- .expect("There must be a `start` token.")
- .value(),
- "bar"
- );
- assert!(
- third_text_directive.end().is_none(),
- "There is no `end` token."
- );
- assert_eq!(
- third_text_directive
- .suffix()
- .as_ref()
- .expect("There must be a `suffix` token.")
- .value(),
- "suffix"
- );
- }
-
- /// Multiple text directives should be parsed correctly
- /// if they are surrounded or separated by unknown directives.
- #[test]
- fn test_parse_multiple_text_directives_with_unknown_directive_in_between() {
- for url in [
- ":~:foo&text=start1&text=start2",
- ":~:text=start1&foo&text=start2",
- ":~:text=start1&text=start2&foo",
- ] {
- let (_, fragment_directive, text_directives) =
- parse_fragment_directive_and_remove_it_from_hash(&url)
- .expect("The parser must find a result.");
- assert_eq!(
- fragment_directive,
- &url[3..],
- "The extracted fragment directive string is unsanitized
- and should contain the unknown directive."
- );
- assert_eq!(
- text_directives.len(),
- 2,
- "There must be exactly two parsed text fragments."
- );
- let first_text_directive = &text_directives[0];
- assert_eq!(
- first_text_directive
- .start()
- .as_ref()
- .expect("There must be a `start` token.")
- .value(),
- "start1"
- );
- let second_text_directive = &text_directives[1];
- assert_eq!(
- second_text_directive
- .start()
- .as_ref()
- .expect("There must be a `start` token.")
- .value(),
- "start2"
- );
- }
- }
-
- /// Ensures that input that doesn't contain a text fragment does not produce a result.
- /// This includes the use of partial identifying tokens necessary for a text fragment
- /// (e.g. `:~:` without `text=`, `text=foo` without the `:~:` or multiple occurrences of `:~:`)
- /// In these cases, the parser must return `None` to indicate that there are no valid text fragments.
- #[test]
- fn test_parse_invalid_or_unknown_fragment_directive() {
- // there is no fragment directive here, hence the original url should not be updated.
- for url in ["foo", "foo:", "text=prefix-,start"] {
- let text_directives = parse_fragment_directive_and_remove_it_from_hash(&url);
- assert!(
- text_directives.is_none(),
- "The fragment `{}` does not contain a valid or known fragment directive.",
- url
- );
- }
- // there is an (invalid) fragment directive present. It needs to be removed from the url.
- for (url, url_without_fragment_directive_ref) in [
- ("foo:~:", "foo"),
- ("foo:~:bar", "foo"),
- (":~:text=foo-,bar,-baz:~:text=foo", ""),
- ] {
- let (url_without_fragment_directive, _, _) =
- parse_fragment_directive_and_remove_it_from_hash(&url)
- .expect("There is a fragment directive which should have been removed.");
- assert_eq!(
- url_without_fragment_directive, url_without_fragment_directive_ref,
- "The fragment directive has not been removed correctly from fragment `{}`.",
- url
- );
- }
- }
-
- /// Ensures that ill-formed text directives (but valid fragment directives)
- /// (starting correctly with `:~:text=`) are not parsed.
- /// Instead `None` must be returned.
- /// Test cases include invalid combinations of `prefix`/`suffix`es,
- /// additional `,`s, too many `start`/`end` tokens, or empty text fragments.
- #[test]
- fn test_parse_invalid_text_fragments() {
- for url in [
- ":~:text=start,start,start",
- ":~:text=prefix-,prefix-",
- ":~:text=prefix-,-suffix",
- ":~:text=prefix-,start,start,start",
- ":~:text=prefix-,start,start,start,-suffix",
- ":~:text=start,start,start,-suffix",
- ":~:text=prefix-,start,end,-suffix,foo",
- ":~:text=foo,prefix-,start",
- ":~:text=prefix-,,start,",
- ":~:text=,prefix,start",
- ":~:text=",
- ":~:text=&",
- ":~:text=,",
- ] {
- let (url_without_fragment_directive, _, _) =
- parse_fragment_directive_and_remove_it_from_hash(&url).expect("");
- assert!(
- url_without_fragment_directive.is_empty(),
- "The fragment directive `{}` does not contain a valid fragment directive. \
- It must be removed from the original url anyway.",
- url
- );
- }
- }
-
- /// Ensure that out of multiple text fragments only the invalid ones are ignored
- /// while valid text fragments are still returned.
- /// Since correct parsing of multiple text fragments as well as
- /// several forms of invalid text fragments are already tested in
- /// `test_parse_multiple_text_fragments` and `test_parse_invalid_text_fragments()`,
- /// it should be enough to test this with only one fragment directive
- /// that contains two text fragments, one of them being invalid.
- #[test]
- fn test_valid_and_invalid_text_directives() {
- for url in [":~:text=start&text=,foo,", ":~:text=foo,foo,foo&text=start"] {
- let (_, fragment_directive, text_directives) =
- parse_fragment_directive_and_remove_it_from_hash(&url)
- .expect("The parser must find a result.");
- assert_eq!(
- fragment_directive,
- &url[3..],
- "The extracted fragment directive string is unsanitized
- and should contain invalid text directives."
- );
- assert_eq!(
- text_directives.len(),
- 1,
- "There must be exactly one parsed text fragment."
- );
- let text_directive = text_directives.first().unwrap();
- assert_eq!(
- text_directive
- .start()
- .as_ref()
- .expect("There must be a `start` value.")
- .value(),
- "start",
- "The `start` value of the text directive has the wrong value."
- );
- }
- }
-
- /// Ensures that a fragment directive that contains percent-encoded characters
- /// is decoded correctly. This explicitly includes characters which are used
- /// for identifying text fragments, i.e. `#`, `, `, `&`, `:`, `~` and `-`.
- #[test]
- fn test_parse_percent_encoding_tokens() {
- let url = ":~:text=prefix%26-,start%20and%2C,end%23,-%26suffix%2D";
- let (_, fragment_directive, text_directives) =
- parse_fragment_directive_and_remove_it_from_hash(&url)
- .expect("The parser must find a result.");
- assert_eq!(
- fragment_directive,
- &url[3..],
- "The extracted fragment directive string is unsanitized
- and should contain the original and percent-decoded string."
- );
- let text_directive = text_directives.first().unwrap();
- assert_eq!(
- text_directive
- .prefix()
- .as_ref()
- .expect("There must be a prefix.")
- .value(),
- "prefix&",
- ""
- );
- assert_eq!(
- text_directive
- .start()
- .as_ref()
- .expect("There must be a prefix.")
- .value(),
- "start and,",
- ""
- );
- assert_eq!(
- text_directive
- .end()
- .as_ref()
- .expect("There must be a prefix.")
- .value(),
- "end#",
- ""
- );
- assert_eq!(
- text_directive
- .suffix()
- .as_ref()
- .expect("There must be a prefix.")
- .value(),
- "&suffix-",
- ""
- );
- }
-
- /// Ensures that a text fragment is created correctly,
- /// based on a given combination of tokens.
- /// This includes all sorts of combinations of
- /// `prefix`, `suffix`, `start` and `end`,
- /// als well as values for these tokens which contain
- /// characters that need to be encoded because they are
- /// identifiers for text fragments
- /// (#`, `, `, `&`, `:`, `~` and `-`).
- #[test]
- fn test_create_fragment_directive() {
- for (text_directive, expected_fragment_directive) in [
- (
- TextDirective::from_parts(
- String::new(),
- String::from("start"),
- String::new(),
- String::new(),
- )
- .unwrap(),
- ":~:text=start",
- ),
- (
- TextDirective::from_parts(
- String::new(),
- String::from("start"),
- String::from("end"),
- String::new(),
- )
- .unwrap(),
- ":~:text=start,end",
- ),
- (
- TextDirective::from_parts(
- String::from("prefix"),
- String::from("start"),
- String::from("end"),
- String::new(),
- )
- .unwrap(),
- ":~:text=prefix-,start,end",
- ),
- (
- TextDirective::from_parts(
- String::from("prefix"),
- String::from("start"),
- String::from("end"),
- String::from("suffix"),
- )
- .unwrap(),
- ":~:text=prefix-,start,end,-suffix",
- ),
- (
- TextDirective::from_parts(
- String::new(),
- String::from("start"),
- String::from("end"),
- String::from("suffix"),
- )
- .unwrap(),
- ":~:text=start,end,-suffix",
- ),
- (
- TextDirective::from_parts(
- String::from("prefix"),
- String::from("start"),
- String::new(),
- String::from("suffix"),
- )
- .unwrap(),
- ":~:text=prefix-,start,-suffix",
- ),
- (
- TextDirective::from_parts(
- String::from("prefix-"),
- String::from("start and,"),
- String::from("&end"),
- String::from("#:~:suffix"),
- )
- .unwrap(),
- ":~:text=prefix%2D-,start%20and%2C,%26end,-%23%3A%7E%3Asuffix",
- ),
- ] {
- let fragment_directive = create_fragment_directive_string(&vec![text_directive])
- .expect("The given input must produce a valid fragment directive.");
- assert_eq!(fragment_directive, expected_fragment_directive);
- }
- }
-
- /// Ensures that a fragment directive is created correctly if multiple text fragments are given.
- /// The resulting fragment must start with `:~:`
- /// and each text fragment must be separated using `&text=`.
- #[test]
- fn test_create_fragment_directive_from_multiple_text_directives() {
- let text_directives = vec![
- TextDirective::from_parts(
- String::new(),
- String::from("start1"),
- String::new(),
- String::new(),
- )
- .unwrap(),
- TextDirective::from_parts(
- String::new(),
- String::from("start2"),
- String::new(),
- String::new(),
- )
- .unwrap(),
- TextDirective::from_parts(
- String::new(),
- String::from("start3"),
- String::new(),
- String::new(),
- )
- .unwrap(),
- ];
- let fragment_directive = create_fragment_directive_string(&text_directives)
- .expect("The given input must produce a valid fragment directive.");
- assert_eq!(
- fragment_directive, ":~:text=start1&text=start2&text=start3",
- "The created fragment directive is wrong for multiple fragments."
- );
- }
-}
diff --git a/dom/base/fragmentdirectives/lib.rs b/dom/base/fragmentdirectives/lib.rs
@@ -5,6 +5,7 @@
use nsstring::{nsCString, nsString};
use thin_vec::ThinVec;
pub mod fragment_directive_impl;
+mod test;
/// This struct contains the percent-decoded parts of a text directive.
/// All parts besides `start` are optional (which is indicated by an empty string).
diff --git a/dom/base/fragmentdirectives/test.rs b/dom/base/fragmentdirectives/test.rs
@@ -0,0 +1,601 @@
+/* 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/. */
+
+#[cfg(test)]
+mod test {
+ use crate::fragment_directive_impl::{
+ create_fragment_directive_string, parse_fragment_directive_and_remove_it_from_hash,
+ TextDirective,
+ };
+
+ /// This test verifies that valid combinations of [prefix-,]start[,end][,-suffix] are parsed correctly.
+ #[test]
+ fn test_parse_fragment_directive_with_one_text_directive() {
+ // U+2705 WHITE HEAVY CHECK MARK - UTF-8 percent encoding: %E2%9C%85
+ let checkmark = String::from_utf8(vec![0xE2, 0x9C, 0x85]).unwrap();
+ let test_cases = vec![
+ (":~:text=start", (None, Some("start"), None, None)),
+ (
+ ":~:text=start,end",
+ (None, Some("start"), Some("end"), None),
+ ),
+ (
+ ":~:text=prefix-,start",
+ (Some("prefix"), Some("start"), None, None),
+ ),
+ (
+ ":~:text=prefix-,start,end",
+ (Some("prefix"), Some("start"), Some("end"), None),
+ ),
+ (
+ ":~:text=prefix-,start,end,-suffix",
+ (Some("prefix"), Some("start"), Some("end"), Some("suffix")),
+ ),
+ (
+ ":~:text=start,-suffix",
+ (None, Some("start"), None, Some("suffix")),
+ ),
+ (
+ ":~:text=start,end,-suffix",
+ (None, Some("start"), Some("end"), Some("suffix")),
+ ),
+ (":~:text=text=", (None, Some("text="), None, None)),
+ (":~:text=%25", (None, Some("%"), None, None)),
+ (":~:text=%", (None, Some("%"), None, None)),
+ (":~:text=%%", (None, Some("%%"), None, None)),
+ (":~:text=%25%25F", (None, Some("%%F"), None, None)),
+ (
+ ":~:text=%E2%9C%85",
+ (None, Some(checkmark.as_str()), None, None),
+ ),
+ (":~:text=#", (None, Some("#"), None, None)),
+ (":~:text=:", (None, Some(":"), None, None)),
+ (
+ ":~:text=prefix--,start",
+ (Some("prefix-"), Some("start"), None, None),
+ ),
+ (
+ ":~:text=p-refix-,start",
+ (Some("p-refix"), Some("start"), None, None),
+ ),
+ ];
+ for (url, (prefix, start, end, suffix)) in test_cases {
+ let (stripped_url, fragment_directive, result) =
+ parse_fragment_directive_and_remove_it_from_hash(&url)
+ .expect("The parser must find a result.");
+ assert_eq!(
+ fragment_directive,
+ &url[3..],
+ "The extracted fragment directive string
+ should be unsanitized and therefore match the input string."
+ );
+ assert_eq!(result.len(), 1, "There must be one parsed text fragment.");
+ assert_eq!(
+ stripped_url, "",
+ "The fragment directive must be removed from the url hash."
+ );
+ let text_directive = result.first().unwrap();
+ if prefix.is_none() {
+ assert!(
+ text_directive.prefix().is_none(),
+ "There must be no `prefix` token (test case `{}`).",
+ url
+ );
+ } else {
+ assert!(
+ text_directive
+ .prefix()
+ .as_ref()
+ .expect("There must be a `prefix` token.")
+ .value()
+ == prefix.unwrap(),
+ "Wrong value for `prefix` (test case `{}`).",
+ url
+ );
+ }
+ if start.is_none() {
+ assert!(
+ text_directive.start().is_none(),
+ "There must be no `start` token (test case `{}`).",
+ url
+ );
+ } else {
+ assert!(
+ text_directive
+ .start()
+ .as_ref()
+ .expect("There must be a `start` token.")
+ .value()
+ == start.unwrap(),
+ "Wrong value for `start` (test case `{}`).",
+ url
+ );
+ }
+ if end.is_none() {
+ assert!(
+ text_directive.end().is_none(),
+ "There must be no `end` token (test case `{}`).",
+ url
+ );
+ } else {
+ assert!(
+ text_directive
+ .end()
+ .as_ref()
+ .expect("There must be a `end` token.")
+ .value()
+ == end.unwrap(),
+ "Wrong value for `end` (test case `{}`).",
+ url
+ );
+ }
+ if suffix.is_none() {
+ assert!(
+ text_directive.suffix().is_none(),
+ "There must be no `suffix` token (test case `{}`).",
+ url
+ );
+ } else {
+ assert!(
+ text_directive
+ .suffix()
+ .as_ref()
+ .expect("There must be a `suffix` token.")
+ .value()
+ == suffix.unwrap(),
+ "Wrong value for `suffix` (test case `{}`).",
+ url
+ );
+ }
+ }
+ }
+
+ /// This test verifies that a text fragment is parsed correctly if it is preceded
+ /// or followed by a fragment (i.e. `#foo:~:text=bar`).
+ #[test]
+ fn test_parse_text_fragment_after_fragments() {
+ let url = "foo:~:text=start";
+ let (stripped_url, fragment_directive, result) =
+ parse_fragment_directive_and_remove_it_from_hash(&url)
+ .expect("The parser must find a result.");
+ assert_eq!(
+ result.len(),
+ 1,
+ "There must be exactly one parsed text fragment."
+ );
+ assert_eq!(
+ stripped_url, "foo",
+ "The fragment directive was not removed correctly."
+ );
+ assert_eq!(
+ fragment_directive, "text=start",
+ "The fragment directive was not extracted correctly."
+ );
+ let fragment = result.first().unwrap();
+ assert!(fragment.prefix().is_none(), "There is no `prefix` token.");
+ assert_eq!(
+ fragment
+ .start()
+ .as_ref()
+ .expect("There must be a `start` token.")
+ .value(),
+ "start"
+ );
+ assert!(fragment.end().is_none(), "There is no `end` token.");
+ assert!(fragment.suffix().is_none(), "There is no `suffix` token.");
+ }
+
+ /// Ensure that multiple text fragments are parsed correctly.
+ #[test]
+ fn test_parse_multiple_text_fragments() {
+ let url = ":~:text=prefix-,start,-suffix&text=foo&text=bar,-suffix";
+ let (_, _, text_directives) = parse_fragment_directive_and_remove_it_from_hash(&url)
+ .expect("The parser must find a result.");
+ assert_eq!(
+ text_directives.len(),
+ 3,
+ "There must be exactly two parsed text fragments."
+ );
+ let first_text_directive = &text_directives[0];
+ assert_eq!(
+ first_text_directive
+ .prefix()
+ .as_ref()
+ .expect("There must be a `prefix` token.")
+ .value(),
+ "prefix"
+ );
+ assert_eq!(
+ first_text_directive
+ .start()
+ .as_ref()
+ .expect("There must be a `start` token.")
+ .value(),
+ "start"
+ );
+ assert!(
+ first_text_directive.end().is_none(),
+ "There is no `end` token."
+ );
+ assert_eq!(
+ first_text_directive
+ .suffix()
+ .as_ref()
+ .expect("There must be a `suffix` token.")
+ .value(),
+ "suffix"
+ );
+
+ let second_text_directive = &text_directives[1];
+ assert!(
+ second_text_directive.prefix().is_none(),
+ "There is no `prefix` token."
+ );
+ assert_eq!(
+ second_text_directive
+ .start()
+ .as_ref()
+ .expect("There must be a `start` token.")
+ .value(),
+ "foo"
+ );
+ assert!(
+ second_text_directive.end().is_none(),
+ "There is no `end` token."
+ );
+ assert!(
+ second_text_directive.suffix().is_none(),
+ "There is no `suffix` token."
+ );
+ let third_text_directive = &text_directives[2];
+ assert!(
+ third_text_directive.prefix().is_none(),
+ "There is no `prefix` token."
+ );
+ assert_eq!(
+ third_text_directive
+ .start()
+ .as_ref()
+ .expect("There must be a `start` token.")
+ .value(),
+ "bar"
+ );
+ assert!(
+ third_text_directive.end().is_none(),
+ "There is no `end` token."
+ );
+ assert_eq!(
+ third_text_directive
+ .suffix()
+ .as_ref()
+ .expect("There must be a `suffix` token.")
+ .value(),
+ "suffix"
+ );
+ }
+
+ /// Multiple text directives should be parsed correctly
+ /// if they are surrounded or separated by unknown directives.
+ #[test]
+ fn test_parse_multiple_text_directives_with_unknown_directive_in_between() {
+ for url in [
+ ":~:foo&text=start1&text=start2",
+ ":~:text=start1&foo&text=start2",
+ ":~:text=start1&text=start2&foo",
+ ] {
+ let (_, fragment_directive, text_directives) =
+ parse_fragment_directive_and_remove_it_from_hash(&url)
+ .expect("The parser must find a result.");
+ assert_eq!(
+ fragment_directive,
+ &url[3..],
+ "The extracted fragment directive string is unsanitized
+ and should contain the unknown directive."
+ );
+ assert_eq!(
+ text_directives.len(),
+ 2,
+ "There must be exactly two parsed text fragments."
+ );
+ let first_text_directive = &text_directives[0];
+ assert_eq!(
+ first_text_directive
+ .start()
+ .as_ref()
+ .expect("There must be a `start` token.")
+ .value(),
+ "start1"
+ );
+ let second_text_directive = &text_directives[1];
+ assert_eq!(
+ second_text_directive
+ .start()
+ .as_ref()
+ .expect("There must be a `start` token.")
+ .value(),
+ "start2"
+ );
+ }
+ }
+
+ /// Ensures that input that doesn't contain a text fragment does not produce a result.
+ /// This includes the use of partial identifying tokens necessary for a text fragment
+ /// (e.g. `:~:` without `text=`, `text=foo` without the `:~:` or multiple occurrences of `:~:`)
+ /// In these cases, the parser must return `None` to indicate that there are no valid text fragments.
+ #[test]
+ fn test_parse_invalid_or_unknown_fragment_directive() {
+ // there is no fragment directive here, hence the original url should not be updated.
+ for url in ["foo", "foo:", "text=prefix-,start"] {
+ let text_directives = parse_fragment_directive_and_remove_it_from_hash(&url);
+ assert!(
+ text_directives.is_none(),
+ "The fragment `{}` does not contain a valid or known fragment directive.",
+ url
+ );
+ }
+ // there is an (invalid) fragment directive present. It needs to be removed from the url.
+ for (url, url_without_fragment_directive_ref) in [
+ ("foo:~:", "foo"),
+ ("foo:~:bar", "foo"),
+ (":~:text=foo-,bar,-baz:~:text=foo", ""),
+ ] {
+ let (url_without_fragment_directive, _, _) =
+ parse_fragment_directive_and_remove_it_from_hash(&url)
+ .expect("There is a fragment directive which should have been removed.");
+ assert_eq!(
+ url_without_fragment_directive, url_without_fragment_directive_ref,
+ "The fragment directive has not been removed correctly from fragment `{}`.",
+ url
+ );
+ }
+ }
+
+ /// Ensures that ill-formed text directives (but valid fragment directives)
+ /// (starting correctly with `:~:text=`) are not parsed.
+ /// Instead `None` must be returned.
+ /// Test cases include invalid combinations of `prefix`/`suffix`es,
+ /// additional `,`s, too many `start`/`end` tokens, or empty text fragments.
+ #[test]
+ fn test_parse_invalid_text_fragments() {
+ for url in [
+ ":~:text=start,start,start",
+ ":~:text=prefix-,prefix-",
+ ":~:text=prefix-,-suffix",
+ ":~:text=prefix-,start,start,start",
+ ":~:text=prefix-,start,start,start,-suffix",
+ ":~:text=start,start,start,-suffix",
+ ":~:text=prefix-,start,end,-suffix,foo",
+ ":~:text=foo,prefix-,start",
+ ":~:text=prefix-,,start,",
+ ":~:text=,prefix,start",
+ ":~:text=",
+ ":~:text=&",
+ ":~:text=,",
+ ] {
+ let (url_without_fragment_directive, _, _) =
+ parse_fragment_directive_and_remove_it_from_hash(&url).expect("");
+ assert!(
+ url_without_fragment_directive.is_empty(),
+ "The fragment directive `{}` does not contain a valid fragment directive. \
+ It must be removed from the original url anyway.",
+ url
+ );
+ }
+ }
+
+ /// Ensure that out of multiple text fragments only the invalid ones are ignored
+ /// while valid text fragments are still returned.
+ /// Since correct parsing of multiple text fragments as well as
+ /// several forms of invalid text fragments are already tested in
+ /// `test_parse_multiple_text_fragments` and `test_parse_invalid_text_fragments()`,
+ /// it should be enough to test this with only one fragment directive
+ /// that contains two text fragments, one of them being invalid.
+ #[test]
+ fn test_valid_and_invalid_text_directives() {
+ for url in [":~:text=start&text=,foo,", ":~:text=foo,foo,foo&text=start"] {
+ let (_, fragment_directive, text_directives) =
+ parse_fragment_directive_and_remove_it_from_hash(&url)
+ .expect("The parser must find a result.");
+ assert_eq!(
+ fragment_directive,
+ &url[3..],
+ "The extracted fragment directive string is unsanitized
+ and should contain invalid text directives."
+ );
+ assert_eq!(
+ text_directives.len(),
+ 1,
+ "There must be exactly one parsed text fragment."
+ );
+ let text_directive = text_directives.first().unwrap();
+ assert_eq!(
+ text_directive
+ .start()
+ .as_ref()
+ .expect("There must be a `start` value.")
+ .value(),
+ "start",
+ "The `start` value of the text directive has the wrong value."
+ );
+ }
+ }
+
+ /// Ensures that a fragment directive that contains percent-encoded characters
+ /// is decoded correctly. This explicitly includes characters which are used
+ /// for identifying text fragments, i.e. `#`, `, `, `&`, `:`, `~` and `-`.
+ #[test]
+ fn test_parse_percent_encoding_tokens() {
+ let url = ":~:text=prefix%26-,start%20and%2C,end%23,-%26suffix%2D";
+ let (_, fragment_directive, text_directives) =
+ parse_fragment_directive_and_remove_it_from_hash(&url)
+ .expect("The parser must find a result.");
+ assert_eq!(
+ fragment_directive,
+ &url[3..],
+ "The extracted fragment directive string is unsanitized
+ and should contain the original and percent-decoded string."
+ );
+ let text_directive = text_directives.first().unwrap();
+ assert_eq!(
+ text_directive
+ .prefix()
+ .as_ref()
+ .expect("There must be a prefix.")
+ .value(),
+ "prefix&",
+ ""
+ );
+ assert_eq!(
+ text_directive
+ .start()
+ .as_ref()
+ .expect("There must be a prefix.")
+ .value(),
+ "start and,",
+ ""
+ );
+ assert_eq!(
+ text_directive
+ .end()
+ .as_ref()
+ .expect("There must be a prefix.")
+ .value(),
+ "end#",
+ ""
+ );
+ assert_eq!(
+ text_directive
+ .suffix()
+ .as_ref()
+ .expect("There must be a prefix.")
+ .value(),
+ "&suffix-",
+ ""
+ );
+ }
+
+ /// Ensures that a text fragment is created correctly,
+ /// based on a given combination of tokens.
+ /// This includes all sorts of combinations of
+ /// `prefix`, `suffix`, `start` and `end`,
+ /// als well as values for these tokens which contain
+ /// characters that need to be encoded because they are
+ /// identifiers for text fragments
+ /// (#`, `, `, `&`, `:`, `~` and `-`).
+ #[test]
+ fn test_create_fragment_directive() {
+ for (text_directive, expected_fragment_directive) in [
+ (
+ TextDirective::from_parts(
+ String::new(),
+ String::from("start"),
+ String::new(),
+ String::new(),
+ )
+ .unwrap(),
+ ":~:text=start",
+ ),
+ (
+ TextDirective::from_parts(
+ String::new(),
+ String::from("start"),
+ String::from("end"),
+ String::new(),
+ )
+ .unwrap(),
+ ":~:text=start,end",
+ ),
+ (
+ TextDirective::from_parts(
+ String::from("prefix"),
+ String::from("start"),
+ String::from("end"),
+ String::new(),
+ )
+ .unwrap(),
+ ":~:text=prefix-,start,end",
+ ),
+ (
+ TextDirective::from_parts(
+ String::from("prefix"),
+ String::from("start"),
+ String::from("end"),
+ String::from("suffix"),
+ )
+ .unwrap(),
+ ":~:text=prefix-,start,end,-suffix",
+ ),
+ (
+ TextDirective::from_parts(
+ String::new(),
+ String::from("start"),
+ String::from("end"),
+ String::from("suffix"),
+ )
+ .unwrap(),
+ ":~:text=start,end,-suffix",
+ ),
+ (
+ TextDirective::from_parts(
+ String::from("prefix"),
+ String::from("start"),
+ String::new(),
+ String::from("suffix"),
+ )
+ .unwrap(),
+ ":~:text=prefix-,start,-suffix",
+ ),
+ (
+ TextDirective::from_parts(
+ String::from("prefix-"),
+ String::from("start and,"),
+ String::from("&end"),
+ String::from("#:~:suffix"),
+ )
+ .unwrap(),
+ ":~:text=prefix%2D-,start%20and%2C,%26end,-%23%3A%7E%3Asuffix",
+ ),
+ ] {
+ let fragment_directive = create_fragment_directive_string(&vec![text_directive])
+ .expect("The given input must produce a valid fragment directive.");
+ assert_eq!(fragment_directive, expected_fragment_directive);
+ }
+ }
+
+ /// Ensures that a fragment directive is created correctly if multiple text fragments are given.
+ /// The resulting fragment must start with `:~:`
+ /// and each text fragment must be separated using `&text=`.
+ #[test]
+ fn test_create_fragment_directive_from_multiple_text_directives() {
+ let text_directives = vec![
+ TextDirective::from_parts(
+ String::new(),
+ String::from("start1"),
+ String::new(),
+ String::new(),
+ )
+ .unwrap(),
+ TextDirective::from_parts(
+ String::new(),
+ String::from("start2"),
+ String::new(),
+ String::new(),
+ )
+ .unwrap(),
+ TextDirective::from_parts(
+ String::new(),
+ String::from("start3"),
+ String::new(),
+ String::new(),
+ )
+ .unwrap(),
+ ];
+ let fragment_directive = create_fragment_directive_string(&text_directives)
+ .expect("The given input must produce a valid fragment directive.");
+ assert_eq!(
+ fragment_directive, ":~:text=start1&text=start2&text=start3",
+ "The created fragment directive is wrong for multiple fragments."
+ );
+ }
+}
diff --git a/toolkit/library/rust/moz.build b/toolkit/library/rust/moz.build
@@ -22,7 +22,6 @@ for feature in gkrust_features:
# compilation artifacts with gkrust.
RUST_TESTS = [
"crashreporter",
- "dom_fragmentdirectives",
"firefox-on-glean",
"l10nregistry",
"rsclientcerts-util",