view C++/tests/Json/main.cpp @ 407:25ef13e25338

Json: remove undefined, not part of JSON spec
author David Demelier <markand@malikania.fr>
date Tue, 06 Oct 2015 09:05:53 +0200
parents d485d36a8de1
children ee155fc84c56
line wrap: on
line source

/*
 * main.cpp -- test the jansson wrapper
 *
 * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <set>
#include <unordered_map>

#include <gtest/gtest.h>

#include "Json.h"

/* --------------------------------------------------------
 * Miscellaneous
 * -------------------------------------------------------- */

TEST(Misc, copy)
{
	json::Object object;

	object.insert("integer", 123);
	object.insert("true", true);

	json::Object object2{object};

	ASSERT_TRUE(object2.isObject());
	ASSERT_EQ(123, object2["integer"].toInt());
	ASSERT_TRUE(object2["true"].toBool());
}

TEST(Misc, copyAssign)
{
	json::Object object;

	{
		json::Object tmp;

		tmp.insert("integer", 123);
		tmp.insert("true", true);

		object = tmp;
	}

	ASSERT_TRUE(object.isObject());
	ASSERT_EQ(123, object["integer"].toInt());
	ASSERT_TRUE(object["true"].toBool());
}

TEST(Misc, move)
{
	json::Object object(123);
	json::Object object2(std::move(object));

	ASSERT_TRUE(object2.isInt());
	ASSERT_EQ(123, object2.toInt());
}

TEST(Misc, moveAssign)
{
	json::Object object(123);
	json::Object object2;

	object2 = std::move(object);

	ASSERT_TRUE(object2.isInt());
	ASSERT_EQ(123, object2.toInt());
}

/* --------------------------------------------------------
 * json::Value constructors
 * -------------------------------------------------------- */

