Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

testing: Prepare light client testing with substrate binary and add subxt-test macro #1507

Merged
merged 44 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
34e9222
testing: Add long running light client flag and cfg aliases
lexnv Feb 29, 2024
518aa8e
testing: Expose clients depending on feature flags
lexnv Feb 29, 2024
ea6f3cc
subxt: Use unstable backend for light client
lexnv Feb 29, 2024
ca36ef6
testing: Disable flaky lightclient tests
lexnv Feb 29, 2024
8fe7e86
ci: Add long runnnig step
lexnv Feb 29, 2024
593fbeb
Revert "subxt: Use unstable backend for light client"
lexnv Feb 29, 2024
1fc1bb4
ci: Long running tests for 60 mins
lexnv Feb 29, 2024
ca60c31
ci: Use 16 cores for light-client testing
lexnv Feb 29, 2024
3c58c5e
ci: Isolate light-client testing to save CI minutes
lexnv Feb 29, 2024
0248343
testing: Retry on Tx::Dropped for lightclinet only
lexnv Mar 1, 2024
8400a0e
testing: Wait for more blocks for the lightclient init
lexnv Mar 1, 2024
d671664
subxt: Use unstable backend for light client
lexnv Feb 29, 2024
318ba5c
testing: Disable legacy RPC tests
lexnv Mar 1, 2024
1a6f1af
testing: Disable sudo and contracts tests
lexnv Mar 1, 2024
332fa72
testing: Retry constructing lightclient on read-proof errors
lexnv Mar 1, 2024
88ccc54
testing: Disable tx dynamic test
lexnv Mar 1, 2024
b6c98c7
proc-macro: Timeout for tests
lexnv Mar 1, 2024
20e75bc
testing: Add timeout 800 seconds
lexnv Mar 1, 2024
c5c7972
proc-macro/tests: Adjust subxt-test proc-macro
lexnv Mar 4, 2024
d3923c8
proc-macro: Rename crate to subxt-test-proc-macro
lexnv Mar 5, 2024
1a64750
Use default subxt-proc-macro timeout
lexnv Mar 5, 2024
4c3226e
light-client: Remove println
lexnv Mar 5, 2024
fbe2a74
subxt: Remove tokio as dependency, use it only for testing
lexnv Mar 5, 2024
30073d3
testing: Chagne default timeout to 6 seconds
lexnv Mar 5, 2024
aa55cea
proc-macro: Add env timeout variable
lexnv Mar 6, 2024
09277fe
ci: Add subxt env var for controling test timeouts
lexnv Mar 6, 2024
5039f1c
tests/tx-retries: Retry on `Non node available` error
lexnv Mar 6, 2024
410aaed
Merge remote-tracking branch 'origin/master' into lenxv/light-client-…
lexnv Mar 26, 2024
d81c70c
testing: Use unstable backend for testing lightclient
lexnv Mar 26, 2024
00c4b78
testing: Remove old lightclient object
lexnv Mar 26, 2024
03da904
testing: Adjust for the new interface
lexnv Mar 26, 2024
292f971
backend/rpc: Allow older version of the initialized event
lexnv Mar 27, 2024
2bc8947
rpc/tests: Check initialized decodes correctly
lexnv Mar 28, 2024
811f70c
ci: Reset workflow
lexnv Mar 28, 2024
2bb8acb
Apply cargo fmt
lexnv Mar 28, 2024
1fdd734
Remove unused dep
lexnv Mar 28, 2024
1bc6eb9
Merge remote-tracking branch 'origin/master' into lexnv/light-client-…
lexnv Mar 28, 2024
5692703
Remove gitmerge old file
lexnv Mar 28, 2024
fd47be1
Remove unused dep
lexnv Mar 28, 2024
be4b34c
rename proc-macro to subxt-test-macro
lexnv Apr 2, 2024
7270da8
tests: Remove txretries for lightclient
lexnv Apr 3, 2024
a62ceb7
tests: Wait for 5 blocks for the lightclient full testing suite
lexnv Apr 3, 2024
7664b56
tests: Group imports
lexnv Apr 8, 2024
510578f
macro: Rename const value
lexnv Apr 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"testing/substrate-runner",
"testing/test-runtime",
"testing/integration-tests",
"testing/integration-tests/proc-macro",
"testing/ui-tests",
"testing/generate-custom-metadata",
"macro",
Expand Down
55 changes: 52 additions & 3 deletions subxt/src/backend/unstable/rpc_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::config::BlockHash;
use crate::{Config, Error};
use derive_where::derive_where;
use futures::{Stream, StreamExt};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::{HashMap, VecDeque};
use std::task::Poll;

