Merge pull request #2060 from Exiv2/main_bmpTests
Extend unit tests for BMP & Datasets
This commit is contained in:
commit
ed09d9f017
23
.github/workflows/on_PR_linux_special_builds.yml
vendored
23
.github/workflows/on_PR_linux_special_builds.yml
vendored
@ -17,6 +17,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get install ninja-build
|
||||
pip3 install conan==1.43.0
|
||||
pip3 install gcovr
|
||||
|
||||
- name: Conan common config
|
||||
run: |
|
||||
@ -32,16 +33,27 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd build
|
||||
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON -DEXIV2_ENABLE_PNG=ON -DEXIV2_ENABLE_WEBREADY=ON -DEXIV2_ENABLE_CURL=ON -DEXIV2_BUILD_UNIT_TESTS=ON -DEXIV2_ENABLE_BMFF=ON -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON -DBUILD_WITH_COVERAGE=ON -DCMAKE_INSTALL_PREFIX=install ..
|
||||
cd build && \
|
||||
cmake -GNinja \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DEXIV2_ENABLE_PNG=ON \
|
||||
-DEXIV2_ENABLE_WEBREADY=ON \
|
||||
-DEXIV2_ENABLE_CURL=ON \
|
||||
-DEXIV2_BUILD_UNIT_TESTS=ON \
|
||||
-DEXIV2_ENABLE_BMFF=ON \
|
||||
-DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \
|
||||
-DEXIV2_BUILD_SAMPLES=ON \
|
||||
-DBUILD_WITH_COVERAGE=ON \
|
||||
-DCMAKE_INSTALL_PREFIX=install \
|
||||
.. && \
|
||||
cmake --build .
|
||||
|
||||
- name: Tests + Upload coverage
|
||||
run: |
|
||||
cd build
|
||||
ctest --output-on-failure
|
||||
pip install gcovr
|
||||
gcovr -r ./../ -x --exclude-unreachable-branches --exclude-throw-branches -o coverage.xml .
|
||||
gcovr --root .. --object-dir . --exclude-directories xmpsdk --exclude-directories unitTests --exclude-directories samples --exclude '.*xmpsdk.*' --exclude '.*unitTests.*' --exclude '.*samples.*' --exclude-unreachable-branches --exclude-throw-branches --xml -o coverage.xml
|
||||
curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --import
|
||||
curl -Os https://uploader.codecov.io/latest/linux/codecov
|
||||
curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM
|
||||
@ -49,7 +61,8 @@ jobs:
|
||||
gpg --verify codecov.SHA256SUM.sig codecov.SHA256SUM
|
||||
shasum -a 256 -c codecov.SHA256SUM
|
||||
chmod +x codecov
|
||||
./codecov -f build/coverage.xml
|
||||
ls -lh
|
||||
./codecov -f coverage.xml
|
||||
|
||||
special_releaseValgrind:
|
||||
name: 'Ubuntu 20.04 - GCC - Release+Valgrind'
|
||||
|
||||
@ -14,6 +14,7 @@ if(BUILD_WITH_COVERAGE)
|
||||
COMMAND ${GCOVR} --root ${CMAKE_SOURCE_DIR} --object-dir=${CMAKE_BINARY_DIR} --html --html-details -o coverage_output/coverage.html
|
||||
--exclude-directories xmpsdk --exclude-directories unitTests --exclude-directories samples
|
||||
--exclude '.*xmpsdk.*' --exclude '.*unitTests.*' --exclude '.*samples.*'
|
||||
--exclude-unreachable-branches --exclude-throw-branches
|
||||
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
@ -79,25 +79,17 @@ namespace Exiv2 {
|
||||
//! @name Manipulators
|
||||
//@{
|
||||
void readMetadata() override;
|
||||
/*!
|
||||
@brief Todo: Write metadata back to the image. This method is not
|
||||
yet(?) implemented. Calling it will throw an Error(kerWritingImageFormatUnsupported).
|
||||
*/
|
||||
|
||||
/// @throws Error(kerWritingImageFormatUnsupported).
|
||||
void writeMetadata() override;
|
||||
/*!
|
||||
@brief Todo: Not supported yet(?). Calling this function will throw
|
||||
an instance of Error(kerInvalidSettingForImage).
|
||||
*/
|
||||
|
||||
/// @throws Error(kerInvalidSettingForImage)
|
||||
void setExifData(const ExifData& exifData) override;
|
||||
/*!
|
||||
@brief Todo: Not supported yet(?). Calling this function will throw
|
||||
an instance of Error(kerInvalidSettingForImage).
|
||||
*/
|
||||
|
||||
/// @throws Error(kerInvalidSettingForImage)
|
||||
void setIptcData(const IptcData& iptcData) override;
|
||||
/*!
|
||||
@brief Not supported. Calling this function will throw an instance
|
||||
of Error(kerInvalidSettingForImage).
|
||||
*/
|
||||
|
||||
/// @throws Error(kerInvalidSettingForImage)
|
||||
void setComment(const std::string& comment) override;
|
||||
//@}
|
||||
|
||||
@ -105,7 +97,6 @@ namespace Exiv2 {
|
||||
//@{
|
||||
std::string mimeType() const override;
|
||||
//@}
|
||||
|
||||
}; // class BmpImage
|
||||
|
||||
// *****************************************************************************
|
||||
|
||||
@ -97,6 +97,7 @@ namespace Exiv2 {
|
||||
static constexpr uint16_t UNO = 100;
|
||||
static constexpr uint16_t ARMId = 120;
|
||||
static constexpr uint16_t ARMVersion = 122;
|
||||
|
||||
static constexpr uint16_t RecordVersion = 0;
|
||||
static constexpr uint16_t ObjectType = 3;
|
||||
static constexpr uint16_t ObjectAttribute = 4;
|
||||
@ -172,6 +173,7 @@ namespace Exiv2 {
|
||||
dataset.
|
||||
*/
|
||||
static std::string dataSetName(uint16_t number, uint16_t recordId);
|
||||
|
||||
/*!
|
||||
@brief Return the title (label) of the dataset.
|
||||
@param number The dataset number
|
||||
@ -179,6 +181,7 @@ namespace Exiv2 {
|
||||
@return The title (label) of the dataset
|
||||
*/
|
||||
static const char* dataSetTitle(uint16_t number, uint16_t recordId);
|
||||
|
||||
/*!
|
||||
@brief Return the description of the dataset.
|
||||
@param number The dataset number
|
||||
@ -186,6 +189,7 @@ namespace Exiv2 {
|
||||
@return The description of the dataset
|
||||
*/
|
||||
static const char* dataSetDesc(uint16_t number, uint16_t recordId);
|
||||
|
||||
/*!
|
||||
@brief Return the Photoshop name of a given dataset.
|
||||
@param number The dataset number
|
||||
@ -194,6 +198,7 @@ namespace Exiv2 {
|
||||
string if Photoshop does not use the dataset.
|
||||
*/
|
||||
static const char* dataSetPsName(uint16_t number, uint16_t recordId);
|
||||
|
||||
/*!
|
||||
@brief Check if a given dataset is repeatable
|
||||
@param number The dataset number
|
||||
@ -201,6 +206,7 @@ namespace Exiv2 {
|
||||
@return true if the given dataset is repeatable otherwise false
|
||||
*/
|
||||
static bool dataSetRepeatable(uint16_t number, uint16_t recordId);
|
||||
|
||||
/*!
|
||||
@brief Return the dataSet number for dataset name and record id
|
||||
|
||||
@ -212,8 +218,10 @@ namespace Exiv2 {
|
||||
@throw Error if the \em dataSetName or \em recordId are invalid
|
||||
*/
|
||||
static uint16_t dataSet(const std::string& dataSetName, uint16_t recordId);
|
||||
|
||||
//! Return the type for dataSet number and Record id
|
||||
static TypeId dataSetType(uint16_t number, uint16_t recordId);
|
||||
|
||||
/*!
|
||||
@brief Return the name of the Record
|
||||
@param recordId The record id
|
||||
@ -222,24 +230,14 @@ namespace Exiv2 {
|
||||
unknown record.
|
||||
*/
|
||||
static std::string recordName(uint16_t recordId);
|
||||
|
||||
/*!
|
||||
@brief Return the description of a record
|
||||
@param recordId Record Id number
|
||||
@return the description of the Record
|
||||
*/
|
||||
static const char* recordDesc(uint16_t recordId);
|
||||
/*!
|
||||
@brief Return the Id number of a record
|
||||
@param recordName Name of a record type
|
||||
@return the Id number of a Record
|
||||
@throw Error if the record is not known;
|
||||
*/
|
||||
static uint16_t recordId(const std::string& recordName);
|
||||
//! Return read-only list of built-in Envelope Record datasets
|
||||
static const DataSet* envelopeRecordList();
|
||||
//! Return read-only list of built-in Application2 Record datasets
|
||||
static const DataSet* application2RecordList();
|
||||
//! Print a list of all dataSets to output stream
|
||||
|
||||
static void dataSetList(std::ostream& os);
|
||||
|
||||
private:
|
||||
@ -247,7 +245,6 @@ namespace Exiv2 {
|
||||
static int dataSetIdx(const std::string& dataSetName, uint16_t recordId);
|
||||
|
||||
static const DataSet* const records_[];
|
||||
static const RecordInfo recordInfo_[];
|
||||
|
||||
}; // class IptcDataSets
|
||||
|
||||
@ -278,17 +275,11 @@ namespace Exiv2 {
|
||||
IptcKey(uint16_t tag, uint16_t record);
|
||||
//! Copy constructor
|
||||
IptcKey(const IptcKey& rhs);
|
||||
IptcKey& operator=(const IptcKey& rhs) = delete;
|
||||
//! Destructor
|
||||
~IptcKey() override = default;
|
||||
//@}
|
||||
|
||||
//! @name Manipulators
|
||||
//@{
|
||||
/*!
|
||||
@brief Assignment operator.
|
||||
*/
|
||||
IptcKey& operator=(const IptcKey& rhs);
|
||||
//@}
|
||||
|
||||
//! @name Accessors
|
||||
//@{
|
||||
@ -331,10 +322,6 @@ namespace Exiv2 {
|
||||
//! Internal virtual copy constructor.
|
||||
IptcKey* clone_() const override;
|
||||
|
||||
// DATA
|
||||
#ifndef SWIG
|
||||
static constexpr auto familyName_ = "Iptc";
|
||||
#endif
|
||||
uint16_t tag_; //!< Tag value
|
||||
uint16_t record_; //!< Record value
|
||||
std::string key_; //!< Key
|
||||
|
||||
@ -368,9 +368,8 @@ namespace Exiv2 {
|
||||
encoded. Initially, it is not set (\em invalidByteOrder).
|
||||
*/
|
||||
ByteOrder byteOrder() const;
|
||||
/*!
|
||||
@brief Check if the Image instance is valid. Use after object
|
||||
construction.
|
||||
|
||||
/*! @brief Check if the Image instance is valid. Use after object construction.
|
||||
@return true if the Image is in a valid state.
|
||||
*/
|
||||
bool good() const;
|
||||
|
||||
@ -45,6 +45,8 @@ namespace Exiv2 {
|
||||
/*!
|
||||
@brief An IPTC metadatum ("dataset"), consisting of an IptcKey and a
|
||||
Value and methods to manipulate these.
|
||||
|
||||
This is referred in the standard as a property.
|
||||
*/
|
||||
class EXIV2API Iptcdatum : public Metadatum {
|
||||
public:
|
||||
@ -156,8 +158,7 @@ namespace Exiv2 {
|
||||
typedef std::vector<Iptcdatum> IptcMetadata;
|
||||
|
||||
/*!
|
||||
@brief A container for IPTC data. This is a top-level class of
|
||||
the %Exiv2 library.
|
||||
@brief A container for IPTC data. This is a top-level class of the %Exiv2 library.
|
||||
|
||||
Provide high-level access to the IPTC data of an image:
|
||||
- read IPTC information from JPEG files
|
||||
|
||||
@ -47,24 +47,22 @@ namespace Exiv2
|
||||
|
||||
std::string BmpImage::mimeType() const
|
||||
{
|
||||
return "image/x-ms-bmp";
|
||||
// "image/bmp" is a Generic Bitmap
|
||||
return "image/x-ms-bmp"; // Microsoft Bitmap
|
||||
}
|
||||
|
||||
void BmpImage::setExifData(const ExifData& /*exifData*/)
|
||||
{
|
||||
// Todo: implement me!
|
||||
throw(Error(kerInvalidSettingForImage, "Exif metadata", "BMP"));
|
||||
}
|
||||
|
||||
void BmpImage::setIptcData(const IptcData& /*iptcData*/)
|
||||
{
|
||||
// Todo: implement me!
|
||||
throw(Error(kerInvalidSettingForImage, "IPTC metadata", "BMP"));
|
||||
}
|
||||
|
||||
void BmpImage::setComment(const std::string& /*comment*/)
|
||||
{
|
||||
// not supported
|
||||
throw(Error(kerInvalidSettingForImage, "Image comment", "BMP"));
|
||||
}
|
||||
|
||||
@ -77,9 +75,11 @@ namespace Exiv2
|
||||
throw Error(kerDataSourceOpenFailed, io_->path(), strError());
|
||||
}
|
||||
IoCloser closer(*io_);
|
||||
|
||||
// Ensure that this is the correct image type
|
||||
if (!isBmpType(*io_, false)) {
|
||||
if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
|
||||
if (io_->error() || io_->eof())
|
||||
throw Error(kerFailedToReadImageData);
|
||||
throw Error(kerNotAnImage, "BMP");
|
||||
}
|
||||
clearMetadata();
|
||||
@ -105,7 +105,7 @@ namespace Exiv2
|
||||
46 4 bytes color count
|
||||
50 4 bytes important colors number of "important" colors
|
||||
*/
|
||||
byte buf[54];
|
||||
byte buf[26];
|
||||
if (io_->read(buf, sizeof(buf)) == sizeof(buf)) {
|
||||
pixelWidth_ = getLong(buf + 18, littleEndian);
|
||||
pixelHeight_ = getLong(buf + 22, littleEndian);
|
||||
@ -114,7 +114,7 @@ namespace Exiv2
|
||||
|
||||
void BmpImage::writeMetadata()
|
||||
{
|
||||
// Todo: implement me!
|
||||
/// \todo implement me!
|
||||
throw(Error(kerWritingImageFormatUnsupported, "BMP"));
|
||||
}
|
||||
|
||||
|
||||
177
src/datasets.cpp
177
src/datasets.cpp
@ -34,12 +34,15 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
// *****************************************************************************
|
||||
// class member definitions
|
||||
|
||||
namespace Exiv2 {
|
||||
constexpr RecordInfo IptcDataSets::recordInfo_[] = {
|
||||
constexpr const char *familyName_{"Iptc"};
|
||||
constexpr RecordInfo recordInfo_[] = {
|
||||
{IptcDataSets::invalidRecord, "(invalid)", N_("(invalid)")},
|
||||
{IptcDataSets::envelope, "Envelope", N_("IIM envelope record")},
|
||||
{IptcDataSets::application2, "Application2", N_("IIM application record 2")},
|
||||
@ -125,11 +128,6 @@ namespace Exiv2 {
|
||||
"(Invalid)", false, false, 0, 0, Exiv2::unsignedShort, IptcDataSets::envelope, ""},
|
||||
};
|
||||
|
||||
const DataSet* IptcDataSets::envelopeRecordList()
|
||||
{
|
||||
return envelopeRecord;
|
||||
}
|
||||
|
||||
constexpr DataSet application2Record[] = {
|
||||
{IptcDataSets::RecordVersion, "RecordVersion", N_("Record Version"),
|
||||
N_("A binary number identifying the version of the Information "
|
||||
@ -402,16 +400,26 @@ namespace Exiv2 {
|
||||
false, false, 0, 0, Exiv2::unsignedShort, IptcDataSets::application2, ""},
|
||||
};
|
||||
|
||||
const DataSet* IptcDataSets::application2RecordList()
|
||||
uint16_t recordId(const std::string& recordName)
|
||||
{
|
||||
return application2Record;
|
||||
uint16_t i;
|
||||
for (i = IptcDataSets::application2; i > 0; --i) {
|
||||
if (recordInfo_[i].name_ == recordName)
|
||||
break;
|
||||
}
|
||||
if (i == 0) {
|
||||
if (!isHex(recordName, 4, "0x"))
|
||||
throw Error(kerInvalidRecord, recordName);
|
||||
std::istringstream is(recordName);
|
||||
is >> std::hex >> i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
constexpr DataSet unknownDataSet{0xffff, "Unknown dataset", N_("Unknown dataset"),
|
||||
N_("Unknown dataset"),
|
||||
false, true, 0, 0xffffffff, Exiv2::string,
|
||||
IptcDataSets::invalidRecord,
|
||||
N_("Unknown dataset"),};
|
||||
constexpr DataSet unknownDataSet{
|
||||
0xffff, "Unknown dataset", N_("Unknown dataset"), N_("Unknown dataset"), false, true, 0,
|
||||
0xffffffff, Exiv2::string, IptcDataSets::invalidRecord, N_("Unknown dataset"),
|
||||
};
|
||||
|
||||
// Dataset lookup lists.This is an array with pointers to one list per IIM4 Record.
|
||||
// The record id is used as the index into the array.
|
||||
@ -424,26 +432,26 @@ namespace Exiv2 {
|
||||
|
||||
int IptcDataSets::dataSetIdx(uint16_t number, uint16_t recordId)
|
||||
{
|
||||
if( recordId != envelope && recordId != application2 ) return -1;
|
||||
const DataSet* dataSet = records_[recordId];
|
||||
if (dataSet == nullptr)
|
||||
if (recordId != envelope && recordId != application2)
|
||||
return -1;
|
||||
const DataSet* dataSet = records_[recordId];
|
||||
int idx;
|
||||
for (idx = 0; dataSet[idx].number_ != number; ++idx) {
|
||||
if (dataSet[idx].number_ == 0xffff) return -1;
|
||||
if (dataSet[idx].number_ == 0xffff)
|
||||
return -1;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
int IptcDataSets::dataSetIdx(const std::string& dataSetName, uint16_t recordId)
|
||||
{
|
||||
if( recordId != envelope && recordId != application2 ) return -1;
|
||||
const DataSet* dataSet = records_[recordId];
|
||||
if (dataSet == nullptr)
|
||||
if (recordId != envelope && recordId != application2)
|
||||
return -1;
|
||||
const DataSet* dataSet = records_[recordId];
|
||||
int idx;
|
||||
for (idx = 0; dataSet[idx].name_ != dataSetName; ++idx) {
|
||||
if (dataSet[idx].number_ == 0xffff) return -1;
|
||||
if (dataSet[idx].number_ == 0xffff)
|
||||
return -1;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
@ -451,60 +459,64 @@ namespace Exiv2 {
|
||||
TypeId IptcDataSets::dataSetType(uint16_t number, uint16_t recordId)
|
||||
{
|
||||
int idx = dataSetIdx(number, recordId);
|
||||
if (idx == -1) return unknownDataSet.type_;
|
||||
if (idx == -1)
|
||||
return unknownDataSet.type_;
|
||||
return records_[recordId][idx].type_;
|
||||
}
|
||||
|
||||
std::string IptcDataSets::dataSetName(uint16_t number, uint16_t recordId)
|
||||
{
|
||||
int idx = dataSetIdx(number, recordId);
|
||||
if (idx != -1) return records_[recordId][idx].name_;
|
||||
if (idx != -1)
|
||||
return records_[recordId][idx].name_;
|
||||
|
||||
std::ostringstream os;
|
||||
os << "0x" << std::setw(4) << std::setfill('0') << std::right
|
||||
<< std::hex << number;
|
||||
os << "0x" << std::setw(4) << std::setfill('0') << std::right << std::hex << number;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
const char* IptcDataSets::dataSetTitle(uint16_t number, uint16_t recordId)
|
||||
{
|
||||
int idx = dataSetIdx(number, recordId);
|
||||
if (idx == -1) return unknownDataSet.title_;
|
||||
if (idx == -1)
|
||||
return unknownDataSet.title_;
|
||||
return records_[recordId][idx].title_;
|
||||
}
|
||||
|
||||
const char* IptcDataSets::dataSetDesc(uint16_t number, uint16_t recordId)
|
||||
{
|
||||
int idx = dataSetIdx(number, recordId);
|
||||
if (idx == -1) return unknownDataSet.desc_;
|
||||
if (idx == -1)
|
||||
return unknownDataSet.desc_;
|
||||
return records_[recordId][idx].desc_;
|
||||
}
|
||||
|
||||
const char* IptcDataSets::dataSetPsName(uint16_t number, uint16_t recordId)
|
||||
{
|
||||
int idx = dataSetIdx(number, recordId);
|
||||
if (idx == -1) return unknownDataSet.photoshop_;
|
||||
if (idx == -1)
|
||||
return unknownDataSet.photoshop_;
|
||||
return records_[recordId][idx].photoshop_;
|
||||
}
|
||||
|
||||
bool IptcDataSets::dataSetRepeatable(uint16_t number, uint16_t recordId)
|
||||
{
|
||||
int idx = dataSetIdx(number, recordId);
|
||||
if (idx == -1) return unknownDataSet.repeatable_;
|
||||
if (idx == -1)
|
||||
return unknownDataSet.repeatable_;
|
||||
return records_[recordId][idx].repeatable_;
|
||||
}
|
||||
|
||||
uint16_t IptcDataSets::dataSet(const std::string& dataSetName,
|
||||
uint16_t recordId)
|
||||
uint16_t IptcDataSets::dataSet(const std::string& dataSetName, uint16_t recordId)
|
||||
{
|
||||
uint16_t dataSet = 0;
|
||||
int idx = dataSetIdx(dataSetName, recordId);
|
||||
if (idx != -1) {
|
||||
// dataSetIdx checks the range of recordId
|
||||
dataSet = records_[recordId][idx].number_;
|
||||
}
|
||||
else {
|
||||
if (!isHex(dataSetName, 4, "0x")) throw Error(kerInvalidDataset, dataSetName);
|
||||
} else {
|
||||
if (!isHex(dataSetName, 4, "0x"))
|
||||
throw Error(kerInvalidDataset, dataSetName);
|
||||
std::istringstream is(dataSetName);
|
||||
is >> std::hex >> dataSet;
|
||||
}
|
||||
@ -518,8 +530,7 @@ namespace Exiv2 {
|
||||
}
|
||||
|
||||
std::ostringstream os;
|
||||
os << "0x" << std::setw(4) << std::setfill('0') << std::right
|
||||
<< std::hex << recordId;
|
||||
os << "0x" << std::setw(4) << std::setfill('0') << std::right << std::hex << recordId;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
@ -531,19 +542,6 @@ namespace Exiv2 {
|
||||
return recordInfo_[recordId].desc_;
|
||||
}
|
||||
|
||||
uint16_t IptcDataSets::recordId(const std::string& recordName)
|
||||
{
|
||||
uint16_t i;
|
||||
for (i = application2; i > 0; --i) {
|
||||
if (recordInfo_[i].name_ == recordName) break;
|
||||
}
|
||||
if (i == 0) {
|
||||
if (!isHex(recordName, 4, "0x")) throw Error(kerInvalidRecord, recordName);
|
||||
std::istringstream is(recordName);
|
||||
is >> std::hex >> i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void IptcDataSets::dataSetList(std::ostream& os)
|
||||
{
|
||||
@ -552,36 +550,22 @@ namespace Exiv2 {
|
||||
os << record[j] << "\n";
|
||||
}
|
||||
}
|
||||
} // IptcDataSets::dataSetList
|
||||
}
|
||||
|
||||
IptcKey::IptcKey(std::string key) : key_(std::move(key))
|
||||
{
|
||||
decomposeKey();
|
||||
}
|
||||
|
||||
IptcKey::IptcKey(uint16_t tag, uint16_t record)
|
||||
: tag_(tag), record_(record)
|
||||
IptcKey::IptcKey(uint16_t tag, uint16_t record) : tag_(tag), record_(record)
|
||||
{
|
||||
makeKey();
|
||||
}
|
||||
|
||||
IptcKey::IptcKey(const IptcKey& rhs)
|
||||
: tag_(rhs.tag_)
|
||||
, record_(rhs.record_)
|
||||
, key_(rhs.key_)
|
||||
IptcKey::IptcKey(const IptcKey& rhs) : tag_(rhs.tag_), record_(rhs.record_), key_(rhs.key_)
|
||||
{
|
||||
}
|
||||
|
||||
IptcKey& IptcKey::operator=(const IptcKey& rhs)
|
||||
{
|
||||
if (this == &rhs) return *this;
|
||||
Key::operator=(rhs);
|
||||
tag_ = rhs.tag_;
|
||||
record_ = rhs.record_;
|
||||
key_ = rhs.key_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string IptcKey::key() const
|
||||
{
|
||||
return key_;
|
||||
@ -634,25 +618,27 @@ namespace Exiv2 {
|
||||
|
||||
void IptcKey::decomposeKey()
|
||||
{
|
||||
// Check that the key has the expected format with RE
|
||||
static const std::regex re(R"((\w+)(\.\w+){2})");
|
||||
std::smatch sm;
|
||||
if (!std::regex_match(key_, sm, re)) {
|
||||
throw Error(kerInvalidKey, key_);
|
||||
}
|
||||
|
||||
// Get the family name, record name and dataSet name parts of the key
|
||||
std::string::size_type pos1 = key_.find('.');
|
||||
if (pos1 == std::string::npos) throw Error(kerInvalidKey, key_);
|
||||
std::string familyName = key_.substr(0, pos1);
|
||||
auto posDot1 = key_.find('.');
|
||||
auto posDot2 = key_.find('.', posDot1+1);
|
||||
|
||||
const std::string familyName = key_.substr(0, posDot1);
|
||||
if (0 != strcmp(familyName.c_str(), familyName_)) {
|
||||
throw Error(kerInvalidKey, key_);
|
||||
}
|
||||
std::string::size_type pos0 = pos1 + 1;
|
||||
pos1 = key_.find('.', pos0);
|
||||
if (pos1 == std::string::npos) throw Error(kerInvalidKey, key_);
|
||||
std::string recordName = key_.substr(pos0, pos1 - pos0);
|
||||
if (recordName.empty())
|
||||
throw Error(kerInvalidKey, key_);
|
||||
std::string dataSetName = key_.substr(pos1 + 1);
|
||||
if (dataSetName.empty())
|
||||
throw Error(kerInvalidKey, key_);
|
||||
|
||||
std::string recordName = key_.substr(posDot1+1, posDot2 - posDot1 - 1);
|
||||
std::string dataSetName = key_.substr(posDot2+1);
|
||||
|
||||
// Use the parts of the key to find dataSet and recordId
|
||||
uint16_t recId = IptcDataSets::recordId(recordName);
|
||||
uint16_t recId = recordId(recordName);
|
||||
uint16_t dataSet = IptcDataSets::dataSet(dataSetName, recId);
|
||||
|
||||
// Possibly translate hex name parts (0xabcd) to real names
|
||||
@ -662,13 +648,12 @@ namespace Exiv2 {
|
||||
tag_ = dataSet;
|
||||
record_ = recId;
|
||||
key_ = familyName + "." + recordName + "." + dataSetName;
|
||||
} // IptcKey::decomposeKey
|
||||
}
|
||||
|
||||
void IptcKey::makeKey()
|
||||
{
|
||||
key_ = std::string(familyName_)
|
||||
+ "." + IptcDataSets::recordName(record_)
|
||||
+ "." + IptcDataSets::dataSetName(tag_, record_);
|
||||
key_ = std::string(familyName_) + "." + IptcDataSets::recordName(record_) + "." +
|
||||
IptcDataSets::dataSetName(tag_, record_);
|
||||
}
|
||||
|
||||
// *************************************************************************
|
||||
@ -676,27 +661,21 @@ namespace Exiv2 {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DataSet& dataSet)
|
||||
{
|
||||
std::ios::fmtflags f( os.flags() );
|
||||
std::ios::fmtflags f(os.flags());
|
||||
IptcKey iptcKey(dataSet.number_, dataSet.recordId_);
|
||||
os << dataSet.name_ << ", "
|
||||
<< std::dec << dataSet.number_ << ", "
|
||||
<< "0x" << std::setw(4) << std::setfill('0')
|
||||
<< std::right << std::hex << dataSet.number_ << ", "
|
||||
<< IptcDataSets::recordName(dataSet.recordId_) << ", "
|
||||
<< std::boolalpha << dataSet.mandatory_ << ", "
|
||||
<< dataSet.repeatable_ << ", "
|
||||
<< std::dec << dataSet.minbytes_ << ", "
|
||||
<< dataSet.maxbytes_ << ", "
|
||||
<< iptcKey.key() << ", "
|
||||
<< TypeInfo::typeName(
|
||||
IptcDataSets::dataSetType(dataSet.number_,
|
||||
dataSet.recordId_)) << ", ";
|
||||
os << dataSet.name_ << ", " << std::dec << dataSet.number_ << ", "
|
||||
<< "0x" << std::setw(4) << std::setfill('0') << std::right << std::hex << dataSet.number_ << ", "
|
||||
<< IptcDataSets::recordName(dataSet.recordId_) << ", " << std::boolalpha << dataSet.mandatory_ << ", "
|
||||
<< dataSet.repeatable_ << ", " << std::dec << dataSet.minbytes_ << ", " << dataSet.maxbytes_ << ", "
|
||||
<< iptcKey.key() << ", " << TypeInfo::typeName(IptcDataSets::dataSetType(dataSet.number_, dataSet.recordId_))
|
||||
<< ", ";
|
||||
// CSV encoded I am \"dead\" beat" => "I am ""dead"" beat"
|
||||
char Q = '"';
|
||||
os << Q;
|
||||
for ( size_t i = 0 ; i < ::strlen(dataSet.desc_) ; i++ ) {
|
||||
for (size_t i = 0; i < ::strlen(dataSet.desc_); i++) {
|
||||
char c = dataSet.desc_[i];
|
||||
if ( c == Q ) os << Q;
|
||||
if (c == Q)
|
||||
os << Q;
|
||||
os << c;
|
||||
}
|
||||
os << Q;
|
||||
|
||||
@ -55,11 +55,11 @@ namespace {
|
||||
{ Exiv2::kerNotAnImage,
|
||||
N_("This does not look like a %1 image") }, // %1=Image type
|
||||
{ Exiv2::kerInvalidDataset,
|
||||
N_("Invalid dataset name `%1'") }, // %1=dataset name
|
||||
N_("Invalid dataset name '%1'") }, // %1=dataset name
|
||||
{ Exiv2::kerInvalidRecord,
|
||||
N_("Invalid record name `%1'") }, // %1=record name
|
||||
N_("Invalid record name '%1'") }, // %1=record name
|
||||
{ Exiv2::kerInvalidKey,
|
||||
N_("Invalid key `%1'") }, // %1=key
|
||||
N_("Invalid key '%1'") }, // %1=key
|
||||
{ Exiv2::kerInvalidTag,
|
||||
N_("Invalid tag name or ifdId `%1', ifdId %2") }, // %1=tag name, %2=ifdId
|
||||
{ Exiv2::kerValueNotSet,
|
||||
|
||||
@ -741,7 +741,8 @@ namespace Exiv2 {
|
||||
|
||||
bool Image::good() const
|
||||
{
|
||||
if (io_->open() != 0) return false;
|
||||
if (io_->open() != 0)
|
||||
return false;
|
||||
IoCloser closer(*io_);
|
||||
return ImageFactory::checkType(imageType_, *io_, false);
|
||||
}
|
||||
@ -809,7 +810,7 @@ namespace Exiv2 {
|
||||
return r->isThisType_(io, advance);
|
||||
}
|
||||
return false;
|
||||
} // ImageFactory::checkType
|
||||
}
|
||||
|
||||
int ImageFactory::getType(const std::string& path)
|
||||
{
|
||||
|
||||
@ -2,6 +2,8 @@ find_package(GTest REQUIRED)
|
||||
|
||||
add_executable(unit_tests
|
||||
mainTestRunner.cpp
|
||||
test_bmpimage.cpp
|
||||
test_datasets.cpp
|
||||
test_DateValue.cpp
|
||||
test_TimeValue.cpp
|
||||
test_XmpKey.cpp
|
||||
@ -12,6 +14,7 @@ add_executable(unit_tests
|
||||
test_futils.cpp
|
||||
test_helper_functions.cpp
|
||||
test_image_int.cpp
|
||||
test_IptcKey.cpp
|
||||
test_pngimage.cpp
|
||||
test_safe_op.cpp
|
||||
test_slice.cpp
|
||||
|
||||
116
unitTests/test_IptcKey.cpp
Normal file
116
unitTests/test_IptcKey.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <exiv2/datasets.hpp>
|
||||
#include <exiv2/error.hpp>
|
||||
|
||||
using namespace Exiv2;
|
||||
|
||||
TEST(IptcKey, creationWithNonValidStringFormatThrows)
|
||||
{
|
||||
try {
|
||||
IptcKey key("Yeah");
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerInvalidKey, e.code());
|
||||
ASSERT_STREQ("Invalid key 'Yeah'", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(IptcKey, creationWithNonValidRecordNameThrows)
|
||||
{
|
||||
try {
|
||||
IptcKey key("Iptc.WrongRecordName.ModelVersion");
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerInvalidRecord, e.code());
|
||||
ASSERT_STREQ("Invalid record name 'WrongRecordName'", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(IptcKey, creationWithNonValidDatasetNameThrows)
|
||||
{
|
||||
try {
|
||||
IptcKey key("Iptc.Envelope.WrongDataset");
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerInvalidDataset, e.code());
|
||||
ASSERT_STREQ("Invalid dataset name 'WrongDataset'", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(IptcKey, creationWithNonValidFamiltyNameThrows)
|
||||
{
|
||||
try {
|
||||
IptcKey key("JOJO.Envelope.WrongDataset");
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerInvalidKey, e.code());
|
||||
ASSERT_STREQ("Invalid key 'JOJO.Envelope.WrongDataset'", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(IptcKey, creationWithValidStringDoesNotThrow)
|
||||
{
|
||||
ASSERT_NO_THROW(IptcKey ("Iptc.Envelope.ModelVersion"));
|
||||
}
|
||||
|
||||
TEST(IptcKey, copyConstructor)
|
||||
{
|
||||
IptcKey key ("Iptc.Envelope.ModelVersion");
|
||||
IptcKey keyCopy (key);
|
||||
}
|
||||
|
||||
TEST(IptcKey, clone)
|
||||
{
|
||||
IptcKey key ("Iptc.Envelope.ModelVersion");
|
||||
auto keyClone = key.clone();
|
||||
ASSERT_EQ("Iptc.Envelope.ModelVersion", keyClone->key());
|
||||
}
|
||||
|
||||
TEST(IptcKey, keyReturnsTheFullString)
|
||||
{
|
||||
IptcKey key ("Iptc.Envelope.ModelVersion");
|
||||
ASSERT_EQ("Iptc.Envelope.ModelVersion", key.key());
|
||||
}
|
||||
|
||||
TEST(IptcKey, familyNameReturnsTheFullString)
|
||||
{
|
||||
IptcKey key ("Iptc.Envelope.ModelVersion");
|
||||
ASSERT_STREQ("Iptc", key.familyName());
|
||||
}
|
||||
|
||||
TEST(IptcKey, groupNameReturnsTheRecordName)
|
||||
{
|
||||
IptcKey key ("Iptc.Envelope.ModelVersion");
|
||||
ASSERT_EQ("Envelope", key.groupName());
|
||||
}
|
||||
|
||||
TEST(IptcKey, recordNameReturnsTheRecordName)
|
||||
{
|
||||
IptcKey key ("Iptc.Envelope.ModelVersion");
|
||||
ASSERT_EQ("Envelope", key.recordName());
|
||||
}
|
||||
|
||||
TEST(IptcKey, tagNameReturnsTheDatasetName)
|
||||
{
|
||||
IptcKey key ("Iptc.Envelope.ModelVersion");
|
||||
ASSERT_EQ("ModelVersion", key.tagName());
|
||||
}
|
||||
|
||||
TEST(IptcKey, tagLabelReturnsTheDatasetTitle)
|
||||
{
|
||||
IptcKey key ("Iptc.Envelope.ModelVersion");
|
||||
ASSERT_EQ("Model Version", key.tagLabel());
|
||||
}
|
||||
|
||||
TEST(IptcKey, tag)
|
||||
{
|
||||
IptcKey key ("Iptc.Envelope.ModelVersion");
|
||||
ASSERT_EQ(IptcDataSets::ModelVersion, key.tag());
|
||||
}
|
||||
|
||||
TEST(IptcKey, record)
|
||||
{
|
||||
IptcKey key ("Iptc.Envelope.ModelVersion");
|
||||
ASSERT_EQ(IptcDataSets::envelope, key.record());
|
||||
}
|
||||
196
unitTests/test_bmpimage.cpp
Normal file
196
unitTests/test_bmpimage.cpp
Normal file
@ -0,0 +1,196 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <exiv2/bmpimage.hpp>
|
||||
|
||||
using namespace Exiv2;
|
||||
|
||||
TEST(BmpImage, canBeOpenedWithEmptyMemIo)
|
||||
{
|
||||
auto memIo = std::make_unique<MemIo>();
|
||||
ASSERT_NO_THROW(BmpImage bmp(std::move(memIo)));
|
||||
}
|
||||
|
||||
TEST(BmpImage, mimeTypeIsBmp)
|
||||
{
|
||||
auto memIo = std::make_unique<MemIo>();
|
||||
BmpImage bmp(std::move(memIo));
|
||||
|
||||
ASSERT_EQ("image/x-ms-bmp", bmp.mimeType());
|
||||
}
|
||||
|
||||
TEST(BmpImage, writeMetadataIsNotImplemented)
|
||||
{
|
||||
auto memIo = std::make_unique<MemIo>();
|
||||
BmpImage bmp(std::move(memIo));
|
||||
|
||||
try {
|
||||
bmp.writeMetadata();
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerWritingImageFormatUnsupported, e.code());
|
||||
ASSERT_STREQ("Writing to BMP images is not supported", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BmpImage, setExitDataIsNotImplemented)
|
||||
{
|
||||
auto memIo = std::make_unique<MemIo>();
|
||||
BmpImage bmp(std::move(memIo));
|
||||
|
||||
try {
|
||||
ExifData data;
|
||||
bmp.setExifData(data);
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerInvalidSettingForImage, e.code());
|
||||
ASSERT_STREQ("Setting Exif metadata in BMP images is not supported", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BmpImage, setIptcDataIsNotImplemented)
|
||||
{
|
||||
auto memIo = std::make_unique<MemIo>();
|
||||
BmpImage bmp(std::move(memIo));
|
||||
|
||||
try {
|
||||
IptcData data;
|
||||
bmp.setIptcData(data);
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerInvalidSettingForImage, e.code());
|
||||
ASSERT_STREQ("Setting IPTC metadata in BMP images is not supported", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BmpImage, setCommentIsNotImplemented)
|
||||
{
|
||||
auto memIo = std::make_unique<MemIo>();
|
||||
BmpImage bmp(std::move(memIo));
|
||||
|
||||
try {
|
||||
bmp.setComment("random comment");
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerInvalidSettingForImage, e.code());
|
||||
ASSERT_STREQ("Setting Image comment in BMP images is not supported", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BmpImage, readMetadataReadsImageDimensionsWhenDataIsAvailable)
|
||||
{
|
||||
const std::array<unsigned char, 26> header{
|
||||
'B', 'M', // Signature off:0 size:2
|
||||
0x4E, 0x47, 0x0D, 0x0A, // Size of the BMP file in bytes off:2, size:4
|
||||
0x1A, 0x0A, // Reserved off:6, size:2
|
||||
0x00, 0x00, // Reserved off:8, size:2
|
||||
0x00, 0x00, 0x00, 0x00, // Offset of the byte where the bitmap image data can be found off:10, size:4
|
||||
0x00, 0x00, 0x00, 0x00, // Size of this header off:14, size:4
|
||||
0x00, 0x05, 0x00, 0x00, // The bitmap width in pixels (unsigned 16 bit) off:18, size:4
|
||||
0x20, 0x03, 0x00, 0x00, // The bitmap height in pixels (unsigned 16 bit) off:22, size:4
|
||||
};
|
||||
|
||||
auto memIo = std::make_unique<MemIo>(header.data(), static_cast<long>(header.size()));
|
||||
BmpImage bmp(std::move(memIo));
|
||||
ASSERT_NO_THROW(bmp.readMetadata());
|
||||
ASSERT_EQ(1280, bmp.pixelWidth());
|
||||
ASSERT_EQ(800, bmp.pixelHeight());
|
||||
}
|
||||
|
||||
TEST(BmpImage, readMetadataThrowsWhenImageIsNotBMP)
|
||||
{
|
||||
const std::array<unsigned char, 26> header{
|
||||
'B', 'A', // Signature off:0 size:2
|
||||
0x4E, 0x47, 0x0D, 0x0A, // Size of the BMP file in bytes off:2, size:4
|
||||
0x1A, 0x0A, // Reserved off:6, size:2
|
||||
0x00, 0x00, // Reserved off:8, size:2
|
||||
0x00, 0x00, 0x00, 0x00, // Offset of the byte where the bitmap image data can be found off:10, size:4
|
||||
0x00, 0x00, 0x00, 0x00, // Size of this header off:14, size:4
|
||||
0x00, 0x05, 0x00, 0x00, // The bitmap width in pixels (unsigned 16 bit) off:18, size:4
|
||||
0x20, 0x03, 0x00, 0x00, // The bitmap height in pixels (unsigned 16 bit) off:22, size:4
|
||||
};
|
||||
|
||||
auto memIo = std::make_unique<MemIo>(header.data(), static_cast<long>(header.size()));
|
||||
BmpImage bmp(std::move(memIo));
|
||||
try {
|
||||
bmp.readMetadata();
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerNotAnImage, e.code());
|
||||
ASSERT_STREQ("This does not look like a BMP image", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BmpImage, readMetadataThrowsWhenThereIsNotEnoughInfoToRead)
|
||||
{
|
||||
const std::array<unsigned char, 1> header{'B'};
|
||||
auto memIo = std::make_unique<MemIo>(header.data(), static_cast<long>(header.size()));
|
||||
BmpImage bmp(std::move(memIo));
|
||||
try {
|
||||
bmp.readMetadata();
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerFailedToReadImageData, e.code());
|
||||
ASSERT_STREQ("Failed to read image data", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(BmpImage, readMetadataThrowsWhenIoCannotBeOpened)
|
||||
{
|
||||
auto fileIo = std::make_unique<FileIo>("NonExistingPath.png");
|
||||
BmpImage bmp(std::move(fileIo));
|
||||
try {
|
||||
bmp.readMetadata();
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerDataSourceOpenFailed, e.code());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(newBmpInstance, createsValidInstace)
|
||||
{
|
||||
const std::array<unsigned char, 14> bitmapHeader{
|
||||
'B', 'M', // Signature
|
||||
0x4E, 0x47, 0x0D, 0x0A, // Size of the BMP file in bytes
|
||||
0x1A, 0x0A, // Reserved
|
||||
0x00, 0x00, // Reserved
|
||||
0x00, 0x00, 0x00, 0x00 // Offset of the byte where the bitmap image data can be found
|
||||
};
|
||||
auto memIo = std::make_unique<MemIo>(bitmapHeader.data(), static_cast<long>(bitmapHeader.size()));
|
||||
auto img = newBmpInstance(std::move(memIo), false);
|
||||
ASSERT_TRUE(img->good());
|
||||
}
|
||||
|
||||
TEST(newBmpInstance, createsInvalidInstaceWithNonExistingFilePath)
|
||||
{
|
||||
auto fileIo = std::make_unique<FileIo>("NonExistingPath.png");
|
||||
auto img = newBmpInstance(std::move(fileIo), false);
|
||||
ASSERT_FALSE(img);
|
||||
}
|
||||
|
||||
TEST(isBmpType, withValidSignatureReturnsTrue)
|
||||
{
|
||||
const std::array<unsigned char, 14> bitmapHeader{
|
||||
'B', 'M', // Signature
|
||||
0x4E, 0x47, 0x0D, 0x0A, // Size of the BMP file in bytes
|
||||
0x1A, 0x0A, // Reserved
|
||||
0x00, 0x00, // Reserved
|
||||
0x00, 0x00, 0x00, 0x00 // Offset of the byte where the bitmap image data can be found
|
||||
};
|
||||
MemIo memIo(bitmapHeader.data(), static_cast<long>(bitmapHeader.size()));
|
||||
ASSERT_TRUE(isBmpType(memIo, false));
|
||||
}
|
||||
|
||||
TEST(isBmpType, withInvalidSignatureReturnsFalse)
|
||||
{
|
||||
const std::array<unsigned char, 14> bitmapHeader{
|
||||
'B', 'A', // Signature
|
||||
0x4E, 0x47, 0x0D, 0x0A, // Size of the BMP file in bytes
|
||||
0x1A, 0x0A, // Reserved
|
||||
0x00, 0x00, // Reserved
|
||||
0x00, 0x00, 0x00, 0x00 // Offset of the byte where the bitmap image data can be found
|
||||
};
|
||||
MemIo memIo(bitmapHeader.data(), static_cast<long>(bitmapHeader.size()));
|
||||
ASSERT_FALSE(isBmpType(memIo, false));
|
||||
}
|
||||
218
unitTests/test_datasets.cpp
Normal file
218
unitTests/test_datasets.cpp
Normal file
@ -0,0 +1,218 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <exiv2/datasets.hpp>
|
||||
#include <exiv2/error.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
|
||||
using namespace Exiv2;
|
||||
using ::testing::StartsWith;
|
||||
|
||||
TEST(IptcDataSets, dataSetName_returnsValidNamesWhenRequestingNumbersAvailableInEnvelopeRecord)
|
||||
{
|
||||
ASSERT_EQ("ModelVersion", IptcDataSets::dataSetName(IptcDataSets::ModelVersion, IptcDataSets::envelope));
|
||||
ASSERT_EQ("Destination", IptcDataSets::dataSetName(IptcDataSets::Destination, IptcDataSets::envelope));
|
||||
ASSERT_EQ("FileFormat", IptcDataSets::dataSetName(IptcDataSets::FileFormat, IptcDataSets::envelope));
|
||||
ASSERT_EQ("FileVersion", IptcDataSets::dataSetName(IptcDataSets::FileVersion, IptcDataSets::envelope));
|
||||
ASSERT_EQ("ServiceId", IptcDataSets::dataSetName(IptcDataSets::ServiceId, IptcDataSets::envelope));
|
||||
ASSERT_EQ("EnvelopeNumber", IptcDataSets::dataSetName(IptcDataSets::EnvelopeNumber, IptcDataSets::envelope));
|
||||
}
|
||||
|
||||
TEST(IptcDataSets, dataSetName_returnsValidNamesWhenRequestingNumbersAvailableInApplicationRecord)
|
||||
{
|
||||
ASSERT_EQ("ObjectType", IptcDataSets::dataSetName(IptcDataSets::ObjectType, IptcDataSets::application2));
|
||||
ASSERT_EQ("ObjectAttribute", IptcDataSets::dataSetName(IptcDataSets::ObjectAttribute, IptcDataSets::application2));
|
||||
}
|
||||
|
||||
TEST(IptcDataSets, dataSetName_returnsWrongNamesWhenRequestingNumbersNotAvailableInEnvelopeRecord)
|
||||
{
|
||||
ASSERT_EQ("0x0003", IptcDataSets::dataSetName(IptcDataSets::ObjectType, IptcDataSets::envelope));
|
||||
ASSERT_EQ("0x0004", IptcDataSets::dataSetName(IptcDataSets::ObjectAttribute, IptcDataSets::envelope));
|
||||
}
|
||||
|
||||
TEST(IptcDataSets, dataSetTitle_returnsValidTitleWhenRequestingNumbersAvailableInRecord)
|
||||
{
|
||||
ASSERT_STREQ("Model Version", IptcDataSets::dataSetTitle(IptcDataSets::ModelVersion, IptcDataSets::envelope));
|
||||
ASSERT_STREQ("Destination", IptcDataSets::dataSetTitle(IptcDataSets::Destination, IptcDataSets::envelope));
|
||||
ASSERT_STREQ("File Format", IptcDataSets::dataSetTitle(IptcDataSets::FileFormat, IptcDataSets::envelope));
|
||||
|
||||
ASSERT_STREQ("Object Type", IptcDataSets::dataSetTitle(IptcDataSets::ObjectType, IptcDataSets::application2));
|
||||
ASSERT_STREQ("Object Attribute",
|
||||
IptcDataSets::dataSetTitle(IptcDataSets::ObjectAttribute, IptcDataSets::application2));
|
||||
}
|
||||
|
||||
TEST(IptcDataSets, dataSetTitle_returnsUnknownStringWhenRequestingNumbersNotAvailableInEnvelopeRecord)
|
||||
{
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetTitle(IptcDataSets::ObjectType, IptcDataSets::envelope));
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetTitle(IptcDataSets::ObjectAttribute, IptcDataSets::envelope));
|
||||
}
|
||||
|
||||
// Unfortunately, some constants such as ModelVersion, Destination or FileFormat has the same values as other constants
|
||||
// available for other records (RecordVersion, ObjectName & SuppCategory respectively)
|
||||
|
||||
// TEST(IptcDataSets, dataSetTitle_returnsUnknownStringWhenRequestingNumbersNotAvailableInApplicationRecord)
|
||||
//{
|
||||
// ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetTitle(IptcDataSets::ModelVersion, IptcDataSets::application2));
|
||||
// ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetTitle(IptcDataSets::Destination, IptcDataSets::application2));
|
||||
// ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetTitle(IptcDataSets::FileFormat, IptcDataSets::application2));
|
||||
//}
|
||||
|
||||
TEST(IptcDataSets, dataSetTitle_returnsUnknownStringWhenRequestingNumbersNotAvailableInApplicationRecord)
|
||||
{
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetTitle(1, IptcDataSets::envelope));
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetTitle(2, IptcDataSets::envelope));
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
|
||||
TEST(IptcDataSets, dataSetDescription_returnsValidDescriptionWhenRequestingNumbersAvailableInRecord)
|
||||
{
|
||||
ASSERT_THAT(IptcDataSets::dataSetDesc(IptcDataSets::ModelVersion, IptcDataSets::envelope),
|
||||
StartsWith("A binary number identifying the version of the Information Interchange Model"));
|
||||
ASSERT_THAT(IptcDataSets::dataSetDesc(IptcDataSets::FileFormat, IptcDataSets::envelope),
|
||||
StartsWith("A binary number representing the file format. The file format must be registered with"));
|
||||
|
||||
ASSERT_THAT(IptcDataSets::dataSetDesc(IptcDataSets::RecordVersion, IptcDataSets::application2),
|
||||
StartsWith("A binary number identifying the version of the Information Interchange Model"));
|
||||
ASSERT_THAT(IptcDataSets::dataSetDesc(IptcDataSets::ObjectType, IptcDataSets::application2),
|
||||
StartsWith("The Object Type is used to distinguish between different types of objects within the IIM"));
|
||||
}
|
||||
|
||||
TEST(IptcDataSets, dataSetDescription_returnsUnknownStringWhenRequestingNumbersNotAvailableInRecord)
|
||||
{
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetDesc(1, IptcDataSets::envelope));
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetDesc(2, IptcDataSets::envelope));
|
||||
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetDesc(1, IptcDataSets::application2));
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetDesc(2, IptcDataSets::application2));
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
|
||||
TEST(IptcDataSets, dataSetPsName_returnsValidPsNameWhenRequestingNumbersAvailableInRecord)
|
||||
{
|
||||
ASSERT_STREQ("", IptcDataSets::dataSetPsName(IptcDataSets::FileFormat, IptcDataSets::envelope));
|
||||
|
||||
ASSERT_STREQ("Document Title", IptcDataSets::dataSetPsName(IptcDataSets::ObjectName, IptcDataSets::application2));
|
||||
ASSERT_STREQ("Urgency", IptcDataSets::dataSetPsName(IptcDataSets::Urgency, IptcDataSets::application2));
|
||||
}
|
||||
|
||||
TEST(IptcDataSets, dataSetPsName_returnsUnknownStringWhenRequestingNumbersNotAvailableInRecord)
|
||||
{
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetPsName(1, IptcDataSets::envelope));
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetPsName(2, IptcDataSets::envelope));
|
||||
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetPsName(1, IptcDataSets::application2));
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::dataSetPsName(2, IptcDataSets::application2));
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
|
||||
TEST(IptcDataSets, dataSetRepeatable_returnsExpectedValueNameWhenRequestingNumbersAvailableInRecord)
|
||||
{
|
||||
ASSERT_TRUE(IptcDataSets::dataSetRepeatable(IptcDataSets::Destination, IptcDataSets::envelope));
|
||||
ASSERT_FALSE(IptcDataSets::dataSetRepeatable(IptcDataSets::FileFormat, IptcDataSets::envelope));
|
||||
|
||||
ASSERT_FALSE(IptcDataSets::dataSetRepeatable(IptcDataSets::ObjectType, IptcDataSets::application2));
|
||||
ASSERT_TRUE(IptcDataSets::dataSetRepeatable(IptcDataSets::ObjectAttribute, IptcDataSets::application2));
|
||||
}
|
||||
|
||||
/// \todo check if we want to return true in this case or throw an exception ...
|
||||
TEST(IptcDataSets, dataSetRepeatable_returnsTrueWhenRequestingNumbersNotAvailableInRecord)
|
||||
{
|
||||
ASSERT_TRUE(IptcDataSets::dataSetRepeatable(1, IptcDataSets::envelope));
|
||||
ASSERT_TRUE(IptcDataSets::dataSetRepeatable(2, IptcDataSets::envelope));
|
||||
|
||||
ASSERT_TRUE(IptcDataSets::dataSetRepeatable(1, IptcDataSets::application2));
|
||||
ASSERT_TRUE(IptcDataSets::dataSetRepeatable(2, IptcDataSets::application2));
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
|
||||
TEST(IptcDataSets, dataSet_returnsExpectedValueWhenRequestingValidDatasetName)
|
||||
{
|
||||
ASSERT_EQ(IptcDataSets::ModelVersion, IptcDataSets::dataSet("ModelVersion", IptcDataSets::envelope));
|
||||
ASSERT_EQ(IptcDataSets::FileFormat, IptcDataSets::dataSet("FileFormat", IptcDataSets::envelope));
|
||||
|
||||
ASSERT_EQ(IptcDataSets::RecordVersion, IptcDataSets::dataSet("RecordVersion", IptcDataSets::application2));
|
||||
ASSERT_EQ(IptcDataSets::FixtureId, IptcDataSets::dataSet("FixtureId", IptcDataSets::application2));
|
||||
}
|
||||
|
||||
TEST(IptcDataSets, dataSet_throwWithNonExistingDatasetName)
|
||||
{
|
||||
try {
|
||||
IptcDataSets::dataSet("NonExistingName", IptcDataSets::envelope);
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerInvalidDataset, e.code());
|
||||
ASSERT_STREQ("Invalid dataset name 'NonExistingName'", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
/// \todo Weird error reporting here. It should indicate that the record specified does not exist
|
||||
TEST(IptcDataSets, dataSet_throwWithNonExistingRecordId)
|
||||
{
|
||||
try {
|
||||
IptcDataSets::dataSet("ModelVersion", 5);
|
||||
FAIL();
|
||||
} catch (const Exiv2::Error& e) {
|
||||
ASSERT_EQ(kerInvalidDataset, e.code());
|
||||
ASSERT_STREQ("Invalid dataset name 'ModelVersion'", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
|
||||
TEST(IptcDataSets, dataSetType_returnsExpectedTypeWhenRequestingValidDataset)
|
||||
{
|
||||
ASSERT_EQ(unsignedShort, IptcDataSets::dataSetType(IptcDataSets::ModelVersion, IptcDataSets::envelope));
|
||||
ASSERT_EQ(Exiv2::string, IptcDataSets::dataSetType(IptcDataSets::Destination, IptcDataSets::envelope));
|
||||
|
||||
ASSERT_EQ(unsignedShort, IptcDataSets::dataSetType(IptcDataSets::RecordVersion, IptcDataSets::application2));
|
||||
ASSERT_EQ(Exiv2::string, IptcDataSets::dataSetType(IptcDataSets::ObjectType, IptcDataSets::application2));
|
||||
}
|
||||
|
||||
/// \todo probably better to throw exception here?
|
||||
TEST(IptcDataSets, dataSetType_returnsStringTypeWhenRecordIdDoesNotExist)
|
||||
{
|
||||
ASSERT_EQ(Exiv2::string, IptcDataSets::dataSetType(1, 5));
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
|
||||
TEST(IptcDataSets, recordName_returnsExpectedNameWhenRequestingValidRecordId)
|
||||
{
|
||||
ASSERT_EQ("Envelope", IptcDataSets::recordName(IptcDataSets::envelope));
|
||||
ASSERT_EQ("Application2", IptcDataSets::recordName(IptcDataSets::application2));
|
||||
}
|
||||
|
||||
TEST(IptcDataSets, recordName_returnsHexStringWhenRecordIdDoesNotExist)
|
||||
{
|
||||
ASSERT_EQ("0x0000", IptcDataSets::recordName(0));
|
||||
ASSERT_EQ("0x0003", IptcDataSets::recordName(3));
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
|
||||
TEST(IptcDataSets, recordDesc_returnsExpectedDescriptionWhenRequestingValidRecordId)
|
||||
{
|
||||
ASSERT_STREQ("IIM envelope record", IptcDataSets::recordDesc(IptcDataSets::envelope));
|
||||
ASSERT_STREQ("IIM application record 2", IptcDataSets::recordDesc(IptcDataSets::application2));
|
||||
}
|
||||
|
||||
TEST(IptcDataSets, recordDesc_)
|
||||
{
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::recordDesc(0));
|
||||
ASSERT_STREQ("Unknown dataset", IptcDataSets::recordDesc(3));
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
|
||||
TEST(IptcDataSets, dataSetLists_printDatasetsIntoOstream)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
ASSERT_NO_THROW(IptcDataSets::dataSetList(stream));
|
||||
ASSERT_FALSE(stream.str().empty());
|
||||
}
|
||||
@ -158,6 +158,14 @@ TEST(PngImage, cannotWriteMetadataToEmptyIo)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PngImage, canWriteMetadataFromCreatedPngImage)
|
||||
{
|
||||
auto memIo = std::make_unique<MemIo>();
|
||||
const bool create {true};
|
||||
PngImage png(std::move(memIo), create);
|
||||
ASSERT_NO_THROW(png.writeMetadata());
|
||||
}
|
||||
|
||||
TEST(PngImage, cannotWriteMetadataToIoWhichCannotBeOpened)
|
||||
{
|
||||
auto memIo = std::make_unique<FileIo>("NonExistingPath.png");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user