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