• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, item 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! This file defines PcapngUciLoggerFactory, which implements UciLoggerFactory
16 //! trait and logging UCI packets into PCAPNG format.
17 
18 use std::fs;
19 use std::io::Write;
20 use std::path::{Path, PathBuf};
21 
22 use log::{debug, error};
23 use tokio::runtime::Handle;
24 use tokio::sync::mpsc;
25 
26 use crate::uci::pcapng_block::{
27     BlockBuilder, BlockOption, HeaderBlockBuilder, InterfaceDescriptionBlockBuilder,
28 };
29 use crate::uci::uci_logger_factory::UciLoggerFactory;
30 use crate::uci::uci_logger_pcapng::UciLoggerPcapng;
31 use crate::utils::consuming_builder_field;
32 
33 const DEFAULT_LOG_DIR: &str = "/var/log/uwb";
34 const DEFAULT_FILE_PREFIX: &str = "uwb_uci";
35 const DEFAULT_BUFFER_SIZE: usize = 10240; // 10 KB
36 const DEFAUL_FILE_SIZE: usize = 1048576; // 1 MB
37 
38 /// The PCAPNG log file factory.
39 pub struct PcapngUciLoggerFactory {
40     /// log_writer references to LogWriterActor.
41     log_writer: LogWriter,
42     /// Maps recording chip-id to interface-id for UciLoggerPcapng.
43     ///
44     /// Map is forwarded LogWriterActor, the "actor" that log_writer owns which performs
45     /// actual writing of files which needs this map to build the InterfaceDescriptionBlock.
46     /// Since PCAPNG format defines the interface ID by the order of appearance of IDB inside file,
47     /// the "map" is a vector whose index coincides with the interface ID.
48     chip_interface_id_map: Vec<String>,
49 }
50 
51 impl UciLoggerFactory for PcapngUciLoggerFactory {
52     type Logger = UciLoggerPcapng;
53 
54     /// PcapngUciLoggerFactory builds UciLoggerPcapng.
build_logger(&mut self, chip_id: &str) -> Option<UciLoggerPcapng>55     fn build_logger(&mut self, chip_id: &str) -> Option<UciLoggerPcapng> {
56         let chip_interface_id = match self.chip_interface_id_map.iter().position(|c| c == chip_id) {
57             Some(id) => id as u32,
58             None => {
59                 let id = self.chip_interface_id_map.len() as u32;
60                 self.chip_interface_id_map.push(chip_id.to_owned());
61                 if self.log_writer.send_chip(chip_id.to_owned(), id).is_none() {
62                     error!("UCI log: associated LogWriterActor is dead");
63                     return None;
64                 }
65                 id
66             }
67         };
68         Some(UciLoggerPcapng::new(self.log_writer.clone(), chip_interface_id))
69     }
70 }
71 
72 /// Builder for PCAPNG log file factory.
73 pub struct PcapngUciLoggerFactoryBuilder {
74     /// Buffer size.
75     buffer_size: usize,
76     /// Max file size:
77     file_size: usize,
78     /// Filename prefix for log file.
79     filename_prefix: String,
80     /// Directory for log file.
81     log_path: PathBuf,
82     /// Range for the rotating index of log files.
83     rotate_range: usize,
84     /// Tokio Runtime Handle for driving Log.
85     runtime_handle: Option<Handle>,
86 }
87 impl Default for PcapngUciLoggerFactoryBuilder {
default() -> Self88     fn default() -> Self {
89         Self {
90             buffer_size: DEFAULT_BUFFER_SIZE,
91             file_size: DEFAUL_FILE_SIZE,
92             filename_prefix: DEFAULT_FILE_PREFIX.to_owned(),
93             log_path: PathBuf::from(DEFAULT_LOG_DIR),
94             rotate_range: 8,
95             runtime_handle: None,
96         }
97     }
98 }
99 
100 impl PcapngUciLoggerFactoryBuilder {
101     /// Constructor.
new() -> Self102     pub fn new() -> Self {
103         PcapngUciLoggerFactoryBuilder::default()
104     }
105 
106     // Setter methods of each field.
107     consuming_builder_field!(runtime_handle, Handle, Some);
108     consuming_builder_field!(filename_prefix, String);
109     consuming_builder_field!(rotate_range, usize);
110     consuming_builder_field!(log_path, PathBuf);
111     consuming_builder_field!(buffer_size, usize);
112     consuming_builder_field!(file_size, usize);
113 
114     /// Builds PcapngUciLoggerFactory
build(self) -> Option<PcapngUciLoggerFactory>115     pub fn build(self) -> Option<PcapngUciLoggerFactory> {
116         let file_factory = FileFactory::new(
117             self.log_path,
118             self.filename_prefix,
119             self.buffer_size,
120             self.rotate_range,
121         );
122         let log_writer = LogWriter::new(file_factory, self.file_size, self.runtime_handle?)?;
123         let manager = PcapngUciLoggerFactory { log_writer, chip_interface_id_map: Vec::new() };
124         Some(manager)
125     }
126 }
127 
128 #[derive(Clone, Debug)]
129 pub(crate) enum PcapngLoggerMessage {
130     ByteStream(Vec<u8>),
131     NewChip((String, u32)),
132 }
133 
134 /// LogWriterActor performs the log writing and file operations asynchronously.
135 struct LogWriterActor {
136     /// Maps chip id to interface id. The content follows the content of the component in
137     /// PcapngUciLoggerFactory with the same name.
138     chip_interface_id_map: Vec<String>,
139     current_file: Option<BufferedFile>,
140     file_factory: FileFactory,
141     file_size_limit: usize,
142     log_receiver: mpsc::UnboundedReceiver<PcapngLoggerMessage>,
143 }
144 
145 impl LogWriterActor {
146     /// write data to file.
write_once(&mut self, data: Vec<u8>) -> Option<()>147     fn write_once(&mut self, data: Vec<u8>) -> Option<()> {
148         // Create new file if the file is not created, or does not fit incoming data:
149         if self.current_file.is_none()
150             || data.len() + self.current_file.as_ref().unwrap().file_size() > self.file_size_limit
151         {
152             self.current_file = Some(
153                 self.file_factory
154                     .build_file_with_metadata(&self.chip_interface_id_map, self.file_size_limit)?,
155             );
156         }
157         self.current_file.as_mut().unwrap().buffered_write(data)
158     }
159 
160     /// Handle single new chip: stores chip in chip_interface_id_map and:
161     ///
162     /// a. Nothing extra if current_file is not created yet.
163     /// b. If current file exists:
164     ///    Insert IDB in current file if it fits, otherwise switch to new file.
handle_new_chip(&mut self, chip_id: String, interface_id: u32) -> Option<()>165     fn handle_new_chip(&mut self, chip_id: String, interface_id: u32) -> Option<()> {
166         if self.chip_interface_id_map.contains(&chip_id)
167             || self.chip_interface_id_map.len() as u32 != interface_id
168         {
169             error!(
170                 "UCI log: unexpected chip_id {} with associated interface id {}",
171                 &chip_id, interface_id
172             );
173             return None;
174         }
175         self.chip_interface_id_map.push(chip_id.clone());
176 
177         if let Some(current_file) = &mut self.current_file {
178             let idb_data = into_interface_description_block(chip_id)?;
179             if idb_data.len() + current_file.file_size() <= self.file_size_limit {
180                 current_file.buffered_write(idb_data)?;
181             } else {
182                 self.current_file =
183                     Some(self.file_factory.build_file_with_metadata(
184                         &self.chip_interface_id_map,
185                         self.file_size_limit,
186                     )?);
187             }
188         }
189         Some(())
190     }
191 
run(&mut self)192     async fn run(&mut self) {
193         debug!("UCI log: LogWriterActor started");
194         loop {
195             match self.log_receiver.recv().await {
196                 Some(PcapngLoggerMessage::NewChip((chip_id, interface_id))) => {
197                     if self.handle_new_chip(chip_id.clone(), interface_id).is_none() {
198                         error!("UCI log: failed logging new chip {}", &chip_id);
199                         break;
200                     }
201                 }
202                 Some(PcapngLoggerMessage::ByteStream(data)) => {
203                     if self.write_once(data).is_none() {
204                         match &self.current_file {
205                             Some(current_file) => {
206                                 error!(
207                                     "UCI log: failed writting packet to log file {:?}",
208                                     current_file.file
209                                 );
210                             }
211                             None => {
212                                 error!("UCI log: failed writting packet to log file: no log file.");
213                             }
214                         }
215                         break;
216                     }
217                 }
218                 None => {
219                     debug!("UCI log: LogWriterActor dropping.");
220                     break;
221                 }
222             }
223         }
224     }
225 }
226 
227 /// Handle to LogWriterActor.
228 #[derive(Clone)]
229 pub(crate) struct LogWriter {
230     log_sender: Option<mpsc::UnboundedSender<PcapngLoggerMessage>>,
231 }
232 
233 impl LogWriter {
234     /// Constructs LogWriter and its actor.
235     ///
236     /// runtime_handle must be a Handle to a multithread runtime that outlives LogWriterActor
new( file_factory: FileFactory, file_size_limit: usize, runtime_handle: Handle, ) -> Option<Self>237     fn new(
238         file_factory: FileFactory,
239         file_size_limit: usize,
240         runtime_handle: Handle,
241     ) -> Option<Self> {
242         let chip_interface_id_map = Vec::new();
243         let (log_sender, log_receiver) = mpsc::unbounded_channel();
244         let mut log_writer_actor = LogWriterActor {
245             chip_interface_id_map,
246             current_file: None,
247             file_factory,
248             file_size_limit,
249             log_receiver,
250         };
251         runtime_handle.spawn(async move { log_writer_actor.run().await });
252         Some(LogWriter { log_sender: Some(log_sender) })
253     }
254 
send_bytes(&mut self, bytes: Vec<u8>) -> Option<()>255     pub fn send_bytes(&mut self, bytes: Vec<u8>) -> Option<()> {
256         let log_sender = self.log_sender.as_ref()?;
257         match log_sender.send(PcapngLoggerMessage::ByteStream(bytes)) {
258             Ok(_) => Some(()),
259             Err(e) => {
260                 error!("UCI log: LogWriterActor dead unexpectedly, sender error: {:?}", e);
261                 self.log_sender = None;
262                 None
263             }
264         }
265     }
266 
send_chip(&mut self, chip_id: String, interface_id: u32) -> Option<()>267     fn send_chip(&mut self, chip_id: String, interface_id: u32) -> Option<()> {
268         let log_sender = self.log_sender.as_ref()?;
269         match log_sender.send(PcapngLoggerMessage::NewChip((chip_id, interface_id))) {
270             Ok(_) => Some(()),
271             Err(e) => {
272                 error!("UCI log: LogWriterActor dead unexpectedly, sender error: {:?}", e);
273                 self.log_sender = None;
274                 None
275             }
276         }
277     }
278 }
279 
into_interface_description_block(chip_id: String) -> Option<Vec<u8>>280 fn into_interface_description_block(chip_id: String) -> Option<Vec<u8>> {
281     let if_name_option = BlockOption::new(0x2, chip_id.into_bytes());
282     InterfaceDescriptionBlockBuilder::new().append_option(if_name_option).into_le_bytes()
283 }
284 
285 /// FileFactory builds next BufferedFile.
286 ///
287 /// The most recent log file is {fileprefix}.pcapng. The archived log files have their index
288 /// increased: {fileprefix}_{n}.pcapng where n = 0..(rotate_range-1).
289 struct FileFactory {
290     log_directory: PathBuf,
291     filename_prefix: String,
292     rotate_range: usize,
293     buffer_size: usize,
294 }
295 
296 impl FileFactory {
297     /// Constructor.
new( log_directory: PathBuf, filename_prefix: String, buffer_size: usize, rotate_range: usize, ) -> FileFactory298     fn new(
299         log_directory: PathBuf,
300         filename_prefix: String,
301         buffer_size: usize,
302         rotate_range: usize,
303     ) -> FileFactory {
304         Self { log_directory, filename_prefix, rotate_range, buffer_size }
305     }
306 
307     /// Builds pcapng file from a file factory, and prepares it with necessary header and metadata.
build_file_with_metadata( &mut self, chip_interface_id_map: &[String], file_size_limit: usize, ) -> Option<BufferedFile>308     fn build_file_with_metadata(
309         &mut self,
310         chip_interface_id_map: &[String],
311         file_size_limit: usize,
312     ) -> Option<BufferedFile> {
313         let mut current_file = self.build_empty_file()?;
314         let mut metadata = Vec::new();
315         metadata.append(&mut HeaderBlockBuilder::new().into_le_bytes()?);
316         for chip_id in chip_interface_id_map.iter() {
317             metadata.append(&mut into_interface_description_block(chip_id.to_owned())?);
318         }
319         if metadata.len() > file_size_limit {
320             error!(
321                 "UCI log: log file size limit is too small ({}) for file header and metadata ({})",
322                 file_size_limit,
323                 metadata.len()
324             );
325         }
326         current_file.buffered_write(metadata)?;
327         Some(current_file)
328     }
329 
330     /// Builds next file as an empty BufferedFile.
build_empty_file(&mut self) -> Option<BufferedFile>331     fn build_empty_file(&mut self) -> Option<BufferedFile> {
332         self.rotate_file()?;
333         let file_path = self.get_file_path(0);
334         BufferedFile::new(&self.log_directory, &file_path, self.buffer_size)
335     }
336 
337     /// get file path for log files of given index.
get_file_path(&self, index: usize) -> PathBuf338     fn get_file_path(&self, index: usize) -> PathBuf {
339         let file_basename = if index == 0 {
340             format!("{}.pcapng", self.filename_prefix)
341         } else {
342             format!("{}_{}.pcapng", self.filename_prefix, index)
343         };
344         self.log_directory.join(file_basename)
345     }
346 
347     /// Vacates {filename_prefix}_0.pcapng for new log.
rotate_file(&self) -> Option<()>348     fn rotate_file(&self) -> Option<()> {
349         for source_idx in (0..self.rotate_range - 1).rev() {
350             let target_idx = source_idx + 1;
351             let source_path = self.get_file_path(source_idx);
352             let target_path = self.get_file_path(target_idx);
353             if source_path.is_dir() {
354                 error!("UCI log: expect {:?} to be a filename, but is a directory", &source_path);
355                 return None;
356             }
357             if source_path.is_file() && fs::rename(&source_path, &target_path).is_err() {
358                 error!(
359                     "UCI log: failed to rename {} to {} while rotating log file.",
360                     source_path.display(),
361                     target_path.display(),
362                 );
363                 return None;
364             }
365         }
366         Some(())
367     }
368 }
369 
370 struct BufferedFile {
371     file: fs::File,
372     written_size: usize,
373     buffer_size: usize,
374     buffer: Vec<u8>,
375 }
376 
377 impl BufferedFile {
378     /// Constructor.
new(log_dir: &Path, file_path: &Path, buffer_size: usize) -> Option<Self>379     pub fn new(log_dir: &Path, file_path: &Path, buffer_size: usize) -> Option<Self> {
380         if file_path.is_file() {
381             if let Err(e) = fs::remove_file(file_path) {
382                 error!("UCI Log: failed to remove {}: {:?}", file_path.display(), e);
383             };
384         }
385         if !log_dir.is_dir() {
386             if let Err(e) = fs::create_dir_all(log_dir) {
387                 error!(
388                     "UCI Log: failed to create log directory {}. Error: {:?}",
389                     log_dir.display(),
390                     e
391                 );
392             }
393         }
394         let file = match fs::OpenOptions::new().write(true).create_new(true).open(file_path) {
395             Ok(f) => f,
396             Err(e) => {
397                 error!(
398                     "UCI Log: failed to create log file {} for write: {:?}",
399                     file_path.display(),
400                     e
401                 );
402                 return None;
403             }
404         };
405         Some(Self { file, written_size: 0, buffer_size, buffer: Vec::new() })
406     }
407 
408     /// Returns the file size received.
file_size(&self) -> usize409     pub fn file_size(&self) -> usize {
410         self.written_size + self.buffer.len()
411     }
412 
413     /// Writes data to file with buffering.
buffered_write(&mut self, mut data: Vec<u8>) -> Option<()>414     pub fn buffered_write(&mut self, mut data: Vec<u8>) -> Option<()> {
415         if self.buffer.len() + data.len() >= self.buffer_size {
416             self.flush_buffer();
417         }
418         self.buffer.append(&mut data);
419         Some(())
420     }
421 
422     /// Clears buffer.
flush_buffer(&mut self) -> Option<()>423     fn flush_buffer(&mut self) -> Option<()> {
424         self.file.write_all(&self.buffer).ok()?;
425         self.written_size += self.buffer.len();
426         self.buffer.clear();
427 
428         self.file.flush().ok()
429     }
430 }
431 
432 /// Manual Drop implementation.
433 impl Drop for BufferedFile {
drop(&mut self)434     fn drop(&mut self) {
435         // Flush buffer before Closing file.
436         self.flush_buffer();
437     }
438 }
439 
440 #[cfg(test)]
441 mod tests {
442     use super::*;
443 
444     use std::{fs, thread, time};
445 
446     use tempfile::tempdir;
447     use tokio::runtime::Builder;
448     use uwb_uci_packets::UciVendor_A_NotificationBuilder;
449 
450     use crate::uci::uci_logger::UciLogger;
451 
452     /// Gets block info from a little-endian PCAPNG file bytestream.
453     ///
454     /// Returns a vector of (block type, block length) if the bytestream is valid PCAPNG.
get_block_info(datastream: Vec<u8>) -> Option<Vec<(u32, u32)>>455     fn get_block_info(datastream: Vec<u8>) -> Option<Vec<(u32, u32)>> {
456         if datastream.len() % 4 != 0 || datastream.is_empty() {
457             return None;
458         }
459         let mut block_info = Vec::new();
460         let mut offset = 0usize;
461         while offset < datastream.len() - 1 {
462             let (_read, unread) = datastream.split_at(offset);
463             if unread.len() < 8 {
464                 return None;
465             }
466             let (type_bytes, unread) = unread.split_at(4);
467             let block_type = u32::from_le_bytes(type_bytes.try_into().unwrap());
468             let (length_bytes, _unread) = unread.split_at(4);
469             let block_length = u32::from_le_bytes(length_bytes.try_into().unwrap());
470             offset += block_length as usize;
471             if offset > datastream.len() {
472                 return None;
473             }
474             block_info.push((block_type, block_length));
475         }
476         Some(block_info)
477     }
478 
479     #[test]
test_no_file_write()480     fn test_no_file_write() {
481         let dir = tempdir().unwrap();
482         {
483             let runtime = Builder::new_multi_thread().enable_all().build().unwrap();
484             let mut file_manager = PcapngUciLoggerFactoryBuilder::new()
485                 .buffer_size(1024)
486                 .filename_prefix("log".to_owned())
487                 .log_path(dir.as_ref().to_owned())
488                 .runtime_handle(runtime.handle().to_owned())
489                 .build()
490                 .unwrap();
491             let _logger_0 = file_manager.build_logger("logger 0").unwrap();
492             let _logger_1 = file_manager.build_logger("logger 1").unwrap();
493             // Sleep needed to guarantee handling pending logs before runtime goes out of scope.
494             thread::sleep(time::Duration::from_millis(10));
495         }
496         // Expect no log file created as no packet is received.
497         let log_path = dir.as_ref().to_owned().join("log.pcapng");
498         assert!(fs::read(log_path).is_err());
499     }
500 
501     #[test]
test_no_preexisting_dir_created()502     fn test_no_preexisting_dir_created() {
503         let dir_root = Path::new("./uwb_test_dir_123");
504         let dir = dir_root.join("this/path/doesnt/exist");
505         {
506             let runtime = Builder::new_multi_thread().enable_all().build().unwrap();
507             let mut file_manager = PcapngUciLoggerFactoryBuilder::new()
508                 .buffer_size(1024)
509                 .filename_prefix("log".to_owned())
510                 .log_path(dir.clone())
511                 .runtime_handle(runtime.handle().to_owned())
512                 .build()
513                 .unwrap();
514             let mut logger_0 = file_manager.build_logger("logger 0").unwrap();
515             let packet_0 = UciVendor_A_NotificationBuilder { opcode: 0, payload: None }.build();
516             logger_0.log_uci_control_packet(packet_0.into());
517             // Sleep needed to guarantee handling pending logs before runtime goes out of scope.
518             thread::sleep(time::Duration::from_millis(10));
519         }
520         // Expect the dir was created.
521         assert!(dir.is_dir());
522         // Expect the log file exists.
523         let log_path = dir.join("log.pcapng");
524         assert!(log_path.is_file());
525         // Clear test dir
526         let _ = fs::remove_dir_all(dir_root);
527     }
528 
529     #[test]
test_single_file_write()530     fn test_single_file_write() {
531         let dir = tempdir().unwrap();
532         {
533             let runtime = Builder::new_multi_thread().enable_all().build().unwrap();
534             let mut file_manager = PcapngUciLoggerFactoryBuilder::new()
535                 .buffer_size(1024)
536                 .filename_prefix("log".to_owned())
537                 .log_path(dir.as_ref().to_owned())
538                 .runtime_handle(runtime.handle().to_owned())
539                 .build()
540                 .unwrap();
541             let mut logger_0 = file_manager.build_logger("logger 0").unwrap();
542             let packet_0 = UciVendor_A_NotificationBuilder { opcode: 0, payload: None }.build();
543             logger_0.log_uci_control_packet(packet_0.into());
544             let mut logger_1 = file_manager.build_logger("logger 1").unwrap();
545             let packet_1 = UciVendor_A_NotificationBuilder { opcode: 1, payload: None }.build();
546             logger_1.log_uci_control_packet(packet_1.into());
547             let packet_2 = UciVendor_A_NotificationBuilder { opcode: 2, payload: None }.build();
548             logger_0.log_uci_control_packet(packet_2.into());
549             // Sleep needed to guarantee handling pending logs before runtime goes out of scope.
550             thread::sleep(time::Duration::from_millis(10));
551         }
552         // Expect file log.pcapng consist of SHB->IDB(logger 0)->EPB(packet 0)->IDB(logger 1)
553         // ->EPB(packet 1)->EPB(packet 2)
554         let log_path = dir.as_ref().to_owned().join("log.pcapng");
555         let log_content = fs::read(log_path).unwrap();
556         let block_info = get_block_info(log_content).unwrap();
557         assert_eq!(block_info.len(), 6);
558         assert_eq!(block_info[0].0, 0x0A0D_0D0A); // SHB
559         assert_eq!(block_info[1].0, 0x1); // IDB
560         assert_eq!(block_info[2].0, 0x6); // EPB
561         assert_eq!(block_info[3].0, 0x1); // IDB
562         assert_eq!(block_info[4].0, 0x6); // EPB
563         assert_eq!(block_info[5].0, 0x6); // EPB
564     }
565 
566     #[test]
test_file_switch_epb_unfit_case()567     fn test_file_switch_epb_unfit_case() {
568         let dir = tempdir().unwrap();
569         let last_file_expected = dir.as_ref().to_owned().join("log_2.pcapng");
570         {
571             let runtime = Builder::new_multi_thread().enable_all().build().unwrap();
572             let mut file_manager_140 = PcapngUciLoggerFactoryBuilder::new()
573                 .buffer_size(1024)
574                 .filename_prefix("log".to_owned())
575                 .log_path(dir.as_ref().to_owned())
576                 .file_size(140)
577                 .runtime_handle(runtime.handle().to_owned())
578                 .build()
579                 .unwrap();
580             let mut logger_0 = file_manager_140.build_logger("logger 0").unwrap();
581             let packet_0 = UciVendor_A_NotificationBuilder { opcode: 0, payload: None }.build();
582             logger_0.log_uci_control_packet(packet_0.into());
583             let mut logger_1 = file_manager_140.build_logger("logger 1").unwrap();
584             let packet_1 = UciVendor_A_NotificationBuilder { opcode: 1, payload: None }.build();
585             logger_1.log_uci_control_packet(packet_1.into());
586             let packet_2 = UciVendor_A_NotificationBuilder { opcode: 2, payload: None }.build();
587             logger_0.log_uci_control_packet(packet_2.into());
588             // Sleep needed to guarantee handling pending logs before runtime goes out of scope.
589             let mut timeout = 100;
590             let timeout_slice = 10;
591             loop {
592                 if last_file_expected.exists() || timeout == 0 {
593                     break;
594                 }
595                 thread::sleep(time::Duration::from_millis(timeout_slice));
596                 timeout -= timeout_slice;
597             }
598         }
599         // Expect (Old to new):
600         // File 2: SHB->IDB->EPB->IDB (cannot fit next)
601         // File 1: SHB->IDB->IDB->EPB (cannot fit next)
602         // File 0: SHB->IDB->IDB->EPB
603         let log_path = dir.as_ref().to_owned().join("log_2.pcapng");
604         let log_content = fs::read(log_path).unwrap();
605         let block_info = get_block_info(log_content).unwrap();
606         assert_eq!(block_info.len(), 4);
607         assert_eq!(block_info[0].0, 0x0A0D_0D0A); // SHB
608         assert_eq!(block_info[1].0, 0x1); // IDB
609         assert_eq!(block_info[2].0, 0x6); // EPB
610         assert_eq!(block_info[3].0, 0x1); // IDB
611         let log_path = dir.as_ref().to_owned().join("log_1.pcapng");
612         let log_content = fs::read(log_path).unwrap();
613         let block_info = get_block_info(log_content).unwrap();
614         assert_eq!(block_info.len(), 4);
615         assert_eq!(block_info[0].0, 0x0A0D_0D0A); // SHB
616         assert_eq!(block_info[1].0, 0x1); // IDB
617         assert_eq!(block_info[2].0, 0x1); // IDB
618         assert_eq!(block_info[3].0, 0x6); // EPB
619         let log_path = dir.as_ref().to_owned().join("log.pcapng");
620         let log_content = fs::read(log_path).unwrap();
621         let block_info = get_block_info(log_content).unwrap();
622         assert_eq!(block_info.len(), 4);
623         assert_eq!(block_info[0].0, 0x0A0D_0D0A); // SHB
624         assert_eq!(block_info[1].0, 0x1); // IDB
625         assert_eq!(block_info[2].0, 0x1); // IDB
626         assert_eq!(block_info[3].0, 0x6); // EPB
627     }
628 
629     #[test]
test_file_switch_idb_unfit_case()630     fn test_file_switch_idb_unfit_case() {
631         let dir = tempdir().unwrap();
632         {
633             let runtime = Builder::new_multi_thread().enable_all().build().unwrap();
634             let mut file_manager_144 = PcapngUciLoggerFactoryBuilder::new()
635                 .buffer_size(1024)
636                 .filename_prefix("log".to_owned())
637                 .log_path(dir.as_ref().to_owned())
638                 .file_size(144)
639                 .runtime_handle(runtime.handle().to_owned())
640                 .build()
641                 .unwrap();
642             let mut logger_0 = file_manager_144.build_logger("logger 0").unwrap();
643             let packet_0 = UciVendor_A_NotificationBuilder { opcode: 0, payload: None }.build();
644             logger_0.log_uci_control_packet(packet_0.into());
645             let packet_2 = UciVendor_A_NotificationBuilder { opcode: 2, payload: None }.build();
646             logger_0.log_uci_control_packet(packet_2.into());
647             let mut logger_1 = file_manager_144.build_logger("logger 1").unwrap();
648             let packet_1 = UciVendor_A_NotificationBuilder { opcode: 1, payload: None }.build();
649             logger_1.log_uci_control_packet(packet_1.into());
650             // Sleep needed to guarantee handling pending logs before runtime goes out of scope.
651             thread::sleep(time::Duration::from_millis(10));
652         }
653         // Expect (Old to new):
654         // File 1: SHB->IDB->EPB->EPB (cannot fit next)
655         // File 0: SHB->IDB->IDB->EPB
656         let log_path = dir.as_ref().to_owned().join("log_1.pcapng");
657         let log_content = fs::read(log_path).unwrap();
658         let block_info = get_block_info(log_content).unwrap();
659         assert_eq!(block_info.len(), 4);
660         assert_eq!(block_info[0].0, 0x0A0D_0D0A); // SHB
661         assert_eq!(block_info[1].0, 0x1); // IDB
662         assert_eq!(block_info[2].0, 0x6); // EPB
663         assert_eq!(block_info[3].0, 0x6); // EPB
664         let log_path = dir.as_ref().to_owned().join("log.pcapng");
665         let log_content = fs::read(log_path).unwrap();
666         let block_info = get_block_info(log_content).unwrap();
667         assert_eq!(block_info.len(), 4);
668         assert_eq!(block_info[0].0, 0x0A0D_0D0A); // SHB
669         assert_eq!(block_info[1].0, 0x1); // IDB
670         assert_eq!(block_info[2].0, 0x1); // IDB
671         assert_eq!(block_info[3].0, 0x6); // EPB
672     }
673 
674     // Program shall not panic even if log writing has failed for some reason.
675     #[test]
test_log_fail_safe()676     fn test_log_fail_safe() {
677         let dir = tempdir().unwrap();
678         {
679             let runtime = Builder::new_multi_thread().enable_all().build().unwrap();
680             let mut file_manager_96 = PcapngUciLoggerFactoryBuilder::new()
681                 .buffer_size(1024)
682                 .filename_prefix("log".to_owned())
683                 .log_path(dir.as_ref().to_owned())
684                 .file_size(96) // Fails logging, as metadata takes 100
685                 .runtime_handle(runtime.handle().to_owned())
686                 .build()
687                 .unwrap();
688             let mut logger_0 = file_manager_96.build_logger("logger 0").unwrap();
689             let packet_0 = UciVendor_A_NotificationBuilder { opcode: 0, payload: None }.build();
690             logger_0.log_uci_control_packet(packet_0.into());
691             let packet_2 = UciVendor_A_NotificationBuilder { opcode: 2, payload: None }.build();
692             logger_0.log_uci_control_packet(packet_2.into());
693             let mut logger_1 = file_manager_96.build_logger("logger 1").unwrap();
694             let packet_1 = UciVendor_A_NotificationBuilder { opcode: 1, payload: None }.build();
695             logger_1.log_uci_control_packet(packet_1.into());
696         }
697     }
698 }
699