/*
 * Copyright (C) 2020-2022 Intel Corporation
 *
 * SPDX-License-Identifier: MIT
 *
 */

#include "opencl/test/unit_test/offline_compiler/ocloc_fatbinary_tests.h"

#include "shared/offline_compiler/source/ocloc_arg_helper.h"
#include "shared/offline_compiler/source/ocloc_error_code.h"
#include "shared/source/device_binary_format/ar/ar.h"
#include "shared/source/device_binary_format/ar/ar_decoder.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/hw_helper.h"

#include "environment.h"
#include "mock/mock_argument_helper.h"
#include "mock/mock_offline_compiler.h"

#include <algorithm>
#include <unordered_set>

extern Environment *gEnvironment;

namespace NEO {

auto searchInArchiveByFilename(const Ar::Ar &archive, const ConstStringRef &name) {
    const auto isSearchedFile = [&name](const auto &file) {
        return file.fileName == name;
    };

    const auto &arFiles = archive.files;
    return std::find_if(arFiles.begin(), arFiles.end(), isSearchedFile);
}

std::string prepareTwoDevices(MockOclocArgHelper *argHelper) {
    auto allEnabledDeviceConfigs = argHelper->getAllSupportedDeviceConfigs();
    if (allEnabledDeviceConfigs.size() < 2) {
        return {};
    }

    const auto cfg1 = argHelper->parseProductConfigFromValue(allEnabledDeviceConfigs[0].config);
    const auto cfg2 = argHelper->parseProductConfigFromValue(allEnabledDeviceConfigs[1].config);

    return cfg1 + "," + cfg2;
}

std::string getDeviceConfig(const OfflineCompiler &offlineCompiler) {
    const auto &hwInfo = offlineCompiler.getHardwareInfo();

    const std::string product = hardwarePrefix[hwInfo.platform.eProductFamily];
    const auto stepping = hwInfo.platform.usRevId;

    return product + "." + std::to_string(stepping);
}

TEST(OclocFatBinaryRequestedFatBinary, WhenDeviceArgMissingThenReturnsFalse) {
    const char *args[] = {"ocloc", "-aaa", "*", "-device", "*"};

    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();

    EXPECT_FALSE(NEO::requestedFatBinary(0, nullptr, argHelper.get()));
    EXPECT_FALSE(NEO::requestedFatBinary(1, args, argHelper.get()));
    EXPECT_FALSE(NEO::requestedFatBinary(2, args, argHelper.get()));
    EXPECT_FALSE(NEO::requestedFatBinary(3, args, argHelper.get()));
    EXPECT_FALSE(NEO::requestedFatBinary(4, args, argHelper.get()));
}

TEST(OclocFatBinaryRequestedFatBinary, GivenDeviceArgProvidedWhenFatBinaryFormatWithRangeIsPassedThenTrueIsReturned) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();

    const char *allPlatforms[] = {"ocloc", "-device", "*"};
    const char *manyPlatforms[] = {"ocloc", "-device", "a,b"};
    const char *manyGens[] = {"ocloc", "-device", "gen0,gen1"};
    const char *rangePlatformFrom[] = {"ocloc", "-device", "skl-"};
    const char *rangePlatformTo[] = {"ocloc", "-device", "-skl"};
    const char *rangePlatformBounds[] = {"ocloc", "-device", "skl-icllp"};
    const char *rangeGenFrom[] = {"ocloc", "-device", "gen0-"};
    const char *rangeGenTo[] = {"ocloc", "-device", "-gen5"};
    const char *rangeGenBounds[] = {"ocloc", "-device", "gen0-gen5"};
    const char *rangeConfigBounds[] = {"ocloc", "-device", "9-11"};
    const char *manyConfigs[] = {"ocloc", "-device", "9.0,11"};
    const char *rangeConfigFrom[] = {"ocloc", "-device", "10.1-"};
    const char *rangeConfigTo[] = {"ocloc", "-device", "-11.2"};
    const char *rangeConfigsBoundsSecond[] = {"ocloc", "-device", "11.2-12.2"};

    EXPECT_TRUE(NEO::requestedFatBinary(3, allPlatforms, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, manyPlatforms, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, manyGens, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, rangePlatformFrom, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, rangePlatformTo, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, rangePlatformBounds, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, rangeGenFrom, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, rangeGenTo, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, rangeGenBounds, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, rangeConfigBounds, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, manyConfigs, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, rangeConfigFrom, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, rangeConfigTo, argHelper.get()));
    EXPECT_TRUE(NEO::requestedFatBinary(3, rangeConfigsBoundsSecond, argHelper.get()));
}

TEST(OclocFatBinaryRequestedFatBinary, GivenDeviceArgToFatBinaryWhenConfigMatchesMoreThanOneProductThenTrueIsReturned) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledDeviceConfigs = argHelper->getAllSupportedDeviceConfigs();

    std::string configNum0 = argHelper->parseProductConfigFromValue(allEnabledDeviceConfigs[allEnabledDeviceConfigs.size() / 2].config);
    auto major_pos = configNum0.find(".");
    auto cutMinorAndRevision = configNum0.substr(0, major_pos);
    auto matchedConfigs = getAllMatchedConfigs(cutMinorAndRevision, argHelper.get());

    if (matchedConfigs.size() < 2) {
        GTEST_SKIP();
    }

    const char *fewConfigs[] = {"ocloc", "-device", cutMinorAndRevision.c_str()};
    EXPECT_TRUE(NEO::requestedFatBinary(3, fewConfigs, argHelper.get()));
}

TEST(OclocFatBinaryRequestedFatBinary, GivenDeviceArgAsSingleProductConfigThenFatBinaryIsNotRequested) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledDeviceConfigs = argHelper->getAllSupportedDeviceConfigs();

    for (auto &deviceConfig : allEnabledDeviceConfigs) {
        std::string configStr = argHelper->parseProductConfigFromValue(deviceConfig.config);
        const char *singleConfig[] = {"ocloc", "-device", configStr.c_str()};
        EXPECT_FALSE(NEO::requestedFatBinary(3, singleConfig, argHelper.get()));
    }
}

TEST(OclocFatBinaryRequestedFatBinary, WhenPlatformIsProvidedButDoesNotContainMoreThanOneProductThenReturnFalse) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    const char *skl[] = {"ocloc", "-device", "skl"};
    EXPECT_FALSE(NEO::requestedFatBinary(3, skl, argHelper.get()));
}

