use crate::common;
use common::e2e_helpers::{create_runtime_with_finalize, transpile};
use pasta_lua::loader::TalkConfig;
use pasta_lua::sakura_script;
#[test]
fn test_comment_line_explicit_parse() {
use pasta_dsl::parser::parse_str;
let source = r#"
# これはコメントです
*メイン
# これもコメント
さくら:「こんにちは」
# 最後のコメント
"#;
let file = parse_str(source, "test.pasta").expect("Should parse with comments");
let scene_count = file
.items
.iter()
.filter(|item| matches!(item, pasta_dsl::parser::ast::FileItem::GlobalSceneScope(_)))
.count();
assert_eq!(scene_count, 1, "Should have exactly 1 scene");
let lua_code = transpile(source);
assert!(
!lua_code.contains("これはコメントです"),
"Comment text should not appear in transpiled code"
);
assert!(
!lua_code.contains("これもコメント"),
"Inline comment should not appear in transpiled code"
);
}
#[test]
fn test_attribute_inheritance() {
let source = r#"
&天気:晴れ
&場所:公園
*メイン
&場所:学校
さくら:「今日は$天気です」
"#;
let lua_code = transpile(source);
assert!(lua_code.contains("create_scene"), "Should create scene");
let lua = create_runtime_with_finalize().unwrap();
lua.load(&lua_code).exec().unwrap();
lua.load("require('pasta').finalize_scene()")
.exec()
.unwrap();
let found: bool = lua
.load(
r#"
local SEARCH = require "@pasta_search"
local name, _ = SEARCH:search_scene("メイン", nil)
return name ~= nil
"#,
)
.eval()
.unwrap();
assert!(
found,
"Scene with inherited attributes should be searchable"
);
}
#[test]
fn test_variable_scope_complete() {
let source = r#"
*メイン
$ローカル=「ローカル値」
$*グローバル=「グローバル値」
さくら:「ローカル:$ローカル、グローバル:$*グローバル」
"#;
let lua_code = transpile(source);
assert!(
lua_code.contains("var."),
"Local variable should use var.name format. Generated code:\n{}",
lua_code
);
assert!(
lua_code.contains("save."),
"Global variable should use save.name format. Generated code:\n{}",
lua_code
);
let lua = create_runtime_with_finalize().unwrap();
lua.load(&lua_code).exec().unwrap();
}
#[test]
fn test_error_message_specificity() {
use pasta_dsl::parser::parse_str;
let invalid_source = "*\n";
let result = parse_str(invalid_source, "test.pasta");
assert!(result.is_err(), "Invalid syntax should produce error");
let error = result.unwrap_err();
let error_str = format!("{:?}", error);
assert!(
error_str.contains("line") || error_str.contains("1"),
"Error should include line information: {}",
error_str
);
let invalid_source2 = "*メイン\n さくら:";
let result2 = parse_str(invalid_source2, "test.pasta");
if let Err(error2) = result2 {
let error_str2 = format!("{:?}", error2);
assert!(
error_str2.len() > 10,
"Error message should be descriptive: {}",
error_str2
);
}
}
#[test]
fn test_fixture_chaintalk_parses() {
let source = include_str!("../fixtures/e2e/runtime_e2e_scene_chaintalk.pasta");
let lua_code = transpile(source);
assert!(
lua_code.contains("create_actor(\"さくら\")"),
"Should contain さくら actor definition. Generated code:\n{}",
lua_code
);
assert!(
lua_code.contains("チェイントークテスト"),
"Should contain チェイントークテスト scene. Generated code:\n{}",
lua_code
);
assert!(
lua_code.contains("\"チェイントーク\""),
"Should contain act:call with チェイントーク key. Generated code:\n{}",
lua_code
);
}
#[test]
fn test_e2e_chaintalk_transpile_and_execute() {
let lua = create_runtime_with_finalize().unwrap();
let config = TalkConfig::default();
let module = sakura_script::register(&lua, Some(&config)).unwrap();
let package: mlua::Table = lua.globals().get("package").unwrap();
let loaded: mlua::Table = package.get("loaded").unwrap();
loaded.set("@pasta_sakura_script", module).unwrap();
let source = include_str!("../fixtures/e2e/runtime_e2e_scene_chaintalk.pasta");
let lua_code = transpile(source);
lua.load(&lua_code).exec().unwrap();
lua.load("require('pasta').finalize_scene()")
.exec()
.unwrap();
let global_ok: bool = lua
.load(
r#"
local GLOBAL = require("pasta.global")
return type(GLOBAL["チェイントーク"]) == "function"
and type(GLOBAL["yield"]) == "function"
"#,
)
.eval()
.unwrap();
assert!(
global_ok,
"GLOBAL.チェイントーク and GLOBAL.yield should be registered"
);
let fire_result: String = lua
.load(
r#"
local STORE = require("pasta.store")
local EVENT = require("pasta.shiori.event")
-- Fire the scene event
local req = { id = "チェイントークテスト" }
local response = EVENT.fire(req)
-- Check result and co_scene state
local co_state = "none"
if STORE.co_scene then
co_state = coroutine.status(STORE.co_scene)
end
return string.format("status=%s co=%s", tostring(response ~= nil), co_state)
"#,
)
.eval()
.unwrap();
assert!(
fire_result.contains("status=true"),
"EVENT.fire should return a response, got: {}",
fire_result
);
assert!(
fire_result.contains("co=suspended"),
"co_scene should be suspended after yield, got: {}",
fire_result
);
}