miri_stress.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*
  2. Stress Miri as much as possible.
  3. Prove that we don't leak memory and that our methods are safe.
  4. Specifically:
  5. - [ ] VirtualDom drops memory safely
  6. - [ ] Borrowed components don't expose invalid pointers
  7. - [ ] Async isn't busted
  8. */
  9. use dioxus::{prelude::*, DomEdit, SchedulerMsg, ScopeId};
  10. use dioxus_core as dioxus;
  11. use dioxus_core_macro::*;
  12. use dioxus_html as dioxus_elements;
  13. mod test_logging;
  14. use DomEdit::*;
  15. const IS_LOGGING_ENABLED: bool = false;
  16. fn new_dom<P: 'static + Send>(app: Component<P>, props: P) -> VirtualDom {
  17. test_logging::set_up_logging(IS_LOGGING_ENABLED);
  18. VirtualDom::new_with_props(app, props)
  19. }
  20. /// This test ensures that if a component aborts early, it is replaced with a placeholder.
  21. /// In debug, this should also toss a warning.
  22. #[test]
  23. fn test_memory_leak() {
  24. fn app(cx: Scope) -> Element {
  25. let val = cx.use_hook(|_| 0);
  26. *val += 1;
  27. if *val == 2 || *val == 4 {
  28. return None;
  29. }
  30. let name = cx.use_hook(|_| String::from("asd"));
  31. cx.render(rsx!(
  32. div { "Hello, world!" }
  33. child()
  34. child()
  35. child()
  36. child()
  37. child()
  38. child()
  39. borrowed_child(na: name)
  40. borrowed_child(na: name)
  41. borrowed_child(na: name)
  42. borrowed_child(na: name)
  43. borrowed_child(na: name)
  44. ))
  45. }
  46. #[derive(Props)]
  47. struct BorrowedProps<'a> {
  48. na: &'a str,
  49. }
  50. fn borrowed_child<'a>(cx: Scope<'a, BorrowedProps<'a>>) -> Element {
  51. rsx!(cx, div {
  52. "goodbye {cx.props.na}"
  53. child()
  54. child()
  55. })
  56. }
  57. fn child(cx: Scope) -> Element {
  58. rsx!(cx, div { "goodbye world" })
  59. }
  60. let mut dom = new_dom(app, ());
  61. dom.rebuild();
  62. dom.hard_diff(ScopeId(0));
  63. dom.hard_diff(ScopeId(0));
  64. dom.hard_diff(ScopeId(0));
  65. dom.hard_diff(ScopeId(0));
  66. dom.hard_diff(ScopeId(0));
  67. dom.hard_diff(ScopeId(0));
  68. }
  69. #[test]
  70. fn memo_works_properly() {
  71. fn app(cx: Scope) -> Element {
  72. let val = cx.use_hook(|_| 0);
  73. *val += 1;
  74. if *val == 2 || *val == 4 {
  75. return None;
  76. }
  77. let name = cx.use_hook(|_| String::from("asd"));
  78. cx.render(rsx!(
  79. div { "Hello, world!" }
  80. child(na: "asdfg".to_string())
  81. ))
  82. }
  83. #[derive(PartialEq, Props)]
  84. struct ChildProps {
  85. na: String,
  86. }
  87. fn child(cx: Scope<ChildProps>) -> Element {
  88. rsx!(cx, div { "goodbye world" })
  89. }
  90. let mut dom = new_dom(app, ());
  91. dom.rebuild();
  92. dom.hard_diff(ScopeId(0));
  93. dom.hard_diff(ScopeId(0));
  94. dom.hard_diff(ScopeId(0));
  95. dom.hard_diff(ScopeId(0));
  96. dom.hard_diff(ScopeId(0));
  97. dom.hard_diff(ScopeId(0));
  98. dom.hard_diff(ScopeId(0));
  99. }
  100. #[test]
  101. fn free_works_on_root_props() {
  102. fn app(cx: Scope<Custom>) -> Element {
  103. cx.render(rsx! {
  104. child(a: "alpha")
  105. child(a: "beta")
  106. child(a: "gamma")
  107. child(a: "delta")
  108. })
  109. }
  110. #[derive(Props, PartialEq)]
  111. struct ChildProps {
  112. a: &'static str,
  113. }
  114. fn child(cx: Scope<ChildProps>) -> Element {
  115. rsx!(cx, "child {cx.props.a}")
  116. }
  117. struct Custom {
  118. val: String,
  119. }
  120. impl Drop for Custom {
  121. fn drop(&mut self) {
  122. dbg!("dropped!");
  123. }
  124. }
  125. let mut dom = new_dom(
  126. app,
  127. Custom {
  128. val: String::from("asd"),
  129. },
  130. );
  131. dom.rebuild();
  132. }
  133. #[test]
  134. fn free_works_on_borrowed() {
  135. fn app(cx: Scope) -> Element {
  136. cx.render(rsx! {
  137. child(a: "alpha", b: "asd".to_string())
  138. })
  139. }
  140. #[derive(Props)]
  141. struct ChildProps<'a> {
  142. a: &'a str,
  143. b: String,
  144. }
  145. fn child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
  146. dbg!("rendering child");
  147. rsx!(cx, "child {cx.props.a}, {cx.props.b}")
  148. }
  149. impl Drop for ChildProps<'_> {
  150. fn drop(&mut self) {
  151. dbg!("dropped child!");
  152. }
  153. }
  154. let mut dom = new_dom(app, ());
  155. let _ = dom.rebuild();
  156. }
  157. #[test]
  158. fn free_works_on_root_hooks() {
  159. /*
  160. On Drop, scopearena drops all the hook contents.
  161. */
  162. struct Droppable<T>(T);
  163. impl<T> Drop for Droppable<T> {
  164. fn drop(&mut self) {
  165. dbg!("dropping droppable");
  166. }
  167. }
  168. fn app(cx: Scope) -> Element {
  169. let name = cx.use_hook(|_| Droppable(String::from("asd")));
  170. rsx!(cx, div { "{name.0}" })
  171. }
  172. let mut dom = new_dom(app, ());
  173. let _ = dom.rebuild();
  174. }
  175. #[test]
  176. fn old_props_arent_stale() {
  177. fn app(cx: Scope) -> Element {
  178. dbg!("rendering parent");
  179. let cnt = cx.use_hook(|_| 0);
  180. *cnt += 1;
  181. if *cnt == 1 {
  182. rsx!(cx, div { child(a: "abcdef".to_string()) })
  183. } else {
  184. rsx!(cx, div { child(a: "abcdef".to_string()) })
  185. }
  186. }
  187. #[derive(Props, PartialEq)]
  188. struct ChildProps {
  189. a: String,
  190. }
  191. fn child(cx: Scope<ChildProps>) -> Element {
  192. dbg!("rendering child", &cx.props.a);
  193. rsx!(cx, div { "child {cx.props.a}" })
  194. }
  195. let mut dom = new_dom(app, ());
  196. let _ = dom.rebuild();
  197. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  198. dom.work_with_deadline(|| false);
  199. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  200. dom.work_with_deadline(|| false);
  201. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  202. dom.work_with_deadline(|| false);
  203. dbg!("forcing update to child");
  204. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  205. dom.work_with_deadline(|| false);
  206. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  207. dom.work_with_deadline(|| false);
  208. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  209. dom.work_with_deadline(|| false);
  210. }
  211. #[test]
  212. fn basic() {
  213. fn app(cx: Scope) -> Element {
  214. rsx!(cx, div {
  215. child(a: "abcdef".to_string())
  216. })
  217. }
  218. #[derive(Props, PartialEq)]
  219. struct ChildProps {
  220. a: String,
  221. }
  222. fn child(cx: Scope<ChildProps>) -> Element {
  223. dbg!("rendering child", &cx.props.a);
  224. rsx!(cx, div { "child {cx.props.a}" })
  225. }
  226. let mut dom = new_dom(app, ());
  227. let _ = dom.rebuild();
  228. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  229. dom.work_with_deadline(|| false);
  230. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  231. dom.work_with_deadline(|| false);
  232. }