267 lines · plain
1//===------- Offload API tests - gtest fixtures --==-----------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include <OffloadAPI.h>10#include <OffloadPrint.hpp>11#include <gtest/gtest.h>12#include <thread>13 14#include "Environment.hpp"15 16#pragma once17 18#ifndef ASSERT_SUCCESS19#define ASSERT_SUCCESS(ACTUAL) \20 do { \21 ol_result_t Res = ACTUAL; \22 if (Res && Res->Code != OL_ERRC_SUCCESS) { \23 GTEST_FAIL() << #ACTUAL " returned " << Res->Code << ": " \24 << Res->Details; \25 } \26 } while (0)27#endif28 29#ifndef ASSERT_SUCCESS_OR_UNSUPPORTED30#define ASSERT_SUCCESS_OR_UNSUPPORTED(ACTUAL) \31 do { \32 ol_result_t Res = ACTUAL; \33 if (Res && Res->Code == OL_ERRC_UNSUPPORTED) { \34 GTEST_SKIP() << #ACTUAL " returned unsupported; skipping test"; \35 return; \36 } else if (Res && Res->Code != OL_ERRC_SUCCESS) { \37 GTEST_FAIL() << #ACTUAL " returned " << Res->Code << ": " \38 << Res->Details; \39 } \40 } while (0)41#endif42 43#ifndef ASSERT_ERROR44#define ASSERT_ERROR(EXPECTED, ACTUAL) \45 do { \46 ol_result_t Res = ACTUAL; \47 if (!Res) \48 GTEST_FAIL() << #ACTUAL " succeeded when we expected it to fail"; \49 if (Res->Code != EXPECTED) \50 GTEST_FAIL() << #ACTUAL " was expected to return " \51 << #EXPECTED " but instead returned " << Res->Code << ": " \52 << Res->Details; \53 } while (0)54#endif55 56#ifndef ASSERT_ANY_ERROR57#define ASSERT_ANY_ERROR(ACTUAL) \58 do { \59 ol_result_t Res = ACTUAL; \60 ASSERT_TRUE(Res); \61 } while (0)62#endif63 64#define RETURN_ON_FATAL_FAILURE(...) \65 __VA_ARGS__; \66 if (this->HasFatalFailure() || this->IsSkipped()) { \67 return; \68 } \69 (void)070 71inline std::string SanitizeString(const std::string &Str) {72 auto NewStr = Str;73 std::replace_if(74 NewStr.begin(), NewStr.end(), [](char C) { return !std::isalnum(C); },75 '_');76 return NewStr;77}78 79template <typename Fn> inline void threadify(Fn body) {80 std::vector<std::thread> Threads;81 for (size_t I = 0; I < 20; I++) {82 Threads.emplace_back(83 [&body](size_t I) {84 std::string ScopeMsg{"Thread #"};85 ScopeMsg.append(std::to_string(I));86 SCOPED_TRACE(ScopeMsg);87 body(I);88 },89 I);90 }91 for (auto &T : Threads) {92 T.join();93 }94}95 96/// Enqueues a task to the queue that can be manually resolved.97// It will block until `trigger` is called.98struct ManuallyTriggeredTask {99 std::mutex M;100 std::condition_variable CV;101 bool Flag = false;102 ol_event_handle_t CompleteEvent;103 104 ol_result_t enqueue(ol_queue_handle_t Queue) {105 if (auto Err = olLaunchHostFunction(106 Queue,107 [](void *That) {108 static_cast<ManuallyTriggeredTask *>(That)->wait();109 },110 this))111 return Err;112 113 return olCreateEvent(Queue, &CompleteEvent);114 }115 116 void wait() {117 std::unique_lock<std::mutex> lk(M);118 CV.wait_for(lk, std::chrono::milliseconds(1000), [&] { return Flag; });119 EXPECT_TRUE(Flag);120 }121 122 ol_result_t trigger() {123 Flag = true;124 CV.notify_one();125 126 return olSyncEvent(CompleteEvent);127 }128};129 130struct OffloadTest : ::testing::Test {131 ol_device_handle_t Host = TestEnvironment::getHostDevice();132};133 134struct OffloadDeviceTest135 : OffloadTest,136 ::testing::WithParamInterface<TestEnvironment::Device> {137 void SetUp() override {138 RETURN_ON_FATAL_FAILURE(OffloadTest::SetUp());139 140 auto DeviceParam = GetParam();141 Device = DeviceParam.Handle;142 if (Device == nullptr)143 GTEST_SKIP() << "No available devices.";144 }145 146 ol_platform_backend_t getPlatformBackend() const {147 ol_platform_handle_t Platform = nullptr;148 if (olGetDeviceInfo(Device, OL_DEVICE_INFO_PLATFORM,149 sizeof(ol_platform_handle_t), &Platform))150 return OL_PLATFORM_BACKEND_UNKNOWN;151 ol_platform_backend_t Backend;152 if (olGetPlatformInfo(Platform, OL_PLATFORM_INFO_BACKEND,153 sizeof(ol_platform_backend_t), &Backend))154 return OL_PLATFORM_BACKEND_UNKNOWN;155 return Backend;156 }157 158 ol_device_handle_t Device = nullptr;159};160 161struct OffloadPlatformTest : OffloadDeviceTest {162 void SetUp() override {163 RETURN_ON_FATAL_FAILURE(OffloadDeviceTest::SetUp());164 165 ASSERT_SUCCESS(olGetDeviceInfo(Device, OL_DEVICE_INFO_PLATFORM,166 sizeof(Platform), &Platform));167 ASSERT_NE(Platform, nullptr);168 }169 170 ol_platform_handle_t Platform = nullptr;171};172 173// Fixture for a generic program test. If you want a different program, use174// offloadQueueTest and create your own program handle with the binary you want.175struct OffloadProgramTest : OffloadDeviceTest {176 void SetUp() override { SetUpWith("foo"); }177 178 void SetUpWith(const char *ProgramName) {179 RETURN_ON_FATAL_FAILURE(OffloadDeviceTest::SetUp());180 ASSERT_TRUE(181 TestEnvironment::loadDeviceBinary(ProgramName, Device, DeviceBin));182 ASSERT_GE(DeviceBin->getBufferSize(), 0lu);183 ASSERT_SUCCESS(olCreateProgram(Device, DeviceBin->getBufferStart(),184 DeviceBin->getBufferSize(), &Program));185 }186 187 void TearDown() override {188 if (Program) {189 olDestroyProgram(Program);190 }191 RETURN_ON_FATAL_FAILURE(OffloadDeviceTest::TearDown());192 }193 194 ol_program_handle_t Program = nullptr;195 std::unique_ptr<llvm::MemoryBuffer> DeviceBin;196};197 198struct OffloadKernelTest : OffloadProgramTest {199 void SetUp() override {200 RETURN_ON_FATAL_FAILURE(OffloadProgramTest::SetUp());201 ASSERT_SUCCESS(olGetSymbol(Program, "foo", OL_SYMBOL_KIND_KERNEL, &Kernel));202 }203 204 void TearDown() override {205 RETURN_ON_FATAL_FAILURE(OffloadProgramTest::TearDown());206 }207 208 ol_symbol_handle_t Kernel = nullptr;209};210 211struct OffloadGlobalTest : OffloadProgramTest {212 void SetUp() override {213 RETURN_ON_FATAL_FAILURE(OffloadProgramTest::SetUpWith("global"));214 ASSERT_SUCCESS(olGetSymbol(Program, "global",215 OL_SYMBOL_KIND_GLOBAL_VARIABLE, &Global));216 }217 218 void TearDown() override {219 RETURN_ON_FATAL_FAILURE(OffloadProgramTest::TearDown());220 }221 222 ol_symbol_handle_t Global = nullptr;223};224 225struct OffloadQueueTest : OffloadDeviceTest {226 void SetUp() override {227 RETURN_ON_FATAL_FAILURE(OffloadDeviceTest::SetUp());228 ASSERT_SUCCESS(olCreateQueue(Device, &Queue));229 }230 231 void TearDown() override {232 if (Queue) {233 olDestroyQueue(Queue);234 }235 RETURN_ON_FATAL_FAILURE(OffloadDeviceTest::TearDown());236 }237 238 ol_queue_handle_t Queue = nullptr;239};240 241struct OffloadEventTest : OffloadQueueTest {242 void SetUp() override {243 RETURN_ON_FATAL_FAILURE(OffloadQueueTest::SetUp());244 ASSERT_SUCCESS(olCreateEvent(Queue, &Event));245 ASSERT_SUCCESS(olSyncQueue(Queue));246 }247 248 void TearDown() override {249 if (Event)250 olDestroyEvent(Event);251 RETURN_ON_FATAL_FAILURE(OffloadQueueTest::TearDown());252 }253 254 ol_event_handle_t Event = nullptr;255};256 257// Devices might not be available for offload testing, so allow uninstantiated258// tests (as the device list will be empty). This means that all tests requiring259// a device will be silently skipped.260#define OFFLOAD_TESTS_INSTANTIATE_DEVICE_FIXTURE(FIXTURE) \261 INSTANTIATE_TEST_SUITE_P( \262 , FIXTURE, ::testing::ValuesIn(TestEnvironment::getDevices()), \263 [](const ::testing::TestParamInfo<TestEnvironment::Device> &info) { \264 return SanitizeString(info.param.Name); \265 }); \266 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FIXTURE)267