App/Toponaming: Tests for Reader and Writer
This commit is contained in:
committed by
Chris Hennes
parent
0a2eacade2
commit
bb7681810a
@@ -2,8 +2,10 @@ target_sources(
|
||||
Tests_run
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Matrix.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Rotation.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tst_Tools.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Unit.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Quantity.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Reader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Rotation.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Unit.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Writer.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tst_Tools.cpp
|
||||
)
|
||||
|
||||
@@ -1,3 +1,228 @@
|
||||
//
|
||||
// Created by Chris Hennes on 3/31/23.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "Base/Exception.h"
|
||||
#include "Base/Reader.h"
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <random>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class ReaderTest: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
xercesc_3_2::XMLPlatformUtils::Initialize();
|
||||
_tempDir = fs::temp_directory_path();
|
||||
std::string filename = uniqueName() + ".xml";
|
||||
_tempFile = _tempDir / filename;
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
std::filesystem::remove(_tempFile);
|
||||
}
|
||||
|
||||
void givenDataAsXMLStream(const std::string& data)
|
||||
{
|
||||
auto stringData = R"(<?xml version="1.0" encoding="UTF-8"?><document>)" + data + "</document>";
|
||||
std::istringstream stream(stringData);
|
||||
std::ofstream fileStream(_tempFile);
|
||||
fileStream.write(stringData.data(), static_cast<std::streamsize>(stringData.length()));
|
||||
fileStream.close();
|
||||
std::ifstream inputStream(_tempFile);
|
||||
_reader = std::make_unique<Base::XMLReader>(_tempFile.string().c_str(), inputStream);
|
||||
}
|
||||
|
||||
/// Generate a random, probably-unique 16-character alphanumeric filename.
|
||||
static std::string uniqueName()
|
||||
{
|
||||
constexpr size_t filenameLength {16};
|
||||
static std::default_random_engine _generator;
|
||||
auto random_char = []() -> char {
|
||||
constexpr int numChars {63};
|
||||
std::array<char, numChars> charset {
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"};
|
||||
std::uniform_int_distribution<int> distribution(0, numChars - 1);
|
||||
return charset.at(distribution(_generator));
|
||||
};
|
||||
std::string str(filenameLength, 0);
|
||||
std::generate_n(str.begin(), filenameLength, random_char);
|
||||
return str;
|
||||
}
|
||||
|
||||
Base::XMLReader* Reader()
|
||||
{
|
||||
return _reader.get();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::unique_ptr<Base::XMLReader> _reader;
|
||||
fs::path _tempDir;
|
||||
fs::path _tempFile;
|
||||
};
|
||||
|
||||
TEST_F(ReaderTest, beginCharStreamNormal)
|
||||
{
|
||||
// Arrange
|
||||
givenDataAsXMLStream("<data>Test ASCII data</data>");
|
||||
Reader()->readElement("data");
|
||||
|
||||
// Act
|
||||
auto& result = Reader()->beginCharStream();
|
||||
|
||||
// Assert
|
||||
EXPECT_TRUE(result.good());
|
||||
}
|
||||
|
||||
TEST_F(ReaderTest, beginCharStreamOpenClose)
|
||||
{
|
||||
// Arrange
|
||||
givenDataAsXMLStream("<data id='12345' />");
|
||||
Reader()->readElement("data");
|
||||
|
||||
// Act
|
||||
auto& result = Reader()->beginCharStream(); // Not an error, even though there is no data
|
||||
|
||||
// Assert
|
||||
EXPECT_TRUE(result.good());
|
||||
}
|
||||
|
||||
TEST_F(ReaderTest, beginCharStreamAlreadyBegun)
|
||||
{
|
||||
// Arrange
|
||||
givenDataAsXMLStream("<data>Test ASCII data</data>");
|
||||
Reader()->readElement("data");
|
||||
Reader()->beginCharStream();
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_THROW(Reader()->beginCharStream(), Base::XMLParseException);
|
||||
}
|
||||
|
||||
TEST_F(ReaderTest, charStreamGood)
|
||||
{
|
||||
// Arrange
|
||||
givenDataAsXMLStream("<data>Test ASCII data</data>");
|
||||
Reader()->readElement("data");
|
||||
Reader()->beginCharStream();
|
||||
|
||||
// Act
|
||||
auto &result = Reader()->charStream();
|
||||
|
||||
// Assert
|
||||
EXPECT_TRUE(result.good());
|
||||
}
|
||||
|
||||
TEST_F(ReaderTest, charStreamBad)
|
||||
{
|
||||
// Arrange
|
||||
givenDataAsXMLStream("<data>Test ASCII data</data>");
|
||||
Reader()->readElement("data");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_THROW(Reader()->charStream(), Base::XMLParseException);
|
||||
}
|
||||
|
||||
TEST_F(ReaderTest, endCharStreamGood)
|
||||
{
|
||||
// Arrange
|
||||
givenDataAsXMLStream("<data>Test ASCII data</data>");
|
||||
Reader()->readElement("data");
|
||||
Reader()->beginCharStream();
|
||||
|
||||
// Act & Assert
|
||||
Reader()->endCharStream(); // Does not throw
|
||||
}
|
||||
|
||||
TEST_F(ReaderTest, endCharStreamBad)
|
||||
{
|
||||
// Arrange
|
||||
givenDataAsXMLStream("<data>Test ASCII data</data>");
|
||||
Reader()->readElement("data");
|
||||
// Do not open the stream...
|
||||
|
||||
// Act & Assert
|
||||
Reader()->endCharStream(); // Does not throw, even with no open stream
|
||||
}
|
||||
|
||||
TEST_F(ReaderTest, readDataSmallerThanBuffer)
|
||||
{
|
||||
// Arrange
|
||||
constexpr size_t bufferSize {20};
|
||||
std::string expectedData {"Test ASCII data"};
|
||||
givenDataAsXMLStream("<data>" + expectedData + "</data>");
|
||||
Reader()->readElement("data");
|
||||
Reader()->beginCharStream();
|
||||
std::array<char,bufferSize> buffer{};
|
||||
|
||||
// Act
|
||||
auto bytesRead = Reader()->read(buffer.data(), bufferSize);
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(expectedData.c_str(), buffer.data());
|
||||
EXPECT_EQ(expectedData.length(), bytesRead);
|
||||
}
|
||||
|
||||
TEST_F(ReaderTest, readDataLargerThanBuffer)
|
||||
{
|
||||
// Arrange
|
||||
constexpr size_t bufferSize {5};
|
||||
std::string expectedData {"Test ASCII data"};
|
||||
givenDataAsXMLStream("<data>" + expectedData + "</data>");
|
||||
Reader()->readElement("data");
|
||||
Reader()->beginCharStream();
|
||||
std::array<char,bufferSize> buffer{};
|
||||
|
||||
// Act
|
||||
auto bytesRead = Reader()->read(buffer.data(), bufferSize);
|
||||
|
||||
// Assert
|
||||
for (size_t i = 0; i < bufferSize; ++i) {
|
||||
EXPECT_EQ(expectedData[i], buffer.at(i));
|
||||
}
|
||||
EXPECT_EQ(bufferSize, bytesRead);
|
||||
}
|
||||
|
||||
TEST_F(ReaderTest, readDataLargerThanBufferSecondRead)
|
||||
{
|
||||
// Arrange
|
||||
constexpr size_t bufferSize {5};
|
||||
std::string expectedData {"Test ASCII data"};
|
||||
givenDataAsXMLStream("<data>" + expectedData + "</data>");
|
||||
Reader()->readElement("data");
|
||||
Reader()->beginCharStream();
|
||||
std::array<char,bufferSize> buffer{};
|
||||
Reader()->read(buffer.data(), bufferSize); // Read the first five bytes
|
||||
|
||||
// Act
|
||||
auto bytesRead = Reader()->read(buffer.data(), bufferSize); // Second five bytes
|
||||
|
||||
// Assert
|
||||
for (size_t i = 0; i < bufferSize; ++i) {
|
||||
EXPECT_EQ(expectedData[i+bufferSize], buffer.at(i));
|
||||
}
|
||||
EXPECT_EQ(bufferSize, bytesRead);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(ReaderTest, readDataNotStarted)
|
||||
{
|
||||
// Arrange
|
||||
constexpr size_t bufferSize {20};
|
||||
std::string expectedData {"Test ASCII data"};
|
||||
givenDataAsXMLStream("<data>" + expectedData + "</data>");
|
||||
Reader()->readElement("data");
|
||||
std::array<char,bufferSize> buffer{};
|
||||
|
||||
// Act
|
||||
auto bytesRead = Reader()->read(buffer.data(), bufferSize);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(-1, bytesRead); // Because we didn't call beginCharStream
|
||||
}
|
||||
|
||||
@@ -1,3 +1,118 @@
|
||||
//
|
||||
// Created by Chris Hennes on 3/31/23.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "Base/Exception.h"
|
||||
#include "Base/Writer.h"
|
||||
|
||||
// Writer is designed to be a base class, so for testing we actually instantiate a StringWriter,
|
||||
// which is derived from it
|
||||
|
||||
class WriterTest : public ::testing::Test {
|
||||
protected:
|
||||
//void SetUp() override {}
|
||||
|
||||
// void TearDown() override {}
|
||||
protected:
|
||||
Base::StringWriter _writer;
|
||||
};
|
||||
|
||||
TEST_F(WriterTest, insertTextSimple)
|
||||
{
|
||||
// Arrange
|
||||
std::string testTextData {"Simple ASCII data"};
|
||||
std::string expectedResult {"<![CDATA[" + testTextData + "]]>"};
|
||||
|
||||
// Act
|
||||
_writer.insertText(testTextData);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(expectedResult, _writer.getString());
|
||||
}
|
||||
|
||||
/// If the data happens to actually include an XML CDATA close marker, that needs to be "escaped" --
|
||||
/// this is done by breaking it up into two separate CDATA sections, splitting apart the marker.
|
||||
TEST_F(WriterTest, insertTextNeedsEscape)
|
||||
{
|
||||
// Arrange
|
||||
std::string testDataA {"ASCII data with a close marker in it, like so: ]]"};
|
||||
std::string testDataB {"> "};
|
||||
std::string expectedResult {"<![CDATA[" + testDataA + "]]><![CDATA[" + testDataB + "]]>"};
|
||||
|
||||
// Act
|
||||
_writer.insertText(testDataA + testDataB);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(expectedResult, _writer.getString());
|
||||
}
|
||||
|
||||
TEST_F(WriterTest, insertNonAsciiData)
|
||||
{
|
||||
// Arrange
|
||||
std::string testData {"\x01\x02\x03\x04\u0001F450😀"};
|
||||
std::string expectedResult {"<![CDATA[" + testData + "]]>"};
|
||||
|
||||
// Act
|
||||
_writer.insertText(testData);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(expectedResult, _writer.getString());
|
||||
}
|
||||
|
||||
TEST_F(WriterTest, beginCharStream)
|
||||
{
|
||||
// Arrange & Act
|
||||
auto & checkStream {_writer.beginCharStream()};
|
||||
|
||||
// Assert
|
||||
EXPECT_TRUE(checkStream.good());
|
||||
}
|
||||
|
||||
TEST_F(WriterTest, beginCharStreamTwice)
|
||||
{
|
||||
// Arrange
|
||||
_writer.beginCharStream();
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_THROW(
|
||||
_writer.beginCharStream(),
|
||||
Base::RuntimeError
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(WriterTest, endCharStream)
|
||||
{
|
||||
// Arrange
|
||||
_writer.beginCharStream();
|
||||
|
||||
// Act
|
||||
_writer.endCharStream();
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ("<![CDATA[]]>", _writer.getString());
|
||||
}
|
||||
|
||||
TEST_F(WriterTest, endCharStreamTwice)
|
||||
{
|
||||
// Arrange
|
||||
_writer.beginCharStream();
|
||||
_writer.endCharStream();
|
||||
|
||||
// Act
|
||||
_writer.endCharStream(); // Doesn't throw, or do anything at all
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ("<![CDATA[]]>", _writer.getString());
|
||||
}
|
||||
|
||||
TEST_F(WriterTest, charStream)
|
||||
{
|
||||
// Arrange
|
||||
auto& streamA {_writer.beginCharStream()};
|
||||
|
||||
// Act
|
||||
auto& streamB {_writer.charStream()};
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(&streamA, &streamB);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user