Skip to content
This repository has been archived by the owner on Feb 11, 2025. It is now read-only.

Commit

Permalink
drink::test creates a session object (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
pmikolajczyk41 authored Jan 22, 2024
1 parent fab9dc7 commit a89802f
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 58 deletions.
52 changes: 44 additions & 8 deletions Cargo.lock

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

7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ homepage = "https://github.com/Cardinal-Cryptography/drink"
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/Cardinal-Cryptography/drink"
version = "0.8.5"
version = "0.8.6"

[workspace.dependencies]
anyhow = { version = "1.0.71" }
Expand All @@ -29,6 +29,7 @@ contract-metadata = { version = "4.0.0-rc.1" }
contract-transcode = { version = "4.0.0-rc.1" }
convert_case = { version = "0.6.0" }
crossterm = { version = "0.26.0" }
darling = { version = "0.20.3" }
parity-scale-codec = { version = "3.4" }
parity-scale-codec-derive = { version = "3.4" }
proc-macro2 = { version = "1" }
Expand Down Expand Up @@ -56,5 +57,5 @@ sp-runtime-interface = { version = "19.0.0" }

# Local dependencies

drink = { version = "0.8.5", path = "drink" }
drink-test-macro = { version = "0.8.5", path = "drink/test-macro" }
drink = { version = "0.8.6", path = "drink" }
drink-test-macro = { version = "0.8.6", path = "drink/test-macro" }
1 change: 1 addition & 0 deletions drink/test-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ cargo_metadata = { workspace = true }
contract-build = { workspace = true }
contract-metadata = { workspace = true }
convert_case = { workspace = true }
darling = { workspace = true }
proc-macro2 = { workspace = true }
syn = { workspace = true, features = ["full"] }
quote = { workspace = true }
47 changes: 41 additions & 6 deletions drink/test-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
mod bundle_provision;
mod contract_building;

use darling::{ast::NestedMeta, FromMeta};
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
Expand All @@ -18,6 +19,7 @@ type SynResult<T> = Result<T, syn::Error>;
///
/// # Requirements
///
/// - Your crate must have `drink` in its dependencies (and it shouldn't be renamed).
/// - You mustn't import `drink::test` in the scope, where the macro is used. In other words, you
/// should always use the macro only with a qualified path `#[drink::test]`.
/// - Your crate cannot be part of a cargo workspace.
Expand All @@ -34,14 +36,22 @@ type SynResult<T> = Result<T, syn::Error>;
///
/// Note: Depending on a non-local contract is not tested yet.
///
/// # Creating a session object
///
/// The macro will also create a new mutable session object and pass it to the decorated function by value. You can
/// configure which runtime should be used (by specifying a path to a type implementing
/// `drink::runtime::RuntimeWithContracts` trait. Thus, your testcase function should accept a single argument:
/// `mut session: Session<_>`.
///
/// By default, the macro will use `drink::runtime::MinimalRuntime`.
///
/// # Example
///
/// ```rust, ignore
/// #[drink::test]
/// fn testcase() {
/// Session::<MinimalRuntime>::new()
/// .unwrap()
/// .deploy(bytes(), "new", NO_ARGS, NO_SALT, NO_ENDOWMENT, &transcoder())
/// fn testcase(mut session: Session<MinimalRuntime>) {
/// session
/// .deploy_bundle(&get_bundle(), "new", NO_ARGS, NO_SALT, NO_ENDOWMENT)
/// .unwrap();
/// }
/// ```
Expand All @@ -53,14 +63,39 @@ pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream {
}
}

#[derive(FromMeta)]
struct TestAttributes {
runtime: Option<syn::Path>,
}

