miri_stress.rs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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::*, 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. const IS_LOGGING_ENABLED: bool = false;
  15. fn new_dom<P: 'static + Send>(app: Component<P>, props: P) -> VirtualDom {
  16. test_logging::set_up_logging(IS_LOGGING_ENABLED);
  17. VirtualDom::new_with_props(app, props)
  18. }
  19. /// This test ensures that if a component aborts early, it is replaced with a placeholder.
  20. /// In debug, this should also toss a warning.
  21. #[test]
  22. fn test_memory_leak() {
  23. fn app(cx: Scope) -> Element {
  24. let val = cx.use_hook(|_| 0);
  25. *val += 1;
  26. if *val == 2 || *val == 4 {
  27. return None;
  28. }
  29. let name = cx.use_hook(|_| String::from("asd"));
  30. cx.render(rsx!(
  31. div { "Hello, world!" }
  32. child()
  33. child()
  34. child()
  35. child()
  36. child()
  37. child()
  38. borrowed_child(na: name)
  39. borrowed_child(na: name)
  40. borrowed_child(na: name)
  41. borrowed_child(na: name)
  42. borrowed_child(na: name)
  43. ))
  44. }
  45. #[derive(Props)]
  46. struct BorrowedProps<'a> {
  47. na: &'a str,
  48. }
  49. fn borrowed_child<'a>(cx: Scope<'a, BorrowedProps<'a>>) -> Element {
  50. rsx!(cx, div {
  51. "goodbye {cx.props.na}"
  52. child()
  53. child()
  54. })
  55. }
  56. fn child(cx: Scope) -> Element {
  57. rsx!(cx, div { "goodbye world" })
  58. }
  59. let mut dom = new_dom(app, ());
  60. dom.rebuild();
  61. dom.hard_diff(ScopeId(0));
  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. }
  68. #[test]
  69. fn memo_works_properly() {
  70. fn app(cx: Scope) -> Element {
  71. let val = cx.use_hook(|_| 0);
  72. *val += 1;
  73. if *val == 2 || *val == 4 {
  74. return None;
  75. }
  76. let name = cx.use_hook(|_| String::from("asd"));
  77. cx.render(rsx!(
  78. div { "Hello, world! {name}" }
  79. child(na: "asdfg".to_string() )
  80. ))
  81. }
  82. #[derive(PartialEq, Props)]
  83. struct ChildProps {
  84. na: String,
  85. }
  86. fn child(cx: Scope<ChildProps>) -> Element {
  87. rsx!(cx, div { "goodbye world" })
  88. }
  89. let mut dom = new_dom(app, ());
  90. dom.rebuild();
  91. dom.hard_diff(ScopeId(0));
  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. }
  99. #[test]
  100. fn free_works_on_root_props() {
  101. fn app(cx: Scope<Custom>) -> Element {
  102. cx.render(rsx! {
  103. child(a: "alpha")
  104. child(a: "beta")
  105. child(a: "gamma")
  106. child(a: "delta")
  107. })
  108. }
  109. #[derive(Props, PartialEq)]
  110. struct ChildProps {
  111. a: &'static str,
  112. }
  113. fn child(cx: Scope<ChildProps>) -> Element {
  114. rsx!(cx, "child {cx.props.a}")
  115. }
  116. struct Custom {
  117. val: String,
  118. }
  119. impl Drop for Custom {
  120. fn drop(&mut self) {
  121. dbg!("dropped! {}", &self.val);
  122. }
  123. }
  124. let mut dom = new_dom(app, Custom { val: String::from("asd") });
  125. dom.rebuild();
  126. }
  127. #[test]
  128. fn free_works_on_borrowed() {
  129. fn app(cx: Scope) -> Element {
  130. cx.render(rsx! {
  131. child(a: "alpha", b: "asd".to_string())
  132. })
  133. }
  134. #[derive(Props)]
  135. struct ChildProps<'a> {
  136. a: &'a str,
  137. b: String,
  138. }
  139. fn child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
  140. dbg!("rendering child");
  141. rsx!(cx, "child {cx.props.a}, {cx.props.b}")
  142. }
  143. impl Drop for ChildProps<'_> {
  144. fn drop(&mut self) {
  145. dbg!("dropped child!");
  146. }
  147. }
  148. let mut dom = new_dom(app, ());
  149. let _ = dom.rebuild();
  150. }
  151. #[test]
  152. fn free_works_on_root_hooks() {
  153. /*
  154. On Drop, scopearena drops all the hook contents.
  155. */
  156. struct Droppable<T>(T);
  157. impl<T> Drop for Droppable<T> {
  158. fn drop(&mut self) {
  159. dbg!("dropping droppable");
  160. }
  161. }
  162. fn app(cx: Scope) -> Element {
  163. let name = cx.use_hook(|_| Droppable(String::from("asd")));
  164. rsx!(cx, div { "{name.0}" })
  165. }
  166. let mut dom = new_dom(app, ());
  167. let _ = dom.rebuild();
  168. }
  169. #[test]
  170. fn old_props_arent_stale() {
  171. fn app(cx: Scope) -> Element {
  172. dbg!("rendering parent");
  173. let cnt = cx.use_hook(|_| 0);
  174. *cnt += 1;
  175. if *cnt == 1 {
  176. rsx!(cx, div { child(a: "abcdef".to_string()) })
  177. } else {
  178. rsx!(cx, div { child(a: "abcdef".to_string()) })
  179. }
  180. }
  181. #[derive(Props, PartialEq)]
  182. struct ChildProps {
  183. a: String,
  184. }
  185. fn child(cx: Scope<ChildProps>) -> Element {
  186. dbg!("rendering child", &cx.props.a);
  187. rsx!(cx, div { "child {cx.props.a}" })
  188. }
  189. let mut dom = new_dom(app, ());
  190. let _ = dom.rebuild();
  191. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  192. dom.work_with_deadline(|| false);
  193. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  194. dom.work_with_deadline(|| false);
  195. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  196. dom.work_with_deadline(|| false);
  197. dbg!("forcing update to child");
  198. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  199. dom.work_with_deadline(|| false);
  200. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  201. dom.work_with_deadline(|| false);
  202. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  203. dom.work_with_deadline(|| false);
  204. }
  205. #[test]
  206. fn basic() {
  207. fn app(cx: Scope) -> Element {
  208. rsx!(cx, div {
  209. child(a: "abcdef".to_string())
  210. })
  211. }
  212. #[derive(Props, PartialEq)]
  213. struct ChildProps {
  214. a: String,
  215. }
  216. fn child(cx: Scope<ChildProps>) -> Element {
  217. dbg!("rendering child", &cx.props.a);
  218. rsx!(cx, div { "child {cx.props.a}" })
  219. }
  220. let mut dom = new_dom(app, ());
  221. let _ = dom.rebuild();
  222. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  223. dom.work_with_deadline(|| false);
  224. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  225. dom.work_with_deadline(|| false);
  226. }
  227. #[test]
  228. fn leak_thru_children() {
  229. fn app(cx: Scope) -> Element {
  230. cx.render(rsx! {
  231. Child {
  232. name: "asd".to_string(),
  233. }
  234. });
  235. cx.render(rsx! {
  236. div {}
  237. })
  238. }
  239. #[inline_props]
  240. fn Child(cx: Scope, name: String) -> Element {
  241. rsx!(cx, div { "child {name}" })
  242. }
  243. let mut dom = new_dom(app, ());
  244. let _ = dom.rebuild();
  245. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  246. dom.work_with_deadline(|| false);
  247. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  248. dom.work_with_deadline(|| false);
  249. }
  250. #[test]
  251. fn test_pass_thru() {
  252. #[inline_props]
  253. fn Router<'a>(cx: Scope, children: Element<'a>) -> Element {
  254. cx.render(rsx! {
  255. &cx.props.children
  256. })
  257. }
  258. fn MemoizedThing(cx: Scope) -> Element {
  259. rsx!(cx, div { "memoized" })
  260. }
  261. fn app(cx: Scope) -> Element {
  262. let thing = cx.use_hook(|_| "asd");
  263. rsx!(cx,
  264. Router {
  265. MemoizedThing {
  266. }
  267. }
  268. )
  269. }
  270. let mut dom = new_dom(app, ());
  271. let _ = dom.rebuild();
  272. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  273. dom.work_with_deadline(|| false);
  274. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  275. dom.work_with_deadline(|| false);
  276. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  277. dom.work_with_deadline(|| false);
  278. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  279. dom.work_with_deadline(|| false);
  280. }