Expand Down Expand Up @@ -377,8 +377,7 @@ pub enum FollowEvent<Hash> {
///
/// This is the first event generated by the `follow` subscription
/// and is submitted only once.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Initialized<Hash> {
/// The hashes of the last finalized blocks.
pub finalized_block_hashes: Vec<Hash>,
Expand All @@ -391,6 +390,30 @@ pub struct Initialized<Hash> {
pub finalized_block_runtime: Option<RuntimeEvent>,
}

impl<'de, Hash: Deserialize<'de>> Deserialize<'de> for Initialized<Hash> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
// Custom struct that can deserialize both `finalizedBlockHash` and `finalizedBlockHashes`.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
struct InitializedIR<Hash> {
finalized_block_hashes: Option<Vec<Hash>>,
finalized_block_hash: Option<Hash>,
finalized_block_runtime: Option<RuntimeEvent>,
}

let ir = InitializedIR::deserialize(deserializer)?;
let finalized_block_hashes = ir
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

slightly weird comment; we could actually throw an error if both finalized_block_hash and finalized_block_hashes.

now, we checking finalized_block_hashes first and if that doesn't exist we fallback to finalized_block_hash which is probably fine but if one inserts both by accident it lead to confusing scenarios but fine :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this only exists until we update smoldot :)

.finalized_block_hashes
.or_else(|| ir.finalized_block_hash.map(|hash| vec![hash]))
.ok_or_else(|| serde::de::Error::custom("Missing finalized block hashes"))?;

Ok(Initialized {
finalized_block_hashes,
finalized_block_runtime: ir.finalized_block_runtime,
})
}
}

/// The runtime event generated if the `follow` subscription
/// has set the `with_runtime` flag.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
Expand Down Expand Up @@ -973,4 +996,30 @@ mod test {
let _ = serde_json::from_value::<Foo32>(from_err)
.expect_err("can't deser invalid num into u32");
}

#[test]
fn chain_head_initialized() {
// Latest format version.
let event = serde_json::json!({
"finalizedBlockHashes": ["0x1", "0x2"],
});
let decoded: Initialized<String> = serde_json::from_value(event).unwrap();
assert_eq!(
decoded.finalized_block_hashes,
vec!["0x1".to_string(), "0x2".to_string()]
);

// Old format.
let event = serde_json::json!({
"finalizedBlockHash": "0x1",
});
let decoded: Initialized<String> = serde_json::from_value(event).unwrap();
assert_eq!(decoded.finalized_block_hashes, vec!["0x1".to_string()]);

// Wrong format.
let event = serde_json::json!({
"finalizedBlockHash": ["0x1"],
});
let _ = serde_json::from_value::<Initialized<String>>(event).unwrap_err();
}
}
7 changes: 7 additions & 0 deletions testing/integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ default = []
# Enable to run the tests with Light Client support.
unstable-light-client = ["subxt/unstable-light-client"]

# Enable to run the full-client tests with Light Client support.
unstable-light-client-long-running = ["subxt/unstable-light-client"]

# Enable this to use the unstable backend in tests _instead of_
# the default one which relies on the "old" RPC methods.
unstable-backend-client = []
Expand All @@ -43,3 +46,7 @@ tracing = { workspace = true }
tracing-subscriber = { workspace = true }
wabt = { workspace = true }
substrate-runner = { workspace = true }
subxt-test-proc-macro = { path = "proc-macro" }

[build-dependencies]
cfg_aliases = "0.2.0"
9 changes: 9 additions & 0 deletions testing/integration-tests/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use cfg_aliases::cfg_aliases;

fn main() {
// Setup cfg aliases
cfg_aliases! {
lightclient: { any(feature = "unstable-light-client", feature = "unstable-light-client-long-running") },
fullclient: { all(not(feature = "unstable-light-client"), not(feature = "unstable-light-client-long-running")) },
}
}
20 changes: 20 additions & 0 deletions testing/integration-tests/proc-macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "subxt-test-proc-macro"
Copy link
Collaborator

@jsdw jsdw Apr 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: also rename the folder to subxt-test-proc-macro (or subxt-test-macro if you prefer to shorten this crate name)?

version.workspace = true
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
publish = false

license.workspace = true
repository.workspace = true
documentation.workspace = true
homepage.workspace = true
description = "Subxt integration tests proc-macros"

[lib]
proc-macro = true

[dependencies]
syn = { workspace = true }
quote = { workspace = true }
95 changes: 95 additions & 0 deletions testing/integration-tests/proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

extern crate proc_macro;
use proc_macro::TokenStream;

use quote::{format_ident, quote};
use syn::{
parse::{Parse, ParseStream},
Error,
};

/// Environment variable for setting the timeout for the test.
const SUBXT_TEST_TIMEOUT: &str = "SUBXT_TEST_TIMEOUT";

/// Default timeout for the test.
const DEFAULT_TIMEOUT: u64 = 60 * 6;

#[proc_macro_attribute]
pub fn subxt_test(attr: TokenStream, item: TokenStream) -> TokenStream {
let subxt_attr = match syn::parse::<SubxtTestAttr>(attr) {
Ok(subxt_attr) => subxt_attr,
Err(err) => return err.into_compile_error().into(),
};

// Timeout is determined by:
// - The timeout attribute if it is set.
// - The SUBXT_TEST_TIMEOUT environment variable if it is set.
// - A default of 6 minutes.
let timeout_duration = subxt_attr.timeout.unwrap_or_else(|| {
std::env::var(SUBXT_TEST_TIMEOUT)
.map(|str| str.parse().unwrap_or(DEFAULT_TIMEOUT))
.unwrap_or(DEFAULT_TIMEOUT)
});

let func: syn::ItemFn = match syn::parse(item) {
Ok(func) => func,
Err(err) => return err.into_compile_error().into(),
};

let func_attrs = &func.attrs;
let func_vis = &func.vis;
let func_sig = &func.sig;
let func_block = &func.block;

let mut inner_func_sig = func.sig.clone();
inner_func_sig.ident = format_ident!("{}_inner", inner_func_sig.ident);
let inner_func_name = &inner_func_sig.ident;

let result = quote! {
#[tokio::test]
#( #func_attrs )*
#func_vis #func_sig {
#func_vis #inner_func_sig
#func_block

tokio::time::timeout(std::time::Duration::from_secs(#timeout_duration), #inner_func_name())
.await
.expect("Test timedout")
}
};
result.into()
}

mod keywords {
syn::custom_keyword!(timeout);
}

struct SubxtTestAttr {
timeout: Option<u64>,
}

impl Parse for SubxtTestAttr {
fn parse(input: ParseStream) -> Result<Self, Error> {
if input.is_empty() {
return Ok(Self { timeout: None });
}

let _keyword = input.parse::<keywords::timeout>()?;
input.parse::<syn::token::Eq>()?;
let timeout = input.parse::<syn::LitInt>()?.base10_parse::<u64>()?;

if !input.is_empty() {
return Err(Error::new(
input.span(),
"Expected tokens: `timeout = value`",
));
}

Ok(Self {
timeout: Some(timeout),
})
}
}
28 changes: 20 additions & 8 deletions testing/integration-tests/src/full_client/blocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,27 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

use crate::{test_context, utils::node_runtime};
use crate::{subxt_test, test_context};
use codec::{Compact, Encode};
use futures::StreamExt;

#[cfg(fullclient)]
use crate::utils::node_runtime;
#[cfg(fullclient)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to merge the use stuff to avoid repeating #[cfg(fullclient)]?

use subxt::config::signed_extensions::{ChargeAssetTxPayment, CheckMortality, CheckNonce};
#[cfg(fullclient)]
use subxt::config::DefaultExtrinsicParamsBuilder;
#[cfg(fullclient)]
use subxt::config::SubstrateConfig;
#[cfg(fullclient)]
use subxt::utils::Era;
use subxt_metadata::Metadata;
#[cfg(fullclient)]
use subxt_signer::sr25519::dev;

#[tokio::test]
use subxt_metadata::Metadata;

#[cfg(fullclient)]
#[subxt_test]
async fn block_subscriptions_are_consistent_with_eachother() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
Expand Down Expand Up @@ -76,7 +86,7 @@ async fn block_subscriptions_are_consistent_with_eachother() -> Result<(), subxt
Ok(())
}

#[tokio::test]
#[subxt_test]
async fn finalized_headers_subscription() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
Expand All @@ -93,7 +103,7 @@ async fn finalized_headers_subscription() -> Result<(), subxt::Error> {
Ok(())
}

#[tokio::test]
#[subxt_test]
async fn missing_block_headers_will_be_filled_in() -> Result<(), subxt::Error> {
use subxt::backend::legacy;

Expand Down Expand Up @@ -138,7 +148,7 @@ async fn missing_block_headers_will_be_filled_in() -> Result<(), subxt::Error> {
}

// Check that we can subscribe to non-finalized blocks.
#[tokio::test]
#[subxt_test]
async fn runtime_api_call() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
Expand All @@ -163,7 +173,8 @@ async fn runtime_api_call() -> Result<(), subxt::Error> {
Ok(())
}

#[tokio::test]
#[cfg(fullclient)]
#[subxt_test]
async fn fetch_block_and_decode_extrinsic_details() {
let ctx = test_context().await;
let api = ctx.client();
Expand Down Expand Up @@ -232,7 +243,8 @@ async fn fetch_block_and_decode_extrinsic_details() {
assert!(tx.is_signed());
}

#[tokio::test]
#[cfg(fullclient)]
#[subxt_test]
async fn decode_signed_extensions_from_blocks() {
let ctx = test_context().await;
let api = ctx.client();
Expand Down
Loading
Loading