This commit introduces a C interface for the ORC runtime's Error
handling system, enabling C clients and language bindings to work with
ORC errors.
The ORC_RT_C_ABI macro applies __attribute__((visibility("default")))
(on platforms that support it), ensuring C API symbols are exported when
building the ORC runtime as a shared library. In the future I expect
that this will be extended to support other platforms (e.g. dllexport on
Windows).
177 lines
5.4 KiB
C++
177 lines
5.4 KiB
C++
//===- ErrorCAPITest.cpp - Tests for Error C API --------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file tests the C API for ORC runtime errors defined in orc-rt-c/Error.h.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "orc-rt-c/Error.h"
|
|
#include "orc-rt/Error.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include <cstring>
|
|
|
|
using namespace orc_rt;
|
|
|
|
namespace {
|
|
|
|
// Test that wrapping a success value produces null.
|
|
TEST(ErrorCAPITest, WrapSuccess) {
|
|
orc_rt_ErrorRef ErrRef = wrap(Error::success());
|
|
EXPECT_EQ(ErrRef, orc_rt_ErrorSuccess);
|
|
}
|
|
|
|
// Test that wrap/unwrap round-trips correctly for error values.
|
|
TEST(ErrorCAPITest, WrapUnwrapRoundTrip) {
|
|
Error Original = make_error<StringError>("test error");
|
|
orc_rt_ErrorRef ErrRef = wrap(std::move(Original));
|
|
|
|
EXPECT_NE(ErrRef, orc_rt_ErrorSuccess);
|
|
|
|
Error Restored = unwrap(ErrRef);
|
|
EXPECT_TRUE(Restored.isA<StringError>());
|
|
EXPECT_EQ(toString(std::move(Restored)), "test error");
|
|
}
|
|
|
|
// Test that unwrapping null produces a success value.
|
|
TEST(ErrorCAPITest, UnwrapSuccess) {
|
|
Error E = unwrap(orc_rt_ErrorSuccess);
|
|
EXPECT_FALSE(E) << "Unwrapping null should produce success";
|
|
}
|
|
|
|
// Test orc_rt_Error_getTypeId returns the correct type ID.
|
|
TEST(ErrorCAPITest, GetTypeId) {
|
|
orc_rt_ErrorRef ErrRef = orc_rt_StringError_create("test");
|
|
orc_rt_Error_TypeId TypeId = orc_rt_Error_getTypeId(ErrRef);
|
|
|
|
EXPECT_EQ(TypeId, orc_rt_StringError_getTypeId());
|
|
|
|
orc_rt_Error_consume(ErrRef);
|
|
}
|
|
|
|
// Test orc_rt_Error_consume properly disposes of an error.
|
|
TEST(ErrorCAPITest, Consume) {
|
|
orc_rt_ErrorRef ErrRef = orc_rt_StringError_create("test");
|
|
EXPECT_NE(ErrRef, orc_rt_ErrorSuccess);
|
|
|
|
// Should not crash or leak.
|
|
orc_rt_Error_consume(ErrRef);
|
|
}
|
|
|
|
// Test orc_rt_Error_cantFail with success value.
|
|
TEST(ErrorCAPITest, CantFailSuccess) {
|
|
// Should not crash.
|
|
orc_rt_Error_cantFail(orc_rt_ErrorSuccess);
|
|
}
|
|
|
|
// Test orc_rt_Error_cantFail aborts on failure value.
|
|
TEST(ErrorCAPITest, CantFailFailure) {
|
|
EXPECT_DEATH(
|
|
{ orc_rt_Error_cantFail(orc_rt_StringError_create("test")); }, "")
|
|
<< "orc_rt_Error_cantFail did not abort on failure value";
|
|
}
|
|
|
|
// Test orc_rt_Error_toString returns the error message and consumes the error.
|
|
TEST(ErrorCAPITest, ToString) {
|
|
orc_rt_ErrorRef ErrRef = orc_rt_StringError_create("hello world");
|
|
char *Msg = orc_rt_Error_toString(ErrRef);
|
|
|
|
EXPECT_STREQ(Msg, "hello world");
|
|
|
|
orc_rt_Error_freeErrorMessage(Msg);
|
|
}
|
|
|
|
// Test orc_rt_StringError_create creates an error with the correct message.
|
|
TEST(ErrorCAPITest, StringErrorCreate) {
|
|
const char *TestMsg = "custom error message";
|
|
orc_rt_ErrorRef ErrRef = orc_rt_StringError_create(TestMsg);
|
|
|
|
EXPECT_NE(ErrRef, orc_rt_ErrorSuccess);
|
|
|
|
// Verify it's a StringError.
|
|
EXPECT_EQ(orc_rt_Error_getTypeId(ErrRef), orc_rt_StringError_getTypeId());
|
|
|
|
// Verify the message.
|
|
char *Msg = orc_rt_Error_toString(ErrRef);
|
|
EXPECT_STREQ(Msg, TestMsg);
|
|
orc_rt_Error_freeErrorMessage(Msg);
|
|
}
|
|
|
|
// Test orc_rt_StringError_getTypeId returns a consistent value.
|
|
TEST(ErrorCAPITest, StringErrorTypeIdConsistent) {
|
|
orc_rt_Error_TypeId TypeId1 = orc_rt_StringError_getTypeId();
|
|
orc_rt_Error_TypeId TypeId2 = orc_rt_StringError_getTypeId();
|
|
|
|
EXPECT_EQ(TypeId1, TypeId2);
|
|
EXPECT_NE(TypeId1, nullptr);
|
|
}
|
|
|
|
// Test that C API type ID matches C++ StringError class ID.
|
|
TEST(ErrorCAPITest, StringErrorTypeIdMatchesCpp) {
|
|
orc_rt_Error_TypeId CTypeId = orc_rt_StringError_getTypeId();
|
|
const void *CppTypeId = StringError::classID();
|
|
|
|
EXPECT_EQ(CTypeId, CppTypeId);
|
|
}
|
|
|
|
// Test creating and consuming multiple errors.
|
|
TEST(ErrorCAPITest, MultipleErrors) {
|
|
orc_rt_ErrorRef Err1 = orc_rt_StringError_create("error 1");
|
|
orc_rt_ErrorRef Err2 = orc_rt_StringError_create("error 2");
|
|
orc_rt_ErrorRef Err3 = orc_rt_StringError_create("error 3");
|
|
|
|
EXPECT_NE(Err1, orc_rt_ErrorSuccess);
|
|
EXPECT_NE(Err2, orc_rt_ErrorSuccess);
|
|
EXPECT_NE(Err3, orc_rt_ErrorSuccess);
|
|
|
|
char *Msg1 = orc_rt_Error_toString(Err1);
|
|
char *Msg2 = orc_rt_Error_toString(Err2);
|
|
char *Msg3 = orc_rt_Error_toString(Err3);
|
|
|
|
EXPECT_STREQ(Msg1, "error 1");
|
|
EXPECT_STREQ(Msg2, "error 2");
|
|
EXPECT_STREQ(Msg3, "error 3");
|
|
|
|
orc_rt_Error_freeErrorMessage(Msg1);
|
|
orc_rt_Error_freeErrorMessage(Msg2);
|
|
orc_rt_Error_freeErrorMessage(Msg3);
|
|
}
|
|
|
|
// Test wrapping a custom C++ error type and checking its type via C API.
|
|
class CustomCAPITestError
|
|
: public ErrorExtends<CustomCAPITestError, ErrorInfoBase> {
|
|
public:
|
|
CustomCAPITestError(int Code) : Code(Code) {}
|
|
std::string toString() const noexcept override {
|
|
return "CustomCAPITestError: " + std::to_string(Code);
|
|
}
|
|
int getCode() const { return Code; }
|
|
|
|
private:
|
|
int Code;
|
|
};
|
|
|
|
TEST(ErrorCAPITest, CustomErrorTypeId) {
|
|
Error CppError = make_error<CustomCAPITestError>(42);
|
|
orc_rt_ErrorRef ErrRef = wrap(std::move(CppError));
|
|
|
|
orc_rt_Error_TypeId TypeId = orc_rt_Error_getTypeId(ErrRef);
|
|
|
|
// Should not be a StringError.
|
|
EXPECT_NE(TypeId, orc_rt_StringError_getTypeId());
|
|
|
|
// Should match the C++ class ID.
|
|
EXPECT_EQ(TypeId, CustomCAPITestError::classID());
|
|
|
|
char *Msg = orc_rt_Error_toString(ErrRef);
|
|
EXPECT_STREQ(Msg, "CustomCAPITestError: 42");
|
|
orc_rt_Error_freeErrorMessage(Msg);
|
|
}
|
|
|
|
} // end anonymous namespace
|