1use rustc_hash::FxHashMap;
2use std::cell::RefCell;
3use std::env;
4use std::fs::{self, File};
5use std::io::{self, BufReader, Read};
6use std::path::{Path, PathBuf};
7use walkdir::WalkDir;
8use zip::ZipArchive;
9
10use crate::defs::*;
11
12#[derive(Debug)]
13pub enum ArchiveType {
14 Zip(RefCell<ZipArchive<BufReader<File>>>),
15 Dir(PathBuf),
16 Plain(Vec<PathBuf>),
17}
18
19#[derive(Debug)]
20pub struct Archive {
21 pub name: String,
22 pub item: RefCell<ArchiveType>,
23}
24
25#[derive(Debug, PartialEq, Eq, Hash)]
26pub struct GCNOStem {
27 pub stem: String,
28 pub llvm: bool,
29}
30
31#[cfg(not(windows))]
32fn clean_path(path: &Path) -> String {
33 path.to_str().unwrap().to_string()
34}
35
36#[cfg(windows)]
37fn clean_path(path: &Path) -> String {
38 path.to_str().unwrap().to_string().replace("\\", "/")
39}
40
41impl Archive {
42 fn insert_vec<'a>(
43 &'a self,
44 filename: String,
45 map: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
46 ) {
47 let mut map = map.borrow_mut();
48 map.entry(filename)
49 .or_insert_with(|| Vec::with_capacity(1))
50 .push(self);
51 }
52
53 fn handle_file<'a>(
54 &'a self,
55 file: Option<&mut impl Read>,
56 path: &Path,
57 gcno_stem_archives: &RefCell<FxHashMap<GCNOStem, &'a Archive>>,
58 gcda_stem_archives: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
59 profdatas: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
60 profraws: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
61 infos: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
62 xmls: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
63 gocovs: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
64 linked_files_maps: &RefCell<FxHashMap<String, &'a Archive>>,
65 is_llvm: bool,
66 ) {
67 if let Some(ext) = path.extension() {
68 match ext.to_str().unwrap() {
69 "gcno" => {
70 let llvm = is_llvm || Archive::check_file(file, &Archive::is_gcno_llvm);
71 let filename = clean_path(&path.with_extension(""));
72 gcno_stem_archives.borrow_mut().insert(
73 GCNOStem {
74 stem: filename,
75 llvm,
76 },
77 self,
78 );
79 }
80 "gcda" => {
81 let filename = clean_path(&path.with_extension(""));
82 self.insert_vec(filename, gcda_stem_archives);
83 }
84 "profdata" => {
85 let filename = clean_path(path);
86 self.insert_vec(filename, profdatas);
87 }
88 "profraw" => {
89 let filename = clean_path(path);
90 self.insert_vec(filename, profraws);
91 }
92 "info" | "dat" => {
93 if Archive::check_file(file, &Archive::is_info) {
94 let filename = clean_path(path);
95 self.insert_vec(filename, infos);
96 }
97 }
98 "xml" => {
99 if Archive::check_file(file, &Archive::is_jacoco) {
100 let filename = clean_path(path);
101 self.insert_vec(filename, xmls);
102 }
103 }
104 "json" => {
105 let filename = path.file_name().unwrap();
106 if filename == "linked-files-map.json" {
107 let filename = clean_path(path);
108 linked_files_maps.borrow_mut().insert(filename, self);
109 }
110 }
111 "out" => {
112 if Archive::check_file(file, &Archive::is_go_cov) {
113 let filename = clean_path(path);
114 self.insert_vec(filename, gocovs);
115 }
116 }
117 _ => {}
118 }
119 }
120 }
121
122 fn is_gcno_llvm(reader: &mut dyn Read) -> bool {
123 let mut bytes: [u8; 8] = [0; 8];
124 reader.read_exact(&mut bytes).is_ok()
125 && &bytes[..5] == b"oncg*"
126 && (&bytes[5..] == b"204" || &bytes[5..] == b"804")
127 }
128
129 fn is_jacoco(reader: &mut dyn Read) -> bool {
130 let mut bytes: [u8; 256] = [0; 256];
131 if reader.read_exact(&mut bytes).is_ok() {
132 return match String::from_utf8(bytes.to_vec()) {
133 Ok(s) => s.contains("-//JACOCO//DTD"),
134 Err(_) => false,
135 };
136 }
137 false
138 }
139
140 fn is_go_cov(reader: &mut dyn Read) -> bool {
141 let mut bytes: [u8; 8] = [0; 8];
142 reader.read_exact(&mut bytes).is_ok() && &bytes[..5] == b"mode:"
143 }
144
145 fn is_info(reader: &mut dyn Read) -> bool {
146 let mut bytes: [u8; 3] = [0; 3];
147 reader.read_exact(&mut bytes).is_ok()
148 && (bytes == [b'T', b'N', b':'] || bytes == [b'S', b'F', b':'])
149 }
150
151 fn check_file(file: Option<&mut impl Read>, checker: &dyn Fn(&mut dyn Read) -> bool) -> bool {
152 file.is_some_and(|f| checker(f))
153 }
154
155 pub fn get_name(&self) -> &String {
156 &self.name
157 }
158
159 pub fn explore<'a>(
160 &'a mut self,
161 gcno_stem_archives: &RefCell<FxHashMap<GCNOStem, &'a Archive>>,
162 gcda_stem_archives: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
163 profdatas: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
164 profraws: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
165 infos: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
166 xmls: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
167 gocovs: &RefCell<FxHashMap<String, Vec<&'a Archive>>>,
168 linked_files_maps: &RefCell<FxHashMap<String, &'a Archive>>,
169 is_llvm: bool,
170 ) {
171 match *self.item.borrow() {
172 ArchiveType::Zip(ref zip) => {
173 let mut zip = zip.borrow_mut();
174 for i in 0..zip.len() {
175 let mut file = zip.by_index(i).unwrap();
176 let path = PathBuf::from(file.name());
177 self.handle_file(
178 Some(&mut file),
179 &path,
180 gcno_stem_archives,
181 gcda_stem_archives,
182 profdatas,
183 profraws,
184 infos,
185 xmls,
186 gocovs,
187 linked_files_maps,
188 is_llvm,
189 );
190 }
191 }
192 ArchiveType::Dir(ref dir) => {
193 for entry in WalkDir::new(dir) {
194 let entry = entry.unwrap_or_else(|err| {
195 panic!(
196 "Failed to open '{}'.",
197 err.path().unwrap().to_string_lossy()
198 )
199 });
200 let full_path = entry.path();
201 if full_path.is_file() {
202 let mut file = File::open(full_path).ok();
203 let path = full_path.strip_prefix(dir).unwrap();
204 self.handle_file(
205 file.as_mut(),
206 path,
207 gcno_stem_archives,
208 gcda_stem_archives,
209 profdatas,
210 profraws,
211 infos,
212 xmls,
213 gocovs,
214 linked_files_maps,
215 is_llvm,
216 );
217 }
218 }
219 }
220 ArchiveType::Plain(ref plain) => {
221 for full_path in plain {
223 let mut file = File::open(full_path).ok();
224 self.handle_file(
225 file.as_mut(),
226 full_path,
227 gcno_stem_archives,
228 gcda_stem_archives,
229 profdatas,
230 profraws,
231 infos,
232 xmls,
233 gocovs,
234 linked_files_maps,
235 is_llvm,
236 );
237 }
238 }
239 }
240 }
241
242 pub fn read(&self, name: &str) -> Option<Vec<u8>> {
243 match *self.item.borrow_mut() {
244 ArchiveType::Zip(ref mut zip) => {
245 let mut zip = zip.borrow_mut();
246 let zipfile = zip.by_name(name);
247 match zipfile {
248 Ok(mut f) => {
249 let mut buf = Vec::with_capacity(f.size() as usize + 1);
250 f.read_to_end(&mut buf).expect("Failed to read gcda file");
251 Some(buf)
252 }
253 Err(_) => None,
254 }
255 }
256 ArchiveType::Dir(ref dir) => {
257 let path = dir.join(name);
258 if let Ok(metadata) = fs::metadata(&path) {
259 match File::open(path) {
260 Ok(mut f) => {
261 let mut buf = Vec::with_capacity(metadata.len() as usize + 1);
262 f.read_to_end(&mut buf).expect("Failed to read gcda file");
263 Some(buf)
264 }
265 Err(_) => None,
266 }
267 } else {
268 None
269 }
270 }
271 ArchiveType::Plain(_) => {
272 if let Ok(metadata) = fs::metadata(name) {
273 match File::open(name) {
274 Ok(mut f) => {
275 let mut buf = Vec::with_capacity(metadata.len() as usize + 1);
276 f.read_to_end(&mut buf)
277 .unwrap_or_else(|_| panic!("Failed to read file: {}.", name));
278 Some(buf)
279 }
280 Err(_) => None,
281 }
282 } else {
283 None
284 }
285 }
286 }
287 }
288
289 pub fn extract(&self, name: &str, path: &Path) -> bool {
290 let dest_parent = path.parent().unwrap();
291 if !dest_parent.exists() {
292 fs::create_dir_all(dest_parent).expect("Cannot create parent directory");
293 }
294
295 match *self.item.borrow_mut() {
296 ArchiveType::Zip(ref mut zip) => {
297 let mut zip = zip.borrow_mut();
298 let zipfile = zip.by_name(name);
299 if let Ok(mut f) = zipfile {
300 let mut file = File::create(path).expect("Failed to create file");
301 io::copy(&mut f, &mut file).expect("Failed to copy file from ZIP");
302 true
303 } else {
304 false
305 }
306 }
307 ArchiveType::Dir(ref dir) => {
308 let src_path = dir.join(name);
310
311 crate::symlink::symlink_file(&src_path, path).unwrap_or_else(|_| {
312 panic!("Failed to create a symlink {:?} -> {:?}", src_path, path)
313 });
314 true
315 }
316 ArchiveType::Plain(_) => {
317 panic!("We shouldn't be there !!");
318 }
319 }
320 }
321}
322
323fn gcno_gcda_producer(
324 tmp_dir: &Path,
325 gcno_stem_archives: &FxHashMap<GCNOStem, &Archive>,
326 gcda_stem_archives: &FxHashMap<String, Vec<&Archive>>,
327 sender: &JobSender,
328 ignore_orphan_gcno: bool,
329) {
330 let send_job = |item, name| {
331 sender
332 .send(Some(WorkItem {
333 format: ItemFormat::Gcno,
334 item,
335 name,
336 }))
337 .unwrap()
338 };
339
340 for (gcno_stem, gcno_archive) in gcno_stem_archives {
341 let stem = &gcno_stem.stem;
342 if let Some(gcda_archives) = gcda_stem_archives.get(stem) {
343 let gcno_archive = *gcno_archive;
344 let gcno = format!("{stem}.gcno").to_string();
345 let physical_gcno_path = tmp_dir.join(format!("{}_{}.gcno", stem, 1));
346 if gcno_stem.llvm {
347 let mut gcda_buffers: Vec<Vec<u8>> = Vec::with_capacity(gcda_archives.len());
348 if let Some(gcno_buffer) = gcno_archive.read(&gcno) {
349 for gcda_archive in gcda_archives {
350 let gcda = format!("{stem}.gcda").to_string();
351 if let Some(gcda_buf) = gcda_archive.read(&gcda) {
352 gcda_buffers.push(gcda_buf);
353 }
354 }
355 send_job(
356 ItemType::Buffers(GcnoBuffers {
357 stem: stem.clone(),
358 gcno_buf: gcno_buffer,
359 gcda_buf: gcda_buffers,
360 }),
361 "".to_string(),
362 );
363 }
364 } else {
365 gcno_archive.extract(&gcno, &physical_gcno_path);
366 for (num, &gcda_archive) in gcda_archives.iter().enumerate() {
367 let gcno_path = tmp_dir.join(format!("{}_{}.gcno", stem, num + 1));
368 let gcda = format!("{stem}.gcda").to_string();
369
370 if num != 0 {
372 fs::hard_link(&physical_gcno_path, &gcno_path).unwrap_or_else(|_| {
373 panic!("Failed to create hardlink {:?}", gcno_path)
374 });
375 }
376
377 let gcda_path = tmp_dir.join(format!("{}_{}.gcda", stem, num + 1));
378 if gcda_archive.extract(&gcda, &gcda_path) || (num == 0 && !ignore_orphan_gcno)
379 {
380 send_job(
381 ItemType::Path((stem.clone(), gcno_path)),
382 gcda_archive.get_name().to_string(),
383 );
384 }
385 }
386 }
387 } else if !ignore_orphan_gcno {
388 let gcno_archive = *gcno_archive;
389 let gcno = format!("{stem}.gcno").to_string();
390 if gcno_stem.llvm {
391 if let Some(gcno_buf) = gcno_archive.read(&gcno) {
392 send_job(
393 ItemType::Buffers(GcnoBuffers {
394 stem: stem.clone(),
395 gcno_buf,
396 gcda_buf: Vec::new(),
397 }),
398 gcno_archive.get_name().to_string(),
399 );
400 }
401 } else {
402 let physical_gcno_path = tmp_dir.join(format!("{}_{}.gcno", stem, 1));
403 if gcno_archive.extract(&gcno, &physical_gcno_path) {
404 send_job(
405 ItemType::Path((stem.clone(), physical_gcno_path)),
406 gcno_archive.get_name().to_string(),
407 );
408 }
409 }
410 }
411 }
412}
413
414fn llvm_format_producer(
415 tmp_dir: &Path,
416 profiles: &FxHashMap<String, Vec<&Archive>>,
417 item_format: ItemFormat,
418 sender: &JobSender,
419) {
420 let ext = match item_format {
421 ItemFormat::Profdata => "profdata",
422 ItemFormat::Profraw => "profraw",
423 _ => panic!("function is specific to profdata and profraw files"),
424 };
425
426 if profiles.is_empty() {
427 return;
428 }
429
430 let mut profile_paths = Vec::new();
431
432 for (name, archives) in profiles {
433 let path = PathBuf::from(name);
434 let stem = clean_path(&path.with_extension(""));
435
436 for (num, &archive) in archives.iter().enumerate() {
439 let profile_path = if let ArchiveType::Plain(_) = *archive.item.borrow() {
440 Some(path.clone())
441 } else {
442 None
443 };
444
445 let profile_path = if let Some(profile_path) = profile_path {
446 profile_path
447 } else {
448 let tmp_path = tmp_dir.join(format!("{}_{}.{}", stem, num + 1, ext));
449 archive.extract(name, &tmp_path);
450 tmp_path
451 };
452
453 profile_paths.push(profile_path);
454 }
455 }
456
457 sender
458 .send(Some(WorkItem {
459 format: item_format,
460 item: ItemType::Paths(profile_paths),
461 name: ext.to_string(),
462 }))
463 .unwrap()
464}
465
466fn file_content_producer(
467 files: &FxHashMap<String, Vec<&Archive>>,
468 sender: &JobSender,
469 item_format: ItemFormat,
470) {
471 for (name, archives) in files {
472 for archive in archives {
473 if let Some(buffer) = archive.read(name) {
474 sender
475 .send(Some(WorkItem {
476 format: item_format,
477 item: ItemType::Content(buffer),
478 name: archive.get_name().to_string(),
479 }))
480 .unwrap();
481 }
482 }
483 }
484}
485
486pub fn get_mapping(linked_files_maps: &FxHashMap<String, &Archive>) -> Option<Vec<u8>> {
487 if let Some((name, archive)) = linked_files_maps.iter().next() {
488 archive.read(name)
489 } else {
490 None
491 }
492}
493
494fn open_archive(path: &str) -> ZipArchive<BufReader<File>> {
495 let file = File::open(path).unwrap_or_else(|_| panic!("Failed to open ZIP file '{}'.", path));
496 let reader = BufReader::new(file);
497 ZipArchive::new(reader).unwrap_or_else(|_| panic!("Failed to parse ZIP file: {}", path))
498}
499
500pub fn producer(
501 tmp_dir: &Path,
502 paths: &[String],
503 sender: &JobSender,
504 ignore_orphan_gcno: bool,
505 is_llvm: bool,
506) -> Option<Vec<u8>> {
507 let mut archives: Vec<Archive> = Vec::new();
508 let mut plain_files: Vec<PathBuf> = Vec::new();
509
510 let current_dir = env::current_dir().unwrap();
511
512 for path in paths {
513 if path.ends_with(".zip") {
514 let archive = open_archive(path);
515 archives.push(Archive {
516 name: path.to_string(),
517 item: RefCell::new(ArchiveType::Zip(RefCell::new(archive))),
518 });
519 } else {
520 let path_dir = PathBuf::from(path);
521 let full_path = if path_dir.is_relative() {
522 current_dir.join(path_dir)
523 } else {
524 path_dir
525 };
526 if full_path.is_dir() {
527 archives.push(Archive {
528 name: path.to_string(),
529 item: RefCell::new(ArchiveType::Dir(full_path)),
530 });
531 } else if let Some(ext) = full_path.clone().extension() {
532 let ext = ext.to_str().unwrap();
533 if ext == "info"
534 || ext == "json"
535 || ext == "xml"
536 || ext == "profraw"
537 || ext == "profdata"
538 || ext == "out"
539 || ext == "dat"
540 {
541 plain_files.push(full_path);
542 } else {
543 panic!(
544 "Cannot load file '{:?}': it isn't a .info, a .json, a .out, a .xml, or a .dat file.",
545 full_path
546 );
547 }
548 } else {
549 panic!("Cannot load file '{:?}': it isn't a directory, a .info, a .json, a .out, a .xml, or a .dat file.", full_path);
550 }
551 }
552 }
553
554 if !plain_files.is_empty() {
555 archives.push(Archive {
556 name: "plain files".to_string(),
557 item: RefCell::new(ArchiveType::Plain(plain_files)),
558 });
559 }
560
561 let gcno_stems_archives: RefCell<FxHashMap<GCNOStem, &Archive>> =
562 RefCell::new(FxHashMap::default());
563 let gcda_stems_archives: RefCell<FxHashMap<String, Vec<&Archive>>> =
564 RefCell::new(FxHashMap::default());
565 let profdatas: RefCell<FxHashMap<String, Vec<&Archive>>> = RefCell::new(FxHashMap::default());
566 let profraws: RefCell<FxHashMap<String, Vec<&Archive>>> = RefCell::new(FxHashMap::default());
567 let infos: RefCell<FxHashMap<String, Vec<&Archive>>> = RefCell::new(FxHashMap::default());
568 let xmls: RefCell<FxHashMap<String, Vec<&Archive>>> = RefCell::new(FxHashMap::default());
569 let gocovs: RefCell<FxHashMap<String, Vec<&Archive>>> = RefCell::new(FxHashMap::default());
570
571 let linked_files_maps: RefCell<FxHashMap<String, &Archive>> =
572 RefCell::new(FxHashMap::default());
573
574 for archive in &mut archives {
575 archive.explore(
576 &gcno_stems_archives,
577 &gcda_stems_archives,
578 &profdatas,
579 &profraws,
580 &infos,
581 &xmls,
582 &gocovs,
583 &linked_files_maps,
584 is_llvm,
585 );
586 }
587
588 assert!(
589 !(gcno_stems_archives.borrow().is_empty()
590 && profdatas.borrow().is_empty()
591 && profraws.borrow().is_empty()
592 && infos.borrow().is_empty()
593 && gocovs.borrow().is_empty()
594 && xmls.borrow().is_empty()),
595 "No input files found"
596 );
597
598 file_content_producer(&infos.into_inner(), sender, ItemFormat::Info);
599 file_content_producer(&xmls.into_inner(), sender, ItemFormat::JacocoXml);
600 file_content_producer(&gocovs.into_inner(), sender, ItemFormat::Gocov);
601 llvm_format_producer(
602 tmp_dir,
603 &profdatas.into_inner(),
604 ItemFormat::Profdata,
605 sender,
606 );
607 llvm_format_producer(tmp_dir, &profraws.into_inner(), ItemFormat::Profraw, sender);
608 gcno_gcda_producer(
609 tmp_dir,
610 &gcno_stems_archives.into_inner(),
611 &gcda_stems_archives.into_inner(),
612 sender,
613 ignore_orphan_gcno,
614 );
615
616 get_mapping(&linked_files_maps.into_inner())
617}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622 use crossbeam_channel::unbounded;
623 use serde_json::{self, Value};
624
625 fn check_produced(
626 directory: PathBuf,
627 receiver: &JobReceiver,
628 expected: Vec<(ItemFormat, bool, &str, bool)>,
629 ) {
630 let mut vec: Vec<Option<WorkItem>> = Vec::new();
631
632 while let Ok(elem) = receiver.try_recv() {
633 vec.push(elem);
634 }
635
636 for elem in &expected {
637 assert!(
638 vec.iter().any(|x| {
639 if !x.is_some() {
640 return false;
641 }
642
643 let x = x.as_ref().unwrap();
644
645 if x.format != elem.0 {
646 return false;
647 }
648
649 match x.item {
650 ItemType::Content(_) => !elem.1,
651 ItemType::Path((_, ref p)) => elem.1 && p.ends_with(elem.2),
652 ItemType::Paths(ref paths) => paths.iter().any(|p| p.ends_with(elem.2)),
653 ItemType::Buffers(ref b) => b.stem.replace('\\', "/").ends_with(elem.2),
654 }
655 }),
656 "Missing {:?}",
657 elem
658 );
659 }
660
661 for v in &vec {
662 let v = v.as_ref().unwrap();
663 assert!(
664 expected.iter().any(|x| {
665 if v.format != x.0 {
666 return false;
667 }
668
669 match v.item {
670 ItemType::Content(_) => !x.1,
671 ItemType::Path((_, ref p)) => x.1 && p.ends_with(x.2),
672 ItemType::Paths(ref paths) => paths.iter().any(|p| p.ends_with(x.2)),
673 ItemType::Buffers(ref b) => b.stem.replace('\\', "/").ends_with(x.2),
674 }
675 }),
676 "Unexpected {:?}",
677 v
678 );
679 }
680
681 assert!(vec.len() <= expected.len());
683
684 for x in expected.iter() {
686 if !x.1 {
687 continue;
688 }
689
690 let p = directory.join(x.2);
691 assert!(p.exists(), "{} doesn't exist", p.display());
692 if x.0 == ItemFormat::Gcno {
693 let gcda =
694 p.with_file_name(format!("{}.gcda", p.file_stem().unwrap().to_str().unwrap()));
695 if x.3 {
696 assert!(gcda.exists(), "{} doesn't exist", gcda.display());
697 } else {
698 assert!(!gcda.exists(), "{} exists", gcda.display());
699 }
700 }
701 }
702 }
703
704 #[test]
705 fn test_dir_producer() {
706 let (sender, receiver) = unbounded();
707
708 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
709 let tmp_path = tmp_dir.path().to_owned();
710 let mapping = producer(&tmp_path, &["test".to_string()], &sender, false, false);
711
712 let expected = vec![
713 (ItemFormat::Gcno, true, "Platform_1.gcno", true),
714 (
715 ItemFormat::Gcno,
716 true,
717 "sub2/RootAccessibleWrap_1.gcno",
718 true,
719 ),
720 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_1.gcno", true),
721 (ItemFormat::Gcno, true, "sub/prova2_1.gcno", true),
722 (
723 ItemFormat::Gcno,
724 true,
725 "nsMaiInterfaceDocument_1.gcno",
726 true,
727 ),
728 (
729 ItemFormat::Gcno,
730 true,
731 "Unified_cpp_netwerk_base0_1.gcno",
732 true,
733 ),
734 (ItemFormat::Gcno, true, "prova_1.gcno", true),
735 (ItemFormat::Gcno, true, "nsGnomeModule_1.gcno", true),
736 (ItemFormat::Gcno, true, "negative_counts_1.gcno", true),
737 (ItemFormat::Gcno, true, "64bit_count_1.gcno", true),
738 (ItemFormat::Gcno, true, "no_gcda/main_1.gcno", false),
739 (ItemFormat::Gcno, true, "only_one_gcda/main_1.gcno", true),
740 (ItemFormat::Gcno, true, "only_one_gcda/orphan_1.gcno", false),
741 (
742 ItemFormat::Gcno,
743 true,
744 "gcno_symlink/gcda/main_1.gcno",
745 true,
746 ),
747 (
748 ItemFormat::Gcno,
749 true,
750 "gcno_symlink/gcno/main_1.gcno",
751 false,
752 ),
753 (
754 ItemFormat::Gcno,
755 false,
756 "rust/generics_with_two_parameters",
757 true,
758 ),
759 (ItemFormat::Gcno, true, "reader_gcc-6_1.gcno", true),
760 (ItemFormat::Gcno, true, "reader_gcc-7_1.gcno", true),
761 (ItemFormat::Gcno, true, "reader_gcc-8_1.gcno", true),
762 (ItemFormat::Gcno, true, "reader_gcc-9_1.gcno", true),
763 (ItemFormat::Gcno, true, "reader_gcc-10_1.gcno", true),
764 (ItemFormat::Gcno, true, "reader_clang-22_1.gcno", true),
765 (ItemFormat::Info, false, "1494603973-2977-7.info", false),
766 (ItemFormat::Info, false, "prova.info", false),
767 (ItemFormat::Info, false, "prova_fn_with_commas.info", false),
768 (ItemFormat::Info, false, "empty_line.info", false),
769 (ItemFormat::Info, false, "invalid_DA_record.info", false),
770 (
771 ItemFormat::Info,
772 false,
773 "relative_path/relative_path.info",
774 false,
775 ),
776 (ItemFormat::Info, false, "dat/simple.dat", false),
777 (ItemFormat::Gcno, false, "llvm/file", true),
778 (ItemFormat::Gcno, false, "llvm/file_branch", true),
779 (ItemFormat::Gcno, false, "llvm/reader", true),
780 (
781 ItemFormat::JacocoXml,
782 false,
783 "jacoco/basic-jacoco.xml",
784 false,
785 ),
786 (
787 ItemFormat::JacocoXml,
788 false,
789 "jacoco/inner-classes.xml",
790 false,
791 ),
792 (
793 ItemFormat::JacocoXml,
794 false,
795 "jacoco/multiple-top-level-classes.xml",
796 false,
797 ),
798 (
799 ItemFormat::JacocoXml,
800 false,
801 "jacoco/full-junit4-report-multiple-top-level-classes.xml",
802 false,
803 ),
804 (
805 ItemFormat::JacocoXml,
806 false,
807 "jacoco/kotlin-jacoco-report.xml",
808 false,
809 ),
810 (ItemFormat::Profraw, true, "default_1.profraw", false),
811 (
812 ItemFormat::Gcno,
813 true,
814 "mozillavpn_serverconnection_1.gcno",
815 true,
816 ),
817 (ItemFormat::Gocov, false, "go.out", false),
818 ];
819
820 check_produced(tmp_path, &receiver, expected);
821 assert!(mapping.is_some());
822 let mapping: Value = serde_json::from_slice(&mapping.unwrap()).unwrap();
823 assert_eq!(
824 mapping
825 .get("dist/include/zlib.h")
826 .unwrap()
827 .as_str()
828 .unwrap(),
829 "modules/zlib/src/zlib.h"
830 );
831 }
832
833 #[test]
834 fn test_dir_producer_multiple_directories() {
835 let (sender, receiver) = unbounded();
836
837 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
838 let tmp_path = tmp_dir.path().to_owned();
839 let mapping = producer(
840 &tmp_path,
841 &["test/sub".to_string(), "test/sub2".to_string()],
842 &sender,
843 false,
844 false,
845 );
846
847 let expected = vec![
848 (ItemFormat::Gcno, true, "RootAccessibleWrap_1.gcno", true),
849 (ItemFormat::Gcno, true, "prova2_1.gcno", true),
850 ];
851
852 check_produced(tmp_path, &receiver, expected);
853 assert!(mapping.is_none());
854 }
855
856 #[test]
857 fn test_dir_producer_directory_with_gcno_symlinks() {
858 let (sender, receiver) = unbounded();
859
860 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
861 let tmp_path = tmp_dir.path().to_owned();
862 let mapping = producer(
863 &tmp_path,
864 &["test/gcno_symlink/gcda".to_string()],
865 &sender,
866 false,
867 false,
868 );
869
870 let expected = vec![(ItemFormat::Gcno, true, "main_1.gcno", true)];
871
872 check_produced(tmp_path, &receiver, expected);
873 assert!(mapping.is_none());
874 }
875
876 #[test]
877 fn test_dir_producer_directory_with_no_gcda() {
878 let (sender, receiver) = unbounded();
879
880 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
881 let tmp_path = tmp_dir.path().to_owned();
882 let mapping = producer(
883 &tmp_path,
884 &["test/only_one_gcda".to_string()],
885 &sender,
886 false,
887 false,
888 );
889
890 let expected = vec![
891 (ItemFormat::Gcno, true, "main_1.gcno", true),
892 (ItemFormat::Gcno, true, "orphan_1.gcno", false),
893 ];
894
895 check_produced(tmp_path, &receiver, expected);
896 assert!(mapping.is_none());
897 }
898
899 #[test]
900 fn test_dir_producer_directory_with_no_gcda_ignore_orphan_gcno() {
901 let (sender, receiver) = unbounded();
902
903 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
904 let tmp_path = tmp_dir.path().to_owned();
905 let mapping = producer(
906 &tmp_path,
907 &["test/only_one_gcda".to_string()],
908 &sender,
909 true,
910 false,
911 );
912
913 let expected = vec![(ItemFormat::Gcno, true, "main_1.gcno", true)];
914
915 check_produced(tmp_path, &receiver, expected);
916 assert!(mapping.is_none());
917 }
918
919 #[test]
920 fn test_zip_producer_with_gcda_dir() {
921 let (sender, receiver) = unbounded();
922
923 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
924 let tmp_path = tmp_dir.path().to_owned();
925 let mapping = producer(
926 &tmp_path,
927 &[
928 "test/zip_dir/gcno.zip".to_string(),
929 "test/zip_dir".to_string(),
930 ],
931 &sender,
932 false,
933 false,
934 );
935
936 let expected = vec![
937 (ItemFormat::Gcno, true, "Platform_1.gcno", true),
938 (
939 ItemFormat::Gcno,
940 true,
941 "sub2/RootAccessibleWrap_1.gcno",
942 true,
943 ),
944 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_1.gcno", true),
945 (ItemFormat::Gcno, true, "sub/prova2_1.gcno", true),
946 (
947 ItemFormat::Gcno,
948 true,
949 "nsMaiInterfaceDocument_1.gcno",
950 true,
951 ),
952 (ItemFormat::Gcno, true, "nsGnomeModule_1.gcno", true),
953 ];
954
955 check_produced(tmp_path, &receiver, expected);
956 assert!(mapping.is_some());
957 let mapping: Value = serde_json::from_slice(&mapping.unwrap()).unwrap();
958 assert_eq!(
959 mapping
960 .get("dist/include/zlib.h")
961 .unwrap()
962 .as_str()
963 .unwrap(),
964 "modules/zlib/src/zlib.h"
965 );
966 }
967
968 #[test]
970 fn test_zip_producer_multiple_gcda_archives() {
971 let (sender, receiver) = unbounded();
972
973 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
974 let tmp_path = tmp_dir.path().to_owned();
975 let mapping = producer(
976 &tmp_path,
977 &[
978 "test/gcno.zip".to_string(),
979 "test/gcda1.zip".to_string(),
980 "test/gcda2.zip".to_string(),
981 ],
982 &sender,
983 false,
984 false,
985 );
986
987 let expected = vec![
988 (ItemFormat::Gcno, true, "Platform_1.gcno", true),
989 (
990 ItemFormat::Gcno,
991 true,
992 "sub2/RootAccessibleWrap_1.gcno",
993 true,
994 ),
995 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_1.gcno", true),
996 (ItemFormat::Gcno, true, "sub/prova2_1.gcno", true),
997 (
998 ItemFormat::Gcno,
999 true,
1000 "nsMaiInterfaceDocument_1.gcno",
1001 true,
1002 ),
1003 (ItemFormat::Gcno, true, "nsGnomeModule_1.gcno", true),
1004 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_2.gcno", true),
1005 (
1006 ItemFormat::Gcno,
1007 true,
1008 "nsMaiInterfaceDocument_2.gcno",
1009 true,
1010 ),
1011 (ItemFormat::Gcno, true, "nsGnomeModule_2.gcno", true),
1012 (ItemFormat::Gcno, true, "sub/prova2_2.gcno", true),
1013 ];
1014
1015 check_produced(tmp_path, &receiver, expected);
1016 assert!(mapping.is_some());
1017 let mapping: Value = serde_json::from_slice(&mapping.unwrap()).unwrap();
1018 assert_eq!(
1019 mapping
1020 .get("dist/include/zlib.h")
1021 .unwrap()
1022 .as_str()
1023 .unwrap(),
1024 "modules/zlib/src/zlib.h"
1025 );
1026 }
1027
1028 #[test]
1030 fn test_zip_producer_gcno_with_no_path_mapping() {
1031 let (sender, receiver) = unbounded();
1032
1033 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1034 let tmp_path = tmp_dir.path().to_owned();
1035 let mapping = producer(
1036 &tmp_path,
1037 &[
1038 "test/gcno_no_path_mapping.zip".to_string(),
1039 "test/gcda1.zip".to_string(),
1040 ],
1041 &sender,
1042 false,
1043 false,
1044 );
1045
1046 let expected = vec![
1047 (ItemFormat::Gcno, true, "Platform_1.gcno", true),
1048 (
1049 ItemFormat::Gcno,
1050 true,
1051 "sub2/RootAccessibleWrap_1.gcno",
1052 true,
1053 ),
1054 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_1.gcno", true),
1055 (ItemFormat::Gcno, true, "sub/prova2_1.gcno", true),
1056 (
1057 ItemFormat::Gcno,
1058 true,
1059 "nsMaiInterfaceDocument_1.gcno",
1060 true,
1061 ),
1062 (ItemFormat::Gcno, true, "nsGnomeModule_1.gcno", true),
1063 ];
1064
1065 check_produced(tmp_path, &receiver, expected);
1066 assert!(mapping.is_none());
1067 }
1068
1069 #[test]
1071 fn test_zip_producer_different_order_of_zip_files() {
1072 let (sender, receiver) = unbounded();
1073
1074 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1075 let tmp_path = tmp_dir.path().to_owned();
1076 producer(
1077 &tmp_path,
1078 &[
1079 "test/gcda1.zip".to_string(),
1080 "test/gcno.zip".to_string(),
1081 "test/gcda2.zip".to_string(),
1082 ],
1083 &sender,
1084 false,
1085 false,
1086 );
1087
1088 let expected = vec![
1089 (ItemFormat::Gcno, true, "Platform_1.gcno", true),
1090 (
1091 ItemFormat::Gcno,
1092 true,
1093 "sub2/RootAccessibleWrap_1.gcno",
1094 true,
1095 ),
1096 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_1.gcno", true),
1097 (ItemFormat::Gcno, true, "sub/prova2_1.gcno", true),
1098 (
1099 ItemFormat::Gcno,
1100 true,
1101 "nsMaiInterfaceDocument_1.gcno",
1102 true,
1103 ),
1104 (ItemFormat::Gcno, true, "nsGnomeModule_1.gcno", true),
1105 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_2.gcno", true),
1106 (
1107 ItemFormat::Gcno,
1108 true,
1109 "nsMaiInterfaceDocument_2.gcno",
1110 true,
1111 ),
1112 (ItemFormat::Gcno, true, "nsGnomeModule_2.gcno", true),
1113 (ItemFormat::Gcno, true, "sub/prova2_2.gcno", true),
1114 ];
1115
1116 check_produced(tmp_path, &receiver, expected);
1117 }
1118
1119 #[test]
1121 fn test_zip_producer_profraw_files() {
1122 let (sender, receiver) = unbounded();
1123
1124 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1125 let tmp_path = tmp_dir.path().to_owned();
1126 producer(
1127 &tmp_path,
1128 &[
1129 "test/profraw1.zip".to_string(),
1130 "test/profraw2.zip".to_string(),
1131 ],
1132 &sender,
1133 false,
1134 false,
1135 );
1136
1137 let expected = vec![
1138 (ItemFormat::Profraw, true, "default_1.profraw", false),
1139 (ItemFormat::Profraw, true, "default_2.profraw", false),
1140 ];
1141
1142 check_produced(tmp_path, &receiver, expected);
1143 }
1144
1145 #[test]
1147 fn test_zip_producer_info_files() {
1148 let (sender, receiver) = unbounded();
1149
1150 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1151 let tmp_path = tmp_dir.path().to_owned();
1152 producer(
1153 &tmp_path,
1154 &["test/info1.zip".to_string(), "test/info2.zip".to_string()],
1155 &sender,
1156 false,
1157 false,
1158 );
1159
1160 let expected = vec![
1161 (ItemFormat::Info, false, "1494603967-2977-2_0.info", true),
1162 (ItemFormat::Info, false, "1494603967-2977-3_0.info", true),
1163 (ItemFormat::Info, false, "1494603967-2977-4_0.info", true),
1164 (ItemFormat::Info, false, "1494603968-2977-5_0.info", true),
1165 (ItemFormat::Info, false, "1494603972-2977-6_0.info", true),
1166 (ItemFormat::Info, false, "1494603973-2977-7_0.info", true),
1167 (ItemFormat::Info, false, "1494603967-2977-2_1.info", true),
1168 (ItemFormat::Info, false, "1494603967-2977-3_1.info", true),
1169 (ItemFormat::Info, false, "1494603967-2977-4_1.info", true),
1170 (ItemFormat::Info, false, "1494603968-2977-5_1.info", true),
1171 (ItemFormat::Info, false, "1494603972-2977-6_1.info", true),
1172 (ItemFormat::Info, false, "1494603973-2977-7_1.info", true),
1173 ];
1174
1175 check_produced(tmp_path, &receiver, expected);
1176 }
1177
1178 #[test]
1180 fn test_zip_producer_jacoco_xml_files() {
1181 let (sender, receiver) = unbounded();
1182
1183 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1184 let tmp_path = tmp_dir.path().to_owned();
1185 producer(
1186 &tmp_path,
1187 &[
1188 "test/jacoco1.zip".to_string(),
1189 "test/jacoco2.zip".to_string(),
1190 ],
1191 &sender,
1192 false,
1193 false,
1194 );
1195
1196 let expected = vec![
1197 (
1198 ItemFormat::JacocoXml,
1199 false,
1200 "jacoco/basic-jacoco.xml",
1201 true,
1202 ),
1203 (ItemFormat::JacocoXml, false, "inner-classes.xml", true),
1204 ];
1205
1206 check_produced(tmp_path, &receiver, expected);
1207 }
1208
1209 #[test]
1211 fn test_zip_producer_both_info_and_jacoco_xml() {
1212 let (sender, receiver) = unbounded();
1213
1214 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1215 let tmp_path = tmp_dir.path().to_owned();
1216 producer(
1217 &tmp_path,
1218 &[
1219 "test/jacoco1.zip".to_string(),
1220 "test/jacoco2.zip".to_string(),
1221 "test/info1.zip".to_string(),
1222 "test/info2.zip".to_string(),
1223 ],
1224 &sender,
1225 false,
1226 false,
1227 );
1228
1229 let expected = vec![
1230 (
1231 ItemFormat::JacocoXml,
1232 false,
1233 "jacoco/basic-jacoco.xml",
1234 true,
1235 ),
1236 (ItemFormat::JacocoXml, false, "inner-classes.xml", true),
1237 (ItemFormat::Info, false, "1494603967-2977-2_0.info", true),
1238 (ItemFormat::Info, false, "1494603967-2977-3_0.info", true),
1239 (ItemFormat::Info, false, "1494603967-2977-4_0.info", true),
1240 (ItemFormat::Info, false, "1494603968-2977-5_0.info", true),
1241 (ItemFormat::Info, false, "1494603972-2977-6_0.info", true),
1242 (ItemFormat::Info, false, "1494603973-2977-7_0.info", true),
1243 (ItemFormat::Info, false, "1494603967-2977-2_1.info", true),
1244 (ItemFormat::Info, false, "1494603967-2977-3_1.info", true),
1245 (ItemFormat::Info, false, "1494603967-2977-4_1.info", true),
1246 (ItemFormat::Info, false, "1494603968-2977-5_1.info", true),
1247 (ItemFormat::Info, false, "1494603972-2977-6_1.info", true),
1248 (ItemFormat::Info, false, "1494603973-2977-7_1.info", true),
1249 ];
1250
1251 check_produced(tmp_path, &receiver, expected);
1252 }
1253
1254 #[test]
1256 fn test_zip_producer_both_info_and_gcnogcda_files() {
1257 let (sender, receiver) = unbounded();
1258
1259 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1260 let tmp_path = tmp_dir.path().to_owned();
1261 producer(
1262 &tmp_path,
1263 &[
1264 "test/gcno.zip".to_string(),
1265 "test/gcda1.zip".to_string(),
1266 "test/info1.zip".to_string(),
1267 "test/info2.zip".to_string(),
1268 ],
1269 &sender,
1270 false,
1271 false,
1272 );
1273
1274 let expected = vec![
1275 (ItemFormat::Gcno, true, "Platform_1.gcno", true),
1276 (
1277 ItemFormat::Gcno,
1278 true,
1279 "sub2/RootAccessibleWrap_1.gcno",
1280 true,
1281 ),
1282 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_1.gcno", true),
1283 (ItemFormat::Gcno, true, "sub/prova2_1.gcno", true),
1284 (
1285 ItemFormat::Gcno,
1286 true,
1287 "nsMaiInterfaceDocument_1.gcno",
1288 true,
1289 ),
1290 (ItemFormat::Gcno, true, "nsGnomeModule_1.gcno", true),
1291 (ItemFormat::Info, false, "1494603967-2977-2_0.info", true),
1292 (ItemFormat::Info, false, "1494603967-2977-3_0.info", true),
1293 (ItemFormat::Info, false, "1494603967-2977-4_0.info", true),
1294 (ItemFormat::Info, false, "1494603968-2977-5_0.info", true),
1295 (ItemFormat::Info, false, "1494603972-2977-6_0.info", true),
1296 (ItemFormat::Info, false, "1494603973-2977-7_0.info", true),
1297 (ItemFormat::Info, false, "1494603967-2977-2_1.info", true),
1298 (ItemFormat::Info, false, "1494603967-2977-3_1.info", true),
1299 (ItemFormat::Info, false, "1494603967-2977-4_1.info", true),
1300 (ItemFormat::Info, false, "1494603968-2977-5_1.info", true),
1301 (ItemFormat::Info, false, "1494603972-2977-6_1.info", true),
1302 (ItemFormat::Info, false, "1494603973-2977-7_1.info", true),
1303 ];
1304
1305 check_produced(tmp_path, &receiver, expected);
1306 }
1307
1308 #[test]
1310 fn test_zip_producer_gcno_with_no_associated_gcda() {
1311 let (sender, receiver) = unbounded();
1312
1313 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1314 let tmp_path = tmp_dir.path().to_owned();
1315 let mapping = producer(
1316 &tmp_path,
1317 &[
1318 "test/no_gcda/main.gcno.zip".to_string(),
1319 "test/no_gcda/empty.gcda.zip".to_string(),
1320 ],
1321 &sender,
1322 false,
1323 false,
1324 );
1325
1326 let expected = vec![(ItemFormat::Gcno, true, "main_1.gcno", false)];
1327
1328 check_produced(tmp_path, &receiver, expected);
1329 assert!(mapping.is_none());
1330 }
1331
1332 #[test]
1334 fn test_zip_producer_gcno_with_associated_gcda_in_only_one_archive() {
1335 let (sender, receiver) = unbounded();
1336
1337 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1338 let tmp_path = tmp_dir.path().to_owned();
1339 let mapping = producer(
1340 &tmp_path,
1341 &[
1342 "test/no_gcda/main.gcno.zip".to_string(),
1343 "test/no_gcda/empty.gcda.zip".to_string(),
1344 "test/no_gcda/main.gcda.zip".to_string(),
1345 ],
1346 &sender,
1347 false,
1348 false,
1349 );
1350
1351 let expected = vec![(ItemFormat::Gcno, true, "main_1.gcno", true)];
1352
1353 check_produced(tmp_path, &receiver, expected);
1354 assert!(mapping.is_none());
1355 }
1356
1357 #[test]
1359 #[should_panic]
1360 fn test_zip_producer_with_gcda_archive_and_no_gcno_archive() {
1361 let (sender, _) = unbounded();
1362
1363 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1364 let tmp_path = tmp_dir.path().to_owned();
1365 producer(
1366 &tmp_path,
1367 &["test/no_gcda/main.gcda.zip".to_string()],
1368 &sender,
1369 false,
1370 false,
1371 );
1372 }
1373
1374 #[test]
1376 fn test_zip_producer_no_matching_gcno() {
1377 let (sender, receiver) = unbounded();
1378
1379 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1380 let tmp_path = tmp_dir.path().to_owned();
1381 producer(
1382 &tmp_path,
1383 &["test/gcno.zip".to_string(), "test/gcda2.zip".to_string()],
1384 &sender,
1385 false,
1386 false,
1387 );
1388
1389 let expected = vec![
1390 (ItemFormat::Gcno, true, "Platform_1.gcno", false),
1391 (
1392 ItemFormat::Gcno,
1393 true,
1394 "sub2/RootAccessibleWrap_1.gcno",
1395 false,
1396 ),
1397 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_1.gcno", true),
1398 (ItemFormat::Gcno, true, "sub/prova2_1.gcno", true),
1399 (
1400 ItemFormat::Gcno,
1401 true,
1402 "nsMaiInterfaceDocument_1.gcno",
1403 true,
1404 ),
1405 (ItemFormat::Gcno, true, "nsGnomeModule_1.gcno", true),
1406 ];
1407
1408 check_produced(tmp_path, &receiver, expected);
1409 }
1410
1411 #[test]
1414 fn test_zip_producer_no_matching_gcno_two_gcda_archives() {
1415 let (sender, receiver) = unbounded();
1416
1417 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1418 let tmp_path = tmp_dir.path().to_owned();
1419 producer(
1420 &tmp_path,
1421 &[
1422 "test/gcno.zip".to_string(),
1423 "test/gcda2.zip".to_string(),
1424 "test/gcda2.zip".to_string(),
1425 ],
1426 &sender,
1427 false,
1428 false,
1429 );
1430
1431 let expected = vec![
1432 (ItemFormat::Gcno, true, "Platform_1.gcno", false),
1433 (
1434 ItemFormat::Gcno,
1435 true,
1436 "sub2/RootAccessibleWrap_1.gcno",
1437 false,
1438 ),
1439 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_1.gcno", true),
1440 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_2.gcno", true),
1441 (ItemFormat::Gcno, true, "sub/prova2_1.gcno", true),
1442 (ItemFormat::Gcno, true, "sub/prova2_2.gcno", true),
1443 (
1444 ItemFormat::Gcno,
1445 true,
1446 "nsMaiInterfaceDocument_1.gcno",
1447 true,
1448 ),
1449 (
1450 ItemFormat::Gcno,
1451 true,
1452 "nsMaiInterfaceDocument_2.gcno",
1453 true,
1454 ),
1455 (ItemFormat::Gcno, true, "nsGnomeModule_1.gcno", true),
1456 (ItemFormat::Gcno, true, "nsGnomeModule_2.gcno", true),
1457 ];
1458
1459 check_produced(tmp_path, &receiver, expected);
1460 }
1461
1462 #[test]
1464 fn test_zip_producer_no_matching_gcno_ignore_orphan_gcno() {
1465 let (sender, receiver) = unbounded();
1466
1467 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1468 let tmp_path = tmp_dir.path().to_owned();
1469 producer(
1470 &tmp_path,
1471 &["test/gcno.zip".to_string(), "test/gcda2.zip".to_string()],
1472 &sender,
1473 true,
1474 false,
1475 );
1476
1477 let expected = vec![
1478 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_1.gcno", true),
1479 (ItemFormat::Gcno, true, "sub/prova2_1.gcno", true),
1480 (
1481 ItemFormat::Gcno,
1482 true,
1483 "nsMaiInterfaceDocument_1.gcno",
1484 true,
1485 ),
1486 (ItemFormat::Gcno, true, "nsGnomeModule_1.gcno", true),
1487 ];
1488
1489 check_produced(tmp_path, &receiver, expected);
1490 }
1491
1492 #[test]
1494 fn test_zip_producer_no_matching_gcno_two_gcda_archives_ignore_orphan_gcno() {
1495 let (sender, receiver) = unbounded();
1496
1497 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1498 let tmp_path = tmp_dir.path().to_owned();
1499 producer(
1500 &tmp_path,
1501 &[
1502 "test/gcno.zip".to_string(),
1503 "test/gcda2.zip".to_string(),
1504 "test/gcda2.zip".to_string(),
1505 ],
1506 &sender,
1507 true,
1508 false,
1509 );
1510
1511 let expected = vec![
1512 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_1.gcno", true),
1513 (ItemFormat::Gcno, true, "nsMaiInterfaceValue_2.gcno", true),
1514 (ItemFormat::Gcno, true, "sub/prova2_1.gcno", true),
1515 (ItemFormat::Gcno, true, "sub/prova2_2.gcno", true),
1516 (
1517 ItemFormat::Gcno,
1518 true,
1519 "nsMaiInterfaceDocument_1.gcno",
1520 true,
1521 ),
1522 (
1523 ItemFormat::Gcno,
1524 true,
1525 "nsMaiInterfaceDocument_2.gcno",
1526 true,
1527 ),
1528 (ItemFormat::Gcno, true, "nsGnomeModule_1.gcno", true),
1529 (ItemFormat::Gcno, true, "nsGnomeModule_2.gcno", true),
1530 ];
1531
1532 check_produced(tmp_path, &receiver, expected);
1533 }
1534
1535 #[test]
1536 fn test_zip_producer_llvm_buffers() {
1537 let (sender, receiver) = unbounded();
1538
1539 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1540 let tmp_path = tmp_dir.path().to_owned();
1541 producer(
1542 &tmp_path,
1543 &[
1544 "test/llvm/gcno.zip".to_string(),
1545 "test/llvm/gcda1.zip".to_string(),
1546 "test/llvm/gcda2.zip".to_string(),
1547 ],
1548 &sender,
1549 true,
1550 true,
1551 );
1552 let gcno_buf: Vec<u8> = vec![
1553 111, 110, 99, 103, 42, 50, 48, 52, 74, 200, 254, 66, 0, 0, 0, 1, 9, 0, 0, 0, 0, 0, 0,
1554 0, 236, 217, 93, 255, 2, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 2, 0, 0, 0, 102, 105,
1555 108, 101, 46, 99, 0, 0, 1, 0, 0, 0, 0, 0, 65, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1556 0, 0, 0, 0, 0, 67, 1, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 1, 3,
1557 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1558 0, 0, 0, 0, 0, 0, 0, 0, 69, 1, 8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 102,
1559 105, 108, 101, 46, 99, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1560 0,
1561 ];
1562 let gcda1_buf: Vec<u8> = vec![
1563 97, 100, 99, 103, 42, 50, 48, 52, 74, 200, 254, 66, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 0, 0,
1564 236, 217, 93, 255, 2, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 0, 0, 161, 1, 4, 0, 0, 0,
1565 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 9, 0, 0, 0, 0, 0, 0, 0,
1566 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1567 0, 0, 0, 0, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1568 ];
1569 let gcda2_buf: Vec<u8> = vec![
1570 97, 100, 99, 103, 42, 50, 48, 52, 74, 200, 254, 66, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 0, 0,
1571 236, 217, 93, 255, 2, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 0, 0, 161, 1, 4, 0, 0, 0,
1572 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 9, 0, 0, 0, 0, 0, 0, 0,
1573 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1574 0, 0, 0, 0, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1575 ];
1576
1577 while let Ok(elem) = receiver.try_recv() {
1578 let elem = elem.unwrap();
1579 if let ItemType::Buffers(buffers) = elem.item {
1580 let stem = PathBuf::from(buffers.stem);
1581 let stem = stem.file_stem().expect("Unable to get file_stem");
1582
1583 assert!(stem == "file", "Unexpected file: {:?}", stem);
1584 assert_eq!(buffers.gcno_buf, gcno_buf);
1585 assert_eq!(buffers.gcda_buf, vec![gcda1_buf.clone(), gcda2_buf.clone()]);
1586 } else {
1587 panic!("Buffers expected");
1588 }
1589 }
1590 }
1591
1592 #[test]
1593 fn test_plain_producer() {
1594 let (sender, receiver) = unbounded();
1595
1596 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1597 let tmp_path = tmp_dir.path().to_owned();
1598 let json_path = "test/linked-files-map.json";
1599 let mapping = producer(
1600 &tmp_path,
1601 &["test/prova.info".to_string(), json_path.to_string()],
1602 &sender,
1603 true,
1604 false,
1605 );
1606
1607 assert!(mapping.is_some());
1608 let mapping = mapping.unwrap();
1609
1610 let expected = vec![(ItemFormat::Info, false, "prova_1.info", true)];
1611
1612 if let Ok(mut reader) = File::open(json_path) {
1613 let mut json = Vec::new();
1614 reader.read_to_end(&mut json).unwrap();
1615 assert_eq!(json, mapping);
1616 } else {
1617 panic!("Failed to read the file: {}", json_path);
1618 }
1619
1620 check_produced(tmp_path, &receiver, expected);
1621 }
1622
1623 #[test]
1624 fn test_plain_profraw_producer() {
1625 let (sender, receiver) = unbounded();
1626
1627 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1628 let tmp_path = tmp_dir.path().to_owned();
1629 producer(
1630 &tmp_path,
1631 &["test/default.profraw".to_string()],
1632 &sender,
1633 true,
1634 false,
1635 );
1636
1637 let expected = vec![(ItemFormat::Profraw, true, "default.profraw", false)];
1638
1639 check_produced(PathBuf::from("test"), &receiver, expected);
1640 }
1641
1642 #[test]
1643 #[should_panic]
1644 fn test_plain_producer_with_gcno() {
1645 let (sender, _) = unbounded();
1646
1647 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1648 let tmp_path = tmp_dir.path().to_owned();
1649 producer(
1650 &tmp_path,
1651 &["sub2/RootAccessibleWrap_1.gcno".to_string()],
1652 &sender,
1653 true,
1654 false,
1655 );
1656 }
1657
1658 #[test]
1659 #[should_panic]
1660 fn test_plain_producer_with_gcda() {
1661 let (sender, _) = unbounded();
1662
1663 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1664 let tmp_path = tmp_dir.path().to_owned();
1665 producer(
1666 &tmp_path,
1667 &["./test/llvm/file.gcda".to_string()],
1668 &sender,
1669 true,
1670 false,
1671 );
1672 }
1673
1674 #[test]
1675 fn test_plain_producer_with_dat_file() {
1676 let (sender, receiver) = unbounded();
1677
1678 let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
1679 let tmp_path = tmp_dir.path().to_owned();
1680 producer(
1681 &tmp_path,
1682 &["test/dat/simple.dat".to_string()],
1683 &sender,
1684 false,
1685 false,
1686 );
1687
1688 let expected = vec![(ItemFormat::Info, false, "simple.dat", true)];
1689
1690 check_produced(tmp_path, &receiver, expected);
1691 }
1692
1693 #[test]
1694 fn test_jacoco_files() {
1695 let mut file = File::open("./test/jacoco/basic-report.xml").ok();
1696 assert!(
1697 Archive::check_file(file.as_mut(), &Archive::is_jacoco),
1698 "A Jacoco XML file expected"
1699 );
1700 let mut file =
1701 File::open("./test/jacoco/full-junit4-report-multiple-top-level-classes.xml").ok();
1702 assert!(
1703 Archive::check_file(file.as_mut(), &Archive::is_jacoco),
1704 "A Jacoco XML file expected"
1705 );
1706 let mut file = File::open("./test/jacoco/inner-classes.xml").ok();
1707 assert!(
1708 Archive::check_file(file.as_mut(), &Archive::is_jacoco),
1709 "A Jacoco XML file expected"
1710 );
1711 let mut file = File::open("./test/jacoco/multiple-top-level-classes.xml").ok();
1712 assert!(
1713 Archive::check_file(file.as_mut(), &Archive::is_jacoco),
1714 "A Jacoco XML file expected"
1715 );
1716 let mut file = File::open("./test/jacoco/not_jacoco_file.xml").ok();
1717 assert!(
1718 !Archive::check_file(file.as_mut(), &Archive::is_jacoco),
1719 "Not a Jacoco XML file expected"
1720 );
1721 }
1722
1723 #[test]
1724 fn test_info_files() {
1725 let mut file = File::open("./test/1494603973-2977-7.info").ok();
1726 assert!(
1727 Archive::check_file(file.as_mut(), &Archive::is_info),
1728 "An info file expected"
1729 );
1730 let mut file = File::open("./test/empty_line.info").ok();
1731 assert!(
1732 Archive::check_file(file.as_mut(), &Archive::is_info),
1733 "An info file expected"
1734 );
1735 let mut file = File::open("./test/relative_path/relative_path.info").ok();
1736 assert!(
1737 Archive::check_file(file.as_mut(), &Archive::is_info),
1738 "An info file expected"
1739 );
1740 let mut file = File::open("./test/not_info_file.info").ok();
1741 assert!(
1742 !Archive::check_file(file.as_mut(), &Archive::is_info),
1743 "Not an info file expected"
1744 );
1745 }
1746}