TEST(OclocFatBinaryToProductConfigStrings, GivenListOfProductIdsThenReturnsListOfStrings) {
    auto platforms = NEO::getAllSupportedTargetPlatforms();
    auto names = NEO::toProductNames(platforms);
    EXPECT_EQ(names.size(), platforms.size());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenDifferentDeviceArgWhenCheckIfPlatformsAbbreviationIsPassedThenReturnCorrectValue) {
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    auto allEnabledDeviceConfigs = oclocArgHelperWithoutInput->getAllSupportedDeviceConfigs();

    if (allEnabledPlatforms.size() < 3 || allEnabledDeviceConfigs.size() < 3) {
        GTEST_SKIP();
    }

    auto platform0 = allEnabledPlatforms[0];
    ConstStringRef platformName0(hardwarePrefix[platform0], strlen(hardwarePrefix[platform0]));
    auto platform1 = allEnabledPlatforms[1];
    ConstStringRef platformName1(hardwarePrefix[platform1], strlen(hardwarePrefix[platform1]));

    auto deviceMapConfig0 = allEnabledDeviceConfigs[0];
    auto configNumConvention0 = oclocArgHelperWithoutInput->parseProductConfigFromValue(deviceMapConfig0.config);
    auto deviceMapConfig1 = allEnabledDeviceConfigs[1];
    auto configNumConvention1 = oclocArgHelperWithoutInput->parseProductConfigFromValue(deviceMapConfig1.config);

    auto twoPlatforms = platformName0.str() + "," + platformName1.str();
    auto configsRange = configNumConvention0 + "-" + configNumConvention1;
    auto gen = std::to_string(deviceMapConfig0.hwInfo->platform.eRenderCoreFamily);

    EXPECT_TRUE(isDeviceWithPlatformAbbreviation(platformName0, oclocArgHelperWithoutInput.get()));
    EXPECT_TRUE(isDeviceWithPlatformAbbreviation(ConstStringRef(twoPlatforms), oclocArgHelperWithoutInput.get()));
    EXPECT_FALSE(isDeviceWithPlatformAbbreviation(ConstStringRef(configsRange), oclocArgHelperWithoutInput.get()));
    EXPECT_FALSE(isDeviceWithPlatformAbbreviation(ConstStringRef(gen), oclocArgHelperWithoutInput.get()));
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenAsterixThenReturnAllEnabledConfigs) {
    auto expected = oclocArgHelperWithoutInput->getAllSupportedDeviceConfigs();
    auto got = NEO::getTargetConfigsForFatbinary("*", oclocArgHelperWithoutInput.get());

    EXPECT_EQ(got.size(), expected.size());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenProductConfigWhenConfigIsUndefinedThenReturnEmptyList) {
    auto got = NEO::getTargetConfigsForFatbinary("0.0.0", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());

    got = NEO::getTargetConfigsForFatbinary("0.0", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());

    got = NEO::getTargetConfigsForFatbinary("0", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenProductConfigOpenRangeToWhenConfigIsUndefinedThenReturnEmptyList) {
    auto got = NEO::getTargetConfigsForFatbinary("-0.0.0", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());

    got = NEO::getTargetConfigsForFatbinary("-0.0", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());

    got = NEO::getTargetConfigsForFatbinary("-0", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenProductConfigOpenRangeFromWhenConfigIsUndefinedThenReturnEmptyList) {
    auto got = NEO::getTargetConfigsForFatbinary("0.0.0-", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());

    got = NEO::getTargetConfigsForFatbinary("0.0-", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());

    got = NEO::getTargetConfigsForFatbinary("0-", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenProductConfigClosedRangeWhenAnyOfConfigIsUndefinedOrIncorrectThenReturnEmptyList) {
    auto allEnabledDeviceConfigs = oclocArgHelperWithoutInput->getAllSupportedDeviceConfigs();
    if (allEnabledDeviceConfigs.size() < 2) {
        GTEST_SKIP();
    }

    auto deviceMapConfig0 = allEnabledDeviceConfigs[0];
    auto config0Str = oclocArgHelperWithoutInput->parseProductConfigFromValue(deviceMapConfig0.config);

    auto got = NEO::getTargetConfigsForFatbinary("1.2-" + config0Str, oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());

    got = NEO::getTargetConfigsForFatbinary(config0Str + "-1.2", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());

    got = NEO::getTargetConfigsForFatbinary("1.a.c-" + config0Str, oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());

    got = NEO::getTargetConfigsForFatbinary(config0Str + "-1.a.c", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());
}

TEST(OclocFatBinaryRequestedFatBinary, GivenDeviceArgProvidedWhenUnknownGenNameIsPassedThenRequestedFatBinaryReturnsFalse) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    const char *unknownGen[] = {"ocloc", "-device", "gen0"};
    const char *unknownGenCaseInsensitive[] = {"ocloc", "-device", "Gen0"};

    EXPECT_FALSE(NEO::requestedFatBinary(3, unknownGen, argHelper.get()));
    EXPECT_FALSE(NEO::requestedFatBinary(3, unknownGenCaseInsensitive, argHelper.get()));
}

TEST(OclocFatBinaryRequestedFatBinary, GivenDeviceArgProvidedWhenKnownGenNameIsPassedThenRequestedFatBinaryReturnsTrue) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    unsigned int i = 0;
    for (; i < IGFX_MAX_CORE; ++i) {
        if (NEO::familyName[i] != nullptr)
            break;
    }
    const char *genFromFamilyName[] = {"ocloc", "-device", NEO::familyName[i]};
    EXPECT_TRUE(NEO::requestedFatBinary(3, genFromFamilyName, argHelper.get()));
}

TEST(OclocFatBinaryGetAllSupportedTargetPlatforms, WhenRequestedThenReturnsAllPlatformsWithNonNullHardwarePrefixes) {
    auto platforms = NEO::getAllSupportedTargetPlatforms();
    std::unordered_set<uint32_t> platformsSet(platforms.begin(), platforms.end());
    for (unsigned int productId = 0; productId < IGFX_MAX_PRODUCT; ++productId) {
        if (nullptr != NEO::hardwarePrefix[productId]) {
            EXPECT_EQ(1U, platformsSet.count(static_cast<PRODUCT_FAMILY>(productId))) << productId;
        } else {
            EXPECT_EQ(0U, platformsSet.count(static_cast<PRODUCT_FAMILY>(productId))) << productId;
        }
    }
}

TEST(OclocFatBinaryAsProductId, GivenEnabledPlatformNameThenReturnsProperPlatformId) {
    auto platforms = NEO::getAllSupportedTargetPlatforms();
    auto names = NEO::toProductNames(platforms);
    for (size_t i = 0; i < platforms.size(); ++i) {
        auto idByName = NEO::asProductId(names[i], platforms);
        EXPECT_EQ(idByName, platforms[i]) << names[i].data() << " : " << platforms[i] << " != " << idByName;
    }
}

TEST(OclocFatBinaryAsProductId, GivenDisabledPlatformNameThenReturnsUnknownPlatformId) {
    auto platforms = NEO::getAllSupportedTargetPlatforms();
    auto names = NEO::toProductNames(platforms);
    platforms.clear();
    for (size_t i = 0; i < platforms.size(); ++i) {
        auto idByName = NEO::asProductId(names[i], platforms);
        EXPECT_EQ(IGFX_UNKNOWN, platforms[i]) << names[i].data() << " : IGFX_UNKNOWN != " << idByName;
    }
}

TEST(OclocFatBinaryAsGfxCoreIdList, GivenEnabledGfxCoreNameThenReturnsNonEmptyList) {

    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();

    for (unsigned int coreId = 0; coreId < IGFX_MAX_CORE; ++coreId) {
        if (nullptr != NEO::familyName[coreId]) {
            EXPECT_TRUE(argHelper->isGen(ConstStringRef(NEO::familyName[coreId]).str()));
            std::string caseInsensitive = NEO::familyName[coreId];
            std::transform(caseInsensitive.begin(), caseInsensitive.end(), caseInsensitive.begin(), ::tolower);
            EXPECT_TRUE(argHelper->isGen(caseInsensitive));

            auto findCore = caseInsensitive.find("_core");
            if (findCore != std::string::npos) {
                caseInsensitive = caseInsensitive.substr(0, findCore);
                EXPECT_TRUE(argHelper->isGen(caseInsensitive));
            }

            auto findUnderline = caseInsensitive.find("_");
            if (findUnderline != std::string::npos) {
                caseInsensitive.erase(std::remove(caseInsensitive.begin(), caseInsensitive.end(), '_'), caseInsensitive.end());
                EXPECT_TRUE(argHelper->isGen(caseInsensitive));
            }
        }
    }
}

TEST(OclocFatBinaryAsGfxCoreIdList, GivenDisabledGfxCoreNameThenReturnsEmptyList) {

    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();

    EXPECT_FALSE(argHelper->isGen(ConstStringRef("genA").str()));
    EXPECT_FALSE(argHelper->isGen(ConstStringRef("gen0").str()));
    EXPECT_FALSE(argHelper->isGen(ConstStringRef("gen1").str()));
    EXPECT_FALSE(argHelper->isGen(ConstStringRef("gen2").str()));
}

TEST(OclocFatBinaryAsGfxCoreIdList, GivenEnabledGfxCoreNameThenReturnsNonNullIGFX) {

    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();

    for (unsigned int coreId = 0; coreId < IGFX_MAX_CORE; ++coreId) {
        if (nullptr != NEO::familyName[coreId]) {
            EXPECT_EQ(argHelper->returnIGFXforGen(ConstStringRef(NEO::familyName[coreId]).str()), coreId);
            std::string caseInsensitive = NEO::familyName[coreId];
            std::transform(caseInsensitive.begin(), caseInsensitive.end(), caseInsensitive.begin(), ::tolower);
            EXPECT_EQ(argHelper->returnIGFXforGen(caseInsensitive), coreId);

            auto findCore = caseInsensitive.find("_core");
            if (findCore != std::string::npos) {
                caseInsensitive = caseInsensitive.substr(0, findCore);
                EXPECT_EQ(argHelper->returnIGFXforGen(caseInsensitive), coreId);
            }

            auto findUnderline = caseInsensitive.find("_");
            if (findUnderline != std::string::npos) {
                caseInsensitive.erase(std::remove(caseInsensitive.begin(), caseInsensitive.end(), '_'), caseInsensitive.end());
                EXPECT_EQ(argHelper->returnIGFXforGen(caseInsensitive), coreId);
            }
        }
    }
}

TEST(OclocFatBinaryAsGfxCoreIdList, GivenDisabledGfxCoreNameThenReturnsNullIGFX) {

    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();

    EXPECT_EQ(argHelper->returnIGFXforGen(ConstStringRef("genA").str()), 0u);
    EXPECT_EQ(argHelper->returnIGFXforGen(ConstStringRef("gen0").str()), 0u);
    EXPECT_EQ(argHelper->returnIGFXforGen(ConstStringRef("gen1").str()), 0u);
    EXPECT_EQ(argHelper->returnIGFXforGen(ConstStringRef("gen2").str()), 0u);
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenMutiplePlatformThenReturnThosePlatforms) {
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    if (allEnabledPlatforms.size() < 2) {
        GTEST_SKIP();
    }
    auto platform0 = allEnabledPlatforms[0];
    std::string platform0Name = NEO::hardwarePrefix[platform0];
    auto platform1 = allEnabledPlatforms[1];
    std::string platform1Name = NEO::hardwarePrefix[platform1];

    std::vector<ConstStringRef> expected{platform0Name, platform1Name};
    auto got = NEO::getTargetPlatformsForFatbinary(platform0Name + "," + platform1Name, oclocArgHelperWithoutInput.get());
    EXPECT_EQ(expected, got);
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenPlatformOpenRangeFromThenReturnAllEnabledPlatformsThatMatch) {
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    if (allEnabledPlatforms.size() < 3) {
        GTEST_SKIP();
    }
    auto platform0 = allEnabledPlatforms[allEnabledPlatforms.size() / 2];
    std::string platformName = NEO::hardwarePrefix[platform0];

    std::vector<PRODUCT_FAMILY> expectedPlatforms;
    auto platformFrom = std::find(allEnabledPlatforms.begin(), allEnabledPlatforms.end(), platform0);
    expectedPlatforms.insert(expectedPlatforms.end(), platformFrom, allEnabledPlatforms.end());
    auto expected = NEO::toProductNames(expectedPlatforms);
    auto got = NEO::getTargetPlatformsForFatbinary(platformName + "-", oclocArgHelperWithoutInput.get());
    EXPECT_EQ(expected, got);
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenPlatformOpenRangeToThenReturnAllEnabledPlatformsThatMatch) {
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    if (allEnabledPlatforms.size() < 3) {
        GTEST_SKIP();
    }
    auto platform0 = allEnabledPlatforms[allEnabledPlatforms.size() / 2];
    std::string platformName = NEO::hardwarePrefix[platform0];

    std::vector<PRODUCT_FAMILY> expectedPlatforms;
    auto platformTo = std::find(allEnabledPlatforms.begin(), allEnabledPlatforms.end(), platform0);
    expectedPlatforms.insert(expectedPlatforms.end(), allEnabledPlatforms.begin(), platformTo + 1);
    auto expected = NEO::toProductNames(expectedPlatforms);
    auto got = NEO::getTargetPlatformsForFatbinary("-" + platformName, oclocArgHelperWithoutInput.get());
    EXPECT_EQ(expected, got);
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenPlatformClosedRangeThenReturnAllEnabledPlatformsThatMatch) {
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    if (allEnabledPlatforms.size() < 4) {
        GTEST_SKIP();
    }
    auto platformFrom = allEnabledPlatforms[1];
    auto platformTo = allEnabledPlatforms[allEnabledPlatforms.size() - 2];
    std::string platformNameFrom = NEO::hardwarePrefix[platformFrom];
    std::string platformNameTo = NEO::hardwarePrefix[platformTo];

    std::vector<PRODUCT_FAMILY> expectedPlatforms;
    expectedPlatforms.insert(expectedPlatforms.end(), allEnabledPlatforms.begin() + 1, allEnabledPlatforms.begin() + allEnabledPlatforms.size() - 1);
    auto expected = NEO::toProductNames(expectedPlatforms);
    auto got = NEO::getTargetPlatformsForFatbinary(platformNameFrom + "-" + platformNameTo, oclocArgHelperWithoutInput.get());
    EXPECT_EQ(expected, got);

    got = NEO::getTargetPlatformsForFatbinary(platformNameTo + "-" + platformNameFrom, oclocArgHelperWithoutInput.get()); // swap min with max implicitly
    EXPECT_EQ(expected, got);
}

std::vector<GFXCORE_FAMILY> getEnabledCores() {
    std::vector<GFXCORE_FAMILY> ret;
    for (unsigned int coreId = 0; coreId < IGFX_MAX_CORE; ++coreId) {
        if (nullptr != NEO::familyName[coreId]) {
            ret.push_back(static_cast<GFXCORE_FAMILY>(coreId));
        }
    }
    return ret;
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenArchitectureThenReturnAllEnabledConfigsThatMatch) {
    auto allEnabledCores = getEnabledCores();
    if (allEnabledCores.size() < 3) {
        GTEST_SKIP();
    }
    auto core = allEnabledCores[allEnabledCores.size() / 2];
    std::string coreName = NEO::familyName[core];
    if (coreName[0] == 'G') {
        coreName[0] = 'g';
    }

    std::vector<DeviceMapping> expected;
    oclocArgHelperWithoutInput->getProductConfigsForGfxCoreFamily(core, expected);
    auto got = NEO::getTargetConfigsForFatbinary(coreName, oclocArgHelperWithoutInput.get());

    EXPECT_EQ(expected.size(), got.size());
    for (unsigned int i = 0; i < got.size(); i++) {
        EXPECT_TRUE(expected[i] == got[i]);
    }
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenArchitectureOpenRangeFromThenReturnAllEnabledConfigsThatMatch) {
    auto allEnabledCores = getEnabledCores();
    if (allEnabledCores.size() < 3) {
        GTEST_SKIP();
    }
    auto core0 = allEnabledCores[allEnabledCores.size() / 2];
    std::string coreName = NEO::familyName[core0];
    if (coreName[0] == 'G') {
        coreName[0] = 'g';
    }

    std::vector<DeviceMapping> expected;
    unsigned int coreIt = core0;
    while (coreIt < static_cast<unsigned int>(IGFX_MAX_CORE)) {
        oclocArgHelperWithoutInput->getProductConfigsForGfxCoreFamily(static_cast<GFXCORE_FAMILY>(coreIt), expected);
        ++coreIt;
    }

    auto got = NEO::getTargetConfigsForFatbinary(coreName + "-", oclocArgHelperWithoutInput.get());
    EXPECT_EQ(expected.size(), got.size());
    for (unsigned int i = 0; i < got.size(); i++) {
        EXPECT_TRUE(expected[i] == got[i]);
    }
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenArchitectureOpenRangeToThenReturnAllEnabledConfigsThatMatch) {
    auto allEnabledCores = getEnabledCores();
    if (allEnabledCores.size() < 3) {
        GTEST_SKIP();
    }

    auto core0 = allEnabledCores[allEnabledCores.size() / 2];
    std::string coreName = NEO::familyName[core0];
    if (coreName[0] == 'G') {
        coreName[0] = 'g';
    }

    std::vector<DeviceMapping> expected;
    unsigned int coreIt = IGFX_UNKNOWN_CORE;
    ++coreIt;
    while (coreIt <= static_cast<unsigned int>(core0)) {
        oclocArgHelperWithoutInput->getProductConfigsForGfxCoreFamily(static_cast<GFXCORE_FAMILY>(coreIt), expected);
        ++coreIt;
    }

    auto got = NEO::getTargetConfigsForFatbinary("-" + coreName, oclocArgHelperWithoutInput.get());
    EXPECT_EQ(expected.size(), got.size());
    for (unsigned int i = 0; i < got.size(); i++) {
        EXPECT_TRUE(expected[i] == got[i]);
    }
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenArchitectureClosedRangeThenReturnAllEnabledConfigsThatMatch) {
    auto allEnabledCores = getEnabledCores();
    if (allEnabledCores.size() < 4) {
        GTEST_SKIP();
    }
    auto coreFrom = allEnabledCores[1];
    auto coreTo = allEnabledCores[allEnabledCores.size() - 2];
    std::string coreNameFrom = NEO::familyName[coreFrom];
    if (coreNameFrom[0] == 'G') {
        coreNameFrom[0] = 'g';
    }
    std::string coreNameTo = NEO::familyName[coreTo];
    if (coreNameTo[0] == 'G') {
        coreNameTo[0] = 'g';
    }

    std::vector<DeviceMapping> expected;
    auto coreIt = coreFrom;
    while (coreIt <= coreTo) {
        oclocArgHelperWithoutInput->getProductConfigsForGfxCoreFamily(static_cast<GFXCORE_FAMILY>(coreIt), expected);
        coreIt = static_cast<GFXCORE_FAMILY>(static_cast<unsigned int>(coreIt) + 1);
    }

    auto got = NEO::getTargetConfigsForFatbinary(coreNameFrom + "-" + coreNameTo, oclocArgHelperWithoutInput.get());
    EXPECT_EQ(expected.size(), got.size());
    for (unsigned int i = 0; i < got.size(); i++) {
        EXPECT_TRUE(expected[i] == got[i]);
    }

    got = NEO::getTargetConfigsForFatbinary(coreNameTo + "-" + coreNameFrom, oclocArgHelperWithoutInput.get()); // swap min with max implicitly
    EXPECT_EQ(expected.size(), got.size());
    for (unsigned int i = 0; i < got.size(); i++) {
        EXPECT_TRUE(expected[i] == got[i]);
    }
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenUnkownArchitectureThenReturnEmptyList) {
    auto got = NEO::getTargetConfigsForFatbinary("gen0", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenMutiplePlatformWhenSecondPlatformsIsUnknownThenReturnErrorMessage) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    auto platform0 = allEnabledPlatforms[0];
    std::string platform0Name = NEO::hardwarePrefix[platform0];

    auto platformTarget = platform0Name + ",unk";

    std::stringstream resString;
    std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        platformTarget};

    testing::internal::CaptureStdout();
    int retVal = buildFatBinary(argv, argHelper.get());
    auto output = testing::internal::GetCapturedStdout();
    EXPECT_NE(retVal, NEO::OclocErrorCode::SUCCESS);

    resString << "Unknown device : unk\n";
    resString << "Failed to parse target devices from : " << platformTarget << "\n";
    EXPECT_STREQ(output.c_str(), resString.str().c_str());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenClosedRangeTooExtensiveWhenConfigIsValidThenErrorMessageAndFailIsReturned) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledDeviceConfigs = argHelper->getAllSupportedDeviceConfigs();
    if (allEnabledDeviceConfigs.size() < 4) {
        GTEST_SKIP();
    }
    std::string configNum0 = argHelper->parseProductConfigFromValue(allEnabledDeviceConfigs[0].config);
    std::string configNum1 = argHelper->parseProductConfigFromValue(allEnabledDeviceConfigs[1].config);
    std::string configNum2 = argHelper->parseProductConfigFromValue(allEnabledDeviceConfigs[2].config);

    std::stringstream configString;
    configString << configNum0 << "-" << configNum1 << "-" << configNum2;

    std::stringstream resString;
    std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        configString.str()};

    testing::internal::CaptureStdout();
    int retVal = buildFatBinary(argv, argHelper.get());
    auto output = testing::internal::GetCapturedStdout();
    EXPECT_NE(retVal, NEO::OclocErrorCode::SUCCESS);
    resString << "Invalid range : " << configString.str() << " - should be from-to or -to or from-"
              << "\n";
    resString << "Failed to parse target devices from : " << configString.str() << "\n";
    EXPECT_STREQ(output.c_str(), resString.str().c_str());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenClosedRangeTooExtensiveWhenPlatformIsValidThenErrorMessageAndReturnEmptyList) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    if (allEnabledPlatforms.size() < 4) {
        GTEST_SKIP();
    }
    auto platform0 = allEnabledPlatforms[0];
    std::string platform0Name = NEO::hardwarePrefix[platform0];
    auto platform1 = allEnabledPlatforms[1];
    std::string platform1Name = NEO::hardwarePrefix[platform1];
    auto platform2 = allEnabledPlatforms[2];
    std::string platform2Name = NEO::hardwarePrefix[platform2];
    std::string platformsTarget = platform0Name + "-" + platform1Name + "-" + platform2Name;

    std::string resString = "Invalid range : " + platformsTarget + " - should be from-to or -to or from-\n";

    testing::internal::CaptureStdout();
    auto got = NEO::getTargetPlatformsForFatbinary(platformsTarget, argHelper.get());
    auto output = testing::internal::GetCapturedStdout();
    EXPECT_STREQ(output.c_str(), resString.c_str());
    EXPECT_TRUE(got.empty());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenPlatformClosedRangeWhenSecondPlatformIsUnkownThenReturnEmptyList) {
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    auto platform0 = allEnabledPlatforms[0];
    std::string platform0Name = NEO::hardwarePrefix[platform0];

    auto got = NEO::getTargetPlatformsForFatbinary(platform0Name + "-unk", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenGenOpenRangeFromWhenGenIsUnknownThenReturnEmptyList) {
    auto got = NEO::getTargetConfigsForFatbinary("gen2-", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenGenOpenRangeToWhenGenIsUnknownThenReturnEmptyList) {
    auto got = NEO::getTargetConfigsForFatbinary("-gen2", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenGenClosedRangeWhenAnyOfGensIsUnknownThenReturnEmptyList) {
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    auto platform0 = allEnabledPlatforms[0];
    auto gfxCore0 = NEO::hardwareInfoTable[platform0]->platform.eRenderCoreFamily;
    std::string genName = NEO::familyName[gfxCore0];
    if (genName[0] == 'G') {
        genName[0] = 'g';
    }

    auto got = NEO::getTargetConfigsForFatbinary("gen2-" + genName, oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());

    got = NEO::getTargetConfigsForFatbinary(genName + "-gen2", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());

    got = NEO::getTargetConfigsForFatbinary(genName + ",gen2", oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());

    got = NEO::getTargetConfigsForFatbinary("gen2," + genName, oclocArgHelperWithoutInput.get());
    EXPECT_TRUE(got.empty());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenTwoPlatformsWhenFatBinaryBuildIsInvokedThenSuccessIsReturned) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    if (allEnabledPlatforms.size() < 3) {
        GTEST_SKIP();
    }

    auto platform0 = allEnabledPlatforms[0];
    ConstStringRef platformName0(hardwarePrefix[platform0], strlen(hardwarePrefix[platform0]));
    auto platform1 = allEnabledPlatforms[1];
    ConstStringRef platformName1(hardwarePrefix[platform1], strlen(hardwarePrefix[platform1]));

    std::vector<ConstStringRef> expected{platformName0, platformName1};

    std::string platformsTarget = platformName0.str() + "," + platformName1.str();

    auto got = NEO::getTargetPlatformsForFatbinary(platformsTarget, argHelper.get());
    EXPECT_EQ(expected, got);

    auto platformRev0 = std::to_string(hardwareInfoTable[platform0]->platform.usRevId);
    auto platformRev1 = std::to_string(hardwareInfoTable[platform1]->platform.usRevId);
    std::vector<std::string> platformsRevision{platformRev0, platformRev1};
    std::stringstream resString;

    std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        platformsTarget};

    testing::internal::CaptureStdout();
    int retVal = buildFatBinary(argv, argHelper.get());
    auto output = testing::internal::GetCapturedStdout();
    EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);

    for (uint32_t i = 0; i < got.size(); i++) {
        resString << "Build succeeded for : " << expected[i].str() + "." + platformsRevision[i] + ".\n";
    }

    EXPECT_STREQ(output.c_str(), resString.str().c_str());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenPlatformsClosedRangeWhenFatBinaryBuildIsInvokedThenSuccessIsReturned) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    if (allEnabledPlatforms.size() < 4) {
        GTEST_SKIP();
    }

    auto platformFrom = allEnabledPlatforms[0];
    ConstStringRef platformNameFrom(hardwarePrefix[platformFrom], strlen(hardwarePrefix[platformFrom]));
    auto platformTo = allEnabledPlatforms[allEnabledPlatforms.size() / 2];
    ConstStringRef platformNameTo(hardwarePrefix[platformTo], strlen(hardwarePrefix[platformTo]));

    if (platformFrom > platformTo) {
        std::swap(platformFrom, platformTo);
    }

    std::vector<PRODUCT_FAMILY> requestedPlatforms;
    auto from = std::find(allEnabledPlatforms.begin(), allEnabledPlatforms.end(), platformFrom);
    auto to = std::find(allEnabledPlatforms.begin(), allEnabledPlatforms.end(), platformTo) + 1;
    requestedPlatforms.insert(requestedPlatforms.end(), from, to);

    auto expected = toProductNames(requestedPlatforms);

    std::string platformsTarget = platformNameFrom.str() + "-" + platformNameTo.str();

    auto got = NEO::getTargetPlatformsForFatbinary(platformsTarget, argHelper.get());
    EXPECT_EQ(expected, got);

    std::vector<std::string> platformsRevisions;

    for (auto platform : requestedPlatforms) {
        platformsRevisions.push_back(std::to_string(hardwareInfoTable[platform]->platform.usRevId));
    }

    std::stringstream resString;
    std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        platformsTarget};

    testing::internal::CaptureStdout();
    int retVal = buildFatBinary(argv, argHelper.get());
    auto output = testing::internal::GetCapturedStdout();
    EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);

    for (uint32_t i = 0; i < got.size(); i++) {
        resString << "Build succeeded for : " << expected[i].str() + "." + platformsRevisions[i] + ".\n";
    }

    EXPECT_STREQ(output.c_str(), resString.str().c_str());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenPlatformsOpenRangeToWhenFatBinaryBuildIsInvokedThenSuccessIsReturned) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    if (allEnabledPlatforms.size() < 4) {
        GTEST_SKIP();
    }

    auto platformTo = allEnabledPlatforms[0];
    ConstStringRef platformNameTo(hardwarePrefix[platformTo], strlen(hardwarePrefix[platformTo]));

    std::vector<PRODUCT_FAMILY> requestedPlatforms;
    auto platformToId = std::find(allEnabledPlatforms.begin(), allEnabledPlatforms.end(), platformTo);
    assert(platformToId != allEnabledPlatforms.end());

    requestedPlatforms.insert(requestedPlatforms.end(), allEnabledPlatforms.begin(), platformToId + 1);

    auto expected = toProductNames(requestedPlatforms);

    std::string platformsTarget = "-" + platformNameTo.str();

    auto got = NEO::getTargetPlatformsForFatbinary(platformsTarget, argHelper.get());
    EXPECT_EQ(expected, got);

    std::vector<std::string> platformsRevisions;

    for (auto platform : requestedPlatforms) {
        platformsRevisions.push_back(std::to_string(hardwareInfoTable[platform]->platform.usRevId));
    }

    std::stringstream resString;
    std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        platformsTarget};

    testing::internal::CaptureStdout();
    int retVal = buildFatBinary(argv, argHelper.get());
    auto output = testing::internal::GetCapturedStdout();
    EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);

    for (uint32_t i = 0; i < got.size(); i++) {
        resString << "Build succeeded for : " << expected[i].str() + "." + platformsRevisions[i] + ".\n";
    }

    EXPECT_STREQ(output.c_str(), resString.str().c_str());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenPlatformsOpenRangeFromWhenFatBinaryBuildIsInvokedThenSuccessIsReturned) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledPlatforms = NEO::getAllSupportedTargetPlatforms();
    if (allEnabledPlatforms.size() < 4) {
        GTEST_SKIP();
    }

    auto platformFrom = allEnabledPlatforms[0];
    ConstStringRef platformNameFrom(hardwarePrefix[platformFrom], strlen(hardwarePrefix[platformFrom]));

    std::vector<PRODUCT_FAMILY> requestedPlatforms;
    auto platformToId = std::find(allEnabledPlatforms.begin(), allEnabledPlatforms.end(), platformFrom);
    assert(platformToId != allEnabledPlatforms.end());

    requestedPlatforms.insert(requestedPlatforms.end(), platformToId, allEnabledPlatforms.end());

    auto expected = toProductNames(requestedPlatforms);

    std::string platformsTarget = platformNameFrom.str() + "-";

    auto got = NEO::getTargetPlatformsForFatbinary(platformsTarget, argHelper.get());
    EXPECT_EQ(expected, got);

    std::vector<std::string> platformsRevisions;

    for (auto platform : requestedPlatforms) {
        platformsRevisions.push_back(std::to_string(hardwareInfoTable[platform]->platform.usRevId));
    }

    std::stringstream resString;
    std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        platformsTarget};

    testing::internal::CaptureStdout();
    int retVal = buildFatBinary(argv, argHelper.get());
    auto output = testing::internal::GetCapturedStdout();
    EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);

    for (uint32_t i = 0; i < got.size(); i++) {
        resString << "Build succeeded for : " << expected[i].str() + "." + platformsRevisions[i] + ".\n";
    }

    EXPECT_STREQ(output.c_str(), resString.str().c_str());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenTwoConfigsWhenFatBinaryBuildIsInvokedThenSuccessIsReturned) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledDeviceConfigs = argHelper->getAllSupportedDeviceConfigs();
    if (allEnabledDeviceConfigs.size() < 2) {
        GTEST_SKIP();
    }
    auto config0 = allEnabledDeviceConfigs[0];
    auto config1 = allEnabledDeviceConfigs[1];

    auto configStr0 = argHelper->parseProductConfigFromValue(config0.config);
    auto configStr1 = argHelper->parseProductConfigFromValue(config1.config);

    std::vector<std::string> targets{configStr0, configStr1};
    std::vector<DeviceMapping> expected;

    for (auto &target : targets) {
        auto configFirstEl = argHelper->findConfigMatch(target, true);

        auto configLastEl = argHelper->findConfigMatch(target, false);
        for (auto &deviceConfig : allEnabledDeviceConfigs) {
            if (deviceConfig.config >= configFirstEl && deviceConfig.config <= configLastEl) {
                expected.push_back(deviceConfig);
            }
        }
    }

    auto configsTarget = configStr0 + "," + configStr1;
    auto got = NEO::getTargetConfigsForFatbinary(configsTarget, argHelper.get());

    EXPECT_EQ(expected.size(), got.size());
    for (unsigned int i = 0; i < got.size(); i++) {
        EXPECT_TRUE(expected[i] == got[i]);
    }

    std::stringstream resString;
    std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        configsTarget};

    testing::internal::CaptureStdout();
    int retVal = buildFatBinary(argv, argHelper.get());
    auto output = testing::internal::GetCapturedStdout();
    EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);

    for (auto deviceConfig : expected) {
        auto targetConfig = argHelper->parseProductConfigFromValue(deviceConfig.config);
        resString << "Build succeeded for : " << targetConfig + ".\n";
    }

    EXPECT_STREQ(output.c_str(), resString.str().c_str());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenProductConfigOpenRangeFromWhenFatBinaryBuildIsInvokedThenSuccessIsReturned) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledDeviceConfigs = argHelper->getAllSupportedDeviceConfigs();
    if (allEnabledDeviceConfigs.size() < 2) {
        GTEST_SKIP();
    }
    auto deviceMapConfig = allEnabledDeviceConfigs[allEnabledDeviceConfigs.size() / 2];
    auto configNumConvention = argHelper->parseProductConfigFromValue(deviceMapConfig.config);

    std::vector<DeviceMapping> expected;
    auto configFrom = std::find_if(allEnabledDeviceConfigs.begin(),
                                   allEnabledDeviceConfigs.end(),
                                   [&cf = deviceMapConfig](const DeviceMapping &c) -> bool { return cf.config == c.config; });

    expected.insert(expected.end(), configFrom, allEnabledDeviceConfigs.end());

    auto configsTarget = configNumConvention + "-";
    auto got = NEO::getTargetConfigsForFatbinary(configsTarget, argHelper.get());

    EXPECT_EQ(expected.size(), got.size());
    for (unsigned int i = 0; i < got.size(); i++) {
        EXPECT_TRUE(expected[i] == got[i]);
    }

    std::stringstream resString;
    std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        configsTarget};

    testing::internal::CaptureStdout();
    int retVal = buildFatBinary(argv, argHelper.get());
    auto output = testing::internal::GetCapturedStdout();
    EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);

    for (auto deviceConfig : expected) {
        auto targetConfig = argHelper->parseProductConfigFromValue(deviceConfig.config);
        resString << "Build succeeded for : " << targetConfig + ".\n";
    }

    EXPECT_STREQ(output.c_str(), resString.str().c_str());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenProductConfigOpenRangeToWhenFatBinaryBuildIsInvokedThenSuccessIsReturned) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledDeviceConfigs = argHelper->getAllSupportedDeviceConfigs();
    if (allEnabledDeviceConfigs.size() < 2) {
        GTEST_SKIP();
    }

    auto deviceMapConfig = allEnabledDeviceConfigs[allEnabledDeviceConfigs.size() / 2];
    auto configNumConvention = argHelper->parseProductConfigFromValue(deviceMapConfig.config);

    std::vector<DeviceMapping> expected;
    for (auto &deviceConfig : allEnabledDeviceConfigs) {
        if (deviceConfig.config <= deviceMapConfig.config) {
            expected.push_back(deviceConfig);
        }
    }
    auto configsTarget = "-" + configNumConvention;
    auto got = NEO::getTargetConfigsForFatbinary(configsTarget, argHelper.get());

    EXPECT_EQ(expected.size(), got.size());
    for (unsigned int i = 0; i < got.size(); i++) {
        EXPECT_TRUE(expected[i] == got[i]);
    }

    std::stringstream resString;
    std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        configsTarget};

    testing::internal::CaptureStdout();
    int retVal = buildFatBinary(argv, argHelper.get());
    auto output = testing::internal::GetCapturedStdout();
    EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);

    for (auto deviceConfig : expected) {
        auto targetConfig = argHelper->parseProductConfigFromValue(deviceConfig.config);
        resString << "Build succeeded for : " << targetConfig + ".\n";
    }

    EXPECT_STREQ(output.c_str(), resString.str().c_str());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenProductConfigClosedRangeWhenFatBinaryBuildIsInvokedThenSuccessIsReturned) {
    std::unique_ptr<OclocArgHelper> argHelper = std::make_unique<OclocArgHelper>();
    auto allEnabledDeviceConfigs = argHelper->getAllSupportedDeviceConfigs();
    if (allEnabledDeviceConfigs.size() < 4) {
        GTEST_SKIP();
    }

    auto deviceMapConfigFrom = allEnabledDeviceConfigs[1];
    auto deviceMapConfigTo = allEnabledDeviceConfigs[allEnabledDeviceConfigs.size() - 2];
    auto configFromNumConvention = argHelper->parseProductConfigFromValue(deviceMapConfigFrom.config);
    auto configToNumConvention = argHelper->parseProductConfigFromValue(deviceMapConfigTo.config);

    std::vector<DeviceMapping> expected;

    for (auto &deviceConfig : allEnabledDeviceConfigs) {
        if (deviceConfig.config >= deviceMapConfigFrom.config && deviceConfig.config <= deviceMapConfigTo.config) {
            expected.push_back(deviceConfig);
        }
    }
    auto configsTarget = configFromNumConvention + "-" + configToNumConvention;
    auto got = NEO::getTargetConfigsForFatbinary(configsTarget, argHelper.get()); // swap min with max implicitly
    EXPECT_EQ(expected.size(), got.size());
    for (unsigned int i = 0; i < got.size(); i++) {
        EXPECT_TRUE(expected[i] == got[i]);
    }
    got = NEO::getTargetConfigsForFatbinary(configToNumConvention + "-" + configFromNumConvention, argHelper.get()); // swap min with max implicitly

    EXPECT_EQ(expected.size(), got.size());
    for (unsigned int i = 0; i < got.size(); i++) {
        EXPECT_TRUE(expected[i] == got[i]);
    }

    std::stringstream resString;
    std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        configsTarget};

    testing::internal::CaptureStdout();
    int retVal = buildFatBinary(argv, argHelper.get());
    auto output = testing::internal::GetCapturedStdout();
    EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);

    for (auto deviceConfig : expected) {
        auto targetConfig = argHelper->parseProductConfigFromValue(deviceConfig.config);
        resString << "Build succeeded for : " << targetConfig + ".\n";
    }

    EXPECT_STREQ(output.c_str(), resString.str().c_str());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenArgsWhenCorrectDeviceNumerationIsProvidedWithoutRevisionThenTargetsAreFound) {
    auto allEnabledDeviceConfigs = oclocArgHelperWithoutInput->getAllSupportedDeviceConfigs();
    if (allEnabledDeviceConfigs.size() < 2) {
        GTEST_SKIP();
    }

    std::string configNum0 = oclocArgHelperWithoutInput->parseProductConfigFromValue(allEnabledDeviceConfigs[0].config);
    auto major_pos = configNum0.find(".");
    auto minor_pos = configNum0.find(".", ++major_pos);
    auto cutRevision = configNum0.substr(0, minor_pos);

    auto got = NEO::getTargetConfigsForFatbinary(ConstStringRef(cutRevision), oclocArgHelperWithoutInput.get());
    EXPECT_FALSE(got.empty());
}

TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenArgsWhenCorrectDeviceNumerationIsProvidedWithoutMinorAndRevisionThenTargetsAreFound) {
    auto allEnabledDeviceConfigs = oclocArgHelperWithoutInput->getAllSupportedDeviceConfigs();
    if (allEnabledDeviceConfigs.size() < 2) {
        GTEST_SKIP();
    }

    std::string configNum0 = oclocArgHelperWithoutInput->parseProductConfigFromValue(allEnabledDeviceConfigs[0].config);
    auto major_pos = configNum0.find(".");
    auto cutMinorAndRevision = configNum0.substr(0, major_pos);

    auto got = NEO::getTargetConfigsForFatbinary(ConstStringRef(cutMinorAndRevision), oclocArgHelperWithoutInput.get());
    EXPECT_FALSE(got.empty());
}

TEST_F(OclocFatBinaryTest, GivenSpirvInputWhenFatBinaryIsRequestedThenArchiveContainsGenericIrFileWithSpirvContent) {
    const auto devices = prepareTwoDevices(&mockArgHelper);
    if (devices.empty()) {
        GTEST_SKIP();
    }

    const std::vector<std::string> args = {
        "ocloc",
        "-output",
        outputArchiveName,
        "-file",
        spirvFilename,
        "-output_no_suffix",
        "-spirv_input",
        "-device",
        devices};

    const auto buildResult = buildFatBinary(args, &mockArgHelper);
    ASSERT_EQ(OclocErrorCode::SUCCESS, buildResult);
    ASSERT_EQ(1u, mockArgHelper.interceptedFiles.count(outputArchiveName));

    const auto &rawArchive = mockArgHelper.interceptedFiles[outputArchiveName];
    const auto archiveBytes = ArrayRef<const std::uint8_t>::fromAny(rawArchive.data(), rawArchive.size());

    std::string outErrReason{};
    std::string outWarning{};
    const auto decodedArchive = NEO::Ar::decodeAr(archiveBytes, outErrReason, outWarning);

    ASSERT_NE(nullptr, decodedArchive.magic);
    ASSERT_TRUE(outErrReason.empty());
    ASSERT_TRUE(outWarning.empty());

    const auto spirvFileIt = searchInArchiveByFilename(decodedArchive, archiveGenericIrName);
    ASSERT_NE(decodedArchive.files.end(), spirvFileIt);

    const auto elf = Elf::decodeElf<Elf::EI_CLASS_64>(spirvFileIt->fileData, outErrReason, outWarning);
    ASSERT_NE(nullptr, elf.elfFileHeader);
    ASSERT_TRUE(outErrReason.empty());
    ASSERT_TRUE(outWarning.empty());

    const auto isSpirvSection = [](const auto &section) {
        return section.header && section.header->type == Elf::SHT_OPENCL_SPIRV;
    };

    const auto spirvSectionIt = std::find_if(elf.sectionHeaders.begin(), elf.sectionHeaders.end(), isSpirvSection);
    ASSERT_NE(elf.sectionHeaders.end(), spirvSectionIt);

    ASSERT_EQ(spirvFileContent.size() + 1, spirvSectionIt->header->size);
    const auto isSpirvDataEqualsInputFileData = std::memcmp(spirvFileContent.data(), spirvSectionIt->data.begin(), spirvFileContent.size()) == 0;
    EXPECT_TRUE(isSpirvDataEqualsInputFileData);
}

