diff --git a/.gitmodules b/.gitmodules index 1f14c394..901e28eb 100755 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "trantor"] path = trantor url = https://github.com/an-tao/trantor.git +[submodule "third_party/googletest"] + path = third_party/googletest + url = https://github.com/google/googletest diff --git a/CMakeLists.txt b/CMakeLists.txt index 216c4be2..35e49987 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -332,7 +332,7 @@ set(INCLUDING_DIRS ${INS_STRING}) configure_file(${PROJECT_SOURCE_DIR}/cmake/templates/config.h.in ${PROJECT_BINARY_DIR}/drogon/config.h @ONLY) -if(MAKETEST STREQUAL YES) +if(MAKE_TESTING) add_subdirectory(lib/tests) if(PostgreSQL_FOUND) add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/postgresql_impl/test) @@ -344,6 +344,9 @@ if(MAKETEST STREQUAL YES) if(SQLITE3_FOUND) add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/sqlite3_impl/test) endif() + enable_testing() + add_subdirectory(third_party/googletest) + add_subdirectory(unittest) endif() # Installation diff --git a/build.sh b/build.sh index ab51f9d7..a5bebb9d 100755 --- a/build.sh +++ b/build.sh @@ -28,7 +28,7 @@ function build_drogon() { echo "Start building drogon ..." if [ $1 -eq 1 ]; then - cmake .. -DMAKETEST=YES + cmake .. -DMAKE_TESTING=YES else cmake .. fi diff --git a/lib/src/Utilities.cc b/lib/src/Utilities.cc index 3f638340..612eefbe 100644 --- a/lib/src/Utilities.cc +++ b/lib/src/Utilities.cc @@ -715,19 +715,28 @@ std::string gzipCompress(const char *data, const size_t ndata) outstr.resize(compressBound(ndata)); strm.next_in = (Bytef *)data; strm.avail_in = ndata; - strm.next_out = (Bytef *)outstr.data(); - strm.avail_out = outstr.length(); - if (deflate(&strm, Z_FINISH) != Z_STREAM_END) + int ret; + do { - LOG_ERROR << "deflate error!"; - return std::string{}; - } - if (deflateEnd(&strm) != Z_OK) - { - LOG_ERROR << "deflateEnd error!"; - return std::string{}; - } + if (strm.total_out >= outstr.size()) + { + outstr.resize(strm.total_out * 2); + } + assert(outstr.size() >= strm.total_out); + strm.avail_out = outstr.size() - strm.total_out; + strm.next_out = (Bytef *)outstr.data() + strm.total_out; + ret = deflate(&strm, Z_FINISH); /* no bad return value */ + if (ret == Z_STREAM_ERROR) + { + (void)deflateEnd(&strm); + return std::string{}; + } + } while (strm.avail_out == 0); + assert(strm.avail_in == 0); + assert(ret == Z_STREAM_END); /* stream will be complete */ outstr.resize(strm.total_out); + /* clean up and return */ + (void)deflateEnd(&strm); return outstr; } return std::string{}; diff --git a/third_party/googletest b/third_party/googletest new file mode 160000 index 00000000..2002f267 --- /dev/null +++ b/third_party/googletest @@ -0,0 +1 @@ +Subproject commit 2002f267f05be6f41a3d458954414ba2bfa3ff1d diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt new file mode 100644 index 00000000..9ab93f3d --- /dev/null +++ b/unittest/CMakeLists.txt @@ -0,0 +1,20 @@ +add_executable(msgbuffer_unittest MsgBufferUnittest.cpp) +add_executable(timer_unittest TimerUnittest.cpp) +add_executable(drobject_unittest DrObjectUnittest.cpp) +add_executable(gzip_unittest GzipUnittest.cpp) + +set(UNITTEST_TARGETS + msgbuffer_unittest + timer_unittest + drobject_unittest + gzip_unittest) +set_property(TARGET ${UNITTEST_TARGETS} + PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD}) +set_property(TARGET ${UNITTEST_TARGETS} PROPERTY CXX_STANDARD_REQUIRED ON) +set_property(TARGET ${UNITTEST_TARGETS} PROPERTY CXX_EXTENSIONS OFF) + +include(GoogleTest) +foreach(T ${UNITTEST_TARGETS}) + target_link_libraries(${T} PRIVATE drogon gtest) + gtest_discover_tests(${T}) +endforeach() diff --git a/unittest/DrObjectUnittest.cpp b/unittest/DrObjectUnittest.cpp new file mode 100644 index 00000000..35837887 --- /dev/null +++ b/unittest/DrObjectUnittest.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +using namespace drogon; +class TestA : public DrObject +{ +}; +namespace test +{ +class TestB : public DrObject +{ +}; +} // namespace test +TEST(DrObjectTest, creationTest) +{ + auto obj = DrClassMap::newObject("TestA"); + EXPECT_NE(obj, nullptr); + + auto objPtr = DrClassMap::getSingleInstance("TestA"); + EXPECT_NE(objPtr.get(), nullptr); + + auto objPtr2 = DrClassMap::getSingleInstance(); + EXPECT_NE(objPtr2.get(), nullptr); + EXPECT_EQ(objPtr, objPtr2); +} + +TEST(DrObjectTest, namespaceTest) +{ + auto obj = DrClassMap::newObject("test::TestB"); + EXPECT_NE(obj, nullptr); + + auto objPtr = DrClassMap::getSingleInstance("test::TestB"); + EXPECT_NE(objPtr.get(), nullptr); + + auto objPtr2 = DrClassMap::getSingleInstance(); + EXPECT_NE(objPtr2.get(), nullptr); + EXPECT_EQ(objPtr, objPtr2); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/unittest/GzipUnittest.cpp b/unittest/GzipUnittest.cpp new file mode 100644 index 00000000..613147ac --- /dev/null +++ b/unittest/GzipUnittest.cpp @@ -0,0 +1,30 @@ +#include +#include +#include +#include +using namespace drogon::utils; +TEST(GzipTest, shortText) +{ + std::string source{"123中文顶替要枯械"}; + auto compressed = gzipCompress(source.data(), source.length()); + auto decompressed = gzipDecompress(compressed.data(), compressed.length()); + EXPECT_EQ(source, decompressed); +} + +TEST(GzipTest, longText) +{ + std::string source; + for (size_t i = 0; i < 100000; i++) + { + source.append(std::to_string(i)); + } + auto compressed = gzipCompress(source.data(), source.length()); + auto decompressed = gzipDecompress(compressed.data(), compressed.length()); + EXPECT_EQ(source, decompressed); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/unittest/MsgBufferUnittest.cpp b/unittest/MsgBufferUnittest.cpp new file mode 100644 index 00000000..9addd2cf --- /dev/null +++ b/unittest/MsgBufferUnittest.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include +using namespace trantor; +TEST(MsgBufferTest, readableTest) +{ + MsgBuffer buffer; + + EXPECT_EQ(0, buffer.readableBytes()); + buffer.append(std::string(128, 'a')); + EXPECT_EQ(128, buffer.readableBytes()); + buffer.retrieve(100); + EXPECT_EQ(28, buffer.readableBytes()); + EXPECT_EQ('a', buffer.peekInt8()); + buffer.retrieveAll(); + EXPECT_EQ(0, buffer.readableBytes()); +} +TEST(MsgBufferTest, writableTest) +{ + MsgBuffer buffer(100); + + EXPECT_EQ(100, buffer.writableBytes()); + buffer.append("abcde"); + EXPECT_EQ(95, buffer.writableBytes()); + buffer.append(std::string(100, 'x')); + EXPECT_EQ(111, buffer.writableBytes()); + buffer.retrieve(100); + EXPECT_EQ(111, buffer.writableBytes()); + buffer.append(std::string(112, 'c')); + EXPECT_EQ(99, buffer.writableBytes()); + buffer.retrieveAll(); + EXPECT_EQ(216, buffer.writableBytes()); +} + +TEST(MsgBufferTest, addInFrontTest) +{ + MsgBuffer buffer(100); + + EXPECT_EQ(100, buffer.writableBytes()); + buffer.addInFrontInt8('a'); + EXPECT_EQ(100, buffer.writableBytes()); + buffer.addInFrontInt64(123); + EXPECT_EQ(92, buffer.writableBytes()); + buffer.addInFrontInt64(100); + EXPECT_EQ(84, buffer.writableBytes()); + buffer.addInFrontInt8(1); + EXPECT_EQ(84, buffer.writableBytes()); +} + +TEST(MsgBuffer, MoveContrustor) +{ + MsgBuffer buf1(100); + const char *bufptr1 = buf1.peek(); + MsgBuffer buffnew1 = std::move(buf1); + EXPECT_EQ(bufptr1, buffnew1.peek()); + + MsgBuffer buf2(100); + const char *bufptr2 = buf2.peek(); + MsgBuffer buffnew2(std::move(buf2)); + EXPECT_EQ(bufptr2, buffnew2.peek()); +} + +TEST(Msgbuffer, MoveAssignmentOperator) +{ + MsgBuffer buf(100); + const char *bufptr = buf.peek(); + size_t writable = buf.writableBytes(); + MsgBuffer buffnew(1000); + buffnew = std::move(buf); + EXPECT_EQ(bufptr, buffnew.peek()); + EXPECT_EQ(writable, buffnew.writableBytes()); +} +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/unittest/TimerUnittest.cpp b/unittest/TimerUnittest.cpp new file mode 100644 index 00000000..ec7f8325 --- /dev/null +++ b/unittest/TimerUnittest.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include +using namespace trantor; +using namespace std::literals; +EventLoop loop; + +TEST(TimerTest, RunEvery) +{ + size_t count{0}; + std::chrono::steady_clock::time_point tp{std::chrono::steady_clock::now()}; + loop.runEvery(0.1s, [&count, tp] { + ++count; + if (count == 10) + { + auto d = std::chrono::steady_clock::now() - tp; + auto err = d > 1s ? d - 1s : 1s - d; + auto msErr = + std::chrono::duration_cast(err) + .count(); + EXPECT_LT(msErr, 100LL); + loop.quit(); + return; + } + std::this_thread::sleep_for(0.02s); + }); + loop.runAfter(0.35s, [tp] { + auto d = std::chrono::steady_clock::now() - tp; + auto err = d > 0.35s ? d - 0.35s : 0.35s - d; + auto msErr = + std::chrono::duration_cast(err).count(); + EXPECT_LT(msErr, 30LL); + }); + loop.loop(); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file