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