* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* 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 FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
#include "specialization/kernel_specialization.h"
#include "specialization/kernel_compilation_workspace.h"
#include "specialization/resource_registry.h"
#include <gtest/gtest.h>
#include <nlohmann/json.hpp>
#include <boost/filesystem.hpp>
#include <boost/system/error_code.hpp>
#include <csignal>
#include <cstdlib>
#include <fstream>
#include <memory>
#include <string>
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>
#include <utility>
#include <vector>
namespace {
namespace fs = boost::filesystem;
using Json = nlohmann::json;
using ascendc::DirectoryCleanupGuard;
using ascendc::aclrtc::KernelCompilationWorkspace;
using ascendc::aclrtc::KernelSpecializationSession;
using ascendc::aclrtc::MaterializedKernelCompilationResource;
using ascendc::aclrtc::NormalizedKernelSpecializationRequest;
using ascendc::aclrtc::WorktreeRetentionPolicy;
using ascendc::specialization_compile::ResourceEntry;
using ascendc::specialization_compile::ResourceFileData;
using ascendc::specialization_compile::ResourceRegistry;
using ascendc::specialization_compile::ResourceSourceType;
using ascendc::specialization_compile::ResourceStatus;
enum class FakeCompilerOutcome : uint32_t {
Success,
Failure,
};
constexpr char RESOURCE_ID[] = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
class ScopedEnvironmentVariable final {
public:
ScopedEnvironmentVariable(const char* name, const char* value) : name_(name)
{
const char* previousValue = std::getenv(name);
if (previousValue != nullptr) {
previousValue_ = previousValue;
wasSet_ = true;
}
if (value == nullptr) {
unsetenv(name);
} else {
setenv(name, value, 1);
}
}
~ScopedEnvironmentVariable()
{
if (wasSet_) {
setenv(name_.c_str(), previousValue_.c_str(), 1);
} else {
unsetenv(name_.c_str());
}
}
private:
std::string name_;
std::string previousValue_;
bool wasSet_{false};
};
class EndToEndTestResource final {
public:
explicit EndToEndTestResource(FakeCompilerOutcome compilerOutcome)
{
static uint64_t sequence = 0;
rootPath_ = fs::temp_directory_path() /
("aclrtc_session_test_" + std::to_string(getpid()) + "_" + std::to_string(sequence++));
worktreePath_ = rootPath_ / "worktree";
sourceDirectoryPath_ = rootPath_ / "user";
toolchainPath_ = rootPath_ / "toolchain";
outputElfPath_ = rootPath_ / "output/kernel.elf";
fs::create_directories(worktreePath_ / "resources/include");
fs::create_directories(worktreePath_ / "resources/src");
fs::create_directories(sourceDirectoryPath_);
fs::create_directories(toolchainPath_ / "bin");
fs::create_directories(outputElfPath_.parent_path());
WriteFile(sourceDirectoryPath_ / "add_custom.cpp", "// user source\n");
WriteFile(worktreePath_ / "resources/include/constants.h", "TILING=@@STATIC_TILING@@;\n");
WriteFile(worktreePath_ / "resources/src/basic.cpp", "// basic source\n");
WriteExecutable(
toolchainPath_ / "bin/bisheng",
compilerOutcome == FakeCompilerOutcome::Success ? CreateOutputScript("object") : "#!/bin/sh\nexit 2\n");
WriteExecutable(toolchainPath_ / "bin/ld.lld", CreateOutputScript("linked-elf"));
manifest_ = CreateManifest();
}
~EndToEndTestResource()
{
boost::system::error_code ignoredError;
fs::remove_all(rootPath_, ignoredError);
}
MaterializedKernelCompilationResource CreateMaterializedCompilationResource() const
{
return MaterializedKernelCompilationResource{manifest_, worktreePath_, sourceDirectoryPath_};
}
const fs::path& ToolchainPath() const { return toolchainPath_; }
const fs::path& WorktreePath() const { return worktreePath_; }
const fs::path& SourceDirectoryPath() const { return sourceDirectoryPath_; }
const fs::path& OutputElfPath() const { return outputElfPath_; }
const Json& Manifest() const { return manifest_; }
private:
static void WriteFile(const fs::path& path, const std::string& contents)
{
std::ofstream stream(path.string());
stream << contents;
}
static void WriteExecutable(const fs::path& path, const std::string& contents)
{
WriteFile(path, contents);
chmod(path.c_str(), 0755);
}
static std::string CreateOutputScript(const char* contents)
{
return std::string("#!/bin/sh\n") +
"while [ \"$#\" -gt 0 ]; do\n"
" if [ \"$1\" = \"-o\" ]; then\n"
" shift\n"
" printf '" +
contents +
"' > \"$1\"\n"
" exit 0\n"
" fi\n"
" shift\n"
"done\n"
"exit 3\n";
}
static Json CreateManifest()
{
return Json{
{"schema_version", "1.0"},
{"source_file", "add_custom.cpp"},
{"kernels", Json::array(
{{{"kernel_name", "add_custom_100000"},
{"constant_infos", Json::array(
{{{"parameter_index", 0},
{"byte_size", 2},
{"file", "${resource}/resources/include/constants.h"},
{"template", "@@STATIC_TILING@@"}}})},
{"link_options", Json::array({"-m", "aicorelinux", "-Ttext=0"})},
{"objects", Json::array(
{{{"object_name", "basic"},
{"object_type", "basic"},
{"commands", Json::array(
{{{"type", "compile"},
{"stage", 0},
{"cmd", Json::array(
{"${env:ASCEND_HOME_PATH}/bin/bisheng",
"${resource}/resources/src/basic.cpp",
"-o", "${output}/basic.o"})}}})},
{"outputs", Json::array({"${output}/basic.o"})}}})}}})}};
}
fs::path rootPath_;
fs::path worktreePath_;
fs::path sourceDirectoryPath_;
fs::path toolchainPath_;
fs::path outputElfPath_;
Json manifest_;
};
class ScopedRegisteredCompilationResource final {
public:
ScopedRegisteredCompilationResource(
std::string resourceId, const Json& manifest, const fs::path& sourceDirectoryPath,
std::vector<ResourceFileData> resourceFiles = {{"payload.bin", "payload.bin", {0x01U, 0x02U}}})
: registry_(ResourceRegistry::Instance()),
resourceId_(std::move(resourceId)),
previousOwnedMaterializationRoot_(std::move(registry_.ownedMaterializationRoot_)),
previousAutomaticLoadAttempted_(registry_.automaticLoadAttempted_),
previousRetainMaterializedDirectories_(registry_.retainMaterializedDirectories_),
previousAutomaticLoadStatus_(registry_.automaticLoadStatus_)
{
registry_.automaticLoadAttempted_ = true;
registry_.retainMaterializedDirectories_ = false;
registry_.automaticLoadStatus_ = ResourceStatus::Success;
std::unique_ptr<ResourceEntry> resourceEntry(new ResourceEntry());
resourceEntry->data.json = manifest;
resourceEntry->data.sourceFilePath = sourceDirectoryPath.string();
resourceEntry->files = std::move(resourceFiles);
resourceEntry->sourceType = ResourceSourceType::External;
inserted_ = registry_.externalResources_.emplace(resourceId_, std::move(resourceEntry)).second;
}
~ScopedRegisteredCompilationResource()
{
if (inserted_) {
registry_.externalResources_.erase(resourceId_);
}
registry_.ownedMaterializationRoot_ = std::move(previousOwnedMaterializationRoot_);
registry_.automaticLoadAttempted_ = previousAutomaticLoadAttempted_;
registry_.retainMaterializedDirectories_ = previousRetainMaterializedDirectories_;
registry_.automaticLoadStatus_ = previousAutomaticLoadStatus_;
}
bool WasInserted() const noexcept { return inserted_; }
void SetAutomaticLoadStatus(ResourceStatus loadStatus) noexcept { registry_.automaticLoadStatus_ = loadStatus; }
private:
ResourceRegistry& registry_;
std::string resourceId_;
DirectoryCleanupGuard previousOwnedMaterializationRoot_;
bool previousAutomaticLoadAttempted_{false};
bool previousRetainMaterializedDirectories_{false};
ResourceStatus previousAutomaticLoadStatus_{ResourceStatus::Success};
bool inserted_{false};
};
aclrtcKernelSpecRequest CreateRequest(const uint64_t* constantByteCounts, const void* const* constantAddresses)
{
aclrtcKernelSpecRequest request{};
request.resourceId = RESOURCE_ID;
request.kernelEntry = "add_custom_100000";
request.argsCount = 1;
request.argsAddr = constantAddresses;
request.argsBytes = constantByteCounts;
return request;
}
NormalizedKernelSpecializationRequest NormalizeRequestForTest(
const aclrtcKernelSpecRequest& request, const fs::path& outputElfPath)
{
NormalizedKernelSpecializationRequest normalizedRequest;
EXPECT_EQ(
ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, outputElfPath.c_str(), normalizedRequest),
ascendc::aclrtc::ACLRTC_SUCCESS);
return normalizedRequest;
}
std::string ReadFile(const fs::path& path)
{
std::ifstream stream(path.string());
return std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
}
TEST(KernelSpecializationSessionTest, RunsPatchCompileLinkPublishAndCleanupWithMaterializedResource)
{
EndToEndTestResource testResource(FakeCompilerOutcome::Success);
ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str());
ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", nullptr);
const uint8_t constantBytes[] = {0x01, 0xfe};
const void* constantAddresses[] = {constantBytes};
const uint64_t constantByteCounts[] = {sizeof(constantBytes)};
aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses);
KernelSpecializationSession session;
EXPECT_EQ(
session.RunSpecializationWithMaterializedResource(
NormalizeRequestForTest(request, testResource.OutputElfPath()),
testResource.CreateMaterializedCompilationResource()),
0);
EXPECT_EQ(ReadFile(testResource.OutputElfPath()), "linked-elf");
EXPECT_FALSE(fs::exists(testResource.WorktreePath()));
}
TEST(KernelSpecializationSessionTest, DoesNotReplaceExistingOutputWhenCompilationFails)
{
EndToEndTestResource testResource(FakeCompilerOutcome::Failure);
ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str());
ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", nullptr);
std::ofstream(testResource.OutputElfPath().string()) << "previous-elf";
const uint8_t constantBytes[] = {0x01, 0xfe};
const void* constantAddresses[] = {constantBytes};
const uint64_t constantByteCounts[] = {sizeof(constantBytes)};
aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses);
KernelSpecializationSession session;
EXPECT_EQ(
session.RunSpecializationWithMaterializedResource(
NormalizeRequestForTest(request, testResource.OutputElfPath()),
testResource.CreateMaterializedCompilationResource()),
ascendc::aclrtc::ACLRTC_ERROR_COMPILATION);
EXPECT_EQ(ReadFile(testResource.OutputElfPath()), "previous-elf");
EXPECT_FALSE(fs::exists(testResource.WorktreePath()));
}
TEST(KernelSpecializationSessionTest, FailsWhenOutputDirectoryIsMissing)
{
EndToEndTestResource testResource(FakeCompilerOutcome::Success);
ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str());
ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", nullptr);
boost::system::error_code removeError;
fs::remove_all(testResource.OutputElfPath().parent_path(), removeError);
ASSERT_FALSE(removeError);
const uint8_t constantBytes[] = {0x01, 0xfe};
const void* constantAddresses[] = {constantBytes};
const uint64_t constantByteCounts[] = {sizeof(constantBytes)};
aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses);
KernelSpecializationSession session;
EXPECT_EQ(
session.RunSpecializationWithMaterializedResource(
NormalizeRequestForTest(request, testResource.OutputElfPath()),
testResource.CreateMaterializedCompilationResource()),
ascendc::aclrtc::ACLRTC_ERROR_FAILURE);
}
TEST(KernelSpecializationSessionTest, RetainsWorktreeWhenKernelMetaSavingValueHasWhitespace)
{
EndToEndTestResource testResource(FakeCompilerOutcome::Success);
ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str());
ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", " \t1\r\n");
const uint8_t constantBytes[] = {0x01, 0xfe};
const void* constantAddresses[] = {constantBytes};
const uint64_t constantByteCounts[] = {sizeof(constantBytes)};
aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses);
KernelSpecializationSession session;
EXPECT_EQ(
session.RunSpecializationWithMaterializedResource(
NormalizeRequestForTest(request, testResource.OutputElfPath()),
testResource.CreateMaterializedCompilationResource()),
0);
EXPECT_TRUE(fs::is_directory(testResource.WorktreePath()));
EXPECT_TRUE(fs::is_regular_file(testResource.WorktreePath() / "aclrtc_manifest.json"));
EXPECT_TRUE(fs::is_regular_file(testResource.WorktreePath() / "aclrtc_compile.log"));
EXPECT_TRUE(fs::is_regular_file(testResource.WorktreePath() / "aclrtc_replay.sh"));
EXPECT_TRUE(fs::is_regular_file(testResource.WorktreePath() / "aclrtc_result.json"));
EXPECT_NE(
ReadFile(testResource.WorktreePath() / "aclrtc_replay.sh").find(testResource.WorktreePath().string()),
std::string::npos);
}
TEST(KernelSpecializationSessionTest, LoadsAndCopiesFreshMaterializedPathsFromRegistry)
{
const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_session_registry_%%%%-%%%%-%%%%");
const fs::path sourceDirectoryPath = testRoot / "source";
fs::create_directories(sourceDirectoryPath);
ScopedEnvironmentVariable temporaryDirectoryEnvironment("TMPDIR", testRoot.c_str());
const std::string resourceId = "aclrtc-session-registry-integration";
const Json manifest = {{"resource_id", resourceId}, {"schema_version", "1.0"}};
ScopedRegisteredCompilationResource registeredResource(resourceId, manifest, sourceDirectoryPath);
ASSERT_TRUE(registeredResource.WasInserted());
registeredResource.SetAutomaticLoadStatus(ResourceStatus::NotFound);
MaterializedKernelCompilationResource rejectedResource;
EXPECT_EQ(
KernelSpecializationSession::LoadAndMaterializeCompilationResource(resourceId, rejectedResource),
ascendc::aclrtc::ACLRTC_ERROR_FAILURE);
registeredResource.SetAutomaticLoadStatus(ResourceStatus::Success);
MaterializedKernelCompilationResource firstResource;
ASSERT_EQ(
KernelSpecializationSession::LoadAndMaterializeCompilationResource(resourceId, firstResource),
ascendc::aclrtc::ACLRTC_SUCCESS);
EXPECT_EQ(firstResource.manifest, manifest);
EXPECT_EQ(firstResource.externalSourceDirectoryPath, sourceDirectoryPath);
EXPECT_TRUE(fs::is_regular_file(firstResource.ownedResourceDirectoryPath / "payload.bin"));
MaterializedKernelCompilationResource secondResource;
ASSERT_EQ(
KernelSpecializationSession::LoadAndMaterializeCompilationResource(resourceId, secondResource),
ascendc::aclrtc::ACLRTC_SUCCESS);
EXPECT_NE(firstResource.ownedResourceDirectoryPath, secondResource.ownedResourceDirectoryPath);
EXPECT_TRUE(fs::is_regular_file(secondResource.ownedResourceDirectoryPath / "payload.bin"));
}
TEST(KernelSpecializationApiTest, ReportsMissingRegisteredResource)
{
const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_session_missing_resource_%%%%-%%%%");
fs::create_directories(testRoot);
ScopedRegisteredCompilationResource registeredResource(
"unrelated-resource", Json{{"schema_version", "1.0"}}, testRoot);
ASSERT_TRUE(registeredResource.WasInserted());
const uint8_t constantBytes[] = {0x01, 0xfe};
const void* constantAddresses[] = {constantBytes};
const uint64_t constantByteCounts[] = {sizeof(constantBytes)};
aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses);
request.resourceId = "missing-resource";
EXPECT_EQ(
aclrtcKernelSpecialization(&request, (testRoot / "kernel.elf").c_str()), ascendc::aclrtc::ACLRTC_ERROR_FAILURE);
boost::system::error_code ignoredError;
fs::remove_all(testRoot, ignoredError);
}
TEST(KernelSpecializationApiTest, RejectsNullPointersBeforeResourceLookup)
{
aclrtcKernelSpecRequest request{};
EXPECT_EQ(aclrtcKernelSpecialization(nullptr, "/tmp/kernel.elf"), ascendc::aclrtc::ACLRTC_ERROR_INVALID_INPUT);
EXPECT_EQ(aclrtcKernelSpecialization(&request, nullptr), ascendc::aclrtc::ACLRTC_ERROR_INVALID_INPUT);
EXPECT_EQ(aclrtcKernelSpecialization(&request, "/tmp/kernel.elf"), ascendc::aclrtc::ACLRTC_ERROR_INVALID_INPUT);
}
TEST(KernelSpecializationApiTest, CompilesRegisteredResourceThroughPublicApi)
{
EndToEndTestResource testResource(FakeCompilerOutcome::Success);
ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str());
ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", nullptr);
const std::vector<ResourceFileData> resourceFiles = {
{"constants.h", "resources/include/constants.h", {'T', 'I', 'L', 'I', 'N', 'G', '=', '@', '@',
'S', 'T', 'A', 'T', 'I', 'C', '_', 'T', 'I',
'L', 'I', 'N', 'G', '@', '@', ';', '\n'}},
{"basic.cpp", "resources/src/basic.cpp", {'/', '/', ' ', 'b', 'a', 's', 'i', 'c', '\n'}},
};
ScopedRegisteredCompilationResource registeredResource(
RESOURCE_ID, testResource.Manifest(), testResource.SourceDirectoryPath(), resourceFiles);
ASSERT_TRUE(registeredResource.WasInserted());
const uint8_t constantBytes[] = {0x01, 0xfe};
const void* constantAddresses[] = {constantBytes};
const uint64_t constantByteCounts[] = {sizeof(constantBytes)};
aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses);
EXPECT_EQ(
aclrtcKernelSpecialization(&request, testResource.OutputElfPath().c_str()), ascendc::aclrtc::ACLRTC_SUCCESS);
EXPECT_EQ(ReadFile(testResource.OutputElfPath()), "linked-elf");
}
TEST(KernelSpecializationSessionTest, ReportsManifestPlanFailure)
{
EndToEndTestResource testResource(FakeCompilerOutcome::Success);
ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str());
ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", nullptr);
const uint8_t constantBytes[] = {0x01, 0xfe};
const void* constantAddresses[] = {constantBytes};
const uint64_t constantByteCounts[] = {sizeof(constantBytes)};
const aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses);
MaterializedKernelCompilationResource resource = testResource.CreateMaterializedCompilationResource();
resource.manifest["schema_version"] = "unsupported";
KernelSpecializationSession session;
EXPECT_EQ(
session.RunSpecializationWithMaterializedResource(
NormalizeRequestForTest(request, testResource.OutputElfPath()), std::move(resource)),
ascendc::aclrtc::ACLRTC_ERROR_FAILURE);
EXPECT_FALSE(fs::exists(testResource.OutputElfPath()));
}
TEST(KernelSpecializationSessionTest, ReportsSourcePatchFailure)
{
EndToEndTestResource testResource(FakeCompilerOutcome::Success);
ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str());
ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", nullptr);
const uint8_t constantBytes[] = {0x01, 0xfe};
const void* constantAddresses[] = {constantBytes};
const uint64_t constantByteCounts[] = {sizeof(constantBytes)};
const aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses);
MaterializedKernelCompilationResource resource = testResource.CreateMaterializedCompilationResource();
resource.manifest["kernels"][0]["constant_infos"][0]["template"] = "@@MISSING_TEMPLATE@@";
KernelSpecializationSession session;
EXPECT_EQ(
session.RunSpecializationWithMaterializedResource(
NormalizeRequestForTest(request, testResource.OutputElfPath()), std::move(resource)),
ascendc::aclrtc::ACLRTC_ERROR_FAILURE);
EXPECT_FALSE(fs::exists(testResource.OutputElfPath()));
}
TEST(KernelSpecializationSessionTest, RejectsMissingLinkedElfDuringPublication)
{
const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_publish_missing_%%%%-%%%%");
const fs::path worktreePath = testRoot / "worktree";
fs::create_directories(worktreePath);
KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation);
KernelSpecializationSession session;
EXPECT_EQ(
session.PublishKernelElf(testRoot / "missing.elf", testRoot / "output.elf", workspace),
ascendc::aclrtc::ACLRTC_ERROR_FAILURE);
boost::system::error_code ignoredError;
fs::remove_all(testRoot, ignoredError);
}
TEST(KernelSpecializationSessionTest, RemovesTemporaryElfWhenPublicationTargetIsDirectory)
{
const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_publish_directory_%%%%-%%%%");
const fs::path worktreePath = testRoot / "worktree";
const fs::path linkedElfPath = testRoot / "linked.elf";
const fs::path outputDirectoryPath = testRoot / "output.elf";
fs::create_directories(worktreePath);
fs::create_directories(outputDirectoryPath);
std::ofstream(linkedElfPath.string()) << "linked-elf";
KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation);
KernelSpecializationSession session;
EXPECT_EQ(
session.PublishKernelElf(linkedElfPath, outputDirectoryPath, workspace), ascendc::aclrtc::ACLRTC_ERROR_FAILURE);
EXPECT_TRUE(fs::is_directory(outputDirectoryPath));
EXPECT_EQ(std::distance(fs::directory_iterator(testRoot), fs::directory_iterator()), 3);
boost::system::error_code ignoredError;
fs::remove_all(testRoot, ignoredError);
}
TEST(KernelSpecializationSessionTest, RemovesTemporaryElfWhenLinkedElfCannotBeRead)
{
const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_publish_read_failure_%%%%-%%%%");
const fs::path linkedElfDirectory = testRoot / "linked.elf";
fs::create_directories(linkedElfDirectory);
KernelCompilationWorkspace workspace(testRoot / "worktree", WorktreeRetentionPolicy::RetainAfterCompilation);
KernelSpecializationSession session;
EXPECT_EQ(
session.PublishKernelElf(linkedElfDirectory, testRoot / "output.elf", workspace),
ascendc::aclrtc::ACLRTC_ERROR_FAILURE);
EXPECT_EQ(std::distance(fs::directory_iterator(testRoot), fs::directory_iterator()), 1);
boost::system::error_code ignoredError;
fs::remove_all(testRoot, ignoredError);
}
TEST(KernelSpecializationSessionTest, RemovesTemporaryElfWhenOutputWriteFails)
{
const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_publish_write_failure_%%%%-%%%%");
fs::create_directories(testRoot);
const fs::path linkedElfPath = testRoot / "linked.elf";
std::ofstream(linkedElfPath.string()) << "linked-elf";
KernelCompilationWorkspace workspace(testRoot / "worktree", WorktreeRetentionPolicy::RetainAfterCompilation);
KernelSpecializationSession session;
struct rlimit originalFileSizeLimit {};
ASSERT_EQ(getrlimit(RLIMIT_FSIZE, &originalFileSizeLimit), 0);
struct sigaction ignoreFileSizeSignal {};
struct sigaction originalFileSizeSignal {};
ignoreFileSizeSignal.sa_handler = SIG_IGN;
ASSERT_EQ(sigemptyset(&ignoreFileSizeSignal.sa_mask), 0);
ASSERT_EQ(sigaction(SIGXFSZ, &ignoreFileSizeSignal, &originalFileSizeSignal), 0);
struct rlimit zeroFileSizeLimit = originalFileSizeLimit;
zeroFileSizeLimit.rlim_cur = 0U;
ASSERT_EQ(setrlimit(RLIMIT_FSIZE, &zeroFileSizeLimit), 0);
const aclError publishResult = session.PublishKernelElf(linkedElfPath, testRoot / "output.elf", workspace);
const int restoreLimitResult = setrlimit(RLIMIT_FSIZE, &originalFileSizeLimit);
const int restoreSignalResult = sigaction(SIGXFSZ, &originalFileSizeSignal, nullptr);
ASSERT_EQ(restoreLimitResult, 0);
ASSERT_EQ(restoreSignalResult, 0);
EXPECT_EQ(publishResult, ascendc::aclrtc::ACLRTC_ERROR_FAILURE);
EXPECT_FALSE(fs::exists(testRoot / "output.elf"));
EXPECT_EQ(std::distance(fs::directory_iterator(testRoot), fs::directory_iterator()), 1);
boost::system::error_code ignoredError;
fs::remove_all(testRoot, ignoredError);
}
TEST(KernelSpecializationSessionTest, StopsPublicationWhenWorktreeCannotBeRemoved)
{
const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_publish_cleanup_failure_%%%%-%%%%");
fs::create_directories(testRoot);
const fs::path linkedElfPath = testRoot / "linked.elf";
std::ofstream(linkedElfPath.string()) << "linked-elf";
KernelCompilationWorkspace workspace("/proc/self/status", WorktreeRetentionPolicy::RemoveAfterCompilation);
KernelSpecializationSession session;
EXPECT_EQ(
session.PublishKernelElf(linkedElfPath, testRoot / "output.elf", workspace),
ascendc::aclrtc::ACLRTC_ERROR_FAILURE);
EXPECT_FALSE(fs::exists(testRoot / "output.elf"));
EXPECT_EQ(std::distance(fs::directory_iterator(testRoot), fs::directory_iterator()), 1);
boost::system::error_code ignoredError;
fs::remove_all(testRoot, ignoredError);
}
}