use candid::Principal;
use pocket_ic::{
CanisterStatusResult, PocketIc, PocketIcBuilder, RejectResponse, common::rest::RawMessageId,
};
use std::time::Duration;
mod baseline;
mod calls;
mod diagnostics;
mod errors;
mod lifecycle;
mod process_lock;
mod runtime;
mod snapshot;
mod standalone;
mod startup;
pub use baseline::{
CachedPicBaseline, CachedPicBaselineGuard, ControllerSnapshots,
restore_or_rebuild_cached_pic_baseline,
};
pub use errors::{PicCallError, PicInstallError, StandaloneCanisterFixtureError};
pub use process_lock::{
PicSerialGuard, PicSerialGuardError, acquire_pic_serial_guard, try_acquire_pic_serial_guard,
};
pub use runtime::PicRuntimeConfig;
pub use startup::PicStartError;
pub use standalone::{
StandaloneCanisterFixture, install_prebuilt_canister, install_prebuilt_canister_with_cycles,
try_install_prebuilt_canister, try_install_prebuilt_canister_with_cycles,
};
#[must_use]
pub fn pic() -> Pic {
try_pic().unwrap_or_else(|err| panic!("failed to start PocketIC: {err}"))
}
pub fn try_pic() -> Result<Pic, PicStartError> {
PicBuilder::new().with_application_subnet().try_build()
}
#[must_use]
pub fn ensure_pocket_ic_bin() -> std::path::PathBuf {
try_ensure_pocket_ic_bin()
.unwrap_or_else(|err| panic!("failed to resolve PocketIC server binary: {err}"))
}
pub fn try_ensure_pocket_ic_bin() -> Result<std::path::PathBuf, PicStartError> {
runtime::ensure_pocket_ic_bin_from_env()
}
pub struct PicBuilder {
inner: PocketIcBuilder,
runtime_config: PicRuntimeConfig,
}
#[expect(clippy::new_without_default)]
impl PicBuilder {
#[must_use]
pub fn new() -> Self {
Self {
inner: PocketIcBuilder::new(),
runtime_config: PicRuntimeConfig::from_env(),
}
}
#[must_use]
pub fn with_runtime_config(mut self, runtime_config: PicRuntimeConfig) -> Self {
self.runtime_config = runtime_config;
self
}
#[must_use]
pub fn with_server_binary(mut self, server_binary: impl Into<std::path::PathBuf>) -> Self {
self.runtime_config = self.runtime_config.pocket_ic_bin(server_binary);
self
}
#[must_use]
pub fn with_application_subnet(mut self) -> Self {
self.inner = self.inner.with_application_subnet();
self
}
#[must_use]
pub fn with_ii_subnet(mut self) -> Self {
self.inner = self.inner.with_ii_subnet();
self
}
#[must_use]
pub fn with_nns_subnet(mut self) -> Self {
self.inner = self.inner.with_nns_subnet();
self
}
#[must_use]
pub fn build(self) -> Pic {
self.try_build()
.unwrap_or_else(|err| panic!("failed to start PocketIC: {err}"))
}
pub fn try_build(self) -> Result<Pic, PicStartError> {
let server_binary = self.runtime_config.ensure_binary()?;
startup::try_build_pic(self.inner.with_server_binary(server_binary))
}
}
pub struct Pic {
inner: PocketIc,
}
impl Pic {
pub fn tick(&self) {
self.inner.tick();
}
pub fn advance_time(&self, duration: Duration) {
self.inner.advance_time(duration);
}
#[must_use]
pub fn create_canister(&self) -> Principal {
self.inner.create_canister()
}
pub fn add_cycles(&self, canister_id: Principal, amount: u128) {
let _ = self.inner.add_cycles(canister_id, amount);
}
pub fn install_canister(
&self,
canister_id: Principal,
wasm_module: Vec<u8>,
arg: Vec<u8>,
sender: Option<Principal>,
) {
self.inner
.install_canister(canister_id, wasm_module, arg, sender);
}
pub fn upgrade_canister(
&self,
canister_id: Principal,
wasm_module: Vec<u8>,
arg: Vec<u8>,
sender: Option<Principal>,
) -> Result<(), RejectResponse> {
self.inner
.upgrade_canister(canister_id, wasm_module, arg, sender)
}
pub fn reinstall_canister(
&self,
canister_id: Principal,
wasm_module: Vec<u8>,
arg: Vec<u8>,
sender: Option<Principal>,
) -> Result<(), RejectResponse> {
self.inner
.reinstall_canister(canister_id, wasm_module, arg, sender)
}
pub fn submit_call(
&self,
canister_id: Principal,
sender: Principal,
method: &str,
payload: Vec<u8>,
) -> Result<RawMessageId, RejectResponse> {
self.inner.submit_call(canister_id, sender, method, payload)
}
pub fn await_call(&self, message_id: RawMessageId) -> Result<Vec<u8>, RejectResponse> {
self.inner.await_call(message_id)
}
pub fn canister_status(
&self,
canister_id: Principal,
sender: Option<Principal>,
) -> Result<CanisterStatusResult, RejectResponse> {
self.inner.canister_status(canister_id, sender)
}
pub fn fetch_canister_logs(
&self,
canister_id: Principal,
sender: Principal,
) -> Result<Vec<pocket_ic::CanisterLogRecord>, RejectResponse> {
self.inner.fetch_canister_logs(canister_id, sender)
}
#[must_use]
pub fn current_time_nanos(&self) -> u64 {
self.inner.get_time().as_nanos_since_unix_epoch()
}
pub fn restore_time_nanos(&self, nanos_since_epoch: u64) {
let restored = pocket_ic::Time::from_nanos_since_unix_epoch(nanos_since_epoch);
self.inner.set_time(restored);
self.inner.set_certified_time(restored);
}
}