118 lines
3.2 KiB
Rust
118 lines
3.2 KiB
Rust
use std::str;
|
|
|
|
use rand::{
|
|
distributions,
|
|
distributions::{Distribution as _, Uniform},
|
|
seq::SliceRandom,
|
|
Rng, SeedableRng,
|
|
};
|
|
|
|
use crate::{
|
|
alphabet,
|
|
encode::encoded_len,
|
|
engine::{
|
|
general_purpose::{GeneralPurpose, GeneralPurposeConfig},
|
|
Config, DecodePaddingMode, Engine,
|
|
},
|
|
};
|
|
|
|
#[test]
|
|
fn roundtrip_random_config_short() {
|
|
// exercise the slower encode/decode routines that operate on shorter buffers more vigorously
|
|
roundtrip_random_config(Uniform::new(0, 50), 10_000);
|
|
}
|
|
|
|
#[test]
|
|
fn roundtrip_random_config_long() {
|
|
roundtrip_random_config(Uniform::new(0, 1000), 10_000);
|
|
}
|
|
|
|
pub fn assert_encode_sanity(encoded: &str, padded: bool, input_len: usize) {
|
|
let input_rem = input_len % 3;
|
|
let expected_padding_len = if input_rem > 0 {
|
|
if padded {
|
|
3 - input_rem
|
|
} else {
|
|
0
|
|
}
|
|
} else {
|
|
0
|
|
};
|
|
|
|
let expected_encoded_len = encoded_len(input_len, padded).unwrap();
|
|
|
|
assert_eq!(expected_encoded_len, encoded.len());
|
|
|
|
let padding_len = encoded.chars().filter(|&c| c == '=').count();
|
|
|
|
assert_eq!(expected_padding_len, padding_len);
|
|
|
|
let _ = str::from_utf8(encoded.as_bytes()).expect("Base64 should be valid utf8");
|
|
}
|
|
|
|
fn roundtrip_random_config(input_len_range: Uniform<usize>, iterations: u32) {
|
|
let mut input_buf: Vec<u8> = Vec::new();
|
|
let mut encoded_buf = String::new();
|
|
let mut rng = rand::rngs::SmallRng::from_entropy();
|
|
|
|
for _ in 0..iterations {
|
|
input_buf.clear();
|
|
encoded_buf.clear();
|
|
|
|
let input_len = input_len_range.sample(&mut rng);
|
|
|
|
let engine = random_engine(&mut rng);
|
|
|
|
for _ in 0..input_len {
|
|
input_buf.push(rng.gen());
|
|
}
|
|
|
|
engine.encode_string(&input_buf, &mut encoded_buf);
|
|
|
|
assert_encode_sanity(&encoded_buf, engine.config().encode_padding(), input_len);
|
|
|
|
assert_eq!(input_buf, engine.decode(&encoded_buf).unwrap());
|
|
}
|
|
}
|
|
|
|
pub fn random_config<R: Rng>(rng: &mut R) -> GeneralPurposeConfig {
|
|
let mode = rng.gen();
|
|
GeneralPurposeConfig::new()
|
|
.with_encode_padding(match mode {
|
|
DecodePaddingMode::Indifferent => rng.gen(),
|
|
DecodePaddingMode::RequireCanonical => true,
|
|
DecodePaddingMode::RequireNone => false,
|
|
})
|
|
.with_decode_padding_mode(mode)
|
|
.with_decode_allow_trailing_bits(rng.gen())
|
|
}
|
|
|
|
impl distributions::Distribution<DecodePaddingMode> for distributions::Standard {
|
|
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> DecodePaddingMode {
|
|
match rng.gen_range(0..=2) {
|
|
0 => DecodePaddingMode::Indifferent,
|
|
1 => DecodePaddingMode::RequireCanonical,
|
|
_ => DecodePaddingMode::RequireNone,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn random_alphabet<R: Rng>(rng: &mut R) -> &'static alphabet::Alphabet {
|
|
ALPHABETS.choose(rng).unwrap()
|
|
}
|
|
|
|
pub fn random_engine<R: Rng>(rng: &mut R) -> GeneralPurpose {
|
|
let alphabet = random_alphabet(rng);
|
|
let config = random_config(rng);
|
|
GeneralPurpose::new(alphabet, config)
|
|
}
|
|
|
|
const ALPHABETS: &[alphabet::Alphabet] = &[
|
|
alphabet::URL_SAFE,
|
|
alphabet::STANDARD,
|
|
alphabet::CRYPT,
|
|
alphabet::BCRYPT,
|
|
alphabet::IMAP_MUTF7,
|
|
alphabet::BIN_HEX,
|
|
];
|