summaryrefslogtreecommitdiffstats
path: root/third_party/rust/base64/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/rust/base64/tests
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--third_party/rust/base64/tests/decode.rs330
-rw-r--r--third_party/rust/base64/tests/encode.rs105
-rw-r--r--third_party/rust/base64/tests/helpers.rs14
-rw-r--r--third_party/rust/base64/tests/tests.rs194
4 files changed, 643 insertions, 0 deletions
diff --git a/third_party/rust/base64/tests/decode.rs b/third_party/rust/base64/tests/decode.rs
new file mode 100644
index 0000000000..282bccd9cd
--- /dev/null
+++ b/third_party/rust/base64/tests/decode.rs
@@ -0,0 +1,330 @@
+extern crate base64;
+
+use base64::*;
+
+mod helpers;
+
+use self::helpers::*;
+
+#[test]
+fn decode_rfc4648_0() {
+ compare_decode("", "");
+}
+
+#[test]
+fn decode_rfc4648_1() {
+ compare_decode("f", "Zg==");
+}
+
+#[test]
+fn decode_rfc4648_1_just_a_bit_of_padding() {
+ // allows less padding than required
+ compare_decode("f", "Zg=");
+}
+
+#[test]
+fn decode_rfc4648_1_no_padding() {
+ compare_decode("f", "Zg");
+}
+
+#[test]
+fn decode_rfc4648_2() {
+ compare_decode("fo", "Zm8=");
+}
+
+#[test]
+fn decode_rfc4648_2_no_padding() {
+ compare_decode("fo", "Zm8");
+}
+
+#[test]
+fn decode_rfc4648_3() {
+ compare_decode("foo", "Zm9v");
+}
+
+#[test]
+fn decode_rfc4648_4() {
+ compare_decode("foob", "Zm9vYg==");
+}
+
+#[test]
+fn decode_rfc4648_4_no_padding() {
+ compare_decode("foob", "Zm9vYg");
+}
+
+#[test]
+fn decode_rfc4648_5() {
+ compare_decode("fooba", "Zm9vYmE=");
+}
+
+#[test]
+fn decode_rfc4648_5_no_padding() {
+ compare_decode("fooba", "Zm9vYmE");
+}
+
+#[test]
+fn decode_rfc4648_6() {
+ compare_decode("foobar", "Zm9vYmFy");
+}
+
+#[test]
+fn decode_reject_null() {
+ assert_eq!(
+ DecodeError::InvalidByte(3, 0x0),
+ decode_config("YWx\0pY2U==", config_std_pad()).unwrap_err()
+ );
+}
+
+#[test]
+fn decode_single_pad_byte_after_2_chars_in_trailing_quad_ok() {
+ for num_quads in 0..25 {
+ let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
+ s.push_str("Zg=");
+
+ let input_len = num_quads * 3 + 1;
+
+ // Since there are 3 bytes in the trailing quad, want to be sure this allows for the fact
+ // that it could be bad padding rather than assuming that it will decode to 2 bytes and
+ // therefore allow 1 extra round of fast decode logic (stage 1 / 2).
+
+ let mut decoded = Vec::new();
+ decoded.resize(input_len, 0);
+
+ assert_eq!(
+ input_len,
+ decode_config_slice(&s, STANDARD, &mut decoded).unwrap()
+ );
+ }
+}
+
+//this is a MAY in the rfc: https://tools.ietf.org/html/rfc4648#section-3.3
+#[test]
+fn decode_1_pad_byte_in_fast_loop_then_extra_padding_chunk_error() {
+ for num_quads in 0..25 {
+ let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
+ s.push_str("YWxpY2U=====");
+
+ // since the first 8 bytes are handled in stage 1 or 2, the padding is detected as a
+ // generic invalid byte, not specifcally a padding issue.
+ // Could argue that the *next* padding byte (in the next quad) is technically the first
+ // erroneous one, but reporting that accurately is more complex and probably nobody cares
+ assert_eq!(
+ DecodeError::InvalidByte(num_quads * 4 + 7, b'='),
+ decode(&s).unwrap_err()
+ );
+ }
+}
+
+#[test]
+fn decode_2_pad_bytes_in_leftovers_then_extra_padding_chunk_error() {
+ for num_quads in 0..25 {
+ let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
+ s.push_str("YWxpY2UABB====");
+
+ // 6 bytes (4 padding) after last 8-byte chunk, so it's decoded by stage 4.
+ // First padding byte is invalid.
+ assert_eq!(
+ DecodeError::InvalidByte(num_quads * 4 + 10, b'='),
+ decode(&s).unwrap_err()
+ );
+ }
+}
+
+#[test]
+fn decode_valid_bytes_after_padding_in_leftovers_error() {
+ for num_quads in 0..25 {
+ let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
+ s.push_str("YWxpY2UABB=B");
+
+ // 4 bytes after last 8-byte chunk, so it's decoded by stage 4.
+ // First (and only) padding byte is invalid.
+ assert_eq!(
+ DecodeError::InvalidByte(num_quads * 4 + 10, b'='),
+ decode(&s).unwrap_err()
+ );
+ }
+}
+
+#[test]
+fn decode_absurd_pad_error() {
+ for num_quads in 0..25 {
+ let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
+ s.push_str("==Y=Wx===pY=2U=====");
+
+ // Plenty of remaining bytes, so handled by stage 1 or 2.
+ // first padding byte
+ assert_eq!(
+ DecodeError::InvalidByte(num_quads * 4, b'='),
+ decode(&s).unwrap_err()
+ );
+ }
+}
+
+#[test]
+fn decode_extra_padding_after_1_pad_bytes_in_trailing_quad_returns_error() {
+ for num_quads in 0..25 {
+ let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
+ s.push_str("EEE===");
+
+ // handled by stage 1, 2, or 4 depending on length
+ // first padding byte -- which would be legal if it was the only padding
+ assert_eq!(
+ DecodeError::InvalidByte(num_quads * 4 + 3, b'='),
+ decode(&s).unwrap_err()
+ );
+ }
+}
+
+#[test]
+fn decode_extra_padding_after_2_pad_bytes_in_trailing_quad_2_returns_error() {
+ for num_quads in 0..25 {
+ let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
+ s.push_str("EE====");
+
+ // handled by stage 1, 2, or 4 depending on length
+ // first padding byte -- which would be legal if it was by itself
+ assert_eq!(
+ DecodeError::InvalidByte(num_quads * 4 + 2, b'='),
+ decode(&s).unwrap_err()
+ );
+ }
+}
+
+#[test]
+fn decode_start_quad_with_padding_returns_error() {
+ for num_quads in 0..25 {
+ // add enough padding to ensure that we'll hit all 4 stages at the different lengths
+ for pad_bytes in 1..32 {
+ let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
+ let padding: String = std::iter::repeat("=").take(pad_bytes).collect();
+ s.push_str(&padding);
+
+ if pad_bytes % 4 == 1 {
+ // detected in early length check
+ assert_eq!(DecodeError::InvalidLength, decode(&s).unwrap_err());
+ } else {
+ // padding lengths 2 - 8 are handled by stage 4
+ // padding length >= 8 will hit at least one chunk at stages 1, 2, 3 at different
+ // prefix lengths
+ assert_eq!(
+ DecodeError::InvalidByte(num_quads * 4, b'='),
+ decode(&s).unwrap_err()
+ );
+ }
+ }
+ }
+}
+
+#[test]
+fn decode_padding_followed_by_non_padding_returns_error() {
+ for num_quads in 0..25 {
+ for pad_bytes in 0..31 {
+ let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
+ let padding: String = std::iter::repeat("=").take(pad_bytes).collect();
+ s.push_str(&padding);
+ s.push_str("E");
+
+ if pad_bytes % 4 == 0 {
+ assert_eq!(DecodeError::InvalidLength, decode(&s).unwrap_err());
+ } else {
+ // pad len 1 - 8 will be handled by stage 4
+ // pad len 9 (suffix len 10) will have 8 bytes of padding handled by stage 3
+ // first padding byte
+ assert_eq!(
+ DecodeError::InvalidByte(num_quads * 4, b'='),
+ decode(&s).unwrap_err()
+ );
+ }
+ }
+ }
+}
+
+#[test]
+fn decode_one_char_in_quad_with_padding_error() {
+ for num_quads in 0..25 {
+ let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
+ s.push_str("E=");
+
+ assert_eq!(
+ DecodeError::InvalidByte(num_quads * 4 + 1, b'='),
+ decode(&s).unwrap_err()
+ );
+
+ // more padding doesn't change the error
+ s.push_str("=");
+ assert_eq!(
+ DecodeError::InvalidByte(num_quads * 4 + 1, b'='),
+ decode(&s).unwrap_err()
+ );
+
+ s.push_str("=");
+ assert_eq!(
+ DecodeError::InvalidByte(num_quads * 4 + 1, b'='),
+ decode(&s).unwrap_err()
+ );
+ }
+}
+
+#[test]
+fn decode_one_char_in_quad_without_padding_error() {
+ for num_quads in 0..25 {
+ let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
+ s.push('E');
+
+ assert_eq!(DecodeError::InvalidLength, decode(&s).unwrap_err());
+ }
+}
+
+#[test]
+fn decode_reject_invalid_bytes_with_correct_error() {
+ for length in 1..100 {
+ for index in 0_usize..length {
+ for invalid_byte in " \t\n\r\x0C\x0B\x00%*.".bytes() {
+ let prefix: String = std::iter::repeat("A").take(index).collect();
+ let suffix: String = std::iter::repeat("B").take(length - index - 1).collect();
+
+ let input = prefix + &String::from_utf8(vec![invalid_byte]).unwrap() + &suffix;
+ assert_eq!(
+ length,
+ input.len(),
+ "length {} error position {}",
+ length,
+ index
+ );
+
+ if length % 4 == 1 && !suffix.is_empty() {
+ assert_eq!(DecodeError::InvalidLength, decode(&input).unwrap_err());
+ } else {
+ assert_eq!(
+ DecodeError::InvalidByte(index, invalid_byte),
+ decode(&input).unwrap_err()
+ );
+ }
+ }
+ }
+ }
+}
+
+#[test]
+fn decode_imap() {
+ assert_eq!(
+ decode_config(b"+,,+", crate::IMAP_MUTF7),
+ decode_config(b"+//+", crate::STANDARD_NO_PAD)
+ );
+}
+
+#[test]
+fn decode_invalid_trailing_bytes() {
+ // The case of trailing newlines is common enough to warrant a test for a good error
+ // message.
+ assert_eq!(
+ Err(DecodeError::InvalidByte(8, b'\n')),
+ decode(b"Zm9vCg==\n")
+ );
+ // extra padding, however, is still InvalidLength
+ assert_eq!(Err(DecodeError::InvalidLength), decode(b"Zm9vCg==="));
+}
+
+fn config_std_pad() -> Config {
+ Config::new(CharacterSet::Standard, true)
+}
diff --git a/third_party/rust/base64/tests/encode.rs b/third_party/rust/base64/tests/encode.rs
new file mode 100644
index 0000000000..0004be00fe
--- /dev/null
+++ b/third_party/rust/base64/tests/encode.rs
@@ -0,0 +1,105 @@
+extern crate base64;
+
+use base64::*;
+
+fn compare_encode(expected: &str, target: &[u8]) {
+ assert_eq!(expected, encode(target));
+}
+
+#[test]
+fn encode_rfc4648_0() {
+ compare_encode("", b"");
+}
+
+#[test]
+fn encode_rfc4648_1() {
+ compare_encode("Zg==", b"f");
+}
+
+#[test]
+fn encode_rfc4648_2() {
+ compare_encode("Zm8=", b"fo");
+}
+
+#[test]
+fn encode_rfc4648_3() {
+ compare_encode("Zm9v", b"foo");
+}
+
+#[test]
+fn encode_rfc4648_4() {
+ compare_encode("Zm9vYg==", b"foob");
+}
+
+#[test]
+fn encode_rfc4648_5() {
+ compare_encode("Zm9vYmE=", b"fooba");
+}
+
+#[test]
+fn encode_rfc4648_6() {
+ compare_encode("Zm9vYmFy", b"foobar");
+}
+
+#[test]
+fn encode_all_ascii() {
+ let mut ascii = Vec::<u8>::with_capacity(128);
+
+ for i in 0..128 {
+ ascii.push(i);
+ }
+
+ compare_encode(
+ "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7P\
+ D0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8\
+ =",
+ &ascii,
+ );
+}
+
+#[test]
+fn encode_all_bytes() {
+ let mut bytes = Vec::<u8>::with_capacity(256);
+
+ for i in 0..255 {
+ bytes.push(i);
+ }
+ bytes.push(255); //bug with "overflowing" ranges?
+
+ compare_encode(
+ "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7P\
+ D0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn\
+ +AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6\
+ /wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==",
+ &bytes,
+ );
+}
+
+#[test]
+fn encode_all_bytes_url() {
+ let mut bytes = Vec::<u8>::with_capacity(256);
+
+ for i in 0..255 {
+ bytes.push(i);
+ }
+ bytes.push(255); //bug with "overflowing" ranges?
+
+ assert_eq!(
+ "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0\
+ -P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn\
+ -AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq\
+ -wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy\
+ 8_T19vf4-fr7_P3-_w==",
+ encode_config(&bytes, URL_SAFE)
+ );
+}
+
+#[test]
+fn encode_url_safe_without_padding() {
+ let encoded = encode_config(b"alice", URL_SAFE_NO_PAD);
+ assert_eq!(&encoded, "YWxpY2U");
+ assert_eq!(
+ String::from_utf8(decode(&encoded).unwrap()).unwrap(),
+ "alice"
+ );
+}
diff --git a/third_party/rust/base64/tests/helpers.rs b/third_party/rust/base64/tests/helpers.rs
new file mode 100644
index 0000000000..5144988448
--- /dev/null
+++ b/third_party/rust/base64/tests/helpers.rs
@@ -0,0 +1,14 @@
+extern crate base64;
+
+use base64::*;
+
+pub fn compare_decode(expected: &str, target: &str) {
+ assert_eq!(
+ expected,
+ String::from_utf8(decode(target).unwrap()).unwrap()
+ );
+ assert_eq!(
+ expected,
+ String::from_utf8(decode(target.as_bytes()).unwrap()).unwrap()
+ );
+}
diff --git a/third_party/rust/base64/tests/tests.rs b/third_party/rust/base64/tests/tests.rs
new file mode 100644
index 0000000000..11fed960ff
--- /dev/null
+++ b/third_party/rust/base64/tests/tests.rs
@@ -0,0 +1,194 @@
+extern crate base64;
+extern crate rand;
+
+use rand::{FromEntropy, Rng};
+
+use base64::*;
+
+mod helpers;
+use self::helpers::*;
+
+// generate random contents of the specified length and test encode/decode roundtrip
+fn roundtrip_random(
+ byte_buf: &mut Vec<u8>,
+ str_buf: &mut String,
+ config: Config,
+ byte_len: usize,
+ approx_values_per_byte: u8,
+ max_rounds: u64,
+) {
+ // let the short ones be short but don't let it get too crazy large
+ let num_rounds = calculate_number_of_rounds(byte_len, approx_values_per_byte, max_rounds);
+ let mut r = rand::rngs::SmallRng::from_entropy();
+ let mut decode_buf = Vec::new();
+
+ for _ in 0..num_rounds {
+ byte_buf.clear();
+ str_buf.clear();
+ decode_buf.clear();
+ while byte_buf.len() < byte_len {
+ byte_buf.push(r.gen::<u8>());
+ }
+
+ encode_config_buf(&byte_buf, config, str_buf);
+ decode_config_buf(&str_buf, config, &mut decode_buf).unwrap();
+
+ assert_eq!(byte_buf, &decode_buf);
+ }
+}
+
+fn calculate_number_of_rounds(byte_len: usize, approx_values_per_byte: u8, max: u64) -> u64 {
+ // don't overflow
+ let mut prod = approx_values_per_byte as u64;
+
+ for _ in 0..byte_len {
+ if prod > max {
+ return max;
+ }
+
+ prod = prod.saturating_mul(prod);
+ }
+
+ prod
+}
+
+fn no_pad_config() -> Config {
+ Config::new(CharacterSet::Standard, false)
+}
+
+#[test]
+fn roundtrip_random_short_standard() {
+ let mut byte_buf: Vec<u8> = Vec::new();
+ let mut str_buf = String::new();
+
+ for input_len in 0..40 {
+ roundtrip_random(&mut byte_buf, &mut str_buf, STANDARD, input_len, 4, 10000);
+ }
+}
+
+#[test]
+fn roundtrip_random_with_fast_loop_standard() {
+ let mut byte_buf: Vec<u8> = Vec::new();
+ let mut str_buf = String::new();
+
+ for input_len in 40..100 {
+ roundtrip_random(&mut byte_buf, &mut str_buf, STANDARD, input_len, 4, 1000);
+ }
+}
+
+#[test]
+fn roundtrip_random_short_no_padding() {
+ let mut byte_buf: Vec<u8> = Vec::new();
+ let mut str_buf = String::new();
+
+ for input_len in 0..40 {
+ roundtrip_random(
+ &mut byte_buf,
+ &mut str_buf,
+ no_pad_config(),
+ input_len,
+ 4,
+ 10000,
+ );
+ }
+}
+
+#[test]
+fn roundtrip_random_no_padding() {
+ let mut byte_buf: Vec<u8> = Vec::new();
+ let mut str_buf = String::new();
+
+ for input_len in 40..100 {
+ roundtrip_random(
+ &mut byte_buf,
+ &mut str_buf,
+ no_pad_config(),
+ input_len,
+ 4,
+ 1000,
+ );
+ }
+}
+
+#[test]
+fn roundtrip_decode_trailing_10_bytes() {
+ // This is a special case because we decode 8 byte blocks of input at a time as much as we can,
+ // ideally unrolled to 32 bytes at a time, in stages 1 and 2. Since we also write a u64's worth
+ // of bytes (8) to the output, we always write 2 garbage bytes that then will be overwritten by
+ // the NEXT block. However, if the next block only contains 2 bytes, it will decode to 1 byte,
+ // and therefore be too short to cover up the trailing 2 garbage bytes. Thus, we have stage 3
+ // to handle that case.
+
+ for num_quads in 0..25 {
+ let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
+ s.push_str("EFGHIJKLZg");
+
+ let decoded = decode(&s).unwrap();
+ assert_eq!(num_quads * 3 + 7, decoded.len());
+
+ assert_eq!(s, encode_config(&decoded, STANDARD_NO_PAD));
+ }
+}
+
+#[test]
+fn display_wrapper_matches_normal_encode() {
+ let mut bytes = Vec::<u8>::with_capacity(256);
+
+ for i in 0..255 {
+ bytes.push(i);
+ }
+ bytes.push(255);
+
+ assert_eq!(
+ encode(&bytes),
+ format!(
+ "{}",
+ base64::display::Base64Display::with_config(&bytes, STANDARD)
+ )
+ );
+}
+
+#[test]
+fn because_we_can() {
+ compare_decode("alice", "YWxpY2U=");
+ compare_decode("alice", &encode(b"alice"));
+ compare_decode("alice", &encode(&decode(&encode(b"alice")).unwrap()));
+}
+
+#[test]
+fn encode_config_slice_can_use_inline_buffer() {
+ let mut buf: [u8; 22] = [0; 22];
+ let mut larger_buf: [u8; 24] = [0; 24];
+ let mut input: [u8; 16] = [0; 16];
+
+ let mut rng = rand::rngs::SmallRng::from_entropy();
+ for elt in &mut input {
+ *elt = rng.gen();
+ }
+
+ assert_eq!(22, encode_config_slice(&input, STANDARD_NO_PAD, &mut buf));
+ let decoded = decode_config(&buf, STANDARD_NO_PAD).unwrap();
+
+ assert_eq!(decoded, input);
+
+ // let's try it again with padding
+
+ assert_eq!(24, encode_config_slice(&input, STANDARD, &mut larger_buf));
+ let decoded = decode_config(&buf, STANDARD).unwrap();
+
+ assert_eq!(decoded, input);
+}
+
+#[test]
+#[should_panic(expected = "index 24 out of range for slice of length 22")]
+fn encode_config_slice_panics_when_buffer_too_small() {
+ let mut buf: [u8; 22] = [0; 22];
+ let mut input: [u8; 16] = [0; 16];
+
+ let mut rng = rand::rngs::SmallRng::from_entropy();
+ for elt in &mut input {
+ *elt = rng.gen();
+ }
+
+ encode_config_slice(&input, STANDARD, &mut buf);
+}