diff options
author | Roland Reichwein <mail@reichwein.it> | 2023-02-26 08:54:17 +0100 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2023-02-26 08:54:17 +0100 |
commit | c9fa963e71258c5adfb71cf1996cd1bcb33df0bb (patch) | |
tree | a40d2260bf03ea5adfca798aef5d49d6a1984cad /tests |
Start with copy of whiteboard
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Makefile | 79 | ||||
-rw-r--r-- | tests/test-compiledsql.cpp | 77 | ||||
-rw-r--r-- | tests/test-config.cpp | 63 | ||||
-rw-r--r-- | tests/test-connectionregistry.cpp | 170 | ||||
-rw-r--r-- | tests/test-diff.cpp | 199 | ||||
-rw-r--r-- | tests/test-qrcode.cpp | 140 | ||||
-rw-r--r-- | tests/test-storage.cpp | 283 | ||||
-rw-r--r-- | tests/test-whiteboard.cpp | 255 |
8 files changed, 1266 insertions, 0 deletions
diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..f3ec6c8 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,79 @@ +CXXFLAGS=-g -O0 + +include ../common.mk + +ifeq ($(CXXTYPE),clang++) +CXXFLAGS+=-fprofile-instr-generate -fcoverage-mapping +LDFLAGS+=-fprofile-instr-generate -fcoverage-mapping +else +# GCC +CXXFLAGS+=--coverage +LDFLAGS+=--coverage +endif + +UNITS=storage.cpp config.cpp compiledsql.cpp qrcode.cpp whiteboard.cpp connectionregistry.cpp diff.cpp + +UNITTESTS=test-config.cpp \ + test-storage.cpp \ + test-connectionregistry.cpp \ + test-compiledsql.cpp \ + test-qrcode.cpp \ + test-whiteboard.cpp \ + test-diff.cpp + +CXXFLAGS+=\ + -I/usr/src/googletest/googletest/include \ + -I/usr/src/googletest/googlemock/include \ + -I/usr/src/googletest/googletest \ + -I/usr/src/googletest/googlemock \ + -I.. + +test: unittests + # https://clang.llvm.org/docs/SourceBasedCodeCoverage.html +ifeq ($(CXXTYPE),clang++) + LLVM_PROFILE_FILE="unittests.profraw" ./unittests + $(LLVMPROFDATA) merge -sparse unittests.profraw -o unittests.profdata + $(LLVMCOV) report --ignore-filename-regex='google' --ignore-filename-regex='test-' --ignore-filename-regex='Magick' --show-region-summary=0 -instr-profile unittests.profdata unittests +else + ./unittests + gcovr -r .. +endif + +coverage: + $(LLVMCOV) show -instr-profile unittests.profdata $(UNITS:.cpp=.o) + +unittests: libgmock.a $(UNITTESTS:.cpp=.o) $(UNITS:.cpp=.o) + $(CXX) $(LDFLAGS) $^ $(LDLIBS) $(LIBS) -o $@ + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -o $@ -c $< + +config.o: ../config.cpp + $(CXX) $(CXXFLAGS) -o $@ -c $< + +connectionregistry.o: ../connectionregistry.cpp + $(CXX) $(CXXFLAGS) -o $@ -c $< + +diff.o: ../diff.cpp + $(CXX) $(CXXFLAGS) -o $@ -c $< + +storage.o: ../storage.cpp + $(CXX) $(CXXFLAGS) -o $@ -c $< + +compiledsql.o: ../compiledsql.cpp + $(CXX) $(CXXFLAGS) -o $@ -c $< + +whiteboard.o: ../whiteboard.cpp + $(CXX) $(CXXFLAGS) -o $@ -c $< + +qrcode.o: ../qrcode.cpp + $(CXX) $(CXXFLAGS) -o $@ -c $< + +libgmock.a: + $(CXX) $(CXXFLAGS) -c /usr/src/googletest/googletest/src/gtest-all.cc + $(CXX) $(CXXFLAGS) -c /usr/src/googletest/googlemock/src/gmock-all.cc + $(CXX) $(CXXFLAGS) -c /usr/src/googletest/googlemock/src/gmock_main.cc + ar -rv libgmock.a gmock-all.o gtest-all.o gmock_main.o + +clean: + -rm -f *.o *.a unittests *.gcda *.gcno *.profraw *.profdata *.gcov diff --git a/tests/test-compiledsql.cpp b/tests/test-compiledsql.cpp new file mode 100644 index 0000000..d14220c --- /dev/null +++ b/tests/test-compiledsql.cpp @@ -0,0 +1,77 @@ +#include <gtest/gtest.h> + +#include <filesystem> +#include <memory> +#include <string> +#include <system_error> + +#include "libreichwein/file.h" + +#include "config.h" +#include "storage.h" +#include "whiteboard.h" + +namespace fs = std::filesystem; + +namespace { + const std::string testDbFilename{"./whiteboard.db3"}; +} + +class CompiledSQLTest: public ::testing::Test +{ +protected: + CompiledSQLTest(){ + } + + ~CompiledSQLTest() override{ + } + + void SetUp() override + { + std::error_code ec; + fs::remove(testDbFilename, ec); + } + + void TearDown() override + { + std::error_code ec; + fs::remove(testDbFilename, ec); + } + +}; + +TEST_F(CompiledSQLTest, create) +{ + SQLite::Database db{testDbFilename, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE}; + + { + CompiledSQL stmt1{db, "CREATE TABLE documents (id VARCHAR(16) PRIMARY KEY)"}; + CompiledSQL::Guard g{stmt1}; + stmt1.execute(); + } + + { + CompiledSQL stmt2{db, "INSERT INTO documents (id) values (?)"}; + CompiledSQL::Guard g{stmt2}; + stmt2.bind(1, "abc"); + ASSERT_TRUE(stmt2.execute()); + } + + { + CompiledSQL stmt3{db, "SELECT id FROM documents WHERE id = ?"}; + CompiledSQL::Guard g{stmt3}; + stmt3.bind(1, "abc"); + ASSERT_TRUE(stmt3.execute()); + EXPECT_EQ(stmt3.getColumn<std::string>(0), "abc"); + } + + { + CompiledSQL stmt4{db, "SELECT id FROM documents WHERE id = ?"}; + CompiledSQL::Guard g{stmt4}; + stmt4.bind(1, "def"); + ASSERT_FALSE(stmt4.execute()); + } + + EXPECT_TRUE(fs::exists(testDbFilename)); +} + diff --git a/tests/test-config.cpp b/tests/test-config.cpp new file mode 100644 index 0000000..816dfea --- /dev/null +++ b/tests/test-config.cpp @@ -0,0 +1,63 @@ +#include <gtest/gtest.h> + +#include <filesystem> +#include <string> +#include <system_error> + +#include "libreichwein/file.h" + +#include "config.h" + +namespace fs = std::filesystem; +using namespace Reichwein; + +namespace { + const std::string testConfigFilename{"./test.conf"}; + const std::string testDbFilename{"./whiteboard.db3"}; +} + +class ConfigTest: public ::testing::Test +{ +protected: + ConfigTest(){ + } + + ~ConfigTest(){ + } +}; + +TEST_F(ConfigTest, defaultData) +{ + std::string filename{testConfigFilename + "doesntexist"}; + std::error_code ec; + fs::remove(filename, ec); + ASSERT_TRUE(!fs::exists(filename)); + { + Config config{filename}; + EXPECT_EQ(config.getDataPath(), "/var/lib/whiteboard"); + EXPECT_EQ(config.getMaxage(), 0UL); + ASSERT_TRUE(!fs::exists(filename)); + } + + ASSERT_TRUE(!fs::exists(filename)); +} + +TEST_F(ConfigTest, testData) +{ + File::setFile(testConfigFilename, R"CONFIG( +<config> + <datapath>/some/other/location</datapath> + <maxage>2592000</maxage> +</config> +)CONFIG"); + + { + Config config{testConfigFilename}; + EXPECT_EQ(config.getDataPath(), "/some/other/location"); + EXPECT_EQ(config.getMaxage(), 2592000UL); + } + + std::error_code ec; + fs::remove(testConfigFilename, ec); +} + diff --git a/tests/test-connectionregistry.cpp b/tests/test-connectionregistry.cpp new file mode 100644 index 0000000..dbc2b7e --- /dev/null +++ b/tests/test-connectionregistry.cpp @@ -0,0 +1,170 @@ +#include <gtest/gtest.h> + +#include <filesystem> +#include <string> +#include <system_error> + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> + +#include "libreichwein/file.h" + +#include "connectionregistry.h" + +namespace fs = std::filesystem; +using namespace Reichwein; + +class ConnectionRegistryTest: public ::testing::Test +{ +protected: + ConnectionRegistryTest(){ + } + + ~ConnectionRegistryTest(){ + } +}; + +TEST_F(ConnectionRegistryTest, constructor) +{ + ConnectionRegistry cr{}; +} + +TEST_F(ConnectionRegistryTest, test_addConnection) +{ + boost::asio::io_context ioc{1}; + + boost::asio::ip::tcp::socket ts0{ioc}; + ConnectionRegistry::connection c0 {std::make_shared<ConnectionRegistry::connection::element_type>(std::move(ts0))}; + + boost::asio::ip::tcp::socket ts1{ioc}; + ConnectionRegistry::connection c1 {std::make_shared<ConnectionRegistry::connection::element_type>(std::move(ts1))}; + + ConnectionRegistry cr{}; + + cr.addConnection(c0); + EXPECT_THROW(cr.addConnection(c0), std::exception); + cr.addConnection(c1); + EXPECT_THROW(cr.addConnection(c0), std::exception); + EXPECT_THROW(cr.addConnection(c1), std::exception); +} + +TEST_F(ConnectionRegistryTest, test_delConnection) +{ + boost::asio::io_context ioc{1}; + + boost::asio::ip::tcp::socket ts0{ioc}; + ConnectionRegistry::connection c0 {std::make_shared<ConnectionRegistry::connection::element_type>(std::move(ts0))}; + + boost::asio::ip::tcp::socket ts1{ioc}; + ConnectionRegistry::connection c1 {std::make_shared<ConnectionRegistry::connection::element_type>(std::move(ts1))}; + + ConnectionRegistry cr{}; + + EXPECT_THROW(cr.delConnection(c0), std::exception); + + cr.addConnection(c0); + cr.delConnection(c0); + + cr.addConnection(c0); + cr.addConnection(c1); + cr.delConnection(c0); + EXPECT_THROW(cr.delConnection(c0), std::exception); + cr.delConnection(c1); + EXPECT_THROW(cr.delConnection(c1), std::exception); +} + +TEST_F(ConnectionRegistryTest, test_setId) +{ + boost::asio::io_context ioc{1}; + boost::asio::ip::tcp::socket ts{ioc}; + + ConnectionRegistry::connection c {std::make_shared<ConnectionRegistry::connection::element_type>(std::move(ts))}; + + ConnectionRegistry cr{}; + + EXPECT_THROW(cr.setId(c, "id1"), std::exception); + cr.addConnection(c); + cr.setId(c, "id2"); +} + +TEST_F(ConnectionRegistryTest, test_dump) +{ + boost::asio::io_context ioc{1}; + boost::asio::ip::tcp::socket ts{ioc}; + + ConnectionRegistry::connection c {std::make_shared<ConnectionRegistry::connection::element_type>(std::move(ts))}; + + ConnectionRegistry cr{}; + + cr.addConnection(c); + cr.dump(); + + cr.setId(c, "id1"); + cr.dump(); +} + +TEST_F(ConnectionRegistryTest, test_iterators) +{ + boost::asio::io_context ioc{1}; + boost::asio::ip::tcp::socket ts{ioc}; + + ConnectionRegistry::connection c {std::make_shared<ConnectionRegistry::connection::element_type>(std::move(ts))}; + + ConnectionRegistry cr{}; + + EXPECT_THROW(cr.begin(""), std::exception); + EXPECT_THROW(cr.end(""), std::exception); + EXPECT_EQ(std::distance(cr.begin(), cr.end()), 0); + + cr.addConnection(c); + EXPECT_THROW(cr.begin(""), std::exception); + EXPECT_THROW(cr.end(""), std::exception); + EXPECT_EQ(std::distance(cr.begin(), cr.end()), 1); + + cr.setId(c, "id1"); + + EXPECT_EQ(std::distance(cr.begin("id1"), cr.end("id1")), 1); + EXPECT_EQ(std::distance(cr.begin(), cr.end()), 1); +} + +TEST_F(ConnectionRegistryTest, test_guard) +{ + boost::asio::io_context ioc{1}; + boost::asio::ip::tcp::socket ts{ioc}; + + ConnectionRegistry::connection c {std::make_shared<ConnectionRegistry::connection::element_type>(std::move(ts))}; + + ConnectionRegistry cr{}; + + { + ConnectionRegistry::RegistryGuard rg{cr, c}; + + EXPECT_THROW(cr.addConnection(c), std::exception); + } + + EXPECT_THROW(cr.delConnection(c), std::exception); +} + +TEST_F(ConnectionRegistryTest, number_of_connections) +{ + boost::asio::io_context ioc{1}; + + boost::asio::ip::tcp::socket ts0{ioc}; + ConnectionRegistry::connection c0 {std::make_shared<ConnectionRegistry::connection::element_type>(std::move(ts0))}; + + boost::asio::ip::tcp::socket ts1{ioc}; + ConnectionRegistry::connection c1 {std::make_shared<ConnectionRegistry::connection::element_type>(std::move(ts1))}; + + ConnectionRegistry cr{}; + + EXPECT_EQ(cr.number_of_connections(), 0); + cr.addConnection(c0); + EXPECT_EQ(cr.number_of_connections(), 1); + cr.addConnection(c1); + EXPECT_EQ(cr.number_of_connections(), 2); + cr.delConnection(c0); + EXPECT_EQ(cr.number_of_connections(), 1); + cr.delConnection(c1); + EXPECT_EQ(cr.number_of_connections(), 0); +} + diff --git a/tests/test-diff.cpp b/tests/test-diff.cpp new file mode 100644 index 0000000..d09c58e --- /dev/null +++ b/tests/test-diff.cpp @@ -0,0 +1,199 @@ +#include <gtest/gtest.h> + +#include <filesystem> +#include <string> +#include <system_error> + +#include <stdlib.h> + +#include <boost/property_tree/ptree.hpp> + +#include "libreichwein/file.h" + +#include "diff.h" + +namespace fs = std::filesystem; +namespace pt = boost::property_tree; +using namespace Reichwein; + +class DiffTest: public ::testing::Test +{ +protected: + DiffTest(){ + } + + ~DiffTest(){ + } +}; + +TEST_F(DiffTest, constructor) +{ + // empty constructor + { + Diff d{}; + EXPECT_EQ(d.get_xml(), "<diff><start>0</start><end>0</end><data/></diff>"); + } + + // constructor via xml diff + { + EXPECT_THROW(Diff d{""}, std::exception); + } + { + EXPECT_THROW(Diff d{"<diff><begin></begin></diff>"}, std::exception); + EXPECT_THROW(Diff d{"<diff><end>0</end><data>abc</data></diff>"}, std::exception); + EXPECT_THROW(Diff d{"<diff><start>0</start><data>abc</data></diff>"}, std::exception); + EXPECT_THROW(Diff d{"<diff><start>0</start><end>0</end></diff>"}, std::exception); + EXPECT_THROW(Diff d{"<diff><start>5</start><end>0</end><data>abc</data></diff>"}, std::exception); + EXPECT_THROW(Diff d{"<diff><start></start><end>0</end><data>abc</data></diff>"}, std::exception); + } + + { + Diff d{"<diff><start>0</start><end>0</end><data>abc</data></diff>"}; + EXPECT_EQ(d.get_xml(), "<diff><start>0</start><end>0</end><data>abc</data></diff>"); + } + + { + Diff d{"<diff><start>5</start><end>5</end><data>abc</data></diff>"}; + EXPECT_EQ(d.get_xml(), "<diff><start>5</start><end>5</end><data>abc</data></diff>"); + } + + { + Diff d{"<diff><start>5</start><end>50</end><data>abc</data></diff>"}; + EXPECT_EQ(d.get_xml(), "<diff><start>5</start><end>50</end><data>abc</data></diff>"); + } + + // constructor via ptree + { + pt::ptree ptree; + EXPECT_THROW(Diff d{ptree}, std::exception); + + ptree.put("diff.start", 0); + ptree.put("diff.end", 0); + ptree.put("diff.data", "abc"); + Diff d{ptree}; + EXPECT_EQ(d.get_xml(), "<diff><start>0</start><end>0</end><data>abc</data></diff>"); + } + + // constructor via versions + { + Diff d{"", ""}; + EXPECT_EQ(d.get_xml(), "<diff><start>0</start><end>0</end><data/></diff>"); + } + { + Diff d{"a", "a"}; + EXPECT_EQ(d.get_xml(), "<diff><start>0</start><end>0</end><data/></diff>"); + } + { + Diff d{"a", "b"}; + EXPECT_EQ(d.get_xml(), "<diff><start>0</start><end>1</end><data>b</data></diff>"); + } + { + Diff d{"0a1", "0b1"}; + EXPECT_EQ(d.get_xml(), "<diff><start>1</start><end>2</end><data>b</data></diff>"); + } + { + Diff d{"0abc1", "00b01"}; + EXPECT_EQ(d.get_xml(), "<diff><start>1</start><end>4</end><data>0b0</data></diff>"); + } + { + Diff d{"0ab1", "00b01"}; + EXPECT_EQ(d.get_xml(), "<diff><start>1</start><end>3</end><data>0b0</data></diff>"); + } + { + Diff d{"0abc1", "00b1"}; + EXPECT_EQ(d.get_xml(), "<diff><start>1</start><end>4</end><data>0b</data></diff>"); + } + { + Diff d{"0abc1", ""}; + EXPECT_EQ(d.get_xml(), "<diff><start>0</start><end>5</end><data/></diff>"); + } + { + Diff d{"bc1", "0abc1"}; + EXPECT_EQ(d.get_xml(), "<diff><start>0</start><end>0</end><data>0a</data></diff>"); + } + { + Diff d{"0abc1", "0ab"}; + EXPECT_EQ(d.get_xml(), "<diff><start>3</start><end>5</end><data/></diff>"); + } + { + Diff d{"0abc1", "0abc123"}; + EXPECT_EQ(d.get_xml(), "<diff><start>5</start><end>5</end><data>23</data></diff>"); + } + { + Diff d{"0abc1", "010abc1"}; + EXPECT_EQ(d.get_xml(), "<diff><start>0</start><end>0</end><data>01</data></diff>"); + } + { + Diff d{"0abc1", "0ac1"}; + EXPECT_EQ(d.get_xml(), "<diff><start>2</start><end>3</end><data/></diff>"); + } + { + Diff d{"0abc1", "0abxc1"}; + EXPECT_EQ(d.get_xml(), "<diff><start>3</start><end>3</end><data>x</data></diff>"); + } + { + Diff d{"abc", "c"}; + EXPECT_EQ(d.get_xml(), "<diff><start>0</start><end>2</end><data/></diff>"); + } + { + Diff d{"aaaa", "aa"}; + EXPECT_EQ(d.get_xml(), "<diff><start>2</start><end>4</end><data/></diff>"); + } + { + Diff d{"baaaa", "baa"}; + EXPECT_EQ(d.get_xml(), "<diff><start>3</start><end>5</end><data/></diff>"); + } + { + Diff d{"baaaab", "baab"}; + EXPECT_EQ(d.get_xml(), "<diff><start>1</start><end>3</end><data/></diff>"); + } + { + Diff d{"baaaab", "baaaaaaab"}; + EXPECT_EQ(d.get_xml(), "<diff><start>1</start><end>1</end><data>aaa</data></diff>"); + } +} + +TEST_F(DiffTest, empty) +{ + { + Diff d; + EXPECT_TRUE(d.empty()); + } + + { + Diff d{"<diff><start>1</start><end>3</end><data/></diff>"}; + EXPECT_FALSE(d.empty()); + } + + { + Diff d{"<diff><start>1</start><end>1</end><data/></diff>"}; + EXPECT_TRUE(d.empty()); + } + + { + Diff d{"<diff><start>1</start><end>1</end><data>abc</data></diff>"}; + EXPECT_FALSE(d.empty()); + } + + { + Diff d{"<diff><start>0</start><end>0</end><data/></diff>"}; + EXPECT_TRUE(d.empty()); + } +} + +TEST_F(DiffTest, diff_create) +{ + const char* result {diff_create("0abc1", "0ab")}; + + EXPECT_EQ(std::string(result), "<diff><start>3</start><end>5</end><data/></diff>"); + free((void*)result); // this will be done by javascript side in real scenario +} + +TEST_F(DiffTest, diff_apply) +{ + const char* result {diff_apply("0abc1", "<diff><start>3</start><end>5</end><data/></diff>")}; + + EXPECT_EQ(std::string(result), "0ab"); + free((void*)result); // this will be done by javascript side in real scenario +} + diff --git a/tests/test-qrcode.cpp b/tests/test-qrcode.cpp new file mode 100644 index 0000000..96cfdb0 --- /dev/null +++ b/tests/test-qrcode.cpp @@ -0,0 +1,140 @@ +#include <gtest/gtest.h> + +#include <filesystem> +#include <memory> +#include <string> +#include <system_error> + +#include "libreichwein/file.h" + +#include "qrcode.h" + +namespace fs = std::filesystem; + +class QRCodeTest: public ::testing::Test +{ +protected: + QRCodeTest(){ + } + + ~QRCodeTest() override{ + } + + void SetUp() override + { + QRCode::init(); + } + + void TearDown() override + { + } + +}; + +TEST_F(QRCodeTest, empty) +{ + unsigned char empty_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x15, + 0x10, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xec, 0x26, 0x09, 0x00, 0x00, 0x00, + 0xa7, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x7d, 0x92, 0x4b, 0x12, 0xc3, + 0x30, 0x08, 0x43, 0x79, 0x99, 0xde, 0xff, 0xca, 0xea, 0x82, 0x51, 0xf9, + 0xc4, 0xae, 0x17, 0x89, 0x01, 0x03, 0x02, 0x89, 0x18, 0x47, 0x8a, 0x00, + 0x29, 0x2d, 0xe8, 0xb1, 0x4f, 0x86, 0x7b, 0x48, 0x02, 0x7b, 0x57, 0xcc, + 0xa6, 0xd4, 0x43, 0xd3, 0x96, 0x22, 0x9e, 0x38, 0x9e, 0x9e, 0xe2, 0xf3, + 0x9c, 0x1f, 0x15, 0xde, 0x81, 0xf5, 0x5d, 0x43, 0x02, 0x3f, 0x3e, 0xd5, + 0x1f, 0xf8, 0x36, 0xee, 0x88, 0x08, 0xae, 0x59, 0x37, 0xac, 0x00, 0xfe, + 0xee, 0x9d, 0x76, 0x6f, 0x48, 0x52, 0xb5, 0xcc, 0x7b, 0xfd, 0xed, 0x1f, + 0x1b, 0xa8, 0xec, 0x64, 0x6d, 0x51, 0x31, 0xab, 0x15, 0xf2, 0xb2, 0xdc, + 0x91, 0x4e, 0x24, 0x48, 0x7d, 0xa3, 0x53, 0x03, 0xaf, 0x0d, 0x54, 0xd3, + 0x9b, 0x6c, 0x16, 0xa1, 0x7b, 0x34, 0x29, 0x82, 0xa9, 0x9e, 0x84, 0x61, + 0xc6, 0x66, 0xfd, 0xc7, 0x9b, 0x73, 0x93, 0x2d, 0xc0, 0xf4, 0xc3, 0x1f, + 0x65, 0xf1, 0x1b, 0xd8, 0x30, 0xae, 0xca, 0x9a, 0x83, 0x02, 0x1c, 0x94, + 0xe5, 0x4d, 0x27, 0x42, 0x57, 0x3e, 0x34, 0xde, 0x74, 0x16, 0xf2, 0x2f, + 0x2f, 0xd5, 0xa5, 0xd0, 0x5a, 0x61, 0xde, 0x49, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + }; + // Test prepared with: + //File::setFile("empty.png", QRCode::getQRCode("")); + //then: xxd -i tests/empty.png >> tests/test-qrcode.cpp + + EXPECT_EQ(QRCode::getQRCode(""), std::string(reinterpret_cast<char*>(&empty_png[0]), sizeof(empty_png))); +} + +TEST_F(QRCodeTest, simple) +{ + unsigned char simple_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x15, + 0x10, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xec, 0x26, 0x09, 0x00, 0x00, 0x00, + 0xa5, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x75, 0x52, 0xd1, 0x0e, 0xc0, + 0x40, 0x04, 0x6b, 0x17, 0xff, 0xff, 0xcb, 0xf6, 0xe0, 0x4c, 0x39, 0x93, + 0x6c, 0x09, 0x47, 0x55, 0x01, 0x9a, 0xb9, 0xbb, 0xbb, 0x03, 0xf9, 0xa9, + 0x99, 0x86, 0x48, 0x80, 0x8c, 0xe4, 0x2c, 0xd5, 0xb7, 0xcf, 0x2d, 0xa4, + 0xfa, 0xeb, 0x9b, 0x61, 0xb5, 0xd9, 0x1c, 0x00, 0x9e, 0x2d, 0x2d, 0x88, + 0xcc, 0xb8, 0xdd, 0x18, 0xa4, 0x3b, 0xa9, 0xad, 0x7f, 0x1b, 0xff, 0x29, + 0xc0, 0xbd, 0x8a, 0x5c, 0xf0, 0x52, 0xc7, 0x2e, 0x90, 0x2a, 0x91, 0x51, + 0x4b, 0xfa, 0xc9, 0x2e, 0xfc, 0x40, 0x55, 0x6c, 0xb2, 0xe1, 0xf5, 0x4e, + 0xd3, 0x7f, 0xaa, 0xf2, 0xa6, 0x52, 0x24, 0x48, 0x77, 0xd9, 0xfa, 0x9c, + 0x7c, 0xae, 0xf7, 0x52, 0xa0, 0xab, 0xaa, 0x71, 0xeb, 0x5b, 0xa9, 0x21, + 0xfa, 0x50, 0x67, 0x5b, 0xe3, 0x7a, 0xd0, 0x67, 0x8f, 0x0e, 0xe4, 0x59, + 0x6c, 0xa4, 0x94, 0x96, 0xda, 0x29, 0xca, 0x80, 0xf5, 0x5c, 0x52, 0x53, + 0x3d, 0x9a, 0xa3, 0xcb, 0x7d, 0xaf, 0x93, 0xf9, 0xa1, 0xd2, 0xb9, 0xea, + 0xc6, 0x92, 0xf1, 0x7a, 0x0f, 0xaa, 0xee, 0x8c, 0x03, 0x2f, 0x88, 0x71, + 0xb5, 0xba, 0xf0, 0x88, 0xba, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, + 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + }; + // Test prepared with: + //File::setFile("simple.png", QRCode::getQRCode("abc")); + //then: xxd -i tests/simple.png >> tests/test-qrcode.cpp + EXPECT_EQ(QRCode::getQRCode("abc"), std::string(reinterpret_cast<char*>(&simple_png[0]), sizeof(simple_png))); +} + +TEST_F(QRCodeTest, url) +{ + unsigned char url_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x1d, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x23, 0x68, 0xe4, 0x90, 0x00, 0x00, 0x01, + 0x1f, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, 0x75, 0x54, 0xdb, 0x0e, 0xc5, + 0x30, 0x08, 0x92, 0xa5, 0xff, 0xff, 0xcb, 0x9c, 0x07, 0xe7, 0x41, 0xb4, + 0x6b, 0xb2, 0x64, 0x36, 0x5e, 0xa8, 0xa0, 0x11, 0x76, 0xc8, 0x08, 0x92, + 0x24, 0xcb, 0xde, 0x7f, 0x75, 0x4e, 0xba, 0xe7, 0x01, 0x22, 0x48, 0x20, + 0xbf, 0xb4, 0x2b, 0xe1, 0xfc, 0x03, 0x4e, 0x85, 0xd4, 0x75, 0x06, 0xe5, + 0xbf, 0xc2, 0xf3, 0xd6, 0x7d, 0x9f, 0x58, 0x07, 0x50, 0xbd, 0xc2, 0x11, + 0x97, 0xb3, 0x42, 0x3b, 0xd0, 0xb4, 0x33, 0x7c, 0x87, 0x1e, 0x7f, 0x81, + 0xd7, 0x51, 0x0a, 0x01, 0xbe, 0x25, 0x69, 0x1d, 0xfe, 0xfe, 0xac, 0xea, + 0x2d, 0x8b, 0x57, 0x50, 0xed, 0xe1, 0xbb, 0x73, 0x3a, 0xaf, 0x65, 0x7b, + 0xba, 0xd7, 0xea, 0x64, 0x94, 0x93, 0x1c, 0x2a, 0xd8, 0xd3, 0x92, 0x7f, + 0x95, 0xec, 0x90, 0xa9, 0x9f, 0xb2, 0x94, 0xee, 0x88, 0xf4, 0xba, 0x72, + 0x4e, 0xf5, 0x5a, 0x69, 0x6b, 0xf0, 0xdc, 0x6b, 0xf8, 0x4b, 0x85, 0xac, + 0x3f, 0x8a, 0x7c, 0xba, 0x0c, 0x66, 0x78, 0xe7, 0x55, 0xdc, 0x96, 0xda, + 0x5e, 0x99, 0x2b, 0x38, 0xc5, 0x3f, 0x61, 0x6d, 0x0a, 0x81, 0xf0, 0x56, + 0x4c, 0x4e, 0x7d, 0xe4, 0x7c, 0x24, 0x9f, 0x39, 0x74, 0xe5, 0x86, 0x86, + 0x87, 0x04, 0x2e, 0x23, 0xd0, 0xdb, 0x20, 0x8a, 0xe6, 0x60, 0x4f, 0x91, + 0x7c, 0x32, 0xe6, 0x2f, 0xdc, 0x12, 0x79, 0x31, 0x0a, 0x56, 0xe7, 0xb2, + 0xef, 0x07, 0x98, 0x8f, 0x8d, 0xe0, 0x6c, 0xd4, 0x04, 0xb6, 0xe1, 0xbf, + 0xc2, 0x98, 0x97, 0x5d, 0x43, 0xaa, 0x5f, 0xdb, 0xca, 0x6f, 0x57, 0xd3, + 0xee, 0xe4, 0xec, 0x3e, 0x9c, 0x4d, 0x8e, 0x81, 0xc2, 0x2d, 0x5d, 0x56, + 0x7e, 0x32, 0x44, 0xbc, 0xed, 0x6a, 0x1a, 0x87, 0xf4, 0x2a, 0xe8, 0x6b, + 0xad, 0x75, 0x51, 0xfa, 0x66, 0xf4, 0x07, 0x01, 0x97, 0x8d, 0x08, 0x90, + 0x09, 0xb5, 0xef, 0x64, 0xe7, 0xbf, 0x49, 0x62, 0x8e, 0xfa, 0xec, 0xfb, + 0x26, 0xad, 0x8d, 0xba, 0x3b, 0xf5, 0xf6, 0xf8, 0x22, 0x95, 0xf7, 0x0f, + 0x4c, 0x20, 0xaf, 0x2d, 0xca, 0xaf, 0x0d, 0xc5, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + }; + // Test prepared with: + //File::setFile("url.png", QRCode::getQRCode("https://reichwein.it/whiteboard")); + //then: xxd -i tests/url.png >> tests/test-qrcode.cpp + EXPECT_EQ(QRCode::getQRCode("https://reichwein.it/whiteboard"), std::string(reinterpret_cast<char*>(&url_png[0]), sizeof(url_png))); +} + +TEST_F(QRCodeTest, deterministic) +{ + EXPECT_EQ(QRCode::getQRCode("data1"), QRCode::getQRCode("data1")); +} + +TEST_F(QRCodeTest, different_data) +{ + EXPECT_NE(QRCode::getQRCode("data1"), QRCode::getQRCode("data2")); +} diff --git a/tests/test-storage.cpp b/tests/test-storage.cpp new file mode 100644 index 0000000..6239f8f --- /dev/null +++ b/tests/test-storage.cpp @@ -0,0 +1,283 @@ +#include <gtest/gtest.h> + +#include <filesystem> +#include <memory> +#include <string> +#include <system_error> + +#include "libreichwein/file.h" + +#include "config.h" +#include "storage.h" + +namespace fs = std::filesystem; +using namespace Reichwein; +using namespace std::string_literals; + +namespace { + const std::string testConfigFilename{"./test.conf"}; + const std::string testDbFilename{"./whiteboard.db3"}; +} + +class StorageTest: public ::testing::Test +{ +protected: + StorageTest(){ + } + + ~StorageTest() override{ + } + + void SetUp() override + { + File::setFile(testConfigFilename, R"CONFIG( +<config> + <datapath>.</datapath> + <maxage>2592000</maxage> +</config> +)CONFIG"); + std::error_code ec; + fs::remove(testDbFilename, ec); + + m_config = std::make_shared<Config>(testConfigFilename); + } + + void TearDown() override + { + std::error_code ec; + fs::remove(testDbFilename, ec); + fs::remove(testConfigFilename, ec); + } + + std::shared_ptr<Config> m_config; +}; + +TEST_F(StorageTest, create) +{ + ASSERT_TRUE(!fs::exists(testDbFilename)); + + { + ASSERT_EQ(m_config->getDataPath(), "."); + ASSERT_TRUE(!fs::exists(testDbFilename)); + Storage storage(*m_config); + } + + ASSERT_TRUE(fs::exists(testDbFilename)); +} + +TEST_F(StorageTest, getNumberOfDocuments) +{ + Storage storage(*m_config); + EXPECT_EQ(storage.getNumberOfDocuments(), 0UL); + storage.setDocument("123", "abc"); + EXPECT_EQ(storage.getNumberOfDocuments(), 1UL); + storage.setDocument("def", "xyz"); + EXPECT_EQ(storage.getNumberOfDocuments(), 2UL); +} + +TEST_F(StorageTest, cleanup_empty) +{ + Storage storage(*m_config); + EXPECT_EQ(storage.getNumberOfDocuments(), 0UL); + storage.cleanup(); + EXPECT_EQ(storage.getNumberOfDocuments(), 0UL); +} + +TEST_F(StorageTest, cleanup) +{ + Storage storage(*m_config); + EXPECT_EQ(storage.getNumberOfDocuments(), 0UL); + storage.setDocument("123", "abc"); + EXPECT_EQ(storage.getNumberOfDocuments(), 1UL); + storage.cleanup(); + EXPECT_EQ(storage.getNumberOfDocuments(), 1UL); +} + +TEST_F(StorageTest, exists) +{ + Storage storage(*m_config); + EXPECT_EQ(storage.exists(""), false); + EXPECT_EQ(storage.exists("0"), false); + EXPECT_EQ(storage.exists("123"), false); + EXPECT_EQ(storage.exists("abcdz"), false); + + storage.setDocument("", "abc"); + EXPECT_EQ(storage.exists(""), true); + storage.setDocument("0", "abc"); + EXPECT_EQ(storage.exists("0"), true); + storage.setDocument("123", "abc"); + EXPECT_EQ(storage.exists("123"), true); + storage.setDocument("abcdz", "abc"); + EXPECT_EQ(storage.exists("abcdz"), true); +} + +TEST_F(StorageTest, setDocument) +{ + Storage storage(*m_config); + storage.setDocument("0", "abc"); + EXPECT_EQ(storage.getNumberOfDocuments(), 1UL); + EXPECT_EQ(storage.getDocument("0"), "abc"); +} + +TEST_F(StorageTest, touchDocument) +{ + Storage storage(*m_config); + EXPECT_THROW(storage.touchDocument("0"), std::exception); + storage.setDocument("0", "abc"); + storage.touchDocument("0"); + EXPECT_EQ(storage.getNumberOfDocuments(), 1UL); + EXPECT_EQ(storage.getDocument("0"), "abc"); +} + +TEST_F(StorageTest, setRevision) +{ + Storage storage(*m_config); + storage.setDocument("0", "abc"); + storage.setRevision("0", 123); + + EXPECT_EQ(storage.getNumberOfDocuments(), 1UL); + EXPECT_EQ(storage.getRevision("0"), 123); + + try { + storage.setRevision("1", 123); + FAIL(); + } catch(const std::exception& ex) { + EXPECT_EQ("Unable to set revision for id 1"s, ex.what()); + } catch(...) { + FAIL(); + } +} + +TEST_F(StorageTest, setCursorPos) +{ + Storage storage(*m_config); + storage.setDocument("0", "abc"); + storage.setCursorPos("0", 1234); + + EXPECT_EQ(storage.getNumberOfDocuments(), 1UL); + EXPECT_EQ(storage.getCursorPos("0"), 1234); + + try { + storage.setCursorPos("1", 12345); + FAIL(); + } catch(const std::exception& ex) { + EXPECT_EQ("Unable to set cursor position for id 1"s, ex.what()); + } catch(...) { + FAIL(); + } +} + +TEST_F(StorageTest, setRow) +{ + Storage storage(*m_config); + storage.setRow("0", "abc", 56, 67); + + EXPECT_EQ(storage.getNumberOfDocuments(), 1UL); + EXPECT_EQ(storage.getDocument("0"), "abc"); + EXPECT_EQ(storage.getRevision("0"), 56); + EXPECT_EQ(storage.getCursorPos("0"), 67); +} + +TEST_F(StorageTest, getDocument) +{ + Storage storage(*m_config); + storage.setDocument("0", "xyz"); + storage.setDocument("0bc", "xyz2"); + storage.setDocument("iabc", "xyz3"); + storage.setDocument("zxy", "xyz4"); + + EXPECT_EQ(storage.getDocument("0"), "xyz"); +} + +TEST_F(StorageTest, getRevision) +{ + Storage storage(*m_config); + storage.setRow("0", "abc", 123, 456); + + EXPECT_EQ(storage.getRevision("0"), 123); +} + +TEST_F(StorageTest, getCursorPos) +{ + Storage storage(*m_config); + storage.setRow("0", "abc", 123, 456); + + EXPECT_EQ(storage.getCursorPos("0"), 456); +} + +TEST_F(StorageTest, getRow) +{ + Storage storage(*m_config); + storage.setRow("0", "abc", 123, 456); + + auto row{storage.getRow("0")}; + EXPECT_EQ(std::get<0>(row), "abc"); + EXPECT_EQ(std::get<1>(row), 123); + EXPECT_EQ(std::get<2>(row), 456); +} + +TEST_F(StorageTest, revision_increment) +{ + Storage storage(*m_config); + storage.setDocument("0", "xyz"); + storage.setDocument("0bc", "xyz2"); + storage.setDocument("iabc", "xyz3"); + + EXPECT_EQ(storage.getRevision("0"), 0); + EXPECT_EQ(storage.getRevision("0bc"), 0); + EXPECT_EQ(storage.getRevision("iabc"), 0); + + storage.setDocument("0bc", "xyz234"); + EXPECT_EQ(storage.getRevision("0bc"), 1); + + storage.setDocument("0bc", "xyz2345"); + EXPECT_EQ(storage.getRevision("0"), 0); + EXPECT_EQ(storage.getRevision("0bc"), 2); + EXPECT_EQ(storage.getRevision("iabc"), 0); +} + +TEST_F(StorageTest, generate_id) +{ + Storage storage(*m_config); + for (int i = 0; i < 100; i++) { + std::string a{storage.generate_id()}; + std::string b{storage.generate_id()}; + + EXPECT_NE(a, b); + EXPECT_NE(a, ""); + EXPECT_NE(b, ""); + + EXPECT_GE(a.size(), 6); + + for (char c: a + b) { + EXPECT_TRUE((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')); + } + } +} + +TEST_F(StorageTest, checksum32) +{ + EXPECT_EQ(checksum32(""), 0); + EXPECT_EQ(checksum32("0"), 48); + EXPECT_EQ(checksum32("\x00"), 0); + EXPECT_EQ(checksum32("123"), 1073741862); + EXPECT_EQ(checksum32("a"), 97); + EXPECT_EQ(checksum32("ab"), 82); + EXPECT_EQ(checksum32("abc"), 1073741898); +} + +TEST_F(StorageTest, db_size) +{ + Storage storage(*m_config); + auto dbsize_gross{storage.dbsize_gross()}; + auto dbsize_net{storage.dbsize_net()}; + + EXPECT_LE(0, storage.dbsize_net()); + EXPECT_LE(storage.dbsize_net(), storage.dbsize_gross()); + + storage.setDocument("0", "xyz"); + + EXPECT_LE(dbsize_net, storage.dbsize_net()); + EXPECT_LE(dbsize_gross, storage.dbsize_gross()); +} + diff --git a/tests/test-whiteboard.cpp b/tests/test-whiteboard.cpp new file mode 100644 index 0000000..3f70bcf --- /dev/null +++ b/tests/test-whiteboard.cpp @@ -0,0 +1,255 @@ +#include <gtest/gtest.h> + +#include <cstring> +#include <filesystem> +#include <memory> +#include <regex> +#include <string> +#include <system_error> +#include <thread> + +#include <boost/process/child.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/asio/buffer.hpp> +#include <boost/asio/buffers_iterator.hpp> +#include <boost/asio/connect.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/local/stream_protocol.hpp> + +#include <unistd.h> + +#include "libreichwein/file.h" +#include "libreichwein/process.h" + +#include "config.h" +#include "storage.h" +#include "whiteboard.h" + +namespace bp = boost::process; +namespace fs = std::filesystem; +using namespace Reichwein; +using namespace std::string_literals; + +namespace { + const fs::path webserverConfigFilename{"./webserver.conf"}; + + const fs::path testConfigFilename{"./whiteboard.conf"}; + const fs::path testDbFilename{"./whiteboard.db3"}; +} + +class WhiteboardTest: public ::testing::Test +{ +protected: + WhiteboardTest(){ + } + + ~WhiteboardTest() override{ + } + + void SetUp() override + { + File::setFile(testConfigFilename, R"CONFIG( +<config> + <port>::1:9876</port> + <datapath>.</datapath> + <maxage>2592000</maxage> + <threads>4</threads> + <maxconnections>3</maxconnections> +</config> +)CONFIG"); + std::error_code ec; + fs::remove(testDbFilename, ec); + + m_config = std::make_shared<Config>(testConfigFilename); + + m_pid = fork(); + if (m_pid == -1) { + throw std::runtime_error("Error on fork(): "s + strerror(errno)); + } else if (m_pid == 0) { // child + Whiteboard whiteboard; + std::vector<std::string> argvv{{"whiteboard", "-c", testConfigFilename.generic_string()}}; + char* argv[] = {argvv[0].data(), argvv[1].data(), argvv[2].data(), nullptr}; + whiteboard.run(argvv.size(), argv); + exit(0); + } + Process::wait_for_pid_listening_on(m_pid, 9876); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + + void TearDown() override + { + if (m_pid == 0) + throw std::runtime_error("Whiteboard not running on requesting SIGTERM"); + + if (kill(m_pid, SIGTERM) != 0) + throw std::runtime_error("Unable to SIGTERM Whiteboard"); + + if (int result = waitpid(m_pid, NULL, 0); result != m_pid) + throw std::runtime_error("waitpid returned "s + std::to_string(result)); + + std::error_code ec; + fs::remove(testDbFilename, ec); + fs::remove(testConfigFilename, ec); + } + + std::shared_ptr<Config> m_config; + pid_t m_pid{}; +}; + +class WebsocketClient +{ +public: + WebsocketClient() + { + std::string host = "::1"; + auto const port = "9876" ; + + // These objects perform our I/O + boost::asio::ip::tcp::resolver resolver{ioc_}; + ws_ = std::make_unique<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>>(ioc_); + + // Look up the domain name + resolver_results_ = resolver.resolve(host, port); + + connect(); + handshake(); + } + + void connect() + { + // Make the connection on the IP address we get from a lookup + ep_ = boost::asio::connect(boost::beast::get_lowest_layer(*ws_), resolver_results_); + } + + void handshake() + { + // Update the host_ string. This will provide the value of the + // Host HTTP header during the WebSocket handshake. + // See https://tools.ietf.org/html/rfc7230#section-5.4 + std::string host{"[::1]:9876"}; + + // Set a decorator to change the User-Agent of the handshake + ws_->set_option(boost::beast::websocket::stream_base::decorator( + [](boost::beast::websocket::request_type& req) + { + req.set(boost::beast::http::field::user_agent, + std::string("Reichwein.IT Test Websocket Client")); + })); + + // Perform the websocket handshake + ws_->handshake(host, "/"); + + } + + void write(const std::string& data) + { + ws_->write(boost::asio::buffer(data)); + } + + std::string read() + { + boost::beast::flat_buffer buffer; + ws_->read(buffer); + return {boost::asio::buffers_begin(buffer.data()), boost::asio::buffers_end(buffer.data())}; + } + + ~WebsocketClient() + { + } + + bool is_open() + { + return ws_->is_open(); + } + +private: + boost::asio::io_context ioc_; + boost::asio::ip::tcp::resolver::results_type resolver_results_; + std::unique_ptr<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>> ws_; + boost::asio::ip::tcp::endpoint ep_; +}; + +// +// tests via websocket server in separate process (hides coverage) +// + +TEST_F(WhiteboardTest, websocket_server_connection) +{ + WebsocketClient wc; +} + +TEST_F(WhiteboardTest, websocket_server_generate_id) +{ + WebsocketClient wc; + + wc.write("<request><command>newid</command></request>"); + std::string result0 {wc.read()}; + ASSERT_TRUE(boost::algorithm::starts_with(result0, "<serverinfo><type>newid</type><id>")); + ASSERT_TRUE(boost::algorithm::ends_with(result0, "</id></serverinfo>")); + ASSERT_EQ(result0.size(), 58); + + wc.write("<request><command>newid</command></request>"); + std::string result1 {wc.read()}; + ASSERT_TRUE(boost::algorithm::starts_with(result1, "<serverinfo><type>newid</type><id>")); + ASSERT_TRUE(boost::algorithm::ends_with(result1, "</id></serverinfo>")); + ASSERT_EQ(result1.size(), 58); + + ASSERT_NE(result0, result1); +} + +// check number of threads as configured +TEST_F(WhiteboardTest, threads) +{ + ASSERT_GE(Process::number_of_threads(m_pid), 4); +} + +TEST_F(WhiteboardTest, max_connections) +{ + WebsocketClient wc1; + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + ASSERT_TRUE(wc1.is_open()); + + WebsocketClient wc2; + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + ASSERT_TRUE(wc2.is_open()); + + WebsocketClient wc3; + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + ASSERT_TRUE(wc3.is_open()); + + ASSERT_THROW(WebsocketClient wc4, std::exception); +} + +TEST_F(WhiteboardTest, id) +{ + WebsocketClient wc; + + wc.write("<request><command>getfile</command><id>1</id></request>"); + std::string result {wc.read()}; + + EXPECT_EQ(result, "<serverinfo><type>getfile</type><data/><revision>0</revision><pos>0</pos></serverinfo>"); + + wc.write("<request><command>getfile</command><id></id></request>"); + result = wc.read(); + EXPECT_EQ(result, "<serverinfo><type>error</type><message>Message handling error: Invalid id (empty)</message></serverinfo>"); + + wc.write("<request><command>getfile</command><id>01234567890123456789</id></request>"); + result = wc.read(); + EXPECT_EQ(result, "<serverinfo><type>error</type><message>Message handling error: Invalid id (size > 16)</message></serverinfo>"); + + wc.write("<request><command>getfile</command><id>X</id></request>"); + result = wc.read(); + EXPECT_EQ(result, "<serverinfo><type>error</type><message>Message handling error: Invalid id char: X</message></serverinfo>"); + + wc.write("<request><command>getfile</command><id>a.</id></request>"); + result = wc.read(); + EXPECT_EQ(result, "<serverinfo><type>error</type><message>Message handling error: Invalid id char: .</message></serverinfo>"); + + wc.write("<request><command>getfile</command><id>a$b</id></request>"); + result = wc.read(); + EXPECT_EQ(result, "<serverinfo><type>error</type><message>Message handling error: Invalid id char: $</message></serverinfo>"); +} + |