mortar_compiler 0.5.3

Mortar language compiler core library
Documentation
//! # serializer_test.rs
//!
//! # serializer_test.rs 文件
//!
//! ## Module Overview
//!
//! ## 模块概述
//!
//! Tests for the serializer module.
//!
//! 序列化器模块的测试。
//!
//! ## Source File Overview
//!
//! ## 源文件概述
//!
//! Verifies that AST structures are correctly converted to the JSON format.
//!
//! 验证 AST 结构体能否被正确转换为 JSON 格式。

use crate::ast::{
    Arg, ChoiceDest, ChoiceItem, Condition, ConstDecl, EnumDef, Event, EventAction, FuncCall,
    FunctionDecl, NodeDef, NodeJump, NodeStmt, Param, Program, TopLevel, VarDecl, VarValue,
    WithEventItem, WithEventsStmt,
};
use crate::serializer::Serializer;
use serde_json::Value;

fn create_test_program() -> Program {
    Program {
        body: vec![
            TopLevel::FunctionDecl(FunctionDecl {
                name: "my_function".to_string(),
                name_span: Some((0, 11)), // Approximate span
                params: vec![Param {
                    name: "p1".to_string(),
                    type_name: "String".to_string(),
                }],
                return_type: Some("Number".to_string()),
            }),
            TopLevel::NodeDef(NodeDef {
                name: "start_node".to_string(),
                name_span: Some((0, 10)), // Approximate span
                body: vec![
                    NodeStmt::Text("This is the first line.".to_string()),
                    NodeStmt::WithEvents(WithEventsStmt {
                        events: vec![WithEventItem::InlineEvent(Event {
                            index: 0.5,
                            action: EventAction {
                                call: FuncCall {
                                    name: "play_sound".to_string(),
                                    name_span: Some((0, 10)), // Approximate span
                                    args: vec![Arg::String("music.mp3".to_string())],
                                },
                                chains: vec![],
                            },
                        })],
                    }),
                    NodeStmt::Text("This is the second line.".to_string()),
                    NodeStmt::Choice(vec![
                        ChoiceItem {
                            text: "Go to next".to_string(),
                            condition: None,
                            target: ChoiceDest::Identifier("next_node".to_string(), Some((0, 9))),
                        },
                        ChoiceItem {
                            text: "Stay here".to_string(),
                            condition: Some(Condition::Identifier("has_item".to_string())),
                            target: ChoiceDest::Break,
                        },
                    ]),
                ],
                jump: Some(NodeJump::Identifier(
                    "default_next".to_string(),
                    Some((0, 12)),
                )),
            }),
        ],
    }
}

#[test]
fn test_serialize_program() {
    let program = create_test_program();
    let json_string = Serializer::serialize_to_json(&program, false).unwrap();
    let json: Value = serde_json::from_str(&json_string).unwrap();

    assert_eq!(json["metadata"]["version"], env!("CARGO_PKG_VERSION"));
    assert!(json["metadata"]["generated_at"].is_string());

    assert_eq!(json["functions"].as_array().unwrap().len(), 1);
    assert_eq!(json["nodes"].as_array().unwrap().len(), 1);
}

#[test]
fn test_serialize_function_decl() {
    let program = create_test_program();
    let json_string = Serializer::serialize_to_json(&program, false).unwrap();
    let json: Value = serde_json::from_str(&json_string).unwrap();

    let function = &json["functions"][0];
    assert_eq!(function["name"], "my_function");
    assert_eq!(function["params"][0]["name"], "p1");
    assert_eq!(function["params"][0]["type"], "String");
    assert_eq!(function["return"], "Number");
}

#[test]
fn test_serialize_node_def() {
    let program = create_test_program();
    let json_string = Serializer::serialize_to_json(&program, false).unwrap();
    let json: Value = serde_json::from_str(&json_string).unwrap();

    let node = &json["nodes"][0];
    assert_eq!(node["name"], "start_node");
    assert_eq!(node["next"], "default_next");

    // The new content array should contain the text, events and choices in order.
    let content = node["content"].as_array().unwrap();
    assert_eq!(content.len(), 3);
    assert_eq!(content[0]["type"], "text");
    assert_eq!(content[1]["type"], "text");
    assert_eq!(content[2]["type"], "choice");
}

