Skip to content

test-utils: build_test!() and Test::expect_stack() take opposite stack orders #2021

@Qyriad

Description

@Qyriad

The build_test!() family of macros take an input operand stack:

($in_debug_mode:expr, $source:expr, $stack_inputs:expr) => {{
use $crate::SourceManager;
let stack_inputs: Vec<u64> = $stack_inputs.to_vec();
let stack_inputs = $crate::StackInputs::try_from_ints(stack_inputs).unwrap();
let advice_inputs = $crate::AdviceInputs::default();
let name = format!("test{}", line!());
let source_manager = ::alloc::sync::Arc::new($crate::DefaultSourceManager::default());
let source = source_manager.load(
$crate::SourceLanguage::Masm,
name.into(),
::alloc::string::String::from($source),
);
$crate::Test {
source_manager,
source,
kernel_source: None,
stack_inputs,
advice_inputs,
in_debug_mode: $in_debug_mode,
libraries: ::alloc::vec::Vec::default(),
handlers: ::alloc::collections::BTreeMap::default(),
add_modules: ::alloc::vec::Vec::default(),
}
}};

and Test::expect_stack() takes an expected output operand stack:

/// Builds a final stack from the provided stack-ordered array and asserts that executing the
/// test will result in the expected final stack state.
#[track_caller]
pub fn expect_stack(&self, final_stack: &[u64]) {
let result = self.get_last_stack_state().as_int_vec();
let expected = resize_to_min_stack_depth(final_stack);
assert_eq!(expected, result, "Expected stack to be {:?}, found {:?}", expected, result);
}

However these two parameters are in opposite order from each other. That is, the following test succeeds:

#[test]
fn test_stack_order() {
    build_test!("begin nop end", &[0, 1, 2, 3]).expect_stack(&[3, 2, 1, 0]);
}

Technically the build_test!() family simply pass the input stack slice as Test { stack_inputs }. stack_inputs is of type StackInputs, which is documented to be in "stack-order":

/// Defines the initial state of the VM's operand stack.
///
/// The values in the struct are stored in the "stack order" - i.e., the last input is at the top
/// of the stack (in position 0).
#[derive(Clone, Debug, Default)]
pub struct StackInputs {

And internally expect_stack() uses StackOutputs, which is documented to be in the opposite order (though this is not called out so explicitly):

/// Output container for Miden VM programs.
///
/// Miden program outputs contain the full state of the stack at the end of execution.
///
/// `stack` is expected to be ordered as if the elements were popped off the stack one by one.
/// Thus, the value at the top of the stack is expected to be in the first position, and the order
/// of the rest of the output elements will also match the order on the stack.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct StackOutputs {

So upon investigation, this behavior is consistent, but it is very confusing; none of the docs for the testing functions, macros, or structs mention this. In fact, expect_stack() claims that its parameter is in stack order:

Builds a final stack from the provided stack-ordered array and asserts that executing the test will result in the expected final stack state.

[Emphasis mine]

Was expect_stack() intended to take stack-order, or is this purely a docs issue?

Metadata

Metadata

Assignees

No one assigned

    Labels

    next milestone candidateIssues that should be considered for the next milestoneprocessorRelated to Miden VM processor

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions