chromium/mojo/public/rust/tests/bindings/validation.rs

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Run standard mojo validation tests on Rust bindings.

use rust_gtest_interop::prelude::*;

chromium::import! {
    "//mojo/public/rust/tests:test_util";
}

use test_interfaces::validation_test_interfaces::*;

macro_rules! define_validation_tests {
    {$($test_name:ident => $interface:ident $typ:ident;)*} => {
        $(
        #[gtest(ValidationTest, $test_name)]
        fn test() {
            let data = include_str!(
                concat!(
                    "../../../interfaces/bindings/tests/data/validation/",
                    stringify!($test_name),
                    ".data",
                )
            );

            let expectation = include_str!(
                concat!(
                    "../../../interfaces/bindings/tests/data/validation/",
                    stringify!($test_name),
                    ".expected",
                )
            );

            define_validation_tests!{@call data expectation $interface $typ}
        }
        )*
    };
    {@call $data:ident $expectation:ident $interface:ident Request} => {
        run_validation_test::<$interface>($data, $expectation, false);
    };
    {@call $data:ident $expectation:ident $interface:ident Response} => {
        run_validation_test::<$interface>($data, $expectation, true);
    };
}

// Each test case corresponds to a pair of files in
// //mojo/public/interfaces/bindings/tests/data/validation/
define_validation_tests! {
    conformance_empty => ConformanceTestInterface Request;
    conformance_msghdr_incomplete_struct => ConformanceTestInterface Request;
    conformance_msghdr_incomplete_struct_header => ConformanceTestInterface Request;
    conformance_msghdr_invalid_flag_combo => ConformanceTestInterface Request;
    conformance_msghdr_missing_request_id => ConformanceTestInterface Request;
    conformance_msghdr_no_such_method => ConformanceTestInterface Request;
    conformance_msghdr_num_bytes_huge => ConformanceTestInterface Request;
    conformance_msghdr_num_bytes_less_than_min_requirement => ConformanceTestInterface Request;
    conformance_msghdr_num_bytes_less_than_struct_header => ConformanceTestInterface Request;
    conformance_msghdr_num_bytes_version_mismatch_1 => ConformanceTestInterface Request;
    conformance_msghdr_num_bytes_version_mismatch_2 => ConformanceTestInterface Request;
    conformance_msghdr_num_bytes_version_mismatch_3 => ConformanceTestInterface Request;
    conformance_mthd0_good => ConformanceTestInterface Request;
    conformance_mthd0_incomplete_struct => ConformanceTestInterface Request;
    conformance_mthd0_incomplete_struct_header => ConformanceTestInterface Request;
    conformance_mthd0_invalid_request_flags => ConformanceTestInterface Request;
    conformance_mthd0_invalid_request_flags2 => ConformanceTestInterface Request;
    conformance_mthd0_struct_num_bytes_huge => ConformanceTestInterface Request;
    conformance_mthd0_struct_num_bytes_less_than_min_requirement => ConformanceTestInterface Request;
    conformance_mthd0_struct_num_bytes_less_than_struct_header => ConformanceTestInterface Request;
    conformance_mthd1_good => ConformanceTestInterface Request;
    conformance_mthd1_misaligned_struct => ConformanceTestInterface Request;
    conformance_mthd1_struct_pointer_overflow => ConformanceTestInterface Request;
    conformance_mthd1_unexpected_null_struct => ConformanceTestInterface Request;
    conformance_mthd2_good => ConformanceTestInterface Request;
    conformance_mthd2_multiple_pointers_to_same_struct => ConformanceTestInterface Request;
    conformance_mthd2_overlapped_objects => ConformanceTestInterface Request;
    conformance_mthd2_wrong_layout_order => ConformanceTestInterface Request;
    conformance_mthd11_good_version0 => ConformanceTestInterface Request;
    conformance_mthd11_good_version1 => ConformanceTestInterface Request;
    conformance_mthd11_good_version2 => ConformanceTestInterface Request;
    conformance_mthd11_good_version3 => ConformanceTestInterface Request;
    conformance_mthd11_good_version_newer_than_known_1 => ConformanceTestInterface Request;
    conformance_mthd11_good_version_newer_than_known_2 => ConformanceTestInterface Request;
    conformance_mthd11_num_bytes_version_mismatch_1 => ConformanceTestInterface Request;
    conformance_mthd11_num_bytes_version_mismatch_2 => ConformanceTestInterface Request;
    conformance_mthd12_invalid_request_flags => ConformanceTestInterface Request;
    conformance_mthd14_good_known_enum_values => ConformanceTestInterface Request;
    conformance_mthd14_good_uknown_extensible_enum_value => ConformanceTestInterface Request;
    conformance_mthd14_uknown_non_extensible_enum_value => ConformanceTestInterface Request;
    resp_boundscheck_msghdr_no_such_method => ConformanceTestInterface Response;
    resp_conformance_msghdr_invalid_response_flags1 => ConformanceTestInterface Response;
    resp_conformance_msghdr_invalid_response_flags2 => ConformanceTestInterface Response;
    resp_conformance_msghdr_no_such_method => ConformanceTestInterface Response;
}

fn run_validation_test<I: bindings::mojom::Interface>(
    input_data_str: &str,
    expected_result: &str,
    is_response: bool,
) {
    let expected_result = expected_result.trim_end();
    match run_validation_test_impl::<I>(input_data_str, is_response) {
        Ok(()) => (),
        Err(validation_error) => {
            let kind_str = validation_error.kind().to_str();
            expect_eq!(expected_result, kind_str);
            if expected_result != kind_str {
                println!("{validation_error:?}");
            }
            return;
        }
    };

    // Message validation succeeded. Check if it should've failed.
    expect_eq!("PASS", expected_result);
}

fn run_validation_test_impl<I: bindings::mojom::Interface>(
    input_data_str: &str,
    is_response: bool,
) -> bindings::Result<()> {
    let (data, _num_handles) = test_util::parse_validation_test_input(input_data_str).unwrap();

    let message_view = bindings::MessageView::new(&data)?;

    if is_response {
        message_view.validate_response::<I>()
    } else {
        message_view.validate_request::<I>()
    }
}