From 2be35156aafbdee3e6a9b647f52e50646f6f004f Mon Sep 17 00:00:00 2001 From: Eitan Seri-Levi Date: Wed, 4 Sep 2024 20:33:22 -0700 Subject: [PATCH] Add code coverage step to CI and add more tests (#15) * add code coverage ci, and improve code coverage in general * add testsuite changes * added json test coverage * Fmt --- .github/workflows/test-suite.yml | 17 ++++++ .gitignore | 1 + Makefile | 4 ++ src/address_hex.rs | 95 ++++++++++++++++++++++++++++++++ src/fixed_bytes_hex.rs | 93 +++++++++++++++++++++++++++++++ src/hex_vec.rs | 49 ++++++++++++++++ src/json_str.rs | 71 ++++++++++++++++++++++++ src/list_of_bytes_lists.rs | 54 ++++++++++++++++++ src/u32_hex.rs | 50 +++++++++++++++++ src/u8_hex.rs | 49 ++++++++++++++++ 10 files changed, 483 insertions(+) create mode 100644 Makefile diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index 27792ae..43150f7 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -31,3 +31,20 @@ jobs: run: rustup update stable - name: Run tests run: cargo test --release + coverage: + runs-on: ubuntu-latest + name: cargo-tarpaulin + steps: + - uses: actions/checkout@v3 + - name: Get latest version of stable Rust + run: rustup update stable + - name: Install cargo-tarpaulin + uses: taiki-e/install-action@cargo-tarpaulin + - name: Check code coverage with cargo-tarpaulin + run: make coverage + - name: Upload to codecov.io + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} + informational: true diff --git a/.gitignore b/.gitignore index 4fffb2f..932baf5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /Cargo.lock +/*.xml diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..84a1286 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +coverage: + cargo-tarpaulin --workspace --all-features --out xml + +.PHONY: coverage \ No newline at end of file diff --git a/src/address_hex.rs b/src/address_hex.rs index 9a132f1..1b39b15 100644 --- a/src/address_hex.rs +++ b/src/address_hex.rs @@ -32,3 +32,98 @@ where array.copy_from_slice(&decoded); Ok(array.into()) } + +#[cfg(test)] +mod test { + use std::str::FromStr; + + use alloy_primitives::Address; + use serde::{Deserialize, Serialize}; + use serde_json; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(transparent)] + struct Wrapper { + #[serde(with = "super")] + val: Address, + } + + #[test] + fn encoding() { + assert_eq!( + &serde_json::to_string(&Wrapper { + val: Address::from_str("0000000000000000000000000000000000000000").unwrap() + }) + .unwrap(), + "\"0x0000000000000000000000000000000000000000\"" + ); + assert_eq!( + &serde_json::to_string(&Wrapper { + val: Address::from_str("0000000000000000000000000000000000000001").unwrap() + }) + .unwrap(), + "\"0x0000000000000000000000000000000000000001\"" + ); + assert_eq!( + &serde_json::to_string(&Wrapper { + val: Address::from_str("1000000000000000000000000000000000000000").unwrap() + }) + .unwrap(), + "\"0x1000000000000000000000000000000000000000\"" + ); + assert_eq!( + &serde_json::to_string(&Wrapper { + val: Address::from_str("1234567890000000000000000000000000000000").unwrap() + }) + .unwrap(), + "\"0x1234567890000000000000000000000000000000\"" + ); + assert_eq!( + &serde_json::to_string(&Wrapper { val: Address::ZERO }).unwrap(), + "\"0x0000000000000000000000000000000000000000\"" + ); + } + + #[test] + fn decoding() { + assert_eq!( + serde_json::from_str::("\"0x0000000000000000000000000000000000000000\"") + .unwrap(), + Wrapper { val: Address::ZERO }, + ); + assert_eq!( + serde_json::from_str::("\"0x0000000000000000000000000000000000000001\"") + .unwrap(), + Wrapper { + val: Address::from_str("0000000000000000000000000000000000000001").unwrap() + }, + ); + assert_eq!( + serde_json::from_str::("\"0x1000000000000000000000000000000000000000\"") + .unwrap(), + Wrapper { + val: Address::from_str("1000000000000000000000000000000000000000").unwrap() + }, + ); + assert_eq!( + serde_json::from_str::("\"0x1234567890000000000000000000000000000000\"") + .unwrap(), + Wrapper { + val: Address::from_str("1234567890000000000000000000000000000000").unwrap() + }, + ); + // Wrong length. + serde_json::from_str::("\"0x0\"").unwrap_err(); + serde_json::from_str::("\"0x0400\"").unwrap_err(); + serde_json::from_str::("\"0x12345678900000000000000000000000000000001\"") + .unwrap_err(); + // Requires 0x. + serde_json::from_str::("\"1234567890000000000000000000000000000000\"") + .unwrap_err(); + serde_json::from_str::("\"ff34567890000000000000000000000000000000\"") + .unwrap_err(); + // Contains invalid characters. + serde_json::from_str::("\"0x-100000000000000000000000000000000000000\"") + .unwrap_err(); + } +} diff --git a/src/fixed_bytes_hex.rs b/src/fixed_bytes_hex.rs index 4e9dc98..c008911 100644 --- a/src/fixed_bytes_hex.rs +++ b/src/fixed_bytes_hex.rs @@ -40,6 +40,99 @@ macro_rules! bytes_hex { array.copy_from_slice(&decoded); Ok(array) } + + #[cfg(test)] + mod test { + use super::*; + use serde::{Deserialize, Serialize}; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(transparent)] + struct Wrapper { + #[serde(with = "super")] + val: [u8; BYTES_LEN], + } + + fn generate_string_value(v1: &str, v2: &str) -> String { + let mut i = 0; + let mut value = String::new(); + while i < BYTES_LEN * 2 { + if i % 2 == 0 { + value.push_str(v1); + } else { + value.push_str(v2); + } + i += 1; + } + value + } + + #[test] + fn encoding() { + let zero = "0".repeat(BYTES_LEN * 2); + assert_eq!( + &serde_json::to_string(&Wrapper { + val: [0; BYTES_LEN] + }) + .unwrap(), + &format!("\"0x{}\"", zero) + ); + + assert_eq!( + &serde_json::to_string(&Wrapper { + val: [123; BYTES_LEN] + }) + .unwrap(), + &format!("\"0x{}\"", generate_string_value("7", "b")) + ); + + let max = "f".repeat(BYTES_LEN * 2); + assert_eq!( + &serde_json::to_string(&Wrapper { + val: [u8::MAX; BYTES_LEN] + }) + .unwrap(), + &format!("\"0x{}\"", max) + ); + } + + #[test] + fn decoding() { + let zero = "0".repeat(BYTES_LEN * 2); + assert_eq!( + serde_json::from_str::(&format!("\"0x{}\"", zero)).unwrap(), + Wrapper { + val: [0; BYTES_LEN] + }, + ); + assert_eq!( + serde_json::from_str::(&format!( + "\"0x{}\"", + generate_string_value("7", "b") + )) + .unwrap(), + Wrapper { + val: [123; BYTES_LEN] + }, + ); + + let max = "f".repeat(BYTES_LEN * 2); + assert_eq!( + serde_json::from_str::(&format!("\"0x{}\"", max)).unwrap(), + Wrapper { + val: [u8::MAX; BYTES_LEN] + }, + ); + + // Require 0x. + serde_json::from_str::(&format!("\"{}\"", "0".repeat(BYTES_LEN * 2))) + .unwrap_err(); + + let exceed_max = "f".repeat((BYTES_LEN * 2) + 1); + // Wrong length. + serde_json::from_str::(&format!("\"0x{}\"", exceed_max)).unwrap_err(); + } + } }; } diff --git a/src/hex_vec.rs b/src/hex_vec.rs index f7f4833..08a0d00 100644 --- a/src/hex_vec.rs +++ b/src/hex_vec.rs @@ -21,3 +21,52 @@ where { deserializer.deserialize_str(PrefixedHexVisitor) } + +#[cfg(test)] +mod test { + use serde::{Deserialize, Serialize}; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(transparent)] + struct Wrapper { + #[serde(with = "super")] + val: Vec, + } + + #[test] + fn encoding() { + assert_eq!( + &serde_json::to_string(&Wrapper { val: vec![0] }).unwrap(), + "\"0x00\"" + ); + assert_eq!( + &serde_json::to_string(&Wrapper { val: vec![0, 1] }).unwrap(), + "\"0x0001\"" + ); + assert_eq!( + &serde_json::to_string(&Wrapper { + val: vec![0, 1, 2, 3] + }) + .unwrap(), + "\"0x00010203\"" + ); + } + + #[test] + fn decoding() { + assert_eq!( + serde_json::from_str::("\"0x00\"").unwrap(), + Wrapper { val: vec![0] }, + ); + assert_eq!( + serde_json::from_str::("\"0x0001\"").unwrap(), + Wrapper { val: vec![0, 1] }, + ); + assert_eq!( + serde_json::from_str::("\"0x00010203\"").unwrap(), + Wrapper { + val: vec![0, 1, 2, 3] + }, + ); + } +} diff --git a/src/json_str.rs b/src/json_str.rs index b9a1813..d007cf5 100644 --- a/src/json_str.rs +++ b/src/json_str.rs @@ -23,3 +23,74 @@ where let json_str = String::deserialize(deserializer)?; serde_json::from_str(&json_str).map_err(D::Error::custom) } + +#[cfg(test)] +mod test { + use serde::{Deserialize, Serialize}; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Wrapper { + val_1: String, + val_2: u8, + val_3: Vec, + val_4: Option, + } + + #[test] + fn encoding() { + assert_eq!( + &serde_json::to_string(&Wrapper { + val_1: "Test".to_string(), + val_2: 5, + val_3: vec![9], + val_4: Some(10) + }) + .unwrap(), + "{\"val_1\":\"Test\",\"val_2\":5,\"val_3\":[9],\"val_4\":10}" + ); + assert_eq!( + &serde_json::to_string(&Wrapper { + val_1: "Test".to_string(), + val_2: 5, + val_3: vec![9], + val_4: None + }) + .unwrap(), + "{\"val_1\":\"Test\",\"val_2\":5,\"val_3\":[9],\"val_4\":null}" + ); + } + + #[test] + fn decoding() { + assert_eq!( + serde_json::from_str::( + "{\"val_1\":\"Test\",\"val_2\":5,\"val_3\":[9],\"val_4\":10}" + ) + .unwrap(), + Wrapper { + val_1: "Test".to_string(), + val_2: 5, + val_3: vec![9], + val_4: Some(10) + }, + ); + assert_eq!( + serde_json::from_str::( + "{\"val_1\":\"Test\",\"val_2\":5,\"val_3\":[9],\"val_4\":null}" + ) + .unwrap(), + Wrapper { + val_1: "Test".to_string(), + val_2: 5, + val_3: vec![9], + val_4: None + }, + ); + + // Violating type constraints. + serde_json::from_str::("{\"val_1\":1,\"val_2\":5,\"val_3\":[9],\"val_4\":null}") + .unwrap_err(); + serde_json::from_str::("{\"val_1\":\"Test\",\"val_2\":5,\"val_4\":null}") + .unwrap_err(); + } +} diff --git a/src/list_of_bytes_lists.rs b/src/list_of_bytes_lists.rs index b93321a..c7007c3 100644 --- a/src/list_of_bytes_lists.rs +++ b/src/list_of_bytes_lists.rs @@ -47,3 +47,57 @@ where { deserializer.deserialize_any(ListOfBytesListVisitor) } + +#[cfg(test)] +mod test { + use serde::{Deserialize, Serialize}; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(transparent)] + struct Wrapper { + #[serde(with = "super")] + val: Vec>, + } + + #[test] + fn encoding() { + assert_eq!( + &serde_json::to_string(&Wrapper { val: vec![vec![0]] }).unwrap(), + "[\"0x00\"]" + ); + assert_eq!( + &serde_json::to_string(&Wrapper { + val: vec![vec![0, 1, 2]] + }) + .unwrap(), + "[\"0x000102\"]" + ); + assert_eq!( + &serde_json::to_string(&Wrapper { + val: vec![vec![0], vec![0, 1, 2]] + }) + .unwrap(), + "[\"0x00\",\"0x000102\"]" + ); + } + + #[test] + fn decoding() { + assert_eq!( + serde_json::from_str::("[\"0x00\"]").unwrap(), + Wrapper { val: vec![vec![0]] }, + ); + assert_eq!( + serde_json::from_str::("[\"0x000102\"]").unwrap(), + Wrapper { + val: vec![vec![0, 1, 2]] + }, + ); + assert_eq!( + serde_json::from_str::("[\"0x00\",\"0x000102\"]").unwrap(), + Wrapper { + val: vec![vec![0], vec![0, 1, 2]] + }, + ); + } +} diff --git a/src/u32_hex.rs b/src/u32_hex.rs index c1ab353..ab41eab 100644 --- a/src/u32_hex.rs +++ b/src/u32_hex.rs @@ -19,3 +19,53 @@ where { bytes_4_hex::deserialize(deserializer).map(u32::from_le_bytes) } + +#[cfg(test)] +pub mod test { + use serde::{Deserialize, Serialize}; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(transparent)] + struct Wrapper { + #[serde(with = "super")] + val: u32, + } + + #[test] + fn encoding() { + assert_eq!( + &serde_json::to_string(&Wrapper { val: 0 }).unwrap(), + "\"0x00000000\"" + ); + assert_eq!( + &serde_json::to_string(&Wrapper { val: 5 }).unwrap(), + "\"0x05000000\"" + ); + + assert_eq!( + &serde_json::to_string(&Wrapper { val: u32::MAX }).unwrap(), + "\"0xffffffff\"" + ); + } + + #[test] + fn decoding() { + assert_eq!( + serde_json::from_str::("\"0x00000000\"").unwrap(), + Wrapper { val: 0 }, + ); + assert_eq!( + serde_json::from_str::("\"0x05000000\"").unwrap(), + Wrapper { val: 5 }, + ); + assert_eq!( + serde_json::from_str::("\"0xffffffff\"").unwrap(), + Wrapper { val: u32::MAX }, + ); + + // Wrong length. + serde_json::from_str::("\"0xfffffffff\"").unwrap_err(); + // Requires 0x. + serde_json::from_str::("\"00000000\"").unwrap_err(); + } +} diff --git a/src/u8_hex.rs b/src/u8_hex.rs index 8083e1d..c75d2af 100644 --- a/src/u8_hex.rs +++ b/src/u8_hex.rs @@ -27,3 +27,52 @@ where } Ok(bytes[0]) } + +#[cfg(test)] +mod test { + use serde::{Deserialize, Serialize}; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(transparent)] + struct Wrapper { + #[serde(with = "super")] + val: u8, + } + + #[test] + fn encoding() { + assert_eq!( + &serde_json::to_string(&Wrapper { val: 0 }).unwrap(), + "\"0x00\"" + ); + assert_eq!( + &serde_json::to_string(&Wrapper { val: 109 }).unwrap(), + "\"0x6d\"" + ); + assert_eq!( + &serde_json::to_string(&Wrapper { val: u8::MAX }).unwrap(), + "\"0xff\"" + ); + } + + #[test] + fn decoding() { + assert_eq!( + serde_json::from_str::("\"0x00\"").unwrap(), + Wrapper { val: 0 }, + ); + assert_eq!( + serde_json::from_str::("\"0x6d\"").unwrap(), + Wrapper { val: 109 }, + ); + assert_eq!( + serde_json::from_str::("\"0xff\"").unwrap(), + Wrapper { val: u8::MAX }, + ); + + // Require 0x. + serde_json::from_str::("\"ff\"").unwrap_err(); + // Wrong length. + serde_json::from_str::("\"0xfff\"").unwrap_err(); + } +}