TEST_F(OclocFatBinaryTest, GivenSpirvInputAndExcludeIrFlagWhenFatBinaryIsRequestedThenArchiveDoesNotContainGenericIrFile) {
    const auto devices = prepareTwoDevices(&mockArgHelper);
    if (devices.empty()) {
        GTEST_SKIP();
    }

    const std::vector<std::string> args = {
        "ocloc",
        "-output",
        outputArchiveName,
        "-file",
        spirvFilename,
        "-output_no_suffix",
        "-spirv_input",
        "-exclude_ir",
        "-device",
        devices};

    const auto buildResult = buildFatBinary(args, &mockArgHelper);
    ASSERT_EQ(OclocErrorCode::SUCCESS, buildResult);
    ASSERT_EQ(1u, mockArgHelper.interceptedFiles.count(outputArchiveName));

    const auto &rawArchive = mockArgHelper.interceptedFiles[outputArchiveName];
    const auto archiveBytes = ArrayRef<const std::uint8_t>::fromAny(rawArchive.data(), rawArchive.size());

    std::string outErrReason{};
    std::string outWarning{};
    const auto decodedArchive = NEO::Ar::decodeAr(archiveBytes, outErrReason, outWarning);

    ASSERT_NE(nullptr, decodedArchive.magic);
    ASSERT_TRUE(outErrReason.empty());
    ASSERT_TRUE(outWarning.empty());

    const auto spirvFileIt = searchInArchiveByFilename(decodedArchive, archiveGenericIrName);
    EXPECT_EQ(decodedArchive.files.end(), spirvFileIt);
}

