1
0

miri_stress.rs 9.8 KB

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