chromium/tools/metrics/histograms/histogram_configuration_model_test_enums.py

# 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

PRETTY_XML_RIGHT_ENUM_ORDER = """
<histogram-configuration>

<enums>

<enum name="Enum1">
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

<enum name="Enum2">
  <summary>Summary text</summary>
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

<enum name="Enum3">
  <obsolete>
    Obsolete text
  </obsolete>
  <summary>Summary text</summary>
  <int value="0" label="Label1">Int text</int>
  <int value="1" label="Label2"/>
</enum>

</enums>

</histogram-configuration>
""".strip()

PRETTY_XML = """
<histogram-configuration>

<enums>

<enum name="Enum1">
  <obsolete>
    Obsolete text
  </obsolete>
  <summary>Summary text</summary>
  <int value="0" label="Label1">Int text</int>
  <int value="1" label="Label2"/>
</enum>

</enums>

</histogram-configuration>
""".strip()

XML_WRONG_ATTRIBUTE_ORDER = """
<histogram-configuration>

<enums>

<enum name="Enum1">
  <obsolete>
    Obsolete text
  </obsolete>
  <summary>Summary text</summary>
  <int label="Label1" value="0" >Int text</int>
  <int value="1" label="Label2"/>
</enum>

</enums>

</histogram-configuration>
""".strip()

XML_WRONG_INDENT = """
<histogram-configuration>

<enums>

  <enum name="Enum1">
    <obsolete>
      Obsolete text
    </obsolete>
    <summary>Summary text</summary>
      <int value="0" label="Label1">Int text</int>
      <int value="1" label="Label2"/>
  </enum>

</enums>

</histogram-configuration>
""".strip()

XML_WRONG_SINGLELINE = """
<histogram-configuration>

<enums>

<enum name="Enum1">
  <obsolete>Obsolete text</obsolete>
  <summary>
    Summary text
  </summary>
    <int value="0" label="Label1">Int text</int>
    <int value="1" label="Label2"/>
</enum>

</enums>

</histogram-configuration>
""".strip()

XML_WRONG_LINEBREAK = """
<histogram-configuration>
<enums>
<enum name="Enum1">
  <obsolete>
    Obsolete text
  </obsolete>
  <summary>Summary text</summary>

  <int value="0" label="Label1">Int text</int>
  <int value="1" label="Label2"/>
</enum>

</enums>


</histogram-configuration>
""".strip()

XML_WRONG_ENUM_ORDER = """
<histogram-configuration>

<enums>

<enum name="Enum2">
  <summary>Summary text</summary>
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

<enum name="Enum3">
  <obsolete>
    Obsolete text
  </obsolete>
  <summary>Summary text</summary>
  <int value="0" label="Label1">Int text</int>
  <int value="1" label="Label2"/>
</enum>

<enum name="Enum1">
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

</enums>

</histogram-configuration>
""".strip()

XML_WRONG_INT_ORDER = """
<histogram-configuration>

<enums>

<enum name="Enum1">
  <obsolete>
    Obsolete text
  </obsolete>
  <summary>Summary text</summary>
  <int value="1" label="Label2"/>
  <int value="0" label="Label1">Int text</int>
</enum>

</enums>

</histogram-configuration>
""".strip()

XML_WRONG_CHILDREN_ORDER = """
<histogram-configuration>

<enums>

<enum name="Enum1">
  <int value="0" label="Label1">Int text</int>
  <int value="1" label="Label2"/>
  <summary>Summary text</summary>
  <obsolete>
    Obsolete text
  </obsolete>
</enum>

</enums>

</histogram-configuration>
""".strip()

PRETTY_XML_WITH_COMMENTS = """
<histogram-configuration>

<enums>

<!-- Comment1 -->

<enum name="Enum1">
<!-- Comment2 -->

  <summary>Summary text</summary>
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

<enum name="Enum2">
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

</enums>

</histogram-configuration>
""".strip()

XML_WITH_COMMENTS_WRONG_INDENT_LINEBREAK = """
<histogram-configuration>

<enums>
  <!-- Comment1 -->

<enum name="Enum1">
<!-- Comment2 -->
  <summary>Summary text</summary>
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

<enum name="Enum2">
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

</enums>

</histogram-configuration>
""".strip()


