#include <cstdio>
#include <cstring>
#include "SerializedValues.hpp"
#include "TextDictTestBase.hpp"
namespace opencc {
class SerializedValuesTest : public TextDictTestBase {
protected:
SerializedValuesTest()
: binDict(new SerializedValues(textDict->GetLexicon())),
fileName("dict.bin"){};
static std::string WriteMalformedFile(uint32_t numItems,
uint32_t valueTotalLength,
const std::string& valueBuffer,
const std::vector<std::vector<uint16_t>>&
itemValueBytes) {
const std::string path = "malformed_test.bin";
FILE* fp = fopen(path.c_str(), "wb");
fwrite(&numItems, sizeof(uint32_t), 1, fp);
fwrite(&valueTotalLength, sizeof(uint32_t), 1, fp);
fwrite(valueBuffer.data(), sizeof(char), valueBuffer.size(), fp);
for (const auto& item : itemValueBytes) {
uint16_t numValues = static_cast<uint16_t>(item.size());
fwrite(&numValues, sizeof(uint16_t), 1, fp);
for (uint16_t nb : item) {
fwrite(&nb, sizeof(uint16_t), 1, fp);
}
}
fclose(fp);
return path;
}
const std::shared_ptr<SerializedValues> binDict;
const std::string fileName;
};
TEST_F(SerializedValuesTest, Serialization) {
binDict->opencc::SerializableDict::SerializeToFile(fileName);
}
TEST_F(SerializedValuesTest, Deserialization) {
const std::shared_ptr<SerializedValues>& deserialized =
SerializableDict::NewFromFile<SerializedValues>(fileName);
const LexiconPtr& lex1 = binDict->GetLexicon();
const LexiconPtr& lex2 = deserialized->GetLexicon();
EXPECT_EQ(lex1->Length(), lex2->Length());
for (size_t i = 0; i < lex1->Length(); i++) {
EXPECT_EQ(lex1->At(i)->NumValues(), lex2->At(i)->NumValues());
}
}
TEST_F(SerializedValuesTest, RejectsValueOffsetOutOfBounds) {
std::string valueBuf = {'a', 'b', '\0', '\0'};
std::string path =
WriteMalformedFile(1, 4, valueBuf, {{10}});
EXPECT_THROW(SerializableDict::NewFromFile<SerializedValues>(path),
InvalidFormat);
std::remove(path.c_str());
}
TEST_F(SerializedValuesTest, RejectsValueNotNullTerminated) {
std::string valueBuf = {'a', 'b', 'c', '\0'};
std::string path =
WriteMalformedFile(1, 4, valueBuf, {{3}});
EXPECT_THROW(SerializableDict::NewFromFile<SerializedValues>(path),
InvalidFormat);
std::remove(path.c_str());
}
TEST_F(SerializedValuesTest, RejectsZeroValueBytes) {
std::string valueBuf = {'a', '\0'};
std::string path =
WriteMalformedFile(1, 2, valueBuf, {{0}});
EXPECT_THROW(SerializableDict::NewFromFile<SerializedValues>(path),
InvalidFormat);
std::remove(path.c_str());
}
TEST_F(SerializedValuesTest, RejectsCumulativeOffsetOverflow) {
std::string valueBuf = {'a', 'b', '\0', 'c', 'd', '\0'};
std::string path =
WriteMalformedFile(1, 6, valueBuf, {{3, 5}});
EXPECT_THROW(SerializableDict::NewFromFile<SerializedValues>(path),
InvalidFormat);
std::remove(path.c_str());
}
TEST_F(SerializedValuesTest, AcceptsWellFormedFile) {
std::string valueBuf = {'a', 'b', '\0', 'c', 'd', '\0'};
std::string path =
WriteMalformedFile(2, 6, valueBuf, {{3}, {3}});
const auto deserialized =
SerializableDict::NewFromFile<SerializedValues>(path);
EXPECT_EQ(deserialized->GetLexicon()->Length(), 2);
std::remove(path.c_str());
}
TEST_F(SerializedValuesTest, RejectsHugeValueTotalLength) {
std::string path =
WriteMalformedFile(1, 0xFFFFFFFF, "", {});
EXPECT_THROW(SerializableDict::NewFromFile<SerializedValues>(path),
InvalidFormat);
std::remove(path.c_str());
}
}