Theme

alex/volcanic

Automated vulkan bindings

BRANCH main
main
SNAPSHOT Improved loading api and made ready for publish
DOWNLOAD
Download file

root / codegen / src / emit / tests.rs

use super::*;

#[test]
fn propagates_promoted_and_conditional_ownership() {
    let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
    let vk = Registry::load(manifest_dir.join("../spec/vk.xml")).unwrap();
    let video = Registry::load(manifest_dir.join("../spec/video.xml")).unwrap();
    let selection = select::select_vulkan(&vk, &video);
    let enum_index = enum_index(&vk, &video, &selection);
    let mut modules = emit_modules(&selection, &enum_index);
    expand_module_dependencies(&vk, &video, &enum_index, &mut modules);
    let ctx = Context::new(&vk, &video, &modules);

    let maintenance5 = select::FeatureExpr::feature("vk_khr_maintenance5");
    assert!(maintenance5.implies(ctx.type_condition("VkPipelineCreateFlags2")));
    assert!(maintenance5.implies(ctx.type_condition("VkPipelineCreateFlags2CreateInfo")));

    let maintenance11_with_extended_flags = select::FeatureExpr::all([
        select::FeatureExpr::feature("vk_khr_maintenance11"),
        select::FeatureExpr::feature("vk_khr_extended_flags"),
    ]);
    let image_flag = ctx.enum_condition("VK_IMAGE_CREATE_2_ALIAS_SINGLE_LAYER_DESCRIPTOR_BIT_KHR");
    assert!(maintenance11_with_extended_flags.implies(image_flag));
    assert!(
        !maintenance11_with_extended_flags
            .implies(ctx.enum_condition("VK_SHADER_CREATE_INDEPENDENT_SETS_BIT_KHR"))
    );
}

#[test]
fn translates_c_constants() {
    assert_eq!(rust_const_expr("(~0U)"), "u32::MAX");
    assert_eq!(rust_const_expr("1000.0F"), "1000.0f32");
    assert_eq!(rust_const_expr("0x7FFFFFFF"), "0x7FFFFFFF");
    assert_eq!(rust_const_expr("0xFF"), "0xFF");
    assert_eq!(rust_const_expr("\"VK_EXT_test\""), "c\"VK_EXT_test\"");
}

#[test]
fn parses_numeric_define_values() {
    assert_eq!(
        numeric_define_value("#define VK_HEADER_VERSION 349", "VK_HEADER_VERSION"),
        Some("349".to_string())
    );
    assert_eq!(
        numeric_define_value("#define EXAMPLE 123U", "EXAMPLE"),
        Some("123".to_string())
    );
    assert_eq!(
        numeric_define_value("#define EXAMPLE SOME_MACRO(1)", "EXAMPLE"),
        None
    );
}

#[test]
fn derives_bitflag_associated_names() {
    assert_eq!(
        flag_assoc_name("VkQueueFlagBits", "VK_QUEUE_GRAPHICS_BIT"),
        "GRAPHICS"
    );
    assert_eq!(
        flag_assoc_name(
            "VkPipelineStageFlagBits2",
            "VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT_KHR"
        ),
        "ALL_TRANSFER_KHR"
    );
    assert_eq!(
        flag_assoc_name(
            "VkTensorCreateFlagBitsARM",
            "VK_TENSOR_CREATE_MUTABLE_FORMAT_BIT_ARM"
        ),
        "MUTABLE_FORMAT_ARM"
    );
}

#[test]
fn derives_rust_enum_variant_names() {
    assert_eq!(
        enum_variant_name("VkStructureType", "VK_STRUCTURE_TYPE_APPLICATION_INFO"),
        "ApplicationInfo"
    );
    assert_eq!(
        enum_variant_name(
            "VkStructureType",
            "VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR"
        ),
        "SwapchainCreateInfoKhr"
    );
    assert_eq!(enum_variant_name("VkResult", "VK_SUCCESS"), "Success");
    assert_eq!(
        enum_variant_name("VkResult", "VK_ERROR_OUT_OF_HOST_MEMORY"),
        "ErrorOutOfHostMemory"
    );
    assert_eq!(
        enum_variant_name("VkPresentModeKHR", "VK_PRESENT_MODE_FIFO_RELAXED_KHR"),
        "FifoRelaxed"
    );
    assert_eq!(
        enum_variant_name(
            "StdVideoH264ProfileIdc",
            "STD_VIDEO_H264_PROFILE_IDC_BASELINE"
        ),
        "Baseline"
    );
}

#[test]
fn resolves_enum_alias_chains_and_rejects_cycles() {
    let condition = select::FeatureExpr::feature("core_1_0");
    let resolved = |name, value, alias| ResolvedEnum {
        entry: enum_entry(name, value, alias),
        group_name: Some("VkResult".into()),
        extension_number: None,
        condition: condition.clone(),
    };
    let entries = [
        resolved("VK_SUCCESS", Some("0"), None),
        resolved("VK_SUCCESS_ALIAS", None, Some("VK_SUCCESS")),
        resolved("VK_SUCCESS_ALIAS2", None, Some("VK_SUCCESS_ALIAS")),
        resolved("VK_CYCLE", None, Some("VK_CYCLE")),
        resolved("VK_MISSING", None, Some("VK_UNKNOWN")),
    ];
    let index = entries
        .iter()
        .map(|entry| (entry.entry.name.as_str(), entry))
        .collect();
    let widths = IndexMap::from([("VkResult".into(), "i32")]);
    assert_eq!(enum_value(&entries[2], &index, &widths), "0");
    for entry in &entries[3..] {
        assert!(std::panic::catch_unwind(|| enum_value(entry, &index, &widths)).is_err());
    }
}

