miri_stress.rs 9.7 KB

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