diff options
author | Hubert Figuière <hub@figuiere.net> | 2023-08-19 23:01:17 -0400 |
---|---|---|
committer | Hubert Figuière <hub@figuiere.net> | 2023-08-19 23:03:56 -0400 |
commit | c21be9f3a7c03a681198cbb01a6be40d8fb631ac (patch) | |
tree | 0bfde69d1e719d69bd4d369117c4b9d7e063e3fd | |
parent | b338eb81f6dcfc4ed82bda45eb3fa3e39cc6e09b (diff) |
testsuite: extract testsuite infrastructure
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | libopenraw-testing/Cargo.toml | 12 | ||||
-rw-r--r-- | libopenraw-testing/src/lib.rs | 206 | ||||
-rw-r--r-- | src/lib.rs | 44 | ||||
-rw-r--r-- | tests/testsuite.rs | 443 |
5 files changed, 446 insertions, 260 deletions
@@ -36,6 +36,7 @@ crc = "2.1.0" serde = { version = "^1, <=1.0.171", features = [ "derive" ] } serde-xml-rs = "^0.5.1" criterion = { version = "0.5.1", features = ["html_reports"] } +libopenraw-testing = { path = "libopenraw-testing" } image = { version = "0.24.6", features = [ "png", "jpeg" ], default-features = false } [features] diff --git a/libopenraw-testing/Cargo.toml b/libopenraw-testing/Cargo.toml new file mode 100644 index 0000000..d3f0476 --- /dev/null +++ b/libopenraw-testing/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "libopenraw-testing" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +crc = "2.1.0" +libopenraw = { path = ".." } +serde = { version = "^1, <=1.0.171", features = [ "derive" ] } +serde-xml-rs = "^0.5.1" diff --git a/libopenraw-testing/src/lib.rs b/libopenraw-testing/src/lib.rs new file mode 100644 index 0000000..c87a45b --- /dev/null +++ b/libopenraw-testing/src/lib.rs @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + * libopenraw - libopenraw-testing.rs + * + * Copyright (C) 2022-2023 Hubert Figuière + * + * This library is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +use std::fmt::Display; +use std::str::FromStr; + +use serde::{de::Error, Deserialize}; + +use libopenraw::{metadata::Value, Bitmap, DataType, Ifd, RawFile, Rect}; + +/// CRC checksum for the RAW data (8 bits only) +pub fn raw_checksum(buf: &[u8]) -> u16 { + // This is the same algorithm as used in the C++ implementation + let crc = crc::Crc::<u16>::new(&crc::CRC_16_IBM_3740); + let mut digest = crc.digest(); + digest.update(buf); + + digest.finalize() +} + +#[derive(Clone, Debug, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct Results { + pub raw_type: Option<String>, + pub raw_type_id: Option<u32>, + /// MakeNoteCount can be -1 for an error (expected) + pub maker_note_count: Option<i32>, + pub maker_note_id: Option<String>, + pub exif_make: Option<String>, + pub exif_model: Option<String>, + pub thumb_num: Option<u32>, + #[serde(deserialize_with = "from_list")] + #[serde(default)] + pub thumb_sizes: Option<Vec<u32>>, + pub thumb_formats: Option<String>, // XXX split array + #[serde(deserialize_with = "from_list")] + #[serde(default)] + pub thumb_data_sizes: Option<Vec<u32>>, + #[serde(deserialize_with = "from_list")] + #[serde(default)] + pub thumb_md5: Option<Vec<u16>>, + pub raw_data_type: Option<String>, + pub raw_data_size: Option<u32>, + #[serde(deserialize_with = "from_list")] + #[serde(default)] + pub raw_data_dimensions: Option<Vec<u32>>, + #[serde(deserialize_with = "from_list")] + #[serde(default)] + pub raw_data_active_area: Option<Vec<u32>>, + pub raw_cfa_pattern: Option<String>, + #[serde(deserialize_with = "from_list")] + #[serde(default)] + pub raw_min_value: Option<Vec<u16>>, + #[serde(deserialize_with = "from_list")] + #[serde(default)] + pub raw_max_value: Option<Vec<u16>>, + pub raw_md5: Option<u16>, + pub meta_orientation: Option<u32>, +} + +/// Deserialize a space separated list on numbers to a vector. +fn from_list<'de, D, T>(deserializer: D) -> Result<Option<Vec<T>>, D::Error> +where + D: serde::Deserializer<'de>, + T: FromStr, + <T as FromStr>::Err: Display, +{ + let s: String = Deserialize::deserialize(deserializer)?; + let v: Vec<&str> = s.split(' ').collect(); + let mut ints = vec![]; + for num in v { + let n = num.parse::<T>().map_err(D::Error::custom)?; + ints.push(n); + } + Ok(Some(ints)) +} + +#[derive(Clone, Debug, Deserialize, PartialEq)] +#[serde(rename = "test")] +pub struct Test { + pub name: String, + pub file: String, + pub source: Option<String>, + pub results: Results, +} + +fn make_results(rawfile: &dyn RawFile) -> Results { + let raw_type = Some(rawfile.type_().into()); + let raw_type_id = Some(rawfile.type_id().into()); + let exif_make = rawfile + .metadata_value(&"Exif.Image.Make".to_string()) + .as_ref() + .and_then(Value::string); + let exif_model = rawfile + .metadata_value(&"Exif.Image.Model".to_string()) + .as_ref() + .and_then(Value::string); + let meta_orientation = Some(rawfile.orientation()); + + let maker_note = rawfile.maker_note_ifd(); + let maker_note_count = maker_note + .map(|mnote| mnote.num_entries() as i32) + .or(Some(-1)); + let maker_note_id = maker_note.and_then(|mnote| String::from_utf8(mnote.id().to_vec()).ok()); + + let thumbnail_sizes = rawfile.thumbnail_sizes(); + let thumb_num = Some(thumbnail_sizes.len() as u32); + let thumb_sizes = Some(thumbnail_sizes.to_vec()); + + let thumbnails = rawfile.thumbnails(); + let thumb_formats = Some( + thumbnails + .thumbnails + .iter() + .map(|t| t.1.data_type.into()) + .collect::<Vec<String>>() + .join(" "), + ); + let thumb_data_sizes = Some( + thumbnails + .thumbnails + .iter() + .map(|t| t.1.data_size() as u32) + .collect(), + ); + let thumb_md5 = Some( + thumbnails + .thumbnails + .iter() + .flat_map(|t| { + rawfile + .thumbnail(t.0) + .ok() + .and_then(|t| t.data8().map(raw_checksum)) + }) + .collect(), + ); + + let rawdata = rawfile.raw_data(false); + let rawdata = rawdata.as_ref(); + let raw_data_type = rawdata.map(|rawdata| rawdata.data_type().into()).ok(); + let raw_data_size = rawdata.map(|rawdata| rawdata.data_size() as u32).ok(); + let raw_data_dimensions = rawdata + .map(|rawdata| vec![rawdata.width(), rawdata.height()]) + .ok(); + let raw_data_active_area = rawdata + .ok() + .and_then(|rawdata| rawdata.active_area()) + .map(Rect::to_vec); + let raw_cfa_pattern = rawdata + .map(|rawdata| rawdata.mosaic_pattern().to_string()) + .ok(); + let raw_min_value = rawdata.map(|rawdata| rawdata.blacks().to_vec()).ok(); + let raw_max_value = rawdata.map(|rawdata| rawdata.whites().to_vec()).ok(); + let raw_md5 = rawdata + .ok() + .and_then(|rawdata| { + if rawdata.data_type() == DataType::CompressedRaw { + rawdata.data8() + } else { + rawdata.data16_as_u8() + } + }) + .map(raw_checksum); + + Results { + raw_type, + raw_type_id, + exif_make, + exif_model, + meta_orientation, + maker_note_count, + maker_note_id, + thumb_num, + thumb_sizes, + thumb_formats, + thumb_data_sizes, + thumb_md5, + raw_data_type, + raw_data_size, + raw_data_dimensions, + raw_data_active_area, + raw_cfa_pattern, + raw_min_value, + raw_max_value, + raw_md5, + } +} @@ -171,6 +171,20 @@ impl From<&str> for DataType { } } +impl From<DataType> for String { + fn from(t: DataType) -> String { + match t { + DataType::Jpeg => "JPEG", + DataType::Raw => "RAW", + DataType::PixmapRgb8 => "8RGB", + DataType::PixmapRgb16 => "16RGB", + DataType::CompressedRaw => "COMP_RAW", + DataType::Unknown => "UNKNOWN", + } + .to_string() + } +} + /// RAW file type. This list the type of files, which /// coincidentally match the vendor, except for DNG. /// @@ -227,6 +241,7 @@ impl From<&str> for Type { "CRW" => Self::Crw, "DNG" => Self::Dng, "ERF" => Self::Erf, + "GPR" => Self::Gpr, "MRW" => Self::Mrw, "NEF" => Self::Nef, "NRW" => Self::Nrw, @@ -234,11 +249,40 @@ impl From<&str> for Type { "PEF" => Self::Pef, "RAF" => Self::Raf, "RW2" => Self::Rw2, + "SR2" => Self::Sr2, _ => Self::Unknown, } } } +impl From<Type> for String { + fn from(t: Type) -> String { + match t { + Type::Arw => "ARW", + Type::Cr2 => "CR2", + Type::Cr3 => "CR3", + Type::Crw => "CRW", + Type::Dng => "DNG", + Type::Erf => "ERF", + Type::Gpr => "GPR", + Type::Jpeg => "JPEG", + Type::Mrw => "MRW", + Type::Nef => "NEF", + Type::Nrw => "NRW", + Type::Orf => "ORF", + Type::Pef => "PEF", + Type::Raf => "RAF", + Type::Rw2 => "RW2", + Type::Sr2 => "SR2", + #[cfg(test)] + Type::Test => "TEST", + Type::Tiff => "TIF", + Type::Unknown => "UNKNOWN", + } + .to_string() + } +} + /// Type ID (vendor, model) #[derive(Clone, Copy, Debug, PartialEq)] pub struct TypeId(u16, u16); diff --git a/tests/testsuite.rs b/tests/testsuite.rs index 2e954f3..03fae7f 100644 --- a/tests/testsuite.rs +++ b/tests/testsuite.rs @@ -19,279 +19,215 @@ * <http://www.gnu.org/licenses/>. */ -use std::fmt::Display; use std::path::Path; -use std::str::FromStr; -use serde::{de::Error, Deserialize}; +use serde::Deserialize; use serde_xml_rs::{de::Deserializer, from_reader}; use libopenraw::metadata::Value; use libopenraw::{Bitmap, DataType, Ifd, RawFile, Type, TypeId}; +use libopenraw_testing::{raw_checksum, Results, Test}; -#[derive(Clone, Debug, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -struct Results { - raw_type: Option<String>, - raw_type_id: Option<u32>, - /// MakeNoteCount can be -1 for an error (expected) - maker_note_count: Option<i32>, - maker_note_id: Option<String>, - exif_make: Option<String>, - exif_model: Option<String>, - thumb_num: Option<u32>, - #[serde(deserialize_with = "from_list")] - #[serde(default)] - thumb_sizes: Option<Vec<u32>>, - thumb_formats: Option<String>, // XXX split array - #[serde(deserialize_with = "from_list")] - #[serde(default)] - thumb_data_sizes: Option<Vec<u32>>, - #[serde(deserialize_with = "from_list")] - #[serde(default)] - thumb_md5: Option<Vec<u16>>, - raw_data_type: Option<String>, - raw_data_size: Option<u32>, - #[serde(deserialize_with = "from_list")] - #[serde(default)] - raw_data_dimensions: Option<Vec<u32>>, - #[serde(deserialize_with = "from_list")] - #[serde(default)] - raw_data_active_area: Option<Vec<u32>>, - raw_cfa_pattern: Option<String>, - #[serde(deserialize_with = "from_list")] - #[serde(default)] - raw_min_value: Option<Vec<u16>>, - #[serde(deserialize_with = "from_list")] - #[serde(default)] - raw_max_value: Option<Vec<u16>>, - raw_md5: Option<u32>, - meta_orientation: Option<u32>, +trait TestRun { + fn run(&self, rawfile: &dyn RawFile, filename: &str) -> u32; } -/// Deserialize a space separated list on numbers to a vector. -fn from_list<'de, D, T>(deserializer: D) -> Result<Option<Vec<T>>, D::Error> -where - D: serde::Deserializer<'de>, - T: FromStr, - <T as FromStr>::Err: Display, -{ - let s: String = Deserialize::deserialize(deserializer)?; - let v: Vec<&str> = s.split(' ').collect(); - let mut ints = vec![]; - for num in v { - let n = num.parse::<T>().map_err(D::Error::custom)?; - ints.push(n); - } - Ok(Some(ints)) -} +/// Test for RAW +fn raw_test(results: &Results, rawfile: &dyn RawFile) -> u32 { + let mut count = 0; -impl Results { - /// CRC checksum for the RAW data (8 bits only) - fn raw_checksum(buf: &[u8]) -> u16 { - // This is the same algorithm as used in the C++ implementation - let crc = crc::Crc::<u16>::new(&crc::CRC_16_IBM_3740); - let mut digest = crc.digest(); - digest.update(buf); + let rawdata = rawfile.raw_data(false); + assert_eq!( + rawdata.is_ok(), + results.raw_data_type.is_some(), + "Expected Raw data wasn't found" + ); - digest.finalize() + // no raw data, bail out + if rawdata.is_err() { + return count; } - /// Test for RAW - fn raw_test(&self, rawfile: &dyn RawFile) -> u32 { - let mut count = 0; + let rawdata = rawdata.unwrap(); - let rawdata = rawfile.raw_data(false); + // RAW data type + if let Some(ref raw_data_type) = results.raw_data_type { + count += 1; assert_eq!( - rawdata.is_ok(), - self.raw_data_type.is_some(), - "Expected Raw data wasn't found" + DataType::from(raw_data_type.as_str()), + rawdata.data_type(), + "Incorrect type for Raw data" ); + } + // RAW data size + if let Some(raw_data_size) = results.raw_data_size { + count += 1; + assert_eq!( + raw_data_size as usize, + rawdata.data_size(), + "Incorrect Raw data size" + ); + } - // no raw data, bail out - if rawdata.is_err() { - return count; - } - - let rawdata = rawdata.unwrap(); - - // RAW data type - if let Some(ref raw_data_type) = self.raw_data_type { - count += 1; - assert_eq!( - DataType::from(raw_data_type.as_str()), - rawdata.data_type(), - "Incorrect type for Raw data" - ); - } - // RAW data size - if let Some(raw_data_size) = self.raw_data_size { - count += 1; - assert_eq!( - raw_data_size as usize, - rawdata.data_size(), - "Incorrect Raw data size" - ); - } - - // RAW dimensions - if let Some(ref dims) = self.raw_data_dimensions { - count += 1; - assert_eq!(dims.len(), 2, "Incorrect number of Raw dimensions"); - assert_eq!( - dims, - &[rawdata.width(), rawdata.height()], - "Incorrect dimensions" - ); - } + // RAW dimensions + if let Some(ref dims) = results.raw_data_dimensions { + count += 1; + assert_eq!(dims.len(), 2, "Incorrect number of Raw dimensions"); + assert_eq!( + dims, + &[rawdata.width(), rawdata.height()], + "Incorrect dimensions" + ); + } - // RAW active area - if let Some(ref raw_data_active_area) = self.raw_data_active_area { - count += 1; - assert_eq!(raw_data_active_area.len(), 4, "Incorrect active area"); - let active_area = rawdata.active_area(); - assert!(active_area.is_some(), "No active area found"); - let active_area = active_area.unwrap(); - assert_eq!( - raw_data_active_area, - &active_area.to_vec(), - "Incorrect active area" - ); - } + // RAW active area + if let Some(ref raw_data_active_area) = results.raw_data_active_area { + count += 1; + assert_eq!(raw_data_active_area.len(), 4, "Incorrect active area"); + let active_area = rawdata.active_area(); + assert!(active_area.is_some(), "No active area found"); + let active_area = active_area.unwrap(); + assert_eq!( + raw_data_active_area, + &active_area.to_vec(), + "Incorrect active area" + ); + } - // CFA pattern - if let Some(ref raw_cfa_pattern) = self.raw_cfa_pattern { - count += 1; - assert_eq!( - &rawdata.mosaic_pattern().to_string(), - raw_cfa_pattern, - "Incorrect CFA pattern" - ); - } + // CFA pattern + if let Some(ref raw_cfa_pattern) = results.raw_cfa_pattern { + count += 1; + assert_eq!( + &rawdata.mosaic_pattern().to_string(), + raw_cfa_pattern, + "Incorrect CFA pattern" + ); + } - // RAW black and white - if let Some(ref raw_min_value) = self.raw_min_value { - count += 1; - assert_eq!( - &rawdata.blacks().to_vec(), - raw_min_value, - "Incorrect black point" - ); - } - if let Some(ref raw_max_value) = self.raw_max_value { - count += 1; - assert_eq!( - &rawdata.whites().to_vec(), - raw_max_value, - "Incorrect white point" - ); - } + // RAW black and white + if let Some(ref raw_min_value) = results.raw_min_value { + count += 1; + assert_eq!( + &rawdata.blacks().to_vec(), + raw_min_value, + "Incorrect black point" + ); + } + if let Some(ref raw_max_value) = results.raw_max_value { + count += 1; + assert_eq!( + &rawdata.whites().to_vec(), + raw_max_value, + "Incorrect white point" + ); + } - // RAW data checksum. It's not even a md5. - if let Some(raw_md5) = self.raw_md5 { - count += 1; - let buf = if rawdata.data_type() == DataType::CompressedRaw { - let buf = rawdata.data8(); - assert!(buf.is_some(), "Compressed Raw data not found"); - buf.unwrap() - } else { - let buf = rawdata.data16_as_u8(); - assert!(buf.is_some(), "16-bits Raw data not found"); - buf.unwrap() - }; - let r = Self::raw_checksum(buf); - assert_eq!(raw_md5, r as u32, "Incorrect Raw data checksum"); - } - count + // RAW data checksum. It's not even a md5. + if let Some(raw_md5) = results.raw_md5 { + count += 1; + let buf = if rawdata.data_type() == DataType::CompressedRaw { + let buf = rawdata.data8(); + assert!(buf.is_some(), "Compressed Raw data not found"); + buf.unwrap() + } else { + let buf = rawdata.data16_as_u8(); + assert!(buf.is_some(), "16-bits Raw data not found"); + buf.unwrap() + }; + let r = raw_checksum(buf); + assert_eq!(raw_md5, r, "Incorrect Raw data checksum"); } + count +} - fn thumbnail_test(&self, rawfile: &dyn RawFile) -> u32 { - let mut count = 0; - // Check the number of thumbnails - if let Some(thumb_num) = self.thumb_num { - count += 1; - let thumbnail_sizes = rawfile.thumbnail_sizes(); - assert_eq!( - thumb_num as usize, - thumbnail_sizes.len(), - "Different number of thumbnails" - ); - } +fn thumbnail_test(results: &Results, rawfile: &dyn RawFile) -> u32 { + let mut count = 0; + // Check the number of thumbnails + if let Some(thumb_num) = results.thumb_num { + count += 1; + let thumbnail_sizes = rawfile.thumbnail_sizes(); + assert_eq!( + thumb_num as usize, + thumbnail_sizes.len(), + "Different number of thumbnails" + ); + } - if let Some(ref sizes) = self.thumb_sizes { - count += 1; - let thumbnail_sizes = rawfile.thumbnail_sizes(); + if let Some(ref sizes) = results.thumb_sizes { + count += 1; + let thumbnail_sizes = rawfile.thumbnail_sizes(); + assert_eq!( + thumbnail_sizes.len(), + sizes.len(), + "Mismatch number of thumbnails" + ); + for (index, size) in sizes.iter().enumerate() { assert_eq!( - thumbnail_sizes.len(), - sizes.len(), - "Mismatch number of thumbnails" + size, &thumbnail_sizes[index], + "Incorrect size for thumbnail {index}" ); - for (index, size) in sizes.iter().enumerate() { - assert_eq!( - size, &thumbnail_sizes[index], - "Incorrect size for thumbnail {index}" - ); - } } + } - if let Some(ref thumb_formats) = self.thumb_formats { - count += 1; - let thumbnails = rawfile.thumbnails(); - let formats: Vec<DataType> = thumb_formats.split(' ').map(DataType::from).collect(); + if let Some(ref thumb_formats) = results.thumb_formats { + count += 1; + let thumbnails = rawfile.thumbnails(); + let formats: Vec<DataType> = thumb_formats.split(' ').map(DataType::from).collect(); + assert_eq!( + thumbnails.sizes.len(), + formats.len(), + "Mismatch number of thumbnail format" + ); + for (index, thumbnail) in thumbnails.thumbnails.iter().enumerate() { assert_eq!( - thumbnails.sizes.len(), - formats.len(), - "Mismatch number of thumbnail format" + thumbnail.1.data_type, formats[index], + "Incorrect data type for thumbnail {index}" ); - for (index, thumbnail) in thumbnails.thumbnails.iter().enumerate() { - assert_eq!( - thumbnail.1.data_type, formats[index], - "Incorrect data type for thumbnail {index}" - ); - } } + } - if let Some(ref data_sizes) = self.thumb_data_sizes { - count += 1; - let thumbnails = rawfile.thumbnails(); + if let Some(ref data_sizes) = results.thumb_data_sizes { + count += 1; + let thumbnails = rawfile.thumbnails(); + assert_eq!( + thumbnails.thumbnails.len(), + data_sizes.len(), + "Mismatch number of thumbnail data sizes" + ); + for (index, thumbnail) in thumbnails.thumbnails.iter().enumerate() { assert_eq!( - thumbnails.thumbnails.len(), - data_sizes.len(), - "Mismatch number of thumbnail data sizes" + thumbnail.1.data_size(), + data_sizes[index] as u64, + "Incorrect data size for thumbnail {index}" ); - for (index, thumbnail) in thumbnails.thumbnails.iter().enumerate() { - assert_eq!( - thumbnail.1.data_size(), - data_sizes[index] as u64, - "Incorrect data size for thumbnail {index}" - ); - } } + } - if let Some(ref md5s) = self.thumb_md5 { - count += 1; - let thumbnails = rawfile.thumbnails(); - assert_eq!( - thumbnails.thumbnails.len(), - md5s.len(), - "Mismatch number of thumbnail checksum" - ); - for (index, thumbnail_desc) in thumbnails.thumbnails.iter().enumerate() { - let thumbnail = rawfile - .thumbnail(thumbnail_desc.0) - .expect("Thumbnail not found"); - let buf = thumbnail.data8().unwrap(); - let r = Self::raw_checksum(buf); - assert_eq!(r, md5s[index], "Incorrect checksum for thumbnail {index}"); - } + if let Some(ref md5s) = results.thumb_md5 { + count += 1; + let thumbnails = rawfile.thumbnails(); + assert_eq!( + thumbnails.thumbnails.len(), + md5s.len(), + "Mismatch number of thumbnail checksum" + ); + for (index, thumbnail_desc) in thumbnails.thumbnails.iter().enumerate() { + let thumbnail = rawfile + .thumbnail(thumbnail_desc.0) + .expect("Thumbnail not found"); + let buf = thumbnail.data8().unwrap(); + let r = raw_checksum(buf); + assert_eq!(r, md5s[index], "Incorrect checksum for thumbnail {index}"); } + } - // XXX todo + // XXX todo - count - } + count +} +impl TestRun for Results { fn run(&self, rawfile: &dyn RawFile, filename: &str) -> u32 { let mut count = 0; // Check RAW type @@ -379,35 +315,12 @@ impl Results { ); } - count += self.thumbnail_test(rawfile); - count += self.raw_test(rawfile); + count += thumbnail_test(self, rawfile); + count += raw_test(self, rawfile); count } } -#[derive(Clone, Debug, Deserialize, PartialEq)] -#[serde(rename = "test")] -struct Test { - name: String, - file: String, - source: Option<String>, - results: Results, -} - -impl Test { - fn run(&self) { - let rawfile = libopenraw::rawfile_from_file(self.file.clone(), None); - match rawfile { - Ok(rawfile) => { - print!("Test '{}'", &self.name); - let count = self.results.run(rawfile.as_ref(), &self.file); - println!(" produced {count} results"); - } - Err(err) => println!("Test '{}' skipped ({}): {}", &self.name, self.file, err), - } - } -} - #[derive(Debug, Deserialize, PartialEq)] #[serde(rename = "testsuite")] struct TestSuite { @@ -434,12 +347,22 @@ struct Overrides { } impl TestSuite { - fn run(&mut self) { - for test in &self.tests { - test.run(); + fn run_test(test: &Test) { + let rawfile = libopenraw::rawfile_from_file(test.file.clone(), None); + match rawfile { + Ok(rawfile) => { + print!("Test '{}'", &test.name); + let count = test.results.run(rawfile.as_ref(), &test.file); + println!(" produced {count} results"); + } + Err(err) => println!("Test '{}' skipped ({}): {}", &test.name, test.file, err), } } + fn run(&mut self) { + self.tests.iter().for_each(TestSuite::run_test); + } + /// Load the overrides to, notably, change the local path of the files. fn load_overrides<P>(&mut self, path: P) where |