| /* |
| * 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. |
| */ |
| #define TVM_FFI_DLL_EXPORT_INCLUDE_METADATA 1 |
| |
| #include <gtest/gtest.h> |
| #include <tvm/ffi/any.h> |
| #include <tvm/ffi/container/array.h> |
| #include <tvm/ffi/container/map.h> |
| #include <tvm/ffi/extra/json.h> |
| #include <tvm/ffi/function.h> |
| #include <tvm/ffi/memory.h> |
| |
| #include "./testing_object.h" |
| |
| namespace { |
| |
| using namespace tvm::ffi; |
| using namespace tvm::ffi::testing; |
| |
| TEST(Func, FromPacked) { |
| Function fadd1 = Function::FromPacked([](const AnyView* args, int32_t num_args, Any* rv) { |
| EXPECT_EQ(num_args, 1); |
| int32_t a = args[0].cast<int32_t>(); |
| *rv = a + 1; |
| }); |
| int b = fadd1(1).cast<int>(); |
| EXPECT_EQ(b, 2); |
| |
| Function fadd2 = Function::FromPacked([](const AnyView* args, int32_t num_args, Any* rv) { |
| EXPECT_EQ(num_args, 1); |
| auto a = args[0].cast<TInt>(); |
| EXPECT_EQ(a.use_count(), 2); |
| *rv = a->value + 1; |
| }); |
| EXPECT_EQ(fadd2(TInt(12)).cast<int>(), 13); |
| } |
| |
| TEST(Func, PackedArgs) { |
| Function fadd1 = Function::FromPacked([](PackedArgs args, Any* rv) { |
| EXPECT_EQ(args.size(), 1); |
| int32_t a = args[0].cast<int>(); |
| *rv = a + 1; |
| }); |
| int b = fadd1(1).cast<int>(); |
| EXPECT_EQ(b, 2); |
| |
| Function fadd2 = Function::FromPacked([](PackedArgs args, Any* rv) { |
| EXPECT_EQ(args.size(), 1); |
| TInt a = args[0].cast<TInt>(); |
| EXPECT_EQ(a.use_count(), 2); |
| *rv = a->value + 1; |
| }); |
| EXPECT_EQ(fadd2(TInt(12)).cast<int>(), 13); |
| |
| TInt v(12); |
| AnyView data[3]; |
| PackedArgs::Fill(data, 3, 1, v); |
| EXPECT_EQ(data[0].cast<int>(), 3); |
| EXPECT_EQ(data[1].cast<int>(), 1); |
| EXPECT_EQ(data[2].cast<TInt>()->value, 12); |
| } |
| |
| TEST(Func, FromTyped) { |
| // try decution |
| Function fadd1 = Function::FromTyped([](const int32_t& a) -> int { return a + 1; }); |
| int b = fadd1(1).cast<int>(); |
| EXPECT_EQ(b, 2); |
| |
| // convert that triggers error |
| EXPECT_THROW( |
| { |
| try { |
| fadd1(1.1); |
| } catch (const Error& error) { |
| EXPECT_EQ(error.kind(), "TypeError"); |
| EXPECT_EQ(error.message(), |
| "Mismatched type on argument #0 when calling: `(0: int) -> int`. " |
| "Expected `int` but got `float`"); |
| throw; |
| } |
| }, |
| ::tvm::ffi::Error); |
| |
| // convert that triggers error |
| EXPECT_THROW( |
| { |
| try { |
| fadd1(); |
| } catch (const Error& error) { |
| EXPECT_EQ(error.kind(), "TypeError"); |
| EXPECT_EQ(error.message(), |
| "Mismatched number of arguments when calling: `(0: int) -> int`. " |
| "Expected 1 but got 0 arguments"); |
| throw; |
| } |
| }, |
| ::tvm::ffi::Error); |
| |
| // convert with DLTensor* triggers error |
| EXPECT_THROW( |
| { |
| try { |
| DLTensor dltensor; |
| fadd1(&dltensor); |
| } catch (const Error& error) { |
| EXPECT_EQ(error.kind(), "TypeError"); |
| EXPECT_EQ(error.message(), |
| "Mismatched type on argument #0 when calling: `(0: int) -> int`. " |
| "Expected `int` but got `DLTensor*`"); |
| throw; |
| } |
| }, |
| ::tvm::ffi::Error); |
| |
| // try decution |
| Function fpass_and_return = Function::FromTyped( |
| // NOLINTNEXTLINE(performance-unnecessary-value-param) |
| [](TInt x, int value, AnyView z) -> Function { |
| EXPECT_EQ(x.use_count(), 2); |
| EXPECT_EQ(x->value, value); |
| if (auto opt = z.as<int>()) { |
| EXPECT_EQ(value, *opt); |
| } |
| return Function::FromTyped([value](int x) -> int { return x + value; }); |
| }, |
| "fpass_and_return"); |
| TInt a(11); |
| auto fret = fpass_and_return(std::move(a), 11, 11).cast<Function>(); |
| EXPECT_EQ(fret(12).cast<int>(), 23); |
| |
| EXPECT_THROW( |
| { |
| try { |
| fpass_and_return(); |
| } catch (const Error& error) { |
| EXPECT_EQ(error.kind(), "TypeError"); |
| EXPECT_EQ(error.message(), |
| "Mismatched number of arguments when calling: " |
| "`fpass_and_return(0: test.Int, 1: int, 2: AnyView) -> ffi.Function`. " |
| "Expected 3 but got 0 arguments"); |
| throw; |
| } |
| }, |
| ::tvm::ffi::Error); |
| |
| Function fconcact = |
| Function::FromTyped([](const String& a, const String& b) -> String { return a + b; }); |
| EXPECT_EQ(fconcact("abc", "def").cast<String>(), "abcdef"); |
| } |
| |
| TEST(Func, PassReturnAny) { |
| // NOLINTNEXTLINE(performance-unnecessary-value-param) |
| Function fadd_one = Function::FromTyped([](Any a) -> Any { return a.cast<int>() + 1; }); |
| EXPECT_EQ(fadd_one(1).cast<int>(), 2); |
| } |
| |
| TEST(Func, Global) { |
| Function::SetGlobal("testing.add1", |
| Function::FromTyped([](const int32_t& a) -> int { return a + 1; })); |
| auto fadd1 = Function::GetGlobalRequired("testing.add1"); |
| int b = fadd1(1).cast<int>(); |
| EXPECT_EQ(b, 2); |
| auto fnot_exist = Function::GetGlobal("testing.not_existing_func"); |
| EXPECT_TRUE(!fnot_exist); |
| |
| auto fname_functor = // NOLINTNEXTLINE(bugprone-unchecked-optional-access) |
| Function::GetGlobal("ffi.FunctionListGlobalNamesFunctor").value()().cast<Function>(); |
| Array<String> names; |
| int len = fname_functor(-1).cast<int>(); |
| for (int i = 0; i < len; ++i) { |
| names.push_back(fname_functor(i).cast<String>()); |
| } |
| EXPECT_TRUE(std::find(names.begin(), names.end(), "testing.add1") != names.end()); |
| } |
| |
| TEST(Func, TypedFunction) { |
| TypedFunction<int(int)> fadd1 = [](int a) -> int { return a + 1; }; |
| EXPECT_EQ(fadd1(1), 2); |
| |
| TypedFunction<int(int)> fadd2([](int a) -> int { return a + 2; }); |
| EXPECT_EQ(fadd2(1), 3); |
| EXPECT_EQ(fadd2.packed()(1).cast<int>(), 3); |
| |
| TypedFunction<void(int)> fcheck_int; |
| EXPECT_TRUE(fcheck_int == nullptr); |
| fcheck_int = [](int a) -> void { EXPECT_EQ(a, 1); }; |
| fcheck_int(1); |
| } |
| |
| TEST(Func, TypedFunctionAsAny) { |
| TypedFunction<int(int)> fadd1 = [](int a) -> int { return a + 1; }; |
| Any fany(std::move(fadd1)); |
| EXPECT_TRUE(fadd1 == nullptr); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move) |
| auto fadd1_dup = fany.cast<TypedFunction<int(int)>>(); |
| EXPECT_EQ(fadd1_dup(1), 2); |
| } |
| |
| TEST(Func, TypedFunctionAsAnyView) { |
| TypedFunction<int(int)> fadd2 = [](int a) -> int { return a + 2; }; |
| AnyView fview(fadd2); |
| auto fadd2_dup = fview.cast<TypedFunction<int(int)>>(); |
| EXPECT_EQ(fadd2_dup(1), 3); |
| } |
| |
| TEST(Func, ObjectRefWithFallbackTraits) { |
| // test cases to test automatic type conversion via ObjectRefWithFallbackTraits |
| // through TPrimExpr |
| Function freturn_primexpr = Function::FromTyped([](TPrimExpr a) -> TPrimExpr { return a; }); |
| |
| auto result_int = freturn_primexpr(1).cast<TPrimExpr>(); |
| EXPECT_EQ(result_int->dtype, "int64"); |
| EXPECT_EQ(result_int->value, 1); |
| |
| // Test case for float |
| auto result_float = freturn_primexpr(2.5).cast<TPrimExpr>(); |
| EXPECT_EQ(result_float->dtype, "float32"); |
| EXPECT_EQ(result_float->value, 2.5); |
| |
| // Test case for bool |
| auto result_bool = freturn_primexpr(true).cast<TPrimExpr>(); |
| EXPECT_EQ(result_bool->dtype, "bool"); |
| EXPECT_EQ(result_bool->value, 1); |
| |
| // Test case for string |
| auto result_string = freturn_primexpr("test_string").cast<TPrimExpr>(); |
| EXPECT_EQ(result_string->dtype, "test_string"); |
| EXPECT_EQ(result_string->value, 0); |
| |
| EXPECT_THROW( |
| { |
| try { |
| freturn_primexpr(TInt(1)); |
| } catch (const Error& error) { |
| EXPECT_EQ(error.kind(), "TypeError"); |
| EXPECT_EQ( |
| error.message(), |
| "Mismatched type on argument #0 when calling: `(0: test.PrimExpr) -> test.PrimExpr`. " |
| "Expected `test.PrimExpr` but got `test.Int`"); |
| throw; |
| } |
| }, |
| ::tvm::ffi::Error); |
| } |
| |
| TEST(SetRaisedFromCStr, ValueError) { |
| TVMFFIErrorSetRaisedFromCStr("ValueError", "Value must be non-negative, got -5"); |
| Error error = tvm::ffi::details::MoveFromSafeCallRaised(); |
| EXPECT_EQ(error.kind(), "ValueError"); |
| EXPECT_EQ(error.message(), "Value must be non-negative, got -5"); |
| } |
| |
| TEST(SetRaisedFromCStrParts, TypeError) { |
| const char* message_parts[] = {"Mismatched", nullptr, " got Tensor"}; |
| TVMFFIErrorSetRaisedFromCStrParts("TypeError", message_parts, 3); |
| Error error = tvm::ffi::details::MoveFromSafeCallRaised(); |
| EXPECT_EQ(error.kind(), "TypeError"); |
| EXPECT_EQ(error.message(), "Mismatched got Tensor"); |
| } |
| |
| int testing_add1(int x) { return x + 1; } |
| |
| TVM_FFI_DLL_EXPORT_TYPED_FUNC(testing_add1, testing_add1) |
| |
| TEST(Func, FromExternC) { |
| // this is the function abi convention |
| Function fadd1 = Function::FromExternC(nullptr, __tvm_ffi_testing_add1, nullptr); |
| EXPECT_EQ(fadd1(1).cast<int>(), 2); |
| } |
| |
| int invoke_testing_add1(int x) { |
| return Function::InvokeExternC(nullptr, __tvm_ffi_testing_add1, x).cast<int>(); |
| } |
| |
| TEST(Func, InvokeExternC) { EXPECT_EQ(invoke_testing_add1(1), 2); } |
| |
| extern "C" int __tvm_ffi__metadata_testing_add1(void*, const TVMFFIAny*, int32_t, TVMFFIAny*); |
| |
| TEST(Func, StaticLinkingMetadata) { |
| String metadata_str = |
| Function::InvokeExternC(nullptr, __tvm_ffi__metadata_testing_add1).cast<String>(); |
| Map<String, Any> metadata = json::Parse(metadata_str).cast<Map<String, Any>>(); |
| EXPECT_TRUE(metadata.count("type_schema")); |
| std::string type_schema_str = metadata["type_schema"].cast<String>(); |
| EXPECT_TRUE(type_schema_str.find("int") != std::string::npos); |
| } |
| |
| extern "C" TVM_FFI_DLL int TVMFFITestingDummyTarget(); |
| |
| TEST(Func, DummyCFunc) { |
| int value = TVMFFITestingDummyTarget(); |
| EXPECT_EQ(value, 0); |
| } |
| |
| } // namespace |