diff --git a/Cargo.lock b/Cargo.lock index e1c9b013a4dca..d1fb32520e7cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8425,6 +8425,7 @@ dependencies = [ "turbo-tasks", "turbo-tasks-build", "turbo-tasks-fs", + "turbo-tasks-memory", "turbo-tasks-testing", "turbopack-core", ] @@ -8506,6 +8507,7 @@ dependencies = [ "trybuild", "turbo-tasks", "turbo-tasks-build", + "turbo-tasks-memory", "turbo-tasks-testing", ] @@ -8559,7 +8561,6 @@ dependencies = [ "rustc-hash", "tokio", "turbo-tasks", - "turbo-tasks-memory", ] [[package]] diff --git a/turbopack/crates/turbo-tasks-build/src/lib.rs b/turbopack/crates/turbo-tasks-build/src/lib.rs index 266797b3a6130..80d882abd56f5 100644 --- a/turbopack/crates/turbo-tasks-build/src/lib.rs +++ b/turbopack/crates/turbo-tasks-build/src/lib.rs @@ -55,7 +55,8 @@ pub fn generate_register() { if examples_dir.exists() { for item in read_dir(examples_dir).unwrap() { let item = item.unwrap(); - if item.file_type().unwrap().is_file() { + let file_type = &item.file_type().unwrap(); + if file_type.is_file() || file_type.is_symlink() { let name = item.file_name(); let name = name.to_string_lossy(); if name.ends_with(".rs") { @@ -68,7 +69,8 @@ pub fn generate_register() { if tests_dir.exists() { for item in read_dir(tests_dir).unwrap() { let item = item.unwrap(); - if item.file_type().unwrap().is_file() { + let file_type = &item.file_type().unwrap(); + if file_type.is_file() || file_type.is_symlink() { let name = item.file_name(); let name = name.to_string_lossy(); if name.ends_with(".rs") { @@ -80,7 +82,7 @@ pub fn generate_register() { if benches_dir.exists() { let bench_mod = benches_dir.join("mod.rs"); - if bench_mod.is_file() { + if bench_mod.is_file() || bench_mod.is_symlink() { let name = bench_mod.file_name().unwrap(); let name = name.to_string_lossy(); if name.ends_with(".rs") { diff --git a/turbopack/crates/turbo-tasks-fetch/Cargo.toml b/turbopack/crates/turbo-tasks-fetch/Cargo.toml index 59a2ed5d63df2..18ed73a43e17e 100644 --- a/turbopack/crates/turbo-tasks-fetch/Cargo.toml +++ b/turbopack/crates/turbo-tasks-fetch/Cargo.toml @@ -31,6 +31,7 @@ turbopack-core = { workspace = true } httpmock = { workspace = true } tokio = { workspace = true, features = ["full"] } turbo-tasks-testing = { workspace = true } +turbo-tasks-memory = { workspace = true } [build-dependencies] turbo-tasks-build = { workspace = true } diff --git a/turbopack/crates/turbo-tasks-fetch/tests/test_config.trs b/turbopack/crates/turbo-tasks-fetch/tests/test_config.trs new file mode 100644 index 0000000000000..d5d5cbf904acd --- /dev/null +++ b/turbopack/crates/turbo-tasks-fetch/tests/test_config.trs @@ -0,0 +1 @@ +turbo_tasks::TurboTasks::new(turbo_tasks_memory::MemoryBackend::new(usize::MAX)) diff --git a/turbopack/crates/turbo-tasks-macros-tests/Cargo.toml b/turbopack/crates/turbo-tasks-macros-tests/Cargo.toml index d52804b5d68d1..4aa905d2721dc 100644 --- a/turbopack/crates/turbo-tasks-macros-tests/Cargo.toml +++ b/turbopack/crates/turbo-tasks-macros-tests/Cargo.toml @@ -12,6 +12,7 @@ tokio = { workspace = true } trybuild = { version = "1.0.97" } turbo-tasks = { workspace = true } turbo-tasks-testing = { workspace = true } +turbo-tasks-memory = { workspace = true } [build-dependencies] turbo-tasks-build = { workspace = true } diff --git a/turbopack/crates/turbo-tasks-macros-tests/tests/test_config.trs b/turbopack/crates/turbo-tasks-macros-tests/tests/test_config.trs new file mode 100644 index 0000000000000..d5d5cbf904acd --- /dev/null +++ b/turbopack/crates/turbo-tasks-macros-tests/tests/test_config.trs @@ -0,0 +1 @@ +turbo_tasks::TurboTasks::new(turbo_tasks_memory::MemoryBackend::new(usize::MAX)) diff --git a/turbopack/crates/turbo-tasks-memory/tests/all_in_one.rs b/turbopack/crates/turbo-tasks-memory/tests/all_in_one.rs deleted file mode 100644 index 91f2cf36980af..0000000000000 --- a/turbopack/crates/turbo-tasks-memory/tests/all_in_one.rs +++ /dev/null @@ -1,201 +0,0 @@ -#![feature(arbitrary_self_types)] - -use anyhow::{anyhow, bail, Result}; -use turbo_tasks::{RcStr, Value, ValueToString, Vc}; -use turbo_tasks_testing::{register, run, Registration}; - -static REGISTRATION: Registration = register!(); - -#[tokio::test] -async fn all_in_one() { - run(®ISTRATION, async { - let a: Vc = Vc::cell(4242); - assert_eq!(*a.await?, 4242); - - let a: Vc = Vc::cell(4242); - assert_eq!(*a.await?, 4242); - - let b = MyEnumValue::cell(MyEnumValue::More(MyEnumValue::Yeah(42).into())); - assert_eq!(*b.to_string().await?, "42"); - - let c = MyStructValue { - value: 42, - next: Some(MyStructValue::new(a)), - } - .into(); - - let result = my_function(a, b.get_last(), c, Value::new(MyEnumValue::Yeah(42))); - assert_eq!(*result.my_trait_function().await?, "42"); - assert_eq!(*result.my_trait_function2().await?, "42"); - assert_eq!(*result.my_trait_function3().await?, "4242"); - assert_eq!(*result.to_string().await?, "42"); - - // Testing Vc in traits - - let a: Vc = Vc::cell(32); - let b: Vc = Vc::cell(10); - let c: Vc = a.add(Vc::upcast(b)); - - assert_eq!(*c.await?, 42); - - let a_erased: Vc> = Vc::upcast(a); - let b_erased: Vc> = Vc::upcast(b); - let c_erased: Vc> = a_erased.add(b_erased); - - assert_eq!( - *Vc::try_resolve_downcast_type::(c_erased) - .await? - .unwrap() - .await?, - 42 - ); - - let b_erased_other: Vc> = Vc::upcast(Vc::::cell(10)); - let c_erased_invalid: Vc> = a_erased.add(b_erased_other); - assert!(c_erased_invalid.resolve().await.is_err()); - - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[turbo_tasks::value(transparent, serialization = "auto_for_input")] -#[derive(Debug, Clone, Hash)] -struct MyTransparentValue(u32); - -#[turbo_tasks::value(shared, serialization = "auto_for_input")] -#[derive(Debug, Clone, Hash)] -enum MyEnumValue { - Yeah(u32), - Nah, - More(Vc), -} - -#[turbo_tasks::value_impl] -impl MyEnumValue { - #[turbo_tasks::function] - pub async fn get_last(self: Vc) -> Result> { - let mut current = self; - while let MyEnumValue::More(more) = &*current.await? { - current = *more; - } - Ok(current) - } -} - -#[turbo_tasks::value_impl] -impl ValueToString for MyEnumValue { - #[turbo_tasks::function] - fn to_string(&self) -> Vc { - match self { - MyEnumValue::Yeah(value) => Vc::cell(value.to_string().into()), - MyEnumValue::Nah => Vc::cell("nah".into()), - MyEnumValue::More(more) => more.to_string(), - } - } -} - -#[turbo_tasks::value(shared)] -struct MyStructValue { - value: u32, - next: Option>, -} - -#[turbo_tasks::value_impl] -impl MyStructValue { - #[turbo_tasks::function] - pub async fn new(value: Vc) -> Result> { - Ok(Self::cell(MyStructValue { - value: *value.await?, - next: None, - })) - } -} - -#[turbo_tasks::value_impl] -impl ValueToString for MyStructValue { - #[turbo_tasks::function] - fn to_string(&self) -> Vc { - Vc::cell(self.value.to_string().into()) - } -} - -#[turbo_tasks::value_impl] -impl MyTrait for MyStructValue { - #[turbo_tasks::function] - fn my_trait_function2(self: Vc) -> Vc { - self.to_string() - } - #[turbo_tasks::function] - async fn my_trait_function3(&self) -> Result> { - if let Some(next) = self.next { - return Ok(next.my_trait_function3()); - } - Ok(Vc::cell(self.value.to_string().into())) - } -} - -#[turbo_tasks::value_trait] -trait MyTrait: ValueToString { - // TODO #[turbo_tasks::function] - async fn my_trait_function(self: Vc) -> Result> { - if *self.to_string().await? != "42" { - return Err(anyhow!( - "my_trait_function must only be called with 42 as value" - )); - } - // Calling a function twice - Ok(self.to_string()) - } - - fn my_trait_function2(self: Vc) -> Vc; - fn my_trait_function3(self: Vc) -> Vc; -} - -#[turbo_tasks::function] -async fn my_function( - a: Vc, - b: Vc, - c: Vc, - d: Value, -) -> Result> { - assert_eq!(*a.await?, 4242); - assert_eq!(*b.await?, MyEnumValue::Yeah(42)); - assert_eq!(c.await?.value, 42); - assert_eq!(d.into_value(), MyEnumValue::Yeah(42)); - Ok(c) -} - -#[turbo_tasks::value_trait] -trait Add { - fn add(self: Vc, other: Vc>) -> Vc; -} - -#[turbo_tasks::value(transparent)] -struct Number(u32); - -#[turbo_tasks::value_impl] -impl Add for Number { - #[turbo_tasks::function] - async fn add(self: Vc, other: Vc>) -> Result> { - let Some(other) = Vc::try_resolve_downcast_type::(other).await? else { - bail!("Expected Number"); - }; - Ok(Vc::cell(*self.await? + *other.await?)) - } -} - -#[turbo_tasks::value(transparent)] -struct NumberB(u32); - -#[turbo_tasks::value_impl] -impl Add for NumberB { - #[turbo_tasks::function] - async fn add(self: Vc, other: Vc>) -> Result> { - let Some(other) = Vc::try_resolve_downcast_type::(other).await? else { - bail!("Expected NumberB"); - }; - Ok(Vc::cell(*self.await? + *other.await?)) - } -} diff --git a/turbopack/crates/turbo-tasks-memory/tests/all_in_one.rs b/turbopack/crates/turbo-tasks-memory/tests/all_in_one.rs new file mode 120000 index 0000000000000..391ab595a93e2 --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/all_in_one.rs @@ -0,0 +1 @@ +../../turbo-tasks-testing/tests/all_in_one.rs \ No newline at end of file diff --git a/turbopack/crates/turbo-tasks-memory/tests/call_types.rs b/turbopack/crates/turbo-tasks-memory/tests/call_types.rs deleted file mode 100644 index 6870d396e64f1..0000000000000 --- a/turbopack/crates/turbo-tasks-memory/tests/call_types.rs +++ /dev/null @@ -1,193 +0,0 @@ -#![feature(arbitrary_self_types)] - -use anyhow::Result; -use turbo_tasks::Vc; -use turbo_tasks_testing::{register, run, Registration}; - -static REGISTRATION: Registration = register!(); - -#[tokio::test] -async fn functions() { - run(®ISTRATION, async { - assert_eq!(*fn_plain().await?, 42); - assert_eq!(*fn_arg(43).await?, 43); - assert_eq!(*fn_vc_arg(Vc::cell(44)).await?, 44); - assert_eq!(*async_fn_plain().await?, 42); - assert_eq!(*async_fn_arg(43).await?, 43); - assert_eq!(*async_fn_vc_arg(Vc::cell(44)).await?, 44); - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[turbo_tasks::function] -fn fn_plain() -> Vc { - Vc::cell(42) -} - -#[turbo_tasks::function] -fn fn_arg(n: u32) -> Vc { - Vc::cell(n) -} - -#[turbo_tasks::function] -fn fn_vc_arg(n: Vc) -> Vc { - n -} - -#[turbo_tasks::function] -async fn async_fn_plain() -> Result> { - Ok(Vc::cell(42)) -} - -#[turbo_tasks::function] -async fn async_fn_arg(n: u32) -> Result> { - Ok(Vc::cell(n)) -} - -#[turbo_tasks::function] -async fn async_fn_vc_arg(n: Vc) -> Result> { - Ok(Vc::cell(*n.await?)) -} - -#[tokio::test] -async fn methods() { - run(®ISTRATION, async { - assert_eq!(*Value::static_method().await?, 42); - assert_eq!(*Value::async_static_method().await?, 42); - - let value = Value(43).cell(); - assert_eq!(*value.method().await?, 43); - assert_eq!(*value.async_method().await?, 43); - assert_eq!(*value.vc_method().await?, 42); - assert_eq!(*value.async_vc_method().await?, 43); - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[turbo_tasks::value] -struct Value(u32); - -#[turbo_tasks::value_impl] -impl Value { - #[turbo_tasks::function] - fn static_method() -> Vc { - Vc::cell(42) - } - - #[turbo_tasks::function] - async fn async_static_method() -> Result> { - Ok(Vc::cell(42)) - } - - #[turbo_tasks::function] - fn method(&self) -> Vc { - Vc::cell(self.0) - } - - #[turbo_tasks::function] - async fn async_method(&self) -> Result> { - Ok(Vc::cell(self.0)) - } - - #[turbo_tasks::function] - fn vc_method(self: Vc) -> Vc { - Vc::cell(42) - } - - #[turbo_tasks::function] - async fn async_vc_method(self: Vc) -> Result> { - Ok(Vc::cell(self.await?.0)) - } -} - -#[tokio::test] -async fn trait_methods() { - run(®ISTRATION, async { - assert_eq!(*Value::static_trait_method().await?, 42); - assert_eq!(*Value::async_static_trait_method().await?, 42); - - let value = Value(43).cell(); - assert_eq!(*value.trait_method().await?, 43); - assert_eq!(*value.async_trait_method().await?, 43); - assert_eq!(*value.default_trait_method().await?, 42); - assert_eq!(*value.default_async_trait_method().await?, 42); - - let trait_value: Vc> = Vc::upcast(value); - assert_eq!(*trait_value.trait_method().await?, 43); - assert_eq!(*trait_value.async_trait_method().await?, 43); - assert_eq!(*trait_value.default_trait_method().await?, 42); - assert_eq!(*trait_value.default_async_trait_method().await?, 42); - - let value = wrap_value(value); - assert_eq!(*value.trait_method().await?, 43); - assert_eq!(*value.async_trait_method().await?, 43); - assert_eq!(*value.default_trait_method().await?, 42); - assert_eq!(*value.default_async_trait_method().await?, 42); - - let trait_value = wrap_trait_value(trait_value); - assert_eq!(*trait_value.trait_method().await?, 43); - assert_eq!(*trait_value.async_trait_method().await?, 43); - assert_eq!(*trait_value.default_trait_method().await?, 42); - assert_eq!(*trait_value.default_async_trait_method().await?, 42); - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[turbo_tasks::function] -fn wrap_value(v: Vc) -> Vc { - v -} - -#[turbo_tasks::function] -fn wrap_trait_value(v: Vc>) -> Vc> { - v -} - -#[turbo_tasks::value_trait] -trait ValueTrait { - fn static_trait_method() -> Vc; - async fn async_static_trait_method() -> Result>; - fn default_static_trait_method() -> Vc { - Vc::cell(42) - } - async fn default_async_static_trait_method() -> Result> { - Ok(Vc::cell(42)) - } - fn trait_method(&self) -> Vc; - fn async_trait_method(&self) -> Result>; - fn default_trait_method(self: Vc) -> Vc { - Vc::cell(42) - } - async fn default_async_trait_method(self: Vc) -> Result> { - Ok(Vc::cell(42)) - } -} - -#[turbo_tasks::value_impl] -impl ValueTrait for Value { - #[turbo_tasks::function] - fn static_trait_method() -> Vc { - Vc::cell(42) - } - - #[turbo_tasks::function] - async fn async_static_trait_method() -> Result> { - Ok(Vc::cell(42)) - } - - #[turbo_tasks::function] - fn trait_method(&self) -> Vc { - Vc::cell(self.0) - } - - #[turbo_tasks::function] - async fn async_trait_method(&self) -> Result> { - Ok(Vc::cell(self.0)) - } -} diff --git a/turbopack/crates/turbo-tasks-memory/tests/call_types.rs b/turbopack/crates/turbo-tasks-memory/tests/call_types.rs new file mode 120000 index 0000000000000..b20501cd53c79 --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/call_types.rs @@ -0,0 +1 @@ +../../turbo-tasks-testing/tests/call_types.rs \ No newline at end of file diff --git a/turbopack/crates/turbo-tasks-memory/tests/collectibles.rs b/turbopack/crates/turbo-tasks-memory/tests/collectibles.rs deleted file mode 100644 index 2bf0b392117ab..0000000000000 --- a/turbopack/crates/turbo-tasks-memory/tests/collectibles.rs +++ /dev/null @@ -1,217 +0,0 @@ -#![feature(arbitrary_self_types)] - -use std::{collections::HashSet, time::Duration}; - -use anyhow::Result; -use auto_hash_map::AutoSet; -use tokio::time::sleep; -use turbo_tasks::{emit, CollectiblesSource, RcStr, ValueToString, Vc}; -use turbo_tasks_testing::{register, run, Registration}; - -static REGISTRATION: Registration = register!(); - -#[tokio::test] -async fn transitive_emitting() { - run(®ISTRATION, async { - let result = my_transitive_emitting_function("".into(), "".into()); - result.strongly_consistent().await?; - let list = result.peek_collectibles::>(); - assert_eq!(list.len(), 2); - let mut expected = ["123", "42"].into_iter().collect::>(); - for collectible in list { - assert!(expected.remove(collectible.to_string().await?.as_str())) - } - assert_eq!(result.await?.0, 0); - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[tokio::test] -async fn transitive_emitting_indirect() { - run(®ISTRATION, async { - let result = my_transitive_emitting_function("".into(), "".into()); - let collectibles = my_transitive_emitting_function_collectibles("".into(), "".into()); - let list = collectibles.strongly_consistent().await?; - assert_eq!(list.len(), 2); - let mut expected = ["123", "42"].into_iter().collect::>(); - for collectible in list.iter() { - assert!(expected.remove(collectible.to_string().await?.as_str())) - } - assert_eq!(result.await?.0, 0); - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[tokio::test] -async fn multi_emitting() { - run(®ISTRATION, async { - let result = my_multi_emitting_function(); - result.strongly_consistent().await?; - let list = result.peek_collectibles::>(); - assert_eq!(list.len(), 2); - let mut expected = ["123", "42"].into_iter().collect::>(); - for collectible in list { - assert!(expected.remove(collectible.to_string().await?.as_str())) - } - assert_eq!(result.await?.0, 0); - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[tokio::test] -async fn taking_collectibles() { - run(®ISTRATION, async { - let result = my_collecting_function(); - let list = result.take_collectibles::>(); - // my_collecting_function already processed the collectibles so the list should - // be empty - assert!(list.is_empty()); - assert_eq!(result.await?.0, 0); - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[tokio::test] -async fn taking_collectibles_extra_layer() { - run(®ISTRATION, async { - let result = my_collecting_function_indirect(); - result.strongly_consistent().await?; - let list = result.take_collectibles::>(); - // my_collecting_function already processed the collectibles so the list should - // be empty - assert!(list.is_empty()); - assert_eq!(result.await?.0, 0); - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[tokio::test] -async fn taking_collectibles_parallel() { - run(®ISTRATION, async { - let result = my_transitive_emitting_function("".into(), "a".into()); - result.strongly_consistent().await?; - let list = result.take_collectibles::>(); - assert_eq!(list.len(), 2); - assert_eq!(result.await?.0, 0); - - let result = my_transitive_emitting_function("".into(), "b".into()); - result.strongly_consistent().await?; - let list = result.take_collectibles::>(); - assert_eq!(list.len(), 2); - assert_eq!(result.await?.0, 0); - - let result = - my_transitive_emitting_function_with_child_scope("".into(), "b".into(), "1".into()); - result.strongly_consistent().await?; - let list = result.take_collectibles::>(); - assert_eq!(list.len(), 2); - assert_eq!(result.await?.0, 0); - - let result = - my_transitive_emitting_function_with_child_scope("".into(), "b".into(), "2".into()); - result.strongly_consistent().await?; - let list = result.take_collectibles::>(); - assert_eq!(list.len(), 2); - assert_eq!(result.await?.0, 0); - - let result = - my_transitive_emitting_function_with_child_scope("".into(), "c".into(), "3".into()); - result.strongly_consistent().await?; - let list = result.take_collectibles::>(); - assert_eq!(list.len(), 2); - assert_eq!(result.await?.0, 0); - - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[turbo_tasks::value(transparent)] -struct Collectibles(AutoSet>>); - -#[turbo_tasks::function] -async fn my_collecting_function() -> Result> { - let result = my_transitive_emitting_function("".into(), "".into()); - result.take_collectibles::>(); - Ok(result) -} - -#[turbo_tasks::function] -async fn my_collecting_function_indirect() -> Result> { - let result = my_collecting_function(); - result.strongly_consistent().await?; - let list = result.peek_collectibles::>(); - // my_collecting_function already processed the collectibles so the list should - // be empty - assert!(list.is_empty()); - Ok(result) -} - -#[turbo_tasks::function] -async fn my_multi_emitting_function() -> Result> { - my_transitive_emitting_function("".into(), "a".into()).await?; - my_transitive_emitting_function("".into(), "b".into()).await?; - my_emitting_function("".into()).await?; - Ok(Thing::cell(Thing(0))) -} - -#[turbo_tasks::function] -async fn my_transitive_emitting_function(key: RcStr, _key2: RcStr) -> Result> { - my_emitting_function(key).await?; - Ok(Thing::cell(Thing(0))) -} - -#[turbo_tasks::function] -async fn my_transitive_emitting_function_collectibles(key: RcStr, key2: RcStr) -> Vc { - let result = my_transitive_emitting_function(key, key2); - Vc::cell(result.peek_collectibles::>()) -} - -#[turbo_tasks::function] -async fn my_transitive_emitting_function_with_child_scope( - key: RcStr, - key2: RcStr, - _key3: RcStr, -) -> Result> { - let thing = my_transitive_emitting_function(key, key2); - thing.strongly_consistent().await?; - let list = thing.peek_collectibles::>(); - assert_eq!(list.len(), 2); - Ok(thing) -} - -#[turbo_tasks::function] -async fn my_emitting_function(_key: RcStr) -> Result<()> { - sleep(Duration::from_millis(100)).await; - emit(Vc::upcast::>(Thing::new(123))); - emit(Vc::upcast::>(Thing::new(42))); - Ok(()) -} - -#[turbo_tasks::value(shared)] -struct Thing(u32); - -impl Thing { - fn new(v: u32) -> Vc { - Self::cell(Thing(v)) - } -} - -#[turbo_tasks::value_impl] -impl ValueToString for Thing { - #[turbo_tasks::function] - fn to_string(&self) -> Vc { - Vc::cell(self.0.to_string().into()) - } -} diff --git a/turbopack/crates/turbo-tasks-memory/tests/collectibles.rs b/turbopack/crates/turbo-tasks-memory/tests/collectibles.rs new file mode 120000 index 0000000000000..7de5bc7d80499 --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/collectibles.rs @@ -0,0 +1 @@ +../../turbo-tasks-testing/tests/collectibles.rs \ No newline at end of file diff --git a/turbopack/crates/turbo-tasks-memory/tests/debug.rs b/turbopack/crates/turbo-tasks-memory/tests/debug.rs deleted file mode 100644 index 42b5366ddd8a4..0000000000000 --- a/turbopack/crates/turbo-tasks-memory/tests/debug.rs +++ /dev/null @@ -1,180 +0,0 @@ -#![feature(arbitrary_self_types)] - -use std::sync::Mutex; - -use turbo_tasks::{debug::ValueDebug, Vc}; -use turbo_tasks_testing::{register, run, Registration}; - -static REGISTRATION: Registration = register!(); - -#[tokio::test] -async fn primitive_debug() { - run(®ISTRATION, async { - let a: Vc = Vc::cell(42); - assert_eq!(format!("{:?}", a.dbg().await.unwrap()), "42"); - }) - .await -} - -#[tokio::test] -async fn transparent_debug() { - run(®ISTRATION, async { - let a: Vc = Transparent(42).cell(); - assert_eq!(format!("{:?}", a.dbg().await.unwrap()), "42"); - }) - .await -} - -#[tokio::test] -async fn enum_none_debug() { - run(®ISTRATION, async { - let a: Vc = Enum::None.cell(); - assert_eq!(format!("{:?}", a.dbg().await.unwrap()), "Enum :: None"); - }) - .await -} - -#[tokio::test] -async fn enum_transparent_debug() { - run(®ISTRATION, async { - let a: Vc = Enum::Transparent(Transparent(42).cell()).cell(); - assert_eq!( - format!("{:?}", a.dbg().await.unwrap()), - r#"Enum :: Transparent( - 42, -)"# - ); - }) - .await -} - -#[tokio::test] -async fn enum_inner_vc_debug() { - run(®ISTRATION, async { - let a: Vc = Enum::Enum(Enum::None.cell()).cell(); - assert_eq!( - format!("{:?}", a.dbg().await.unwrap()), - r#"Enum :: Enum( - Enum :: None, -)"# - ); - }) - .await -} - -#[tokio::test] -async fn struct_unit_debug() { - run(®ISTRATION, async { - let a: Vc = StructUnit.cell(); - assert_eq!(format!("{:?}", a.dbg().await.unwrap()), "StructUnit"); - }) - .await -} - -#[tokio::test] -async fn struct_transparent_debug() { - run(®ISTRATION, async { - let a: Vc = StructWithTransparent { - transparent: Transparent(42).cell(), - } - .cell(); - assert_eq!( - format!("{:?}", a.dbg().await.unwrap()), - r#"StructWithTransparent { - transparent: 42, -}"# - ); - }) - .await -} - -#[tokio::test] -async fn struct_vec_debug() { - run(®ISTRATION, async { - let a: Vc = StructWithVec { vec: vec![] }.cell(); - assert_eq!( - format!("{:?}", a.dbg().await.unwrap()), - r#"StructWithVec { - vec: [], -}"# - ); - - let b: Vc = StructWithVec { - vec: vec![Transparent(42).cell()], - } - .cell(); - assert_eq!( - format!("{:?}", b.dbg().await.unwrap()), - r#"StructWithVec { - vec: [ - 42, - ], -}"# - ); - }) - .await -} - -#[tokio::test] -async fn struct_ignore_debug() { - run(®ISTRATION, async { - let a: Vc = StructWithIgnore { - dont_ignore: 42, - ignore: Mutex::new(()), - } - .cell(); - assert_eq!( - format!("{:?}", a.dbg().await.unwrap()), - r#"StructWithIgnore { - dont_ignore: 42, -}"# - ); - }) - .await -} - -#[turbo_tasks::value(transparent, shared)] -struct Transparent(u32); - -// Allow Enum::Enum -#[allow(clippy::enum_variant_names)] -#[turbo_tasks::value(shared)] -enum Enum { - None, - Transparent(Vc), - Enum(Vc), -} - -#[turbo_tasks::value(shared)] -struct StructUnit; - -#[turbo_tasks::value(shared)] -struct StructWithTransparent { - transparent: Vc, -} - -#[turbo_tasks::value(shared)] -struct StructWithOption { - option: Option>, -} - -#[turbo_tasks::value(shared)] -struct StructWithVec { - vec: Vec>, -} - -#[turbo_tasks::value(shared, eq = "manual")] -struct StructWithIgnore { - dont_ignore: u32, - // We're using a `Mutex` instead of a `T: Debug` type to ensure we support `T: !Debug`. - #[turbo_tasks(debug_ignore, trace_ignore)] - ignore: Mutex<()>, -} - -impl PartialEq for StructWithIgnore { - fn eq(&self, other: &Self) -> bool { - self.dont_ignore == other.dont_ignore - } -} - -impl Eq for StructWithIgnore {} diff --git a/turbopack/crates/turbo-tasks-memory/tests/debug.rs b/turbopack/crates/turbo-tasks-memory/tests/debug.rs new file mode 120000 index 0000000000000..ee7aea7eab52f --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/debug.rs @@ -0,0 +1 @@ +../../turbo-tasks-testing/tests/debug.rs \ No newline at end of file diff --git a/turbopack/crates/turbo-tasks-memory/tests/dirty_in_progress.rs b/turbopack/crates/turbo-tasks-memory/tests/dirty_in_progress.rs deleted file mode 100644 index 6f67cdc0b45b5..0000000000000 --- a/turbopack/crates/turbo-tasks-memory/tests/dirty_in_progress.rs +++ /dev/null @@ -1,102 +0,0 @@ -#![feature(arbitrary_self_types)] - -use std::time::Duration; - -use anyhow::{bail, Result}; -use turbo_tasks::{emit, CollectiblesSource, RcStr, State, ValueToString, Vc}; -use turbo_tasks_testing::{register, run, Registration}; - -static REGISTRATION: Registration = register!(); - -#[tokio::test] -async fn dirty_in_progress() { - run(®ISTRATION, async { - let cases = [ - (1, 3, 2, 2, ""), - (11, 13, 12, 42, "12"), - (1, 13, 11, 42, "11"), - (1, 3, 11, 42, "11"), - (11, 3, 2, 2, ""), - (11, 13, 2, 2, ""), - ]; - for (a, b, c, value, collectible) in cases { - println!("{} -> {} -> {} = {} {}", a, b, c, value, collectible); - let input = ChangingInput { - state: State::new(a), - } - .cell(); - let input_val = input.await.unwrap(); - let output = compute(input); - output.await.unwrap(); - println!("update to {}", b); - input_val.state.set(b); - tokio::time::sleep(Duration::from_millis(100)).await; - println!("update to {}", c); - input_val.state.set(c); - let read = output.strongly_consistent().await.unwrap(); - assert_eq!(read.value, value); - assert_eq!(read.collectible, collectible); - } - }) - .await -} - -#[turbo_tasks::value] -struct ChangingInput { - state: State, -} - -#[turbo_tasks::value] -struct Output { - value: u32, - collectible: String, -} - -#[turbo_tasks::value] -struct Collectible { - value: u32, -} - -#[turbo_tasks::value_impl] -impl ValueToString for Collectible { - #[turbo_tasks::function] - fn to_string(&self) -> Vc { - Vc::cell(self.value.to_string().into()) - } -} - -#[turbo_tasks::function] -async fn inner_compute(input: Vc) -> Result> { - println!("start inner_compute"); - let value = *input.await?.state.get(); - tokio::time::sleep(Duration::from_millis(200)).await; - if value > 10 { - let collectible: Vc> = Vc::upcast(Collectible { value }.cell()); - emit(collectible); - - println!("end inner_compute with collectible"); - Ok(Vc::cell(42)) - } else { - println!("end inner_compute without collectible"); - Ok(Vc::cell(value)) - } -} - -#[turbo_tasks::function] -async fn compute(input: Vc) -> Result> { - println!("start compute"); - let operation = inner_compute(input); - let value = *operation.await?; - let collectibles = operation.peek_collectibles::>(); - if collectibles.len() > 1 { - bail!("expected 0..1 collectible, found {}", collectibles.len()); - } - let first = collectibles.iter().next(); - let collectible = if let Some(first) = first { - first.to_string().await?.to_string() - } else { - "".to_string() - }; - println!("end compute"); - Ok(Output { value, collectible }.cell()) -} diff --git a/turbopack/crates/turbo-tasks-memory/tests/dirty_in_progress.rs b/turbopack/crates/turbo-tasks-memory/tests/dirty_in_progress.rs new file mode 120000 index 0000000000000..0a45daf6b8443 --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/dirty_in_progress.rs @@ -0,0 +1 @@ +../../turbo-tasks-testing/tests/dirty_in_progress.rs \ No newline at end of file diff --git a/turbopack/crates/turbo-tasks-memory/tests/emptied_cells.rs b/turbopack/crates/turbo-tasks-memory/tests/emptied_cells.rs deleted file mode 100644 index 326636c709b54..0000000000000 --- a/turbopack/crates/turbo-tasks-memory/tests/emptied_cells.rs +++ /dev/null @@ -1,67 +0,0 @@ -#![feature(arbitrary_self_types)] - -use anyhow::Result; -use turbo_tasks::{State, Vc}; -use turbo_tasks_testing::{register, run, Registration}; - -static REGISTRATION: Registration = register!(); - -#[tokio::test] -async fn recompute() { - run(®ISTRATION, async { - let input = ChangingInput { - state: State::new(1), - } - .cell(); - let output = compute(input); - assert_eq!(*output.await.unwrap(), 1); - - println!("changing input"); - input.await.unwrap().state.set(10); - assert_eq!(*output.strongly_consistent().await.unwrap(), 10); - - println!("changing input"); - input.await.unwrap().state.set(5); - assert_eq!(*output.strongly_consistent().await.unwrap(), 5); - - println!("changing input"); - input.await.unwrap().state.set(20); - assert_eq!(*output.strongly_consistent().await.unwrap(), 20); - - println!("changing input"); - input.await.unwrap().state.set(15); - assert_eq!(*output.strongly_consistent().await.unwrap(), 15); - - println!("changing input"); - input.await.unwrap().state.set(1); - assert_eq!(*output.strongly_consistent().await.unwrap(), 1); - }) - .await -} - -#[turbo_tasks::value] -struct ChangingInput { - state: State, -} - -#[turbo_tasks::function] -async fn compute(input: Vc) -> Result> { - let value = *inner_compute(input).await?; - Ok(Vc::cell(value)) -} - -#[turbo_tasks::function] -async fn inner_compute(input: Vc) -> Result> { - let state_value = *input.await?.state.get(); - let mut last = None; - for i in 0..=state_value { - last = Some(compute2(Vc::cell(i))); - } - Ok(last.unwrap()) -} - -#[turbo_tasks::function] -async fn compute2(input: Vc) -> Result> { - let value = *input.await?; - Ok(Vc::cell(value)) -} diff --git a/turbopack/crates/turbo-tasks-memory/tests/emptied_cells.rs b/turbopack/crates/turbo-tasks-memory/tests/emptied_cells.rs new file mode 120000 index 0000000000000..9070c4d0b4dcc --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/emptied_cells.rs @@ -0,0 +1 @@ +../../turbo-tasks-testing/tests/emptied_cells.rs \ No newline at end of file diff --git a/turbopack/crates/turbo-tasks-memory/tests/generics.rs b/turbopack/crates/turbo-tasks-memory/tests/generics.rs deleted file mode 100644 index b3e8990ba4031..0000000000000 --- a/turbopack/crates/turbo-tasks-memory/tests/generics.rs +++ /dev/null @@ -1,202 +0,0 @@ -#![feature(arbitrary_self_types)] - -use std::sync::{Arc, Mutex}; - -use indexmap::{IndexMap, IndexSet}; -use turbo_tasks::{debug::ValueDebug, Invalidator, ReadRef, TaskId, Vc}; -use turbo_tasks_testing::{register, run, Registration}; - -static REGISTRATION: Registration = register!(); - -#[tokio::test] -async fn test_option_some() { - run(®ISTRATION, async move { - let vc_42 = Vc::cell(42); - let option: Vc>> = Vc::cell(Some(vc_42)); - assert!(*option.is_some().await.unwrap()); - assert!(!(*option.is_none().await.unwrap())); - assert_eq!(&*option.await.unwrap(), &Some(vc_42)); - assert_eq!(option.dbg().await.unwrap().to_string(), "Some(\n 42,\n)"); - }) - .await -} - -#[tokio::test] -async fn test_option_none() { - run(®ISTRATION, async move { - let option: Vc>> = Default::default(); - assert!(!(*option.is_some().await.unwrap())); - assert!(*option.is_none().await.unwrap()); - assert_eq!(&*option.await.unwrap(), &None); - assert_eq!(option.dbg().await.unwrap().to_string(), "None"); - }) - .await -} - -#[tokio::test] -async fn test_vec() { - run(®ISTRATION, async move { - let vc_42 = Vc::cell(42); - let vec: Vc>> = Vc::cell(vec![vc_42]); - assert_eq!(*vec.len().await.unwrap(), 1); - assert!(!(*vec.is_empty().await.unwrap())); - assert_eq!(&*vec.await.unwrap(), &[vc_42]); - assert_eq!(vec.dbg().await.unwrap().to_string(), "[\n 42,\n]"); - }) - .await -} - -#[tokio::test] -async fn test_empty_vec() { - run(®ISTRATION, async move { - let vec: Vc>> = Default::default(); - assert_eq!(*vec.len().await.unwrap(), 0); - assert!(*vec.is_empty().await.unwrap()); - assert_eq!(vec.dbg().await.unwrap().to_string(), "[]"); - }) - .await -} - -#[tokio::test] -async fn test_nested_empty_vec() { - run(®ISTRATION, async move { - let vec: Vc>>>> = Default::default(); - assert_eq!(*vec.len().await.unwrap(), 0); - assert_eq!(vec.dbg().await.unwrap().to_string(), "[]"); - }) - .await -} - -#[tokio::test] -async fn test_index_set() { - run(®ISTRATION, async move { - let vc_42 = Vc::cell(42); - let set: Vc>> = Vc::cell(IndexSet::from([vc_42])); - assert_eq!(*set.len().await.unwrap(), 1); - assert!(!(*set.is_empty().await.unwrap())); - assert_eq!(&*set.await.unwrap(), &IndexSet::from([vc_42])); - assert_eq!(set.dbg().await.unwrap().to_string(), "{\n 42,\n}"); - }) - .await -} - -#[tokio::test] -async fn test_empty_index_set() { - run(®ISTRATION, async move { - let set: Vc>> = Default::default(); - assert_eq!(*set.len().await.unwrap(), 0); - assert!(*set.is_empty().await.unwrap()); - assert_eq!(&*set.await.unwrap(), &IndexSet::>::default()); - assert_eq!(set.dbg().await.unwrap().to_string(), "{}"); - }) - .await -} - -#[tokio::test] -async fn test_index_map() { - run(®ISTRATION, async move { - let vc_42 = Vc::cell(42); - let map: Vc, _>> = Vc::cell(IndexMap::from([(vc_42, vc_42)])); - assert_eq!(*map.len().await.unwrap(), 1); - assert!(!(*map.is_empty().await.unwrap())); - assert_eq!(&*map.await.unwrap(), &IndexMap::from([(vc_42, vc_42)])); - assert_eq!(map.dbg().await.unwrap().to_string(), "{\n 42: 42,\n}"); - }) - .await -} - -#[tokio::test] -async fn test_empty_index_map() { - run(®ISTRATION, async move { - let map: Vc, Vc>> = Default::default(); - assert_eq!(*map.len().await.unwrap(), 0); - assert!(*map.is_empty().await.unwrap()); - assert_eq!( - &*map.await.unwrap(), - &IndexMap::, Vc>::default() - ); - assert_eq!(map.dbg().await.unwrap().to_string(), "{}"); - }) - .await -} - -// Simulate a non-deterministic function that stores different generic types in -// it's cells each time it runs. -#[tokio::test] -async fn test_changing_generic() { - run(®ISTRATION, async move { - let state_vc = State::default().cell(); - let state_ref = state_vc.await.unwrap(); - for _i in 0..10 { - let _ = non_deterministic(state_vc) - .resolve_strongly_consistent() - .await - .unwrap(); - state_ref - .inner - .lock() - .unwrap() - .last_invalidator - .take() - .unwrap() - .invalidate(); - } - }) - .await -} - -// Test that we can convert a `Vc` to a `ReadRef`, and then back to a `Vc`. -#[tokio::test] -async fn test_read_ref_round_trip() { - run(®ISTRATION, async move { - let c: Vc>> = Vc::cell(Some(Vc::cell(1))); - let _ = ReadRef::cell(c.await.unwrap()).await.unwrap(); - }) - .await -} - -#[turbo_tasks::value(eq = "manual")] -#[derive(Default)] -struct State { - #[turbo_tasks(debug_ignore, trace_ignore)] - #[serde(skip)] - inner: Arc>, -} - -#[derive(Default)] -struct StateInner { - branch: bool, - last_invalidator: Option, - last_task_id: Option, -} - -impl PartialEq for State { - fn eq(&self, other: &Self) -> bool { - std::ptr::eq(self as *const _, other as *const _) - } -} - -impl Eq for State {} - -#[turbo_tasks::function] -async fn non_deterministic(state: Vc) { - let state = state.await.unwrap(); - let mut state_inner = state.inner.lock().unwrap(); - - let task_id = if state_inner.branch { - let c: Vc>> = Vc::cell(Some(Vc::cell(1))); - println!("u8 branch"); - Vc::into_raw(c).get_task_id() - } else { - let c: Vc>> = Vc::cell(Some(Vc::cell(1))); - println!("u32 branch"); - Vc::into_raw(c).get_task_id() - }; - - state_inner.branch = !state_inner.branch; - if let Some(last_task_id) = state_inner.last_task_id { - assert_eq!(last_task_id, task_id); - } - state_inner.last_task_id = Some(task_id); - state_inner.last_invalidator = Some(turbo_tasks::get_invalidator()); -} diff --git a/turbopack/crates/turbo-tasks-memory/tests/generics.rs b/turbopack/crates/turbo-tasks-memory/tests/generics.rs new file mode 120000 index 0000000000000..526d71f58d8ba --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/generics.rs @@ -0,0 +1 @@ +../../turbo-tasks-testing/tests/generics.rs \ No newline at end of file diff --git a/turbopack/crates/turbo-tasks-memory/tests/read_ref_cell.rs b/turbopack/crates/turbo-tasks-memory/tests/read_ref_cell.rs deleted file mode 100644 index 6266d3c56fe5d..0000000000000 --- a/turbopack/crates/turbo-tasks-memory/tests/read_ref_cell.rs +++ /dev/null @@ -1,85 +0,0 @@ -#![feature(arbitrary_self_types)] - -use std::sync::Mutex; - -use anyhow::Result; -use turbo_tasks::{get_invalidator, Invalidator, ReadRef, Vc}; -use turbo_tasks_testing::{register, run, Registration}; - -static REGISTRATION: Registration = register!(); - -#[tokio::test] -async fn read_ref() { - run(®ISTRATION, async { - let counter = Counter::cell(Counter { - value: Mutex::new((0, None)), - }); - - let counter_value = counter.get_value(); - - assert_eq!(*counter.get_value().strongly_consistent().await?, 0); - assert_eq!(*counter_value.strongly_consistent().await?, 0); - - counter.await?.incr(); - - assert_eq!(*counter.get_value().strongly_consistent().await?, 1); - assert_eq!(*counter_value.strongly_consistent().await?, 1); - - // `ref_counter` will still point to the same `counter` instance as `counter`. - let ref_counter = ReadRef::cell(counter.await?); - let ref_counter_value = ref_counter.get_value(); - - // However, `local_counter_value` will point to the value of `counter_value` - // at the time it was turned into a trait reference (just like a `ReadRef` - // would). - let local_counter_value = ReadRef::cell(counter_value.await?).get_value(); - - counter.await?.incr(); - - assert_eq!(*counter.get_value().strongly_consistent().await?, 2); - assert_eq!(*counter_value.strongly_consistent().await?, 2); - assert_eq!(*ref_counter_value.strongly_consistent().await?, 2); - assert_eq!(*local_counter_value.strongly_consistent().await?, 1); - - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[turbo_tasks::value(transparent)] -struct CounterValue(usize); - -#[turbo_tasks::value(serialization = "none", cell = "new", eq = "manual")] -struct Counter { - #[turbo_tasks(debug_ignore, trace_ignore)] - value: Mutex<(usize, Option)>, -} - -impl Counter { - fn incr(&self) { - let mut lock = self.value.lock().unwrap(); - lock.0 += 1; - if let Some(i) = lock.1.take() { - i.invalidate(); - } - } -} - -#[turbo_tasks::value_impl] -impl Counter { - #[turbo_tasks::function] - async fn get_value(&self) -> Result> { - let mut lock = self.value.lock().unwrap(); - lock.1 = Some(get_invalidator()); - Ok(Vc::cell(lock.0)) - } -} - -#[turbo_tasks::value_impl] -impl CounterValue { - #[turbo_tasks::function] - fn get_value(self: Vc) -> Vc { - self - } -} diff --git a/turbopack/crates/turbo-tasks-memory/tests/read_ref_cell.rs b/turbopack/crates/turbo-tasks-memory/tests/read_ref_cell.rs new file mode 120000 index 0000000000000..4e1719dfefec2 --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/read_ref_cell.rs @@ -0,0 +1 @@ +../../turbo-tasks-testing/tests/read_ref_cell.rs \ No newline at end of file diff --git a/turbopack/crates/turbo-tasks-memory/tests/recompute.rs b/turbopack/crates/turbo-tasks-memory/tests/recompute.rs deleted file mode 100644 index 976efd03e66b7..0000000000000 --- a/turbopack/crates/turbo-tasks-memory/tests/recompute.rs +++ /dev/null @@ -1,93 +0,0 @@ -#![feature(arbitrary_self_types)] - -use anyhow::Result; -use turbo_tasks::{State, Vc}; -use turbo_tasks_testing::{register, run, Registration}; - -static REGISTRATION: Registration = register!(); - -#[tokio::test] -async fn recompute() { - run(®ISTRATION, async { - let input = ChangingInput { - state: State::new(1), - } - .cell(); - let input2 = ChangingInput { - state: State::new(10), - } - .cell(); - let output = compute(input, input2); - let read = output.await?; - assert_eq!(read.state_value, 1); - assert_eq!(read.state_value2, 10); - let random_value = read.random_value; - - println!("changing input"); - input.await?.state.set(2); - let read = output.strongly_consistent().await?; - assert_eq!(read.state_value, 2); - assert_ne!(read.random_value, random_value); - let random_value = read.random_value; - - println!("changing input2"); - input2.await?.state.set(20); - let read = output.strongly_consistent().await?; - assert_eq!(read.state_value2, 20); - assert_ne!(read.random_value, random_value); - let random_value = read.random_value; - - println!("changing input"); - input.await?.state.set(5); - let read = output.strongly_consistent().await?; - assert_eq!(read.state_value, 5); - assert_eq!(read.state_value2, 42); - assert_ne!(read.random_value, random_value); - let random_value = read.random_value; - - println!("changing input2"); - input2.await?.state.set(30); - let read = output.strongly_consistent().await?; - assert_eq!(read.random_value, random_value); - - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[turbo_tasks::value] -struct ChangingInput { - state: State, -} - -#[turbo_tasks::value] -struct Output { - state_value: u32, - state_value2: u32, - random_value: u32, -} - -#[turbo_tasks::function] -async fn compute(input: Vc, input2: Vc) -> Result> { - let state_value = *input.await?.state.get(); - let state_value2 = if state_value < 5 { - *compute2(input2).await? - } else { - 42 - }; - let random_value = rand::random(); - - Ok(Output { - state_value, - state_value2, - random_value, - } - .cell()) -} - -#[turbo_tasks::function] -async fn compute2(input: Vc) -> Result> { - let state_value = *input.await?.state.get(); - Ok(Vc::cell(state_value)) -} diff --git a/turbopack/crates/turbo-tasks-memory/tests/recompute.rs b/turbopack/crates/turbo-tasks-memory/tests/recompute.rs new file mode 120000 index 0000000000000..5c35fb81af4e3 --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/recompute.rs @@ -0,0 +1 @@ +../../turbo-tasks-testing/tests/recompute.rs \ No newline at end of file diff --git a/turbopack/crates/turbo-tasks-memory/tests/recompute_collectibles.rs b/turbopack/crates/turbo-tasks-memory/tests/recompute_collectibles.rs deleted file mode 100644 index 07391c207a703..0000000000000 --- a/turbopack/crates/turbo-tasks-memory/tests/recompute_collectibles.rs +++ /dev/null @@ -1,94 +0,0 @@ -#![feature(arbitrary_self_types)] - -use anyhow::{bail, Result}; -use turbo_tasks::{emit, CollectiblesSource, RcStr, State, ValueToString, Vc}; -use turbo_tasks_testing::{register, run, Registration}; - -static REGISTRATION: Registration = register!(); - -#[tokio::test] -async fn recompute() { - run(®ISTRATION, async { - let input = ChangingInput { - state: State::new(1), - } - .cell(); - let output = compute(input, 100); - let read = output.await.unwrap(); - assert_eq!(read.value, 42); - assert_eq!(read.collectible, "1"); - - for i in 2..100 { - input.await.unwrap().state.set(i); - let read = output.strongly_consistent().await.unwrap(); - assert_eq!(read.value, 42); - assert_eq!(read.collectible, i.to_string()); - } - }) - .await -} - -#[turbo_tasks::value] -struct ChangingInput { - state: State, -} - -#[turbo_tasks::value] -struct Output { - value: u32, - collectible: String, -} - -#[turbo_tasks::value] -struct Collectible { - value: u32, -} - -#[turbo_tasks::value_impl] -impl ValueToString for Collectible { - #[turbo_tasks::function] - fn to_string(&self) -> Vc { - Vc::cell(self.value.to_string().into()) - } -} - -#[turbo_tasks::function] -fn inner_compute(input: Vc) -> Vc { - inner_compute2(input, 1000) -} - -#[turbo_tasks::function] -async fn inner_compute2(input: Vc, innerness: u32) -> Result> { - if innerness > 0 { - return Ok(inner_compute2(input, innerness - 1)); - } - let collectible: Vc> = Vc::upcast( - Collectible { - value: *input.await?.state.get(), - } - .cell(), - ); - emit(collectible); - - Ok(Vc::cell(42)) -} - -#[turbo_tasks::function] -async fn compute(input: Vc, innerness: u32) -> Result> { - if innerness > 0 { - return Ok(compute(input, innerness - 1)); - } - let operation = inner_compute(input); - let value = *operation.await?; - let collectibles = operation.peek_collectibles::>(); - if collectibles.len() != 1 { - bail!("expected 1 collectible, found {}", collectibles.len()); - } - let first = *collectibles.iter().next().unwrap(); - let collectible = first.to_string().await?; - Ok(Output { - value, - collectible: collectible.to_string(), - } - .cell()) -} diff --git a/turbopack/crates/turbo-tasks-memory/tests/recompute_collectibles.rs b/turbopack/crates/turbo-tasks-memory/tests/recompute_collectibles.rs new file mode 120000 index 0000000000000..664d8d48d1408 --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/recompute_collectibles.rs @@ -0,0 +1 @@ +../../turbo-tasks-testing/tests/recompute_collectibles.rs \ No newline at end of file diff --git a/turbopack/crates/turbo-tasks-memory/tests/scope_stress.rs b/turbopack/crates/turbo-tasks-memory/tests/scope_stress.rs deleted file mode 100644 index fa5e827269815..0000000000000 --- a/turbopack/crates/turbo-tasks-memory/tests/scope_stress.rs +++ /dev/null @@ -1,52 +0,0 @@ -#![feature(arbitrary_self_types)] - -use anyhow::Result; -use turbo_tasks::{Completion, TryJoinIterExt, TurboTasks, Vc}; -use turbo_tasks_memory::MemoryBackend; -use turbo_tasks_testing::{register, Registration}; - -static REGISTRATION: Registration = register!(); - -#[test] -fn rectangle_stress() { - REGISTRATION.ensure_registered(); - let rt = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap(); - rt.block_on(async { - let tt = TurboTasks::new(MemoryBackend::default()); - let size = std::env::var("TURBOPACK_TEST_RECTANGLE_STRESS_SIZE") - .map(|size| size.parse().unwrap()) - .unwrap_or(50); - (0..size) - .map(|a| (a, size - 1)) - .chain((0..size - 1).map(|b| (size - 1, b))) - .map(|(a, b)| { - let tt = &tt; - async move { - let task = tt.spawn_once_task(async move { - rectangle(a, b).strongly_consistent().await?; - Ok(Vc::<()>::default()) - }); - tt.wait_task_completion(task, false).await - } - }) - .try_join() - .await - .unwrap(); - }) -} - -/// This fills a rectagle from (0, 0) to (a, b) by -/// first filling (0, 0) to (a - 1, b) and then (0, 0) to (a, b - 1) recursively -#[turbo_tasks::function] -async fn rectangle(a: u32, b: u32) -> Result> { - if a > 0 { - rectangle(a - 1, b).await?; - } - if b > 0 { - rectangle(a, b - 1).await?; - } - Ok(Completion::new()) -} diff --git a/turbopack/crates/turbo-tasks-memory/tests/scope_stress.rs b/turbopack/crates/turbo-tasks-memory/tests/scope_stress.rs new file mode 120000 index 0000000000000..0ca21b0967629 --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/scope_stress.rs @@ -0,0 +1 @@ +../../turbo-tasks-testing/tests/scope_stress.rs \ No newline at end of file diff --git a/turbopack/crates/turbo-tasks-memory/tests/test_config.trs b/turbopack/crates/turbo-tasks-memory/tests/test_config.trs new file mode 100644 index 0000000000000..d5d5cbf904acd --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/test_config.trs @@ -0,0 +1 @@ +turbo_tasks::TurboTasks::new(turbo_tasks_memory::MemoryBackend::new(usize::MAX)) diff --git a/turbopack/crates/turbo-tasks-memory/tests/trait_ref_cell.rs b/turbopack/crates/turbo-tasks-memory/tests/trait_ref_cell.rs deleted file mode 100644 index cfde0d3f4d828..0000000000000 --- a/turbopack/crates/turbo-tasks-memory/tests/trait_ref_cell.rs +++ /dev/null @@ -1,104 +0,0 @@ -#![feature(arbitrary_self_types)] - -use std::sync::Mutex; - -use anyhow::Result; -use turbo_tasks::{get_invalidator, IntoTraitRef, Invalidator, TraitRef, Vc}; -use turbo_tasks_testing::{register, run, Registration}; - -static REGISTRATION: Registration = register!(); - -#[tokio::test] -async fn trait_ref() { - run(®ISTRATION, async { - let counter = Counter::cell(Counter { - value: Mutex::new((0, None)), - }); - - let counter_value = counter.get_value(); - - assert_eq!(*counter.get_value().strongly_consistent().await?, 0); - assert_eq!(*counter_value.strongly_consistent().await?, 0); - - counter.await?.incr(); - - assert_eq!(*counter.get_value().strongly_consistent().await?, 1); - assert_eq!(*counter_value.strongly_consistent().await?, 1); - - // `ref_counter` will still point to the same `counter` instance as `counter`. - let ref_counter = TraitRef::cell( - Vc::upcast::>(counter) - .into_trait_ref() - .await?, - ); - let ref_counter_value = ref_counter.get_value(); - - // However, `local_counter_value` will point to the value of `counter_value` - // at the time it was turned into a trait reference (just like a `ReadRef` - // would). - let local_counter_value = TraitRef::cell( - Vc::upcast::>(counter_value) - .into_trait_ref() - .await?, - ) - .get_value(); - - counter.await?.incr(); - - assert_eq!(*counter.get_value().strongly_consistent().await?, 2); - assert_eq!(*counter_value.strongly_consistent().await?, 2); - assert_eq!(*ref_counter_value.strongly_consistent().await?, 2); - assert_eq!(*local_counter_value.strongly_consistent().await?, 1); - - anyhow::Ok(()) - }) - .await - .unwrap() -} - -#[turbo_tasks::value(transparent)] -struct CounterValue(usize); - -#[turbo_tasks::value(serialization = "none", cell = "new", eq = "manual")] -struct Counter { - #[turbo_tasks(debug_ignore, trace_ignore)] - value: Mutex<(usize, Option)>, -} - -impl Counter { - fn incr(&self) { - let mut lock = self.value.lock().unwrap(); - lock.0 += 1; - if let Some(i) = lock.1.take() { - i.invalidate(); - } - } -} - -#[turbo_tasks::value_trait] -trait CounterTrait { - fn get_value(&self) -> Vc; -} - -#[turbo_tasks::value_impl] -impl CounterTrait for Counter { - #[turbo_tasks::function] - async fn get_value(&self) -> Result> { - let mut lock = self.value.lock().unwrap(); - lock.1 = Some(get_invalidator()); - Ok(Vc::cell(lock.0)) - } -} - -#[turbo_tasks::value_trait] -trait CounterValueTrait { - fn get_value(&self) -> Vc; -} - -#[turbo_tasks::value_impl] -impl CounterValueTrait for CounterValue { - #[turbo_tasks::function] - fn get_value(self: Vc) -> Vc { - self - } -} diff --git a/turbopack/crates/turbo-tasks-memory/tests/trait_ref_cell.rs b/turbopack/crates/turbo-tasks-memory/tests/trait_ref_cell.rs new file mode 120000 index 0000000000000..026eed7f3b50f --- /dev/null +++ b/turbopack/crates/turbo-tasks-memory/tests/trait_ref_cell.rs @@ -0,0 +1 @@ +../../turbo-tasks-testing/tests/trait_ref_cell.rs \ No newline at end of file diff --git a/turbopack/crates/turbo-tasks-testing/Cargo.toml b/turbopack/crates/turbo-tasks-testing/Cargo.toml index 30de612015616..14f374cf5fe50 100644 --- a/turbopack/crates/turbo-tasks-testing/Cargo.toml +++ b/turbopack/crates/turbo-tasks-testing/Cargo.toml @@ -5,6 +5,7 @@ description = "TBD" license = "MPL-2.0" edition = "2021" autobenches = false +autotests = false [lib] bench = false @@ -19,4 +20,3 @@ futures = { workspace = true } rustc-hash = { workspace = true } tokio = { workspace = true } turbo-tasks = { workspace = true } -turbo-tasks-memory = { workspace = true } diff --git a/turbopack/crates/turbo-tasks-testing/src/run.rs b/turbopack/crates/turbo-tasks-testing/src/run.rs index 7b101049af59b..ef6b9ac89f9a2 100644 --- a/turbopack/crates/turbo-tasks-testing/src/run.rs +++ b/turbopack/crates/turbo-tasks-testing/src/run.rs @@ -1,19 +1,23 @@ -use std::{future::Future, sync::OnceLock}; +use std::{ + future::Future, + sync::{Arc, OnceLock}, +}; -use turbo_tasks::{trace::TraceRawVcs, TurboTasks}; -use turbo_tasks_memory::MemoryBackend; +use turbo_tasks::{run_once, trace::TraceRawVcs, TurboTasksApi}; pub struct Registration { execution_lock: OnceLock<()>, func: fn(), + create_turbo_tasks: fn() -> Arc, } impl Registration { #[doc(hidden)] - pub const fn new(func: fn()) -> Self { + pub const fn new(create_turbo_tasks: fn() -> Arc, func: fn()) -> Self { Registration { execution_lock: OnceLock::new(), func, + create_turbo_tasks, } } @@ -23,11 +27,23 @@ impl Registration { pub fn ensure_registered(&self) { self.execution_lock.get_or_init(self.func); } + + pub fn create_turbo_tasks(&self) -> Arc { + (self.create_turbo_tasks)() + } } #[macro_export] macro_rules! register { ($($other_register_fns:expr),* $(,)?) => {{ + use turbo_tasks::TurboTasksApi; + use std::sync::Arc; + fn create_turbo_tasks() -> Arc { + include!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/test_config.trs" + )) + } fn register_impl() { $($other_register_fns();)* turbo_tasks::register(); @@ -38,7 +54,7 @@ macro_rules! register { ".rs", )); } - turbo_tasks_testing::Registration::new(register_impl) + turbo_tasks_testing::Registration::new(create_turbo_tasks, register_impl) }}; } @@ -47,6 +63,6 @@ where T: TraceRawVcs + Send + 'static, { registration.ensure_registered(); - let tt = TurboTasks::new(MemoryBackend::default()); - tt.run_once(async move { Ok(fut.await) }).await.unwrap() + let tt = registration.create_turbo_tasks(); + run_once(tt, async move { Ok(fut.await) }).await.unwrap() } diff --git a/turbopack/crates/turbo-tasks-testing/tests/all_in_one.rs b/turbopack/crates/turbo-tasks-testing/tests/all_in_one.rs new file mode 100644 index 0000000000000..91f2cf36980af --- /dev/null +++ b/turbopack/crates/turbo-tasks-testing/tests/all_in_one.rs @@ -0,0 +1,201 @@ +#![feature(arbitrary_self_types)] + +use anyhow::{anyhow, bail, Result}; +use turbo_tasks::{RcStr, Value, ValueToString, Vc}; +use turbo_tasks_testing::{register, run, Registration}; + +static REGISTRATION: Registration = register!(); + +#[tokio::test] +async fn all_in_one() { + run(®ISTRATION, async { + let a: Vc = Vc::cell(4242); + assert_eq!(*a.await?, 4242); + + let a: Vc = Vc::cell(4242); + assert_eq!(*a.await?, 4242); + + let b = MyEnumValue::cell(MyEnumValue::More(MyEnumValue::Yeah(42).into())); + assert_eq!(*b.to_string().await?, "42"); + + let c = MyStructValue { + value: 42, + next: Some(MyStructValue::new(a)), + } + .into(); + + let result = my_function(a, b.get_last(), c, Value::new(MyEnumValue::Yeah(42))); + assert_eq!(*result.my_trait_function().await?, "42"); + assert_eq!(*result.my_trait_function2().await?, "42"); + assert_eq!(*result.my_trait_function3().await?, "4242"); + assert_eq!(*result.to_string().await?, "42"); + + // Testing Vc in traits + + let a: Vc = Vc::cell(32); + let b: Vc = Vc::cell(10); + let c: Vc = a.add(Vc::upcast(b)); + + assert_eq!(*c.await?, 42); + + let a_erased: Vc> = Vc::upcast(a); + let b_erased: Vc> = Vc::upcast(b); + let c_erased: Vc> = a_erased.add(b_erased); + + assert_eq!( + *Vc::try_resolve_downcast_type::(c_erased) + .await? + .unwrap() + .await?, + 42 + ); + + let b_erased_other: Vc> = Vc::upcast(Vc::::cell(10)); + let c_erased_invalid: Vc> = a_erased.add(b_erased_other); + assert!(c_erased_invalid.resolve().await.is_err()); + + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[turbo_tasks::value(transparent, serialization = "auto_for_input")] +#[derive(Debug, Clone, Hash)] +struct MyTransparentValue(u32); + +#[turbo_tasks::value(shared, serialization = "auto_for_input")] +#[derive(Debug, Clone, Hash)] +enum MyEnumValue { + Yeah(u32), + Nah, + More(Vc), +} + +#[turbo_tasks::value_impl] +impl MyEnumValue { + #[turbo_tasks::function] + pub async fn get_last(self: Vc) -> Result> { + let mut current = self; + while let MyEnumValue::More(more) = &*current.await? { + current = *more; + } + Ok(current) + } +} + +#[turbo_tasks::value_impl] +impl ValueToString for MyEnumValue { + #[turbo_tasks::function] + fn to_string(&self) -> Vc { + match self { + MyEnumValue::Yeah(value) => Vc::cell(value.to_string().into()), + MyEnumValue::Nah => Vc::cell("nah".into()), + MyEnumValue::More(more) => more.to_string(), + } + } +} + +#[turbo_tasks::value(shared)] +struct MyStructValue { + value: u32, + next: Option>, +} + +#[turbo_tasks::value_impl] +impl MyStructValue { + #[turbo_tasks::function] + pub async fn new(value: Vc) -> Result> { + Ok(Self::cell(MyStructValue { + value: *value.await?, + next: None, + })) + } +} + +#[turbo_tasks::value_impl] +impl ValueToString for MyStructValue { + #[turbo_tasks::function] + fn to_string(&self) -> Vc { + Vc::cell(self.value.to_string().into()) + } +} + +#[turbo_tasks::value_impl] +impl MyTrait for MyStructValue { + #[turbo_tasks::function] + fn my_trait_function2(self: Vc) -> Vc { + self.to_string() + } + #[turbo_tasks::function] + async fn my_trait_function3(&self) -> Result> { + if let Some(next) = self.next { + return Ok(next.my_trait_function3()); + } + Ok(Vc::cell(self.value.to_string().into())) + } +} + +#[turbo_tasks::value_trait] +trait MyTrait: ValueToString { + // TODO #[turbo_tasks::function] + async fn my_trait_function(self: Vc) -> Result> { + if *self.to_string().await? != "42" { + return Err(anyhow!( + "my_trait_function must only be called with 42 as value" + )); + } + // Calling a function twice + Ok(self.to_string()) + } + + fn my_trait_function2(self: Vc) -> Vc; + fn my_trait_function3(self: Vc) -> Vc; +} + +#[turbo_tasks::function] +async fn my_function( + a: Vc, + b: Vc, + c: Vc, + d: Value, +) -> Result> { + assert_eq!(*a.await?, 4242); + assert_eq!(*b.await?, MyEnumValue::Yeah(42)); + assert_eq!(c.await?.value, 42); + assert_eq!(d.into_value(), MyEnumValue::Yeah(42)); + Ok(c) +} + +#[turbo_tasks::value_trait] +trait Add { + fn add(self: Vc, other: Vc>) -> Vc; +} + +#[turbo_tasks::value(transparent)] +struct Number(u32); + +#[turbo_tasks::value_impl] +impl Add for Number { + #[turbo_tasks::function] + async fn add(self: Vc, other: Vc>) -> Result> { + let Some(other) = Vc::try_resolve_downcast_type::(other).await? else { + bail!("Expected Number"); + }; + Ok(Vc::cell(*self.await? + *other.await?)) + } +} + +#[turbo_tasks::value(transparent)] +struct NumberB(u32); + +#[turbo_tasks::value_impl] +impl Add for NumberB { + #[turbo_tasks::function] + async fn add(self: Vc, other: Vc>) -> Result> { + let Some(other) = Vc::try_resolve_downcast_type::(other).await? else { + bail!("Expected NumberB"); + }; + Ok(Vc::cell(*self.await? + *other.await?)) + } +} diff --git a/turbopack/crates/turbo-tasks-testing/tests/call_types.rs b/turbopack/crates/turbo-tasks-testing/tests/call_types.rs new file mode 100644 index 0000000000000..6870d396e64f1 --- /dev/null +++ b/turbopack/crates/turbo-tasks-testing/tests/call_types.rs @@ -0,0 +1,193 @@ +#![feature(arbitrary_self_types)] + +use anyhow::Result; +use turbo_tasks::Vc; +use turbo_tasks_testing::{register, run, Registration}; + +static REGISTRATION: Registration = register!(); + +#[tokio::test] +async fn functions() { + run(®ISTRATION, async { + assert_eq!(*fn_plain().await?, 42); + assert_eq!(*fn_arg(43).await?, 43); + assert_eq!(*fn_vc_arg(Vc::cell(44)).await?, 44); + assert_eq!(*async_fn_plain().await?, 42); + assert_eq!(*async_fn_arg(43).await?, 43); + assert_eq!(*async_fn_vc_arg(Vc::cell(44)).await?, 44); + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[turbo_tasks::function] +fn fn_plain() -> Vc { + Vc::cell(42) +} + +#[turbo_tasks::function] +fn fn_arg(n: u32) -> Vc { + Vc::cell(n) +} + +#[turbo_tasks::function] +fn fn_vc_arg(n: Vc) -> Vc { + n +} + +#[turbo_tasks::function] +async fn async_fn_plain() -> Result> { + Ok(Vc::cell(42)) +} + +#[turbo_tasks::function] +async fn async_fn_arg(n: u32) -> Result> { + Ok(Vc::cell(n)) +} + +#[turbo_tasks::function] +async fn async_fn_vc_arg(n: Vc) -> Result> { + Ok(Vc::cell(*n.await?)) +} + +#[tokio::test] +async fn methods() { + run(®ISTRATION, async { + assert_eq!(*Value::static_method().await?, 42); + assert_eq!(*Value::async_static_method().await?, 42); + + let value = Value(43).cell(); + assert_eq!(*value.method().await?, 43); + assert_eq!(*value.async_method().await?, 43); + assert_eq!(*value.vc_method().await?, 42); + assert_eq!(*value.async_vc_method().await?, 43); + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[turbo_tasks::value] +struct Value(u32); + +#[turbo_tasks::value_impl] +impl Value { + #[turbo_tasks::function] + fn static_method() -> Vc { + Vc::cell(42) + } + + #[turbo_tasks::function] + async fn async_static_method() -> Result> { + Ok(Vc::cell(42)) + } + + #[turbo_tasks::function] + fn method(&self) -> Vc { + Vc::cell(self.0) + } + + #[turbo_tasks::function] + async fn async_method(&self) -> Result> { + Ok(Vc::cell(self.0)) + } + + #[turbo_tasks::function] + fn vc_method(self: Vc) -> Vc { + Vc::cell(42) + } + + #[turbo_tasks::function] + async fn async_vc_method(self: Vc) -> Result> { + Ok(Vc::cell(self.await?.0)) + } +} + +#[tokio::test] +async fn trait_methods() { + run(®ISTRATION, async { + assert_eq!(*Value::static_trait_method().await?, 42); + assert_eq!(*Value::async_static_trait_method().await?, 42); + + let value = Value(43).cell(); + assert_eq!(*value.trait_method().await?, 43); + assert_eq!(*value.async_trait_method().await?, 43); + assert_eq!(*value.default_trait_method().await?, 42); + assert_eq!(*value.default_async_trait_method().await?, 42); + + let trait_value: Vc> = Vc::upcast(value); + assert_eq!(*trait_value.trait_method().await?, 43); + assert_eq!(*trait_value.async_trait_method().await?, 43); + assert_eq!(*trait_value.default_trait_method().await?, 42); + assert_eq!(*trait_value.default_async_trait_method().await?, 42); + + let value = wrap_value(value); + assert_eq!(*value.trait_method().await?, 43); + assert_eq!(*value.async_trait_method().await?, 43); + assert_eq!(*value.default_trait_method().await?, 42); + assert_eq!(*value.default_async_trait_method().await?, 42); + + let trait_value = wrap_trait_value(trait_value); + assert_eq!(*trait_value.trait_method().await?, 43); + assert_eq!(*trait_value.async_trait_method().await?, 43); + assert_eq!(*trait_value.default_trait_method().await?, 42); + assert_eq!(*trait_value.default_async_trait_method().await?, 42); + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[turbo_tasks::function] +fn wrap_value(v: Vc) -> Vc { + v +} + +#[turbo_tasks::function] +fn wrap_trait_value(v: Vc>) -> Vc> { + v +} + +#[turbo_tasks::value_trait] +trait ValueTrait { + fn static_trait_method() -> Vc; + async fn async_static_trait_method() -> Result>; + fn default_static_trait_method() -> Vc { + Vc::cell(42) + } + async fn default_async_static_trait_method() -> Result> { + Ok(Vc::cell(42)) + } + fn trait_method(&self) -> Vc; + fn async_trait_method(&self) -> Result>; + fn default_trait_method(self: Vc) -> Vc { + Vc::cell(42) + } + async fn default_async_trait_method(self: Vc) -> Result> { + Ok(Vc::cell(42)) + } +} + +#[turbo_tasks::value_impl] +impl ValueTrait for Value { + #[turbo_tasks::function] + fn static_trait_method() -> Vc { + Vc::cell(42) + } + + #[turbo_tasks::function] + async fn async_static_trait_method() -> Result> { + Ok(Vc::cell(42)) + } + + #[turbo_tasks::function] + fn trait_method(&self) -> Vc { + Vc::cell(self.0) + } + + #[turbo_tasks::function] + async fn async_trait_method(&self) -> Result> { + Ok(Vc::cell(self.0)) + } +} diff --git a/turbopack/crates/turbo-tasks-testing/tests/collectibles.rs b/turbopack/crates/turbo-tasks-testing/tests/collectibles.rs new file mode 100644 index 0000000000000..2bf0b392117ab --- /dev/null +++ b/turbopack/crates/turbo-tasks-testing/tests/collectibles.rs @@ -0,0 +1,217 @@ +#![feature(arbitrary_self_types)] + +use std::{collections::HashSet, time::Duration}; + +use anyhow::Result; +use auto_hash_map::AutoSet; +use tokio::time::sleep; +use turbo_tasks::{emit, CollectiblesSource, RcStr, ValueToString, Vc}; +use turbo_tasks_testing::{register, run, Registration}; + +static REGISTRATION: Registration = register!(); + +#[tokio::test] +async fn transitive_emitting() { + run(®ISTRATION, async { + let result = my_transitive_emitting_function("".into(), "".into()); + result.strongly_consistent().await?; + let list = result.peek_collectibles::>(); + assert_eq!(list.len(), 2); + let mut expected = ["123", "42"].into_iter().collect::>(); + for collectible in list { + assert!(expected.remove(collectible.to_string().await?.as_str())) + } + assert_eq!(result.await?.0, 0); + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[tokio::test] +async fn transitive_emitting_indirect() { + run(®ISTRATION, async { + let result = my_transitive_emitting_function("".into(), "".into()); + let collectibles = my_transitive_emitting_function_collectibles("".into(), "".into()); + let list = collectibles.strongly_consistent().await?; + assert_eq!(list.len(), 2); + let mut expected = ["123", "42"].into_iter().collect::>(); + for collectible in list.iter() { + assert!(expected.remove(collectible.to_string().await?.as_str())) + } + assert_eq!(result.await?.0, 0); + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[tokio::test] +async fn multi_emitting() { + run(®ISTRATION, async { + let result = my_multi_emitting_function(); + result.strongly_consistent().await?; + let list = result.peek_collectibles::>(); + assert_eq!(list.len(), 2); + let mut expected = ["123", "42"].into_iter().collect::>(); + for collectible in list { + assert!(expected.remove(collectible.to_string().await?.as_str())) + } + assert_eq!(result.await?.0, 0); + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[tokio::test] +async fn taking_collectibles() { + run(®ISTRATION, async { + let result = my_collecting_function(); + let list = result.take_collectibles::>(); + // my_collecting_function already processed the collectibles so the list should + // be empty + assert!(list.is_empty()); + assert_eq!(result.await?.0, 0); + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[tokio::test] +async fn taking_collectibles_extra_layer() { + run(®ISTRATION, async { + let result = my_collecting_function_indirect(); + result.strongly_consistent().await?; + let list = result.take_collectibles::>(); + // my_collecting_function already processed the collectibles so the list should + // be empty + assert!(list.is_empty()); + assert_eq!(result.await?.0, 0); + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[tokio::test] +async fn taking_collectibles_parallel() { + run(®ISTRATION, async { + let result = my_transitive_emitting_function("".into(), "a".into()); + result.strongly_consistent().await?; + let list = result.take_collectibles::>(); + assert_eq!(list.len(), 2); + assert_eq!(result.await?.0, 0); + + let result = my_transitive_emitting_function("".into(), "b".into()); + result.strongly_consistent().await?; + let list = result.take_collectibles::>(); + assert_eq!(list.len(), 2); + assert_eq!(result.await?.0, 0); + + let result = + my_transitive_emitting_function_with_child_scope("".into(), "b".into(), "1".into()); + result.strongly_consistent().await?; + let list = result.take_collectibles::>(); + assert_eq!(list.len(), 2); + assert_eq!(result.await?.0, 0); + + let result = + my_transitive_emitting_function_with_child_scope("".into(), "b".into(), "2".into()); + result.strongly_consistent().await?; + let list = result.take_collectibles::>(); + assert_eq!(list.len(), 2); + assert_eq!(result.await?.0, 0); + + let result = + my_transitive_emitting_function_with_child_scope("".into(), "c".into(), "3".into()); + result.strongly_consistent().await?; + let list = result.take_collectibles::>(); + assert_eq!(list.len(), 2); + assert_eq!(result.await?.0, 0); + + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[turbo_tasks::value(transparent)] +struct Collectibles(AutoSet>>); + +#[turbo_tasks::function] +async fn my_collecting_function() -> Result> { + let result = my_transitive_emitting_function("".into(), "".into()); + result.take_collectibles::>(); + Ok(result) +} + +#[turbo_tasks::function] +async fn my_collecting_function_indirect() -> Result> { + let result = my_collecting_function(); + result.strongly_consistent().await?; + let list = result.peek_collectibles::>(); + // my_collecting_function already processed the collectibles so the list should + // be empty + assert!(list.is_empty()); + Ok(result) +} + +#[turbo_tasks::function] +async fn my_multi_emitting_function() -> Result> { + my_transitive_emitting_function("".into(), "a".into()).await?; + my_transitive_emitting_function("".into(), "b".into()).await?; + my_emitting_function("".into()).await?; + Ok(Thing::cell(Thing(0))) +} + +#[turbo_tasks::function] +async fn my_transitive_emitting_function(key: RcStr, _key2: RcStr) -> Result> { + my_emitting_function(key).await?; + Ok(Thing::cell(Thing(0))) +} + +#[turbo_tasks::function] +async fn my_transitive_emitting_function_collectibles(key: RcStr, key2: RcStr) -> Vc { + let result = my_transitive_emitting_function(key, key2); + Vc::cell(result.peek_collectibles::>()) +} + +#[turbo_tasks::function] +async fn my_transitive_emitting_function_with_child_scope( + key: RcStr, + key2: RcStr, + _key3: RcStr, +) -> Result> { + let thing = my_transitive_emitting_function(key, key2); + thing.strongly_consistent().await?; + let list = thing.peek_collectibles::>(); + assert_eq!(list.len(), 2); + Ok(thing) +} + +#[turbo_tasks::function] +async fn my_emitting_function(_key: RcStr) -> Result<()> { + sleep(Duration::from_millis(100)).await; + emit(Vc::upcast::>(Thing::new(123))); + emit(Vc::upcast::>(Thing::new(42))); + Ok(()) +} + +#[turbo_tasks::value(shared)] +struct Thing(u32); + +impl Thing { + fn new(v: u32) -> Vc { + Self::cell(Thing(v)) + } +} + +#[turbo_tasks::value_impl] +impl ValueToString for Thing { + #[turbo_tasks::function] + fn to_string(&self) -> Vc { + Vc::cell(self.0.to_string().into()) + } +} diff --git a/turbopack/crates/turbo-tasks-testing/tests/debug.rs b/turbopack/crates/turbo-tasks-testing/tests/debug.rs new file mode 100644 index 0000000000000..42b5366ddd8a4 --- /dev/null +++ b/turbopack/crates/turbo-tasks-testing/tests/debug.rs @@ -0,0 +1,180 @@ +#![feature(arbitrary_self_types)] + +use std::sync::Mutex; + +use turbo_tasks::{debug::ValueDebug, Vc}; +use turbo_tasks_testing::{register, run, Registration}; + +static REGISTRATION: Registration = register!(); + +#[tokio::test] +async fn primitive_debug() { + run(®ISTRATION, async { + let a: Vc = Vc::cell(42); + assert_eq!(format!("{:?}", a.dbg().await.unwrap()), "42"); + }) + .await +} + +#[tokio::test] +async fn transparent_debug() { + run(®ISTRATION, async { + let a: Vc = Transparent(42).cell(); + assert_eq!(format!("{:?}", a.dbg().await.unwrap()), "42"); + }) + .await +} + +#[tokio::test] +async fn enum_none_debug() { + run(®ISTRATION, async { + let a: Vc = Enum::None.cell(); + assert_eq!(format!("{:?}", a.dbg().await.unwrap()), "Enum :: None"); + }) + .await +} + +#[tokio::test] +async fn enum_transparent_debug() { + run(®ISTRATION, async { + let a: Vc = Enum::Transparent(Transparent(42).cell()).cell(); + assert_eq!( + format!("{:?}", a.dbg().await.unwrap()), + r#"Enum :: Transparent( + 42, +)"# + ); + }) + .await +} + +#[tokio::test] +async fn enum_inner_vc_debug() { + run(®ISTRATION, async { + let a: Vc = Enum::Enum(Enum::None.cell()).cell(); + assert_eq!( + format!("{:?}", a.dbg().await.unwrap()), + r#"Enum :: Enum( + Enum :: None, +)"# + ); + }) + .await +} + +#[tokio::test] +async fn struct_unit_debug() { + run(®ISTRATION, async { + let a: Vc = StructUnit.cell(); + assert_eq!(format!("{:?}", a.dbg().await.unwrap()), "StructUnit"); + }) + .await +} + +#[tokio::test] +async fn struct_transparent_debug() { + run(®ISTRATION, async { + let a: Vc = StructWithTransparent { + transparent: Transparent(42).cell(), + } + .cell(); + assert_eq!( + format!("{:?}", a.dbg().await.unwrap()), + r#"StructWithTransparent { + transparent: 42, +}"# + ); + }) + .await +} + +#[tokio::test] +async fn struct_vec_debug() { + run(®ISTRATION, async { + let a: Vc = StructWithVec { vec: vec![] }.cell(); + assert_eq!( + format!("{:?}", a.dbg().await.unwrap()), + r#"StructWithVec { + vec: [], +}"# + ); + + let b: Vc = StructWithVec { + vec: vec![Transparent(42).cell()], + } + .cell(); + assert_eq!( + format!("{:?}", b.dbg().await.unwrap()), + r#"StructWithVec { + vec: [ + 42, + ], +}"# + ); + }) + .await +} + +#[tokio::test] +async fn struct_ignore_debug() { + run(®ISTRATION, async { + let a: Vc = StructWithIgnore { + dont_ignore: 42, + ignore: Mutex::new(()), + } + .cell(); + assert_eq!( + format!("{:?}", a.dbg().await.unwrap()), + r#"StructWithIgnore { + dont_ignore: 42, +}"# + ); + }) + .await +} + +#[turbo_tasks::value(transparent, shared)] +struct Transparent(u32); + +// Allow Enum::Enum +#[allow(clippy::enum_variant_names)] +#[turbo_tasks::value(shared)] +enum Enum { + None, + Transparent(Vc), + Enum(Vc), +} + +#[turbo_tasks::value(shared)] +struct StructUnit; + +#[turbo_tasks::value(shared)] +struct StructWithTransparent { + transparent: Vc, +} + +#[turbo_tasks::value(shared)] +struct StructWithOption { + option: Option>, +} + +#[turbo_tasks::value(shared)] +struct StructWithVec { + vec: Vec>, +} + +#[turbo_tasks::value(shared, eq = "manual")] +struct StructWithIgnore { + dont_ignore: u32, + // We're using a `Mutex` instead of a `T: Debug` type to ensure we support `T: !Debug`. + #[turbo_tasks(debug_ignore, trace_ignore)] + ignore: Mutex<()>, +} + +impl PartialEq for StructWithIgnore { + fn eq(&self, other: &Self) -> bool { + self.dont_ignore == other.dont_ignore + } +} + +impl Eq for StructWithIgnore {} diff --git a/turbopack/crates/turbo-tasks-testing/tests/dirty_in_progress.rs b/turbopack/crates/turbo-tasks-testing/tests/dirty_in_progress.rs new file mode 100644 index 0000000000000..6f67cdc0b45b5 --- /dev/null +++ b/turbopack/crates/turbo-tasks-testing/tests/dirty_in_progress.rs @@ -0,0 +1,102 @@ +#![feature(arbitrary_self_types)] + +use std::time::Duration; + +use anyhow::{bail, Result}; +use turbo_tasks::{emit, CollectiblesSource, RcStr, State, ValueToString, Vc}; +use turbo_tasks_testing::{register, run, Registration}; + +static REGISTRATION: Registration = register!(); + +#[tokio::test] +async fn dirty_in_progress() { + run(®ISTRATION, async { + let cases = [ + (1, 3, 2, 2, ""), + (11, 13, 12, 42, "12"), + (1, 13, 11, 42, "11"), + (1, 3, 11, 42, "11"), + (11, 3, 2, 2, ""), + (11, 13, 2, 2, ""), + ]; + for (a, b, c, value, collectible) in cases { + println!("{} -> {} -> {} = {} {}", a, b, c, value, collectible); + let input = ChangingInput { + state: State::new(a), + } + .cell(); + let input_val = input.await.unwrap(); + let output = compute(input); + output.await.unwrap(); + println!("update to {}", b); + input_val.state.set(b); + tokio::time::sleep(Duration::from_millis(100)).await; + println!("update to {}", c); + input_val.state.set(c); + let read = output.strongly_consistent().await.unwrap(); + assert_eq!(read.value, value); + assert_eq!(read.collectible, collectible); + } + }) + .await +} + +#[turbo_tasks::value] +struct ChangingInput { + state: State, +} + +#[turbo_tasks::value] +struct Output { + value: u32, + collectible: String, +} + +#[turbo_tasks::value] +struct Collectible { + value: u32, +} + +#[turbo_tasks::value_impl] +impl ValueToString for Collectible { + #[turbo_tasks::function] + fn to_string(&self) -> Vc { + Vc::cell(self.value.to_string().into()) + } +} + +#[turbo_tasks::function] +async fn inner_compute(input: Vc) -> Result> { + println!("start inner_compute"); + let value = *input.await?.state.get(); + tokio::time::sleep(Duration::from_millis(200)).await; + if value > 10 { + let collectible: Vc> = Vc::upcast(Collectible { value }.cell()); + emit(collectible); + + println!("end inner_compute with collectible"); + Ok(Vc::cell(42)) + } else { + println!("end inner_compute without collectible"); + Ok(Vc::cell(value)) + } +} + +#[turbo_tasks::function] +async fn compute(input: Vc) -> Result> { + println!("start compute"); + let operation = inner_compute(input); + let value = *operation.await?; + let collectibles = operation.peek_collectibles::>(); + if collectibles.len() > 1 { + bail!("expected 0..1 collectible, found {}", collectibles.len()); + } + let first = collectibles.iter().next(); + let collectible = if let Some(first) = first { + first.to_string().await?.to_string() + } else { + "".to_string() + }; + println!("end compute"); + Ok(Output { value, collectible }.cell()) +} diff --git a/turbopack/crates/turbo-tasks-testing/tests/emptied_cells.rs b/turbopack/crates/turbo-tasks-testing/tests/emptied_cells.rs new file mode 100644 index 0000000000000..326636c709b54 --- /dev/null +++ b/turbopack/crates/turbo-tasks-testing/tests/emptied_cells.rs @@ -0,0 +1,67 @@ +#![feature(arbitrary_self_types)] + +use anyhow::Result; +use turbo_tasks::{State, Vc}; +use turbo_tasks_testing::{register, run, Registration}; + +static REGISTRATION: Registration = register!(); + +#[tokio::test] +async fn recompute() { + run(®ISTRATION, async { + let input = ChangingInput { + state: State::new(1), + } + .cell(); + let output = compute(input); + assert_eq!(*output.await.unwrap(), 1); + + println!("changing input"); + input.await.unwrap().state.set(10); + assert_eq!(*output.strongly_consistent().await.unwrap(), 10); + + println!("changing input"); + input.await.unwrap().state.set(5); + assert_eq!(*output.strongly_consistent().await.unwrap(), 5); + + println!("changing input"); + input.await.unwrap().state.set(20); + assert_eq!(*output.strongly_consistent().await.unwrap(), 20); + + println!("changing input"); + input.await.unwrap().state.set(15); + assert_eq!(*output.strongly_consistent().await.unwrap(), 15); + + println!("changing input"); + input.await.unwrap().state.set(1); + assert_eq!(*output.strongly_consistent().await.unwrap(), 1); + }) + .await +} + +#[turbo_tasks::value] +struct ChangingInput { + state: State, +} + +#[turbo_tasks::function] +async fn compute(input: Vc) -> Result> { + let value = *inner_compute(input).await?; + Ok(Vc::cell(value)) +} + +#[turbo_tasks::function] +async fn inner_compute(input: Vc) -> Result> { + let state_value = *input.await?.state.get(); + let mut last = None; + for i in 0..=state_value { + last = Some(compute2(Vc::cell(i))); + } + Ok(last.unwrap()) +} + +#[turbo_tasks::function] +async fn compute2(input: Vc) -> Result> { + let value = *input.await?; + Ok(Vc::cell(value)) +} diff --git a/turbopack/crates/turbo-tasks-testing/tests/generics.rs b/turbopack/crates/turbo-tasks-testing/tests/generics.rs new file mode 100644 index 0000000000000..b3e8990ba4031 --- /dev/null +++ b/turbopack/crates/turbo-tasks-testing/tests/generics.rs @@ -0,0 +1,202 @@ +#![feature(arbitrary_self_types)] + +use std::sync::{Arc, Mutex}; + +use indexmap::{IndexMap, IndexSet}; +use turbo_tasks::{debug::ValueDebug, Invalidator, ReadRef, TaskId, Vc}; +use turbo_tasks_testing::{register, run, Registration}; + +static REGISTRATION: Registration = register!(); + +#[tokio::test] +async fn test_option_some() { + run(®ISTRATION, async move { + let vc_42 = Vc::cell(42); + let option: Vc>> = Vc::cell(Some(vc_42)); + assert!(*option.is_some().await.unwrap()); + assert!(!(*option.is_none().await.unwrap())); + assert_eq!(&*option.await.unwrap(), &Some(vc_42)); + assert_eq!(option.dbg().await.unwrap().to_string(), "Some(\n 42,\n)"); + }) + .await +} + +#[tokio::test] +async fn test_option_none() { + run(®ISTRATION, async move { + let option: Vc>> = Default::default(); + assert!(!(*option.is_some().await.unwrap())); + assert!(*option.is_none().await.unwrap()); + assert_eq!(&*option.await.unwrap(), &None); + assert_eq!(option.dbg().await.unwrap().to_string(), "None"); + }) + .await +} + +#[tokio::test] +async fn test_vec() { + run(®ISTRATION, async move { + let vc_42 = Vc::cell(42); + let vec: Vc>> = Vc::cell(vec![vc_42]); + assert_eq!(*vec.len().await.unwrap(), 1); + assert!(!(*vec.is_empty().await.unwrap())); + assert_eq!(&*vec.await.unwrap(), &[vc_42]); + assert_eq!(vec.dbg().await.unwrap().to_string(), "[\n 42,\n]"); + }) + .await +} + +#[tokio::test] +async fn test_empty_vec() { + run(®ISTRATION, async move { + let vec: Vc>> = Default::default(); + assert_eq!(*vec.len().await.unwrap(), 0); + assert!(*vec.is_empty().await.unwrap()); + assert_eq!(vec.dbg().await.unwrap().to_string(), "[]"); + }) + .await +} + +#[tokio::test] +async fn test_nested_empty_vec() { + run(®ISTRATION, async move { + let vec: Vc>>>> = Default::default(); + assert_eq!(*vec.len().await.unwrap(), 0); + assert_eq!(vec.dbg().await.unwrap().to_string(), "[]"); + }) + .await +} + +#[tokio::test] +async fn test_index_set() { + run(®ISTRATION, async move { + let vc_42 = Vc::cell(42); + let set: Vc>> = Vc::cell(IndexSet::from([vc_42])); + assert_eq!(*set.len().await.unwrap(), 1); + assert!(!(*set.is_empty().await.unwrap())); + assert_eq!(&*set.await.unwrap(), &IndexSet::from([vc_42])); + assert_eq!(set.dbg().await.unwrap().to_string(), "{\n 42,\n}"); + }) + .await +} + +#[tokio::test] +async fn test_empty_index_set() { + run(®ISTRATION, async move { + let set: Vc>> = Default::default(); + assert_eq!(*set.len().await.unwrap(), 0); + assert!(*set.is_empty().await.unwrap()); + assert_eq!(&*set.await.unwrap(), &IndexSet::>::default()); + assert_eq!(set.dbg().await.unwrap().to_string(), "{}"); + }) + .await +} + +#[tokio::test] +async fn test_index_map() { + run(®ISTRATION, async move { + let vc_42 = Vc::cell(42); + let map: Vc, _>> = Vc::cell(IndexMap::from([(vc_42, vc_42)])); + assert_eq!(*map.len().await.unwrap(), 1); + assert!(!(*map.is_empty().await.unwrap())); + assert_eq!(&*map.await.unwrap(), &IndexMap::from([(vc_42, vc_42)])); + assert_eq!(map.dbg().await.unwrap().to_string(), "{\n 42: 42,\n}"); + }) + .await +} + +#[tokio::test] +async fn test_empty_index_map() { + run(®ISTRATION, async move { + let map: Vc, Vc>> = Default::default(); + assert_eq!(*map.len().await.unwrap(), 0); + assert!(*map.is_empty().await.unwrap()); + assert_eq!( + &*map.await.unwrap(), + &IndexMap::, Vc>::default() + ); + assert_eq!(map.dbg().await.unwrap().to_string(), "{}"); + }) + .await +} + +// Simulate a non-deterministic function that stores different generic types in +// it's cells each time it runs. +#[tokio::test] +async fn test_changing_generic() { + run(®ISTRATION, async move { + let state_vc = State::default().cell(); + let state_ref = state_vc.await.unwrap(); + for _i in 0..10 { + let _ = non_deterministic(state_vc) + .resolve_strongly_consistent() + .await + .unwrap(); + state_ref + .inner + .lock() + .unwrap() + .last_invalidator + .take() + .unwrap() + .invalidate(); + } + }) + .await +} + +// Test that we can convert a `Vc` to a `ReadRef`, and then back to a `Vc`. +#[tokio::test] +async fn test_read_ref_round_trip() { + run(®ISTRATION, async move { + let c: Vc>> = Vc::cell(Some(Vc::cell(1))); + let _ = ReadRef::cell(c.await.unwrap()).await.unwrap(); + }) + .await +} + +#[turbo_tasks::value(eq = "manual")] +#[derive(Default)] +struct State { + #[turbo_tasks(debug_ignore, trace_ignore)] + #[serde(skip)] + inner: Arc>, +} + +#[derive(Default)] +struct StateInner { + branch: bool, + last_invalidator: Option, + last_task_id: Option, +} + +impl PartialEq for State { + fn eq(&self, other: &Self) -> bool { + std::ptr::eq(self as *const _, other as *const _) + } +} + +impl Eq for State {} + +#[turbo_tasks::function] +async fn non_deterministic(state: Vc) { + let state = state.await.unwrap(); + let mut state_inner = state.inner.lock().unwrap(); + + let task_id = if state_inner.branch { + let c: Vc>> = Vc::cell(Some(Vc::cell(1))); + println!("u8 branch"); + Vc::into_raw(c).get_task_id() + } else { + let c: Vc>> = Vc::cell(Some(Vc::cell(1))); + println!("u32 branch"); + Vc::into_raw(c).get_task_id() + }; + + state_inner.branch = !state_inner.branch; + if let Some(last_task_id) = state_inner.last_task_id { + assert_eq!(last_task_id, task_id); + } + state_inner.last_task_id = Some(task_id); + state_inner.last_invalidator = Some(turbo_tasks::get_invalidator()); +} diff --git a/turbopack/crates/turbo-tasks-memory/tests/local_cell.rs b/turbopack/crates/turbo-tasks-testing/tests/local_cell.rs similarity index 100% rename from turbopack/crates/turbo-tasks-memory/tests/local_cell.rs rename to turbopack/crates/turbo-tasks-testing/tests/local_cell.rs diff --git a/turbopack/crates/turbo-tasks-testing/tests/read_ref_cell.rs b/turbopack/crates/turbo-tasks-testing/tests/read_ref_cell.rs new file mode 100644 index 0000000000000..6266d3c56fe5d --- /dev/null +++ b/turbopack/crates/turbo-tasks-testing/tests/read_ref_cell.rs @@ -0,0 +1,85 @@ +#![feature(arbitrary_self_types)] + +use std::sync::Mutex; + +use anyhow::Result; +use turbo_tasks::{get_invalidator, Invalidator, ReadRef, Vc}; +use turbo_tasks_testing::{register, run, Registration}; + +static REGISTRATION: Registration = register!(); + +#[tokio::test] +async fn read_ref() { + run(®ISTRATION, async { + let counter = Counter::cell(Counter { + value: Mutex::new((0, None)), + }); + + let counter_value = counter.get_value(); + + assert_eq!(*counter.get_value().strongly_consistent().await?, 0); + assert_eq!(*counter_value.strongly_consistent().await?, 0); + + counter.await?.incr(); + + assert_eq!(*counter.get_value().strongly_consistent().await?, 1); + assert_eq!(*counter_value.strongly_consistent().await?, 1); + + // `ref_counter` will still point to the same `counter` instance as `counter`. + let ref_counter = ReadRef::cell(counter.await?); + let ref_counter_value = ref_counter.get_value(); + + // However, `local_counter_value` will point to the value of `counter_value` + // at the time it was turned into a trait reference (just like a `ReadRef` + // would). + let local_counter_value = ReadRef::cell(counter_value.await?).get_value(); + + counter.await?.incr(); + + assert_eq!(*counter.get_value().strongly_consistent().await?, 2); + assert_eq!(*counter_value.strongly_consistent().await?, 2); + assert_eq!(*ref_counter_value.strongly_consistent().await?, 2); + assert_eq!(*local_counter_value.strongly_consistent().await?, 1); + + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[turbo_tasks::value(transparent)] +struct CounterValue(usize); + +#[turbo_tasks::value(serialization = "none", cell = "new", eq = "manual")] +struct Counter { + #[turbo_tasks(debug_ignore, trace_ignore)] + value: Mutex<(usize, Option)>, +} + +impl Counter { + fn incr(&self) { + let mut lock = self.value.lock().unwrap(); + lock.0 += 1; + if let Some(i) = lock.1.take() { + i.invalidate(); + } + } +} + +#[turbo_tasks::value_impl] +impl Counter { + #[turbo_tasks::function] + async fn get_value(&self) -> Result> { + let mut lock = self.value.lock().unwrap(); + lock.1 = Some(get_invalidator()); + Ok(Vc::cell(lock.0)) + } +} + +#[turbo_tasks::value_impl] +impl CounterValue { + #[turbo_tasks::function] + fn get_value(self: Vc) -> Vc { + self + } +} diff --git a/turbopack/crates/turbo-tasks-testing/tests/recompute.rs b/turbopack/crates/turbo-tasks-testing/tests/recompute.rs new file mode 100644 index 0000000000000..976efd03e66b7 --- /dev/null +++ b/turbopack/crates/turbo-tasks-testing/tests/recompute.rs @@ -0,0 +1,93 @@ +#![feature(arbitrary_self_types)] + +use anyhow::Result; +use turbo_tasks::{State, Vc}; +use turbo_tasks_testing::{register, run, Registration}; + +static REGISTRATION: Registration = register!(); + +#[tokio::test] +async fn recompute() { + run(®ISTRATION, async { + let input = ChangingInput { + state: State::new(1), + } + .cell(); + let input2 = ChangingInput { + state: State::new(10), + } + .cell(); + let output = compute(input, input2); + let read = output.await?; + assert_eq!(read.state_value, 1); + assert_eq!(read.state_value2, 10); + let random_value = read.random_value; + + println!("changing input"); + input.await?.state.set(2); + let read = output.strongly_consistent().await?; + assert_eq!(read.state_value, 2); + assert_ne!(read.random_value, random_value); + let random_value = read.random_value; + + println!("changing input2"); + input2.await?.state.set(20); + let read = output.strongly_consistent().await?; + assert_eq!(read.state_value2, 20); + assert_ne!(read.random_value, random_value); + let random_value = read.random_value; + + println!("changing input"); + input.await?.state.set(5); + let read = output.strongly_consistent().await?; + assert_eq!(read.state_value, 5); + assert_eq!(read.state_value2, 42); + assert_ne!(read.random_value, random_value); + let random_value = read.random_value; + + println!("changing input2"); + input2.await?.state.set(30); + let read = output.strongly_consistent().await?; + assert_eq!(read.random_value, random_value); + + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[turbo_tasks::value] +struct ChangingInput { + state: State, +} + +#[turbo_tasks::value] +struct Output { + state_value: u32, + state_value2: u32, + random_value: u32, +} + +#[turbo_tasks::function] +async fn compute(input: Vc, input2: Vc) -> Result> { + let state_value = *input.await?.state.get(); + let state_value2 = if state_value < 5 { + *compute2(input2).await? + } else { + 42 + }; + let random_value = rand::random(); + + Ok(Output { + state_value, + state_value2, + random_value, + } + .cell()) +} + +#[turbo_tasks::function] +async fn compute2(input: Vc) -> Result> { + let state_value = *input.await?.state.get(); + Ok(Vc::cell(state_value)) +} diff --git a/turbopack/crates/turbo-tasks-testing/tests/recompute_collectibles.rs b/turbopack/crates/turbo-tasks-testing/tests/recompute_collectibles.rs new file mode 100644 index 0000000000000..07391c207a703 --- /dev/null +++ b/turbopack/crates/turbo-tasks-testing/tests/recompute_collectibles.rs @@ -0,0 +1,94 @@ +#![feature(arbitrary_self_types)] + +use anyhow::{bail, Result}; +use turbo_tasks::{emit, CollectiblesSource, RcStr, State, ValueToString, Vc}; +use turbo_tasks_testing::{register, run, Registration}; + +static REGISTRATION: Registration = register!(); + +#[tokio::test] +async fn recompute() { + run(®ISTRATION, async { + let input = ChangingInput { + state: State::new(1), + } + .cell(); + let output = compute(input, 100); + let read = output.await.unwrap(); + assert_eq!(read.value, 42); + assert_eq!(read.collectible, "1"); + + for i in 2..100 { + input.await.unwrap().state.set(i); + let read = output.strongly_consistent().await.unwrap(); + assert_eq!(read.value, 42); + assert_eq!(read.collectible, i.to_string()); + } + }) + .await +} + +#[turbo_tasks::value] +struct ChangingInput { + state: State, +} + +#[turbo_tasks::value] +struct Output { + value: u32, + collectible: String, +} + +#[turbo_tasks::value] +struct Collectible { + value: u32, +} + +#[turbo_tasks::value_impl] +impl ValueToString for Collectible { + #[turbo_tasks::function] + fn to_string(&self) -> Vc { + Vc::cell(self.value.to_string().into()) + } +} + +#[turbo_tasks::function] +fn inner_compute(input: Vc) -> Vc { + inner_compute2(input, 1000) +} + +#[turbo_tasks::function] +async fn inner_compute2(input: Vc, innerness: u32) -> Result> { + if innerness > 0 { + return Ok(inner_compute2(input, innerness - 1)); + } + let collectible: Vc> = Vc::upcast( + Collectible { + value: *input.await?.state.get(), + } + .cell(), + ); + emit(collectible); + + Ok(Vc::cell(42)) +} + +#[turbo_tasks::function] +async fn compute(input: Vc, innerness: u32) -> Result> { + if innerness > 0 { + return Ok(compute(input, innerness - 1)); + } + let operation = inner_compute(input); + let value = *operation.await?; + let collectibles = operation.peek_collectibles::>(); + if collectibles.len() != 1 { + bail!("expected 1 collectible, found {}", collectibles.len()); + } + let first = *collectibles.iter().next().unwrap(); + let collectible = first.to_string().await?; + Ok(Output { + value, + collectible: collectible.to_string(), + } + .cell()) +} diff --git a/turbopack/crates/turbo-tasks-testing/tests/scope_stress.rs b/turbopack/crates/turbo-tasks-testing/tests/scope_stress.rs new file mode 100644 index 0000000000000..b5a62fbe6f28c --- /dev/null +++ b/turbopack/crates/turbo-tasks-testing/tests/scope_stress.rs @@ -0,0 +1,51 @@ +#![feature(arbitrary_self_types)] + +use anyhow::Result; +use turbo_tasks::{run_once, Completion, TryJoinIterExt, Vc}; +use turbo_tasks_testing::{register, Registration}; + +static REGISTRATION: Registration = register!(); + +#[test] +fn rectangle_stress() { + REGISTRATION.ensure_registered(); + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + rt.block_on(async { + let tt = REGISTRATION.create_turbo_tasks(); + let size = std::env::var("TURBOPACK_TEST_RECTANGLE_STRESS_SIZE") + .map(|size| size.parse().unwrap()) + .unwrap_or(50); + (0..size) + .map(|a| (a, size - 1)) + .chain((0..size - 1).map(|b| (size - 1, b))) + .map(|(a, b)| { + let tt = tt.clone(); + async move { + run_once(tt, async move { + rectangle(a, b).strongly_consistent().await?; + Ok(Vc::<()>::default()) + }) + .await + } + }) + .try_join() + .await + .unwrap(); + }) +} + +/// This fills a rectagle from (0, 0) to (a, b) by +/// first filling (0, 0) to (a - 1, b) and then (0, 0) to (a, b - 1) recursively +#[turbo_tasks::function] +async fn rectangle(a: u32, b: u32) -> Result> { + if a > 0 { + rectangle(a - 1, b).await?; + } + if b > 0 { + rectangle(a, b - 1).await?; + } + Ok(Completion::new()) +} diff --git a/turbopack/crates/turbo-tasks-testing/tests/trait_ref_cell.rs b/turbopack/crates/turbo-tasks-testing/tests/trait_ref_cell.rs new file mode 100644 index 0000000000000..cfde0d3f4d828 --- /dev/null +++ b/turbopack/crates/turbo-tasks-testing/tests/trait_ref_cell.rs @@ -0,0 +1,104 @@ +#![feature(arbitrary_self_types)] + +use std::sync::Mutex; + +use anyhow::Result; +use turbo_tasks::{get_invalidator, IntoTraitRef, Invalidator, TraitRef, Vc}; +use turbo_tasks_testing::{register, run, Registration}; + +static REGISTRATION: Registration = register!(); + +#[tokio::test] +async fn trait_ref() { + run(®ISTRATION, async { + let counter = Counter::cell(Counter { + value: Mutex::new((0, None)), + }); + + let counter_value = counter.get_value(); + + assert_eq!(*counter.get_value().strongly_consistent().await?, 0); + assert_eq!(*counter_value.strongly_consistent().await?, 0); + + counter.await?.incr(); + + assert_eq!(*counter.get_value().strongly_consistent().await?, 1); + assert_eq!(*counter_value.strongly_consistent().await?, 1); + + // `ref_counter` will still point to the same `counter` instance as `counter`. + let ref_counter = TraitRef::cell( + Vc::upcast::>(counter) + .into_trait_ref() + .await?, + ); + let ref_counter_value = ref_counter.get_value(); + + // However, `local_counter_value` will point to the value of `counter_value` + // at the time it was turned into a trait reference (just like a `ReadRef` + // would). + let local_counter_value = TraitRef::cell( + Vc::upcast::>(counter_value) + .into_trait_ref() + .await?, + ) + .get_value(); + + counter.await?.incr(); + + assert_eq!(*counter.get_value().strongly_consistent().await?, 2); + assert_eq!(*counter_value.strongly_consistent().await?, 2); + assert_eq!(*ref_counter_value.strongly_consistent().await?, 2); + assert_eq!(*local_counter_value.strongly_consistent().await?, 1); + + anyhow::Ok(()) + }) + .await + .unwrap() +} + +#[turbo_tasks::value(transparent)] +struct CounterValue(usize); + +#[turbo_tasks::value(serialization = "none", cell = "new", eq = "manual")] +struct Counter { + #[turbo_tasks(debug_ignore, trace_ignore)] + value: Mutex<(usize, Option)>, +} + +impl Counter { + fn incr(&self) { + let mut lock = self.value.lock().unwrap(); + lock.0 += 1; + if let Some(i) = lock.1.take() { + i.invalidate(); + } + } +} + +#[turbo_tasks::value_trait] +trait CounterTrait { + fn get_value(&self) -> Vc; +} + +#[turbo_tasks::value_impl] +impl CounterTrait for Counter { + #[turbo_tasks::function] + async fn get_value(&self) -> Result> { + let mut lock = self.value.lock().unwrap(); + lock.1 = Some(get_invalidator()); + Ok(Vc::cell(lock.0)) + } +} + +#[turbo_tasks::value_trait] +trait CounterValueTrait { + fn get_value(&self) -> Vc; +} + +#[turbo_tasks::value_impl] +impl CounterValueTrait for CounterValue { + #[turbo_tasks::function] + fn get_value(self: Vc) -> Vc { + self + } +} diff --git a/turbopack/crates/turbo-tasks-memory/tests/trait_ref_cell_mode.rs b/turbopack/crates/turbo-tasks-testing/tests/trait_ref_cell_mode.rs similarity index 100% rename from turbopack/crates/turbo-tasks-memory/tests/trait_ref_cell_mode.rs rename to turbopack/crates/turbo-tasks-testing/tests/trait_ref_cell_mode.rs