tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mdom/base/fragmentdirectives/fragment_directive_impl.rs | 598-------------------------------------------------------------------------------
Mdom/base/fragmentdirectives/lib.rs | 1+
Adom/base/fragmentdirectives/test.rs | 601+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/library/rust/moz.build | 1-
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",