171 lines
6.1 KiB
Rust
171 lines
6.1 KiB
Rust
|
|
//! Equivalence tests between `base64` crate and `base64ct`.
|
||
|
|
|
||
|
|
#![cfg(feature = "std")]
|
||
|
|
// TODO(tarcieri): fix `base64` crate deprecations
|
||
|
|
// warning: use of deprecated function `base64::encode`: Use Engine::encode
|
||
|
|
#![allow(deprecated)]
|
||
|
|
|
||
|
|
use base64ct::{Base64 as Base64ct, Base64Unpadded as Base64UnpaddedCt, Encoding};
|
||
|
|
use proptest::{prelude::*, string::*};
|
||
|
|
|
||
|
|
/// Incremental Base64 decoder.
|
||
|
|
type Decoder<'a> = base64ct::Decoder<'a, Base64ct>;
|
||
|
|
|
||
|
|
/// Incremental Base64 encoder.
|
||
|
|
type Encoder<'a> = base64ct::Encoder<'a, Base64ct>;
|
||
|
|
|
||
|
|
proptest! {
|
||
|
|
/// Ensure `base64ct` decodes data encoded by `base64` ref crate
|
||
|
|
#[test]
|
||
|
|
fn decode_equiv(bytes in bytes_regex(".{0,256}").unwrap()) {
|
||
|
|
let encoded = base64::encode(&bytes);
|
||
|
|
let decoded = Base64ct::decode_vec(&encoded);
|
||
|
|
prop_assert_eq!(Ok(bytes), decoded);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Ensure that `base64ct`'s incremental decoder is able to decode randomly
|
||
|
|
/// generated inputs encoded by the `base64` ref crate
|
||
|
|
#[test]
|
||
|
|
fn decode_incremental(bytes in bytes_regex(".{1,256}").unwrap(), chunk_size in 1..256usize) {
|
||
|
|
let encoded = base64::encode(&bytes);
|
||
|
|
let chunk_size = match chunk_size % bytes.len() {
|
||
|
|
0 => 1,
|
||
|
|
n => n
|
||
|
|
};
|
||
|
|
|
||
|
|
let mut buffer = [0u8; 384];
|
||
|
|
let mut decoder = Decoder::new(encoded.as_bytes()).unwrap();
|
||
|
|
let mut remaining_len = decoder.remaining_len();
|
||
|
|
|
||
|
|
for chunk in bytes.chunks(chunk_size) {
|
||
|
|
prop_assert!(!decoder.is_finished());
|
||
|
|
|
||
|
|
let decoded = decoder.decode(&mut buffer[..chunk.len()]);
|
||
|
|
prop_assert_eq!(Ok(chunk), decoded);
|
||
|
|
|
||
|
|
remaining_len -= decoded.unwrap().len();
|
||
|
|
prop_assert_eq!(remaining_len, decoder.remaining_len());
|
||
|
|
}
|
||
|
|
|
||
|
|
prop_assert!(decoder.is_finished());
|
||
|
|
prop_assert_eq!(decoder.remaining_len(), 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn decode_incremental_wrapped(
|
||
|
|
bytes in bytes_regex(".{1,256}").unwrap(),
|
||
|
|
line_width in 4..128usize,
|
||
|
|
chunk_size in 1..256usize
|
||
|
|
) {
|
||
|
|
for line_ending in ["\r", "\n", "\r\n"] {
|
||
|
|
let encoded = base64::encode(&bytes);
|
||
|
|
|
||
|
|
let mut encoded_wrapped = Vec::new();
|
||
|
|
let mut lines = encoded.as_bytes().chunks_exact(line_width);
|
||
|
|
|
||
|
|
for line in &mut lines {
|
||
|
|
encoded_wrapped.extend_from_slice(line);
|
||
|
|
encoded_wrapped.extend_from_slice(line_ending.as_bytes());
|
||
|
|
}
|
||
|
|
|
||
|
|
let last = lines.remainder();
|
||
|
|
|
||
|
|
if last.is_empty() {
|
||
|
|
encoded_wrapped.truncate(encoded_wrapped.len() - line_ending.len());
|
||
|
|
} else {
|
||
|
|
encoded_wrapped.extend_from_slice(last);
|
||
|
|
}
|
||
|
|
|
||
|
|
let chunk_size = match chunk_size % bytes.len() {
|
||
|
|
0 => 1,
|
||
|
|
n => n
|
||
|
|
};
|
||
|
|
|
||
|
|
let mut buffer = [0u8; 384];
|
||
|
|
let mut decoder = Decoder::new_wrapped(&encoded_wrapped, line_width).unwrap();
|
||
|
|
let mut remaining_len = decoder.remaining_len();
|
||
|
|
|
||
|
|
for chunk in bytes.chunks(chunk_size) {
|
||
|
|
prop_assert!(!decoder.is_finished());
|
||
|
|
|
||
|
|
let decoded = decoder.decode(&mut buffer[..chunk.len()]);
|
||
|
|
prop_assert_eq!(Ok(chunk), decoded);
|
||
|
|
|
||
|
|
remaining_len -= decoded.unwrap().len();
|
||
|
|
prop_assert_eq!(remaining_len, decoder.remaining_len());
|
||
|
|
}
|
||
|
|
|
||
|
|
prop_assert!(decoder.is_finished());
|
||
|
|
prop_assert_eq!(decoder.remaining_len(), 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Ensure `base64ct` and `base64` ref crate decode randomly generated
|
||
|
|
/// inputs equivalently.
|
||
|
|
///
|
||
|
|
/// Inputs are selected to be valid characters in the standard Base64
|
||
|
|
/// padded alphabet, but are not necessarily valid Base64.
|
||
|
|
#[test]
|
||
|
|
fn decode_random(base64ish in string_regex("[A-Za-z0-9+/]{0,256}").unwrap()) {
|
||
|
|
let base64ish_padded = match base64ish.len() % 4 {
|
||
|
|
0 => base64ish,
|
||
|
|
n => {
|
||
|
|
let padding_len = 4 - n;
|
||
|
|
base64ish + &"=".repeat(padding_len)
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
let decoded_ct = Base64ct::decode_vec(&base64ish_padded).ok();
|
||
|
|
let decoded_ref = base64::decode(&base64ish_padded).ok();
|
||
|
|
prop_assert_eq!(decoded_ct, decoded_ref);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Ensure `base64ct` and the `base64` ref crate encode randomly generated
|
||
|
|
/// inputs equivalently.
|
||
|
|
#[test]
|
||
|
|
fn encode_equiv(bytes in bytes_regex(".{0,256}").unwrap()) {
|
||
|
|
let encoded_ct = Base64ct::encode_string(&bytes);
|
||
|
|
let encoded_ref = base64::encode(&bytes);
|
||
|
|
prop_assert_eq!(encoded_ct, encoded_ref);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Ensure that `base64ct`'s incremental encoder is able to encode randomly
|
||
|
|
/// generated inputs which match what's encoded by the `base64` ref crate
|
||
|
|
#[test]
|
||
|
|
fn encode_incremental(bytes in bytes_regex(".{1,256}").unwrap(), chunk_size in 1..256usize) {
|
||
|
|
let expected = base64::encode(&bytes);
|
||
|
|
let chunk_size = match chunk_size % bytes.len() {
|
||
|
|
0 => 1,
|
||
|
|
n => n
|
||
|
|
};
|
||
|
|
|
||
|
|
let mut buffer = [0u8; 1024];
|
||
|
|
let mut encoder = Encoder::new(&mut buffer).unwrap();
|
||
|
|
|
||
|
|
for chunk in bytes.chunks(chunk_size) {
|
||
|
|
encoder.encode(chunk).unwrap();
|
||
|
|
}
|
||
|
|
|
||
|
|
prop_assert_eq!(expected, encoder.finish().unwrap());
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Make sure that base64ct and base64 both decode the same values
|
||
|
|
/// when expecting padded inputs, and produce the same outputs for those values.
|
||
|
|
#[test]
|
||
|
|
fn decode_arbitrary_padded(string in string_regex("[a-zA-Z0-9/+=?]{0,256}").unwrap()) {
|
||
|
|
let actual = Base64ct::decode_vec(&string);
|
||
|
|
let expected = base64::decode( &string);
|
||
|
|
assert_eq!(actual.ok(), expected.ok());
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Make sure that base64ct and base64 both decode the same values
|
||
|
|
/// when expecting unpadded inputs, and produce the same outputs for those values.
|
||
|
|
#[test]
|
||
|
|
fn decode_arbitrary_unpadded(string in string_regex("[a-zA-Z0-9/+=?]{0,256}").unwrap()) {
|
||
|
|
use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _};
|
||
|
|
let actual = Base64UnpaddedCt::decode_vec(&string);
|
||
|
|
let expected = STANDARD_NO_PAD.decode(&string);
|
||
|
|
assert_eq!(actual.ok(), expected.ok());
|
||
|
|
}
|
||
|
|
}
|