Browse Source

Added a bunch more tests (#2885)

* Added a bunch more tests

* Removed extra file

* Fix tests

* Increasing code coverage

* Fix clippy lint

* Removed ut_ prefix for tests
pull/2899/head
Iban Eguia Moraza 2 years ago committed by GitHub
parent
commit
7cb58dea97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      boa_engine/src/builtins/array_buffer/tests.rs
  2. 4
      boa_engine/src/builtins/regexp/tests.rs
  3. 2
      boa_engine/src/builtins/uri/mod.rs
  4. 2
      boa_engine/src/tests/control_flow/loops.rs
  5. 6
      boa_engine/src/tests/control_flow/mod.rs
  6. 2
      boa_engine/src/tests/function.rs
  7. 2
      boa_engine/src/tests/mod.rs
  8. 38
      boa_engine/src/tests/operators.rs
  9. 2
      boa_engine/src/value/conversions/serde_json.rs
  10. 35
      boa_parser/src/error/mod.rs
  11. 166
      boa_parser/src/error/tests.rs
  12. 53
      boa_parser/src/lexer/error.rs
  13. 2
      boa_parser/src/parser/expression/assignment/arrow_function.rs
  14. 2
      boa_parser/src/parser/expression/assignment/async_arrow_function.rs
  15. 55
      boa_parser/src/source.rs
  16. 3
      boa_unicode/src/lib.rs
  17. 146
      boa_unicode/src/tests.rs

13
boa_engine/src/builtins/array_buffer/tests.rs

@ -1,11 +1,8 @@
use super::*;
#[test]
fn ut_sunny_day_create_byte_data_block() {
assert!(create_byte_data_block(100).is_ok());
}
fn create_byte_data_block() {
// Sunny day
assert!(super::create_byte_data_block(100).is_ok());
#[test]
fn ut_rainy_day_create_byte_data_block() {
assert!(create_byte_data_block(u64::MAX).is_err());
// Rainy day
assert!(super::create_byte_data_block(u64::MAX).is_err());
}

4
boa_engine/src/builtins/regexp/tests.rs

@ -121,12 +121,12 @@ fn no_panic_on_parse_fail() {
TestAction::assert_native_error(
r"var re = /]/u;",
ErrorKind::Syntax,
"Invalid regular expression literal: Unbalanced bracket at position: 1:10",
"Invalid regular expression literal: Unbalanced bracket at line 1, col 10",
),
TestAction::assert_native_error(
r"var re = /a{/u;",
ErrorKind::Syntax,
"Invalid regular expression literal: Invalid quantifier at position: 1:10",
"Invalid regular expression literal: Invalid quantifier at line 1, col 10",
),
]);
}

2
boa_engine/src/builtins/uri/mod.rs

@ -528,7 +528,7 @@ mod tests {
/// Checks that the `decode_byte()` function works as expected.
#[test]
fn ut_decode_byte() {
fn decode_byte() {
// Sunny day tests
assert_eq!(
decode_hex_byte(u16::from(b'2'), u16::from(b'0')).unwrap(),

2
boa_engine/src/tests/control_flow/loops.rs

@ -139,7 +139,7 @@ fn test_invalid_break_target() {
}
"#},
ErrorKind::Syntax,
"undefined break target: nonexistent at position: 1:1",
"undefined break target: nonexistent at line 1, col 1",
)]);
}

6
boa_engine/src/tests/control_flow/mod.rs

@ -8,7 +8,7 @@ fn test_invalid_break() {
run_test_actions([TestAction::assert_native_error(
"break;",
ErrorKind::Syntax,
"illegal break statement at position: 1:1",
"illegal break statement at line 1, col 1",
)]);
}
@ -21,7 +21,7 @@ fn test_invalid_continue_target() {
}
"#},
ErrorKind::Syntax,
"undefined continue target: nonexistent at position: 1:1",
"undefined continue target: nonexistent at line 1, col 1",
)]);
}
@ -30,7 +30,7 @@ fn test_invalid_continue() {
run_test_actions([TestAction::assert_native_error(
"continue;",
ErrorKind::Syntax,
"illegal continue statement at position: 1:1",
"illegal continue statement at line 1, col 1",
)]);
}

