dioxus/packages/core/tests/fuzzing.rs

372 lines
12 KiB
Rust
Raw Normal View History

#![cfg(not(miri))]
2024-01-11 21:18:11 +00:00
use dioxus::prelude::*;
2024-01-15 16:21:44 +00:00
use dioxus_core::{
prelude::EventHandler, AttributeValue, DynamicNode, NoOpMutations, VComponent, VNode, *,
};
2024-01-11 21:18:11 +00:00
use std::{cfg, collections::HashSet, default::Default};
2023-01-05 16:28:07 +00:00
fn random_ns() -> Option<&'static str> {
let namespace = rand::random::<u8>() % 2;
match namespace {
0 => None,
1 => Some(Box::leak(
format!("ns{}", rand::random::<usize>()).into_boxed_str(),
)),
_ => unreachable!(),
}
}
2024-01-05 14:39:22 +00:00
fn create_random_attribute(attr_idx: &mut usize) -> TemplateAttribute {
2023-01-05 16:28:07 +00:00
match rand::random::<u8>() % 2 {
0 => TemplateAttribute::Static {
name: Box::leak(format!("attr{}", rand::random::<usize>()).into_boxed_str()),
value: Box::leak(format!("value{}", rand::random::<usize>()).into_boxed_str()),
namespace: random_ns(),
},
1 => TemplateAttribute::Dynamic {
id: {
let old_idx = *attr_idx;
*attr_idx += 1;
old_idx
},
},
_ => unreachable!(),
}
}
fn create_random_template_node(
2023-01-12 22:34:46 +00:00
dynamic_node_types: &mut Vec<DynamicNodeType>,
2023-01-05 16:28:07 +00:00
template_idx: &mut usize,
attr_idx: &mut usize,
depth: usize,
2024-01-11 21:18:11 +00:00
) -> TemplateNode {
2023-01-12 21:28:10 +00:00
match rand::random::<u8>() % 4 {
2023-01-05 16:28:07 +00:00
0 => {
let attrs = {
let attrs: Vec<_> = (0..(rand::random::<usize>() % 10))
.map(|_| create_random_attribute(attr_idx))
.collect();
Box::leak(attrs.into_boxed_slice())
};
TemplateNode::Element {
tag: Box::leak(format!("tag{}", rand::random::<usize>()).into_boxed_str()),
namespace: random_ns(),
attrs,
children: {
2023-01-12 22:34:46 +00:00
if depth > 4 {
2023-01-05 16:28:07 +00:00
&[]
} else {
let children: Vec<_> = (0..(rand::random::<usize>() % 3))
2023-01-12 22:34:46 +00:00
.map(|_| {
create_random_template_node(
dynamic_node_types,
template_idx,
attr_idx,
depth + 1,
)
})
2023-01-05 16:28:07 +00:00
.collect();
Box::leak(children.into_boxed_slice())
}
},
}
}
1 => TemplateNode::Text {
text: Box::leak(format!("{}", rand::random::<usize>()).into_boxed_str()),
},
2 => TemplateNode::DynamicText {
id: {
let old_idx = *template_idx;
*template_idx += 1;
2023-01-12 22:34:46 +00:00
dynamic_node_types.push(DynamicNodeType::Text);
2023-01-05 16:28:07 +00:00
old_idx
},
},
2023-01-12 21:28:10 +00:00
3 => TemplateNode::Dynamic {
2023-01-05 16:28:07 +00:00
id: {
let old_idx = *template_idx;
*template_idx += 1;
2023-01-12 22:34:46 +00:00
dynamic_node_types.push(DynamicNodeType::Other);
2023-01-05 16:28:07 +00:00
old_idx
},
},
_ => unreachable!(),
}
}
fn generate_paths(
2024-01-11 21:18:11 +00:00
node: &TemplateNode,
2023-01-12 21:53:33 +00:00
current_path: &[u8],
2023-01-05 16:28:07 +00:00
node_paths: &mut Vec<Vec<u8>>,
attr_paths: &mut Vec<Vec<u8>>,
) {
match node {
TemplateNode::Element { children, attrs, .. } => {
for attr in *attrs {
match attr {
TemplateAttribute::Static { .. } => {}
TemplateAttribute::Dynamic { .. } => {
2023-01-12 21:53:33 +00:00
attr_paths.push(current_path.to_vec());
2023-01-05 16:28:07 +00:00
}
}
}
for (i, child) in children.iter().enumerate() {
2023-01-12 21:53:33 +00:00
let mut current_path = current_path.to_vec();
2023-01-05 16:28:07 +00:00
current_path.push(i as u8);
generate_paths(child, &current_path, node_paths, attr_paths);
}
}
TemplateNode::Text { .. } => {}
TemplateNode::DynamicText { .. } => {
2023-01-12 21:53:33 +00:00
node_paths.push(current_path.to_vec());
2023-01-05 16:28:07 +00:00
}
TemplateNode::Dynamic { .. } => {
2023-01-12 21:53:33 +00:00
node_paths.push(current_path.to_vec());
2023-01-05 16:28:07 +00:00
}
}
}
2023-01-12 22:34:46 +00:00
enum DynamicNodeType {
Text,
Other,
}
fn create_random_template(name: &'static str) -> (Template, Vec<DynamicNodeType>) {
2023-01-12 22:34:46 +00:00
let mut dynamic_node_type = Vec::new();
2023-01-05 16:28:07 +00:00
let mut template_idx = 0;
let mut attr_idx = 0;
let roots = (0..(1 + rand::random::<usize>() % 5))
2023-01-12 22:34:46 +00:00
.map(|_| {
create_random_template_node(&mut dynamic_node_type, &mut template_idx, &mut attr_idx, 0)
})
2023-01-05 16:28:07 +00:00
.collect::<Vec<_>>();
2023-01-12 21:53:33 +00:00
assert!(!roots.is_empty());
2023-01-05 16:28:07 +00:00
let roots = Box::leak(roots.into_boxed_slice());
let mut node_paths = Vec::new();
let mut attr_paths = Vec::new();
for (i, root) in roots.iter().enumerate() {
2023-01-12 21:53:33 +00:00
generate_paths(root, &[i as u8], &mut node_paths, &mut attr_paths);
2023-01-05 16:28:07 +00:00
}
let node_paths = Box::leak(
node_paths
.into_iter()
.map(|v| &*Box::leak(v.into_boxed_slice()))
.collect::<Vec<_>>()
.into_boxed_slice(),
);
let attr_paths = Box::leak(
attr_paths
.into_iter()
.map(|v| &*Box::leak(v.into_boxed_slice()))
.collect::<Vec<_>>()
.into_boxed_slice(),
);
2023-01-12 22:34:46 +00:00
(
Template { name, roots, node_paths, attr_paths },
dynamic_node_type,
)
2023-01-05 16:28:07 +00:00
}
2024-01-11 21:18:11 +00:00
fn create_random_dynamic_node(depth: usize) -> DynamicNode {
let range = if depth > 5 { 1 } else { 3 };
2023-01-05 16:28:07 +00:00
match rand::random::<u8>() % range {
2023-01-12 22:34:46 +00:00
0 => DynamicNode::Placeholder(Default::default()),
2024-01-11 21:18:11 +00:00
1 => (0..(rand::random::<u8>() % 5))
.map(|_| {
VNode::new(
None,
Template {
2024-01-15 16:52:30 +00:00
name: create_template_location(),
2024-01-11 21:18:11 +00:00
roots: &[TemplateNode::Dynamic { id: 0 }],
node_paths: &[&[0]],
attr_paths: &[],
},
Box::new([DynamicNode::Component(VComponent::new(
create_random_element,
DepthProps { depth, root: false },
"create_random_element",
))]),
Box::new([]),
)
})
.into_dyn_node(),
2 => DynamicNode::Component(VComponent::new(
2023-01-05 16:28:07 +00:00
create_random_element,
2023-01-08 00:52:37 +00:00
DepthProps { depth, root: false },
2023-01-05 16:28:07 +00:00
"create_random_element",
2024-01-11 21:18:11 +00:00
)),
2023-01-05 16:28:07 +00:00
_ => unreachable!(),
}
}
2024-01-11 21:18:11 +00:00
fn create_random_dynamic_attr() -> Attribute {
2023-06-02 19:20:03 +00:00
let value = match rand::random::<u8>() % 7 {
2024-01-11 21:18:11 +00:00
0 => AttributeValue::Text(format!("{}", rand::random::<usize>())),
2023-01-05 16:28:07 +00:00
1 => AttributeValue::Float(rand::random()),
2 => AttributeValue::Int(rand::random()),
3 => AttributeValue::Bool(rand::random()),
2024-01-11 21:18:11 +00:00
4 => AttributeValue::any_value(rand::random::<usize>()),
2023-01-05 16:28:07 +00:00
5 => AttributeValue::None,
2023-06-02 19:20:03 +00:00
6 => {
2024-01-11 21:18:11 +00:00
let value = AttributeValue::listener(|e: Event<String>| println!("{:?}", e));
return Attribute::new("ondata", value, None, false);
2023-06-02 19:20:03 +00:00
}
2023-01-05 16:28:07 +00:00
_ => unreachable!(),
};
Attribute::new(
Box::leak(format!("attr{}", rand::random::<usize>()).into_boxed_str()),
2023-01-05 16:28:07 +00:00
value,
random_ns(),
rand::random(),
)
.into()
2023-01-05 16:28:07 +00:00
}
static mut TEMPLATE_COUNT: usize = 0;
2024-01-15 16:52:30 +00:00
fn create_template_location() -> &'static str {
Box::leak(
format!(
"{}{}",
concat!(file!(), ":", line!(), ":", column!(), ":"),
{
unsafe {
let old = TEMPLATE_COUNT;
TEMPLATE_COUNT += 1;
old
}
}
)
.into_boxed_str(),
)
}
2024-01-11 21:18:11 +00:00
#[derive(PartialEq, Props, Clone)]
2023-01-05 16:28:07 +00:00
struct DepthProps {
depth: usize,
2023-01-08 00:52:37 +00:00
root: bool,
2023-01-05 16:28:07 +00:00
}
2024-01-11 21:18:11 +00:00
fn create_random_element(cx: DepthProps) -> Element {
2023-06-02 19:20:03 +00:00
if rand::random::<usize>() % 10 == 0 {
2024-01-11 21:18:11 +00:00
needs_update();
2023-06-02 19:20:03 +00:00
}
2024-01-11 17:11:44 +00:00
let range = if cx.root { 2 } else { 3 };
2023-01-08 00:52:37 +00:00
let node = match rand::random::<usize>() % range {
2023-01-05 16:28:07 +00:00
0 | 1 => {
2024-01-15 16:52:30 +00:00
let (template, dynamic_node_types) = create_random_template(create_template_location());
2024-01-14 21:21:19 +00:00
let node = VNode::new(
None,
template,
2024-01-11 21:18:11 +00:00
dynamic_node_types
.iter()
.map(|ty| match ty {
DynamicNodeType::Text => {
DynamicNode::Text(VText::new(format!("{}", rand::random::<usize>())))
}
DynamicNodeType::Other => create_random_dynamic_node(cx.depth + 1),
})
.collect(),
(0..template.attr_paths.len())
2024-01-14 21:21:19 +00:00
.map(|_| Box::new([create_random_dynamic_attr()]) as Box<[Attribute]>)
2024-01-11 21:18:11 +00:00
.collect(),
);
2023-01-05 16:28:07 +00:00
Some(node)
}
_ => None,
2023-01-08 00:52:37 +00:00
};
2023-06-02 19:20:03 +00:00
// println!("{node:#?}");
2023-01-08 00:52:37 +00:00
node
2023-01-05 16:28:07 +00:00
}
// test for panics when creating random nodes and templates
#[test]
fn create() {
let repeat_count = if cfg!(miri) { 100 } else { 1000 };
for _ in 0..repeat_count {
2023-01-08 00:52:37 +00:00
let mut vdom =
VirtualDom::new_with_props(create_random_element, DepthProps { depth: 0, root: true });
2024-01-15 16:21:44 +00:00
vdom.rebuild(&mut NoOpMutations);
2023-01-05 16:28:07 +00:00
}
}
// test for panics when diffing random nodes
// This test will change the template every render which is not very realistic, but it helps stress the system
#[test]
fn diff() {
let repeat_count = if cfg!(miri) { 100 } else { 1000 };
for _ in 0..repeat_count {
2023-01-08 00:52:37 +00:00
let mut vdom =
VirtualDom::new_with_props(create_random_element, DepthProps { depth: 0, root: true });
2024-01-15 16:21:44 +00:00
vdom.rebuild(&mut NoOpMutations);
2023-06-02 19:20:03 +00:00
// A list of all elements that have had event listeners
// This is intentionally never cleared, so that we can test that calling event listeners that are removed doesn't cause a panic
let mut event_listeners = HashSet::new();
for _ in 0..100 {
for &id in &event_listeners {
println!("firing event on {:?}", id);
vdom.handle_event(
"data",
std::rc::Rc::new(String::from("hello world")),
id,
true,
);
}
{
2024-01-15 16:52:30 +00:00
vdom.render_immediate(&mut InsertEventListenerMutationHandler(
&mut event_listeners,
));
2023-06-02 19:20:03 +00:00
}
2023-01-05 16:28:07 +00:00
}
}
}
2024-01-15 16:52:30 +00:00
struct InsertEventListenerMutationHandler<'a>(&'a mut HashSet<ElementId>);
impl WriteMutations for InsertEventListenerMutationHandler<'_> {
fn register_template(&mut self, _: Template) {}
fn append_children(&mut self, _: ElementId, _: usize) {}
fn assign_node_id(&mut self, _: &'static [u8], _: ElementId) {}
fn create_placeholder(&mut self, _: ElementId) {}
fn create_text_node(&mut self, _: &str, _: ElementId) {}
fn hydrate_text_node(&mut self, _: &'static [u8], _: &str, _: ElementId) {}
fn load_template(&mut self, _: &'static str, _: usize, _: ElementId) {}
fn replace_node_with(&mut self, _: ElementId, _: usize) {}
fn replace_placeholder_with_nodes(&mut self, _: &'static [u8], _: usize) {}
fn insert_nodes_after(&mut self, _: ElementId, _: usize) {}
fn insert_nodes_before(&mut self, _: ElementId, _: usize) {}
fn set_attribute(
&mut self,
_: &'static str,
_: Option<&'static str>,
_: &AttributeValue,
_: ElementId,
) {
}
fn set_node_text(&mut self, _: &str, _: ElementId) {}
fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
println!("new event listener on {:?} for {:?}", id, name);
self.0.insert(id);
}
fn remove_event_listener(&mut self, _: &'static str, _: ElementId) {}
fn remove_node(&mut self, _: ElementId) {}
fn push_root(&mut self, _: ElementId) {}
}