blob: 9078110493306ebbcccc41307f24213fe590ee85 [file] [log] [blame] [edit]
/*
* Copyright 2021 Daniel R. Carvalho
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <gmock/gmock.h>
#include <gtest/gtest-spi.h>
#include <gtest/gtest.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <array>
#include <cassert>
#include <cstdint>
#include <deque>
#include <fstream>
#include <list>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/compiler.hh"
#include "base/gtest/cur_tick_fake.hh"
#include "base/gtest/logging.hh"
#include "base/gtest/serialization_fixture.hh"
#include "sim/serialize.hh"
using namespace gem5;
// Instantiate the mock class to have a valid curTick of 0
GTestTickHandler tickHandler;
/** @return Whether the given dir exists and is accessible. */
bool
dirExists(std::string dir)
{
struct stat info;
return (stat(dir.c_str(), &info) == 0) && (info.st_mode & S_IFDIR);
}
using SerializeFixture = SerializationFixture;
class CheckpointInFixture : public SerializationFixture
{
public:
std::unique_ptr<CheckpointIn> cpt;
using SerializeFixture::SerializeFixture;
void
SetUp() override
{
SerializeFixture::SetUp();
std::ofstream file(getCptPath());
assert(file.good());
file << R"cpt_file(
[General]
Test1=BARasdf
Test2=bar
[Junk]
Test3=yo
Test4=mama
[Foo]
Foo1=89
Foo2=384
[General]
Test3=89
[Junk]
Test4+=mia
)cpt_file";
file.close();
cpt = std::make_unique<CheckpointIn>(getDirName());
}
void
TearDown() override
{
std::remove((getCptPath()).c_str());
SerializeFixture::TearDown();
}
};
/**
* A fixture to handle checkpoint in and out variables, as well as the
* testing of the temporary directory.
*/
class SerializableFixture : public SerializeFixture
{
public:
std::unique_ptr<CheckpointIn> cpt_in;
std::unique_ptr<std::ofstream> cpt_out;
using SerializeFixture::SerializeFixture;
void
SetUp() override
{
SerializeFixture::SetUp();
cpt_out = std::make_unique<std::ofstream>(getCptPath());
assert(cpt_out->good());
cpt_in = std::make_unique<CheckpointIn>(getDirName());
}
void
TearDown() override
{
std::remove((getCptPath()).c_str());
SerializeFixture::TearDown();
}
};
using SerializeFixtureDeathTest = SerializeFixture;
using CheckpointInFixtureDeathTest = CheckpointInFixture;
using SerializableFixtureDeathTest = SerializableFixture;
/** Tests that when setting a checkpoint dir it always ends with a slash. */
TEST(CheckpointInTest, SetGetDirSlash)
{
ASSERT_EQ(CheckpointIn::setDir(""), "/");
ASSERT_EQ(CheckpointIn::dir(), "/");
ASSERT_EQ(CheckpointIn::setDir("/"), "/");
ASSERT_EQ(CheckpointIn::dir(), "/");
ASSERT_EQ(CheckpointIn::setDir("test_cpt_dir"), "test_cpt_dir/");
ASSERT_EQ(CheckpointIn::dir(), "test_cpt_dir/");
ASSERT_EQ(CheckpointIn::setDir("test_cpt_dir_2/"), "test_cpt_dir_2/");
ASSERT_EQ(CheckpointIn::dir(), "test_cpt_dir_2/");
}
/**
* Tests that when the dir name has a "%d" curTick is added. It may also
* work with other formats, but it is not intended.
*/
TEST(CheckpointInTest, SetGetDirTick)
{
ASSERT_EQ(CheckpointIn::setDir("tick%d"), "tick0/");
ASSERT_EQ(CheckpointIn::dir(), "tick0/");
ASSERT_EQ(CheckpointIn::setDir("ti%dck"), "ti0ck/");
ASSERT_EQ(CheckpointIn::dir(), "ti0ck/");
}
/**
* Test constructor failure by requesting the creation of a checkpoint in
* a non-existent dir.
*/
TEST_F(SerializeFixtureDeathTest, ConstructorFailure)
{
// Assign another cpt dir to make sure that it is changed when a new
// CheckpointIn instance is created
CheckpointIn::setDir("test");
// Make sure file does not exist, so that the constructor will fail
std::ifstream file(getCptPath());
assert(!file.good());
ASSERT_ANY_THROW(CheckpointIn cpt(getDirName()));
}
/**
* Test constructor success by requesting the creation of a checkpoint
* in a specific valid dir. The static cpt dir is change on instantiation.
*/
TEST_F(SerializeFixture, ConstructorSuccess)
{
// Assign another cpt dir to make sure that it is changed when a new
// CheckpointIn instance is created
CheckpointIn::setDir("test");
// Create the file before creating the cpt to make sure it exists
std::ofstream file(getCptPath());
assert(file.good());
file.close();
CheckpointIn cpt(getDirName());
// When a new CheckpointIn instance is created the static cpt dir changes
EXPECT_EQ(CheckpointIn::dir(), getDirName());
}
/**
* Test that changing the static cpt dir does not change the name of the
* cpt dir of previously created instances.
*/
TEST_F(CheckpointInFixtureDeathTest, GetCptDir)
{
ASSERT_ANY_THROW(CheckpointIn("/random_dir_name"));
}
/** Test finding sections. */
TEST_F(CheckpointInFixture, FindSections)
{
// Successful searches
ASSERT_TRUE(cpt->sectionExists("General"));
ASSERT_TRUE(cpt->sectionExists("Junk"));
ASSERT_TRUE(cpt->sectionExists("Foo"));
// Failed searches
ASSERT_FALSE(cpt->sectionExists("Junk2"));
ASSERT_FALSE(cpt->sectionExists("Test1"));
}
/** Test finding entries. */
TEST_F(CheckpointInFixture, FindEntries)
{
// Successful searches
ASSERT_TRUE(cpt->entryExists("General", "Test2"));
ASSERT_TRUE(cpt->entryExists("Junk", "Test3"));
ASSERT_TRUE(cpt->entryExists("Junk", "Test4"));
ASSERT_TRUE(cpt->entryExists("General", "Test1"));
ASSERT_TRUE(cpt->entryExists("General", "Test3"));
// Failed searches
ASSERT_FALSE(cpt->entryExists("Junk2", "test3"));
ASSERT_FALSE(cpt->entryExists("Junk", "test4"));
}
/** Test extracting the values of entries. */
TEST_F(CheckpointInFixture, ExtractEntries)
{
std::string value;
// Successful searches
ASSERT_TRUE(cpt->find("General", "Test2", value));
ASSERT_EQ(value, "bar");
ASSERT_TRUE(cpt->find("Junk", "Test3", value));
ASSERT_EQ(value, "yo");
ASSERT_TRUE(cpt->find("Junk", "Test4", value));
ASSERT_EQ(value, "mama mia");
ASSERT_TRUE(cpt->find("General", "Test1", value));
ASSERT_EQ(value, "BARasdf");
ASSERT_TRUE(cpt->find("General", "Test3", value));
ASSERT_EQ(value, "89");
// Failed searches
ASSERT_FALSE(cpt->find("Junk2", "test3", value));
ASSERT_FALSE(cpt->find("Junk", "test4", value));
}
/**
* Test that paths are increased and decreased according to the scope that
* its SCS was created in (using CheckpointIn).
*/
TEST_F(CheckpointInFixture, SCSCptInPathScoped)
{
Serializable::ScopedCheckpointSection scs(*(cpt.get()), "Section1");
ASSERT_EQ(Serializable::currentSection(), "Section1");
{
Serializable::ScopedCheckpointSection scs_2(*(cpt.get()), "Section2");
ASSERT_EQ(Serializable::currentSection(), "Section1.Section2");
Serializable::ScopedCheckpointSection scs_3(*(cpt.get()), "Section3");
ASSERT_EQ(Serializable::currentSection(),
"Section1.Section2.Section3");
}
Serializable::ScopedCheckpointSection scs_4(*(cpt.get()), "Section4");
ASSERT_EQ(Serializable::currentSection(), "Section1.Section4");
}
/**
* Test that paths are increased and decreased according to the scope that
* its SCS was created in (using CheckpointOut).
*/
TEST_F(SerializeFixture, SCSCptOutPathScoped)
{
std::ofstream cpt(getCptPath());
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
ASSERT_EQ(Serializable::currentSection(), "Section1");
{
Serializable::ScopedCheckpointSection scs_2(cpt, "Section2");
ASSERT_EQ(Serializable::currentSection(), "Section1.Section2");
Serializable::ScopedCheckpointSection scs_3(cpt, "Section3");
ASSERT_EQ(Serializable::currentSection(),
"Section1.Section2.Section3");
}
Serializable::ScopedCheckpointSection scs_4(cpt, "Section4");
ASSERT_EQ(Serializable::currentSection(), "Section1.Section4");
}
/**
* Make sure that a SCS that uses a CheckpointIn does not change the
* checkpoint file's contents.
*/
TEST_F(SerializeFixture, SCSNoChangeCptIn)
{
{
// Create empty cpt file
std::ofstream os(getCptPath());
assert(os.good());
os.close();
}
CheckpointIn cpt(getDirName());
std::ifstream is(getCptPath());
assert(is.good());
ASSERT_EQ(is.peek(), std::ifstream::traits_type::eof());
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
ASSERT_EQ(is.peek(), std::ifstream::traits_type::eof());
ASSERT_EQ(Serializable::currentSection(), "Section1");
}
/** @return The ostream as a std::string. */
std::string
getString(std::istream &is)
{
auto buf = is.rdbuf();
std::ostringstream oss;
oss << buf;
return oss.str();
}
/**
* Flushes the checkpoint and reads its contents.
*
* @param cpt The checkpoint to be flushed.
* @param filename The name of the file to be read.
* @return The contents in the filename, as a string.
*/
std::string
getContents(std::ofstream &cpt, std::string filename)
{
// The checkpoint must be flushed, otherwise the file may not be up-
// to-date and the assertions below will fail
cpt.flush();
std::ifstream is(filename);
assert(is.good());
return std::string(std::istreambuf_iterator<char>(is),
std::istreambuf_iterator<char>());
}
/**
* Make sure that a SCS that uses a CheckpointOut changes the checkpoint
* file's contents (single section).
*/
TEST_F(SerializableFixture, SCSChangeCptOutSingle)
{
Serializable::ScopedCheckpointSection scs(*cpt_out, "Section1");
ASSERT_EQ(getContents(*cpt_out, getCptPath()),
"\n[Section1]\n");
}
/**
* Make sure that a SCS that uses a CheckpointOut changes the checkpoint
* file's contents (multiple sections).
*/
TEST_F(SerializableFixture, SCSChangeCptOutMultiple)
{
std::string expected = "";
Serializable::ScopedCheckpointSection scs(*cpt_out, "Section1");
expected += "\n[Section1]\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
{
Serializable::ScopedCheckpointSection scs_2(*cpt_out, "Section2");
expected += "\n[Section1.Section2]\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
Serializable::ScopedCheckpointSection scs_3(*cpt_out, "Section3");
expected += "\n[Section1.Section2.Section3]\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
}
Serializable::ScopedCheckpointSection scs_4(*cpt_out, "Section4");
expected += "\n[Section1.Section4]\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
}
/**
* Make sure that a SCS that uses a CheckpointOut changes the checkpoint
* file's contents (large test).
*/
TEST_F(SerializableFixture, SCSChangeCptOutLarge)
{
std::string expected = "";
Serializable::ScopedCheckpointSection scs(*cpt_out, "Section1");
expected += "\n[Section1]\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
{
Serializable::ScopedCheckpointSection scs_2(*cpt_out, "Section2");
expected += "\n[Section1.Section2]\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
{
Serializable::ScopedCheckpointSection scs_3(*cpt_out, "Section3");
expected += "\n[Section1.Section2.Section3]\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()),
expected);
}
Serializable::ScopedCheckpointSection scs_4(*cpt_out, "Section4");
expected += "\n[Section1.Section2.Section4]\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
}
Serializable::ScopedCheckpointSection scs_5(*cpt_out, "Section5");
expected += "\n[Section1.Section5]\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
{
Serializable::ScopedCheckpointSection scs_6(*cpt_out, "Section6");
expected += "\n[Section1.Section5.Section6]\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
Serializable::ScopedCheckpointSection scs_7(*cpt_out, "Section7");
expected += "\n[Section1.Section5.Section6.Section7]\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
}
Serializable::ScopedCheckpointSection scs_8(*cpt_out, "Section8");
expected += "\n[Section1.Section5.Section8]\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
}
/** Test failure to create dir. Try to create a dir in a non-existent dir. */
TEST(SerializableDeathTest, GenerateCptOutFail)
{
std::ofstream cpt;
const std::string dir_name =
SerializeFixture::generateTempDirName();
ASSERT_FALSE(dirExists(dir_name));
ASSERT_ANY_THROW(Serializable::generateCheckpointOut(
dir_name + "/b/a/n/a/n/a/", cpt));
}
/** Test successful CheckpointOut generation with non-existent dir. */
TEST_F(SerializeFixture, GenerateCptOut)
{
// The fixture will auto-create the dir. Remove it.
assert(rmdir(getDirName().c_str()) == 0);
std::ofstream cpt;
Serializable::generateCheckpointOut(getDirName(), cpt);
ASSERT_TRUE(dirExists(getDirName()));
// Make sure the checkpoint was properly created. Using EXPECT to
// force removing the directory at the end
EXPECT_NE(getContents(cpt, getCptPath()).find(
"## checkpoint generated: "), std::string::npos);
}
/** Test successful CheckpointOut generation with existing dir. */
TEST_F(SerializeFixture, GenerateCptOutExistent)
{
assert(dirExists(getDirName()));
// Create the checkpoint and make sure it has been properly created by
// deleting it and making sure the function was successful
std::ofstream cpt;
Serializable::generateCheckpointOut(getDirName(), cpt);
EXPECT_TRUE(remove((getCptPath()).c_str()) == 0);
}
class SerializableType : public Serializable
{
private:
mutable bool _serialized = false;
bool _unserialized = false;
public:
SerializableType() : Serializable() {}
void serialize(CheckpointOut &cp) const override { _serialized = true; }
void unserialize(CheckpointIn &cp) override { _unserialized = true; }
/**
* Checks if serialize() has been called and then marks it as not called.
*
* @return True if serialize() has been called.
*/
bool
checkAndResetSerialized()
{
const bool serialized = _serialized;
_serialized = false;
return serialized;
}
/**
* Checks if unserialize() has been called and then marks it as not called.
*
* @return True if unserialize() has been called.
*/
bool
checkAndResetUnserialized()
{
const bool unserialized = _unserialized;
_unserialized = false;
return unserialized;
}
};
/**
* Test section serialization and unserialization for an object without
* serializable contents. Since how (un)serialize() works is independent of
* the Serializable class, we just make sure that the respective functions
* are called when calling (un)serializeSection().
*/
TEST_F(SerializableFixture, SectionSerializationSimple)
{
SerializableType serializable;
// Serialization
{
serializable.serializeSection(*cpt_out, "Section1");
ASSERT_EQ(getContents(*cpt_out, getCptPath()),
"\n[Section1]\n");
ASSERT_TRUE(serializable.checkAndResetSerialized());
ASSERT_FALSE(serializable.checkAndResetUnserialized());
serializable.serializeSection(*cpt_out, "Section2");
ASSERT_EQ(getContents(*cpt_out, getCptPath()),
"\n[Section1]\n\n[Section2]\n");
ASSERT_TRUE(serializable.checkAndResetSerialized());
ASSERT_FALSE(serializable.checkAndResetUnserialized());
serializable.serializeSection(*cpt_out, "Section3");
ASSERT_EQ(getContents(*cpt_out, getCptPath()),
"\n[Section1]\n\n[Section2]\n\n[Section3]\n");
ASSERT_TRUE(serializable.checkAndResetSerialized());
ASSERT_FALSE(serializable.checkAndResetUnserialized());
}
// Unserialization. Since the object has no serializable contents,
// just make sure it does not throw for existent and non-existent
// sections
{
ASSERT_NO_THROW(serializable.unserializeSection(*cpt_in, "Section1"));
ASSERT_FALSE(serializable.checkAndResetSerialized());
ASSERT_TRUE(serializable.checkAndResetUnserialized());
ASSERT_NO_THROW(serializable.unserializeSection(*cpt_in, "Section2"));
ASSERT_FALSE(serializable.checkAndResetSerialized());
ASSERT_TRUE(serializable.checkAndResetUnserialized());
ASSERT_NO_THROW(serializable.unserializeSection(*cpt_in, "Section3"));
ASSERT_FALSE(serializable.checkAndResetSerialized());
ASSERT_TRUE(serializable.checkAndResetUnserialized());
ASSERT_NO_THROW(serializable.unserializeSection(*cpt_in, "Section4"));
ASSERT_FALSE(serializable.checkAndResetSerialized());
ASSERT_TRUE(serializable.checkAndResetUnserialized());
}
}
/**
* Test that paramIn called on a param that does not exist triggers an error.
*/
TEST_F(SerializableFixtureDeathTest, ParamIn)
{
int unserialized_integer;
Serializable::ScopedCheckpointSection scs(*cpt_in, "Section1");
ASSERT_ANY_THROW(paramIn(*cpt_in, "Param1", unserialized_integer));
}
/**
* Test serialization followed by unserialization, using ParamInImpl and
* ParamOut.
*/
TEST_F(SerializableFixture, ParamOutIn)
{
const int integer = 5;
const double real = 3.7;
const bool boolean = true;
const std::string str = "string test";
const char character = 'c';
// Serialization
{
Serializable::ScopedCheckpointSection scs(*cpt_out, "Section1");
std::string expected = "\n[Section1]\n";
paramOut(*cpt_out, "Param1", integer);
expected += "Param1=5\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
paramOut(*cpt_out, "Param2", real);
expected += "Param2=3.7\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
paramOut(*cpt_out, "Param3", boolean);
expected += "Param3=true\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
paramOut(*cpt_out, "Param4", str);
expected += "Param4=string test\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
paramOut(*cpt_out, "Param5", character);
expected += "Param5=99\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
}
// Unserialization
{
CheckpointIn cpt(getDirName());
int unserialized_integer;
double unserialized_real;
bool unserialized_boolean;
std::string unserialized_str;
char unserialized_character;
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
paramIn(cpt, "Param1", unserialized_integer);
ASSERT_EQ(integer, unserialized_integer);
paramIn(cpt, "Param2", unserialized_real);
ASSERT_EQ(real, unserialized_real);
paramIn(cpt, "Param3", unserialized_boolean);
ASSERT_EQ(boolean, unserialized_boolean);
paramIn(cpt, "Param4", unserialized_str);
ASSERT_EQ(str, unserialized_str);
paramIn(cpt, "Param5", unserialized_character);
ASSERT_EQ(character, unserialized_character);
}
}
/**
* Test serialization followed by unserialization, using ParamInImpl and
* ParamOut, when multiple sections exist.
*/
TEST_F(SerializableFixture, ParamOutInMultipleSections)
{
const int integer = 5;
const double real = 3.7;
const bool boolean = true;
const std::string str = "string test";
const char character = 'c';
// Serialization
{
Serializable::ScopedCheckpointSection scs(*cpt_out, "Section1");
std::string expected = "\n[Section1]\n";
paramOut(*cpt_out, "Param1", integer);
expected += "Param1=5\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
paramOut(*cpt_out, "Param2", real);
expected += "Param2=3.7\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
{
Serializable::ScopedCheckpointSection scs_2(*cpt_out, "Section2");
expected += "\n[Section1.Section2]\n";
paramOut(*cpt_out, "Param3", boolean);
expected += "Param3=true\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()),
expected);
}
// Possibly unexpected behavior: Since scs_2 has gone out of scope
// the user may expect that we'd go back to Section1; however, this
// is not the case, and Param4 is added to Section1.Section2
paramOut(*cpt_out, "Param4", str);
expected += "Param4=string test\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()), expected);
{
Serializable::ScopedCheckpointSection scs_3(*cpt_out, "Section3");
expected += "\n[Section1.Section3]\n";
Serializable::ScopedCheckpointSection scs_4(*cpt_out, "Section4");
expected += "\n[Section1.Section3.Section4]\n";
paramOut(*cpt_out, "Param5", character);
expected += "Param5=99\n";
ASSERT_EQ(getContents(*cpt_out, getCptPath()),
expected);
}
}
// Unserialization
{
CheckpointIn cpt(getDirName());
int unserialized_integer;
double unserialized_real;
bool unserialized_boolean;
std::string unserialized_str;
char unserialized_character;
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
paramIn(cpt, "Param1", unserialized_integer);
ASSERT_EQ(integer, unserialized_integer);
paramIn(cpt, "Param2", unserialized_real);
ASSERT_EQ(real, unserialized_real);
{
Serializable::ScopedCheckpointSection scs_2(cpt, "Section2");
paramIn(cpt, "Param3", unserialized_boolean);
ASSERT_EQ(boolean, unserialized_boolean);
// Due to the reason mentioned above on serialization, this param
// must be extracted within the scope of Section 2
paramIn(cpt, "Param4", unserialized_str);
ASSERT_EQ(str, unserialized_str);
}
{
Serializable::ScopedCheckpointSection scs_3(cpt, "Section3");
Serializable::ScopedCheckpointSection scs_4(cpt, "Section4");
paramIn(cpt, "Param5", unserialized_character);
ASSERT_EQ(character, unserialized_character);
}
}
}
/** Test optional parameters. */
TEST_F(SerializeFixture, OptParamOutIn)
{
const double real = 3.7;
const std::string str = "string test";
// Serialization
{
std::ofstream cpt(getCptPath());
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
paramOut(cpt, "Param2", real);
paramOut(cpt, "Param4", str);
}
// Unserialization
{
CheckpointIn cpt(getDirName());
int unserialized_integer;
double unserialized_real;
bool unserialized_boolean;
std::string unserialized_str;
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
// Optional without warning
gtestLogOutput.str("");
ASSERT_FALSE(optParamIn(cpt, "Param1", unserialized_integer, false));
ASSERT_EQ(gtestLogOutput.str(), "");
gtestLogOutput.str("");
ASSERT_TRUE(optParamIn(cpt, "Param2", unserialized_real, false));
ASSERT_EQ(real, unserialized_real);
ASSERT_EQ(gtestLogOutput.str(), "");
// Optional with default request for warning
gtestLogOutput.str("");
ASSERT_FALSE(optParamIn(cpt, "Param3", unserialized_boolean));
ASSERT_THAT(gtestLogOutput.str(),
::testing::HasSubstr("warn: optional parameter Section1:Param3 "
"not present\n"));
gtestLogOutput.str("");
ASSERT_TRUE(optParamIn(cpt, "Param4", unserialized_str));
ASSERT_EQ(str, unserialized_str);
ASSERT_EQ(gtestLogOutput.str(), "");
// Explicit request for warning
gtestLogOutput.str("");
ASSERT_FALSE(optParamIn(cpt, "Param5", unserialized_boolean, true));
ASSERT_THAT(gtestLogOutput.str(),
::testing::HasSubstr("warn: optional parameter Section1:Param5 "
"not present\n"));
}
}
/** Check for death when there is no section and paramIn is requested. */
TEST_F(SerializableFixtureDeathTest, NoSectionParamIn)
{
#ifndef NDEBUG
GTEST_SKIP() << "Skipping as assertions are "
"stripped out of fast builds";
#endif
std::ofstream of(getCptPath());
CheckpointIn cpt(getDirName());
int unserialized_integer;
ASSERT_DEATH(paramIn(cpt, "Param1", unserialized_integer), "");
}
/** Test arrayParamOut and arrayParamIn. */
TEST_F(SerializeFixture, ArrayParamOutIn)
{
const int integer[] = {5, 10, 15};
std::array<double, 4> real = {0.1, 1.345, 892.72, 1e+10};
std::list<bool> boolean = {true, false};
std::vector<std::string> str = {"a", "string", "test"};
std::set<uint64_t> uint64 = {12751928501, 13, 111111};
std::deque<uint8_t> uint8 = {17, 42, 255};
// Serialization
{
std::ofstream cpt(getCptPath());
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
std::string expected = "\n[Section1]\n";
arrayParamOut(cpt, "Param1", integer);
expected += "Param1=5 10 15\n";
ASSERT_EQ(getContents(cpt, getCptPath()), expected);
arrayParamOut(cpt, "Param2", real);
expected += "Param2=0.1 1.345 892.72 1e+10\n";
ASSERT_EQ(getContents(cpt, getCptPath()), expected);
arrayParamOut(cpt, "Param3", boolean);
expected += "Param3=true false\n";
ASSERT_EQ(getContents(cpt, getCptPath()), expected);
arrayParamOut(cpt, "Param4", str);
expected += "Param4=a string test\n";
ASSERT_EQ(getContents(cpt, getCptPath()), expected);
arrayParamOut(cpt, "Param5", uint64);
expected += "Param5=13 111111 12751928501\n";
ASSERT_EQ(getContents(cpt, getCptPath()), expected);
arrayParamOut(cpt, "Param6", uint8);
expected += "Param6=17 42 255\n";
ASSERT_EQ(getContents(cpt, getCptPath()), expected);
}
// Unserialization
{
CheckpointIn cpt(getDirName());
int unserialized_integer[3];
std::array<double, 4> unserialized_real;
std::list<bool> unserialized_boolean;
std::vector<std::string> unserialized_str;
std::set<uint64_t> unserialized_uint64;
std::deque<uint8_t> unserialized_uint8;
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
arrayParamIn(cpt, "Param1", unserialized_integer, 3);
ASSERT_THAT(unserialized_integer, testing::ElementsAre(5, 10, 15));
arrayParamIn(cpt, "Param2", unserialized_real.data(),
unserialized_real.size());
ASSERT_EQ(real, unserialized_real);
arrayParamIn(cpt, "Param3", unserialized_boolean);
ASSERT_EQ(boolean, unserialized_boolean);
arrayParamIn(cpt, "Param4", unserialized_str);
ASSERT_EQ(str, unserialized_str);
arrayParamIn(cpt, "Param5", unserialized_uint64);
ASSERT_EQ(uint64, unserialized_uint64);
arrayParamIn(cpt, "Param6", unserialized_uint8);
ASSERT_EQ(uint8, unserialized_uint8);
}
}
/**
* Test arrayParamOut and arrayParamIn for strings with spaces.
* @todo This is broken because spaces are delimiters between array
* entries; so, strings containing spaces are seen as multiple entries
*/
TEST_F(SerializeFixture, DISABLED_ArrayParamOutInSpacedStrings)
{
std::vector<std::string> str = {"a string test", "for", "array param out"};
// Serialization
{
std::ofstream cpt(getCptPath());
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
std::string expected = "\n[Section1]\n";
arrayParamOut(cpt, "Param1", str);
expected += "Param1=string test for array param out\n";
ASSERT_EQ(getContents(cpt, getCptPath()), expected);
}
// Unserialization
{
CheckpointIn cpt(getDirName());
std::vector<std::string> unserialized_str;
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
arrayParamIn(cpt, "Param1", unserialized_str);
ASSERT_EQ(str, unserialized_str);
}
}
/**
* Test that using arrayParamIn with sizes smaller than the container's
* throws an exception.
*/
TEST_F(SerializeFixtureDeathTest, ArrayParamOutInSmaller)
{
// Serialization
{
const int integer[] = {5, 10, 15};
std::ofstream cpt(getCptPath());
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
std::string expected = "\n[Section1]\n";
arrayParamOut(cpt, "Param1", integer);
expected += "Param1=5 10 15\n";
ASSERT_EQ(getContents(cpt, getCptPath()), expected);
}
// Unserialization
{
CheckpointIn cpt(getDirName());
int unserialized_integer[1];
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
ASSERT_ANY_THROW(arrayParamIn(cpt, "Param1", unserialized_integer, 2));
}
}
/** Test mappingParamOut and mappingParamIn with all keys. */
TEST_F(SerializeFixture, MappingParamOutIn)
{
const int integers[] = {10, 32, 100};
std::array<double, 4> reals = {0.1, 1.345, 892.72, 1e+10};
const char* const names_ints[] = {"ten", "thirty-two", "one hundred"};
const char* const names_reals[] = {"first", "second", "third", "fourth"};
// Serialization
{
std::ofstream cpt(getCptPath());
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
std::string expected = "\n[Section1]\n";
mappingParamOut(cpt, "Integers", names_ints, integers, 3);
expected += "\n[Section1.Integers]\nten=10\nthirty-two=32\n"
"one hundred=100\n";
ASSERT_EQ(getContents(cpt, getCptPath()), expected);
mappingParamOut(cpt, "Reals", names_reals, reals.data(), reals.size());
expected += "\n[Section1.Reals]\nfirst=0.1\nsecond=1.345\n"
"third=892.72\nfourth=1e+10\n";
ASSERT_EQ(getContents(cpt, getCptPath()), expected);
}
// Unserialization
{
CheckpointIn cpt(getDirName());
int unserialized_integers[3];
std::array<double, 4> unserialized_reals;
mappingParamIn(cpt, "Section1.Integers", names_ints,
unserialized_integers, 3);
ASSERT_THAT(unserialized_integers, testing::ElementsAre(10, 32, 100));
mappingParamIn(cpt, "Section1.Reals", names_reals,
unserialized_reals.data(), unserialized_reals.size());
ASSERT_EQ(unserialized_reals, reals);
}
}
/** Test that missing keys are ignored on mappingParamIn. */
TEST_F(SerializeFixture, MappingParamOutInMissing)
{
const int integers[] = {10, 32, 100};
std::array<double, 4> reals = {0.1, 1.345, 892.72, 1e+10};
// Serialization
{
const char* const names_ints[] = {"ten", "thirty-two", "one hundred"};
const char* const names_reals[] =
{"first", "second", "third", "fourth"};
std::ofstream cpt(getCptPath());
Serializable::ScopedCheckpointSection scs(cpt, "Section1");
std::string expected = "\n[Section1]\n";
mappingParamOut(cpt, "Integers", names_ints, integers, 3);
expected += "\n[Section1.Integers]\nten=10\nthirty-two=32\n"
"one hundred=100\n";
ASSERT_EQ(getContents(cpt, getCptPath()), expected);
mappingParamOut(cpt, "Reals", names_reals, reals.data(), reals.size());
expected += "\n[Section1.Reals]\nfirst=0.1\nsecond=1.345\n"
"third=892.72\nfourth=1e+10\n";
ASSERT_EQ(getContents(cpt, getCptPath()), expected);
}
// Unserialization
{
const char* const names_ints[] = {"one hundred"};
const char* const names_reals[] = {"first", "third"};
std::array<double, 2> expected_reals = {0.1, 892.72};
CheckpointIn cpt(getDirName());
std::string err;
int unserialized_integers[1];
std::array<double, 2> unserialized_reals;
gtestLogOutput.str("");
mappingParamIn(cpt, "Section1.Integers", names_ints,
unserialized_integers, 1);
ASSERT_THAT(unserialized_integers, testing::ElementsAre(100));
err = gtestLogOutput.str();
ASSERT_THAT(err, ::testing::HasSubstr("warn: unknown entry found in "
"checkpoint: Section1.Integers thirty-two 32\n"));
ASSERT_THAT(err, ::testing::HasSubstr("warn: unknown entry found in "
"checkpoint: Section1.Integers ten 10\n"));
gtestLogOutput.str("");
mappingParamIn(cpt, "Section1.Reals", names_reals,
unserialized_reals.data(), unserialized_reals.size());
ASSERT_EQ(unserialized_reals, expected_reals);
err = gtestLogOutput.str();
ASSERT_THAT(err, ::testing::HasSubstr("warn: unknown entry found in "
"checkpoint: Section1.Reals fourth 1e+10\n"));
ASSERT_THAT(err, ::testing::HasSubstr("warn: unknown entry found in "
"checkpoint: Section1.Reals second 1.345\n"));
}
}
/**
* Test serialization followed by unserialization, using SERIALIZE_SCALAR and
* UNSERIALIZE_SCALAR.
*/
TEST_F(SerializeFixture, SerializeScalar)
{
const int expected_integer = 5;
const double expected_real = 3.7;
const bool expected_boolean = true;
const std::string expected_str = "string test";
// Serialization
{
const int integer = expected_integer;
const double real = expected_real;
const bool boolean = expected_boolean;
const std::string str = expected_str;
std::ofstream cp(getCptPath());
Serializable::ScopedCheckpointSection scs(cp, "Section1");
std::string expected = "\n[Section1]\n";
SERIALIZE_SCALAR(integer);
expected += "integer=5\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
SERIALIZE_SCALAR(real);
expected += "real=3.7\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
SERIALIZE_SCALAR(boolean);
expected += "boolean=true\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
SERIALIZE_SCALAR(str);
expected += "str=string test\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
}
// Unserialization
{
CheckpointIn cp(getDirName());
int integer;
double real;
bool boolean;
std::string str;
Serializable::ScopedCheckpointSection scs(cp, "Section1");
UNSERIALIZE_SCALAR(integer);
ASSERT_EQ(integer, expected_integer);
UNSERIALIZE_SCALAR(real);
ASSERT_EQ(real, expected_real);
UNSERIALIZE_SCALAR(boolean);
ASSERT_EQ(boolean, expected_boolean);
UNSERIALIZE_SCALAR(str);
ASSERT_EQ(str, expected_str);
}
}
/** Test optional parameters with UNSERIALIZE_OPT_SCALAR. */
TEST_F(SerializeFixture, UnserializeOptScalar)
{
const double expected_real = 3.7;
const std::string expected_str = "string test";
// Serialization
{
const double real = expected_real;
const std::string str = expected_str;
std::ofstream cp(getCptPath());
Serializable::ScopedCheckpointSection scs(cp, "Section1");
SERIALIZE_SCALAR(real);
SERIALIZE_SCALAR(str);
}
// Unserialization
{
CheckpointIn cp(getDirName());
int integer;
double real;
bool boolean;
std::string str;
Serializable::ScopedCheckpointSection scs(cp, "Section1");
// Optional without warning
ASSERT_FALSE(UNSERIALIZE_OPT_SCALAR(integer));
ASSERT_TRUE(UNSERIALIZE_OPT_SCALAR(real));
ASSERT_EQ(real, expected_real);
// Optional with default request for warning
ASSERT_FALSE(UNSERIALIZE_OPT_SCALAR(boolean));
ASSERT_TRUE(UNSERIALIZE_OPT_SCALAR(str));
ASSERT_EQ(str, expected_str);
}
}
/**
* Test serialization followed by unserialization, using SERIALIZE_ENUM and
* UNSERIALIZE_ENUM.
*/
TEST_F(SerializeFixture, SerializeEnum)
{
enum Number
{
ZERO,
TEN=10,
THIRTY_TWO=32
};
const Number expected_val = ZERO;
const Number expected_val_2 = THIRTY_TWO;
// Serialization
{
const Number zero = expected_val;
const Number thirty_two = expected_val_2;
std::ofstream cp(getCptPath());
Serializable::ScopedCheckpointSection scs(cp, "Section1");
std::string expected = "\n[Section1]\n";
SERIALIZE_ENUM(zero);
expected += "zero=0\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
SERIALIZE_ENUM(thirty_two);
expected += "thirty_two=32\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
}
// Unserialization
{
CheckpointIn cp(getDirName());
Number zero;
Number thirty_two;
Serializable::ScopedCheckpointSection scs(cp, "Section1");
UNSERIALIZE_ENUM(zero);
ASSERT_EQ(zero, expected_val);
UNSERIALIZE_ENUM(thirty_two);
ASSERT_EQ(thirty_two, expected_val_2);
}
}
/** Test SERIALIZE_ARRAY and UNSERIALIZE_ARRAY. */
TEST_F(SerializeFixture, SerializeArray)
{
std::array<double, 4> expected_real = {0.1, 1.345, 892.72, 1e+10};
// Serialization
{
const int integer[] = {5, 10, 15};
const double *real = expected_real.data();
std::ofstream cp(getCptPath());
Serializable::ScopedCheckpointSection scs(cp, "Section1");
std::string expected = "\n[Section1]\n";
SERIALIZE_ARRAY(integer, 3);
expected += "integer=5 10 15\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
SERIALIZE_ARRAY(real, expected_real.size());
expected += "real=0.1 1.345 892.72 1e+10\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
}
// Unserialization
{
CheckpointIn cp(getDirName());
int integer[3];
std::array<double, 4> real_array;
double *real = real_array.data();
Serializable::ScopedCheckpointSection scs(cp, "Section1");
UNSERIALIZE_ARRAY(integer, 3);
ASSERT_THAT(integer, testing::ElementsAre(5, 10, 15));
UNSERIALIZE_ARRAY(real, expected_real.size());
ASSERT_EQ(real_array, expected_real);
}
}
/** Test SERIALIZE_CONTAINER and UNSERIALIZE_CONTAINER. */
TEST_F(SerializeFixture, SerializeContainer)
{
std::list<bool> expected_boolean = {true, false};
std::vector<std::string> expected_str = {"a", "string", "test"};
std::set<uint64_t> expected_uint64 = {12751928501, 13, 111111};
std::deque<uint8_t> expected_uint8 = {17, 42, 255};
// Serialization
{
const std::list<bool> boolean = expected_boolean;
const std::vector<std::string> str = expected_str;
const std::set<uint64_t> uint64 = expected_uint64;
const std::deque<uint8_t> uint8 = expected_uint8;
std::ofstream cp(getCptPath());
Serializable::ScopedCheckpointSection scs(cp, "Section1");
std::string expected = "\n[Section1]\n";
SERIALIZE_CONTAINER(boolean);
expected += "boolean=true false\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
SERIALIZE_CONTAINER(str);
expected += "str=a string test\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
SERIALIZE_CONTAINER(uint64);
expected += "uint64=13 111111 12751928501\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
SERIALIZE_CONTAINER(uint8);
expected += "uint8=17 42 255\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
}
// Unserialization
{
CheckpointIn cp(getDirName());
std::list<bool> boolean;
std::vector<std::string> str;
std::set<uint64_t> uint64;
std::deque<uint8_t> uint8;
Serializable::ScopedCheckpointSection scs(cp, "Section1");
UNSERIALIZE_CONTAINER(boolean);
ASSERT_EQ(boolean, expected_boolean);
UNSERIALIZE_CONTAINER(str);
ASSERT_EQ(str, expected_str);
UNSERIALIZE_CONTAINER(uint64);
ASSERT_EQ(uint64, expected_uint64);
UNSERIALIZE_CONTAINER(uint8);
ASSERT_EQ(uint8, expected_uint8);
}
}
/** Test SERIALIZE_MAPPING and UNSERIALIZE_MAPPING with all keys. */
TEST_F(SerializeFixture, SerializeMapping)
{
const int expected_integers[] = {10, 32, 100};
std::array<double, 4> expected_reals = {0.1, 1.345, 892.72, 1e+10};
const char* const names_ints[] = {"ten", "thirty-two", "one hundred"};
const char* const names_reals[] = {"first", "second", "third", "fourth"};
// Serialization
{
const int *integers = expected_integers;
const double *reals = expected_reals.data();
std::ofstream cp(getCptPath());
Serializable::ScopedCheckpointSection scs(cp, "Section1");
std::string expected = "\n[Section1]\n";
SERIALIZE_MAPPING(integers, names_ints, 3);
expected += "\n[Section1.integers]\nten=10\nthirty-two=32\n"
"one hundred=100\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
SERIALIZE_MAPPING(reals, names_reals, expected_reals.size());
expected += "\n[Section1.reals]\nfirst=0.1\nsecond=1.345\n"
"third=892.72\nfourth=1e+10\n";
ASSERT_EQ(getContents(cp, getCptPath()), expected);
}
// Unserialization
{
CheckpointIn cp(getDirName());
int integers[3];
double reals[4];
Serializable::ScopedCheckpointSection scs(cp, "Section1");
UNSERIALIZE_MAPPING(integers, names_ints, 3);
ASSERT_THAT(integers, testing::ElementsAre(10, 32, 100));
UNSERIALIZE_MAPPING(reals, names_reals, expected_reals.size());
ASSERT_THAT(reals, testing::ElementsAre(0.1, 1.345, 892.72, 1e+10));
}
}