CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
323 lines
9.3 KiB
C++
323 lines
9.3 KiB
C++
// Copyright (c) 2025 Advanced Micro Devices, Inc.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <filesystem>
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "rocm_kpack/kpack.h"
|
|
|
|
// Cross-platform packed struct support
|
|
#ifdef _MSC_VER
|
|
#define PACKED_STRUCT_BEGIN __pragma(pack(push, 1))
|
|
#define PACKED_STRUCT_END __pragma(pack(pop))
|
|
#define PACKED_ATTR
|
|
#else
|
|
#define PACKED_STRUCT_BEGIN
|
|
#define PACKED_STRUCT_END
|
|
#define PACKED_ATTR __attribute__((packed))
|
|
#endif
|
|
|
|
// Helper to create a temp file with specific content
|
|
// Uses mkstemp() on POSIX for race-free temp file creation
|
|
class TempFile {
|
|
public:
|
|
TempFile(const void* data, size_t size) {
|
|
#ifdef _WIN32
|
|
// Use GetTempPath2 (Windows 10 20H2+) for better security, fall back to
|
|
// GetTempPath
|
|
wchar_t temp_path[32768]; // No MAX_PATH limitation
|
|
DWORD len = GetTempPath2W(32768, temp_path);
|
|
if (len == 0) {
|
|
len = GetTempPathW(32768, temp_path);
|
|
}
|
|
wchar_t temp_file[32768];
|
|
if (len > 0 && GetTempFileNameW(temp_path, L"kpk", 0, temp_file)) {
|
|
path_ = std::filesystem::path(temp_file);
|
|
FILE* f = _wfopen(temp_file, L"wb");
|
|
if (f) {
|
|
fwrite(data, 1, size, f);
|
|
fclose(f);
|
|
}
|
|
}
|
|
#else
|
|
std::string tmpl =
|
|
(std::filesystem::temp_directory_path() / "kpack_test_XXXXXX").string();
|
|
int fd = mkstemp(tmpl.data());
|
|
if (fd >= 0) {
|
|
ssize_t written = write(fd, data, size);
|
|
(void)written; // Ignore return in tests
|
|
close(fd);
|
|
path_ = tmpl;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
TempFile(const std::string& content)
|
|
: TempFile(content.data(), content.size()) {}
|
|
|
|
~TempFile() { std::filesystem::remove(path_); }
|
|
|
|
const std::filesystem::path& path() const { return path_; }
|
|
std::string str() const { return path_.string(); }
|
|
|
|
private:
|
|
std::filesystem::path path_;
|
|
};
|
|
|
|
// Test that library links and basic error handling works
|
|
TEST(KpackAPITest, NullArguments) {
|
|
kpack_error_t err;
|
|
|
|
// kpack_open with NULL path
|
|
kpack_archive_t archive;
|
|
err = kpack_open(nullptr, &archive);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_ARGUMENT);
|
|
|
|
// kpack_open with NULL archive pointer (path can be any valid-looking path)
|
|
std::string temp_path =
|
|
(std::filesystem::temp_directory_path() / "test.kpack").string();
|
|
err = kpack_open(temp_path.c_str(), nullptr);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_ARGUMENT);
|
|
|
|
// kpack_get_architecture_count with NULL archive
|
|
size_t count;
|
|
err = kpack_get_architecture_count(nullptr, &count);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_ARGUMENT);
|
|
|
|
// kpack_get_kernel with NULL arguments
|
|
void* data;
|
|
size_t size;
|
|
err = kpack_get_kernel(nullptr, "test", "gfx1100", &data, &size);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_ARGUMENT);
|
|
}
|
|
|
|
// Test file not found
|
|
TEST(KpackAPITest, FileNotFound) {
|
|
kpack_archive_t archive;
|
|
kpack_error_t err = kpack_open("/nonexistent/test.kpack", &archive);
|
|
EXPECT_EQ(err, KPACK_ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
// Test kpack_close with NULL (should not crash)
|
|
TEST(KpackAPITest, CloseNull) {
|
|
kpack_close(nullptr);
|
|
// Should not crash
|
|
}
|
|
|
|
// Test kpack_get_binary with NULL arguments
|
|
TEST(KpackAPITest, GetBinary_NullArchive) {
|
|
const char* binary;
|
|
kpack_error_t err = kpack_get_binary(nullptr, 0, &binary);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_ARGUMENT);
|
|
}
|
|
|
|
TEST(KpackAPITest, GetBinary_NullBinaryOut) {
|
|
// Need a valid archive to test this - skip if no test assets
|
|
const char* dir = std::getenv("ROCM_KPACK_TEST_ASSETS_DIR");
|
|
if (!dir) {
|
|
GTEST_SKIP() << "ROCM_KPACK_TEST_ASSETS_DIR not set";
|
|
}
|
|
|
|
std::string test_kpack = std::string(dir) + "/test_noop.kpack";
|
|
kpack_archive_t archive;
|
|
kpack_error_t err = kpack_open(test_kpack.c_str(), &archive);
|
|
ASSERT_EQ(err, KPACK_SUCCESS);
|
|
|
|
err = kpack_get_binary(archive, 0, nullptr);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_ARGUMENT);
|
|
|
|
kpack_close(archive);
|
|
}
|
|
|
|
TEST(KpackAPITest, GetBinary_IndexOutOfRange) {
|
|
const char* dir = std::getenv("ROCM_KPACK_TEST_ASSETS_DIR");
|
|
if (!dir) {
|
|
GTEST_SKIP() << "ROCM_KPACK_TEST_ASSETS_DIR not set";
|
|
}
|
|
|
|
std::string test_kpack = std::string(dir) + "/test_noop.kpack";
|
|
kpack_archive_t archive;
|
|
kpack_error_t err = kpack_open(test_kpack.c_str(), &archive);
|
|
ASSERT_EQ(err, KPACK_SUCCESS);
|
|
|
|
size_t binary_count;
|
|
err = kpack_get_binary_count(archive, &binary_count);
|
|
ASSERT_EQ(err, KPACK_SUCCESS);
|
|
ASSERT_GT(binary_count, 0u);
|
|
|
|
// Test index exactly at boundary (should fail)
|
|
const char* binary;
|
|
err = kpack_get_binary(archive, binary_count, &binary);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_ARGUMENT);
|
|
|
|
// Test way out of range
|
|
err = kpack_get_binary(archive, 99999, &binary);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_ARGUMENT);
|
|
|
|
kpack_close(archive);
|
|
}
|
|
|
|
// Test kpack_get_architecture with invalid index
|
|
TEST(KpackAPITest, GetArchitecture_IndexOutOfRange) {
|
|
const char* dir = std::getenv("ROCM_KPACK_TEST_ASSETS_DIR");
|
|
if (!dir) {
|
|
GTEST_SKIP() << "ROCM_KPACK_TEST_ASSETS_DIR not set";
|
|
}
|
|
|
|
std::string test_kpack = std::string(dir) + "/test_noop.kpack";
|
|
kpack_archive_t archive;
|
|
kpack_error_t err = kpack_open(test_kpack.c_str(), &archive);
|
|
ASSERT_EQ(err, KPACK_SUCCESS);
|
|
|
|
size_t arch_count;
|
|
err = kpack_get_architecture_count(archive, &arch_count);
|
|
ASSERT_EQ(err, KPACK_SUCCESS);
|
|
ASSERT_GT(arch_count, 0u);
|
|
|
|
// Test index exactly at boundary (should fail)
|
|
const char* arch;
|
|
err = kpack_get_architecture(archive, arch_count, &arch);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_ARGUMENT);
|
|
|
|
// Test way out of range
|
|
err = kpack_get_architecture(archive, 99999, &arch);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_ARGUMENT);
|
|
|
|
kpack_close(archive);
|
|
}
|
|
|
|
// Test kpack_get_binary_count with NULL count pointer
|
|
TEST(KpackAPITest, GetBinaryCount_NullCount) {
|
|
const char* dir = std::getenv("ROCM_KPACK_TEST_ASSETS_DIR");
|
|
if (!dir) {
|
|
GTEST_SKIP() << "ROCM_KPACK_TEST_ASSETS_DIR not set";
|
|
}
|
|
|
|
std::string test_kpack = std::string(dir) + "/test_noop.kpack";
|
|
kpack_archive_t archive;
|
|
kpack_error_t err = kpack_open(test_kpack.c_str(), &archive);
|
|
ASSERT_EQ(err, KPACK_SUCCESS);
|
|
|
|
err = kpack_get_binary_count(archive, nullptr);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_ARGUMENT);
|
|
|
|
kpack_close(archive);
|
|
}
|
|
|
|
// Test kpack_get_architecture_count with NULL count pointer
|
|
TEST(KpackAPITest, GetArchitectureCount_NullCount) {
|
|
const char* dir = std::getenv("ROCM_KPACK_TEST_ASSETS_DIR");
|
|
if (!dir) {
|
|
GTEST_SKIP() << "ROCM_KPACK_TEST_ASSETS_DIR not set";
|
|
}
|
|
|
|
std::string test_kpack = std::string(dir) + "/test_noop.kpack";
|
|
kpack_archive_t archive;
|
|
kpack_error_t err = kpack_open(test_kpack.c_str(), &archive);
|
|
ASSERT_EQ(err, KPACK_SUCCESS);
|
|
|
|
err = kpack_get_architecture_count(archive, nullptr);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_ARGUMENT);
|
|
|
|
kpack_close(archive);
|
|
}
|
|
|
|
// Test kpack_free_kernel with NULL (should not crash)
|
|
TEST(KpackAPITest, FreeKernel_Null) {
|
|
kpack_free_kernel(nullptr);
|
|
// Should not crash
|
|
}
|
|
|
|
//
|
|
// Invalid archive format tests
|
|
//
|
|
|
|
TEST(KpackAPITest, InvalidArchive_EmptyFile) {
|
|
TempFile empty_file("", 0);
|
|
|
|
kpack_archive_t archive;
|
|
kpack_error_t err = kpack_open(empty_file.str().c_str(), &archive);
|
|
// Empty file should fail - can't read header
|
|
EXPECT_NE(err, KPACK_SUCCESS);
|
|
}
|
|
|
|
TEST(KpackAPITest, InvalidArchive_WrongMagic) {
|
|
// Create a file with wrong magic bytes
|
|
// Header format: magic (4), version (4), compression (4), toc_offset (8)
|
|
PACKED_STRUCT_BEGIN
|
|
struct FakeHeader {
|
|
char magic[4] = {'X', 'X', 'X', 'X'}; // Wrong magic
|
|
uint32_t version = 1;
|
|
uint32_t compression = 0;
|
|
uint64_t toc_offset = 20;
|
|
} PACKED_ATTR;
|
|
PACKED_STRUCT_END
|
|
FakeHeader header;
|
|
|
|
TempFile bad_magic(&header, sizeof(header));
|
|
|
|
kpack_archive_t archive;
|
|
kpack_error_t err = kpack_open(bad_magic.str().c_str(), &archive);
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_FORMAT);
|
|
}
|
|
|
|
TEST(KpackAPITest, InvalidArchive_UnsupportedVersion) {
|
|
// Create a file with correct magic but wrong version
|
|
PACKED_STRUCT_BEGIN
|
|
struct FakeHeader {
|
|
char magic[4] = {'K', 'P', 'A', 'K'}; // Correct magic
|
|
uint32_t version = 999; // Unsupported version
|
|
uint32_t compression = 0;
|
|
uint64_t toc_offset = 20;
|
|
} PACKED_ATTR;
|
|
PACKED_STRUCT_END
|
|
FakeHeader header;
|
|
|
|
TempFile bad_version(&header, sizeof(header));
|
|
|
|
kpack_archive_t archive;
|
|
kpack_error_t err = kpack_open(bad_version.str().c_str(), &archive);
|
|
EXPECT_EQ(err, KPACK_ERROR_UNSUPPORTED_VERSION);
|
|
}
|
|
|
|
TEST(KpackAPITest, InvalidArchive_TruncatedHeader) {
|
|
// Create a file with only partial header (8 bytes instead of 20)
|
|
const char partial_header[] = "KPAK\x01\x00\x00\x00"; // magic + version only
|
|
|
|
TempFile truncated(partial_header, 8);
|
|
|
|
kpack_archive_t archive;
|
|
kpack_error_t err = kpack_open(truncated.str().c_str(), &archive);
|
|
// Should fail - can't read full header
|
|
EXPECT_NE(err, KPACK_SUCCESS);
|
|
}
|
|
|
|
TEST(KpackAPITest, InvalidArchive_TOCOffsetBeyondFile) {
|
|
// Create a file with valid header but TOC offset beyond file size
|
|
PACKED_STRUCT_BEGIN
|
|
struct FakeHeader {
|
|
char magic[4] = {'K', 'P', 'A', 'K'};
|
|
uint32_t version = 1;
|
|
uint32_t compression = 0;
|
|
uint64_t toc_offset = 999999; // Way beyond file size
|
|
} PACKED_ATTR;
|
|
PACKED_STRUCT_END
|
|
FakeHeader header;
|
|
|
|
TempFile bad_offset(&header, sizeof(header));
|
|
|
|
kpack_archive_t archive;
|
|
kpack_error_t err = kpack_open(bad_offset.str().c_str(), &archive);
|
|
// Should fail with INVALID_FORMAT - TOC offset is beyond file size
|
|
EXPECT_EQ(err, KPACK_ERROR_INVALID_FORMAT);
|
|
}
|