mod common;
use crate::common::{check, should_error, MAX_DEPTH};
use serde_luaq::{lua_value, LuaTableEntry, LuaValue};
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
wasm_bindgen_test_configure!(run_in_browser);
type Result<T = ()> = std::result::Result<T, Box<dyn std::error::Error>>;
#[test]
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
fn basics() {
check(b"\"\"", LuaValue::String(b"".into()));
check(b"''", LuaValue::String(b"".into()));
check(b"\"hello world\"", LuaValue::String(b"hello world".into()));
check(b"'hello world'", LuaValue::String(b"hello world".into()));
should_error(b"\"hello");
should_error(b"'hello");
should_error(b"\"hello\\z \n\n");
should_error(b"'hello\\z \n\n");
}
#[test]
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
fn long_string() -> Result {
check(b"[[]]", LuaValue::String(b"".into()));
check(b"[=[]=]", LuaValue::String(b"".into()));
check(b"[==[]==]", LuaValue::String(b"".into()));
check(b"[===[]===]", LuaValue::String(b"".into()));
check(b"[====[]====]", LuaValue::String(b"".into()));
check(b"[=====[]=====]", LuaValue::String(b"".into()));
check(b"[[hello world]]", LuaValue::String(b"hello world".into()));
check(
b"[=[hello world]=]",
LuaValue::String(b"hello world".into()),
);
check(
b"[==[hello world]==]",
LuaValue::String(b"hello world".into()),
);
check(
b"[===[hello world]===]",
LuaValue::String(b"hello world".into()),
);
check(
b"[====[hello world]====]",
LuaValue::String(b"hello world".into()),
);
check(
b"[=====[hello world]=====]",
LuaValue::String(b"hello world".into()),
);
check(
b"[=[hello \n\n world]=]",
LuaValue::String(b"hello \n\n world".into()),
);
check(
b"[=[hello \r\r world]=]",
LuaValue::String(b"hello \r\r world".into()),
);
check(
b"[=[hello \r\n world]=]",
LuaValue::String(b"hello \r\n world".into()),
);
check(
b"[=[hello \n\r world]=]",
LuaValue::String(b"hello \n\r world".into()),
);
check(
br"[[\0\001\n\r\023\t\u{65e5}]]",
LuaValue::String(br"\0\001\n\r\023\t\u{65e5}".into()),
);
check(
br"[==[\0\001\n\r\023\t\u{65e5}]==]",
LuaValue::String(br"\0\001\n\r\023\t\u{65e5}".into()),
);
for c in ('\0'..='\\').chain('^'..='\u{ff}') {
let expected = format!("\\{c}");
let i1 = format!("[[{expected}]]");
let i2 = format!("[=[{expected}]=]");
check(i1.as_bytes(), LuaValue::String(expected.as_bytes().into()));
check(i2.as_bytes(), LuaValue::String(expected.as_bytes().into()));
}
check(
b"[=[hell[==[o]==] world]=]",
LuaValue::String(b"hell[==[o]==] world".into()),
);
check(
b"[=[hell[==[o[==[ world]=]",
LuaValue::String(b"hell[==[o[==[ world".into()),
);
check(
b"[=[hell[[o]] world]=]",
LuaValue::String(b"hell[[o]] world".into()),
);
check(
b"[[hell[=[o]=] world]]",
LuaValue::String(b"hell[=[o]=] world".into()),
);
check(
b"[[hell[=[o[==[ world]]",
LuaValue::String(b"hell[=[o[==[ world".into()),
);
check(
b"[[hell\"o\" w'o'rld]]",
LuaValue::String(b"hell\"o\" w'o'rld".into()),
);
check(
b"\"hell[[o]] w[=[o]=]rld\"",
LuaValue::String(b"hell[[o]] w[=[o]=]rld".into()),
);
check(
b"{[[hello]],[=[world]=],'!',\"?\"}",
LuaValue::Table(vec![
LuaTableEntry::Value(Box::new(LuaValue::String(b"hello".into()))),
LuaTableEntry::Value(Box::new(LuaValue::String(b"world".into()))),
LuaTableEntry::Value(Box::new(LuaValue::String(b"!".into()))),
LuaTableEntry::Value(Box::new(LuaValue::String(b"?".into()))),
]),
);
const A2048: [u8; 2048] = [b'a'; 2048];
let mut b = Vec::with_capacity(A2048.len() + 4);
b.extend_from_slice(b"[[");
b.extend_from_slice(&A2048);
b.extend_from_slice(b"]]");
check(&b, LuaValue::String((&A2048).into()));
check(b"[==[]=]==]", LuaValue::String(b"]=".into()));
check(
b"[==[[===[[=[]]=][====[]]===]===]==]",
LuaValue::String(b"[===[[=[]]=][====[]]===]===".into()),
);
check(
b"[====[[===[[=[]]=][====[]]===]===]====]",
LuaValue::String(b"[===[[=[]]=][====[]]===]===".into()),
);
check(b"[=[]]]]]]]]]=]", LuaValue::String(b"]]]]]]]]".into()));
Ok(())
}
#[test]
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
fn newlines() {
should_error(b"'\nfoo'");
should_error(b"'\rfoo'");
should_error(b"'\r\nfoo'");
should_error(b"'\n\rfoo'");
should_error(b"\"\nfoo\"");
should_error(b"\"\rfoo\"");
should_error(b"\"\r\nfoo\"");
should_error(b"\"\n\rfoo\"");
check(
b"\"hello\\\nworld\"",
LuaValue::String(b"hello\nworld".into()),
);
check(
b"\"hello\\\r\nworld\"",
LuaValue::String(b"hello\r\nworld".into()),
);
check(
b"\"hello\\\rworld\"",
LuaValue::String(b"hello\rworld".into()),
);
check(
b"\"hello\\\n\rworld\"",
LuaValue::String(b"hello\n\rworld".into()),
);
let expected = LuaValue::String(b"hello".into());
check(b"[[\nhello]]", &expected);
check(b"[[\rhello]]", &expected);
check(b"[[\r\nhello]]", &expected);
check(b"[[\n\rhello]]", &expected);
check(b"[=[\nhello]=]", &expected);
check(b"[=[\rhello]=]", &expected);
check(b"[=[\r\nhello]=]", &expected);
check(b"[=[\n\rhello]=]", &expected);
let expected = LuaValue::String(b" hello".into());
check(b"[[\n hello]]", &expected);
check(b"[[\r hello]]", &expected);
check(b"[[\r\n hello]]", &expected);
check(b"[[\n\r hello]]", &expected);
check(b"[=[\n hello]=]", &expected);
check(b"[=[\r hello]=]", &expected);
check(b"[=[\r\n hello]=]", &expected);
check(b"[=[\n\r hello]=]", &expected);
check(b"[[\n\nhello]]", LuaValue::String(b"\nhello".into()));
check(b"[[\r\rhello]]", LuaValue::String(b"\rhello".into()));
check(b"[[\r\n\r\nhello]]", LuaValue::String(b"\r\nhello".into()));
check(b"[[\n\r\n\rhello]]", LuaValue::String(b"\n\rhello".into()));
check(b"[=[\n\nhello]=]", LuaValue::String(b"\nhello".into()));
check(b"[=[\r\rhello]=]", LuaValue::String(b"\rhello".into()));
check(
b"[=[\r\n\r\nhello]=]",
LuaValue::String(b"\r\nhello".into()),
);
check(
b"[=[\n\r\n\rhello]=]",
LuaValue::String(b"\n\rhello".into()),
);
check(b"[[\n\nhello\n]]", LuaValue::String(b"\nhello\n".into()));
check(b"[[\r\rhello\r]]", LuaValue::String(b"\rhello\r".into()));
check(
b"[[\r\n\r\nhello\r\n]]",
LuaValue::String(b"\r\nhello\r\n".into()),
);
check(
b"[[\n\r\n\rhello\n\r]]",
LuaValue::String(b"\n\rhello\n\r".into()),
);
check(b"[=[\n\nhello\n]=]", LuaValue::String(b"\nhello\n".into()));
check(b"[=[\r\rhello\r]=]", LuaValue::String(b"\rhello\r".into()));
check(
b"[=[\r\n\r\nhello\r\n]=]",
LuaValue::String(b"\r\nhello\r\n".into()),
);
check(
b"[=[\n\r\n\rhello\n\r]=]",
LuaValue::String(b"\n\rhello\n\r".into()),
);
}
#[test]
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
fn escapes() {
check(
r#""\"Ãlo\"\n\\""#.as_bytes(),
LuaValue::String("\"Ãlo\"\n\\".as_bytes().into()),
);
check(b"'\\09912'", LuaValue::String(b"c12".into()));
check(b"'\\99ab'", LuaValue::String(b"cab".into()));
check(b"'\\099\\n'", LuaValue::String(b"c\n".into()));
check(
br"'\0\5\16\31\60\255\232'",
LuaValue::String(b"\x00\x05\x10\x1f\x3C\xFF\xE8".into()),
);
check(b"\"\0\"", LuaValue::String(b"\0".into()));
check(b"\"\xFEedMe\"", LuaValue::String(b"\xFEedMe".into()));
check(
b"\"\0\x01\0023\x05\0009\"",
LuaValue::String(b"\0\x01\0023\x05\0009".into()),
);
check(
b"\"\\0\\1\\02\\0023\\5\\0009\"",
LuaValue::String(b"\0\x01\x02\x023\x05\09".into()),
);
check(
b"\"\0\\rx\\r\\n\xFE\\0\\00\\000\\x00\\10\\010\\255\\xFF\\u{1f4a9}\"",
LuaValue::String(b"\0\rx\r\n\xFE\0\0\0\0\x0A\x0A\xFF\xFF\xf0\x9f\x92\xa9".into()),
);
}
#[test]
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
fn z_escapes() {
check(b"\"\\z\"", LuaValue::String(b"".into()));
check(br"'\z'", LuaValue::String(b"".into()));
check(br"'\z '", LuaValue::String(b"".into()));
check(b"\"\\z \n\t\x0c\x0b\n\"", LuaValue::String(b"".into()));
check(b"\"abc\\z \n efg\"", LuaValue::String(b"abcefg".into()));
check(b"\"abc\\zefg\"", LuaValue::String(b"abcefg".into()));
check(b"\"abc\\z \n\n\n\"", LuaValue::String(b"abc".into()));
check(br"'\z \n '", LuaValue::String(b"\n ".into()));
}
#[test]
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
fn single_char_escapes() {
check(br"'\a'", LuaValue::String(b"\x07".into()));
check(b"\"\\a\"", LuaValue::String(b"\x07".into()));
check(br"'\b'", LuaValue::String(b"\x08".into()));
check(b"\"\\b\"", LuaValue::String(b"\x08".into()));
check(br"'\f'", LuaValue::String(b"\x0c".into()));
check(b"\"\\f\"", LuaValue::String(b"\x0c".into()));
check(br"'\n'", LuaValue::String(b"\n".into()));
check(b"\"\\n\"", LuaValue::String(b"\n".into()));
check(br"'\r'", LuaValue::String(b"\r".into()));
check(b"\"\\r\"", LuaValue::String(b"\r".into()));
check(br"'\t'", LuaValue::String(b"\t".into()));
check(b"\"\\t\"", LuaValue::String(b"\t".into()));
check(br"'\v'", LuaValue::String(b"\x0B".into()));
check(b"\"\\v\"", LuaValue::String(b"\x0B".into()));
check(br"'\\'", LuaValue::String(b"\\".into()));
check(b"\"\\\\\"", LuaValue::String(b"\\".into()));
check(br"'\''", LuaValue::String(b"'".into()));
check(b"\"\\'\"", LuaValue::String(b"'".into()));
check(b"'\\\"'", LuaValue::String(b"\"".into()));
check(b"\"\\\"\"", LuaValue::String(b"\"".into()));
check(b"\"'\"", LuaValue::String(b"'".into()));
check(b"'\"'", LuaValue::String(b"\"".into()));
}
#[test]
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
fn newline_escapes() {
check(
b"'hello\\\nworld'",
LuaValue::String(b"hello\nworld".into()),
);
check(
b"\"hello\\\nworld\"",
LuaValue::String(b"hello\nworld".into()),
);
check(
b"'hello\\\rworld'",
LuaValue::String(b"hello\rworld".into()),
);
check(
b"\"hello\\\rworld\"",
LuaValue::String(b"hello\rworld".into()),
);
check(
b"'hello\\\n\rworld'",
LuaValue::String(b"hello\n\rworld".into()),
);
check(
b"\"hello\\\n\rworld\"",
LuaValue::String(b"hello\n\rworld".into()),
);
check(
b"'hello\\\r\nworld'",
LuaValue::String(b"hello\r\nworld".into()),
);
check(
b"\"hello\\\r\nworld\"",
LuaValue::String(b"hello\r\nworld".into()),
);
check(
b"'hello\\\n world'",
LuaValue::String(b"hello\n world".into()),
);
check(
b"\"hello\\\n world\"",
LuaValue::String(b"hello\n world".into()),
);
check(
b"'hello\\\r world'",
LuaValue::String(b"hello\r world".into()),
);
check(
b"\"hello\\\r world\"",
LuaValue::String(b"hello\r world".into()),
);
check(
b"'hello\\\n\r world'",
LuaValue::String(b"hello\n\r world".into()),
);
check(
b"\"hello\\\n\r world\"",
LuaValue::String(b"hello\n\r world".into()),
);
check(
b"'hello\\\r\n world'",
LuaValue::String(b"hello\r\n world".into()),
);
check(
b"\"hello\\\r\n world\"",
LuaValue::String(b"hello\r\n world".into()),
);
}
#[test]
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
fn decimal_x_escapes() {
for c in 0u8..=255 {
let expected = &[c][..];
let expected_a = &[c, b'a'][..];
check(
format!("'\\{c}'").as_bytes(),
LuaValue::String(expected.into()),
);
check(
format!("\"\\{c}\"").as_bytes(),
LuaValue::String(expected.into()),
);
check(
format!("'\\{c}a'").as_bytes(),
LuaValue::String(expected_a.into()),
);
check(
format!("\"\\{c}a\"").as_bytes(),
LuaValue::String(expected_a.into()),
);
check(
format!("'\\x{c:02x}'").as_bytes(),
LuaValue::String(expected.into()),
);
check(
format!("\"\\x{c:02x}\"").as_bytes(),
LuaValue::String(expected.into()),
);
check(
format!("'\\x{c:02X}'").as_bytes(),
LuaValue::String(expected.into()),
);
check(
format!("\"\\x{c:02X}\"").as_bytes(),
LuaValue::String(expected.into()),
);
check(
format!("'\\x{c:02x}a'").as_bytes(),
LuaValue::String(expected_a.into()),
);
check(
format!("\"\\x{c:02x}a\"").as_bytes(),
LuaValue::String(expected_a.into()),
);
check(
format!("'\\x{c:02X}a'").as_bytes(),
LuaValue::String(expected_a.into()),
);
check(
format!("\"\\x{c:02X}a\"").as_bytes(),
LuaValue::String(expected_a.into()),
);
if c < 100 {
check(
format!("'\\0{c}'").as_bytes(),
LuaValue::String(expected.into()),
);
check(
format!("\"\\0{c}\"").as_bytes(),
LuaValue::String(expected.into()),
);
check(
format!("'\\0{c}a'").as_bytes(),
LuaValue::String(expected_a.into()),
);
check(
format!("\"\\0{c}a\"").as_bytes(),
LuaValue::String(expected_a.into()),
);
}
if c < 10 {
check(
format!("'\\00{c}'").as_bytes(),
LuaValue::String(expected.into()),
);
check(
format!("\"\\00{c}\"").as_bytes(),
LuaValue::String(expected.into()),
);
check(
format!("'\\00{c}a'").as_bytes(),
LuaValue::String(expected_a.into()),
);
check(
format!("\"\\00{c}a\"").as_bytes(),
LuaValue::String(expected_a.into()),
);
let expected_1 = &[c, b'1'][..];
check(
format!("'\\00{c}1'").as_bytes(),
LuaValue::String(expected_1.into()),
);
check(
format!("\"\\00{c}1\"").as_bytes(),
LuaValue::String(expected_1.into()),
);
}
}
for c in 256..=999 {
should_error(format!("'\\{c}'").as_bytes());
should_error(format!("\"\\{c}\"").as_bytes());
}
}
#[test]
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
fn unicode_escapes() {
check(br"'\u{0}'", LuaValue::String(b"\0".into()));
check(br"'\u{00}'", LuaValue::String(b"\0".into()));
check(br"'\u{00000000}'", LuaValue::String(b"\0".into()));
check(br"'\u{0000000000000000}'", LuaValue::String(b"\0".into()));
check(br"'\u{80}'", LuaValue::String(b"\xC2\x80".into()));
check(
br"'\u{10FFFF}'",
LuaValue::String(b"\xF4\x8F\xBF\xBF".into()),
);
check(
br"'\u{0000000000000010FFFF}'",
LuaValue::String(b"\xF4\x8F\xBF\xBF".into()),
);
check(
br"'\u{65E5}\u{672c}\u{8A9e}'",
LuaValue::String("\u{65E5}\u{672C}\u{8a9E}".as_bytes().into()),
);
check(br"'\u{d800}'", LuaValue::String(b"\xED\xA0\x80".into()));
check(br"'\u{dfff}'", LuaValue::String(b"\xED\xBF\xBF".into()));
check(
br"'\u{110000}'",
LuaValue::String(b"\xF4\x90\x80\x80".into()),
);
check(
br"'\u{70000000}'",
LuaValue::String(b"\xFD\xB0\x80\x80\x80\x80".into()),
);
check(
br"'\u{7fffffff}'",
LuaValue::String(b"\xFD\xBF\xBF\xBF\xBF\xBF".into()),
);
check(
br"'\u{07fffffff}'",
LuaValue::String(b"\xFD\xBF\xBF\xBF\xBF\xBF".into()),
);
check(
br"'\u{00000000000000007fffffff}'",
LuaValue::String(b"\xFD\xBF\xBF\xBF\xBF\xBF".into()),
);
check(
b"'fran\xC3\xA7ais'",
LuaValue::String(b"fran\xc3\xa7ais".into()),
);
check(
br"'fran\xC3\xA7ais'",
LuaValue::String(b"fran\xc3\xa7ais".into()),
);
check(
br"'fran\u{E7}ais'",
LuaValue::String(b"fran\xc3\xa7ais".into()),
);
check(
b"'franc\xCC\xA7ais'",
LuaValue::String(b"franc\xcc\xa7ais".into()),
);
check(
br"'franc\xCC\xA7ais'",
LuaValue::String(b"franc\xcc\xa7ais".into()),
);
check(
br"'franc\u{327}ais'",
LuaValue::String(b"franc\xcc\xa7ais".into()),
);
should_error(br"'\u'");
should_error(br"'\u '");
should_error(br"'\u\n'");
should_error(br"'\u12'");
should_error(br"'\uab'");
should_error(br"'\u{'");
should_error(br"'\u{hi'");
should_error(br"'\u{327hi'");
should_error(br"'\u{}'");
should_error(br"'\u{ }'");
should_error(br"'\u{hello}'");
should_error(br"'\u{80000000}'");
should_error(br"'\u{-80}'");
}
#[test]
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
fn invalid_escapes() {
should_error(br"'\ '");
should_error(b"'\\\t'");
for l in ('\0'..='\x09')
.chain(['\x0b', '\x0c'])
.chain('\x0e'..='!')
.chain('#'..='&')
.chain('('..='/')
.chain( ':'..='[')
.chain(']'..='`')
.chain('c'..='e')
.chain('g'..='m')
.chain('o'..='q')
.chain([
's',
'u',
'w',
'x',
'y',
])
.chain('{'..='\u{ff}')
{
should_error(format!("'\\{l}'").as_bytes());
should_error(format!("\"\\{l}\"").as_bytes());
}
for x in 0..=0xf {
should_error(format!("'\\x{x:x}'").as_bytes());
should_error(format!("\"\\x{x:x}\"").as_bytes());
}
should_error(br"'\xyz'");
should_error(br"'\'");
should_error(b"\"\\\"");
}
#[test]
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
fn borrows() -> Result {
assert!(lua_value(b"[[]]", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"[=[]=]", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"''", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"\"\"", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"[[hello]]", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"[=[hello]=]", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"'hello'", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"\"hello\"", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(br"[[hello\nworld]]", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(br"[=[hello\nworld]=]", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"[[hello\nworld]]", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"[=[hello\nworld]=]", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(br"'\n'", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"\"\\n\"", MAX_DEPTH)?.is_borrowed());
assert!(!lua_value(br"'\r\n'", MAX_DEPTH)?.is_borrowed());
assert!(!lua_value(b"\"\\r\\n\"", MAX_DEPTH)?.is_borrowed());
assert!(!lua_value(br"'hello\n'", MAX_DEPTH)?.is_borrowed());
assert!(!lua_value(b"\"hello\\n\"", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"'\\z\n'", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"\"\\z\n\"", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"'\\z\n\\z\n'", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"\"\\z\n\\z\n\"", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"'\\z\nhello'", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"\"\\z\nhello\"", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"'\\z\nhello\\z\n'", MAX_DEPTH)?.is_borrowed());
assert!(lua_value(b"\"\\z\nhello\\z\n\"", MAX_DEPTH)?.is_borrowed());
Ok(())
}