/* -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; tab-width:4; -*- */
/*
* libopenraw - testsuite.cpp
*
* Copyright (C) 2008-2024 Hubert Figuière
* Copyright (C) 2008 Novell, Inc.
*
* 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
* .
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include // for boost::crc_basic, boost::crc_optimal
#define IN_TESTSUITE
#include
#include
#include
#include
#include "xmlhandler.h"
#include "testsuite.h"
#include "testsuitehandler.h"
#include "testsuitetags.h"
using std::unique_ptr;
ThumbnailDeleter td;
RawDataDeleter rd;
RawFileDeleter rfd;
template
std::string to_string(const std::vector &v)
{
std::string s = "[";
for (auto value: v) {
s += str(boost::format("%1%, ") % value);
}
s += "]";
return s;
}
#define RETURN_TEST_EQUALS(a,b) \
{ \
bool _success = (a == b); \
if(!_success) { \
fprintf(stderr, "FAILED: %s on equality. found '%s', " \
"expected '%s'\n", \
__FUNCTION__, a.c_str(), b.c_str()); \
} \
return _success; \
}
// Float comparison has nan != nan. Here we want nan == nan for the sake to
// testing.
static bool floats_equals(const std::vector & a, const std::vector & b)
{
if (a.size() != b.size()) {
return false;
}
for (size_t i = 0; i < a.size(); i++) {
if (a[i] != b[i] && !std::isnan(a[i]) && !std::isnan(b[i])) {
return false;
}
}
return true;
}
#define RETURN_TEST_EQUALS_OF(a,b) \
{ \
bool _success = floats_equals(a, b); \
if(!_success) { \
std::cerr << boost::format("FAILED: %1% on equality. found '%2%', expected '%3%'\n") \
% __FUNCTION__ % to_string(a) % to_string(b); \
} \
return _success; \
}
#define RETURN_TEST_EQUALS_O(a,b) \
{ \
bool _success = (a == b); \
if(!_success) { \
std::cerr << boost::format("FAILED: %1% on equality. found '%2%', expected '%3%'\n") \
% __FUNCTION__ % to_string(a) % to_string(b); \
} \
return _success; \
}
#define RETURN_TEST_EQUALS_N(a,b) \
{ \
bool _success = (a == b); \
if(!_success) { \
fprintf(stderr, "FAILED: %s on equality. found %ld, " \
"expected '%ld'\n", \
__FUNCTION__, (long)a, (long)b); \
} \
return _success; \
}
// a and b are strings. b is the expected value
// success is return. Set to true if it is successful
#define CHECK_TEST_EQUALS(a,b,success) \
{ \
success = (a == b); \
if(!success) { \
fprintf(stderr, "FAILED: %s on equality with '%s', expected '%s'\n", \
__FUNCTION__, a.c_str(), b.c_str()); \
} \
}
// a and b are integers. b is the expected value
// success is return. Set to true if it is successful
#define CHECK_TEST_EQUALS_N(a,b,success) \
{ \
success = (a == b); \
if(!success) { \
fprintf(stderr, "FAILED: %s on equality with %ld, expected %ld\n", \
__FUNCTION__, (long)a, (long)b); \
} \
}
#define RETURN_TEST(test,expected) \
{ \
bool _success = (test); \
if(!_success) { \
fprintf(stderr, "FAILED: %s on '%s', expected '%s'\n", \
__FUNCTION__, #test, expected.c_str()); \
} \
return _success; \
}
#define RETURN_FAIL(message) \
{ \
fprintf(stderr, "FAILED: %s with '%s'\n", \
__FUNCTION__, message); \
return false; \
}
namespace {
std::string cfaPatternToString(::or_cfa_pattern t)
{
switch(t) {
case OR_CFA_PATTERN_NONE:
return "NONE";
case OR_CFA_PATTERN_NON_RGB22:
return "NON_RGB22";
case OR_CFA_PATTERN_RGGB:
return "RGGB";
case OR_CFA_PATTERN_GBRG:
return "GBRG";
case OR_CFA_PATTERN_BGGR:
return "BGGR";
case OR_CFA_PATTERN_GRBG:
return "GRBG";
case _OR_CFA_PATTERN_INVALID:
return "INVALID";
}
return "";
}
bool equalDataType(const std::string & result, or_data_type t)
{
bool equal = false;
switch(t) {
case OR_DATA_TYPE_NONE:
equal = (result == "NONE");
break;
case OR_DATA_TYPE_UNKNOWN:
equal = (result == "UNKNOWN");
break;
case OR_DATA_TYPE_PIXMAP_8RGB:
equal = (result == "8RGB");
break;
case OR_DATA_TYPE_PIXMAP_16RGB:
equal = (result == "16RGB");
break;
case OR_DATA_TYPE_JPEG:
equal = (result == "JPEG");
break;
case OR_DATA_TYPE_TIFF:
equal = (result == "TIFF");
break;
case OR_DATA_TYPE_PNG:
equal = (result == "PNG");
break;
case OR_DATA_TYPE_RAW:
equal = (result == "RAW");
break;
case OR_DATA_TYPE_COMPRESSED_RAW:
equal = (result == "COMP_RAW");
break;
}
return equal;
}
}
Test::Test()
: m_download_disabled(false)
, m_total(0)
, m_success(0)
, m_failure(0)
{
}
Test::~Test()
{
}
bool Test::testRawType(const std::string & result)
{
or_rawfile_type t = or_rawfile_get_type(m_rawfile.get());
// test the detection by content....
or_rawfile_type t2;
FILE* f = fopen(m_file.c_str(), "rb");
if (!f) {
std::string message("failed to open ");
message += boost::lexical_cast(errno);
RETURN_FAIL(message.c_str());
}
fseek(f, 0, SEEK_END);
off_t len = ftell(f);
fseek(f, 0, SEEK_SET);
auto buff = std::make_unique(len);
int res = fread(buff.get(), 1, len, f);
fclose(f);
if (res == len) {
unique_ptr<_RawFile, RawFileDeleter> r2(or_rawfile_new_from_memory(buff.get(), len, OR_RAWFILE_TYPE_UNKNOWN), rfd);
if (!r2) {
RETURN_FAIL("failed to load from memory");
}
t2 = or_rawfile_get_type(r2.get());
if (t2 != t) {
RETURN_FAIL("type mismatch");
}
}
else {
std::string message("failed to load into buffer ");
message += boost::lexical_cast(res);
RETURN_FAIL(message.c_str());
}
switch(t) {
case OR_RAWFILE_TYPE_CR2:
RETURN_TEST(result == "CR2", result);
break;
case OR_RAWFILE_TYPE_CR3:
RETURN_TEST(result == "CR3", result);
break;
case OR_RAWFILE_TYPE_CRW:
RETURN_TEST(result == "CRW", result);
break;
case OR_RAWFILE_TYPE_NEF:
RETURN_TEST(result == "NEF", result);
break;
case OR_RAWFILE_TYPE_MRW:
RETURN_TEST(result == "MRW", result);
break;
case OR_RAWFILE_TYPE_ARW:
RETURN_TEST(result == "ARW", result);
break;
case OR_RAWFILE_TYPE_DNG:
RETURN_TEST(result == "DNG", result);
break;
case OR_RAWFILE_TYPE_ORF:
RETURN_TEST(result == "ORF", result);
break;
case OR_RAWFILE_TYPE_PEF:
RETURN_TEST(result == "PEF", result);
break;
case OR_RAWFILE_TYPE_ERF:
RETURN_TEST(result == "ERF", result);
break;
case OR_RAWFILE_TYPE_NRW:
RETURN_TEST(result == "NRW", result);
break;
case OR_RAWFILE_TYPE_RW2:
RETURN_TEST(result == "RW2", result);
break;
case OR_RAWFILE_TYPE_RAF:
RETURN_TEST(result == "RAF", result);
break;
case OR_RAWFILE_TYPE_GPR:
RETURN_TEST(result == "GPR", result);
break;
case OR_RAWFILE_TYPE_SR2:
RETURN_TEST(result == "SR2", result);
break;
case OR_RAWFILE_TYPE_TIFF:
RETURN_TEST(result == "TIFF", result);
break;
case OR_RAWFILE_TYPE_UNKNOWN:
RETURN_TEST(result == "UNKNOWN", result);
break;
}
RETURN_TEST(false, result);
}
bool Test::testRawTypeId(const std::string & result)
{
auto type_id = or_rawfile_get_typeid(m_rawfile.get());
auto vendor_id = or_rawfile_get_vendorid(m_rawfile.get());
bool test = false;
CHECK_TEST_EQUALS_N(OR_GET_FILE_TYPEID_VENDOR(type_id), vendor_id, test);
if (!test) {
return test;
}
CHECK_TEST_EQUALS_N(type_id, boost::lexical_cast(result), test);
return test;
}
bool Test::testThumbNum(const std::string & result)
{
size_t num = 0;
/*auto thumbs =*/ or_rawfile_get_thumbnail_sizes(m_rawfile.get(), &num);
try {
RETURN_TEST_EQUALS_N(num, boost::lexical_cast(result));
}
catch(...)
{
}
RETURN_FAIL("conversion failed");
}
bool Test::testThumbSizes(const std::string& result)
{
size_t num = 0;
auto thumbs = or_rawfile_get_thumbnail_sizes(m_rawfile.get(), &num);
std::vector v;
boost::split(v, result, boost::is_any_of(" "));
if (v.size() != num) {
RETURN_FAIL("mismatch number of elements");
}
std::vector v2;
for (const auto & s : v)
{
try {
v2.push_back(boost::lexical_cast(s));
}
catch(...)
{
RETURN_FAIL("conversion failed");
}
}
bool success = true;
for (size_t i = 0; i < num; i++) {
bool test = false;
CHECK_TEST_EQUALS_N(thumbs[i], v2[i], test);
if (!test) {
success = false;
}
}
return success;
}
bool Test::testThumbFormats(const std::string & result)
{
bool success = true;
size_t num = 0;
auto thumbs = or_rawfile_get_thumbnail_sizes(m_rawfile.get(), &num);
std::vector< std::string > v;
boost::split(v, result, boost::is_any_of(" "));
auto result_iter = v.cbegin();
if (v.size() != num) {
RETURN_FAIL("mismatch number of elements");
}
for (size_t i = 0; i < num; i++) {
unique_ptr<_Thumbnail, ThumbnailDeleter> t(or_rawfile_get_thumbnail(m_rawfile.get(), thumbs[i], nullptr), td);
success &= equalDataType(*result_iter, or_thumbnail_format(t.get()));
result_iter++;
}
RETURN_TEST(success, result);
}
bool Test::testThumbDataSizes(const std::string & result)
{
bool success = true;
size_t num = 0;
auto thumbs = or_rawfile_get_thumbnail_sizes(m_rawfile.get(), &num);
std::vector< std::string > v;
boost::split(v, result, boost::is_any_of(" "));
auto result_iter = v.cbegin();
if (v.size() != num) {
RETURN_FAIL("mismatch number of elements");
}
for (size_t i = 0; i < num; i++) {
unique_ptr<_Thumbnail, ThumbnailDeleter> t(or_rawfile_get_thumbnail(m_rawfile.get(), thumbs[i], nullptr), td);
try {
bool succ = false;
CHECK_TEST_EQUALS_N(or_thumbnail_data_size(t.get()), boost::lexical_cast(*result_iter), succ);
success &= succ;
result_iter++;
}
catch(...) {
RETURN_FAIL("conversion failed");
}
}
RETURN_TEST(success, result);
}
namespace {
uint32_t computeCrc(ORThumbnailRef thumb)
{
boost::crc_optimal<16, 0x1021, 0xFFFF, 0, false, false> crc_ccitt2;
const uint8_t * data = static_cast(or_thumbnail_data(thumb));
size_t data_len = or_thumbnail_data_size(thumb);
crc_ccitt2 = std::for_each(data, data + data_len, crc_ccitt2);
return crc_ccitt2();
}
}
bool Test::testThumbMd5(const std::string & result)
{
bool success = true;
size_t num = 0;
auto thumbs = or_rawfile_get_thumbnail_sizes(m_rawfile.get(), &num);
std::vector< std::string > v;
boost::split(v, result, boost::is_any_of(" "));
auto result_iter = v.cbegin();
if (v.size() != num) {
RETURN_FAIL("mismatch number of elements");
}
for (size_t i = 0; i < num; i++) {
unique_ptr<_Thumbnail, ThumbnailDeleter> t(or_rawfile_get_thumbnail(m_rawfile.get(), thumbs[i], nullptr), td);
try {
bool succ = false;
uint32_t crc = computeCrc(t.get());
CHECK_TEST_EQUALS(boost::lexical_cast(crc), (*result_iter), succ);
success &= succ;
result_iter++;
}
catch(...) {
RETURN_FAIL("conversion failed");
}
}
RETURN_TEST(success, result);
}
namespace {
unique_ptr<_RawData, RawDataDeleter> loadRawData(const unique_ptr<_RawFile, RawFileDeleter> & file)
{
unique_ptr<_RawData, RawDataDeleter> rawdata(or_rawfile_get_rawdata(file.get(), OR_OPTIONS_NONE, nullptr), rd);
return rawdata;
}
uint32_t computeCrc(_RawData* rawdata)
{
boost::crc_optimal<16, 0x1021, 0xFFFF, 0, false, false> crc_ccitt2;
auto data = static_cast(or_rawdata_data(rawdata));
size_t data_len = or_rawdata_data_size(rawdata);
crc_ccitt2 = std::for_each( data, data + data_len, crc_ccitt2 );
return crc_ccitt2();
}
}
bool Test::testRawDataType(const std::string & result)
{
if (!m_rawdata) {
m_rawdata = loadRawData(m_rawfile);
if (!m_rawdata) {
RETURN_FAIL("failed to get rawData");
}
}
RETURN_TEST(equalDataType(result, or_rawdata_format(m_rawdata.get())), result);
}
bool Test::testRawDataSize(const std::string & result)
{
if (!m_rawdata) {
m_rawdata = loadRawData(m_rawfile);
if (!m_rawdata) {
RETURN_FAIL("failed to get rawData");
}
}
try {
RETURN_TEST_EQUALS_N(or_rawdata_data_size(m_rawdata.get()), boost::lexical_cast(result));
}
catch(...) {
}
RETURN_FAIL("conversion failed");
}
bool Test::testRawDataDimensions(const std::string & result)
{
if (!m_rawdata) {
m_rawdata = loadRawData(m_rawfile);
if (!m_rawdata) {
RETURN_FAIL("failed to get rawData");
}
}
std::vector< std::string > v;
boost::split(v, result, boost::is_any_of(" "));
if(v.size() != 2) {
RETURN_FAIL("mismatch number of elements from expected result");
}
uint32_t x, y;
bool success = true;
try {
x = boost::lexical_cast(v[0]);
y = boost::lexical_cast(v[1]);
bool succ = false;
uint32_t rx, ry;
rx = ry = 0;
or_rawdata_dimensions(m_rawdata.get(), &rx, &ry);
CHECK_TEST_EQUALS_N(rx, x, succ);
success &= succ;
CHECK_TEST_EQUALS_N(ry, y, succ);
success &= succ;
}
catch(...)
{
RETURN_FAIL("conversion failed");
}
RETURN_TEST(success, result)
}
bool Test::testRawDataActiveArea(const std::string & result)
{
if(m_rawdata == NULL) {
m_rawdata = loadRawData(m_rawfile);
if(m_rawdata == NULL) {
RETURN_FAIL("failed to get rawData");
}
}
std::vector< std::string > v;
boost::split(v, result, boost::is_any_of(" "));
if(v.size() != 4) {
RETURN_FAIL("mismatch number of elements from expected result");
}
uint32_t x, y, w, h;
try {
x = boost::lexical_cast(v[0]);
y = boost::lexical_cast(v[1]);
w = boost::lexical_cast(v[2]);
h = boost::lexical_cast(v[3]);
}
catch(...)
{
RETURN_FAIL("conversion failed");
}
uint32_t rx, ry, rw, rh;
rx = ry = rw = rh = 0;
or_rawdata_get_active_area(m_rawdata.get(), &rx, &ry, &rw, &rh);
RETURN_TEST(x == rx && y == ry && w == rw && h == rh, result);
}
bool Test::testRawDataUserCrop(const std::string & result)
{
if(m_rawdata == NULL) {
m_rawdata = loadRawData(m_rawfile);
if(m_rawdata == NULL) {
RETURN_FAIL("failed to get rawData");
}
}
bool is_none = (result == "NONE");
uint32_t x, y, w, h;
if (!is_none) {
std::vector< std::string > v;
boost::split(v, result, boost::is_any_of(" "));
if(v.size() != 4) {
RETURN_FAIL("mismatch number of elements from expected result");
}
try {
x = boost::lexical_cast(v[0]);
y = boost::lexical_cast(v[1]);
w = boost::lexical_cast(v[2]);
h = boost::lexical_cast(v[3]);
}
catch(...)
{
RETURN_FAIL("conversion failed");
}
}
uint32_t rx, ry, rw, rh;
rx = ry = rw = rh = 0;
auto res = or_rawdata_get_user_crop(m_rawdata.get(), &rx, &ry, &rw, &rh);
if (is_none) {
RETURN_TEST(res == OR_ERROR_NOT_FOUND, result);
} else {
RETURN_TEST(x == rx && y == ry && w == rw && h == rh, result);
}
}
bool Test::testRawDataUserAspectRatio(const std::string & result)
{
if(m_rawdata == NULL) {
m_rawdata = loadRawData(m_rawfile);
if(m_rawdata == NULL) {
RETURN_FAIL("failed to get rawData");
}
}
bool is_none = (result == "NONE");
uint32_t w, h;
if (!is_none) {
std::vector< std::string > v;
boost::split(v, result, boost::is_any_of(" "));
if(v.size() != 2) {
RETURN_FAIL("mismatch number of elements from expected result");
}
try {
w = boost::lexical_cast(v[0]);
h = boost::lexical_cast(v[1]);
}
catch(...)
{
RETURN_FAIL("conversion failed");
}
}
uint32_t rw, rh;
rw = rh = 0;
auto res = or_rawdata_get_user_aspect_ratio(m_rawdata.get(), &rw, &rh);
if (is_none) {
RETURN_TEST(res == OR_ERROR_NOT_FOUND, result);
} else {
RETURN_TEST(w == rw && h == rh, result);
}
}
bool Test::testRawCfaPattern(const std::string & result)
{
if(m_rawdata == NULL) {
m_rawdata = loadRawData(m_rawfile);
if(m_rawdata == NULL) {
RETURN_FAIL("failed to get rawData");
}
}
bool succ = false;
CHECK_TEST_EQUALS(cfaPatternToString(or_rawdata_get_cfa_pattern_type(m_rawdata.get())),
result, succ);
return succ;
}
bool Test::testRawMinValue(const std::string & result)
{
if(m_rawdata == NULL) {
m_rawdata = loadRawData(m_rawfile);
if(m_rawdata == NULL) {
RETURN_FAIL("failed to get rawData");
}
}
std::vector v;
boost::split(v, result, boost::is_any_of(" "));
if (v.size() != 4) {
RETURN_FAIL("mismatch number of elements");
}
std::vector v2;
for (const auto & s : v)
{
try {
v2.push_back(boost::lexical_cast(s));
}
catch(...)
{
RETURN_FAIL("conversion failed");
}
}
std::vector blacks = { 0, 0, 0, 0 };
or_rawdata_levels(m_rawdata.get(), blacks.data(), nullptr);
RETURN_TEST_EQUALS_O(blacks, v2);
}
bool Test::testRawMaxValue(const std::string & result)
{
if(m_rawdata == NULL) {
m_rawdata = loadRawData(m_rawfile);
if(m_rawdata == NULL) {
RETURN_FAIL("failed to get rawData");
}
}
std::vector v;
boost::split(v, result, boost::is_any_of(" "));
if (v.size() != 4) {
RETURN_FAIL("mismatch number of elements");
}
std::vector v2;
for (const auto & s : v)
{
try {
v2.push_back(boost::lexical_cast(s));
}
catch(...)
{
RETURN_FAIL("conversion failed");
}
}
std::vector whites = { 0, 0, 0, 0 };
or_rawdata_levels(m_rawdata.get(), nullptr, whites.data());
RETURN_TEST_EQUALS_O(whites, v2);
}
bool Test::testRawAsShotNeutral(const std::string & result)
{
if (m_rawdata == NULL) {
m_rawdata = loadRawData(m_rawfile);
if (m_rawdata == NULL) {
RETURN_FAIL("failed to get rawData");
}
}
bool is_none = (result == "NONE");
std::vector v2;
if (!is_none) {
std::vector v;
boost::split(v, result, boost::is_any_of(" "));
if (v.size() != 4) {
RETURN_FAIL("mismatch number of elements");
}
for (const auto & s : v) {
try {
v2.push_back(boost::lexical_cast(s));
}
catch(...)
{
RETURN_FAIL("conversion failed");
}
}
}
std::vector wb = { 0, 0, 0, 0 };
auto res = or_rawdata_as_shot_neutral(m_rawdata.get(), wb.data());
if (is_none) {
RETURN_TEST(res == OR_ERROR_NOT_FOUND, result);
} else {
RETURN_TEST_EQUALS_OF(wb, v2);
}
}
bool Test::testRawMd5(const std::string & result)
{
if(m_rawdata == NULL) {
m_rawdata = loadRawData(m_rawfile);
if(m_rawdata == NULL) {
RETURN_FAIL("failed to get rawData");
}
}
uint32_t crc = computeCrc(m_rawdata.get());
uint32_t expected = 0;
try {
expected = boost::lexical_cast(result);
}
catch(...)
{
RETURN_FAIL("conversion failed");
}
RETURN_TEST_EQUALS_N(crc, expected);
}
bool Test::testRawDecompressedMd5(const std::string &)
{
if(m_rawdata == NULL) {
m_rawdata = loadRawData(m_rawfile);
if(m_rawdata == NULL) {
RETURN_FAIL("failed to get rawData");
}
}
return false;
}
bool Test::testMetaOrientation(const std::string & result)
{
int32_t orientation = or_rawfile_get_orientation(m_rawfile.get());
RETURN_TEST_EQUALS_N(orientation, boost::lexical_cast(result));
}
bool Test::testExifString(const char* meta_index, const std::string & result)
{
auto val = or_rawfile_get_metavalue(m_rawfile.get(), meta_index);
if (val) {
//
auto stringVal = std::string(or_metavalue_get_string(val));
or_metavalue_release(val);
RETURN_TEST_EQUALS(stringVal, result);
}
RETURN_FAIL("meta data not found");
}
bool Test::testMakerNoteCount(const std::string & result)
{
try {
auto ifd = or_rawfile_get_ifd(m_rawfile.get(), OR_IFD_MNOTE);
auto numTags = or_ifd_count_tags(ifd);
RETURN_TEST_EQUALS_N(numTags,
boost::lexical_cast(result));
}
catch(const std::bad_cast & e) {
RETURN_FAIL("not an IFD file");
}
catch(...) {
RETURN_FAIL("unknown exception");
}
}
bool Test::testMakerNoteId(const std::string & result)
{
auto ifd = or_rawfile_get_ifd(m_rawfile.get(), OR_IFD_MNOTE);
if (!ifd) {
RETURN_FAIL("no MakerNote found");
}
auto makernote_id = or_ifd_get_makernote_id(ifd);
if (!makernote_id) {
RETURN_FAIL("no MakeNote id");
}
RETURN_TEST_EQUALS(std::string(makernote_id), result);
}
/** run the test.
* @return the number of failures. 0 means success
*/
int Test::run()
{
// load rawfile
fprintf(stderr, "running test %s on file %s\n", m_name.c_str(),
m_file.c_str());
struct stat buf;
if(stat(m_file.c_str(), &buf) == -1) {
fprintf(stderr, "File not found, skipping. (%d)\n", errno);
return 0;
}
m_rawfile.reset(or_rawfile_new(m_file.c_str(), OR_RAWFILE_TYPE_UNKNOWN));
if(m_rawfile == NULL) {
RETURN_FAIL("m_rawfile == NULL");
}
for(const auto & elem : m_results) {
bool pass = false;
switch(elem.first)
{
case XML_rawType:
pass = testRawType(elem.second);
break;
case XML_rawTypeId:
pass = testRawTypeId(elem.second);
break;
case XML_thumbNum:
pass = testThumbNum(elem.second);
break;
case XML_thumbSizes:
pass = testThumbSizes(elem.second);
break;
case XML_thumbFormats:
pass = testThumbFormats(elem.second);
break;
case XML_thumbDataSizes:
pass = testThumbDataSizes(elem.second);
break;
case XML_thumbMd5:
pass = testThumbMd5(elem.second);
break;
case XML_rawDataType:
pass = testRawDataType(elem.second);
break;
case XML_rawDataSize:
pass = testRawDataSize(elem.second);
break;
case XML_rawDataDimensions:
pass = testRawDataDimensions(elem.second);
break;
case XML_rawDataActiveArea:
pass = testRawDataActiveArea(elem.second);
break;
case XML_rawDataUserCrop:
pass = testRawDataUserCrop(elem.second);
break;
case XML_rawDataUserAspectRatio:
pass = testRawDataUserAspectRatio(elem.second);
break;
case XML_rawCfaPattern:
pass = testRawCfaPattern(elem.second);
break;
case XML_rawMinValue:
pass = testRawMinValue(elem.second);
break;
case XML_rawMaxValue:
pass = testRawMaxValue(elem.second);
break;
case XML_rawAsShotNeutral:
pass = testRawAsShotNeutral(elem.second);
break;
case XML_rawMd5:
pass = testRawMd5(elem.second);
break;
case XML_rawDecompressedMd5:
pass = testRawDecompressedMd5(elem.second);
break;
case XML_metaOrientation:
pass = testMetaOrientation(elem.second);
break;
case XML_exifMake:
pass = testExifString("Exif.Image.Make", elem.second);
break;
case XML_exifModel:
pass = testExifString("Exif.Image.Model", elem.second);
break;
case XML_makerNoteCount:
pass = testMakerNoteCount(elem.second);
break;
case XML_makerNoteId:
pass = testMakerNoteId(elem.second);
break;
default:
pass = false;
break;
}
m_total++;
if(!pass) {
m_failure++;
}
else {
m_success++;
}
}
if (!m_to_run.empty()) {
fprintf(stderr, "Not all test have been run. Still to run: ");
for (int t : m_to_run) {
auto iter = testsuitetagnames.find(t);
if (iter != testsuitetagnames.end()) {
fprintf(stderr, "%s, ", iter->second.c_str());
} else {
fprintf(stderr, "%d, ", t);
}
}
fprintf(stderr, "\n");
}
fprintf(stderr, "total %d, success %d, failure %d\n", m_total,
m_success, m_failure);
return m_failure;
}
void Test::merge(const Test::Ptr & t)
{
// skip m_name
if(!t->m_file.empty()) {
m_file = t->m_file;
}
if(!t->m_source.empty()) {
m_source = t->m_source;
}
// the results for t invariably replace the
// existing on in this.
for(const auto & elem : t->m_results) {
m_results[elem.first] = elem.second;
}
}
TestSuite::TestSuite()
{
}
void TestSuite::add_test(Test::Ptr && t)
{
auto iter = m_tests.find(t->name());
if(iter == m_tests.end()) {
m_tests.insert(std::make_pair(t->name(), std::move(t)));
}
else {
iter->second->merge(t);
}
}
int TestSuite::load_tests(const char * testsuite_file)
{
xml::HandlerPtr handler(new TestSuiteHandler(testsuite_file, this));
bool has_data = false;
has_data = handler->process();
return !has_data;
}
int TestSuite::load_overrides(const std::string & overrides_file)
{
xml::HandlerPtr handler(new TestSuiteHandler(overrides_file, this));
handler->process();
return 0;
}
namespace {
#if HAVE_CURL
void set_file_override(xmlNode *test, const std::string & path)
{
xmlNode * childrens = test->children;
while(childrens) {
if(strcmp((const char*)(childrens->name), "file") == 0) {
xmlNodeSetContent(childrens, (const xmlChar*)path.c_str());
return;
}
childrens = childrens->next;
}
xmlNewTextChild(test, NULL, (const xmlChar*)"file",
(const xmlChar*)path.c_str());
}
int download(const std::string & source, const std::string& referer,
CURL* handle,
const std::string & download_dir, std::string & dest)
{
dest = "";
FILE *fp = NULL;
const char * s = source.c_str();
const char * n = strrchr(s, '/');
if(n) {
n++;
dest = download_dir + '/' + n;
}
if(!dest.empty()) {
struct stat f_stat;
if(stat(dest.c_str(), &f_stat) == -1) {
CURLcode error;
struct curl_slist* hlist = NULL;
std::cout << "Downloading " << source
<< " to " << dest << std::endl;
fp = fopen(dest.c_str(), "wb");
if(fp == NULL) {
std::cout << " File Error " << strerror(errno) << std::endl;
dest = "";
return -1;
}
curl_easy_setopt(handle, CURLOPT_WRITEDATA, fp);
if (!referer.empty()) {
std::string header("Referer: ");
header += referer;
hlist = curl_slist_append(hlist, header.c_str());
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, hlist);
std::cout << "Set HTTP header " << header << std::endl;
}
curl_easy_setopt(handle, CURLOPT_URL, source.c_str());
curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1L);
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L);
error = curl_easy_perform(handle);
if (hlist) {
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, nullptr);
curl_slist_free_all(hlist);
}
auto written = ftell(fp);
fclose(fp);
if (!error && written > 0) {
std::cout << " DONE\n";
} else {
if (error) {
std::cout << " CURL Error " << error << std::endl;
} else {
std::cout << " Empty file\n";
}
unlink(dest.c_str());
dest = "";
return -1;
}
}
else {
std::cout << dest << " exists." << std::endl;
}
}
return 0;
}
#endif
}
#if HAVE_CURL
void TestSuite::walk_tests(xmlNode * testsuite, CURL* handle,
const std::string & download_dir)
{
std::map overrides;
xmlNode *test = testsuite->children;
while(test) {
if((test->type == XML_ELEMENT_NODE)
&& (strcmp((const char*)(test->name), "test") == 0)) {
xmlNode * childrens = test->children;
while(childrens) {
if(strcmp((const char*)(childrens->name), "name") == 0) {
overrides.insert(std::make_pair((const char*)(childrens->name),
test));
}
childrens = childrens->next;
}
}
test = test->next;
}
for (const auto & elem : m_tests) {
std::string n = elem.first;
std::string dest;
if (elem.second->download_disabled()) {
fprintf(stderr, "Skipping source %s\n",
elem.second->source().c_str());
continue;
}
int ret = download(elem.second->source(), elem.second->referer(),
handle, download_dir, dest);
if (ret == 0 && !dest.empty()) {
xmlNode * test2 = NULL;
auto iter2 = overrides.find(n);
if (iter2 != overrides.cend()) {
test2 = iter2->second;
} else {
test2 = xmlNewNode(NULL, (const xmlChar*)"test");
xmlAddChild(testsuite, test2);
xmlNode *name_node = xmlNewTextChild(
test2, NULL, (const xmlChar*)"name",
(const xmlChar*)n.c_str());
xmlAddChild(test2, name_node);
}
set_file_override(test2, dest);
}
}
}
#endif
namespace {
#if HAVE_CURL
int curl_write_function(void *buffer, size_t size, size_t nmemb, void *stream)
{
FILE *fp = (FILE *) stream;
size_t w;
w = fwrite(buffer, size, nmemb, fp);
if (w < size * nmemb) {
}
else {
std::cout << ".";
}
return (int) w;
}
#endif
}
#if HAVE_CURL
int TestSuite::bootstrap(const std::string & overrides_file,
const std::string & download_dir)
{
xmlDocPtr doc;
CURL *handle;
handle = curl_easy_init();
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curl_write_function);
doc = xmlReadFile(overrides_file.c_str(), NULL, 0);
xmlNode *root_element;
if (doc) {
root_element = xmlDocGetRootElement(doc);
}
else {
doc = xmlNewDoc((const xmlChar *)"1.0");
root_element = xmlNewDocNode(doc,
NULL, (xmlChar*)"testsuite", NULL);
xmlDocSetRootElement(doc, root_element);
}
if(root_element->type == XML_ELEMENT_NODE) {
if(strcmp((const char*)(root_element->name), "testsuite") == 0) {
walk_tests(root_element, handle, download_dir);
}
}
xmlSaveFormatFile(overrides_file.c_str(), doc, XML_SAVE_FORMAT);
xmlFreeDoc(doc);
curl_easy_cleanup(handle);
return 0;
}
#else
int TestSuite::bootstrap(const std::string & /*overrides_file*/,
const std::string & /*download_dir*/)
{
return 1;
}
#endif
int TestSuite::run_all()
{
int failures = 0;
for(const auto & elem : m_tests) {
failures += elem.second->run();
}
return failures;
}
int main(int argc, char ** argv)
{
bool bootstrap = false;
std::string download_dir;
int opt;
while ((opt = getopt(argc, argv, "bd:")) != -1) {
switch(opt) {
case 'b':
#if HAVE_CURL
bootstrap = true;
#else
fprintf(stderr, "Bootstraping is disabled. Please rebuild "
"with CURL support. Quitting.\n");
return 1;
#endif
break;
case 'd':
if(optarg[0] != '/') {
#ifdef HAVE_GET_CURRENT_DIR_NAME
char * dir = get_current_dir_name();
#else
char * dir = (char *) malloc(PATH_MAX * sizeof(char));
getcwd(dir, PATH_MAX);
#endif
download_dir = dir;
download_dir += '/';
download_dir += optarg;
free(dir);
}
else {
download_dir = optarg;
}
break;
default:
break;
}
}
const char * srcdir = getenv("srcdir");
if(srcdir == NULL) {
srcdir = "./";
}
std::string testsuite_file;
if (argv[optind]) {
testsuite_file = argv[optind];
} else {
testsuite_file = srcdir;
testsuite_file += "/testsuite.xml";
}
or_debug_set_level(DEBUG2);
TestSuite testsuite;
testsuite.load_tests(testsuite_file.c_str());
std::string override_file = testsuite_file + ".overrides";
if(!bootstrap) {
testsuite.load_overrides(override_file);
return testsuite.run_all();
}
else {
testsuite.bootstrap(override_file, download_dir);
}
return 0;
}
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0))
c-basic-offset: 4
tab-width: 4
indent-tabs-mode:nil
fill-column:80
End:
*/