1use super::{ActorRuntime, KernelState, LinearMemory, MetaState, PluginState, WorkflowInstance};
13use crate::types::{
14 ActorId, CapId, Capability, MemAddr, PluginId, ResourceId, SecurityLevel, Size, Time,
15 WorkflowId,
16};
17
18#[derive(Debug)]
20pub enum StateError {
21 PluginNotFound(PluginId),
23 ActorNotFound(ActorId),
25 ResourceNotFound(ResourceId),
27 WorkflowNotFound(WorkflowId),
29 CapabilityNotFound(CapId),
31 CapabilityNotHeld(PluginId, CapId),
33 IsolationViolation(PluginId, PluginId),
35 DanglingCapability(MemAddr),
37 PluginExists(PluginId),
39 ActorExists(ActorId),
41 ResourceExists(ResourceId),
43 WorkflowExists(WorkflowId),
45 CounterOverflow(&'static str),
47}
48
49impl std::fmt::Display for StateError {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 match self {
52 StateError::PluginNotFound(id) => write!(f, "plugin {id} not found"),
53 StateError::ActorNotFound(id) => write!(f, "actor {id} not found"),
54 StateError::ResourceNotFound(id) => write!(f, "resource {id} not found"),
55 StateError::WorkflowNotFound(id) => write!(f, "workflow {id} not found"),
56 StateError::CapabilityNotFound(id) => write!(f, "capability {id} not found"),
57 StateError::CapabilityNotHeld(pid, cid) => {
58 write!(f, "plugin {pid} does not hold capability {cid}")
59 }
60 StateError::IsolationViolation(active, affected) => {
61 write!(f, "isolation violation: {active} affects {affected}")
62 }
63 StateError::DanglingCapability(addr) => {
64 write!(f, "freeing address {addr} would create dangling capability")
65 }
66 StateError::PluginExists(id) => write!(f, "plugin {id} already exists"),
67 StateError::ActorExists(id) => write!(f, "actor {id} already exists"),
68 StateError::ResourceExists(id) => write!(f, "resource {id} already exists"),
69 StateError::WorkflowExists(id) => write!(f, "workflow {id} already exists"),
70 StateError::CounterOverflow(name) => write!(f, "{name} counter overflow"),
71 }
72 }
73}
74
75impl std::error::Error for StateError {}
76
77#[derive(Debug, Clone, PartialEq, Eq, Default)]
81pub struct ResourceInfo {
82 pub(crate) level: SecurityLevel,
86}
87
88impl ResourceInfo {
89 pub fn new(level: SecurityLevel) -> Self {
91 ResourceInfo { level }
92 }
93
94 #[inline]
96 pub fn level(&self) -> SecurityLevel {
97 self.level
98 }
99}
100
101#[derive(Debug, Clone)]
112#[must_use = "state transitions return new state that must be used"]
113pub struct State {
114 pub(crate) kernel: KernelState,
118
119 pub(crate) plugins: Vec<(PluginId, PluginState)>,
124
125 pub(crate) actors: Vec<(ActorId, ActorRuntime)>,
130
131 pub(crate) resources: Vec<(ResourceId, ResourceInfo)>,
136
137 pub(crate) workflows: Vec<(WorkflowId, WorkflowInstance)>,
142
143 pub(crate) ghost: MetaState,
147}
148
149impl State {
150 pub fn empty() -> Self {
152 State {
153 kernel: KernelState::default(),
154 plugins: Vec::new(),
155 actors: Vec::new(),
156 resources: Vec::new(),
157 workflows: Vec::new(),
158 ghost: MetaState::empty(),
159 }
160 }
161
162 fn find_plugin_index(&self, pid: PluginId) -> Option<usize> {
168 self.plugins
169 .binary_search_by_key(&pid, |entry| entry.0)
170 .ok()
171 }
172
173 fn find_plugin_insert_pos(&self, pid: PluginId) -> (usize, bool) {
175 match self.plugins.binary_search_by_key(&pid, |entry| entry.0) {
176 Ok(pos) => (pos, true),
177 Err(pos) => (pos, false),
178 }
179 }
180
181 fn find_actor_index(&self, aid: ActorId) -> Option<usize> {
183 self.actors.binary_search_by_key(&aid, |entry| entry.0).ok()
184 }
185
186 fn find_actor_insert_pos(&self, aid: ActorId) -> (usize, bool) {
188 match self.actors.binary_search_by_key(&aid, |entry| entry.0) {
189 Ok(pos) => (pos, true),
190 Err(pos) => (pos, false),
191 }
192 }
193
194 fn find_resource_index(&self, rid: ResourceId) -> Option<usize> {
196 self.resources
197 .binary_search_by_key(&rid, |entry| entry.0)
198 .ok()
199 }
200
201 fn find_resource_insert_pos(&self, rid: ResourceId) -> (usize, bool) {
203 match self.resources.binary_search_by_key(&rid, |entry| entry.0) {
204 Ok(pos) => (pos, true),
205 Err(pos) => (pos, false),
206 }
207 }
208
209 fn find_workflow_index(&self, wid: WorkflowId) -> Option<usize> {
211 self.workflows
212 .binary_search_by_key(&wid, |entry| entry.0)
213 .ok()
214 }
215
216 fn find_workflow_insert_pos(&self, wid: WorkflowId) -> (usize, bool) {
218 match self.workflows.binary_search_by_key(&wid, |entry| entry.0) {
219 Ok(pos) => (pos, true),
220 Err(pos) => (pos, false),
221 }
222 }
223
224 pub fn plugin_memory(&self, pid: PluginId) -> Option<&LinearMemory> {
232 self.find_plugin_index(pid)
233 .map(|idx| self.plugins[idx].1.memory())
234 }
235
236 pub fn plugin_level(&self, pid: PluginId) -> Option<SecurityLevel> {
240 self.find_plugin_index(pid)
241 .map(|idx| self.plugins[idx].1.level())
242 }
243
244 pub fn resource_level(&self, rid: ResourceId) -> Option<SecurityLevel> {
248 self.find_resource_index(rid)
249 .map(|idx| self.resources[idx].1.level())
250 }
251
252 pub fn get_cap(&self, cap_id: CapId) -> Option<&Capability> {
256 self.kernel.revocation().get(cap_id)
257 }
258
259 pub fn cap_is_valid(&self, cap_id: CapId) -> bool {
263 self.kernel.revocation().is_valid(cap_id)
264 }
265
266 pub fn plugin_holds(&self, pid: PluginId, cap_id: CapId) -> bool {
270 self.find_plugin_index(pid)
271 .is_some_and(|idx| self.plugins[idx].1.holds_cap(cap_id))
272 }
273
274 pub fn apply_alloc(&self, owner: PluginId, _size: Size) -> Self {
278 let addr = self.ghost.resource_count() as MemAddr;
279 let new_ghost = self.ghost.alloc(addr, owner);
280 State {
281 kernel: self.kernel.clone(),
282 plugins: self.plugins.clone(),
283 actors: self.actors.clone(),
284 resources: self.resources.clone(),
285 workflows: self.workflows.clone(),
286 ghost: new_ghost,
287 }
288 }
289
290 pub fn apply_alloc_mut(&mut self, owner: PluginId, _size: Size) -> MemAddr {
292 let addr = self.ghost.resource_count() as MemAddr;
293 self.ghost.alloc_mut(addr, owner);
294 addr
295 }
296
297 pub fn apply_free(&self, addr: MemAddr) -> Result<Self, StateError> {
309 if self
312 .kernel
313 .revocation()
314 .any_valid_targeting(u128::from(addr))
315 {
316 return Err(StateError::DanglingCapability(addr));
317 }
318
319 Ok(State {
320 kernel: self.kernel.clone(),
321 plugins: self.plugins.clone(),
322 actors: self.actors.clone(),
323 resources: self.resources.clone(),
324 workflows: self.workflows.clone(),
325 ghost: self.ghost.free(addr),
326 })
327 }
328
329 pub fn apply_free_mut(&mut self, addr: MemAddr) -> Result<(), StateError> {
339 if self
342 .kernel
343 .revocation()
344 .any_valid_targeting(u128::from(addr))
345 {
346 return Err(StateError::DanglingCapability(addr));
347 }
348 match self.ghost.free_mut(addr) {
349 Ok(()) => Ok(()),
350 Err(super::MemoryError::DoubleFree(_)) => {
351 Err(StateError::ResourceNotFound(addr.into()))
352 }
353 Err(super::MemoryError::NotAllocated(_)) => {
354 Err(StateError::ResourceNotFound(addr.into()))
355 }
356 Err(super::MemoryError::UseAfterFree(_)) => {
357 Err(StateError::ResourceNotFound(addr.into()))
358 }
359 Err(super::MemoryError::OutOfBounds(_, _, _)) => {
360 Err(StateError::ResourceNotFound(addr.into()))
361 }
362 }
363 }
364
365 pub fn apply_revoke(&self, cap_id: CapId) -> Self {
369 State {
370 kernel: KernelState {
371 key_state: self.kernel.key_state().clone(),
372 policy: self.kernel.policy().clone(),
373 revocation: self.kernel.revocation().revoke(cap_id),
374 now: self.kernel.now(),
375 next_cap_id: self.kernel.next_cap_id(),
376 },
377 plugins: self.plugins.clone(),
378 actors: self.actors.clone(),
379 resources: self.resources.clone(),
380 workflows: self.workflows.clone(),
381 ghost: self.ghost.clone(),
382 }
383 }
384
385 pub fn apply_cap_revoke(&self, cap_id: CapId) -> Self {
389 State {
390 kernel: KernelState {
391 key_state: self.kernel.key_state().clone(),
392 policy: self.kernel.policy().clone(),
393 revocation: self.kernel.revocation().revoke_transitive(cap_id),
394 now: self.kernel.now(),
395 next_cap_id: self.kernel.next_cap_id(),
396 },
397 plugins: self.plugins.clone(),
398 actors: self.actors.clone(),
399 resources: self.resources.clone(),
400 workflows: self.workflows.clone(),
401 ghost: self.ghost.clone(),
402 }
403 }
404
405 pub fn apply_cap_revoke_mut(&mut self, cap_id: CapId) -> Result<(), super::KernelError> {
414 self.kernel
415 .revocation_mut()
416 .revoke_transitive_fast_mut(cap_id)
417 }
418
419 pub fn apply_cap_delegate(
429 &self,
430 new_cap: Capability,
431 target: PluginId,
432 ) -> Result<Self, super::KernelError> {
433 let cap_id = new_cap.id();
434 let new_revocation = self.kernel.revocation().insert(new_cap)?;
435 let mut new_plugins = self.plugins.clone();
436 if let Ok(idx) = new_plugins.binary_search_by_key(&target, |(pid, _)| *pid) {
438 new_plugins[idx].1.grant_cap_mut(cap_id);
439 }
440
441 Ok(State {
442 kernel: KernelState {
443 key_state: self.kernel.key_state().clone(),
444 policy: self.kernel.policy().clone(),
445 revocation: new_revocation,
446 now: self.kernel.now(),
447 next_cap_id: self.kernel.next_cap_id(),
448 },
449 plugins: new_plugins,
450 actors: self.actors.clone(),
451 resources: self.resources.clone(),
452 workflows: self.workflows.clone(),
453 ghost: self.ghost.clone(),
454 })
455 }
456
457 pub fn apply_cap_delegate_mut(
463 &mut self,
464 new_cap: Capability,
465 target: PluginId,
466 ) -> Result<(), super::KernelError> {
467 let cap_id = new_cap.id();
468 self.kernel.revocation_mut().insert_mut(new_cap)?;
469 if let Some(idx) = self.find_plugin_index(target) {
470 self.plugins[idx].1.grant_cap_mut(cap_id);
471 }
472 Ok(())
473 }
474
475 pub fn preserves_isolation(&self, other: &State, active: PluginId) -> bool {
479 self.plugins.iter().all(|(pid, ps)| {
480 if *pid == active {
481 return true;
482 }
483 match other.plugins.binary_search_by_key(pid, |(id, _)| *id) {
484 Ok(idx) => *ps == other.plugins[idx].1,
485 Err(_) => false,
486 }
487 })
488 }
489
490 pub fn temporal_safety(&self, other: &State) -> bool {
494 self.ghost
495 .freed_set
496 .iter()
497 .all(|addr| other.ghost.is_freed(*addr))
498 }
499
500 #[inline]
504 pub fn time(&self) -> Time {
505 self.kernel.now()
506 }
507
508 pub fn tick(&mut self) -> Result<(), StateError> {
516 self.kernel
517 .tick_checked()
518 .map_err(|_| StateError::CounterOverflow("time"))
519 }
520
521 #[inline]
523 pub fn kernel(&self) -> &KernelState {
524 &self.kernel
525 }
526
527 #[inline]
529 #[allow(dead_code)] pub(crate) fn kernel_mut(&mut self) -> &mut KernelState {
531 &mut self.kernel
532 }
533
534 #[inline]
536 pub fn ghost(&self) -> &MetaState {
537 &self.ghost
538 }
539
540 pub fn get_plugin(&self, pid: PluginId) -> Option<&PluginState> {
542 self.find_plugin_index(pid).map(|idx| &self.plugins[idx].1)
543 }
544
545 pub fn get_plugin_mut(&mut self, pid: PluginId) -> Option<&mut PluginState> {
547 self.find_plugin_index(pid)
548 .map(|idx| &mut self.plugins[idx].1)
549 }
550
551 pub fn insert_plugin(&mut self, pid: PluginId, ps: PluginState) -> Result<(), StateError> {
559 let (pos, exists) = self.find_plugin_insert_pos(pid);
560 if exists {
561 return Err(StateError::PluginExists(pid));
562 }
563 self.plugins.insert(pos, (pid, ps));
564 Ok(())
565 }
566
567 pub fn get_actor(&self, aid: ActorId) -> Option<&ActorRuntime> {
569 self.find_actor_index(aid).map(|idx| &self.actors[idx].1)
570 }
571
572 pub fn get_actor_mut(&mut self, aid: ActorId) -> Option<&mut ActorRuntime> {
574 self.find_actor_index(aid)
575 .map(|idx| &mut self.actors[idx].1)
576 }
577
578 pub fn insert_actor(&mut self, aid: ActorId, ar: ActorRuntime) -> Result<(), StateError> {
586 let (pos, exists) = self.find_actor_insert_pos(aid);
587 if exists {
588 return Err(StateError::ActorExists(aid));
589 }
590 self.actors.insert(pos, (aid, ar));
591 Ok(())
592 }
593
594 pub fn get_resource(&self, rid: ResourceId) -> Option<&ResourceInfo> {
596 self.find_resource_index(rid)
597 .map(|idx| &self.resources[idx].1)
598 }
599
600 pub fn insert_resource(&mut self, rid: ResourceId, ri: ResourceInfo) -> Result<(), StateError> {
608 let (pos, exists) = self.find_resource_insert_pos(rid);
609 if exists {
610 return Err(StateError::ResourceExists(rid));
611 }
612 self.resources.insert(pos, (rid, ri));
613 Ok(())
614 }
615
616 pub fn get_workflow(&self, wid: WorkflowId) -> Option<&WorkflowInstance> {
618 self.find_workflow_index(wid)
619 .map(|idx| &self.workflows[idx].1)
620 }
621
622 pub fn get_workflow_mut(&mut self, wid: WorkflowId) -> Option<&mut WorkflowInstance> {
624 self.find_workflow_index(wid)
625 .map(|idx| &mut self.workflows[idx].1)
626 }
627
628 pub fn insert_workflow(
636 &mut self,
637 wid: WorkflowId,
638 wi: WorkflowInstance,
639 ) -> Result<(), StateError> {
640 let (pos, exists) = self.find_workflow_insert_pos(wid);
641 if exists {
642 return Err(StateError::WorkflowExists(wid));
643 }
644 self.workflows.insert(pos, (wid, wi));
645 Ok(())
646 }
647
648 #[inline]
650 pub fn plugin_count(&self) -> usize {
651 self.plugins.len()
652 }
653
654 #[inline]
656 pub fn actor_count(&self) -> usize {
657 self.actors.len()
658 }
659
660 #[inline]
662 pub fn resource_count(&self) -> usize {
663 self.resources.len()
664 }
665
666 #[inline]
668 pub fn workflow_count(&self) -> usize {
669 self.workflows.len()
670 }
671
672 pub fn plugin_ids(&self) -> Vec<PluginId> {
674 let mut ids = Vec::with_capacity(self.plugins.len());
675 let mut i = 0;
676 while i < self.plugins.len() {
677 ids.push(self.plugins[i].0);
678 i += 1;
679 }
680 ids
681 }
682
683 pub fn resource_ids(&self) -> Vec<ResourceId> {
685 let mut ids = Vec::with_capacity(self.resources.len());
686 let mut i = 0;
687 while i < self.resources.len() {
688 ids.push(self.resources[i].0);
689 i += 1;
690 }
691 ids
692 }
693}
694
695impl Default for State {
696 fn default() -> Self {
697 State::empty()
698 }
699}
700
701#[allow(dead_code)] pub fn low_equivalent_left(l: SecurityLevel, s1: &State, s2: &State) -> bool {
708 plugins_low_equiv(l, s1, s2) && resources_low_equiv(l, s1, s2)
709}
710
711#[allow(dead_code)] fn plugins_low_equiv(l: SecurityLevel, s1: &State, s2: &State) -> bool {
714 s1.plugins.iter().all(|(pid, ps1)| {
715 if ps1.level() > l {
716 return true;
717 }
718 match s2.plugins.binary_search_by_key(pid, |(id, _)| *id) {
719 Ok(idx) => *ps1 == s2.plugins[idx].1,
720 Err(_) => false,
721 }
722 })
723}
724
725#[allow(dead_code)] fn resources_low_equiv(l: SecurityLevel, s1: &State, s2: &State) -> bool {
728 s1.resources.iter().all(|(rid, ri1)| {
729 if ri1.level() > l {
730 return true;
731 }
732 match s2.resources.binary_search_by_key(rid, |(id, _)| *id) {
733 Ok(idx) => *ri1 == s2.resources[idx].1,
734 Err(_) => false,
735 }
736 })
737}
738
739#[allow(dead_code)] pub fn low_equivalent(l: SecurityLevel, s1: &State, s2: &State) -> bool {
746 low_equivalent_left(l, s1, s2) && low_equivalent_left(l, s2, s1)
747}
748
749#[cfg(test)]
750mod tests {
751 use super::*;
752 use crate::types::{Right, Rights, SealedTag};
753
754 fn make_test_cap(id: CapId) -> Capability {
755 Capability::new(
756 id,
757 1,
758 1,
759 Rights::singleton(Right::Read),
760 None,
761 0,
762 SealedTag::empty(),
763 )
764 .expect("valid capability")
765 }
766
767 #[test]
768 fn test_state_empty() {
769 let s = State::empty();
770 assert_eq!(s.time(), 0);
771 assert_eq!(s.plugin_count(), 0);
772 assert_eq!(s.actor_count(), 0);
773 }
774
775 #[test]
776 fn test_state_apply_alloc() {
777 let mut s = State::empty();
778 let addr = s.apply_alloc_mut(1, 1024);
779
780 assert!(s.ghost.is_live(addr));
781 }
782
783 #[test]
784 fn test_state_apply_free() {
785 let mut s = State::empty();
786 let addr = s.apply_alloc_mut(1, 1024);
787 s.apply_free_mut(addr).expect("free should succeed");
788
789 assert!(!s.ghost.is_live(addr));
790 assert!(s.ghost.is_freed(addr));
791 }
792
793 #[test]
794 fn test_state_temporal_safety() {
795 let mut s1 = State::empty();
796 let addr = s1.apply_alloc_mut(1, 1024);
797 s1.apply_free_mut(addr).expect("free should succeed");
798
799 let s2 = s1.clone();
801 assert!(s1.temporal_safety(&s2));
802
803 let mut s3 = s1.clone();
805 s3.apply_alloc_mut(2, 512);
806 assert!(s1.temporal_safety(&s3));
807 }
808
809 #[test]
810 fn test_state_plugin_holds() {
811 let mut s = State::empty();
812 let ps = PluginState::empty(SecurityLevel::Public, 0);
813 s.insert_plugin(1, ps).unwrap();
814
815 assert!(!s.plugin_holds(1, 42));
817
818 if let Some(ps) = s.get_plugin_mut(1) {
820 ps.grant_cap_mut(42);
821 }
822 assert!(s.plugin_holds(1, 42));
823 }
824
825 #[test]
826 fn test_state_cap_delegate() {
827 let mut s = State::empty();
828 let ps = PluginState::empty(SecurityLevel::Public, 0);
829 s.insert_plugin(1, ps).unwrap();
830
831 let cap = make_test_cap(100);
832 s.apply_cap_delegate_mut(cap, 1)
833 .expect("delegate should succeed");
834
835 assert!(s.plugin_holds(1, 100));
837 assert!(s.cap_is_valid(100));
839 }
840
841 #[test]
842 fn test_state_cap_revoke() {
843 let mut s = State::empty();
844 let ps = PluginState::empty(SecurityLevel::Public, 0);
845 s.insert_plugin(1, ps).unwrap();
846
847 let cap = make_test_cap(100);
848 s.apply_cap_delegate_mut(cap, 1)
849 .expect("delegate should succeed");
850
851 assert!(s.cap_is_valid(100));
852
853 s.apply_cap_revoke_mut(100).expect("revoke should succeed");
854 assert!(!s.cap_is_valid(100));
855 }
856
857 #[test]
858 fn test_state_isolation() {
859 let mut s1 = State::empty();
860 let _ = s1.insert_plugin(1, PluginState::empty(SecurityLevel::Public, 100));
861 let _ = s1.insert_plugin(2, PluginState::empty(SecurityLevel::Internal, 200));
862
863 let mut s2 = s1.clone();
865 if let Some(ps) = s2.get_plugin_mut(1) {
866 ps.grant_cap_mut(42);
867 }
868
869 assert!(s1.preserves_isolation(&s2, 1));
871 }
872
873 #[test]
874 fn test_low_equivalent_refl() {
875 let s = State::empty();
877 assert!(low_equivalent(SecurityLevel::Public, &s, &s));
878 assert!(low_equivalent(SecurityLevel::Secret, &s, &s));
879 }
880
881 #[test]
882 fn test_low_equivalent_symm() {
883 let mut s1 = State::empty();
885 let _ = s1.insert_plugin(1, PluginState::empty(SecurityLevel::Public, 0));
886
887 let s2 = s1.clone();
888
889 assert!(low_equivalent(SecurityLevel::Public, &s1, &s2));
890 assert!(low_equivalent(SecurityLevel::Public, &s2, &s1));
891 }
892
893 #[test]
894 fn test_low_equivalent_high_changes_invisible() {
895 let mut s1 = State::empty();
896 let _ = s1.insert_plugin(1, PluginState::empty(SecurityLevel::Public, 0));
897 let _ = s1.insert_plugin(2, PluginState::empty(SecurityLevel::Secret, 0));
898
899 let mut s2 = s1.clone();
900 if let Some(ps) = s2.get_plugin_mut(2) {
902 ps.grant_cap_mut(42);
903 }
904
905 assert!(low_equivalent(SecurityLevel::Public, &s1, &s2));
908
909 assert!(!low_equivalent(SecurityLevel::Secret, &s1, &s2));
911 }
912
913 #[test]
914 fn test_resource_info() {
915 let ri = ResourceInfo::new(SecurityLevel::Confidential);
916 assert_eq!(ri.level(), SecurityLevel::Confidential);
917
918 let ri_default = ResourceInfo::default();
919 assert_eq!(ri_default.level(), SecurityLevel::Public);
920 }
921
922 #[test]
923 fn test_state_tick() {
924 let mut s = State::empty();
925 assert_eq!(s.time(), 0);
926
927 s.tick().expect("tick should succeed");
928 assert_eq!(s.time(), 1);
929
930 s.tick().expect("tick should succeed");
931 assert_eq!(s.time(), 2);
932 }
933
934 #[test]
935 fn test_state_time_delegates_to_kernel() {
936 let s = State::empty();
938 assert_eq!(s.time(), s.kernel().now());
939 }
940
941 #[test]
944 fn test_state_plugin_insert_order() {
945 let mut s = State::empty();
946 let ps = || PluginState::empty(SecurityLevel::Public, 1024);
947 s.insert_plugin(50, ps()).unwrap();
949 s.insert_plugin(10, ps()).unwrap();
950 s.insert_plugin(90, ps()).unwrap();
951 s.insert_plugin(30, ps()).unwrap();
952
953 let ids: Vec<u128> = s.plugins.iter().map(|(id, _)| *id).collect();
955 assert_eq!(ids, vec![10, 30, 50, 90]);
956 }
957
958 #[test]
959 fn test_state_empty_lookup() {
960 let s = State::empty();
961 assert!(s.get_plugin(0).is_none());
962 assert!(s.get_actor(0).is_none());
963 assert!(s.get_workflow(0).is_none());
964 }
965
966 #[test]
967 fn test_state_single_element_lookup() {
968 let mut s = State::empty();
969 s.insert_plugin(42, PluginState::empty(SecurityLevel::Public, 1024))
970 .unwrap();
971
972 assert!(s.get_plugin(42).is_some());
973 assert!(s.get_plugin(41).is_none());
974 assert!(s.get_plugin(43).is_none());
975 }
976
977 #[test]
978 fn test_state_plugin_collision() {
979 let mut s = State::empty();
980 let ps = || PluginState::empty(SecurityLevel::Public, 1024);
981 s.insert_plugin(1, ps()).unwrap();
982 let result = s.insert_plugin(1, ps());
983 assert!(matches!(result, Err(StateError::PluginExists(1))));
984 }
985}