You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

393 lines
12 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
#include <gtest/gtest.h>
#include <cstdint>
#include <exiv2/exiv2.hpp>
#include "slice.hpp"
#include "types.hpp"
using namespace Exiv2;
template <typename T>
class slice;
/*!
* This namespace contains the helper-function get_test_data. It is intented
* to be used for test with the slice fixture: it returns the appropriate
* data to the constructor of slice. For (const) T==std::vector it returns the
Spelling (#2067) * spelling: accelerometer Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: address Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: anonymous Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: available Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: baby Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: buffer Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: build Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: builds Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: by inclusion Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: byte order Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: camera Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: command Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: consistency Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: constructor Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: continuous Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: continuously Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: correspond Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: decompression Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: dedicated Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: denominator Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: dependencies Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: detector Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: dlighting Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: dragging Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: duplicate Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: dynamic Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: empty Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: enable Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: endianness Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: examining Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: exceed Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: extension Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: fact Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: fails Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: fixed Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: fluorescent Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: focuspoint Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: focuspoints Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: from Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: happened Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: hierarchy Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: initiate Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: interesting Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: internal Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: javascript Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: language Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: libiconv Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: libraries Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: library Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: localization Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: macros Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: member Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: members Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: metadata Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: miscellaneous Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: mnemonic Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: modified Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: multiple Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: multithreaded Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: mysterious Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: photoshop Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: platforms Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: preliminary Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: priority Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: properties Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: qualifier Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: recognition Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: recognized Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: reentrancy Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: remaining Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: remotetest Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: requested Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: sensitivity Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: standardize Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: successful Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: such Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: tags Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: temperature Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: test Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: the Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: these Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: third Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: this Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: transparent Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: unable Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: underwater Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: uninterpreted Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: using Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: utc - coordinated universal time Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: which Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: with Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> Co-authored-by: Josh Soref <jsoref@users.noreply.github.com>
3 years ago
* fixtures member vec_, for (const) T==int* it returns vec_.data()
*
* Due to C++98's limitations, this requires a separate traits class, that
* specifies the return type *and* a specialization of get_test_data for each
* case (maybe some can be reduced with SFINAE, but that ain't improving
* readability either).
*
* Unfortunately, C++11 will probably only make the return_type_traits go away,
* but not the template specializations of get_test_data (for that we need
* C++17, so see you in 2025).
*/
namespace cpp_98_boilerplate {
template <typename T>
struct return_type_traits {
using type = T;
};
template <typename U>
struct return_type_traits<std::vector<U>> {
using type = typename std::vector<U>&;
};
template <typename U>
struct return_type_traits<const std::vector<U>> {
using type = const typename std::vector<U>&;
};
template <typename T>
typename return_type_traits<T>::type get_test_data(slice<T>& st);
} // namespace cpp_98_boilerplate
/*!
* Fixture for slice testing. Has one public vector of ints with size vec_size
* that is filled with the numbers from 0 to vec_size - 1.
*
* The vector vec_ is used to construct slices either from a std::vector, or
* from raw C-arrays. Which type is used, is set by the template parameter
* T. Thus we guarantee, that the interface is completely independent of the
* underlying datatype.
*
* @tparam T Type that is used to construct a slice for testing.
*/
template <typename T>
class slice : public ::testing::Test {
public:
static const size_t vec_size = 10;
void SetUp() override {
vec_.reserve(vec_size);
for (unsigned int i = 0; i < vec_size; ++i) {
vec_.push_back(i);
}
}
Slice<T> getTestSlice(size_t begin = 1, size_t end = vec_size - 1) {
return Slice<T>(cpp_98_boilerplate::get_test_data<T>(*this), begin, end);
}
// TODO: once we have C++11: use initializer list
std::vector<int> vec_;
};
// specializations of get_test_data are provided here, since they must have the
// full definition of slice available
namespace cpp_98_boilerplate {
template <>
int* get_test_data<int*>(slice<int*>& st) {
return st.vec_.data();
}
template <>
const int* get_test_data<const int*>(slice<const int*>& st) {
return st.vec_.data();
}
template <>
std::vector<int>& get_test_data<std::vector<int>>(slice<std::vector<int>>& st) {
return st.vec_;
}
template <>
const std::vector<int>& get_test_data<const std::vector<int>>(slice<const std::vector<int>>& st) {
return st.vec_;
}
} // namespace cpp_98_boilerplate
/*!
* Fixture to run test for mutable slices.
*
* It adds nothing new, it is just a separate class, so that we can run
* different tests on it.
*/
template <typename T>
class mutableSlice : public slice<T> {};
TYPED_TEST_SUITE_P(slice);
TYPED_TEST_SUITE_P(mutableSlice);
TYPED_TEST_P(slice, atAccess) {
// typedef Slice<TypeParam> slice_t;
// const size_t begin = 1;
// const size_t end = this->vec_.size() - 1;
Slice<TypeParam> sl = this->getTestSlice();
ASSERT_EQ(this->vec_.size() - 2, sl.size());
for (unsigned int i = 0; i < sl.size(); ++i) {
ASSERT_EQ(this->vec_.at(i + 1), sl.at(i));
}
}
// TODO C++11: test range based for loop
TYPED_TEST_P(slice, iteratorAccess) {
Slice<TypeParam> sl = this->getTestSlice();
auto vec_it = this->vec_.begin() + 1;
for (auto it = sl.cbegin(); it < sl.cend(); ++it, ++vec_it) {
ASSERT_EQ(*it, *vec_it);
}
ASSERT_THROW(sl.at(sl.size()), std::out_of_range);
}
TYPED_TEST_P(slice, constructionFailsFromInvalidRange) {
// start > end
ASSERT_THROW(this->getTestSlice(2, 1), std::out_of_range);
}
TYPED_TEST_P(slice, constructionFailsWithZeroLength) {
ASSERT_THROW(this->getTestSlice(1, 1), std::out_of_range);
}
/*!
* Test the construction of subSlices and their behavior.
*/
TYPED_TEST_P(slice, subSliceSuccessfulConstruction) {
using slice_t = Slice<TypeParam>;
// 0 1 2 3 4 5 6 7 8 9
// | | center_vals
// | | middle
slice_t center_vals = this->getTestSlice(3, 7);
ASSERT_EQ(center_vals.size(), static_cast<size_t>(4));
ASSERT_NO_THROW(center_vals.subSlice(1, 3));
ASSERT_NO_THROW(center_vals.subSlice(1, center_vals.size()));
}
TYPED_TEST_P(slice, subSliceFunctions) {
Slice<TypeParam> middle = this->getTestSlice(3, 7).subSlice(1, 3);
ASSERT_EQ(middle.size(), static_cast<size_t>(2));
ASSERT_EQ(middle.at(1), static_cast<typename Slice<TypeParam>::value_type>(5));
}
TYPED_TEST_P(slice, subSliceFailedConstruction) {
// 0 1 2 3 4 5 6 7 8 9
// | | middle
Slice<TypeParam> middle = this->getTestSlice(4, 6);
ASSERT_THROW(middle.subSlice(1, 5), std::out_of_range);
ASSERT_THROW(middle.subSlice(2, 1), std::out_of_range);
ASSERT_THROW(middle.subSlice(2, 2), std::out_of_range);
}
/*! try to cause integer overflows in a sub-optimal implementation */
TYPED_TEST_P(slice, subSliceConstructionOverflowResistance) {
Slice<TypeParam> center_vals = this->getTestSlice(3, 7);
ASSERT_THROW(center_vals.subSlice(std::numeric_limits<size_t>::max() - 2, 3), std::out_of_range);
ASSERT_THROW(center_vals.subSlice(2, std::numeric_limits<size_t>::max() - 1), std::out_of_range);
}
/*!
* This function's purpose is only to check whether we can pass all slices by
* constant reference.
*/
template <typename T>
void checkConstSliceValueAt(const Slice<T>& sl, typename Slice<T>::value_type value, size_t index) {
ASSERT_EQ(sl.at(index), value);
}
/*!
* Check that the contents of the slice are ascending via an iterator based for
* loop.
*/
template <typename T>
void checkConstSliceIterator(const Slice<T>& sl, typename Slice<T>::value_type first_value) {
for (auto it = sl.cbegin(); it < sl.cend(); ++it) {
ASSERT_EQ(*it, first_value++);
}
}
template <typename T>
void checkSubSlice(const Slice<T>& sl) {
ASSERT_EQ(sl.at(1), sl.subSlice(1, sl.size()).at(0));
}
/*!
* Test that all slices can be also passed as const references and still work
*/
TYPED_TEST_P(slice, constMethodsPreserveConst) {
using slice_t = Slice<TypeParam>;
// 0 1 2 3 4 5 6 7 8 9
// | | center_vals
slice_t center_vals = this->getTestSlice(3, 7);
// check at() const works
checkConstSliceValueAt(center_vals, 4, 1);
checkConstSliceIterator(center_vals, 3);
checkSubSlice(center_vals);
}
/*!
* Test the non-const iterators
*/
TYPED_TEST_P(mutableSlice, iterators) {
using slice_t = Slice<TypeParam>;
slice_t sl = this->getTestSlice();
ASSERT_EQ(*sl.begin(), static_cast<typename slice_t::value_type>(1));
ASSERT_EQ(*sl.end(), static_cast<typename slice_t::value_type>(this->vec_size - 1));
for (auto it = sl.begin(); it < sl.end(); ++it) {
*it = 2 * (*it);
}
ASSERT_EQ(this->vec_.at(0), 0);
for (size_t j = 1; j < this->vec_size - 1; ++j) {
ASSERT_EQ(this->vec_.at(j), static_cast<typename slice_t::value_type>(2 * j));
ASSERT_EQ(this->vec_.at(j), sl.at(j - 1));
}
ASSERT_EQ(this->vec_.at(this->vec_size - 1), static_cast<typename slice_t::value_type>(this->vec_size - 1));
}
/*!
* Test the non-const version of at()
*/
TYPED_TEST_P(mutableSlice, at) {
using slice_t = Slice<TypeParam>;
slice_t sl = this->getTestSlice(2, 4);
sl.at(0) = 6;
sl.at(1) = 12;
ASSERT_EQ(this->vec_.at(2), 6);
ASSERT_EQ(this->vec_.at(3), 12);
for (size_t j = 0; j < this->vec_size - 1; ++j) {
if (j == 2 || j == 3) {
continue;
}
ASSERT_EQ(this->vec_.at(j), static_cast<typename slice_t::value_type>(j));
}
}
TEST(pointerSlice, failedConstructionFromNullpointer) {
ASSERT_THROW(Slice<long*>(nullptr, 1, 2), std::invalid_argument);
}
/*!
* Test the construction of an invalid slices from a container (so that a proper
* range check can be conducted)
*/
TEST(containerSlice, failedConstructionFromContainer) {
std::vector<int> tmp(10);
// slice end too large
ASSERT_THROW(Slice<std::vector<int>>(tmp, 1, tmp.size() + 1), std::out_of_range);
}
/*!
* Test all functions from the makeSlice* family.
*/
TEST(containerSlice, makeSlice) {
std::string str = "this is a sentence";
Slice<std::string> is = makeSlice(str, 5, 7);
ASSERT_TRUE(std::equal(is.begin(), is.end(), "is"));
Slice<std::string> sl_this = makeSliceUntil(str, 4);
ASSERT_TRUE(std::equal(sl_this.begin(), sl_this.end(), "this"));
Slice<std::string> sl_sentence = makeSliceFrom(str, 10);
ASSERT_TRUE(std::equal(sl_sentence.begin(), sl_sentence.end(), "sentence"));
Slice<std::string> sl_full = makeSlice(str);
ASSERT_TRUE(std::equal(sl_full.begin(), sl_full.end(), str.c_str()));
}
struct stringSlice : public ::testing::Test {
std::string sentence;
void SetUp() override {
sentence = "this is a sentence";
}
};
TEST_F(stringSlice, at) {
const Slice<const std::string> is_a = makeSlice(static_cast<const std::string&>(this->sentence), 5, 10);
ASSERT_EQ(is_a.at(0), 'i');
ASSERT_EQ(is_a.at(4), ' ');
}
TEST_F(stringSlice, atFailure) {
const Slice<const std::string> is_a = makeSlice(static_cast<const std::string&>(this->sentence), 5, 10);
ASSERT_THROW(is_a.at(5), std::out_of_range);
}
TEST_F(stringSlice, size) {
const Slice<const std::string> is_a = makeSlice(static_cast<const std::string&>(this->sentence), 5, 10);
ASSERT_EQ(is_a.size(), static_cast<size_t>(5));
}
TEST_F(stringSlice, mutateString) {
Slice<std::string> is_a_mutable = makeSlice(this->sentence, 5, 10);
for (auto it = is_a_mutable.begin(); it < is_a_mutable.end(); ++it) {
*it = ' ';
}
ASSERT_STREQ(this->sentence.c_str(), "this sentence");
}
template <typename T>
struct dataBufSlice : public ::testing::Test {
static byte data[4]; // = {0xde, 0xad, 0xbe, 0xef};
DataBuf buf;
void SetUp() override {
buf = DataBuf(data, sizeof(data));
}
};
template <typename T>
byte dataBufSlice<T>::data[4] = {0xde, 0xad, 0xbe, 0xef};
TYPED_TEST_SUITE_P(dataBufSlice);
TYPED_TEST_P(dataBufSlice, successfulConstruction) {
// just check that makeSlice appears to work
ASSERT_EQ(makeSlice(static_cast<TypeParam>(this->buf), 1, 3).size(), static_cast<size_t>(2));
}
TYPED_TEST_P(dataBufSlice, failedConstruction) {
// check that we get an exception when end is larger than LONG_MAX
ASSERT_THROW(
makeSlice(static_cast<TypeParam>(this->buf), 1, static_cast<size_t>(std::numeric_limits<long>::max()) + 1),
std::invalid_argument);
// check that we get an exception when end is larger than the DataBuf
ASSERT_THROW(makeSlice(static_cast<TypeParam>(this->buf), 1, 5), std::out_of_range);
}
//
// GTest boilerplate to get the tests running for all the different types
//
REGISTER_TYPED_TEST_SUITE_P(slice, atAccess, iteratorAccess, constructionFailsFromInvalidRange,
constructionFailsWithZeroLength, subSliceSuccessfulConstruction, subSliceFunctions,
subSliceFailedConstruction, subSliceConstructionOverflowResistance,
constMethodsPreserveConst);
using test_types_t = ::testing::Types<const std::vector<int>, std::vector<int>, int*, const int*>;
INSTANTIATE_TYPED_TEST_SUITE_P(slice, slice, test_types_t);
REGISTER_TYPED_TEST_SUITE_P(mutableSlice, iterators, at);
using mut_test_types_t = ::testing::Types<std::vector<int>, int*>;
INSTANTIATE_TYPED_TEST_SUITE_P(slice, mutableSlice, mut_test_types_t);
REGISTER_TYPED_TEST_SUITE_P(dataBufSlice, successfulConstruction, failedConstruction);
using data_buf_types_t = ::testing::Types<DataBuf&, const DataBuf&>;
INSTANTIATE_TYPED_TEST_SUITE_P(slice, dataBufSlice, data_buf_types_t);