Skip to content

Commit

Permalink
chore(starknet_committer_and_os_cli): generalize python tests
Browse files Browse the repository at this point in the history
  • Loading branch information
amosStarkware committed Feb 3, 2025
1 parent 7705fb7 commit b75181b
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ use tracing_subscriber::reload::Handle;
use tracing_subscriber::Registry;

use crate::committer_cli::commands::parse_and_commit;
use crate::committer_cli::tests::python_tests::PythonTest;
use crate::shared_utils::read::{read_input, write_to_file};
use crate::shared_utils::types::IoArgs;
use crate::committer_cli::tests::python_tests::CommitterPythonTestRunner;
use crate::shared_utils::types::{run_python_test, IoArgs, PythonTestArg};

#[derive(Parser, Debug)]
pub struct CommitterCliCommand {
Expand All @@ -22,14 +21,7 @@ enum Command {
#[clap(flatten)]
io_args: IoArgs,
},
PythonTest {
#[clap(flatten)]
io_args: IoArgs,

/// Test name.
#[clap(long)]
test_name: String,
},
PythonTest(PythonTestArg),
}

pub async fn run_committer_cli(
Expand All @@ -42,20 +34,8 @@ pub async fn run_committer_cli(
parse_and_commit(input_path, output_path, log_filter_handle).await;
}

Command::PythonTest { io_args: IoArgs { input_path, output_path }, test_name } => {
// Create PythonTest from test_name.
let test = PythonTest::try_from(test_name)
.unwrap_or_else(|error| panic!("Failed to create PythonTest: {}", error));
let input = read_input(input_path);

// Run relevant test.
let output = test
.run(Some(&input))
.await
.unwrap_or_else(|error| panic!("Failed to run test: {}", error));

// Write test's output.
write_to_file(&output_path, &output);
Command::PythonTest(python_test_arg) => {
run_python_test::<CommitterPythonTestRunner>(python_test_arg).await;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,23 @@ use starknet_types_core::hash::{Pedersen, StarkHash};
use thiserror;
use tracing::{debug, error, info, warn};

use super::utils::objects::{get_thin_state_diff, get_transaction_output_for_hash, get_tx_data};
use super::utils::parse_from_python::TreeFlowInput;
use crate::committer_cli::filled_tree_output::filled_forest::SerializedForest;
use crate::committer_cli::parse_input::cast::InputImpl;
use crate::committer_cli::parse_input::read::parse_input;
use crate::committer_cli::tests::utils::objects::{
get_thin_state_diff,
get_transaction_output_for_hash,
get_tx_data,
};
use crate::committer_cli::tests::utils::parse_from_python::parse_input_single_storage_tree_flow_test;
use crate::committer_cli::tests::utils::random_structs::DummyRandomValue;
use crate::shared_utils::types::{PythonTestError, PythonTestRunner};

pub type CommitterPythonTestError = PythonTestError<CommitterSpecificTestError>;

// Enum representing different Python tests.
pub enum PythonTest {
pub enum CommitterPythonTestRunner {
ExampleTest,
FeltSerialize,
HashFunction,
Expand All @@ -70,13 +77,9 @@ pub enum PythonTest {
LogError,
}

/// Error type for PythonTest enum.
/// Error type for CommitterPythonTest enum.
#[derive(Debug, thiserror::Error)]
pub enum PythonTestError {
#[error("Unknown test name: {0}")]
UnknownTestName(String),
#[error(transparent)]
ParseInputError(#[from] serde_json::Error),
pub enum CommitterSpecificTestError {
#[error(transparent)]
ParseIntError(#[from] std::num::ParseIntError),
#[error("{0}")]
Expand All @@ -85,13 +88,11 @@ pub enum PythonTestError {
InvalidCastError(#[from] std::num::TryFromIntError),
#[error(transparent)]
DeserializationTestFailure(#[from] DeserializationError),
#[error("None value found in input.")]
NoneInputError,
}

/// Implements conversion from a string to a `PythonTest`.
impl TryFrom<String> for PythonTest {
type Error = PythonTestError;
/// Implements conversion from a string to the test runner.
impl TryFrom<String> for CommitterPythonTestRunner {
type Error = CommitterPythonTestError;

fn try_from(value: String) -> Result<Self, Self::Error> {
match value.as_str() {
Expand All @@ -118,22 +119,21 @@ impl TryFrom<String> for PythonTest {
}
}

impl PythonTest {
/// Returns the input string if it's `Some`, or an error if it's `None`.
pub fn non_optional_input(input: Option<&str>) -> Result<&str, PythonTestError> {
input.ok_or(PythonTestError::NoneInputError)
}
impl PythonTestRunner for CommitterPythonTestRunner {
type SpecificError = CommitterSpecificTestError;

/// Runs the test with the given arguments.
pub async fn run(&self, input: Option<&str>) -> Result<String, PythonTestError> {
async fn run(&self, input: Option<&str>) -> Result<String, CommitterPythonTestError> {
match self {
Self::ExampleTest => {
let example_input: HashMap<String, String> =
serde_json::from_str(Self::non_optional_input(input)?)?;
Ok(example_test(example_input))
}
Self::FeltSerialize => {
let felt = Self::non_optional_input(input)?.parse::<u128>()?;
let felt = Self::non_optional_input(input)?.parse::<u128>().map_err(|err| {
PythonTestError::SpecificError(CommitterSpecificTestError::ParseIntError(err))
})?;
Ok(felt_serialize_test(felt))
}
Self::HashFunction => {
Expand Down Expand Up @@ -232,12 +232,12 @@ fn serialize_for_rust_committer_flow_test(input: HashMap<String, String>) -> Str
fn get_or_key_not_found<'a, T: Debug>(
map: &'a HashMap<String, T>,
key: &'a str,
) -> Result<&'a T, PythonTestError> {
) -> Result<&'a T, CommitterPythonTestError> {
map.get(key).ok_or_else(|| {
PythonTestError::KeyNotFound(format!(
PythonTestError::SpecificError(CommitterSpecificTestError::KeyNotFound(format!(
"Failed to get value for key '{}' from {:?}.",
key, map
))
)))
})
}

Expand Down Expand Up @@ -334,8 +334,12 @@ pub(crate) fn test_binary_serialize_test(binary_input: HashMap<String, u128>) ->
.unwrap_or_else(|error| panic!("Failed to serialize binary fact: {}", error))
}

pub(crate) fn parse_input_test(committer_input: String) -> Result<String, PythonTestError> {
Ok(create_output_to_python(parse_input(&committer_input)?))
pub(crate) fn parse_input_test(
committer_input: String,
) -> Result<String, CommitterPythonTestError> {
Ok(create_output_to_python(parse_input(&committer_input).map_err(|err| {
PythonTestError::SpecificError(CommitterSpecificTestError::DeserializationTestFailure(err))
})?))
}

fn create_output_to_python(actual_input: InputImpl) -> String {
Expand Down Expand Up @@ -513,7 +517,7 @@ pub(crate) fn test_node_db_key() -> String {
/// StorageValue pairs for u128 values in the range 0..=1000,
/// serializes it to a JSON string using Serde,
/// and returns the serialized JSON string or panics with an error message if serialization fails.
pub(crate) fn storage_serialize_test() -> Result<String, PythonTestError> {
pub(crate) fn storage_serialize_test() -> Result<String, CommitterPythonTestError> {
let mut storage = MapStorage { storage: HashMap::new() };
for i in 0..=99_u128 {
let key = StorageKey(Felt::from(i).to_bytes_be().to_vec());
Expand Down Expand Up @@ -547,9 +551,9 @@ fn python_hash_constants_compare() -> String {
/// - `"contract_class_leaf"`: Compiled class leaf data.
///
/// # Returns
/// A `Result<String, PythonTestError>` containing a serialized map of all nodes on success, or an
/// error if keys are missing or parsing fails.
fn test_storage_node(data: HashMap<String, String>) -> Result<String, PythonTestError> {
/// A `Result<String, CommitterTestError>` containing a serialized map of all nodes on
/// success, or an error if keys are missing or parsing fails.
fn test_storage_node(data: HashMap<String, String>) -> Result<String, CommitterPythonTestError> {
// Create a storage to store the nodes.
let mut rust_fact_storage = MapStorage { storage: HashMap::new() };

Expand Down Expand Up @@ -579,8 +583,14 @@ fn test_storage_node(data: HashMap<String, String>) -> Result<String, PythonTest
bottom_hash: HashOutput(Felt::from(*get_or_key_not_found(&edge_data, "bottom")?)),
path_to_bottom: PathToBottom::new(
U256::from(*get_or_key_not_found(&edge_data, "path")?).into(),
EdgePathLength::new((*get_or_key_not_found(&edge_data, "length")?).try_into()?)
.expect("Invalid length"),
EdgePathLength::new(
(*get_or_key_not_found(&edge_data, "length")?).try_into().map_err(|err| {
PythonTestError::SpecificError(
CommitterSpecificTestError::InvalidCastError(err),
)
})?,
)
.expect("Invalid length"),
)
.expect("Illegal PathToBottom"),
}),
Expand Down Expand Up @@ -653,7 +663,7 @@ fn test_storage_node(data: HashMap<String, String>) -> Result<String, PythonTest
}

/// Generates a dummy random filled forest and serializes it to a JSON string.
pub(crate) fn filled_forest_output_test() -> Result<String, PythonTestError> {
pub(crate) fn filled_forest_output_test() -> Result<String, CommitterPythonTestError> {
let dummy_forest = SerializedForest(FilledForest::dummy_random(&mut rand::thread_rng(), None));
let output = dummy_forest.forest_to_output();
let output_string = serde_json::to_string(&output).expect("Failed to serialize");
Expand Down
63 changes: 63 additions & 0 deletions crates/starknet_committer_and_os_cli/src/shared_utils/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use std::fmt::Debug;

use clap::Args;

use crate::shared_utils::read::{read_input, write_to_file};

// TODO(Amos): Make this optional.
#[derive(Debug, Args)]
pub(crate) struct IoArgs {
/// File path to input.
Expand All @@ -10,3 +15,61 @@ pub(crate) struct IoArgs {
#[clap(long, short = 'o', default_value = "stdout")]
pub(crate) output_path: String,
}

#[derive(Debug, Args)]
pub(crate) struct PythonTestArg {
#[clap(flatten)]
pub(crate) io_args: IoArgs,

/// Test name.
#[clap(long)]
pub(crate) test_name: String,
}

/// Error type for PythonTest enum.
#[derive(Debug, thiserror::Error)]
pub enum PythonTestError<E> {
#[error("Unknown test name: {0}")]
UnknownTestName(String),
#[error(transparent)]
ParseInputError(#[from] serde_json::Error),
#[error("None value found in input.")]
NoneInputError,
#[error(transparent)]
SpecificError(E),
}

pub(crate) trait PythonTestRunner: TryFrom<String> {
type SpecificError: Debug;

/// Returns the input string if it's `Some`, or an error if it's `None`.
fn non_optional_input(
input: Option<&str>,
) -> Result<&str, PythonTestError<Self::SpecificError>> {
input.ok_or(PythonTestError::NoneInputError)
}

async fn run(
&self,
input: Option<&str>,
) -> Result<String, PythonTestError<Self::SpecificError>>;
}

pub(crate) async fn run_python_test<PT: PythonTestRunner>(python_test_arg: PythonTestArg)
where
<PT as TryFrom<String>>::Error: Debug,
{
// Create PythonTest from test_name.
let test = PT::try_from(python_test_arg.test_name)
.unwrap_or_else(|error| panic!("Failed to create PythonTest: {:?}", error));
let input = read_input(python_test_arg.io_args.input_path);

// Run relevant test.
let output = test
.run(Some(&input))
.await
.unwrap_or_else(|error| panic!("Failed to run test: {:?}", error));

// Write test's output.
write_to_file(&python_test_arg.io_args.output_path, &output);
}

0 comments on commit b75181b

Please sign in to comment.