diff --git a/tests/rwrcmp.rs b/tests/rwrcmp.rs new file mode 100644 index 0000000..89a5898 --- /dev/null +++ b/tests/rwrcmp.rs @@ -0,0 +1,225 @@ +// +// Copyright (c) 2017 KAMADA Ken'ichi. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +// + +//! Read, write, re-read, and compare the results. +//! +//! This test can be also compiled as a command-line program. + +extern crate exif; + +use std::env; +use std::fs::File; +use std::io::{BufReader, Cursor}; +use std::path::Path; + +#[cfg(not(test))] +use exif::Error; +use exif::{Reader, Value, tag}; +use exif::experimental::Writer; + +#[test] +fn exif_jpg() { + rwr_compare("tests/exif.jpg"); +} + +#[test] +fn exif_tif() { + rwr_compare("tests/exif.tif"); +} + +fn main() { + for path in env::args_os().skip(1) { + rwr_compare(&path); + } +} + +fn rwr_compare

(path: P) where P: AsRef { + let path = path.as_ref(); + + // Read. + let file = File::open(path).unwrap(); + #[cfg(test)] + let reader1 = Reader::new(&mut BufReader::new(&file)).unwrap(); + #[cfg(not(test))] + let reader1 = match Reader::new(&mut BufReader::new(&file)) { + Ok(reader) => reader, + Err(e) => { + println!("{}: {}: Skipped", path.display(), e); + return; + }, + }; + let strips = get_strips(&reader1, false); + let tn_strips = get_strips(&reader1, true); + let tiles = get_tiles(&reader1, false); + let tn_jpeg = get_jpeg(&reader1, true); + + // Write. + let mut writer = Writer::new(); + for f in reader1.fields() { + writer.push_field(f); + } + if let Some(ref strips) = strips { + writer.set_strips(strips); + } + if let Some(ref tn_strips) = tn_strips { + writer.set_thumbnail_strips(tn_strips); + } + if let Some(ref tiles) = tiles { + writer.set_tiles(tiles); + } + if let Some(ref tn_jpeg) = tn_jpeg { + writer.set_thumbnail_jpeg(tn_jpeg); + } + let mut out = Cursor::new(Vec::new()); + #[cfg(test)] + writer.write(&mut out, reader1.little_endian()).unwrap(); + #[cfg(not(test))] + match writer.write(&mut out, reader1.little_endian()) { + Ok(_) => {}, + Err(Error::InvalidFormat("Cannot write unknown field types")) => { + println!("{}: Contains unknown type", path.display()); + return; + }, + e => e.unwrap(), + } + let out = out.into_inner(); + + // Re-read. + let reader2 = Reader::new(&mut &out[..]).unwrap(); + + // Sort the fields (some files have wrong tag order). + let mut fields1 = reader1.fields().iter().map(|f| f).collect::>(); + fields1.sort_by_key(|f| f.tag.number()); + let mut fields2 = reader2.fields().iter().map(|f| f).collect::>(); + fields2.sort_by_key(|f| f.tag.number()); + + // Compare. + assert_eq!(reader1.fields().len(), reader2.fields().len()); + for (f1, f2) in fields1.iter().zip(fields2.iter()) { + assert_eq!(f1.tag, f2.tag); + assert_eq!(f1.thumbnail, f2.thumbnail); + match f1.tag { + tag::StripOffsets | tag::TileOffsets | + tag::JPEGInterchangeFormat => continue, + _ => {}, + } + compare_field_value(&f1.value, &f2.value); + } + assert_eq!(get_strips(&reader2, false), strips); + assert_eq!(get_strips(&reader2, true), tn_strips); + assert_eq!(get_tiles(&reader2, false), tiles); + assert_eq!(get_jpeg(&reader2, true), tn_jpeg); +} + +// Compare field values. +fn compare_field_value(value1: &Value, value2: &Value) { + // The TIFF standard requires that BYTE, SHORT, or LONG values should + // be accepted for any unsigned integer field. + if let (Some(it1), Some(it2)) = (value1.iter_uint(), value2.iter_uint()) { + assert!(it1.eq(it2)); + return; + } + // Compare other fields strictly. + match (value1, value2) { + (&Value::Ascii(ref v1), &Value::Ascii(ref v2)) => + assert_eq!(v1, v2), + (&Value::Rational(ref v1), &Value::Rational(ref v2)) => { + assert_eq!(v1.len(), v2.len()); + for (r1, r2) in v1.iter().zip(v2.iter()) { + assert_eq!(r1.num, r2.num); + assert_eq!(r1.denom, r2.denom); + } + }, + (&Value::SByte(ref v1), &Value::SByte(ref v2)) => + assert_eq!(v1, v2), + (&Value::Undefined(ref v1), &Value::Undefined(ref v2)) => + assert_eq!(v1, v2), + (&Value::SShort(ref v1), &Value::SShort(ref v2)) => + assert_eq!(v1, v2), + (&Value::SLong(ref v1), &Value::SLong(ref v2)) => + assert_eq!(v1, v2), + (&Value::SRational(ref v1), &Value::SRational(ref v2)) => { + assert_eq!(v1.len(), v2.len()); + for (r1, r2) in v1.iter().zip(v2.iter()) { + assert_eq!(r1.num, r2.num); + assert_eq!(r1.denom, r2.denom); + } + }, + (&Value::Float(ref v1), &Value::Float(ref v2)) => + assert_eq!(v1, v2), + (&Value::Double(ref v1), &Value::Double(ref v2)) => + assert_eq!(v1, v2), + _ => panic!(format!("{:?} != {:?}", value1, value2)), + } +} + +fn get_strips(reader: &Reader, thumbnail: bool) -> Option> { + let offsets = reader.get_field(tag::StripOffsets, thumbnail) + .and_then(|f| f.value.iter_uint()); + let counts = reader.get_field(tag::StripByteCounts, thumbnail) + .and_then(|f| f.value.iter_uint()); + let (offsets, counts) = match (offsets, counts) { + (Some(offsets), Some(counts)) => (offsets, counts), + (None, None) => return None, + _ => panic!("inconsistent strip offsets and byte counts"), + }; + let buf = reader.buf(); + assert_eq!(offsets.len(), counts.len()); + let strips = offsets.zip(counts).map( + |(ofs, cnt)| &buf[ofs as usize .. (ofs + cnt) as usize]).collect(); + Some(strips) +} + +fn get_tiles(reader: &Reader, thumbnail: bool) -> Option> { + let offsets = reader.get_field(tag::TileOffsets, thumbnail) + .and_then(|f| f.value.iter_uint()); + let counts = reader.get_field(tag::TileByteCounts, thumbnail) + .and_then(|f| f.value.iter_uint()); + let (offsets, counts) = match (offsets, counts) { + (Some(offsets), Some(counts)) => (offsets, counts), + (None, None) => return None, + _ => panic!("inconsistent tile offsets and byte counts"), + }; + assert_eq!(offsets.len(), counts.len()); + let buf = reader.buf(); + let strips = offsets.zip(counts).map( + |(ofs, cnt)| &buf[ofs as usize .. (ofs + cnt) as usize]).collect(); + Some(strips) +} + +fn get_jpeg(reader: &Reader, thumbnail: bool) -> Option<&[u8]> { + let offset = reader.get_field(tag::JPEGInterchangeFormat, thumbnail) + .and_then(|f| f.value.get_uint(0)); + let len = reader.get_field(tag::JPEGInterchangeFormatLength, thumbnail) + .and_then(|f| f.value.get_uint(0)); + let (offset, len) = match (offset, len) { + (Some(offset), Some(len)) => (offset as usize, len as usize), + (None, None) => return None, + _ => panic!("inconsistent JPEG offset and length"), + }; + let buf = reader.buf(); + Some(&buf[offset..offset+len]) +}