This repository has been archived by the owner on Feb 11, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 25
#[drink::test] macro #70
Merged
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
cee4cd1
Create new crate for a procedural macro. Bump version
pmikolajczyk41 d060fad
macro flow
pmikolajczyk41 484f6dd
Config tests
pmikolajczyk41 e1414c9
Export macro, use it in tests
pmikolajczyk41 f3e7ecd
Finish work on macro side
pmikolajczyk41 470ec9c
use workspace deps
pmikolajczyk41 681f9b2
minor bump
pmikolajczyk41 0eb96dd
Documentation
pmikolajczyk41 6798296
warning
pmikolajczyk41 36766aa
Merge branch 'main' into drink-macro
pmikolajczyk41 0f8bd70
Prepare for compile-time contract building
pmikolajczyk41 09dcef0
Move to compile time in narration
pmikolajczyk41 3dde972
Merge branch 'main' into drink-macro
pmikolajczyk41 04a877c
Minor corrections
pmikolajczyk41 350367b
Build contract
pmikolajczyk41 f18b654
Build once
pmikolajczyk41 b2230fd
No need to run cargo test in CI
pmikolajczyk41 8268421
Merge remote-tracking branch 'origin/main' into drink-macro
pmikolajczyk41 60bf331
lint
pmikolajczyk41 8b7de41
rust-src is still needed
pmikolajczyk41 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
[package] | ||
name = "drink-test-macro" | ||
authors.workspace = true | ||
edition.workspace = true | ||
homepage.workspace = true | ||
license.workspace = true | ||
readme.workspace = true | ||
repository.workspace = true | ||
version.workspace = true | ||
description = "Procedural macro providing a `#[drink::test]` attribute for `drink`-based contract testing" | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] | ||
cargo_metadata = { workspace = true } | ||
contract-build = { workspace = true } | ||
contract-metadata = { workspace = true } | ||
proc-macro2 = { workspace = true } | ||
syn = { workspace = true, features = ["full"] } | ||
quote = { workspace = true } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
use std::{ | ||
collections::HashSet, | ||
path::PathBuf, | ||
sync::{Mutex, OnceLock}, | ||
}; | ||
|
||
use cargo_metadata::{Metadata, MetadataCommand, Package}; | ||
use contract_build::{ | ||
BuildArtifacts, BuildMode, ExecuteArgs, Features, ManifestPath, Network, OptimizationPasses, | ||
OutputType, Target, UnstableFlags, Verbosity, | ||
}; | ||
|
||
/// Contract package differentiator. | ||
const INK_AS_DEPENDENCY_FEATURE: &str = "ink-as-dependency"; | ||
|
||
/// Stores the manifest paths of all contracts that have already been built. | ||
/// | ||
/// This prevents from building the same contract for every testcase separately. | ||
static CONTRACTS_BUILT: OnceLock<Mutex<HashSet<PathBuf>>> = OnceLock::new(); | ||
|
||
/// Build the current package with `cargo contract build --release` (if it is a contract package), | ||
/// as well as all its contract dependencies. | ||
/// | ||
/// A package is considered as a contract package, if it has the `ink-as-dependency` feature. | ||
/// | ||
/// A contract dependency, is a package defined in the `Cargo.toml` file with the | ||
/// `ink-as-dependency` feature enabled. | ||
pub fn build_contracts() { | ||
let metadata = MetadataCommand::new() | ||
.exec() | ||
.expect("Error invoking `cargo metadata`"); | ||
|
||
for contract_crate in get_contract_crates(&metadata) { | ||
build_contract_crate(contract_crate); | ||
} | ||
} | ||
|
||
fn get_contract_crates(metadata: &Metadata) -> Vec<&Package> { | ||
let pkg_lookup = |id| { | ||
metadata | ||
.packages | ||
.iter() | ||
.find(|package| package.id == id) | ||
.unwrap_or_else(|| panic!("Error resolving package {id}")) | ||
}; | ||
|
||
let dep_graph = metadata | ||
.resolve | ||
.as_ref() | ||
.expect("Error resolving dependencies"); | ||
|
||
let contract_deps = dep_graph | ||
.nodes | ||
.iter() | ||
.filter_map(|node| { | ||
node.features | ||
.contains(&INK_AS_DEPENDENCY_FEATURE.to_string()) | ||
.then(|| node.id.clone()) | ||
}) | ||
.map(pkg_lookup); | ||
|
||
let root = dep_graph | ||
.root | ||
.as_ref() | ||
.expect("Error resolving root package"); | ||
let root = pkg_lookup(root.clone()); | ||
|
||
root.features | ||
.contains_key(INK_AS_DEPENDENCY_FEATURE) | ||
.then_some(root) | ||
.into_iter() | ||
.chain(contract_deps) | ||
.collect() | ||
} | ||
|
||
fn build_contract_crate(pkg: &Package) { | ||
let manifest_path = get_manifest_path(pkg); | ||
|
||
if !CONTRACTS_BUILT | ||
.get_or_init(|| Mutex::new(HashSet::new())) | ||
.lock() | ||
.expect("Error locking mutex") | ||
.insert(manifest_path.clone().into()) | ||
{ | ||
return; | ||
} | ||
|
||
let args = ExecuteArgs { | ||
manifest_path, | ||
verbosity: Verbosity::Default, | ||
build_mode: BuildMode::Release, | ||
features: Features::default(), | ||
network: Network::Online, | ||
build_artifact: BuildArtifacts::All, | ||
unstable_flags: UnstableFlags::default(), | ||
optimization_passes: Some(OptimizationPasses::default()), | ||
keep_debug_symbols: false, | ||
lint: false, | ||
output_type: OutputType::HumanReadable, | ||
skip_wasm_validation: false, | ||
target: Target::Wasm, | ||
}; | ||
|
||
contract_build::execute(args).expect("Error building contract"); | ||
} | ||
|
||
fn get_manifest_path(package: &Package) -> ManifestPath { | ||
ManifestPath::new(package.manifest_path.clone().into_std_path_buf()) | ||
.unwrap_or_else(|_| panic!("Error resolving manifest path for package {}", package.name)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
//! Procedural macro providing a `#[drink::test]` attribute for `drink`-based contract testing. | ||
|
||
#![warn(missing_docs)] | ||
|
||
mod contract_building; | ||
|
||
use proc_macro::TokenStream; | ||
use proc_macro2::TokenStream as TokenStream2; | ||
use quote::quote; | ||
use syn::ItemFn; | ||
|
||
use crate::contract_building::build_contracts; | ||
|
||
type SynResult<T> = Result<T, syn::Error>; | ||
|
||
/// Defines a drink!-based test. | ||
/// | ||
/// # Requirements | ||
/// | ||
/// - 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. | ||
/// | ||
/// # Impact | ||
/// | ||
/// This macro will take care of building all needed contracts for the test. The building process | ||
/// will be executed during compile time. | ||
/// | ||
/// Contracts to be built: | ||
/// - current cargo package if contains a `ink-as-dependency` feature | ||
/// - all dependencies declared in the `Cargo.toml` file with the `ink-as-dependency` feature | ||
/// enabled | ||
/// | ||
/// Note: Depending on a non-local contract is not tested yet. | ||
/// | ||
/// # Example | ||
/// | ||
/// ```rust, ignore | ||
/// #[drink::test] | ||
/// fn testcase() { | ||
/// Session::<MinimalRuntime>::new() | ||
/// .unwrap() | ||
/// .deploy(bytes(), "new", NO_ARGS, vec![], None, &transcoder()) | ||
/// .unwrap(); | ||
/// } | ||
/// ``` | ||
#[proc_macro_attribute] | ||
pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream { | ||
match test_internal(attr.into(), item.into()) { | ||
Ok(ts) => ts.into(), | ||
Err(e) => e.to_compile_error().into(), | ||
} | ||
} | ||
|
||
/// Auxiliary function to enter ?-based error propagation. | ||
fn test_internal(_attr: TokenStream2, item: TokenStream2) -> SynResult<TokenStream2> { | ||
let item_fn = syn::parse2::<ItemFn>(item)?; | ||
build_contracts(); | ||
|
||
Ok(quote! { | ||
#[test] | ||
#item_fn | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering - some of these params are definable in
Cargo.toml
- especially for the root package - and here we're "overwriting" them. That's probably fine since we are "optimising for drink!" but just something to be aware of.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#72