diff options
author | Hubert Figuière <hub@figuiere.net> | 2023-08-20 09:54:43 -0400 |
---|---|---|
committer | Hubert Figuière <hub@figuiere.net> | 2023-08-20 15:16:46 -0400 |
commit | 2dc375087af02dd35df2f42b4ce71bb56099fc1d (patch) | |
tree | cd8f0b5bc0697087f76fa07468f9a2edb27b9955 | |
parent | c21be9f3a7c03a681198cbb01a6be40d8fb631ac (diff) |
libopenraw-testing: added gentest
- use quick-xml for the serialization
- update xml-rs for the deserialization
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | libopenraw-testing/Cargo.toml | 3 | ||||
-rw-r--r-- | libopenraw-testing/src/bin/gentest.rs | 76 | ||||
-rw-r--r-- | libopenraw-testing/src/lib.rs | 143 |
4 files changed, 202 insertions, 22 deletions
@@ -34,7 +34,7 @@ cc = { version = "1.0", features = ["parallel"] } [dev-dependencies] crc = "2.1.0" serde = { version = "^1, <=1.0.171", features = [ "derive" ] } -serde-xml-rs = "^0.5.1" +serde-xml-rs = "^0.6" criterion = { version = "0.5.1", features = ["html_reports"] } libopenraw-testing = { path = "libopenraw-testing" } image = { version = "0.24.6", features = [ "png", "jpeg" ], default-features = false } diff --git a/libopenraw-testing/Cargo.toml b/libopenraw-testing/Cargo.toml index d3f0476..fc324fe 100644 --- a/libopenraw-testing/Cargo.toml +++ b/libopenraw-testing/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] crc = "2.1.0" +getopts = "0.2.21" libopenraw = { path = ".." } +quick-xml = { version = "0.30.0", features = [ "serialize" ] } serde = { version = "^1, <=1.0.171", features = [ "derive" ] } -serde-xml-rs = "^0.5.1" diff --git a/libopenraw-testing/src/bin/gentest.rs b/libopenraw-testing/src/bin/gentest.rs new file mode 100644 index 0000000..fd1b6f8 --- /dev/null +++ b/libopenraw-testing/src/bin/gentest.rs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +/* + * libopenraw - libopenraw-testing.rs + * + * Copyright (C) 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/>. + */ + +//! Generate a test case from a raw file. + +use std::io::Write; +use std::path::Path; + +use getopts::Options; + +use libopenraw::{rawfile_from_file, Type}; +use libopenraw_testing::{make_results, Test}; + +pub fn main() { + let args: Vec<String> = std::env::args().collect(); + + let mut opts = Options::new(); + opts.optopt("o", "", "Output", "OUTPUT"); + + let matches = match opts.parse(&args[1..]) { + Ok(m) => m, + Err(f) => panic!("{}", f.to_string()), + }; + + let output = matches.opt_str("o"); + + let file = matches.free.first().expect("Input file required"); + + // XXX normalize in case of relative. + let rawfile = rawfile_from_file(file, None).expect("Raw file opening failed"); + let filename = Path::new(file) + .file_name() + .map(|s| s.to_string_lossy()) + .unwrap_or_default(); + let name = format!( + "{} - {filename}", + <Type as Into<String>>::into(rawfile.type_()) + ); + + let results = make_results(&*rawfile); + + let t = Test { + name, + file: file.to_string(), + source: None, + results, + }; + + let xml_serialized = t.serialize_to_xml(); + if let Some(output) = output { + println!("writing to {output}"); + let mut file = std::fs::File::create(output).expect("Couldn't open file"); + file.write_all(&xml_serialized.into_bytes()) + .expect("Writing failed"); + } else { + println!("{xml_serialized}"); + } +} diff --git a/libopenraw-testing/src/lib.rs b/libopenraw-testing/src/lib.rs index c87a45b..e355c71 100644 --- a/libopenraw-testing/src/lib.rs +++ b/libopenraw-testing/src/lib.rs @@ -22,7 +22,7 @@ use std::fmt::Display; use std::str::FromStr; -use serde::{de::Error, Deserialize}; +use serde::{de::Error, Deserialize, Serialize}; use libopenraw::{metadata::Value, Bitmap, DataType, Ifd, RawFile, Rect}; @@ -36,46 +36,63 @@ pub fn raw_checksum(buf: &[u8]) -> u16 { digest.finalize() } -#[derive(Clone, Debug, Deserialize, PartialEq)] +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Results { pub raw_type: Option<String>, + #[serde(default)] pub raw_type_id: Option<u32>, - /// MakeNoteCount can be -1 for an error (expected) + /// MakerNoteCount can be -1 for an error (expected) + #[serde(default)] 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_num: Option<u32>, + #[serde(deserialize_with = "from_list", serialize_with = "to_list", default)] pub thumb_sizes: Option<Vec<u32>>, pub thumb_formats: Option<String>, // XXX split array - #[serde(deserialize_with = "from_list")] - #[serde(default)] + #[serde(deserialize_with = "from_list", serialize_with = "to_list", default)] pub thumb_data_sizes: Option<Vec<u32>>, - #[serde(deserialize_with = "from_list")] - #[serde(default)] + #[serde(deserialize_with = "from_list", serialize_with = "to_list", 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_size: Option<u32>, + #[serde(deserialize_with = "from_list", serialize_with = "to_list", default)] pub raw_data_dimensions: Option<Vec<u32>>, - #[serde(deserialize_with = "from_list")] - #[serde(default)] + #[serde(deserialize_with = "from_list", serialize_with = "to_list", default)] pub raw_data_active_area: Option<Vec<u32>>, pub raw_cfa_pattern: Option<String>, - #[serde(deserialize_with = "from_list")] - #[serde(default)] + #[serde(deserialize_with = "from_list", serialize_with = "to_list", default)] pub raw_min_value: Option<Vec<u16>>, - #[serde(deserialize_with = "from_list")] - #[serde(default)] + #[serde(deserialize_with = "from_list", serialize_with = "to_list", default)] pub raw_max_value: Option<Vec<u16>>, + #[serde(default)] pub raw_md5: Option<u16>, + #[serde(default)] pub meta_orientation: Option<u32>, } +pub fn to_list<T, S>(value: &Option<Vec<T>>, serializer: S) -> Result<S::Ok, S::Error> +where + T: ToString, + S: serde::Serializer, +{ + if let Some(value) = value { + serializer.serialize_str( + &value + .iter() + .map(|v| v.to_string()) + .collect::<Vec<String>>() + .join(" "), + ) + } else { + serializer.serialize_none() + } +} + /// 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 @@ -93,7 +110,7 @@ where Ok(Some(ints)) } -#[derive(Clone, Debug, Deserialize, PartialEq)] +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename = "test")] pub struct Test { pub name: String, @@ -102,7 +119,22 @@ pub struct Test { pub results: Results, } -fn make_results(rawfile: &dyn RawFile) -> Results { +impl Test { + pub fn serialize_to_xml(&self) -> String { + let mut buffer = String::default(); + let mut serializer = quick_xml::se::Serializer::new(&mut buffer); + serializer.indent(' ', 2); + + self.serialize(serializer).expect("serialize"); + buffer + } + + pub fn deserialize_from_xml(serialized: &str) -> Test { + quick_xml::de::from_str(serialized).expect("deserialize") + } +} + +pub 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 @@ -119,7 +151,9 @@ fn make_results(rawfile: &dyn RawFile) -> Results { 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 maker_note_id = maker_note + .and_then(|mnote| std::ffi::CStr::from_bytes_with_nul(mnote.id()).ok()) + .map(|s| s.to_string_lossy().to_string()); let thumbnail_sizes = rawfile.thumbnail_sizes(); let thumb_num = Some(thumbnail_sizes.len() as u32); @@ -204,3 +238,72 @@ fn make_results(rawfile: &dyn RawFile) -> Results { raw_md5, } } + +#[cfg(test)] +mod tests { + use super::{Results, Test}; + + #[test] + fn deser() { + let xml_ser = "<test>\n\ + <name>test case</name>\n\ + <file>/var/home/hub/samples/cr2/20D/img_0620.cr2</file>\n\ + <source/>\n\ + <results>\n\ + <rawType>CR2</rawType>\n\ + <rawTypeId>65537</rawTypeId>\n\ + <makerNoteCount>24</makerNoteCount>\n\ + <makerNoteId>Exif.Canon</makerNoteId>\n\ + <exifMake>Canon</exifMake>\n\ + <exifModel>Canon EOS 20D</exifModel>\n\ + <thumbNum>2</thumbNum>\n\ + <thumbSizes>1536 160</thumbSizes>\n\ + <thumbFormats>JPEG JPEG</thumbFormats>\n\ + <thumbDataSizes>377981 7239</thumbDataSizes>\n\ + <thumbMd5>32808 59392</thumbMd5>\n\ + <rawDataType>RAW</rawDataType>\n\ + <rawDataSize>16973120</rawDataSize>\n\ + <rawDataDimensions>3596 2360</rawDataDimensions>\n\ + <rawDataActiveArea>84 19 3504 2336</rawDataActiveArea>\n\ + <rawCfaPattern>RGGB</rawCfaPattern>\n\ + <rawMinValue>0 0 0 0</rawMinValue>\n\ + <rawMaxValue>4095 4095 4095 4095</rawMaxValue>\n\ + <rawMd5>51638</rawMd5>\n\ + <metaOrientation>1</metaOrientation>\n\ + </results>\n\ +</test>"; + let newtest: Test = Test::deserialize_from_xml(&xml_ser); + + assert_eq!(newtest.results.raw_type, Some("CR2".to_string())); + assert_eq!(newtest.results.raw_type_id, Some(65537)); + assert_eq!( + newtest.results.raw_data_active_area, + Some(vec![84, 19, 3504, 2336]) + ); + } + + #[test] + #[ignore] + fn ser_deser() { + let results = Results { + thumb_num: Some(1), + thumb_sizes: Some(vec![1, 3, 5, 7]), + thumb_formats: Some("foo bar".into()), + ..Default::default() + }; + let t = Test { + name: "name of test".to_string(), + file: "/var/home/user/some/file.txt".to_string(), + source: None, + results, + }; + + let serial = t.serialize_to_xml(); + + println!("serial {serial}"); + + let newtest: Test = Test::deserialize_from_xml(&serial); + + assert_eq!(t, newtest); + } +} |