1 /*
2 * Copyright (C) 2021 The Android Open Source Project
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
17 #include "perfetto/tracing/traced_value.h"
18
19 #include <array>
20 #include <deque>
21 #include <forward_list>
22 #include <map>
23 #include <queue>
24 #include <set>
25 #include <sstream>
26 #include <stack>
27 #include <unordered_map>
28 #include <unordered_set>
29
30 #include "perfetto/base/template_util.h"
31 #include "perfetto/protozero/scattered_heap_buffer.h"
32 #include "perfetto/test/traced_value_test_support.h"
33 #include "perfetto/tracing/debug_annotation.h"
34 #include "perfetto/tracing/track_event.h"
35 #include "protos/perfetto/trace/track_event/debug_annotation.gen.h"
36 #include "protos/perfetto/trace/track_event/debug_annotation.pb.h"
37 #include "test/gtest_and_gmock.h"
38
39 namespace perfetto {
40
41 // static asserts checking for conversion support for known types.
42
43 #define ASSERT_TYPE_SUPPORTED(T) \
44 static_assert(check_traced_value_support<T>::value, ""); \
45 static_assert(internal::has_traced_value_support<T>::value, "")
46
47 #define ASSERT_TYPE_NOT_SUPPORTED(T) \
48 static_assert(!internal::has_traced_value_support<T>::value, "")
49
50 struct NonSupportedType {};
51
52 ASSERT_TYPE_SUPPORTED(bool);
53
54 ASSERT_TYPE_NOT_SUPPORTED(NonSupportedType);
55
56 // Integer types.
57 ASSERT_TYPE_SUPPORTED(short int);
58 ASSERT_TYPE_SUPPORTED(unsigned short int);
59 ASSERT_TYPE_SUPPORTED(int);
60 ASSERT_TYPE_SUPPORTED(unsigned int);
61 ASSERT_TYPE_SUPPORTED(long int);
62 ASSERT_TYPE_SUPPORTED(unsigned long int);
63 ASSERT_TYPE_SUPPORTED(long long int);
64 ASSERT_TYPE_SUPPORTED(unsigned long long int);
65
66 // References and const references types.
67 ASSERT_TYPE_SUPPORTED(int&);
68 ASSERT_TYPE_SUPPORTED(const int&);
69 ASSERT_TYPE_NOT_SUPPORTED(NonSupportedType&);
70 ASSERT_TYPE_NOT_SUPPORTED(const NonSupportedType&);
71
72 // Character types.
73 ASSERT_TYPE_SUPPORTED(signed char);
74 ASSERT_TYPE_SUPPORTED(unsigned char);
75 ASSERT_TYPE_SUPPORTED(char);
76 ASSERT_TYPE_SUPPORTED(wchar_t);
77
78 // Float types.
79 ASSERT_TYPE_SUPPORTED(float);
80 ASSERT_TYPE_SUPPORTED(double);
81 ASSERT_TYPE_SUPPORTED(long double);
82
83 // Strings.
84 ASSERT_TYPE_SUPPORTED(const char*);
85 ASSERT_TYPE_SUPPORTED(const char[]);
86 ASSERT_TYPE_SUPPORTED(const char[2]);
87 ASSERT_TYPE_SUPPORTED(std::string);
88
89 // Pointers.
90 ASSERT_TYPE_SUPPORTED(int*);
91 ASSERT_TYPE_SUPPORTED(const int*);
92 ASSERT_TYPE_SUPPORTED(void*);
93 ASSERT_TYPE_SUPPORTED(const void*);
94 ASSERT_TYPE_SUPPORTED(std::nullptr_t);
95 ASSERT_TYPE_NOT_SUPPORTED(NonSupportedType*);
96 ASSERT_TYPE_NOT_SUPPORTED(const NonSupportedType*);
97
98 // Arrays.
99 ASSERT_TYPE_NOT_SUPPORTED(int[]);
100 ASSERT_TYPE_NOT_SUPPORTED(const int[]);
101 ASSERT_TYPE_NOT_SUPPORTED(NonSupportedType[]);
102 ASSERT_TYPE_NOT_SUPPORTED(const NonSupportedType[]);
103 ASSERT_TYPE_SUPPORTED(int (&)[3]);
104 ASSERT_TYPE_SUPPORTED(const int (&)[3]);
105 ASSERT_TYPE_NOT_SUPPORTED(NonSupportedType (&)[3]);
106 ASSERT_TYPE_NOT_SUPPORTED(const NonSupportedType (&)[3]);
107
108 // STL containers.
109 ASSERT_TYPE_SUPPORTED(std::vector<int>);
110 ASSERT_TYPE_NOT_SUPPORTED(std::vector<NonSupportedType>);
111
112 using array_int_t = std::array<int, 4>;
113 ASSERT_TYPE_SUPPORTED(array_int_t);
114 ASSERT_TYPE_SUPPORTED(std::deque<int>);
115 ASSERT_TYPE_SUPPORTED(std::forward_list<int>);
116 ASSERT_TYPE_SUPPORTED(std::list<int>);
117 ASSERT_TYPE_NOT_SUPPORTED(std::stack<int>);
118 ASSERT_TYPE_NOT_SUPPORTED(std::queue<int>);
119 ASSERT_TYPE_NOT_SUPPORTED(std::priority_queue<int>);
120 ASSERT_TYPE_SUPPORTED(std::set<int>);
121 ASSERT_TYPE_SUPPORTED(std::multiset<int>);
122 using map_int_int_t = std::map<int, int>;
123 ASSERT_TYPE_NOT_SUPPORTED(map_int_int_t);
124 using multimap_int_int_t = std::multimap<int, int>;
125 ASSERT_TYPE_NOT_SUPPORTED(multimap_int_int_t);
126 ASSERT_TYPE_SUPPORTED(std::unordered_set<int>);
127 ASSERT_TYPE_SUPPORTED(std::unordered_multiset<int>);
128 using unordered_map_int_int_t = std::unordered_map<int, int>;
129 ASSERT_TYPE_NOT_SUPPORTED(unordered_map_int_int_t);
130 using unordered_multimap_int_int_t = std::unordered_multimap<int, int>;
131 ASSERT_TYPE_NOT_SUPPORTED(unordered_multimap_int_int_t);
132
133 // unique_ptr.
134 ASSERT_TYPE_SUPPORTED(std::unique_ptr<int>);
135 ASSERT_TYPE_NOT_SUPPORTED(std::unique_ptr<NonSupportedType>);
136
137
TEST(TracedValueTest,FlatDictionary_Explicit)138 TEST(TracedValueTest, FlatDictionary_Explicit) {
139 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
140 {
141 auto dict =
142 internal::CreateTracedValueFromProto(message.get()).WriteDictionary();
143 dict.AddItem("bool").WriteBoolean(true);
144 dict.AddItem("double").WriteDouble(0.0);
145 dict.AddItem("int").WriteInt64(2014);
146 dict.AddItem("string").WriteString("string");
147 dict.AddItem("truncated_string").WriteString("truncated_string", 9);
148 dict.AddItem("ptr").WritePointer(reinterpret_cast<void*>(0x1234));
149 }
150 EXPECT_EQ(
151 "{bool:true,double:0,int:2014,string:string,truncated_string:truncated,"
152 "ptr:0x1234}",
153 internal::DebugAnnotationToString(message.SerializeAsString()));
154 }
155
TEST(TracedValueTest,FlatDictionary_Short)156 TEST(TracedValueTest, FlatDictionary_Short) {
157 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
158 {
159 auto dict =
160 internal::CreateTracedValueFromProto(message.get()).WriteDictionary();
161 dict.Add("bool", true);
162 dict.Add("double", 0.0);
163 dict.Add("int", 2014);
164 dict.Add("string", "string");
165 dict.Add("ptr", reinterpret_cast<void*>(0x1234));
166 }
167 EXPECT_EQ("{bool:true,double:0,int:2014,string:string,ptr:0x1234}",
168 internal::DebugAnnotationToString(message.SerializeAsString()));
169 }
170
TEST(TracedValueTest,Hierarchy_Explicit)171 TEST(TracedValueTest, Hierarchy_Explicit) {
172 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
173 {
174 auto root_dict =
175 internal::CreateTracedValueFromProto(message.get()).WriteDictionary();
176 {
177 auto array = root_dict.AddItem("a1").WriteArray();
178 array.AppendItem().WriteInt64(1);
179 array.AppendItem().WriteBoolean(true);
180 {
181 auto dict = array.AppendItem().WriteDictionary();
182 dict.AddItem("i2").WriteInt64(3);
183 }
184 }
185 root_dict.AddItem("b0").WriteBoolean(true);
186 root_dict.AddItem("d0").WriteDouble(0.0);
187 {
188 auto dict1 = root_dict.AddItem("dict1").WriteDictionary();
189 {
190 auto dict2 = dict1.AddItem("dict2").WriteDictionary();
191 dict2.AddItem("b2").WriteBoolean(false);
192 }
193 dict1.AddItem("i1").WriteInt64(2014);
194 dict1.AddItem("s1").WriteString("foo");
195 }
196 root_dict.AddItem("i0").WriteInt64(2014);
197 root_dict.AddItem("s0").WriteString("foo");
198 }
199
200 EXPECT_EQ(
201 "{"
202 "a1:[1,true,{i2:3}],"
203 "b0:true,"
204 "d0:0,"
205 "dict1:{dict2:{b2:false},i1:2014,s1:foo},"
206 "i0:2014,"
207 "s0:foo}",
208 internal::DebugAnnotationToString(message.SerializeAsString()));
209 }
210
TEST(TracedValueTest,Hierarchy_Short)211 TEST(TracedValueTest, Hierarchy_Short) {
212 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
213 {
214 auto root_dict =
215 internal::CreateTracedValueFromProto(message.get()).WriteDictionary();
216 {
217 auto array = root_dict.AddArray("a1");
218 array.Append(1);
219 array.Append(true);
220 {
221 auto dict = array.AppendDictionary();
222 dict.Add("i2", 3);
223 }
224 }
225 root_dict.Add("b0", true);
226 root_dict.Add("d0", 0.0);
227 {
228 auto dict1 = root_dict.AddDictionary("dict1");
229 {
230 auto dict2 = dict1.AddDictionary("dict2");
231 dict2.Add("b2", false);
232 }
233 dict1.Add("i1", 2014);
234 dict1.Add("s1", "foo");
235 }
236 root_dict.Add("i0", 2014);
237 root_dict.Add("s0", "foo");
238 }
239
240 EXPECT_EQ(
241 "{"
242 "a1:[1,true,{i2:3}],"
243 "b0:true,"
244 "d0:0,"
245 "dict1:{dict2:{b2:false},i1:2014,s1:foo},"
246 "i0:2014,"
247 "s0:foo}",
248 internal::DebugAnnotationToString(message.SerializeAsString()));
249 }
250
251 namespace {
252
253 class HasWriteIntoTracedValueConvertorMember {
254 public:
WriteIntoTracedValue(TracedValue context) const255 void WriteIntoTracedValue(TracedValue context) const {
256 auto dict = std::move(context).WriteDictionary();
257 dict.Add("int", 42);
258 dict.Add("bool", false);
259 }
260 };
261
262 class HasWriteIntoTraceConvertorMember {
263 public:
WriteIntoTrace(TracedValue context) const264 void WriteIntoTrace(TracedValue context) const {
265 auto dict = std::move(context).WriteDictionary();
266 dict.Add("int", 42);
267 dict.Add("bool", false);
268 }
269 };
270
271 class HasExternalWriteIntoTraceConvertor {};
272 class HasExternalWriteIntoTracedValueConvertor {};
273
274 class HasAllConversionMethods {
275 public:
WriteIntoTracedValue(TracedValue context) const276 void WriteIntoTracedValue(TracedValue context) const {
277 std::move(context).WriteString("T::WriteIntoTracedValue");
278 }
279
operator ()(TracedValue context) const280 void operator()(TracedValue context) const {
281 std::move(context).WriteString("T::()");
282 }
283 };
284
285 class NoConversions {};
286
287 class HasConstWriteMember {
288 public:
WriteIntoTracedValue(TracedValue context) const289 void WriteIntoTracedValue(TracedValue context) const {
290 std::move(context).WriteString("T::WriteIntoTracedValue const");
291 }
292 };
293
294 class HasNonConstWriteMember {
295 public:
WriteIntoTracedValue(TracedValue context)296 void WriteIntoTracedValue(TracedValue context) {
297 std::move(context).WriteString("T::WriteIntoTracedValue");
298 }
299 };
300
301 class HasConstAndNonConstWriteMember {
302 public:
WriteIntoTracedValue(TracedValue context)303 void WriteIntoTracedValue(TracedValue context) {
304 std::move(context).WriteString("T::WriteIntoTracedValue");
305 }
306
WriteIntoTracedValue(TracedValue context) const307 void WriteIntoTracedValue(TracedValue context) const {
308 std::move(context).WriteString("T::WriteIntoTracedValue const");
309 }
310 };
311
312 } // namespace
313
314 template <>
315 struct TraceFormatTraits<HasExternalWriteIntoTraceConvertor> {
WriteIntoTraceperfetto::TraceFormatTraits316 static void WriteIntoTrace(TracedValue context,
317 const HasExternalWriteIntoTraceConvertor&) {
318 std::move(context).WriteString("TraceFormatTraits::WriteIntoTrace");
319 }
320 };
321
322 template <>
323 struct TraceFormatTraits<HasExternalWriteIntoTracedValueConvertor> {
WriteIntoTracedValueperfetto::TraceFormatTraits324 static void WriteIntoTracedValue(
325 TracedValue context,
326 const HasExternalWriteIntoTracedValueConvertor&) {
327 std::move(context).WriteString("TraceFormatTraits::WriteIntoTracedValue");
328 }
329 };
330
331 template <>
332 struct TraceFormatTraits<HasAllConversionMethods> {
WriteIntoTracedValueperfetto::TraceFormatTraits333 static void WriteIntoTracedValue(TracedValue context,
334 const HasAllConversionMethods&) {
335 std::move(context).WriteString("TraceFormatTraits::WriteIntoTracedValue");
336 }
337 };
338
339 template <typename T>
ToStringWithFallback(T && value,const std::string & fallback)340 std::string ToStringWithFallback(T&& value, const std::string& fallback) {
341 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
342 WriteIntoTracedValueWithFallback(
343 internal::CreateTracedValueFromProto(message.get()),
344 std::forward<T>(value), fallback);
345 return internal::DebugAnnotationToString(message.SerializeAsString());
346 }
347
348 ASSERT_TYPE_SUPPORTED(HasWriteIntoTraceConvertorMember);
349 ASSERT_TYPE_SUPPORTED(HasWriteIntoTracedValueConvertorMember);
350 ASSERT_TYPE_SUPPORTED(HasExternalWriteIntoTraceConvertor);
351 ASSERT_TYPE_SUPPORTED(HasExternalWriteIntoTracedValueConvertor);
352 ASSERT_TYPE_SUPPORTED(HasAllConversionMethods);
353
354 ASSERT_TYPE_SUPPORTED(HasConstWriteMember);
355 ASSERT_TYPE_SUPPORTED(HasConstWriteMember&);
356 ASSERT_TYPE_SUPPORTED(HasConstWriteMember*);
357 ASSERT_TYPE_SUPPORTED(std::unique_ptr<HasConstWriteMember>);
358 ASSERT_TYPE_SUPPORTED(std::vector<HasConstWriteMember>);
359 ASSERT_TYPE_SUPPORTED(const HasConstWriteMember);
360 ASSERT_TYPE_SUPPORTED(const HasConstWriteMember&);
361 ASSERT_TYPE_SUPPORTED(const HasConstWriteMember*);
362 ASSERT_TYPE_SUPPORTED(std::unique_ptr<const HasConstWriteMember>);
363 ASSERT_TYPE_SUPPORTED(const std::vector<HasConstWriteMember>);
364 ASSERT_TYPE_SUPPORTED(std::vector<const HasConstWriteMember*>);
365
366 ASSERT_TYPE_SUPPORTED(HasNonConstWriteMember);
367 ASSERT_TYPE_SUPPORTED(HasNonConstWriteMember&);
368 ASSERT_TYPE_SUPPORTED(HasNonConstWriteMember*);
369 ASSERT_TYPE_SUPPORTED(std::unique_ptr<HasNonConstWriteMember>);
370 ASSERT_TYPE_SUPPORTED(std::vector<HasNonConstWriteMember>);
371 ASSERT_TYPE_NOT_SUPPORTED(const HasNonConstWriteMember);
372 ASSERT_TYPE_NOT_SUPPORTED(const HasNonConstWriteMember&);
373 ASSERT_TYPE_NOT_SUPPORTED(const HasNonConstWriteMember*);
374 ASSERT_TYPE_NOT_SUPPORTED(std::unique_ptr<const HasNonConstWriteMember>);
375 ASSERT_TYPE_NOT_SUPPORTED(const std::vector<HasNonConstWriteMember>);
376 ASSERT_TYPE_NOT_SUPPORTED(std::vector<const HasNonConstWriteMember*>);
377
378 ASSERT_TYPE_SUPPORTED(HasConstAndNonConstWriteMember);
379 ASSERT_TYPE_SUPPORTED(HasConstAndNonConstWriteMember&);
380 ASSERT_TYPE_SUPPORTED(HasConstAndNonConstWriteMember*);
381 ASSERT_TYPE_SUPPORTED(std::unique_ptr<HasConstAndNonConstWriteMember>);
382 ASSERT_TYPE_SUPPORTED(const HasConstAndNonConstWriteMember);
383 ASSERT_TYPE_SUPPORTED(const HasConstAndNonConstWriteMember&);
384 ASSERT_TYPE_SUPPORTED(const HasConstAndNonConstWriteMember*);
385 ASSERT_TYPE_SUPPORTED(std::unique_ptr<const HasConstAndNonConstWriteMember*>);
386
TEST(TracedValueTest,UserDefinedConvertors)387 TEST(TracedValueTest, UserDefinedConvertors) {
388 HasWriteIntoTraceConvertorMember value1;
389 EXPECT_EQ(TracedValueToString(value1), "{int:42,bool:false}");
390 EXPECT_EQ(TracedValueToString(&value1), "{int:42,bool:false}");
391
392 HasWriteIntoTracedValueConvertorMember value2;
393 EXPECT_EQ(TracedValueToString(value2), "{int:42,bool:false}");
394 EXPECT_EQ(TracedValueToString(&value2), "{int:42,bool:false}");
395
396 HasExternalWriteIntoTracedValueConvertor value3;
397 EXPECT_EQ(TracedValueToString(value3),
398 "TraceFormatTraits::WriteIntoTracedValue");
399 EXPECT_EQ(TracedValueToString(&value3),
400 "TraceFormatTraits::WriteIntoTracedValue");
401
402 HasExternalWriteIntoTraceConvertor value4;
403 EXPECT_EQ(TracedValueToString(value4), "TraceFormatTraits::WriteIntoTrace");
404 EXPECT_EQ(TracedValueToString(&value4), "TraceFormatTraits::WriteIntoTrace");
405
406 HasAllConversionMethods value5;
407 EXPECT_EQ(TracedValueToString(value5), "T::WriteIntoTracedValue");
408 EXPECT_EQ(TracedValueToString(&value5), "T::WriteIntoTracedValue");
409 }
410
TEST(TracedValueTest,WriteAsLambda)411 TEST(TracedValueTest, WriteAsLambda) {
412 EXPECT_EQ("42", TracedValueToString([&](TracedValue context) {
413 std::move(context).WriteInt64(42);
414 }));
415 }
416
417 #if PERFETTO_DCHECK_IS_ON()
418 // This death test makes sense only when dchecks are enabled.
TEST(TracedValueTest,FailOnIncorrectUsage)419 TEST(TracedValueTest, FailOnIncorrectUsage) {
420 // A new call to AddItem is not allowed before the previous result is
421 // consumed.
422 EXPECT_DEATH(
423
424 {
425 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
426 auto dict = internal::CreateTracedValueFromProto(message.get())
427 .WriteDictionary();
428 auto scope1 = dict.AddItem("key1");
429 auto scope2 = dict.AddItem("key2");
430 std::move(scope1).WriteInt64(1);
431 std::move(scope2).WriteInt64(2);
432 },
433 "");
434
435 // A new call to AppendItem is not allowed before the previous result is
436 // consumed.
437 EXPECT_DEATH(
438 {
439 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
440 auto array =
441 internal::CreateTracedValueFromProto(message.get()).WriteArray();
442 auto scope1 = array.AppendItem();
443 auto scope2 = array.AppendItem();
444 std::move(scope1).WriteInt64(1);
445 std::move(scope2).WriteInt64(2);
446 },
447 "");
448
449 // Writing to parent scope is not allowed.
450 EXPECT_DEATH(
451 {
452 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
453 auto outer_dict = internal::CreateTracedValueFromProto(message.get())
454 .WriteDictionary();
455 {
456 auto inner_dict = outer_dict.AddDictionary("inner");
457 outer_dict.Add("key", "value");
458 }
459 },
460 "");
461 }
462 #endif // PERFETTO_DCHECK_IS_ON()
463
TEST(TracedValueTest,PrimitiveTypesSupport)464 TEST(TracedValueTest, PrimitiveTypesSupport) {
465 EXPECT_EQ("0x0", TracedValueToString(nullptr));
466 EXPECT_EQ("0x1", TracedValueToString(reinterpret_cast<void*>(1)));
467
468 const int int_value = 1;
469 EXPECT_EQ("1", TracedValueToString(int_value));
470 EXPECT_EQ("1", TracedValueToString(&int_value));
471
472 EXPECT_EQ("1.5", TracedValueToString(1.5));
473 EXPECT_EQ("true", TracedValueToString(true));
474 EXPECT_EQ("foo", TracedValueToString("foo"));
475 EXPECT_EQ("bar", TracedValueToString(std::string("bar")));
476 }
477
TEST(TracedValueTest,UniquePtrSupport)478 TEST(TracedValueTest, UniquePtrSupport) {
479 std::unique_ptr<int> value1;
480 EXPECT_EQ("0x0", TracedValueToString(value1));
481
482 std::unique_ptr<int> value2(new int(4));
483 EXPECT_EQ("4", TracedValueToString(value2));
484 }
485
486 namespace {
487
488 enum OldStyleEnum { kFoo, kBar };
489
490 enum class NewStyleEnum { kValue1, kValue2 };
491
492 enum class EnumWithPrettyPrint { kValue1, kValue2 };
493
494 } // namespace
495
496 template <>
497 struct TraceFormatTraits<EnumWithPrettyPrint> {
WriteIntoTracedValueperfetto::TraceFormatTraits498 static void WriteIntoTracedValue(TracedValue context,
499 EnumWithPrettyPrint value) {
500 switch (value) {
501 case EnumWithPrettyPrint::kValue1:
502 std::move(context).WriteString("value1");
503 return;
504 case EnumWithPrettyPrint::kValue2:
505 std::move(context).WriteString("value2");
506 return;
507 }
508 }
509 };
510
TEST(TracedValueTest,EnumSupport)511 TEST(TracedValueTest, EnumSupport) {
512 EXPECT_EQ(TracedValueToString(kFoo), "0");
513 EXPECT_EQ(TracedValueToString(NewStyleEnum::kValue2), "1");
514 EXPECT_EQ(TracedValueToString(EnumWithPrettyPrint::kValue2), "value2");
515 }
516
TEST(TracedValueTest,ContainerSupport)517 TEST(TracedValueTest, ContainerSupport) {
518 std::vector<std::list<int>> value1{{1, 2}, {3, 4}};
519 EXPECT_EQ("[[1,2],[3,4]]", TracedValueToString(value1));
520 }
521
TEST(TracedValueTest,WriteWithFallback)522 TEST(TracedValueTest, WriteWithFallback) {
523 EXPECT_EQ("1", ToStringWithFallback(1, "fallback"));
524 EXPECT_EQ("true", ToStringWithFallback(true, "fallback"));
525 EXPECT_EQ("fallback", ToStringWithFallback(NonSupportedType(), "fallback"));
526 }
527
TEST(TracedValueTest,ConstAndNotConstSupport)528 TEST(TracedValueTest, ConstAndNotConstSupport) {
529 {
530 HasConstWriteMember value;
531 EXPECT_EQ("T::WriteIntoTracedValue const", TracedValueToString(value));
532 EXPECT_EQ("T::WriteIntoTracedValue const", TracedValueToString(&value));
533
534 std::vector<HasConstWriteMember> arr(1, value);
535 EXPECT_EQ("[T::WriteIntoTracedValue const]", TracedValueToString(arr));
536 }
537
538 {
539 const HasConstWriteMember value;
540 EXPECT_EQ("T::WriteIntoTracedValue const", TracedValueToString(value));
541 EXPECT_EQ("T::WriteIntoTracedValue const", TracedValueToString(&value));
542
543 const std::vector<HasConstWriteMember> arr(1, value);
544 EXPECT_EQ("[T::WriteIntoTracedValue const]", TracedValueToString(arr));
545 }
546
547 {
548 HasNonConstWriteMember value;
549 EXPECT_EQ("T::WriteIntoTracedValue", TracedValueToString(value));
550 EXPECT_EQ("T::WriteIntoTracedValue", TracedValueToString(&value));
551
552 std::vector<HasNonConstWriteMember> arr(1, value);
553 EXPECT_EQ("[T::WriteIntoTracedValue]", TracedValueToString(arr));
554 }
555
556 {
557 HasConstAndNonConstWriteMember value;
558 EXPECT_EQ("T::WriteIntoTracedValue", TracedValueToString(value));
559 EXPECT_EQ("T::WriteIntoTracedValue", TracedValueToString(&value));
560
561 std::vector<HasConstAndNonConstWriteMember> arr(1, value);
562 EXPECT_EQ("[T::WriteIntoTracedValue]", TracedValueToString(arr));
563 }
564
565 {
566 const HasConstAndNonConstWriteMember value;
567 EXPECT_EQ("T::WriteIntoTracedValue const", TracedValueToString(value));
568 EXPECT_EQ("T::WriteIntoTracedValue const", TracedValueToString(&value));
569
570 const std::vector<HasConstAndNonConstWriteMember> arr(1, value);
571 EXPECT_EQ("[T::WriteIntoTracedValue const]", TracedValueToString(arr));
572 }
573 }
574
575 // Note: interning of the dictionary keys is not implemented yet, so there is no
576 // difference in behaviour for StaticString and DynamicString yet.
TEST(TracedValueTest,DictionaryKeys)577 TEST(TracedValueTest, DictionaryKeys) {
578 EXPECT_EQ("{literal:1}", TracedValueToString([&](TracedValue context) {
579 auto dict = std::move(context).WriteDictionary();
580 dict.Add("literal", 1);
581 }));
582
583 EXPECT_EQ("{static:1}", TracedValueToString([&](TracedValue context) {
584 auto dict = std::move(context).WriteDictionary();
585 const char* key = "static";
586 dict.Add(StaticString{key}, 1);
587 }));
588
589 EXPECT_EQ("{dynamic:1}", TracedValueToString([&](TracedValue context) {
590 auto dict = std::move(context).WriteDictionary();
591 std::string key = "dynamic";
592 dict.Add(DynamicString{key.data()}, 1);
593 }));
594
595 EXPECT_EQ("{dynamic:1}", TracedValueToString([&](TracedValue context) {
596 auto dict = std::move(context).WriteDictionary();
597 std::string key = "dynamic";
598 dict.Add(DynamicString{key.data(), key.length()}, 1);
599 }));
600
601 EXPECT_EQ("{dynamic:1}", TracedValueToString([&](TracedValue context) {
602 auto dict = std::move(context).WriteDictionary();
603 std::string key = "dynamic";
604 dict.Add(DynamicString{key}, 1);
605 }));
606 }
607
TEST(TracedValueTest,EmptyDict)608 TEST(TracedValueTest, EmptyDict) {
609 EXPECT_EQ("{}", TracedValueToString([&](TracedValue context) {
610 auto dict = std::move(context).WriteDictionary();
611 }));
612 }
613
TEST(TracedValueTest,EmptyArray)614 TEST(TracedValueTest, EmptyArray) {
615 // For now we do not distinguish between empty arrays and empty dicts on proto
616 // level as trace processor ignores them anyway.
617 EXPECT_EQ("{}", TracedValueToString([&](TracedValue context) {
618 auto array = std::move(context).WriteArray();
619 }));
620 }
621
622 } // namespace perfetto
623