TEST_F(OclocFatBinaryTest, GivenClInputFileWhenFatBinaryIsRequestedThenArchiveDoesNotContainGenericIrFile) {
    const auto devices = prepareTwoDevices(&mockArgHelper);
    if (devices.empty()) {
        GTEST_SKIP();
    }

    const std::string clFilename = "some_kernel.cl";
    mockArgHelperFilesMap[clFilename] = "__kernel void some_kernel(){}";

    const std::vector<std::string> args = {
        "ocloc",
        "-output",
        outputArchiveName,
        "-file",
        clFilename,
        "-output_no_suffix",
        "-device",
        devices};

    const auto buildResult = buildFatBinary(args, &mockArgHelper);
    ASSERT_EQ(OclocErrorCode::SUCCESS, buildResult);
    ASSERT_EQ(1u, mockArgHelper.interceptedFiles.count(outputArchiveName));

    const auto &rawArchive = mockArgHelper.interceptedFiles[outputArchiveName];
    const auto archiveBytes = ArrayRef<const std::uint8_t>::fromAny(rawArchive.data(), rawArchive.size());

    std::string outErrReason{};
    std::string outWarning{};
    const auto decodedArchive = NEO::Ar::decodeAr(archiveBytes, outErrReason, outWarning);

    ASSERT_NE(nullptr, decodedArchive.magic);
    ASSERT_TRUE(outErrReason.empty());
    ASSERT_TRUE(outWarning.empty());

    const auto spirvFileIt = searchInArchiveByFilename(decodedArchive, archiveGenericIrName);
    EXPECT_EQ(decodedArchive.files.end(), spirvFileIt);
}

