From e09e8e899f1951f4067daa982d77d69862381935 Mon Sep 17 00:00:00 2001 From: Jeremy Arnold Date: Thu, 15 Feb 2024 21:54:53 -0800 Subject: [PATCH 1/6] Beginning work on DioxusLabs/dioxus#1161: fix a few typos --- packages/core/src/virtual_dom.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 2de7f05144..6c64ea59bc 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -478,7 +478,7 @@ impl VirtualDom { /// /// The caller must ensure that the template references the same dynamic attributes and nodes as the original template. /// - /// This will only replace the the parent template, not any nested templates. + /// This will only replace the parent template, not any nested templates. pub fn replace_template(&mut self, template: Template) { self.register_template_first_byte_index(template); // iterating a slab is very inefficient, but this is a rare operation that will only happen during development so it's fine @@ -518,7 +518,7 @@ impl VirtualDom { /// The mutations item expects the RealDom's stack to be the root of the application. /// /// Tasks will not be polled with this method, nor will any events be processed from the event queue. Instead, the - /// root component will be ran once and then diffed. All updates will flow out as mutations. + /// root component will be run once and then diffed. All updates will flow out as mutations. /// /// All state stored in components will be completely wiped away. /// @@ -584,7 +584,7 @@ impl VirtualDom { /// The mutations will be thrown out, so it's best to use this method for things like SSR that have async content /// /// We don't call "flush_sync" here since there's no sync work to be done. Futures will be progressed like usual, - /// however any futures wating on flush_sync will remain pending + /// however any futures waiting on flush_sync will remain pending pub async fn wait_for_suspense(&mut self) { loop { if self.suspended_scopes.is_empty() { From 7990ce60009eab04a6ac871d2c1505f3aa2d8fd6 Mon Sep 17 00:00:00 2001 From: Jeremy Arnold Date: Fri, 16 Feb 2024 09:22:45 -0800 Subject: [PATCH 2/6] Add tracing-fluent-assertion dependency. Start adding spans to some virtual_dom functions. --- Cargo.lock | 13 +++ packages/core/Cargo.toml | 2 + packages/core/src/virtual_dom.rs | 11 ++- packages/core/tests/tracing.rs | 137 +++++++++++++++++++++++++++++++ 4 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 packages/core/tests/tracing.rs diff --git a/Cargo.lock b/Cargo.lock index 478626fa89..7987e3ab2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2479,6 +2479,8 @@ dependencies = [ "slab", "tokio", "tracing", + "tracing-fluent-assertions", + "tracing-subscriber", ] [[package]] @@ -10392,6 +10394,17 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-fluent-assertions" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12de1a8c6bcfee614305e836308b596bbac831137a04c61f7e5b0b0bf2cfeaf6" +dependencies = [ + "tracing", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "tracing-futures" version = "0.2.5" diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 01ed1847d4..765f9b12f5 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -20,9 +20,11 @@ slab = { workspace = true } futures-channel = { workspace = true } tracing = { workspace = true } serde = { version = "1", features = ["derive"], optional = true } +tracing-subscriber = "0.3.18" [dev-dependencies] tokio = { workspace = true, features = ["full"] } +tracing-fluent-assertions = "0.3.0" dioxus = { workspace = true } pretty_assertions = "1.3.0" rand = "0.8.5" diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 6c64ea59bc..206d0e62a2 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -19,6 +19,7 @@ use futures_util::StreamExt; use rustc_hash::{FxHashMap, FxHashSet}; use slab::Slab; use std::{any::Any, collections::BTreeSet, rc::Rc}; +use tracing::instrument; /// A virtual node system that progresses user events and diffs UI trees. /// @@ -225,6 +226,7 @@ impl VirtualDom { /// ``` /// /// Note: the VirtualDom is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it. + #[instrument(level = "trace", name = "VirtualDom::new")] pub fn new(app: fn() -> Element) -> Self { Self::new_with_props(app, ()) } @@ -373,7 +375,7 @@ impl VirtualDom { return; }; - tracing::trace!("Marking scope {:?} ({}) as dirty", id, scope.name); + tracing::event!(tracing::Level::TRACE, "Marking scope {:?} ({}) as dirty", id, scope.name); self.dirty_scopes.insert(DirtyScope { height: scope.height(), id, @@ -389,6 +391,7 @@ impl VirtualDom { /// It is up to the listeners themselves to mark nodes as dirty. /// /// If you have multiple events, you can call this method multiple times before calling "render_with_deadline" + #[instrument(skip(self), level = "trace", name = "VirtualDom::handle_event")] pub fn handle_event( &mut self, name: &str, @@ -422,12 +425,14 @@ impl VirtualDom { /// ```rust, ignore /// let dom = VirtualDom::new(app); /// ``` + #[instrument(skip(self), level = "trace", name = "VirtualDom::wait_for_work")] pub async fn wait_for_work(&mut self) { // And then poll the futures self.poll_tasks().await; } /// + #[instrument(skip(self), level = "trace", name = "VirtualDom::poll_tasks")] async fn poll_tasks(&mut self) { // Release the flush lock // This will cause all the flush wakers to immediately spring to life, which we will off with process_events @@ -461,6 +466,7 @@ impl VirtualDom { } /// Process all events in the queue until there are no more left + #[instrument(skip(self), level = "trace", name = "VirtualDom::process_events")] pub fn process_events(&mut self) { let _runtime = RuntimeGuard::new(self.runtime.clone()); @@ -479,6 +485,7 @@ impl VirtualDom { /// The caller must ensure that the template references the same dynamic attributes and nodes as the original template. /// /// This will only replace the parent template, not any nested templates. + #[instrument(skip(self), level = "trace", name = "VirtualDom::replace_template")] pub fn replace_template(&mut self, template: Template) { self.register_template_first_byte_index(template); // iterating a slab is very inefficient, but this is a rare operation that will only happen during development so it's fine @@ -533,6 +540,7 @@ impl VirtualDom { /// /// apply_edits(edits); /// ``` + #[instrument(skip(self, to), level = "trace", name = "VirtualDom::rebuild")] pub fn rebuild(&mut self, to: &mut impl WriteMutations) { self.flush_templates(to); let _runtime = RuntimeGuard::new(self.runtime.clone()); @@ -546,6 +554,7 @@ impl VirtualDom { /// Render whatever the VirtualDom has ready as fast as possible without requiring an executor to progress /// suspended subtrees. + #[instrument(skip(self, to), level = "trace", name = "VirtualDom::render_immediate")] pub fn render_immediate(&mut self, to: &mut impl WriteMutations) { self.flush_templates(to); diff --git a/packages/core/tests/tracing.rs b/packages/core/tests/tracing.rs new file mode 100644 index 0000000000..a92c4e260a --- /dev/null +++ b/packages/core/tests/tracing.rs @@ -0,0 +1,137 @@ +use std::rc::Rc; +use dioxus::dioxus_core::Mutation::*; +use dioxus::prelude::*; +use dioxus_core::ElementId; +use tracing_fluent_assertions::{AssertionsLayer, AssertionRegistry}; +use tracing_subscriber::{layer::SubscriberExt, Registry}; +use dioxus::html::SerializedHtmlEventConverter; + + +#[test] +fn miri_rollover() { + // setup tracing + let assertion_registry = AssertionRegistry::default(); + let base_subscriber = Registry::default(); + let std_out_log = tracing_subscriber::fmt::layer().pretty(); + let subscriber = base_subscriber + .with(std_out_log) + .with(AssertionsLayer::new(&assertion_registry)); + tracing::subscriber::set_global_default(subscriber).unwrap(); + + let new_virtual_dom = assertion_registry.build() + .with_name("VirtualDom::new") + .was_created() + .was_entered_exactly(1) + .was_closed() + .finalize(); + + let edited_virtual_dom = assertion_registry.build() + .with_name("VirtualDom::rebuild") + .was_created() + .was_entered_exactly(1) + .was_closed() + .finalize(); + + set_event_converter(Box::new(SerializedHtmlEventConverter)); + let mut dom = VirtualDom::new(app); + + dom.rebuild(&mut dioxus_core::NoOpMutations); + + new_virtual_dom.assert(); + edited_virtual_dom.assert(); + + for _ in 0..3 { + dom.handle_event( + "click", + Rc::new(PlatformEventData::new(Box::::default())), + ElementId(2), + true, + ); + dom.process_events(); + _ = dom.render_immediate_to_vec(); + } +} + +fn app() -> Element { + let mut idx = use_signal(|| 0); + let onhover = |_| println!("go!"); + + rsx! { + div { + button { + onclick: move |_| { + idx += 1; + println!("Clicked"); + }, + "+" + } + button { onclick: move |_| idx -= 1, "-" } + ul { + {(0..idx()).map(|i| rsx! { + ChildExample { i: i, onhover: onhover } + })} + } + } + } +} + +#[component] +fn ChildExample(i: i32, onhover: EventHandler) -> Element { + rsx! { li { onmouseover: move |e| onhover.call(e), "{i}" } } +} + + + + + + +#[test] +fn test_original_diff() { + // Create the assertion registry and install the assertion layer, + // then install that subscriber as the global default. + let assertion_registry = AssertionRegistry::default(); + let base_subscriber = Registry::default(); + // create a std out logger just to help with testing + let std_out_log = tracing_subscriber::fmt::layer().pretty(); + let subscriber = base_subscriber + .with(std_out_log) + .with(AssertionsLayer::new(&assertion_registry)); + tracing::subscriber::set_global_default(subscriber).unwrap(); + + let new_virtual_dom = assertion_registry.build() + .with_name("VirtualDom::new") + .was_created() + .was_entered_exactly(1) + .was_exited() + .was_closed() + .finalize(); + + let edited_virtual_dom = assertion_registry.build() + .with_name("VirtualDom::rebuild") + .was_created() + .was_entered_exactly(1) + .was_exited() + .was_closed() + .finalize(); + + let mut dom = VirtualDom::new(|| { + rsx! { + div { div { "Hello, world!" } } + } + }); + + + let edits = dom.rebuild_to_vec().santize(); + + new_virtual_dom.assert(); + edited_virtual_dom.assert(); + + assert_eq!( + edits.edits, + [ + // add to root + LoadTemplate { name: "template", index: 0, id: ElementId(1) }, + AppendChildren { m: 1, id: ElementId(0) } + ] + ) +} \ No newline at end of file From 913fcba3d72bb42b8d3d6999d595f25646d19cfd Mon Sep 17 00:00:00 2001 From: Jeremy Arnold Date: Fri, 16 Feb 2024 15:12:05 -0800 Subject: [PATCH 3/6] Unit test showing use of spans. Spans/events for virtual_dom. --- packages/core/src/virtual_dom.rs | 8 ++++- packages/core/tests/tracing.rs | 58 +------------------------------- 2 files changed, 8 insertions(+), 58 deletions(-) diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 206d0e62a2..f132c9cbe0 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -226,7 +226,6 @@ impl VirtualDom { /// ``` /// /// Note: the VirtualDom is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it. - #[instrument(level = "trace", name = "VirtualDom::new")] pub fn new(app: fn() -> Element) -> Self { Self::new_with_props(app, ()) } @@ -305,6 +304,7 @@ impl VirtualDom { /// let mut dom = VirtualDom::new_from_root(VComponent::new(Example, SomeProps { name: "jane" }, "Example")); /// let mutations = dom.rebuild(); /// ``` + #[instrument(skip(root), level = "trace", name = "VirtualDom::new")] pub(crate) fn new_with_component(root: impl AnyProps + 'static) -> Self { let (tx, rx) = futures_channel::mpsc::unbounded(); @@ -347,6 +347,7 @@ impl VirtualDom { } /// Run a closure inside the dioxus runtime + #[instrument(skip(self, f), level = "trace", name = "VirtualDom::in_runtime")] pub fn in_runtime(&self, f: impl FnOnce() -> O) -> O { let _runtime = RuntimeGuard::new(self.runtime.clone()); f() @@ -594,6 +595,7 @@ impl VirtualDom { /// /// We don't call "flush_sync" here since there's no sync work to be done. Futures will be progressed like usual, /// however any futures waiting on flush_sync will remain pending + #[instrument(skip(self), level = "trace", name = "VirtualDom::wait_for_suspense")] pub async fn wait_for_suspense(&mut self) { loop { if self.suspended_scopes.is_empty() { @@ -614,6 +616,7 @@ impl VirtualDom { } /// Flush any queued template changes + #[instrument(skip(self, to), level = "trace", name = "VirtualDom::flush_templates")] fn flush_templates(&mut self, to: &mut impl WriteMutations) { for template in self.queued_templates.drain(..) { to.register_template(template); @@ -641,6 +644,7 @@ impl VirtualDom { | | | <-- no, broke early | <-- no, broke early */ + #[instrument(skip(self, uievent), level = "trace", name = "VirtualDom::handle_bubbling_event")] fn handle_bubbling_event( &mut self, mut parent: Option, @@ -679,6 +683,7 @@ impl VirtualDom { // Now that we've accumulated all the parent attributes for the target element, call them in reverse order // We check the bubble state between each call to see if the event has been stopped from bubbling + tracing::event!(tracing::Level::TRACE, "Calling {} listeners", listeners.len()); for listener in listeners.into_iter().rev() { if let AttributeValue::Listener(listener) = listener { self.runtime.rendering.set(false); @@ -697,6 +702,7 @@ impl VirtualDom { } /// Call an event listener in the simplest way possible without bubbling upwards + #[instrument(skip(self, uievent), level = "trace", name = "VirtualDom::handle_non_bubbling_event")] fn handle_non_bubbling_event(&mut self, node: ElementRef, name: &str, uievent: Event) { let el_ref = &self.mounts[node.mount.0].node; let node_template = el_ref.template.get(); diff --git a/packages/core/tests/tracing.rs b/packages/core/tests/tracing.rs index a92c4e260a..d93698990a 100644 --- a/packages/core/tests/tracing.rs +++ b/packages/core/tests/tracing.rs @@ -1,5 +1,4 @@ use std::rc::Rc; -use dioxus::dioxus_core::Mutation::*; use dioxus::prelude::*; use dioxus_core::ElementId; use tracing_fluent_assertions::{AssertionsLayer, AssertionRegistry}; @@ -12,6 +11,7 @@ fn miri_rollover() { // setup tracing let assertion_registry = AssertionRegistry::default(); let base_subscriber = Registry::default(); + // log to standard out for testing let std_out_log = tracing_subscriber::fmt::layer().pretty(); let subscriber = base_subscriber .with(std_out_log) @@ -79,59 +79,3 @@ fn app() -> Element { fn ChildExample(i: i32, onhover: EventHandler) -> Element { rsx! { li { onmouseover: move |e| onhover.call(e), "{i}" } } } - - - - - - -#[test] -fn test_original_diff() { - // Create the assertion registry and install the assertion layer, - // then install that subscriber as the global default. - let assertion_registry = AssertionRegistry::default(); - let base_subscriber = Registry::default(); - // create a std out logger just to help with testing - let std_out_log = tracing_subscriber::fmt::layer().pretty(); - let subscriber = base_subscriber - .with(std_out_log) - .with(AssertionsLayer::new(&assertion_registry)); - tracing::subscriber::set_global_default(subscriber).unwrap(); - - let new_virtual_dom = assertion_registry.build() - .with_name("VirtualDom::new") - .was_created() - .was_entered_exactly(1) - .was_exited() - .was_closed() - .finalize(); - - let edited_virtual_dom = assertion_registry.build() - .with_name("VirtualDom::rebuild") - .was_created() - .was_entered_exactly(1) - .was_exited() - .was_closed() - .finalize(); - - let mut dom = VirtualDom::new(|| { - rsx! { - div { div { "Hello, world!" } } - } - }); - - - let edits = dom.rebuild_to_vec().santize(); - - new_virtual_dom.assert(); - edited_virtual_dom.assert(); - - assert_eq!( - edits.edits, - [ - // add to root - LoadTemplate { name: "template", index: 0, id: ElementId(1) }, - AppendChildren { m: 1, id: ElementId(0) } - ] - ) -} \ No newline at end of file From 660c0f23033136a285ea5bfc46ab8102bb4f7bca Mon Sep 17 00:00:00 2001 From: Jeremy Arnold Date: Fri, 16 Feb 2024 15:15:08 -0800 Subject: [PATCH 4/6] Reformat files --- packages/core/src/virtual_dom.rs | 25 +++++++++++++++++++++---- packages/core/tests/tracing.rs | 13 +++++++------ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index f132c9cbe0..8e640b463b 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -376,7 +376,12 @@ impl VirtualDom { return; }; - tracing::event!(tracing::Level::TRACE, "Marking scope {:?} ({}) as dirty", id, scope.name); + tracing::event!( + tracing::Level::TRACE, + "Marking scope {:?} ({}) as dirty", + id, + scope.name + ); self.dirty_scopes.insert(DirtyScope { height: scope.height(), id, @@ -644,7 +649,11 @@ impl VirtualDom { | | | <-- no, broke early | <-- no, broke early */ - #[instrument(skip(self, uievent), level = "trace", name = "VirtualDom::handle_bubbling_event")] + #[instrument( + skip(self, uievent), + level = "trace", + name = "VirtualDom::handle_bubbling_event" + )] fn handle_bubbling_event( &mut self, mut parent: Option, @@ -683,7 +692,11 @@ impl VirtualDom { // Now that we've accumulated all the parent attributes for the target element, call them in reverse order // We check the bubble state between each call to see if the event has been stopped from bubbling - tracing::event!(tracing::Level::TRACE, "Calling {} listeners", listeners.len()); + tracing::event!( + tracing::Level::TRACE, + "Calling {} listeners", + listeners.len() + ); for listener in listeners.into_iter().rev() { if let AttributeValue::Listener(listener) = listener { self.runtime.rendering.set(false); @@ -702,7 +715,11 @@ impl VirtualDom { } /// Call an event listener in the simplest way possible without bubbling upwards - #[instrument(skip(self, uievent), level = "trace", name = "VirtualDom::handle_non_bubbling_event")] + #[instrument( + skip(self, uievent), + level = "trace", + name = "VirtualDom::handle_non_bubbling_event" + )] fn handle_non_bubbling_event(&mut self, node: ElementRef, name: &str, uievent: Event) { let el_ref = &self.mounts[node.mount.0].node; let node_template = el_ref.template.get(); diff --git a/packages/core/tests/tracing.rs b/packages/core/tests/tracing.rs index d93698990a..9b08ff0c26 100644 --- a/packages/core/tests/tracing.rs +++ b/packages/core/tests/tracing.rs @@ -1,10 +1,9 @@ -use std::rc::Rc; +use dioxus::html::SerializedHtmlEventConverter; use dioxus::prelude::*; use dioxus_core::ElementId; -use tracing_fluent_assertions::{AssertionsLayer, AssertionRegistry}; +use std::rc::Rc; +use tracing_fluent_assertions::{AssertionRegistry, AssertionsLayer}; use tracing_subscriber::{layer::SubscriberExt, Registry}; -use dioxus::html::SerializedHtmlEventConverter; - #[test] fn miri_rollover() { @@ -18,14 +17,16 @@ fn miri_rollover() { .with(AssertionsLayer::new(&assertion_registry)); tracing::subscriber::set_global_default(subscriber).unwrap(); - let new_virtual_dom = assertion_registry.build() + let new_virtual_dom = assertion_registry + .build() .with_name("VirtualDom::new") .was_created() .was_entered_exactly(1) .was_closed() .finalize(); - let edited_virtual_dom = assertion_registry.build() + let edited_virtual_dom = assertion_registry + .build() .with_name("VirtualDom::rebuild") .was_created() .was_entered_exactly(1) From c409a78928604efcf9894dd8f05c115dd6276e12 Mon Sep 17 00:00:00 2001 From: Jeremy Arnold Date: Fri, 16 Feb 2024 16:10:04 -0800 Subject: [PATCH 5/6] rename test --- packages/core/tests/tracing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/tests/tracing.rs b/packages/core/tests/tracing.rs index 9b08ff0c26..1fe04033d2 100644 --- a/packages/core/tests/tracing.rs +++ b/packages/core/tests/tracing.rs @@ -6,7 +6,7 @@ use tracing_fluent_assertions::{AssertionRegistry, AssertionsLayer}; use tracing_subscriber::{layer::SubscriberExt, Registry}; #[test] -fn miri_rollover() { +fn basic_tracing() { // setup tracing let assertion_registry = AssertionRegistry::default(); let base_subscriber = Registry::default(); From 4339665fc3b98afefd3d1d187a5f76f7d6338707 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 29 Feb 2024 17:30:51 -0600 Subject: [PATCH 6/6] fix formatting --- packages/core/src/virtual_dom.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 1a86b86f61..b1a4e12039 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -375,14 +375,14 @@ impl VirtualDom { let Some(scope) = self.runtime.get_state(id) else { return; }; - + tracing::event!( tracing::Level::TRACE, "Marking scope {:?} ({}) as dirty", id, scope.name ); - + self.dirty_scopes.insert(DirtyScope { height: scope.height(), id,