diff --git a/boa_engine/src/builtins/array_buffer/tests.rs b/boa_engine/src/builtins/array_buffer/tests.rs index ae4bcd7da71..c8996399309 100644 --- a/boa_engine/src/builtins/array_buffer/tests.rs +++ b/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()); } diff --git a/boa_engine/src/builtins/regexp/tests.rs b/boa_engine/src/builtins/regexp/tests.rs index 4aab948a055..b5c59c00262 100644 --- a/boa_engine/src/builtins/regexp/tests.rs +++ b/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", ), ]); } diff --git a/boa_engine/src/builtins/uri/mod.rs b/boa_engine/src/builtins/uri/mod.rs index 20ff40701e0..d059fa98468 100644 --- a/boa_engine/src/builtins/uri/mod.rs +++ b/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(), diff --git a/boa_engine/src/tests/control_flow/loops.rs b/boa_engine/src/tests/control_flow/loops.rs index 61ee02b6c3c..1a91fd6f788 100644 --- a/boa_engine/src/tests/control_flow/loops.rs +++ b/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", )]); } diff --git a/boa_engine/src/tests/control_flow/mod.rs b/boa_engine/src/tests/control_flow/mod.rs index 70ea43e7f98..738f7bb0d2f 100644 --- a/boa_engine/src/tests/control_flow/mod.rs +++ b/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", )]); } diff --git a/boa_engine/src/tests/function.rs b/boa_engine/src/tests/function.rs index 8e4e7ce8438..8fab9cbb664 100644 --- a/boa_engine/src/tests/function.rs +++ b/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", )]); } diff --git a/boa_engine/src/tests/mod.rs b/boa_engine/src/tests/mod.rs index 34930868bb4..c59dabec59e 100644 --- a/boa_engine/src/tests/mod.rs +++ b/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", )]); } diff --git a/boa_engine/src/tests/operators.rs b/boa_engine/src/tests/operators.rs index 0ed66599788..5c89157d6a6 100644 --- a/boa_engine/src/tests/operators.rs +++ b/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", )]); } diff --git a/boa_engine/src/value/conversions/serde_json.rs b/boa_engine/src/value/conversions/serde_json.rs index b4131ee1141..c33c3f2734d 100644 --- a/boa_engine/src/value/conversions/serde_json.rs +++ b/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", diff --git a/boa_parser/src/error.rs b/boa_parser/src/error/mod.rs similarity index 87% rename from boa_parser/src/error.rs rename to boa_parser/src/error/mod.rs index ffa4c25f41b..9f9077877f6 100644 --- a/boa_parser/src/error.rs +++ b/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 = Result; 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 ErrorContext for ParseResult { - 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(expected: E, found: F, span: Span, context: &'static str) -> Self where E: Into>, F: Into>, { + 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), } } } diff --git a/boa_parser/src/error/tests.rs b/boa_parser/src/error/tests.rs new file mode 100644 index 00000000000..93596068836 --- /dev/null +++ b/boa_parser/src/error/tests.rs @@ -0,0 +1,166 @@ +use super::*; + +#[test] +fn context() { + let result: ParseResult = 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"); +} diff --git a/boa_parser/src/lexer/error.rs b/boa_parser/src/lexer/error.rs index aebcde5f2fe..242dfcc38aa 100644 --- a/boa_parser/src/lexer/error.rs +++ b/boa_parser/src/lexer/error.rs @@ -34,7 +34,7 @@ impl From for Error { impl Error { /// Creates a new syntax error. #[inline] - pub(super) fn syntax(err: M, pos: P) -> Self + pub(crate) fn syntax(err: M, pos: P) -> Self where M: Into>, P: Into, @@ -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"); + }, + ); + } +} diff --git a/boa_parser/src/parser/expression/assignment/arrow_function.rs b/boa_parser/src/parser/expression/assignment/arrow_function.rs index 12ab91bcacf..45b929a747a 100644 --- a/boa_parser/src/parser/expression/assignment/arrow_function.rs +++ b/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), diff --git a/boa_parser/src/parser/expression/assignment/async_arrow_function.rs b/boa_parser/src/parser/expression/assignment/async_arrow_function.rs index c3120bf7a93..8f48c029d80 100644 --- a/boa_parser/src/parser/expression/assignment/async_arrow_function.rs +++ b/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), diff --git a/boa_parser/src/source.rs b/boa_parser/src/source.rs index 539c8c8c02f..af60e9467ed 100644 --- a/boa_parser/src/source.rs +++ b/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';"); + } +} diff --git a/boa_unicode/src/lib.rs b/boa_unicode/src/lib.rs index 9d90dda9e58..1359003cc62 100644 --- a/boa_unicode/src/lib.rs +++ b/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) diff --git a/boa_unicode/src/tests.rs b/boa_unicode/src/tests.rs index b433c4c6d20..fd868005418 100644 --- a/boa_unicode/src/tests.rs +++ b/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()); + } }