TEST_F(OclocFatBinaryTest, GivenEmptyFileWhenAppendingGenericIrThenInvalidFileIsReturned) {
    Ar::ArEncoder ar;
    std::string emptyFile{"empty_file.spv"};
    mockArgHelperFilesMap[emptyFile] = "";
    mockArgHelper.shouldLoadDataFromFileReturnZeroSize = true;

    ::testing::internal::CaptureStdout();
    const auto errorCode{appendGenericIr(ar, emptyFile, &mockArgHelper)};
    const auto output{::testing::internal::GetCapturedStdout()};

    EXPECT_EQ(OclocErrorCode::INVALID_FILE, errorCode);
    EXPECT_EQ("Error! Couldn't read input file!\n", output);
}

TEST_F(OclocFatBinaryTest, GivenInvalidIrFileWhenAppendingGenericIrThenInvalidFileIsReturned) {
    Ar::ArEncoder ar;
    std::string dummyFile{"dummy_file.spv"};
    mockArgHelperFilesMap[dummyFile] = "This is not IR!";

    ::testing::internal::CaptureStdout();
    const auto errorCode{appendGenericIr(ar, dummyFile, &mockArgHelper)};
    const auto output{::testing::internal::GetCapturedStdout()};

    EXPECT_EQ(OclocErrorCode::INVALID_FILE, errorCode);

    const auto expectedErrorMessage{"Error! Input file is not in supported generic IR format! "
                                    "Currently supported format is SPIR-V.\n"};
    EXPECT_EQ(expectedErrorMessage, output);
}

