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 exec_build_contracts = quote! {
59            ::ink_e2e::build_root_and_contract_dependencies()
60        };
61
62        let client_building = match self.test.config.backend() {
63            Backend::Node(node_config) => {
64                build_full_client(&environment, exec_build_contracts, node_config)
65            }
66            #[cfg(any(test, feature = "sandbox"))]
67            Backend::RuntimeOnly(runtime) => {
68                build_runtime_client(exec_build_contracts, runtime.into())
69            }
70        };
71
72        quote! {
73            #( #attrs )*
74            #[test]
75            #vis fn #fn_name () #ret {
76                use ::ink_e2e::log_info;
77                ::ink_e2e::LOG_PREFIX.with(|log_prefix| {
78                    let str = format!("test: {}", stringify!(#fn_name));
79                    *log_prefix.borrow_mut() = String::from(str);
80                });
81                log_info("setting up e2e test");
82
83                ::ink_e2e::INIT.call_once(|| {
84                    ::ink_e2e::tracing_subscriber::fmt::init();
85                });
86
87                log_info("creating new client");
88
89                let run = async {
90                    #client_building
91
92                    let __ret = {
93                        #block
94                    };
95                    __ret
96                };
97
98                {
99                    return ::ink_e2e::tokio::runtime::Builder::new_current_thread()
100                        .enable_all()
101                        .build()
102                        .unwrap_or_else(|err| panic!("Failed building the Runtime: {err}"))
103                        .block_on(run);
104                }
105            }
106        }
107    }
108}
109
110fn build_full_client(
111    environment: &syn::Path,
112    contracts: TokenStream2,
113    node_config: Node,
114) -> TokenStream2 {
115    match node_config.url() {
116        Some(url) => {
117            quote! {
118                let contracts = #contracts;
119                let rpc = ::ink_e2e::RpcClient::from_url(#url)
120                    .await
121                    .unwrap_or_else(|err|
122                        ::core::panic!("Error connecting to node at {}: {err:?}", #url)
123                    );
124                let mut client = ::ink_e2e::Client::<
125                    ::ink_e2e::PolkadotConfig,
126                    #environment
127                >::new(rpc, contracts, #url.to_string()).await?;
128            }
129        }
130        None => {
131            quote! {
132                let contracts = #contracts;
133                let node_rpc = ::ink_e2e::TestNodeProcess::<::ink_e2e::PolkadotConfig>
134                    ::build_with_env_or_default()
135                    .spawn()
136                    .await
137                    .unwrap_or_else(|err|
138                        ::core::panic!("Error spawning ink-node: {err:?}")
139                    );
140                let mut client = ::ink_e2e::Client::<
141                    ::ink_e2e::PolkadotConfig,
142                    #environment
143                >::new(node_rpc.rpc(), contracts, node_rpc.url().to_string()).await?;
144            }
145        }
146    }
147}
148
149#[cfg(any(test, feature = "sandbox"))]
150fn build_runtime_client(contracts: TokenStream2, runtime: syn::Path) -> TokenStream2 {
151    quote! {
152        let contracts = #contracts;
153        let mut client = ::ink_e2e::SandboxClient::<_, #runtime>::new(contracts);
154    }
155}