1use candid::Principal;
4use pocket_ic::{
5 CanisterStatusResult, PocketIc, PocketIcBuilder, RejectResponse, common::rest::RawMessageId,
6};
7use std::time::Duration;
8
9mod baseline;
10mod calls;
11mod diagnostics;
12mod errors;
13mod lifecycle;
14mod process_lock;
15mod runtime;
16mod snapshot;
17mod standalone;
18mod startup;
19
20pub use baseline::{
21 CachedPicBaseline, CachedPicBaselineGuard, ControllerSnapshots,
22 restore_or_rebuild_cached_pic_baseline,
23};
24pub use errors::{
25 PicCallContext, PicCallError, PicCallErrorKind, PicInstallError, StandaloneCanisterFixtureError,
26};
27pub use lifecycle::InstallSpec;
28pub use process_lock::{
29 PicSerialGuard, PicSerialGuardError, acquire_pic_serial_guard, try_acquire_pic_serial_guard,
30};
31pub use runtime::PicRuntimeConfig;
32pub use startup::PicStartError;
33
34pub use standalone::{
35 StandaloneCanisterFixture, install_prebuilt_canister, install_prebuilt_canister_with_cycles,
36 try_install_prebuilt_canister, try_install_prebuilt_canister_with_cycles,
37};
38
39#[must_use]
48pub fn pic() -> Pic {
49 try_pic().unwrap_or_else(|err| panic!("failed to start PocketIC: {err}"))
50}
51
52pub fn try_pic() -> Result<Pic, PicStartError> {
54 PicBuilder::new().with_application_subnet().try_build()
55}
56
57#[must_use]
59pub fn ensure_pocket_ic_bin() -> std::path::PathBuf {
60 try_ensure_pocket_ic_bin()
61 .unwrap_or_else(|err| panic!("failed to resolve PocketIC server binary: {err}"))
62}
63
64pub fn try_ensure_pocket_ic_bin() -> Result<std::path::PathBuf, PicStartError> {
66 runtime::ensure_pocket_ic_bin_from_env()
67}
68
69pub struct PicBuilder {
80 inner: PocketIcBuilder,
81 runtime_config: PicRuntimeConfig,
82}
83
84#[expect(clippy::new_without_default)]
85impl PicBuilder {
86 #[must_use]
88 pub fn new() -> Self {
89 Self {
90 inner: PocketIcBuilder::new(),
91 runtime_config: PicRuntimeConfig::from_env(),
92 }
93 }
94
95 #[must_use]
97 pub fn with_runtime_config(mut self, runtime_config: PicRuntimeConfig) -> Self {
98 self.runtime_config = runtime_config;
99 self
100 }
101
102 #[must_use]
104 pub fn with_server_binary(mut self, server_binary: impl Into<std::path::PathBuf>) -> Self {
105 self.runtime_config = self.runtime_config.pocket_ic_bin(server_binary);
106 self
107 }
108
109 #[must_use]
111 pub fn with_application_subnet(mut self) -> Self {
112 self.inner = self.inner.with_application_subnet();
113 self
114 }
115
116 #[must_use]
118 pub fn with_ii_subnet(mut self) -> Self {
119 self.inner = self.inner.with_ii_subnet();
120 self
121 }
122
123 #[must_use]
125 pub fn with_nns_subnet(mut self) -> Self {
126 self.inner = self.inner.with_nns_subnet();
127 self
128 }
129
130 #[must_use]
132 pub fn build(self) -> Pic {
133 self.try_build()
134 .unwrap_or_else(|err| panic!("failed to start PocketIC: {err}"))
135 }
136
137 pub fn try_build(self) -> Result<Pic, PicStartError> {
139 let server_binary = self.runtime_config.ensure_binary()?;
140 startup::try_build_pic(self.inner.with_server_binary(server_binary))
141 }
142}
143pub struct Pic {
152 inner: PocketIc,
153}
154
155impl Pic {
156 pub fn tick(&self) {
158 self.inner.tick();
159 }
160
161 pub fn advance_time(&self, duration: Duration) {
163 self.inner.advance_time(duration);
164 }
165
166 #[must_use]
168 pub fn create_canister(&self) -> Principal {
169 self.inner.create_canister()
170 }
171
172 pub fn add_cycles(&self, canister_id: Principal, amount: u128) {
174 let _ = self.inner.add_cycles(canister_id, amount);
175 }
176
177 pub fn install_canister(
179 &self,
180 canister_id: Principal,
181 wasm_module: Vec<u8>,
182 arg: Vec<u8>,
183 sender: Option<Principal>,
184 ) {
185 self.inner
186 .install_canister(canister_id, wasm_module, arg, sender);
187 }
188
189 pub fn upgrade_canister(
191 &self,
192 canister_id: Principal,
193 wasm_module: Vec<u8>,
194 arg: Vec<u8>,
195 sender: Option<Principal>,
196 ) -> Result<(), RejectResponse> {
197 self.inner
198 .upgrade_canister(canister_id, wasm_module, arg, sender)
199 }
200
201 pub fn reinstall_canister(
203 &self,
204 canister_id: Principal,
205 wasm_module: Vec<u8>,
206 arg: Vec<u8>,
207 sender: Option<Principal>,
208 ) -> Result<(), RejectResponse> {
209 self.inner
210 .reinstall_canister(canister_id, wasm_module, arg, sender)
211 }
212
213 pub fn submit_call(
215 &self,
216 canister_id: Principal,
217 sender: Principal,
218 method: &str,
219 payload: Vec<u8>,
220 ) -> Result<RawMessageId, RejectResponse> {
221 self.inner.submit_call(canister_id, sender, method, payload)
222 }
223
224 pub fn await_call(&self, message_id: RawMessageId) -> Result<Vec<u8>, RejectResponse> {
226 self.inner.await_call(message_id)
227 }
228
229 pub fn canister_status(
231 &self,
232 canister_id: Principal,
233 sender: Option<Principal>,
234 ) -> Result<CanisterStatusResult, RejectResponse> {
235 self.inner.canister_status(canister_id, sender)
236 }
237
238 pub fn fetch_canister_logs(
240 &self,
241 canister_id: Principal,
242 sender: Principal,
243 ) -> Result<Vec<pocket_ic::CanisterLogRecord>, RejectResponse> {
244 self.inner.fetch_canister_logs(canister_id, sender)
245 }
246
247 #[must_use]
249 pub fn current_time_nanos(&self) -> u64 {
250 self.inner.get_time().as_nanos_since_unix_epoch()
251 }
252
253 pub fn restore_time_nanos(&self, nanos_since_epoch: u64) {
255 let restored = pocket_ic::Time::from_nanos_since_unix_epoch(nanos_since_epoch);
256 self.inner.set_time(restored);
257 self.inner.set_certified_time(restored);
258 }
259}