/// Auxiliary function to enter ?-based error propagation.
fn test_internal(_attr: TokenStream2, item: TokenStream2) -> SynResult<TokenStream2> {
fn test_internal(attr: TokenStream2, item: TokenStream2) -> SynResult<TokenStream2> {
let item_fn = syn::parse2::<ItemFn>(item)?;
let macro_args = TestAttributes::from_list(&NestedMeta::parse_meta_list(attr)?)?;

build_contracts();

let fn_vis = item_fn.vis;
let fn_attrs = item_fn.attrs;
let fn_block = item_fn.block;
let fn_name = item_fn.sig.ident;
let fn_async = item_fn.sig.asyncness;
let fn_generics = item_fn.sig.generics;
let fn_output = item_fn.sig.output;
let fn_const = item_fn.sig.constness;
let fn_unsafety = item_fn.sig.unsafety;

let runtime = macro_args
.runtime
.unwrap_or(syn::parse2(quote! { ::drink::runtime::MinimalRuntime })?);

Ok(quote! {
#[test]
#item_fn
#(#fn_attrs)*
#fn_vis #fn_async #fn_const #fn_unsafety fn #fn_name #fn_generics () #fn_output {
let mut session = Session::<#runtime>::new().expect("Failed to create a session");
#fn_block
}
})
}

Expand Down
6 changes: 3 additions & 3 deletions examples/chain-extension/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ mod tests {
);

