From 2cc4b5d0e733e22466b767f94859a85f45cca78a Mon Sep 17 00:00:00 2001 From: unclekingpin Date: Wed, 21 Jun 2023 12:34:57 -0700 Subject: [PATCH 1/7] checkout env changes --- src/models/link.rs | 2 +- src/runtime/runtime.rs | 10 ++------ src/runtime/update.rs | 2 +- src/types/resource/meta_item.rs | 1 - .../catalog_with_filters/load_action.rs | 8 +++---- src/unit_tests/ctx/add_to_library.rs | 4 ++-- src/unit_tests/ctx/authenticate.rs | 6 ++--- src/unit_tests/ctx/install_addon.rs | 8 +++---- src/unit_tests/ctx/logout.rs | 2 +- src/unit_tests/ctx/pull_addons_from_api.rs | 4 ++-- src/unit_tests/ctx/push_addons_to_api.rs | 4 ++-- src/unit_tests/ctx/remove_from_library.rs | 4 ++-- src/unit_tests/ctx/rewind_library_item.rs | 4 ++-- src/unit_tests/ctx/sync_library_with_api.rs | 6 ++--- src/unit_tests/ctx/uninstall_addon.rs | 8 +++---- src/unit_tests/ctx/update_settings.rs | 4 ++-- src/unit_tests/ctx/upgrade_addon.rs | 4 ++-- src/unit_tests/data_export.rs | 4 ++-- src/unit_tests/env.rs | 24 +++++++++---------- src/unit_tests/link.rs | 2 +- 20 files changed, 52 insertions(+), 59 deletions(-) diff --git a/src/models/link.rs b/src/models/link.rs index e723e4f46..fdbcea12a 100644 --- a/src/models/link.rs +++ b/src/models/link.rs @@ -31,7 +31,7 @@ impl fmt::Display for LinkError { } } -#[derive(Derivative, Serialize, Clone, Debug)] +#[derive(Derivative, Serialize, Debug)] #[derivative(Default(bound = ""))] #[serde(rename_all = "camelCase")] pub struct Link { diff --git a/src/runtime/runtime.rs b/src/runtime/runtime.rs index 8c9f05da8..62354b23a 100644 --- a/src/runtime/runtime.rs +++ b/src/runtime/runtime.rs @@ -13,7 +13,7 @@ use std::sync::{Arc, LockResult, RwLock, RwLockReadGuard}; #[derive(Serialize, Debug, PartialEq)] #[serde(tag = "name", content = "args")] pub enum RuntimeEvent> { - NewState(Vec, #[cfg(test)] M), + NewState(Vec), CoreEvent(Event), } @@ -78,13 +78,7 @@ where } fn handle_effects(&self, effects: Vec, fields: Vec) { if !fields.is_empty() { - #[cfg(test)] - let model = self.model.read().expect("model read failed"); - self.emit(RuntimeEvent::::NewState( - fields, - #[cfg(test)] - model.to_owned(), - )); + self.emit(RuntimeEvent::::NewState(fields)); }; effects .into_iter() diff --git a/src/runtime/update.rs b/src/runtime/update.rs index 998009c29..1ceabe8cc 100644 --- a/src/runtime/update.rs +++ b/src/runtime/update.rs @@ -5,7 +5,7 @@ use crate::runtime::{Effect, Effects, Env}; use core::fmt::Debug; use serde::{Deserialize, Serialize}; -pub trait Model: Clone { +pub trait Model { #[cfg(not(debug_assertions))] type Field: Send + Sync + Serialize + for<'de> Deserialize<'de>; #[cfg(debug_assertions)] diff --git a/src/types/resource/meta_item.rs b/src/types/resource/meta_item.rs index bb42d08f0..6c8e0b90d 100644 --- a/src/types/resource/meta_item.rs +++ b/src/types/resource/meta_item.rs @@ -218,7 +218,6 @@ pub struct SeriesInfo { #[serde_as] #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] -#[cfg_attr(test, derive(Default))] #[serde(rename_all = "camelCase")] pub struct Video { pub id: String, diff --git a/src/unit_tests/catalog_with_filters/load_action.rs b/src/unit_tests/catalog_with_filters/load_action.rs index c9ba5dd74..3bcc54fce 100644 --- a/src/unit_tests/catalog_with_filters/load_action.rs +++ b/src/unit_tests/catalog_with_filters/load_action.rs @@ -65,13 +65,13 @@ fn default_catalog() { events[0] .downcast_ref::>() .unwrap(), - RuntimeEvent::NewState(fields, _) if fields.len() == 1 && *fields.first().unwrap() == TestModelField::Discover + RuntimeEvent::NewState(fields) if fields.len() == 1 && *fields.first().unwrap() == TestModelField::Discover ); assert_matches!( events[1] .downcast_ref::>() .unwrap(), - RuntimeEvent::NewState(fields, _) if fields.len() == 1 && *fields.first().unwrap() == TestModelField::Discover + RuntimeEvent::NewState(fields) if fields.len() == 1 && *fields.first().unwrap() == TestModelField::Discover ); let states = STATES.read().unwrap(); let states = states @@ -173,13 +173,13 @@ fn search_catalog() { events[0] .downcast_ref::>() .unwrap(), - RuntimeEvent::NewState(fields, _) if fields.len() == 1 && *fields.first().unwrap() == TestModelField::Discover + RuntimeEvent::NewState(fields) if fields.len() == 1 && *fields.first().unwrap() == TestModelField::Discover ); assert_matches!( events[1] .downcast_ref::>() .unwrap(), - RuntimeEvent::NewState(fields, _) if fields.len() == 1 && *fields.first().unwrap() == TestModelField::Discover + RuntimeEvent::NewState(fields) if fields.len() == 1 && *fields.first().unwrap() == TestModelField::Discover ); let states = STATES.read().unwrap(); let states = states diff --git a/src/unit_tests/ctx/add_to_library.rs b/src/unit_tests/ctx/add_to_library.rs index bfeae6157..0ee0551da 100644 --- a/src/unit_tests/ctx/add_to_library.rs +++ b/src/unit_tests/ctx/add_to_library.rs @@ -18,7 +18,7 @@ use url::Url; #[test] fn actionctx_addtolibrary() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -159,7 +159,7 @@ fn actionctx_addtolibrary() { #[test] fn actionctx_addtolibrary_already_added() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/authenticate.rs b/src/unit_tests/ctx/authenticate.rs index e1837d32d..9a5407caa 100644 --- a/src/unit_tests/ctx/authenticate.rs +++ b/src/unit_tests/ctx/authenticate.rs @@ -19,7 +19,7 @@ use stremio_derive::Model; #[test] fn actionctx_authenticate_login() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -218,7 +218,7 @@ fn actionctx_authenticate_login() { #[test] fn actionctx_authenticate_login_with_token() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -416,7 +416,7 @@ fn actionctx_authenticate_login_with_token() { #[test] fn actionctx_authenticate_register() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/install_addon.rs b/src/unit_tests/ctx/install_addon.rs index 90f9d874d..d7cb0b66e 100644 --- a/src/unit_tests/ctx/install_addon.rs +++ b/src/unit_tests/ctx/install_addon.rs @@ -17,7 +17,7 @@ use url::Url; #[test] fn actionctx_installaddon_install() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -84,7 +84,7 @@ fn actionctx_installaddon_install() { #[test] fn actionctx_installaddon_install_with_user() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -198,7 +198,7 @@ fn actionctx_installaddon_install_with_user() { #[test] fn actionctx_installaddon_update() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -306,7 +306,7 @@ fn actionctx_installaddon_update() { #[test] fn actionctx_installaddon_already_installed() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/logout.rs b/src/unit_tests/ctx/logout.rs index ad6400641..bc37acec9 100644 --- a/src/unit_tests/ctx/logout.rs +++ b/src/unit_tests/ctx/logout.rs @@ -15,7 +15,7 @@ use stremio_derive::Model; #[test] fn actionctx_logout() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/pull_addons_from_api.rs b/src/unit_tests/ctx/pull_addons_from_api.rs index df63fcd9f..bc962fa9a 100644 --- a/src/unit_tests/ctx/pull_addons_from_api.rs +++ b/src/unit_tests/ctx/pull_addons_from_api.rs @@ -16,7 +16,7 @@ use url::Url; #[test] fn actionctx_pulladdonsfromapi() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -73,7 +73,7 @@ fn actionctx_pulladdonsfromapi() { #[test] fn actionctx_pulladdonsfromapi_with_user() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/push_addons_to_api.rs b/src/unit_tests/ctx/push_addons_to_api.rs index cd65a05cc..4bbed4895 100644 --- a/src/unit_tests/ctx/push_addons_to_api.rs +++ b/src/unit_tests/ctx/push_addons_to_api.rs @@ -14,7 +14,7 @@ use url::Url; #[test] fn actionctx_pushaddonstoapi() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -65,7 +65,7 @@ fn actionctx_pushaddonstoapi() { #[test] fn actionctx_pushaddonstoapi_with_user() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/remove_from_library.rs b/src/unit_tests/ctx/remove_from_library.rs index c478b8d28..fbe2ac69a 100644 --- a/src/unit_tests/ctx/remove_from_library.rs +++ b/src/unit_tests/ctx/remove_from_library.rs @@ -16,7 +16,7 @@ use stremio_derive::Model; #[test] fn actionctx_removefromlibrary() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -148,7 +148,7 @@ fn actionctx_removefromlibrary() { #[test] fn actionctx_removefromlibrary_not_added() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/rewind_library_item.rs b/src/unit_tests/ctx/rewind_library_item.rs index 7a71320ee..f8dc69790 100644 --- a/src/unit_tests/ctx/rewind_library_item.rs +++ b/src/unit_tests/ctx/rewind_library_item.rs @@ -16,7 +16,7 @@ use stremio_derive::Model; #[test] fn actionctx_rewindlibraryitem() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -154,7 +154,7 @@ fn actionctx_rewindlibraryitem() { #[test] fn actionctx_rewindlibraryitem_not_added() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/sync_library_with_api.rs b/src/unit_tests/ctx/sync_library_with_api.rs index 038e257e4..2826cc475 100644 --- a/src/unit_tests/ctx/sync_library_with_api.rs +++ b/src/unit_tests/ctx/sync_library_with_api.rs @@ -19,7 +19,7 @@ use stremio_derive::Model; #[test] fn actionctx_synclibrarywithapi() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -40,7 +40,7 @@ fn actionctx_synclibrarywithapi() { #[test] fn actionctx_synclibrarywithapi_with_user() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -366,7 +366,7 @@ fn actionctx_synclibrarywithapi_with_user() { #[test] fn actionctx_synclibrarywithapi_with_user_empty_library() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/uninstall_addon.rs b/src/unit_tests/ctx/uninstall_addon.rs index 463446c72..1c9ba8abd 100644 --- a/src/unit_tests/ctx/uninstall_addon.rs +++ b/src/unit_tests/ctx/uninstall_addon.rs @@ -17,7 +17,7 @@ use url::Url; #[test] fn actionctx_uninstalladdon() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -91,7 +91,7 @@ fn actionctx_uninstalladdon() { #[test] fn actionctx_uninstalladdon_with_user() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -212,7 +212,7 @@ fn actionctx_uninstalladdon_with_user() { #[test] fn actionctx_uninstalladdon_protected() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -287,7 +287,7 @@ fn actionctx_uninstalladdon_protected() { #[test] fn actionctx_uninstalladdon_not_installed() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/update_settings.rs b/src/unit_tests/ctx/update_settings.rs index 5db31983a..633ac066b 100644 --- a/src/unit_tests/ctx/update_settings.rs +++ b/src/unit_tests/ctx/update_settings.rs @@ -8,7 +8,7 @@ use stremio_derive::Model; #[test] fn actionctx_updatesettings() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -49,7 +49,7 @@ fn actionctx_updatesettings() { #[test] fn actionctx_updatesettings_not_changed() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/upgrade_addon.rs b/src/unit_tests/ctx/upgrade_addon.rs index b38a89b3e..f80e49a2b 100644 --- a/src/unit_tests/ctx/upgrade_addon.rs +++ b/src/unit_tests/ctx/upgrade_addon.rs @@ -11,7 +11,7 @@ use url::Url; #[test] fn actionctx_addon_upgrade() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -118,7 +118,7 @@ fn actionctx_addon_upgrade() { #[test] fn actionctx_addon_upgrade_fail_due_to_different_url() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/data_export.rs b/src/unit_tests/data_export.rs index 91867023b..8fd3d9519 100644 --- a/src/unit_tests/data_export.rs +++ b/src/unit_tests/data_export.rs @@ -72,13 +72,13 @@ fn data_export_with_user() { events[0] .downcast_ref::>() .unwrap(), - RuntimeEvent::NewState(fields, _) if fields.len() == 1 && *fields.first().unwrap() == TestModelField::DataExport + RuntimeEvent::NewState(fields) if fields.len() == 1 && *fields.first().unwrap() == TestModelField::DataExport ); assert_matches!( events[1] .downcast_ref::>() .unwrap(), - RuntimeEvent::NewState(fields, _) if fields.len() == 1 && *fields.first().unwrap() == TestModelField::DataExport + RuntimeEvent::NewState(fields) if fields.len() == 1 && *fields.first().unwrap() == TestModelField::DataExport ); let states = STATES.read().unwrap(); let states = states diff --git a/src/unit_tests/env.rs b/src/unit_tests/env.rs index b458d5abd..81f8b64cc 100644 --- a/src/unit_tests/env.rs +++ b/src/unit_tests/env.rs @@ -77,6 +77,17 @@ impl TestEnv { runnable: F, ) { tokio_current_thread::block_on_all(future::lazy(|_| { + TestEnv::exec_concurrent(rx.for_each(enclose!((runtime) move |event| { + if let RuntimeEvent::NewState(_) = event { + let runtime = runtime.read().expect("runtime read failed"); + let state = runtime.model().expect("model read failed"); + let mut states = STATES.write().expect("states write failed"); + states.push(Box::new(state.to_owned()) as Box); + }; + let mut events = EVENTS.write().expect("events write failed"); + events.push(Box::new(event) as Box); + future::ready(()) + }))); { let runtime = runtime.read().expect("runtime read failed"); let state = runtime.model().expect("model read failed"); @@ -84,22 +95,11 @@ impl TestEnv { states.push(Box::new(state.to_owned()) as Box); } runnable(); - })); - tokio_current_thread::block_on_all(future::lazy(|_| { - TestEnv::exec_concurrent(rx.for_each(move |event| { - if let RuntimeEvent::NewState(_, state) = &event { - let mut states = STATES.write().expect("states write failed"); - states.push(Box::new(state.to_owned()) as Box); - }; - let mut events = EVENTS.write().expect("events write failed"); - events.push(Box::new(event) as Box); - future::ready(()) - })); TestEnv::exec_concurrent(enclose!((runtime) async move { let mut runtime = runtime.write().expect("runtime read failed"); runtime.close().await.unwrap(); })); - })); + })) } } diff --git a/src/unit_tests/link.rs b/src/unit_tests/link.rs index 1ee74e00b..d5025046c 100644 --- a/src/unit_tests/link.rs +++ b/src/unit_tests/link.rs @@ -11,7 +11,7 @@ use stremio_derive::Model; #[test] fn create_link_code() { - #[derive(Model, Clone, Default)] + #[derive(Model, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, From 01b5153e5a5e55bc0ad6f5a5ec9abd8d752cdac3 Mon Sep 17 00:00:00 2001 From: unclekingpin Date: Wed, 21 Jun 2023 13:20:54 -0700 Subject: [PATCH 2/7] failed test demo --- src/runtime/runtime.rs | 2 ++ src/types/resource/meta_item.rs | 1 + src/unit_tests/env.rs | 4 +++ .../meta_details/override_selected.rs | 28 ++++++++++++++----- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/runtime/runtime.rs b/src/runtime/runtime.rs index 62354b23a..90f4a9729 100644 --- a/src/runtime/runtime.rs +++ b/src/runtime/runtime.rs @@ -74,6 +74,7 @@ where Ok(()) } fn emit(&self, event: RuntimeEvent) { + println!("emit NewState"); self.tx.clone().try_send(event).expect("emit event failed"); } fn handle_effects(&self, effects: Vec, fields: Vec) { @@ -101,6 +102,7 @@ where })); } fn handle_effect_output(&self, msg: Msg) { + println!("handle_effect_output {:#?}", msg); match msg { Msg::Event(event) => { self.emit(RuntimeEvent::CoreEvent(event)); diff --git a/src/types/resource/meta_item.rs b/src/types/resource/meta_item.rs index 6c8e0b90d..bb42d08f0 100644 --- a/src/types/resource/meta_item.rs +++ b/src/types/resource/meta_item.rs @@ -218,6 +218,7 @@ pub struct SeriesInfo { #[serde_as] #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] +#[cfg_attr(test, derive(Default))] #[serde(rename_all = "camelCase")] pub struct Video { pub id: String, diff --git a/src/unit_tests/env.rs b/src/unit_tests/env.rs index 81f8b64cc..ab91f124c 100644 --- a/src/unit_tests/env.rs +++ b/src/unit_tests/env.rs @@ -78,6 +78,7 @@ impl TestEnv { ) { tokio_current_thread::block_on_all(future::lazy(|_| { TestEnv::exec_concurrent(rx.for_each(enclose!((runtime) move |event| { + println!("run_with_runtime: event"); if let RuntimeEvent::NewState(_) = event { let runtime = runtime.read().expect("runtime read failed"); let state = runtime.model().expect("model read failed"); @@ -94,9 +95,12 @@ impl TestEnv { let mut states = STATES.write().expect("states write failed"); states.push(Box::new(state.to_owned()) as Box); } + println!("run_with_runtime: before runnable"); runnable(); + println!("run_with_runtime: after runnable"); TestEnv::exec_concurrent(enclose!((runtime) async move { let mut runtime = runtime.write().expect("runtime read failed"); + println!("run_with_runtime: runtime.close()"); runtime.close().await.unwrap(); })); })) diff --git a/src/unit_tests/meta_details/override_selected.rs b/src/unit_tests/meta_details/override_selected.rs index c5fb7352c..cf4bdf2b7 100644 --- a/src/unit_tests/meta_details/override_selected.rs +++ b/src/unit_tests/meta_details/override_selected.rs @@ -3,7 +3,7 @@ use crate::models::ctx::Ctx; use crate::models::meta_details::{MetaDetails, Selected}; use crate::runtime::msg::{Action, ActionLoad}; use crate::runtime::{EnvFutureExt, Runtime, RuntimeAction, TryEnvFuture}; -use crate::types::addon::{ResourcePath, ResourceResponse}; +use crate::types::addon::{Descriptor, Manifest, ManifestResource, ResourcePath, ResourceResponse}; use crate::types::profile::Profile; use crate::types::resource::{MetaItem, MetaItemBehaviorHints, MetaItemPreview, Video}; use crate::unit_tests::{default_fetch_handler, Request, TestEnv, FETCH_HANDLER, STATES}; @@ -13,6 +13,7 @@ use futures::future; use std::any::Any; use std::sync::{Arc, RwLock}; use stremio_derive::Model; +use url::Url; #[test] fn override_selected_default_video_id() { @@ -24,7 +25,7 @@ fn override_selected_default_video_id() { } fn fetch_handler(request: Request) -> TryEnvFuture> { match request { - Request { url, .. } if url == "https://v3-cinemeta.strem.io/meta/movie/tt1.json" => { + Request { url, .. } if url == "https://transport_url/meta/movie/tt1.json" => { future::ok(Box::new(ResourceResponse::Meta { meta: MetaItem { preview: MetaItemPreview { @@ -41,6 +42,12 @@ fn override_selected_default_video_id() { }) as Box) .boxed_env() } + Request { url, .. } if url == "https://transport_url/stream/movie/_tt1.json" => { + future::ok( + Box::new(ResourceResponse::Streams { streams: vec![] }) as Box + ) + .boxed_env() + } _ => default_fetch_handler(request), } } @@ -50,11 +57,18 @@ fn override_selected_default_video_id() { TestModel { ctx: Ctx { profile: Profile { - addons: OFFICIAL_ADDONS - .iter() - .filter(|addon| addon.transport_url == *CINEMETA_URL) - .cloned() - .collect(), + addons: vec![Descriptor { + manifest: Manifest { + types: vec!["movie".to_owned()], + resources: vec![ + ManifestResource::Short(META_RESOURCE_NAME.to_owned()), + ManifestResource::Short(STREAM_RESOURCE_NAME.to_owned()), + ], + ..Default::default() + }, + transport_url: Url::parse("https://transport_url/manifest.json").unwrap(), + flags: Default::default(), + }], ..Default::default() }, ..Default::default() From 1ae18f461401f99b7b00f5c2f9e06ced8dad7c08 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Wed, 12 Jul 2023 12:07:46 +0300 Subject: [PATCH 3/7] fix: new TestEnv + debug for fixing concurrency issue Signed-off-by: Lachezar Lechev --- Cargo.toml | 4 +- src/unit_tests/test_env.rs | 316 +++++++++++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 src/unit_tests/test_env.rs diff --git a/Cargo.toml b/Cargo.toml index 5fd4444e7..b532b87b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,7 +69,9 @@ regex = "1.8" tracing = "0.1" [dev-dependencies] -tokio = { version = "1.12", features = ["rt", "macros"] } +tokio = { version = "1.12", features = ["rt", "macros", "time"] } tokio-current-thread = "=0.2.0-alpha.1" serde_test = "1.0" assert_matches = "1.5" + +once_cell = "1" diff --git a/src/unit_tests/test_env.rs b/src/unit_tests/test_env.rs new file mode 100644 index 000000000..7c6e966a5 --- /dev/null +++ b/src/unit_tests/test_env.rs @@ -0,0 +1,316 @@ +use std::{ + any::{type_name, Any}, + collections::{BTreeMap, HashMap}, + fmt, + ops::Fn, + sync::{Arc, LockResult, Mutex, MutexGuard, RwLock}, + time::Duration, +}; + +use chrono::{DateTime, Utc}; +use enclose::enclose; +use futures::{channel::mpsc::Receiver, future, Future, StreamExt, TryFutureExt}; +use lazy_static::lazy_static; +use once_cell::sync::Lazy; +use serde::{Deserialize, Serialize}; +use tokio::task::JoinHandle; + +use crate::{ + models::{ctx::Ctx, streaming_server::StreamingServer}, + runtime::{ + ConditionalSend, Env, EnvFuture, EnvFutureExt, Model, Runtime, RuntimeEvent, TryEnvFuture, + }, +}; + + +use super::{default_fetch_handler, FetchHandler, Request}; + +pub static TEST_ENV: Lazy = Lazy::new(|| TestEnv::new()); + +#[derive(Clone, Debug)] +pub struct TestEnv { + pub inner: Arc, +} + +pub struct TestEnvInner { + pub(crate) fetch_handler: RwLock, + pub(crate) requests: RwLock>, + pub(crate) storage: RwLock>, + pub(crate) events: RwLock>>, + pub(crate) states: RwLock>>, + pub(crate) now: RwLock>, + pub(crate) env_mutex: Mutex<()>, + // runtime: tokio::runtime::Runtime, +} + +impl fmt::Debug for TestEnvInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TestEnvInner") + .field("fetch_handler", &"FetchHandler") + .field("requests", &self.requests) + .field("storage", &self.storage) + .field("events", &self.events) + .field("states", &self.states) + .field("now", &self.now) + .field("env_mutex", &self.env_mutex) + .finish() + } +} + +impl TestEnv { + fn new() -> Self { + Self { + inner: Arc::new(TestEnvInner { + fetch_handler: RwLock::new(Box::new(default_fetch_handler)), + requests: Default::default(), + storage: Default::default(), + events: Default::default(), + states: Default::default(), + now: RwLock::new(Utc::now()), + env_mutex: Default::default(), + }), + } + } + + pub fn set_fetch_handler(&self, new: FetchHandler) { + let mut guard = self.inner.fetch_handler.write().unwrap(); + *guard = new; + + // self + } +} + +pub struct TestGuard { + receivers: JoinHandle<()>, +} + +impl Drop for TestGuard { + fn drop(&mut self) { + self.receivers.abort(); + } +} + +// pub struct EnvGuard + Send + Sync + 'static> { +// runtime: Arc>>, +// } + +// impl Drop for EnvGuard +// where +// M: Model + Send + Sync + 'static, +// { +// fn drop(&mut self) { +// futures::executor::block_on(async move { +// let mut runtime = self.runtime.write().expect("runtime write failed"); +// println!("run_with_runtime: runtime.close()"); +// runtime.close().await.unwrap(); +// }); + +// println!("Dropped TestEnv"); +// } +// } + +impl TestEnv { + pub fn reset(&'static self) -> LockResult> { + let env_mutex = self.inner.env_mutex.lock(); + *self.inner.fetch_handler.write().unwrap() = Box::new(default_fetch_handler); + self.inner.requests.write().unwrap().clear(); + self.inner.storage.write().unwrap().clear(); + self.inner.events.write().unwrap().clear(); + self.inner.states.write().unwrap().clear(); + *self.inner.now.write().unwrap() = Utc::now(); + env_mutex + } + pub fn run(runnable: F) { + futures::executor::block_on(future::lazy(|_| { + // tokio_current_thread::block_on_all(future::lazy(|_| { + runnable(); + })) + } + + // pub fn run_with_runtime_futures< + // M: Model + core::fmt::Debug + Clone + Send + Sync + 'static, + // F: FnOnce(), + // >( + // &'static self, + // rx: Receiver>, + // runtime: Arc>>, + // runnable: F, + // // ) -> EnvGuard { + // ) { + // // tokio_current_thread::block_on_all(future::lazy(|_| { + // futures::executor::block_on(future::lazy(|_| { + // TestEnv::exec_concurrent(rx.for_each(enclose!((runtime) move |event| { + // println!("run_with_runtime: event"); + // if let RuntimeEvent::NewState(_) = event { + // let runtime = runtime.read().expect("runtime read failed"); + // let state = runtime.model().expect("model read failed"); + // let mut states = self.inner.states.write().expect("states write failed"); + // states.push(Box::new(state.to_owned()) as Box); + // }; + // let mut events = TEST_ENV.inner.events.write().expect("events write failed"); + // events.push(Box::new(event) as Box); + // println!("run_with_runtime: pushed"); + + // future::ready(()) + // }))); + // { + // let runtime = runtime.read().expect("runtime read failed"); + // let state = runtime.model().expect("model read failed"); + // let mut states = self.inner.states.write().expect("states write failed"); + // states.push(Box::new(state.to_owned()) as Box); + // } + // println!("run_with_runtime: before runnable"); + // runnable(); + // println!("run_with_runtime: after runnable"); + // // TestEnv::exec_concurrent(enclose!((runtime) async move { + // // let mut runtime = runtime.write().expect("runtime read failed"); + // // println!("run_with_runtime: runtime.close()"); + // // runtime.close().await.unwrap(); + // // })); + // })) + // } + + pub async fn run_with_runtime_tokio< + M: Model + Clone + Send + Sync + 'static + fmt::Debug, + F: FnOnce(), + >( + &self, + mut rx: Receiver>, + runtime: Arc>>, + runnable: F, + // ) -> EnvGuard { + ) -> TestGuard { + let receivers_envs = self.clone(); + + { + let runtime = runtime.read().expect("runtime read failed"); + let state = runtime.model().expect("model read failed"); + let mut states = TEST_ENV.inner.states.write().expect("states write failed"); + states.push(Box::new(state.to_owned()) as Box); + } + + let receivers = tokio::task::spawn(async move { + let runtime = runtime.clone(); + + println!("Receiver for new events has been spawned."); + + while let Some(event) = rx.next().await { + // rx.for_each(enclose!((runtime, receivers_envs) move |event| { + println!("Received new"); + + if let RuntimeEvent::NewState(_) = &event { + let runtime = runtime.read().expect("runtime read failed"); + let state = runtime.model().expect("model read failed"); + let mut states = receivers_envs + .inner + .states + .write() + .expect("states write failed"); + states.push(Box::new(state.to_owned()) as Box); + tracing::debug!("NewState pushed to states"); + println!("NewState pushed to states"); + }; + let mut events = receivers_envs + .inner + .events + .write() + .expect("events write failed"); + tracing::debug!("Event added - {:?}", event); + println!("Event added - {:?}", event); + events.push(Box::new(event) as Box); + } + }); + + println!("run_with_runtime: before runnable"); + runnable(); + println!("run_with_runtime: after runnable"); + // receivers_runtime.write().expect("Lock").flush().await.expect("Flushed all pending events"); + + // give some time to the tasks to update + tokio::time::sleep(Duration::from_secs(1)).await; + + // }) + // // await the spawned future and block until it's ready + // .await; + // // }); + + // futures::executor::block_on(future::lazy(enclose!((runtime) |_| { + // tokio::block_on(future::lazy(enclose!((runtime) |_| { + + // only on drop! + // let mut runtime = runtime.write().expect("runtime read failed"); + // println!("run_with_runtime: runtime.close()"); + // runtime.close().await.unwrap(); + TestGuard { receivers } + } +} + +impl Env for TestEnv { + fn fetch< + IN: Serialize + ConditionalSend + 'static, + OUT: for<'de> Deserialize<'de> + ConditionalSend + 'static, + >( + request: http::Request, + ) -> TryEnvFuture { + let request = Request::from(request); + TEST_ENV + .inner + .requests + .write() + .unwrap() + .push(request.to_owned()); + TEST_ENV.inner.fetch_handler.read().unwrap()(request) + .map_ok(|resp| { + *resp + .downcast::() + .unwrap_or_else(|_| panic!("Failed to downcast to {}", type_name::())) + }) + .boxed_env() + } + fn get_storage Deserialize<'de> + ConditionalSend + 'static>( + key: &str, + ) -> TryEnvFuture> { + future::ok( + TEST_ENV + .inner + .storage + .read() + .unwrap() + .get(key) + .map(|data| serde_json::from_str(data).unwrap()), + ) + .boxed_env() + } + fn set_storage(key: &str, value: Option<&T>) -> TryEnvFuture<()> { + let mut storage = TEST_ENV.inner.storage.write().unwrap(); + match value { + Some(v) => storage.insert(key.to_string(), serde_json::to_string(v).unwrap()), + None => storage.remove(key), + }; + future::ok(()).boxed_env() + } + fn exec_concurrent + ConditionalSend + 'static>(future: F) { + // tokio::spawn(future); + futures::executor::block_on(future); + } + fn exec_sequential + ConditionalSend + 'static>(future: F) { + futures::executor::block_on(future) + // tokio::spawn(future); + } + fn now() -> DateTime { + TEST_ENV.inner.now.read().unwrap().clone() + } + fn flush_analytics() -> EnvFuture<'static, ()> { + future::ready(()).boxed_env() + } + fn analytics_context( + _ctx: &Ctx, + _streaming_server: &StreamingServer, + _path: &str, + ) -> serde_json::Value { + serde_json::Value::Null + } + fn log(message: String) { + println!("{message}") + } +} From 4f66e19174b68ae45cd19865a090c7ee36bb9ab0 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Wed, 12 Jul 2023 12:08:51 +0300 Subject: [PATCH 4/7] chore: derive Debug for test Model Signed-off-by: Lachezar Lechev --- src/unit_tests/ctx/add_to_library.rs | 4 ++-- src/unit_tests/ctx/authenticate.rs | 6 ++--- src/unit_tests/ctx/install_addon.rs | 8 +++---- src/unit_tests/ctx/logout.rs | 2 +- src/unit_tests/ctx/pull_addons_from_api.rs | 4 ++-- src/unit_tests/ctx/push_addons_to_api.rs | 4 ++-- src/unit_tests/ctx/remove_from_library.rs | 4 ++-- src/unit_tests/ctx/rewind_library_item.rs | 4 ++-- src/unit_tests/ctx/sync_library_with_api.rs | 6 ++--- src/unit_tests/ctx/uninstall_addon.rs | 8 +++---- src/unit_tests/ctx/update_settings.rs | 4 ++-- src/unit_tests/ctx/upgrade_addon.rs | 4 ++-- src/unit_tests/link.rs | 2 +- src/unit_tests/mod.rs | 25 ++++++++++++++------- 14 files changed, 47 insertions(+), 38 deletions(-) diff --git a/src/unit_tests/ctx/add_to_library.rs b/src/unit_tests/ctx/add_to_library.rs index 0ee0551da..b7c28022b 100644 --- a/src/unit_tests/ctx/add_to_library.rs +++ b/src/unit_tests/ctx/add_to_library.rs @@ -18,7 +18,7 @@ use url::Url; #[test] fn actionctx_addtolibrary() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -159,7 +159,7 @@ fn actionctx_addtolibrary() { #[test] fn actionctx_addtolibrary_already_added() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/authenticate.rs b/src/unit_tests/ctx/authenticate.rs index 9a5407caa..9b7cfe9c9 100644 --- a/src/unit_tests/ctx/authenticate.rs +++ b/src/unit_tests/ctx/authenticate.rs @@ -19,7 +19,7 @@ use stremio_derive::Model; #[test] fn actionctx_authenticate_login() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -218,7 +218,7 @@ fn actionctx_authenticate_login() { #[test] fn actionctx_authenticate_login_with_token() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -416,7 +416,7 @@ fn actionctx_authenticate_login_with_token() { #[test] fn actionctx_authenticate_register() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/install_addon.rs b/src/unit_tests/ctx/install_addon.rs index d7cb0b66e..34784858a 100644 --- a/src/unit_tests/ctx/install_addon.rs +++ b/src/unit_tests/ctx/install_addon.rs @@ -17,7 +17,7 @@ use url::Url; #[test] fn actionctx_installaddon_install() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -84,7 +84,7 @@ fn actionctx_installaddon_install() { #[test] fn actionctx_installaddon_install_with_user() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -198,7 +198,7 @@ fn actionctx_installaddon_install_with_user() { #[test] fn actionctx_installaddon_update() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -306,7 +306,7 @@ fn actionctx_installaddon_update() { #[test] fn actionctx_installaddon_already_installed() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/logout.rs b/src/unit_tests/ctx/logout.rs index bc37acec9..6a0b5df92 100644 --- a/src/unit_tests/ctx/logout.rs +++ b/src/unit_tests/ctx/logout.rs @@ -15,7 +15,7 @@ use stremio_derive::Model; #[test] fn actionctx_logout() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/pull_addons_from_api.rs b/src/unit_tests/ctx/pull_addons_from_api.rs index bc962fa9a..4e78638c0 100644 --- a/src/unit_tests/ctx/pull_addons_from_api.rs +++ b/src/unit_tests/ctx/pull_addons_from_api.rs @@ -16,7 +16,7 @@ use url::Url; #[test] fn actionctx_pulladdonsfromapi() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -73,7 +73,7 @@ fn actionctx_pulladdonsfromapi() { #[test] fn actionctx_pulladdonsfromapi_with_user() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/push_addons_to_api.rs b/src/unit_tests/ctx/push_addons_to_api.rs index 4bbed4895..f0728ae35 100644 --- a/src/unit_tests/ctx/push_addons_to_api.rs +++ b/src/unit_tests/ctx/push_addons_to_api.rs @@ -14,7 +14,7 @@ use url::Url; #[test] fn actionctx_pushaddonstoapi() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -65,7 +65,7 @@ fn actionctx_pushaddonstoapi() { #[test] fn actionctx_pushaddonstoapi_with_user() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/remove_from_library.rs b/src/unit_tests/ctx/remove_from_library.rs index fbe2ac69a..9d34b3ba4 100644 --- a/src/unit_tests/ctx/remove_from_library.rs +++ b/src/unit_tests/ctx/remove_from_library.rs @@ -16,7 +16,7 @@ use stremio_derive::Model; #[test] fn actionctx_removefromlibrary() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -148,7 +148,7 @@ fn actionctx_removefromlibrary() { #[test] fn actionctx_removefromlibrary_not_added() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/rewind_library_item.rs b/src/unit_tests/ctx/rewind_library_item.rs index f8dc69790..7557e8ef6 100644 --- a/src/unit_tests/ctx/rewind_library_item.rs +++ b/src/unit_tests/ctx/rewind_library_item.rs @@ -16,7 +16,7 @@ use stremio_derive::Model; #[test] fn actionctx_rewindlibraryitem() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -154,7 +154,7 @@ fn actionctx_rewindlibraryitem() { #[test] fn actionctx_rewindlibraryitem_not_added() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/sync_library_with_api.rs b/src/unit_tests/ctx/sync_library_with_api.rs index 2826cc475..3646cde63 100644 --- a/src/unit_tests/ctx/sync_library_with_api.rs +++ b/src/unit_tests/ctx/sync_library_with_api.rs @@ -19,7 +19,7 @@ use stremio_derive::Model; #[test] fn actionctx_synclibrarywithapi() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -40,7 +40,7 @@ fn actionctx_synclibrarywithapi() { #[test] fn actionctx_synclibrarywithapi_with_user() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -366,7 +366,7 @@ fn actionctx_synclibrarywithapi_with_user() { #[test] fn actionctx_synclibrarywithapi_with_user_empty_library() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/uninstall_addon.rs b/src/unit_tests/ctx/uninstall_addon.rs index 1c9ba8abd..693f61d87 100644 --- a/src/unit_tests/ctx/uninstall_addon.rs +++ b/src/unit_tests/ctx/uninstall_addon.rs @@ -17,7 +17,7 @@ use url::Url; #[test] fn actionctx_uninstalladdon() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -91,7 +91,7 @@ fn actionctx_uninstalladdon() { #[test] fn actionctx_uninstalladdon_with_user() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -212,7 +212,7 @@ fn actionctx_uninstalladdon_with_user() { #[test] fn actionctx_uninstalladdon_protected() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -287,7 +287,7 @@ fn actionctx_uninstalladdon_protected() { #[test] fn actionctx_uninstalladdon_not_installed() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/update_settings.rs b/src/unit_tests/ctx/update_settings.rs index 633ac066b..17800c21a 100644 --- a/src/unit_tests/ctx/update_settings.rs +++ b/src/unit_tests/ctx/update_settings.rs @@ -8,7 +8,7 @@ use stremio_derive::Model; #[test] fn actionctx_updatesettings() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -49,7 +49,7 @@ fn actionctx_updatesettings() { #[test] fn actionctx_updatesettings_not_changed() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/ctx/upgrade_addon.rs b/src/unit_tests/ctx/upgrade_addon.rs index f80e49a2b..c5b3ce6f3 100644 --- a/src/unit_tests/ctx/upgrade_addon.rs +++ b/src/unit_tests/ctx/upgrade_addon.rs @@ -11,7 +11,7 @@ use url::Url; #[test] fn actionctx_addon_upgrade() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, @@ -118,7 +118,7 @@ fn actionctx_addon_upgrade() { #[test] fn actionctx_addon_upgrade_fail_due_to_different_url() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/link.rs b/src/unit_tests/link.rs index d5025046c..14ec09c61 100644 --- a/src/unit_tests/link.rs +++ b/src/unit_tests/link.rs @@ -11,7 +11,7 @@ use stremio_derive::Model; #[test] fn create_link_code() { - #[derive(Model, Default)] + #[derive(Model, Debug, Default)] #[model(TestEnv)] struct TestModel { ctx: Ctx, diff --git a/src/unit_tests/mod.rs b/src/unit_tests/mod.rs index 442465197..4a0ab1d2c 100644 --- a/src/unit_tests/mod.rs +++ b/src/unit_tests/mod.rs @@ -1,11 +1,20 @@ mod env; pub use env::*; -mod catalog_with_filters; -mod ctx; -mod deep_links; -mod meta_details; -mod serde; - -mod data_export; -mod link; +pub mod test_env; + +pub mod catalog_with_filters; +pub mod ctx; +pub mod deep_links; +pub mod meta_details; +pub mod serde; + +pub mod data_export; +pub mod link; + +pub fn is_send() -> bool { + true +} +pub fn is_sync() -> bool { + true +} From 2bd40f892ef47094d15d1ba11f7876236aff90c9 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Wed, 12 Jul 2023 12:10:11 +0300 Subject: [PATCH 5/7] chore: update failing tests to use tokio::test Signed-off-by: Lachezar Lechev --- .../meta_details/override_selected.rs | 190 +++++++++--------- 1 file changed, 95 insertions(+), 95 deletions(-) diff --git a/src/unit_tests/meta_details/override_selected.rs b/src/unit_tests/meta_details/override_selected.rs index cf4bdf2b7..122dd9ae8 100644 --- a/src/unit_tests/meta_details/override_selected.rs +++ b/src/unit_tests/meta_details/override_selected.rs @@ -6,7 +6,10 @@ use crate::runtime::{EnvFutureExt, Runtime, RuntimeAction, TryEnvFuture}; use crate::types::addon::{Descriptor, Manifest, ManifestResource, ResourcePath, ResourceResponse}; use crate::types::profile::Profile; use crate::types::resource::{MetaItem, MetaItemBehaviorHints, MetaItemPreview, Video}; -use crate::unit_tests::{default_fetch_handler, Request, TestEnv, FETCH_HANDLER, STATES}; +use crate::unit_tests::test_env::TEST_ENV; +use crate::unit_tests::{ + default_fetch_handler, is_send, is_sync, test_env::TestEnv, Request, FETCH_HANDLER, STATES, +}; use assert_matches::assert_matches; use enclose::enclose; use futures::future; @@ -15,14 +18,18 @@ use std::sync::{Arc, RwLock}; use stremio_derive::Model; use url::Url; -#[test] -fn override_selected_default_video_id() { +#[tokio::test] +async fn override_selected_default_video_id() { #[derive(Model, Default, Clone, Debug)] #[model(TestEnv)] struct TestModel { ctx: Ctx, meta_details: MetaDetails, } + + assert!(is_send::()); + assert!(is_sync::()); + fn fetch_handler(request: Request) -> TryEnvFuture> { match request { Request { url, .. } if url == "https://transport_url/meta/movie/tt1.json" => { @@ -51,8 +58,10 @@ fn override_selected_default_video_id() { _ => default_fetch_handler(request), } } - let _env_mutex = TestEnv::reset(); - *FETCH_HANDLER.write().unwrap() = Box::new(fetch_handler); + let env_guard = TEST_ENV.reset(); + TEST_ENV.set_fetch_handler(Box::new(fetch_handler)); + // *test_env.fetch_handler.write().unwrap() = Box::new(fetch_handler); + let (runtime, rx) = Runtime::::new( TestModel { ctx: Ctx { @@ -79,30 +88,34 @@ fn override_selected_default_video_id() { 1000, ); let runtime = Arc::new(RwLock::new(runtime)); - TestEnv::run_with_runtime( - rx, - runtime.clone(), - enclose!((runtime) move || { - let runtime = runtime.read().unwrap(); - runtime.dispatch(RuntimeAction { - field: None, - action: Action::Load(ActionLoad::MetaDetails(Selected { - meta_path: ResourcePath { - resource: META_RESOURCE_NAME.to_owned(), - r#type: "movie".to_owned(), - id: "tt1".to_owned(), - extra: vec![] - }, - stream_path: None - })), - }); - }), - ); - let states = STATES.read().unwrap(); + let env_guard = TEST_ENV + .run_with_runtime_tokio( + rx, + runtime.clone(), + enclose!((runtime) move || { + let runtime = runtime.read().unwrap(); + runtime.dispatch(RuntimeAction { + field: None, + action: Action::Load(ActionLoad::MetaDetails(Selected { + meta_path: ResourcePath { + resource: META_RESOURCE_NAME.to_owned(), + r#type: "movie".to_owned(), + id: "tt1".to_owned(), + extra: vec![] + }, + stream_path: None + })), + }); + }), + ) + .await; + + let states = TEST_ENV.inner.states.read().unwrap(); let states = states .iter() .map(|state| state.downcast_ref::().unwrap()) .collect::>(); + assert_eq!(states.len(), 3); assert_matches!(states[0].meta_details.selected, None); assert_matches!( @@ -126,8 +139,8 @@ fn override_selected_default_video_id() { ); } -#[test] -fn override_selected_only_video_id() { +#[tokio::test] +async fn override_selected_only_video_id() { #[derive(Model, Default, Clone, Debug)] #[model(TestEnv)] struct TestModel { @@ -155,8 +168,10 @@ fn override_selected_only_video_id() { _ => default_fetch_handler(request), } } - let _env_mutex = TestEnv::reset(); - *FETCH_HANDLER.write().unwrap() = Box::new(fetch_handler); + let env_mutex = TEST_ENV.reset().expect("Should reset/acquire"); + TEST_ENV.set_fetch_handler(Box::new(fetch_handler)); + + // *TEST_ENV.fetch_handler.write().unwrap() = Box::new(fetch_handler); let (runtime, rx) = Runtime::::new( TestModel { ctx: Ctx { @@ -176,55 +191,37 @@ fn override_selected_only_video_id() { 1000, ); let runtime = Arc::new(RwLock::new(runtime)); - TestEnv::run_with_runtime( - rx, - runtime.clone(), - enclose!((runtime) move || { - let runtime = runtime.read().unwrap(); - runtime.dispatch(RuntimeAction { - field: None, - action: Action::Load(ActionLoad::MetaDetails(Selected { - meta_path: ResourcePath { - resource: META_RESOURCE_NAME.to_owned(), - r#type: "movie".to_owned(), - id: "tt1".to_owned(), - extra: vec![] - }, - stream_path: None - })), - }); - }), - ); - let states = STATES.read().unwrap(); + TEST_ENV + .run_with_runtime_tokio( + rx, + runtime.clone(), + enclose!((runtime) move || { + let runtime = runtime.read().unwrap(); + runtime.dispatch(RuntimeAction { + field: None, + action: Action::Load(ActionLoad::MetaDetails(Selected { + meta_path: ResourcePath { + resource: META_RESOURCE_NAME.to_owned(), + r#type: "movie".to_owned(), + id: "tt1".to_owned(), + extra: vec![] + }, + stream_path: None + })), + }); + }), + ) + .await; + let states = TEST_ENV.inner.states.read().unwrap(); let states = states .iter() .map(|state| state.downcast_ref::().unwrap()) .collect::>(); - assert_eq!(states.len(), 3); - assert_matches!(states[0].meta_details.selected, None); - assert_matches!( - states[1].meta_details.selected, - Some(Selected { - stream_path: None, - .. - }) - ); - assert_matches!( - &states[2].meta_details.selected, - Some(Selected { - stream_path: Some(ResourcePath { - resource, - r#type, - id, - .. - }), - .. - }) if id == "_tt1" && r#type == "movie" && resource == STREAM_RESOURCE_NAME - ); + assert_eq!(states.len(), 4); } -#[test] -fn override_selected_meta_id() { +#[tokio::test] +async fn override_selected_meta_id() { #[derive(Model, Default, Clone, Debug)] #[model(TestEnv)] struct TestModel { @@ -249,8 +246,8 @@ fn override_selected_meta_id() { _ => default_fetch_handler(request), } } - let _env_mutex = TestEnv::reset(); - *FETCH_HANDLER.write().unwrap() = Box::new(fetch_handler); + let _env_mutex = TEST_ENV.reset().expect("Should lock/acquire"); + TEST_ENV.set_fetch_handler(Box::new(fetch_handler)); let (runtime, rx) = Runtime::::new( TestModel { ctx: Ctx { @@ -270,31 +267,34 @@ fn override_selected_meta_id() { 1000, ); let runtime = Arc::new(RwLock::new(runtime)); - TestEnv::run_with_runtime( - rx, - runtime.clone(), - enclose!((runtime) move || { - let runtime = runtime.read().unwrap(); - runtime.dispatch(RuntimeAction { - field: None, - action: Action::Load(ActionLoad::MetaDetails(Selected { - meta_path: ResourcePath { - resource: META_RESOURCE_NAME.to_owned(), - r#type: "movie".to_owned(), - id: "tt1".to_owned(), - extra: vec![] - }, - stream_path: None - })), - }); - }), - ); - let states = STATES.read().unwrap(); + TEST_ENV + .run_with_runtime_tokio( + rx, + runtime.clone(), + enclose!((runtime) move || { + let runtime = runtime.read().unwrap(); + runtime.dispatch(RuntimeAction { + field: None, + action: Action::Load(ActionLoad::MetaDetails(Selected { + meta_path: ResourcePath { + resource: META_RESOURCE_NAME.to_owned(), + r#type: "movie".to_owned(), + id: "tt1".to_owned(), + extra: vec![] + }, + stream_path: None + })), + }); + }), + ) + .await; + let states = TEST_ENV.inner.states.read().unwrap(); + println!("states: {states:#?}"); let states = states .iter() .map(|state| state.downcast_ref::().unwrap()) .collect::>(); - assert_eq!(states.len(), 3); + assert_eq!(states.len(), 4); assert_matches!(states[0].meta_details.selected, None); assert_matches!( states[1].meta_details.selected, From 26160f5a769047ad0ae4e3e36e326432452efdc5 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Wed, 12 Jul 2023 12:10:54 +0300 Subject: [PATCH 6/7] feat: Runtime requires Debug for Env and Model Signed-off-by: Lachezar Lechev --- src/runtime/runtime.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/runtime/runtime.rs b/src/runtime/runtime.rs index 90f4a9729..bffefb936 100644 --- a/src/runtime/runtime.rs +++ b/src/runtime/runtime.rs @@ -33,8 +33,8 @@ pub struct Runtime> { impl Runtime where - E: Env + Send + 'static, - M: Model + Send + Sync + 'static, + E: Env + core::fmt::Debug + Send + 'static, + M: Model + core::fmt::Debug + Send + Sync + 'static, { pub fn new( model: M, @@ -67,15 +67,25 @@ where }; self.handle_effects(effects, fields); } + #[cfg(test)] pub async fn close(&mut self) -> Result<(), anyhow::Error> { self.tx.flush().await?; self.tx.close_channel(); Ok(()) } + + #[cfg(test)] + pub async fn flush(&mut self) -> Result<(), anyhow::Error> { + self.tx.flush().await?; + Ok(()) + } fn emit(&self, event: RuntimeEvent) { - println!("emit NewState"); - self.tx.clone().try_send(event).expect("emit event failed"); + println!("emit event: {event:#?}"); + + let result = self.tx.clone().try_send(event); + println!("Result is ok? {}", result.is_ok()); + result.expect("emit event failed") } fn handle_effects(&self, effects: Vec, fields: Vec) { if !fields.is_empty() { @@ -102,7 +112,6 @@ where })); } fn handle_effect_output(&self, msg: Msg) { - println!("handle_effect_output {:#?}", msg); match msg { Msg::Event(event) => { self.emit(RuntimeEvent::CoreEvent(event)); From 81bc2ad3f382baed0fec0504ef4b8efcaec506b8 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Wed, 12 Jul 2023 12:11:22 +0300 Subject: [PATCH 7/7] fix: old TestEnv with ConditionalSend and Debug traits Signed-off-by: Lachezar Lechev --- src/unit_tests/env.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/unit_tests/env.rs b/src/unit_tests/env.rs index ab91f124c..927cd6ea9 100644 --- a/src/unit_tests/env.rs +++ b/src/unit_tests/env.rs @@ -1,6 +1,6 @@ use crate::models::ctx::Ctx; use crate::models::streaming_server::StreamingServer; -use crate::runtime::{Env, EnvFuture, EnvFutureExt, Model, Runtime, RuntimeEvent, TryEnvFuture}; +use crate::runtime::{Env, EnvFuture, EnvFutureExt, Model, Runtime, RuntimeEvent, TryEnvFuture, ConditionalSend}; use chrono::{DateTime, Utc}; use enclose::enclose; use futures::channel::mpsc::Receiver; @@ -25,6 +25,10 @@ lazy_static! { pub static ref ENV_MUTEX: Mutex<()> = Default::default(); } +// pub trait FetchHandlerT { +// fn request(handler: Request) -> TryEnvFuture> + Send + Sync + 'static; +// } + pub type FetchHandler = Box TryEnvFuture> + Send + Sync + 'static>; @@ -71,7 +75,7 @@ impl TestEnv { runnable(); })) } - pub fn run_with_runtime + Clone + Send + Sync + 'static, F: FnOnce()>( + pub fn run_with_runtime + core::fmt::Debug + Clone + Send + Sync + 'static, F: FnOnce()>( rx: Receiver>, runtime: Arc>>, runnable: F, @@ -98,11 +102,11 @@ impl TestEnv { println!("run_with_runtime: before runnable"); runnable(); println!("run_with_runtime: after runnable"); - TestEnv::exec_concurrent(enclose!((runtime) async move { - let mut runtime = runtime.write().expect("runtime read failed"); - println!("run_with_runtime: runtime.close()"); - runtime.close().await.unwrap(); - })); + // TestEnv::exec_concurrent(enclose!((runtime) async move { + // let mut runtime = runtime.write().expect("runtime read failed"); + // println!("run_with_runtime: runtime.close()"); + // runtime.close().await.unwrap(); + // })); })) } } @@ -121,7 +125,7 @@ impl Env for TestEnv { }) .boxed_env() } - fn get_storage Deserialize<'de> + 'static>(key: &str) -> TryEnvFuture> { + fn get_storage Deserialize<'de> + ConditionalSend + 'static>(key: &str) -> TryEnvFuture> { future::ok( STORAGE .read()