TEST(Constructors, null)
{
	try {
		json::Value value{nullptr};

		ASSERT_TRUE(value.isNull());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(Constructors, boolean)
{
	try {
		json::Value value{true};

		ASSERT_TRUE(value.isBool());
		ASSERT_TRUE(value.toBool());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(Constructors, integer)
{
	try {
		json::Value value{123};

		ASSERT_TRUE(value.isInt());
		ASSERT_TRUE(value.isNumber());
		ASSERT_TRUE(value.isNumber());
		ASSERT_EQ(123, value.toInt());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(Constructors, real)
{
	try {
		json::Value value{9.2};

		ASSERT_TRUE(value.isNumber());
		ASSERT_TRUE(value.isNumber());
		ASSERT_FALSE(value.isInt());
		ASSERT_EQ(9.2, value.toReal());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(Constructors, string)
{
	try {
		json::Value value("hello");

		ASSERT_TRUE(value.isString());
		ASSERT_EQ("hello", value.toString());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

/* --------------------------------------------------------
 * Object
 * -------------------------------------------------------- */

TEST(Object, set)
{
	try {
		json::Object object;

		object.insert("integer", 123);
		object.insert("string", "hello");
		object.insert("true", true);

		ASSERT_EQ(123, object["integer"].toInt());
		ASSERT_EQ("hello", object["string"].toString());
		ASSERT_TRUE(object["true"].toBool());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(Object, clear)
{
	try {
		json::Object object;

		object.insert("integer", 123);
		object.insert("string", "hello");
		object.insert("true", true);

		object.clear();

		ASSERT_EQ(0, static_cast<int>(object.size()));
		ASSERT_FALSE(object.contains("integer"));
		ASSERT_FALSE(object.contains("string"));
		ASSERT_FALSE(object.contains("true"));
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(Object, erase)
{
	try {
		json::Object object;

		object.insert("integer", 123);
		object.insert("string", "hello");
		object.insert("true", true);

		object.erase("integer");

		ASSERT_EQ(2, static_cast<int>(object.size()));
		ASSERT_FALSE(object.contains("integer"));
		ASSERT_TRUE(object.contains("string"));
		ASSERT_TRUE(object.contains("true"));
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(ObjectInitializer, simple)
{
	try {
		json::Object object{
			{ "username", "jean"	},
			{ "age", 99		}
		};

		ASSERT_EQ(2, static_cast<int>(object.size()));
		ASSERT_EQ("jean", object["username"].toString());
		ASSERT_EQ(99, object["age"].toInt());
	} catch (const std::exception &ex) {
		FAIL() << ex.what();
	}
}

TEST(ObjectInitializer, deep)
{
	try {
		json::Object object{
			{ "username", "jean"		},
			{ "age", 99			},
			{ "network", json::Object{
					{ "port", 9999 		},
					{ "host", "localhost"	}
				}
			}
		};

		// First
		ASSERT_EQ(3, static_cast<int>(object.size()));
		ASSERT_EQ("jean", object["username"].toString());
		ASSERT_EQ(99, object["age"].toInt());

		// Second
		json::Object network = object["network"].toObject();
		ASSERT_TRUE(network.isObject());
		ASSERT_EQ(2, static_cast<int>(network.size()));
		ASSERT_EQ(9999, network["port"].toInt());
		ASSERT_EQ("localhost", network["host"].toString());
	} catch (const std::exception &ex) {
		FAIL() << ex.what();
	}
}

/* --------------------------------------------------------
 * Array
 * -------------------------------------------------------- */

TEST(Array, push)
{
	try {
		json::Array array;

		ASSERT_TRUE(array.isArray());

		array.push(1);
		array.push("hello");
		array.push(true);

		ASSERT_EQ(3, static_cast<int>(array.size()));
		ASSERT_TRUE(array[0].toBool());
		ASSERT_EQ("hello", array[1].toString());
		ASSERT_EQ(1, array[2].toInt());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(Array, append)
{
	try {
		json::Array array;

		ASSERT_TRUE(array.isArray());

		array.append(1);
		array.append("hello");
		array.append(true);

		ASSERT_EQ(3, static_cast<int>(array.size()));
		ASSERT_EQ(1, array[0].toInt());
		ASSERT_EQ("hello", array[1].toString());
		ASSERT_TRUE(array[2].toBool());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(Array, insert)
{
	try {
		json::Array array;

		ASSERT_TRUE(array.isArray());

		array.insert(0, 1);
		array.insert(1, "hello");
		array.insert(0, true);

		ASSERT_EQ(3, static_cast<int>(array.size()));
		ASSERT_TRUE(array[0].toBool());
		ASSERT_EQ(1, array[1].toInt());
		ASSERT_EQ("hello", array[2].toString());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(Array, clear)
{
	try {
		json::Array array;

		array.append(1);
		array.append("hello");
		array.append(true);

		array.clear();

		ASSERT_EQ(0, static_cast<int>(array.size()));
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(Array, erase)
{
	try {
		json::Array array;

		array.append(1);
		array.append("hello");
		array.append(true);

		array.erase(0);

		ASSERT_EQ(2, static_cast<int>(array.size()));
		ASSERT_EQ("hello", array[0].toString());
		ASSERT_TRUE(array[1].toBool());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

#if 0

TEST(Array, eraseIterator)
{
	try {
		json::Array array;

		array.append(1);
		array.append("hello");
		array.append(true);

		array.erase(array.begin());

		ASSERT_EQ(2, static_cast<int>(array.size()));
		ASSERT_EQ("hello", array[0].toString());
		ASSERT_TRUE(array[1].toBool());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(Array, eraseConstIterator)
{
	try {
		json::Array array;

		array.append(1);
		array.append("hello");
		array.append(true);

		array.erase(array.cbegin());

		ASSERT_EQ(2, static_cast<int>(array.size()));
		ASSERT_EQ("hello", array[0].toString());
		ASSERT_TRUE(array[1].toBool());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

#endif

TEST(ArrayInitializer, simple)
{
	try {
		json::Array array{123, true, "hello"};

		ASSERT_EQ(3, static_cast<int>(array.size()));
		ASSERT_EQ(123, array[0].toInt());
		ASSERT_TRUE(array[1].toBool());
		ASSERT_EQ("hello", array[2].toString());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(ArrayInitializer, deep)
{
	try {
		json::Array array{
			123,
			true,
			"hello",
			json::Array{
				321,
				false,
				"olleh"
			}
		};

		// First
		ASSERT_EQ(4, static_cast<int>(array.size()));
		ASSERT_EQ(123, array[0].toInt());
		ASSERT_TRUE(array[1].toBool());
		ASSERT_EQ("hello", array[2].toString());

		// Second
		json::Array array2 = array[3].toArray();
		ASSERT_TRUE(array.isArray());
		ASSERT_EQ(3, static_cast<int>(array2.size()));
		ASSERT_EQ(321, array2[0].toInt());
		ASSERT_FALSE(array2[1].toBool());
		ASSERT_EQ("olleh", array2[2].toString());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

/* --------------------------------------------------------
 * I/O
 * -------------------------------------------------------- */

TEST(FileRead, simple)
{
	try {
		json::Document doc(json::File{"Json/simple.json"});

		ASSERT_TRUE(doc.isObject());
		ASSERT_FALSE(doc.isArray());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(FileRead, fail)
{
	try {
		json::Document(json::File{"Json/notexist.json"});

		FAIL() << "Exception expected";
	} catch (const json::Error &) {
	}
}

#if 0

TEST(FileWrite, simple)
{
	try {
		json::Object object{
			{ "name", "jean" },
			{ "age", 99 }
		};

		object.write(std::ofstream("object-write.json"));

		json::Object object2 = json::Document(json::File("object-write.json")).toObject();

		ASSERT_EQ(object2, object);
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

#endif

TEST(StringRead, simple)
{
	try {
		json::Document doc(json::Buffer{"{ \"license\": \"ISC\" }"});

		ASSERT_TRUE(doc.isObject());
		ASSERT_FALSE(doc.isArray());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST(StringRead, fail)
{
	try {
		json::Document(json::Buffer{"{ \"license\": ISC }"});

		FAIL() << "Exception expected";
	} catch (const json::Error &ex) {
	}
}

#if 0

TEST(StringWrite, simple)
{
	try {
		json::Object object{
			{ "name", "jean" },
			{ "age", 99 }
		};

		json::Object object2 = json::Document(object.dump()).toObject();

		ASSERT_EQ(object2, object);
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

#endif

/* --------------------------------------------------------
 * Object read
 * -------------------------------------------------------- */

class ObjectRead : public testing::Test {
protected:
	json::Object m_object;
	json::Object m_objectAll;

public:
	ObjectRead()
	{
		m_object = json::Document(json::File{"Json/object.json"}).toObject();
		m_objectAll = json::Document(json::File{"Json/object-all.json"}).toObject();
	}
};

TEST_F(ObjectRead, simple)
{
	try {
		json::Value name = m_object["name"];
		json::Value description = m_object["description"];

		ASSERT_TRUE(name.isString());
		ASSERT_TRUE(description.isString());
		ASSERT_EQ("simple", name.toString());
		ASSERT_EQ("basic JSON file", description.toString());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST_F(ObjectRead, all)
{
	try {
		ASSERT_TRUE(m_objectAll.contains("integer"));
		ASSERT_FALSE(m_objectAll.contains("unexistant"));

		ASSERT_TRUE(m_objectAll["integer"].isInt());
		ASSERT_TRUE(m_objectAll["integer"].isNumber());
		ASSERT_EQ(123, m_objectAll["integer"].toInt());

		ASSERT_TRUE(m_objectAll["real"].isNumber());
		ASSERT_TRUE(m_objectAll["real"].isNumber());
		ASSERT_EQ(9.2, m_objectAll["real"].toReal());

		ASSERT_TRUE(m_objectAll["false"].isBool());
		ASSERT_FALSE(m_objectAll["false"].toBool());
		ASSERT_FALSE(m_objectAll["false"].toBool());

		ASSERT_TRUE(m_objectAll["true"].isBool());
		ASSERT_TRUE(m_objectAll["true"].toBool());
		ASSERT_TRUE(m_objectAll["true"].toBool());

		ASSERT_TRUE(m_objectAll["null"].isNull());

		ASSERT_TRUE(m_objectAll["object"].isObject());
		ASSERT_TRUE(m_objectAll["array"].isArray());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

/* --------------------------------------------------------
 * Array read
 * -------------------------------------------------------- */

class ArrayRead : public testing::Test {
protected:
	json::Array m_array;
	json::Array m_arrayAll;

public:
	ArrayRead()
	{
		m_array = json::Document(json::File{"Json/array.json"}).toArray();
		m_arrayAll = json::Document(json::File{"Json/array-all.json"}).toArray();
	}
};

TEST_F(ArrayRead, simple)
{
	try {
		ASSERT_EQ(3, static_cast<int>(m_array.size()));
		ASSERT_EQ(1, m_array[0].toInt());
		ASSERT_EQ(2, m_array[1].toInt());
		ASSERT_EQ(3, m_array[2].toInt());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

TEST_F(ArrayRead, all)
{
	try {
		ASSERT_TRUE(m_arrayAll[0].isInt());
		ASSERT_TRUE(m_arrayAll[0].isNumber());
		ASSERT_EQ(123, m_arrayAll[0].toInt());

		ASSERT_TRUE(m_arrayAll[1].isNumber());
		ASSERT_TRUE(m_arrayAll[1].isNumber());
		ASSERT_EQ(9.2, m_arrayAll[1].toReal());

		ASSERT_TRUE(m_arrayAll[2].isBool());
		ASSERT_FALSE(m_arrayAll[2].toBool());
		ASSERT_FALSE(m_arrayAll[2].toBool());

		ASSERT_TRUE(m_arrayAll[3].isBool());
		ASSERT_TRUE(m_arrayAll[3].toBool());
		ASSERT_TRUE(m_arrayAll[3].toBool());

		ASSERT_TRUE(m_arrayAll[4].isNull());

		ASSERT_TRUE(m_arrayAll[5].isObject());
		ASSERT_TRUE(m_arrayAll[6].isArray());
	} catch (const json::Error &ex) {
		FAIL() << ex.what();
	}
}

/* --------------------------------------------------------
 * Object iterators
 * -------------------------------------------------------- */

class ObjectIteratorsTest : public testing::Test {
protected:
	json::Object m_object;

public:
	ObjectIteratorsTest()
	{
		m_object.insert("integer", 1);
		m_object.insert("string", "hello");
		m_object.insert("boolean", true);
	}
};

TEST_F(ObjectIteratorsTest, operators)
{
	// Read only (non const)
	{
		std::set<std::string> expected{"boolean", "integer", "string"};
		std::set<std::string> result;
		std::unordered_map<std::string, json::Value> values;
		auto it = m_object.begin();
		auto end = m_object.end();

		while (it != end) {
			values.insert({it->first, it->second});
			result.insert((*it++).first);
		}

		ASSERT_EQ(expected, result);
		ASSERT_EQ(1, values["integer"].toInt());
		ASSERT_EQ("hello", values["string"].toString());
		ASSERT_TRUE(values["boolean"].toBool());
	}

	// Read only (const)
	{
		std::set<std::string> expected{"boolean", "integer", "string"};
		std::set<std::string> result;
		std::unordered_map<std::string, json::Value> values;
		auto it = m_object.cbegin();
		auto end = m_object.cend();

		while (it != end) {
			values.insert({it->first, it->second});
			result.insert((*it++).first);
		}

		ASSERT_EQ(expected, result);
		ASSERT_EQ(1, values["integer"].toInt());
		ASSERT_EQ("hello", values["string"].toString());
		ASSERT_TRUE(values["boolean"].toBool());
	}
}

TEST_F(ObjectIteratorsTest, assign)
{
	// Assign (non const)
	{
		auto it = m_object.begin();
		auto key = it->first;

		it->second = json::Value("CHANGED");

		ASSERT_EQ("CHANGED", m_object[key].toString());
		ASSERT_EQ("CHANGED", it->second.toString());
		ASSERT_EQ(3, static_cast<int>(m_object.size()));
	}
}

#if 0

TEST_F(ObjectIteratorsTest, assignConst)
{
	// Assign (const)
	{
		auto it = m_object.cbegin();
		auto key = it->first;
		auto orig = it->second;

		it->second = json::Value("CHANGED");

		ASSERT_TRUE(m_object.contains(key));
		ASSERT_EQ(orig, m_object[key]);
		ASSERT_EQ(3, static_cast<int>(m_object.size()));
	}
}

#endif

/* --------------------------------------------------------
 * Array iterators
 * -------------------------------------------------------- */

class ArrayIteratorsTest : public testing::Test {
protected:
	json::Array m_array;

public:
	ArrayIteratorsTest()
	{
		m_array.append(1);
		m_array.append("hello");
		m_array.append(true);
	}
};

TEST_F(ArrayIteratorsTest, operators)
{
	// Read only (non const)
	{
		auto it = m_array.begin();

		ASSERT_EQ(1, (*it).toInt());
		ASSERT_EQ(1, it->toInt());
		ASSERT_EQ("hello", it[1].toString());
		ASSERT_TRUE(it[2].toBool());

		auto it2 = it + 1;
		ASSERT_EQ(1, it2[-1].toInt());
		ASSERT_EQ("hello", it2->toString());
		ASSERT_TRUE(it2[1].toBool());

		auto it3 = it;
		ASSERT_TRUE(it2 != it);
		ASSERT_FALSE(it3 != it);

		ASSERT_FALSE(it2 == it);
		ASSERT_TRUE(it3 == it);

		ASSERT_TRUE(it3 >= it);
		ASSERT_TRUE(it2 >= it);

		ASSERT_TRUE(it2 > it);
		ASSERT_FALSE(it3 > it);

		ASSERT_FALSE(it2 <= it);
		ASSERT_TRUE(it3 <= it);

		ASSERT_FALSE(it2 < it);
		ASSERT_FALSE(it3 < it);
	}

	// Read only (const)
	{
		auto it = m_array.cbegin();

		ASSERT_EQ(1, (*it).toInt());
		ASSERT_EQ(1, it->toInt());
		ASSERT_EQ("hello", it[1].toString());
		ASSERT_TRUE(it[2].toBool());

		auto it2 = it + 1;
		ASSERT_EQ(1, it2[-1].toInt());
		ASSERT_EQ("hello", it2->toString());
		ASSERT_TRUE(it2[1].toBool());

		auto it3 = it;
		ASSERT_TRUE(it2 != it);
		ASSERT_FALSE(it3 != it);

		ASSERT_FALSE(it2 == it);
		ASSERT_TRUE(it3 == it);

		ASSERT_TRUE(it3 >= it);
		ASSERT_TRUE(it2 >= it);

		ASSERT_TRUE(it2 > it);
		ASSERT_FALSE(it3 > it);

		ASSERT_FALSE(it2 <= it);
		ASSERT_TRUE(it3 <= it);

		ASSERT_FALSE(it2 < it);
		ASSERT_FALSE(it3 < it);
	}
}

TEST_F(ArrayIteratorsTest, assign)
{
	// Assign (non const)
	{
		auto it = m_array.begin();

		*it = json::Value(9999);

		ASSERT_EQ(3, static_cast<int>(m_array.size()));
		ASSERT_EQ(9999, it->toInt());
		ASSERT_EQ(9999, m_array[0].toInt());
	}
}

#if 0

TEST_F(ArrayIteratorsTest, assignConst)
{
	// Assign (const)
	{
		auto it = m_array.cbegin();

		*it = json::Value(9999);

		ASSERT_EQ(3, static_cast<int>(m_array.size()));
		ASSERT_EQ(1, it->toInt());
		ASSERT_EQ(1, m_array[0].toInt());
	}
}

#endif

TEST_F(ArrayIteratorsTest, castToRef)
{
	json::Array array{1, 2, 3};
	int i = 1;

	for (const json::Value &v : array)
	{
		ASSERT_EQ(i++, v.toInt());
	}
}

int main(int argc, char **argv)
{
	testing::InitGoogleTest(&argc, argv);

	return RUN_ALL_TESTS();
}