model_based_testing/
model_based_testing.rs

1use rustate::{
2    Action, ActionType, Machine, MachineBuilder, ModelChecker, Property, PropertyType, State,
3    TestGenerator, TestRunner, Transition,
4};
5
6fn main() {
7    // 状態マシンを作成
8    let machine = create_test_machine();
9    println!("状態マシンを作成しました: {}", machine.name);
10
11    // テストケース生成
12    println!("\n=== テストケースの生成 ===");
13    let mut generator = TestGenerator::new(&machine);
14
15    println!("\n>> 全状態カバレッジのテストケース");
16    let state_tests = generator.generate_all_states();
17    for test in &state_tests {
18        println!(
19            "- {}: {} -> {} (イベント数: {})",
20            test.name,
21            test.initial_state,
22            test.expected_state,
23            test.events.len()
24        );
25    }
26
27    println!("\n>> 全遷移カバレッジのテストケース");
28    let transition_tests = generator.generate_all_transitions();
29    for test in &transition_tests {
30        println!(
31            "- {}: {} -> {} (イベント数: {})",
32            test.name,
33            test.initial_state,
34            test.expected_state,
35            test.events.len()
36        );
37    }
38
39    println!("\n>> ループカバレッジのテストケース");
40    let loop_tests = generator.generate_loop_coverage();
41    for test in &loop_tests {
42        println!(
43            "- {}: {} -> {} (イベント数: {})",
44            test.name,
45            test.initial_state,
46            test.expected_state,
47            test.events.len()
48        );
49    }
50
51    // テスト実行
52    println!("\n=== テスト実行 ===");
53    let mut runner = TestRunner::new(&machine);
54
55    println!("\n>> 状態カバレッジテストの実行");
56    let state_results = runner.run_tests(state_tests);
57    print_test_results(&state_results);
58
59    println!("\n>> 遷移カバレッジテストの実行");
60    let transition_results = runner.run_tests(transition_tests);
61    print_test_results(&transition_results);
62
63    // モデル検査
64    println!("\n=== モデル検査 ===");
65    let mut checker = ModelChecker::new(&machine);
66
67    // 到達可能性プロパティ
68    let reachability = Property {
69        name: "赤信号に到達可能か".to_string(),
70        property_type: PropertyType::Reachability,
71        target_states: vec!["red".to_string()],
72        description: Some("赤信号状態に到達できることを確認".to_string()),
73    };
74
75    let result = checker.verify_property(&reachability);
76    println!("\n>> プロパティ: {}", reachability.name);
77    println!(
78        "結果: {}",
79        if result.satisfied {
80            "満たされています"
81        } else {
82            "満たされていません"
83        }
84    );
85    if let Some(msg) = result.message {
86        println!("メッセージ: {}", msg);
87    }
88
89    // 安全性プロパティ
90    let safety = Property {
91        name: "存在しない状態に到達しないか".to_string(),
92        property_type: PropertyType::Safety,
93        target_states: vec!["invalid".to_string()],
94        description: Some("存在しない状態に決して到達しないことを確認".to_string()),
95    };
96
97    let result = checker.verify_property(&safety);
98    println!("\n>> プロパティ: {}", safety.name);
99    println!(
100        "結果: {}",
101        if result.satisfied {
102            "満たされています"
103        } else {
104            "満たされていません"
105        }
106    );
107    if let Some(msg) = result.message {
108        println!("メッセージ: {}", msg);
109    }
110
111    // デッドロックの検出
112    println!("\n>> デッドロック検出");
113    let deadlocks = checker.detect_deadlocks();
114    if deadlocks.is_empty() {
115        println!("デッドロック状態はありません");
116    } else {
117        println!("デッドロック状態: {}", deadlocks.join(", "));
118    }
119
120    // 到達不可能な状態の検出
121    println!("\n>> 到達不可能な状態の検出");
122    let unreachable = checker.detect_unreachable_states();
123    if unreachable.is_empty() {
124        println!("到達不可能な状態はありません");
125    } else {
126        println!("到達不可能な状態: {}", unreachable.join(", "));
127    }
128}
129
130// テスト用の信号機状態マシンを作成
131fn create_test_machine() -> Machine {
132    // 状態の作成
133    let green = State::new("green");
134    let yellow = State::new("yellow");
135    let red = State::new("red");
136
137    // 遷移の作成
138    let green_to_yellow = Transition::new("green", "TIMER", "yellow");
139    let yellow_to_red = Transition::new("yellow", "TIMER", "red");
140    let red_to_green = Transition::new("red", "TIMER", "green");
141
142    // アクションの定義
143    let log_green = Action::new("logGreen", ActionType::Entry, |_ctx, _evt| {
144        println!("緑信号になりました - 進んでください")
145    });
146
147    let log_yellow = Action::new("logYellow", ActionType::Entry, |_ctx, _evt| {
148        println!("黄信号になりました - 注意してください")
149    });
150
151    let log_red = Action::new("logRed", ActionType::Entry, |_ctx, _evt| {
152        println!("赤信号になりました - 停止してください")
153    });
154
155    // マシンの構築
156    MachineBuilder::new("trafficLight")
157        .state(green)
158        .state(yellow)
159        .state(red)
160        .initial("green")
161        .transition(green_to_yellow)
162        .transition(yellow_to_red)
163        .transition(red_to_green)
164        .on_entry("green", log_green)
165        .on_entry("yellow", log_yellow)
166        .on_entry("red", log_red)
167        .build()
168        .unwrap()
169}
170
171// テスト結果を出力
172fn print_test_results(results: &rustate::TestResults) {
173    println!("総テスト数: {}", results.total_count());
174    println!("成功: {}", results.success_count());
175    println!("失敗: {}", results.failure_count());
176    println!("成功率: {:.1}%", results.success_rate());
177
178    // カバレッジレポート
179    let coverage = results.get_coverage();
180    println!("状態カバレッジ: {:.1}%", coverage.state_coverage());
181    println!("遷移カバレッジ: {:.1}%", coverage.transition_coverage());
182
183    // 失敗したテストがあれば詳細を表示
184    let failed_tests = results
185        .results
186        .iter()
187        .filter(|r| !r.success)
188        .collect::<Vec<_>>();
189    if !failed_tests.is_empty() {
190        println!("\n失敗したテスト:");
191        for failed in failed_tests {
192            println!(
193                "- {}: 期待={}, 実際={}",
194                failed.test_name, failed.expected_state, failed.actual_state
195            );
196            if let Some(err) = &failed.error_message {
197                println!("  エラー: {}", err);
198            }
199        }
200    }
201}