#[test]
fn test_serialize_text_and_events() {
    let program = create_test_program();
    let json_string = Serializer::serialize_to_json(&program, false).unwrap();
    let json: Value = serde_json::from_str(&json_string).unwrap();

    let content = &json["nodes"][0]["content"];
    let text1 = &content[0];
    assert_eq!(text1["type"], "text");
    assert_eq!(text1["value"], "This is the first line.");
    assert_eq!(text1["events"][0]["index"], 0.5);
    assert_eq!(text1["events"][0]["actions"][0]["type"], "play_sound");
    assert_eq!(text1["events"][0]["actions"][0]["args"][0], "music.mp3");

    let text2 = &content[1];
    assert_eq!(text2["type"], "text");
    assert_eq!(text2["value"], "This is the second line.");
    assert!(text2["events"].is_null());
}

#[test]
fn test_serialize_choices() {
    let program = create_test_program();
    let json_string = Serializer::serialize_to_json(&program, false).unwrap();
    let json: Value = serde_json::from_str(&json_string).unwrap();

    let content = &json["nodes"][0]["content"];
    let choice_item = &content[2];
    assert_eq!(choice_item["type"], "choice");

    let options = choice_item["options"].as_array().unwrap();
    assert_eq!(options.len(), 2);

    let choice1 = &options[0];
    assert_eq!(choice1["text"], "Go to next");
    assert!(choice1["condition"].is_null());
    assert_eq!(choice1["next"], "next_node");

    let choice2 = &options[1];
    assert_eq!(choice2["text"], "Stay here");
    assert_eq!(choice2["condition"]["type"], "has_item");
    assert_eq!(choice2["action"], "break");
}

#[test]
fn test_serialize_empty_program() {
    use crate::ast::Program;

    let program = Program { body: vec![] };
    let result = Serializer::serialize_to_json(&program, false);
    assert!(result.is_ok());

    let json = result.unwrap();
    assert!(json.contains("metadata"));
    assert!(json.contains("nodes"));
    assert!(json.contains("functions"));
}

#[test]
fn test_serialize_to_json_pretty() {
    use crate::ast::Program;

    let program = Program { body: vec![] };
    let result = Serializer::serialize_to_json(&program, true);
    assert!(result.is_ok());

    let json = result.unwrap();
    // Pretty formatted should have indentation or newlines
    assert!(json.contains("  ") || json.contains("\n"));
}

#[test]
fn test_save_to_file_basic() {
    use crate::ast::Program;
    use tempfile::TempDir;

    let program = Program { body: vec![] };
    let temp_dir = TempDir::new().unwrap();
    let output_file = temp_dir.path().join("output.mortared");

    let result = Serializer::save_to_file(&program, output_file.to_str().unwrap(), false);

    assert!(result.is_ok());
    assert!(output_file.exists());
}

#[test]
fn test_save_to_file_with_language() {
    use crate::Language;
    use tempfile::TempDir;

    let program = Program { body: vec![] };
    let temp_dir = TempDir::new().unwrap();
    let output_file = temp_dir.path().join("output.mortared");

    let result = Serializer::save_to_file_with_language(
        &program,
        output_file.to_str().unwrap(),
        true, // pretty
        Language::English,
    );

    assert!(result.is_ok());
    assert!(output_file.exists());
}

#[test]
fn test_serialize_variable_declarations() {
    let program = Program {
        body: vec![
            TopLevel::VarDecl(VarDecl {
                name: "player_name".to_string(),
                name_span: Some((0, 11)),
                type_name: "String".to_string(),
                value: None,
            }),
            TopLevel::VarDecl(VarDecl {
                name: "score".to_string(),
                name_span: Some((0, 5)),
                type_name: "Number".to_string(),
                value: Some(VarValue::Number(100.0)),
            }),
        ],
    };

    let json_string = Serializer::serialize_to_json(&program, false).unwrap();
    let json: Value = serde_json::from_str(&json_string).unwrap();

    assert_eq!(json["variables"].as_array().unwrap().len(), 2);
    assert_eq!(json["variables"][0]["name"], "player_name");
    assert_eq!(json["variables"][0]["type"], "String");
    assert!(json["variables"][0]["value"].is_null());

    assert_eq!(json["variables"][1]["name"], "score");
    assert_eq!(json["variables"][1]["type"], "Number");
    assert_eq!(json["variables"][1]["value"], 100.0);
}

