#![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
use zerodds_wire::{
Endianness, Reader, WireError, WirePosition, Writer, WstringLengthCounts, WstringLengthPolicy,
WstringTerminator, WstringUnitOrder, WstringUnitWidth,
};
const BOUNDED: &str = "\u{1234}\u{00e4}Z";
const UNBOUNDED: &str = "\u{00e4}\u{00f6}\u{00fc} \u{1234} Z";
const ASTRAL: &str = "a\u{1d11e}b";
fn hex(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{b:02x}")).collect()
}
fn wrote(
text: &str,
policy: WstringLengthPolicy,
order: WstringUnitOrder,
endianness: Endianness,
) -> String {
let mut w = Writer::new(endianness);
w.write_wstring(text, policy, order, WirePosition::Member)
.expect("the cell writes this value");
hex(w.as_bytes())
}
fn wrote_stream(text: &str, policy: WstringLengthPolicy) -> String {
wrote(text, policy, WstringUnitOrder::Stream, Endianness::Little)
}
const OCTETS: WstringLengthCounts = WstringLengthCounts::Octets;
const CHARS: WstringLengthCounts = WstringLengthCounts::Characters;
const ABSENT: WstringTerminator = WstringTerminator::Absent;
const PRESENT: WstringTerminator = WstringTerminator::Present;
const TWO: WstringUnitWidth = WstringUnitWidth::Two;
const FOUR: WstringUnitWidth = WstringUnitWidth::Four;
#[test]
fn every_cell_of_the_product_puts_its_own_octets_on_the_wire() {
let cells: [(WstringLengthPolicy, &str, &str); 8] = [
(
WstringLengthPolicy::new(OCTETS, ABSENT, TWO),
"specification",
"0400000061006200",
),
(
WstringLengthPolicy::new(OCTETS, ABSENT, FOUR),
"specification-wide-units",
"080000006100000062000000",
),
(
WstringLengthPolicy::new(OCTETS, PRESENT, TWO),
"octets/terminated/2",
"06000000610062000000",
),
(
WstringLengthPolicy::new(OCTETS, PRESENT, FOUR),
"octets/terminated/4",
"0c000000610000006200000000000000",
),
(
WstringLengthPolicy::new(CHARS, ABSENT, TWO),
"character-count",
"0200000061006200",
),
(
WstringLengthPolicy::new(CHARS, ABSENT, FOUR),
"ros-type-support",
"020000006100000062000000",
),
(
WstringLengthPolicy::new(CHARS, PRESENT, TWO),
"characters/terminated/2",
"03000000610062000000",
),
(
WstringLengthPolicy::new(CHARS, PRESENT, FOUR),
"character-count-terminated",
"03000000610000006200000000000000",
),
];
assert_eq!(cells.len(), WstringLengthPolicy::ALL_CELLS.len());
for (policy, name, expected) in cells {
assert_eq!(policy.as_str(), name, "the cell's own name");
assert_eq!(wrote_stream("ab", policy), expected, "cell {name}");
}
let mut octets: Vec<String> = cells
.iter()
.map(|(policy, _, _)| wrote_stream("ab", *policy))
.collect();
octets.sort_unstable();
octets.dedup();
assert_eq!(octets.len(), 8, "eight cells, eight byte strings");
}
#[test]
fn every_cell_round_trips_under_every_unit_order() {
for policy in WstringLengthPolicy::ALL_CELLS {
for order in [
WstringUnitOrder::Stream,
WstringUnitOrder::BigEndianAlways,
WstringUnitOrder::ByteOrderMark,
WstringUnitOrder::HostUnswapped,
] {
for endianness in [Endianness::Little, Endianness::Big] {
let mut w = Writer::new(endianness);
w.write_wstring(UNBOUNDED, policy, order, WirePosition::Member)
.expect("every cell writes BMP text");
let bytes = w.finish();
let mut r = Reader::new(&bytes, endianness);
let back = r
.read_wstring(policy, order, WirePosition::Member)
.unwrap_or_else(|error| {
panic!(
"{} / {} / {endianness:?} reads back: {error:?}",
policy.as_str(),
order.spelling()
)
});
assert_eq!(back, UNBOUNDED, "{} / {order:?}", policy.as_str());
assert_eq!(r.remaining(), 0, "the whole run was consumed");
}
}
}
}
#[test]
fn the_two_positions_still_agree() {
for policy in WstringLengthPolicy::ALL_CELLS {
for order in [
WstringUnitOrder::Stream,
WstringUnitOrder::BigEndianAlways,
WstringUnitOrder::ByteOrderMark,
WstringUnitOrder::HostUnswapped,
] {
let mut member = Writer::new(Endianness::Little);
member
.write_wstring(UNBOUNDED, policy, order, WirePosition::Member)
.expect("a member");
let mut element = Writer::new(Endianness::Little);
element
.write_wstring(UNBOUNDED, policy, order, WirePosition::Element)
.expect("an element");
assert_eq!(
hex(member.as_bytes()),
hex(element.as_bytes()),
"{} / {order:?}",
policy.as_str()
);
}
}
}
#[test]
fn cyclone_and_opendds_are_an_octet_count_over_two_octet_units() {
assert_eq!(
wrote_stream(UNBOUNDED, WstringLengthPolicy::SPECIFICATION),
"0e000000e400f600fc002000341220005a00"
);
assert_eq!(
wrote_stream(BOUNDED, WstringLengthPolicy::SPECIFICATION),
"060000003412e4005a00"
);
}
#[test]
fn fastdds_is_a_character_count_over_two_octet_units() {
assert_eq!(
wrote_stream(UNBOUNDED, WstringLengthPolicy::CHARACTER_COUNT),
"07000000e400f600fc002000341220005a00"
);
assert_eq!(
wrote_stream(BOUNDED, WstringLengthPolicy::CHARACTER_COUNT),
"030000003412e4005a00"
);
}
#[test]
fn rti_xcdr1_is_a_character_count_over_four_octet_units_with_a_terminator() {
assert_eq!(
wrote_stream(UNBOUNDED, WstringLengthPolicy::CHARACTER_COUNT_TERMINATED),
"08000000e4000000f6000000fc0000002000000034120000200000005a00000000000000"
);
assert_eq!(
wrote_stream(BOUNDED, WstringLengthPolicy::CHARACTER_COUNT_TERMINATED),
"0400000034120000e40000005a00000000000000"
);
}
#[test]
fn tao_is_an_octet_count_over_four_octet_units() {
assert_eq!(
wrote_stream(BOUNDED, WstringLengthPolicy::SPECIFICATION_WIDE_UNITS),
"0c00000034120000e40000005a000000"
);
}
#[test]
fn both_ros_type_supports_are_a_character_count_over_four_octet_units() {
assert_eq!(
wrote_stream(UNBOUNDED, WstringLengthPolicy::ROS_TYPE_SUPPORT),
"07000000e4000000f6000000fc0000002000000034120000200000005a000000"
);
assert_eq!(
wrote_stream(BOUNDED, WstringLengthPolicy::ROS_TYPE_SUPPORT),
"0300000034120000e40000005a000000"
);
}
#[test]
fn omniorb_writes_a_counted_byte_order_mark() {
assert_eq!(
wrote(
BOUNDED,
WstringLengthPolicy::SPECIFICATION,
WstringUnitOrder::ByteOrderMark,
Endianness::Little,
),
"08000000fffe3412e4005a00"
);
assert_eq!(
wrote(
BOUNDED,
WstringLengthPolicy::SPECIFICATION,
WstringUnitOrder::ByteOrderMark,
Endianness::Big,
),
"00000008fffe3412e4005a00"
);
}
#[test]
fn an_empty_wstring_carries_no_byte_order_mark() {
for order in [
WstringUnitOrder::Stream,
WstringUnitOrder::BigEndianAlways,
WstringUnitOrder::ByteOrderMark,
WstringUnitOrder::HostUnswapped,
] {
assert_eq!(
wrote(
"",
WstringLengthPolicy::SPECIFICATION,
order,
Endianness::Little,
),
"00000000",
"{order:?}"
);
}
assert_eq!(
wrote_stream("", WstringLengthPolicy::CHARACTER_COUNT_TERMINATED),
"0100000000000000"
);
}
#[test]
fn cyclone_big_endian_writes_little_endian_units() {
assert_eq!(
wrote(
UNBOUNDED,
WstringLengthPolicy::SPECIFICATION,
WstringUnitOrder::HostUnswapped,
Endianness::Big,
),
"0000000ee400f600fc002000341220005a00"
);
assert_eq!(
wrote(
UNBOUNDED,
WstringLengthPolicy::ROS_TYPE_SUPPORT,
WstringUnitOrder::HostUnswapped,
Endianness::Big,
),
"00000007e4000000f6000000fc0000002000000034120000200000005a000000"
);
assert_eq!(
wrote(
UNBOUNDED,
WstringLengthPolicy::SPECIFICATION,
WstringUnitOrder::HostUnswapped,
Endianness::Little,
),
wrote_stream(UNBOUNDED, WstringLengthPolicy::SPECIFICATION)
);
}
#[test]
fn jacorb_reads_big_endian_units_out_of_either_stream() {
assert_eq!(
wrote(
BOUNDED,
WstringLengthPolicy::SPECIFICATION,
WstringUnitOrder::BigEndianAlways,
Endianness::Big,
),
"00000006123400e4005a"
);
assert_eq!(
wrote(
BOUNDED,
WstringLengthPolicy::SPECIFICATION,
WstringUnitOrder::BigEndianAlways,
Endianness::Little,
),
"06000000123400e4005a"
);
}
#[test]
fn an_octet_count_and_a_character_count_are_different_numbers() {
let octets = wrote_stream(UNBOUNDED, WstringLengthPolicy::SPECIFICATION);
let characters = wrote_stream(UNBOUNDED, WstringLengthPolicy::CHARACTER_COUNT);
assert_eq!(&octets[..8], "0e000000", "fourteen octets");
assert_eq!(&characters[..8], "07000000", "seven characters");
assert_eq!(octets[8..], characters[8..]);
}
#[test]
fn a_terminator_is_written_and_counted() {
let without = wrote_stream(UNBOUNDED, WstringLengthPolicy::ROS_TYPE_SUPPORT);
let with = wrote_stream(UNBOUNDED, WstringLengthPolicy::CHARACTER_COUNT_TERMINATED);
assert_eq!(&without[..8], "07000000", "seven characters");
assert_eq!(&with[..8], "08000000", "seven characters and the NUL");
assert_eq!(with[8..], format!("{}00000000", &without[8..]));
assert_eq!(
with.len(),
without.len() + 8,
"one four-octet unit longer on the wire"
);
}
#[test]
fn the_unit_width_reaches_both_the_length_and_the_octets() {
let narrow = wrote_stream("ab", WstringLengthPolicy::SPECIFICATION);
let wide = wrote_stream("ab", WstringLengthPolicy::SPECIFICATION_WIDE_UNITS);
assert_eq!(narrow, "0400000061006200");
assert_eq!(wide, "080000006100000062000000");
assert_eq!(
WstringLengthPolicy::SPECIFICATION.character_width(WirePosition::Member),
2
);
assert_eq!(
WstringLengthPolicy::SPECIFICATION_WIDE_UNITS.character_width(WirePosition::Member),
4
);
}
#[test]
fn a_character_count_over_two_octet_units_refuses_a_code_point_outside_the_bmp() {
let mut w = Writer::new(Endianness::Little);
let error = w
.write_wstring(
ASTRAL,
WstringLengthPolicy::CHARACTER_COUNT,
WstringUnitOrder::Stream,
WirePosition::Member,
)
.expect_err("the cell has no way to carry U+1D11E");
match error {
WireError::OutOfRange { expected, .. } => {
assert!(expected.contains("U+1D11E"), "{expected}");
assert!(expected.contains("BMP"), "{expected}");
}
other => panic!("expected an OutOfRange, got {other:?}"),
}
assert!(w.as_bytes().is_empty(), "nothing went on the wire");
let mut w = Writer::new(Endianness::Little);
assert!(
w.write_wstring(
ASTRAL,
WstringLengthPolicy::new(CHARS, PRESENT, TWO),
WstringUnitOrder::Stream,
WirePosition::Member,
)
.is_err(),
"characters/terminated/2 has the same narrow unit"
);
}
#[test]
fn a_character_count_over_four_octet_units_carries_the_surrogate_pair() {
assert_eq!(
wrote_stream("\u{1d11e}", WstringLengthPolicy::CHARACTER_COUNT_TERMINATED),
"0300000034d800001edd000000000000"
);
assert_eq!(
wrote_stream("\u{1d11e}", WstringLengthPolicy::ROS_TYPE_SUPPORT),
"0200000034d800001edd0000"
);
assert_eq!(
wrote_stream("\u{1d11e}", WstringLengthPolicy::SPECIFICATION),
"0400000034d81edd"
);
}
#[test]
fn an_octet_count_the_unit_width_does_not_divide_is_refused() {
let bytes = [0x03, 0x00, 0x00, 0x00, 0x12, 0x34, 0xe4, 0x00, 0x5a, 0x00];
let mut r = Reader::new(&bytes, Endianness::Little);
let error = r
.read_wstring(
WstringLengthPolicy::SPECIFICATION,
WstringUnitOrder::Stream,
WirePosition::Member,
)
.expect_err("three is not an even octet count");
match error {
WireError::WrongFraming {
expected,
found,
fits,
..
} => {
assert!(expected.contains("specification"), "{expected}");
assert!(found.contains("character-count"), "{found}");
assert!(
fits.is_some_and(|flag| flag.starts_with("--wstring-length ")),
"the flag that fixes it, not just a complaint"
);
}
other => panic!("expected a WrongFraming, got {other:?}"),
}
}
#[test]
fn a_terminated_cell_refuses_a_frame_without_its_terminator() {
let policy = WstringLengthPolicy::CHARACTER_COUNT_TERMINATED;
let zero = [0x00, 0x00, 0x00, 0x00];
let mut r = Reader::new(&zero, Endianness::Little);
assert!(matches!(
r.read_wstring(policy, WstringUnitOrder::Stream, WirePosition::Member),
Err(WireError::WrongFraming { .. })
));
let unterminated = [
0x02, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00,
];
let mut r = Reader::new(&unterminated, Endianness::Little);
match r.read_wstring(policy, WstringUnitOrder::Stream, WirePosition::Member) {
Err(WireError::OutOfRange { expected, .. }) => {
assert!(expected.contains("NUL"), "{expected}");
}
other => panic!("expected an OutOfRange about the NUL, got {other:?}"),
}
let inner = [
0x03, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
];
let mut r = Reader::new(&inner, Endianness::Little);
match r.read_wstring(policy, WstringUnitOrder::Stream, WirePosition::Member) {
Err(WireError::OutOfRange { expected, .. }) => {
assert!(expected.contains("C peer"), "{expected}");
}
other => panic!("expected an OutOfRange about the inner NUL, got {other:?}"),
}
let mut r = Reader::new(&inner, Endianness::Little);
let back = r
.read_wstring(
WstringLengthPolicy::ROS_TYPE_SUPPORT,
WstringUnitOrder::Stream,
WirePosition::Member,
)
.expect("an unterminated cell may carry U+0000");
assert_eq!(back, "a\u{0}\u{0}");
}
#[test]
fn a_wide_unit_that_is_not_a_code_unit_is_refused() {
let bytes = [0x01, 0x00, 0x00, 0x00, 0x1e, 0xd1, 0x01, 0x00];
let mut r = Reader::new(&bytes, Endianness::Little);
match r.read_wstring(
WstringLengthPolicy::ROS_TYPE_SUPPORT,
WstringUnitOrder::Stream,
WirePosition::Member,
) {
Err(WireError::OutOfRange { expected, .. }) => {
assert!(expected.contains("one UTF-16 code unit"), "{expected}");
}
other => panic!("expected an OutOfRange, got {other:?}"),
}
}
#[test]
fn a_length_the_stream_cannot_hold_is_refused_before_allocating() {
let bytes = [0xff, 0xff, 0xff, 0x7f, 0x61, 0x00];
let mut r = Reader::new(&bytes, Endianness::Little);
assert!(matches!(
r.read_wstring(
WstringLengthPolicy::CHARACTER_COUNT,
WstringUnitOrder::Stream,
WirePosition::Member,
),
Err(WireError::ImpossibleLength { .. })
));
}
#[test]
fn an_unpaired_surrogate_is_refused_and_not_replaced() {
let bytes = [0x02, 0x00, 0x00, 0x00, 0x34, 0xd8, 0x61, 0x00];
let mut r = Reader::new(&bytes, Endianness::Little);
match r.read_wstring(
WstringLengthPolicy::SPECIFICATION,
WstringUnitOrder::Stream,
WirePosition::Member,
) {
Err(WireError::OutOfRange { expected, .. }) => {
assert!(expected.contains("UTF-16"), "{expected}");
}
other => panic!("expected an OutOfRange, got {other:?}"),
}
}
#[test]
fn the_byte_order_mark_decides_on_read() {
let policy = WstringLengthPolicy::SPECIFICATION;
let order = WstringUnitOrder::ByteOrderMark;
let upright = [0x06, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x61, 0x00, 0x62, 0x00];
let mut r = Reader::new(&upright, Endianness::Little);
assert_eq!(
r.read_wstring(policy, order, WirePosition::Member)
.expect("an upright mark"),
"ab"
);
let swapped = [0x06, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x61, 0x00, 0x62];
let mut r = Reader::new(&swapped, Endianness::Little);
assert_eq!(
r.read_wstring(policy, order, WirePosition::Member)
.expect("a swapped mark"),
"ab"
);
let bare = [0x04, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x62];
let mut r = Reader::new(&bare, Endianness::Little);
assert_eq!(
r.read_wstring(policy, order, WirePosition::Member)
.expect("no mark, so big-endian"),
"ab"
);
}
#[test]
fn a_bounded_wstring_counts_code_units_and_refuses_an_overrun() {
let policy = WstringLengthPolicy::SPECIFICATION;
let order = WstringUnitOrder::Stream;
let mut w = Writer::new(Endianness::Little);
w.write_wstring_bounded(BOUNDED, 3, policy, order, WirePosition::Member)
.expect("three code units fit a bound of three");
assert_eq!(hex(w.as_bytes()), "060000003412e4005a00");
let mut w = Writer::new(Endianness::Little);
let error = w
.write_wstring_bounded(ASTRAL, 3, policy, order, WirePosition::Member)
.expect_err("four code units do not fit a bound of three");
match error {
WireError::OutOfRange { expected, .. } => {
assert!(expected.contains("code units"), "{expected}");
}
other => panic!("expected an OutOfRange, got {other:?}"),
}
assert!(w.as_bytes().is_empty(), "nothing went on the wire");
}
#[test]
fn the_terminator_is_not_counted_against_the_bound() {
let mut w = Writer::new(Endianness::Little);
w.write_wstring_bounded(
BOUNDED,
3,
WstringLengthPolicy::CHARACTER_COUNT_TERMINATED,
WstringUnitOrder::Stream,
WirePosition::Member,
)
.expect("three characters fit wstring<3> even with a NUL behind them");
assert_eq!(
hex(w.as_bytes()),
"0400000034120000e40000005a00000000000000"
);
}
#[test]
fn fit_wstring_counts_code_units_and_trims_whole_characters() {
use zerodds_wire::{Narrowed, TryConstruct, fit_wstring};
assert!(matches!(
fit_wstring(0, ASTRAL.to_string(), 3, TryConstruct::Discard),
Err(WireError::Discarded { .. })
));
assert!(matches!(
fit_wstring(0, ASTRAL.to_string(), 4, TryConstruct::Discard),
Ok(Narrowed::Value(_))
));
assert!(matches!(
fit_wstring(0, ASTRAL.to_string(), 3, TryConstruct::UseDefault),
Ok(Narrowed::Default)
));
match fit_wstring(0, ASTRAL.to_string(), 2, TryConstruct::Trim) {
Ok(Narrowed::Value(trimmed)) => assert_eq!(trimmed, "a"),
other => panic!("expected a trim to \"a\", got {other:?}"),
}
match fit_wstring(0, ASTRAL.to_string(), 3, TryConstruct::Trim) {
Ok(Narrowed::Value(trimmed)) => assert_eq!(trimmed, "a\u{1d11e}"),
other => panic!("expected a trim to \"a𝄞\", got {other:?}"),
}
}
#[test]
fn every_cell_has_a_name_and_a_flag_of_its_own() {
let mut names: Vec<&str> = WstringLengthPolicy::ALL_CELLS
.iter()
.map(|cell| cell.as_str())
.collect();
names.sort_unstable();
names.dedup();
assert_eq!(names.len(), 8);
let mut flags: Vec<&str> = WstringLengthPolicy::ALL_CELLS
.iter()
.map(|cell| cell.flag())
.collect();
flags.sort_unstable();
flags.dedup();
assert_eq!(flags.len(), 8);
for cell in WstringLengthPolicy::ALL_CELLS {
assert_eq!(cell.flag(), format!("--wstring-length {}", cell.as_str()));
}
assert!(WstringLengthPolicy::SPECIFICATION.is_default());
for position in [WirePosition::Member, WirePosition::Element] {
assert!(WstringUnitOrder::ByteOrderMark.has_mark());
assert!(!WstringUnitOrder::Stream.has_mark());
assert!(!WstringUnitOrder::BigEndianAlways.has_mark());
assert!(!WstringUnitOrder::HostUnswapped.has_mark());
assert!(
WstringUnitOrder::HostUnswapped.writes_little_endian_units(position),
"{position:?}"
);
assert!(
WstringUnitOrder::ByteOrderMark.writes_little_endian_units(position),
"{position:?}"
);
assert!(!WstringUnitOrder::Stream.writes_little_endian_units(position));
assert!(!WstringUnitOrder::BigEndianAlways.writes_little_endian_units(position));
}
assert_eq!(
WstringLengthPolicy::default(),
WstringLengthPolicy::SPECIFICATION
);
}
#[test]
fn a_cell_admits_only_the_lengths_it_could_have_written() {
assert!(WstringLengthPolicy::SPECIFICATION.admits_length(4));
assert!(!WstringLengthPolicy::SPECIFICATION.admits_length(3));
assert!(WstringLengthPolicy::SPECIFICATION_WIDE_UNITS.admits_length(8));
assert!(!WstringLengthPolicy::SPECIFICATION_WIDE_UNITS.admits_length(6));
assert!(WstringLengthPolicy::CHARACTER_COUNT.admits_length(3));
assert!(WstringLengthPolicy::CHARACTER_COUNT.admits_length(0));
assert!(WstringLengthPolicy::CHARACTER_COUNT_TERMINATED.admits_length(1));
assert!(!WstringLengthPolicy::CHARACTER_COUNT_TERMINATED.admits_length(0));
}
trait OrderName {
fn spelling(self) -> &'static str;
}
impl OrderName for WstringUnitOrder {
fn spelling(self) -> &'static str {
match self {
WstringUnitOrder::Stream => "stream",
WstringUnitOrder::BigEndianAlways => "big-endian-always",
WstringUnitOrder::ByteOrderMark => "byte-order-mark",
WstringUnitOrder::HostUnswapped => "host-unswapped",
}
}
}
#[test]
fn a_length_that_does_not_fit_the_uint32_field_is_refused_and_not_wrapped() {
let member = WirePosition::Member;
assert_eq!(
WstringLengthPolicy::CHARACTER_COUNT.declared_length(u32::MAX as usize, member),
Some(u32::MAX)
);
assert_eq!(
WstringLengthPolicy::CHARACTER_COUNT.declared_length(u32::MAX as usize + 1, member),
None
);
let half = (u32::MAX as usize).div_ceil(2);
assert_eq!(
WstringLengthPolicy::SPECIFICATION.declared_length(half - 1, member),
Some(0xFFFF_FFFE)
);
assert_eq!(
WstringLengthPolicy::SPECIFICATION.declared_length(half, member),
None
);
let quarter = (u32::MAX as usize).div_ceil(4);
assert_eq!(
WstringLengthPolicy::SPECIFICATION_WIDE_UNITS.declared_length(quarter, member),
None
);
for cell in WstringLengthPolicy::ALL_CELLS {
assert_eq!(
cell.declared_length(usize::MAX, member),
None,
"{}",
cell.as_str()
);
}
assert_eq!(
WstringLengthPolicy::SPECIFICATION.declared_length(7, member),
Some(14)
);
assert_eq!(
WstringLengthPolicy::CHARACTER_COUNT.declared_length(7, member),
Some(7)
);
}