blob: 7d3111490bdf87523cdfc2840ef86a1b7da129dc [file] [log] [blame]
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import math
import unittest
from tracing.proto import histogram_proto
from tracing.value import histogram
from tracing.value import histogram_set
from tracing.value.diagnostics import date_range
from tracing.value.diagnostics import diagnostic_ref
from tracing.value.diagnostics import generic_set
def _AddHist(hist_set, name=None, unit=None):
hist = hist_set.histograms.add()
hist.name = name or '_'
hist.unit.unit = unit or histogram_proto.Pb2().MS
return hist
class HistogramSetUnittest(unittest.TestCase):
def testGetSharedDiagnosticsOfType(self):
d0 = generic_set.GenericSet(['foo'])
d1 = date_range.DateRange(0)
hs = histogram_set.HistogramSet()
hs.AddSharedDiagnosticToAllHistograms('generic', d0)
hs.AddSharedDiagnosticToAllHistograms('generic', d1)
diagnostics = hs.GetSharedDiagnosticsOfType(generic_set.GenericSet)
self.assertEqual(len(diagnostics), 1)
self.assertIsInstance(diagnostics[0], generic_set.GenericSet)
def testImportDicts(self):
hist = histogram.Histogram('', 'unitless')
hists = histogram_set.HistogramSet([hist])
hists2 = histogram_set.HistogramSet()
hists2.ImportDicts(hists.AsDicts())
self.assertEqual(len(hists), len(hists2))
def testAssertType(self):
hs = histogram_set.HistogramSet()
with self.assertRaises(AssertionError):
hs.ImportDicts([{'type': ''}])
def testIgnoreTagMap(self):
histogram_set.HistogramSet().ImportDicts([{'type': 'TagMap'}])
def testFilterHistogram(self):
a = histogram.Histogram('a', 'unitless')
b = histogram.Histogram('b', 'unitless')
c = histogram.Histogram('c', 'unitless')
hs = histogram_set.HistogramSet([a, b, c])
hs.FilterHistograms(lambda h: h.name == 'b')
names = set(['a', 'c'])
for h in hs:
self.assertIn(h.name, names)
names.remove(h.name)
self.assertEqual(0, len(names))
def testRemoveOrphanedDiagnostics(self):
da = generic_set.GenericSet(['a'])
db = generic_set.GenericSet(['b'])
a = histogram.Histogram('a', 'unitless')
b = histogram.Histogram('b', 'unitless')
hs = histogram_set.HistogramSet([a])
hs.AddSharedDiagnosticToAllHistograms('a', da)
hs.AddHistogram(b)
hs.AddSharedDiagnosticToAllHistograms('b', db)
hs.FilterHistograms(lambda h: h.name == 'a')
dicts = hs.AsDicts()
self.assertEqual(3, len(dicts))
hs.RemoveOrphanedDiagnostics()
dicts = hs.AsDicts()
self.assertEqual(2, len(dicts))
def testAddSharedDiagnostic(self):
diags = {}
da = generic_set.GenericSet(['a'])
db = generic_set.GenericSet(['b'])
diags['da'] = da
diags['db'] = db
a = histogram.Histogram('a', 'unitless')
b = histogram.Histogram('b', 'unitless')
hs = histogram_set.HistogramSet()
hs.AddSharedDiagnostic(da)
hs.AddHistogram(a, {'da': da})
hs.AddHistogram(b, {'db': db})
# This should produce one shared diagnostic and 2 histograms.
dicts = hs.AsDicts()
self.assertEqual(3, len(dicts))
self.assertEqual(da.AsDict(), dicts[0])
# Assert that you only see the shared diagnostic once.
seen_once = False
for idx, val in enumerate(dicts):
if idx == 0:
continue
if 'da' in val['diagnostics']:
self.assertFalse(seen_once)
self.assertEqual(val['diagnostics']['da'], da.guid)
seen_once = True
def testMerge(self):
hs1 = histogram_set.HistogramSet([histogram.Histogram('a', 'unitless')])
hs1.AddSharedDiagnosticToAllHistograms('name',
generic_set.GenericSet(['diag1']))
hs2 = histogram_set.HistogramSet([histogram.Histogram('b', 'unitless')])
hs2.AddSharedDiagnosticToAllHistograms('name',
generic_set.GenericSet(['diag2']))
hs1.Merge(hs2)
self.assertEqual(len(hs1), 2)
self.assertEqual(len(hs1.shared_diagnostics), 2)
self.assertEqual(hs1.GetHistogramNamed('a').diagnostics['name'],
generic_set.GenericSet(['diag1']))
self.assertEqual(hs1.GetHistogramNamed('b').diagnostics['name'],
generic_set.GenericSet(['diag2']))
def testSharedDiagnostic(self):
hist = histogram.Histogram('', 'unitless')
hists = histogram_set.HistogramSet([hist])
diag = generic_set.GenericSet(['shared'])
hists.AddSharedDiagnosticToAllHistograms('generic', diag)
# Serializing a single Histogram with a single shared diagnostic should
# produce 2 dicts.
ds = hists.AsDicts()
self.assertEqual(len(ds), 2)
self.assertEqual(diag.AsDict(), ds[0])
# The serialized Histogram should refer to the shared diagnostic by its
# guid.
self.assertEqual(ds[1]['diagnostics']['generic'], diag.guid)
# Deserialize ds.
hists2 = histogram_set.HistogramSet()
hists2.ImportDicts(ds)
self.assertEqual(len(hists2), 1)
hist2 = [h for h in hists2][0]
self.assertIsInstance(
hist2.diagnostics.get('generic'), generic_set.GenericSet)
self.assertEqual(list(diag), list(hist2.diagnostics.get('generic')))
def testReplaceSharedDiagnostic(self):
hist = histogram.Histogram('', 'unitless')
hists = histogram_set.HistogramSet([hist])
diag0 = generic_set.GenericSet(['shared0'])
diag1 = generic_set.GenericSet(['shared1'])
hists.AddSharedDiagnosticToAllHistograms('generic0', diag0)
hists.AddSharedDiagnosticToAllHistograms('generic1', diag1)
guid0 = diag0.guid
guid1 = diag1.guid
hists.ReplaceSharedDiagnostic(
guid0, diagnostic_ref.DiagnosticRef('fakeGuid'))
self.assertEqual(hist.diagnostics['generic0'].guid, 'fakeGuid')
self.assertEqual(hist.diagnostics['generic1'].guid, guid1)
def testReplaceSharedDiagnostic_NonRefAddsToMap(self):
hist = histogram.Histogram('', 'unitless')
hists = histogram_set.HistogramSet([hist])
diag0 = generic_set.GenericSet(['shared0'])
diag1 = generic_set.GenericSet(['shared1'])
hists.AddSharedDiagnosticToAllHistograms('generic0', diag0)
guid0 = diag0.guid
guid1 = diag1.guid
hists.ReplaceSharedDiagnostic(guid0, diag1)
self.assertIsNotNone(hists.LookupDiagnostic(guid1))
def testDeduplicateDiagnostics(self):
generic_a = generic_set.GenericSet(['A'])
generic_b = generic_set.GenericSet(['B'])
date_a = date_range.DateRange(42)
date_b = date_range.DateRange(57)
a_hist = histogram.Histogram('a', 'unitless')
generic0 = generic_set.GenericSet.FromDict(generic_a.AsDict())
generic0.AddDiagnostic(generic_b)
a_hist.diagnostics['generic'] = generic0
date0 = date_range.DateRange.FromDict(date_a.AsDict())
date0.AddDiagnostic(date_b)
a_hist.diagnostics['date'] = date0
b_hist = histogram.Histogram('b', 'unitless')
generic1 = generic_set.GenericSet.FromDict(generic_a.AsDict())
generic1.AddDiagnostic(generic_b)
b_hist.diagnostics['generic'] = generic1
date1 = date_range.DateRange.FromDict(date_a.AsDict())
date1.AddDiagnostic(date_b)
b_hist.diagnostics['date'] = date1
c_hist = histogram.Histogram('c', 'unitless')
c_hist.diagnostics['generic'] = generic1
histograms = histogram_set.HistogramSet([a_hist, b_hist, c_hist])
self.assertNotEqual(
a_hist.diagnostics['generic'].guid, b_hist.diagnostics['generic'].guid)
self.assertEqual(
b_hist.diagnostics['generic'].guid, c_hist.diagnostics['generic'].guid)
self.assertEqual(
a_hist.diagnostics['generic'], b_hist.diagnostics['generic'])
self.assertNotEqual(
a_hist.diagnostics['date'].guid, b_hist.diagnostics['date'].guid)
self.assertEqual(
a_hist.diagnostics['date'], b_hist.diagnostics['date'])
histograms.DeduplicateDiagnostics()
self.assertEqual(
a_hist.diagnostics['generic'].guid, b_hist.diagnostics['generic'].guid)
self.assertEqual(
b_hist.diagnostics['generic'].guid, c_hist.diagnostics['generic'].guid)
self.assertEqual(
a_hist.diagnostics['generic'], b_hist.diagnostics['generic'])
self.assertEqual(
a_hist.diagnostics['date'].guid, b_hist.diagnostics['date'].guid)
self.assertEqual(
a_hist.diagnostics['date'], b_hist.diagnostics['date'])
histogram_dicts = histograms.AsDicts()
# All diagnostics should have been serialized as DiagnosticRefs.
for d in histogram_dicts:
if 'type' not in d:
for diagnostic_dict in d['diagnostics'].values():
self.assertIsInstance(diagnostic_dict, str)
histograms2 = histogram_set.HistogramSet()
histograms2.ImportDicts(histograms.AsDicts())
a_hists = histograms2.GetHistogramsNamed('a')
self.assertEqual(len(a_hists), 1)
a_hist2 = a_hists[0]
b_hists = histograms2.GetHistogramsNamed('b')
self.assertEqual(len(b_hists), 1)
b_hist2 = b_hists[0]
self.assertEqual(
a_hist2.diagnostics['generic'].guid,
b_hist2.diagnostics['generic'].guid)
self.assertEqual(
a_hist2.diagnostics['generic'],
b_hist2.diagnostics['generic'])
self.assertEqual(
a_hist2.diagnostics['date'].guid,
b_hist2.diagnostics['date'].guid)
self.assertEqual(
a_hist2.diagnostics['date'],
b_hist2.diagnostics['date'])
def testBasicImportFromProto(self):
hist_set = histogram_proto.Pb2().HistogramSet()
hist = hist_set.histograms.add()
hist.name = 'metric1'
hist.unit.unit = histogram_proto.Pb2().TS_MS
hist = hist_set.histograms.add()
hist.name = 'metric2'
hist.unit.unit = histogram_proto.Pb2().SIGMA
hist.unit.improvement_direction = histogram_proto.Pb2().BIGGER_IS_BETTER
parsed = histogram_set.HistogramSet()
parsed.ImportProto(hist_set.SerializeToString())
hists = list(parsed)
# The order of the histograms isn't guaranteed.
self.assertEqual(len(hists), 2)
self.assertItemsEqual(
[hists[0].name, hists[1].name], ['metric1', 'metric2'])
self.assertItemsEqual(
[hists[0].unit, hists[1].unit], ['tsMs', 'sigma_biggerIsBetter'])
def testSimpleFieldsFromProto(self):
hist_set = histogram_proto.Pb2().HistogramSet()
hist = _AddHist(hist_set)
hist.description = 'description!'
hist.sample_values.append(21)
hist.sample_values.append(22)
hist.sample_values.append(23)
hist.max_num_sample_values = 3
hist.num_nans = 1
parsed = histogram_set.HistogramSet()
parsed.ImportProto(hist_set.SerializeToString())
parsed_hist = parsed.GetFirstHistogram()
self.assertEqual(parsed_hist.description, 'description!')
self.assertEqual(parsed_hist.sample_values, [21, 22, 23])
self.assertEqual(parsed_hist.max_num_sample_values, 3)
self.assertEqual(parsed_hist.num_nans, 1)
def testRaisesOnMissingMandatoryFieldsInProto(self):
hist_set = histogram_proto.Pb2().HistogramSet()
hist = hist_set.histograms.add()
with self.assertRaises(ValueError):
# Missing name.
parsed = histogram_set.HistogramSet()
parsed.ImportProto(hist_set.SerializeToString())
with self.assertRaises(ValueError):
# Missing unit.
hist.name = "eh"
parsed.ImportProto(hist_set.SerializeToString())
def testMinimalBinBoundsInProto(self):
hist_set = histogram_proto.Pb2().HistogramSet()
hist = _AddHist(hist_set)
hist.bin_boundaries.first_bin_boundary = 1
parsed = histogram_set.HistogramSet()
parsed.ImportProto(hist_set.SerializeToString())
parsed_hist = parsed.GetFirstHistogram()
# The transport format for bins is relatively easily understood, whereas
# how bins are generated is very complex, so use the former for the bin
# bounds tests. See the histogram spec in docs/histogram-set-json-format.md.
dict_format = parsed_hist.AsDict()['binBoundaries']
self.assertEqual(dict_format, [1])
def testComplexBinBounds(self):
hist_set = histogram_proto.Pb2().HistogramSet()
hist = _AddHist(hist_set)
hist.bin_boundaries.first_bin_boundary = 17
spec1 = hist.bin_boundaries.bin_specs.add()
spec1.bin_boundary = 18
spec2 = hist.bin_boundaries.bin_specs.add()
spec2.bin_spec.boundary_type = (
histogram_proto.Pb2().BinBoundaryDetailedSpec.EXPONENTIAL)
spec2.bin_spec.maximum_bin_boundary = 19
spec2.bin_spec.num_bin_boundaries = 20
spec3 = hist.bin_boundaries.bin_specs.add()
spec3.bin_spec.boundary_type = (
histogram_proto.Pb2().BinBoundaryDetailedSpec.LINEAR)
spec3.bin_spec.maximum_bin_boundary = 21
spec3.bin_spec.num_bin_boundaries = 22
parsed = histogram_set.HistogramSet()
parsed.ImportProto(hist_set.SerializeToString())
parsed_hist = parsed.GetFirstHistogram()
dict_format = parsed_hist.AsDict()['binBoundaries']
self.assertEqual(dict_format, [17, 18, [1, 19, 20], [0, 21, 22]])
def testImportRunningStatisticsFromProto(self):
hist_set = histogram_proto.Pb2().HistogramSet()
hist = _AddHist(hist_set)
hist.running.count = 4
hist.running.max = 23
hist.running.meanlogs = 1
hist.running.mean = 22
hist.running.min = 21
hist.running.sum = 66
hist.running.variance = 1
parsed = histogram_set.HistogramSet()
parsed.ImportProto(hist_set.SerializeToString())
parsed_hist = parsed.GetFirstHistogram()
# We get at meanlogs through geometric_mean. Variance is after Bessel's
# correction has been applied.
self.assertEqual(parsed_hist.running.count, 4)
self.assertEqual(parsed_hist.running.max, 23)
self.assertEqual(parsed_hist.running.geometric_mean, math.exp(1))
self.assertEqual(parsed_hist.running.mean, 22)
self.assertEqual(parsed_hist.running.min, 21)
self.assertEqual(parsed_hist.running.sum, 66)
self.assertAlmostEqual(parsed_hist.running.variance, 0.3333333333)
def testImportAllBinsFromProto(self):
hist_set = histogram_proto.Pb2().HistogramSet()
hist = _AddHist(hist_set)
hist.all_bins[0].bin_count = 24
map1 = hist.all_bins[0].diagnostic_maps.add().diagnostic_map
map1['some bin diagnostic'].generic_set.values.append('"some value"')
map2 = hist.all_bins[0].diagnostic_maps.add().diagnostic_map
map2['other bin diagnostic'].generic_set.values.append('"some other value"')
parsed = histogram_set.HistogramSet()
parsed.ImportProto(hist_set.SerializeToString())
parsed_hist = parsed.GetFirstHistogram()
self.assertGreater(len(parsed_hist.bins), 1)
self.assertEqual(len(parsed_hist.bins[0].diagnostic_maps), 2)
self.assertEqual(len(parsed_hist.bins[0].diagnostic_maps[0]), 1)
self.assertEqual(len(parsed_hist.bins[0].diagnostic_maps[1]), 1)
self.assertEqual(
parsed_hist.bins[0].diagnostic_maps[0]['some bin diagnostic'],
generic_set.GenericSet(values=['some value']))
self.assertEqual(
parsed_hist.bins[0].diagnostic_maps[1]['other bin diagnostic'],
generic_set.GenericSet(values=['some other value']))
def testSummaryOptionsFromProto(self):
hist_set = histogram_proto.Pb2().HistogramSet()
hist = _AddHist(hist_set)
hist.summary_options.avg = True
hist.summary_options.nans = True
hist.summary_options.geometric_mean = True
hist.summary_options.percentile.append(0.90)
hist.summary_options.percentile.append(0.95)
hist.summary_options.percentile.append(0.99)
parsed = histogram_set.HistogramSet()
parsed.ImportProto(hist_set.SerializeToString())
parsed_hist = parsed.GetFirstHistogram()
# See the histogram spec in docs/histogram-set-json-format.md.
# Serializing to proto leads to funny rounding errors.
self.assertEqual(
parsed_hist.statistics_names,
set(['pct_099_0000009537', 'pct_089_9999976158', 'pct_094_9999988079',
'nans', 'avg', 'geometricMean']),
msg=str(parsed_hist.statistics_names))
def testImportSharedDiagnosticsFromProto(self):
guid1 = 'f7f17394-fa4a-481e-86bd-a82cd55935a7'
guid2 = '88ea36c7-6dcb-4ba8-ba56-1979de05e16f'
hist_set = histogram_proto.Pb2().HistogramSet()
hist_set.shared_diagnostics[guid1].generic_set.values.append(
'"webrtc_perf_tests"')
hist_set.shared_diagnostics[guid2].generic_set.values.append('123456')
hist_set.shared_diagnostics['whatever'].generic_set.values.append('2')
hist = hist_set.histograms.add()
hist.name = "_"
hist.unit.unit = histogram_proto.Pb2().MS
hist.diagnostics.diagnostic_map['bots'].shared_diagnostic_guid = guid1
hist.diagnostics.diagnostic_map['pointId'].shared_diagnostic_guid = guid2
parsed = histogram_set.HistogramSet()
parsed.ImportProto(hist_set.SerializeToString())
parsed_hist = parsed.GetFirstHistogram()
self.assertIsNotNone(parsed_hist)
self.assertEqual(len(parsed_hist.diagnostics), 2)
self.assertEqual(parsed_hist.diagnostics['pointId'],
generic_set.GenericSet(values=[123456]))
self.assertEqual(parsed_hist.diagnostics['bots'],
generic_set.GenericSet(values=['webrtc_perf_tests']))