#[test]
fn test_serialize_constant_declarations() {
    let program = Program {
        body: vec![
            TopLevel::ConstDecl(ConstDecl {
                is_public: true,
                name: "game_title".to_string(),
                name_span: Some((0, 10)),
                type_name: "String".to_string(),
                value: VarValue::String("My Game".to_string()),
            }),
            TopLevel::ConstDecl(ConstDecl {
                is_public: false,
                name: "max_level".to_string(),
                name_span: Some((0, 9)),
                type_name: "Number".to_string(),
                value: VarValue::Number(99.0),
            }),
        ],
    };

    let json_string = Serializer::serialize_to_json(&program, false).unwrap();
    let json: Value = serde_json::from_str(&json_string).unwrap();

    assert_eq!(json["constants"].as_array().unwrap().len(), 2);

    assert_eq!(json["constants"][0]["name"], "game_title");
    assert_eq!(json["constants"][0]["type"], "String");
    assert_eq!(json["constants"][0]["value"], "My Game");
    assert_eq!(json["constants"][0]["public"], true);

    assert_eq!(json["constants"][1]["name"], "max_level");
    assert_eq!(json["constants"][1]["public"], false);
}

#[test]
fn test_serialize_enum_definitions() {
    let program = Program {
        body: vec![TopLevel::EnumDef(EnumDef {
            name: "GameState".to_string(),
            name_span: Some((0, 9)),
            variants: vec![
                "menu".to_string(),
                "playing".to_string(),
                "paused".to_string(),
            ],
        })],
    };

    let json_string = Serializer::serialize_to_json(&program, false).unwrap();
    let json: Value = serde_json::from_str(&json_string).unwrap();

    assert_eq!(json["enums"].as_array().unwrap().len(), 1);
    assert_eq!(json["enums"][0]["name"], "GameState");
    assert_eq!(json["enums"][0]["variants"].as_array().unwrap().len(), 3);
    assert_eq!(json["enums"][0]["variants"][0], "menu");
    assert_eq!(json["enums"][0]["variants"][1], "playing");
    assert_eq!(json["enums"][0]["variants"][2], "paused");
}

#[test]
fn test_serialize_line_stmt() {
    let program = Program {
        body: vec![TopLevel::NodeDef(NodeDef {
            name: "eat_food".to_string(),
            name_span: Some((0, 8)),
            body: vec![
                NodeStmt::Line("You ate the food.".to_string()),
                NodeStmt::Line("You recovered 10 HP!".to_string()),
            ],
            jump: None,
        })],
    };

    let json_string = Serializer::serialize_to_json(&program, false).unwrap();
    let json: Value = serde_json::from_str(&json_string).unwrap();

    let content = &json["nodes"][0]["content"];
    assert_eq!(content[0]["type"], "line");
    assert_eq!(content[0]["value"], "You ate the food.");
    assert_eq!(content[1]["type"], "line");
    assert_eq!(content[1]["value"], "You recovered 10 HP!");
}

#[test]
fn test_serialize_line_and_text_mixed() {
    let program = Program {
        body: vec![TopLevel::NodeDef(NodeDef {
            name: "mixed".to_string(),
            name_span: Some((0, 5)),
            body: vec![
                NodeStmt::Text("Step 1".to_string()),
                NodeStmt::Line("Line A".to_string()),
                NodeStmt::Line("Line B".to_string()),
                NodeStmt::Text("Step 2".to_string()),
            ],
            jump: None,
        })],
    };

    let json_string = Serializer::serialize_to_json(&program, false).unwrap();
    let json: Value = serde_json::from_str(&json_string).unwrap();

    let content = json["nodes"][0]["content"].as_array().unwrap();
    assert_eq!(content.len(), 4);
    assert_eq!(content[0]["type"], "text");
    assert_eq!(content[1]["type"], "line");
    assert_eq!(content[2]["type"], "line");
    assert_eq!(content[3]["type"], "text");
}