Make UnknownElement moveable and not copyable

And remove the unused comparison functionality.
This commit is contained in:
Thomas Goyne 2014-06-27 20:44:47 -07:00
parent 4c0e578eda
commit 4c88449e4c
19 changed files with 155 additions and 392 deletions

View File

@ -12,80 +12,59 @@ Author: Terry Caton
#include <utility>
namespace {
using namespace json;
using namespace json;
class CastVisitorBase : public Visitor, public ConstVisitor {
void Visit(Array&) { }
void Visit(Object&) { }
void Visit(Integer&) { }
void Visit(Double&) { }
void Visit(String&) { }
void Visit(Boolean&) { }
void Visit(Null&) { is_null = true; }
void Visit(Array const&) { }
void Visit(Object const&) { }
void Visit(Integer) { }
void Visit(Double) { }
void Visit(String const&) { }
void Visit(Boolean) { }
void Visit(Null const&) { is_null = true; }
public:
bool is_null = false;
};
class CastVisitorBase : public Visitor {
void Visit(Array&) override { }
void Visit(Object&) override { }
void Visit(Integer&) override { }
void Visit(Double&) override { }
void Visit(String&) override { }
void Visit(Boolean&) override { }
void Visit(Null&) override { is_null = true; }
public:
bool is_null = false;
};
template<class T>
struct CastVisitor final : public CastVisitorBase {
T *element = nullptr;
void Visit(T& ele) { element = &ele; }
};
template<typename T>
struct CastVisitor final : public CastVisitorBase {
T *element = nullptr;
void Visit(T& ele) override { element = &ele; }
};
}
namespace json
{
/////////////////////////
// UnknownElement members
class UnknownElement::Imp
{
namespace json {
class UnknownElement::Imp {
public:
virtual ~Imp() {}
virtual Imp* Clone() const = 0;
virtual bool Compare(const Imp& imp) const = 0;
virtual void Accept(ConstVisitor& visitor) const = 0;
virtual void Accept(Visitor& visitor) = 0;
virtual ~Imp() {}
virtual void Accept(ConstVisitor& visitor) const = 0;
virtual void Accept(Visitor& visitor) = 0;
};
}
namespace {
template <typename ElementTypeT>
class UnknownElement::Imp_T final : public UnknownElement::Imp
{
class Imp_T final : public UnknownElement::Imp {
ElementTypeT m_Element;
public:
Imp_T(const ElementTypeT& element) : m_Element(element) { }
Imp* Clone() const { return new Imp_T<ElementTypeT>(*this); }
Imp_T(ElementTypeT element) : m_Element(std::move(element)) { }
void Accept(ConstVisitor& visitor) const { visitor.Visit(m_Element); }
void Accept(Visitor& visitor) { visitor.Visit(m_Element); }
bool Compare(const Imp& imp) const
{
CastVisitor<const ElementTypeT> castVisitor;
imp.Accept(castVisitor);
return castVisitor.element && m_Element == *castVisitor.element;
}
private:
ElementTypeT m_Element;
void Accept(ConstVisitor& visitor) const { visitor.Visit(m_Element); }
void Accept(Visitor& visitor) { visitor.Visit(m_Element); }
};
}
namespace json {
UnknownElement::UnknownElement() : m_pImp(new Imp_T<Null>(Null())) {}
UnknownElement::UnknownElement(UnknownElement&& unknown) : m_pImp(std::move(unknown.m_pImp)) {}
UnknownElement::UnknownElement(int number) : m_pImp(new Imp_T<Integer>(number)) {}
UnknownElement::UnknownElement(const char *string) : m_pImp(new Imp_T<String>(string)) {}
UnknownElement::UnknownElement() : m_pImp(new Imp_T<Null>(Null())) {}
UnknownElement::UnknownElement(const UnknownElement& unknown) : m_pImp(unknown.m_pImp->Clone()) {}
UnknownElement::UnknownElement(int number) : m_pImp(new Imp_T<Integer>(number)) {}
UnknownElement::UnknownElement(const char *string) : m_pImp(new Imp_T<String>(string)) {}
UnknownElement::~UnknownElement() { }
#define DEFINE_UE_TYPE(Type) \
UnknownElement::UnknownElement(Type const& val) : m_pImp(new Imp_T<Type>(val)) { } \
UnknownElement::UnknownElement(Type val) : m_pImp(new Imp_T<Type>(std::move(val))) { } \
UnknownElement::operator Type const&() const { return CastTo<Type>(); } \
UnknownElement::operator Type&() { return CastTo<Type>(); }
@ -97,40 +76,22 @@ DEFINE_UE_TYPE(Boolean)
DEFINE_UE_TYPE(String)
DEFINE_UE_TYPE(Null)
UnknownElement& UnknownElement::operator =(const UnknownElement& unknown)
{
m_pImp.reset(unknown.m_pImp->Clone());
UnknownElement& UnknownElement::operator=(UnknownElement&& unknown) {
m_pImp = std::move(unknown.m_pImp);
return *this;
}
UnknownElement& UnknownElement::operator[](const std::string& key)
{
return CastTo<Object>()[key];
}
const UnknownElement& UnknownElement::operator[] (const std::string& key) const
{
// throws if we aren't an object
Object const& obj = CastTo<Object>();
Object::const_iterator it = obj.find(key);
if (it == obj.end())
throw Exception("Object member not found: " + key);
return it->second;
}
template <typename ElementTypeT>
ElementTypeT const& UnknownElement::CastTo() const
{
CastVisitor<const ElementTypeT> castVisitor;
Accept(castVisitor);
ElementTypeT const& UnknownElement::CastTo() const {
CastVisitor<ElementTypeT> castVisitor;
const_cast<UnknownElement *>(this)->Accept(castVisitor);
if (!castVisitor.element)
throw Exception("Bad cast");
return *castVisitor.element;
}
template <typename ElementTypeT>
ElementTypeT& UnknownElement::CastTo()
{
ElementTypeT& UnknownElement::CastTo() {
CastVisitor<ElementTypeT> castVisitor;
Accept(castVisitor);
@ -148,10 +109,4 @@ ElementTypeT& UnknownElement::CastTo()
void UnknownElement::Accept(ConstVisitor& visitor) const { m_pImp->Accept(visitor); }
void UnknownElement::Accept(Visitor& visitor) { m_pImp->Accept(visitor); }
bool UnknownElement::operator == (const UnknownElement& element) const
{
return m_pImp->Compare(*element.m_pImp);
}
} // end namespace

View File

@ -288,7 +288,7 @@ UnknownElement Reader::ParseObject(Reader::TokenStream& tokenStream) {
MatchExpectedToken(Token::TOKEN_OBJECT_END, tokenStream);
return object;
return std::move(object);
}
UnknownElement Reader::ParseArray(Reader::TokenStream& tokenStream) {
@ -296,8 +296,7 @@ UnknownElement Reader::ParseArray(Reader::TokenStream& tokenStream) {
Array array;
while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
{
while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END) {
array.push_back(Parse(tokenStream));
if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
@ -306,7 +305,7 @@ UnknownElement Reader::ParseArray(Reader::TokenStream& tokenStream) {
MatchExpectedToken(Token::TOKEN_ARRAY_END, tokenStream);
return array;
return std::move(array);
}
UnknownElement Reader::ParseString(Reader::TokenStream& tokenStream) {

View File

@ -44,13 +44,13 @@ void Hotkey::ComboInsert(Combo const& combo) {
cmd_map.insert(make_pair(combo.CmdName(), combo));
}
Hotkey::Hotkey(agi::fs::path const& file, std::pair<const char *, size_t> default_config)
Hotkey::Hotkey(fs::path const& file, std::pair<const char *, size_t> default_config)
: config_file(file)
{
LOG_D("hotkey/init") << "Generating hotkeys.";
const json::Object object(agi::json_util::file(config_file, default_config));
for (auto const& hotkey_context : object)
auto root = json_util::file(config_file, default_config);
for (auto const& hotkey_context : static_cast<json::Object const&>(root))
BuildHotkey(hotkey_context.first, hotkey_context.second);
}
@ -58,19 +58,26 @@ void Hotkey::BuildHotkey(std::string const& context, json::Object const& hotkeys
for (auto const& command : hotkeys) {
const json::Array& command_hotkeys = command.second;
for (auto const& hotkey : command_hotkeys) {
for (json::Object const& hotkey : command_hotkeys) {
std::vector<std::string> keys;
try {
const json::Array& arr_mod = hotkey["modifiers"];
keys.reserve(arr_mod.size() + 1);
copy(arr_mod.begin(), arr_mod.end(), back_inserter(keys));
keys.push_back(hotkey["key"]);
}
catch (json::Exception const& e) {
LOG_E("agi/hotkey/load") << "Failed loading hotkey for command '" << command.first << "': " << e.what();
auto it = hotkey.find("modifiers");
if (it == end(hotkey)) {
LOG_E("agi/hotkey/load") << "Hotkey for command '" << command.first << "' is missing modifiers";
continue;
}
json::Array const& arr_mod = it->second;
keys.reserve(arr_mod.size() + 1);
copy(arr_mod.begin(), arr_mod.end(), back_inserter(keys));
it = hotkey.find("key");
if (it == end(hotkey)) {
LOG_E("agi/hotkey/load") << "Hotkey for command '" << command.first << "' is missing the key";
continue;
}
keys.push_back(it->second);
ComboInsert(Combo(context, command.first, keys));
}
}
@ -157,12 +164,13 @@ void Hotkey::Flush() {
modifiers.insert(modifiers.end(), combo.Get().begin(), combo.Get().end() - 1);
}
json::Array& combo_array = root[combo.Context()][combo.CmdName()];
json::Object& context = root[combo.Context()];
json::Array& combo_array = context[combo.CmdName()];
combo_array.push_back(std::move(hotkey));
}
io::Save file(config_file);
agi::JsonWriter::Write(root, file.Get());
JsonWriter::Write(root, file.Get());
}
void Hotkey::SetHotkeyMap(HotkeyMap const& new_map) {

View File

@ -38,8 +38,8 @@ MRUManager::MRUManager(agi::fs::path const& config, std::pair<const char *, size
{
LOG_D("agi/mru") << "Loading MRU List";
json::Object root(json_util::file(config, default_config));
for (auto const& it : root)
auto root = json_util::file(config, default_config);
for (auto const& it : static_cast<json::Object const&>(root))
Load(it.first, it.second);
}
@ -76,8 +76,7 @@ const MRUManager::MRUListMap* MRUManager::Get(std::string const& key) {
}
agi::fs::path const& MRUManager::GetEntry(std::string const& key, const size_t entry) {
const MRUManager::MRUListMap *const map = Get(key);
const auto map = Get(key);
if (entry >= map->size())
throw MRUErrorIndexOutOfRange("Requested element index is out of range.");

View File

@ -77,7 +77,7 @@ class ConfigVisitor final : public json::ConstVisitor {
void Visit(const json::Object& object) {
auto old_name = name;
for (auto const& obj : object) {
name = old_name + (name.empty() ? "" : "/") + obj.first;
name = old_name + (old_name.empty() ? "" : "/") + obj.first;
obj.second.Accept(*this);
}
name = old_name;
@ -144,15 +144,15 @@ public:
/// @param[out] obj Parent object
/// @param[in] path Path option should be stored in.
/// @param[in] value Value to write.
void put_option(json::Object &obj, const std::string &path, const json::UnknownElement &value) {
void put_option(json::Object &obj, const std::string &path, json::UnknownElement value) {
std::string::size_type pos = path.find('/');
// Not having a '/' denotes it is a leaf.
if (pos == std::string::npos) {
assert(obj.find(path) == obj.end());
obj[path] = value;
obj[path] = std::move(value);
}
else
put_option(obj[path.substr(0, pos)], path.substr(pos + 1), value);
put_option(obj[path.substr(0, pos)], path.substr(pos + 1), std::move(value));
}
template<class T>
@ -163,7 +163,7 @@ void put_array(json::Object &obj, const std::string &path, const char *element_k
static_cast<json::Object&>(array.back())[element_key] = (json::UnknownElement)elem;
}
put_option(obj, path, array);
put_option(obj, path, std::move(array));
}
struct option_name_cmp {

View File

@ -56,20 +56,20 @@ public:
class UnknownElement {
public:
UnknownElement();
UnknownElement(const UnknownElement& unknown);
UnknownElement(const Object& object);
UnknownElement(const Array& array);
UnknownElement(double const& number);
UnknownElement(UnknownElement&& unknown);
UnknownElement(Object object);
UnknownElement(Array array);
UnknownElement(double number);
UnknownElement(int number);
UnknownElement(int64_t const& number);
UnknownElement(bool const& boolean);
UnknownElement(int64_t number);
UnknownElement(bool boolean);
UnknownElement(const char *string);
UnknownElement(const String& string);
UnknownElement(const Null& null);
UnknownElement(String string);
UnknownElement(Null null);
~UnknownElement();
UnknownElement& operator = (const UnknownElement& unknown);
UnknownElement& operator=(UnknownElement&& unknown);
// implicit cast to actual element type. throws on failure
operator Object const&() const;
@ -87,27 +87,14 @@ public:
operator String&();
operator Null&();
// provides quick access to children when real element type is object
UnknownElement& operator[] (const std::string& key);
const UnknownElement& operator[] (const std::string& key) const;
template<int N>
UnknownElement& operator[] (const char(&key)[N]) { return operator[](std::string(key)); }
template<int N>
const UnknownElement& operator[] (const char(&key)[N]) const { return operator[](std::string(key)); }
// implements visitor pattern
void Accept(ConstVisitor& visitor) const;
void Accept(Visitor& visitor);
// tests equality. first checks type, then value if possible
bool operator ==(const UnknownElement& element) const;
bool operator !=(const UnknownElement& element) const { return !(*this == element); }
private:
class Imp;
template <typename ElementTypeT>
class Imp_T;
private:
UnknownElement(UnknownElement const& unknown) = delete;
template <typename ElementTypeT>
ElementTypeT const& CastTo() const;
@ -121,8 +108,6 @@ private:
/////////////////////////////////////////////////////////////////////////////////
// Null - doesn't do much of anything but satisfy the JSON spec. It is the default
// element type of UnknownElement
struct Null {
bool operator == (const Null&) const { return true; }
};
struct Null { };
} // end namespace

View File

@ -68,7 +68,7 @@ class DialogShiftTimes final : public wxDialog {
wxRadioBox *time_fields;
wxListBox *history_box;
void SaveHistory(json::Array const& shifted_blocks);
void SaveHistory(json::Array shifted_blocks);
void LoadHistory();
void Process(wxCommandEvent&);
int Shift(int initial_time, int shift, bool by_time, agi::vfr::Time type);
@ -103,19 +103,20 @@ static wxString get_history_string(json::Object &obj) {
time_field == 1 ? _("s") :
_("e") ;
json::Array const& sel = obj["selection"];
json::Array& sel = obj["selection"];
wxString lines;
int64_t sel_mode = obj["mode"];
if (sel_mode == 0)
lines = _("all");
else if (sel_mode == 2)
lines = fmt_tl("from %d onward", (int64_t)sel.front()["start"]);
lines = fmt_tl("from %d onward", (int64_t)static_cast<json::Object&>(sel.front())["start"]);
else {
lines += _("sel ");
for (auto it = sel.begin(); it != sel.end(); ++it) {
int beg = (int64_t)(*it)["start"];
int end = (int64_t)(*it)["end"];
json::Object& range = *it;
int beg = (int64_t)range["start"];
int end = (int64_t)range["end"];
if (beg == end)
lines += std::to_wstring(beg);
else
@ -306,7 +307,7 @@ void DialogShiftTimes::OnHistoryClick(wxCommandEvent &evt) {
time_fields->SetSelection((int64_t)obj["fields"]);
}
void DialogShiftTimes::SaveHistory(json::Array const& shifted_blocks) {
void DialogShiftTimes::SaveHistory(json::Array shifted_blocks) {
json::Object new_entry;
new_entry["filename"] = context->subsController->Filename().filename().string();
new_entry["is by time"] = shift_by_time->GetValue();
@ -314,9 +315,9 @@ void DialogShiftTimes::SaveHistory(json::Array const& shifted_blocks) {
new_entry["amount"] = from_wx(shift_by_time->GetValue() ? shift_time->GetValue() : shift_frames->GetValue());
new_entry["fields"] = time_fields->GetSelection();
new_entry["mode"] = selection_mode->GetSelection();
new_entry["selection"] = shifted_blocks;
new_entry["selection"] = std::move(shifted_blocks);
history.insert(history.begin(), new_entry);
history.insert(history.begin(), std::move(new_entry));
if (history.size() > 50)
history.resize(50);
@ -335,7 +336,7 @@ void DialogShiftTimes::LoadHistory() {
try {
json::UnknownElement root;
json::Reader::Read(root, *agi::io::Open(history_filename));
history = root;
history = std::move(static_cast<json::Array&>(root));
for (auto& history_entry : history)
history_box->Append(get_history_string(history_entry));
@ -386,7 +387,7 @@ void DialogShiftTimes::Process(wxCommandEvent &) {
json::Object block;
block["start"] = block_start;
block["end"] = line.Row;
shifted_blocks.push_back(block);
shifted_blocks.push_back(std::move(block));
block_start = 0;
}
if (mode == 1) continue;
@ -407,10 +408,10 @@ void DialogShiftTimes::Process(wxCommandEvent &) {
json::Object block;
block["start"] = block_start;
block["end"] = context->ass->Events.back().Row + 1;
shifted_blocks.push_back(block);
shifted_blocks.push_back(std::move(block));
}
SaveHistory(shifted_blocks);
SaveHistory(std::move(shifted_blocks));
Close();
}

View File

@ -295,7 +295,7 @@ json::Object const& get_menus_root() {
if (!root.empty()) return root;
try {
root = agi::json_util::file(config::path->Decode("?user/menu.json"), GET_DEFAULT_CONFIG(default_menu));
root = std::move(static_cast<json::Object&>(agi::json_util::file(config::path->Decode("?user/menu.json"), GET_DEFAULT_CONFIG(default_menu))));
return root;
}
catch (json::Reader::ParseException const& e) {

View File

@ -42,7 +42,7 @@ namespace {
static json::Object root;
if (root.empty()) {
boost::interprocess::ibufferstream stream((const char *)default_toolbar, sizeof(default_toolbar));
root = agi::json_util::parse(stream);
root = std::move(static_cast<json::Object&>(agi::json_util::parse(stream)));
}
return root;
}

View File

@ -1,14 +0,0 @@
{
"Integer" : 0,
"Double" : 0.1,
"String" : "",
"Color" : "rgb(0, 0, 0)",
"Boolean" : false,
"Array" : {
"Integer" : [ { "int" : 0 }, {"int" : 0 } ],
"Double" : [ { "double" : 0.1 }, {"double" : 0.1 } ],
"String" : [ { "string" : "" }, {"string" : "" } ],
"Color" : [ { "color" : "rgb(0,0,0)" }, {"color" : "rgb(0,0,0)" } ],
"Boolean" : [ { "bool" : false }, {"bool" : false } ]
}
}

View File

@ -1 +0,0 @@
{"Bool" : [{"bool" : true}, {"bool" : true}]}

View File

@ -1 +0,0 @@
{"Double" : [{"double" : 2.1}, {"double" : 2.1}]}

View File

@ -1 +0,0 @@
{"Integer" : [{"int" : 1}, {"int" : 1}]}

View File

@ -1 +0,0 @@
{"String" : [{"string" : "This is a test"}, {"string" : "This is a test"}]}

View File

@ -1 +0,0 @@
{"Bool" : true}

View File

@ -1 +0,0 @@
{"Double" : 2.1}

View File

@ -1 +0,0 @@
{"Integer" : 1}

View File

@ -25,78 +25,9 @@
#include <libaegisub/cajun/elements.h>
#include <libaegisub/cajun/visitor.h>
class lagi_cajun : public libagi {
class lagi_cajun : public libagi { };
protected:
// place holder for future code placement
};
TEST_F(lagi_cajun, Compare) {
json::UnknownElement Integer1 = 0;
json::UnknownElement Integer2 = 1;
json::UnknownElement String1 = "1";
json::UnknownElement String2 = "2";
json::UnknownElement Boolean1 = false;
json::UnknownElement Boolean2 = true;
json::UnknownElement Array1 = json::Array();
json::UnknownElement Array2 = json::Array();
json::UnknownElement Object1 = json::Object();
json::UnknownElement Object2 = json::Object();
json::UnknownElement Null = json::Null();
static_cast<json::Array&>(Array2).push_back(1);
Object2["a"] = "b";
// Test that things are equal to themselves and mixed comparisons are not errors
EXPECT_EQ(Integer1, Integer1);
EXPECT_NE(Integer1, Integer2);
EXPECT_NE(Integer1, String1);
EXPECT_NE(Integer1, Boolean1);
EXPECT_NE(Integer1, Array1);
EXPECT_NE(Integer1, Object1);
EXPECT_NE(Integer1, Null);
EXPECT_EQ(String1, String1);
EXPECT_NE(String1, Integer2);
EXPECT_NE(String1, String2);
EXPECT_NE(String1, Boolean1);
EXPECT_NE(String1, Array1);
EXPECT_NE(String1, Object1);
EXPECT_NE(String1, Null);
EXPECT_EQ(Boolean1, Boolean1);
EXPECT_NE(Boolean1, Integer2);
EXPECT_NE(Boolean1, String1);
EXPECT_NE(Boolean1, Boolean2);
EXPECT_NE(Boolean1, Array1);
EXPECT_NE(Boolean1, Object1);
EXPECT_NE(Boolean1, Null);
EXPECT_EQ(Array1, Array1);
EXPECT_NE(Array1, Integer2);
EXPECT_NE(Array1, String1);
EXPECT_NE(Array1, Boolean1);
EXPECT_NE(Array1, Array2);
EXPECT_NE(Array1, Object1);
EXPECT_NE(Array1, Null);
EXPECT_EQ(Object1, Object1);
EXPECT_NE(Object1, Integer2);
EXPECT_NE(Object1, String1);
EXPECT_NE(Object1, Boolean1);
EXPECT_NE(Object1, Array2);
EXPECT_NE(Object1, Object2);
EXPECT_NE(Object1, Null);
EXPECT_EQ(Null, Null);
EXPECT_NE(Null, Integer2);
EXPECT_NE(Null, String1);
EXPECT_NE(Null, Boolean1);
EXPECT_NE(Null, Array2);
EXPECT_NE(Null, Object2);
}
TEST_F(lagi_cajun, CastNonConst) {
TEST(lagi_cajun, CastNonConst) {
json::UnknownElement Integer = 0;
json::UnknownElement Double = 0.0;
json::UnknownElement String = "1";
@ -119,7 +50,7 @@ TEST_F(lagi_cajun, CastNonConst) {
EXPECT_NO_THROW(static_cast<json::Object const&>(Object));
}
TEST_F(lagi_cajun, CastConst) {
TEST(lagi_cajun, CastConst) {
const json::UnknownElement Integer = 10;
const json::UnknownElement Double = 10.0;
const json::UnknownElement String = "1";
@ -151,21 +82,7 @@ TEST_F(lagi_cajun, CastConst) {
EXPECT_TRUE(static_cast<json::Object const&>(Object).empty());
}
TEST_F(lagi_cajun, UnknownIsIndexable) {
json::Object obj;
obj["Integer"] = 1;
json::UnknownElement unk_obj = obj;
EXPECT_NO_THROW(unk_obj["Integer"]);
EXPECT_EQ(1, (json::Integer)unk_obj["Integer"]);
EXPECT_NO_THROW(unk_obj["Nonexistent Key"]);
json::UnknownElement const& const_unk_obj = obj;
EXPECT_NO_THROW(const_unk_obj["Integer"]);
EXPECT_THROW(const_unk_obj["Another nonexistent Key"], json::Exception);
}
TEST_F(lagi_cajun, ObjectStoreInteger) {
TEST(lagi_cajun, ObjectStoreInteger) {
json::Object obj;
obj["Integer"] = 1;
EXPECT_EQ(1, static_cast<json::Integer>(obj["Integer"]));
@ -177,7 +94,7 @@ TEST_F(lagi_cajun, ObjectStoreInteger) {
EXPECT_THROW(static_cast<json::Object const&>(obj["Integer"]), json::Exception);
}
TEST_F(lagi_cajun, ObjectStoreDouble) {
TEST(lagi_cajun, ObjectStoreDouble) {
json::Object obj;
obj["Double"] = 1.0;
EXPECT_EQ(1.0, static_cast<json::Double>(obj["Double"]));
@ -189,7 +106,7 @@ TEST_F(lagi_cajun, ObjectStoreDouble) {
EXPECT_THROW(static_cast<json::Object const&>(obj["Double"]), json::Exception);
}
TEST_F(lagi_cajun, ObjectStoreString) {
TEST(lagi_cajun, ObjectStoreString) {
json::Object obj;
obj["String"] = "test";
EXPECT_STREQ("test", static_cast<std::string>(obj["String"]).c_str());
@ -201,7 +118,7 @@ TEST_F(lagi_cajun, ObjectStoreString) {
EXPECT_THROW(static_cast<json::Object const&>(obj["String"]), json::Exception);
}
TEST_F(lagi_cajun, ObjectStoreBoolean) {
TEST(lagi_cajun, ObjectStoreBoolean) {
json::Object obj;
obj["Boolean"] = true;
EXPECT_TRUE(static_cast<json::Boolean>(obj["Boolean"]));
@ -213,7 +130,7 @@ TEST_F(lagi_cajun, ObjectStoreBoolean) {
EXPECT_THROW(static_cast<json::Object const&>(obj["Boolean"]), json::Exception);
}
TEST_F(lagi_cajun, ObjectStoreNull) {
TEST(lagi_cajun, ObjectStoreNull) {
json::Object obj;
obj["Null"] = json::Null();
@ -241,41 +158,27 @@ TEST_F(lagi_cajun, ObjectStoreNull) {
EXPECT_THROW(static_cast<json::Null>(obj["Null"]), json::Exception);
}
TEST_F(lagi_cajun, ObjectCreateArray) {
TEST(lagi_cajun, ObjectCreateArray) {
json::Object obj;
obj["Inside"] = "";
json::Array array;
array.push_back(obj);
array.push_back(std::move(obj));
EXPECT_STREQ("", static_cast<std::string>(array[0]["Inside"]).c_str());
EXPECT_STREQ("", static_cast<std::string>(static_cast<json::Object&>(array[0])["Inside"]).c_str());
}
TEST_F(lagi_cajun, ObjectEquality) {
json::Object obj;
obj["Inside"] = "";
json::Array array;
array.push_back(obj);
obj["Array"] = array;
json::Object obj_dupe = obj;
EXPECT_TRUE(obj_dupe == obj);
obj_dupe["NotEqual"] = array;
EXPECT_FALSE(obj_dupe == obj);
}
TEST_F(lagi_cajun, Read) {
json::UnknownElement obj;
TEST(lagi_cajun, Read) {
json::UnknownElement root;
std::istringstream doc("{\"String\" : \"This is a test\", \"Boolean\" : false, \"Null\" : null }");
EXPECT_NO_THROW(json::Reader::Read(obj, doc));
EXPECT_NO_THROW(json::Reader::Read(root, doc));
json::Object& obj = root;
EXPECT_NO_THROW(obj["String"]);
EXPECT_STREQ("This is a test", static_cast<std::string>(obj["String"]).c_str());
EXPECT_FALSE(static_cast<json::Boolean>(obj["Boolean"]));
EXPECT_NO_THROW(static_cast<json::Null>(obj["Null"]));
}
TEST_F(lagi_cajun, Write) {
TEST(lagi_cajun, Write) {
json::Object obj;
obj["Boolean"] = true;
obj["String"] = "This \"is\" \\a \t test";
@ -305,7 +208,7 @@ TEST_F(lagi_cajun, Write) {
EXPECT_STREQ("null", stream.str().c_str());
}
TEST_F(lagi_cajun, ReaderParserErrors) {
TEST(lagi_cajun, ReaderParserErrors) {
json::UnknownElement ue;
std::istringstream missing_comma("[1 2]");
@ -333,7 +236,7 @@ TEST_F(lagi_cajun, ReaderParserErrors) {
EXPECT_NO_THROW(json::Reader::Read(ue, unique_keys));
}
TEST_F(lagi_cajun, ReaderScanErrors) {
TEST(lagi_cajun, ReaderScanErrors) {
json::UnknownElement ue;
std::istringstream doc("[true, false, thiswontwork]");
@ -359,23 +262,23 @@ std::string roundtrip_test(const char *in) {
return oss.str();
}
TEST_F(lagi_cajun, round_double_roundtrips) {
TEST(lagi_cajun, round_double_roundtrips) {
EXPECT_STREQ("1.0", roundtrip_test("1.0").c_str());
}
TEST_F(lagi_cajun, representable_double_roundtrips) {
TEST(lagi_cajun, representable_double_roundtrips) {
EXPECT_STREQ("1.5", roundtrip_test("1.5").c_str());
}
TEST_F(lagi_cajun, int_roundtrips) {
TEST(lagi_cajun, int_roundtrips) {
EXPECT_STREQ("1", roundtrip_test("1").c_str());
}
TEST_F(lagi_cajun, bool_roundtrips) {
TEST(lagi_cajun, bool_roundtrips) {
EXPECT_STREQ("true", roundtrip_test("true").c_str());
EXPECT_STREQ("false", roundtrip_test("false").c_str());
}
TEST_F(lagi_cajun, null_roundtrips) {
TEST(lagi_cajun, null_roundtrips) {
EXPECT_STREQ("null", roundtrip_test("null").c_str());
}

View File

@ -22,6 +22,20 @@
#include <fstream>
static const char default_opt[] = "{\"Valid\" : \"This is valid\"}";
static const char all_types[] = R"raw({
"Integer" : 0,
"Double" : 0.1,
"String" : "",
"Color" : "rgb(0, 0, 0)",
"Boolean" : false,
"Array" : {
"Integer" : [{ "int" : 0 }, {"int" : 0}],
"Double" : [{ "double" : 0.1 }, {"double" : 0.1}],
"String" : [{ "string" : "" }, {"string" : ""}],
"Color" : [{ "color" : "rgb(0,0,0)" }, {"color" : "rgb(0,0,0)"}],
"Boolean" : [{ "bool" : false }, {"bool" : false}]
}
})raw";
class lagi_option : public libagi {
protected:
@ -76,30 +90,14 @@ TEST_F(lagi_option, existent_but_invalid_file_uses_default) {
EXPECT_THROW(opt.Get("Null"), agi::Exception);
}
TEST_F(lagi_option, load_several_config_files) {
agi::Options opt("", default_opt, agi::Options::FLUSH_SKIP);
{ std::ifstream f("data/options/string.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); }
{ std::ifstream f("data/options/integer.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); }
{ std::ifstream f("data/options/double.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); }
{ std::ifstream f("data/options/bool.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); }
EXPECT_NO_THROW(opt.Get("String")->GetString());
EXPECT_NO_THROW(opt.Get("Integer")->GetInt());
EXPECT_NO_THROW(opt.Get("Double")->GetDouble());
EXPECT_NO_THROW(opt.Get("Bool")->GetBool());
}
TEST_F(lagi_option, arrays) {
agi::Options opt("", default_opt, agi::Options::FLUSH_SKIP);
{ std::ifstream f("data/options/array_string.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); }
{ std::ifstream f("data/options/array_integer.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); }
{ std::ifstream f("data/options/array_double.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); }
{ std::ifstream f("data/options/array_bool.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); }
agi::Options opt("", all_types, agi::Options::FLUSH_SKIP);
EXPECT_NO_THROW(opt.Get("String")->GetListString());
EXPECT_NO_THROW(opt.Get("Integer")->GetListInt());
EXPECT_NO_THROW(opt.Get("Double")->GetListDouble());
EXPECT_NO_THROW(opt.Get("Bool")->GetListBool());
EXPECT_NO_THROW(opt.Get("Array/String")->GetListString());
EXPECT_NO_THROW(opt.Get("Array/Integer")->GetListInt());
EXPECT_NO_THROW(opt.Get("Array/Double")->GetListDouble());
EXPECT_NO_THROW(opt.Get("Array/Boolean")->GetListBool());
EXPECT_NO_THROW(opt.Get("Array/Color")->GetListColor());
}
TEST_F(lagi_option, bad_default_throws_and_null_is_rejected) {
@ -107,7 +105,7 @@ TEST_F(lagi_option, bad_default_throws_and_null_is_rejected) {
}
TEST_F(lagi_option, nested_options) {
const char conf[] = "{ \"a\" : { \"b\" : { \"c\" : { \"c\" : \"value\" } } } }";
const char conf[] = R"raw({ "a" : { "b" : { "c" : { "c" : "value" } } } })raw";
ASSERT_NO_THROW(agi::Options("", conf, agi::Options::FLUSH_SKIP));
agi::Options opt("", conf, agi::Options::FLUSH_SKIP);
ASSERT_NO_THROW(opt.Get("a/b/c/c"));
@ -134,28 +132,22 @@ TEST_F(lagi_option, flush_roundtrip) {
agi::fs::Remove("data/options/tmp");
{
agi::Options opt("data/options/tmp", "{}");
{ std::ifstream f("data/options/all_types.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); }
agi::Options opt("data/options/tmp", all_types);
EXPECT_NO_THROW(opt.Get("Integer")->SetInt(1));
EXPECT_NO_THROW(opt.Get("Double")->SetDouble(1.1));
EXPECT_NO_THROW(opt.Get("String")->SetString("hello"));
EXPECT_NO_THROW(opt.Get("Color")->SetColor(agi::Color("rgb(255,255,255)")));
EXPECT_NO_THROW(opt.Get("Boolean")->SetBool(true));
std::vector<int64_t> int_arr; int_arr.push_back(1);
EXPECT_NO_THROW(opt.Get("Array/Integer")->SetListInt(int_arr));
std::vector<double> double_arr; double_arr.push_back(1.1);
EXPECT_NO_THROW(opt.Get("Array/Double")->SetListDouble(double_arr));
std::vector<std::string> str_arr; str_arr.push_back("hello");
EXPECT_NO_THROW(opt.Get("Array/String")->SetListString(str_arr));
std::vector<agi::Color> clr_arr; clr_arr.push_back(agi::Color("rgb(255,255,255)"));
EXPECT_NO_THROW(opt.Get("Array/Color")->SetListColor(clr_arr));
std::vector<bool> bool_arr; bool_arr.push_back(true);
EXPECT_NO_THROW(opt.Get("Array/Boolean")->SetListBool(bool_arr));
EXPECT_NO_THROW(opt.Get("Array/Integer")->SetListInt({1}));
EXPECT_NO_THROW(opt.Get("Array/Double")->SetListDouble({1.1}));
EXPECT_NO_THROW(opt.Get("Array/String")->SetListString({"hello"}));
EXPECT_NO_THROW(opt.Get("Array/Color")->SetListColor({agi::Color("rgb(255,255,255)")}));
EXPECT_NO_THROW(opt.Get("Array/Boolean")->SetListBool({true}));
}
{
agi::Options opt("data/options/tmp", "{}");
agi::Options opt("data/options/tmp", all_types);
ASSERT_NO_THROW(opt.ConfigUser());
EXPECT_EQ(1, opt.Get("Integer")->GetInt());
@ -187,10 +179,6 @@ TEST_F(lagi_option, mixed_valid_and_invalid_in_user_conf_loads_all_valid) {
EXPECT_EQ(true, opt.Get("3")->GetBool());
}
TEST_F(lagi_option, empty_array_works) {
EXPECT_NO_THROW(agi::Options("", "{ \"arr\" : [] }", agi::Options::FLUSH_SKIP));
}
TEST_F(lagi_option, empty_object_works) {
EXPECT_NO_THROW(agi::Options("", "{ \"obj\" : {} }", agi::Options::FLUSH_SKIP));
}
@ -215,60 +203,6 @@ struct empty_arr_options : public agi::Options {
empty_arr_options() : agi::Options("", "{ \"arr\" : [] }", agi::Options::FLUSH_SKIP) { }
};
TEST_F(lagi_option, empty_array_decays_to_first_used_type) {
ASSERT_NO_THROW(empty_arr_options());
{
empty_arr_options opt;
EXPECT_NO_THROW(opt.Get("arr")->GetListBool());
EXPECT_THROW(opt.Get("arr")->GetListColor(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListString(), agi::InternalError);
}
{
empty_arr_options opt;
EXPECT_NO_THROW(opt.Get("arr")->GetListColor());
EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListString(), agi::InternalError);
}
{
empty_arr_options opt;
EXPECT_NO_THROW(opt.Get("arr")->GetListDouble());
EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListColor(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListString(), agi::InternalError);
}
{
empty_arr_options opt;
EXPECT_NO_THROW(opt.Get("arr")->GetListInt());
EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListColor(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListString(), agi::InternalError);
}
{
empty_arr_options opt;
EXPECT_NO_THROW(opt.Get("arr")->GetListString());
EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListColor(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::InternalError);
EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::InternalError);
}
}
#define CHECK_TYPE(str, type) \
do { \
agi::Options opt("", "{ \"" str "\" : \"" str "\" }", agi::Options::FLUSH_SKIP); \