98 lines
3.3 KiB
Rust
98 lines
3.3 KiB
Rust
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();
|
|
}
|