Commit 559d6de3 authored by tby's avatar tby Committed by Commit Bot

[Structured metrics] XML config changes.

This makes three updates to the XML config:

1. All events must now have an associated project.

2. All projects now have an ID field.

3. Events no longer need owners fields, only the project itself.

Bug: 1148168
Change-Id: I128d365819f9d54a92b3293b21b9b78a16af059c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2550150
Commit-Queue: Tony Yeoman <tby@chromium.org>
Reviewed-by: default avatarRachel Wong <wrong@chromium.org>
Cr-Commit-Position: refs/heads/master@{#830079}
parent b068d2b8
...@@ -34,19 +34,17 @@ namespace { ...@@ -34,19 +34,17 @@ namespace {
// 32 byte long test key, matching the size of a real key. // 32 byte long test key, matching the size of a real key.
constexpr char kKey[] = "abcdefghijklmnopqrstuvwxyzabcdef"; constexpr char kKey[] = "abcdefghijklmnopqrstuvwxyzabcdef";
// These event and metric names are used for testing. // These project, event, and metric names are used for testing.
// - event: TestEventOne // - project: TestProjectOne
// - metric: TestValueOne // - event: TestEventOne
// - metric: TestValueTwo // - metric: TestMetricOne
// - event: TestEventTwo // - metric: TestMetricTwo
// - metric: TestValueOne // - project: TestProjectTwo
// The name hash of "TestEventOne". // The name hash of "TestProjectOne".
constexpr uint64_t kEventOneHash = UINT64_C(15619026293081468407); constexpr uint64_t kProjectOneHash = UINT64_C(16881314472396226433);
// The name hash of "TestEventTwo". // The name hash of "TestProjectTwo".
constexpr uint64_t kEventTwoHash = UINT64_C(15791833939776536363); constexpr uint64_t kProjectTwoHash = UINT64_C(5876808001962504629);
// The name hash of "TestProject".
constexpr uint64_t kProjectHash = UINT64_C(17426425568333718899);
// The name hash of "TestMetricOne". // The name hash of "TestMetricOne".
constexpr uint64_t kMetricOneHash = UINT64_C(637929385654885975); constexpr uint64_t kMetricOneHash = UINT64_C(637929385654885975);
...@@ -57,9 +55,7 @@ constexpr uint64_t kMetricTwoHash = UINT64_C(14083999144141567134); ...@@ -57,9 +55,7 @@ constexpr uint64_t kMetricTwoHash = UINT64_C(14083999144141567134);
constexpr char kUserId[] = "2070DF23E0D95759"; constexpr char kUserId[] = "2070DF23E0D95759";
// Test values and their hashes. Hashes are the first 8 bytes of: // Test values and their hashes. Hashes are the first 8 bytes of:
// // HMAC_SHA256(concat(hex(kMetricNHash), kValueN), kKey)
// HMAC_SHA256(concat(hex(kMetricNHash), kValueN),
// "abcdefghijklmnopqrstuvwxyzabcdef")
constexpr char kValueOne[] = "value one"; constexpr char kValueOne[] = "value one";
constexpr char kValueTwo[] = "value two"; constexpr char kValueTwo[] = "value two";
constexpr char kValueOneHash[] = "805B8790DC69B773"; constexpr char kValueOneHash[] = "805B8790DC69B773";
...@@ -179,16 +175,16 @@ class KeyDataTest : public testing::Test { ...@@ -179,16 +175,16 @@ class KeyDataTest : public testing::Test {
}; };
// If there is no key store file present, check that new keys are generated for // If there is no key store file present, check that new keys are generated for
// each event, and those keys are of the right length and different from each // each project, and those keys are of the right length and different from each
// other. // other.
TEST_F(KeyDataTest, GeneratesKeysForEvents) { TEST_F(KeyDataTest, GeneratesKeysForProjects) {
StandardSetup(); StandardSetup();
histogram_tester_.ExpectUniqueSample( histogram_tester_.ExpectUniqueSample(
"UMA.StructuredMetrics.KeyValidationState", KeyValidationState::kCreated, "UMA.StructuredMetrics.KeyValidationState", KeyValidationState::kCreated,
NumberOfEvents()); NumberOfEvents());
const std::string key_one = GetString(KeyPath(kEventOneHash)); const std::string key_one = GetString(KeyPath(kProjectOneHash));
const std::string key_two = GetString(KeyPath(kProjectHash)); const std::string key_two = GetString(KeyPath(kProjectTwoHash));
EXPECT_EQ(key_one.size(), 32ul); EXPECT_EQ(key_one.size(), 32ul);
EXPECT_EQ(key_two.size(), 32ul); EXPECT_EQ(key_two.size(), 32ul);
...@@ -203,7 +199,7 @@ TEST_F(KeyDataTest, GeneratesDistinctKeys) { ...@@ -203,7 +199,7 @@ TEST_F(KeyDataTest, GeneratesDistinctKeys) {
for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; ++i) {
ResetState(); ResetState();
StandardSetup(); StandardSetup();
keys.insert(GetString(KeyPath(kEventOneHash))); keys.insert(GetString(KeyPath(kProjectOneHash)));
histogram_tester_.ExpectUniqueSample( histogram_tester_.ExpectUniqueSample(
"UMA.StructuredMetrics.KeyValidationState", "UMA.StructuredMetrics.KeyValidationState",
KeyValidationState::kCreated, NumberOfEvents() * (i + 1)); KeyValidationState::kCreated, NumberOfEvents() * (i + 1));
...@@ -218,7 +214,7 @@ TEST_F(KeyDataTest, ReuseExistingKeys) { ...@@ -218,7 +214,7 @@ TEST_F(KeyDataTest, ReuseExistingKeys) {
histogram_tester_.ExpectBucketCount( histogram_tester_.ExpectBucketCount(
"UMA.StructuredMetrics.KeyValidationState", KeyValidationState::kCreated, "UMA.StructuredMetrics.KeyValidationState", KeyValidationState::kCreated,
NumberOfEvents()); NumberOfEvents());
const std::string key_one = GetString(KeyPath(kEventOneHash)); const std::string key_one = GetString(KeyPath(kProjectOneHash));
CommitKeyStore(); CommitKeyStore();
key_data_.reset(); key_data_.reset();
...@@ -227,7 +223,7 @@ TEST_F(KeyDataTest, ReuseExistingKeys) { ...@@ -227,7 +223,7 @@ TEST_F(KeyDataTest, ReuseExistingKeys) {
histogram_tester_.ExpectBucketCount( histogram_tester_.ExpectBucketCount(
"UMA.StructuredMetrics.KeyValidationState", KeyValidationState::kValid, "UMA.StructuredMetrics.KeyValidationState", KeyValidationState::kValid,
NumberOfEvents()); NumberOfEvents());
const std::string key_two = GetString(KeyPath(kEventOneHash)); const std::string key_two = GetString(KeyPath(kProjectOneHash));
EXPECT_EQ(key_one, key_two); EXPECT_EQ(key_one, key_two);
} }
...@@ -236,57 +232,56 @@ TEST_F(KeyDataTest, ReuseExistingKeys) { ...@@ -236,57 +232,56 @@ TEST_F(KeyDataTest, ReuseExistingKeys) {
// value. // value.
TEST_F(KeyDataTest, DifferentEventsDifferentHashes) { TEST_F(KeyDataTest, DifferentEventsDifferentHashes) {
StandardSetup(); StandardSetup();
// Even though
EXPECT_NE( EXPECT_NE(
key_data_->HashForEventMetric(kEventOneHash, kMetricOneHash, "value"), key_data_->HashForEventMetric(kProjectOneHash, kMetricOneHash, "value"),
key_data_->HashForEventMetric(kEventTwoHash, kMetricOneHash, "value")); key_data_->HashForEventMetric(kProjectTwoHash, kMetricOneHash, "value"));
ExpectNoErrors(); ExpectNoErrors();
} }
// Check that an event has different hashes for different values of the same // Check that an event has different hashes for different metrics with the same
// metric. // value.
TEST_F(KeyDataTest, DifferentMetricsDifferentHashes) { TEST_F(KeyDataTest, DifferentMetricsDifferentHashes) {
StandardSetup(); StandardSetup();
EXPECT_NE( EXPECT_NE(
key_data_->HashForEventMetric(kEventOneHash, kMetricOneHash, "first"), key_data_->HashForEventMetric(kProjectOneHash, kMetricOneHash, "value"),
key_data_->HashForEventMetric(kEventOneHash, kMetricOneHash, "second")); key_data_->HashForEventMetric(kProjectOneHash, kMetricTwoHash, "value"));
ExpectNoErrors(); ExpectNoErrors();
} }
// Check that an event has different hashes for different metrics with the same // Check that an event has different hashes for different values of the same
// value. // metric.
TEST_F(KeyDataTest, DifferentValuesDifferentHashes) { TEST_F(KeyDataTest, DifferentValuesDifferentHashes) {
StandardSetup(); StandardSetup();
EXPECT_NE( EXPECT_NE(
key_data_->HashForEventMetric(kEventOneHash, kMetricOneHash, "value"), key_data_->HashForEventMetric(kProjectOneHash, kMetricOneHash, "first"),
key_data_->HashForEventMetric(kEventOneHash, kMetricTwoHash, "value")); key_data_->HashForEventMetric(kProjectOneHash, kMetricOneHash, "second"));
ExpectNoErrors(); ExpectNoErrors();
} }
// Ensure that KeyData::UserId is the expected value of SHA256(key). // Ensure that KeyData::UserId is the expected value of SHA256(key).
TEST_F(KeyDataTest, CheckUserIDs) { TEST_F(KeyDataTest, CheckUserIDs) {
MakeKeyStore(); MakeKeyStore();
SetKeyData(kEventOneHash, kKey, 0, 90); SetKeyData(kProjectOneHash, kKey, 0, 90);
CommitKeyStore(); CommitKeyStore();
MakeKeyData(); MakeKeyData();
EXPECT_EQ(HashToHex(key_data_->UserEventId(kEventOneHash)), kUserId); EXPECT_EQ(HashToHex(key_data_->UserEventId(kProjectOneHash)), kUserId);
EXPECT_NE(HashToHex(key_data_->UserEventId(kEventTwoHash)), kUserId); EXPECT_NE(HashToHex(key_data_->UserEventId(kProjectTwoHash)), kUserId);
ExpectNoErrors(); ExpectNoErrors();
} }
// Ensure that KeyData::Hash returns expected values for a known key and value. // Ensure that KeyData::Hash returns expected values for a known key and value.
TEST_F(KeyDataTest, CheckHashes) { TEST_F(KeyDataTest, CheckHashes) {
MakeKeyStore(); MakeKeyStore();
SetString(KeyPath(kEventOneHash), kKey); SetString(KeyPath(kProjectOneHash), kKey);
SetKeyData(kEventOneHash, kKey, 0, 90); SetKeyData(kProjectOneHash, kKey, 0, 90);
CommitKeyStore(); CommitKeyStore();
MakeKeyData(); MakeKeyData();
EXPECT_EQ(HashToHex(key_data_->HashForEventMetric(kEventOneHash, EXPECT_EQ(HashToHex(key_data_->HashForEventMetric(kProjectOneHash,
kMetricOneHash, kValueOne)), kMetricOneHash, kValueOne)),
kValueOneHash); kValueOneHash);
EXPECT_EQ(HashToHex(key_data_->HashForEventMetric(kEventOneHash, EXPECT_EQ(HashToHex(key_data_->HashForEventMetric(kProjectOneHash,
kMetricTwoHash, kValueTwo)), kMetricTwoHash, kValueTwo)),
kValueTwoHash); kValueTwoHash);
ExpectNoErrors(); ExpectNoErrors();
...@@ -301,22 +296,22 @@ TEST_F(KeyDataTest, KeysRotated) { ...@@ -301,22 +296,22 @@ TEST_F(KeyDataTest, KeysRotated) {
// logic. // logic.
StandardSetup(); StandardSetup();
const uint64_t first_id = key_data_->UserEventId(kEventOneHash); const uint64_t first_id = key_data_->UserEventId(kProjectOneHash);
const int start_day = (base::Time::Now() - base::Time::UnixEpoch()).InDays(); const int start_day = (base::Time::Now() - base::Time::UnixEpoch()).InDays();
// TestEventOne has a default rotation period of 90 days. // TestEventOne has a default rotation period of 90 days.
EXPECT_EQ(GetInt(RotationPeriodPath(kEventOneHash)), 90); EXPECT_EQ(GetInt(RotationPeriodPath(kProjectOneHash)), 90);
// Set the last rotation to today for testing. // Set the last rotation to today for testing.
SetInt(LastRotationPath(kEventOneHash), start_day); SetInt(LastRotationPath(kProjectOneHash), start_day);
{ {
// Advancing by 50 days, the key should not be rotated. // Advancing by 50 days, the key should not be rotated.
key_data_.reset(); key_data_.reset();
time_.Advance(base::TimeDelta::FromDays(50)); time_.Advance(base::TimeDelta::FromDays(50));
StandardSetup(); StandardSetup();
EXPECT_EQ(key_data_->UserEventId(kEventOneHash), first_id); EXPECT_EQ(key_data_->UserEventId(kProjectOneHash), first_id);
EXPECT_EQ(GetInt(LastRotationPath(kEventOneHash)), start_day); EXPECT_EQ(GetInt(LastRotationPath(kProjectOneHash)), start_day);
} }
{ {
...@@ -325,8 +320,8 @@ TEST_F(KeyDataTest, KeysRotated) { ...@@ -325,8 +320,8 @@ TEST_F(KeyDataTest, KeysRotated) {
key_data_.reset(); key_data_.reset();
time_.Advance(base::TimeDelta::FromDays(50)); time_.Advance(base::TimeDelta::FromDays(50));
StandardSetup(); StandardSetup();
EXPECT_NE(key_data_->UserEventId(kEventOneHash), first_id); EXPECT_NE(key_data_->UserEventId(kProjectOneHash), first_id);
EXPECT_EQ(GetInt(LastRotationPath(kEventOneHash)), start_day + 90); EXPECT_EQ(GetInt(LastRotationPath(kProjectOneHash)), start_day + 90);
} }
{ {
...@@ -335,7 +330,7 @@ TEST_F(KeyDataTest, KeysRotated) { ...@@ -335,7 +330,7 @@ TEST_F(KeyDataTest, KeysRotated) {
key_data_.reset(); key_data_.reset();
time_.Advance(base::TimeDelta::FromDays(453)); time_.Advance(base::TimeDelta::FromDays(453));
StandardSetup(); StandardSetup();
EXPECT_EQ(GetInt(LastRotationPath(kEventOneHash)), start_day + 6 * 90); EXPECT_EQ(GetInt(LastRotationPath(kProjectOneHash)), start_day + 6 * 90);
} }
} }
......
...@@ -27,28 +27,31 @@ namespace structured { ...@@ -27,28 +27,31 @@ namespace structured {
namespace { namespace {
// These event and metric names are used for testing. // These project, event, and metric names are used for testing.
// - event: TestEventOne // - project: TestProjectOne
// - metric: TestMetricOne // - event: TestEventOne
// - metric: TestMetricTwo // - metric: TestMetricOne
// - event: TestsEventTwo // - metric: TestMetricTwo
// - metric: TestMetricThree // - project: TestProjectTwo
// - event: TestEventTwo
// - metric: TestMetricThree
// - event: TestEventThree
// - metric: TestMetricFour
// To test that the right values are calculated for hashed metrics, we need to // To test that the right values are calculated for hashed metrics, we need to
// set up some fake keys that we know the output hashes for. kKeyData contains // set up some fake keys that we know the output hashes for. kKeyData contains
// the JSON for a simple structured_metrics.json file with keys for the test // the JSON for a simple structured_metrics.json file with keys for the test
// events. The two keys are ID'd by the name hashes of "TestEventOne" and // projects, "TestProjectOne" and "TestProjectTwo".
// "TestProject", because TestEventTwo is associated with TestProject.
// TODO(crbug.com/1016655): Once custom rotation periods have been implemented, // TODO(crbug.com/1016655): Once custom rotation periods have been implemented,
// change the large constants to 0. // change the large constants to 0.
constexpr char kKeyData[] = R"({ constexpr char kKeyData[] = R"({
"keys":{ "keys":{
"15619026293081468407":{ "16881314472396226433":{
"rotation_period":1000000, "rotation_period":1000000,
"last_rotation":1000000, "last_rotation":1000000,
"key":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "key":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}, },
"17426425568333718899":{ "5876808001962504629":{
"rotation_period":1000000, "rotation_period":1000000,
"last_rotation":1000000, "last_rotation":1000000,
"key":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" "key":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
...@@ -69,11 +72,13 @@ constexpr uint64_t kMetricOneHash = UINT64_C(637929385654885975); ...@@ -69,11 +72,13 @@ constexpr uint64_t kMetricOneHash = UINT64_C(637929385654885975);
constexpr uint64_t kMetricTwoHash = UINT64_C(14083999144141567134); constexpr uint64_t kMetricTwoHash = UINT64_C(14083999144141567134);
// The name hash of "TestMetricThree". // The name hash of "TestMetricThree".
constexpr uint64_t kMetricThreeHash = UINT64_C(13469300759843809564); constexpr uint64_t kMetricThreeHash = UINT64_C(13469300759843809564);
// The name hash of "TestMetricFour".
// constexpr uint64_t kMetricFourHash = UINT64_C(13469300759843809564);
// The hex-encoded first 8 bytes of SHA256("aaa...a") // The hex-encoded first 8 bytes of SHA256("aaa...a")
constexpr char kKeyOneId[] = "3BA3F5F43B926026"; constexpr char kProjectOneId[] = "3BA3F5F43B926026";
// The hex-encoded first 8 bytes of SHA256("bbb...b") // The hex-encoded first 8 bytes of SHA256("bbb...b")
constexpr char kKeyTwoId[] = "BDB339768BC5E4FE"; constexpr char kProjectTwoId[] = "BDB339768BC5E4FE";
// Test values. // Test values.
constexpr char kValueOne[] = "value one"; constexpr char kValueOne[] = "value one";
...@@ -252,7 +257,7 @@ TEST_F(StructuredMetricsProviderTest, EventsReportedCorrectly) { ...@@ -252,7 +257,7 @@ TEST_F(StructuredMetricsProviderTest, EventsReportedCorrectly) {
{ // First event { // First event
const auto& event = uma.structured_event(0); const auto& event = uma.structured_event(0);
EXPECT_EQ(event.event_name_hash(), kEventOneHash); EXPECT_EQ(event.event_name_hash(), kEventOneHash);
EXPECT_EQ(HashToHex(event.profile_event_id()), kKeyOneId); EXPECT_EQ(HashToHex(event.profile_event_id()), kProjectOneId);
ASSERT_EQ(event.metrics_size(), 2); ASSERT_EQ(event.metrics_size(), 2);
{ // First metric { // First metric
...@@ -274,7 +279,7 @@ TEST_F(StructuredMetricsProviderTest, EventsReportedCorrectly) { ...@@ -274,7 +279,7 @@ TEST_F(StructuredMetricsProviderTest, EventsReportedCorrectly) {
{ // Second event { // Second event
const auto& event = uma.structured_event(1); const auto& event = uma.structured_event(1);
EXPECT_EQ(event.event_name_hash(), kEventTwoHash); EXPECT_EQ(event.event_name_hash(), kEventTwoHash);
EXPECT_EQ(HashToHex(event.profile_event_id()), kKeyTwoId); EXPECT_EQ(HashToHex(event.profile_event_id()), kProjectTwoId);
ASSERT_EQ(event.metrics_size(), 1); ASSERT_EQ(event.metrics_size(), 1);
{ // First metric { // First metric
...@@ -313,9 +318,9 @@ TEST_F(StructuredMetricsProviderTest, EventsWithinProjectReportedWithSameID) { ...@@ -313,9 +318,9 @@ TEST_F(StructuredMetricsProviderTest, EventsWithinProjectReportedWithSameID) {
// Events two and three share a project, so should have the same ID. Event // Events two and three share a project, so should have the same ID. Event
// one should have its own ID. // one should have its own ID.
EXPECT_EQ(HashToHex(event_one.profile_event_id()), kKeyOneId); EXPECT_EQ(HashToHex(event_one.profile_event_id()), kProjectOneId);
EXPECT_EQ(HashToHex(event_two.profile_event_id()), kKeyTwoId); EXPECT_EQ(HashToHex(event_two.profile_event_id()), kProjectTwoId);
EXPECT_EQ(HashToHex(event_three.profile_event_id()), kKeyTwoId); EXPECT_EQ(HashToHex(event_three.profile_event_id()), kProjectTwoId);
histogram_tester_.ExpectTotalCount("UMA.StructuredMetrics.InternalError", 0); histogram_tester_.ExpectTotalCount("UMA.StructuredMetrics.InternalError", 0);
histogram_tester_.ExpectTotalCount("UMA.StructuredMetrics.PrefReadError", 0); histogram_tester_.ExpectTotalCount("UMA.StructuredMetrics.PrefReadError", 0);
......
...@@ -36,15 +36,21 @@ class EventInfo(object): ...@@ -36,15 +36,21 @@ class EventInfo(object):
self.name = sanitize_name(event_obj['name']) self.name = sanitize_name(event_obj['name'])
self.name_hash = HashName(event_obj['name']) self.name_hash = HashName(event_obj['name'])
# If a project is associated with this event, project_obj will be non-None project_name = sanitize_name(project_obj['name'])
# and we should use the project's name as the key name hash. Otherwise, use
# the event's name as the key name hash.
if project_obj:
project_name = sanitize_name(project_obj['name'])
else:
project_name = sanitize_name(event_obj['name'])
self.project_name_hash = HashName(project_name) self.project_name_hash = HashName(project_name)
id_type = project_obj['id']['text']
if id_type == 'uma':
self.project_id_type = 'kUmaId'
elif id_type == 'per-project':
self.project_id_type = 'kProjectId'
elif id_type == 'none':
self.project_id_type = 'kUnidentified'
else:
raise Exception(
"Structured metrics event '{}' has invalid id field '{}'".format(
self.name, id_type))
class MetricInfo(object): class MetricInfo(object):
def __init__(self, json_obj): def __init__(self, json_obj):
......
...@@ -9,6 +9,24 @@ from model import _EVENT_TYPE, _EVENTS_TYPE ...@@ -9,6 +9,24 @@ from model import _EVENT_TYPE, _EVENTS_TYPE
from model import _PROJECT_TYPE, _PROJECTS_TYPE from model import _PROJECT_TYPE, _PROJECTS_TYPE
def projectsHaveRequiredFields(data):
"""Check that projects have all fields required for compilation."""
for project in data[_PROJECTS_TYPE.tag][_PROJECT_TYPE.tag]:
if 'name' not in project:
raise Exception('Structured metrics project has no name')
if 'id' not in project:
raise Exception("Structured metrics project '{}' has no id field.".format(
project['name']))
name_counts = Counter(
project['name']
for project in data[_PROJECTS_TYPE.tag][_PROJECT_TYPE.tag])
for name, count in name_counts.items():
if count != 1:
raise Exception(
"Structured metrics projects have duplicate name '{}'.".format(name))
def eventsReferenceValidProjects(data): def eventsReferenceValidProjects(data):
"""Check that any project referenced by an event exists.""" """Check that any project referenced by an event exists."""
projects = { projects = {
...@@ -24,26 +42,25 @@ def eventsReferenceValidProjects(data): ...@@ -24,26 +42,25 @@ def eventsReferenceValidProjects(data):
event['name'], project_name)) event['name'], project_name))
def projectAndEventNamesDontCollide(data): def metricNamesUniqueWithinEvent(data):
"""Check that there are no events with the same name as a project.""" """Check that no two metrics within an event have the same name."""
projects = {
project['name']
for project in data[_PROJECTS_TYPE.tag][_PROJECT_TYPE.tag]
}
for event in data[_EVENTS_TYPE.tag][_EVENT_TYPE.tag]: for event in data[_EVENTS_TYPE.tag][_EVENT_TYPE.tag]:
if event['name'] in projects: name_counts = Counter(metric['name'] for metric in event[_METRIC_TYPE.tag])
raise Exception(("Structured metrics event and project have the same " for name, count in name_counts.items():
"name: '{}'.").format(event['name'])) if count != 1:
raise Exception(("Structured metrics event '{}' has duplicated metric "
"name '{}'.").format(event['name'], name))
def eventNamesUnique(data): def eventNamesUniqueWithinProject(data):
"""Check that no two events have the same name.""" """Check that no two events in a project have the same name."""
name_counts = Counter( name_counts = Counter((event['project'], event['name'])
event['name'] for event in data[_EVENTS_TYPE.tag][_EVENT_TYPE.tag]) for event in data[_EVENTS_TYPE.tag][_EVENT_TYPE.tag])
for name, count in name_counts.items(): for (project, name), count in name_counts.items():
if count != 1: if count != 1:
raise Exception( raise Exception(
"Structured metrics events have duplicate name '{}'.".format(name)) "Structured metrics project '{}' has events with duplicate "
"name '{}'.".format(project, name))
def projectNamesUnique(data): def projectNamesUnique(data):
...@@ -57,19 +74,9 @@ def projectNamesUnique(data): ...@@ -57,19 +74,9 @@ def projectNamesUnique(data):
"Structured metrics projects have duplicate name '{}'.".format(name)) "Structured metrics projects have duplicate name '{}'.".format(name))
def metricNamesUniqueWithinEvent(data):
"""Check that no two metrics within an event have the same name."""
for event in data[_EVENTS_TYPE.tag][_EVENT_TYPE.tag]:
name_counts = Counter(metric['name'] for metric in event[_METRIC_TYPE.tag])
for name, count in name_counts.items():
if count != 1:
raise Exception(("Structured metrics event '{}' has duplicated metric "
"name '{}'.").format(event['name'], name))
def validate(data): def validate(data):
projectsHaveRequiredFields(data)
eventsReferenceValidProjects(data) eventsReferenceValidProjects(data)
projectAndEventNamesDontCollide(data)
eventNamesUnique(data)
projectNamesUnique(data)
metricNamesUniqueWithinEvent(data) metricNamesUniqueWithinEvent(data)
eventNamesUniqueWithinProject(data)
projectNamesUnique(data)
...@@ -44,7 +44,8 @@ class {event.name} final : public ::metrics::structured::EventBase {{ ...@@ -44,7 +44,8 @@ class {event.name} final : public ::metrics::structured::EventBase {{
~{event.name}() override; ~{event.name}() override;
static constexpr uint64_t kEventNameHash = UINT64_C({event.name_hash}); static constexpr uint64_t kEventNameHash = UINT64_C({event.name_hash});
static constexpr uint64_t kProjectNameHash = UINT64_C({event.project_name_hash});\ static constexpr uint64_t kProjectNameHash = UINT64_C({event.project_name_hash});
static constexpr IdentifierType kIdType = IdentifierType::{event.project_id_type};\
{metric_code} {metric_code}
}};\ }};\
""" """
......
...@@ -20,6 +20,7 @@ _KEEP_ORDER = lambda node: 1 ...@@ -20,6 +20,7 @@ _KEEP_ORDER = lambda node: 1
_OBSOLETE_TYPE = models.TextNodeType('obsolete') _OBSOLETE_TYPE = models.TextNodeType('obsolete')
_OWNER_TYPE = models.TextNodeType('owner', single_line=True) _OWNER_TYPE = models.TextNodeType('owner', single_line=True)
_ID_TYPE = models.TextNodeType('id', single_line=True)
_SUMMARY_TYPE = models.TextNodeType('summary') _SUMMARY_TYPE = models.TextNodeType('summary')
_METRIC_TYPE = models.ObjectNodeType( _METRIC_TYPE = models.ObjectNodeType(
...@@ -67,22 +68,32 @@ _EVENTS_TYPE = models.ObjectNodeType( ...@@ -67,22 +68,32 @@ _EVENTS_TYPE = models.ObjectNodeType(
models.ChildType(_EVENT_TYPE.tag, _EVENT_TYPE, multiple=True), models.ChildType(_EVENT_TYPE.tag, _EVENT_TYPE, multiple=True),
]) ])
_PROJECT_TYPE = models.ObjectNodeType( _PROJECT_TYPE = models.ObjectNodeType('project',
'project', attributes=[
attributes=[ ('name', unicode,
('name', unicode, r'^[A-Z][A-Za-z0-9.]*$'), r'^[A-Z][A-Za-z0-9.]*$'),
], ],
alphabetization=[ alphabetization=[
(_OBSOLETE_TYPE.tag, lambda _: 1), (_OBSOLETE_TYPE.tag, lambda _: 1),
(_OWNER_TYPE.tag, lambda _: 2), (_OWNER_TYPE.tag, lambda _: 2),
(_SUMMARY_TYPE.tag, lambda _: 3), (_ID_TYPE.tag, lambda _: 3),
], (_SUMMARY_TYPE.tag, lambda _: 4),
extra_newlines=(1, 1, 1), ],
children=[ extra_newlines=(1, 1, 1),
models.ChildType(_OBSOLETE_TYPE.tag, _OBSOLETE_TYPE, multiple=False), children=[
models.ChildType(_OWNER_TYPE.tag, _OWNER_TYPE, multiple=True), models.ChildType(_OBSOLETE_TYPE.tag,
models.ChildType(_SUMMARY_TYPE.tag, _SUMMARY_TYPE, multiple=False), _OBSOLETE_TYPE,
]) multiple=False),
models.ChildType(_OWNER_TYPE.tag,
_OWNER_TYPE,
multiple=True),
models.ChildType(_ID_TYPE.tag,
_ID_TYPE,
multiple=False),
models.ChildType(_SUMMARY_TYPE.tag,
_SUMMARY_TYPE,
multiple=False),
])
_PROJECTS_TYPE = models.ObjectNodeType( _PROJECTS_TYPE = models.ObjectNodeType(
'projects', 'projects',
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
<events> <events>
<event name="CrOSActionEvent.FileOpened" project="Hindsight"> <event name="CrOSActionEvent.FileOpened" project="Hindsight">
<owner>charleszhao@chromium.org</owner>
<summary> <summary>
Records whenever a file is opened in the File App on ChromeOS. Records whenever a file is opened in the File App on ChromeOS.
</summary> </summary>
...@@ -32,7 +31,6 @@ ...@@ -32,7 +31,6 @@
</event> </event>
<event name="CrOSActionEvent.SearchResultLaunched" project="Hindsight"> <event name="CrOSActionEvent.SearchResultLaunched" project="Hindsight">
<owner>charleszhao@chromium.org</owner>
<summary> <summary>
Records information about the launch of an item from ChromeOS launcher. Records information about the launch of an item from ChromeOS launcher.
</summary> </summary>
...@@ -61,7 +59,6 @@ ...@@ -61,7 +59,6 @@
</event> </event>
<event name="CrOSActionEvent.SettingChanged" project="Hindsight"> <event name="CrOSActionEvent.SettingChanged" project="Hindsight">
<owner>charleszhao@chromium.org</owner>
<summary> <summary>
Records when a setting is changed; only records quick settings for now. Records when a setting is changed; only records quick settings for now.
</summary> </summary>
...@@ -100,7 +97,6 @@ ...@@ -100,7 +97,6 @@
</event> </event>
<event name="CrOSActionEvent.TabEvent.TabNavigated" project="Hindsight"> <event name="CrOSActionEvent.TabEvent.TabNavigated" project="Hindsight">
<owner>charleszhao@chromium.org</owner>
<summary> <summary>
Records a tab activity of navigating to a new url. Records a tab activity of navigating to a new url.
</summary> </summary>
...@@ -132,7 +128,6 @@ ...@@ -132,7 +128,6 @@
</event> </event>
<event name="CrOSActionEvent.TabEvent.TabOpened" project="Hindsight"> <event name="CrOSActionEvent.TabEvent.TabOpened" project="Hindsight">
<owner>charleszhao@chromium.org</owner>
<summary> <summary>
Records a tab activity of opening a new url with ctrl+click. Records a tab activity of opening a new url with ctrl+click.
</summary> </summary>
...@@ -165,7 +160,6 @@ ...@@ -165,7 +160,6 @@
</event> </event>
<event name="CrOSActionEvent.TabEvent.TabReactivated" project="Hindsight"> <event name="CrOSActionEvent.TabEvent.TabReactivated" project="Hindsight">
<owner>charleszhao@chromium.org</owner>
<summary> <summary>
Records a tab activity of reactivating an existing tab. Records a tab activity of reactivating an existing tab.
</summary> </summary>
...@@ -186,8 +180,7 @@ ...@@ -186,8 +180,7 @@
</metric> </metric>
</event> </event>
<event name="LauncherUsage"> <event name="LauncherUsage" project="LauncherUsage">
<owner>tby@chromium.org</owner>
<summary> <summary>
Records information about the launch of an item (such as an app or a file) Records information about the launch of an item (such as an app or a file)
from the ChromeOS launcher. One event is recorded for every launch from the ChromeOS launcher. One event is recorded for every launch
...@@ -236,8 +229,7 @@ ...@@ -236,8 +229,7 @@
</metric> </metric>
</event> </event>
<event name="TestEventOne"> <event name="TestEventOne" project="TestProjectOne">
<owner>tby@chromium.org</owner>
<summary> <summary>
Event for unit testing, do not use. Event for unit testing, do not use.
</summary> </summary>
...@@ -253,8 +245,7 @@ ...@@ -253,8 +245,7 @@
</metric> </metric>
</event> </event>
<event name="TestEventThree" project="TestProject"> <event name="TestEventThree" project="TestProjectTwo">
<owner>tby@chromium.org</owner>
<summary> <summary>
Event for unit testing, do not use. Event for unit testing, do not use.
</summary> </summary>
...@@ -265,8 +256,7 @@ ...@@ -265,8 +256,7 @@
</metric> </metric>
</event> </event>
<event name="TestEventTwo" project="TestProject"> <event name="TestEventTwo" project="TestProjectTwo">
<owner>tby@chromium.org</owner>
<summary> <summary>
Event for unit testing, do not use. Event for unit testing, do not use.
</summary> </summary>
...@@ -283,13 +273,32 @@ ...@@ -283,13 +273,32 @@
<project name="Hindsight"> <project name="Hindsight">
<owner>charleszhao@chromium.org</owner> <owner>charleszhao@chromium.org</owner>
<owner>tby@chromium.org</owner>
<id>per-project</id>
<summary> <summary>
Project for recording CrOSActions. Project for recording CrOSActions.
</summary> </summary>
</project> </project>
<project name="TestProject"> <project name="LauncherUsage">
<owner>tby@chromium.org</owner>
<id>per-project</id>
<summary>
Records information about search results launched from the launcher.
</summary>
</project>
<project name="TestProjectOne">
<owner>tby@chromium.org</owner>
<id>none</id>
<summary>
Project for unit testing, do not use.
</summary>
</project>
<project name="TestProjectTwo">
<owner>tby@chromium.org</owner> <owner>tby@chromium.org</owner>
<id>none</id>
<summary> <summary>
Project for unit testing, do not use. Project for unit testing, do not use.
</summary> </summary>
......
...@@ -17,7 +17,7 @@ STRUCTURED_XML = path_util.GetInputFile(('tools/metrics/structured/' ...@@ -17,7 +17,7 @@ STRUCTURED_XML = path_util.GetInputFile(('tools/metrics/structured/'
def checkElementOwners(config, element_tag): def checkElementOwners(config, element_tag):
"""Check that every element in the config has at least one owner.""" """Check that every element with the given tag has at least one owner."""
errors = [] errors = []
for node in config.getElementsByTagName(element_tag): for node in config.getElementsByTagName(element_tag):
...@@ -103,7 +103,6 @@ def main(): ...@@ -103,7 +103,6 @@ def main():
[projects] = config.getElementsByTagName('projects') [projects] = config.getElementsByTagName('projects')
errors = [] errors = []
errors.extend(checkElementOwners(events, 'event'))
errors.extend(checkElementOwners(projects, 'project')) errors.extend(checkElementOwners(projects, 'project'))
errors.extend(checkElementsNotDuplicated(events, 'event')) errors.extend(checkElementsNotDuplicated(events, 'event'))
errors.extend(checkElementsNotDuplicated(projects, 'project')) errors.extend(checkElementsNotDuplicated(projects, 'project'))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment