Skip to content

Commit

Permalink
feat: custom error panics
Browse files Browse the repository at this point in the history
  • Loading branch information
buffalojoec committed Oct 18, 2024
1 parent 2b973db commit 5a92e17
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 26 deletions.
9 changes: 5 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ solana-program = "2.0"
solana-program-runtime = "2.0"
solana-system-program = "2.0"
solana-sdk = "2.0"
thiserror = "1.0.64"
1 change: 1 addition & 0 deletions harness/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ solana-program-runtime = { workspace = true }
solana-system-program = { workspace = true }
solana-sdk = { workspace = true }
solana-logger = { workspace = true }
thiserror = { workspace = true }

[[bench]]
name = "ips"
Expand Down
46 changes: 46 additions & 0 deletions harness/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! Mollusk errors. These errors will throw a panic. They represent
//! misconfiguration of test inputs or the test environment.

use {
solana_sdk::pubkey::Pubkey,
std::{fmt::Display, path::Path},
thiserror::Error,
};

#[derive(Debug, Error)]
pub enum MolluskError<'a> {
/// Failed to open file.
#[error(" [MOLLUSK]: Failed to open file: {0}")]
FileOpenError(&'a Path),
/// Failed to read file.
#[error(" [MOLLUSK]: Failed to read file: {0}")]
FileReadError(&'a Path),
/// Program file not found.
#[error(" [MOLLUSK]: Program file not found: {0}")]
FileNotFound(&'a str),
/// An account required by the instruction was not provided.
#[error(" [MOLLUSK]: An account required by the instruction was not provided: {0}")]
AccountMissing(&'a Pubkey),
/// Program targeted by the instruction is missing from the cache.
#[error(" [MOLLUSK]: Program targeted by the instruction is missing from the cache: {0}")]
ProgramNotCached(&'a Pubkey),
}

pub trait MolluskPanic<T> {
fn or_panic_with(self, error: MolluskError) -> T;
}

impl<T, E> MolluskPanic<T> for Result<T, E>
where
E: Display,
{
fn or_panic_with(self, mollusk_err: MolluskError) -> T {
self.unwrap_or_else(|err| panic!("{}: {}", mollusk_err, err))
}
}

impl<T> MolluskPanic<T> for Option<T> {
fn or_panic_with(self, mollusk_err: MolluskError) -> T {
self.unwrap_or_else(|| panic!("{}", mollusk_err))
}
}
19 changes: 10 additions & 9 deletions harness/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@
//! purposes, most of them will panic if the file is not found or if there is an
//! error reading the file.

use std::{
fs::File,
io::Read,
path::{Path, PathBuf},
use {
crate::error::{MolluskError, MolluskPanic},
std::{
fs::File,
io::Read,
path::{Path, PathBuf},
},
};

fn default_shared_object_dirs() -> Vec<PathBuf> {
Expand Down Expand Up @@ -55,12 +58,11 @@ fn find_file(filename: &str) -> Option<PathBuf> {
/// Read the contents of a file into a `Vec<u8>`.
pub fn read_file<P: AsRef<Path>>(path: P) -> Vec<u8> {
let path = path.as_ref();
let mut file = File::open(path)
.unwrap_or_else(|err| panic!("Failed to open \"{}\": {}", path.display(), err));
let mut file = File::open(path).or_panic_with(MolluskError::FileOpenError(path));

let mut file_data = Vec::new();
file.read_to_end(&mut file_data)
.unwrap_or_else(|err| panic!("Failed to read \"{}\": {}", path.display(), err));
.or_panic_with(MolluskError::FileReadError(path));
file_data
}

Expand All @@ -77,7 +79,6 @@ pub fn read_file<P: AsRef<Path>>(path: P) -> Vec<u8> {
/// The name of the program ELF file is expected to be `{program_name}.so`.
pub fn load_program_elf(program_name: &str) -> Vec<u8> {
let file_name = format!("{program_name}.so");
let program_file = find_file(&file_name)
.unwrap_or_else(|| panic!("Program file data not available for \"{}\"", file_name,));
let program_file = find_file(&file_name).or_panic_with(MolluskError::FileNotFound(&file_name));
read_file(program_file)
}
9 changes: 2 additions & 7 deletions harness/src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
//! <https://github.com/anza-xyz/agave/blob/c6e8239843af8e6301cd198e39d0a44add427bef/sdk/program/src/message/legacy.rs#L357>.

use {
crate::error::{MolluskError, MolluskPanic},
solana_sdk::{
account::{AccountSharedData, WritableAccount},
instruction::Instruction,
Expand Down Expand Up @@ -141,13 +142,7 @@ pub fn compile_accounts(
.iter()
.find(|(k, _)| k == *key)
.map(|(_, account)| account.clone())
.unwrap_or_else(|| {
panic!(
" [mollusk]: An account required by the instruction was not \
provided: {:?}",
key,
)
});
.or_panic_with(MolluskError::AccountMissing(key));
(**key, account)
}
})
Expand Down
9 changes: 3 additions & 6 deletions harness/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
//! * `process_and_validate_instruction`: Process an instruction and perform a
//! series of checks on the result, panicking if any checks fail.

mod error;
pub mod file;
mod keys;
pub mod program;
Expand All @@ -35,6 +36,7 @@ pub mod sysvar;

use {
crate::{
error::{MolluskError, MolluskPanic},
program::ProgramCache,
result::{Check, InstructionResult},
sysvar::Sysvars,
Expand Down Expand Up @@ -145,12 +147,7 @@ impl Mollusk {
let loader_key = self
.program_cache
.load_program(&instruction.program_id)
.unwrap_or_else(|| {
panic!(
" [mollusk]: Program targeted by instruction is missing from cache: {:?}",
instruction.program_id,
)
})
.or_panic_with(MolluskError::ProgramNotCached(&instruction.program_id))
.account_owner();

let CompiledAccounts {
Expand Down

0 comments on commit 5a92e17

Please sign in to comment.