fmi-rs 0.3.1

Functional Mock-up Interface (FMI) for Rust
#![allow(
    clippy::arc_with_non_send_sync,
    clippy::arithmetic_side_effects,
    clippy::expect_used,
    clippy::indexing_slicing,
    clippy::panic,
    clippy::redundant_field_names,
    clippy::unwrap_used
)]

use fmi_rs::model_description::ValidationError;
use fmi_rs::model_description::fmi3::ModelDescription;
use fmi_rs::model_description::validation::validate_structured_variable_name;

fn validate_model_description(xml: &str) -> Vec<ValidationError> {
    ModelDescription::from_string(xml)
        .expect("parse model description")
        .validate()
}

fn initial_unknowns_error(problems: &[ValidationError]) -> bool {
    problems.iter().any(|p| p.message.contains("initial unknowns"))
}

const CLOCK_TEST_BASE: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<fmiModelDescription fmiVersion="3.0" modelName="ClockTest" instantiationToken="">
  <CoSimulation modelIdentifier="ClockTest" hasEventMode="true"/>
  <ModelVariables>
    <Float64 name="time" valueReference="0" causality="independent" variability="continuous"/>
    <Clock name="inClock" valueReference="1" causality="input" intervalVariability="triggered"/>
    <Clock name="alarm" valueReference="2" causality="output" intervalVariability="triggered"/>
    <Float64 name="y" valueReference="3" causality="output" variability="discrete"/>
  </ModelVariables>
  <ModelStructure>
    <Output valueReference="2"/>
    <Output valueReference="3"/>
    {initial_unknowns}
  </ModelStructure>
</fmiModelDescription>"#;

#[test]
fn output_clock_not_required_in_initial_unknowns() {
    let xml = CLOCK_TEST_BASE.replace("{initial_unknowns}", r#"<InitialUnknown valueReference="3"/>"#);
    assert!(!initial_unknowns_error(&validate_model_description(&xml)));
}

#[test]
fn output_clock_must_not_be_listed_in_initial_unknowns() {
    let xml = CLOCK_TEST_BASE.replace(
        "{initial_unknowns}",
        r#"<InitialUnknown valueReference="2"/><InitialUnknown valueReference="3"/>"#,
    );
    assert!(initial_unknowns_error(&validate_model_description(&xml)));
}

#[test]
fn clocked_output_may_be_listed_in_initial_unknowns() {
    let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<fmiModelDescription fmiVersion="3.0" modelName="ClockTest" instantiationToken="">
  <CoSimulation modelIdentifier="ClockTest" hasEventMode="true"/>
  <ModelVariables>
    <Float64 name="time" valueReference="0" causality="independent" variability="continuous"/>
    <Clock name="inClock" valueReference="1" causality="input" intervalVariability="triggered"/>
    <Int32 name="z" valueReference="4" causality="output" variability="discrete" clocks="1"/>
    <Float64 name="y" valueReference="3" causality="output" variability="discrete"/>
  </ModelVariables>
  <ModelStructure>
    <Output valueReference="3"/>
    <Output valueReference="4"/>
    <InitialUnknown valueReference="3"/>
    <InitialUnknown valueReference="4"/>
  </ModelStructure>
</fmiModelDescription>"#;
    assert!(!initial_unknowns_error(&validate_model_description(xml)));
}

#[test]
fn test_structured_variable_name_validation() {
    let valid_names = vec![
        "_",
        "der(x)",
        "a.b.c",
        "v[1]",
        "v[1,2]",
        "'quoted.name'",
        "der('quoted name'[1])",
        "der(a[1].b[2,3].c)",
    ];

    for name in valid_names {
        assert_eq!(validate_structured_variable_name(name), Ok(()));
    }

    let invalid_names = vec![
        (
            "1x",
            "syntax error, unexpected UNSIGNED_INTEGER, expecting DER or NONDIGIT or Q_NAME",
        ),
        (
            "a..b",
            "syntax error, unexpected '.', expecting NONDIGIT or Q_NAME",
        ),
        (
            "a[1",
            "syntax error, unexpected end of file, expecting ',' or ']'",
        ),
        (
            "a[1,2",
            "syntax error, unexpected end of file, expecting ',' or ']'",
        ),
        (
            "a[1]b",
            "syntax error, unexpected NONDIGIT, expecting end of file",
        ),
        (
            "der(a[1].b[2].c",
            "syntax error, unexpected end of file, expecting ')' or ',' or '.'",
        ),
    ];

    for (name, expected_error) in invalid_names {
        assert_eq!(
            validate_structured_variable_name(name),
            Err(expected_error.to_string())
        );
    }
}