From bb7681810a3fe2d977ee21741498f05b96f37826 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Fri, 31 Mar 2023 23:44:31 -0500 Subject: [PATCH] App/Toponaming: Tests for Reader and Writer --- tests/src/Base/CMakeLists.txt | 8 +- tests/src/Base/Reader.cpp | 231 +++++++++++++++++++++++++++++++++- tests/src/Base/Writer.cpp | 121 +++++++++++++++++- 3 files changed, 351 insertions(+), 9 deletions(-) diff --git a/tests/src/Base/CMakeLists.txt b/tests/src/Base/CMakeLists.txt index 44e5fa268d..638c5a1fad 100644 --- a/tests/src/Base/CMakeLists.txt +++ b/tests/src/Base/CMakeLists.txt @@ -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 ) diff --git a/tests/src/Base/Reader.cpp b/tests/src/Base/Reader.cpp index fbd41317e2..9db994be6a 100644 --- a/tests/src/Base/Reader.cpp +++ b/tests/src/Base/Reader.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 +#include +#include +#include +#include + +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"()" + data + ""; + std::istringstream stream(stringData); + std::ofstream fileStream(_tempFile); + fileStream.write(stringData.data(), static_cast(stringData.length())); + fileStream.close(); + std::ifstream inputStream(_tempFile); + _reader = std::make_unique(_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 charset { + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"}; + std::uniform_int_distribution 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 _reader; + fs::path _tempDir; + fs::path _tempFile; +}; + +TEST_F(ReaderTest, beginCharStreamNormal) +{ + // Arrange + givenDataAsXMLStream("Test ASCII data"); + Reader()->readElement("data"); + + // Act + auto& result = Reader()->beginCharStream(); + + // Assert + EXPECT_TRUE(result.good()); +} + +TEST_F(ReaderTest, beginCharStreamOpenClose) +{ + // Arrange + givenDataAsXMLStream(""); + 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("Test ASCII data"); + Reader()->readElement("data"); + Reader()->beginCharStream(); + + // Act & Assert + EXPECT_THROW(Reader()->beginCharStream(), Base::XMLParseException); +} + +TEST_F(ReaderTest, charStreamGood) +{ + // Arrange + givenDataAsXMLStream("Test ASCII data"); + Reader()->readElement("data"); + Reader()->beginCharStream(); + + // Act + auto &result = Reader()->charStream(); + + // Assert + EXPECT_TRUE(result.good()); +} + +TEST_F(ReaderTest, charStreamBad) +{ + // Arrange + givenDataAsXMLStream("Test ASCII data"); + Reader()->readElement("data"); + + // Act & Assert + EXPECT_THROW(Reader()->charStream(), Base::XMLParseException); +} + +TEST_F(ReaderTest, endCharStreamGood) +{ + // Arrange + givenDataAsXMLStream("Test ASCII data"); + Reader()->readElement("data"); + Reader()->beginCharStream(); + + // Act & Assert + Reader()->endCharStream(); // Does not throw +} + +TEST_F(ReaderTest, endCharStreamBad) +{ + // Arrange + givenDataAsXMLStream("Test ASCII 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("" + expectedData + ""); + Reader()->readElement("data"); + Reader()->beginCharStream(); + std::array 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("" + expectedData + ""); + Reader()->readElement("data"); + Reader()->beginCharStream(); + std::array 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("" + expectedData + ""); + Reader()->readElement("data"); + Reader()->beginCharStream(); + std::array 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("" + expectedData + ""); + Reader()->readElement("data"); + std::array buffer{}; + + // Act + auto bytesRead = Reader()->read(buffer.data(), bufferSize); + + // Assert + EXPECT_EQ(-1, bytesRead); // Because we didn't call beginCharStream +} diff --git a/tests/src/Base/Writer.cpp b/tests/src/Base/Writer.cpp index fbd41317e2..e4fcd3deed 100644 --- a/tests/src/Base/Writer.cpp +++ b/tests/src/Base/Writer.cpp @@ -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 {""}; + + // 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 {""}; + + // 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 {""}; + + // 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("", _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("", _writer.getString()); +} + +TEST_F(WriterTest, charStream) +{ + // Arrange + auto& streamA {_writer.beginCharStream()}; + + // Act + auto& streamB {_writer.charStream()}; + + // Assert + EXPECT_EQ(&streamA, &streamB); +}