Skip to content

Commit

Permalink
Merge branch 'develop' into wip/radeusgd/file-delimited-encoding-1819…
Browse files Browse the repository at this point in the history
…98375
  • Loading branch information
mergify[bot] authored May 10, 2022
2 parents 9580830 + 0e904b2 commit e375979
Show file tree
Hide file tree
Showing 24 changed files with 753 additions and 271 deletions.
177 changes: 101 additions & 76 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ members = [
# The default memebers are those we want to check and test by default.
default-members = ["app/gui", "lib/rust/*"]

# We are using a version with extended functionality. The changes have been PR'd upstream:
# https://github.com/rustwasm/console_error_panic_hook/pull/24
# Remove this patch when the issue is resolved.
[patch.crates-io]
console_error_panic_hook = { git = 'https://github.com/enso-org/console_error_panic_hook' }

[profile.dev]
opt-level = 0
lto = false
Expand Down
2 changes: 2 additions & 0 deletions app/gui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ double-representation = { version = "0.1.0", path = "controller/double-represent
enso-config = { path = "config" }
enso-callback = { path = "../../lib/rust/callback" }
enso-data-structures = { path = "../../lib/rust/data-structures" }
enso-debug-api = { path = "../../lib/rust/debug-api" }
enso-debug-scene = { path = "view/debug_scene" }
enso-frp = { path = "../../lib/rust/frp" }
enso-logger = { path = "../../lib/rust/logger"}
Expand Down Expand Up @@ -52,6 +53,7 @@ uuid = { version = "0.8", features = ["serde", "v4", "wasm-bindgen"] }
# See for more information. https://github.com/rustwasm/wasm-bindgen/issues/2774
# Should be removed once 0.2.80 is available.
wasm-bindgen = { version = "=0.2.78" }
wasm-bindgen-futures = "0.4"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
websocket = "0.23.0"
Expand Down
3 changes: 3 additions & 0 deletions app/gui/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,8 @@ ensogl::read_args! {
authentication_enabled : bool,
email : String,
application_config_url : String,
/// When profiling the application (e.g. with the `./run profile` command), this argument
/// chooses what is profiled.
test_workflow : String,
}
}
38 changes: 35 additions & 3 deletions app/gui/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,48 @@ impl BackendService {
#[derive(Clone, Debug, Default)]
pub struct Startup {
/// The configuration of connection to the backend service.
pub backend: BackendService,
pub backend: BackendService,
/// The project name we want to open on startup.
pub project_name: Option<ProjectName>,
pub project_name: Option<ProjectName>,
/// Whether to open directly to the project view, skipping the welcome screen.
pub initial_view: InitialView,
/// Identifies the element to create the IDE's DOM nodes as children of.
pub dom_parent_id: Option<String>,
}

impl Startup {
/// Read configuration from the web arguments. See also [`web::Arguments`] documentation.
pub fn from_web_arguments() -> FallibleResult<Startup> {
let backend = BackendService::from_web_arguments(&ARGS)?;
let project_name = ARGS.project.clone().map(Into::into);
Ok(Startup { backend, project_name })
let initial_view = match ARGS.project {
Some(_) => InitialView::Project,
None => InitialView::WelcomeScreen,
};
let dom_parent_id = None;
Ok(Startup { backend, project_name, initial_view, dom_parent_id })
}

/// Identifies the element to create the IDE's DOM nodes as children of.
pub fn dom_parent_id(&self) -> &str {
// The main entry point requires that this matches the ID defined in `index.html`.
match &self.dom_parent_id {
Some(id) => id.as_ref(),
None => "root",
}
}
}


// === InitialView ===

/// Identifies the view initially active on startup.
#[derive(Clone, Copy, Debug, Derivative)]
#[derivative(Default)]
pub enum InitialView {
/// Start to the Welcome Screen.
#[derivative(Default)]
WelcomeScreen,
/// Start to the Project View.
Project,
}
7 changes: 4 additions & 3 deletions app/gui/src/ide/initializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,17 @@ impl Initializer {
pub async fn start(self) -> Result<Ide, FailedIde> {
info!(self.logger, "Starting IDE with the following config: {self.config:?}");

let ensogl_app = ensogl::application::Application::new("root");
let ensogl_app = ensogl::application::Application::new(self.config.dom_parent_id());
Initializer::register_views(&ensogl_app);
let view = ensogl_app.new_view::<ide_view::root::View>();

// IDE was opened with `project` argument, we should skip the Welcome Screen.
// We are doing it early, because Controllers initialization
// takes some time and Welcome Screen might be visible for a brief moment while
// controllers are not ready.
if self.config.project_name.is_some() {
view.switch_view_to_project();
match self.config.initial_view {
config::InitialView::WelcomeScreen => (),
config::InitialView::Project => view.switch_view_to_project(),
}

let status_bar = view.status_bar().clone_ref();
Expand Down
154 changes: 154 additions & 0 deletions app/gui/src/integration_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//! Support structures for high-level testing crates that must connect to the Project Manager.
//! This includes integration tests and batch-mode application profiling.
use crate::prelude::*;
use enso_web::traits::*;

use crate::config::InitialView;
use crate::executor::web::EventLoopExecutor;
use crate::initializer::setup_global_executor;
use crate::Ide;
use enso_frp::future::EventOutputExt;
use enso_web::Closure;
use enso_web::HtmlDivElement;
use ensogl::application::test_utils::ApplicationExt;
use std::pin::Pin;



/// Reexports of commonly-used structures, methods and traits.
pub mod prelude {
pub use super::Fixture;

pub use super::wait_a_frame;
pub use crate::prelude::*;
pub use enso_frp::future::EventOutputExt;
}



// ===============
// === Fixture ===
// ===============

/// A root object for tests; contains the IDE, and objects that support it.
#[derive(Debug)]
pub struct Fixture {
/// The top-level object of the IDE.
pub ide: Ide,
/// Runs the IDE's async tasks.
pub executor: EventLoopExecutor,
/// Container for the IDE's HTML elements.
pub root_div: HtmlDivElement,
}

impl Fixture {
/// Initializes the executor and `Ide` structure and returns new Fixture.
pub async fn new(initial_view: InitialView) -> Self {
let config = crate::config::Startup { initial_view, ..default() };
let executor = setup_global_executor();
let root_div = enso_web::document.create_div_or_panic();
root_div.set_id(config.dom_parent_id());
root_div.set_style_or_warn("display", "none");
enso_web::document.body_or_panic().append_or_warn(&root_div);
let initializer = crate::ide::Initializer::new(config);
let ide = initializer.start().await.expect("Failed to initialize the application.");
ide.ensogl_app.set_screen_size_for_tests();
Self { executor, ide, root_div }
}

/// Initializes the executor and `Ide` structure (loading the welcome screen),
/// and returns the new Fixture.
pub async fn setup() -> Self {
Self::new(InitialView::WelcomeScreen).await
}

/// Create a fixture for testing in a newly-created project.
///
/// Initializes the IDE, creates a new project, and waits until the project is ready.
pub async fn setup_new_project() -> Self {
let fixture = Self::new(InitialView::Project).await;
fixture.new_project().await;
fixture
}

/// After returning, the IDE is in a state with new project opened and ready to work
/// (after libraries' compilation).
pub async fn new_project(&self) {
let project = self.ide.presenter.view().project();
let controller = self.ide.presenter.controller();
let project_management =
controller.manage_projects().expect("Cannot access Managing Project API");

let expect_prompt = project.show_prompt.next_event();
project_management.create_new_project(None).await.expect("Failed to create new project");
expect_prompt.await;
}

/// Get the Project View.
pub fn project_view(&self) -> crate::view::project::View {
self.ide.presenter.view().project()
}

/// Get the Graph Editor.
pub fn graph_editor(&self) -> crate::view::graph_editor::GraphEditor {
self.project_view().graph().clone_ref()
}
}

impl Drop for Fixture {
fn drop(&mut self) {
self.root_div.remove();
}
}



// ==================
// === WaitAFrame ===
// ==================

/// A future that resolves after one frame.
#[derive(Default, Debug)]
#[cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
pub struct WaitAFrame {
frame_passed: Rc<Cell<bool>>,
closure: Option<Closure<dyn FnMut(f64)>>,
}

impl Future for WaitAFrame {
type Output = ();

#[cfg(not(target_arch = "wasm32"))]
fn poll(
self: Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
std::task::Poll::Ready(())
}

#[cfg(target_arch = "wasm32")]
fn poll(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
if self.frame_passed.get() {
std::task::Poll::Ready(())
} else {
let waker = cx.waker().clone();
let frame_passed = self.frame_passed.clone_ref();
let closure = Closure::once(move |_| {
frame_passed.set(true);
waker.wake()
});
enso_web::window.request_animation_frame_with_closure_or_panic(&closure);
self.closure = Some(closure);
std::task::Poll::Pending
}
}
}

/// Return a future that resolves after one frame.
pub fn wait_a_frame() -> impl Future<Output = ()> {
WaitAFrame::default()
}
3 changes: 3 additions & 0 deletions app/gui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
use wasm_bindgen::prelude::*;



// ==============
// === Export ===
// ==============
Expand All @@ -68,6 +69,7 @@ pub mod constants;
pub mod controller;
pub mod executor;
pub mod ide;
pub mod integration_test;
pub mod model;
pub mod notification;
pub mod presenter;
Expand Down Expand Up @@ -126,6 +128,7 @@ mod examples {
}
#[allow(unused_imports)]
use examples::*;
mod profile_workflow;

use prelude::profiler;
use prelude::profiler::prelude::*;
Expand Down
59 changes: 59 additions & 0 deletions app/gui/src/profile_workflow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//! Defines profilable workflows, and an entry point that runs a specified workflow.
use crate::integration_test::prelude::*;
use wasm_bindgen::prelude::*;

use enso_debug_api as debug_api;
use enso_web as web;



// ===================
// === Entry point ===
// ===================

/// Startup function for running and profiling a test workflow.
#[wasm_bindgen]
#[allow(dead_code)] // Used from JavaScript.
pub async fn entry_point_profile() {
web::forward_panic_hook_to_console();
ensogl_text_msdf_sys::initialized().await;

// Run selected workflow.
let need_workflow = "`profile` entry point requires --workflow argument. \
Try --workflow=help to see a list of options.";
let selected = enso_config::ARGS.test_workflow.as_ref().expect(need_workflow);
reflect_match!(match selected as options {
"add_node" => profile_add_node().await,
"new_project" => profile_new_project().await,
_ => panic!("Unknown workflow: {selected}. Must be one of: {options:?}."),
});

// Emit profile and exit.
debug_api::save_profile(&profiler::internal::take_log());
debug_api::LifecycleController::new().expect("Workflows run in Electron").quit();
}



// ============================
// === Workflow definitions ===
// ============================

#[profile(Objective)]
async fn profile_add_node() {
let test = Fixture::setup_new_project().await;
let graph_editor = test.graph_editor();
let initial_nodes = graph_editor.nodes().all.len();
let expect_node_added = graph_editor.node_added.next_event();
graph_editor.add_node();
let _ = expect_node_added.expect();
let added_nodes = 1;
assert_eq!(graph_editor.nodes().all.len(), initial_nodes + added_nodes);
}

#[profile(Objective)]
async fn profile_new_project() {
let test = Fixture::setup_new_project().await;
mem::forget(Box::new(test));
}
1 change: 1 addition & 0 deletions app/gui/view/graph-editor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1447,6 +1447,7 @@ struct NodeCreationContext<'a> {
}

impl GraphEditorModelWithNetwork {
#[profile(Objective)]
fn create_node(
&self,
ctx: &NodeCreationContext,
Expand Down
Loading

0 comments on commit e375979

Please sign in to comment.