• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "fcp/aggregation/core/input_tensor_list.h"
17 
18 #include <algorithm>
19 #include <cstdint>
20 #include <memory>
21 #include <utility>
22 
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "fcp/aggregation/core/tensor.h"
26 #include "fcp/aggregation/core/tensor_shape.h"
27 #include "fcp/aggregation/testing/test_data.h"
28 
29 namespace fcp {
30 namespace aggregation {
31 namespace {
32 
33 using testing::Eq;
34 using testing::Not;
35 
36 class InputTensorListTest : public testing::Test {
37  protected:
InputTensorListTest()38   InputTensorListTest()
39       : t1_(Tensor::Create(DT_FLOAT, {1}, CreateTestData<float>({1})).value()),
40         t2_(Tensor::Create(DT_INT32, {2}, CreateTestData<int32_t>({2, 3}))
41                 .value()),
42         t3_(Tensor::Create(DT_INT64, {3}, CreateTestData<int64_t>({4, 5, 6}))
43                 .value()),
44         t4_(Tensor::Create(DT_FLOAT, {4}, CreateTestData<float>({7, 8, 9, 10}))
45                 .value()),
46         t5_(Tensor::Create(DT_INT32, {5},
47                            CreateTestData<int32_t>({11, 12, 13, 14, 15}))
48                 .value()),
49         t6_(Tensor::Create(DT_INT64, {6},
50                            CreateTestData<int64_t>({16, 17, 18, 19, 20, 21}))
51                 .value()) {}
52 
CreateInlined()53   InputTensorList CreateInlined() {
54     return InputTensorList({&t1_, &t2_, &t3_});
55   }
56 
CreateAllocated()57   InputTensorList CreateAllocated() {
58     return InputTensorList({&t1_, &t2_, &t3_, &t4_, &t5_, &t6_});
59   }
60 
61   Tensor t1_;
62   Tensor t2_;
63   Tensor t3_;
64   Tensor t4_;
65   Tensor t5_;
66   Tensor t6_;
67 };
68 
TEST_F(InputTensorListTest,Inlined_Size)69 TEST_F(InputTensorListTest, Inlined_Size) {
70   InputTensorList tensor_list = CreateInlined();
71   EXPECT_THAT(tensor_list.size(), Eq(3));
72 }
73 
TEST_F(InputTensorListTest,Inlined_Iterate)74 TEST_F(InputTensorListTest, Inlined_Iterate) {
75   InputTensorList tensor_list = CreateInlined();
76   auto iter = tensor_list.begin();
77   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{1}));
78   iter++;
79   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{2}));
80   iter++;
81   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{3}));
82   iter++;
83   EXPECT_THAT(iter, Eq(tensor_list.end()));
84 }
85 
TEST_F(InputTensorListTest,Inlined_MoveConstructor_Iterate)86 TEST_F(InputTensorListTest, Inlined_MoveConstructor_Iterate) {
87   InputTensorList moved_tensor_list = CreateInlined();
88   InputTensorList tensor_list(std::move(moved_tensor_list));
89   auto iter = tensor_list.begin();
90   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{1}));
91   iter++;
92   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{2}));
93   iter++;
94   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{3}));
95   iter++;
96   EXPECT_THAT(iter, Eq(tensor_list.end()));
97 }
98 
TEST_F(InputTensorListTest,Inlined_MoveAssignment_Iterate)99 TEST_F(InputTensorListTest, Inlined_MoveAssignment_Iterate) {
100   InputTensorList moved_tensor_list = CreateInlined();
101   // Initially, create the tensor list as an allocated tensor list before
102   // assigning it to an inlined InputTensorList via move assignment.
103   InputTensorList tensor_list = CreateAllocated();
104   tensor_list = std::move(moved_tensor_list);
105   auto iter = tensor_list.begin();
106   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{1}));
107   iter++;
108   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{2}));
109   iter++;
110   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{3}));
111   iter++;
112   EXPECT_THAT(iter, Eq(tensor_list.end()));
113 
114   // Assigning back to the moved variable is valid.
115   moved_tensor_list = std::move(tensor_list);
116   iter = moved_tensor_list.begin();
117   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{1}));
118   iter++;
119   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{2}));
120   iter++;
121   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{3}));
122   iter++;
123   EXPECT_THAT(iter, Eq(moved_tensor_list.end()));
124 }
125 
TEST_F(InputTensorListTest,Inlined_ForEachLoop)126 TEST_F(InputTensorListTest, Inlined_ForEachLoop) {
127   InputTensorList tensor_list = CreateInlined();
128   uint64_t expected_size = 1;
129   for (const Tensor* t : tensor_list) {
130     EXPECT_THAT(t->shape().NumElements(), Eq(expected_size));
131     expected_size++;
132   }
133 }
134 
TEST_F(InputTensorListTest,Inlined_Iterate_MultiPassGuarantee)135 TEST_F(InputTensorListTest, Inlined_Iterate_MultiPassGuarantee) {
136   // Ensure the iterator meets the multi-pass guarantee requirements required
137   // by forward iterators.
138   // (https://en.cppreference.com/w/cpp/iterator/forward_iterator)
139   InputTensorList tensor_list = CreateInlined();
140   auto iterI = tensor_list.begin();
141   auto iterJ = tensor_list.begin();
142   EXPECT_THAT(iterI, Eq(iterJ));
143   EXPECT_THAT(*iterI, Eq(*iterJ));
144   const Tensor* elem = *iterI;
145   iterI++;
146   // iterJ points to the same element as before even though iterI was moved
147   // forward.
148   EXPECT_THAT(elem, Eq(*iterJ));
149   EXPECT_THAT(*iterI, Not(Eq(*iterJ)));
150   // After both iterators are incremented the same number of times they should
151   // again point to the same element.
152   iterI++;
153   iterJ++;
154   iterJ++;
155   EXPECT_THAT(*iterI, Eq(*iterJ));
156 }
157 
TEST_F(InputTensorListTest,Inlined_Iterate_PostincrementAndPreincrement)158 TEST_F(InputTensorListTest, Inlined_Iterate_PostincrementAndPreincrement) {
159   InputTensorList tensor_list = CreateInlined();
160   auto iterI = tensor_list.begin();
161   // If postincrement works as expected, iterJ will be set to the value of iterI
162   // before it is incremented.
163   auto iterJ = iterI++;
164   EXPECT_THAT(iterJ, Eq(tensor_list.begin()));
165   // If preincrement works as expected, iterK should be set to the value of
166   // iterJ after it is incremented, which is now the same as iterI.
167   auto iterK = ++iterJ;
168   EXPECT_THAT(iterK, Eq(iterJ));
169   EXPECT_THAT(iterK, Eq(iterI));
170 }
171 
TEST_F(InputTensorListTest,Inlined_Index)172 TEST_F(InputTensorListTest, Inlined_Index) {
173   InputTensorList tensor_list = CreateInlined();
174   EXPECT_THAT(tensor_list[0]->shape(), Eq(TensorShape{1}));
175   EXPECT_THAT(tensor_list[1]->shape(), Eq(TensorShape{2}));
176   EXPECT_THAT(tensor_list[2]->shape(), Eq(TensorShape{3}));
177 }
178 
TEST_F(InputTensorListTest,Inlined_SizeConstructorAndMutableIndex)179 TEST_F(InputTensorListTest, Inlined_SizeConstructorAndMutableIndex) {
180   InputTensorList tensor_list(3);
181   tensor_list[0] = &t1_;
182   tensor_list[1] = &t2_;
183   tensor_list[2] = &t3_;
184 
185   EXPECT_THAT(tensor_list[0]->shape(), Eq(TensorShape{1}));
186   EXPECT_THAT(tensor_list[1]->shape(), Eq(TensorShape{2}));
187   EXPECT_THAT(tensor_list[2]->shape(), Eq(TensorShape{3}));
188 }
189 
TEST_F(InputTensorListTest,Inlined_SizeConstructor_InitializesPointersToNull)190 TEST_F(InputTensorListTest, Inlined_SizeConstructor_InitializesPointersToNull) {
191   InputTensorList tensor_list(3);
192 
193   EXPECT_THAT(tensor_list[0], Eq(nullptr));
194   EXPECT_THAT(tensor_list[1], Eq(nullptr));
195   EXPECT_THAT(tensor_list[2], Eq(nullptr));
196 }
197 
TEST_F(InputTensorListTest,Allocated_Size)198 TEST_F(InputTensorListTest, Allocated_Size) {
199   InputTensorList tensor_list = CreateAllocated();
200   EXPECT_THAT(tensor_list.size(), Eq(6));
201 }
202 
TEST_F(InputTensorListTest,Allocated_Iterate)203 TEST_F(InputTensorListTest, Allocated_Iterate) {
204   InputTensorList tensor_list = CreateAllocated();
205   auto iter = tensor_list.begin();
206   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{1}));
207   iter++;
208   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{2}));
209   iter++;
210   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{3}));
211   iter++;
212   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{4}));
213   iter++;
214   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{5}));
215   iter++;
216   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{6}));
217   iter++;
218   EXPECT_THAT(iter, Eq(tensor_list.end()));
219 }
220 
TEST_F(InputTensorListTest,Allocated_MoveConstructor_Iterate)221 TEST_F(InputTensorListTest, Allocated_MoveConstructor_Iterate) {
222   InputTensorList moved_tensor_list = CreateAllocated();
223   InputTensorList tensor_list(std::move(moved_tensor_list));
224   auto iter = tensor_list.begin();
225   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{1}));
226   iter++;
227   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{2}));
228   iter++;
229   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{3}));
230   iter++;
231   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{4}));
232   iter++;
233   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{5}));
234   iter++;
235   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{6}));
236   iter++;
237   EXPECT_THAT(iter, Eq(tensor_list.end()));
238 }
239 
TEST_F(InputTensorListTest,Allocated_MoveAssignment_Iterate)240 TEST_F(InputTensorListTest, Allocated_MoveAssignment_Iterate) {
241   InputTensorList moved_tensor_list = CreateAllocated();
242   // Initially, create the tensor list as an inlined tensor list before
243   // assigning it to an inlined InputTensorList via move assignment.
244   InputTensorList tensor_list = CreateInlined();
245   tensor_list = std::move(moved_tensor_list);
246   auto iter = tensor_list.begin();
247   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{1}));
248   iter++;
249   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{2}));
250   iter++;
251   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{3}));
252   iter++;
253   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{4}));
254   iter++;
255   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{5}));
256   iter++;
257   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{6}));
258   iter++;
259   EXPECT_THAT(iter, Eq(tensor_list.end()));
260 
261   // Assigning back to the moved variable is valid.
262   moved_tensor_list = std::move(tensor_list);
263   iter = moved_tensor_list.begin();
264   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{1}));
265   iter++;
266   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{2}));
267   iter++;
268   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{3}));
269   iter++;
270   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{4}));
271   iter++;
272   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{5}));
273   iter++;
274   EXPECT_THAT((*iter)->shape(), Eq(TensorShape{6}));
275   iter++;
276   EXPECT_THAT(iter, Eq(moved_tensor_list.end()));
277 }
278 
TEST_F(InputTensorListTest,Allocated_ForEachLoop)279 TEST_F(InputTensorListTest, Allocated_ForEachLoop) {
280   InputTensorList tensor_list = CreateAllocated();
281   uint64_t expected_size = 1;
282   for (const Tensor* t : tensor_list) {
283     EXPECT_THAT(t->shape().NumElements(), Eq(expected_size));
284     expected_size++;
285   }
286 }
287 
TEST_F(InputTensorListTest,Allocated_Iterate_MultiPassGuarantee)288 TEST_F(InputTensorListTest, Allocated_Iterate_MultiPassGuarantee) {
289   // Ensure the iterator meets the multi-pass guarantee requirements required
290   // by forward iterators
291   // (https://en.cppreference.com/w/cpp/iterator/forward_iterator)
292   InputTensorList tensor_list = CreateAllocated();
293   auto iterI = tensor_list.begin();
294   auto iterJ = tensor_list.begin();
295   EXPECT_THAT(iterI, Eq(iterJ));
296   EXPECT_THAT(*iterI, Eq(*iterJ));
297   const Tensor* elem = *iterI;
298   iterI++;
299   // iterJ points to the same element as before even though iterI was moved
300   // forward.
301   EXPECT_THAT(elem, Eq(*iterJ));
302   EXPECT_THAT(*iterI, Not(Eq(*iterJ)));
303   // After both iterators are incremented the same number of times they should
304   // again point to the same element.
305   iterI++;
306   iterJ++;
307   iterJ++;
308   EXPECT_THAT(*iterI, Eq(*iterJ));
309 }
310 
TEST_F(InputTensorListTest,Allocated_Iterate_PostincrementAndPreincrement)311 TEST_F(InputTensorListTest, Allocated_Iterate_PostincrementAndPreincrement) {
312   InputTensorList tensor_list = CreateAllocated();
313   auto iterI = tensor_list.begin();
314   // If postincrement works as expected, iterJ will be set to the value of iterI
315   // before it is incremented.
316   auto iterJ = iterI++;
317   EXPECT_THAT(iterJ, Eq(tensor_list.begin()));
318   // If preincrement works as expected, iterK should be set to the value of
319   // iterJ after it is incremented, which is now the same as iterI.
320   auto iterK = ++iterJ;
321   EXPECT_THAT(iterK, Eq(iterJ));
322   EXPECT_THAT(iterK, Eq(iterI));
323 }
324 
TEST_F(InputTensorListTest,Allocated_Index)325 TEST_F(InputTensorListTest, Allocated_Index) {
326   InputTensorList tensor_list = CreateAllocated();
327   EXPECT_THAT(tensor_list[0]->shape(), Eq(TensorShape{1}));
328   EXPECT_THAT(tensor_list[1]->shape(), Eq(TensorShape{2}));
329   EXPECT_THAT(tensor_list[2]->shape(), Eq(TensorShape{3}));
330   EXPECT_THAT(tensor_list[3]->shape(), Eq(TensorShape{4}));
331   EXPECT_THAT(tensor_list[4]->shape(), Eq(TensorShape{5}));
332   EXPECT_THAT(tensor_list[5]->shape(), Eq(TensorShape{6}));
333 }
334 
TEST_F(InputTensorListTest,Allocated_SizeConstructorAndMutableIndex)335 TEST_F(InputTensorListTest, Allocated_SizeConstructorAndMutableIndex) {
336   InputTensorList tensor_list(6);
337   tensor_list[0] = &t1_;
338   tensor_list[1] = &t2_;
339   tensor_list[2] = &t3_;
340   tensor_list[3] = &t4_;
341   tensor_list[4] = &t5_;
342   tensor_list[5] = &t6_;
343 
344   EXPECT_THAT(tensor_list[0]->shape(), Eq(TensorShape{1}));
345   EXPECT_THAT(tensor_list[1]->shape(), Eq(TensorShape{2}));
346   EXPECT_THAT(tensor_list[2]->shape(), Eq(TensorShape{3}));
347   EXPECT_THAT(tensor_list[3]->shape(), Eq(TensorShape{4}));
348   EXPECT_THAT(tensor_list[4]->shape(), Eq(TensorShape{5}));
349   EXPECT_THAT(tensor_list[5]->shape(), Eq(TensorShape{6}));
350 }
351 
TEST_F(InputTensorListTest,Allocated_SizeConstructor_InitializesPointersToNull)352 TEST_F(InputTensorListTest,
353        Allocated_SizeConstructor_InitializesPointersToNull) {
354   InputTensorList tensor_list(6);
355 
356   EXPECT_THAT(tensor_list[0], Eq(nullptr));
357   EXPECT_THAT(tensor_list[1], Eq(nullptr));
358   EXPECT_THAT(tensor_list[2], Eq(nullptr));
359   EXPECT_THAT(tensor_list[3], Eq(nullptr));
360   EXPECT_THAT(tensor_list[4], Eq(nullptr));
361   EXPECT_THAT(tensor_list[5], Eq(nullptr));
362 }
363 
364 }  // namespace
365 }  // namespace aggregation
366 }  // namespace fcp
367