# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
from parameterized import parameterized
import sys
import unittest
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common'))
import etree_util
import histogram_configuration_model
XML_RIGHT_ORDER = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<histogram name="hist.a" enum="enum1" expires_after="2019-11-02">
<owner>[email protected]</owner>
<owner>[email protected]</owner>
<summary>Summary text</summary>
</histogram>
<histogram name="hist.b" expires_after="M85">
<owner>[email protected]</owner>
<owner>[email protected]</owner>
<summary>Summary text</summary>
</histogram>
<histogram name="hist.c" enum="enum3" expires_after="never">
<owner>[email protected]</owner>
<owner>[email protected]</owner>
<summary>Summary text</summary>
</histogram>
</histograms>
<histogram_suffixes_list>
<histogram_suffixes name="suffix1" separator="." ordering="prefix">
<suffix base="true" name="suffix_name" label="label"/>
<affected-histogram name="histogram1"/>
<affected-histogram name="histogram2"/>
<affected-histogram name="histogram3"/>
</histogram_suffixes>
<histogram_suffixes name="suffix2" separator="_" ordering="prefix,2">
<suffix name="suffix_name" label="label"/>
<affected-histogram name="histogram1"/>
<affected-histogram name="histogram2"/>
<affected-histogram name="histogram3"/>
</histogram_suffixes>
</histogram_suffixes_list>
</histogram-configuration>
""".strip()
PRETTY_XML = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<histogram base="true" name="hist.a" expires_after="2019-11-02">
<!-- Comment in histogram -->
<owner>[email protected]</owner>
<owner>[email protected]</owner>
<component>Component>Subcomponent</component>
<summary>Summary text</summary>
</histogram>
</histograms>
<histogram_suffixes_list>
<histogram_suffixes name="suffix1" separator="." ordering="prefix">
<!-- Comment in histogram_suffixes -->
<suffix base="true" name="suffix_name" label="label"/>
<suffix name="suffix_name" label="label"/>
<affected-histogram name="histogram1"/>
</histogram_suffixes>
</histogram_suffixes_list>
</histogram-configuration>
""".strip()
XML_WRONG_ATTRIBUTE_ORDER = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<histogram expires_after="2019-11-02" name="hist.a" base="true" >
<!-- Comment in histogram -->
<owner>[email protected]</owner>
<owner>[email protected]</owner>
<component>Component>Subcomponent</component>
<summary>Summary text</summary>
</histogram>
</histograms>
<histogram_suffixes_list>
<histogram_suffixes name="suffix1" separator="." ordering="prefix">
<!-- Comment in histogram_suffixes -->
<suffix name="suffix_name" base="true" label="label"/>
<suffix label="label" name="suffix_name"/>
<affected-histogram name="histogram1"/>
</histogram_suffixes>
</histogram_suffixes_list>
</histogram-configuration>
""".strip()
XML_MISSING_SEPARATOR = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<histogram base="true" name="hist.a" expires_after="2019-11-02">
<!-- Comment in histogram -->
<owner>[email protected]</owner>
<owner>[email protected]</owner>
<summary>Summary text</summary>
</histogram>
</histograms>
<histogram_suffixes_list>
<histogram_suffixes name="suffix1" ordering="prefix">
<!-- Comment in histogram_suffixes -->
<suffix base="true" name="suffix_name" label="label"/>
<suffix name="suffix_name" label="label"/>
<affected-histogram name="histogram1"/>
</histogram_suffixes>
</histogram_suffixes_list>
</histogram-configuration>
""".strip()
XML_WRONG_INDENT = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<histogram base="true" name="hist.a" expires_after="2019-11-02">
<!-- Comment in histogram -->
<owner>[email protected]</owner>
<owner>[email protected]</owner>
<component>Component>Subcomponent</component>
<summary>Summary text</summary>
</histogram>
</histograms>
<histogram_suffixes_list>
<histogram_suffixes name="suffix1" separator="." ordering="prefix">
<!-- Comment in histogram_suffixes -->
<suffix base="true" name="suffix_name" label="label"/>
<suffix name="suffix_name" label="label"/>
<affected-histogram name="histogram1"/>
</histogram_suffixes>
</histogram_suffixes_list>
</histogram-configuration>
""".strip()
XML_WRONG_SINGLELINE = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<histogram base="true" name="hist.a" expires_after="2019-11-02">
<!-- Comment in histogram -->
<owner>
[email protected]
</owner>
<owner>[email protected]</owner>
<component>
Component>Subcomponent
</component>
<summary>
Summary text
</summary>
</histogram>
</histograms>
<histogram_suffixes_list>
<histogram_suffixes name="suffix1" separator="." ordering="prefix">
<!-- Comment in histogram_suffixes -->
<suffix base="true" name="suffix_name" label="label"/>
<suffix name="suffix_name" label="label"/>
<affected-histogram name="histogram1"/>
</histogram_suffixes>
</histogram_suffixes_list>
</histogram-configuration>
""".strip()
XML_WRONG_LINEBREAK = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<histogram base="true" name="hist.a" expires_after="2019-11-02">
<!-- Comment in histogram -->
<owner>[email protected]</owner>
<owner>[email protected]</owner>
<component>Component>Subcomponent</component>
<summary>Summary text</summary>
</histogram>
</histograms>
<histogram_suffixes_list>
<histogram_suffixes name="suffix1" separator="." ordering="prefix">
<!-- Comment in histogram_suffixes -->
<suffix base="true" name="suffix_name" label="label"/>
<suffix name="suffix_name" label="label"/>
<affected-histogram name="histogram1"/>
</histogram_suffixes>
</histogram_suffixes_list>
</histogram-configuration>
""".strip()
XML_WRONG_CHILDREN_ORDER = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<histogram base="true" name="hist.a" expires_after="2019-11-02">
<!-- Comment in histogram -->
<owner>[email protected]</owner>
<summary>Summary text</summary>
<component>Component>Subcomponent</component>
<owner>[email protected]</owner>
</histogram>
</histograms>
<histogram_suffixes_list>
<histogram_suffixes name="suffix1" separator="." ordering="prefix">
<!-- Comment in histogram_suffixes -->
<suffix base="true" name="suffix_name" label="label"/>
<suffix name="suffix_name" label="label"/>
<affected-histogram name="histogram1"/>
</histogram_suffixes>
</histogram_suffixes_list>
</histogram-configuration>
""".strip()
XML_WRONG_ORDER = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<histogram name="hist.c" enum="enum3" expires_after="never">
<owner>[email protected]</owner>
<owner>[email protected]</owner>
<summary>Summary text</summary>
</histogram>
<histogram name="hist.a" enum="enum1" expires_after="2019-11-02">
<owner>[email protected]</owner>
<owner>[email protected]</owner>
<summary>Summary text</summary>
</histogram>
<histogram name="hist.b" expires_after="M85">
<owner>[email protected]</owner>
<owner>[email protected]</owner>
<summary>Summary text</summary>
</histogram>
</histograms>
<histogram_suffixes_list>
<histogram_suffixes name="suffix2" separator="_" ordering="prefix,2">
<suffix name="suffix_name" label="label"/>
<affected-histogram name="histogram1"/>
<affected-histogram name="histogram2"/>
<affected-histogram name="histogram3"/>
</histogram_suffixes>
<histogram_suffixes name="suffix1" separator="." ordering="prefix">
<suffix base="true" name="suffix_name" label="label"/>
<affected-histogram name="histogram1"/>
<affected-histogram name="histogram2"/>
<affected-histogram name="histogram3"/>
</histogram_suffixes>
</histogram_suffixes_list>
</histogram-configuration>
""".strip()
PRETTY_XML_WITH_TOKEN = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<variants name="OmniboxProviderVersion">
<variant name="" summary="all versions"/>
<variant name=".Provider2" summary="the second version"/>
</variants>
<histogram name="Omnibox{version}{content}.Time" units="ms"
expires_after="2020-12-25">
<owner>[email protected]</owner>
<summary>
The length of time taken by {version} of {content} provider's synchronous
pass.
</summary>
<token key="version" variants="OmniboxProviderVersion"/>
<token key="content">
<variant name=".ExtensionApp" summary="ExtensionApp">
<owner>[email protected]</owner>
</variant>
<variant name=".HistoryContents" summary="HistoryContents"/>
<variant name=".HistoryQuick" summary="HistoryQuick"/>
</token>
</histogram>
</histograms>
</histogram-configuration>
""".strip()
XML_WRONG_VARIANT_CHILDREN_ORDER = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<variants name="OmniboxProviderVersion">
<variant name="" summary="all versions"/>
<variant name=".Provider2" summary="the second version"/>
</variants>
<histogram name="Omnibox{version}{content}.Time" units="ms"
expires_after="2020-12-25">
<owner>[email protected]</owner>
<summary>
The length of time taken by {version} of {content} provider's synchronous
pass.
</summary>
<token key="version" variants="OmniboxProviderVersion"/>
<token key="content">
<variant name=".ExtensionApp" summary="ExtensionApp">
<owner>[email protected]</owner>
</variant>
<variant name=".HistoryContents" summary="HistoryContents"/>
<variant name=".HistoryQuick" summary="HistoryQuick"/>
</token>
</histogram>
</histograms>
</histogram-configuration>
""".strip()
XML_WRONG_VARIANT_ORDER = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<variants name="OmniboxProviderVersion">
<variant name="" summary="all versions"/>
<variant name=".Provider2" summary="the second version"/>
</variants>
<histogram name="Omnibox{version}{content}.Time" units="ms"
expires_after="2020-12-25">
<owner>[email protected]</owner>
<summary>
The length of time taken by {version} of {content} provider's synchronous
pass.
</summary>
<token key="version" variants="OmniboxProviderVersion"/>
<token key="content">
<variant name=".ExtensionApp" summary="ExtensionApp">
<owner>[email protected]</owner>
</variant>
<variant name=".HistoryQuick" summary="HistoryQuick"/>
<variant name=".HistoryContents" summary="HistoryContents"/>
</token>
</histogram>
</histograms>
</histogram-configuration>
""".strip()
XML_WRONG_HISTOGRAM_VARIANTS_ORDER = """
<histogram-configuration>
<!-- Histogram definitions -->
<histograms>
<histogram name="Omnibox{version}{content}.Time" units="ms"
expires_after="2020-12-25">
<owner>[email protected]</owner>
<summary>
The length of time taken by {version} of {content} provider's synchronous
pass.
</summary>
<token key="version" variants="OmniboxProviderVersion"/>
<token key="content">
<variant name=".ExtensionApp" summary="ExtensionApp">
<owner>[email protected]</owner>
</variant>
<variant name=".HistoryContents" summary="HistoryContents"/>
<variant name=".HistoryQuick" summary="HistoryQuick"/>
</token>
</histogram>
<variants name="OmniboxProviderVersion">
<variant name="" summary="all versions"/>
<variant name=".Provider2" summary="the second version"/>
</variants>
</histograms>
</histogram-configuration>
""".strip()
class HistogramXmlTest(unittest.TestCase):
@parameterized.expand([
# Test prettify already pretty XML to verify the pretty-printed version
# is the same.
('AlreadyPrettyXml', PRETTY_XML, PRETTY_XML),
('AttributeOrder', XML_WRONG_ATTRIBUTE_ORDER, PRETTY_XML),
('Indent', XML_WRONG_INDENT, PRETTY_XML),
('SingleLine', XML_WRONG_SINGLELINE, PRETTY_XML),
('LineBreak', XML_WRONG_LINEBREAK, PRETTY_XML),
# Test prettify already pretty XML with right order to verify the
# pretty-printed version is the same.
('AlreadyPrettyXmlRightOrder', XML_RIGHT_ORDER, XML_RIGHT_ORDER),
('HistogramAndSuffixOrder', XML_WRONG_ORDER, XML_RIGHT_ORDER),
('ChildrenOrder', XML_WRONG_CHILDREN_ORDER, PRETTY_XML),
])
def testPrettify(self, _, input_xml, expected_xml):
result = histogram_configuration_model.PrettifyTree(
etree_util.ParseXMLString(input_xml))
self.maxDiff = None
self.assertMultiLineEqual(expected_xml, result.strip())
def testMissingRequiredAttribute(self):
with self.assertRaises(Exception) as context:
histogram_configuration_model.PrettifyTree(
etree_util.ParseXMLString(XML_MISSING_SEPARATOR))
self.assertIn('separator', str(context.exception))
self.assertIn('Missing attribute', str(context.exception))
@parameterized.expand([
# The "base" attribute of <suffix> only allows
# "true", "True", "false" or "False"
('BadSuffixBaseBoolean', XML_RIGHT_ORDER, 'true', 'yes'),
# The "expires-after" attribute of <histogram> only allows:
# Date given in the format YYYY-{M, MM}-{D, DD},
# Milestone given in the format e.g. M81
# or "never" or "Never"
('BadExpiresAfterDate', PRETTY_XML, '2019-11-02', 'Nov 2019'),
('BadExpiresAfterDateSeparator', PRETTY_XML, '2019-11-02', '2019/11/02'),
('BadExpiresAfterMilestone', XML_RIGHT_ORDER, 'M85', 'Milestone 85'),
('BadExpiresAfterNever', XML_RIGHT_ORDER, 'never', 'NEVER'),
('BadExpiresAfterOtherIllegalWords', XML_RIGHT_ORDER, 'never', 'hello'),
# The "enum" attribute of <histogram> only allows alphanumeric characters
# and punctuations "." and "_". It does not allow space.
('BadEnumNameIllegalPunctuation', XML_RIGHT_ORDER, 'enum1', 'enum:1'),
('BadEnumNameWithSpace', XML_RIGHT_ORDER, 'enum1', 'enum 1'),
# The "ordering" attribute of <histogram_suffixes> only allow
# "suffix", "prefix" or "prefix," followed by a non-negative integer
('BadOrderingIllegalPunctuation', XML_RIGHT_ORDER, 'prefix,2', 'prefix-2'
),
('BadOrderingNonNumber', XML_RIGHT_ORDER, 'prefix,2', 'prefix,two'),
])
def testRegex(self, _, pretty_input_xml, original_string, bad_string):
BAD_XML = pretty_input_xml.replace(original_string, bad_string)
with self.assertRaises(ValueError) as context:
histogram_configuration_model.PrettifyTree(
etree_util.ParseXMLString(BAD_XML))
self.assertIn(bad_string, str(context.exception))
self.assertIn('does not match regex', str(context.exception))
@parameterized.expand([
# Test prettify already pretty XML to verify the pretty-printed version
# is the same.
('AlreadyPrettyXml', PRETTY_XML_WITH_TOKEN, PRETTY_XML_WITH_TOKEN),
('ChildrenOrder', XML_WRONG_VARIANT_CHILDREN_ORDER,
PRETTY_XML_WITH_TOKEN),
('VariantOrder', XML_WRONG_VARIANT_ORDER, PRETTY_XML_WITH_TOKEN),
('HistogramVariantsOrder', XML_WRONG_HISTOGRAM_VARIANTS_ORDER,
PRETTY_XML_WITH_TOKEN),
])
def testTokenPrettify(self, _, input_xml, expected_xml):
result = histogram_configuration_model.PrettifyTree(
etree_util.ParseXMLString(input_xml))
self.maxDiff = None
self.assertMultiLineEqual(expected_xml, result.strip())
def testIndividualTagParsing_improvement(self):
"""Tests that <improvement> has the right format and can be parsed."""
improvement_tag_good = '<improvement direction="HIGHER_IS_BETTER"/>'
improvement_tag_bad = ' <improvement>HIGHER_IS_BETTER</improvement>'
config = """
<histogram-configuration>
<histograms>
<histogram name="Histogram.With.ImprovementTag" expires_after="M100">
<owner>[email protected]</owner>
{improvement_tag}
<summary>The improvement tag says higher value is good!</summary>
</histogram>
</histograms>
</histogram-configuration>"""
config_good = config.format(improvement_tag=improvement_tag_good)
config_bad = config.format(improvement_tag=improvement_tag_bad)
result = histogram_configuration_model.PrettifyTree(
etree_util.ParseXMLString(config_good))
self.maxDiff = None
self.assertMultiLineEqual(config_good.strip(), result.strip())
with self.assertRaisesRegex(ValueError,
'direction "" does not match regex'):
histogram_configuration_model.PrettifyTree(
etree_util.ParseXMLString(config_bad))
if __name__ == '__main__':
unittest.main()