xref: /aosp_15_r20/external/executorch/runtime/core/test/evalue_test.cpp (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree.
7  */
8 
9 #include <executorch/runtime/core/evalue.h>
10 
11 #include <gtest/gtest.h>
12 
13 #include <executorch/runtime/core/exec_aten/testing_util/tensor_factory.h>
14 #include <executorch/runtime/platform/runtime.h>
15 #include <executorch/test/utils/DeathTest.h>
16 
17 using namespace ::testing;
18 
19 using exec_aten::ScalarType;
20 using executorch::runtime::BoxedEvalueList;
21 using executorch::runtime::EValue;
22 using executorch::runtime::Tag;
23 using executorch::runtime::testing::TensorFactory;
24 
25 class EValueTest : public ::testing::Test {
26  protected:
SetUp()27   void SetUp() override {
28     // Since these tests cause ET_LOG to be called, the PAL must be initialized
29     // first.
30     executorch::runtime::runtime_init();
31   }
32 };
33 
34 // An utility class used in tests to simulate objects that manage Tensors.
35 // The overloaded operator*() is used to return the underlying Tensor, mimicking
36 // behavior of smart pointers.
37 class TensorWrapper {
38  public:
TensorWrapper(exec_aten::Tensor tensor)39   explicit TensorWrapper(exec_aten::Tensor tensor)
40       : tensor_(std::make_unique<exec_aten::Tensor>(std::move(tensor))) {}
41 
operator *()42   exec_aten::Tensor& operator*() {
43     return *tensor_;
44   }
45 
operator *() const46   const exec_aten::Tensor& operator*() const {
47     return *tensor_;
48   }
49 
operator bool() const50   operator bool() const {
51     return static_cast<bool>(tensor_);
52   }
53 
operator ==(std::nullptr_t) const54   bool operator==(std::nullptr_t) const {
55     return tensor_ == nullptr;
56   }
57 
operator !=(std::nullptr_t) const58   bool operator!=(std::nullptr_t) const {
59     return tensor_ != nullptr;
60   }
61 
62  private:
63   std::unique_ptr<exec_aten::Tensor> tensor_;
64 };
65 
TEST_F(EValueTest,CopyTrivialType)66 TEST_F(EValueTest, CopyTrivialType) {
67   EValue a;
68   EValue b(true);
69   EXPECT_TRUE(a.isNone());
70   a = b;
71   EXPECT_TRUE(a.isBool());
72   EXPECT_EQ(a.to<bool>(), true);
73   EXPECT_EQ(b.to<bool>(), true);
74 }
75 
TEST_F(EValueTest,CopyTensor)76 TEST_F(EValueTest, CopyTensor) {
77   TensorFactory<ScalarType::Float> tf;
78   EValue a(tf.ones({3, 2}));
79   EValue b(tf.ones({1}));
80   EXPECT_EQ(a.toTensor().dim(), 2);
81   a = b;
82   EXPECT_EQ(a.toTensor().dim(), 1);
83 }
84 
TEST_F(EValueTest,TypeMismatchFatals)85 TEST_F(EValueTest, TypeMismatchFatals) {
86   ET_EXPECT_DEATH(
87       {
88         auto e = EValue(true);
89         e.toInt();
90       },
91       "");
92 }
93 
TEST_F(EValueTest,NoneByDefault)94 TEST_F(EValueTest, NoneByDefault) {
95   EValue e;
96   EXPECT_TRUE(e.isNone());
97 }
98 
TEST_F(EValueTest,ToOptionalInt)99 TEST_F(EValueTest, ToOptionalInt) {
100   EValue e((int64_t)5);
101   EXPECT_TRUE(e.isInt());
102   EXPECT_FALSE(e.isNone());
103 
104   exec_aten::optional<int64_t> o = e.toOptional<int64_t>();
105   EXPECT_TRUE(o.has_value());
106   EXPECT_EQ(o.value(), 5);
107 }
108 
TEST_F(EValueTest,NoneToOptionalInt)109 TEST_F(EValueTest, NoneToOptionalInt) {
110   EValue e;
111   EXPECT_TRUE(e.isNone());
112 
113   exec_aten::optional<int64_t> o = e.toOptional<int64_t>();
114   EXPECT_FALSE(o.has_value());
115 }
116 
TEST_F(EValueTest,ToOptionalScalar)117 TEST_F(EValueTest, ToOptionalScalar) {
118   exec_aten::Scalar s((double)3.141);
119   EValue e(s);
120   EXPECT_TRUE(e.isScalar());
121   EXPECT_FALSE(e.isNone());
122 
123   exec_aten::optional<exec_aten::Scalar> o = e.toOptional<exec_aten::Scalar>();
124   EXPECT_TRUE(o.has_value());
125   EXPECT_TRUE(o.value().isFloatingPoint());
126   EXPECT_EQ(o.value().to<double>(), 3.141);
127 }
128 
TEST_F(EValueTest,ScalarToType)129 TEST_F(EValueTest, ScalarToType) {
130   exec_aten::Scalar s_d((double)3.141);
131   EXPECT_EQ(s_d.to<double>(), 3.141);
132   exec_aten::Scalar s_i((int64_t)3);
133   EXPECT_EQ(s_i.to<int64_t>(), 3);
134   exec_aten::Scalar s_b(true);
135   EXPECT_EQ(s_b.to<bool>(), true);
136 }
137 
TEST_F(EValueTest,NoneToOptionalScalar)138 TEST_F(EValueTest, NoneToOptionalScalar) {
139   EValue e;
140   EXPECT_TRUE(e.isNone());
141 
142   exec_aten::optional<exec_aten::Scalar> o = e.toOptional<exec_aten::Scalar>();
143   EXPECT_FALSE(o.has_value());
144 }
145 
TEST_F(EValueTest,NoneToOptionalTensor)146 TEST_F(EValueTest, NoneToOptionalTensor) {
147   EValue e;
148   EXPECT_TRUE(e.isNone());
149 
150   exec_aten::optional<exec_aten::Tensor> o = e.toOptional<exec_aten::Tensor>();
151   EXPECT_FALSE(o.has_value());
152 }
153 
TEST_F(EValueTest,ToScalarType)154 TEST_F(EValueTest, ToScalarType) {
155   EValue e((int64_t)4);
156   auto o = e.toScalarType();
157   EXPECT_EQ(o, exec_aten::ScalarType::Long);
158   EValue f((int64_t)4);
159   auto o2 = e.toOptional<exec_aten::ScalarType>();
160   EXPECT_TRUE(o2.has_value());
161   EXPECT_EQ(o2.value(), exec_aten::ScalarType::Long);
162 }
163 
TEST_F(EValueTest,toString)164 TEST_F(EValueTest, toString) {
165   const EValue e("foo", 3);
166   EXPECT_TRUE(e.isString());
167   EXPECT_FALSE(e.isNone());
168 
169   exec_aten::string_view x = e.toString();
170   EXPECT_EQ(x, "foo");
171 }
172 
TEST_F(EValueTest,MemoryFormat)173 TEST_F(EValueTest, MemoryFormat) {
174   const EValue e((int64_t)0);
175   EXPECT_TRUE(e.isInt());
176   const exec_aten::MemoryFormat m = e.to<exec_aten::MemoryFormat>();
177   EXPECT_EQ(m, exec_aten::MemoryFormat::Contiguous);
178 }
179 
TEST_F(EValueTest,Layout)180 TEST_F(EValueTest, Layout) {
181   const EValue e((int64_t)0);
182   EXPECT_TRUE(e.isInt());
183   const exec_aten::Layout l = e.to<exec_aten::Layout>();
184   EXPECT_EQ(l, exec_aten::Layout::Strided);
185 }
186 
TEST_F(EValueTest,Device)187 TEST_F(EValueTest, Device) {
188   const EValue e((int64_t)0);
189   EXPECT_TRUE(e.isInt());
190   const exec_aten::Device d = e.to<exec_aten::Device>();
191   EXPECT_TRUE(d.is_cpu());
192 }
193 
TEST_F(EValueTest,BoxedEvalueList)194 TEST_F(EValueTest, BoxedEvalueList) {
195   // create fake values table to point to
196   EValue values[3] = {
197       EValue((int64_t)1), EValue((int64_t)2), EValue((int64_t)3)};
198   // create wrapped and unwrapped lists
199   EValue* values_p[3] = {&values[0], &values[1], &values[2]};
200   int64_t storage[3] = {0, 0, 0};
201   // Create Object List and test
202   BoxedEvalueList<int64_t> x{values_p, storage, 3};
203   auto unwrapped = x.get();
204   EXPECT_EQ(unwrapped.size(), 3);
205   EXPECT_EQ(unwrapped[0], 1);
206   EXPECT_EQ(unwrapped[1], 2);
207   EXPECT_EQ(unwrapped[2], 3);
208 }
209 
TEST_F(EValueTest,toOptionalTensorList)210 TEST_F(EValueTest, toOptionalTensorList) {
211   // create list, empty evalue ctor gets tag::None
212   EValue values[2] = {EValue(), EValue()};
213   EValue* values_p[2] = {&values[0], &values[1]};
214   exec_aten::optional<exec_aten::Tensor> storage[2];
215   // wrap in array ref
216   BoxedEvalueList<exec_aten::optional<exec_aten::Tensor>> a(
217       values_p, storage, 2);
218 
219   // create Evalue
220   EValue e(a);
221   e.tag = Tag::ListOptionalTensor;
222   EXPECT_TRUE(e.isListOptionalTensor());
223 
224   // Convert back to list
225   exec_aten::ArrayRef<exec_aten::optional<exec_aten::Tensor>> x =
226       e.toListOptionalTensor();
227   EXPECT_EQ(x.size(), 2);
228   EXPECT_FALSE(x[0].has_value());
229   EXPECT_FALSE(x[1].has_value());
230 }
231 
TEST_F(EValueTest,ConstructFromUniquePtr)232 TEST_F(EValueTest, ConstructFromUniquePtr) {
233   TensorFactory<ScalarType::Float> tf;
234   auto tensor_ptr = std::make_unique<exec_aten::Tensor>(tf.ones({2, 3}));
235 
236   EValue evalue(std::move(tensor_ptr));
237 
238   EXPECT_TRUE(evalue.isTensor());
239   EXPECT_EQ(evalue.toTensor().dim(), 2);
240   EXPECT_EQ(evalue.toTensor().numel(), 6);
241 
242   EValue evalue2(std::make_unique<exec_aten::Tensor>(tf.ones({4, 5})));
243 
244   EXPECT_TRUE(evalue2.isTensor());
245   EXPECT_EQ(evalue2.toTensor().dim(), 2);
246   EXPECT_EQ(evalue2.toTensor().numel(), 20);
247 }
248 
TEST_F(EValueTest,ConstructFromSharedPtr)249 TEST_F(EValueTest, ConstructFromSharedPtr) {
250   TensorFactory<ScalarType::Float> tf;
251   auto tensor_ptr = std::make_shared<exec_aten::Tensor>(tf.ones({4, 5}));
252 
253   EValue evalue(tensor_ptr);
254 
255   EXPECT_TRUE(evalue.isTensor());
256   EXPECT_EQ(evalue.toTensor().dim(), 2);
257   EXPECT_EQ(evalue.toTensor().numel(), 20);
258 }
259 
TEST_F(EValueTest,ConstructFromTensorWrapper)260 TEST_F(EValueTest, ConstructFromTensorWrapper) {
261   TensorFactory<ScalarType::Float> tf;
262   TensorWrapper tensor_wrapper(tf.ones({4, 5}));
263 
264   EValue evalue(tensor_wrapper);
265 
266   EXPECT_TRUE(evalue.isTensor());
267   EXPECT_EQ(evalue.toTensor().dim(), 2);
268   EXPECT_EQ(evalue.toTensor().numel(), 20);
269 }
270 
TEST_F(EValueTest,ConstructFromNullPtrAborts)271 TEST_F(EValueTest, ConstructFromNullPtrAborts) {
272   std::unique_ptr<exec_aten::Tensor> null_ptr;
273 
274   ET_EXPECT_DEATH({ EValue evalue(null_ptr); }, "");
275 }
276