1use candid::{Principal, encode_args, encode_one};
4use canic::{
5 Error,
6 cdk::types::TC,
7 dto::{
8 abi::v1::CanisterInitPayload,
9 env::EnvBootstrapArgs,
10 subnet::SubnetIdentity,
11 topology::{AppIndexArgs, SubnetIndexArgs},
12 },
13 ids::CanisterRole,
14};
15use pocket_ic::{
16 CanisterStatusResult, PocketIc, PocketIcBuilder, RejectResponse, common::rest::RawMessageId,
17};
18use std::time::Duration;
19
20mod baseline;
21mod calls;
22mod diagnostics;
23mod errors;
24mod lifecycle;
25mod process_lock;
26mod readiness;
27mod snapshot;
28mod standalone;
29mod startup;
30
31pub use baseline::{
32 CachedPicBaseline, CachedPicBaselineGuard, ControllerSnapshots,
33 restore_or_rebuild_cached_pic_baseline,
34};
35pub use errors::{PicInstallError, StandaloneCanisterFixtureError};
36pub use process_lock::{
37 PicSerialGuard, PicSerialGuardError, acquire_pic_serial_guard, try_acquire_pic_serial_guard,
38};
39pub use readiness::{role_pid, wait_until_ready};
40pub use startup::PicStartError;
41const INSTALL_CYCLES: u128 = 500 * TC;
42
43pub use standalone::{
44 StandaloneCanisterFixture, install_prebuilt_canister, install_prebuilt_canister_with_cycles,
45 install_standalone_canister, try_install_prebuilt_canister,
46 try_install_prebuilt_canister_with_cycles,
47};
48
49#[must_use]
58pub fn pic() -> Pic {
59 try_pic().unwrap_or_else(|err| panic!("failed to start PocketIC: {err}"))
60}
61
62pub fn try_pic() -> Result<Pic, PicStartError> {
64 PicBuilder::new().with_application_subnet().try_build()
65}
66
67pub struct PicBuilder(PocketIcBuilder);
78
79#[expect(clippy::new_without_default)]
80impl PicBuilder {
81 #[must_use]
83 pub fn new() -> Self {
84 Self(PocketIcBuilder::new())
85 }
86
87 #[must_use]
89 pub fn with_application_subnet(mut self) -> Self {
90 self.0 = self.0.with_application_subnet();
91 self
92 }
93
94 #[must_use]
96 pub fn with_ii_subnet(mut self) -> Self {
97 self.0 = self.0.with_ii_subnet();
98 self
99 }
100
101 #[must_use]
103 pub fn with_nns_subnet(mut self) -> Self {
104 self.0 = self.0.with_nns_subnet();
105 self
106 }
107
108 #[must_use]
110 pub fn build(self) -> Pic {
111 self.try_build()
112 .unwrap_or_else(|err| panic!("failed to start PocketIC: {err}"))
113 }
114
115 pub fn try_build(self) -> Result<Pic, PicStartError> {
117 startup::try_build_pic(self.0)
118 }
119}
120pub struct Pic {
129 inner: PocketIc,
130}
131
132impl Pic {
133 pub fn tick(&self) {
135 self.inner.tick();
136 }
137
138 pub fn advance_time(&self, duration: Duration) {
140 self.inner.advance_time(duration);
141 }
142
143 #[must_use]
145 pub fn create_canister(&self) -> Principal {
146 self.inner.create_canister()
147 }
148
149 pub fn add_cycles(&self, canister_id: Principal, amount: u128) {
151 let _ = self.inner.add_cycles(canister_id, amount);
152 }
153
154 pub fn install_canister(
156 &self,
157 canister_id: Principal,
158 wasm_module: Vec<u8>,
159 arg: Vec<u8>,
160 sender: Option<Principal>,
161 ) {
162 self.inner
163 .install_canister(canister_id, wasm_module, arg, sender);
164 }
165
166 pub fn upgrade_canister(
168 &self,
169 canister_id: Principal,
170 wasm_module: Vec<u8>,
171 arg: Vec<u8>,
172 sender: Option<Principal>,
173 ) -> Result<(), RejectResponse> {
174 self.inner
175 .upgrade_canister(canister_id, wasm_module, arg, sender)
176 }
177
178 pub fn reinstall_canister(
180 &self,
181 canister_id: Principal,
182 wasm_module: Vec<u8>,
183 arg: Vec<u8>,
184 sender: Option<Principal>,
185 ) -> Result<(), RejectResponse> {
186 self.inner
187 .reinstall_canister(canister_id, wasm_module, arg, sender)
188 }
189
190 pub fn submit_call(
192 &self,
193 canister_id: Principal,
194 sender: Principal,
195 method: &str,
196 payload: Vec<u8>,
197 ) -> Result<RawMessageId, RejectResponse> {
198 self.inner.submit_call(canister_id, sender, method, payload)
199 }
200
201 pub fn await_call(&self, message_id: RawMessageId) -> Result<Vec<u8>, RejectResponse> {
203 self.inner.await_call(message_id)
204 }
205
206 pub fn canister_status(
208 &self,
209 canister_id: Principal,
210 sender: Option<Principal>,
211 ) -> Result<CanisterStatusResult, RejectResponse> {
212 self.inner.canister_status(canister_id, sender)
213 }
214
215 pub fn fetch_canister_logs(
217 &self,
218 canister_id: Principal,
219 sender: Principal,
220 ) -> Result<Vec<pocket_ic::CanisterLogRecord>, RejectResponse> {
221 self.inner.fetch_canister_logs(canister_id, sender)
222 }
223
224 #[must_use]
226 pub fn current_time_nanos(&self) -> u64 {
227 self.inner.get_time().as_nanos_since_unix_epoch()
228 }
229
230 pub fn restore_time_nanos(&self, nanos_since_epoch: u64) {
232 let restored = pocket_ic::Time::from_nanos_since_unix_epoch(nanos_since_epoch);
233 self.inner.set_time(restored);
234 self.inner.set_certified_time(restored);
235 }
236}
237
238fn install_args(role: CanisterRole) -> Result<Vec<u8>, Error> {
253 if role.is_root() {
254 install_root_args()
255 } else {
256 let env = EnvBootstrapArgs {
259 prime_root_pid: None,
260 subnet_role: None,
261 subnet_pid: None,
262 root_pid: None,
263 canister_role: Some(role),
264 parent_pid: None,
265 };
266
267 let payload = CanisterInitPayload {
270 env,
271 app_index: AppIndexArgs(Vec::new()),
272 subnet_index: SubnetIndexArgs(Vec::new()),
273 };
274
275 encode_args::<(CanisterInitPayload, Option<Vec<u8>>)>((payload, None))
276 .map_err(|err| Error::internal(format!("encode_args failed: {err}")))
277 }
278}
279
280fn install_root_args() -> Result<Vec<u8>, Error> {
281 encode_one(SubnetIdentity::Manual)
282 .map_err(|err| Error::internal(format!("encode_one failed: {err}")))
283}
284
285