/// Test that we can call chain extension from ink! contract and get a correct result.
#[drink::test]
fn we_can_test_chain_extension() -> Result<(), Box<dyn std::error::Error>> {
let result: u32 = Session::<RuntimeWithCE>::new()?
#[drink::test(runtime = RuntimeWithCE)]
fn we_can_test_chain_extension(mut session: Session) -> Result<(), Box<dyn std::error::Error>> {
let result: u32 = session
.deploy_bundle_and(
BundleProvider::local()?,
"new",
Expand Down
8 changes: 2 additions & 6 deletions examples/contract-events/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,16 @@ mod flipper {
mod tests {
use std::error::Error;

use drink::{
runtime::MinimalRuntime,
session::{Session, NO_ARGS, NO_ENDOWMENT},
};
use drink::session::{Session, NO_ARGS, NO_ENDOWMENT};

#[drink::contract_bundle_provider]
enum BundleProvider {}

#[drink::test]
fn we_can_inspect_emitted_events() -> Result<(), Box<dyn Error>> {
fn we_can_inspect_emitted_events(mut session: Session) -> Result<(), Box<dyn Error>> {
let bundle = BundleProvider::local()?;

// Firstly, we deploy the contract and call its `flip` method.
let mut session = Session::<MinimalRuntime>::new()?;
session.deploy_bundle(bundle.clone(), "new", &["false"], vec![], NO_ENDOWMENT)?;
session.call("flip", NO_ARGS, NO_ENDOWMENT)??;

Expand Down
8 changes: 2 additions & 6 deletions examples/cross-contract-call-tracing/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,7 @@ mod tests {
use std::{cell::RefCell, error::Error};

use drink::{
runtime::{
pallet_contracts_debugging::{TracingExt, TracingExtT},
MinimalRuntime,
},
runtime::pallet_contracts_debugging::{TracingExt, TracingExtT},
session::{contract_transcode::Value, Session, NO_ARGS, NO_ENDOWMENT},
AccountId32,
};
Expand Down Expand Up @@ -131,8 +128,7 @@ mod tests {
}

#[drink::test]
fn test() -> Result<(), Box<dyn Error>> {
let mut session = Session::<MinimalRuntime>::new()?;
fn test(mut session: Session) -> Result<(), Box<dyn Error>> {
session.set_tracing_extension(TracingExt(Box::new(TestDebugger {})));

let outer_address = session.deploy_bundle(
Expand Down
13 changes: 5 additions & 8 deletions examples/flipper/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,15 @@ mod flipper {
mod tests {
use std::error::Error;

use drink::{
runtime::MinimalRuntime,
session::{Session, NO_ARGS, NO_ENDOWMENT, NO_SALT},
};
use drink::session::{Session, NO_ARGS, NO_ENDOWMENT, NO_SALT};

#[drink::contract_bundle_provider]
enum BundleProvider {}

#[drink::test]
fn initialization() -> Result<(), Box<dyn Error>> {
fn initialization(mut session: Session) -> Result<(), Box<dyn Error>> {
let contract = BundleProvider::local()?;
let init_value: bool = Session::<MinimalRuntime>::new()?
let init_value: bool = session
.deploy_bundle_and(contract, "new", &["true"], NO_SALT, NO_ENDOWMENT)?
.call_and("get", NO_ARGS, NO_ENDOWMENT)?
.record()
Expand All @@ -58,9 +55,9 @@ mod tests {
}

#[drink::test]
fn flipping() -> Result<(), Box<dyn Error>> {
fn flipping(mut session: Session) -> Result<(), Box<dyn Error>> {
let contract = BundleProvider::Flipper.bundle()?;
let init_value: bool = Session::<MinimalRuntime>::new()?
let init_value: bool = session
.deploy_bundle_and(contract, "new", &["true"], NO_SALT, NO_ENDOWMENT)?
.call_and("flip", NO_ARGS, NO_ENDOWMENT)?
.call_and("flip", NO_ARGS, NO_ENDOWMENT)?
Expand Down
5 changes: 1 addition & 4 deletions examples/mocking/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ mod tests {

use drink::{
mock_message,
runtime::MinimalRuntime,
session::{mocking_api::MockingApi, Session, NO_ARGS, NO_ENDOWMENT, NO_SALT},
ContractMock,
};
Expand All @@ -52,9 +51,7 @@ mod tests {
enum BundleProvider {}

#[drink::test]
fn call_mocked_message() -> Result<(), Box<dyn Error>> {
let mut session = Session::<MinimalRuntime>::new()?;

fn call_mocked_message(mut session: Session) -> Result<(), Box<dyn Error>> {
// Firstly, we create the mocked contract.
const RETURN_VALUE: (u8, u8) = (4, 1);
let mocked_contract =
Expand Down
23 changes: 9 additions & 14 deletions examples/quick-start-with-drink/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ mod flipper {
mod tests {
use drink::{
contract_api::decode_debug_buffer,
runtime::MinimalRuntime,
session::{Session, NO_ARGS, NO_ENDOWMENT, NO_SALT},
};

Expand All @@ -70,15 +69,12 @@ mod tests {
/// have to run `cargo contract build` manually for every contract dependency.
///
/// For convenience of using `?` operator, we mark the test function as returning a `Result`.
///
/// `drink::test` will already provide us with a `Session` object. It is a wrapper around a runtime and it exposes
/// a broad API for interacting with it. Session is generic over the runtime type, but usually and by default, we
/// use `MinimalRuntime`, which is a minimalistic runtime that allows using smart contracts.
#[drink::test]
fn deploy_and_call_a_contract() -> Result<(), Box<dyn std::error::Error>> {
// Firstly, we create a `Session` object. It is a wrapper around a runtime and it exposes a
// broad API for interacting with it.
//
// It is generic over the runtime type, but usually, it is sufficient to use
// `MinimalRuntime`, which is a minimalistic runtime that allows using smart contracts.
let mut session = Session::<MinimalRuntime>::new()?;

fn deploy_and_call_a_contract(mut session: Session) -> Result<(), Box<dyn std::error::Error>> {
// Now we get the contract bundle from the `BundleProvider` enum. Since the current crate
// comes with a contract, we can use the `local` method to get the bundle for it.
let contract_bundle = BundleProvider::local()?;
Expand Down Expand Up @@ -122,9 +118,7 @@ mod tests {

/// In this testcase we will see how to get and read debug logs from the contract.
#[drink::test]
fn get_debug_logs() -> Result<(), Box<dyn std::error::Error>> {
// We create a session object as usual and deploy the contract bundle.
let mut session = Session::<MinimalRuntime>::new()?;
fn get_debug_logs(mut session: Session) -> Result<(), Box<dyn std::error::Error>> {
session.deploy_bundle(
BundleProvider::local()?,
"new",
Expand All @@ -150,8 +144,9 @@ mod tests {

/// In this testcase we will see how to work with multiple contracts.
#[drink::test]
fn work_with_multiple_contracts() -> Result<(), Box<dyn std::error::Error>> {
let mut session = Session::<MinimalRuntime>::new()?;
fn work_with_multiple_contracts(
mut session: Session,
) -> Result<(), Box<dyn std::error::Error>> {
let bundle = BundleProvider::local()?;

// We can deploy the same contract multiple times. However, we have to ensure that the
Expand Down

0 comments on commit a89802f

Please sign in to comment.