miri_stress.rs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. #![allow(non_snake_case)]
  2. /*
  3. Stress Miri as much as possible.
  4. Prove that we don't leak memory and that our methods are safe.
  5. Specifically:
  6. - [ ] VirtualDom drops memory safely
  7. - [ ] Borrowed components don't expose invalid pointers
  8. - [ ] Async isn't busted
  9. */
  10. use dioxus::{prelude::*, SchedulerMsg, ScopeId};
  11. use dioxus_core as dioxus;
  12. use dioxus_core_macro::*;
  13. use dioxus_html as dioxus_elements;
  14. mod test_logging;
  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! {name}" }
  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! {}", &self.val);
  123. }
  124. }
  125. let mut dom = new_dom(app, Custom { val: String::from("asd") });
  126. dom.rebuild();
  127. }
  128. #[test]
  129. fn free_works_on_borrowed() {
  130. fn app(cx: Scope) -> Element {
  131. cx.render(rsx! {
  132. child(a: "alpha", b: "asd".to_string())
  133. })
  134. }
  135. #[derive(Props)]
  136. struct ChildProps<'a> {
  137. a: &'a str,
  138. b: String,
  139. }
  140. fn child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
  141. dbg!("rendering child");
  142. rsx!(cx, "child {cx.props.a}, {cx.props.b}")
  143. }
  144. impl Drop for ChildProps<'_> {
  145. fn drop(&mut self) {
  146. dbg!("dropped child!");
  147. }
  148. }
  149. let mut dom = new_dom(app, ());
  150. let _ = dom.rebuild();
  151. }
  152. #[test]
  153. fn free_works_on_root_hooks() {
  154. /*
  155. On Drop, scopearena drops all the hook contents.
  156. */
  157. struct Droppable<T>(T);
  158. impl<T> Drop for Droppable<T> {
  159. fn drop(&mut self) {
  160. dbg!("dropping droppable");
  161. }
  162. }
  163. fn app(cx: Scope) -> Element {
  164. let name = cx.use_hook(|_| Droppable(String::from("asd")));
  165. rsx!(cx, div { "{name.0}" })
  166. }
  167. let mut dom = new_dom(app, ());
  168. let _ = dom.rebuild();
  169. }
  170. #[test]
  171. fn old_props_arent_stale() {
  172. fn app(cx: Scope) -> Element {
  173. dbg!("rendering parent");
  174. let cnt = cx.use_hook(|_| 0);
  175. *cnt += 1;
  176. if *cnt == 1 {
  177. rsx!(cx, div { child(a: "abcdef".to_string()) })
  178. } else {
  179. rsx!(cx, div { child(a: "abcdef".to_string()) })
  180. }
  181. }
  182. #[derive(Props, PartialEq)]
  183. struct ChildProps {
  184. a: String,
  185. }
  186. fn child(cx: Scope<ChildProps>) -> Element {
  187. dbg!("rendering child", &cx.props.a);
  188. rsx!(cx, div { "child {cx.props.a}" })
  189. }
  190. let mut dom = new_dom(app, ());
  191. let _ = dom.rebuild();
  192. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  193. dom.work_with_deadline(|| false);
  194. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  195. dom.work_with_deadline(|| false);
  196. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  197. dom.work_with_deadline(|| false);
  198. dbg!("forcing update to child");
  199. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  200. dom.work_with_deadline(|| false);
  201. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  202. dom.work_with_deadline(|| false);
  203. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  204. dom.work_with_deadline(|| false);
  205. }
  206. #[test]
  207. fn basic() {
  208. fn app(cx: Scope) -> Element {
  209. rsx!(cx, div {
  210. child(a: "abcdef".to_string())
  211. })
  212. }
  213. #[derive(Props, PartialEq)]
  214. struct ChildProps {
  215. a: String,
  216. }
  217. fn child(cx: Scope<ChildProps>) -> Element {
  218. dbg!("rendering child", &cx.props.a);
  219. rsx!(cx, div { "child {cx.props.a}" })
  220. }
  221. let mut dom = new_dom(app, ());
  222. let _ = dom.rebuild();
  223. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  224. dom.work_with_deadline(|| false);
  225. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  226. dom.work_with_deadline(|| false);
  227. }
  228. #[test]
  229. fn leak_thru_children() {
  230. fn app(cx: Scope) -> Element {
  231. cx.render(rsx! {
  232. Child {
  233. name: "asd".to_string(),
  234. }
  235. });
  236. cx.render(rsx! {
  237. div {}
  238. })
  239. }
  240. #[inline_props]
  241. fn Child(cx: Scope, name: String) -> Element {
  242. rsx!(cx, div { "child {name}" })
  243. }
  244. let mut dom = new_dom(app, ());
  245. let _ = dom.rebuild();
  246. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  247. dom.work_with_deadline(|| false);
  248. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  249. dom.work_with_deadline(|| false);
  250. }
  251. #[test]
  252. fn test_pass_thru() {
  253. #[inline_props]
  254. fn NavContainer<'a>(cx: Scope, children: Element<'a>) -> Element {
  255. cx.render(rsx! {
  256. header {
  257. nav { children }
  258. }
  259. })
  260. }
  261. fn NavMenu(cx: Scope) -> Element {
  262. rsx!(cx,
  263. NavBrand {}
  264. div {
  265. NavStart {}
  266. NavEnd {}
  267. }
  268. )
  269. }
  270. fn NavBrand(cx: Scope) -> Element {
  271. rsx!(cx, div {})
  272. }
  273. fn NavStart(cx: Scope) -> Element {
  274. rsx!(cx, div {})
  275. }
  276. fn NavEnd(cx: Scope) -> Element {
  277. rsx!(cx, div {})
  278. }
  279. #[inline_props]
  280. fn MainContainer<'a>(
  281. cx: Scope,
  282. nav: Element<'a>,
  283. body: Element<'a>,
  284. footer: Element<'a>,
  285. ) -> Element {
  286. cx.render(rsx! {
  287. div {
  288. class: "columns is-mobile",
  289. div {
  290. class: "column is-full",
  291. nav,
  292. body,
  293. footer,
  294. }
  295. }
  296. })
  297. }
  298. fn app(cx: Scope) -> Element {
  299. let nav = cx.render(rsx! {
  300. NavContainer {
  301. NavMenu {}
  302. }
  303. });
  304. let body = cx.render(rsx! {
  305. div {}
  306. });
  307. let footer = cx.render(rsx! {
  308. div {}
  309. });
  310. cx.render(rsx! {
  311. MainContainer {
  312. nav: nav,
  313. body: body,
  314. footer: footer,
  315. }
  316. })
  317. }
  318. let mut dom = new_dom(app, ());
  319. let _ = dom.rebuild();
  320. for _ in 0..40 {
  321. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  322. dom.work_with_deadline(|| false);
  323. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  324. dom.work_with_deadline(|| false);
  325. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  326. dom.work_with_deadline(|| false);
  327. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  328. dom.work_with_deadline(|| false);
  329. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  330. dom.work_with_deadline(|| false);
  331. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  332. dom.work_with_deadline(|| false);
  333. dom.handle_message(SchedulerMsg::Immediate(ScopeId(2)));
  334. dom.work_with_deadline(|| false);
  335. dom.handle_message(SchedulerMsg::Immediate(ScopeId(2)));
  336. dom.work_with_deadline(|| false);
  337. dom.handle_message(SchedulerMsg::Immediate(ScopeId(2)));
  338. dom.work_with_deadline(|| false);
  339. dom.handle_message(SchedulerMsg::Immediate(ScopeId(3)));
  340. dom.work_with_deadline(|| false);
  341. dom.handle_message(SchedulerMsg::Immediate(ScopeId(3)));
  342. dom.work_with_deadline(|| false);
  343. dom.handle_message(SchedulerMsg::Immediate(ScopeId(3)));
  344. dom.work_with_deadline(|| false);
  345. }
  346. }