cranpose_testing/
testing.rs1use cranpose_core::{
2 location_key, ApplierGuard, Composition, Key, MemoryApplier, NodeError, NodeId, RuntimeHandle,
3};
4use cranpose_ui::request_render_invalidation;
5
6#[cfg(test)]
7use cranpose_core::{
8 pop_parent, push_parent, with_current_composer, with_node_mut, MutableState, Node,
9};
10#[cfg(test)]
11use std::cell::Cell;
12#[cfg(test)]
13use std::rc::Rc;
14
15pub struct ComposeTestRule {
22 composition: Composition<MemoryApplier>,
23 content: Option<Box<dyn FnMut()>>, root_key: Key,
25}
26
27impl ComposeTestRule {
28 pub fn new() -> Self {
30 Self {
31 composition: Composition::new(MemoryApplier::new()),
32 content: None,
33 root_key: location_key(file!(), line!(), column!()),
34 }
35 }
36
37 pub fn set_content(&mut self, content: impl FnMut() + 'static) -> Result<(), NodeError> {
40 self.content = Some(Box::new(content));
41 self.render()
42 }
43
44 pub fn recomposition(&mut self) -> Result<(), NodeError> {
46 self.render()
47 }
48
49 pub fn advance_frame(&mut self, frame_time_nanos: u64) -> Result<(), NodeError> {
52 let handle = self.composition.runtime_handle();
53 handle.drain_frame_callbacks(frame_time_nanos);
54 self.pump_until_idle()
55 }
56
57 pub fn pump_until_idle(&mut self) -> Result<(), NodeError> {
60 let mut i = 0;
61 loop {
62 let mut progressed = false;
63 i += 1;
64 if i > 100 {
65 panic!("pump_until_idle looped too many times!");
66 }
67
68 if self.composition.should_render() {
69 eprintln!("pump_until_idle: should_render() is true");
70 self.render()?;
71 progressed = true;
72 }
73
74 let handle = self.composition.runtime_handle();
75 if handle.has_updates() {
76 eprintln!("pump_until_idle: has_updates() is true");
77 self.composition.flush_pending_node_updates()?;
78 progressed = true;
79 }
80
81 if handle.has_invalid_scopes() {
82 eprintln!("pump_until_idle: has_invalid_scopes() is true");
83 let changed = self.composition.process_invalid_scopes()?;
84 if changed {
85 eprintln!("pump_until_idle: process_invalid_scopes returned true");
86 request_render_invalidation();
88 }
89 progressed = true;
90 }
91
92 if !progressed {
93 break;
94 }
95 }
96 Ok(())
97 }
98
99 pub fn runtime_handle(&self) -> RuntimeHandle {
102 self.composition.runtime_handle()
103 }
104
105 pub fn applier_mut(&mut self) -> ApplierGuard<'_, MemoryApplier> {
108 self.composition.applier_mut()
109 }
110
111 pub fn dump_tree(&mut self) -> String {
113 let root = self.composition.root();
114 let applier = self.composition.applier_mut();
115 applier.dump_tree(root)
116 }
117
118 pub fn has_content(&self) -> bool {
120 self.content.is_some()
121 }
122
123 pub fn root_id(&self) -> Option<NodeId> {
125 self.composition.root()
126 }
127
128 pub fn composition(&mut self) -> &mut Composition<MemoryApplier> {
130 &mut self.composition
131 }
132
133 fn render(&mut self) -> Result<(), NodeError> {
134 if let Some(content) = self.content.as_mut() {
135 self.composition.render(self.root_key, &mut **content)?;
136 request_render_invalidation();
139 }
140 Ok(())
141 }
142}
143
144impl Default for ComposeTestRule {
145 fn default() -> Self {
146 Self::new()
147 }
148}
149
150pub fn run_test_composition<R>(f: impl FnOnce(&mut ComposeTestRule) -> R) -> R {
153 let mut rule = ComposeTestRule::new();
154 f(&mut rule)
155}
156
157#[cfg(test)]
158#[path = "tests/testing_tests.rs"]
159mod tests;
160
161#[cfg(test)]
162#[path = "tests/recomposition_tests.rs"]
163mod recomposition_tests;