#[test]
fn enum_synonyms_keep_their_own_feature_conditions() {
    let mut infos = IndexMap::new();
    let core = select::FeatureExpr::feature("core_1_0");
    let extension = select::FeatureExpr::feature("vk_ext_example");
    insert_enum_constant(&mut infos, "VkResult", "Success", "0".into(), &core);
    insert_enum_constant(
        &mut infos,
        "VkResult",
        "SuccessAlias",
        "0".into(),
        &extension,
    );
    insert_enum_constant(&mut infos, "VkResult", "Success", "0".into(), &extension);
    let constants = &infos["VkResult"].constants;
    assert_eq!(constants.len(), 2);
    assert_eq!(
        constants["Success"].condition,
        select::FeatureExpr::any([core, extension.clone()])
    );
    assert_eq!(constants["SuccessAlias"].condition, extension);
}

#[test]
fn selects_bitmask_storage_width() {
    assert_eq!(bitmask_bits_ty(Some("VkFlags")), "u32");
    assert_eq!(bitmask_bits_ty(Some("VkFlags64")), "u64");
    assert_eq!(bitmask_bits_ty(None), "u32");
}

#[test]
fn classifies_command_scopes() {
    let commands = [
        command("vkCreateInstance", &[]),
        command("vkGetInstanceProcAddr", &[("instance", "VkInstance")]),
        command("vkGetDeviceProcAddr", &[("device", "VkDevice")]),
        command(
            "vkGetPhysicalDeviceProperties",
            &[("physicalDevice", "VkPhysicalDevice")],
        ),
        command("vkQueueSubmit", &[("queue", "VkQueue")]),
        command("vkCmdDraw", &[("commandBuffer", "VkCommandBuffer")]),
    ];
    let command_map: IndexMap<_, _> = commands
        .iter()
        .map(|command| (command.name.as_str(), command))
        .collect();
    let handle_parents = IndexMap::from([
        ("VkPhysicalDevice".to_string(), "VkInstance".to_string()),
        ("VkDevice".to_string(), "VkPhysicalDevice".to_string()),
        ("VkQueue".to_string(), "VkDevice".to_string()),
        ("VkCommandBuffer".to_string(), "VkCommandPool".to_string()),
        ("VkCommandPool".to_string(), "VkDevice".to_string()),
    ]);
    let dispatchable_handles = IndexSet::from([
        "VkInstance".to_string(),
        "VkPhysicalDevice".to_string(),
        "VkDevice".to_string(),
        "VkQueue".to_string(),
        "VkCommandBuffer".to_string(),
    ]);

    let scope = |name| {
        command_scope(
            command_map.get(name).copied().unwrap(),
            &command_map,
            &handle_parents,
            &dispatchable_handles,
        )
    };

    assert_eq!(scope("vkCreateInstance"), CommandScope::Global);
    assert_eq!(scope("vkGetInstanceProcAddr"), CommandScope::Global);
    assert_eq!(scope("vkGetDeviceProcAddr"), CommandScope::Instance);
    assert_eq!(
        scope("vkGetPhysicalDeviceProperties"),
        CommandScope::Instance
    );
    assert_eq!(scope("vkQueueSubmit"), CommandScope::Device);
    assert_eq!(scope("vkCmdDraw"), CommandScope::Device);
}

#[test]
fn renders_loader_call_arguments() {
    let decls = vec![
        decl("commandBuffer", "VkCommandBuffer"),
        decl("type", "VkImageType"),
    ];
    assert_eq!(call_args(&decls), "commandBuffer, r#type");
}

fn enum_entry(name: &str, value: Option<&str>, alias: Option<&str>) -> EnumEntry {
    EnumEntry {
        name: name.to_string(),
        value: value.map(ToOwned::to_owned),
        bitpos: None,
        offset: None,
        alias: alias.map(ToOwned::to_owned),
        extends: None,
        extnumber: None,
        dir: None,
        ty: None,
        api: None,
    }
}

fn command(name: &str, params: &[(&str, &str)]) -> Command {
    Command {
        name: name.to_string(),
        api: None,
        alias: None,
        proto: Some(decl(name, "void")),
        params: params.iter().map(|(name, base)| decl(name, base)).collect(),
    }
}

fn decl(name: &str, base: &str) -> Decl {
    Decl {
        name: name.to_string(),
        api: None,
        base: base.to_string(),
        base_const: false,
        pointer_const: Vec::new(),
        arrays: Vec::new(),
        bit_width: None,
    }
}

#[test]
fn array_parameters_decay_only_the_outer_dimension() {
    let doc = roxmltree::Document::parse(
        "<param>const <type>float</type> <name>matrix</name>[3][4]</param>",
    )
    .unwrap();
    let decl = crate::ctype::parse_decl(doc.root_element());
    assert_eq!(rust_decl_type(&decl, false), "[[f32; 4]; 3]");
    assert_eq!(rust_param_type(&decl), "*const [f32; 4]");
}

#[test]
fn empty_sections_preserve_surrounding_code() {
    let mut out = "pub const BEFORE: u32 = 1;\n".to_string();
    section(&mut out, "Feature and extension guards").finish(&mut out);
    out.push_str("pub const AFTER: u32 = 2;\n");
    assert_eq!(
        out,
        "pub const BEFORE: u32 = 1;\npub const AFTER: u32 = 2;\n"
    );
}