Files
create/tests/src/App/StringHasher.cpp

1654 lines
41 KiB
C++

// SPDX-License-Identifier: LGPL-2.1-or-later
#include "App/MappedName.h"
#include "gtest/gtest.h"
#include <App/StringHasher.h>
#include <App/StringHasherPy.h>
#include <App/StringIDPy.h>
#include <QCryptographicHash>
#include <array>
class StringIDTest: public ::testing::Test
{
protected:
// void SetUp() override {}
// void TearDown() override {}
static App::StringID givenFlaggedStringID(App::StringID::Flag flag)
{
const long value {42};
const QByteArray data {"data", 4};
return App::StringID {value, data, flag};
}
};
TEST_F(StringIDTest, stringIDManualConstructionNoFlags)// NOLINT
{
// Arrange
const long expectedValue {42};
const QByteArray expectedData {"data", 4};
// Act
auto id = App::StringID(expectedValue, expectedData);
// Assert
EXPECT_EQ(expectedValue, id.value());
EXPECT_EQ(expectedData, id.data());
EXPECT_FALSE(id.isBinary());
}
TEST_F(StringIDTest, stringIDManualConstructionWithFlag)// NOLINT
{
// Arrange
const long expectedValue {42};
const QByteArray expectedData {"data", 4};
const App::StringID::Flags expectedFlags {App::StringID::Flag::Binary};
// Act
auto id = App::StringID(expectedValue, expectedData, expectedFlags);
// Assert
EXPECT_EQ(expectedValue, id.value());
EXPECT_EQ(expectedData, id.data());
EXPECT_TRUE(id.isBinary());
}
TEST_F(StringIDTest, stringIDDefaultConstruction)// NOLINT
{
// Arrange & Act
auto id = App::StringID();
// Assert
EXPECT_EQ(0, id.value());
}
TEST_F(StringIDTest, value)// NOLINT
{
// Arrange
const long expectedValueA {0};
auto idA = App::StringID(expectedValueA, nullptr);
const long expectedValueB {42};
auto idB = App::StringID(expectedValueB, nullptr);
const long expectedValueC {314159};
auto idC = App::StringID(expectedValueC, nullptr);
// Act
auto valueA = idA.value();
auto valueB = idB.value();
auto valueC = idC.value();
// Assert
EXPECT_EQ(expectedValueA, valueA);
EXPECT_EQ(expectedValueB, valueB);
EXPECT_EQ(expectedValueC, valueC);
}
TEST_F(StringIDTest, relatedIDs)// NOLINT
{
// Nothing to test -- relatedIDs are storage-only in this class
}
TEST_F(StringIDTest, isBinary)// NOLINT
{
// Arrange
auto flaggedID = givenFlaggedStringID(App::StringID::Flag::Binary);
auto controlID = App::StringID {};
// Act & Assert
EXPECT_TRUE(flaggedID.isBinary());
EXPECT_FALSE(controlID.isBinary());
}
TEST_F(StringIDTest, isHashed)// NOLINT
{
// Arrange
auto flaggedID = givenFlaggedStringID(App::StringID::Flag::Hashed);
auto controlID = App::StringID {};
// Act & Assert
EXPECT_TRUE(flaggedID.isHashed());
EXPECT_FALSE(controlID.isHashed());
}
TEST_F(StringIDTest, isPostfixed)// NOLINT
{
// Arrange
auto flaggedID = givenFlaggedStringID(App::StringID::Flag::Postfixed);
auto controlID = App::StringID {};
// Act & Assert
EXPECT_TRUE(flaggedID.isPostfixed());
EXPECT_FALSE(controlID.isPostfixed());
}
TEST_F(StringIDTest, isPostfixEncoded)// NOLINT
{
// Arrange
auto flaggedID = givenFlaggedStringID(App::StringID::Flag::PostfixEncoded);
auto controlID = App::StringID {};
// Act & Assert
EXPECT_TRUE(flaggedID.isPostfixEncoded());
EXPECT_FALSE(controlID.isPostfixEncoded());
}
TEST_F(StringIDTest, isIndexed)// NOLINT
{
// Arrange
auto flaggedID = givenFlaggedStringID(App::StringID::Flag::Indexed);
auto controlID = App::StringID {};
// Act & Assert
EXPECT_TRUE(flaggedID.isIndexed());
EXPECT_FALSE(controlID.isIndexed());
}
TEST_F(StringIDTest, isPrefixID)// NOLINT
{
// Arrange
auto flaggedID = givenFlaggedStringID(App::StringID::Flag::PrefixID);
auto controlID = App::StringID {};
// Act & Assert
EXPECT_TRUE(flaggedID.isPrefixID());
EXPECT_FALSE(controlID.isPrefixID());
}
TEST_F(StringIDTest, isPrefixIDIndex)// NOLINT
{
// Arrange
auto flaggedID = givenFlaggedStringID(App::StringID::Flag::PrefixIDIndex);
auto controlID = App::StringID {};
// Act & Assert
EXPECT_TRUE(flaggedID.isPrefixIDIndex());
EXPECT_FALSE(controlID.isPrefixIDIndex());
}
TEST_F(StringIDTest, isMarked)// NOLINT
{
// Arrange
auto flaggedID = givenFlaggedStringID(App::StringID::Flag::Marked);
auto controlID = App::StringID {};
// Act & Assert
EXPECT_TRUE(flaggedID.isMarked());
EXPECT_FALSE(controlID.isMarked());
}
TEST_F(StringIDTest, isPersistent)// NOLINT
{
// Arrange
auto flaggedID = givenFlaggedStringID(App::StringID::Flag::Persistent);
auto controlID = App::StringID {};
// Act & Assert
EXPECT_TRUE(flaggedID.isPersistent());
EXPECT_FALSE(controlID.isPersistent());
}
TEST_F(StringIDTest, isFromSameHasher)// NOLINT
{
// Nothing to test except when used by StringHasher
}
TEST_F(StringIDTest, getHasher)// NOLINT
{
// Nothing to test except when used by StringHasher
}
TEST_F(StringIDTest, data)// NOLINT
{
// Arrange
QByteArray expectedData {"data", 4};
auto id = App::StringID(1, expectedData);
// Act
auto data = id.data();
// Assert
EXPECT_EQ(expectedData, data);
}
TEST_F(StringIDTest, postfix)// NOLINT
{
// Nothing to test except when used by StringHasher
}
TEST_F(StringIDTest, getPyObject)// NOLINT
{
// Arrange
Py_Initialize();
auto id = new App::StringID(1, nullptr);
id->ref();
// Act
Py::Object py(id->getPyObject(), true);
id->unref();
// Assert
EXPECT_TRUE(PyObject_TypeCheck(py.ptr(), &App::StringIDPy::Type));
}
TEST_F(StringIDTest, getPyObjectWithIndex)// NOLINT
{
// Arrange
Py_Initialize();
auto id = new App::StringID(1, nullptr);
id->ref();
// Act
Py::Object py(id->getPyObjectWithIndex(2), true);
id->unref();
// Assert
ASSERT_TRUE(PyObject_TypeCheck(py.ptr(), &App::StringIDPy::Type));
}
TEST_F(StringIDTest, toStringWithoutIndex)// NOLINT
{
// Arrange
const long bigHex = 0xfcad10;
auto idA = App::StringID(1, QByteArray {"data", 4});
auto idB = App::StringID(bigHex, QByteArray {"data", 4});
// Act
auto resultA = idA.toString();
auto resultB = idB.toString();
// Assert
EXPECT_EQ(std::string("#1"), resultA);
EXPECT_EQ(std::string("#fcad10"), resultB);// Make sure result is in hex
}
TEST_F(StringIDTest, toStringWithIndex)// NOLINT
{
// Arrange
const long bigHex = 0xfcad10;
auto id = App::StringID(1, QByteArray {"data", 4});
// Act
auto resultA = id.toString(bigHex);
auto resultB = id.toString(0);
// Assert
EXPECT_EQ(std::string("#1:fcad10"), resultA);
EXPECT_EQ(std::string("#1"), resultB);
}
TEST_F(StringIDTest, fromStringWithEOFAndLengthGood)// NOLINT
{
// Arrange
const std::string testString {"#1:fcad"};
// Act
auto result =
App::StringID::fromString(testString.c_str(), true, static_cast<int>(testString.length()));
// Assert
EXPECT_EQ(result.id, 1);
EXPECT_EQ(result.index, 0xfcad);
}
TEST_F(StringIDTest, fromStringExtraData)// NOLINT
{
// Arrange
const std::string testString {"#1:fcad#2:bad"};
// Act
auto trueResult =
App::StringID::fromString(testString.c_str(), true, static_cast<int>(testString.length()));
auto falseResult =
App::StringID::fromString(testString.c_str(), false, static_cast<int>(testString.length()));
// Assert
EXPECT_EQ(trueResult.id, -1);
EXPECT_EQ(falseResult.id, 1);
}
TEST_F(StringIDTest, fromStringLengthUnspecified)// NOLINT
{
// Arrange
const std::string testString {"#1:fcad#2:bad"};
// Act
auto trueResult = App::StringID::fromString(testString.c_str(), true);
auto falseResult = App::StringID::fromString(testString.c_str(), false);
// Assert
EXPECT_EQ(trueResult.id, -1);
EXPECT_EQ(falseResult.id, 1);
}
TEST_F(StringIDTest, fromStringShorterLength)// NOLINT
{
// Arrange
const int dataLength {7};
const std::string testString {"#1:fcad#2:bad"};
// Act
auto trueResult = App::StringID::fromString(testString.c_str(), true, dataLength);
auto falseResult = App::StringID::fromString(testString.c_str(), false, dataLength);
// Assert
EXPECT_EQ(trueResult.id, 1);
EXPECT_EQ(falseResult.id, 1);
}
TEST_F(StringIDTest, fromStringNoHashtag)// NOLINT
{
// Arrange
const std::string testString {"1:fcad"};
// Act
auto result = App::StringID::fromString(testString.c_str(), true);
// Assert
EXPECT_EQ(result.id, -1);
}
TEST_F(StringIDTest, fromStringNotHex)// NOLINT
{
// Arrange
const std::string testStringA {"1:freecad"};
const std::string testStringB {"zoink:2"};
// Act
auto resultA = App::StringID::fromString(testStringA.c_str(), false);
auto resultB = App::StringID::fromString(testStringB.c_str(), false);
// Assert
EXPECT_EQ(resultA.id, -1);
EXPECT_EQ(resultB.id, -1);
}
TEST_F(StringIDTest, fromStringQByteArray)// NOLINT
{
// Arrange
const QByteArray testString {"#1:fcad", 7};
// Act
auto result = App::StringID::fromString(testString, true);
// Assert
EXPECT_EQ(result.id, 1);
EXPECT_EQ(result.index, 0xfcad);
}
TEST_F(StringIDTest, dataToTextHashed)// NOLINT
{
// Arrange
QByteArray buffer {"120ca87015d849dbea060eaf2295fcc4ee981427", 40};// NOLINT
auto id = App::StringID(1, buffer, App::StringID::Flag::Hashed);
// Act
auto result = id.dataToText(0);
// Assert
EXPECT_EQ(result, buffer.toBase64().constData());
}
TEST_F(StringIDTest, dataToTextBinary)// NOLINT
{
// Arrange
QByteArray buffer {"120ca87015d849dbea060eaf2295fcc4ee981427", 40};// NOLINT
auto id = App::StringID(1, buffer, App::StringID::Flag::Binary);
// Act
auto result = id.dataToText(0);
// Assert
EXPECT_EQ(result, buffer.toBase64().constData());
}
TEST_F(StringIDTest, dataToTextNoIndex)// NOLINT
{
// Arrange
QByteArray data {"data", 4};
auto id = App::StringID(1, data);
// Act
auto result = id.dataToText(0);
// Assert
EXPECT_EQ(result, "data");
}
TEST_F(StringIDTest, dataToTextWithIndex)// NOLINT
{
// Arrange
QByteArray data {"data", 4};
auto id = App::StringID(1, data);
// Act
auto resultA = id.dataToText(1);
auto resultB = id.dataToText(1024);// NOLINT
// Assert
EXPECT_EQ(resultA, "data1");
EXPECT_EQ(resultB, "data1024");// Not hex!
}
TEST_F(StringIDTest, dataToTextWithPostfix)// NOLINT
{
// Arrange
QByteArray data {"data", 4};
QByteArray postfix {"postfix", 7};// NOLINT
auto id = App::StringID(1, data);
id.setPostfix(postfix);
// Act
auto result = id.dataToText(1);
// Assert
EXPECT_EQ(result, "data1postfix");
}
TEST_F(StringIDTest, dataToBytesNoIndex)// NOLINT
{
// Arrange
QByteArray data {"data", 4};
auto id = App::StringID(1, data);
// Act
auto result = id.dataToBytes();
// Assert
EXPECT_EQ(data, result);
}
TEST_F(StringIDTest, dataToBytesWithIndex)// NOLINT
{
// Arrange
QByteArray data {"data", 4};
const int index {1234};
auto id = App::StringID(1, data);
// Act
auto result = id.dataToBytes(index);
// Assert
EXPECT_EQ(data + QByteArray::number(index), result);
}
TEST_F(StringIDTest, dataToBytesWithPostfix)// NOLINT
{
// Arrange
QByteArray data {"data", 4};
QByteArray postfix {"postfix", 7};// NOLINT
auto id = App::StringID(1, data);
id.setPostfix(postfix);
// Act
auto result = id.dataToBytes();
// Assert
EXPECT_EQ(data + postfix, result);
}
TEST_F(StringIDTest, dataToBytesWithIndexAndPostfix)// NOLINT
{
// Arrange
QByteArray data {"data", 4};
QByteArray postfix {"postfix", 7};// NOLINT
const int index {1234};
auto id = App::StringID(1, data);
id.setPostfix(postfix);
// Act
auto result = id.dataToBytes(index);
// Assert
EXPECT_EQ(data + QByteArray::number(index) + postfix, result);
}
TEST_F(StringIDTest, mark)// NOLINT
{
// Arrange
QByteArray data {"data", 4};
auto id = App::StringID(1, data);
ASSERT_FALSE(id.isMarked());
// Act
id.mark();
// Assert
EXPECT_TRUE(id.isMarked());
}
TEST_F(StringIDTest, setPersistent)// NOLINT
{
// Arrange
QByteArray data {"data", 4};
auto id = App::StringID(1, data);
ASSERT_FALSE(id.isPersistent());
// Act
id.setPersistent(true);
// Assert
EXPECT_TRUE(id.isPersistent());
}
TEST_F(StringIDTest, operatorLessThan)// NOLINT
{
// Can't test without a _hasher
}
TEST_F(StringIDTest, compare)// NOLINT
{
// Can't test without a _hasher
}
TEST_F(StringIDTest, IndexIDBooleanConversion)// NOLINT
{
// Arrange
const long id {42};
const int index {123};
App::StringID::IndexID indexIdTrue {id, index};
App::StringID::IndexID indexIdFalse {0, index};
// Act & Assert
EXPECT_TRUE(indexIdTrue);
EXPECT_FALSE(indexIdFalse);
}
TEST_F(StringIDTest, IndexIDStreamInsertionOperator)// NOLINT
{
// Arrange
const long id {42};
const int index {123};
App::StringID::IndexID indexIdNonZero {id, index};
App::StringID::IndexID indexIdZero {id, 0};
std::ostringstream stream;
// Act
stream << indexIdNonZero << " " << indexIdZero;
// Assert
EXPECT_EQ("42:123 42", stream.str());
}
class StringIDRefTest: public ::testing::Test
{
protected:
// void SetUp() override {}
// void TearDown() override {}
App::StringID* createStringID() const
{
return new App::StringID {_id, _data};
}
private:
QByteArray _data {"data", 4};
int _id {1};
};
TEST_F(StringIDRefTest, defaultConstructor)// NOLINT
{
// Arrange & Act
auto idRef = App::StringIDRef();
// Assert
EXPECT_FALSE(idRef);
}
TEST_F(StringIDRefTest, constructFromNewStringID)// NOLINT
{
// Arrange & Act
auto idRef = App::StringIDRef(createStringID());
// Assert
EXPECT_TRUE(idRef);
EXPECT_EQ(1, idRef.getRefCount());
// NOTE: the dynamically-allocated StringID is automatically deallocated by the StringIDRef
// when its destructor is called (upon exit from this test function).
}
TEST_F(StringIDRefTest, constructFromStringIDAndIndex)// NOLINT
{
// Arrange
const int index {42};
// Act
auto idRef = App::StringIDRef(createStringID(), index);
// Assert
EXPECT_TRUE(idRef);
EXPECT_EQ(1, idRef.getRefCount());
EXPECT_EQ(index, idRef.getIndex());
// NOTE: the dynamically-allocated StringID is automatically deallocated by the StringIDRef
// when its destructor is called (upon exit from this test function).
}
TEST_F(StringIDRefTest, copyConstructor)// NOLINT
{
// Arrange
const int index {42};
auto idRef = App::StringIDRef(createStringID(), index);
// Act
auto newIdRef = App::StringIDRef(idRef);
// Assert
EXPECT_TRUE(newIdRef);
EXPECT_EQ(2, newIdRef.getRefCount());
EXPECT_EQ(index, idRef.getIndex());
EXPECT_EQ(index, newIdRef.getIndex());
}
TEST_F(StringIDRefTest, copyConstructorWithIndex)// NOLINT
{
// Arrange
const int index {42};
const int otherIndex {12345};
auto idRef = App::StringIDRef(createStringID(), index);
// Act
auto newIdRef = App::StringIDRef(idRef, otherIndex);
// Assert
EXPECT_TRUE(newIdRef);
EXPECT_EQ(2, newIdRef.getRefCount());
EXPECT_EQ(index, idRef.getIndex());
EXPECT_EQ(otherIndex, newIdRef.getIndex());
}
TEST_F(StringIDRefTest, moveConstructor)// NOLINT
{
// Arrange
auto idRef = App::StringIDRef(createStringID());
// Act
auto newIdRef = App::StringIDRef(std::move(idRef));
// Assert
EXPECT_EQ(1, newIdRef.getRefCount());
}
TEST_F(StringIDRefTest, destructor)// NOLINT
{
// Arrange
auto idRef = App::StringIDRef(createStringID());
{
auto newIdRef = App::StringIDRef(idRef);
ASSERT_EQ(2, idRef.getRefCount());// Verify the test setup
// Act
// The scope ends, causing newIdRef destructor execution
}
// Assert
EXPECT_EQ(1, idRef.getRefCount());
}
TEST_F(StringIDRefTest, reset)// NOLINT
{
// Arrange
auto idRef = App::StringIDRef(createStringID());
// Act
idRef.reset();
// Assert
EXPECT_FALSE(idRef);
}
TEST_F(StringIDRefTest, resetWithStringID)// NOLINT
{
// Arrange
const int index {42};
auto idRef = App::StringIDRef(createStringID(), index);
// Act
idRef.reset(createStringID());
// Assert
EXPECT_TRUE(idRef);
EXPECT_NE(index, idRef.getIndex());
}
TEST_F(StringIDRefTest, resetWithStringIDAndIndex)// NOLINT
{
// Arrange
const int indexA {42};
const int indexB {12345};
auto idRef = App::StringIDRef(createStringID(), indexA);
// Act
idRef.reset(createStringID(), indexB);
// Assert
EXPECT_TRUE(idRef);
EXPECT_EQ(indexB, idRef.getIndex());
}
TEST_F(StringIDRefTest, swap)// NOLINT
{
// Arrange
const int indexA {42};
const int indexB {12345};
auto idRefA = App::StringIDRef(createStringID(), indexA);
auto idRefB = App::StringIDRef(createStringID(), indexB);
// Act
idRefA.swap(idRefB);
// Assert
EXPECT_EQ(indexB, idRefA.getIndex());
EXPECT_EQ(indexA, idRefB.getIndex());
}
TEST_F(StringIDRefTest, assignmentFromSelf)// NOLINT
{
// Arrange
auto idRef = App::StringIDRef(createStringID());
// Act
idRef = idRef;
// Assert
EXPECT_EQ(1, idRef.getRefCount());
}
TEST_F(StringIDRefTest, assignmentToEmptyFromStringID)// NOLINT
{
// Arrange
Py_Initialize();
auto idRef = App::StringIDRef();
ASSERT_FALSE(idRef);// Verify setup
// Act
idRef = createStringID();
// Assert
EXPECT_TRUE(idRef);
}
TEST_F(StringIDRefTest, assignmentFromStringIDRef)// NOLINT
{
// Arrange
auto firstIdRef = App::StringIDRef(createStringID());
auto firstIdRefExtra = firstIdRef;
auto secondIdRef = App::StringIDRef(createStringID());
// Act
firstIdRef = secondIdRef;
// Assert
EXPECT_EQ(2, secondIdRef.getRefCount());
EXPECT_EQ(2, firstIdRef.getRefCount());
EXPECT_EQ(1, firstIdRefExtra.getRefCount());
}
TEST_F(StringIDRefTest, moveAssignmentFromStringIDRef)// NOLINT
{
auto emptyIdRef = App::StringIDRef();
auto goodIdRef = App::StringIDRef(createStringID());
ASSERT_FALSE(emptyIdRef);// Verify setup
// Act
emptyIdRef = std::move(goodIdRef);
// Assert
EXPECT_TRUE(emptyIdRef);
EXPECT_EQ(1, emptyIdRef.getRefCount());
}
TEST_F(StringIDRefTest, operatorLess)// NOLINT
{
// Arrange
auto emptySIDA = App::StringIDRef();
auto emptySIDB = App::StringIDRef();
auto lowID = App::StringIDRef(new App::StringID {1, nullptr});
auto highID = App::StringIDRef(new App::StringID {2, nullptr});
// Act & Assert
EXPECT_FALSE(emptySIDA < emptySIDB);
EXPECT_FALSE(emptySIDB < emptySIDA);
EXPECT_TRUE(emptySIDA < lowID);
EXPECT_TRUE(emptySIDA < highID);
EXPECT_TRUE(lowID < highID);
EXPECT_FALSE(highID < lowID);
// NOTE: Cannot test the impact of hasher without a StringHasher
}
TEST_F(StringIDRefTest, operatorEquality)// NOLINT
{
// Arrange
auto emptySIDA = App::StringIDRef();
auto emptySIDB = App::StringIDRef();
auto nonEmptyA = App::StringIDRef(new App::StringID {1, nullptr});
auto nonEmptyB = App::StringIDRef(new App::StringID {1, nullptr});
auto nonEmptyOther = App::StringIDRef(new App::StringID {2, nullptr});
// Act & Assert
EXPECT_TRUE(emptySIDA == emptySIDB);
EXPECT_TRUE(nonEmptyA == nonEmptyB);
EXPECT_FALSE(emptySIDA == nonEmptyA);
EXPECT_FALSE(nonEmptyA == nonEmptyOther);
}
TEST_F(StringIDRefTest, operatorInequality)// NOLINT
{
// Arrange
auto emptySIDA = App::StringIDRef();
auto emptySIDB = App::StringIDRef();
auto nonEmptyA = App::StringIDRef(new App::StringID {1, nullptr});
auto nonEmptyB = App::StringIDRef(new App::StringID {1, nullptr});
auto nonEmptyOther = App::StringIDRef(new App::StringID {2, nullptr});
// Act & Assert
EXPECT_FALSE(emptySIDA != emptySIDB);
EXPECT_FALSE(nonEmptyA != nonEmptyB);
EXPECT_TRUE(emptySIDA != nonEmptyA);
EXPECT_TRUE(nonEmptyA != nonEmptyOther);
}
TEST_F(StringIDRefTest, booleanConversion)// NOLINT
{
// Arrange
auto emptySID = App::StringIDRef();
auto nonEmpty = App::StringIDRef(new App::StringID {1, nullptr});
// Act & Assert
EXPECT_FALSE(emptySID);
EXPECT_TRUE(nonEmpty);
}
TEST_F(StringIDRefTest, getRefCount)// NOLINT
{
// Arrange
auto stringID = createStringID();
auto stringIDRef = App::StringIDRef(stringID);
// Act
auto firstCount = stringIDRef.getRefCount();
auto stringIDRef2 = App::StringIDRef(stringID);
auto secondCount = stringIDRef.getRefCount();
// Assert
EXPECT_EQ(1, firstCount);
EXPECT_EQ(2, secondCount);
}
TEST_F(StringIDRefTest, toString)// NOLINT
{
// Arrange
auto emptySID = App::StringIDRef();
auto nonEmpty = App::StringIDRef(createStringID());
// Act
auto empty = emptySID.toString();
auto nonempty = nonEmpty.toString();
// Assert
// Only confirm that the function call is passed along: the real test is in the StringID class
EXPECT_TRUE(empty.empty());
EXPECT_FALSE(nonempty.empty());
}
TEST_F(StringIDRefTest, dataToText)// NOLINT
{
// Arrange
auto emptySID = App::StringIDRef();
auto nonEmpty = App::StringIDRef(createStringID());
// Act
auto empty = emptySID.dataToText();
auto nonempty = nonEmpty.dataToText();
// Assert
// Only confirm that the function call is passed along: the real test is in the StringID class
EXPECT_TRUE(empty.empty());
EXPECT_FALSE(nonempty.empty());
}
TEST_F(StringIDRefTest, constData)// NOLINT
{
// Arrange
auto sid = App::StringIDRef(createStringID());
// Act
auto constData = sid.constData();
// Assert
ASSERT_NE(constData, nullptr);
EXPECT_STREQ(constData, "data");
}
TEST_F(StringIDRefTest, deref)// NOLINT
{
// Arrange
auto sid = createStringID();
auto ref = App::StringIDRef(sid);
// Act & Assert
EXPECT_EQ(sid, &(ref.deref()));
}
TEST_F(StringIDRefTest, value)// NOLINT
{
// Arrange
auto empty = App::StringIDRef();
auto nonEmpty = App::StringIDRef(createStringID());
// Act
auto emptyValue = empty.value();
auto nonEmptyValue = nonEmpty.value();
// Assert
EXPECT_EQ(0, emptyValue);
EXPECT_NE(0, nonEmptyValue);
}
TEST_F(StringIDRefTest, relatedIDs)// NOLINT
{
// Nothing to test without a StringHasher
}
TEST_F(StringIDRefTest, isBinary)// NOLINT
{
// Arrange
auto nothing = App::StringIDRef();
auto binary = App::StringIDRef(new App::StringID {1, nullptr, App::StringID::Flag::Binary});
auto nonBinary = App::StringIDRef(new App::StringID {1, nullptr, App::StringID::Flag::None});
// Act & Assert
EXPECT_FALSE(nothing.isBinary());
EXPECT_TRUE(binary.isBinary());
EXPECT_FALSE(nonBinary.isBinary());
}
TEST_F(StringIDRefTest, isHashed)// NOLINT
{
// Arrange
auto nothing = App::StringIDRef();
auto hashed = App::StringIDRef(new App::StringID {1, nullptr, App::StringID::Flag::Hashed});
auto nonHashed = App::StringIDRef(new App::StringID {1, nullptr, App::StringID::Flag::None});
// Act & Assert
EXPECT_FALSE(nothing.isHashed());
EXPECT_TRUE(hashed.isHashed());
EXPECT_FALSE(nonHashed.isHashed());
}
TEST_F(StringIDRefTest, toBytes)// NOLINT
{
// Arrange
QByteArray byteStorage;
auto ref = App::StringIDRef(createStringID());
// Act
ref.toBytes(byteStorage);
// Assert
EXPECT_FALSE(byteStorage.isNull());
}
TEST_F(StringIDRefTest, getPyObject)// NOLINT
{
// Arrange
auto ref = App::StringIDRef(createStringID());
auto empty = App::StringIDRef();
// Act
Py::Object pyObject(ref.getPyObject());
Py::Object none(empty.getPyObject());
// Assert
EXPECT_TRUE(PyObject_TypeCheck(pyObject.ptr(), &App::StringIDPy::Type));
EXPECT_EQ(none.ptr(), Py_None);
}
TEST_F(StringIDRefTest, mark)// NOLINT
{
// Arrange
auto ref = App::StringIDRef(createStringID());
ASSERT_FALSE(ref.isMarked());
// Act
ref.mark();
// Assert
EXPECT_TRUE(ref.isMarked());
}
TEST_F(StringIDRefTest, isMarked)// NOLINT
{
// Arrange
auto marked = App::StringIDRef(new App::StringID(1, nullptr, App::StringID::Flag::Marked));
auto notMarked = App::StringIDRef(createStringID());
// Act & Assert
EXPECT_TRUE(marked.isMarked());
EXPECT_FALSE(notMarked.isMarked());
}
TEST_F(StringIDRefTest, isFromSameHasher)// NOLINT
{
// Nothing to test, requires a StringHasher
}
TEST_F(StringIDRefTest, getHasher)// NOLINT
{
// Nothing to test, requires a StringHasher
}
TEST_F(StringIDRefTest, setPersistent)// NOLINT
{
// Arrange
auto persistent = App::StringIDRef(createStringID());
ASSERT_FALSE(persistent.deref().isPersistent());
// Act
persistent.setPersistent(true);
// Assert
ASSERT_TRUE(persistent.deref().isPersistent());
}
class StringHasherTest: public ::testing::Test
{
protected:
void SetUp() override
{
Py_Initialize();
_hasher = Base::Reference<App::StringHasher>(new App::StringHasher);
}
void TearDown() override
{
_hasher->clear();
}
Base::Reference<App::StringHasher> Hasher()
{
return _hasher;
}
static Data::MappedName givenMappedName(const char* name, const char* postfix = nullptr)
{
QByteArray expectedPrefix {name, static_cast<int>(std::strlen(name))};
Data::MappedName mappedName(expectedPrefix);
if (postfix) {
QByteArray expectedPostfix {postfix, static_cast<int>(std::strlen(postfix))};
Data::MappedName mappedNameA(mappedName, expectedPostfix.data());
return mappedNameA;
}
return mappedName;
}
/// Put a couple of things into the hash table: at the end of this call the size of the table
/// is 2, one postfix string and one prefix+postfix combination.
App::StringIDRef givenSomeHashedValues()
{
const std::string prefix {"Test1"};
const std::string postfix {";:M;FUS;:Hb:7,F"};
auto mappedName = givenMappedName(prefix.c_str(), postfix.c_str());
QVector<App::StringIDRef> sids;
return Hasher()->getID(mappedName, sids);
}
private:
Base::Reference<App::StringHasher> _hasher;
};
TEST_F(StringHasherTest, defaultConstructor)// NOLINT
{
// Arrange
// Done in Setup()
// Act
// Done in Setup()
// Assert
EXPECT_EQ(0, Hasher()->size());
}
TEST_F(StringHasherTest, getMemSize)// NOLINT
{
// Arrange
givenSomeHashedValues();
// Act
auto result = Hasher()->getMemSize();
// Assert
// getMemSize is advisory only, so the only thing we can confidently assert is that it is larger
// than the number of values in the hash table.
EXPECT_LT(Hasher()->size(), result);
}
TEST_F(StringHasherTest, Save)// NOLINT
{
// Arrange
// Act
// Assert
}
TEST_F(StringHasherTest, Restore)// NOLINT
{
// Arrange
// Act
// Assert
}
TEST_F(StringHasherTest, SaveDocFile)// NOLINT
{
// Arrange
// Act
// Assert
}
TEST_F(StringHasherTest, RestoreDocFile)// NOLINT
{
// Arrange
// Act
// Assert
}
TEST_F(StringHasherTest, setPersistenceFileName)// NOLINT
{
// Arrange
// Act
// Assert
}
TEST_F(StringHasherTest, getPersistenceFileName)// NOLINT
{
// Arrange
// Act
// Assert
}
TEST_F(StringHasherTest, getIDFromQByteArrayShort)// NOLINT
{
// Arrange
const std::array<char, 5> string {"data"};
QByteArray qba(string.data(), string.size());
Hasher()->setThreshold(string.size() + 1);
// Act
auto id = Hasher()->getID(qba, App::StringHasher::Option::Hashable);
// Assert
EXPECT_STREQ(string.data(), id.constData());
EXPECT_FALSE(id.isHashed());
EXPECT_NE(qba.constData(), id.constData());// A copy was made, the pointers differ
EXPECT_EQ(2, id.getRefCount());
}
TEST_F(StringHasherTest, getIDFromQByteArrayLongHashable)// NOLINT
{
// Arrange
const std::array<char, 47> string {"data that is longer than our hasher threshold"};
QByteArray qba(string.data(), string.size());
Hasher()->setThreshold(string.size() - 1);
// Act
auto id = Hasher()->getID(qba, App::StringHasher::Option::Hashable);
// Assert
EXPECT_STRNE(string.data(), id.constData());
EXPECT_TRUE(id.isHashed());
EXPECT_NE(qba.constData(), id.constData());// A copy was made, the pointers differ
}
TEST_F(StringHasherTest, getIDFromQByteArrayLongUnhashable)// NOLINT
{
// Arrange
const std::array<char, 47> string {"data that is longer than our hasher threshold"};
QByteArray qba(string.data(), string.size());
Hasher()->setThreshold(string.size() - 1);
// Act
auto id = Hasher()->getID(qba, App::StringHasher::Option::None);
// Assert
EXPECT_STREQ(string.data(), id.constData());
EXPECT_FALSE(id.isHashed());
EXPECT_NE(qba.constData(), id.constData());// A copy was made, the pointers differ
}
TEST_F(StringHasherTest, getIDFromQByteArrayNoCopy)// NOLINT
{
// Arrange
const std::array<char, 5> string {"data"};
QByteArray qba(string.data(), string.size());
Hasher()->setThreshold(string.size() + 1);
// Act
auto id = Hasher()->getID(qba, App::StringHasher::Option::NoCopy);
// Assert
EXPECT_STREQ(string.data(), id.constData());
EXPECT_EQ(qba.constData(), id.constData());// No copy was made, the pointers are the same
}
TEST_F(StringHasherTest, getIDFromQByteArrayTwoDifferentStrings)// NOLINT
{
// Arrange
const std::array<char, 6> stringA {"dataA"};
QByteArray qbaA(stringA.data(), stringA.size());
const std::array<char, 6> stringB {"dataB"};
QByteArray qbaB(stringB.data(), stringB.size());
// Act
auto idA = Hasher()->getID(qbaA);
auto idB = Hasher()->getID(qbaB);
// Assert
EXPECT_EQ(2, Hasher()->size());
}
TEST_F(StringHasherTest, getIDFromQByteArrayTwoIdenticalStrings)// NOLINT
{
// Arrange
const std::array<char, 5> stringA {"data"};
QByteArray qbaA(stringA.data(), stringA.size());
const std::array<char, 5> stringB {"data"};
QByteArray qbaB(stringB.data(), stringB.size());
// Act
auto idA = Hasher()->getID(qbaA);
auto idB = Hasher()->getID(qbaB);
// Assert
EXPECT_EQ(1, Hasher()->size());
}
TEST_F(StringHasherTest, getIDFromQByteArrayBinaryFlag)// NOLINT
{
// Arrange
const std::array<char, 5> string {"data"};
QByteArray qba(string.data(), string.size());
// Act
auto id = Hasher()->getID(qba, App::StringHasher::Option::Binary);
// Assert
EXPECT_TRUE(id.isBinary());
}
TEST_F(StringHasherTest, getIDFromCString)// NOLINT
{
// Arrange
// Act
// Assert
}
/*
* Things that have to be tested for getIDFromMappedName:
* 1. With and without postfix (every other path must test both)
* 2. Existing entry: short circuits
* 3. Raw data and non-raw
* 4. Postfix contains # and not
* 5. Indexed name and not
* 6. sids empty and sids with content
* 7. sids whose hasher==this and whose hasher is something else
* 8. If sids.size() > 10, duplicates get removed
*/
TEST_F(StringHasherTest, getIDFromMappedNameWithoutPostfixWithoutIndex)// NOLINT
{
// Arrange
const char* name {"Face"};
QByteArray expectedPrefix {name, static_cast<int>(std::strlen(name))};
Data::MappedName mappedName1(expectedPrefix);
QVector<App::StringIDRef> sids;
// Act
auto id = Hasher()->getID(mappedName1, sids);
// Assert
EXPECT_EQ(expectedPrefix, id.deref().data());
EXPECT_EQ(nullptr, id.deref().postfix());
EXPECT_EQ(0, id.getIndex());
EXPECT_EQ(id, Hasher()->getID(expectedPrefix));
}
TEST_F(StringHasherTest, getIDFromMappedNameWithoutPostfixWithIndex)// NOLINT
{
// Arrange
const char* expectedName {"Face"};
QByteArray expectedPrefix {expectedName, static_cast<int>(std::strlen(expectedName))};
const char* name {"Face3"};
QByteArray prefix {name, static_cast<int>(std::strlen(name))};
Data::MappedName mappedName1(prefix);
QVector<App::StringIDRef> sids;
// Act
auto id = Hasher()->getID(mappedName1, sids);
// Assert
EXPECT_EQ(expectedPrefix, id.deref().data());
EXPECT_EQ(nullptr, id.deref().postfix());
EXPECT_EQ(3, id.getIndex());
}
TEST_F(StringHasherTest, getIDFromMappedNameWithoutIndexWithPostfix)// NOLINT
{
// Arrange
const char* name {"Face"};
QByteArray expectedPrefix {name, static_cast<int>(std::strlen(name))};
const char* postfix {";:M;FUS;:Hb:7,F"};
QByteArray expectedPostfix {postfix, static_cast<int>(std::strlen(postfix))};
Data::MappedName mappedName1(expectedPrefix);
Data::MappedName mappedName2(mappedName1, expectedPostfix.data());
QVector<App::StringIDRef> sids;
// Act
auto id = Hasher()->getID(mappedName2, sids);
// Assert
EXPECT_EQ(expectedPrefix, id.deref().data());
EXPECT_EQ(expectedPostfix, id.deref().postfix());
}
TEST_F(StringHasherTest, getIDFromMappedNameWithIndexWithPostfix)// NOLINT
{
// Arrange
const char* name {"Face3"};
QByteArray expectedPrefix {name, static_cast<int>(std::strlen(name))};
const char* postfix {";:M;FUS;:Hb:7,F"};
QByteArray expectedPostfix {postfix, static_cast<int>(std::strlen(postfix))};
Data::MappedName mappedName1(expectedPrefix);
Data::MappedName mappedName2(mappedName1, expectedPostfix.data());
QVector<App::StringIDRef> sids;
// Act
auto id = Hasher()->getID(mappedName2, sids);
// Assert
// Because there is a postfix, the index is basically ignored, and hashed along with the rest
// of the string.
EXPECT_EQ(expectedPrefix, id.deref().data());
EXPECT_EQ(expectedPostfix, id.deref().postfix());
EXPECT_EQ(0, id.getIndex());
}
TEST_F(StringHasherTest, getIDFromMappedNameExistingNameNoIndex)// NOLINT
{
// Arrange
Data::MappedName mappedName1 = givenMappedName("SomeTestName");
QVector<App::StringIDRef> sids;
auto firstIDInserted = Hasher()->getID(mappedName1, sids);
ASSERT_EQ(1, Hasher()->size());
// Act
auto secondIDInserted = Hasher()->getID(mappedName1, sids);
// Assert
EXPECT_EQ(1, Hasher()->size());
}
TEST_F(StringHasherTest, getIDFromMappedNameExistingNameWithIndex)// NOLINT
{
// Arrange
auto mappedNameA = givenMappedName("Test1");
auto mappedNameB = givenMappedName("Test2");
QVector<App::StringIDRef> sids;
auto firstIDInserted = Hasher()->getID(mappedNameA, sids);
ASSERT_EQ(1, Hasher()->size());
ASSERT_EQ(1, firstIDInserted.getIndex());
// Act
auto secondIDInserted = Hasher()->getID(mappedNameB, sids);
// Assert
EXPECT_EQ(1, Hasher()->size());
EXPECT_EQ(2, secondIDInserted.getIndex());
}
TEST_F(StringHasherTest, getIDFromMappedNameExistingNameWithIndexAndPostfix)// NOLINT
{
// Arrange
auto mappedNameA = givenMappedName("Test1", ";:M;FUS;:Hb:7,F");
auto mappedNameB = givenMappedName("Test2", ";:M;FUS;:Hb:7,F");
QVector<App::StringIDRef> sids;
auto firstIDInserted = Hasher()->getID(mappedNameA, sids);
ASSERT_EQ(2, Hasher()->size());// 2 because the postfix gets inserted alone as well
ASSERT_EQ(0, firstIDInserted.getIndex());
// Act
auto secondIDInserted = Hasher()->getID(mappedNameB, sids);
// Assert
// Second ID has a different index, and since it also has a postfix, that index is encoded as
// part of the string. So the strings don't match, and a second copy is inserted.
EXPECT_EQ(3, Hasher()->size());
EXPECT_EQ(0, secondIDInserted.getIndex());
EXPECT_EQ(1, secondIDInserted.relatedIDs().size());// The postfix is in the list
}
TEST_F(StringHasherTest, getIDFromMappedNameDuplicateWithEncodedPostfix)// NOLINT
{
// Arrange
auto mappedNameA = givenMappedName("Test1", ";:M;FUS;:Hb:7,F");
auto mappedNameB = givenMappedName("Test1", "#1");
QVector<App::StringIDRef> sids;
auto firstIDInserted = Hasher()->getID(mappedNameA, sids);
ASSERT_EQ(2, Hasher()->size());// 2 because the postfix gets inserted alone as well
ASSERT_EQ(0, firstIDInserted.getIndex());
// Act
auto secondIDInserted = Hasher()->getID(mappedNameB, sids);
// Assert
// The #1 is NOT inserted into the string table because it refers to the ";:M;FUS;:Hb:7,F",
// but mappedNameB IS inserted, even though conceptually it is already in the table
EXPECT_EQ(3, Hasher()->size());
}
TEST_F(StringHasherTest, getIDFromIntegerID)// NOLINT
{
// Arrange
const std::string prefix {"Test1"};
const std::string postfix {";:M;FUS;:Hb:7,F"};
auto mappedName = givenMappedName(prefix.c_str(), postfix.c_str());
QVector<App::StringIDRef> sids;
auto inserted = Hasher()->getID(mappedName, sids);
ASSERT_EQ(2, Hasher()->size());// 2 because the postfix gets inserted alone as well
// Act
auto result1 = Hasher()->getID(1);
auto result2 = Hasher()->getID(2);
// Assert
EXPECT_EQ(postfix, result1.dataToText());
EXPECT_EQ(prefix + postfix, result2.dataToText());
}
TEST_F(StringHasherTest, getIDFromIntegerIDNoSuchID)// NOLINT
{
// Arrange
// Do nothing, so the hash table is empty
// Act
auto result = Hasher()->getID(1);
// Assert
EXPECT_FALSE(result);
}
TEST_F(StringHasherTest, getIDFromIntegerIDBadID)// NOLINT
{
// Arrange
const std::string prefix {"Test1"};
auto mappedName = givenMappedName(prefix.c_str());
QVector<App::StringIDRef> sids;
auto inserted = Hasher()->getID(mappedName, sids);
ASSERT_EQ(1, Hasher()->size());
// Act
auto result = Hasher()->getID(-1);
// Assert
EXPECT_FALSE(result);
}
TEST_F(StringHasherTest, getIDFromIntegerWithIndex)// NOLINT
{
// Arrange
const std::string prefix {"Test1"};
const std::string postfix {";:M;FUS;:Hb:7,F"};
auto mappedName = givenMappedName(prefix.c_str(), postfix.c_str());
QVector<App::StringIDRef> sids;
auto inserted = Hasher()->getID(mappedName, sids);
ASSERT_EQ(2, Hasher()->size());// 2 because the postfix gets inserted alone as well
// Act
auto result = Hasher()->getID(2, 3);
// Assert
EXPECT_EQ(prefix + "3" + postfix, result.dataToText());
EXPECT_EQ(result.getIndex(), 3);
}
TEST_F(StringHasherTest, getIDFromIndexID)// NOLINT
{
// Arrange
const std::string prefix {"Test1"};
const std::string postfix {";:M;FUS;:Hb:7,F"};
auto mappedName = givenMappedName(prefix.c_str(), postfix.c_str());
QVector<App::StringIDRef> sids;
auto inserted = Hasher()->getID(mappedName, sids);
App::StringID::IndexID indexId {2, 3};
ASSERT_EQ(2, Hasher()->size());// 2 because the postfix gets inserted alone as well
// Act
auto result = Hasher()->getID(indexId);
// Assert
EXPECT_EQ(prefix + "3" + postfix, result.dataToText());
EXPECT_EQ(result.getIndex(), 3);
}
TEST_F(StringHasherTest, getIDMap)// NOLINT
{
// Arrange
givenSomeHashedValues();
// Act
auto map = Hasher()->getIDMap();
// Assert
EXPECT_EQ(2, map.size());
}
TEST_F(StringHasherTest, clear)// NOLINT
{
// Arrange
givenSomeHashedValues();
// Act
Hasher()->clear();
// Assert
EXPECT_EQ(0, Hasher()->size());
}
TEST_F(StringHasherTest, size)// NOLINT
{
// Arrange
givenSomeHashedValues();
// Act
auto result = Hasher()->size();
// Assert
EXPECT_EQ(2, result);
}
TEST_F(StringHasherTest, count)// NOLINT
{
// Arrange
givenSomeHashedValues();
// Act
auto result = Hasher()->count();
// Assert
// This is 1 because the hash table itself, which has not yet been compacted, still retains one
// reference to the postfix string. Even though all real references have been deleted when they
// went out of scope, one reference still exists.
EXPECT_EQ(1, result);
}
TEST_F(StringHasherTest, getPyObject)// NOLINT
{
// Arrange - done in setUp()
// Act
Py::Object py(Hasher()->getPyObject(), true);
// Assert
EXPECT_TRUE(PyObject_TypeCheck(py.ptr(), &App::StringHasherPy::Type));
}
TEST_F(StringHasherTest, setGetSaveAll)// NOLINT
{
// Arrange - done by setUp()
// Act
Hasher()->setSaveAll(true);
bool expectedTrue = Hasher()->getSaveAll();
Hasher()->setSaveAll(false);
bool expectedFalse = Hasher()->getSaveAll();
// Assert
EXPECT_TRUE(expectedTrue);
EXPECT_FALSE(expectedFalse);
}
TEST_F(StringHasherTest, setGetThreshold)// NOLINT
{
// Arrange
const int expectedThreshold {42};
// Act
Hasher()->setThreshold(expectedThreshold);
auto foundThreshold = Hasher()->getThreshold();
// Assert
EXPECT_EQ(expectedThreshold, foundThreshold);
}
TEST_F(StringHasherTest, clearMarks)// NOLINT
{
// Arrange
auto ref = givenSomeHashedValues();
ref.mark();
ASSERT_TRUE(ref.isMarked());
// Act
Hasher()->clearMarks();
// Assert
ASSERT_FALSE(ref.isMarked());
}
TEST_F(StringHasherTest, compact)// NOLINT
{
// Arrange
givenSomeHashedValues();
// Act
Hasher()->compact();
// Assert
EXPECT_EQ(0, Hasher()->count());
}