// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/metrics/cast_event_builder_impl.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/metrics_hashes.h"
#include "net/base/ip_address.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/cast_logs.pb.h"
namespace chromecast {
namespace {
// Bitmasks and values for the |transport_connection_id| field when recording
// discovery metrics for mDNS.
const uint32_t kDiscoverySenderMask = 0x0000FFFF;
const uint32_t kDiscoveryUnicastBit = 0x80000000;
} // namespace
//=============================================================================
// CastEventBuilderImplTest
//=============================================================================
TEST(CastEventBuilderImplTest, GetName) {
const char kCastEvent[] = "Cast.Test.Event";
CastEventBuilderImpl builder;
builder.SetName(kCastEvent);
EXPECT_STRCASEEQ(builder.GetName().c_str(), kCastEvent);
auto proto = base::WrapUnique(builder.Build());
EXPECT_EQ(proto->name_hash(), base::HashMetricName(kCastEvent));
}
TEST(CastEventBuilderImplTest, MergeFromCastEventProto) {
const char kOldCastEvent[] = "Cast.Test.Event.Old";
const char kNewCastEvent[] = "Cast.Test.Event.New";
auto old_proto =
base::WrapUnique(CastEventBuilderImpl().SetName(kOldCastEvent).Build());
auto new_proto =
base::WrapUnique(CastEventBuilderImpl().SetName(kNewCastEvent).Build());
auto merged_proto = base::WrapUnique(CastEventBuilderImpl()
.SetName(kOldCastEvent)
.MergeFrom(new_proto.get())
.Build());
EXPECT_NE(old_proto->name_hash(), merged_proto->name_hash());
EXPECT_EQ(new_proto->name_hash(), merged_proto->name_hash());
}
TEST(CastEventBuilderImplTest, DiscoveryEvent_AppSubtype) {
const char kCastEvent[] = "Cast.Discovery.App.Subtype";
CastEventBuilderImpl builder;
builder.SetName(kCastEvent);
builder.SetDiscoveryAppSubtype("1234ABCD");
auto proto = base::WrapUnique(builder.Build());
EXPECT_EQ(proto->name_hash(), base::HashMetricName(kCastEvent));
EXPECT_EQ(proto->app_id(), 0x1234ABCDu);
}
TEST(CastEventBuilderImplTest, DiscoveryEvent_NamespaceSubtype) {
const char kCastEvent[] = "Cast.Discovery.Namespace.Subtype";
CastEventBuilderImpl builder;
builder.SetName(kCastEvent);
builder.SetDiscoveryNamespaceSubtype(
"4abd03646f53722ded0335a84493136da95d5dcb");
auto proto = base::WrapUnique(builder.Build());
EXPECT_EQ(proto->name_hash(), base::HashMetricName(kCastEvent));
EXPECT_EQ(proto->app_id(), 0x4ABD0364u);
}
TEST(CastEventBuilderImplTest, DiscoveryEvent_UnicastFlag) {
const char kCastEvent[] = "Cast.Discovery.Generic.Event";
// Set unicast bit, i.e. multicast was not used for discovery event.
{
CastEventBuilderImpl builder;
builder.SetName(kCastEvent);
builder.SetDiscoveryUnicastFlag(true);
auto proto = base::WrapUnique(builder.Build());
EXPECT_EQ(proto->name_hash(), base::HashMetricName(kCastEvent));
EXPECT_TRUE(proto->transport_connection_id() & kDiscoveryUnicastBit);
}
// Clear unicast bit, i.e. multicast was used for discovery event.
{
CastEventBuilderImpl builder;
builder.SetName(kCastEvent);
builder.SetDiscoveryUnicastFlag(false);
auto proto = base::WrapUnique(builder.Build());
EXPECT_EQ(proto->name_hash(), base::HashMetricName(kCastEvent));
EXPECT_FALSE(proto->transport_connection_id() & kDiscoveryUnicastBit);
}
}
TEST(CastEventBuilderImplTest, DiscoveryEvent_SenderAddress) {
const char kCastEvent[] = "Cast.Discovery.Generic.Event";
// IPv4 sender IP address. The last 2 bytes of the address in network order
// are grabbed and packed into 32-bit value.
{
CastEventBuilderImpl builder;
builder.SetName(kCastEvent);
net::IPAddress sender_ip;
ASSERT_TRUE(sender_ip.AssignFromIPLiteral("172.17.36.97"));
builder.SetDiscoverySender(sender_ip.bytes());
auto proto = base::WrapUnique(builder.Build());
EXPECT_EQ(proto->name_hash(), base::HashMetricName(kCastEvent));
EXPECT_EQ(proto->transport_connection_id() & kDiscoverySenderMask,
0x00002461u);
}
// IPv6 sender IP address. The last 2 bytes of the address in network order
// are grabbed and packed into 32-bit value.
{
CastEventBuilderImpl builder;
builder.SetName(kCastEvent);
net::IPAddress sender_ip;
ASSERT_TRUE(
sender_ip.AssignFromIPLiteral("2620:0:1000:2100:c59c:d8ec:85fe:11fe"));
builder.SetDiscoverySender(sender_ip.bytes());
auto proto = base::WrapUnique(builder.Build());
EXPECT_EQ(proto->name_hash(), base::HashMetricName(kCastEvent));
EXPECT_EQ(proto->transport_connection_id() & kDiscoverySenderMask,
0x000011FEu);
}
// IPv4 mapped address, where IPv4 address is mapped into a IPv6 address. The
// last 2 bytes of the original IPv4 address and mapped IPv4 address should
// match.
// "172.17.36.97" (IPv4) -> "0:0:0:0:0:ffff:ac11:2461" (IPv6)
{
CastEventBuilderImpl builder;
builder.SetName(kCastEvent);
net::IPAddress sender_ip;
ASSERT_TRUE(sender_ip.AssignFromIPLiteral("0:0:0:0:0:ffff:ac11:2461"));
builder.SetDiscoverySender(sender_ip.bytes());
auto proto = base::WrapUnique(builder.Build());
EXPECT_EQ(proto->name_hash(), base::HashMetricName(kCastEvent));
// Last 4 bytes of mapped IPv4 address should match original IPv4 address.
EXPECT_EQ(proto->transport_connection_id() & kDiscoverySenderMask,
0x00002461u);
}
}
TEST(CastEventBuilderImplTest, DiscoveryEvent_AddressAndFlags) {
const char kCastEvent[] = "Cast.Discovery.Generic.Event";
CastEventBuilderImpl builder;
builder.SetName(kCastEvent);
builder.SetDiscoveryUnicastFlag(false);
net::IPAddress sender_ip;
ASSERT_TRUE(sender_ip.AssignFromIPLiteral("172.17.36.97"));
builder.SetDiscoverySender(sender_ip.bytes());
builder.SetDiscoveryUnicastFlag(true);
auto proto = base::WrapUnique(builder.Build());
EXPECT_EQ(proto->name_hash(), base::HashMetricName(kCastEvent));
EXPECT_EQ(proto->transport_connection_id() & kDiscoverySenderMask,
0x00002461u);
EXPECT_TRUE(proto->transport_connection_id() & kDiscoveryUnicastBit);
}
} // namespace chromecast