* -------------------------------------------------------------------------
* This file is part of the MultimodalSDK project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* MultimodalSDK is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
* Description: file utils api.
* Author: ACC SDK
* Create: 2025
* History: NA
*/
#include <pwd.h>
#include <fstream>
#include <unistd.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <gtest/gtest.h>
#include <acc/utils/FileUtils.h>
#include "acc/ErrorCode.h"
static int (*g_realStat)(const char*, struct stat*) = nullptr;
extern "C" int __xstat(int ver, const char* path, struct stat* buf)
{
static int (*real_xstat)(int, const char*, struct stat*) = nullptr;
if (!real_xstat) {
real_xstat = (decltype(real_xstat))dlsym(RTLD_NEXT, "__xstat");
}
return real_xstat(ver, path, buf);
}
using namespace Acc;
namespace {
constexpr int FILE_PATH_MAX = 4096;
constexpr mode_t MOCK_MODE = 0755;
const std::string EXPECT_FILE_CONTENT = "Hello, World!";
class FileUtilsTest : public testing::Test {
protected:
void SetUp() override
{
std::ofstream outfile("valid_file.bin", std::ios::binary);
outfile << EXPECT_FILE_CONTENT;
outfile.close();
std::ofstream outfile2("empty_file.bin", std::ios::binary);
outfile2.close();
std::ofstream("regular_file.txt").close();
std::ofstream("symlink.txt").close();
std::remove("symlink.txt");
symlink("regular_file.txt", "symlink.txt");
}
void TearDown() override
{
std::remove("valid_file.bin");
std::remove("empty_file.bin");
std::remove("regular_file.txt");
std::remove("symlink.txt");
}
};
TEST_F(FileUtilsTest, CheckFileExtension_ShouldReturnTrue_WhenPathIsValid)
{
const char* path = "test.jpg";
bool ret = CheckFileExtension(path, "jpg");
EXPECT_TRUE(ret);
}
TEST_F(FileUtilsTest, CheckFileExtension_ShouldReturnFalse_WhenPathIsNullptr)
{
bool ret = CheckFileExtension(nullptr, "jpg");
EXPECT_EQ(ret, false);
}
TEST_F(FileUtilsTest, CheckFileExtension_ShouldReturnFalse_WhenPathNoSuffix)
{
const char* path = "test";
bool ret = CheckFileExtension(path, "jpg");
EXPECT_EQ(ret, false);
}
TEST_F(FileUtilsTest, CheckFileOpen_ShouldReturnTrue_WhenFileExists)
{
std::ifstream file;
bool ret = CheckFileOpen("valid_file.bin", file);
EXPECT_TRUE(ret);
file.close();
}
TEST_F(FileUtilsTest, CheckFileOpen_ShouldReturnFalse_WhenFileDoesNotExist)
{
std::ifstream file;
bool ret = CheckFileOpen("non_existent_file.bin", file);
EXPECT_EQ(ret, false);
}
TEST_F(FileUtilsTest, CheckAndGetFileSize_ShouldReturnFalse_WhenFileIsEmpty)
{
std::ifstream file;
int64_t fileSize = -1;
constexpr size_t kMaxFileSize = 1024 * 1024;
bool ret = CheckAndGetFileSize(file, kMaxFileSize, fileSize);
EXPECT_FALSE(ret);
}
TEST_F(FileUtilsTest, CheckAndGetFileSize_ShouldReturnFalse_WhenFileIsTooLarge)
{
const char* testFilePath = "test_2mb_file.bin";
const size_t maxSize = 1 * 1024 * 1024;
{
std::ofstream ofs(testFilePath, std::ios::binary);
std::vector<char> buffer(1024 * 1024, 0xAA);
ofs.write(buffer.data(), buffer.size());
ofs.write(buffer.data(), buffer.size());
ofs.close();
}
std::ifstream file;
ASSERT_TRUE(CheckFileOpen(testFilePath, file));
int64_t fileSize = -1;
bool ret = CheckAndGetFileSize(file, maxSize, fileSize);
EXPECT_FALSE(ret);
std::remove(testFilePath);
}
TEST_F(FileUtilsTest, ReadFile_ShouldReturnSuccess_WhenFileReadSucceeds)
{
std::vector<uint8_t> data;
ErrorCode ret = ReadFile("valid_file.bin", data);
EXPECT_EQ(ret, SUCCESS);
EXPECT_EQ(data.size(), EXPECT_FILE_CONTENT.size());
if (data.size() >= EXPECT_FILE_CONTENT.size()) {
const std::vector<uint8_t> expectedData(EXPECT_FILE_CONTENT.begin(), EXPECT_FILE_CONTENT.end());
EXPECT_EQ(data, expectedData);
}
}
TEST_F(FileUtilsTest, ReadFile_ShouldReturnErr_WhenPathIsInvalid)
{
std::vector<uint8_t> data;
ErrorCode ret = ReadFile("invalid_path.bin", data);
EXPECT_EQ(ret, ERR_OPEN_FILE_FAILURE);
}
TEST_F(FileUtilsTest, ReadFile_ShouldReturnErr_WhenFileIsEmpty)
{
std::vector<uint8_t> data;
ErrorCode ret = ReadFile("empty_file.bin", data);
EXPECT_EQ(ret, ERR_INVALID_FILE_SIZE);
}
TEST_F(FileUtilsTest, ReadFile_ShouldReturnErr_WhenFileIsTooLarge)
{
const char* testFilePath = "test_2mb_file.bin";
const size_t maxSize = 1 * 1024 * 1024;
{
std::ofstream ofs(testFilePath, std::ios::binary);
std::vector<char> buffer(1024 * 1024, 0xAA);
ofs.write(buffer.data(), buffer.size());
ofs.write(buffer.data(), buffer.size());
ofs.close();
}
std::vector<uint8_t> data;
ErrorCode ret = ReadFile(testFilePath, data, maxSize);
EXPECT_EQ(ret, ERR_INVALID_FILE_SIZE);
std::remove(testFilePath);
}
TEST_F(FileUtilsTest, CheckFilePath_ShouldReturnFalse_WhenPathIsEmpty)
{
std::string emptyPath = "";
EXPECT_FALSE(CheckFilePath(emptyPath));
}
TEST_F(FileUtilsTest, CheckFilePath_ShouldReturnFalse_WhenPathExceedsMaxLength)
{
std::string longPath(FILE_PATH_MAX + 1, 'a');
EXPECT_FALSE(CheckFilePath(longPath));
}
TEST_F(FileUtilsTest, CheckFilePath_ShouldReturnFalse_WhenPathDoesNotExist)
{
std::string nonExistentPath = "non_existent_file.txt";
EXPECT_FALSE(CheckFilePath(nonExistentPath));
}
TEST_F(FileUtilsTest, CheckFilePath_ShouldReturnFalse_WhenPathIsSymlink)
{
std::string symlinkPath = "symlink.txt";
EXPECT_FALSE(CheckFilePath(symlinkPath));
}
TEST_F(FileUtilsTest, CheckFilePath_ShouldReturnFalse_WhenPathIsNotRegularFile)
{
std::string directoryPath = ".";
EXPECT_FALSE(CheckFilePath(directoryPath));
}
TEST_F(FileUtilsTest, CheckFilePath_ShouldReturnTrue_WhenPathIsValid)
{
std::string validPath = "regular_file.txt";
EXPECT_TRUE(CheckFilePath(validPath));
}
TEST_F(FileUtilsTest, CheckFileOwner_ShouldReturnFalse_WhenStatFails)
{
std::string invalidPath = "/path/to/nonexistent/file";
bool result = CheckFileOwner(invalidPath);
EXPECT_FALSE(result);
}
TEST_F(FileUtilsTest, CheckFileOwner_ShouldReturnFalse_WhenFileOwnerMismatch)
{
ASSERT_NO_THROW({
std::string filePath = "test.txt";
std::string targetUser = "HwHiAiUser";
struct passwd* pwd = getpwnam(targetUser.c_str());
if (pwd == nullptr) {
throw std::runtime_error("User not found: " + targetUser);
}
int fd = open(filePath.c_str(), O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fd == -1) {
throw std::runtime_error("Failed to create file: " + filePath + ", error: " + std::string(strerror(errno)));
}
uid_t targetUid = pwd->pw_uid;
if (chown(filePath.c_str(), targetUid, -1) != 0) {
close(fd);
unlink(filePath.c_str());
throw std::runtime_error("Failed to change file owner: " + filePath +
", error: " + std::string(strerror(errno)));
}
bool ret = CheckFileOwner(filePath);
EXPECT_FALSE(ret);
close(fd);
std::remove("test.txt");
});
}
TEST_F(FileUtilsTest, CheckFileOwner_ShouldReturnTrue_WhenFileOwnerMatches)
{
std::string filePath = "valid_file.bin";
bool ret = CheckFileOwner(filePath);
EXPECT_TRUE(ret);
}
TEST_F(FileUtilsTest, CheckFilePermission_ShouldReturnFalse_WhenStatCallFails)
{
std::string invalidPath = "invalid_path.bin";
mode_t mode = 0700;
EXPECT_FALSE(CheckFilePermission(invalidPath, mode));
}
TEST_F(FileUtilsTest, CheckFilePermission_ShouldReturnFalse_WhenFilePermissionExceedsMode)
{
std::string filePath = "valid_file.bin";
mode_t mode = 0700;
auto oldStat = g_realStat;
g_realStat = [](const char*, struct stat* buf) -> int {
buf->st_mode = MOCK_MODE;
return 0;
};
EXPECT_FALSE(CheckFilePermission(filePath, mode));
g_realStat = oldStat;
}
TEST_F(FileUtilsTest, CheckFilePermission_ShouldReturnTrue_WhenFilePermissionDoesNotExceedMode)
{
std::string filePath = "valid_file.bin";
mode_t mode = 0755;
auto oldStat = g_realStat;
g_realStat = [](const char*, struct stat* buf) -> int {
buf->st_mode = MOCK_MODE;
return 0;
};
EXPECT_TRUE(CheckFilePermission(filePath, mode));
g_realStat = oldStat;
}
}
int main(int argc, char* argv[])
{
testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
return ret;
}