Precommit (#1)

* first commit

* cleanup
This commit is contained in:
tompzf
2025-11-04 13:28:06 +01:00
committed by GitHub
parent dba45dc636
commit 6ed4b1534e
898 changed files with 256340 additions and 0 deletions

View File

@@ -0,0 +1,537 @@
#include "composer_test_suite.h"
#include <filesystem>
#include <fstream>
#include <cstdlib>
#include <string>
#include "../../../global/process_watchdog.h"
#include "../../../global/exec_dir_helper.h"
#include "../../../sdv_services/core/installation_manifest.cpp"
#include "../../../sdv_services/core/installation_composer.cpp"
#include "../../../sdv_services/core/toml_parser/parser_toml.cpp"
#include "../../../sdv_services/core/toml_parser/lexer_toml.cpp"
#include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp"
#include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp"
#include <support/app_control.h>
#if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain(int argc, wchar_t* argv[])
#else
extern "C" int main(int argc, char* argv[])
#endif
{
CProcessWatchdog watchdog;
CLocalMemMgr memmgr;
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
void CInstallPackageComposerTest::SetUp()
{}
void CInstallPackageComposerTest::TearDown()
{
DeleteTargetTestDir();
}
void CInstallPackageComposerTest::SetUpTestCase()
{
DeleteTestDirs();
// Set the application installation directory
sdv::app::CAppControl::SetComponentInstallDirectory(GetExecDirectory() / "install_package_composer_targets");
// Make rand random
std::srand(static_cast<unsigned int>(std::time({})));
// Create test data
try
{
// Create a dummy file with at the given path.
auto fnCreateDummyFile = [](const std::filesystem::path& rpathFile, uint32_t uiMinSize = 0)
{
std::ofstream stream(rpathFile.native().c_str());
if (!stream.is_open())
{
std::cerr << "Could not open file '" << rpathFile.generic_u8string() << "'" << std::endl;
ASSERT_TRUE(false);
}
uint32_t uiTotalSize = 0;
do
{
uint32_t uiSize = std::rand() & 0x1ffff;
for (uint32_t uiVal = 0; uiVal < uiSize; uiVal++)
stream << uiVal;
uiTotalSize += uiSize * 4;
}
while (uiTotalSize < uiMinSize);
stream.close();
};
// The source directory
std::filesystem::path pathSrcDir = GetExecDirectory() / "install_package_composer_sources";
// Create dummy packages (one deliberately with a wrong extension)
std::filesystem::create_directories(pathSrcDir);
fnCreateDummyFile(pathSrcDir / "file1.sdv_package");
fnCreateDummyFile(pathSrcDir / "file2.sdv_package");
fnCreateDummyFile(pathSrcDir / "file3.sdvpackage");
fnCreateDummyFile(pathSrcDir / "config.toml");
// Create 10 dummy packages
std::filesystem::path pathSrcDummy = pathSrcDir / "dummy_package";
std::filesystem::create_directories(pathSrcDummy);
for (size_t n = 0; n < 10; n++)
fnCreateDummyFile(pathSrcDummy / (std::string("file") + std::to_string(n) + ".bin"));
// Create the following dummy packages:
// subdir1/file10.bin
// subdir1/file11.bin
// subdir2/file20.bin
// subdir2/file21.bin
// subdir2/file22.bia
// subdir3_xyz/file30.bin
// subdir3_xyz/file31.bin
// subdir3_xyz/file32a.bia
// subdir3_xyz/subdir4/file40a.bin
// subdir3_xyz/subdir4/subdir5/file50a.bin
std::filesystem::path pathSrcDummySubDir1 = pathSrcDummy / "subdir1";
std::filesystem::create_directories(pathSrcDummySubDir1);
fnCreateDummyFile(pathSrcDummySubDir1 / "file10.bin");
fnCreateDummyFile(pathSrcDummySubDir1 / "file11.bin");
std::filesystem::path pathSrcDummySubDir2 = pathSrcDummy / "subdir2";
std::filesystem::create_directories(pathSrcDummySubDir2);
fnCreateDummyFile(pathSrcDummySubDir2 / "file20.bin");
fnCreateDummyFile(pathSrcDummySubDir2 / "file21.bin");
fnCreateDummyFile(pathSrcDummySubDir2 / "file22.bia");
std::filesystem::path pathSrcDummySubDir3 = pathSrcDummy / "subdir3_xyz";
std::filesystem::create_directories(pathSrcDummySubDir3);
fnCreateDummyFile(pathSrcDummySubDir3 / "file30.bin");
fnCreateDummyFile(pathSrcDummySubDir3 / "file31.bin");
fnCreateDummyFile(pathSrcDummySubDir3 / "file32a.bia");
std::filesystem::path pathSrcDummySubDir4 = pathSrcDummySubDir3 / "subdir4";
std::filesystem::create_directories(pathSrcDummySubDir4);
fnCreateDummyFile(pathSrcDummySubDir4 / "file40a.bin");
std::filesystem::path pathSrcDummySubDir5 = pathSrcDummySubDir4 / "subdir5";
std::filesystem::create_directories(pathSrcDummySubDir5);
fnCreateDummyFile(pathSrcDummySubDir5 / "file50a.bin");
// Files for filename duplication test
std::filesystem::path pathSrcDummySubDir1b = pathSrcDummy / "subdir1b";
std::filesystem::create_directories(pathSrcDummySubDir1b);
fnCreateDummyFile(pathSrcDummySubDir1b / "file10.bin");
fnCreateDummyFile(pathSrcDummySubDir1b / "file11.bin");
// XXL-Files
std::filesystem::path pathSrcDummyXXL = pathSrcDummy / "xxl";
std::filesystem::create_directories(pathSrcDummyXXL);
fnCreateDummyFile(pathSrcDummyXXL / "fileXXLa.bin", 20*1024*1024);
fnCreateDummyFile(pathSrcDummyXXL / "fileXXLb.bin", 2*10124*1024);
// Files for file attribute and change time test
std::filesystem::path pathSrcDummyTimeAttr = pathSrcDummy / "time_attr";
std::filesystem::create_directories(pathSrcDummyTimeAttr);
// Create time (for Windows only)
#ifdef _WIN32
// Use the first presentation of the Comodore 64 (7th of January 1982)
std::tm sCreateTimeInfo{};
sCreateTimeInfo.tm_year = 82;
sCreateTimeInfo.tm_mon = 0;
sCreateTimeInfo.tm_mday = 7;
sCreateTimeInfo.tm_hour = 11;
sCreateTimeInfo.tm_min = 0;
sCreateTimeInfo.tm_sec = 0;
std::time_t tmCreateTime = _mkgmtime(&sCreateTimeInfo);
fnCreateDummyFile(pathSrcDummyTimeAttr / "create_time.bin");
SetCreateTime(pathSrcDummyTimeAttr / "create_time.bin", static_cast<uint64_t>(tmCreateTime) * 1000000ull);
#endif
// Change time
// Use the first presentation of the personal computer (12th of August 1981)
std::tm sChangeTimeInfo{};
sChangeTimeInfo.tm_year = 81;
sChangeTimeInfo.tm_mon = 7;
sChangeTimeInfo.tm_mday = 12;
sChangeTimeInfo.tm_hour = 9;
sChangeTimeInfo.tm_min = 0;
sChangeTimeInfo.tm_sec = 0;
#ifdef _WIN32
std::time_t tmChangeTime = _mkgmtime(&sChangeTimeInfo);
#else
std::time_t tmChangeTime = timegm(&sChangeTimeInfo);
#endif
fnCreateDummyFile(pathSrcDummyTimeAttr / "change_time.bin");
SetChangeTime(pathSrcDummyTimeAttr / "change_time.bin", static_cast<uint64_t>(tmChangeTime) * 1000000ull);
// Read only (for Windows and Linux only)
#if defined _WIN32 || (defined __linux__ && COMPOSER_SUPPORT_READONLY_LINUX)
fnCreateDummyFile(pathSrcDummyTimeAttr / "readonly.bin");
SetReadOnly(pathSrcDummyTimeAttr / "readonly.bin");
#endif
#ifdef __unix__
fnCreateDummyFile(pathSrcDummyTimeAttr / "executable.bin");
SetExecutable(pathSrcDummyTimeAttr / "executable.bin");
#endif
// Change times and attributes
fnCreateDummyFile(pathSrcDummyTimeAttr / "all.bin");
#ifdef _WIN32
SetCreateTime(pathSrcDummyTimeAttr / "all.bin", static_cast<uint64_t>(tmCreateTime) * 1000000ull);
#endif
SetChangeTime(pathSrcDummyTimeAttr / "all.bin", static_cast<uint64_t>(tmChangeTime) * 1000000ull);
#ifdef __unix__
SetExecutable(pathSrcDummyTimeAttr / "all.bin");
#endif
#if defined _WIN32 || (defined __linux__ && COMPOSER_SUPPORT_READONLY_LINUX)
SetReadOnly(pathSrcDummyTimeAttr / "all.bin");
#endif
}
catch (const std::filesystem::filesystem_error& rexception)
{
// Exception occurred.
std::cerr << "Filesystem exception occurred (create test data): " << rexception.what() << std::endl;
ASSERT_TRUE(false);
}
}
void CInstallPackageComposerTest::TearDownTestSuite()
{
DeleteTestDirs();
}
void CInstallPackageComposerTest::DeleteTargetTestDir()
{
try
{
std::filesystem::path pathTgtDir = GetExecDirectory() / "install_package_composer_targets";
if (!std::filesystem::exists(pathTgtDir)) return;
#if defined _WIN32 || (defined __linux__ && COMPOSER_SUPPORT_READONLY_LINUX)
auto vecFiles = CollectWildcardPath(pathTgtDir, "**/readonly.bin");
for (const auto& rpathFile : vecFiles)
ResetReadOnly(pathTgtDir / rpathFile);
vecFiles = CollectWildcardPath(pathTgtDir, "**/all.bin");
for (const auto& rpathFile : vecFiles)
ResetReadOnly(pathTgtDir / rpathFile);
#endif
std::filesystem::remove_all(pathTgtDir);
}
catch (const std::filesystem::filesystem_error& rexception)
{
// Exception occurred.
std::cerr << "Filesystem exception occurred (delete target test directories): " << rexception.what() << std::endl;
ASSERT_TRUE(false);
}
}
void CInstallPackageComposerTest::DeleteTestDirs()
{
try
{
std::filesystem::path pathSrcDir = GetExecDirectory() / "install_package_composer_sources/dummy_package";
if (!std::filesystem::exists(pathSrcDir)) return;
#if defined _WIN32 || (defined __linux__ && COMPOSER_SUPPORT_READONLY_LINUX)
if (std::filesystem::exists(pathSrcDir / "time_attr/readonly.bin"))
ResetReadOnly(pathSrcDir / "time_attr/readonly.bin");
if (std::filesystem::exists(pathSrcDir / "time_attr/all.bin"))
ResetReadOnly(pathSrcDir / "time_attr/all.bin");
#endif
std::filesystem::remove_all(pathSrcDir);
}
catch (const std::filesystem::filesystem_error& rexception)
{
// Exception occurred.
std::cerr << "Filesystem exception occurred (delete test directories): " << rexception.what() << std::endl;
ASSERT_TRUE(false);
}
DeleteTargetTestDir();
}
bool CInstallPackageComposerTest::AreFilesEqual(const std::filesystem::path& rpath1, const std::filesystem::path& rpath2,
bool bCheckTimes /*= false*/, bool bCheckAttributes /*= false*/)
{
std::ifstream stream1(rpath1.native().c_str());
if (!stream1.is_open()) return false;
std::ifstream stream2(rpath2.native().c_str());
if (!stream2.is_open()) return false;
sdv::pointer<uint8_t> ptrFile1 = sdv::from_stream(stream1.rdbuf());
sdv::pointer<uint8_t> ptrFile2 = sdv::from_stream(stream2.rdbuf());
if ((!ptrFile1 && ptrFile2) || (ptrFile1 && !ptrFile2)) return false;
if (ptrFile1.size() != ptrFile2.size()) return false;
if (!std::equal(ptrFile1.get(), ptrFile1.get() + ptrFile1.size(), ptrFile2.get())) return false;
// Check attributes
if (bCheckAttributes)
{
#if defined _WIN32 || (defined __linux__ && COMPOSER_SUPPORT_READONLY_LINUX)
if (IsReadOnly(rpath1) != IsReadOnly(rpath2)) return false;
#endif
#ifdef __unix__
if (IsExecutable(rpath1) != IsExecutable(rpath2)) return false;
#endif
}
// File time and attributes
if (bCheckTimes)
{
#ifdef _WIN32
if (GetCreateTime(rpath1) != GetCreateTime(rpath2)) return false;
#endif
if (GetChangeTime(rpath1) != GetChangeTime(rpath2)) return false;
}
// Content, times and attributes are equal.
return true;
}
#ifdef _WIN32
void CInstallPackageComposerTest::SetReadOnly(const std::filesystem::path& rpathFile)
{
// Get the file attributes
WIN32_FILE_ATTRIBUTE_DATA sFileAttr{};
if (!GetFileAttributesExW(rpathFile.native().c_str(), GetFileExInfoStandard, &sFileAttr))
return;
// Set the file attributes
SetFileAttributesW(rpathFile.native().c_str(), sFileAttr.dwFileAttributes | FILE_ATTRIBUTE_READONLY);
}
void CInstallPackageComposerTest::ResetReadOnly(const std::filesystem::path& rpathFile)
{
// Get the file attributes
WIN32_FILE_ATTRIBUTE_DATA sFileAttr{};
if (!GetFileAttributesExW(rpathFile.native().c_str(), GetFileExInfoStandard, &sFileAttr))
return;
// Set the file attributes
SetFileAttributesW(rpathFile.native().c_str(), sFileAttr.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
}
bool CInstallPackageComposerTest::IsReadOnly(const std::filesystem::path& rpathFile)
{
WIN32_FILE_ATTRIBUTE_DATA sFileAttr{};
if (!GetFileAttributesExW(rpathFile.native().c_str(), GetFileExInfoStandard, &sFileAttr))
return false;
return sFileAttr.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? true : false;
}
#elif defined __linux__ && COMPOSER_SUPPORT_READONLY_LINUX
void CInstallPackageComposerTest::SetReadOnly(const std::filesystem::path& rpathFile)
{
// Enable the immutable capability
cap_t caps = cap_get_proc();
{
perror("cap_get_proc error");
sdv::XCannotChangeFileTimesOrAttributes exception;
exception.ssPath = pathFile.generic_u8string();
throw exception;
}
cap_value_t capList[1] = {CAP_LINUX_IMMUTABLE};
if (cap_set_flag(caps, CAP_EFFECTIVE, 1, capList, CAP_SET) == -1)
{
perror("cap_set_flag error");
cap_free(caps);
sdv::XCannotChangeFileTimesOrAttributes exception;
exception.ssPath = pathFile.generic_u8string();
throw exception;
}
// Set the immutable flag
int fd = open(pathFile.native().c_str(), O_RDONLY);
if (fd == -1)
{
cap_free(caps);
sdv::XCannotChangeFileTimesOrAttributes exception;
exception.ssPath = pathFile.generic_u8string();
throw exception;
}
int flags = 0;
if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1)
{
perror("ioctl error");
cap_free(caps);
close(fd);
sdv::XCannotChangeFileTimesOrAttributes exception;
exception.ssPath = pathFile.generic_u8string();
throw exception;
}
flags |= FS_IMMUTABLE_FL;
if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1)
{
cap_free(caps);
close(fd);
sdv::XCannotChangeFileTimesOrAttributes exception;
exception.ssPath = pathFile.generic_u8string();
throw exception;
}
close(fd);
cap_free(caps);
}
void CInstallPackageComposerTest::ResetReadOnly(const std::filesystem::path& rpathFile)
{
// Enable the immutable capability
cap_t caps = cap_get_proc();
{
perror("cap_get_proc error");
sdv::XCannotChangeFileTimesOrAttributes exception;
exception.ssPath = pathFile.generic_u8string();
throw exception;
}
cap_value_t capList[1] = {CAP_LINUX_IMMUTABLE};
if (cap_set_flag(caps, CAP_EFFECTIVE, 1, capList, CAP_SET) == -1)
{
perror("cap_set_flag error");
cap_free(caps);
sdv::XCannotChangeFileTimesOrAttributes exception;
exception.ssPath = pathFile.generic_u8string();
throw exception;
}
// Set the immutable flag
int fd = open(pathFile.native().c_str(), O_RDONLY);
if (fd == -1)
{
cap_free(caps);
sdv::XCannotChangeFileTimesOrAttributes exception;
exception.ssPath = pathFile.generic_u8string();
throw exception;
}
int flags = 0;
if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1)
{
perror("ioctl error");
cap_free(caps);
close(fd);
sdv::XCannotChangeFileTimesOrAttributes exception;
exception.ssPath = pathFile.generic_u8string();
throw exception;
}
flags &= ~FS_IMMUTABLE_FL;
if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1)
{
cap_free(caps);
close(fd);
sdv::XCannotChangeFileTimesOrAttributes exception;
exception.ssPath = pathFile.generic_u8string();
throw exception;
}
close(fd);
cap_free(caps);
}
bool CInstallPackageComposerTest::IsReadOnly(const std::filesystem::path& rpathFile)
{
struct statx sStat{};
sStat.stx_mask = STATX_BTIME | STATX_MTIME;
if (statx(0, rsModuleEntry.pathSrcModule.native().c_str(), 0, STATX_BTIME | STATX_MTIME, &sStat) ||
sStat.stx_btime.tv_sec > 2524608000 || sStat.stx_btime.tv_nsec > 999999999 || sStat.stx_mtime.tv_sec > 2524608000 ||
sStat.stx_mtime.tv_nsec > 999999999)
return false;
return sStat.stx_attributes_mask & STATX_ATTR_IMMUTABLE && sStat.stx_attributes & STATX_ATTR_IMMUTABLE;
}
#endif
#ifdef __unix__
void CInstallPackageComposerTest::SetExecutable(const std::filesystem::path& rpathFile)
{
chmod(rpathFile.native().c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
}
bool CInstallPackageComposerTest::IsExecutable(const std::filesystem::path& rpathFile)
{
struct stat sStat{};
if (stat(rpathFile.native().c_str(), &sStat) == 0 && S_ISREG(sStat.st_mode))
{
// Check if any execute bit is set
return (sStat.st_mode & S_IXUSR) || (sStat.st_mode & S_IXGRP) || (sStat.st_mode & S_IXOTH);
}
return false;
}
#endif
#ifdef _WIN32
void CInstallPackageComposerTest::SetCreateTime(const std::filesystem::path& rpathFile, uint64_t uiTimeMicrosec)
{
// Get the file times
WIN32_FILE_ATTRIBUTE_DATA sFileAttr{};
if (!GetFileAttributesExW(rpathFile.native().c_str(), GetFileExInfoStandard, &sFileAttr))
return;
// Set file times
HANDLE hFile = CreateFile(rpathFile.native().c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return;
FILETIME sCreationTime = PosixTimeToWindowsTime(uiTimeMicrosec);
FILETIME sAccessTime = sFileAttr.ftLastAccessTime;
FILETIME sWriteTime = sFileAttr.ftLastWriteTime;
SetFileTime(hFile, &sCreationTime, &sAccessTime, &sWriteTime);
CloseHandle(hFile);
}
uint64_t CInstallPackageComposerTest::GetCreateTime(const std::filesystem::path& rpathFile)
{
WIN32_FILE_ATTRIBUTE_DATA sFileAttr{};
if (!GetFileAttributesExW(rpathFile.native().c_str(), GetFileExInfoStandard, &sFileAttr))
return 0ull;
return WindowsTimeToPosixTime(sFileAttr.ftCreationTime);
}
void CInstallPackageComposerTest::SetChangeTime(const std::filesystem::path& rpathFile, uint64_t uiTimeMicrosec)
{
// Get the file times
WIN32_FILE_ATTRIBUTE_DATA sFileAttr{};
if (!GetFileAttributesExW(rpathFile.native().c_str(), GetFileExInfoStandard, &sFileAttr))
return;
// Set file times
HANDLE hFile = CreateFile(rpathFile.native().c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return;
FILETIME sCreationTime = sFileAttr.ftCreationTime;
FILETIME sAccessTime = sFileAttr.ftLastAccessTime;
FILETIME sWriteTime = PosixTimeToWindowsTime(uiTimeMicrosec);
SetFileTime(hFile, &sCreationTime, &sAccessTime, &sWriteTime);
CloseHandle(hFile);
}
uint64_t CInstallPackageComposerTest::GetChangeTime(const std::filesystem::path& rpathFile)
{
WIN32_FILE_ATTRIBUTE_DATA sFileAttr{};
if (!GetFileAttributesExW(rpathFile.native().c_str(), GetFileExInfoStandard, &sFileAttr))
return 0ull;
return WindowsTimeToPosixTime(sFileAttr.ftLastWriteTime);
}
#else
void CInstallPackageComposerTest::SetChangeTime(const std::filesystem::path& rpathFile, uint64_t uiTimeMicrosec)
{
struct timespec sTimes[2] = {};
sTimes[0].tv_sec = static_cast<time_t>(uiTimeMicrosec / 1000000ull); // Set access to change time
sTimes[0].tv_nsec = uiTimeMicrosec % 1000000ull * 1000ull;
sTimes[1].tv_sec = static_cast<time_t>(uiTimeMicrosec / 1000000ull); // Set modification time to change time
sTimes[1].tv_nsec = uiTimeMicrosec % 1000000ull * 1000ull;
// Test for illegal time values... tv_sec should be below 1st of January 2050; tv_nsec should be below or equal to 999999999.
if (sTimes[0].tv_sec >= 2524608000 || sTimes[0].tv_nsec > 999999999)
return;
// Set the times
utimensat(AT_FDCWD, rpathFile.native().c_str(), sTimes, 0);
}
uint64_t CInstallPackageComposerTest::GetChangeTime(const std::filesystem::path& rpathFile)
{
// Test for illegal time values... tv_sec should be below 1st of January 2050; tv_nsec should be below or equal to 999999999.
struct stat sStat{};
if (stat(rpathFile.u8string().c_str(), &sStat) || sStat.st_mtim.tv_sec >= 2524608000 ||
sStat.st_mtim.tv_nsec > 999999999)
return 0;
return static_cast<uint64_t>(sStat.st_mtim.tv_sec) * 1000000ull + static_cast<uint64_t>(sStat.st_mtim.tv_nsec) / 1000ull;
}
#endif