model_based_testing/
model_based_testing.rs1use rustate::{
2 Action, ActionType, Machine, MachineBuilder, ModelChecker, Property, PropertyType, State,
3 TestGenerator, TestRunner, Transition,
4};
5
6fn main() {
7 let machine = create_test_machine();
9 println!("状態マシンを作成しました: {}", machine.name);
10
11 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 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 println!("\n=== モデル検査 ===");
65 let mut checker = ModelChecker::new(&machine);
66
67 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 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 println!("\n>> デッドロック検出");
113 let deadlocks = checker.detect_deadlocks();
114 if deadlocks.is_empty() {
115 println!("デッドロック状態はありません");
116 } else {
117 println!("デッドロック状態: {}", deadlocks.join(", "));
118 }
119
120 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
130fn create_test_machine() -> Machine {
132 let green = State::new("green");
134 let yellow = State::new("yellow");
135 let red = State::new("red");
136
137 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 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 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
171fn 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 let coverage = results.get_coverage();
180 println!("状態カバレッジ: {:.1}%", coverage.state_coverage());
181 println!("遷移カバレッジ: {:.1}%", coverage.transition_coverage());
182
183 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}