program-test: Set context without panic (#14997)

* program-test: Fix CPI and multiple instructions

* Whitespace

* Add CPI test in program-test
This commit is contained in:
Jon Cinque 2021-02-03 13:45:29 +01:00 committed by GitHub
parent 286e4d6924
commit 4324374ab5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 8 deletions

View File

@ -71,13 +71,11 @@ pub fn to_instruction_error(error: ProgramError) -> InstructionError {
} }
thread_local! { thread_local! {
static INVOKE_CONTEXT:RefCell<Option<(usize, usize)>> = RefCell::new(None); static INVOKE_CONTEXT: RefCell<Option<(usize, usize)>> = RefCell::new(None);
} }
fn set_invoke_context(new: &mut dyn InvokeContext) { fn set_invoke_context(new: &mut dyn InvokeContext) {
INVOKE_CONTEXT.with(|invoke_context| { INVOKE_CONTEXT.with(|invoke_context| unsafe {
if unsafe { invoke_context.replace(Some(transmute::<_, (usize, usize)>(new))) }.is_some() { invoke_context.replace(Some(transmute::<_, (usize, usize)>(new)))
panic!("Overwiting invoke context!")
}
}); });
} }
fn get_invoke_context<'a>() -> &'a mut dyn InvokeContext { fn get_invoke_context<'a>() -> &'a mut dyn InvokeContext {

79
program-test/tests/cpi.rs Normal file
View File

@ -0,0 +1,79 @@
use {
solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
instruction::{AccountMeta, Instruction},
msg,
program::invoke,
pubkey::Pubkey,
},
solana_program_test::{processor, ProgramTest},
solana_sdk::{signature::Signer, transaction::Transaction},
};
// Process instruction to invoke into another program
fn invoker_process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
_input: &[u8],
) -> ProgramResult {
// if we can call `msg!` successfully, then InvokeContext exists as required
msg!("Processing invoker instruction before CPI");
let account_info_iter = &mut accounts.iter();
let invoked_program_info = next_account_info(account_info_iter)?;
invoke(
&Instruction::new(*invoked_program_info.key, &[0], vec![]),
&[invoked_program_info.clone()],
)?;
msg!("Processing invoker instruction after CPI");
Ok(())
}
// Process instruction to be invoked by another program
#[allow(clippy::unnecessary_wraps)]
fn invoked_process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_input: &[u8],
) -> ProgramResult {
// if we can call `msg!` successfully, then InvokeContext exists as required
msg!("Processing invoked instruction");
Ok(())
}
#[tokio::test]
async fn cpi() {
let invoker_program_id = Pubkey::new_unique();
// Initialize and start the test network
let mut program_test = ProgramTest::new(
"program-test-fuzz-invoker",
invoker_program_id,
processor!(invoker_process_instruction),
);
let invoked_program_id = Pubkey::new_unique();
program_test.add_program(
"program-test-fuzz-invoked",
invoked_program_id,
processor!(invoked_process_instruction),
);
let mut test_state = program_test.start_with_context().await;
let instructions = vec![Instruction::new(
invoker_program_id,
&[0],
vec![AccountMeta::new_readonly(invoked_program_id, false)],
)];
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&test_state.payer.pubkey()),
&[&test_state.payer],
test_state.last_blockhash,
);
test_state
.banks_client
.process_transaction(transaction)
.await
.unwrap();
}

View File

@ -1,8 +1,8 @@
use { use {
solana_banks_client::BanksClient, solana_banks_client::BanksClient,
solana_program::{ solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, hash::Hash, pubkey::Pubkey, account_info::AccountInfo, entrypoint::ProgramResult, hash::Hash, instruction::Instruction,
rent::Rent, msg, pubkey::Pubkey, rent::Rent,
}, },
solana_program_test::{processor, ProgramTest}, solana_program_test::{processor, ProgramTest},
solana_sdk::{ solana_sdk::{
@ -10,13 +10,14 @@ use {
}, },
}; };
// Dummy process instruction required to instantiate ProgramTest
#[allow(clippy::unnecessary_wraps)] #[allow(clippy::unnecessary_wraps)]
fn process_instruction( fn process_instruction(
_program_id: &Pubkey, _program_id: &Pubkey,
_accounts: &[AccountInfo], _accounts: &[AccountInfo],
_input: &[u8], _input: &[u8],
) -> ProgramResult { ) -> ProgramResult {
// if we can call `msg!` successfully, then InvokeContext exists as required
msg!("Processing instruction");
Ok(()) Ok(())
} }
@ -94,6 +95,7 @@ async fn run_fuzz_instructions(
program_id, program_id,
); );
instructions.push(instruction); instructions.push(instruction);
instructions.push(Instruction::new(*program_id, &[0], vec![]));
signer_keypairs.push(keypair); signer_keypairs.push(keypair);
} }
// Process transaction on test network // Process transaction on test network