summaryrefslogtreecommitdiffstats
path: root/vendor/base64ct/tests/proptests.rs
blob: 4d5e1890a466423ddef3ed96e56c2ea962e8acdc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! 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, Encoding};
use proptest::{prelude::*, string::*};
use std::iter;

/// 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 + &iter::repeat("=").take(padding_len).collect::<String>()
            }
        };

        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());
    }
}