зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 5c047b770037 (bug 1302663) for eslint failures
--HG-- extra : rebase_source : 2cd803ba85c6e0a17f0f9b7135e0b01f652557bc
This commit is contained in:
Родитель
fa25c7a85c
Коммит
e599757126
|
@ -1,52 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#ifndef TelemetryEventInfo_h__
|
|
||||||
#define TelemetryEventInfo_h__
|
|
||||||
|
|
||||||
// This module is internal to Telemetry. The structures here hold data that
|
|
||||||
// describe events.
|
|
||||||
// It should only be used by TelemetryEventData.h and TelemetryEvent.cpp.
|
|
||||||
//
|
|
||||||
// For the public interface to Telemetry functionality, see Telemetry.h.
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
struct CommonEventInfo {
|
|
||||||
// Indices for the category and expiration strings.
|
|
||||||
uint32_t category_offset;
|
|
||||||
uint32_t expiration_version_offset;
|
|
||||||
|
|
||||||
// The index and count for the extra key offsets in the extra table.
|
|
||||||
uint32_t extra_index;
|
|
||||||
uint32_t extra_count;
|
|
||||||
|
|
||||||
// The day since UNIX epoch that this probe expires on.
|
|
||||||
uint32_t expiration_day;
|
|
||||||
|
|
||||||
// The dataset this event is recorded in.
|
|
||||||
uint32_t dataset;
|
|
||||||
|
|
||||||
// Convenience functions for accessing event strings.
|
|
||||||
const char* expiration_version() const;
|
|
||||||
const char* category() const;
|
|
||||||
const char* extra_key(uint32_t index) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct EventInfo {
|
|
||||||
// The corresponding CommonEventInfo.
|
|
||||||
const CommonEventInfo& common_info;
|
|
||||||
|
|
||||||
// Indices for the method & object strings.
|
|
||||||
uint32_t method_offset;
|
|
||||||
uint32_t object_offset;
|
|
||||||
|
|
||||||
const char* method() const;
|
|
||||||
const char* object() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
#endif // TelemetryEventInfo_h__
|
|
|
@ -1,53 +0,0 @@
|
||||||
# This category contains event entries used for Telemetry tests.
|
|
||||||
# They will not be sent out with any pings.
|
|
||||||
telemetry.test:
|
|
||||||
- methods: ["test1", "test2"]
|
|
||||||
objects: ["object1", "object2"]
|
|
||||||
bug_numbers: [1286606]
|
|
||||||
notification_emails: ["telemetry-client-dev@mozilla.com"]
|
|
||||||
description: This is a test entry for Telemetry.
|
|
||||||
expiry_date: never
|
|
||||||
extra_keys:
|
|
||||||
key1: This is just a test description.
|
|
||||||
key2: This is another test description.
|
|
||||||
- methods: ["test_optout"]
|
|
||||||
objects: ["object1", "object2"]
|
|
||||||
bug_numbers: [1286606]
|
|
||||||
notification_emails: ["telemetry-client-dev@mozilla.com"]
|
|
||||||
description: This is an opt-out test entry.
|
|
||||||
expiry_date: never
|
|
||||||
release_channel_collection: opt-out
|
|
||||||
extra_keys:
|
|
||||||
key1: This is just a test description.
|
|
||||||
- methods: ["test_expired_version"]
|
|
||||||
objects: ["object1", "object2"]
|
|
||||||
bug_numbers: [1286606]
|
|
||||||
notification_emails: ["telemetry-client-dev@mozilla.com"]
|
|
||||||
description: This is a test entry with an expired version.
|
|
||||||
expiry_version: "3.6"
|
|
||||||
- methods: ["test_expired_date"]
|
|
||||||
objects: ["object1", "object2"]
|
|
||||||
bug_numbers: [1286606]
|
|
||||||
notification_emails: ["telemetry-client-dev@mozilla.com"]
|
|
||||||
description: This is a test entry with an expired date.
|
|
||||||
expiry_date: 2014-01-28
|
|
||||||
- methods: ["test_not_expired_optout"]
|
|
||||||
objects: ["object1"]
|
|
||||||
bug_numbers: [1286606]
|
|
||||||
notification_emails: ["telemetry-client-dev@mozilla.com"]
|
|
||||||
description: This is an opt-out test entry with unexpired date and version.
|
|
||||||
release_channel_collection: opt-out
|
|
||||||
expiry_date: 2099-01-01
|
|
||||||
expiry_version: "999.0"
|
|
||||||
|
|
||||||
# This is a secondary category used for Telemetry tests.
|
|
||||||
# The events here will not be sent out with any pings.
|
|
||||||
telemetry.test.second:
|
|
||||||
- methods: ["test"]
|
|
||||||
objects: ["object1", "object2", "object3"]
|
|
||||||
bug_numbers: [1286606]
|
|
||||||
notification_emails: ["telemetry-client-dev@mozilla.com"]
|
|
||||||
description: This is a test entry for Telemetry.
|
|
||||||
expiry_date: never
|
|
||||||
extra_keys:
|
|
||||||
key1: This is just a test description.
|
|
|
@ -1,142 +0,0 @@
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
# Write out event information for C++. The events are defined
|
|
||||||
# in a file provided as a command-line argument.
|
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
from shared_telemetry_utils import StringTable, static_assert
|
|
||||||
|
|
||||||
import parse_events
|
|
||||||
import sys
|
|
||||||
import itertools
|
|
||||||
|
|
||||||
# The banner/text at the top of the generated file.
|
|
||||||
banner = """/* This file is auto-generated, only for internal use in TelemetryEvent.h,
|
|
||||||
see gen-event-data.py. */
|
|
||||||
"""
|
|
||||||
|
|
||||||
file_header = """\
|
|
||||||
#ifndef mozilla_TelemetryEventData_h
|
|
||||||
#define mozilla_TelemetryEventData_h
|
|
||||||
#include "EventInfo.h"
|
|
||||||
namespace {
|
|
||||||
"""
|
|
||||||
|
|
||||||
file_footer = """\
|
|
||||||
} // namespace
|
|
||||||
#endif // mozilla_TelemetryEventData_h
|
|
||||||
"""
|
|
||||||
|
|
||||||
def write_extra_table(events, output, string_table):
|
|
||||||
table_name = "gExtraKeysTable"
|
|
||||||
extra_table = []
|
|
||||||
extra_count = 0
|
|
||||||
|
|
||||||
print("const uint32_t %s[] = {" % table_name, file=output)
|
|
||||||
|
|
||||||
for e in events:
|
|
||||||
extra_index = 0
|
|
||||||
extra_keys = e.extra_keys
|
|
||||||
if len(extra_keys) > 0:
|
|
||||||
extra_index = extra_count
|
|
||||||
extra_count += len(extra_keys)
|
|
||||||
indexes = string_table.stringIndexes(extra_keys)
|
|
||||||
|
|
||||||
print(" // %s, [%s], [%s]" % (
|
|
||||||
e.category,
|
|
||||||
", ".join(e.methods),
|
|
||||||
", ".join(e.objects)),
|
|
||||||
file=output)
|
|
||||||
print(" // extra_keys: %s" % ", ".join(extra_keys), file=output)
|
|
||||||
print(" %s," % ", ".join(map(str, indexes)),
|
|
||||||
file=output)
|
|
||||||
|
|
||||||
extra_table.append((extra_index, len(extra_keys)))
|
|
||||||
|
|
||||||
print("};", file=output)
|
|
||||||
static_assert(output, "sizeof(%s) <= UINT32_MAX" % table_name,
|
|
||||||
"index overflow")
|
|
||||||
|
|
||||||
return extra_table
|
|
||||||
|
|
||||||
def write_common_event_table(events, output, string_table, extra_table):
|
|
||||||
table_name = "gCommonEventInfo"
|
|
||||||
extra_count = 0
|
|
||||||
|
|
||||||
print("const CommonEventInfo %s[] = {" % table_name, file=output)
|
|
||||||
for e,extras in zip(events, extra_table):
|
|
||||||
# Write a comment to make the file human-readable.
|
|
||||||
print(" // category: %s" % e.category, file=output)
|
|
||||||
print(" // methods: [%s]" % ", ".join(e.methods), file=output)
|
|
||||||
print(" // objects: [%s]" % ", ".join(e.objects), file=output)
|
|
||||||
|
|
||||||
# Write the common info structure
|
|
||||||
print(" {%d, %d, %d, %d, %d, %s}," %
|
|
||||||
(string_table.stringIndex(e.category),
|
|
||||||
string_table.stringIndex(e.expiry_version),
|
|
||||||
extras[0], # extra keys index
|
|
||||||
extras[1], # extra keys count
|
|
||||||
e.expiry_day,
|
|
||||||
e.dataset),
|
|
||||||
file=output)
|
|
||||||
|
|
||||||
print("};", file=output)
|
|
||||||
static_assert(output, "sizeof(%s) <= UINT32_MAX" % table_name,
|
|
||||||
"index overflow")
|
|
||||||
|
|
||||||
def write_event_table(events, output, string_table):
|
|
||||||
table_name = "gEventInfo"
|
|
||||||
print("const EventInfo %s[] = {" % table_name, file=output)
|
|
||||||
|
|
||||||
for common_info_index,e in enumerate(events):
|
|
||||||
for method_name, object_name in itertools.product(e.methods, e.objects):
|
|
||||||
print(" // category: %s, method: %s, object: %s" %
|
|
||||||
(e.category, method_name, object_name),
|
|
||||||
file=output)
|
|
||||||
|
|
||||||
print(" {gCommonEventInfo[%d], %d, %d}," %
|
|
||||||
(common_info_index,
|
|
||||||
string_table.stringIndex(method_name),
|
|
||||||
string_table.stringIndex(object_name)),
|
|
||||||
file=output)
|
|
||||||
|
|
||||||
print("};", file=output)
|
|
||||||
static_assert(output, "sizeof(%s) <= UINT32_MAX" % table_name,
|
|
||||||
"index overflow")
|
|
||||||
|
|
||||||
def main(output, *filenames):
|
|
||||||
# Load the event data.
|
|
||||||
if len(filenames) > 1:
|
|
||||||
raise Exception('We don\'t support loading from more than one file.')
|
|
||||||
events = parse_events.load_events(filenames[0])
|
|
||||||
|
|
||||||
# Write the scalar data file.
|
|
||||||
print(banner, file=output)
|
|
||||||
print(file_header, file=output)
|
|
||||||
|
|
||||||
# Write the extra keys table.
|
|
||||||
string_table = StringTable()
|
|
||||||
extra_table = write_extra_table(events, output, string_table)
|
|
||||||
print("", file=output)
|
|
||||||
|
|
||||||
# Write a table with the common event data.
|
|
||||||
write_common_event_table(events, output, string_table, extra_table)
|
|
||||||
print("", file=output)
|
|
||||||
|
|
||||||
# Write the data for individual events.
|
|
||||||
write_event_table(events, output, string_table)
|
|
||||||
print("", file=output)
|
|
||||||
|
|
||||||
# Write the string table.
|
|
||||||
string_table_name = "gEventsStringTable"
|
|
||||||
string_table.writeDefinition(output, string_table_name)
|
|
||||||
static_assert(output, "sizeof(%s) <= UINT32_MAX" % string_table_name,
|
|
||||||
"index overflow")
|
|
||||||
print("", file=output)
|
|
||||||
|
|
||||||
print(file_footer, file=output)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main(sys.stdout, *sys.argv[1:])
|
|
|
@ -1,73 +0,0 @@
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
# Write out C++ enum definitions that represent the different event types.
|
|
||||||
#
|
|
||||||
# The events are defined in files provided as command-line arguments.
|
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import parse_events
|
|
||||||
|
|
||||||
banner = """/* This file is auto-generated, see gen-event-enum.py. */
|
|
||||||
"""
|
|
||||||
|
|
||||||
file_header = """\
|
|
||||||
#ifndef mozilla_TelemetryEventEnums_h
|
|
||||||
#define mozilla_TelemetryEventEnums_h
|
|
||||||
namespace mozilla {
|
|
||||||
namespace Telemetry {
|
|
||||||
namespace EventID {
|
|
||||||
"""
|
|
||||||
|
|
||||||
file_footer = """\
|
|
||||||
} // namespace EventID
|
|
||||||
} // namespace mozilla
|
|
||||||
} // namespace Telemetry
|
|
||||||
#endif // mozilla_TelemetryEventEnums_h
|
|
||||||
"""
|
|
||||||
|
|
||||||
def main(output, *filenames):
|
|
||||||
# Load the events first.
|
|
||||||
if len(filenames) > 1:
|
|
||||||
raise Exception('We don\'t support loading from more than one file.')
|
|
||||||
events = parse_events.load_events(filenames[0])
|
|
||||||
|
|
||||||
grouped = dict()
|
|
||||||
index = 0
|
|
||||||
for e in events:
|
|
||||||
category = e.category
|
|
||||||
if not category in grouped:
|
|
||||||
grouped[category] = []
|
|
||||||
grouped[category].append((index, e))
|
|
||||||
index += len(e.enum_labels)
|
|
||||||
|
|
||||||
# Write the enum file.
|
|
||||||
print(banner, file=output)
|
|
||||||
print(file_header, file=output);
|
|
||||||
|
|
||||||
for category,indexed in grouped.iteritems():
|
|
||||||
category_cpp = indexed[0][1].category_cpp
|
|
||||||
|
|
||||||
print("// category: %s" % category, file=output)
|
|
||||||
print("enum class %s : uint32_t {" % category_cpp, file=output)
|
|
||||||
|
|
||||||
for event_index,e in indexed:
|
|
||||||
cpp_guard = e.cpp_guard
|
|
||||||
if cpp_guard:
|
|
||||||
print("#if defined(%s)" % cpp_guard, file=output)
|
|
||||||
for offset,label in enumerate(e.enum_labels):
|
|
||||||
print(" %s = %d," % (label, event_index + offset), file=output)
|
|
||||||
if cpp_guard:
|
|
||||||
print("#endif", file=output)
|
|
||||||
|
|
||||||
print("};\n", file=output)
|
|
||||||
|
|
||||||
print("const uint32_t EventCount = %d;\n" % index, file=output)
|
|
||||||
|
|
||||||
print(file_footer, file=output)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main(sys.stdout, *sys.argv[1:])
|
|
|
@ -6,22 +6,6 @@
|
||||||
|
|
||||||
HAS_MISC_RULE = True
|
HAS_MISC_RULE = True
|
||||||
|
|
||||||
include('/ipc/chromium/chromium-config.mozbuild')
|
|
||||||
|
|
||||||
FINAL_LIBRARY = 'xul'
|
|
||||||
|
|
||||||
DEFINES['MOZ_APP_VERSION'] = '"%s"' % CONFIG['MOZ_APP_VERSION']
|
|
||||||
|
|
||||||
LOCAL_INCLUDES += [
|
|
||||||
'/xpcom/build',
|
|
||||||
'/xpcom/threads',
|
|
||||||
]
|
|
||||||
|
|
||||||
SPHINX_TREES['telemetry'] = 'docs'
|
|
||||||
|
|
||||||
if CONFIG['GNU_CXX']:
|
|
||||||
CXXFLAGS += ['-Wno-error=shadow']
|
|
||||||
|
|
||||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||||
BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
|
BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
|
||||||
|
|
||||||
|
@ -74,16 +58,17 @@ TESTING_JS_MODULES += [
|
||||||
'tests/unit/TelemetryArchiveTesting.jsm',
|
'tests/unit/TelemetryArchiveTesting.jsm',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
include('/ipc/chromium/chromium-config.mozbuild')
|
||||||
|
|
||||||
|
FINAL_LIBRARY = 'xul'
|
||||||
|
|
||||||
GENERATED_FILES = [
|
GENERATED_FILES = [
|
||||||
'TelemetryEventData.h',
|
|
||||||
'TelemetryEventEnums.h',
|
|
||||||
'TelemetryHistogramData.inc',
|
'TelemetryHistogramData.inc',
|
||||||
'TelemetryHistogramEnums.h',
|
'TelemetryHistogramEnums.h',
|
||||||
'TelemetryScalarData.h',
|
'TelemetryScalarData.h',
|
||||||
'TelemetryScalarEnums.h',
|
'TelemetryScalarEnums.h',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Generate histogram files.
|
|
||||||
histogram_files = [
|
histogram_files = [
|
||||||
'Histograms.json',
|
'Histograms.json',
|
||||||
'/dom/base/UseCounters.conf',
|
'/dom/base/UseCounters.conf',
|
||||||
|
@ -98,7 +83,7 @@ enums = GENERATED_FILES['TelemetryHistogramEnums.h']
|
||||||
enums.script = 'gen-histogram-enum.py'
|
enums.script = 'gen-histogram-enum.py'
|
||||||
enums.inputs = histogram_files
|
enums.inputs = histogram_files
|
||||||
|
|
||||||
# Generate scalar files.
|
# Generate Scalars
|
||||||
scalar_files = [
|
scalar_files = [
|
||||||
'Scalars.yaml',
|
'Scalars.yaml',
|
||||||
]
|
]
|
||||||
|
@ -111,18 +96,17 @@ scalar_enums = GENERATED_FILES['TelemetryScalarEnums.h']
|
||||||
scalar_enums.script = 'gen-scalar-enum.py'
|
scalar_enums.script = 'gen-scalar-enum.py'
|
||||||
scalar_enums.inputs = scalar_files
|
scalar_enums.inputs = scalar_files
|
||||||
|
|
||||||
# Generate event files.
|
DEFINES['MOZ_APP_VERSION'] = '"%s"' % CONFIG['MOZ_APP_VERSION']
|
||||||
event_files = [
|
|
||||||
'Events.yaml',
|
LOCAL_INCLUDES += [
|
||||||
|
'/xpcom/build',
|
||||||
|
'/xpcom/threads',
|
||||||
]
|
]
|
||||||
|
|
||||||
event_data = GENERATED_FILES['TelemetryEventData.h']
|
SPHINX_TREES['telemetry'] = 'docs'
|
||||||
event_data.script = 'gen-event-data.py'
|
|
||||||
event_data.inputs = event_files
|
|
||||||
|
|
||||||
event_enums = GENERATED_FILES['TelemetryEventEnums.h']
|
if CONFIG['GNU_CXX']:
|
||||||
event_enums.script = 'gen-event-enum.py'
|
CXXFLAGS += ['-Wno-error=shadow']
|
||||||
event_enums.inputs = event_files
|
|
||||||
|
|
||||||
with Files('**'):
|
with Files('**'):
|
||||||
BUG_COMPONENT = ('Toolkit', 'Telemetry')
|
BUG_COMPONENT = ('Toolkit', 'Telemetry')
|
||||||
|
|
|
@ -1,271 +0,0 @@
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
import re
|
|
||||||
import yaml
|
|
||||||
import itertools
|
|
||||||
import datetime
|
|
||||||
import string
|
|
||||||
from shared_telemetry_utils import add_expiration_postfix
|
|
||||||
|
|
||||||
MAX_CATEGORY_NAME_LENGTH = 100
|
|
||||||
MAX_METHOD_NAME_LENGTH = 40
|
|
||||||
MAX_OBJECT_NAME_LENGTH = 40
|
|
||||||
MAX_EXTRA_KEYS_COUNT = 20
|
|
||||||
MAX_EXTRA_KEY_NAME_LENGTH = 20
|
|
||||||
|
|
||||||
IDENTIFIER_PATTERN = r'^[a-zA-Z][a-zA-Z0-9_.]+[a-zA-Z0-9]$'
|
|
||||||
DATE_PATTERN = r'^[0-9]{4}-[0-9]{2}-[0-9]{2}$'
|
|
||||||
|
|
||||||
def nice_type_name(t):
|
|
||||||
if isinstance(t, basestring):
|
|
||||||
return "string"
|
|
||||||
return t.__name__
|
|
||||||
|
|
||||||
def convert_to_cpp_identifier(s, sep):
|
|
||||||
return string.capwords(s, sep).replace(sep, "")
|
|
||||||
|
|
||||||
class OneOf:
|
|
||||||
"""This is a placeholder type for the TypeChecker below.
|
|
||||||
It signals that the checked value should match one of the following arguments
|
|
||||||
passed to the TypeChecker constructor.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class TypeChecker:
|
|
||||||
"""This implements a convenience type TypeChecker to make the validation code more readable."""
|
|
||||||
def __init__(self, kind, *args):
|
|
||||||
"""This takes 1-3 arguments, specifying the value type to check for.
|
|
||||||
It supports:
|
|
||||||
- atomic values, e.g.: TypeChecker(int)
|
|
||||||
- list values, e.g.: TypeChecker(list, basestring)
|
|
||||||
- dict values, e.g.: TypeChecker(dict, basestring, int)
|
|
||||||
- atomic values that can have different types, e.g.: TypeChecker(OneOf, int, date)"""
|
|
||||||
self._kind = kind
|
|
||||||
self._args = args
|
|
||||||
|
|
||||||
def check(self, key, value):
|
|
||||||
# Check fields that can be one of two different types.
|
|
||||||
if self._kind is OneOf:
|
|
||||||
if not isinstance(value, self._args[0]) and not isinstance(value, self._args[1]):
|
|
||||||
raise ValueError, "failed type check for %s - expected %s or %s, got %s" %\
|
|
||||||
(key,
|
|
||||||
nice_type_name(self._args[0]),
|
|
||||||
nice_type_name(self._args[1]),
|
|
||||||
nice_type_name(type(value)))
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check basic type of value.
|
|
||||||
if not isinstance(value, self._kind):
|
|
||||||
raise ValueError, "failed type check for %s - expected %s, got %s" %\
|
|
||||||
(key,
|
|
||||||
nice_type_name(self._kind),
|
|
||||||
nice_type_name(type(value)))
|
|
||||||
|
|
||||||
# Check types of values in lists.
|
|
||||||
if self._kind is list:
|
|
||||||
if len(value) < 1:
|
|
||||||
raise ValueError, "failed check for %s - list should not be empty" % key
|
|
||||||
for x in value:
|
|
||||||
if not isinstance(x, self._args[0]):
|
|
||||||
raise ValueError, "failed type check for %s - expected list value type %s, got %s" %\
|
|
||||||
(key,
|
|
||||||
nice_type_name(self._args[0]),
|
|
||||||
nice_type_name(type(x)))
|
|
||||||
# Check types of keys and values in dictionaries.
|
|
||||||
elif self._kind is dict:
|
|
||||||
if len(value.keys()) < 1:
|
|
||||||
raise ValueError, "failed check for %s - dict should not be empty" % key
|
|
||||||
for x in value.iterkeys():
|
|
||||||
if not isinstance(x, self._args[0]):
|
|
||||||
raise ValueError, "failed dict type check for %s - expected key type %s, got %s" %\
|
|
||||||
(key,
|
|
||||||
nice_type_name(self._args[0]),
|
|
||||||
nice_type_name(type(x)))
|
|
||||||
for k,v in value.iteritems():
|
|
||||||
if not isinstance(x, self._args[1]):
|
|
||||||
raise ValueError, "failed dict type check for %s - expected value type %s for key %s, got %s" %\
|
|
||||||
(key,
|
|
||||||
nice_type_name(self._args[1]),
|
|
||||||
k,
|
|
||||||
nice_type_name(type(x)))
|
|
||||||
|
|
||||||
def type_check_event_fields(category, definition):
|
|
||||||
"""Perform a type/schema check on the event definition."""
|
|
||||||
REQUIRED_FIELDS = {
|
|
||||||
'methods': TypeChecker(list, basestring),
|
|
||||||
'objects': TypeChecker(list, basestring),
|
|
||||||
'bug_numbers': TypeChecker(list, int),
|
|
||||||
'notification_emails': TypeChecker(list, basestring),
|
|
||||||
'description': TypeChecker(basestring),
|
|
||||||
}
|
|
||||||
OPTIONAL_FIELDS = {
|
|
||||||
'release_channel_collection': TypeChecker(basestring),
|
|
||||||
'expiry_date': TypeChecker(OneOf, basestring, datetime.date),
|
|
||||||
'expiry_version': TypeChecker(basestring),
|
|
||||||
'extra_keys': TypeChecker(dict, basestring, basestring),
|
|
||||||
}
|
|
||||||
ALL_FIELDS = REQUIRED_FIELDS.copy()
|
|
||||||
ALL_FIELDS.update(OPTIONAL_FIELDS)
|
|
||||||
|
|
||||||
# Check that all the required fields are available.
|
|
||||||
missing_fields = [f for f in REQUIRED_FIELDS.keys() if f not in definition]
|
|
||||||
if len(missing_fields) > 0:
|
|
||||||
raise KeyError(category + ' - missing required fields: ' + ', '.join(missing_fields))
|
|
||||||
|
|
||||||
# Is there any unknown field?
|
|
||||||
unknown_fields = [f for f in definition.keys() if f not in ALL_FIELDS]
|
|
||||||
if len(unknown_fields) > 0:
|
|
||||||
raise KeyError(category + ' - unknown fields: ' + ', '.join(unknown_fields))
|
|
||||||
|
|
||||||
# Type-check fields.
|
|
||||||
for k,v in definition.iteritems():
|
|
||||||
ALL_FIELDS[k].check(k, v)
|
|
||||||
|
|
||||||
def string_check(category, field_name, value, min_length, max_length, regex=None):
|
|
||||||
# Length check.
|
|
||||||
if len(value) > max_length:
|
|
||||||
raise ValueError("Value for %s in %s exceeds maximum length of %d" %\
|
|
||||||
(field_name, category, max_length))
|
|
||||||
# Regex check.
|
|
||||||
if regex and not re.match(regex, value):
|
|
||||||
raise ValueError, 'String value for %s in %s is not matching pattern "%s": %s' % \
|
|
||||||
(field_name, category, regex, value)
|
|
||||||
|
|
||||||
class EventData:
|
|
||||||
"""A class representing one event."""
|
|
||||||
|
|
||||||
def __init__(self, category, definition):
|
|
||||||
type_check_event_fields(category, definition)
|
|
||||||
|
|
||||||
string_check(category, 'methods', definition.get('methods')[0], 1, MAX_METHOD_NAME_LENGTH, regex=IDENTIFIER_PATTERN)
|
|
||||||
string_check(category, 'objects', definition.get('objects')[0], 1, MAX_OBJECT_NAME_LENGTH, regex=IDENTIFIER_PATTERN)
|
|
||||||
|
|
||||||
# Check release_channel_collection
|
|
||||||
rcc_key = 'release_channel_collection'
|
|
||||||
rcc = definition.get(rcc_key, 'opt-in')
|
|
||||||
allowed_rcc = ["opt-in", "opt-out"]
|
|
||||||
if not rcc in allowed_rcc:
|
|
||||||
raise ValueError, "Value for %s in %s should be one of: %s" %\
|
|
||||||
(rcc_key, category, ", ".join(allowed_rcc))
|
|
||||||
|
|
||||||
# Check extra_keys.
|
|
||||||
extra_keys = definition.get('extra_keys', {})
|
|
||||||
if len(extra_keys.keys()) > MAX_EXTRA_KEYS_COUNT:
|
|
||||||
raise ValueError, "Number of extra_keys in %s exceeds limit %d" %\
|
|
||||||
(category, MAX_EXTRA_KEYS_COUNT)
|
|
||||||
for key in extra_keys.iterkeys():
|
|
||||||
string_check(category, 'extra_keys', key, 1, MAX_EXTRA_KEY_NAME_LENGTH, regex=IDENTIFIER_PATTERN)
|
|
||||||
|
|
||||||
# Check expiry.
|
|
||||||
if not 'expiry_version' in definition and not 'expiry_date' in definition:
|
|
||||||
raise KeyError, "Event in %s is missing an expiration - either expiry_version or expiry_date is required" %\
|
|
||||||
(category)
|
|
||||||
expiry_date = definition.get('expiry_date')
|
|
||||||
if expiry_date and isinstance(expiry_date, basestring) and expiry_date != 'never':
|
|
||||||
if not re.match(DATE_PATTERN, expiry_date):
|
|
||||||
raise ValueError, "Event in %s has invalid expiry_date, it should be either 'never' or match this format: %s" %\
|
|
||||||
(category, DATE_PATTERN)
|
|
||||||
# Parse into date.
|
|
||||||
definition['expiry_date'] = datetime.datetime.strptime(expiry_date, '%Y-%m-%d')
|
|
||||||
|
|
||||||
# Finish setup.
|
|
||||||
self._category = category
|
|
||||||
self._definition = definition
|
|
||||||
definition['expiry_version'] = add_expiration_postfix(definition.get('expiry_version', 'never'))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def category(self):
|
|
||||||
return self._category
|
|
||||||
|
|
||||||
@property
|
|
||||||
def category_cpp(self):
|
|
||||||
# Transform e.g. category.example into CategoryExample.
|
|
||||||
return convert_to_cpp_identifier(self._category, ".")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def methods(self):
|
|
||||||
return self._definition.get('methods')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def objects(self):
|
|
||||||
return self._definition.get('objects')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def expiry_version(self):
|
|
||||||
return self._definition.get('expiry_version')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def expiry_day(self):
|
|
||||||
date = self._definition.get('expiry_date')
|
|
||||||
if not date:
|
|
||||||
return 0
|
|
||||||
if isinstance(date, basestring) and date == 'never':
|
|
||||||
return 0
|
|
||||||
|
|
||||||
# Convert date to days since UNIX epoch.
|
|
||||||
epoch = datetime.date(1970, 1, 1)
|
|
||||||
days = (date - epoch).total_seconds() / (24 * 60 * 60)
|
|
||||||
return round(days)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def cpp_guard(self):
|
|
||||||
return self._definition.get('cpp_guard')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def enum_labels(self):
|
|
||||||
def enum(method_name, object_name):
|
|
||||||
m = convert_to_cpp_identifier(method_name, "_")
|
|
||||||
o = convert_to_cpp_identifier(object_name, "_")
|
|
||||||
return m + '_' + o
|
|
||||||
combinations = itertools.product(self.methods, self.objects)
|
|
||||||
return [enum(t[0], t[1]) for t in combinations]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def dataset(self):
|
|
||||||
"""Get the nsITelemetry constant equivalent for release_channel_collection.
|
|
||||||
"""
|
|
||||||
rcc = self._definition.get('release_channel_collection', 'opt-in')
|
|
||||||
if rcc == 'opt-out':
|
|
||||||
return 'nsITelemetry::DATASET_RELEASE_CHANNEL_OPTOUT'
|
|
||||||
else:
|
|
||||||
return 'nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN'
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_keys(self):
|
|
||||||
return self._definition.get('extra_keys', {}).keys()
|
|
||||||
|
|
||||||
def load_events(filename):
|
|
||||||
"""Parses a YAML file containing the event definitions.
|
|
||||||
|
|
||||||
:param filename: the YAML file containing the event definitions.
|
|
||||||
:raises Exception: if the event file cannot be opened or parsed.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Parse the event definitions from the YAML file.
|
|
||||||
events = None
|
|
||||||
try:
|
|
||||||
with open(filename, 'r') as f:
|
|
||||||
events = yaml.safe_load(f)
|
|
||||||
except IOError, e:
|
|
||||||
raise Exception('Error opening ' + filename + ': ' + e.message)
|
|
||||||
except ValueError, e:
|
|
||||||
raise Exception('Error parsing events in ' + filename + ': ' + e.message)
|
|
||||||
|
|
||||||
event_list = []
|
|
||||||
|
|
||||||
# Events are defined in a fixed two-level hierarchy within the definition file.
|
|
||||||
# The first level contains the category (group name), while the second level contains the
|
|
||||||
# event definitions (e.g. "category.name: [<event definition>, ...], ...").
|
|
||||||
for category_name,category in events.iteritems():
|
|
||||||
string_check('', 'category', category_name, 1, MAX_CATEGORY_NAME_LENGTH, regex=IDENTIFIER_PATTERN)
|
|
||||||
|
|
||||||
# Make sure that the category has at least one entry in it.
|
|
||||||
if not category or len(category) == 0:
|
|
||||||
raise ValueError(category_name + ' must contain at least one entry')
|
|
||||||
|
|
||||||
for entry in category:
|
|
||||||
event_list.append(EventData(category_name, entry))
|
|
||||||
|
|
||||||
return event_list
|
|
Загрузка…
Ссылка в новой задаче