ink_e2e_macro/
codegen.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::{
16    config::{
17        Backend,
18        Node,
19    },
20    ir,
21};
22use derive_more::From;
23use proc_macro2::TokenStream as TokenStream2;
24use quote::quote;
25
26/// Generates code for the `[ink_e2e::test]` macro.
27#[derive(From)]
28pub struct InkE2ETest {
29    /// The test function to generate code for.
30    test: ir::InkE2ETest,
31}
32
33impl InkE2ETest {
34    /// Generates the code for `#[ink_e2e:test]`.
35    pub fn generate_code(&self) -> TokenStream2 {
36        #[cfg(clippy)]
37        if true {
38            return quote! {};
39        }
40
41        let item_fn = &self.test.item_fn.item_fn;
42        let fn_name = &item_fn.sig.ident;
43        let block = &item_fn.block;
44        let fn_return_type = &item_fn.sig.output;
45        let vis = &item_fn.vis;
46        let attrs = &item_fn.attrs;
47        let ret = match fn_return_type {
48            syn::ReturnType::Default => quote! {},
49            syn::ReturnType::Type(rarrow, ret_type) => quote! { #rarrow #ret_type },
50        };
51
52        let environment = self
53            .test
54            .config
55            .environment()
56            .unwrap_or_else(|| syn::parse_quote! { ::ink::env::DefaultEnvironment });
57
58        let features = self.test.config.features();
59        let exec_build_contracts = quote! {
60            ::ink_e2e::build_root_and_contract_dependencies(
61                vec![#( #features.to_string() ),*]
62            )
63        };
64
65        let client_building = match self.test.config.backend() {
66            Backend::Node(node_config) => {
67                build_full_client(&environment, exec_build_contracts, node_config)
68            }
69            #[cfg(any(test, feature = "sandbox"))]
70            Backend::RuntimeOnly(runtime) => {
71                build_runtime_client(exec_build_contracts, runtime.into())
72            }
73        };
74
75        quote! {
76            #( #attrs )*
77            #[test]
78            #vis fn #fn_name () #ret {
79                use ::ink_e2e::log_info;
80                ::ink_e2e::LOG_PREFIX.with(|log_prefix| {
81                    let str = format!("test: {}", stringify!(#fn_name));
82                    *log_prefix.borrow_mut() = String::from(str);
83                });
84                log_info("setting up e2e test");
85
86                ::ink_e2e::INIT.call_once(|| {
87                    ::ink_e2e::tracing_subscriber::fmt::init();
88                });
89
90                log_info("creating new client");
91
92                let run = async {
93                    #client_building
94
95                    let __ret = {
96                        #block
97                    };
98                    __ret
99                };
100
101                {
102                    return ::ink_e2e::tokio::runtime::Builder::new_current_thread()
103                        .enable_all()
104                        .build()
105                        .unwrap_or_else(|err| panic!("Failed building the Runtime: {err}"))
106                        .block_on(run);
107                }
108            }
109        }
110    }
111}
112
113fn build_full_client(
114    environment: &syn::Path,
115    contracts: TokenStream2,
116    node_config: Node,
117) -> TokenStream2 {
118    match node_config.url() {
119        Some(url) => {
120            quote! {
121                let contracts = #contracts;
122                let rpc = ::ink_e2e::RpcClient::from_url(#url)
123                    .await
124                    .unwrap_or_else(|err|
125                        ::core::panic!("Error connecting to node at {}: {err:?}", #url)
126                    );
127                let mut client = ::ink_e2e::Client::<
128                    ::ink_e2e::PolkadotConfig,
129                    #environment
130                >::new(rpc, contracts, #url.to_string()).await?;
131            }
132        }
133        None => {
134            quote! {
135                let contracts = #contracts;
136                let node_rpc = ::ink_e2e::TestNodeProcess::<::ink_e2e::PolkadotConfig>
137                    ::build_with_env_or_default()
138                    .spawn()
139                    .await
140                    .unwrap_or_else(|err|
141                        ::core::panic!("Error spawning ink-node: {err:?}")
142                    );
143                let mut client = ::ink_e2e::Client::<
144                    ::ink_e2e::PolkadotConfig,
145                    #environment
146                >::new(node_rpc.rpc(), contracts, node_rpc.url().to_string()).await?;
147            }
148        }
149    }
150}
151
152#[cfg(any(test, feature = "sandbox"))]
153fn build_runtime_client(contracts: TokenStream2, runtime: syn::Path) -> TokenStream2 {
154    quote! {
155        let contracts = #contracts;
156        let mut client = ::ink_e2e::SandboxClient::<_, #runtime>::new(contracts);
157    }
158}