blob: 7a16a0009f4f73b989df1a4724c350a145df8d06 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <gtest/gtest.h>
#include <tvm/ffi/any.h>
#include <tvm/ffi/error.h>
#include <tvm/ffi/optional.h>
namespace {
using namespace tvm::ffi;
void ThrowRuntimeError() { TVM_FFI_THROW(RuntimeError) << "test0"; }
TEST(Error, Backtrace) {
EXPECT_THROW(
{
try {
ThrowRuntimeError();
} catch (const Error& error) {
EXPECT_EQ(error.message(), "test0");
EXPECT_EQ(error.kind(), "RuntimeError");
std::string full_message = error.FullMessage();
EXPECT_NE(full_message.find("line"), std::string::npos);
EXPECT_NE(full_message.find("ThrowRuntimeError"), std::string::npos);
EXPECT_NE(full_message.find("RuntimeError: test0"), std::string::npos);
throw;
}
},
::tvm::ffi::Error);
}
TEST(CheckError, Backtrace) {
EXPECT_THROW(
{
try {
TVM_FFI_ICHECK_GT(2, 3);
} catch (const Error& error) {
EXPECT_EQ(error.kind(), "InternalError");
std::string full_message = error.FullMessage();
EXPECT_NE(full_message.find("line"), std::string::npos);
EXPECT_NE(full_message.find("2 > 3"), std::string::npos);
throw;
}
},
::tvm::ffi::Error);
}
TEST(CheckError, ValueError) {
int value = -5;
EXPECT_THROW(
{
try {
TVM_FFI_CHECK(value >= 0, ValueError) << "Value must be non-negative, got " << value;
} catch (const Error& error) {
EXPECT_EQ(error.kind(), "ValueError");
std::string full_message = error.FullMessage();
EXPECT_NE(full_message.find("line"), std::string::npos);
EXPECT_NE(full_message.find("Check failed: (value >= 0) is false"), std::string::npos);
EXPECT_NE(full_message.find("Value must be non-negative, got -5"), std::string::npos);
throw;
}
},
::tvm::ffi::Error);
}
TEST(CheckError, IndexError) {
int index = 10;
int array_size = 5;
EXPECT_THROW(
{
try {
TVM_FFI_CHECK(index < array_size, IndexError)
<< "Index " << index << " out of bounds for array of size " << array_size;
} catch (const Error& error) {
EXPECT_EQ(error.kind(), "IndexError");
std::string full_message = error.FullMessage();
EXPECT_NE(full_message.find("line"), std::string::npos);
EXPECT_NE(full_message.find("Check failed: (index < array_size) is false"),
std::string::npos);
EXPECT_NE(full_message.find("Index 10 out of bounds for array of size 5"),
std::string::npos);
throw;
}
},
::tvm::ffi::Error);
}
TEST(CheckError, PassingCondition) {
// This should not throw
EXPECT_NO_THROW(TVM_FFI_CHECK(true, ValueError));
EXPECT_NO_THROW(TVM_FFI_CHECK(5 < 10, IndexError));
}
// Helper: expect that a throwing statement throws Error with the given kind and
// a message containing the expected substring.
#define EXPECT_CHECK_ERROR(stmt, expected_kind, expected_substr) \
{ \
bool caught = false; \
try { \
stmt; \
} catch (const ::tvm::ffi::Error& e) { \
caught = true; \
EXPECT_EQ(e.kind(), expected_kind); \
EXPECT_NE(e.message().find(expected_substr), std::string::npos) \
<< "message: " << e.message(); \
} \
EXPECT_TRUE(caught) << "Expected " #stmt " to throw"; \
}
TEST(CheckError, CheckBinaryOps) {
// EQ: pass and fail
TVM_FFI_CHECK_EQ(3, 3, ValueError);
EXPECT_CHECK_ERROR(TVM_FFI_CHECK_EQ(3, 5, ValueError), "ValueError", "3 vs. 5");
// NE: pass and fail
TVM_FFI_CHECK_NE(3, 4, ValueError);
EXPECT_CHECK_ERROR(TVM_FFI_CHECK_NE(3, 3, ValueError), "ValueError", "3 vs. 3");
// LT: pass and fail
TVM_FFI_CHECK_LT(3, 4, IndexError);
EXPECT_CHECK_ERROR(TVM_FFI_CHECK_LT(5, 3, IndexError), "IndexError", "5 vs. 3");
// GT: pass and fail
TVM_FFI_CHECK_GT(4, 3, IndexError);
EXPECT_CHECK_ERROR(TVM_FFI_CHECK_GT(3, 5, IndexError), "IndexError", "3 vs. 5");
// LE: pass and fail
TVM_FFI_CHECK_LE(3, 3, TypeError);
TVM_FFI_CHECK_LE(2, 3, TypeError);
EXPECT_CHECK_ERROR(TVM_FFI_CHECK_LE(5, 3, TypeError), "TypeError", "5 vs. 3");
// GE: pass and fail
TVM_FFI_CHECK_GE(4, 3, TypeError);
TVM_FFI_CHECK_GE(3, 3, TypeError);
EXPECT_CHECK_ERROR(TVM_FFI_CHECK_GE(2, 5, TypeError), "TypeError", "2 vs. 5");
// NOTNULL: pass and fail
int x = 42;
int* p = &x;
EXPECT_EQ(TVM_FFI_CHECK_NOTNULL(p, ValueError), p);
int* q = nullptr;
EXPECT_CHECK_ERROR((void)TVM_FFI_CHECK_NOTNULL(q, ValueError), "ValueError", "Check not null");
}
TEST(CheckError, DCheck) {
#ifdef NDEBUG
// Release: failing conditions are no-ops.
TVM_FFI_DCHECK(false);
TVM_FFI_DCHECK_EQ(1, 2);
TVM_FFI_DCHECK_NE(1, 1);
TVM_FFI_DCHECK_LT(5, 3);
TVM_FFI_DCHECK_GT(3, 5);
TVM_FFI_DCHECK_LE(5, 3);
TVM_FFI_DCHECK_GE(3, 5);
int* q = nullptr;
int* r = TVM_FFI_DCHECK_NOTNULL(q);
EXPECT_EQ(r, nullptr);
#else
// Debug: passing conditions succeed.
TVM_FFI_DCHECK(true);
TVM_FFI_DCHECK_EQ(3, 3);
TVM_FFI_DCHECK_NE(3, 4);
TVM_FFI_DCHECK_LT(3, 4);
TVM_FFI_DCHECK_GT(4, 3);
TVM_FFI_DCHECK_LE(3, 3);
TVM_FFI_DCHECK_GE(4, 3);
int x = 42;
int* p = &x;
EXPECT_EQ(TVM_FFI_DCHECK_NOTNULL(p), p);
// Debug: failing conditions throw InternalError.
EXPECT_CHECK_ERROR(TVM_FFI_DCHECK(false), "InternalError", "Check failed");
EXPECT_CHECK_ERROR(TVM_FFI_DCHECK_EQ(1, 2), "InternalError", "1 vs. 2");
EXPECT_CHECK_ERROR(TVM_FFI_DCHECK_NE(1, 1), "InternalError", "1 vs. 1");
EXPECT_CHECK_ERROR(TVM_FFI_DCHECK_LT(5, 3), "InternalError", "5 vs. 3");
EXPECT_CHECK_ERROR(TVM_FFI_DCHECK_GT(3, 5), "InternalError", "3 vs. 5");
EXPECT_CHECK_ERROR(TVM_FFI_DCHECK_LE(5, 3), "InternalError", "5 vs. 3");
EXPECT_CHECK_ERROR(TVM_FFI_DCHECK_GE(3, 5), "InternalError", "3 vs. 5");
#endif
}
TEST(Error, AnyConvert) {
Any any = Error("TypeError", "here", "test0");
Optional<Error> opt_err = any.as<Error>();
EXPECT_EQ(opt_err.value().kind(), "TypeError");
EXPECT_EQ(opt_err.value().message(), "here");
}
TEST(Error, TracebackMostRecentCallLast) {
Error error("TypeError", "here", "test0\ntest1\ntest2\n");
EXPECT_EQ(error.TracebackMostRecentCallLast(), "test2\ntest1\ntest0\n");
}
TEST(Error, CauseChain) {
Error original_error("TypeError", "here", "test0");
Error cause_chain("ValueError", "cause", "test1", original_error, std::nullopt);
auto opt_cause = cause_chain.cause_chain();
EXPECT_TRUE(opt_cause.has_value());
if (opt_cause.has_value()) {
EXPECT_EQ(opt_cause->kind(), "TypeError");
}
EXPECT_TRUE(!cause_chain.extra_context().has_value());
}
} // namespace