summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHubert Figuière <hub@figuiere.net>2023-08-20 09:54:43 -0400
committerHubert Figuière <hub@figuiere.net>2023-08-20 15:16:46 -0400
commit2dc375087af02dd35df2f42b4ce71bb56099fc1d (patch)
treecd8f0b5bc0697087f76fa07468f9a2edb27b9955
parentc21be9f3a7c03a681198cbb01a6be40d8fb631ac (diff)
libopenraw-testing: added gentest
- use quick-xml for the serialization - update xml-rs for the deserialization
-rw-r--r--Cargo.toml2
-rw-r--r--libopenraw-testing/Cargo.toml3
-rw-r--r--libopenraw-testing/src/bin/gentest.rs76
-rw-r--r--libopenraw-testing/src/lib.rs143
4 files changed, 202 insertions, 22 deletions
diff --git a/Cargo.toml b/Cargo.toml
index ce035a3..907c1cd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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);
+ }
+}