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::{PicCallError, PicInstallError, StandaloneCanisterFixtureError};
25pub use process_lock::{
26 PicSerialGuard, PicSerialGuardError, acquire_pic_serial_guard, try_acquire_pic_serial_guard,
27};
28pub use runtime::PicRuntimeConfig;
29pub use startup::PicStartError;
30
31pub use standalone::{
32 StandaloneCanisterFixture, install_prebuilt_canister, install_prebuilt_canister_with_cycles,
33 try_install_prebuilt_canister, try_install_prebuilt_canister_with_cycles,
34};
35
36#[must_use]
45pub fn pic() -> Pic {
46 try_pic().unwrap_or_else(|err| panic!("failed to start PocketIC: {err}"))
47}
48
49pub fn try_pic() -> Result<Pic, PicStartError> {
51 PicBuilder::new().with_application_subnet().try_build()
52}
53
54#[must_use]
56pub fn ensure_pocket_ic_bin() -> std::path::PathBuf {
57 try_ensure_pocket_ic_bin()
58 .unwrap_or_else(|err| panic!("failed to resolve PocketIC server binary: {err}"))
59}
60
61pub fn try_ensure_pocket_ic_bin() -> Result<std::path::PathBuf, PicStartError> {
63 runtime::ensure_pocket_ic_bin_from_env()
64}
65
66pub struct PicBuilder {
77 inner: PocketIcBuilder,
78 runtime_config: PicRuntimeConfig,
79}
80
81#[expect(clippy::new_without_default)]
82impl PicBuilder {
83 #[must_use]
85 pub fn new() -> Self {
86 Self {
87 inner: PocketIcBuilder::new(),
88 runtime_config: PicRuntimeConfig::from_env(),
89 }
90 }
91
92 #[must_use]
94 pub fn with_runtime_config(mut self, runtime_config: PicRuntimeConfig) -> Self {
95 self.runtime_config = runtime_config;
96 self
97 }
98
99 #[must_use]
101 pub fn with_server_binary(mut self, server_binary: impl Into<std::path::PathBuf>) -> Self {
102 self.runtime_config = self.runtime_config.pocket_ic_bin(server_binary);
103 self
104 }
105
106 #[must_use]
108 pub fn with_application_subnet(mut self) -> Self {
109 self.inner = self.inner.with_application_subnet();
110 self
111 }
112
113 #[must_use]
115 pub fn with_ii_subnet(mut self) -> Self {
116 self.inner = self.inner.with_ii_subnet();
117 self
118 }
119
120 #[must_use]
122 pub fn with_nns_subnet(mut self) -> Self {
123 self.inner = self.inner.with_nns_subnet();
124 self
125 }
126
127 #[must_use]
129 pub fn build(self) -> Pic {
130 self.try_build()
131 .unwrap_or_else(|err| panic!("failed to start PocketIC: {err}"))
132 }
133
134 pub fn try_build(self) -> Result<Pic, PicStartError> {
136 let server_binary = self.runtime_config.ensure_binary()?;
137 startup::try_build_pic(self.inner.with_server_binary(server_binary))
138 }
139}
140pub struct Pic {
149 inner: PocketIc,
150}
151
152impl Pic {
153 pub fn tick(&self) {
155 self.inner.tick();
156 }
157
158 pub fn advance_time(&self, duration: Duration) {
160 self.inner.advance_time(duration);
161 }
162
163 #[must_use]
165 pub fn create_canister(&self) -> Principal {
166 self.inner.create_canister()
167 }
168
169 pub fn add_cycles(&self, canister_id: Principal, amount: u128) {
171 let _ = self.inner.add_cycles(canister_id, amount);
172 }
173
174 pub fn install_canister(
176 &self,
177 canister_id: Principal,
178 wasm_module: Vec<u8>,
179 arg: Vec<u8>,
180 sender: Option<Principal>,
181 ) {
182 self.inner
183 .install_canister(canister_id, wasm_module, arg, sender);
184 }
185
186 pub fn upgrade_canister(
188 &self,
189 canister_id: Principal,
190 wasm_module: Vec<u8>,
191 arg: Vec<u8>,
192 sender: Option<Principal>,
193 ) -> Result<(), RejectResponse> {
194 self.inner
195 .upgrade_canister(canister_id, wasm_module, arg, sender)
196 }
197
198 pub fn reinstall_canister(
200 &self,
201 canister_id: Principal,
202 wasm_module: Vec<u8>,
203 arg: Vec<u8>,
204 sender: Option<Principal>,
205 ) -> Result<(), RejectResponse> {
206 self.inner
207 .reinstall_canister(canister_id, wasm_module, arg, sender)
208 }
209
210 pub fn submit_call(
212 &self,
213 canister_id: Principal,
214 sender: Principal,
215 method: &str,
216 payload: Vec<u8>,
217 ) -> Result<RawMessageId, RejectResponse> {
218 self.inner.submit_call(canister_id, sender, method, payload)
219 }
220
221 pub fn await_call(&self, message_id: RawMessageId) -> Result<Vec<u8>, RejectResponse> {
223 self.inner.await_call(message_id)
224 }
225
226 pub fn canister_status(
228 &self,
229 canister_id: Principal,
230 sender: Option<Principal>,
231 ) -> Result<CanisterStatusResult, RejectResponse> {
232 self.inner.canister_status(canister_id, sender)
233 }
234
235 pub fn fetch_canister_logs(
237 &self,
238 canister_id: Principal,
239 sender: Principal,
240 ) -> Result<Vec<pocket_ic::CanisterLogRecord>, RejectResponse> {
241 self.inner.fetch_canister_logs(canister_id, sender)
242 }
243
244 #[must_use]
246 pub fn current_time_nanos(&self) -> u64 {
247 self.inner.get_time().as_nanos_since_unix_epoch()
248 }
249
250 pub fn restore_time_nanos(&self, nanos_since_epoch: u64) {
252 let restored = pocket_ic::Time::from_nanos_since_unix_epoch(nanos_since_epoch);
253 self.inner.set_time(restored);
254 self.inner.set_certified_time(restored);
255 }
256}