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