fuzzing.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. use dioxus::prelude::Props;
  2. use dioxus_core::*;
  3. use dioxus_native_core::{
  4. node_ref::{AttributeMask, NodeView},
  5. real_dom::RealDom,
  6. state::{ParentDepState, State},
  7. NodeMask, SendAnyMap,
  8. };
  9. use dioxus_native_core_macro::{sorted_str_slice, State};
  10. use std::cell::Cell;
  11. fn random_ns() -> Option<&'static str> {
  12. let namespace = rand::random::<u8>() % 2;
  13. match namespace {
  14. 0 => None,
  15. 1 => Some(Box::leak(
  16. format!("ns{}", rand::random::<usize>()).into_boxed_str(),
  17. )),
  18. _ => unreachable!(),
  19. }
  20. }
  21. fn create_random_attribute(attr_idx: &mut usize) -> TemplateAttribute<'static> {
  22. match rand::random::<u8>() % 2 {
  23. 0 => TemplateAttribute::Static {
  24. name: Box::leak(format!("attr{}", rand::random::<usize>()).into_boxed_str()),
  25. value: Box::leak(format!("value{}", rand::random::<usize>()).into_boxed_str()),
  26. namespace: random_ns(),
  27. },
  28. 1 => TemplateAttribute::Dynamic {
  29. id: {
  30. let old_idx = *attr_idx;
  31. *attr_idx += 1;
  32. old_idx
  33. },
  34. },
  35. _ => unreachable!(),
  36. }
  37. }
  38. fn create_random_template_node(
  39. dynamic_node_types: &mut Vec<DynamicNodeType>,
  40. template_idx: &mut usize,
  41. attr_idx: &mut usize,
  42. depth: usize,
  43. ) -> TemplateNode<'static> {
  44. match rand::random::<u8>() % 4 {
  45. 0 => {
  46. let attrs = {
  47. let attrs: Vec<_> = (0..(rand::random::<usize>() % 10))
  48. .map(|_| create_random_attribute(attr_idx))
  49. .collect();
  50. Box::leak(attrs.into_boxed_slice())
  51. };
  52. TemplateNode::Element {
  53. tag: Box::leak(format!("tag{}", rand::random::<usize>()).into_boxed_str()),
  54. namespace: random_ns(),
  55. attrs,
  56. children: {
  57. if depth > 4 {
  58. &[]
  59. } else {
  60. let children: Vec<_> = (0..(rand::random::<usize>() % 3))
  61. .map(|_| {
  62. create_random_template_node(
  63. dynamic_node_types,
  64. template_idx,
  65. attr_idx,
  66. depth + 1,
  67. )
  68. })
  69. .collect();
  70. Box::leak(children.into_boxed_slice())
  71. }
  72. },
  73. }
  74. }
  75. 1 => TemplateNode::Text {
  76. text: Box::leak(format!("{}", rand::random::<usize>()).into_boxed_str()),
  77. },
  78. 2 => TemplateNode::DynamicText {
  79. id: {
  80. let old_idx = *template_idx;
  81. *template_idx += 1;
  82. dynamic_node_types.push(DynamicNodeType::Text);
  83. old_idx
  84. },
  85. },
  86. 3 => TemplateNode::Dynamic {
  87. id: {
  88. let old_idx = *template_idx;
  89. *template_idx += 1;
  90. dynamic_node_types.push(DynamicNodeType::Other);
  91. old_idx
  92. },
  93. },
  94. _ => unreachable!(),
  95. }
  96. }
  97. fn generate_paths(
  98. node: &TemplateNode<'static>,
  99. current_path: &[u8],
  100. node_paths: &mut Vec<Vec<u8>>,
  101. attr_paths: &mut Vec<Vec<u8>>,
  102. ) {
  103. match node {
  104. TemplateNode::Element {
  105. children, attrs, ..
  106. } => {
  107. for attr in *attrs {
  108. match attr {
  109. TemplateAttribute::Static { .. } => {}
  110. TemplateAttribute::Dynamic { .. } => {
  111. attr_paths.push(current_path.to_vec());
  112. }
  113. }
  114. }
  115. for (i, child) in children.iter().enumerate() {
  116. let mut current_path = current_path.to_vec();
  117. current_path.push(i as u8);
  118. generate_paths(child, &current_path, node_paths, attr_paths);
  119. }
  120. }
  121. TemplateNode::Text { .. } => {}
  122. TemplateNode::DynamicText { .. } => {
  123. node_paths.push(current_path.to_vec());
  124. }
  125. TemplateNode::Dynamic { .. } => {
  126. node_paths.push(current_path.to_vec());
  127. }
  128. }
  129. }
  130. enum DynamicNodeType {
  131. Text,
  132. Other,
  133. }
  134. fn create_random_template(name: &'static str) -> (Template<'static>, Vec<DynamicNodeType>) {
  135. let mut dynamic_node_type = Vec::new();
  136. let mut template_idx = 0;
  137. let mut attr_idx = 0;
  138. let roots = (0..(1 + rand::random::<usize>() % 5))
  139. .map(|_| {
  140. create_random_template_node(&mut dynamic_node_type, &mut template_idx, &mut attr_idx, 0)
  141. })
  142. .collect::<Vec<_>>();
  143. assert!(!roots.is_empty());
  144. let roots = Box::leak(roots.into_boxed_slice());
  145. let mut node_paths = Vec::new();
  146. let mut attr_paths = Vec::new();
  147. for (i, root) in roots.iter().enumerate() {
  148. generate_paths(root, &[i as u8], &mut node_paths, &mut attr_paths);
  149. }
  150. let node_paths = Box::leak(
  151. node_paths
  152. .into_iter()
  153. .map(|v| &*Box::leak(v.into_boxed_slice()))
  154. .collect::<Vec<_>>()
  155. .into_boxed_slice(),
  156. );
  157. let attr_paths = Box::leak(
  158. attr_paths
  159. .into_iter()
  160. .map(|v| &*Box::leak(v.into_boxed_slice()))
  161. .collect::<Vec<_>>()
  162. .into_boxed_slice(),
  163. );
  164. (
  165. Template {
  166. name,
  167. roots,
  168. node_paths,
  169. attr_paths,
  170. },
  171. dynamic_node_type,
  172. )
  173. }
  174. fn create_random_dynamic_node(cx: &ScopeState, depth: usize) -> DynamicNode {
  175. let range = if depth > 3 { 1 } else { 3 };
  176. match rand::random::<u8>() % range {
  177. 0 => DynamicNode::Placeholder(Default::default()),
  178. 1 => cx.make_node((0..(rand::random::<u8>() % 5)).map(|_| VNode {
  179. key: None,
  180. parent: Default::default(),
  181. template: Cell::new(Template {
  182. name: concat!(file!(), ":", line!(), ":", column!(), ":0"),
  183. roots: &[TemplateNode::Dynamic { id: 0 }],
  184. node_paths: &[&[0]],
  185. attr_paths: &[],
  186. }),
  187. root_ids: Default::default(),
  188. dynamic_nodes: cx.bump().alloc([cx.component(
  189. create_random_element,
  190. DepthProps { depth, root: false },
  191. "create_random_element",
  192. )]),
  193. dynamic_attrs: &[],
  194. })),
  195. 2 => cx.component(
  196. create_random_element,
  197. DepthProps { depth, root: false },
  198. "create_random_element",
  199. ),
  200. _ => unreachable!(),
  201. }
  202. }
  203. fn create_random_dynamic_attr(cx: &ScopeState) -> Attribute {
  204. let value = match rand::random::<u8>() % 6 {
  205. 0 => AttributeValue::Text(Box::leak(
  206. format!("{}", rand::random::<usize>()).into_boxed_str(),
  207. )),
  208. 1 => AttributeValue::Float(rand::random()),
  209. 2 => AttributeValue::Int(rand::random()),
  210. 3 => AttributeValue::Bool(rand::random()),
  211. 4 => cx.any_value(rand::random::<usize>()),
  212. 5 => AttributeValue::None,
  213. // Listener(RefCell<Option<ListenerCb<'a>>>),
  214. _ => unreachable!(),
  215. };
  216. Attribute {
  217. name: Box::leak(format!("attr{}", rand::random::<usize>()).into_boxed_str()),
  218. value,
  219. namespace: random_ns(),
  220. mounted_element: Default::default(),
  221. volatile: rand::random(),
  222. }
  223. }
  224. static mut TEMPLATE_COUNT: usize = 0;
  225. #[derive(PartialEq, Props)]
  226. struct DepthProps {
  227. depth: usize,
  228. root: bool,
  229. }
  230. fn create_random_element(cx: Scope<DepthProps>) -> Element {
  231. cx.needs_update();
  232. let range = if cx.props.root { 2 } else { 3 };
  233. let node = match rand::random::<usize>() % range {
  234. 0 | 1 => {
  235. let (template, dynamic_node_types) = create_random_template(Box::leak(
  236. format!(
  237. "{}{}",
  238. concat!(file!(), ":", line!(), ":", column!(), ":"),
  239. {
  240. unsafe {
  241. let old = TEMPLATE_COUNT;
  242. TEMPLATE_COUNT += 1;
  243. old
  244. }
  245. }
  246. )
  247. .into_boxed_str(),
  248. ));
  249. println!("{template:#?}");
  250. let node = VNode {
  251. key: None,
  252. parent: None,
  253. template: Cell::new(template),
  254. root_ids: Default::default(),
  255. dynamic_nodes: {
  256. let dynamic_nodes: Vec<_> = dynamic_node_types
  257. .iter()
  258. .map(|ty| match ty {
  259. DynamicNodeType::Text => DynamicNode::Text(VText {
  260. value: Box::leak(
  261. format!("{}", rand::random::<usize>()).into_boxed_str(),
  262. ),
  263. id: Default::default(),
  264. }),
  265. DynamicNodeType::Other => {
  266. create_random_dynamic_node(cx, cx.props.depth + 1)
  267. }
  268. })
  269. .collect();
  270. cx.bump().alloc(dynamic_nodes)
  271. },
  272. dynamic_attrs: cx.bump().alloc(
  273. (0..template.attr_paths.len())
  274. .map(|_| create_random_dynamic_attr(cx))
  275. .collect::<Vec<_>>(),
  276. ),
  277. };
  278. Some(node)
  279. }
  280. _ => None,
  281. };
  282. println!("{node:#?}");
  283. node
  284. }
  285. #[derive(Debug, Clone, PartialEq, Eq, Default)]
  286. pub struct BlablaState {}
  287. /// Font style are inherited by default if not specified otherwise by some of the supported attributes.
  288. impl ParentDepState for BlablaState {
  289. type Ctx = ();
  290. type DepState = (Self,);
  291. const NODE_MASK: NodeMask =
  292. NodeMask::new_with_attrs(AttributeMask::Static(&sorted_str_slice!(["blabla",])));
  293. fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
  294. false
  295. }
  296. }
  297. #[derive(Clone, State, Default, Debug)]
  298. pub struct NodeState {
  299. #[parent_dep_state(blabla)]
  300. blabla: BlablaState,
  301. }
  302. // test for panics when creating random nodes and templates
  303. #[test]
  304. fn create() {
  305. for _ in 0..100 {
  306. let mut vdom = VirtualDom::new_with_props(
  307. create_random_element,
  308. DepthProps {
  309. depth: 0,
  310. root: true,
  311. },
  312. );
  313. let mutations = vdom.rebuild();
  314. let mut rdom: RealDom<NodeState> = RealDom::new();
  315. let (to_update, _diff) = rdom.apply_mutations(mutations);
  316. let ctx = SendAnyMap::new();
  317. rdom.update_state(to_update, ctx);
  318. }
  319. }
  320. // test for panics when diffing random nodes
  321. // This test will change the template every render which is not very realistic, but it helps stress the system
  322. #[test]
  323. fn diff() {
  324. for _ in 0..10 {
  325. let mut vdom = VirtualDom::new_with_props(
  326. create_random_element,
  327. DepthProps {
  328. depth: 0,
  329. root: true,
  330. },
  331. );
  332. let mutations = vdom.rebuild();
  333. let mut rdom: RealDom<NodeState> = RealDom::new();
  334. let (to_update, _diff) = rdom.apply_mutations(mutations);
  335. let ctx = SendAnyMap::new();
  336. rdom.update_state(to_update, ctx);
  337. for _ in 0..10 {
  338. let mutations = vdom.render_immediate();
  339. let (to_update, _diff) = rdom.apply_mutations(mutations);
  340. let ctx = SendAnyMap::new();
  341. rdom.update_state(to_update, ctx);
  342. }
  343. }
  344. }