PRETTY_XML_WITH_IFTTT_COMMENTS = """
<histogram-configuration>

<enums>

<!-- LINT.IfChange(Enum1) -->

<enum name="Enum1">
  <summary>Summary text</summary>
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

<!-- LINT.ThenChange(//path/to/file.cpp:CppEnum1) -->

<enum name="Enum2">
<!-- LINT.IfChange(Enum2a) -->

  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
<!-- LINT.ThenChange(//path/to/file.cpp:CppEnum2a) -->

<!-- LINT.IfChange(Enum2b) -->

  <int value="1000" label="Label3"/>
  <int value="1001" label="Label4"/>
<!-- LINT.ThenChange(//path/to/file.cpp:CppEnum2b) -->

</enum>

<!-- LINT.IfChange(Enum3) -->

<enum name="Enum3">
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

<!-- LINT.ThenChange(//path/to/file.cpp:CppEnum3) -->

</enums>

</histogram-configuration>
""".strip()

PRETTY_XML_WITH_IFTTT_COMMENTS_MIDDLE = """
<histogram-configuration>

<enums>

<enum name="Enum1">
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

<!-- LINT.IfChange(Enum2) -->

<enum name="Enum2">
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

<!-- LINT.ThenChange(//path/to/file.cpp:CppEnum2) -->

<enum name="Enum3">
  <int value="0" label="Label1"/>
  <int value="1" label="Label2"/>
</enum>

</enums>

</histogram-configuration>
""".strip()


class EnumXmlTest(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),
      # <int> tags of enums should be sorted by the integer value
      ('IntOrder', XML_WRONG_INT_ORDER, PRETTY_XML),
      # The children of enums should be sorted in the order of <obsolete>,
      # <summary> and <int>
      ('ChildrenOrder', XML_WRONG_CHILDREN_ORDER, PRETTY_XML),

      # Test prettify already pretty XML with right enum order to verify the
      # pretty-printed version is the same.
      ('AlreadyRightOrder', PRETTY_XML_RIGHT_ENUM_ORDER,
       PRETTY_XML_RIGHT_ENUM_ORDER),
      # Enums should be sorted in the order of their name attribute
      ('EnumOrder', XML_WRONG_ENUM_ORDER, PRETTY_XML_RIGHT_ENUM_ORDER),

      # Test prettify already pretty XML with comments to verify the
      # pretty-printed version is the same.
      ('AlreadyPrettyWithComments', PRETTY_XML_WITH_COMMENTS,
       PRETTY_XML_WITH_COMMENTS),
      ('CommentsIndentsLineBreak', XML_WITH_COMMENTS_WRONG_INDENT_LINEBREAK,
       PRETTY_XML_WITH_COMMENTS),

      # Tests that that LINT.IfChange / LINT.ThenChange comments are correctly
      # preserved in an already-pretty XML.
      ('AlreadyPrettyIfttt', PRETTY_XML_WITH_IFTTT_COMMENTS,
       PRETTY_XML_WITH_IFTTT_COMMENTS),
      ('AlreadyPrettyIftttMiddle', PRETTY_XML_WITH_IFTTT_COMMENTS_MIDDLE,
       PRETTY_XML_WITH_IFTTT_COMMENTS_MIDDLE),
  ])
  def testPrettify(self, _, input_xml, expected_xml):
    result = histogram_configuration_model.PrettifyTree(
        etree_util.ParseXMLString(input_xml))
    self.assertMultiLineEqual(result.strip(), expected_xml)

  @parameterized.expand([
      # The "name" attribute of <enum> only allows alphanumeric characters
      # and punctuations "." and "_". It does not allow space.
      ('BadEnumNameIllegalPunctuation', PRETTY_XML, 'Enum1', 'Enum:1'),
      ('BadEnumNameWithSpace', PRETTY_XML, 'Enum1', 'Enum 1'),
      ('BadIntValueNegative', PRETTY_XML, '1', '-5'),
      ('BadIntValueNonNumeric', PRETTY_XML, '1', 'hello')
  ])
  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))


if __name__ == '__main__':
  unittest.main()