/* * Copyright (C) 2018-2021 Intel Corporation * * SPDX-License-Identifier: MIT * */ #include "shared/source/helpers/aligned_memory.h" #include "shared/source/helpers/string.h" #include "shared/source/program/print_formatter.h" #include "shared/test/common/mocks/mock_device.h" #include "shared/test/common/mocks/mock_graphics_allocation.h" #include "opencl/test/unit_test/mocks/mock_cl_device.h" #include "opencl/test/unit_test/mocks/mock_kernel.h" #include "opencl/test/unit_test/mocks/mock_program.h" #include "gtest/gtest.h" #include using namespace NEO; using namespace iOpenCL; // -------------------- Base Fixture ------------------------ class PrintFormatterTest : public testing::Test { public: std::unique_ptr printFormatter; static const size_t maxPrintfOutputLength = 4096; static const size_t printfBufferSize = 1024; std::string format; uint8_t buffer; MockGraphicsAllocation *data; MockKernel *kernel; std::unique_ptr program; std::unique_ptr kernelInfo; ClDevice *device; uint8_t underlyingBuffer[maxPrintfOutputLength]; uint32_t offset; int maxStringIndex; protected: void SetUp() override { offset = 4; maxStringIndex = 0; data = new MockGraphicsAllocation(underlyingBuffer, maxPrintfOutputLength); kernelInfo = std::make_unique(); device = new MockClDevice{MockDevice::createWithNewExecutionEnvironment(nullptr)}; program = std::make_unique(toClDeviceVector(*device)); kernel = new MockKernel(program.get(), *kernelInfo, *device); printFormatter = std::unique_ptr(new PrintFormatter(static_cast(data->getUnderlyingBuffer()), printfBufferSize, is32bit, &kernelInfo->kernelDescriptor.kernelMetadata.printfStringsMap)); underlyingBuffer[0] = 0; underlyingBuffer[1] = 0; underlyingBuffer[2] = 0; underlyingBuffer[3] = 0; } void TearDown() override { delete data; delete kernel; delete device; } enum class PRINTF_DATA_TYPE : int { INVALID, BYTE, SHORT, INT, FLOAT, STRING, LONG, POINTER, DOUBLE, VECTOR_BYTE, VECTOR_SHORT, VECTOR_INT, VECTOR_LONG, VECTOR_FLOAT, VECTOR_DOUBLE }; PRINTF_DATA_TYPE getPrintfDataType(int8_t value) { return PRINTF_DATA_TYPE::BYTE; }; PRINTF_DATA_TYPE getPrintfDataType(uint8_t value) { return PRINTF_DATA_TYPE::BYTE; }; PRINTF_DATA_TYPE getPrintfDataType(int16_t value) { return PRINTF_DATA_TYPE::SHORT; }; PRINTF_DATA_TYPE getPrintfDataType(uint16_t value) { return PRINTF_DATA_TYPE::SHORT; }; PRINTF_DATA_TYPE getPrintfDataType(int32_t value) { return PRINTF_DATA_TYPE::INT; }; PRINTF_DATA_TYPE getPrintfDataType(uint32_t value) { return PRINTF_DATA_TYPE::INT; }; PRINTF_DATA_TYPE getPrintfDataType(int64_t value) { return PRINTF_DATA_TYPE::LONG; }; PRINTF_DATA_TYPE getPrintfDataType(uint64_t value) { return PRINTF_DATA_TYPE::LONG; }; PRINTF_DATA_TYPE getPrintfDataType(float value) { return PRINTF_DATA_TYPE::FLOAT; }; PRINTF_DATA_TYPE getPrintfDataType(double value) { return PRINTF_DATA_TYPE::DOUBLE; }; PRINTF_DATA_TYPE getPrintfDataType(char *value) { return PRINTF_DATA_TYPE::STRING; }; template void injectValue(T value) { storeData(getPrintfDataType(value)); storeData(value); } void injectStringValue(int value) { storeData(PRINTF_DATA_TYPE::STRING); storeData(value); } template void storeData(T value) { T *valuePointer = reinterpret_cast(underlyingBuffer + offset); if (isAligned(valuePointer)) *valuePointer = value; else { memcpy_s(valuePointer, sizeof(underlyingBuffer) - offset, &value, sizeof(T)); } offset += sizeof(T); // first four bytes always store the size uint32_t *pointer = reinterpret_cast(underlyingBuffer); *pointer = offset; } int injectFormatString(std::string str) { auto index = maxStringIndex++; kernelInfo->addToPrintfStringsMap(index, str); return index; } }; // for tests printing a single value template struct SingleValueTestParam { std::string format; T value; }; typedef SingleValueTestParam Int8Params; typedef SingleValueTestParam Uint8Params; typedef SingleValueTestParam Int16Params; typedef SingleValueTestParam Uint16Params; typedef SingleValueTestParam Int32Params; typedef SingleValueTestParam Uint32Params; typedef SingleValueTestParam Int64Params; typedef SingleValueTestParam Uint64Params; typedef SingleValueTestParam FloatParams; typedef SingleValueTestParam DoubleParams; typedef SingleValueTestParam StringParams; Int8Params byteValues[] = { {"%c", 'a'}, }; class PrintfInt8Test : public PrintFormatterTest, public ::testing::WithParamInterface {}; TEST_P(PrintfInt8Test, GivenFormatContainingIntWhenPrintingThenValueIsInserted) { auto input = GetParam(); auto stringIndex = injectFormatString(input.format); storeData(stringIndex); injectValue(input.value); char referenceOutput[maxPrintfOutputLength]; char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); snprintf(referenceOutput, sizeof(referenceOutput), input.format.c_str(), input.value); EXPECT_STREQ(referenceOutput, actualOutput); } INSTANTIATE_TEST_CASE_P(PrintfInt8Test, PrintfInt8Test, ::testing::ValuesIn(byteValues)); Int32Params intValues[] = { {"%d", 0}, {"%d", 1}, {"%d", -1}, {"%d", INT32_MAX}, {"%d", INT32_MIN}, {"%5d", 10}, {"%-5d", 10}, {"%05d", 10}, {"%+5d", 10}, {"%-+5d", 10}, {"%.5i", 100}, {"%6.5i", 100}, {"%-06i", 100}, {"%06.5i", 100}}; class PrintfInt32Test : public PrintFormatterTest, public ::testing::WithParamInterface {}; TEST_P(PrintfInt32Test, GivenFormatContainingIntWhenPrintingThenValueIsInserted) { auto input = GetParam(); auto stringIndex = injectFormatString(input.format); storeData(stringIndex); injectValue(input.value); char referenceOutput[maxPrintfOutputLength]; char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); snprintf(referenceOutput, sizeof(referenceOutput), input.format.c_str(), input.value); EXPECT_STREQ(referenceOutput, actualOutput); } INSTANTIATE_TEST_CASE_P(PrintfInt32Test, PrintfInt32Test, ::testing::ValuesIn(intValues)); Uint32Params uintValues[] = { {"%u", 0}, {"%u", 1}, {"%u", UINT32_MAX}, {"%.0u", 0}, // octal {"%o", 10}, {"%.5o", 10}, {"%#o", 100000000}, {"%04.5o", 10}, // hexadecimal {"%#x", 0xABCDEF}, {"%#X", 0xABCDEF}, {"%#X", 0}, {"%8x", 399}, {"%04x", 399}}; class PrintfUint32Test : public PrintFormatterTest, public ::testing::WithParamInterface {}; TEST_P(PrintfUint32Test, GivenFormatContainingUintWhenPrintingThenValueIsInserted) { auto input = GetParam(); auto stringIndex = injectFormatString(input.format); storeData(stringIndex); injectValue(input.value); char referenceOutput[maxPrintfOutputLength]; char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); snprintf(referenceOutput, sizeof(referenceOutput), input.format.c_str(), input.value); EXPECT_STREQ(referenceOutput, actualOutput); } TEST_P(PrintfUint32Test, GivenBufferSizeGreaterThanPrintBufferWhenPrintingThenBufferIsTrimmed) { auto input = GetParam(); printFormatter = std::unique_ptr(new PrintFormatter(static_cast(data->getUnderlyingBuffer()), 0, is32bit, &kernelInfo->kernelDescriptor.kernelMetadata.printfStringsMap)); auto stringIndex = injectFormatString(input.format); storeData(stringIndex); injectValue(input.value); char referenceOutput[maxPrintfOutputLength] = ""; char actualOutput[maxPrintfOutputLength] = ""; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ(referenceOutput, actualOutput); } INSTANTIATE_TEST_CASE_P(PrintfUint32Test, PrintfUint32Test, ::testing::ValuesIn(uintValues)); FloatParams floatValues[] = { {"%f", 10.3456f}, {"%.1f", 10.3456f}, {"%.2f", 10.3456f}, {"%8.3f", 10.3456f}, {"%08.2f", 10.3456f}, {"%-8.2f", 10.3456f}, {"%+8.2f", -10.3456f}, {"%.0f", 0.1f}, {"%.0f", 0.6f}, {"%0f", 0.6f}, }; class PrintfFloatTest : public PrintFormatterTest, public ::testing::WithParamInterface {}; TEST_P(PrintfFloatTest, GivenFormatContainingFloatWhenPrintingThenValueIsInserted) { auto input = GetParam(); auto stringIndex = injectFormatString(input.format); storeData(stringIndex); injectValue(input.value); char referenceOutput[maxPrintfOutputLength]; char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); snprintf(referenceOutput, sizeof(referenceOutput), input.format.c_str(), input.value); EXPECT_STREQ(referenceOutput, actualOutput); } INSTANTIATE_TEST_CASE_P(PrintfFloatTest, PrintfFloatTest, ::testing::ValuesIn(floatValues)); class PrintfDoubleToFloatTest : public PrintFormatterTest, public ::testing::WithParamInterface {}; DoubleParams doubleToFloatValues[] = { {"%f", 10.3456}, {"%.1f", 10.3456}, {"%.2f", 10.3456}, {"%8.3f", 10.3456}, {"%08.2f", 10.3456}, {"%-8.2f", 10.3456}, {"%+8.2f", -10.3456}, {"%.0f", 0.1}, {"%0f", 0.6}, {"%4g", 12345.6789}, {"%4.2g", 12345.6789}, {"%4G", 0.0000023}, {"%4G", 0.023}, {"%-#20.15e", 19456120.0}, {"%+#21.15E", 19456120.0}, {"%.6a", 0.1}, {"%10.2a", 9990.235}}; TEST_P(PrintfDoubleToFloatTest, GivenFormatContainingFloatAndDoubleWhenPrintingThenValueIsInserted) { auto input = GetParam(); auto stringIndex = injectFormatString(input.format); storeData(stringIndex); injectValue(input.value); char referenceOutput[maxPrintfOutputLength]; char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); snprintf(referenceOutput, sizeof(referenceOutput), input.format.c_str(), input.value); EXPECT_STREQ(referenceOutput, actualOutput); } INSTANTIATE_TEST_CASE_P(PrintfDoubleToFloatTest, PrintfDoubleToFloatTest, ::testing::ValuesIn(doubleToFloatValues)); DoubleParams doubleValues[] = { {"%f", 302230.12156260}, {"%+f", 2937289102.1562}, {"% #F", (double)-1254}, {"%6.2f", 0.1562}, {"%06.2f", -0.1562}, {"%e", 0.1562}, {"%E", -1254.0001001}, {"%+.10e", 0.1562000102241}, {"% E", (double)1254}, {"%10.2e", 100230.1562}, {"%g", 74010.00001562}, {"%G", -1254.0001001}, {"%+g", 325001.00001562}, {"%+#G", -1254.0001001}, {"%8.2g", 19.844}, {"%1.5G", -1.1}, {"%.13a", 1890.00001562}, {"%.13A", -1254.0001001}, }; class PrintfDoubleTest : public PrintFormatterTest, public ::testing::WithParamInterface {}; TEST_P(PrintfDoubleTest, GivenFormatContainingDoubleWhenPrintingThenValueIsInserted) { auto input = GetParam(); auto stringIndex = injectFormatString(input.format); storeData(stringIndex); injectValue(input.value); char referenceOutput[maxPrintfOutputLength]; char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); if (input.format[input.format.length() - 1] == 'F') input.format[input.format.length() - 1] = 'f'; snprintf(referenceOutput, sizeof(referenceOutput), input.format.c_str(), input.value); EXPECT_STREQ(referenceOutput, actualOutput); } INSTANTIATE_TEST_CASE_P(PrintfDoubleTest, PrintfDoubleTest, ::testing::ValuesIn(doubleValues)); std::pair specialValues[] = { {"%%", "%"}, {"nothing%", "nothing"}, }; class PrintfSpecialTest : public PrintFormatterTest, public ::testing::WithParamInterface> {}; TEST_P(PrintfSpecialTest, GivenFormatContainingDoublePercentageWhenPrintingThenValueIsInsertedCorrectly) { auto input = GetParam(); auto stringIndex = injectFormatString(input.first); storeData(stringIndex); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ(input.second.c_str(), actualOutput); } INSTANTIATE_TEST_CASE_P(PrintfSpecialTest, PrintfSpecialTest, ::testing::ValuesIn(specialValues)); // ------------------------- Testing Strings only with no Formatting ------------------------ class PrintfNoArgumentsTest : public PrintFormatterTest, public ::testing::WithParamInterface> {}; // escape/non-escaped strings are specified manually to avoid converting them in code // automatic code would have to do the same thing it is testing and therefore would be prone to mistakes // this is needed because compiler doesn't escape the format strings and provides them exactly as they were typed in kernel source std::pair stringValues[] = { {R"(test)", "test"}, {R"(test\n)", "test\n"}, }; TEST_P(PrintfNoArgumentsTest, GivenNoArgumentsWhenPrintingThenCharsAreEscaped) { auto input = GetParam(); auto stringIndex = injectFormatString(input.first); storeData(stringIndex); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ(input.second.c_str(), actualOutput); } INSTANTIATE_TEST_CASE_P(PrintfNoArgumentsTest, PrintfNoArgumentsTest, ::testing::ValuesIn(stringValues)); StringParams stringValues2[] = { {"%s", "foo"}, }; class PrintfStringTest : public PrintFormatterTest, public ::testing::WithParamInterface {}; TEST_P(PrintfStringTest, GivenFormatContainingStringWhenPrintingThenValueIsInserted) { auto input = GetParam(); auto stringIndex = injectFormatString(input.format); storeData(stringIndex); auto inputIndex = injectFormatString(input.value); injectStringValue(inputIndex); char referenceOutput[maxPrintfOutputLength]; char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); snprintf(referenceOutput, sizeof(referenceOutput), input.format.c_str(), input.value.c_str()); EXPECT_STREQ(input.value.c_str(), actualOutput); } INSTANTIATE_TEST_CASE_P(PrintfStringTest, PrintfStringTest, ::testing::ValuesIn(stringValues2)); TEST_F(PrintFormatterTest, GivenLongStringValueWhenPrintedThenFullStringIsPrinted) { char testedLongString[maxPrintfOutputLength]; memset(testedLongString, 'a', maxPrintfOutputLength - 1); testedLongString[maxPrintfOutputLength - 1] = '\0'; auto stringIndex = injectFormatString(testedLongString); storeData(stringIndex); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ(testedLongString, actualOutput); } TEST_F(PrintFormatterTest, GivenStringSpecifierWhenLongStringIsPassedAsValueThenFullStringIsPrinted) { char testedLongString[maxPrintfOutputLength]; memset(testedLongString, 'a', maxPrintfOutputLength - 5); testedLongString[maxPrintfOutputLength - 5] = '\0'; auto stringIndex = injectFormatString("%s"); storeData(stringIndex); auto inputIndex = injectFormatString(testedLongString); injectStringValue(inputIndex); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ(testedLongString, actualOutput); } TEST_F(PrintFormatterTest, GivenTooLongStringWhenPrintedThenOutputIsTruncated) { std::unique_ptr testedLongString(new char[PrintFormatter::maxSinglePrintStringLength + 1024]); memset(testedLongString.get(), 'a', PrintFormatter::maxSinglePrintStringLength + 1024 - 1); testedLongString[PrintFormatter::maxSinglePrintStringLength + 1024 - 1] = '\0'; auto stringIndex = injectFormatString(testedLongString.get()); storeData(stringIndex); std::unique_ptr actualOutput(new char[PrintFormatter::maxSinglePrintStringLength + 1024]); printFormatter->printKernelOutput([&actualOutput](char *str) { size_t length = strnlen_s(str, PrintFormatter::maxSinglePrintStringLength + 1024); strncpy_s(actualOutput.get(), PrintFormatter::maxSinglePrintStringLength + 1024, str, length); }); auto testedLength = strnlen_s(testedLongString.get(), PrintFormatter::maxSinglePrintStringLength + 1024); auto actualLength = strnlen_s(actualOutput.get(), PrintFormatter::maxSinglePrintStringLength + 1024); EXPECT_GT(testedLength, actualLength); } TEST_F(PrintFormatterTest, GivenNullTokenWhenPrintingThenNullIsInserted) { auto stringIndex = injectFormatString("%s"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_INT); storeData(0); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("(null)", actualOutput); } // ----------------------- Vector channel count --------------------------------- TEST_F(PrintFormatterTest, GivenVector2WhenPrintingThenAllValuesAreInserted) { int channelCount = 2; auto stringIndex = injectFormatString("%v2d"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_INT); // channel count storeData(channelCount); // channel values for (int i = 0; i < channelCount; i++) storeData(i + 1); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("1,2", actualOutput); } TEST_F(PrintFormatterTest, GivenVector4WhenPrintingThenAllValuesAreInserted) { int channelCount = 4; auto stringIndex = injectFormatString("%v4d"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_INT); // channel count storeData(channelCount); // channel values for (int i = 0; i < channelCount; i++) storeData(i + 1); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("1,2,3,4", actualOutput); } TEST_F(PrintFormatterTest, GivenVector8WhenPrintingThenAllValuesAreInserted) { int channelCount = 8; auto stringIndex = injectFormatString("%v8d"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_INT); // channel count storeData(channelCount); // channel values for (int i = 0; i < channelCount; i++) storeData(i + 1); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("1,2,3,4,5,6,7,8", actualOutput); } TEST_F(PrintFormatterTest, GivenVector16WhenPrintingThenAllValuesAreInserted) { int channelCount = 16; auto stringIndex = injectFormatString("%v16d"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_INT); // channel count storeData(channelCount); // channel values for (int i = 0; i < channelCount; i++) storeData(i + 1); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16", actualOutput); } // ------------------- vector types ---------------------------- TEST_F(PrintFormatterTest, GivenVectorOfBytesWhenPrintingThenAllValuesAreInserted) { int channelCount = 2; auto stringIndex = injectFormatString("%v2hhd"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_BYTE); // channel count storeData(channelCount); storeData(1); storeData(2); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("1,2", actualOutput); } TEST_F(PrintFormatterTest, GivenVectorOfShortsWhenPrintingThenAllValuesAreInserted) { int channelCount = 2; auto stringIndex = injectFormatString("%v2hd"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_SHORT); // channel count storeData(channelCount); storeData(1); storeData(2); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("1,2", actualOutput); } TEST_F(PrintFormatterTest, GivenVectorOfIntsWhenPrintingThenAllValuesAreInserted) { int channelCount = 2; auto stringIndex = injectFormatString("%v2d"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_INT); // channel count storeData(channelCount); storeData(1); storeData(2); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("1,2", actualOutput); } TEST_F(PrintFormatterTest, GivenSpecialVectorWhenPrintingThenAllValuesAreInserted) { int channelCount = 2; auto stringIndex = injectFormatString("%v2hld"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_INT); // channel count storeData(channelCount); storeData(1); storeData(2); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("1,2", actualOutput); } TEST_F(PrintFormatterTest, GivenVectorOfLongsWhenPrintingThenAllValuesAreInserted) { int channelCount = 2; auto stringIndex = injectFormatString("%v2lld"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_LONG); // channel count storeData(channelCount); storeData(1); storeData(2); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("1,2", actualOutput); } TEST_F(PrintFormatterTest, GivenVectorOfFloatsWhenPrintingThenAllValuesAreInserted) { int channelCount = 2; auto stringIndex = injectFormatString("%v2f"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_FLOAT); // channel count storeData(channelCount); storeData(1.0); storeData(2.0); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("1.000000,2.000000", actualOutput); } TEST_F(PrintFormatterTest, GivenVectorOfDoublesWhenPrintingThenAllValuesAreInserted) { int channelCount = 2; auto stringIndex = injectFormatString("%v2f"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_DOUBLE); // channel count storeData(channelCount); storeData(1.0); storeData(2.0); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("1.000000,2.000000", actualOutput); } TEST_F(PrintFormatterTest, GivenPointerWhenPrintingThenValueIsInserted) { auto stringIndex = injectFormatString("%p"); storeData(stringIndex); int temp; storeData(PRINTF_DATA_TYPE::POINTER); // channel count storeData(reinterpret_cast(&temp)); // on 32bit configurations add extra 4 bytes when storing pointers, IGC always stores pointers on 8 bytes if (is32bit) { uint32_t padding = 0; storeData(padding); } char actualOutput[maxPrintfOutputLength]; char referenceOutput[maxPrintfOutputLength]; snprintf(referenceOutput, sizeof(referenceOutput), "%p", reinterpret_cast(&temp)); printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ(referenceOutput, actualOutput); } TEST_F(PrintFormatterTest, GivenPointerWith32BitKernelWhenPrintingThen32BitPointerIsPrinted) { printFormatter.reset(new PrintFormatter(static_cast(data->getUnderlyingBuffer()), printfBufferSize, true, &kernelInfo->kernelDescriptor.kernelMetadata.printfStringsMap)); auto stringIndex = injectFormatString("%p"); storeData(stringIndex); kernelInfo->kernelDescriptor.kernelAttributes.gpuPointerSize = 4; storeData(PRINTF_DATA_TYPE::POINTER); // store pointer uint32_t addressValue = 0; storeData(addressValue); void *pointer = nullptr; // store non zero padding uint32_t padding = 0xdeadbeef; storeData(padding); char actualOutput[maxPrintfOutputLength]; char referenceOutput[maxPrintfOutputLength]; snprintf(referenceOutput, sizeof(referenceOutput), "%p", pointer); printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ(referenceOutput, actualOutput); } TEST_F(PrintFormatterTest, Given2ByteVectorsWhenPrintingThenDataBufferParsedProperly) { int channelCount = 4; auto stringIndex = injectFormatString("%v4hhd %v4hhd"); storeData(stringIndex); storeData(PRINTF_DATA_TYPE::VECTOR_BYTE); // channel count storeData(channelCount); // channel values for (int i = 0; i < channelCount; i++) storeData(static_cast(i + 1)); // filler, should not be printed for (int i = 0; i < 12; i++) storeData(static_cast(0)); storeData(PRINTF_DATA_TYPE::VECTOR_BYTE); // channel count storeData(channelCount); // channel values for (int i = 0; i < channelCount; i++) storeData(static_cast(i + 1)); // filler, should not be printed for (int i = 0; i < 12; i++) storeData(static_cast(0)); char actualOutput[maxPrintfOutputLength]; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("1,2,3,4 1,2,3,4", actualOutput); } TEST_F(PrintFormatterTest, GivenEmptyBufferWhenPrintingThenFailSafely) { char actualOutput[maxPrintfOutputLength]; actualOutput[0] = 0; printFormatter->printKernelOutput([&actualOutput](char *str) { strncpy_s(actualOutput, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ("", actualOutput); } TEST_F(PrintFormatterTest, GivenNoStringMapAndBufferWithFormatStringThenItIsPrintedProperly) { printFormatter.reset(new PrintFormatter(static_cast(data->getUnderlyingBuffer()), printfBufferSize, true)); const char *formatString = "test string"; storeData(formatString); char output[maxPrintfOutputLength]; printFormatter->printKernelOutput([&output](char *str) { strncpy_s(output, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ(formatString, output); } TEST_F(PrintFormatterTest, GivenNoStringMapAndBufferWithFormatStringAnd2StringsThenDataIsParsedAndPrintedProperly) { printFormatter.reset(new PrintFormatter(static_cast(data->getUnderlyingBuffer()), printfBufferSize, true)); const char *formatString = "%s %s"; storeData(formatString); const char *string1 = "str1"; storeData(PRINTF_DATA_TYPE::STRING); storeData(string1); const char *string2 = "str2"; storeData(PRINTF_DATA_TYPE::STRING); storeData(string2); const char *expectedOutput = "str1 str2"; char output[maxPrintfOutputLength]; printFormatter->printKernelOutput([&output](char *str) { strncpy_s(output, maxPrintfOutputLength, str, maxPrintfOutputLength); }); EXPECT_STREQ(expectedOutput, output); } TEST(printToSTDOUTTest, GivenStringWhenPrintingToStdoutThenOutputOccurs) { testing::internal::CaptureStdout(); printToSTDOUT("test"); std::string output = testing::internal::GetCapturedStdout(); EXPECT_STREQ("test", output.c_str()); } TEST(simpleSprintf, GivenEmptyFormatStringWhenSimpleSprintfIsCalledThenBailOutWith0) { char out[1024] = {7, 0}; auto ret = simple_sprintf(out, sizeof(out), "", 3.0f); EXPECT_EQ(0U, ret); EXPECT_EQ(0, out[0]); EXPECT_EQ(0, out[1]); }