TEST(OclocFatBinaryHelpersTest, GivenPreviousCompilationErrorWhenBuildingFatbinaryForTargetThenNothingIsDoneAndErrorIsReturned) {
    const std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        gEnvironment->devicePrefix.c_str()};

    MockOfflineCompiler mockOfflineCompiler{};
    mockOfflineCompiler.initialize(argv.size(), argv);

    // We expect that nothing is done and error is returned.
    // Therefore, if offline compiler is used, ensure that it just returns error code,
    // which is different than expected one.
    mockOfflineCompiler.buildReturnValue = OclocErrorCode::SUCCESS;

    Ar::ArEncoder ar;
    const std::string pointerSize{"32"};
    const auto mockArgHelper = mockOfflineCompiler.uniqueHelper.get();
    const auto deviceConfig = getDeviceConfig(mockOfflineCompiler);

    const int previousReturnValue{OclocErrorCode::INVALID_FILE};
    const auto buildResult = buildFatBinaryForTarget(previousReturnValue, argv, pointerSize, ar, &mockOfflineCompiler, mockArgHelper, deviceConfig);

    EXPECT_EQ(OclocErrorCode::INVALID_FILE, buildResult);
    EXPECT_EQ(0, mockOfflineCompiler.buildCalledCount);
}

TEST(OclocFatBinaryHelpersTest, GivenPreviousCompilationSuccessAndFailingBuildWhenBuildingFatbinaryForTargetThenCompilationIsInvokedAndErrorLogIsPrinted) {
    const std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        gEnvironment->devicePrefix.c_str()};

    MockOfflineCompiler mockOfflineCompiler{};
    mockOfflineCompiler.initialize(argv.size(), argv);

    mockOfflineCompiler.buildReturnValue = OclocErrorCode::INVALID_FILE;

    Ar::ArEncoder ar;
    const std::string pointerSize{"32"};
    const auto mockArgHelper = mockOfflineCompiler.uniqueHelper.get();
    const auto deviceConfig = getDeviceConfig(mockOfflineCompiler);

    ::testing::internal::CaptureStdout();
    const int previousReturnValue{OclocErrorCode::SUCCESS};
    const auto buildResult = buildFatBinaryForTarget(previousReturnValue, argv, pointerSize, ar, &mockOfflineCompiler, mockArgHelper, deviceConfig);
    const auto output{::testing::internal::GetCapturedStdout()};

    EXPECT_EQ(OclocErrorCode::INVALID_FILE, buildResult);
    EXPECT_EQ(1, mockOfflineCompiler.buildCalledCount);

    std::string commandString{};
    for (const auto &arg : argv) {
        commandString += " ";
        commandString += arg;
    }

    const std::string expectedOutput{
        "Build failed for : " + deviceConfig + " with error code: -5151\n"
                                               "Command was:" +
        commandString + "\n"};
    EXPECT_EQ(expectedOutput, output);
}

