miri_stress.rs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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::*;
  11. use dioxus_core::SchedulerMsg;
  12. fn new_dom<P: 'static + Send>(app: Component<P>, props: P) -> VirtualDom {
  13. VirtualDom::new_with_props(app, props)
  14. }
  15. /// This test ensures that if a component aborts early, it is replaced with a placeholder.
  16. /// In debug, this should also toss a warning.
  17. #[test]
  18. #[ignore]
  19. fn test_memory_leak() {
  20. fn app(cx: Scope) -> Element {
  21. let val = cx.use_hook(|| 0);
  22. *val += 1;
  23. if *val == 2 || *val == 4 {
  24. return None;
  25. }
  26. let name = cx.use_hook(|| String::from("asd"));
  27. cx.render(rsx!(
  28. div { "Hello, world!" }
  29. Child {}
  30. Child {}
  31. Child {}
  32. Child {}
  33. Child {}
  34. Child {}
  35. BorrowedChild { na: name }
  36. BorrowedChild { na: name }
  37. BorrowedChild { na: name }
  38. BorrowedChild { na: name }
  39. BorrowedChild { na: name }
  40. ))
  41. }
  42. #[derive(Props)]
  43. struct BorrowedProps<'a> {
  44. na: &'a str,
  45. }
  46. fn BorrowedChild<'a>(cx: Scope<'a, BorrowedProps<'a>>) -> Element {
  47. render!(div {
  48. "goodbye {cx.props.na}"
  49. Child {}
  50. Child {}
  51. })
  52. }
  53. fn Child(cx: Scope) -> Element {
  54. render!(div { "goodbye world" })
  55. }
  56. let mut dom = new_dom(app, ());
  57. dom.rebuild();
  58. dom.hard_diff(ScopeId(0));
  59. dom.hard_diff(ScopeId(0));
  60. dom.hard_diff(ScopeId(0));
  61. dom.hard_diff(ScopeId(0));
  62. dom.hard_diff(ScopeId(0));
  63. dom.hard_diff(ScopeId(0));
  64. }
  65. #[test]
  66. fn memo_works_properly() {
  67. fn app(cx: Scope) -> Element {
  68. let val = cx.use_hook(|| 0);
  69. *val += 1;
  70. if *val == 2 || *val == 4 {
  71. return None;
  72. }
  73. let name = cx.use_hook(|| String::from("asd"));
  74. cx.render(rsx!(
  75. div { "Hello, world! {name}" }
  76. Child { na: "asdfg".to_string() }
  77. ))
  78. }
  79. #[derive(PartialEq, Props)]
  80. struct ChildProps {
  81. na: String,
  82. }
  83. fn Child(cx: Scope<ChildProps>) -> Element {
  84. render!(div { "goodbye world" })
  85. }
  86. let mut dom = new_dom(app, ());
  87. dom.rebuild();
  88. dom.hard_diff(ScopeId(0));
  89. dom.hard_diff(ScopeId(0));
  90. dom.hard_diff(ScopeId(0));
  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. }
  96. #[test]
  97. fn free_works_on_root_props() {
  98. fn app(cx: Scope<Custom>) -> Element {
  99. cx.render(rsx! {
  100. Child { a: "alpha"}
  101. Child { a: "beta"}
  102. Child { a: "gamma"}
  103. Child { a: "delta"}
  104. })
  105. }
  106. #[derive(Props, PartialEq)]
  107. struct ChildProps {
  108. a: &'static str,
  109. }
  110. fn Child(cx: Scope<ChildProps>) -> Element {
  111. render!("child {cx.props.a}")
  112. }
  113. struct Custom {
  114. val: String,
  115. }
  116. impl Drop for Custom {
  117. fn drop(&mut self) {
  118. dbg!("dropped! {}", &self.val);
  119. }
  120. }
  121. let mut dom = new_dom(app, Custom { val: String::from("asd") });
  122. dom.rebuild();
  123. }
  124. #[test]
  125. fn free_works_on_borrowed() {
  126. fn app(cx: Scope) -> Element {
  127. cx.render(rsx! {
  128. Child { a: "alpha", b: "asd".to_string() }
  129. })
  130. }
  131. #[derive(Props)]
  132. struct ChildProps<'a> {
  133. a: &'a str,
  134. b: String,
  135. }
  136. fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
  137. dbg!("rendering child");
  138. render!("child {cx.props.a}, {cx.props.b}")
  139. }
  140. impl Drop for ChildProps<'_> {
  141. fn drop(&mut self) {
  142. dbg!("dropped child!");
  143. }
  144. }
  145. let mut dom = new_dom(app, ());
  146. let _ = dom.rebuild();
  147. }
  148. #[test]
  149. fn free_works_on_root_hooks() {
  150. /*
  151. On Drop, scopearena drops all the hook contents.
  152. */
  153. struct Droppable<T>(T);
  154. impl<T> Drop for Droppable<T> {
  155. fn drop(&mut self) {
  156. dbg!("dropping droppable");
  157. }
  158. }
  159. fn app(cx: Scope) -> Element {
  160. let name = cx.use_hook(|| Droppable(String::from("asd")));
  161. render!(div { "{name.0}" })
  162. }
  163. let mut dom = new_dom(app, ());
  164. let _ = dom.rebuild();
  165. }
  166. #[test]
  167. fn old_props_arent_stale() {
  168. fn app(cx: Scope) -> Element {
  169. dbg!("rendering parent");
  170. let cnt = cx.use_hook(|| 0);
  171. *cnt += 1;
  172. if *cnt == 1 {
  173. render!(div { Child { a: "abcdef".to_string() } })
  174. } else {
  175. render!(div { Child { a: "abcdef".to_string() } })
  176. }
  177. }
  178. #[derive(Props, PartialEq)]
  179. struct ChildProps {
  180. a: String,
  181. }
  182. fn Child(cx: Scope<ChildProps>) -> Element {
  183. dbg!("rendering child", &cx.props.a);
  184. render!(div { "child {cx.props.a}" })
  185. }
  186. let mut dom = new_dom(app, ());
  187. let _ = dom.rebuild();
  188. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  189. dom.work_with_deadline(|| false);
  190. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  191. dom.work_with_deadline(|| false);
  192. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  193. dom.work_with_deadline(|| false);
  194. dbg!("forcing update to child");
  195. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  196. dom.work_with_deadline(|| false);
  197. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  198. dom.work_with_deadline(|| false);
  199. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  200. dom.work_with_deadline(|| false);
  201. }
  202. #[test]
  203. fn basic() {
  204. fn app(cx: Scope) -> Element {
  205. render!(div {
  206. Child { a: "abcdef".to_string() }
  207. })
  208. }
  209. #[derive(Props, PartialEq)]
  210. struct ChildProps {
  211. a: String,
  212. }
  213. fn Child(cx: Scope<ChildProps>) -> Element {
  214. dbg!("rendering child", &cx.props.a);
  215. render!(div { "child {cx.props.a}" })
  216. }
  217. let mut dom = new_dom(app, ());
  218. let _ = dom.rebuild();
  219. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  220. dom.work_with_deadline(|| false);
  221. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  222. dom.work_with_deadline(|| false);
  223. }
  224. #[test]
  225. fn leak_thru_children() {
  226. fn app(cx: Scope) -> Element {
  227. cx.render(rsx! {
  228. Child {
  229. name: "asd".to_string(),
  230. }
  231. });
  232. cx.render(rsx! {
  233. div {}
  234. })
  235. }
  236. #[inline_props]
  237. fn Child(cx: Scope, name: String) -> Element {
  238. render!(div { "child {name}" })
  239. }
  240. let mut dom = new_dom(app, ());
  241. let _ = dom.rebuild();
  242. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  243. dom.work_with_deadline(|| false);
  244. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  245. dom.work_with_deadline(|| false);
  246. }
  247. #[test]
  248. fn test_pass_thru() {
  249. #[inline_props]
  250. fn NavContainer<'a>(cx: Scope, children: Element<'a>) -> Element {
  251. cx.render(rsx! {
  252. header {
  253. nav { children }
  254. }
  255. })
  256. }
  257. fn NavMenu(cx: Scope) -> Element {
  258. render!( NavBrand {}
  259. div {
  260. NavStart {}
  261. NavEnd {}
  262. }
  263. )
  264. }
  265. fn NavBrand(cx: Scope) -> Element {
  266. render!(div {})
  267. }
  268. fn NavStart(cx: Scope) -> Element {
  269. render!(div {})
  270. }
  271. fn NavEnd(cx: Scope) -> Element {
  272. render!(div {})
  273. }
  274. #[inline_props]
  275. fn MainContainer<'a>(
  276. cx: Scope,
  277. nav: Element<'a>,
  278. body: Element<'a>,
  279. footer: Element<'a>,
  280. ) -> Element {
  281. cx.render(rsx! {
  282. div {
  283. class: "columns is-mobile",
  284. div {
  285. class: "column is-full",
  286. nav,
  287. body,
  288. footer,
  289. }
  290. }
  291. })
  292. }
  293. fn app(cx: Scope) -> Element {
  294. let nav = cx.render(rsx! {
  295. NavContainer {
  296. NavMenu {}
  297. }
  298. });
  299. let body = cx.render(rsx! {
  300. div {}
  301. });
  302. let footer = cx.render(rsx! {
  303. div {}
  304. });
  305. cx.render(rsx! {
  306. MainContainer {
  307. nav: nav,
  308. body: body,
  309. footer: footer,
  310. }
  311. })
  312. }
  313. let mut dom = new_dom(app, ());
  314. let _ = dom.rebuild();
  315. for _ in 0..40 {
  316. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  317. dom.work_with_deadline(|| false);
  318. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  319. dom.work_with_deadline(|| false);
  320. dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
  321. dom.work_with_deadline(|| false);
  322. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  323. dom.work_with_deadline(|| false);
  324. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  325. dom.work_with_deadline(|| false);
  326. dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
  327. dom.work_with_deadline(|| false);
  328. dom.handle_message(SchedulerMsg::Immediate(ScopeId(2)));
  329. dom.work_with_deadline(|| false);
  330. dom.handle_message(SchedulerMsg::Immediate(ScopeId(2)));
  331. dom.work_with_deadline(|| false);
  332. dom.handle_message(SchedulerMsg::Immediate(ScopeId(2)));
  333. dom.work_with_deadline(|| false);
  334. dom.handle_message(SchedulerMsg::Immediate(ScopeId(3)));
  335. dom.work_with_deadline(|| false);
  336. dom.handle_message(SchedulerMsg::Immediate(ScopeId(3)));
  337. dom.work_with_deadline(|| false);
  338. dom.handle_message(SchedulerMsg::Immediate(ScopeId(3)));
  339. dom.work_with_deadline(|| false);
  340. }
  341. }