1use std::{
2 borrow::Cow,
3 error::Error,
4 fmt::{self, Debug, Display},
5 future::Future,
6 hash::{BuildHasher, BuildHasherDefault, Hash},
7 pin::Pin,
8 sync::Arc,
9};
10
11use anyhow::{Result, anyhow};
12use auto_hash_map::AutoMap;
13use bincode::{
14 Decode, Encode,
15 de::Decoder,
16 enc::Encoder,
17 error::{DecodeError, EncodeError},
18 impl_borrow_decode,
19};
20use rustc_hash::FxHasher;
21use smallvec::SmallVec;
22use tracing::Span;
23use turbo_bincode::{
24 TurboBincodeDecode, TurboBincodeDecoder, TurboBincodeEncode, TurboBincodeEncoder,
25 impl_decode_for_turbo_bincode_decode, impl_encode_for_turbo_bincode_encode, new_hash_encoder,
26};
27use turbo_rcstr::RcStr;
28use turbo_tasks_hash::DeterministicHasher;
29
30use crate::{
31 RawVc, ReadCellOptions, ReadOutputOptions, ReadRef, SharedReference, TaskId, TaskIdSet,
32 TaskPriority, TraitRef, TraitTypeId, TurboTasksCallApi, TurboTasksPanic, ValueTypeId,
33 VcValueTrait, VcValueType,
34 dyn_task_inputs::{DynTaskInputs, StackDynTaskInputs},
35 event::EventListener,
36 macro_helpers::NativeFunction,
37 manager::{TaskPersistence, TurboTasksBackendApi},
38 raw_vc::CellId,
39 registry,
40 task::shared_reference::TypedSharedReference,
41 task_statistics::TaskStatisticsApi,
42 turbo_tasks,
43};
44
45pub type TransientTaskRoot =
46 Box<dyn Fn() -> Pin<Box<dyn Future<Output = Result<RawVc>> + Send>> + Send + Sync>;
47
48pub enum TransientTaskType {
49 Root(TransientTaskRoot),
55
56 Once(Pin<Box<dyn Future<Output = Result<RawVc>> + Send + 'static>>),
66}
67
68impl Debug for TransientTaskType {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 match self {
71 Self::Root(_) => f.debug_tuple("Root").finish(),
72 Self::Once(_) => f.debug_tuple("Once").finish(),
73 }
74 }
75}
76
77#[derive(Debug, Eq)]
80pub struct CachedTaskType {
81 pub native_fn: &'static NativeFunction,
82 pub this: Option<RawVc>,
83 pub arg: Box<dyn DynTaskInputs>,
84}
85
86impl CachedTaskType {
87 pub fn get_name(&self) -> &'static str {
90 self.native_fn.ty.name
91 }
92
93 pub fn hash_encode<H: DeterministicHasher>(&self, hasher: &mut H) {
98 Self::hash_encode_components(self.native_fn, self.this, &*self.arg, hasher);
99 }
100}
101
102impl TurboBincodeEncode for CachedTaskType {
103 fn encode(&self, encoder: &mut TurboBincodeEncoder) -> Result<(), EncodeError> {
104 Encode::encode(®istry::get_function_id(self.native_fn), encoder)?;
105
106 let (encode_arg_any, _) = self.native_fn.arg_meta.bincode;
107 Encode::encode(&self.this, encoder)?;
108 encode_arg_any(&*self.arg, encoder)?;
109
110 Ok(())
111 }
112}
113
114impl<Context> TurboBincodeDecode<Context> for CachedTaskType {
115 fn decode(decoder: &mut TurboBincodeDecoder) -> Result<Self, DecodeError> {
116 let native_fn = registry::get_native_function(Decode::decode(decoder)?);
117
118 let (_, decode_arg_any) = native_fn.arg_meta.bincode;
119 let this = Decode::decode(decoder)?;
120 let arg = decode_arg_any(decoder)?;
121
122 Ok(Self {
123 native_fn,
124 this,
125 arg,
126 })
127 }
128}
129
130impl_encode_for_turbo_bincode_encode!(CachedTaskType);
131impl_decode_for_turbo_bincode_decode!(CachedTaskType);
132impl_borrow_decode!(CachedTaskType);
133
134impl PartialEq for CachedTaskType {
137 #[expect(clippy::op_ref)]
138 fn eq(&self, other: &Self) -> bool {
139 self.native_fn == other.native_fn && self.this == other.this && &self.arg == &other.arg
140 }
141}
142
143impl Hash for CachedTaskType {
146 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
147 self.native_fn.hash(state);
148 self.this.hash(state);
149 self.arg.hash(state);
150 }
151}
152
153impl Display for CachedTaskType {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 f.write_str(self.get_name())
156 }
157}
158
159impl CachedTaskType {
160 pub fn hash_from_components(
163 hasher: &impl BuildHasher,
164 native_fn: &'static NativeFunction,
165 this: Option<RawVc>,
166 arg: &dyn DynTaskInputs,
167 ) -> u64 {
168 use std::hash::Hasher;
169 let mut state = hasher.build_hasher();
170 native_fn.hash(&mut state);
171 this.hash(&mut state);
172 arg.hash(&mut state);
173 state.finish()
174 }
175
176 pub fn hash_encode_components<H: DeterministicHasher>(
181 native_fn: &'static NativeFunction,
182 this: Option<RawVc>,
183 arg: &dyn DynTaskInputs,
184 hasher: &mut H,
185 ) {
186 let fn_id = registry::get_function_id(native_fn);
187 {
188 let mut encoder = new_hash_encoder(hasher);
189 Encode::encode(&fn_id, &mut encoder).expect("fn_id encoding should not fail");
190 Encode::encode(&this, &mut encoder).expect("this encoding should not fail");
191 }
192 (native_fn.arg_meta.hash_encode)(arg, hasher);
193 }
194
195 pub fn eq_components(
197 &self,
198 native_fn: &'static NativeFunction,
199 this: Option<RawVc>,
200 arg: &dyn DynTaskInputs,
201 ) -> bool {
202 std::ptr::eq(self.native_fn, native_fn) && self.this == this && &*self.arg == arg
203 }
204}
205
206pub struct TaskExecutionSpec<'a> {
207 pub future: Pin<Box<dyn Future<Output = Result<RawVc>> + Send + 'a>>,
208 pub span: Span,
209}
210
211#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
212pub struct CellContent(pub Option<SharedReference>);
213#[derive(Clone, Debug, PartialEq, Eq, Hash)]
214pub struct TypedCellContent(pub ValueTypeId, pub CellContent);
215
216impl Display for CellContent {
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 match &self.0 {
219 None => write!(f, "empty"),
220 Some(content) => Display::fmt(content, f),
221 }
222 }
223}
224
225impl TypedCellContent {
226 pub fn cast<T: VcValueType>(self) -> Result<ReadRef<T>> {
227 let data = self.1.0.ok_or_else(|| anyhow!("Cell is empty"))?;
228 let data = data
229 .downcast::<T>()
230 .map_err(|_err| anyhow!("Unexpected type in cell"))?;
231 Ok(ReadRef::new_arc(data))
232 }
233
234 pub fn cast_trait<T>(self) -> Result<TraitRef<T>>
239 where
240 T: VcValueTrait + ?Sized,
241 {
242 let shared_reference = self
243 .1
244 .0
245 .ok_or_else(|| anyhow!("Cell is empty"))?
246 .into_typed(self.0);
247 Ok(
248 TraitRef::new(shared_reference),
250 )
251 }
252
253 pub fn into_untyped(self) -> CellContent {
254 self.1
255 }
256
257 pub fn encode(&self, enc: &mut TurboBincodeEncoder) -> Result<(), EncodeError> {
258 let Self(type_id, content) = self;
259 let value_type = registry::get_value_type(*type_id);
260 type_id.encode(enc)?;
261 if let Some(bincode) = value_type.bincode {
262 if let Some(reference) = &content.0 {
263 true.encode(enc)?;
264 bincode.0(&*reference.0, enc)?;
265 Ok(())
266 } else {
267 false.encode(enc)?;
268 Ok(())
269 }
270 } else {
271 Ok(())
272 }
273 }
274
275 pub fn decode(dec: &mut TurboBincodeDecoder) -> Result<Self, DecodeError> {
276 let type_id = ValueTypeId::decode(dec)?;
277 let value_type = registry::get_value_type(type_id);
278 if let Some(bincode) = value_type.bincode {
279 let is_some = bool::decode(dec)?;
280 if is_some {
281 let reference = bincode.1(dec)?;
282 return Ok(TypedCellContent(type_id, CellContent(Some(reference))));
283 }
284 }
285 Ok(TypedCellContent(type_id, CellContent(None)))
286 }
287}
288
289impl From<TypedSharedReference> for TypedCellContent {
290 fn from(value: TypedSharedReference) -> Self {
291 TypedCellContent(value.type_id, CellContent(Some(value.reference)))
292 }
293}
294
295impl TryFrom<TypedCellContent> for TypedSharedReference {
296 type Error = TypedCellContent;
297
298 fn try_from(content: TypedCellContent) -> Result<Self, TypedCellContent> {
299 if let TypedCellContent(type_id, CellContent(Some(reference))) = content {
300 Ok(TypedSharedReference { type_id, reference })
301 } else {
302 Err(content)
303 }
304 }
305}
306
307impl CellContent {
308 pub fn into_typed(self, type_id: ValueTypeId) -> TypedCellContent {
309 TypedCellContent(type_id, self)
310 }
311}
312
313impl From<SharedReference> for CellContent {
314 fn from(value: SharedReference) -> Self {
315 CellContent(Some(value))
316 }
317}
318
319impl From<Option<SharedReference>> for CellContent {
320 fn from(value: Option<SharedReference>) -> Self {
321 CellContent(value)
322 }
323}
324
325impl TryFrom<CellContent> for SharedReference {
326 type Error = CellContent;
327
328 fn try_from(content: CellContent) -> Result<Self, CellContent> {
329 if let CellContent(Some(shared_reference)) = content {
330 Ok(shared_reference)
331 } else {
332 Err(content)
333 }
334 }
335}
336
337pub type TaskCollectiblesMap = AutoMap<RawVc, i32, BuildHasherDefault<FxHasher>, 1>;
338
339pub type CellHash = [u8; 16];
345
346#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
349pub enum TurboTasksExecutionErrorMessage {
350 PIISafe(#[bincode(with = "turbo_bincode::owned_cow")] Cow<'static, str>),
351 NonPIISafe(String),
352}
353
354impl Display for TurboTasksExecutionErrorMessage {
355 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356 match self {
357 TurboTasksExecutionErrorMessage::PIISafe(msg) => write!(f, "{msg}"),
358 TurboTasksExecutionErrorMessage::NonPIISafe(msg) => write!(f, "{msg}"),
359 }
360 }
361}
362
363#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
364pub struct TurboTasksError {
365 pub message: TurboTasksExecutionErrorMessage,
366 pub source: Option<TurboTasksExecutionError>,
367}
368
369#[derive(Clone)]
373pub struct TurboTaskContextError {
374 pub turbo_tasks: Arc<dyn TurboTasksCallApi>,
375 pub task_id: TaskId,
376 pub source: Option<TurboTasksExecutionError>,
377}
378
379impl PartialEq for TurboTaskContextError {
380 fn eq(&self, other: &Self) -> bool {
381 self.task_id == other.task_id && self.source == other.source
382 }
383}
384impl Eq for TurboTaskContextError {}
385
386impl Encode for TurboTaskContextError {
387 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
388 Encode::encode(&self.task_id, encoder)?;
389 Encode::encode(&self.source, encoder)?;
390 Ok(())
391 }
392}
393
394impl<Context> Decode<Context> for TurboTaskContextError {
395 fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
396 let task_id = Decode::decode(decoder)?;
397 let source = Decode::decode(decoder)?;
398 let turbo_tasks = turbo_tasks();
399 Ok(Self {
400 turbo_tasks,
401 task_id,
402 source,
403 })
404 }
405}
406
407impl_borrow_decode!(TurboTaskContextError);
408
409impl Debug for TurboTaskContextError {
410 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
411 f.debug_struct("TurboTaskContextError")
412 .field("task_id", &self.task_id)
413 .field("source", &self.source)
414 .finish()
415 }
416}
417
418#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
421pub struct TurboTaskLocalContextError {
422 pub name: RcStr,
423 pub source: Option<TurboTasksExecutionError>,
424}
425
426#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
427pub enum TurboTasksExecutionError {
428 Panic(Arc<TurboTasksPanic>),
429 Error(Arc<TurboTasksError>),
430 TaskContext(Arc<TurboTaskContextError>),
431 LocalTaskContext(Arc<TurboTaskLocalContextError>),
432}
433
434impl TurboTasksExecutionError {
435 pub fn with_task_context(
438 self,
439 task_id: TaskId,
440 turbo_tasks: Arc<dyn TurboTasksCallApi>,
441 ) -> Self {
442 TurboTasksExecutionError::TaskContext(Arc::new(TurboTaskContextError {
443 task_id,
444 turbo_tasks,
445 source: Some(self),
446 }))
447 }
448
449 pub fn with_local_task_context(self, name: String) -> Self {
452 TurboTasksExecutionError::LocalTaskContext(Arc::new(TurboTaskLocalContextError {
453 name: RcStr::from(name),
454 source: Some(self),
455 }))
456 }
457}
458
459impl Error for TurboTasksExecutionError {
460 fn source(&self) -> Option<&(dyn Error + 'static)> {
461 match self {
462 TurboTasksExecutionError::Panic(_panic) => None,
463 TurboTasksExecutionError::Error(error) => {
464 error.source.as_ref().map(|s| s as &dyn Error)
465 }
466 TurboTasksExecutionError::TaskContext(context_error) => {
467 context_error.source.as_ref().map(|s| s as &dyn Error)
468 }
469 TurboTasksExecutionError::LocalTaskContext(context_error) => {
470 context_error.source.as_ref().map(|s| s as &dyn Error)
471 }
472 }
473 }
474}
475
476impl Display for TurboTasksExecutionError {
477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478 match self {
479 TurboTasksExecutionError::Panic(panic) => write!(f, "{}", &panic),
480 TurboTasksExecutionError::Error(error) => {
481 write!(f, "{}", error.message)
482 }
483 TurboTasksExecutionError::TaskContext(context_error) => {
484 let task_id = context_error.task_id;
485 let name = context_error.turbo_tasks.get_task_name(task_id);
486 if cfg!(feature = "task_id_details") {
487 write!(f, "Execution of {name} ({}) failed", task_id)
488 } else {
489 write!(f, "Execution of {name} failed")
490 }
491 }
492 TurboTasksExecutionError::LocalTaskContext(context_error) => {
493 write!(f, "Execution of {} failed", context_error.name)
494 }
495 }
496 }
497}
498
499impl<'l> From<&'l (dyn std::error::Error + 'static)> for TurboTasksExecutionError {
500 fn from(err: &'l (dyn std::error::Error + 'static)) -> Self {
501 if let Some(err) = err.downcast_ref::<TurboTasksExecutionError>() {
502 return err.clone();
503 }
504 let message = err.to_string();
505 let source = err.source().map(|source| source.into());
506
507 TurboTasksExecutionError::Error(Arc::new(TurboTasksError {
508 message: TurboTasksExecutionErrorMessage::NonPIISafe(message),
509 source,
510 }))
511 }
512}
513
514impl From<anyhow::Error> for TurboTasksExecutionError {
515 fn from(err: anyhow::Error) -> Self {
516 let current: &(dyn std::error::Error + 'static) = err.as_ref();
517 current.into()
518 }
519}
520
521pub enum VerificationMode {
522 EqualityCheck,
523 Skip,
524}
525
526pub trait Backend: Sync + Send {
527 #[allow(unused_variables)]
528 fn startup(&self, turbo_tasks: &dyn TurboTasksBackendApi<Self>) {}
529
530 #[allow(unused_variables)]
531 fn stop(&self, turbo_tasks: &dyn TurboTasksBackendApi<Self>) {}
532 #[allow(unused_variables)]
533 fn stopping(&self, turbo_tasks: &dyn TurboTasksBackendApi<Self>) {}
534
535 #[allow(unused_variables)]
536 fn idle_start(&self, turbo_tasks: &dyn TurboTasksBackendApi<Self>) {}
537 #[allow(unused_variables)]
538 fn idle_end(&self, turbo_tasks: &dyn TurboTasksBackendApi<Self>) {}
539
540 fn invalidate_task(&self, task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi<Self>);
541
542 fn invalidate_tasks(&self, tasks: &[TaskId], turbo_tasks: &dyn TurboTasksBackendApi<Self>);
543 fn invalidate_tasks_set(&self, tasks: &TaskIdSet, turbo_tasks: &dyn TurboTasksBackendApi<Self>);
544
545 fn invalidate_serialization(
546 &self,
547 _task: TaskId,
548 _turbo_tasks: &dyn TurboTasksBackendApi<Self>,
549 ) {
550 }
551
552 fn try_start_task_execution<'a>(
553 &'a self,
554 task: TaskId,
555 priority: TaskPriority,
556 turbo_tasks: &dyn TurboTasksBackendApi<Self>,
557 ) -> Option<TaskExecutionSpec<'a>>;
558
559 fn task_execution_canceled(&self, task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi<Self>);
560
561 fn task_execution_completed(
562 &self,
563 task: TaskId,
564 result: Result<RawVc, TurboTasksExecutionError>,
565 cell_counters: &AutoMap<ValueTypeId, u32, BuildHasherDefault<FxHasher>, 8>,
566 #[cfg(feature = "verify_determinism")] stateful: bool,
567 has_invalidator: bool,
568 turbo_tasks: &dyn TurboTasksBackendApi<Self>,
569 ) -> bool;
570
571 type BackendJob: Send + 'static;
572
573 fn run_backend_job<'a>(
574 &'a self,
575 job: Self::BackendJob,
576 turbo_tasks: &'a dyn TurboTasksBackendApi<Self>,
577 ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
578
579 fn try_read_task_output(
582 &self,
583 task: TaskId,
584 reader: Option<TaskId>,
585 options: ReadOutputOptions,
586 turbo_tasks: &dyn TurboTasksBackendApi<Self>,
587 ) -> Result<Result<RawVc, EventListener>>;
588
589 fn try_read_task_cell(
592 &self,
593 task: TaskId,
594 index: CellId,
595 reader: Option<TaskId>,
596 options: ReadCellOptions,
597 turbo_tasks: &dyn TurboTasksBackendApi<Self>,
598 ) -> Result<Result<TypedCellContent, EventListener>>;
599
600 fn try_read_own_task_cell(
603 &self,
604 current_task: TaskId,
605 index: CellId,
606 options: ReadCellOptions,
607 turbo_tasks: &dyn TurboTasksBackendApi<Self>,
608 ) -> Result<TypedCellContent> {
609 match self.try_read_task_cell(current_task, index, None, options, turbo_tasks)? {
610 Ok(content) => Ok(content),
611 Err(_) => Ok(TypedCellContent(index.type_id, CellContent(None))),
612 }
613 }
614
615 fn read_task_collectibles(
618 &self,
619 task: TaskId,
620 trait_id: TraitTypeId,
621 reader: Option<TaskId>,
622 turbo_tasks: &dyn TurboTasksBackendApi<Self>,
623 ) -> TaskCollectiblesMap;
624
625 fn emit_collectible(
626 &self,
627 trait_type: TraitTypeId,
628 collectible: RawVc,
629 task: TaskId,
630 turbo_tasks: &dyn TurboTasksBackendApi<Self>,
631 );
632
633 fn unemit_collectible(
634 &self,
635 trait_type: TraitTypeId,
636 collectible: RawVc,
637 count: u32,
638 task: TaskId,
639 turbo_tasks: &dyn TurboTasksBackendApi<Self>,
640 );
641
642 fn update_task_cell(
643 &self,
644 task: TaskId,
645 index: CellId,
646 is_serializable_cell_content: bool,
647 content: CellContent,
648 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
649 content_hash: Option<CellHash>,
650 verification_mode: VerificationMode,
651 turbo_tasks: &dyn TurboTasksBackendApi<Self>,
652 );
653
654 fn get_or_create_task(
655 &self,
656 native_fn: &'static NativeFunction,
657 this: Option<RawVc>,
658 arg: &mut dyn StackDynTaskInputs,
659 parent_task: Option<TaskId>,
660 persistence: TaskPersistence,
661 turbo_tasks: &dyn TurboTasksBackendApi<Self>,
662 ) -> TaskId;
663
664 fn connect_task(
665 &self,
666 task: TaskId,
667 parent_task: Option<TaskId>,
668 turbo_tasks: &dyn TurboTasksBackendApi<Self>,
669 );
670
671 fn mark_own_task_as_finished(
672 &self,
673 _task: TaskId,
674 _turbo_tasks: &dyn TurboTasksBackendApi<Self>,
675 ) {
676 }
678
679 fn mark_own_task_as_session_dependent(
680 &self,
681 _task: TaskId,
682 _turbo_tasks: &dyn TurboTasksBackendApi<Self>,
683 ) {
684 }
686
687 fn create_transient_task(
688 &self,
689 task_type: TransientTaskType,
690 turbo_tasks: &dyn TurboTasksBackendApi<Self>,
691 ) -> TaskId;
692
693 fn dispose_root_task(&self, task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi<Self>);
694
695 fn task_statistics(&self) -> &TaskStatisticsApi;
696
697 fn is_tracking_dependencies(&self) -> bool;
698
699 fn get_task_name(&self, task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi<Self>) -> String;
702}
703
704#[cfg(test)]
705mod cached_task_type_tests {
706 use std::{collections::hash_map::RandomState, hash::BuildHasher};
707
708 use crate::{
709 RawVc, TaskId,
710 backend::CachedTaskType,
711 dyn_task_inputs::DynTaskInputs,
712 macro_helpers::{ArgMeta, NativeFunction, into_task_fn},
713 };
714
715 fn dummy_fn_a() {}
720 fn dummy_fn_b() {}
721
722 static FN_A: NativeFunction = NativeFunction::new(
723 "dummy_fn_a",
724 "dummy_fn_a",
725 ArgMeta::new::<(i32,)>(),
726 &into_task_fn(dummy_fn_a),
727 false,
728 );
729
730 static FN_B: NativeFunction = NativeFunction::new(
731 "dummy_fn_b",
732 "dummy_fn_b",
733 ArgMeta::new::<(i32,)>(),
734 &into_task_fn(dummy_fn_b),
735 false,
736 );
737
738 fn hash_task(rs: &RandomState, task: &CachedTaskType) -> u64 {
740 rs.hash_one(task)
741 }
742
743 fn make_arg(value: i32) -> Box<dyn DynTaskInputs> {
745 Box::new((value,))
746 }
747
748 fn make_this(id: u32) -> Option<RawVc> {
750 Some(RawVc::TaskOutput(
751 TaskId::new(id).expect("non-zero task id"),
752 ))
753 }
754
755 #[test]
760 fn hash_from_components_matches_hash_impl_no_this() {
761 let rs = RandomState::new();
762 let arg = make_arg(42);
763 let task = CachedTaskType {
764 native_fn: &FN_A,
765 this: None,
766 arg: make_arg(42),
767 };
768 let expected = hash_task(&rs, &task);
769 let actual = CachedTaskType::hash_from_components(&rs, &FN_A, None, &*arg);
770 assert_eq!(actual, expected);
771 }
772
773 #[test]
774 fn hash_from_components_matches_hash_impl_with_this() {
775 let rs = RandomState::new();
776 let this = make_this(1);
777 let arg = make_arg(99);
778 let task = CachedTaskType {
779 native_fn: &FN_A,
780 this,
781 arg: make_arg(99),
782 };
783 let expected = hash_task(&rs, &task);
784 let actual = CachedTaskType::hash_from_components(&rs, &FN_A, this, &*arg);
785 assert_eq!(actual, expected);
786 }
787
788 #[test]
793 fn eq_components_returns_true_when_all_match() {
794 let task = CachedTaskType {
795 native_fn: &FN_A,
796 this: None,
797 arg: make_arg(7),
798 };
799 assert!(task.eq_components(&FN_A, None, &(7i32,)));
800 }
801
802 #[test]
803 fn eq_components_returns_true_with_matching_this() {
804 let this = make_this(1);
805 let task = CachedTaskType {
806 native_fn: &FN_A,
807 this,
808 arg: make_arg(7),
809 };
810 assert!(task.eq_components(&FN_A, this, &(7i32,)));
811 }
812
813 #[test]
818 fn eq_components_returns_false_when_native_fn_differs() {
819 let task = CachedTaskType {
820 native_fn: &FN_A,
821 this: None,
822 arg: make_arg(7),
823 };
824 assert!(!task.eq_components(&FN_B, None, &(7i32,)));
826 }
827
828 #[test]
833 fn eq_components_returns_false_when_this_differs() {
834 let task = CachedTaskType {
835 native_fn: &FN_A,
836 this: None,
837 arg: make_arg(7),
838 };
839 assert!(!task.eq_components(&FN_A, make_this(1), &(7i32,)));
841 }
842
843 #[test]
844 fn eq_components_returns_false_when_this_has_different_task_id() {
845 let task = CachedTaskType {
846 native_fn: &FN_A,
847 this: make_this(1),
848 arg: make_arg(7),
849 };
850 assert!(!task.eq_components(&FN_A, make_this(2), &(7i32,)));
851 }
852
853 #[test]
858 fn eq_components_returns_false_when_arg_differs() {
859 let task = CachedTaskType {
860 native_fn: &FN_A,
861 this: None,
862 arg: make_arg(1),
863 };
864 assert!(!task.eq_components(&FN_A, None, &(2i32,)));
866 }
867}