* Copyright (C) 2025-2026. Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <mockcpp/mockcpp.hpp>
#include <fstream>
#include <memory>
#include <filesystem>
#include <vector>
#include <algorithm>
#include <unistd.h>
#include "plugin/ipc_monitor/jsonl/RotateLogger.h"
#include "plugin/ipc_monitor/utils/utils.h"
using namespace dynolog_npu::ipc_monitor::jsonl;
std::string ReadFileContent(const std::string& filePath)
{
std::ifstream ifs(filePath, std::ios::binary | std::ios::ate);
if (!ifs)
{
return "";
}
auto size = ifs.tellg();
if (size < 0)
{
return "";
}
ifs.seekg(0);
std::string content;
content.resize(static_cast<size_t>(size));
ifs.read(content.data(), size);
return content;
}
class RotateLoggerTest : public ::testing::Test {
protected:
std::string testLogDir;
void SetUp() override
{
GlobalMockObject::verify();
MOCKER_CPP(&dynolog_npu::ipc_monitor::GetRankId)
.stubs()
.will(returnValue(0));
testLogDir = "./test_rotate_logger_" + std::to_string(getpid());
std::filesystem::create_directory(testLogDir);
}
void TearDown() override
{
if (std::filesystem::exists(testLogDir)) {
std::filesystem::remove_all(testLogDir);
}
}
};
TEST_F(RotateLoggerTest, ConstructorInitialization)
{
RotateLogger logger(testLogDir, 100, 5);
SUCCEED();
}
TEST_F(RotateLoggerTest, BasicLogToFile)
{
RotateLogger logger(testLogDir, 100, 5);
std::string message = "Test log message\n";
EXPECT_NO_THROW(logger.Log(message));
EXPECT_NO_THROW(logger.Flush());
EXPECT_NO_THROW(logger.UnInit());
bool fileFound = false;
for (const auto& entry : std::filesystem::directory_iterator(testLogDir)) {
if (entry.path().extension() == ".jsonl") {
fileFound = true;
std::string content = ReadFileContent(entry.path().string());
EXPECT_EQ(content, message);
break;
}
}
EXPECT_TRUE(fileFound);
}
TEST_F(RotateLoggerTest, LineLimitTriggersRotation)
{
RotateLogger logger(testLogDir, 2, 5);
EXPECT_NO_THROW(logger.Log("Message 1\n"));
EXPECT_NO_THROW(logger.Log("Message 2\n"));
EXPECT_NO_THROW(logger.Log("Message 3\n"));
EXPECT_NO_THROW(logger.Log("Message 4\n"));
EXPECT_NO_THROW(logger.Flush());
EXPECT_NO_THROW(logger.UnInit());
int fileCount = 0;
for (const auto& entry : std::filesystem::directory_iterator(testLogDir)) {
if (entry.path().extension() == ".jsonl") {
fileCount++;
}
}
EXPECT_GE(fileCount, 2);
}
TEST_F(RotateLoggerTest, FileLimitEnforcement)
{
RotateLogger logger(testLogDir, 1, 3);
for (int i = 0; i < 5; ++i) {
EXPECT_NO_THROW(logger.Log("Message " + std::to_string(i) + "\n"));
}
EXPECT_NO_THROW(logger.Flush());
EXPECT_NO_THROW(logger.UnInit());
int fileCount = 0;
for (const auto& entry : std::filesystem::directory_iterator(testLogDir)) {
if (entry.path().extension() == ".jsonl") {
fileCount++;
}
}
EXPECT_LE(fileCount, 3);
}
TEST_F(RotateLoggerTest, EmptyMessageHandling)
{
RotateLogger logger(testLogDir, 100, 5);
EXPECT_NO_THROW(logger.Log(""));
std::string validMessage = "Valid message\n";
EXPECT_NO_THROW(logger.Log(validMessage));
EXPECT_NO_THROW(logger.Flush());
EXPECT_NO_THROW(logger.UnInit());
bool fileFound = false;
for (const auto& entry : std::filesystem::directory_iterator(testLogDir)) {
if (entry.path().extension() == ".jsonl") {
fileFound = true;
std::string content = ReadFileContent(entry.path().string());
EXPECT_EQ(content, validMessage);
break;
}
}
EXPECT_TRUE(fileFound);
}
TEST_F(RotateLoggerTest, MultipleMessagesInOneFile)
{
RotateLogger logger(testLogDir, 10, 5);
std::vector<std::string> messages = {
"First message\n",
"Second message\n",
"Third message\n"
};
for (const auto& msg : messages) {
EXPECT_NO_THROW(logger.Log(msg));
}
EXPECT_NO_THROW(logger.Flush());
EXPECT_NO_THROW(logger.UnInit());
std::vector<std::string> foundFiles;
for (const auto& entry : std::filesystem::directory_iterator(testLogDir)) {
if (entry.path().extension() == ".jsonl") {
foundFiles.push_back(entry.path().string());
}
}
ASSERT_EQ(foundFiles.size(), 1);
std::string content = ReadFileContent(foundFiles[0]);
EXPECT_NE(content.find(messages[0]), std::string::npos);
EXPECT_NE(content.find(messages[1]), std::string::npos);
EXPECT_NE(content.find(messages[2]), std::string::npos);
}
TEST_F(RotateLoggerTest, UnInitClosesResources)
{
std::string tempDir = testLogDir + "_uninit";
std::filesystem::create_directory(tempDir);
{
RotateLogger logger(tempDir, 100, 5);
EXPECT_NO_THROW(logger.Log("Test message before UnInit\n"));
EXPECT_NO_THROW(logger.Flush());
EXPECT_NO_THROW(logger.UnInit());
EXPECT_NO_THROW(logger.Log("Message after UnInit\n"));
}
int fileCount = 0;
for (const auto& entry : std::filesystem::directory_iterator(tempDir)) {
if (entry.path().extension() == ".jsonl") {
fileCount++;
}
}
EXPECT_GE(fileCount, 0);
std::filesystem::remove_all(tempDir);
}
TEST_F(RotateLoggerTest, FlushEnsuresDataPersistence)
{
RotateLogger logger(testLogDir, 100, 5);
EXPECT_NO_THROW(logger.Log("Flushing test message\n"));
EXPECT_NO_THROW(logger.Flush());
EXPECT_NO_THROW(logger.UnInit());
bool foundContent = false;
for (const auto& entry : std::filesystem::directory_iterator(testLogDir)) {
if (entry.path().extension() == ".jsonl") {
std::string content = ReadFileContent(entry.path().string());
if (content.find("Flushing test message") != std::string::npos) {
foundContent = true;
break;
}
}
}
EXPECT_TRUE(foundContent);
}
TEST_F(RotateLoggerTest, DestructorHandlesCleanup)
{
std::string tempDir = testLogDir + "_dtor";
std::filesystem::create_directory(tempDir);
{
RotateLogger logger(tempDir, 100, 5);
EXPECT_NO_THROW(logger.Log("Test message before destruction\n"));
}
EXPECT_TRUE(std::filesystem::exists(tempDir));
bool fileFound = false;
for (const auto& entry : std::filesystem::directory_iterator(tempDir)) {
if (entry.path().extension() == ".jsonl") {
fileFound = true;
break;
}
}
EXPECT_TRUE(fileFound);
std::filesystem::remove_all(tempDir);
}
TEST_F(RotateLoggerTest, StressTestWithManyMessages)
{
RotateLogger logger(testLogDir, 5, 10);
constexpr int numMessages = 50;
for (int i = 0; i < numMessages; ++i) {
EXPECT_NO_THROW(logger.Log("Stress test message " + std::to_string(i) + "\n"));
}
EXPECT_NO_THROW(logger.Flush());
EXPECT_NO_THROW(logger.UnInit());
int fileCount = 0;
std::vector<std::string> filePaths;
for (const auto& entry : std::filesystem::directory_iterator(testLogDir)) {
if (entry.path().extension() == ".jsonl") {
fileCount++;
filePaths.push_back(entry.path().string());
}
}
EXPECT_LE(fileCount, 10);
EXPECT_GE(fileCount, 1);
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}