summaryrefslogtreecommitdiffstats
path: root/openpgp/tests/for-each-artifact.rs
blob: b45364ea08323afd9b7a465aec944aafbd38783e (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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
use std::env;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

extern crate sequoia_openpgp as openpgp;
use crate::openpgp::parse::*;
// Rustc 1.34 thinks SerializeInto is unused, but if we don't import
// it, it correctly complains about no trait being in scope providing
// .to_vec().  This seems to be a compiler bug, because rustc 1.40
// behaves correctly.  Hence, we work around the unused import warning
// until we rise our MSRV.
#[allow(unused_imports)] // XXX: Remove me.
use crate::openpgp::serialize::{Serialize, SerializeInto};

mod for_each_artifact {
    use super::*;

    #[test]
    fn packet_roundtrip() {
        for_all_files(&test_data_dir(), |src| {
            for_all_packets(src, |p| {
                let mut v = Vec::new();
                p.serialize(&mut v)?;
                let q = openpgp::Packet::from_bytes(&v)?;
                if p != &q {
                    return Err(anyhow::anyhow!(
                        "assertion failed: p == q\np = {:?}\nq = {:?}", p, q));
                }
                let w = p.to_vec()?;
                if v != w {
                    return Err(anyhow::anyhow!(
                        "assertion failed: v == w\nv = {:?}\nw = {:?}", v, w));
                }
                Ok(())
            })
        }).unwrap();
    }

    #[test]
    fn cert_roundtrip() {
        for_all_files(&test_data_dir(), |src| {
            let p = if let Ok(cert) = openpgp::Cert::from_file(src) {
                cert
            } else {
                // Ignore non-Cert files.
                return Ok(());
            };

            let mut v = Vec::new();
            p.as_tsk().serialize(&mut v)?;
            let q = openpgp::Cert::from_bytes(&v)?;
            if p != q {
                eprintln!("roundtripping {:?} failed", src);

                let p_: Vec<_> = p.clone().into_packets().collect();
                let q_: Vec<_> = q.clone().into_packets().collect();
                eprintln!("original: {} packets; roundtripped: {} packets",
                          p_.len(), q_.len());

                for (i, (p, q)) in p_.iter().zip(q_.iter()).enumerate() {
                    if p != q {
                        eprintln!("First difference at packet {}:\nOriginal: {:?}\nNew: {:?}",
                                  i, p, q);
                        break;
                    }
                }

                eprintln!("This is the recovered cert:\n{}",
                          String::from_utf8_lossy(
                              &q.armored().to_vec().unwrap()));
            }
            assert_eq!(p, q, "roundtripping {:?} failed", src);

            let w = p.as_tsk().to_vec().unwrap();
            assert_eq!(v, w,
                       "Serialize and SerializeInto disagree on {:?}", p);

            // Check that Cert::into_packets() and Cert::to_vec()
            // agree.
            let v = p.to_vec()?;
            let mut buf = Vec::new();
            for p in p.clone().into_packets() {
                p.serialize(&mut buf)?;
            }
            assert_eq!(buf, v);
            Ok(())
        }).unwrap();
    }

    #[test]
    fn message_roundtrip() {
        for_all_files(&test_data_dir(), |src| {
            let p = if let Ok(msg) = openpgp::Message::from_file(src) {
                msg
            } else {
                // Ignore non-Message files.
                return Ok(());
            };

            let mut v = Vec::new();
            p.serialize(&mut v)?;
            let q = openpgp::Message::from_bytes(&v)?;
            assert_eq!(p, q, "roundtripping {:?} failed", src);

            let w = p.to_vec().unwrap();
            assert_eq!(v, w,
                       "Serialize and SerializeInto disagree on {:?}", p);
            Ok(())
        }).unwrap();
    }
}

/// Computes the path to the test directory.
fn test_data_dir() -> PathBuf {
    let manifest_dir = PathBuf::from(
        env::var_os("CARGO_MANIFEST_DIR")
        .as_ref()
        .expect("CARGO_MANIFEST_DIR not set"));

    manifest_dir.join("tests").join("data")
}

/// Maps the given function `fun` over all Rust files in `src`.
fn for_all_files<F>(src: &Path, mut fun: F) -> openpgp::Result<()>
    where F: FnMut(&Path) -> openpgp::Result<()>
{
    let mut dirs = vec![src.to_path_buf()];

    while let Some(dir) = dirs.pop() {
        for entry in fs::read_dir(dir).unwrap() {
            let entry = entry?;
            let path = entry.path();
            if path.is_file() {
                // XXX: Look at the file extension and skip non-PGP
                // files.  We need to do this because the
                // Armor-heuristic is so slow.  See #204.
                if let Some(extension) =
                    path.extension().and_then(|e| e.to_str())
                {
                    match extension {
                        "pgp" | "gpg" | "asc" | "key" => (),
                        e if e.contains("key") => (),
                        _ => continue,
                    }
                } else {
                    // No extension or not valid UTF-8.
                    continue;
                }

                eprintln!("Processing {:?}", path);
                match fun(&path) {
                    Ok(_) => (),
                    Err(e) => {
                        eprintln!("Failed on file {:?}:\n", path);
                        return Err(e);
                    },
                }
            }
            if path.is_dir() {
                dirs.push(path.clone());
            }
        }
    }
    Ok(())
}

/// Maps the given function `fun` over all packets in `src`.
fn for_all_packets<F>(src: &Path, mut fun: F) -> openpgp::Result<()>
    where F: FnMut(&openpgp::Packet) -> openpgp::Result<()>
{
    let ppb = PacketParserBuilder::from_file(src)?.buffer_unread_content();
    let mut ppr = if let Ok(ppr) = ppb.build() {
        ppr
    } else {
        // Ignore junk.
        return Ok(());
    };

    while let PacketParserResult::Some(pp) = ppr {
        match pp.recurse() {
            Ok((packet, ppr_)) => {
                ppr = ppr_;

                if let openpgp::Packet::Unknown(_) = packet {
                    continue;  // Ignore packets that we cannot parse.
                }

                match fun(&packet) {
                    Ok(_) => (),
                    Err(e) => {
                        eprintln!("Failed on packet {:?}:\n", packet);
                        let mut sink =