| /* |
| * 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/container/array.h> |
| #include <tvm/ffi/memory.h> |
| #include <tvm/ffi/optional.h> |
| |
| #include "./testing_object.h" |
| |
| namespace { |
| |
| using namespace tvm::ffi; |
| using namespace tvm::ffi::testing; |
| |
| TEST(Optional, TInt) { |
| Optional<TInt> x; |
| Optional<TInt> y = TInt(11); |
| static_assert(sizeof(Optional<TInt>) == sizeof(ObjectRef)); |
| |
| EXPECT_TRUE(!x.has_value()); |
| EXPECT_EQ(x.value_or(TInt(12))->value, 12); |
| |
| EXPECT_TRUE(y.has_value()); |
| EXPECT_EQ(y.value_or(TInt(12))->value, 11); |
| |
| Any z_any = std::move(y); |
| EXPECT_TRUE(z_any != nullptr); |
| EXPECT_EQ((z_any.cast<TInt>())->value, 11); |
| // NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move) |
| EXPECT_TRUE(!y.has_value()); |
| |
| // move from any to optional |
| auto y2 = std::move(z_any).cast<Optional<TInt>>(); |
| EXPECT_EQ(y2.use_count(), 1); |
| EXPECT_TRUE(y2.has_value()); |
| EXPECT_EQ(y2.value_or(TInt(12))->value, 11); |
| } |
| |
| TEST(Optional, double) { |
| Optional<double> x; |
| Optional<double> y = 11.0; |
| static_assert(sizeof(Optional<double>) > sizeof(ObjectRef)); |
| |
| EXPECT_TRUE(!x.has_value()); |
| EXPECT_EQ(x.value_or(12), 12); |
| EXPECT_TRUE(x != 12); |
| |
| EXPECT_TRUE(y.has_value()); |
| EXPECT_EQ(y.value_or(12), 11); |
| EXPECT_TRUE(y == 11); |
| EXPECT_TRUE(y != 12); |
| } |
| |
| TEST(Optional, AnyConvertInt) { |
| Optional<int> opt_v0 = 1; |
| EXPECT_EQ(opt_v0.value(), 1); |
| EXPECT_TRUE(opt_v0.has_value()); |
| |
| AnyView view0 = opt_v0; |
| EXPECT_EQ(view0.cast<int>(), 1); |
| |
| Any any1; |
| auto opt_v1 = std::move(any1).cast<Optional<int>>(); |
| EXPECT_TRUE(!opt_v1.has_value()); |
| Optional<int> opt_v2 = 11; |
| Any any2 = std::move(opt_v2); |
| EXPECT_EQ(any2.cast<int>(), 11); |
| } |
| |
| TEST(Optional, AnyConvertArray) { |
| AnyView view0; |
| Array<Array<TNumber>> arr_nested = {{}, {TInt(1), TFloat(2)}}; |
| view0 = arr_nested; |
| |
| auto opt_arr = view0.cast<Optional<Array<Array<TNumber>>>>(); |
| EXPECT_EQ(arr_nested.use_count(), 2); |
| |
| auto arr1 = view0.cast<Optional<Array<Array<TNumber>>>>(); |
| EXPECT_EQ(arr_nested.use_count(), 3); |
| EXPECT_EQ(arr1.value()[1][1].as<TFloatObj>()->value, 2); |
| |
| Any any1; |
| auto arr2 = any1.cast<Optional<Array<Array<TNumber>>>>(); |
| EXPECT_TRUE(!arr2.has_value()); |
| |
| EXPECT_THROW( |
| { |
| try { |
| [[maybe_unused]] auto arr2 = view0.cast<Optional<Array<Array<int>>>>(); |
| } catch (const Error& error) { |
| EXPECT_EQ(error.kind(), "TypeError"); |
| std::string what = error.what(); |
| std::cout << what << std::endl; |
| EXPECT_NE(what.find("to `Optional<Array<Array<int>>>`"), std::string::npos); |
| throw; |
| } |
| }, |
| ::tvm::ffi::Error); |
| } |
| |
| TEST(Optional, OptionalOfOptional) { |
| // testcase of optional<optional> |
| Optional<Optional<int>> opt_opt_int; |
| EXPECT_TRUE(!opt_opt_int.has_value()); |
| |
| Optional<Optional<int>> opt_opt_int2 = Optional<int>(std::nullopt); |
| EXPECT_TRUE(opt_opt_int2.has_value()); |
| EXPECT_TRUE(!opt_opt_int2.value().has_value()); |
| |
| // Optional<Optional<ObjectRef>> |
| Optional<Optional<TInt>> opt_opt_tint; |
| EXPECT_TRUE(!opt_opt_tint.has_value()); |
| |
| Optional<Optional<TInt>> opt_opt_tint2 = Optional<TInt>(std::nullopt); |
| EXPECT_TRUE(opt_opt_tint2.has_value()); |
| EXPECT_TRUE(!opt_opt_tint2.value().has_value()); |
| opt_opt_tint2 = std::nullopt; |
| EXPECT_TRUE(!opt_opt_tint2.has_value()); |
| |
| Optional<Optional<TInt>> opt_opt_tint3 = Optional<TInt>(TInt(42)); |
| EXPECT_TRUE(opt_opt_tint3.has_value()); |
| EXPECT_TRUE(opt_opt_tint3.value().has_value()); |
| EXPECT_EQ(opt_opt_tint3.value().value()->value, 42); |
| } |
| |
| TEST(Optional, ValueMove) { |
| Optional<TInt> y = TInt(11); |
| TInt x = std::move(y).value(); |
| // NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move) |
| EXPECT_TRUE(!y.has_value()); |
| EXPECT_EQ(x->value, 11); |
| |
| Optional<TInt> opt_tint = TInt(21); |
| EXPECT_TRUE(opt_tint.has_value()); |
| EXPECT_EQ((*opt_tint)->value, 21); |
| |
| TInt moved_tint = *std::move(opt_tint); |
| EXPECT_EQ(moved_tint->value, 21); |
| // NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move) |
| EXPECT_TRUE(!opt_tint.has_value()); |
| } |
| |
| TEST(Optional, OptionalInArray) { |
| // This pattern plus iteration may cause memory leak |
| // this is because arr[0] returns a temporary object |
| // and further call arr[0].value() may return a reference to |
| // the temporary object |
| Array<Optional<Array<TInt>>> arr = {Array<TInt>({TInt(0), TInt(1)})}; |
| int counter = 0; |
| |
| for (const auto& x : arr[0].value()) { |
| EXPECT_EQ(x->value, counter++); |
| } |
| |
| Any any = arr; |
| auto opt_arr = any.cast<Array<Optional<Array<TInt>>>>(); |
| EXPECT_EQ(opt_arr[0].value()[0]->value, 0); |
| } |
| |
| TEST(Optional, String) { |
| Optional<String> opt_str; |
| EXPECT_TRUE(!opt_str.has_value()); |
| EXPECT_EQ(opt_str.value_or("default"), "default"); |
| EXPECT_TRUE(opt_str != "default"); |
| EXPECT_TRUE(opt_str != String("default")); |
| EXPECT_TRUE(opt_str == std::nullopt); |
| |
| opt_str = "hello"; |
| EXPECT_TRUE(opt_str.has_value()); |
| EXPECT_EQ(opt_str.value(), "hello"); |
| EXPECT_TRUE(opt_str == "hello"); |
| EXPECT_TRUE(opt_str == String("hello")); |
| EXPECT_TRUE(opt_str != std::nullopt); |
| static_assert(sizeof(Optional<String>) == sizeof(String)); |
| } |
| |
| TEST(Optional, Bytes) { |
| Optional<Bytes> opt_bytes; |
| EXPECT_TRUE(!opt_bytes.has_value()); |
| EXPECT_EQ(opt_bytes.value_or(std::string("default")), "default"); |
| |
| opt_bytes = std::string("hello"); |
| EXPECT_TRUE(opt_bytes.has_value()); |
| EXPECT_EQ(opt_bytes.value().operator std::string(), "hello"); |
| EXPECT_TRUE(opt_bytes != std::nullopt); |
| static_assert(sizeof(Optional<Bytes>) == sizeof(Bytes)); |
| } |
| } // namespace |