Skip to main content

canic_testkit/pic/
diagnostics.rs

1use candid::Principal;
2use canic::{Error, protocol};
3
4use super::{Pic, startup};
5
6impl Pic {
7    /// Dump basic PocketIC status and log context for one canister.
8    pub fn dump_canister_debug(&self, canister_id: Principal, context: &str) {
9        eprintln!("{context}: debug for canister {canister_id}");
10
11        match self.canister_status(canister_id, None) {
12            Ok(status) => eprintln!("canister_status: {status:?}"),
13            Err(err) => {
14                let message = err.to_string();
15                if startup::is_dead_instance_transport_error(&message) {
16                    eprintln!("canister_status unavailable: PocketIC instance no longer reachable");
17                    return;
18                }
19                eprintln!("canister_status failed: {err:?}");
20            }
21        }
22
23        match self.fetch_canister_logs(canister_id, Principal::anonymous()) {
24            Ok(records) => {
25                if records.is_empty() {
26                    eprintln!("canister logs: <empty>");
27                } else {
28                    for record in records {
29                        eprintln!("canister log: {record:?}");
30                    }
31                }
32            }
33            Err(err) => {
34                let message = err.to_string();
35                if startup::is_dead_instance_transport_error(&message) {
36                    eprintln!(
37                        "fetch_canister_logs unavailable: PocketIC instance no longer reachable"
38                    );
39                    return;
40                }
41                eprintln!("fetch_canister_logs failed: {err:?}");
42            }
43        }
44    }
45
46    // Query `canic_ready` and panic with debug context on transport failures.
47    pub(super) fn fetch_ready(&self, canister_id: Principal) -> bool {
48        match self.query_call(canister_id, protocol::CANIC_READY, ()) {
49            Ok(ready) => ready,
50            Err(err) => {
51                self.debug_or_panic_dead_instance(canister_id, "query canic_ready failed", &err)
52            }
53        }
54    }
55
56    // Fail fast once the PocketIC instance itself is gone.
57    fn panic_dead_instance_transport(context: &str, err: &Error) -> ! {
58        panic!("{context}: PocketIC instance no longer reachable: {err}");
59    }
60
61    // Emit local debug when possible, but avoid cascading debug failures on a dead instance.
62    fn debug_or_panic_dead_instance(
63        &self,
64        canister_id: Principal,
65        context: &str,
66        err: &Error,
67    ) -> ! {
68        if startup::is_dead_instance_transport_error(&err.to_string()) {
69            Self::panic_dead_instance_transport(context, err);
70        }
71
72        self.dump_canister_debug(canister_id, context);
73        panic!("{context}: {err:?}");
74    }
75}