TEST(OclocFatBinaryHelpersTest, GivenNonEmptyBuildLogWhenBuildingFatbinaryForTargetThenBuildLogIsPrinted) {
    using namespace std::string_literals;

    const std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-device",
        gEnvironment->devicePrefix.c_str()};

    MockOfflineCompiler mockOfflineCompiler{};
    mockOfflineCompiler.initialize(argv.size(), argv);

    const char buildWarning[] = "Warning: This is a build log!";
    mockOfflineCompiler.updateBuildLog(buildWarning, sizeof(buildWarning));
    mockOfflineCompiler.buildReturnValue = OclocErrorCode::SUCCESS;

    // Dummy value
    mockOfflineCompiler.elfBinary = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

    Ar::ArEncoder ar;
    const std::string pointerSize{"32"};
    const auto mockArgHelper = mockOfflineCompiler.uniqueHelper.get();
    const auto deviceConfig = getDeviceConfig(mockOfflineCompiler);

    ::testing::internal::CaptureStdout();
    const int previousReturnValue{OclocErrorCode::SUCCESS};
    const auto buildResult = buildFatBinaryForTarget(previousReturnValue, argv, pointerSize, ar, &mockOfflineCompiler, mockArgHelper, deviceConfig);
    const auto output{::testing::internal::GetCapturedStdout()};

    EXPECT_EQ(OclocErrorCode::SUCCESS, buildResult);
    EXPECT_EQ(1, mockOfflineCompiler.buildCalledCount);

    const std::string expectedOutput{buildWarning + "\nBuild succeeded for : "s + deviceConfig + ".\n"s};
    EXPECT_EQ(expectedOutput, output);
}

TEST(OclocFatBinaryHelpersTest, GivenQuietModeWhenBuildingFatbinaryForTargetThenNothingIsPrinted) {
    using namespace std::string_literals;

    const std::vector<std::string> argv = {
        "ocloc",
        "-file",
        "test_files/copybuffer.cl",
        "-q",
        "-device",
        gEnvironment->devicePrefix.c_str()};

    MockOfflineCompiler mockOfflineCompiler{};
    mockOfflineCompiler.initialize(argv.size(), argv);

    // Dummy value
    mockOfflineCompiler.elfBinary = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    mockOfflineCompiler.buildReturnValue = OclocErrorCode::SUCCESS;

    Ar::ArEncoder ar;
    const std::string pointerSize{"32"};
    const auto mockArgHelper = mockOfflineCompiler.uniqueHelper.get();
    const auto deviceConfig = getDeviceConfig(mockOfflineCompiler);

    ::testing::internal::CaptureStdout();
    const int previousReturnValue{OclocErrorCode::SUCCESS};
    const auto buildResult = buildFatBinaryForTarget(previousReturnValue, argv, pointerSize, ar, &mockOfflineCompiler, mockArgHelper, deviceConfig);
    const auto output{::testing::internal::GetCapturedStdout()};

    EXPECT_EQ(OclocErrorCode::SUCCESS, buildResult);
    EXPECT_EQ(1, mockOfflineCompiler.buildCalledCount);

    EXPECT_TRUE(output.empty()) << output;
}

} // namespace NEO