2
boa_engine/src/tests/function.rs

@ -141,7 +141,7 @@ fn strict_mode_dup_func_parameters() {
function f(a, b, b) {}
"#},
ErrorKind::Syntax,
"Duplicate parameter name not allowed in this context at position: 2:12",
"Duplicate parameter name not allowed in this context at line 2, col 12",
)]);
}

2
boa_engine/src/tests/mod.rs

@ -387,7 +387,7 @@ fn strict_mode_octal() {
var n = 023;
"#},
ErrorKind::Syntax,
"implicit octal literals are not allowed in strict mode at position: 2:9",
"implicit octal literals are not allowed in strict mode at line 2, col 9",
)]);
}

38
boa_engine/src/tests/operators.rs

@ -144,22 +144,22 @@ fn invalid_unary_access() {
TestAction::assert_native_error(
"++[]",
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:1",
"Invalid left-hand side in assignment at line 1, col 1",
),
TestAction::assert_native_error(
"[]++",
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:3",
"Invalid left-hand side in assignment at line 1, col 3",
),
TestAction::assert_native_error(
"--[]",
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:1",
"Invalid left-hand side in assignment at line 1, col 1",
),
TestAction::assert_native_error(
"[]--",
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:3",
"Invalid left-hand side in assignment at line 1, col 3",
),
]);
}
@ -171,22 +171,22 @@ fn unary_operations_on_this() {
TestAction::assert_native_error(
"++this",
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:1",
"Invalid left-hand side in assignment at line 1, col 1",
),
TestAction::assert_native_error(
"--this",
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:1",
"Invalid left-hand side in assignment at line 1, col 1",
),
TestAction::assert_native_error(
"this++",
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:5",
"Invalid left-hand side in assignment at line 1, col 5",
),
TestAction::assert_native_error(
"this--",
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:5",
"Invalid left-hand side in assignment at line 1, col 5",
),
]);
}
@ -306,7 +306,7 @@ fn assignment_to_non_assignable() {
TestAction::assert_native_error(
src,
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:3",
"Invalid left-hand side in assignment at line 1, col 3",
)
}),
);
@ -331,7 +331,7 @@ fn assignment_to_non_assignable_ctd() {
TestAction::assert_native_error(
src,
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:13",
"Invalid left-hand side in assignment at line 1, col 13",
)
}),
);
@ -345,7 +345,7 @@ fn multicharacter_assignment_to_non_assignable() {
TestAction::assert_native_error(
src,
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:3",
"Invalid left-hand side in assignment at line 1, col 3",
)
}));
}
@ -359,7 +359,7 @@ fn multicharacter_assignment_to_non_assignable_ctd() {
TestAction::assert_native_error(
src,
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:13",
"Invalid left-hand side in assignment at line 1, col 13",
)
}),
);
@ -374,7 +374,7 @@ fn multicharacter_bitwise_assignment_to_non_assignable() {
TestAction::assert_native_error(
src,
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:3",
"Invalid left-hand side in assignment at line 1, col 3",
)
}),
);
@ -394,7 +394,7 @@ fn multicharacter_bitwise_assignment_to_non_assignable_ctd() {
TestAction::assert_native_error(
src,
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:13",
"Invalid left-hand side in assignment at line 1, col 13",
)
}),
);
@ -406,22 +406,22 @@ fn assign_to_array_decl() {
TestAction::assert_native_error(
"[1] = [2]",
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:5",
"Invalid left-hand side in assignment at line 1, col 5",
),
TestAction::assert_native_error(
"[3, 5] = [7, 8]",
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:8",
"Invalid left-hand side in assignment at line 1, col 8",
),
TestAction::assert_native_error(
"[6, 8] = [2]",
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:8",
"Invalid left-hand side in assignment at line 1, col 8",
),
TestAction::assert_native_error(
"[6] = [2, 9]",
ErrorKind::Syntax,
"Invalid left-hand side in assignment at position: 1:5",
"Invalid left-hand side in assignment at line 1, col 5",
),
]);
}
@ -505,7 +505,7 @@ fn delete_variable_in_strict() {
delete x;
"#},
ErrorKind::Syntax,
"cannot delete variables in strict mode at position: 3:1",
"cannot delete variables in strict mode at line 3, col 1",
)]);
}

