compute-runtime/opencl/test/unit_test/offline_compiler/offline_linker_tests.cpp

770 lines
30 KiB
C++

/*
* Copyright (C) 2022-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "offline_linker_tests.h"
#include "shared/offline_compiler/source/ocloc_api.h"
#include "shared/source/device_binary_format/elf/elf_decoder.h"
#include "shared/source/device_binary_format/elf/ocl_elf.h"
#include "shared/source/helpers/string.h"
#include "shared/source/os_interface/os_inc_base.h"
#include "shared/test/common/helpers/mock_file_io.h"
#include "shared/test/common/helpers/stdout_capture.h"
#include "shared/test/common/mocks/mock_compilers.h"
#include "environment.h"
#include <algorithm>
#include <array>
#include <cstring>
#include <iterator>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
extern Environment *gEnvironment;
namespace NEO {
using OperationMode = MockOfflineLinker::OperationMode;
void OfflineLinkerTest::SetUp() {
std::string spvFile = std::string("copybuffer") + "_" + gEnvironment->devicePrefix + ".spv";
std::string binFile = std::string("copybuffer") + "_" + gEnvironment->devicePrefix + ".bin";
std::string dbgFile = std::string("copybuffer") + "_" + gEnvironment->devicePrefix + ".dbg";
constexpr unsigned char mockByteArray[] = {0x01, 0x02, 0x03, 0x04};
std::string_view byteArrayView(reinterpret_cast<const char *>(mockByteArray), sizeof(mockByteArray));
writeDataToFile(spvFile.c_str(), byteArrayView);
writeDataToFile(binFile.c_str(), byteArrayView);
writeDataToFile(dbgFile.c_str(), byteArrayView);
MockCompilerDebugVars igcDebugVars{gEnvironment->igcDebugVars};
igcDebugVars.binaryToReturn = binaryToReturn;
igcDebugVars.binaryToReturnSize = sizeof(binaryToReturn);
setIgcDebugVars(igcDebugVars);
}
void OfflineLinkerTest::TearDown() {
setIgcDebugVars(gEnvironment->igcDebugVars);
}
std::string OfflineLinkerTest::getEmptySpirvFile() const {
std::string spirv{"\x07\x23\x02\x03"};
spirv.resize(64, '\0');
return spirv;
}
std::string OfflineLinkerTest::getEmptyLlvmBcFile() const {
std::string llvmbc{"BC\xc0\xde"};
llvmbc.resize(64, '\0');
return llvmbc;
}
MockOfflineLinker::InputFileContent OfflineLinkerTest::createFileContent(const std::string &content, IGC::CodeType::CodeType_t codeType) const {
std::unique_ptr<char[]> bytes{new char[content.size()]};
std::copy(content.begin(), content.end(), bytes.get());
return {std::move(bytes), content.size(), codeType};
}
TEST_F(OfflineLinkerTest, GivenDefaultConstructedLinkerThenRequiredFieldsHaveDefaultValues) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
EXPECT_EQ(OperationMode::skipExecution, mockOfflineLinker.operationMode);
EXPECT_EQ("linker_output", mockOfflineLinker.outputFilename);
EXPECT_EQ(IGC::CodeType::llvmBc, mockOfflineLinker.outputFormat);
}
TEST_F(OfflineLinkerTest, GivenLessThanTwoArgumentsWhenParsingThenInvalidCommandIsReturned) {
const std::vector<std::string> argv = {
"ocloc.exe"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
EXPECT_EQ(OCLOC_INVALID_COMMAND_LINE, result);
}
TEST_F(OfflineLinkerTest, GivenInputFilesArgumentsWhenParsingThenListOfFilenamesIsPopulated) {
const std::string firstFile{"sample_input_1.spv"};
const std::string secondFile{"sample_input_2.spv"};
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
firstFile,
"-file",
secondFile};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OCLOC_SUCCESS, result);
ASSERT_EQ(2u, mockOfflineLinker.inputFilenames.size());
EXPECT_EQ(firstFile, mockOfflineLinker.inputFilenames[0]);
EXPECT_EQ(secondFile, mockOfflineLinker.inputFilenames[1]);
}
TEST_F(OfflineLinkerTest, GivenOutputFilenameArgumentWhenParsingThenOutputFilenameIsSetAccordingly) {
const std::string outputFilename{"my_custom_output_filename"};
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-out",
outputFilename};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OCLOC_SUCCESS, result);
EXPECT_EQ(outputFilename, mockOfflineLinker.outputFilename);
}
TEST_F(OfflineLinkerTest, GivenValidOutputFileFormatWhenParsingThenOutputFormatIsSetAccordingly) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-out_format",
"LLVM_BC"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OCLOC_SUCCESS, result);
EXPECT_EQ(IGC::CodeType::llvmBc, mockOfflineLinker.outputFormat);
}
TEST_F(OfflineLinkerTest, GivenUnknownOutputFileFormatWhenParsingThenInvalidFormatIsSet) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-out_format",
"StrangeFormat"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OCLOC_SUCCESS, result);
EXPECT_EQ(IGC::CodeType::invalid, mockOfflineLinker.outputFormat);
}
TEST_F(OfflineLinkerTest, GivenOptionsArgumentWhenParsingThenOptionsAreSet) {
const std::string options{"-g"};
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-options",
options};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OCLOC_SUCCESS, result);
EXPECT_EQ(options, mockOfflineLinker.options);
}
TEST_F(OfflineLinkerTest, GivenInternalOptionsArgumentWhenParsingThenInternalOptionsAreSet) {
const std::string internalOptions{"-ze-disable-zebin"};
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-internal_options",
internalOptions};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OCLOC_SUCCESS, result);
EXPECT_EQ(internalOptions, mockOfflineLinker.internalOptions);
}
TEST_F(OfflineLinkerTest, GivenHelpArgumentWhenParsingThenShowHelpOperationIsSet) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"--help"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OCLOC_SUCCESS, result);
EXPECT_EQ(OperationMode::showHelp, mockOfflineLinker.operationMode);
}
TEST_F(OfflineLinkerTest, GivenUnknownArgumentWhenParsingThenErrorIsReported) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-some_new_unknown_command"};
StdoutCapture capture;
capture.captureStdout();
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
const auto result = mockOfflineLinker.initialize(argv.size(), argv);
const auto output{capture.getCapturedStdout()};
EXPECT_EQ(OCLOC_INVALID_COMMAND_LINE, result);
const std::string expectedErrorMessage{"Invalid option (arg 2): -some_new_unknown_command\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenFlagsWhichRequireMoreArgsWithoutThemWhenParsingThenErrorIsReported) {
const std::array<std::string, 5> flagsToTest = {
"-file", "-out", "-out_format", "-options", "-internal_options"};
for (const auto &flag : flagsToTest) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
flag};
mockOclocIgcFacade = std::make_unique<MockOclocIgcFacade>(&mockArgHelper);
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
StdoutCapture capture;
capture.captureStdout();
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
const auto output{capture.getCapturedStdout()};
EXPECT_EQ(OCLOC_INVALID_COMMAND_LINE, result);
const std::string expectedErrorMessage{"Invalid option (arg 2): " + flag + "\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
}
TEST_F(OfflineLinkerTest, GivenCommandWithoutInputFilesWhenVerificationIsPerformedThenErrorIsReturned) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.inputFilenames = {};
StdoutCapture capture;
capture.captureStdout();
const auto verificationResult = mockOfflineLinker.verifyLinkerCommand();
const auto output{capture.getCapturedStdout()};
EXPECT_EQ(OCLOC_INVALID_COMMAND_LINE, verificationResult);
const std::string expectedErrorMessage{"Error: Input name is missing! At least one input file is required!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenCommandWithEmptyFilenameWhenVerificationIsPerformedThenErrorIsReturned) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
""};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
StdoutCapture capture;
capture.captureStdout();
const auto verificationResult = mockOfflineLinker.initialize(argv.size(), argv);
const auto output{capture.getCapturedStdout()};
EXPECT_EQ(OCLOC_INVALID_COMMAND_LINE, verificationResult);
const std::string expectedErrorMessage{"Error: Empty filename cannot be used!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenCommandWithNonexistentInputFileWhenVerificationIsPerformedThenErrorIsReturned) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
"some_file1.spv",
"-file",
"some_file2.spv"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
const auto parsingResult = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OCLOC_SUCCESS, parsingResult);
StdoutCapture capture;
capture.captureStdout();
const auto verificationResult = mockOfflineLinker.verifyLinkerCommand();
const auto output{capture.getCapturedStdout()};
EXPECT_EQ(OCLOC_INVALID_FILE, verificationResult);
const std::string expectedErrorMessage{"Error: Input file some_file1.spv missing.\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenCommandWithInvalidOutputFormatWhenVerificationIsPerformedThenErrorIsReturned) {
mockArgHelperFilesMap["some_file.spv"] = getEmptySpirvFile();
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
"some_file.spv",
"-out_format",
"SomeDummyUnknownFormat"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
const auto parsingResult = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OCLOC_SUCCESS, parsingResult);
StdoutCapture capture;
capture.captureStdout();
const auto verificationResult = mockOfflineLinker.verifyLinkerCommand();
const auto output{capture.getCapturedStdout()};
EXPECT_EQ(OCLOC_INVALID_COMMAND_LINE, verificationResult);
const std::string expectedErrorMessage{"Error: Invalid output type!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenValidCommandWhenVerificationIsPerformedThenSuccessIsReturned) {
mockArgHelperFilesMap["some_file1.spv"] = getEmptySpirvFile();
mockArgHelperFilesMap["some_file2.spv"] = getEmptySpirvFile();
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
"some_file1.spv",
"-file",
"some_file2.spv"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
const auto parsingResult = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OCLOC_SUCCESS, parsingResult);
StdoutCapture capture;
capture.captureStdout();
const auto verificationResult = mockOfflineLinker.verifyLinkerCommand();
const auto output{capture.getCapturedStdout()};
EXPECT_EQ(OCLOC_SUCCESS, verificationResult);
EXPECT_TRUE(output.empty());
}
TEST_F(OfflineLinkerTest, GivenEmptyFileWhenLoadingInputFilesThenErrorIsReturned) {
const std::string filename{"some_file.spv"};
mockArgHelperFilesMap[filename] = "";
mockArgHelper.shouldLoadDataFromFileReturnZeroSize = true;
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
filename};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
StdoutCapture capture;
capture.captureStdout();
const auto initializationResult = mockOfflineLinker.initialize(argv.size(), argv);
const auto output{capture.getCapturedStdout()};
ASSERT_EQ(OCLOC_INVALID_FILE, initializationResult);
const std::string expectedErrorMessage{"Error: Cannot read input file: some_file.spv\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenValidFileWithUnknownFormatWhenLoadingInputFilesThenErrorIsReturned) {
const std::string filename{"some_file.unknown"};
// Spir-V or LLVM-BC magic constants are required. This should be treated as error.
mockArgHelperFilesMap[filename] = "Some unknown format!";
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.inputFilenames.push_back(filename);
StdoutCapture capture;
capture.captureStdout();
const auto readingResult = mockOfflineLinker.loadInputFilesContent();
const auto output{capture.getCapturedStdout()};
ASSERT_EQ(OCLOC_INVALID_PROGRAM, readingResult);
const std::string expectedErrorMessage{"Error: Unsupported format of input file: some_file.unknown\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenValidFilesWithValidFormatsWhenLoadingInputFilesThenFilesAreLoadedAndSuccessIsReturned) {
const std::string firstFilename{"some_file1.spv"};
const std::string secondFilename{"some_file2.llvmbc"};
mockArgHelperFilesMap[firstFilename] = getEmptySpirvFile();
mockArgHelperFilesMap[secondFilename] = getEmptyLlvmBcFile();
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.inputFilenames.push_back(firstFilename);
mockOfflineLinker.inputFilenames.push_back(secondFilename);
StdoutCapture capture;
capture.captureStdout();
const auto readingResult = mockOfflineLinker.loadInputFilesContent();
const auto output{capture.getCapturedStdout()};
ASSERT_EQ(OCLOC_SUCCESS, readingResult);
EXPECT_TRUE(output.empty());
const auto &firstExpectedContent = mockArgHelperFilesMap[firstFilename];
const auto &firstActualContent = mockOfflineLinker.inputFilesContent[0];
ASSERT_EQ(firstExpectedContent.size() + 1, firstActualContent.size);
const auto isFirstPairEqual = std::equal(firstExpectedContent.begin(), firstExpectedContent.end(), firstActualContent.bytes.get());
EXPECT_TRUE(isFirstPairEqual);
const auto &secondExpectedContent = mockArgHelperFilesMap[secondFilename];
const auto &secondActualContent = mockOfflineLinker.inputFilesContent[1];
ASSERT_EQ(secondExpectedContent.size() + 1, secondActualContent.size);
const auto isSecondPairEqual = std::equal(secondExpectedContent.begin(), secondExpectedContent.end(), secondActualContent.bytes.get());
EXPECT_TRUE(isSecondPairEqual);
}
TEST_F(OfflineLinkerTest, GivenValidFilesWhenInitializationIsSuccessfulThenLinkModeOfOperationIsSet) {
const std::string firstFilename{"some_file1.spv"};
const std::string secondFilename{"some_file2.llvmbc"};
mockArgHelperFilesMap[firstFilename] = getEmptySpirvFile();
mockArgHelperFilesMap[secondFilename] = getEmptyLlvmBcFile();
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
firstFilename,
"-file",
secondFilename};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
StdoutCapture capture;
capture.captureStdout();
const auto initializationResult = mockOfflineLinker.initialize(argv.size(), argv);
const auto output{capture.getCapturedStdout()};
EXPECT_EQ(OCLOC_SUCCESS, initializationResult);
EXPECT_TRUE(output.empty());
EXPECT_EQ(OperationMode::linkFiles, mockOfflineLinker.operationMode);
}
TEST_F(OfflineLinkerTest, GivenSPIRVandLLVMBCFilesWhenElfOutputIsRequestedThenElfWithSPIRVAndLLVMSectionsIsCreated) {
auto spirvFileContent = createFileContent(getEmptySpirvFile(), IGC::CodeType::spirV);
auto llvmbcFileContent = createFileContent(getEmptyLlvmBcFile(), IGC::CodeType::llvmBc);
mockArgHelper.interceptOutput = true;
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.inputFilesContent.emplace_back(std::move(spirvFileContent.bytes), spirvFileContent.size, spirvFileContent.codeType);
mockOfflineLinker.inputFilesContent.emplace_back(std::move(llvmbcFileContent.bytes), llvmbcFileContent.size, llvmbcFileContent.codeType);
mockOfflineLinker.outputFormat = IGC::CodeType::elf;
mockOfflineLinker.operationMode = OperationMode::linkFiles;
const auto linkingResult{mockOfflineLinker.execute()};
ASSERT_EQ(OCLOC_SUCCESS, linkingResult);
ASSERT_EQ(1u, mockArgHelper.interceptedFiles.count("linker_output"));
const auto &rawOutput{mockArgHelper.interceptedFiles.at("linker_output")};
const auto encodedElf{ArrayRef<const uint8_t>::fromAny(rawOutput.data(), rawOutput.size())};
std::string errorReason{};
std::string warning{};
const auto elf{Elf::decodeElf(encodedElf, errorReason, warning)};
ASSERT_TRUE(errorReason.empty());
EXPECT_TRUE(warning.empty());
// SPIR-V bitcode section.
EXPECT_EQ(Elf::SHT_OPENCL_SPIRV, elf.sectionHeaders[1].header->type);
const auto &expectedFirstSection = mockOfflineLinker.inputFilesContent[0];
const auto &actualFirstSection = elf.sectionHeaders[1];
ASSERT_EQ(expectedFirstSection.size, actualFirstSection.header->size);
const auto isFirstSectionContentEqual = std::memcmp(actualFirstSection.data.begin(), expectedFirstSection.bytes.get(), expectedFirstSection.size) == 0;
EXPECT_TRUE(isFirstSectionContentEqual);
// LLVM bitcode section.
EXPECT_EQ(Elf::SHT_OPENCL_LLVM_BINARY, elf.sectionHeaders[2].header->type);
const auto &expectedSecondSection = mockOfflineLinker.inputFilesContent[1];
const auto &actualSecondSection = elf.sectionHeaders[2];
ASSERT_EQ(expectedSecondSection.size, actualSecondSection.header->size);
const auto isSecondSectionContentEqual = std::memcmp(actualSecondSection.data.begin(), expectedSecondSection.bytes.get(), expectedSecondSection.size) == 0;
EXPECT_TRUE(isSecondSectionContentEqual);
}
TEST_F(OfflineLinkerTest, GivenValidInputFileContentsWhenLlvmBcOutputIsRequestedThenSuccessIsReturnedAndFileIsWritten) {
auto spirvFileContent = createFileContent(getEmptySpirvFile(), IGC::CodeType::spirV);
auto llvmbcFileContent = createFileContent(getEmptyLlvmBcFile(), IGC::CodeType::llvmBc);
mockArgHelper.interceptOutput = true;
HardwareInfo hwInfo{};
const auto igcInitializationResult{mockOclocIgcFacade->initialize(hwInfo)};
ASSERT_EQ(OCLOC_SUCCESS, igcInitializationResult);
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.inputFilesContent.emplace_back(std::move(spirvFileContent.bytes), spirvFileContent.size, spirvFileContent.codeType);
mockOfflineLinker.inputFilesContent.emplace_back(std::move(llvmbcFileContent.bytes), llvmbcFileContent.size, llvmbcFileContent.codeType);
mockOfflineLinker.outputFormat = IGC::CodeType::llvmBc;
mockOfflineLinker.operationMode = OperationMode::linkFiles;
const auto linkingResult{mockOfflineLinker.execute()};
ASSERT_EQ(OCLOC_SUCCESS, linkingResult);
ASSERT_EQ(1u, mockArgHelper.interceptedFiles.count("linker_output"));
const auto &actualOutput{mockArgHelper.interceptedFiles.at("linker_output")};
const auto &expectedOutput{binaryToReturn};
ASSERT_EQ(sizeof(expectedOutput), actualOutput.size());
const auto isActualOutputSameAsExpected{std::equal(std::begin(expectedOutput), std::end(expectedOutput), std::begin(actualOutput))};
EXPECT_TRUE(isActualOutputSameAsExpected);
}
TEST_F(OfflineLinkerTest, GivenValidInputFileContentsAndFailingIGCWhenLlvmBcOutputIsRequestedThenErrorIsReturned) {
MockCompilerDebugVars igcDebugVars{gEnvironment->igcDebugVars};
igcDebugVars.forceBuildFailure = true;
setIgcDebugVars(igcDebugVars);
auto spirvFileContent = createFileContent(getEmptySpirvFile(), IGC::CodeType::spirV);
auto llvmbcFileContent = createFileContent(getEmptyLlvmBcFile(), IGC::CodeType::llvmBc);
mockArgHelper.interceptOutput = true;
HardwareInfo hwInfo{};
const auto igcInitializationResult{mockOclocIgcFacade->initialize(hwInfo)};
ASSERT_EQ(OCLOC_SUCCESS, igcInitializationResult);
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.inputFilesContent.emplace_back(std::move(spirvFileContent.bytes), spirvFileContent.size, spirvFileContent.codeType);
mockOfflineLinker.inputFilesContent.emplace_back(std::move(llvmbcFileContent.bytes), llvmbcFileContent.size, llvmbcFileContent.codeType);
mockOfflineLinker.outputFormat = IGC::CodeType::llvmBc;
mockOfflineLinker.operationMode = OperationMode::linkFiles;
StdoutCapture capture;
capture.captureStdout();
const auto linkingResult{mockOfflineLinker.execute()};
const auto output{capture.getCapturedStdout()};
ASSERT_EQ(OCLOC_BUILD_PROGRAM_FAILURE, linkingResult);
EXPECT_EQ(0u, mockArgHelper.interceptedFiles.count("linker_output"));
const std::string expectedErrorMessage{"Error: Translation has failed! IGC returned empty output.\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenValidInputFileContentsAndIGCSignalingSuccessButReturningEmptyOutputWhenLlvmBcOutputIsRequestedThenErrorIsReturned) {
MockCompilerDebugVars igcDebugVars{gEnvironment->igcDebugVars};
igcDebugVars.forceSuccessWithEmptyOutput = true;
setIgcDebugVars(igcDebugVars);
auto spirvFileContent = createFileContent(getEmptySpirvFile(), IGC::CodeType::spirV);
auto llvmbcFileContent = createFileContent(getEmptyLlvmBcFile(), IGC::CodeType::llvmBc);
mockArgHelper.interceptOutput = true;
HardwareInfo hwInfo{};
const auto igcInitializationResult{mockOclocIgcFacade->initialize(hwInfo)};
ASSERT_EQ(OCLOC_SUCCESS, igcInitializationResult);
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.inputFilesContent.emplace_back(std::move(spirvFileContent.bytes), spirvFileContent.size, spirvFileContent.codeType);
mockOfflineLinker.inputFilesContent.emplace_back(std::move(llvmbcFileContent.bytes), llvmbcFileContent.size, llvmbcFileContent.codeType);
mockOfflineLinker.outputFormat = IGC::CodeType::llvmBc;
mockOfflineLinker.operationMode = OperationMode::linkFiles;
StdoutCapture capture;
capture.captureStdout();
const auto linkingResult{mockOfflineLinker.execute()};
const auto output{capture.getCapturedStdout()};
ASSERT_EQ(OCLOC_BUILD_PROGRAM_FAILURE, linkingResult);
EXPECT_EQ(0u, mockArgHelper.interceptedFiles.count("linker_output"));
const std::string expectedErrorMessage{"Error: Translation has failed! IGC returned empty output.\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenValidInputFileContentsAndInvalidTranslationOutputWhenLlvmBcOutputIsRequestedThenErrorIsReturned) {
MockCompilerDebugVars igcDebugVars{gEnvironment->igcDebugVars};
igcDebugVars.shouldReturnInvalidTranslationOutput = true;
setIgcDebugVars(igcDebugVars);
auto spirvFileContent = createFileContent(getEmptySpirvFile(), IGC::CodeType::spirV);
auto llvmbcFileContent = createFileContent(getEmptyLlvmBcFile(), IGC::CodeType::llvmBc);
mockArgHelper.interceptOutput = true;
HardwareInfo hwInfo{};
const auto igcInitializationResult{mockOclocIgcFacade->initialize(hwInfo)};
ASSERT_EQ(OCLOC_SUCCESS, igcInitializationResult);
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.inputFilesContent.emplace_back(std::move(spirvFileContent.bytes), spirvFileContent.size, spirvFileContent.codeType);
mockOfflineLinker.inputFilesContent.emplace_back(std::move(llvmbcFileContent.bytes), llvmbcFileContent.size, llvmbcFileContent.codeType);
mockOfflineLinker.outputFormat = IGC::CodeType::llvmBc;
mockOfflineLinker.operationMode = OperationMode::linkFiles;
StdoutCapture capture;
capture.captureStdout();
const auto linkingResult{mockOfflineLinker.execute()};
const auto output{capture.getCapturedStdout()};
ASSERT_EQ(OCLOC_OUT_OF_HOST_MEMORY, linkingResult);
EXPECT_EQ(0u, mockArgHelper.interceptedFiles.count("linker_output"));
const std::string expectedErrorMessage{"Error: Translation has failed! IGC output is nullptr!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenUninitializedLinkerWhenExecuteIsInvokedThenErrorIsIssued) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
StdoutCapture capture;
capture.captureStdout();
const auto executionResult{mockOfflineLinker.execute()};
const auto output{capture.getCapturedStdout()};
ASSERT_EQ(OCLOC_INVALID_COMMAND_LINE, executionResult);
ASSERT_FALSE(output.empty());
}
TEST_F(OfflineLinkerTest, GivenHelpRequestWhenExecuteIsInvokedThenHelpIsPrinted) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.operationMode = OperationMode::showHelp;
StdoutCapture capture;
capture.captureStdout();
const auto executionResult{mockOfflineLinker.execute()};
const auto output{capture.getCapturedStdout()};
ASSERT_EQ(OCLOC_SUCCESS, executionResult);
ASSERT_FALSE(output.empty());
}
TEST_F(OfflineLinkerTest, GivenInvalidOperationModeWhenExecuteIsInvokedThenErrorIsIssued) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.operationMode = static_cast<OperationMode>(7); // NOLINT(clang-analyzer-optin.core.EnumCastOutOfRange)
StdoutCapture capture;
capture.captureStdout();
const auto executionResult{mockOfflineLinker.execute()};
const auto output{capture.getCapturedStdout()};
ASSERT_EQ(OCLOC_INVALID_COMMAND_LINE, executionResult);
ASSERT_FALSE(output.empty());
}
TEST_F(OfflineLinkerTest, GivenUninitializedHwInfoWhenInitIsCalledThenHwInfoIsInitialized) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
ASSERT_EQ(IGFX_UNKNOWN, mockOfflineLinker.hwInfo.platform.eProductFamily);
const auto hwInfoInitializationResult{mockOfflineLinker.initHardwareInfo()};
ASSERT_EQ(OCLOC_SUCCESS, hwInfoInitializationResult);
EXPECT_NE(IGFX_UNKNOWN, mockOfflineLinker.hwInfo.platform.eProductFamily);
}
TEST_F(OfflineLinkerTest, GivenEmptyHwInfoTableWhenInitializationIsPerformedThenItFailsOnHwInit) {
const std::string firstFilename{"some_file1.spv"};
const std::string secondFilename{"some_file2.llvmbc"};
mockArgHelperFilesMap[firstFilename] = getEmptySpirvFile();
mockArgHelperFilesMap[secondFilename] = getEmptyLlvmBcFile();
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
firstFilename,
"-file",
secondFilename};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.shouldReturnEmptyHardwareInfoTable = true;
StdoutCapture capture;
capture.captureStdout();
const auto initializationResult = mockOfflineLinker.initialize(argv.size(), argv);
const auto output{capture.getCapturedStdout()};
EXPECT_EQ(OCLOC_INVALID_DEVICE, initializationResult);
const std::string expectedErrorMessage{"Error! Cannot retrieve any valid hardware information!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenMissingIgcLibraryWhenInitializationIsPerformedThenItFailsOnIgcPreparation) {
const std::string firstFilename{"some_file1.spv"};
const std::string secondFilename{"some_file2.llvmbc"};
mockArgHelperFilesMap[firstFilename] = getEmptySpirvFile();
mockArgHelperFilesMap[secondFilename] = getEmptyLlvmBcFile();
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
firstFilename,
"-file",
secondFilename};
mockOclocIgcFacade->shouldFailLoadingOfIgcLib = true;
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
StdoutCapture capture;
capture.captureStdout();
const auto initializationResult = mockOfflineLinker.initialize(argv.size(), argv);
const auto output{capture.getCapturedStdout()};
EXPECT_EQ(OCLOC_OUT_OF_HOST_MEMORY, initializationResult);
std::stringstream expectedErrorMessage;
expectedErrorMessage << "Error! Loading of IGC library has failed! Filename: " << Os::igcDllName << "\n";
EXPECT_EQ(expectedErrorMessage.str(), output);
}
TEST_F(OfflineLinkerTest, GivenOfflineLinkerWhenStoringValidBuildLogThenItIsSaved) {
const std::string someValidLog{"Warning: This is a build log!"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.tryToStoreBuildLog(someValidLog.data(), someValidLog.size());
EXPECT_EQ(someValidLog, mockOfflineLinker.getBuildLog());
}
TEST_F(OfflineLinkerTest, GivenOfflineLinkerWhenStoringInvalidBuildLogThenItIsIgnored) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper, std::move(mockOclocIgcFacade)};
mockOfflineLinker.tryToStoreBuildLog(nullptr, 0);
const auto buildLog{mockOfflineLinker.getBuildLog()};
EXPECT_TRUE(buildLog.empty());
// Invalid size has been passed.
const char *log{"Info: This is a log!"};
mockOfflineLinker.tryToStoreBuildLog(log, 0);
const auto buildLog2{mockOfflineLinker.getBuildLog()};
EXPECT_TRUE(buildLog2.empty());
}
} // namespace NEO