#![cfg(feature = "stream")]
use base64_ng::stream::{Decoder, DecoderReader, Encoder, EncoderReader};
use base64_ng::{STANDARD, STANDARD_NO_PAD, URL_SAFE_NO_PAD};
use std::io::{Cursor, Read, Write};
#[cfg(feature = "stream")]
struct ChunkedReader<'a> {
input: &'a [u8],
max_chunk: usize,
}
#[cfg(feature = "stream")]
impl Read for ChunkedReader<'_> {
fn read(&mut self, output: &mut [u8]) -> std::io::Result<usize> {
let len = self.input.len().min(self.max_chunk).min(output.len());
if len == 0 {
return Ok(0);
}
output[..len].copy_from_slice(&self.input[..len]);
self.input = &self.input[len..];
Ok(len)
}
}
#[cfg(feature = "stream")]
struct FramedChunkedReader<'a> {
input: &'a [u8],
max_chunk: usize,
}
#[cfg(feature = "stream")]
impl<'a> FramedChunkedReader<'a> {
fn remaining(&self) -> &'a [u8] {
self.input
}
}
#[cfg(feature = "stream")]
impl Read for FramedChunkedReader<'_> {
fn read(&mut self, output: &mut [u8]) -> std::io::Result<usize> {
let len = self.input.len().min(self.max_chunk).min(output.len());
if len == 0 {
return Ok(0);
}
output[..len].copy_from_slice(&self.input[..len]);
self.input = &self.input[len..];
Ok(len)
}
}
#[cfg(feature = "stream")]
struct PoisoningReadError<'a> {
poison: &'a [u8],
kind: std::io::ErrorKind,
}
#[cfg(feature = "stream")]
impl Read for PoisoningReadError<'_> {
fn read(&mut self, output: &mut [u8]) -> std::io::Result<usize> {
let len = self.poison.len().min(output.len());
output[..len].copy_from_slice(&self.poison[..len]);
Err(std::io::Error::new(self.kind, "injected read failure"))
}
}
#[cfg(feature = "stream")]
#[derive(Default)]
struct FailOnceWriter {
output: Vec<u8>,
fail_next: bool,
fail_flush_next: bool,
}
#[cfg(feature = "stream")]
impl Write for FailOnceWriter {
fn write(&mut self, input: &[u8]) -> std::io::Result<usize> {
if self.fail_next {
self.fail_next = false;
return Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"injected writer failure",
));
}
self.output.extend_from_slice(input);
Ok(input.len())
}
fn flush(&mut self) -> std::io::Result<()> {
if self.fail_flush_next {
self.fail_flush_next = false;
return Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"injected flush failure",
));
}
Ok(())
}
}
#[cfg(feature = "stream")]
struct ShortWriter {
output: Vec<u8>,
max_write: usize,
write_calls: usize,
}
#[cfg(feature = "stream")]
impl Write for ShortWriter {
fn write(&mut self, input: &[u8]) -> std::io::Result<usize> {
self.write_calls += 1;
let written = input.len().min(self.max_write);
self.output.extend_from_slice(&input[..written]);
Ok(written)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[cfg(feature = "stream")]
struct OverReportingWriter;
#[cfg(feature = "stream")]
impl Write for OverReportingWriter {
fn write(&mut self, input: &[u8]) -> std::io::Result<usize> {
Ok(input.len() + 1)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[cfg(feature = "stream")]
#[test]
fn stream_engine_convenience_constructors_attach_policy() {
let mut encoder = STANDARD.encoder_writer(Vec::new());
assert_eq!(encoder.engine(), STANDARD);
encoder.write_all(b"hello").unwrap();
assert_eq!(encoder.finish().unwrap(), b"aGVsbG8=");
let mut decoder = STANDARD.decoder_writer(Vec::new());
assert_eq!(decoder.engine(), STANDARD);
decoder.write_all(b"aGVsbG8=").unwrap();
assert_eq!(decoder.finish().unwrap(), b"hello");
let mut reader = URL_SAFE_NO_PAD.encoder_reader(&b"\xfb\xff"[..]);
assert_eq!(reader.engine(), URL_SAFE_NO_PAD);
let mut encoded = String::new();
reader.read_to_string(&mut encoded).unwrap();
assert_eq!(encoded, "-_8");
let mut reader = URL_SAFE_NO_PAD.decoder_reader(&b"-_8"[..]);
assert_eq!(reader.engine(), URL_SAFE_NO_PAD);
let mut decoded = Vec::new();
reader.read_to_end(&mut decoded).unwrap();
assert_eq!(decoded, b"\xfb\xff");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_handles_chunk_boundaries() {
let mut encoder = Encoder::new(Vec::new(), STANDARD);
assert_eq!(encoder.engine(), STANDARD);
assert!(encoder.is_padded());
assert_eq!(encoder.pending_len(), 0);
assert_eq!(encoder.pending_input_needed_len(), 0);
assert!(!encoder.has_pending_input());
encoder.write_all(b"h").unwrap();
assert_eq!(encoder.pending_len(), 1);
assert_eq!(encoder.pending_input_needed_len(), 2);
assert!(encoder.has_pending_input());
encoder.write_all(b"el").unwrap();
assert_eq!(encoder.pending_len(), 0);
assert_eq!(encoder.pending_input_needed_len(), 0);
assert!(!encoder.has_pending_input());
assert_eq!(encoder.buffered_output_len(), 4);
assert_eq!(encoder.buffered_output_capacity(), 1024);
assert_eq!(encoder.buffered_output_remaining_capacity(), 1020);
assert!(encoder.has_buffered_output());
encoder.write_all(b"lo").unwrap();
assert_eq!(encoder.pending_len(), 2);
assert_eq!(encoder.pending_input_needed_len(), 1);
let encoded = encoder.finish().unwrap();
assert_eq!(encoded, b"aGVsbG8=");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_supports_no_padding() {
let mut encoder = Encoder::new(Vec::new(), STANDARD_NO_PAD);
assert_eq!(encoder.engine(), STANDARD_NO_PAD);
assert!(!encoder.is_padded());
encoder.write_all(b"he").unwrap();
encoder.write_all(b"llo").unwrap();
let encoded = encoder.finish().unwrap();
assert_eq!(encoded, b"aGVsbG8");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_exposes_inner_writer() {
let mut encoder = Encoder::new(Vec::new(), URL_SAFE_NO_PAD);
assert!(encoder.get_ref().is_empty());
encoder.write_all(b"\xfb\xff").unwrap();
assert!(encoder.get_ref().is_empty());
let encoded = encoder.finish().unwrap();
assert_eq!(encoded, b"-_8");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_try_finish_keeps_adapter_available() {
let mut encoder = Encoder::new(Vec::new(), STANDARD);
assert!(!encoder.is_finalized());
encoder.write_all(b"he").unwrap();
assert!(encoder.has_pending_input());
encoder.try_finish().unwrap();
assert!(encoder.is_finalized());
assert_eq!(encoder.get_ref(), b"aGU=");
assert_eq!(encoder.pending_len(), 0);
assert!(!encoder.has_pending_input());
let err = encoder.write(&[]).unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
let err = encoder.write_all(b"llo").unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
let inner = encoder.finish().unwrap();
assert_eq!(inner, b"aGU=");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_try_finish_write_failure_buffers_output_for_retry() {
let writer = FailOnceWriter {
output: Vec::new(),
fail_next: true,
fail_flush_next: false,
};
let mut encoder = Encoder::new(writer, STANDARD);
encoder.write_all(b"he").unwrap();
let err = encoder.try_finish().unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::BrokenPipe);
assert!(encoder.is_finalized());
assert_eq!(encoder.pending_len(), 0);
assert!(!encoder.has_pending_input());
assert!(encoder.has_buffered_output());
assert_eq!(encoder.get_ref().output, b"");
encoder.try_finish().unwrap();
assert!(encoder.is_finalized());
assert_eq!(encoder.pending_len(), 0);
assert!(!encoder.has_buffered_output());
assert_eq!(encoder.get_ref().output, b"aGU=");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_try_finish_flush_failure_does_not_reemit_final_quantum() {
let writer = FailOnceWriter {
output: Vec::new(),
fail_next: false,
fail_flush_next: true,
};
let mut encoder = Encoder::new(writer, STANDARD);
encoder.write_all(b"he").unwrap();
let err = encoder.try_finish().unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::BrokenPipe);
assert!(encoder.is_finalized());
assert_eq!(encoder.pending_len(), 0);
assert!(!encoder.has_pending_input());
assert_eq!(encoder.get_ref().output, b"aGU=");
encoder.try_finish().unwrap();
assert!(encoder.is_finalized());
assert_eq!(encoder.get_ref().output, b"aGU=");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_write_failure_preserves_pending_input() {
let writer = FailOnceWriter {
output: Vec::new(),
fail_next: true,
fail_flush_next: false,
};
let mut encoder = Encoder::new(writer, STANDARD);
encoder.write_all(b"h").unwrap();
assert_eq!(encoder.pending_len(), 1);
assert!(encoder.has_pending_input());
encoder.write_all(b"el").unwrap();
assert_eq!(encoder.pending_len(), 0);
assert!(!encoder.has_pending_input());
assert!(encoder.has_buffered_output());
let err = encoder.flush().unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::BrokenPipe);
assert_eq!(encoder.pending_len(), 0);
assert!(!encoder.has_pending_input());
assert!(encoder.has_buffered_output());
assert_eq!(encoder.get_ref().output, b"");
encoder.flush().unwrap();
assert_eq!(encoder.pending_len(), 0);
assert!(!encoder.has_pending_input());
assert!(!encoder.has_buffered_output());
assert_eq!(encoder.get_ref().output, b"aGVs");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_direct_write_buffers_tail_bytes() {
let mut encoder = Encoder::new(Vec::new(), STANDARD);
let written = encoder.write(b"hello").unwrap();
assert_eq!(written, 5);
assert_eq!(encoder.get_ref(), b"");
assert_eq!(encoder.buffered_output_len(), 4);
assert!(encoder.has_buffered_output());
assert_eq!(encoder.pending_len(), 2);
assert!(!encoder.can_into_inner());
let encoded = encoder.finish().unwrap();
assert_eq!(encoded, b"aGVsbG8=");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_direct_write_reports_partial_progress_for_large_input() {
let input = vec![b'a'; 1025];
let mut encoder = Encoder::new(Vec::new(), STANDARD);
let written = encoder.write(&input).unwrap();
assert_eq!(written, 768);
assert_eq!(encoder.buffered_output_len(), 1024);
assert_eq!(encoder.pending_len(), 0);
assert!(encoder.has_buffered_output());
encoder.write_all(&input[written..]).unwrap();
let encoded = encoder.finish().unwrap();
assert_eq!(encoded, STANDARD.encode_vec(&input).unwrap());
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_drains_buffered_output_with_short_writes() {
let writer = ShortWriter {
output: Vec::new(),
max_write: 2,
write_calls: 0,
};
let mut encoder = Encoder::new(writer, STANDARD);
assert_eq!(encoder.write(b"hello").unwrap(), 5);
assert_eq!(encoder.buffered_output_len(), 4);
assert_eq!(encoder.pending_len(), 2);
encoder.flush().unwrap();
assert_eq!(encoder.buffered_output_len(), 0);
assert_eq!(encoder.get_ref().output, b"aGVs");
assert_eq!(encoder.get_ref().write_calls, 2);
let writer = encoder.finish().unwrap();
assert_eq!(writer.output, b"aGVsbG8=");
assert_eq!(writer.write_calls, 4);
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_into_inner_still_returns_writer() {
let mut encoder = Encoder::new(Vec::new(), STANDARD);
encoder.write_all(b"he").unwrap();
let inner = encoder.into_inner();
assert!(inner.is_empty());
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_try_into_inner_rejects_pending_input() {
let mut encoder = Encoder::new(Vec::new(), STANDARD);
assert!(encoder.can_into_inner());
encoder.write_all(b"he").unwrap();
assert!(!encoder.can_into_inner());
let mut encoder = match encoder.try_into_inner() {
Ok(_) => panic!("pending stream encoder was recovered"),
Err(encoder) => encoder,
};
assert_eq!(encoder.pending_len(), 2);
encoder.try_finish().unwrap();
assert!(encoder.can_into_inner());
assert_eq!(encoder.try_into_inner().unwrap(), b"aGU=");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_try_into_inner_returns_writer_without_pending_input() {
let mut encoder = Encoder::new(Vec::new(), STANDARD);
encoder.write_all(b"hel").unwrap();
assert!(!encoder.can_into_inner());
assert!(encoder.has_buffered_output());
encoder.flush().unwrap();
assert!(encoder.can_into_inner());
let inner = encoder.try_into_inner().unwrap();
assert_eq!(inner, b"aGVs");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_marks_failed_after_unrecoverable_internal_error() {
let mut encoder = Encoder::new(OverReportingWriter, STANDARD);
encoder.write_all(b"hel").unwrap();
let err = encoder.flush().unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
assert!(encoder.is_failed());
assert!(!encoder.can_into_inner());
assert_eq!(
encoder.write(b"lo").unwrap_err().kind(),
std::io::ErrorKind::InvalidInput
);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_marks_failed_after_unrecoverable_internal_error() {
let mut decoder = Decoder::new(OverReportingWriter, STANDARD);
assert_eq!(decoder.write(b"aGk=AA").unwrap(), 4);
let err = decoder.flush().unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
assert!(decoder.is_failed());
assert!(!decoder.can_into_inner());
assert_eq!(
decoder.write(b"AA").unwrap_err().kind(),
std::io::ErrorKind::InvalidInput
);
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_reader_handles_small_reads() {
let mut reader = EncoderReader::new(&b"hello"[..], STANDARD);
assert_eq!(reader.engine(), STANDARD);
assert!(reader.is_padded());
assert!(!reader.has_finished_input());
assert!(!reader.is_finished());
assert_eq!(reader.pending_len(), 0);
assert_eq!(reader.pending_input_needed_len(), 0);
assert!(!reader.has_pending_input());
assert_eq!(reader.buffered_output_len(), 0);
assert_eq!(reader.buffered_output_capacity(), 1024);
assert_eq!(reader.buffered_output_remaining_capacity(), 1024);
assert!(!reader.has_buffered_output());
let mut output = [0u8; 8];
let mut written = 0;
while written < output.len() {
let read = reader.read(&mut output[written..written + 1]).unwrap();
if read == 0 {
break;
}
written += read;
}
assert_eq!(&output[..written], b"aGVsbG8=");
assert_eq!(reader.pending_len(), 0);
assert_eq!(reader.pending_input_needed_len(), 0);
assert!(!reader.has_pending_input());
assert_eq!(reader.buffered_output_len(), 0);
assert_eq!(reader.buffered_output_remaining_capacity(), 1024);
assert!(!reader.has_buffered_output());
assert!(reader.has_finished_input());
assert!(reader.is_finished());
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_reader_reports_buffered_output() {
let mut reader = EncoderReader::new(&b"hello"[..], STANDARD);
assert!(!reader.is_failed());
assert!(!reader.is_finished());
let mut first = [0u8; 1];
assert_eq!(reader.read(&mut first).unwrap(), 1);
assert_eq!(first, [b'a']);
assert_eq!(reader.pending_len(), 2);
assert_eq!(reader.pending_input_needed_len(), 1);
assert!(reader.has_pending_input());
assert_eq!(reader.buffered_output_len(), 3);
assert_eq!(reader.buffered_output_capacity(), 1024);
assert_eq!(reader.buffered_output_remaining_capacity(), 1021);
assert!(reader.has_buffered_output());
assert!(!reader.has_finished_input());
assert!(!reader.is_finished());
let mut rest = Vec::new();
reader.read_to_end(&mut rest).unwrap();
assert_eq!(rest, b"GVsbG8=");
assert_eq!(reader.buffered_output_len(), 0);
assert_eq!(reader.pending_input_needed_len(), 0);
assert_eq!(reader.buffered_output_remaining_capacity(), 1024);
assert!(!reader.has_buffered_output());
assert!(reader.has_finished_input());
assert!(reader.is_finished());
assert!(!reader.is_failed());
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_reader_finished_input_before_buffer_drain() {
let mut reader = EncoderReader::new(&b"h"[..], STANDARD);
assert!(!reader.has_finished_input());
assert!(!reader.is_finished());
let mut first = [0u8; 1];
assert_eq!(reader.read(&mut first).unwrap(), 1);
assert_eq!(first, [b'a']);
assert_eq!(reader.pending_len(), 0);
assert_eq!(reader.buffered_output_len(), 3);
assert!(reader.has_buffered_output());
assert!(reader.has_finished_input());
assert!(!reader.is_finished());
let mut rest = Vec::new();
reader.read_to_end(&mut rest).unwrap();
assert_eq!(rest, b"A==");
assert_eq!(reader.buffered_output_len(), 0);
assert!(!reader.has_buffered_output());
assert!(reader.has_finished_input());
assert!(reader.is_finished());
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_reader_supports_no_padding() {
let mut reader = EncoderReader::new(&b"hello"[..], STANDARD_NO_PAD);
assert_eq!(reader.engine(), STANDARD_NO_PAD);
assert!(!reader.is_padded());
let mut encoded = Vec::new();
reader.read_to_end(&mut encoded).unwrap();
assert_eq!(encoded, b"aGVsbG8");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_reader_supports_url_safe() {
let mut reader = EncoderReader::new(&b"\xfb\xff"[..], URL_SAFE_NO_PAD);
let mut encoded = Vec::new();
assert_eq!(reader.get_ref().len(), 2);
reader.read_to_end(&mut encoded).unwrap();
assert_eq!(encoded, b"-_8");
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_reader_into_inner_still_returns_reader() {
let reader = EncoderReader::new(&b"hello"[..], STANDARD);
let inner = reader.into_inner();
assert_eq!(inner, &b"hello"[..]);
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_reader_try_into_inner_rejects_buffered_output() {
let mut reader = EncoderReader::new(Cursor::new(&b"hello"[..]), STANDARD);
assert!(!reader.can_into_inner());
let mut first = [0u8; 1];
assert_eq!(reader.read(&mut first).unwrap(), 1);
assert!(!reader.can_into_inner());
let mut reader = match reader.try_into_inner() {
Ok(_) => panic!("buffered encoder reader was recovered"),
Err(reader) => reader,
};
assert!(reader.has_buffered_output());
let mut rest = Vec::new();
reader.read_to_end(&mut rest).unwrap();
assert!(reader.can_into_inner());
assert_eq!(reader.try_into_inner().unwrap().position(), 5);
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_reader_try_into_inner_returns_finished_reader() {
let mut reader = EncoderReader::new(Cursor::new(&b"hello"[..]), STANDARD);
let mut encoded = Vec::new();
reader.read_to_end(&mut encoded).unwrap();
assert!(reader.can_into_inner());
let inner = reader.try_into_inner().unwrap();
assert_eq!(encoded, b"aGVsbG8=");
assert_eq!(inner.position(), 5);
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_reader_handles_fragmented_sources() {
let input = b"any carnal pleasure.";
let source = ChunkedReader {
input,
max_chunk: 1,
};
let mut reader = EncoderReader::new(source, STANDARD);
let mut encoded = Vec::new();
let mut scratch = [0u8; 2];
loop {
let read = reader.read(&mut scratch).unwrap();
if read == 0 {
break;
}
encoded.extend_from_slice(&scratch[..read]);
}
assert_eq!(encoded, STANDARD.encode_vec(input).unwrap());
}
#[cfg(feature = "stream")]
#[test]
fn stream_encoder_reader_propagates_read_error_after_wiping_input_buffer() {
let source = PoisoningReadError {
poison: b"raw-private-key-material",
kind: std::io::ErrorKind::Interrupted,
};
let mut reader = EncoderReader::new(source, STANDARD);
let mut output = [0u8; 8];
let err = reader.read(&mut output).unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::Interrupted);
assert_eq!(reader.pending_len(), 0);
assert!(!reader.has_pending_input());
assert_eq!(reader.buffered_output_len(), 0);
assert!(!reader.has_buffered_output());
assert!(!reader.has_finished_input());
assert!(!reader.is_finished());
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_handles_chunk_boundaries() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
assert_eq!(decoder.engine(), STANDARD);
assert!(decoder.is_padded());
assert_eq!(decoder.pending_len(), 0);
assert_eq!(decoder.pending_input_needed_len(), 0);
assert!(!decoder.has_pending_input());
assert!(!decoder.has_terminal_padding());
decoder.write_all(b"a").unwrap();
assert_eq!(decoder.pending_len(), 1);
assert_eq!(decoder.pending_input_needed_len(), 3);
assert!(decoder.has_pending_input());
decoder.write_all(b"GVs").unwrap();
assert_eq!(decoder.pending_len(), 0);
assert_eq!(decoder.pending_input_needed_len(), 0);
assert!(!decoder.has_pending_input());
assert_eq!(decoder.buffered_output_len(), 3);
assert_eq!(decoder.buffered_output_capacity(), 1024);
assert_eq!(decoder.buffered_output_remaining_capacity(), 1021);
assert!(decoder.has_buffered_output());
decoder.write_all(b"bG8=").unwrap();
assert!(decoder.has_terminal_padding());
let decoded = decoder.finish().unwrap();
assert_eq!(decoded, b"hello");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_supports_no_padding() {
let mut decoder = Decoder::new(Vec::new(), STANDARD_NO_PAD);
assert_eq!(decoder.engine(), STANDARD_NO_PAD);
assert!(!decoder.is_padded());
decoder.write_all(b"aGV").unwrap();
decoder.write_all(b"sbG8").unwrap();
let decoded = decoder.finish().unwrap();
assert_eq!(decoded, b"hello");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_try_finish_keeps_adapter_available() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
assert!(!decoder.is_finalized());
decoder.write_all(b"aGk=").unwrap();
assert!(decoder.has_terminal_padding());
decoder.try_finish().unwrap();
assert!(decoder.is_finalized());
assert_eq!(decoder.get_ref(), b"hi");
assert_eq!(decoder.pending_len(), 0);
assert!(!decoder.has_pending_input());
let err = decoder.write_all(b"AA").unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
let inner = decoder.finish().unwrap();
assert_eq!(inner, b"hi");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_try_finish_terminal_for_unpadded_payloads() {
let mut decoder = Decoder::new(Vec::new(), STANDARD_NO_PAD);
assert!(!decoder.is_finalized());
decoder.write_all(b"aGk").unwrap();
decoder.try_finish().unwrap();
assert!(decoder.is_finalized());
assert_eq!(decoder.get_ref(), b"hi");
assert_eq!(decoder.pending_len(), 0);
assert!(!decoder.has_terminal_padding());
let err = decoder.write_all(b"AA").unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_try_finish_flush_failure_does_not_reemit_final_bytes() {
let writer = FailOnceWriter {
output: Vec::new(),
fail_next: false,
fail_flush_next: true,
};
let mut decoder = Decoder::new(writer, STANDARD_NO_PAD);
decoder.write_all(b"aGk").unwrap();
let err = decoder.try_finish().unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::BrokenPipe);
assert!(decoder.is_finalized());
assert_eq!(decoder.pending_len(), 0);
assert!(!decoder.has_pending_input());
assert_eq!(decoder.get_ref().output, b"hi");
decoder.try_finish().unwrap();
assert!(decoder.is_finalized());
assert_eq!(decoder.get_ref().output, b"hi");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_try_finish_write_failure_buffers_output_for_retry() {
let writer = FailOnceWriter {
output: Vec::new(),
fail_next: true,
fail_flush_next: false,
};
let mut decoder = Decoder::new(writer, STANDARD_NO_PAD);
decoder.write_all(b"aGk").unwrap();
let err = decoder.try_finish().unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::BrokenPipe);
assert!(decoder.is_finalized());
assert_eq!(decoder.pending_len(), 0);
assert!(!decoder.has_pending_input());
assert!(decoder.has_buffered_output());
assert_eq!(decoder.get_ref().output, b"");
decoder.try_finish().unwrap();
assert!(decoder.is_finalized());
assert_eq!(decoder.pending_len(), 0);
assert!(!decoder.has_pending_input());
assert!(!decoder.has_buffered_output());
assert_eq!(decoder.get_ref().output, b"hi");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_write_failure_preserves_pending_input() {
let writer = FailOnceWriter {
output: Vec::new(),
fail_next: true,
fail_flush_next: false,
};
let mut decoder = Decoder::new(writer, STANDARD);
decoder.write_all(b"a").unwrap();
assert_eq!(decoder.pending_len(), 1);
assert!(decoder.has_pending_input());
decoder.write_all(b"Gk=").unwrap();
assert_eq!(decoder.pending_len(), 0);
assert!(!decoder.has_pending_input());
assert!(decoder.has_terminal_padding());
assert!(decoder.has_buffered_output());
let err = decoder.flush().unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::BrokenPipe);
assert_eq!(decoder.pending_len(), 0);
assert!(!decoder.has_pending_input());
assert!(decoder.has_terminal_padding());
assert!(decoder.has_buffered_output());
assert_eq!(decoder.get_ref().output, b"");
decoder.flush().unwrap();
assert_eq!(decoder.pending_len(), 0);
assert!(!decoder.has_pending_input());
assert!(decoder.has_terminal_padding());
assert!(!decoder.has_buffered_output());
assert_eq!(decoder.get_ref().output, b"hi");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_direct_write_processes_multiple_quads() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
let written = decoder.write(b"aGVsbG8=").unwrap();
assert_eq!(written, 8);
assert_eq!(decoder.buffered_output_len(), 5);
assert!(decoder.has_terminal_padding());
let decoded = decoder.finish().unwrap();
assert_eq!(decoded, b"hello");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_direct_write_continues_after_pending_quad() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
assert_eq!(decoder.write(b"a").unwrap(), 1);
assert_eq!(decoder.pending_len(), 1);
let written = decoder.write(b"GVsbG8=").unwrap();
assert_eq!(written, 7);
assert_eq!(decoder.pending_len(), 0);
assert_eq!(decoder.buffered_output_len(), 5);
assert!(decoder.has_terminal_padding());
let decoded = decoder.finish().unwrap();
assert_eq!(decoded, b"hello");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_direct_write_reports_partial_progress_for_large_input() {
let input = vec![b'a'; 1500];
let encoded = STANDARD.encode_vec(&input).unwrap();
let mut decoder = Decoder::new(Vec::new(), STANDARD);
let written = decoder.write(&encoded).unwrap();
assert_eq!(written, 1364);
assert_eq!(decoder.buffered_output_len(), 1023);
assert_eq!(decoder.pending_len(), 0);
assert!(decoder.has_buffered_output());
decoder.write_all(&encoded[written..]).unwrap();
let decoded = decoder.finish().unwrap();
assert_eq!(decoded, input);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_direct_write_reports_partial_progress() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
let written = decoder.write(b"aGk=AA").unwrap();
assert_eq!(written, 4);
assert_eq!(decoder.get_ref(), b"");
assert_eq!(decoder.buffered_output_len(), 2);
assert!(decoder.has_buffered_output());
assert!(decoder.has_terminal_padding());
assert!(!decoder.can_into_inner());
decoder.flush().unwrap();
assert_eq!(decoder.get_ref(), b"hi");
assert!(!decoder.has_buffered_output());
let err = decoder.write(b"AA").unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_direct_write_marks_failed_after_deferred_error() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
let written = decoder.write(b"Zm9v$$$$").unwrap();
assert_eq!(written, 4);
assert!(decoder.is_failed());
assert_eq!(decoder.buffered_output_len(), 3);
let err = decoder.write(b"YmFy").unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
assert!(decoder.is_failed());
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_drains_buffered_output_with_short_writes() {
let writer = ShortWriter {
output: Vec::new(),
max_write: 1,
write_calls: 0,
};
let mut decoder = Decoder::new(writer, STANDARD);
assert_eq!(decoder.write(b"aGk=AA").unwrap(), 4);
assert_eq!(decoder.buffered_output_len(), 2);
decoder.flush().unwrap();
assert_eq!(decoder.buffered_output_len(), 0);
assert_eq!(decoder.get_ref().output, b"hi");
assert_eq!(decoder.get_ref().write_calls, 2);
let err = decoder.write(b"AA").unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_rejects_bad_final_pending_input() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
decoder.write_all(b"a").unwrap();
assert!(decoder.finish().is_err());
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_try_finish_reports_bad_final_pending_input() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
decoder.write_all(b"a").unwrap();
let err = decoder.try_finish().unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
assert!(!decoder.is_finalized());
assert!(decoder.is_failed());
assert_eq!(decoder.pending_len(), 0);
assert!(!decoder.has_pending_input());
assert!(!decoder.can_into_inner());
assert!(decoder.get_ref().is_empty());
assert_eq!(
decoder.write_all(b"Gk=").unwrap_err().kind(),
std::io::ErrorKind::InvalidInput
);
assert_eq!(
decoder.try_finish().unwrap_err().kind(),
std::io::ErrorKind::InvalidInput
);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_rejects_trailing_input_after_padding() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
let err = decoder.write_all(b"aGk=AA").unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
assert!(decoder.is_failed());
assert!(!decoder.can_into_inner());
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_rejects_short_trailing_input_after_pending_padding() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
decoder.write_all(b"aG").unwrap();
let err = decoder.write_all(b"k=A").unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
assert!(decoder.is_failed());
assert_eq!(decoder.pending_len(), 0);
assert!(!decoder.can_into_inner());
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_fails_closed_after_malformed_input() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
let err = decoder.write(b"!!!!").unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
assert!(decoder.is_failed());
assert!(!decoder.is_finalized());
assert_eq!(decoder.pending_len(), 0);
assert!(!decoder.can_into_inner());
assert_eq!(
decoder.write(b"aGk=").unwrap_err().kind(),
std::io::ErrorKind::InvalidInput
);
assert_eq!(
decoder.flush().unwrap_err().kind(),
std::io::ErrorKind::InvalidInput
);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_exposes_inner_writer_after_refactor() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
assert!(decoder.get_ref().is_empty());
decoder.write_all(b"aGk=").unwrap();
assert!(decoder.has_buffered_output());
assert!(decoder.get_ref().is_empty());
decoder.flush().unwrap();
assert!(!decoder.has_buffered_output());
assert_eq!(decoder.get_ref(), b"hi");
let inner = decoder.finish().unwrap();
assert_eq!(inner, b"hi");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_into_inner_still_returns_writer() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
decoder.write_all(b"a").unwrap();
let inner = decoder.into_inner();
assert!(inner.is_empty());
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_try_into_inner_rejects_pending_input() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
assert!(decoder.can_into_inner());
decoder.write_all(b"aG").unwrap();
assert!(!decoder.can_into_inner());
let mut decoder = match decoder.try_into_inner() {
Ok(_) => panic!("pending stream decoder was recovered"),
Err(decoder) => decoder,
};
assert_eq!(decoder.pending_len(), 2);
decoder.write_all(b"k=").unwrap();
assert!(!decoder.can_into_inner());
assert!(decoder.has_buffered_output());
decoder.flush().unwrap();
assert!(decoder.can_into_inner());
assert_eq!(decoder.try_into_inner().unwrap(), b"hi");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_try_into_inner_returns_writer_without_pending_input() {
let mut decoder = Decoder::new(Vec::new(), STANDARD);
decoder.write_all(b"aGk=").unwrap();
assert!(!decoder.can_into_inner());
assert!(decoder.has_buffered_output());
decoder.flush().unwrap();
assert!(decoder.can_into_inner());
let inner = decoder.try_into_inner().unwrap();
assert_eq!(inner, b"hi");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_handles_small_reads() {
let mut reader = DecoderReader::new(&b"aGVsbG8="[..], STANDARD);
assert_eq!(reader.engine(), STANDARD);
assert!(reader.is_padded());
assert!(!reader.has_finished_input());
assert!(!reader.is_finished());
assert_eq!(reader.pending_len(), 0);
assert_eq!(reader.pending_input_needed_len(), 0);
assert!(!reader.has_pending_input());
assert_eq!(reader.buffered_output_len(), 0);
assert_eq!(reader.buffered_output_capacity(), 3);
assert_eq!(reader.buffered_output_remaining_capacity(), 3);
assert!(!reader.has_buffered_output());
assert!(!reader.has_terminal_padding());
let mut output = [0u8; 5];
let mut written = 0;
while written < output.len() {
let read = reader.read(&mut output[written..written + 1]).unwrap();
if read == 0 {
break;
}
written += read;
}
assert_eq!(&output[..written], b"hello");
assert_eq!(reader.pending_len(), 0);
assert_eq!(reader.pending_input_needed_len(), 0);
assert_eq!(reader.buffered_output_len(), 0);
assert_eq!(reader.buffered_output_remaining_capacity(), 3);
assert!(!reader.has_buffered_output());
assert!(reader.has_terminal_padding());
assert!(reader.has_finished_input());
assert!(reader.is_finished());
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_reports_buffered_output() {
let mut reader = DecoderReader::new(&b"aGVsbG8="[..], STANDARD);
assert!(!reader.is_finished());
let mut first = [0u8; 1];
assert_eq!(reader.read(&mut first).unwrap(), 1);
assert_eq!(first, [b'h']);
assert_eq!(reader.buffered_output_len(), 2);
assert_eq!(reader.buffered_output_capacity(), 3);
assert_eq!(reader.buffered_output_remaining_capacity(), 1);
assert!(reader.has_buffered_output());
assert!(!reader.has_terminal_padding());
assert!(!reader.has_finished_input());
assert!(!reader.is_finished());
let mut rest = Vec::new();
reader.read_to_end(&mut rest).unwrap();
assert_eq!(rest, b"ello");
assert_eq!(reader.buffered_output_len(), 0);
assert_eq!(reader.buffered_output_remaining_capacity(), 3);
assert!(!reader.has_buffered_output());
assert!(reader.has_terminal_padding());
assert!(reader.has_finished_input());
assert!(reader.is_finished());
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_terminal_padding_finishes_after_buffer_drain() {
let mut reader = DecoderReader::new(&b"aGk="[..], STANDARD);
let mut first = [0u8; 1];
assert_eq!(reader.read(&mut first).unwrap(), 1);
assert_eq!(first, [b'h']);
assert_eq!(reader.buffered_output_len(), 1);
assert!(reader.has_buffered_output());
assert!(reader.has_terminal_padding());
assert!(reader.has_finished_input());
assert!(!reader.is_finished());
let mut rest = Vec::new();
reader.read_to_end(&mut rest).unwrap();
assert_eq!(rest, b"i");
assert_eq!(reader.buffered_output_len(), 0);
assert!(!reader.has_buffered_output());
assert!(reader.has_terminal_padding());
assert!(reader.has_finished_input());
assert!(reader.is_finished());
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_supports_no_padding() {
let mut reader = DecoderReader::new(&b"aGVsbG8"[..], STANDARD_NO_PAD);
assert_eq!(reader.engine(), STANDARD_NO_PAD);
assert!(!reader.is_padded());
let mut decoded = Vec::new();
reader.read_to_end(&mut decoded).unwrap();
assert_eq!(decoded, b"hello");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_supports_url_safe() {
let mut reader = DecoderReader::new(&b"-_8"[..], URL_SAFE_NO_PAD);
let mut decoded = Vec::new();
assert_eq!(reader.get_ref().len(), 3);
reader.read_to_end(&mut decoded).unwrap();
assert_eq!(decoded, b"\xfb\xff");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_rejects_bad_final_pending_input() {
let mut reader = DecoderReader::new(&b"a"[..], STANDARD);
let mut decoded = Vec::new();
assert!(reader.read_to_end(&mut decoded).is_err());
assert!(reader.is_failed());
assert_eq!(reader.pending_len(), 0);
assert!(!reader.can_into_inner());
let mut output = [0u8; 1];
assert_eq!(
reader.read(&mut output).unwrap_err().kind(),
std::io::ErrorKind::InvalidInput
);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_fails_closed_after_malformed_input() {
let mut reader = DecoderReader::new(Cursor::new(&b"!!!!aGk="[..]), STANDARD);
let mut output = [0u8; 1];
let err = reader.read(&mut output).unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
assert!(reader.is_failed());
assert!(!reader.can_into_inner());
assert_eq!(
reader.read(&mut output).unwrap_err().kind(),
std::io::ErrorKind::InvalidInput
);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_propagates_read_error_after_wiping_input_buffer() {
let source = PoisoningReadError {
poison: b"aGVs",
kind: std::io::ErrorKind::Interrupted,
};
let mut reader = DecoderReader::new(source, STANDARD);
let mut output = [0u8; 3];
let err = reader.read(&mut output).unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::Interrupted);
assert_eq!(reader.pending_len(), 0);
assert!(!reader.has_pending_input());
assert_eq!(reader.buffered_output_len(), 0);
assert!(!reader.has_buffered_output());
assert!(!reader.has_finished_input());
assert!(!reader.is_finished());
assert!(!reader.is_failed());
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_into_inner_still_returns_reader() {
let mut reader = DecoderReader::new(Cursor::new(&b"aGVsbG8="[..]), STANDARD);
let mut output = [0u8; 1];
let read = reader.read(&mut output).unwrap();
assert_eq!(read, 1);
assert_eq!(output, [b'h']);
let inner = reader.into_inner();
assert_eq!(inner.position(), 4);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_try_into_inner_rejects_buffered_output() {
let mut reader = DecoderReader::new(Cursor::new(&b"aGk=NEXT"[..]), STANDARD);
assert!(!reader.can_into_inner());
let mut first = [0u8; 1];
assert_eq!(reader.read(&mut first).unwrap(), 1);
assert!(!reader.can_into_inner());
let mut reader = match reader.try_into_inner() {
Ok(_) => panic!("buffered decoder reader was recovered"),
Err(reader) => reader,
};
assert!(reader.has_buffered_output());
let mut rest = Vec::new();
reader.read_to_end(&mut rest).unwrap();
assert!(reader.can_into_inner());
assert_eq!(reader.try_into_inner().unwrap().position(), 4);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_try_into_inner_returns_finished_reader() {
let mut reader = DecoderReader::new(Cursor::new(&b"aGk=NEXT"[..]), STANDARD);
let mut decoded = Vec::new();
reader.read_to_end(&mut decoded).unwrap();
assert!(reader.can_into_inner());
let inner = reader.try_into_inner().unwrap();
assert_eq!(decoded, b"hi");
assert_eq!(inner.position(), 4);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_leaves_trailing_input_after_padding_unread() {
let mut reader = DecoderReader::new(Cursor::new(&b"aGk=AA"[..]), STANDARD);
let mut decoded = Vec::new();
reader.read_to_end(&mut decoded).unwrap();
assert_eq!(decoded, b"hi");
assert_eq!(reader.get_ref().position(), 4);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_leaves_adjacent_payload_unread_after_padding() {
let mut reader = DecoderReader::new(Cursor::new(&b"aGk=NEXT"[..]), STANDARD);
let mut decoded = Vec::new();
reader.read_to_end(&mut decoded).unwrap();
assert_eq!(decoded, b"hi");
assert_eq!(reader.pending_len(), 0);
assert_eq!(reader.buffered_output_len(), 0);
assert!(!reader.has_buffered_output());
assert!(!reader.has_pending_input());
assert!(reader.has_terminal_padding());
assert!(reader.is_finished());
assert_eq!(reader.get_ref().position(), 4);
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_leaves_fragmented_adjacent_payload_unread() {
let source = FramedChunkedReader {
input: b"aGk=NEXT",
max_chunk: 2,
};
let mut reader = DecoderReader::new(source, STANDARD);
let mut decoded = Vec::new();
let mut scratch = [0u8; 1];
loop {
let read = reader.read(&mut scratch).unwrap();
if read == 0 {
break;
}
decoded.extend_from_slice(&scratch[..read]);
}
assert_eq!(decoded, b"hi");
assert_eq!(reader.pending_len(), 0);
assert_eq!(reader.buffered_output_len(), 0);
assert!(reader.has_terminal_padding());
assert!(reader.is_finished());
assert_eq!(reader.get_ref().remaining(), b"NEXT");
}
#[cfg(feature = "stream")]
#[test]
fn stream_decoder_reader_handles_fragmented_sources() {
let encoded = b"YW55IGNhcm5hbCBwbGVhc3VyZS4=";
let source = ChunkedReader {
input: encoded,
max_chunk: 1,
};
let mut reader = DecoderReader::new(source, STANDARD);
let mut decoded = Vec::new();
let mut scratch = [0u8; 2];
loop {
let read = reader.read(&mut scratch).unwrap();
if read == 0 {
break;
}
decoded.extend_from_slice(&scratch[..read]);
}
assert_eq!(decoded, b"any carnal pleasure.");
}
#[cfg(feature = "stream")]
#[test]
fn stream_debug_output_redacts_wrapped_io() {
let mut encoder = Encoder::new(b"raw-secret".to_vec(), STANDARD);
encoder.write_all(b"h").unwrap();
let debug = format!("{encoder:?}");
assert!(debug.contains("Encoder"));
assert!(debug.contains("pending_len"));
assert!(debug.contains("pending_input_needed_len"));
assert!(debug.contains("buffered_output_len"));
assert!(debug.contains("buffered_output_capacity"));
assert!(debug.contains("buffered_output_remaining_capacity"));
assert!(debug.contains("can_into_inner"));
assert!(debug.contains("<present>"));
assert!(!debug.contains("raw-secret"));
let mut decoder = Decoder::new(b"decoded-secret".to_vec(), STANDARD);
decoder.write_all(b"aGk=").unwrap();
let debug = format!("{decoder:?}");
assert!(debug.contains("Decoder"));
assert!(debug.contains("pending_input_needed_len"));
assert!(debug.contains("buffered_output_len"));
assert!(debug.contains("buffered_output_capacity"));
assert!(debug.contains("buffered_output_remaining_capacity"));
assert!(debug.contains("can_into_inner"));
assert!(debug.contains("terminal_padding"));
assert!(!debug.contains("decoded-secret"));
let mut decoder_reader = DecoderReader::new(Cursor::new(&b"aGVsbG8="[..]), STANDARD);
let mut one = [0u8; 1];
decoder_reader.read_exact(&mut one).unwrap();
let debug = format!("{decoder_reader:?}");
assert!(debug.contains("DecoderReader"));
assert!(debug.contains("pending_input_needed_len"));
assert!(debug.contains("buffered_output_len"));
assert!(debug.contains("buffered_output_capacity"));
assert!(debug.contains("buffered_output_remaining_capacity"));
assert!(debug.contains("can_into_inner"));
assert!(!debug.contains("aGVsbG8"));
let mut encoder_reader = EncoderReader::new(Cursor::new(&b"raw-secret"[..]), STANDARD);
encoder_reader.read_exact(&mut one).unwrap();
let debug = format!("{encoder_reader:?}");
assert!(debug.contains("EncoderReader"));
assert!(debug.contains("pending_input_needed_len"));
assert!(debug.contains("buffered_output_len"));
assert!(debug.contains("buffered_output_capacity"));
assert!(debug.contains("buffered_output_remaining_capacity"));
assert!(debug.contains("can_into_inner"));
assert!(!debug.contains("raw-secret"));
}