Skip to content

Commit

Permalink
Add/move tests for Vc generics (#8843)
Browse files Browse the repository at this point in the history
## Description

- Move the tests out of `all_in_one` so that we can see exactly which
parts are failing more easily.
- Add a few more tests. **The newly added `test_read_ref_round_trip`
fails** (and is ignored), indicating a bug in the generics
implementation!!! This is fixed in #8845.

## Why does `test_read_ref_round_trip` fail?

The theory is that Vcs are supposed to be stored as `<<T as
VcValueType>::Read as VcRead<T>>::Repr`. However, it looks like due to
an oversight, `ReadRef::cell` is trying to store the value as `T`.

## Why add these tests at all?

The plan (as of yesterday) is to remove support for generics, but I'd
feel more confident if I can fix the casting issues in my local Vc PRs
before I do that.

These tests should help me understand what's happening and debug things.

## Testing Instructions

```
cargo nextest r -p turbo-tasks -p turbo-tasks-memory
```
  • Loading branch information
bgw authored Jul 29, 2024
1 parent d24b396 commit 12f7f73
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 57 deletions.
58 changes: 1 addition & 57 deletions crates/turbo-tasks-memory/tests/all_in_one.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#![feature(arbitrary_self_types)]

use anyhow::{anyhow, bail, Result};
use indexmap::{IndexMap, IndexSet};
use turbo_tasks::{debug::ValueDebug, RcStr, Value, ValueToString, Vc};
use turbo_tasks::{RcStr, Value, ValueToString, Vc};
use turbo_tasks_testing::{register, run, Registration};

static REGISTRATION: Registration = register!();
Expand Down Expand Up @@ -55,61 +54,6 @@ async fn all_in_one() {
let c_erased_invalid: Vc<Box<dyn Add>> = a_erased.add(b_erased_other);
assert!(c_erased_invalid.resolve().await.is_err());

// Testing generic types.

let vc_42 = Vc::cell(42);

let option: Vc<Option<Vc<u32>>> = Vc::cell(Some(vc_42));
assert_eq!(*option.is_some().await?, true);
assert_eq!(*option.is_none().await?, false);
assert_eq!(&*option.await?, &Some(vc_42));
assert_eq!(option.dbg().await?.to_string(), "Some(\n 42,\n)");

let option: Vc<Option<Vc<u32>>> = Default::default();
assert_eq!(*option.is_some().await?, false);
assert_eq!(*option.is_none().await?, true);
assert_eq!(&*option.await?, &None);
assert_eq!(option.dbg().await?.to_string(), "None");

let vec: Vc<Vec<Vc<u32>>> = Vc::cell(vec![vc_42]);
assert_eq!(*vec.len().await?, 1);
assert_eq!(*vec.is_empty().await?, false);
assert_eq!(&*vec.await?, &[vc_42]);
assert_eq!(vec.dbg().await?.to_string(), "[\n 42,\n]");

let vec: Vc<Vec<Vc<u32>>> = Default::default();
assert_eq!(*vec.len().await?, 0);
assert_eq!(*vec.is_empty().await?, true);
assert_eq!(vec.dbg().await?.to_string(), "[]");

let vec: Vc<Vec<Vc<Vec<Vc<u32>>>>> = Default::default();
assert_eq!(*vec.len().await?, 0);
assert_eq!(vec.dbg().await?.to_string(), "[]");

let set: Vc<IndexSet<Vc<u32>>> = Vc::cell(IndexSet::from([vc_42]));
assert_eq!(*set.len().await?, 1);
assert_eq!(*set.is_empty().await?, false);
assert_eq!(&*set.await?, &IndexSet::from([vc_42]));
assert_eq!(set.dbg().await?.to_string(), "{\n 42,\n}");

let set: Vc<IndexSet<Vc<u32>>> = Default::default();
assert_eq!(*set.len().await?, 0);
assert_eq!(*set.is_empty().await?, true);
assert_eq!(&*set.await?, &IndexSet::<Vc<u32>>::default());
assert_eq!(set.dbg().await?.to_string(), "{}");

let map: Vc<IndexMap<_, _>> = Vc::cell(IndexMap::from([(vc_42, vc_42)]));
assert_eq!(*map.len().await?, 1);
assert_eq!(*map.is_empty().await?, false);
assert_eq!(&*map.await?, &IndexMap::from([(vc_42, vc_42)]));
assert_eq!(map.dbg().await?.to_string(), "{\n 42: 42,\n}");

let map: Vc<IndexMap<Vc<u32>, Vc<u32>>> = Default::default();
assert_eq!(*map.len().await?, 0);
assert_eq!(*map.is_empty().await?, true);
assert_eq!(&*map.await?, &IndexMap::<Vc<u32>, Vc<u32>>::default());
assert_eq!(map.dbg().await?.to_string(), "{}");

anyhow::Ok(())
})
.await
Expand Down
203 changes: 203 additions & 0 deletions crates/turbo-tasks-memory/tests/generics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#![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(&REGISTRATION, async move {
let vc_42 = Vc::cell(42);
let option: Vc<Option<Vc<u32>>> = Vc::cell(Some(vc_42));
assert_eq!(*option.is_some().await.unwrap(), true);
assert_eq!(*option.is_none().await.unwrap(), false);
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(&REGISTRATION, async move {
let option: Vc<Option<Vc<u32>>> = Default::default();
assert_eq!(*option.is_some().await.unwrap(), false);
assert_eq!(*option.is_none().await.unwrap(), true);
assert_eq!(&*option.await.unwrap(), &None);
assert_eq!(option.dbg().await.unwrap().to_string(), "None");
})
.await
}

