release: escpos-vfd 0.3.0

This commit is contained in:
Кобелев Андрей Андреевич
2026-08-20 19:25:08 +05:00
parent 244d907cc2
commit 37cdba4898
14 changed files with 472 additions and 69 deletions
+97
View File
@@ -0,0 +1,97 @@
use escpos_vfd::codec::EpsonCodec;
use escpos_vfd::{
ConfigError, DisplaySettings, MAX_MARQUEE_CHARS, MAX_MARQUEE_CPS, MAX_QUEUED_RAW_BYTES,
TextEncoding, Vfd, VfdError, VfdWorker,
};
use std::time::Duration;
#[test]
fn public_text_api_neutralizes_controls_and_legacy_fallback_is_one_byte() {
let display = DisplaySettings::new(8, 1, TextEncoding::Ascii);
let mut vfd = Vfd::from_transport(Vec::<u8>::new(), display).unwrap();
vfd.print_line(1, "A\u{1b}@B\u{0c}C").unwrap();
assert_eq!(&vfd.into_inner()[6..], b"A @B C ");
for encoding in [TextEncoding::Cp866, TextEncoding::Windows1251] {
let codec = EpsonCodec::new(DisplaySettings::new(1, 1, encoding)).unwrap();
assert_eq!(codec.encode_text("😀"), vec![b'?']);
}
}
#[test]
fn public_codec_constructor_returns_geometry_errors() {
assert!(matches!(
EpsonCodec::new(DisplaySettings::new(usize::MAX, 1, TextEncoding::Ascii)),
Err(ConfigError::InvalidColumns(usize::MAX))
));
}
#[test]
fn sync_worker_enforces_payload_and_rate_boundaries() {
let display = DisplaySettings::new(20, 2, TextEncoding::Ascii);
let worker = VfdWorker::from_transport(Vec::<u8>::new(), display, 2).unwrap();
let handle = worker.handle();
handle.print_line(1, "x".repeat(1_000_000)).unwrap();
handle
.set_marquee_text("m".repeat(MAX_MARQUEE_CHARS))
.unwrap();
assert!(matches!(
handle.set_marquee_text("m".repeat(MAX_MARQUEE_CHARS + 1)),
Err(VfdError::TextTooLong { .. })
));
handle.write_raw(vec![0; MAX_QUEUED_RAW_BYTES]).unwrap();
assert!(matches!(
handle.write_raw(vec![0; MAX_QUEUED_RAW_BYTES + 1]),
Err(VfdError::RawPayloadTooLarge { .. })
));
handle
.start_marquee(2, MAX_MARQUEE_CPS, Duration::ZERO)
.unwrap();
handle.stop_marquee().unwrap();
assert!(matches!(
handle.start_marquee(2, MAX_MARQUEE_CPS + 1, Duration::ZERO),
Err(VfdError::InvalidMarqueeSpeed { .. })
));
let bytes = worker.shutdown().unwrap().into_inner();
assert!(bytes.windows(20).any(|window| window == [b'x'; 20]));
}
#[cfg(feature = "tokio")]
#[tokio::test]
async fn tokio_public_api_matches_security_boundaries() {
use escpos_vfd::tokio::{AsyncVfd, AsyncVfdWorker};
let display = DisplaySettings::new(8, 1, TextEncoding::Ascii);
let mut vfd = AsyncVfd::from_transport(Vec::<u8>::new(), display)
.await
.unwrap();
vfd.print_line(1, "A\u{1b}@B\u{0c}C").await.unwrap();
assert_eq!(&vfd.into_inner()[6..], b"A @B C ");
let display = DisplaySettings::new(20, 2, TextEncoding::Ascii);
let worker = AsyncVfdWorker::from_transport(Vec::<u8>::new(), display, 2)
.await
.unwrap();
let handle = worker.handle();
assert!(matches!(
handle
.set_marquee_text("m".repeat(MAX_MARQUEE_CHARS + 1))
.await,
Err(VfdError::TextTooLong { .. })
));
assert!(matches!(
handle.write_raw(vec![0; MAX_QUEUED_RAW_BYTES + 1]).await,
Err(VfdError::RawPayloadTooLarge { .. })
));
assert!(matches!(
handle
.start_marquee(1, MAX_MARQUEE_CPS + 1, Duration::ZERO)
.await,
Err(VfdError::InvalidMarqueeSpeed { .. })
));
worker.shutdown().await.unwrap();
}