]>
Commit | Line | Data |
---|---|---|
3a7b42d0 WB |
1 | //! Test for old timestamp format compatibility. |
2 | ||
3 | use std::io::{self, Read}; | |
4 | ||
5 | use endian_trait::Endian; | |
6 | ||
7 | use pxar::{decoder, format, EntryKind}; | |
8 | ||
9 | fn write_raw_struct<T: Endian, W: io::Write + ?Sized>(output: &mut W, data: T) -> io::Result<()> { | |
10 | let data = data.to_le(); | |
11 | output.write_all(unsafe { | |
12 | std::slice::from_raw_parts(&data as *const T as *const u8, std::mem::size_of::<T>()) | |
13 | }) | |
14 | } | |
15 | ||
16 | fn write_data<W>(output: &mut W, htype: u64, data: &[u8]) -> io::Result<()> | |
17 | where | |
18 | W: io::Write + ?Sized, | |
19 | { | |
20 | let header = format::Header::with_content_size(htype, data.len() as u64); | |
21 | header.check_header_size()?; | |
22 | write_raw_struct(output, header)?; | |
23 | output.write_all(data) | |
24 | } | |
25 | ||
26 | fn write_entry<T, W>(output: &mut W, htype: u64, data: T) -> io::Result<()> | |
27 | where | |
28 | T: Endian, | |
29 | W: io::Write + ?Sized, | |
30 | { | |
31 | let data = data.to_le(); | |
32 | let data = unsafe { | |
33 | std::slice::from_raw_parts(&data as *const T as *const u8, std::mem::size_of::<T>()) | |
34 | }; | |
35 | write_data(output, htype, data) | |
36 | } | |
37 | ||
38 | const MAY_1_2015_1530: u64 = 1430487000u64; | |
39 | ||
40 | const FILE_NAME: &str = "file.txt"; | |
41 | const FILE_NAME_BYTES: &[u8] = b"file.txt\0"; | |
42 | const FILE_CONTENT: &[u8] = b"This is a small text file.\n"; | |
2ea8aff2 | 43 | const ROOT_STAT: format::Stat_V1 = format::Stat_V1 { |
3a7b42d0 WB |
44 | mode: format::mode::IFDIR | 0o755, |
45 | flags: 0, | |
46 | uid: 1000, | |
47 | gid: 1000, | |
48 | mtime: MAY_1_2015_1530 * 1_000_000_000u64, | |
49 | }; | |
2ea8aff2 | 50 | const FILE_STAT: format::Stat_V1 = format::Stat_V1 { |
3a7b42d0 WB |
51 | mode: format::mode::IFREG | 0o644, |
52 | flags: 0, | |
53 | uid: 1000, | |
54 | gid: 1000, | |
55 | mtime: MAY_1_2015_1530 * 1_000_000_000u64, | |
56 | }; | |
57 | ||
58 | fn create_archive() -> io::Result<Vec<u8>> { | |
59 | let mut out = Vec::new(); | |
60 | ||
61 | write_entry(&mut out, format::PXAR_ENTRY_V1, ROOT_STAT.clone())?; | |
62 | ||
63 | let file_offset = out.len(); | |
64 | write_data(&mut out, format::PXAR_FILENAME, FILE_NAME_BYTES)?; | |
65 | write_entry(&mut out, format::PXAR_ENTRY_V1, FILE_STAT.clone())?; | |
66 | write_data(&mut out, format::PXAR_PAYLOAD, FILE_CONTENT)?; | |
67 | ||
68 | let mut gbt = Vec::new(); | |
69 | write_raw_struct( | |
70 | &mut gbt, | |
71 | format::GoodbyeItem::new( | |
72 | FILE_NAME.as_bytes(), | |
73 | file_offset as u64, | |
74 | FILE_CONTENT.len() as u64, | |
75 | ), | |
76 | )?; | |
77 | ||
78 | let gbt_size = gbt.len(); | |
79 | write_raw_struct( | |
80 | &mut gbt, | |
81 | format::GoodbyeItem { | |
82 | hash: format::PXAR_GOODBYE_TAIL_MARKER, | |
83 | offset: out.len() as u64, | |
84 | size: gbt_size as u64, | |
85 | }, | |
86 | )?; | |
87 | ||
88 | write_data(&mut out, format::PXAR_GOODBYE, &{ gbt })?; | |
89 | ||
90 | Ok(out) | |
91 | } | |
92 | ||
93 | #[test] | |
94 | fn test_archive() { | |
95 | let archive = create_archive().expect("failed to create test archive"); | |
96 | let mut input = &archive[..]; | |
97 | let mut decoder = decoder::Decoder::from_std(&mut input).expect("failed to create decoder"); | |
98 | ||
99 | let item = decoder | |
100 | .next() | |
101 | .expect("missing root directory in test archive") | |
102 | .expect("failed to extract root directory from test archive"); | |
103 | match item.kind() { | |
104 | EntryKind::Directory => (), | |
105 | other => panic!("unexpected root entry in archive: {:?}", other), | |
106 | } | |
107 | assert_eq!(item.file_name(), ""); | |
108 | assert_eq!(item.metadata().stat, ROOT_STAT.into()); | |
109 | assert_eq!( | |
110 | item.metadata().stat.mtime, | |
180186c5 | 111 | format::StatxTimestamp::new(MAY_1_2015_1530 as i64, 0), |
3a7b42d0 WB |
112 | ); |
113 | ||
114 | let item = decoder | |
115 | .next() | |
116 | .expect("missing file entry in test archive") | |
117 | .expect("failed to extract file entry from test archive"); | |
118 | match item.kind() { | |
119 | EntryKind::File { size, .. } => assert_eq!(*size, FILE_CONTENT.len() as u64), | |
120 | other => panic!("unexpected file entry in archive: {:?}", other), | |
121 | } | |
122 | assert_eq!(item.file_name(), FILE_NAME); | |
123 | assert_eq!(item.metadata().stat, FILE_STAT.into()); | |
124 | let mut content = Vec::new(); | |
125 | decoder | |
126 | .contents() | |
127 | .expect("failed to get contents for file entry") | |
128 | .read_to_end(&mut content) | |
129 | .expect("failed to read test file contents"); | |
130 | assert_eq!(&content[..], FILE_CONTENT); | |
131 | ||
132 | assert!(decoder.next().is_none(), "expected end of test archive"); | |
133 | } |