xref: /aosp_15_r20/external/perfetto/src/trace_processor/metrics/metrics_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2019 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 "src/trace_processor/metrics/metrics.h"
18 
19 #include <cstdint>
20 #include <optional>
21 #include <string>
22 #include <unordered_map>
23 #include <vector>
24 
25 #include "perfetto/protozero/field.h"
26 #include "perfetto/protozero/proto_decoder.h"
27 #include "perfetto/protozero/proto_utils.h"
28 #include "perfetto/trace_processor/basic_types.h"
29 #include "protos/perfetto/common/descriptor.pbzero.h"
30 #include "src/base/test/status_matchers.h"
31 #include "src/trace_processor/util/descriptors.h"
32 #include "test/gtest_and_gmock.h"
33 
34 #include "protos/perfetto/trace_processor/metrics_impl.pbzero.h"
35 
36 namespace perfetto::trace_processor::metrics {
37 namespace {
38 using base::gtest_matchers::IsError;
39 
40 using base::gtest_matchers::IsError;
41 using testing::IsEmpty;
42 
RunTemplateReplace(const std::string & str,const std::unordered_map<std::string,std::string> & subs)43 std::string RunTemplateReplace(
44     const std::string& str,
45     const std::unordered_map<std::string, std::string>& subs) {
46   std::string out;
47   EXPECT_EQ(TemplateReplace(str, subs, &out), 0);
48   return out;
49 }
50 
TEST(MetricsTest,TemplateReplace)51 TEST(MetricsTest, TemplateReplace) {
52   auto res = RunTemplateReplace("no templates here", {});
53   ASSERT_EQ(res, "no templates here");
54 
55   res = RunTemplateReplace("{{justtemplate}}", {{"justtemplate", "result"}});
56   ASSERT_EQ(res, "result");
57 
58   res = RunTemplateReplace("{{temp1}} {{temp2}}!",
59                            {{"temp1", "hello"}, {"temp2", "world"}});
60   ASSERT_EQ(res, "hello world!");
61 
62   std::string unused;
63   ASSERT_NE(TemplateReplace("{{missing}}", {{}}, &unused), 0);
64 }
65 
66 class ProtoBuilderTest : public ::testing::Test {
67  protected:
68   template <bool repeated>
DecodeSingleFieldProto(const std::vector<uint8_t> & result_ser)69   protozero::TypedProtoDecoder<1, repeated> DecodeSingleFieldProto(
70       const std::vector<uint8_t>& result_ser) {
71     protos::pbzero::ProtoBuilderResult::Decoder result(result_ser.data(),
72                                                        result_ser.size());
73     protos::pbzero::SingleBuilderResult::Decoder single(result.single());
74     protozero::ConstBytes proto_ser = single.protobuf();
75     return protozero::TypedProtoDecoder<1, repeated>(proto_ser.data,
76                                                      proto_ser.size);
77   }
78 };
79 
TEST_F(ProtoBuilderTest,AppendLong)80 TEST_F(ProtoBuilderTest, AppendLong) {
81   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
82 
83   // Create the descriptor version of the following message:
84   // message TestProto {
85   //   optional int64 int_value = 1;
86   // }
87   DescriptorPool pool;
88   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
89                              ".perfetto.protos.TestProto",
90                              ProtoDescriptor::Type::kMessage, std::nullopt);
91   descriptor.AddField(
92       FieldDescriptor("int_value", 1, FieldDescriptorProto::TYPE_INT64, "",
93                       std::vector<uint8_t>(), std::nullopt, false, false));
94 
95   ProtoBuilder builder(&pool, &descriptor);
96   ASSERT_OK(builder.AppendSqlValue("int_value", SqlValue::Long(12345)));
97 
98   auto result_ser = builder.SerializeToProtoBuilderResult();
99   auto proto = DecodeSingleFieldProto<false>(result_ser);
100   const protozero::Field& int_field = proto.Get(1);
101   ASSERT_EQ(int_field.as_int64(), 12345);
102 }
103 
TEST_F(ProtoBuilderTest,AppendDouble)104 TEST_F(ProtoBuilderTest, AppendDouble) {
105   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
106 
107   // Create the descriptor version of the following message:
108   // message TestProto {
109   //   optional double double_value = 1;
110   // }
111   DescriptorPool pool;
112   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
113                              ".perfetto.protos.TestProto",
114                              ProtoDescriptor::Type::kMessage, std::nullopt);
115   descriptor.AddField(
116       FieldDescriptor("double_value", 1, FieldDescriptorProto::TYPE_DOUBLE, "",
117                       std::vector<uint8_t>(), std::nullopt, false, false));
118 
119   ProtoBuilder builder(&pool, &descriptor);
120   ASSERT_OK(builder.AppendSqlValue("double_value", SqlValue::Double(1.2345)));
121 
122   auto result_ser = builder.SerializeToProtoBuilderResult();
123   auto proto = DecodeSingleFieldProto<false>(result_ser);
124   const protozero::Field& db_field = proto.Get(1);
125   ASSERT_DOUBLE_EQ(db_field.as_double(), 1.2345);
126 }
127 
TEST_F(ProtoBuilderTest,AppendString)128 TEST_F(ProtoBuilderTest, AppendString) {
129   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
130 
131   // Create the descriptor version of the following message:
132   // message TestProto {
133   //   optional string string_value = 1;
134   // }
135   DescriptorPool pool;
136   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
137                              ".perfetto.protos.TestProto",
138                              ProtoDescriptor::Type::kMessage, std::nullopt);
139   descriptor.AddField(
140       FieldDescriptor("string_value", 1, FieldDescriptorProto::TYPE_STRING, "",
141                       std::vector<uint8_t>(), std::nullopt, false, false));
142 
143   ProtoBuilder builder(&pool, &descriptor);
144   ASSERT_OK(
145       builder.AppendSqlValue("string_value", SqlValue::String("hello world!")));
146 
147   auto result_ser = builder.SerializeToProtoBuilderResult();
148   auto proto = DecodeSingleFieldProto<false>(result_ser);
149   const protozero::Field& str_field = proto.Get(1);
150   ASSERT_EQ(str_field.as_std_string(), "hello world!");
151 }
152 
TEST_F(ProtoBuilderTest,AppendNested)153 TEST_F(ProtoBuilderTest, AppendNested) {
154   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
155 
156   // Create the descriptor version of the following message:
157   // message TestProto {
158   //   message NestedProto {
159   //     optional int64 nested_int_value = 1;
160   //   }
161   //   optional NestedProto nested_value = 1;
162   // }
163   DescriptorPool pool;
164   ProtoDescriptor nested("file.proto", ".perfetto.protos",
165                          ".perfetto.protos.TestProto.NestedProto",
166                          ProtoDescriptor::Type::kMessage, std::nullopt);
167   nested.AddField(
168       FieldDescriptor("nested_int_value", 1, FieldDescriptorProto::TYPE_INT64,
169                       "", std::vector<uint8_t>(), std::nullopt, false, false));
170 
171   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
172                              ".perfetto.protos.TestProto",
173                              ProtoDescriptor::Type::kMessage, std::nullopt);
174   auto field =
175       FieldDescriptor("nested_value", 1, FieldDescriptorProto::TYPE_MESSAGE,
176                       ".perfetto.protos.TestProto.NestedProto",
177                       std::vector<uint8_t>(), std::nullopt, false, false);
178   field.set_resolved_type_name(".perfetto.protos.TestProto.NestedProto");
179   descriptor.AddField(field);
180 
181   ProtoBuilder nest_builder(&pool, &nested);
182   ASSERT_OK(
183       nest_builder.AppendSqlValue("nested_int_value", SqlValue::Long(789)));
184 
185   auto nest_ser = nest_builder.SerializeToProtoBuilderResult();
186 
187   ProtoBuilder builder(&pool, &descriptor);
188   ASSERT_OK(builder.AppendSqlValue(
189       "nested_value", SqlValue::Bytes(nest_ser.data(), nest_ser.size())));
190 
191   auto result_ser = builder.SerializeToProtoBuilderResult();
192   auto proto = DecodeSingleFieldProto<false>(result_ser);
193   const protozero::Field& nest_field = proto.Get(1);
194   ASSERT_EQ(nest_field.type(),
195             protozero::proto_utils::ProtoWireType::kLengthDelimited);
196 
197   protozero::ConstBytes nest_bytes = nest_field.as_bytes();
198   protozero::TypedProtoDecoder<1, false> nest(nest_bytes.data, nest_bytes.size);
199 
200   const protozero::Field& nest_int_field = nest.Get(1);
201   ASSERT_EQ(nest_int_field.type(),
202             protozero::proto_utils::ProtoWireType::kVarInt);
203   ASSERT_EQ(nest_int_field.as_int64(), 789);
204 }
205 
TEST_F(ProtoBuilderTest,AppendRepeatedEmpty)206 TEST_F(ProtoBuilderTest, AppendRepeatedEmpty) {
207   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
208 
209   // Create the descriptor version of the following message:
210   // message TestProto {
211   //   repeated int64 int_value = 1;
212   // }
213   DescriptorPool pool;
214   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
215                              ".perfetto.protos.TestProto",
216                              ProtoDescriptor::Type::kMessage, std::nullopt);
217   descriptor.AddField(
218       FieldDescriptor("rep_int_value", 1, FieldDescriptorProto::TYPE_INT64, "",
219                       std::vector<uint8_t>(), std::nullopt, true, false));
220 
221   ASSERT_THAT(RepeatedFieldBuilder().SerializeToProtoBuilderResult(),
222               IsEmpty());
223 
224   ProtoBuilder builder(&pool, &descriptor);
225   ASSERT_OK(builder.AppendSqlValue("rep_int_value", SqlValue()));
226 
227   auto proto =
228       DecodeSingleFieldProto<true>(builder.SerializeToProtoBuilderResult());
229   auto it = proto.GetRepeated<int64_t>(1);
230   ASSERT_FALSE(it);
231 }
232 
TEST_F(ProtoBuilderTest,AppendRepeatedPrimitive)233 TEST_F(ProtoBuilderTest, AppendRepeatedPrimitive) {
234   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
235 
236   // Create the descriptor version of the following message:
237   // message TestProto {
238   //   repeated int64 int_value = 1;
239   // }
240   DescriptorPool pool;
241   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
242                              ".perfetto.protos.TestProto",
243                              ProtoDescriptor::Type::kMessage, std::nullopt);
244   descriptor.AddField(
245       FieldDescriptor("rep_int_value", 1, FieldDescriptorProto::TYPE_INT64, "",
246                       std::vector<uint8_t>(), std::nullopt, true, false));
247 
248   RepeatedFieldBuilder rep_builder;
249   rep_builder.AddSqlValue(SqlValue::Long(1234));
250   rep_builder.AddSqlValue(SqlValue::Long(5678));
251 
252   std::vector<uint8_t> rep_ser = rep_builder.SerializeToProtoBuilderResult();
253 
254   ProtoBuilder builder(&pool, &descriptor);
255   ASSERT_OK(builder.AppendSqlValue(
256       "rep_int_value", SqlValue::Bytes(rep_ser.data(), rep_ser.size())));
257 
258   auto proto =
259       DecodeSingleFieldProto<true>(builder.SerializeToProtoBuilderResult());
260   auto it = proto.GetRepeated<int64_t>(1);
261   ASSERT_EQ(*it, 1234);
262   ASSERT_EQ(*++it, 5678);
263   ASSERT_FALSE(++it);
264 }
265 
TEST_F(ProtoBuilderTest,AppendEnums)266 TEST_F(ProtoBuilderTest, AppendEnums) {
267   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
268 
269   // Create the descriptor version of the following enum and message:
270   // enum TestEnum {
271   //   FIRST = 1,
272   //   SECOND = 2,
273   //   THIRD = 3
274   // }
275   // message TestMessage {
276   //   optional TestEnum enum_value = 1;
277   // }
278   DescriptorPool pool;
279   ProtoDescriptor enum_descriptor("file.proto", ".perfetto.protos",
280                                   ".perfetto.protos.TestEnum",
281                                   ProtoDescriptor::Type::kEnum, std::nullopt);
282   enum_descriptor.AddEnumValue(1, "FIRST");
283   enum_descriptor.AddEnumValue(2, "SECOND");
284   enum_descriptor.AddEnumValue(3, "THIRD");
285   pool.AddProtoDescriptorForTesting(enum_descriptor);
286 
287   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
288                              ".perfetto.protos.TestMessage",
289                              ProtoDescriptor::Type::kMessage, std::nullopt);
290   FieldDescriptor enum_field("enum_value", 1, FieldDescriptorProto::TYPE_ENUM,
291                              ".perfetto.protos.TestEnum",
292                              std::vector<uint8_t>(), std::nullopt, false,
293                              false);
294   enum_field.set_resolved_type_name(".perfetto.protos.TestEnum");
295   descriptor.AddField(enum_field);
296   pool.AddProtoDescriptorForTesting(descriptor);
297 
298   ProtoBuilder value_builder(&pool, &descriptor);
299   ASSERT_THAT(value_builder.AppendSqlValue("enum_value", SqlValue::Long(4)),
300               IsError());
301   ASSERT_OK(value_builder.AppendSqlValue("enum_value", SqlValue::Long(3)));
302   ASSERT_THAT(value_builder.AppendSqlValue("enum_value", SqlValue::Long(6)),
303               IsError());
304 
305   auto value_proto = DecodeSingleFieldProto<false>(
306       value_builder.SerializeToProtoBuilderResult());
307   ASSERT_EQ(value_proto.Get(1).as_int32(), 3);
308 
309   ProtoBuilder str_builder(&pool, &descriptor);
310   ASSERT_THAT(
311       str_builder.AppendSqlValue("enum_value", SqlValue::String("FOURTH")),
312       IsError());
313   ASSERT_OK(
314       str_builder.AppendSqlValue("enum_value", SqlValue::String("SECOND")));
315   ASSERT_THAT(
316       str_builder.AppendSqlValue("enum_value", SqlValue::String("OTHER")),
317       IsError());
318 
319   auto str_proto = DecodeSingleFieldProto<false>(
320       str_builder.SerializeToProtoBuilderResult());
321   ASSERT_EQ(str_proto.Get(1).as_int32(), 2);
322 }
323 
324 }  // namespace
325 }  // namespace perfetto::trace_processor::metrics
326