2
boa_engine/src/value/conversions/serde_json.rs

@ -181,7 +181,7 @@ mod tests {
use crate::{string::utf16, JsValue};
#[test]
fn ut_json_conversions() {
fn json_conversions() {
const DATA: &str = indoc! {r#"
{
"name": "John Doe",

35
boa_parser/src/error.rs → boa_parser/src/error/mod.rs

@ -1,5 +1,8 @@
//! Error and result implementation for the parser.
#[cfg(test)]
mod tests;
use crate::lexer::Error as LexError;
use boa_ast::{Position, Span};
use std::fmt;
@ -8,12 +11,20 @@ use std::fmt;
pub type ParseResult<T> = Result<T, Error>;
pub(crate) trait ErrorContext {
fn context(self, context: &'static str) -> Self;
/// Sets the context of the error, if possible.
fn set_context(self, context: &'static str) -> Self;
/// Gets the context of the error, if any.
fn context(&self) -> Option<&'static str>;
}
impl<T> ErrorContext for ParseResult<T> {
fn context(self, context: &'static str) -> Self {
self.map_err(|e| e.context(context))
fn set_context(self, context: &'static str) -> Self {
self.map_err(|e| e.set_context(context))
}
fn context(&self) -> Option<&'static str> {
self.as_ref().err().and_then(Error::context)
}
}
@ -75,7 +86,7 @@ pub enum Error {
impl Error {
/// Changes the context of the error, if any.
fn context(self, new_context: &'static str) -> Self {
fn set_context(self, new_context: &'static str) -> Self {
match self {
Self::Expected {
expected,
@ -87,14 +98,26 @@ impl Error {
}
}
/// Gets the context of the error, if any.
const fn context(&self) -> Option<&'static str> {
if let Self::Expected { context, .. } = self {
Some(context)
} else {
None
}
}
/// Creates an `Expected` parsing error.
pub(crate) fn expected<E, F>(expected: E, found: F, span: Span, context: &'static str) -> Self
where
E: Into<Box<[String]>>,
F: Into<Box<str>>,
{
let expected = expected.into();
debug_assert_ne!(expected.len(), 0);
Self::Expected {
expected: expected.into(),
expected,
found: found.into(),
span,
context,
@ -209,7 +232,7 @@ impl fmt::Display for Error {
position.line_number(),
position.column_number()
),
Self::Lex { err } => write!(f, "{err}"),
Self::Lex { err } => err.fmt(f),
}
}
}

166
boa_parser/src/error/tests.rs

@ -0,0 +1,166 @@
use super::*;
#[test]
fn context() {
let result: ParseResult<String> = ParseResult::Err(Error::expected(
["testing".to_owned()],
"nottesting",
Span::new(Position::new(1, 1), Position::new(1, 1)),
"before",
));
assert_eq!(result.context(), Some("before"));
let result = result.set_context("after");
assert_eq!(result.context(), Some("after"));
let error = result.unwrap_err();
if let Error::Expected {
expected,
found,
span,
context,
} = error
{
assert_eq!(expected.as_ref(), &["testing".to_owned()]);
assert_eq!(found, "nottesting".into());
assert_eq!(span, Span::new(Position::new(1, 1), Position::new(1, 1)));
assert_eq!(context, "after");
} else {
unreachable!();
}
let err = Error::AbruptEnd;
assert!(err.context().is_none());
let err = err.set_context("ignored");
assert!(err.context().is_none());
}
#[test]
fn from_lex_error() {
let lex_err = LexError::syntax("testing", Position::new(1, 1));
let parse_err: Error = lex_err.into();
assert!(matches!(parse_err, Error::Lex { .. }));
let lex_err = LexError::syntax("testing", Position::new(1, 1));
let parse_err = Error::lex(lex_err);
assert!(matches!(parse_err, Error::Lex { .. }));
}
#[test]
fn misplaced_function_declaration() {
let err = Error::misplaced_function_declaration(Position::new(1, 1), false);
if let Error::General { message, position } = err {
assert_eq!(
message.as_ref(),
"functions can only be declared at the top level or inside a block."
);
assert_eq!(position, Position::new(1, 1));
} else {
unreachable!()
}
let err = Error::misplaced_function_declaration(Position::new(1, 1), true);
if let Error::General { message, position } = err {
assert_eq!(
message.as_ref(),
"in strict mode code, functions can only be declared at the top level or inside a block."
);
assert_eq!(position, Position::new(1, 1));
} else {
unreachable!()
}
}
#[test]
fn wrong_labelled_function_declaration() {
let err = Error::wrong_labelled_function_declaration(Position::new(1, 1));
if let Error::General { message, position } = err {
assert_eq!(
message.as_ref(),
"labelled functions can only be declared at the top level or inside a block"
);
assert_eq!(position, Position::new(1, 1));
} else {
unreachable!()
}
}
#[test]
fn display() {
let err = Error::expected(
["testing".to_owned()],
"nottesting",
Span::new(Position::new(1, 1), Position::new(1, 1)),
"context",
);
assert_eq!(
err.to_string(),
"expected token 'testing', got 'nottesting' in context at line 1, col 1"
);
let err = Error::expected(
["testing".to_owned(), "more".to_owned()],
"nottesting",
Span::new(Position::new(1, 1), Position::new(1, 3)),
"context",
);
assert_eq!(
err.to_string(),
"expected one of 'testing' or 'more', got 'nottesting' in context at line 1, col 1"
);
let err = Error::expected(
["testing".to_owned(), "more".to_owned(), "tokens".to_owned()],
"nottesting",
Span::new(Position::new(1, 1), Position::new(1, 3)),
"context",
);
assert_eq!(
err.to_string(),
"expected one of 'testing', 'more' or 'tokens', got 'nottesting' in context at line 1, col 1"
);
let err = Error::expected(
[
"testing".to_owned(),
"more".to_owned(),
"tokens".to_owned(),
"extra".to_owned(),
],
"nottesting",
Span::new(Position::new(1, 1), Position::new(1, 3)),
"context",
);
assert_eq!(
err.to_string(),
"expected one of 'testing', 'more', 'tokens' or 'extra', got 'nottesting' in context at line 1, col 1"
);
let err = Error::unexpected(
"nottesting",
Span::new(Position::new(1, 1), Position::new(1, 3)),
"error message",
);
assert_eq!(
err.to_string(),
"unexpected token 'nottesting', error message at line 1, col 1"
);
let err = Error::general("this is a general error message", Position::new(1, 1));
assert_eq!(
err.to_string(),
"this is a general error message at line 1, col 1"
);
let err = Error::AbruptEnd;
assert_eq!(err.to_string(), "abrupt end");
let lex_err = LexError::syntax("testing", Position::new(1, 1));
let err = Error::lex(lex_err);
assert_eq!(err.to_string(), "testing at line 1, col 1");
}

53
boa_parser/src/lexer/error.rs

@ -34,7 +34,7 @@ impl From<io::Error> for Error {
impl Error {
/// Creates a new syntax error.
#[inline]
pub(super) fn syntax<M, P>(err: M, pos: P) -> Self
pub(crate) fn syntax<M, P>(err: M, pos: P) -> Self
where
M: Into<Box<str>>,
P: Into<Position>,
@ -47,8 +47,13 @@ impl fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IO(e) => write!(f, "{e}"),
Self::Syntax(e, pos) => write!(f, "{e} at position: {pos}"),
Self::IO(e) => e.fmt(f),
Self::Syntax(e, pos) => write!(
f,
"{e} at line {}, col {}",
pos.line_number(),
pos.column_number()
),
}
}
}
@ -62,3 +67,45 @@ impl error::Error for Error {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::{error::Error as _, io};
#[test]
fn syntax() {
let err = Error::syntax("testing", Position::new(1, 1));
if let Error::Syntax(err, pos) = err {
assert_eq!(err.as_ref(), "testing");
assert_eq!(pos, Position::new(1, 1));
} else {
unreachable!()
}
let err = Error::syntax("testing", Position::new(1, 1));
assert_eq!(err.to_string(), "testing at line 1, col 1");
assert!(err.source().is_none());
}
#[test]
fn io() {
let custom_error = io::Error::new(io::ErrorKind::Other, "I/O error");
let err = custom_error.into();
if let Error::IO(err) = err {
assert_eq!(err.to_string(), "I/O error");
} else {
unreachable!()
}
let custom_error = io::Error::new(io::ErrorKind::Other, "I/O error");
let err: Error = custom_error.into();
assert_eq!(err.to_string(), "I/O error");
err.source().map_or_else(
|| unreachable!(),
|io_err| {
assert_eq!(io_err.to_string(), "I/O error");
},
);
}
}

2
boa_parser/src/parser/expression/assignment/arrow_function.rs

@ -98,7 +98,7 @@ where
let params_start_position = next_token.span().start();
let param = BindingIdentifier::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)
.context("arrow function")?;
.set_context("arrow function")?;
(
FormalParameterList::try_from(FormalParameter::new(
Variable::from_identifier(param, None),

2
boa_parser/src/parser/expression/assignment/async_arrow_function.rs

@ -92,7 +92,7 @@ where
let params_start_position = next_token.span().start();
let param = BindingIdentifier::new(self.allow_yield, true)
.parse(cursor, interner)
.context("async arrow function")?;
.set_context("async arrow function")?;
(
FormalParameterList::try_from(FormalParameter::new(
Variable::from_identifier(param, None),

55
boa_parser/src/source.rs

@ -86,3 +86,58 @@ impl<'path, R: Read> Source<'path, R> {
Self { reader, path }
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::{fs, io::Cursor};
#[test]
fn from_bytes() {
let mut source = Source::from_bytes("'Hello' + 'World';");
assert!(source.path.is_none());
let mut content = String::new();
source.reader.read_to_string(&mut content).unwrap();
assert_eq!(content, "'Hello' + 'World';");
}
#[test]
fn from_filepath() {
fs::write("test.js", "'Hello' + 'World';").unwrap();
let mut source = Source::from_filepath("test.js".as_ref()).unwrap();
assert_eq!(source.path, Some("test.js".as_ref()));
let mut content = String::new();
source.reader.read_to_string(&mut content).unwrap();
assert_eq!(content, "'Hello' + 'World';");
}
#[test]
fn from_reader() {
// Without path
let mut source = Source::from_reader(Cursor::new("'Hello' + 'World';"), None);
assert!(source.path.is_none());
let mut content = String::new();
source.reader.read_to_string(&mut content).unwrap();
assert_eq!(content, "'Hello' + 'World';");
// With path
let mut source =
Source::from_reader(Cursor::new("'Hello' + 'World';"), Some("test.js".as_ref()));
assert_eq!(source.path, Some("test.js".as_ref()));
let mut content = String::new();
source.reader.read_to_string(&mut content).unwrap();
assert_eq!(content, "'Hello' + 'World';");
}
}

3
boa_unicode/src/lib.rs

@ -168,14 +168,17 @@ impl UnicodeProperties for char {
fn is_other_id_start(self) -> bool {
table_binary_search(self, tables::OTHER_ID_START)
}
#[inline]
fn is_other_id_continue(self) -> bool {
table_binary_search(self, tables::OTHER_ID_CONTINUE)
}
#[inline]
fn is_pattern_syntax(self) -> bool {
table_binary_search(self, tables::PATTERN_SYNTAX)
}
#[inline]
fn is_pattern_whitespace(self) -> bool {
table_binary_search(self, tables::PATTERN_WHITE_SPACE)

146
boa_unicode/src/tests.rs

@ -1,7 +1,145 @@
#![allow(clippy::cognitive_complexity)]
use super::*;
#[test]
fn check_unicode_version() {
assert_eq!(
super::UNICODE_VERSION,
unicode_general_category::UNICODE_VERSION
);
assert_eq!(UNICODE_VERSION, unicode_general_category::UNICODE_VERSION);
}
#[test]
fn is_id_start() {
// Sunny day
for c in 'a'..='z' {
assert!(c.is_id_start());
}
for c in 'A'..='Z' {
assert!(c.is_id_start());
}
for c in '\u{00E0}'..='\u{00F6}' {
assert!(c.is_id_start());
}
for c in '\u{02B0}'..='\u{02C1}' {
assert!(c.is_id_start());
}
for c in '\u{05D0}'..='\u{05DE}' {
assert!(c.is_id_start());
}
for c in '\u{1F88}'..='\u{1F89}' {
assert!(c.is_id_start());
}
for c in '\u{0391}'..='\u{039F}' {
assert!(c.is_id_start());
}
for c in '\u{2160}'..='\u{216F}' {
assert!(c.is_id_start());
}
// Rainy day
for c in '0'..='9' {
assert!(!c.is_id_start());
}
assert!(!' '.is_id_start());
assert!(!'\n'.is_id_start());
assert!(!'\t'.is_id_start());
assert!(!'!'.is_id_start());
assert!(!';'.is_id_start());
assert!(!'-'.is_id_start());
assert!(!'_'.is_id_start());
assert!(!'='.is_id_start());
assert!(!'+'.is_id_start());
assert!(!'('.is_id_start());
assert!(!')'.is_id_start());
}
#[test]
fn is_id_continue() {
// Sunny day
for c in 'a'..='z' {
assert!(c.is_id_continue());
}
for c in 'A'..='Z' {
assert!(c.is_id_continue());
}
for c in '0'..='9' {
assert!(c.is_id_continue());
}
for c in '\u{0300}'..='\u{036F}' {
assert!(c.is_id_continue());
}
for c in '\u{093E}'..='\u{094F}' {
assert!(c.is_id_continue());
}
for c in '\u{0660}'..='\u{0669}' {
assert!(c.is_id_continue());
}
for c in ['_', '\u{203F}', '\u{2040}', '\u{2054}', '\u{FE33}'] {
assert!(c.is_id_continue());
}
// Rainy day
for c in [' ', '\n', '\t', '!', ';', '-', '=', '+', '(', '('] {
assert!(!c.is_id_continue());
}
}
#[test]
fn is_orther_id_start() {
// Sunny day
for c in tables::OTHER_ID_START {
assert!(c.is_other_id_start());
}
// Rainy day
for c in [' ', '\n', '='] {
assert!(!c.is_other_id_start());
}
}
#[test]
fn is_orther_id_continue() {
// Sunny day
for c in tables::OTHER_ID_CONTINUE {
assert!(c.is_other_id_continue());
}
// Rainy day
for c in [' ', '\n', '='] {
assert!(!c.is_other_id_continue());
}
}
#[test]
fn is_pattern_syntax() {
// Sunny day
for c in tables::PATTERN_SYNTAX {
assert!(c.is_pattern_syntax());
}
// Rainy day
for c in [' ', '\t', '\n', '\r'] {
assert!(!c.is_pattern_syntax());
}
}
#[test]
fn is_pattern_whitespace() {
// Sunny day
for c in tables::PATTERN_WHITE_SPACE {
assert!(c.is_pattern_whitespace());
}
// Rainy day
for c in ['+', '~', '`', '!', '@', '^', '='] {
assert!(!c.is_pattern_whitespace());
}
for c in '0'..='9' {
assert!(!c.is_pattern_whitespace());
}
for c in 'a'..='z' {
assert!(!c.is_pattern_whitespace());
}
for c in 'A'..='Z' {
assert!(!c.is_pattern_whitespace());
}
}

Loading…
Cancel
Save