This commit is contained in:
Raoul Wols 2018-02-08 07:57:53 +00:00 committed by GitHub
commit 09246db439
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 686 additions and 81 deletions

View File

@ -8,10 +8,19 @@
#endif
#include <array>
#include <bitset>
#include <cassert>
#include <deque>
#include <forward_list>
#include <limits>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "yaml-cpp/binary.h"
@ -76,7 +85,7 @@ struct convert<const char*> {
template <std::size_t N>
struct convert<const char[N]> {
static Node encode(const char(&rhs)[N]) { return Node(rhs); }
static Node encode(const char (&rhs)[N]) { return Node(rhs); }
};
template <>
@ -161,83 +170,161 @@ struct convert<bool> {
YAML_CPP_API static bool decode(const Node& node, bool& rhs);
};
// std::map
template <typename K, typename V>
struct convert<std::map<K, V>> {
static Node encode(const std::map<K, V>& rhs) {
// Helper class for converting something which is "sequence-like" to and from a
// Node.
template <class T>
struct convert_as_sequence {
static Node encode(const T& sequence) {
Node node(NodeType::Sequence);
for (const auto& item : sequence) {
node.push_back(item);
}
return node;
}
static bool decode(const Node& node, T& sequence) {
if (!node.IsSequence())
return false;
sequence.clear();
for (const auto& item : node) {
sequence.push_back(item.template as<typename T::value_type>());
}
return true;
}
};
// Helper class for converting something which is "map-like" to and from a Node.
template <class T, class Key = typename T::key_type,
class Value = typename T::mapped_type>
struct convert_as_map {
static Node encode(const T& map) {
Node node(NodeType::Map);
for (typename std::map<K, V>::const_iterator it = rhs.begin();
it != rhs.end(); ++it)
node.force_insert(it->first, it->second);
for (const auto& kv : map) {
node.force_insert(kv.first, kv.second);
}
return node;
}
static bool decode(const Node& node, std::map<K, V>& rhs) {
static bool decode(const Node& node, T& map) {
if (!node.IsMap())
return false;
map.clear();
for (const auto& kv : node) {
// if we're dealing with GCC (note: clang also defines __GNUC__)
#if defined(__GNUC__)
#if __GNUC__ < 4
// gcc version < 4
map.insert(std::make_pair(kv.first.template as<Key>(),
kv.second.template as<Value>()));
#elif __GNUC__ == 4 && __GNUC_MINOR__ < 8
// 4.0 <= gcc version < 4.8
map.insert(std::make_pair(kv.first.as<Key>(), kv.second.as<Value>()));
#else
// 4.8 <= gcc version
map.emplace(kv.first.as<Key>(), kv.second.as<Value>());
#endif // __GNUC__ < 4
#else
// for anything not GCC or clang...
// probably some more #ifdef guards are needed for MSVC.
map.emplace(kv.first.as<Key>(), kv.second.as<Value>());
#endif // defined(__GNUC__)
}
return true;
}
};
// Helper class for converting something which is "set-like" to and from a Node.
// A set is realized as a yaml sequence.
template <class T>
struct convert_as_set {
static Node encode(const T& set) {
Node node(NodeType::Sequence);
for (const auto& item : set) {
node.push_back(item);
}
return node;
}
static bool decode(const Node& node, T& set) {
if (!node.IsSequence())
return false;
set.clear();
for (const auto& item : node) {
rhs.clear();
for (const_iterator it = node.begin(); it != node.end(); ++it)
#if defined(__GNUC__) && __GNUC__ < 4
// workaround for GCC 3:
rhs[it->first.template as<K>()] = it->second.template as<V>();
#else
rhs[it->first.as<K>()] = it->second.as<V>();
set.insert(item.template as<typename T::value_type>());
#else // __GNUC__ is not defined or __GNUC__ >= 4
set.insert(item.as<typename T::value_type>());
#endif
}
return true;
}
};
// std::vector
template <typename T>
struct convert<std::vector<T>> {
static Node encode(const std::vector<T>& rhs) {
Node node(NodeType::Sequence);
for (typename std::vector<T>::const_iterator it = rhs.begin();
it != rhs.end(); ++it)
node.push_back(*it);
return node;
}
template <typename T, class Alloc>
struct convert<std::vector<T, Alloc>>
: public convert_as_sequence<std::vector<T, Alloc>> {};
static bool decode(const Node& node, std::vector<T>& rhs) {
if (!node.IsSequence())
return false;
rhs.clear();
for (const_iterator it = node.begin(); it != node.end(); ++it)
#if defined(__GNUC__) && __GNUC__ < 4
// workaround for GCC 3:
rhs.push_back(it->template as<T>());
#else
rhs.push_back(it->as<T>());
#endif
return true;
}
};
// std::deque
template <class T, class Alloc>
struct convert<std::deque<T, Alloc>>
: public convert_as_sequence<std::deque<T, Alloc>> {};
// std::list
template <typename T>
struct convert<std::list<T>> {
static Node encode(const std::list<T>& rhs) {
template <typename T, class Alloc>
struct convert<std::list<T, Alloc>>
: public convert_as_sequence<std::list<T, Alloc>> {};
// std::map
template <class Key, class T, class Compare, class Alloc>
struct convert<std::map<Key, T, Compare, Alloc>>
: public convert_as_map<std::map<Key, T, Compare, Alloc>> {};
// std::unordered_map
template <class Key, class T, class Hash, class KeyEqual, class Alloc>
struct convert<std::unordered_map<Key, T, Hash, KeyEqual, Alloc>>
: public convert_as_map<std::unordered_map<Key, T, Hash, KeyEqual, Alloc>> {
};
// std::set
template <class Key, class Compare, class Alloc>
struct convert<std::set<Key, Compare, Alloc>>
: public convert_as_set<std::set<Key, Compare, Alloc>> {};
// std::unordered_set
template <class Key, class Hash, class KeyEqual, class Alloc>
struct convert<std::unordered_set<Key, Hash, KeyEqual, Alloc>>
: public convert_as_set<std::unordered_set<Key, Hash, KeyEqual, Alloc>> {};
// std::forward_list
template <class T, class Alloc>
struct convert<std::forward_list<T, Alloc>> {
static Node encode(const std::forward_list<T, Alloc>& sequence) {
Node node(NodeType::Sequence);
for (typename std::list<T>::const_iterator it = rhs.begin();
it != rhs.end(); ++it)
node.push_back(*it);
for (const auto& item : sequence) {
node.push_back(item);
}
return node;
}
static bool decode(const Node& node, std::list<T>& rhs) {
static bool decode(const Node& node, std::forward_list<T, Alloc>& sequence) {
if (!node.IsSequence())
return false;
rhs.clear();
for (const_iterator it = node.begin(); it != node.end(); ++it)
sequence.clear();
// Walk the node backwards, because std::forward_list does not have
// push_back, only push_front.
for (std::size_t i = node.size() - 1; i != (std::size_t)-1; --i) {
#if defined(__GNUC__) && __GNUC__ < 4
// workaround for GCC 3:
rhs.push_back(it->template as<T>());
sequence.push_front(node[i].template as<T>());
#else
rhs.push_back(it->as<T>());
sequence.push_front(node[i].as<T>());
#endif
}
return true;
}
};
@ -245,15 +332,15 @@ struct convert<std::list<T>> {
// std::array
template <typename T, std::size_t N>
struct convert<std::array<T, N>> {
static Node encode(const std::array<T, N>& rhs) {
static Node encode(const std::array<T, N>& sequence) {
Node node(NodeType::Sequence);
for (const auto& element : rhs) {
node.push_back(element);
for (const auto& item : sequence) {
node.push_back(item);
}
return node;
}
static bool decode(const Node& node, std::array<T, N>& rhs) {
static bool decode(const Node& node, std::array<T, N>& sequence) {
if (!isNodeValid(node)) {
return false;
}
@ -261,9 +348,9 @@ struct convert<std::array<T, N>> {
for (auto i = 0u; i < node.size(); ++i) {
#if defined(__GNUC__) && __GNUC__ < 4
// workaround for GCC 3:
rhs[i] = node[i].template as<T>();
sequence[i] = node[i].template as<T>();
#else
rhs[i] = node[i].as<T>();
sequence[i] = node[i].as<T>();
#endif
}
return true;
@ -275,34 +362,109 @@ struct convert<std::array<T, N>> {
}
};
// std::pair
template <typename T, typename U>
struct convert<std::pair<T, U>> {
static Node encode(const std::pair<T, U>& rhs) {
// std::bitset
template <std::size_t N>
struct convert<std::bitset<N>> {
using value_type = std::bitset<N>;
static Node encode(const value_type& rhs) {
return convert<std::string>::encode(rhs.to_string());
}
static bool decode(const Node& node, value_type& rhs) {
std::string representation;
if (!convert<std::string>::decode(node, representation))
return false;
if (representation.size() != N)
return false;
try {
// bitset constructor will throw std:invalid_argument if the decoded
// string contains characters other than 0 and 1.
rhs = value_type(representation);
} catch (const std::invalid_argument& /*error*/) {
return false;
}
return true;
}
};
namespace detail {
template <std::size_t Index = 0, typename... Args>
inline typename std::enable_if<Index == sizeof...(Args), void>::type
encode_tuple(Node& /*node*/, const std::tuple<Args...>& /*tup*/) {}
template <std::size_t Index = 0, typename... Args>
inline typename std::enable_if<Index != sizeof...(Args), void>::type
encode_tuple(Node& node, const std::tuple<Args...>& tup) {
node.push_back(std::get<Index>(tup));
encode_tuple<Index + 1, Args...>(node, tup);
}
template <std::size_t Index = 0, typename... Args>
inline typename std::enable_if<Index == sizeof...(Args), void>::type
decode_tuple(const Node& /*node*/, std::tuple<Args...>& /*tup*/) {}
template <std::size_t Index = 0, typename... Args>
inline typename std::enable_if<Index != sizeof...(Args), void>::type
decode_tuple(const Node& node, std::tuple<Args...>& tup) {
std::get<Index>(tup) =
node[Index]
.template as<
typename std::tuple_element<Index, std::tuple<Args...>>::type>();
decode_tuple<Index + 1, Args...>(node, tup);
}
} // namespace detail
// std::tuple
template <typename... Args>
struct convert<std::tuple<Args...>> {
static Node encode(const std::tuple<Args...>& tup) {
static_assert(sizeof...(Args) > 0,
"wrong template specialization selected");
Node node(NodeType::Sequence);
node.push_back(rhs.first);
node.push_back(rhs.second);
detail::encode_tuple(node, tup);
return node;
}
static bool decode(const Node& node, std::pair<T, U>& rhs) {
if (!node.IsSequence())
return false;
if (node.size() != 2)
static bool decode(const Node& node, std::tuple<Args...>& tup) {
static_assert(sizeof...(Args) > 0,
"wrong template specialization selected");
if (!node.IsSequence() || node.size() != sizeof...(Args))
return false;
detail::decode_tuple(node, tup);
return true;
}
};
#if defined(__GNUC__) && __GNUC__ < 4
// workaround for GCC 3:
rhs.first = node[0].template as<T>();
#else
rhs.first = node[0].as<T>();
#endif
#if defined(__GNUC__) && __GNUC__ < 4
// workaround for GCC 3:
rhs.second = node[1].template as<U>();
#else
rhs.second = node[1].as<U>();
#endif
// std::tuple -- empty
template <>
struct convert<std::tuple<>> {
static Node encode(const std::tuple<>& /*tup*/) {
return convert<_Null>::encode(Null);
}
static bool decode(const Node& node, std::tuple<>& /*tup*/) {
return convert<_Null>::decode(node, Null);
}
};
// std::pair -- special case of std::tuple
template <class First, class Second>
struct convert<std::pair<First, Second>> {
static Node encode(const std::pair<First, Second>& tup) {
Node node(NodeType::Sequence);
node.push_back(std::get<0>(tup));
node.push_back(std::get<1>(tup));
return node;
}
static bool decode(const Node& node, std::pair<First, Second>& tup) {
if (!node.IsSequence() || node.size() != 2)
return false;
std::get<0>(tup) = node[0].template as<First>();
std::get<1>(tup) = node[1].template as<Second>();
return true;
}
};

View File

@ -237,5 +237,55 @@ TEST(NodeTest, LoadTagWithParenthesis) {
EXPECT_EQ(node.as<std::string>(), "foo");
}
TEST(NodeTest, MapOfSequences) {
const auto doc = Load(R"(
homebrews:
vita:
- name: "PSP2048"
author: dots-tb
- name: "PSVident"
author: Freakler
)");
const auto homebrews = doc["homebrews"];
// should succeed
std::map<std::string, std::vector<std::map<std::string, std::string>>> info;
info = homebrews.as<
std::map<std::string, std::vector<std::map<std::string, std::string>>>>();
const auto vita = info["vita"];
const auto vita0 = vita[0];
const auto vita1 = vita[1];
EXPECT_NE(vita0.end(), vita0.find("name"));
EXPECT_NE(vita1.end(), vita1.find("name"));
EXPECT_NE(vita0.end(), vita0.find("author"));
EXPECT_NE(vita1.end(), vita1.find("author"));
const auto name0 = vita0.find("name");
const auto name1 = vita1.find("name");
const auto author0 = vita0.find("author");
const auto author1 = vita1.find("author");
EXPECT_EQ(name0->second, "PSP2048");
EXPECT_EQ(name1->second, "PSVident");
EXPECT_EQ(author0->second, "dots-tb");
EXPECT_EQ(author1->second, "Freakler");
}
TEST(NodeTest, SeqMap) {
const auto doc = Load(R"(
- firstname: john
surname: doe
)");
const auto info = doc.as<std::vector<std::map<std::string, std::string>>>();
EXPECT_EQ(1, info.size());
auto name = info[0];
EXPECT_EQ(2, name.size());
EXPECT_EQ("john", name["firstname"]);
EXPECT_EQ("doe", name["surname"]);
}
} // namespace
} // namespace YAML

View File

@ -1,10 +1,10 @@
#include "yaml-cpp/emitter.h"
#include "yaml-cpp/node/emit.h"
#include "yaml-cpp/node/node.h"
#include "yaml-cpp/node/impl.h"
#include "yaml-cpp/node/convert.h"
#include "yaml-cpp/node/iterator.h"
#include "yaml-cpp/node/detail/impl.h"
#include "yaml-cpp/node/emit.h"
#include "yaml-cpp/node/impl.h"
#include "yaml-cpp/node/iterator.h"
#include "yaml-cpp/node/node.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@ -211,6 +211,20 @@ TEST(NodeTest, StdVector) {
EXPECT_EQ(primes, node["primes"].as<std::vector<int>>());
}
TEST(NodeTest, StdDeque) {
std::deque<int> primes;
primes.push_back(2);
primes.push_back(3);
primes.push_back(5);
primes.push_back(7);
primes.push_back(11);
primes.push_back(13);
Node node;
node["primes"] = primes;
EXPECT_EQ(primes, node["primes"].as<std::deque<int>>());
}
TEST(NodeTest, StdList) {
std::list<int> primes;
primes.push_back(2);
@ -225,6 +239,20 @@ TEST(NodeTest, StdList) {
EXPECT_EQ(primes, node["primes"].as<std::list<int>>());
}
TEST(NodeTest, StdForwardList) {
std::forward_list<int> primes;
primes.push_front(13);
primes.push_front(11);
primes.push_front(7);
primes.push_front(5);
primes.push_front(3);
primes.push_front(2);
Node node;
node["primes"] = primes;
EXPECT_EQ(primes, node["primes"].as<std::forward_list<int>>());
}
TEST(NodeTest, StdMap) {
std::map<int, int> squares;
squares[0] = 0;
@ -239,6 +267,291 @@ TEST(NodeTest, StdMap) {
EXPECT_EQ(squares, actualSquares);
}
TEST(NodeTest, StdUnorderedMap) {
std::unordered_map<int, int> squares;
squares[0] = 0;
squares[1] = 1;
squares[2] = 4;
squares[3] = 9;
squares[4] = 16;
Node node;
node["squares"] = squares;
const auto actualSquares = node["squares"].as<std::unordered_map<int, int>>();
EXPECT_EQ(squares, actualSquares);
}
TEST(NodeTest, StdSet) {
std::set<int> primes;
primes.insert(2);
primes.insert(3);
primes.insert(5);
primes.insert(7);
primes.insert(11);
primes.insert(13);
Node node;
node["primes"] = primes;
const auto actualPrimes = node["primes"].as<std::set<int>>();
EXPECT_EQ(primes, actualPrimes);
}
TEST(NodeTest, StdUnorderedSet) {
std::unordered_set<int> primes;
primes.insert(0);
primes.insert(1);
primes.insert(4);
primes.insert(9);
primes.insert(16);
Node node;
node["primes"] = primes;
const auto actualPrimes = node["primes"].as<std::unordered_set<int>>();
EXPECT_EQ(primes, actualPrimes);
}
TEST(NodeTest, StdBitset) {
std::bitset<8> bits;
bits.set(0, true);
bits.set(1, true);
bits.set(2, false);
bits.set(3, true);
bits.set(4, true);
bits.set(5, false);
bits.set(6, false);
bits.set(7, false);
Node node;
node["bits"] = bits;
const auto actual = node["bits"].as<std::bitset<8>>();
EXPECT_EQ(bits, actual);
}
TEST(NodeTest, StdBitsetWrongSize) {
std::bitset<8> bits;
bits.set(0, true);
bits.set(1, true);
bits.set(2, false);
bits.set(3, true);
bits.set(4, true);
bits.set(5, false);
bits.set(6, false);
bits.set(7, false);
Node node;
node["bits"] = bits;
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<1>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<2>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<3>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<4>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<5>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<6>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<7>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<9>>()),
ErrorMsg::BAD_CONVERSION);
}
TEST(NodeTest, StdBitsetWrongRepresentation) {
const std::string representation =
"0011"
"1000"
"1101"
"x000"; // 16 bits, note the wrong character 'x' here
Node node;
node["not_really_bits"] = representation;
EXPECT_THROW_REPRESENTATION_EXCEPTION(
(node["not_really_bits"].as<std::bitset<16>>()),
ErrorMsg::BAD_CONVERSION);
}
#define TEST_FROM_TO(TESTNAME, FROM, TO) \
TEST(NodeTest, TESTNAME) { \
const std::FROM primes = {{2, 3, 5, 7, 11, 13}}; \
std::size_t count0 = std::distance(std::begin(primes), std::end(primes)); \
const Node node(primes); \
const auto decoded = node.as<std::TO>(); \
auto iter0 = primes.begin(); \
auto iter1 = decoded.begin(); \
std::size_t count1 = 0; \
while (iter0 != primes.end() && iter1 != decoded.end()) { \
EXPECT_EQ(*iter0, *iter1); \
++iter0; \
++iter1; \
++count1; \
} \
EXPECT_EQ(count0, count1); \
EXPECT_THROW_REPRESENTATION_EXCEPTION((node.as<std::map<int, int>>()), \
ErrorMsg::BAD_CONVERSION); \
}
// needed because the comma would otherwise be seen as the start of a new macro
// argument.
#define ARRAY_TYPE array<int, 6>
TEST_FROM_TO(StdFromVectorToDeque, vector<int>, deque<int>);
TEST_FROM_TO(StdFromVectorToList, vector<int>, list<int>);
TEST_FROM_TO(StdFromVectorToForwardList, vector<int>, forward_list<int>);
TEST_FROM_TO(StdFromVectorToArray, vector<int>, ARRAY_TYPE);
TEST_FROM_TO(StdFromDequeToVector, deque<int>, vector<int>);
TEST_FROM_TO(StdFromDequeToList, deque<int>, list<int>);
TEST_FROM_TO(StdFromDequeToForwardList, deque<int>, forward_list<int>);
TEST_FROM_TO(StdFromDequeToArray, deque<int>, ARRAY_TYPE);
TEST_FROM_TO(StdFromListToVector, list<int>, vector<int>);
TEST_FROM_TO(StdFromListToDeque, list<int>, deque<int>);
TEST_FROM_TO(StdFromListToForwardList, list<int>, forward_list<int>);
TEST_FROM_TO(StdFromListToArray, list<int>, ARRAY_TYPE);
TEST_FROM_TO(StdFromForwardListToVector, forward_list<int>, vector<int>);
TEST_FROM_TO(StdFromForwardListToDeque, forward_list<int>, deque<int>);
TEST_FROM_TO(StdFromForwardListToList, forward_list<int>, list<int>);
TEST_FROM_TO(StdFromForwardListToArray, forward_list<int>, ARRAY_TYPE);
TEST_FROM_TO(StdFromArrayToVector, ARRAY_TYPE, vector<int>);
TEST_FROM_TO(StdFromArrayToDeque, ARRAY_TYPE, deque<int>);
TEST_FROM_TO(StdFromArrayToList, ARRAY_TYPE, list<int>);
TEST_FROM_TO(StdFromArrayToForwardList, ARRAY_TYPE, forward_list<int>);
#undef ARRAY_TYPE
#undef TEST_FROM_TO
TEST(NodeTest, SequenceWithDuplicatesCanBeParsedAsSet) {
const std::vector<int> withDuplicates{1, 5, 3, 6, 4, 7, 5, 3, 3, 4, 5,
5, 3, 2, 7, 8, 9, 9, 9, 5, 2, 0};
const auto node = Node(withDuplicates);
// should succeed -- an std::set is considered a yaml sequence
const auto withoutDuplicatesAndSorted = node.as<std::set<int>>();
EXPECT_EQ(withoutDuplicatesAndSorted.size(), 10);
std::size_t i = 0;
for (const auto digit : withoutDuplicatesAndSorted) {
EXPECT_EQ(digit, i);
++i;
}
EXPECT_EQ(i, 10);
}
TEST(NodeTest, SequenceWithDuplicatesCanBeParsedAsUnorderedSet) {
const std::vector<int> withDuplicates{1, 5, 3, 6, 4, 7, 5, 3, 3, 4, 5,
5, 3, 2, 7, 8, 9, 9, 9, 5, 2, 0};
const auto node = Node(withDuplicates);
// should succeed -- an std::unordered_set is considered a yaml sequence
const auto withoutDuplicates = node.as<std::unordered_set<int>>();
EXPECT_EQ(withoutDuplicates.size(), 10);
int occurences[10];
std::fill(std::begin(occurences), std::end(occurences), 0);
for (const auto digit : withoutDuplicates) {
++occurences[digit];
}
for (std::size_t i = 0; i < 10; ++i)
EXPECT_EQ(1, occurences[i]);
}
TEST(NodeTest, StdFromMapToUnorderedMap) {
std::map<std::string, std::vector<std::string>> typeSafeTravisFile{
{"language", {"c++"}},
{"os", {"linux", "osx"}},
{"compiler", {"clang", "gcc"}},
{"before_install", {"do some stuff", "do some more stuff"}},
{"before_script", {"mkdir build", "cd build", "cmake .."}},
{"script", {"make", "test/run-tests"}}};
const auto node = Node(typeSafeTravisFile);
// should succeed -- precisely two values in the sequence
const auto os = node["os"].as<std::pair<std::string, std::string>>();
EXPECT_EQ(os.first, "linux");
EXPECT_EQ(os.second, "osx");
// should succeed -- yaml doesn't know the difference between ordered and
// unordered.
using T = std::unordered_map<std::string, std::vector<std::string>>;
EXPECT_THAT(node.as<T>(),
testing::UnorderedElementsAreArray(typeSafeTravisFile.begin(),
typeSafeTravisFile.end()));
// should fail -- a yaml map is not a yaml sequence
EXPECT_THROW_REPRESENTATION_EXCEPTION(
(node.as< // looks structurally somewhat like a map
std::vector<std::pair<std::string, std::vector<std::string>>>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION(
(node.as< // looks structurally somewhat like a map
std::deque<std::pair<std::string, std::deque<std::string>>>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION(
(node.as< // looks structurally somewhat like a map
std::list<std::pair<std::string, std::list<std::string>>>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION(
(node.as< // looks structurally somewhat like a map
std::forward_list<
std::pair<std::string, std::forward_list<std::string>>>>()),
ErrorMsg::BAD_CONVERSION);
}
TEST(NodeTest, StdFromUnorderedMapToMap) {
std::unordered_map<std::string, std::vector<std::string>> typeSafeTravisFile{
{"language", {"c++"}},
{"os", {"linux", "osx"}},
{"compiler", {"clang", "gcc"}},
{"before_install", {"do some stuff", "do some more stuff"}},
{"before_script", {"mkdir build", "cd build", "cmake .."}},
{"script", {"make", "test/run-tests"}}};
const auto node = Node(typeSafeTravisFile);
// should succeed -- precisely two values in the sequence
const auto os = node["os"].as<std::pair<std::string, std::string>>();
EXPECT_EQ(os.first, "linux");
EXPECT_EQ(os.second, "osx");
// should succeed -- yaml doesn't know the difference between ordered and
// unordered.
using T = std::map<std::string, std::vector<std::string>>;
EXPECT_THAT(node.as<T>(),
testing::UnorderedElementsAreArray(typeSafeTravisFile.begin(),
typeSafeTravisFile.end()));
// should fail -- a yaml map is not a yaml sequence
EXPECT_THROW_REPRESENTATION_EXCEPTION(
(node.as< // looks structurally somewhat like a map
std::vector<std::pair<std::string, std::vector<std::string>>>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION(
(node.as< // looks structurally somewhat like a map
std::deque<std::pair<std::string, std::deque<std::string>>>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION(
(node.as< // looks structurally somewhat like a map
std::list<std::pair<std::string, std::list<std::string>>>>()),
ErrorMsg::BAD_CONVERSION);
EXPECT_THROW_REPRESENTATION_EXCEPTION(
(node.as< // looks structurally somewhat like a map
std::forward_list<
std::pair<std::string, std::forward_list<std::string>>>>()),
ErrorMsg::BAD_CONVERSION);
}
TEST(NodeTest, StdPair) {
std::pair<int, std::string> p;
p.first = 5;
@ -251,6 +564,86 @@ TEST(NodeTest, StdPair) {
EXPECT_EQ(p, actualP);
}
TEST(NodeTest, StdPairFailure) {
std::vector<int> triple{1, 2, 3};
const auto node = Node(triple);
// should fail -- an std::pair needs a sequence of length exactly 2, while
// the Node holds a sequence of length exactly 3.
EXPECT_THROW_REPRESENTATION_EXCEPTION((node.as<std::pair<int, int>>()),
ErrorMsg::BAD_CONVERSION);
}
TEST(NodeTest, StdTupleSize0) {
const auto expected = std::tuple<>();
const auto node = Node(expected);
const auto actual = node.as<std::tuple<>>();
EXPECT_EQ(expected, actual);
EXPECT_EQ(true, node.IsNull());
}
TEST(NodeTest, StdTupleSize1) {
const auto expected = std::make_tuple(42);
const auto node = Node(expected);
const auto actual = node.as<std::remove_cv<decltype(expected)>::type>();
EXPECT_EQ(expected, actual);
// should succeed -- tuple is realized as a yaml sequence
const auto vec = node.as<std::vector<int>>();
EXPECT_EQ(std::get<0>(expected), vec[0]);
}
TEST(NodeTest, StdTupleSize2) {
const auto expected = std::make_tuple(42, 3.141592f);
const auto node = Node(expected);
const auto actual = node.as<std::remove_cv<decltype(expected)>::type>();
EXPECT_EQ(expected, actual);
// should succeed -- tuple is realized as a yaml sequence
const auto vec = node.as<std::vector<float>>();
EXPECT_EQ(std::get<0>(expected), vec[0]);
EXPECT_EQ(std::get<1>(expected), vec[1]);
// should succeed -- can also be a pair in this case
const auto pair = node.as<std::pair<int, float>>();
EXPECT_EQ(std::get<0>(expected), pair.first);
EXPECT_EQ(std::get<1>(expected), pair.second);
}
TEST(NodeTest, StdTupleSize3) {
const auto expected = std::make_tuple(42, 3.141592f, std::string("hello"));
const auto node = Node(expected);
const auto actual = node.as<std::remove_cv<decltype(expected)>::type>();
EXPECT_EQ(expected, actual);
}
TEST(NodeTest, StdTupleSize4) {
const auto expected = std::make_tuple(42, 3.141592f, std::string("hello"),
std::string("world"));
const auto node = Node(expected);
const auto actual = node.as<std::remove_cv<decltype(expected)>::type>();
EXPECT_EQ(expected, actual);
}
TEST(NodeTest, StdTupleSize5) {
const auto expected =
std::make_tuple(42, 3.141592f, std::string("hello"), std::string("world"),
std::vector<int>{2, 3, 5, 7});
const auto node = Node(expected);
const auto actual = node.as<std::remove_cv<decltype(expected)>::type>();
EXPECT_EQ(expected, actual);
}
TEST(NodeTest, StdTupleSize6) {
const auto expected =
std::make_tuple(42, 3.141592f, std::string("hello"), std::string("world"),
std::vector<int>{2, 3, 5, 7},
std::map<int, int>{{1, 1}, {2, 4}, {3, 9}, {4, 16}});
const auto node = Node(expected);
const auto actual = node.as<std::remove_cv<decltype(expected)>::type>();
EXPECT_EQ(expected, actual);
}
TEST(NodeTest, SimpleAlias) {
Node node;
node["foo"] = "value";