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_from_spec,
36 install_prebuilt_canister_with_cycles, try_install_prebuilt_canister,
37 try_install_prebuilt_canister_from_spec, try_install_prebuilt_canister_with_cycles,
38};
39
40#[must_use]
49pub fn pic() -> Pic {
50 try_pic().unwrap_or_else(|err| panic!("failed to start PocketIC: {err}"))
51}
52
53pub fn try_pic() -> Result<Pic, PicStartError> {
55 PicBuilder::new().with_application_subnet().try_build()
56}
57
58#[must_use]
60pub fn ensure_pocket_ic_bin() -> std::path::PathBuf {
61 try_ensure_pocket_ic_bin()
62 .unwrap_or_else(|err| panic!("failed to resolve PocketIC server binary: {err}"))
63}
64
65pub fn try_ensure_pocket_ic_bin() -> Result<std::path::PathBuf, PicStartError> {
67 runtime::ensure_pocket_ic_bin_from_env()
68}
69
70pub struct PicBuilder {
81 inner: PocketIcBuilder,
82 runtime_config: PicRuntimeConfig,
83}
84
85#[expect(clippy::new_without_default)]
86impl PicBuilder {
87 #[must_use]
89 pub fn new() -> Self {
90 Self {
91 inner: PocketIcBuilder::new(),
92 runtime_config: PicRuntimeConfig::from_env(),
93 }
94 }
95
96 #[must_use]
98 pub fn with_runtime_config(mut self, runtime_config: PicRuntimeConfig) -> Self {
99 self.runtime_config = runtime_config;
100 self
101 }
102
103 #[must_use]
105 pub fn with_server_binary(mut self, server_binary: impl Into<std::path::PathBuf>) -> Self {
106 self.runtime_config = self.runtime_config.pocket_ic_bin(server_binary);
107 self
108 }
109
110 #[must_use]
112 pub fn with_application_subnet(mut self) -> Self {
113 self.inner = self.inner.with_application_subnet();
114 self
115 }
116
117 #[must_use]
119 pub fn with_ii_subnet(mut self) -> Self {
120 self.inner = self.inner.with_ii_subnet();
121 self
122 }
123
124 #[must_use]
126 pub fn with_nns_subnet(mut self) -> Self {
127 self.inner = self.inner.with_nns_subnet();
128 self
129 }
130
131 #[must_use]
133 pub fn build(self) -> Pic {
134 self.try_build()
135 .unwrap_or_else(|err| panic!("failed to start PocketIC: {err}"))
136 }
137
138 pub fn try_build(self) -> Result<Pic, PicStartError> {
140 let server_binary = self.runtime_config.ensure_binary()?;
141 startup::try_build_pic(self.inner.with_server_binary(server_binary))
142 }
143}
144pub struct Pic {
153 inner: PocketIc,
154}
155
156impl Pic {
157 pub fn tick(&self) {
159 self.inner.tick();
160 }
161
162 pub fn advance_time(&self, duration: Duration) {
164 self.inner.advance_time(duration);
165 }
166
167 #[must_use]
169 pub fn create_canister(&self) -> Principal {
170 self.inner.create_canister()
171 }
172
173 pub fn add_cycles(&self, canister_id: Principal, amount: u128) {
175 let _ = self.inner.add_cycles(canister_id, amount);
176 }
177
178 pub fn install_canister(
180 &self,
181 canister_id: Principal,
182 wasm_module: Vec<u8>,
183 arg: Vec<u8>,
184 sender: Option<Principal>,
185 ) {
186 self.inner
187 .install_canister(canister_id, wasm_module, arg, sender);
188 }
189
190 pub fn upgrade_canister(
192 &self,
193 canister_id: Principal,
194 wasm_module: Vec<u8>,
195 arg: Vec<u8>,
196 sender: Option<Principal>,
197 ) -> Result<(), RejectResponse> {
198 self.inner
199 .upgrade_canister(canister_id, wasm_module, arg, sender)
200 }
201
202 pub fn reinstall_canister(
204 &self,
205 canister_id: Principal,
206 wasm_module: Vec<u8>,
207 arg: Vec<u8>,
208 sender: Option<Principal>,
209 ) -> Result<(), RejectResponse> {
210 self.inner
211 .reinstall_canister(canister_id, wasm_module, arg, sender)
212 }
213
214 pub fn submit_call(
216 &self,
217 canister_id: Principal,
218 sender: Principal,
219 method: &str,
220 payload: Vec<u8>,
221 ) -> Result<RawMessageId, RejectResponse> {
222 self.inner.submit_call(canister_id, sender, method, payload)
223 }
224
225 pub fn await_call(&self, message_id: RawMessageId) -> Result<Vec<u8>, RejectResponse> {
227 self.inner.await_call(message_id)
228 }
229
230 pub fn canister_status(
232 &self,
233 canister_id: Principal,
234 sender: Option<Principal>,
235 ) -> Result<CanisterStatusResult, RejectResponse> {
236 self.inner.canister_status(canister_id, sender)
237 }
238
239 pub fn fetch_canister_logs(
241 &self,
242 canister_id: Principal,
243 sender: Principal,
244 ) -> Result<Vec<pocket_ic::CanisterLogRecord>, RejectResponse> {
245 self.inner.fetch_canister_logs(canister_id, sender)
246 }
247
248 #[must_use]
250 pub fn current_time_nanos(&self) -> u64 {
251 self.inner.get_time().as_nanos_since_unix_epoch()
252 }
253
254 pub fn restore_time_nanos(&self, nanos_since_epoch: u64) {
256 let restored = pocket_ic::Time::from_nanos_since_unix_epoch(nanos_since_epoch);
257 self.inner.set_time(restored);
258 self.inner.set_certified_time(restored);
259 }
260}