1# Write a Sanity Test
2
3Finally, to tie everything together, let's write a sanity test that round trips
4some text through compression and decompression, and then asserts that it came
5back out the same as it went in. This is a little wordy using the raw FFI
6bindings, but hopefully we wouldn't usually ask people to do this, we'd provide
7a nice Rust-y API on top of the raw FFI bindings for them. However, since this
8is for testing the bindings directly, our sanity test will use the bindings
9directly.
10
11The test data I'm round tripping are some Futurama quotes I got off the internet
12and put in the `futurama-quotes.txt` file, which is read into a `&'static str`
13at compile time via the `include_str!("../futurama-quotes.txt")` macro
14invocation.
15
16Without further ado, here is the test, which should be appended to the bottom of
17our `src/lib.rs` file:
18
19```rust
20#[cfg(test)]
21mod tests {
22    use super::*;
23    use std::mem;
24
25    #[test]
26    fn round_trip_compression_decompression() {
27        unsafe {
28            let input = include_str!("../futurama-quotes.txt").as_bytes();
29            let mut compressed_output: Vec<u8> = vec![0; input.len()];
30            let mut decompressed_output: Vec<u8> = vec![0; input.len()];
31
32            // Construct a compression stream.
33            let mut stream: bz_stream = mem::zeroed();
34            let result = BZ2_bzCompressInit(&mut stream as *mut _,
35                                            1,   // 1 x 100000 block size
36                                            4,   // verbosity (4 = most verbose)
37                                            0);  // default work factor
38            match result {
39                r if r == (BZ_CONFIG_ERROR as _) => panic!("BZ_CONFIG_ERROR"),
40                r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"),
41                r if r == (BZ_MEM_ERROR as _) => panic!("BZ_MEM_ERROR"),
42                r if r == (BZ_OK as _) => {},
43                r => panic!("Unknown return value = {}", r),
44            }
45
46            // Compress `input` into `compressed_output`.
47            stream.next_in = input.as_ptr() as *mut _;
48            stream.avail_in = input.len() as _;
49            stream.next_out = compressed_output.as_mut_ptr() as *mut _;
50            stream.avail_out = compressed_output.len() as _;
51            let result = BZ2_bzCompress(&mut stream as *mut _, BZ_FINISH as _);
52            match result {
53                r if r == (BZ_RUN_OK as _) => panic!("BZ_RUN_OK"),
54                r if r == (BZ_FLUSH_OK as _) => panic!("BZ_FLUSH_OK"),
55                r if r == (BZ_FINISH_OK as _) => panic!("BZ_FINISH_OK"),
56                r if r == (BZ_SEQUENCE_ERROR as _) => panic!("BZ_SEQUENCE_ERROR"),
57                r if r == (BZ_STREAM_END as _) => {},
58                r => panic!("Unknown return value = {}", r),
59            }
60
61            // Finish the compression stream.
62            let result = BZ2_bzCompressEnd(&mut stream as *mut _);
63            match result {
64                r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"),
65                r if r == (BZ_OK as _) => {},
66                r => panic!("Unknown return value = {}", r),
67            }
68
69            // Construct a decompression stream.
70            let mut stream: bz_stream = mem::zeroed();
71            let result = BZ2_bzDecompressInit(&mut stream as *mut _,
72                                              4,   // verbosity (4 = most verbose)
73                                              0);  // default small factor
74            match result {
75                r if r == (BZ_CONFIG_ERROR as _) => panic!("BZ_CONFIG_ERROR"),
76                r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"),
77                r if r == (BZ_MEM_ERROR as _) => panic!("BZ_MEM_ERROR"),
78                r if r == (BZ_OK as _) => {},
79                r => panic!("Unknown return value = {}", r),
80            }
81
82            // Decompress `compressed_output` into `decompressed_output`.
83            stream.next_in = compressed_output.as_ptr() as *mut _;
84            stream.avail_in = compressed_output.len() as _;
85            stream.next_out = decompressed_output.as_mut_ptr() as *mut _;
86            stream.avail_out = decompressed_output.len() as _;
87            let result = BZ2_bzDecompress(&mut stream as *mut _);
88            match result {
89                r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"),
90                r if r == (BZ_DATA_ERROR as _) => panic!("BZ_DATA_ERROR"),
91                r if r == (BZ_DATA_ERROR_MAGIC as _) => panic!("BZ_DATA_ERROR"),
92                r if r == (BZ_MEM_ERROR as _) => panic!("BZ_MEM_ERROR"),
93                r if r == (BZ_OK as _) => panic!("BZ_OK"),
94                r if r == (BZ_STREAM_END as _) => {},
95                r => panic!("Unknown return value = {}", r),
96            }
97
98            // Close the decompression stream.
99            let result = BZ2_bzDecompressEnd(&mut stream as *mut _);
100            match result {
101                r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"),
102                r if r == (BZ_OK as _) => {},
103                r => panic!("Unknown return value = {}", r),
104            }
105
106            assert_eq!(input, &decompressed_output[..]);
107        }
108    }
109}
110```
111
112Now let's run `cargo test` again and verify that everything is linking and binding
113properly!
114
115```bash
116$ cargo test
117   Compiling bindgen-tutorial-bzip2-sys v0.1.0
118    Finished debug [unoptimized + debuginfo] target(s) in 0.54 secs
119     Running target/debug/deps/bindgen_tutorial_bzip2_sys-1c5626bbc4401c3a
120
121running 15 tests
122test bindgen_test_layout___darwin_pthread_handler_rec ... ok
123test bindgen_test_layout___sFILE ... ok
124test bindgen_test_layout___sbuf ... ok
125test bindgen_test_layout__bindgen_ty_1 ... ok
126test bindgen_test_layout__bindgen_ty_2 ... ok
127test bindgen_test_layout__opaque_pthread_attr_t ... ok
128test bindgen_test_layout__opaque_pthread_cond_t ... ok
129test bindgen_test_layout__opaque_pthread_condattr_t ... ok
130test bindgen_test_layout__opaque_pthread_mutex_t ... ok
131test bindgen_test_layout__opaque_pthread_mutexattr_t ... ok
132test bindgen_test_layout__opaque_pthread_once_t ... ok
133test bindgen_test_layout__opaque_pthread_rwlock_t ... ok
134test bindgen_test_layout__opaque_pthread_rwlockattr_t ... ok
135test bindgen_test_layout__opaque_pthread_t ... ok
136    block 1: crc = 0x47bfca17, combined CRC = 0x47bfca17, size = 2857
137        bucket sorting ...
138        depth      1 has   2849 unresolved strings
139        depth      2 has   2702 unresolved strings
140        depth      4 has   1508 unresolved strings
141        depth      8 has    538 unresolved strings
142        depth     16 has    148 unresolved strings
143        depth     32 has      0 unresolved strings
144        reconstructing block ...
145      2857 in block, 2221 after MTF & 1-2 coding, 61+2 syms in use
146      initial group 5, [0 .. 1], has 570 syms (25.7%)
147      initial group 4, [2 .. 2], has 256 syms (11.5%)
148      initial group 3, [3 .. 6], has 554 syms (24.9%)
149      initial group 2, [7 .. 12], has 372 syms (16.7%)
150      initial group 1, [13 .. 62], has 469 syms (21.1%)
151      pass 1: size is 2743, grp uses are 13 6 15 0 11
152      pass 2: size is 1216, grp uses are 13 7 15 0 10
153      pass 3: size is 1214, grp uses are 13 8 14 0 10
154      pass 4: size is 1213, grp uses are 13 9 13 0 10
155      bytes: mapping 19, selectors 17, code lengths 79, codes 1213
156    final combined CRC = 0x47bfca17
157
158    [1: huff+mtf rt+rld {0x47bfca17, 0x47bfca17}]
159    combined CRCs: stored = 0x47bfca17, computed = 0x47bfca17
160test tests::round_trip_compression_decompression ... ok
161
162test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured
163
164   Doc-tests bindgen-tutorial-bzip2-sys
165
166running 0 tests
167
168test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
169```
170