#[tokio::test]
async fn test_vec() {
run(&REGISTRATION, async move {
let vc_42 = Vc::cell(42);
let vec: Vc<Vec<Vc<u32>>> = Vc::cell(vec![vc_42]);
assert_eq!(*vec.len().await.unwrap(), 1);
assert_eq!(*vec.is_empty().await.unwrap(), false);
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(&REGISTRATION, async move {
let vec: Vc<Vec<Vc<u32>>> = Default::default();
assert_eq!(*vec.len().await.unwrap(), 0);
assert_eq!(*vec.is_empty().await.unwrap(), true);
assert_eq!(vec.dbg().await.unwrap().to_string(), "[]");
})
.await
}

#[tokio::test]
async fn test_nested_empty_vec() {
run(&REGISTRATION, async move {
let vec: Vc<Vec<Vc<Vec<Vc<u32>>>>> = 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(&REGISTRATION, async move {
let vc_42 = Vc::cell(42);
let set: Vc<IndexSet<Vc<u32>>> = Vc::cell(IndexSet::from([vc_42]));
assert_eq!(*set.len().await.unwrap(), 1);
assert_eq!(*set.is_empty().await.unwrap(), false);
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(&REGISTRATION, async move {
let set: Vc<IndexSet<Vc<u32>>> = Default::default();
assert_eq!(*set.len().await.unwrap(), 0);
assert_eq!(*set.is_empty().await.unwrap(), true);
assert_eq!(&*set.await.unwrap(), &IndexSet::<Vc<u32>>::default());
assert_eq!(set.dbg().await.unwrap().to_string(), "{}");
})
.await
}

#[tokio::test]
async fn test_index_map() {
run(&REGISTRATION, async move {
let vc_42 = Vc::cell(42);
let map: Vc<IndexMap<Vc<u32>, _>> = Vc::cell(IndexMap::from([(vc_42, vc_42)]));
assert_eq!(*map.len().await.unwrap(), 1);
assert_eq!(*map.is_empty().await.unwrap(), false);
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(&REGISTRATION, async move {
let map: Vc<IndexMap<Vc<u32>, Vc<u32>>> = Default::default();
assert_eq!(*map.len().await.unwrap(), 0);
assert_eq!(*map.is_empty().await.unwrap(), true);
assert_eq!(
&*map.await.unwrap(),
&IndexMap::<Vc<u32>, Vc<u32>>::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(&REGISTRATION, 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]
#[ignore = "TODO: This panics! There's a casting bug in the generics implementation."]
async fn test_read_ref_round_trip() {
run(&REGISTRATION, async move {
let c: Vc<Option<Vc<u8>>> = 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<Mutex<StateInner>>,
}

#[derive(Default)]
struct StateInner {
branch: bool,
last_invalidator: Option<Invalidator>,
last_task_id: Option<TaskId>,
}

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<State>) {
let state = state.await.unwrap();
let mut state_inner = state.inner.lock().unwrap();

let task_id = if state_inner.branch {
let c: Vc<Option<Vc<u8>>> = Vc::cell(Some(Vc::cell(1)));
println!("u8 branch");
Vc::into_raw(c).get_task_id()
} else {
let c: Vc<Option<Vc<u32>>> = 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());
}

0 comments on commit 12f7f73

Please sign in to comment.