use crate::common;
use common::{create_sakura_test_runtime, create_sakura_test_runtime_with_config};
use pasta_lua::loader::TalkConfig;
use pasta_lua::sakura_script::tokenizer::{CharSets, Token, TokenKind, Tokenizer};
use pasta_lua::sakura_script::wait_inserter::{WaitValues, insert_waits};
fn default_tokenizer() -> Tokenizer {
Tokenizer::new(&TalkConfig::default()).unwrap()
}
fn test_wait_values() -> WaitValues {
WaitValues {
normal: 100,
period: 1000,
comma: 500,
strong: 500,
leader: 200,
}
}
#[test]
fn test_tokenize_lone_backslash_at_end_is_general() {
let tokens = default_tokenizer().tokenize("あ\\");
assert_eq!(tokens.len(), 2);
assert_eq!(tokens[0].kind, TokenKind::General);
assert_eq!(tokens[0].text, "あ");
assert_eq!(tokens[1].kind, TokenKind::General);
assert_eq!(tokens[1].text, "\\");
}
#[test]
fn test_tokenize_backslash_before_non_tag_char_is_general() {
let tokens = default_tokenizer().tokenize("\\あ");
assert_eq!(tokens.len(), 2);
assert_eq!(tokens[0].kind, TokenKind::General);
assert_eq!(tokens[0].text, "\\");
assert_eq!(tokens[1].kind, TokenKind::General);
assert_eq!(tokens[1].text, "あ");
}
#[test]
fn test_tokenize_double_backslash_then_tag() {
let tokens = default_tokenizer().tokenize(r"\\h");
assert_eq!(tokens.len(), 2);
assert_eq!(tokens[0].kind, TokenKind::General);
assert_eq!(tokens[0].text, "\\");
assert_eq!(tokens[1].kind, TokenKind::SakuraScript);
assert_eq!(tokens[1].text, r"\h");
}
#[test]
fn test_tokenize_unclosed_bracket_not_part_of_tag() {
let tokens = default_tokenizer().tokenize(r"\s[0");
assert_eq!(tokens.len(), 3);
assert_eq!(tokens[0].kind, TokenKind::SakuraScript);
assert_eq!(tokens[0].text, r"\s");
assert_eq!(tokens[1].kind, TokenKind::LineEndProhibited);
assert_eq!(tokens[1].text, "[");
assert_eq!(tokens[2].kind, TokenKind::General);
assert_eq!(tokens[2].text, "0");
}
#[test]
fn test_tokenize_empty_bracket_param() {
let tokens = default_tokenizer().tokenize(r"\s[]");
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0].kind, TokenKind::SakuraScript);
assert_eq!(tokens[0].text, r"\s[]");
}
#[test]
fn test_tokenize_bang_tag_with_comma_params() {
let tokens = default_tokenizer().tokenize(r"\![open,inputbox]");
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0].kind, TokenKind::SakuraScript);
assert_eq!(tokens[0].text, r"\![open,inputbox]");
}
#[test]
fn test_classify_priority_leader_wins_over_line_start_prohibited() {
let char_sets = CharSets::from_config(&TalkConfig::default());
assert_eq!(char_sets.classify('・'), TokenKind::Leader);
}
#[test]
fn test_classify_halfwidth_comma_is_line_start_prohibited() {
let char_sets = CharSets::from_config(&TalkConfig::default());
assert_eq!(char_sets.classify('、'), TokenKind::LineStartProhibited);
}
#[test]
fn test_line_start_prohibited_only_run_gets_no_wait() {
let tokens = vec![
Token::new(TokenKind::LineStartProhibited, "」"),
Token::new(TokenKind::LineStartProhibited, "』"),
];
let result = insert_waits(&tokens, &test_wait_values());
assert_eq!(result, "」』");
}
#[test]
fn test_line_start_prohibited_extends_run_without_raising_max() {
let tokens = vec![
Token::new(TokenKind::Comma, "、"),
Token::new(TokenKind::LineStartProhibited, "」"),
];
let result = insert_waits(&tokens, &test_wait_values());
assert_eq!(result, r"、」\_w[450]");
}
#[test]
fn test_line_start_prohibited_first_then_strong_uses_strong_wait() {
let tokens = vec![
Token::new(TokenKind::LineStartProhibited, "」"),
Token::new(TokenKind::Strong, "!"),
];
let result = insert_waits(&tokens, &test_wait_values());
assert_eq!(result, r"」!\_w[450]");
}
#[test]
fn test_sakura_script_tag_flushes_pending_punctuation() {
let tokens = vec![
Token::new(TokenKind::Period, "。"),
Token::new(TokenKind::SakuraScript, r"\h"),
Token::new(TokenKind::General, "あ"),
];
let result = insert_waits(&tokens, &test_wait_values());
assert_eq!(result, r"。\_w[950]\hあ\_w[50]");
}
#[test]
fn test_line_end_prohibited_flushes_pending_punctuation() {
let tokens = vec![
Token::new(TokenKind::Period, "。"),
Token::new(TokenKind::LineEndProhibited, "「"),
Token::new(TokenKind::General, "あ"),
];
let result = insert_waits(&tokens, &test_wait_values());
assert_eq!(result, r"。\_w[950]「あ\_w[50]");
}
#[test]
fn test_register_with_none_config_uses_defaults() {
let lua = mlua::Lua::new();
let module = pasta_lua::sakura_script::register(&lua, None).unwrap();
let talk_to_script: mlua::Function = module.get("talk_to_script").unwrap();
let result: String = talk_to_script
.call((mlua::Value::Nil, "あ。"))
.unwrap();
assert_eq!(result, r"あ。\_w[950]");
}
#[test]
fn test_actor_string_value_falls_back_to_config() {
let config = TalkConfig {
script_wait_normal: 100, ..Default::default()
};
let lua = create_sakura_test_runtime_with_config(&config);
let result: String = lua
.load(
r#"
local SAKURA = require "@pasta_sakura_script"
return SAKURA.talk_to_script("actor-name", "あ")
"#,
)
.eval()
.unwrap();
assert_eq!(result, r"あ\_w[50]");
}
#[test]
fn test_actor_wait_below_threshold_suppresses_tags() {
let config = TalkConfig {
script_wait_normal: 100,
..Default::default()
};
let lua = create_sakura_test_runtime_with_config(&config);
let result: String = lua
.load(
r#"
local SAKURA = require "@pasta_sakura_script"
local actor = { script_wait_normal = 30 }
return SAKURA.talk_to_script(actor, "あ")
"#,
)
.eval()
.unwrap();
assert_eq!(result, "あ");
}
#[test]
fn test_actor_budoux_empty_table_no_line_breaks() {
let lua = create_sakura_test_runtime();
let result: String = lua
.load(
r#"
local SAKURA = require "@pasta_sakura_script"
local actor = { budoux = {} }
return SAKURA.talk_to_script(actor, "今日はいい天気ですね")
"#,
)
.eval()
.unwrap();
assert!(
!result.contains("\\n"),
"Empty budoux array must not insert line breaks: {}",
result
);
}
#[test]
fn test_actor_budoux_non_table_value_ignored() {
let lua = create_sakura_test_runtime();
let result: String = lua
.load(
r#"
local SAKURA = require "@pasta_sakura_script"
local actor = { budoux = 6 }
return SAKURA.talk_to_script(actor, "今日はいい天気ですね")
"#,
)
.eval()
.unwrap();
assert!(
!result.contains("\\n"),
"Non-table budoux value must not insert line breaks: {}",
result
);
}
#[test]
fn test_actor_budoux_non_numeric_entry_raises_error() {
let lua = create_sakura_test_runtime();
let result = lua
.load(
r#"
local SAKURA = require "@pasta_sakura_script"
local actor = { budoux = {true} }
return SAKURA.talk_to_script(actor, "今日はいい天気ですね")
"#,
)
.eval::<String>();
assert!(
result.is_err(),
"Non-numeric budoux entry should raise a Lua error"
);
}
#[test]
fn test_break_lines_non_table_widths_returns_input() {
let lua = create_sakura_test_runtime();
let result: String = lua
.load(
r#"
local SAKURA = require "@pasta_sakura_script"
return SAKURA.break_lines("今日はいい天気ですね", "abc")
"#,
)
.eval()
.unwrap();
assert_eq!(result, "今日はいい天気ですね");
}
#[test]
fn test_break_lines_tags_only_input_unchanged() {
let lua = create_sakura_test_runtime();
let result: String = lua
.load(
r#"
local SAKURA = require "@pasta_sakura_script"
return SAKURA.break_lines("\\h\\s[0]", {4})
"#,
)
.eval()
.unwrap();
assert_eq!(result, r"\h\s[0]");
}