chromium/tools/metrics/actions/actions_model_test.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.

from parameterized import parameterized
import unittest
import xml.dom.minidom

import actions_model

PRETTY_XML = """
<actions>

<action name="Action1">
  <owner>[email protected]</owner>
  <description>Description1</description>
</action>

<action name="Action2" not_user_triggered="true">
  <obsolete>Obsolete text</obsolete>
  <owner>[email protected]</owner>
  <description>Description2</description>
</action>

<action name="Action3">
  <obsolete>Obsolete text</obsolete>
  <owner>[email protected]</owner>
  <owner>[email protected]</owner>
  <description>Description3</description>
</action>

<action-suffix separator="." ordering="suffix">
  <suffix name="AppMenu" label="Users opened a new incognito tab by app menu"/>
  <suffix name="LongTapMenu"
      label="Users opened a new incognito tab by long tap menu"/>
</action-suffix>

<action-suffix separator="." ordering="suffix">
  <suffix name="LongTapMenu" label="Users closed a tab by long tap menu"/>
  <affected-action name="AnAction"/>
  <affected-action name="MobileMenuCloseTab"/>
</action-suffix>

</actions>
""".strip()

XML_WITH_WRONG_INDENT = """
<actions>

  <action name="Action1">
    <owner>[email protected]</owner>
    <description>Description1</description>
  </action>

<action name="Action2" not_user_triggered="true">
    <obsolete>Obsolete text</obsolete>
    <owner>[email protected]</owner>
    <description>Description2</description>
</action>

<action name="Action3">
<obsolete>Obsolete text</obsolete>
<owner>[email protected]</owner>
<owner>[email protected]</owner>
<description>Description3</description>
</action>

  <action-suffix separator="." ordering="suffix">
  <suffix name="AppMenu" label="Users opened a new incognito tab by app menu"/>
    <suffix name="LongTapMenu"
        label="Users opened a new incognito tab by long tap menu"/>
  </action-suffix>

  <action-suffix separator="." ordering="suffix">
    <suffix name="LongTapMenu" label="Users closed a tab by long tap menu"/>
    <affected-action name="AnAction"/>
    <affected-action name="MobileMenuCloseTab"/>
  </action-suffix>

</actions>
""".strip()

XML_WITH_WRONG_SINGLELINE = """
<actions>

<action name="Action1">
  <owner>
    [email protected]
  </owner>
  <description>Description1</description>
</action>

<action name="Action2" not_user_triggered="true">
  <obsolete>
    Obsolete text
  </obsolete>
  <owner>[email protected]</owner>
  <description>Description2</description>
</action>

<action name="Action3">
  <obsolete>Obsolete text</obsolete>
  <owner>[email protected]</owner>
  <owner>[email protected]</owner>
  <description>
    Description3
  </description>
</action>

<action-suffix separator="." ordering="suffix">
  <suffix name="AppMenu" label="Users opened a new incognito tab by app menu"/>
  <suffix name="LongTapMenu"
      label="Users opened a new incognito tab by long tap menu"/>
</action-suffix>

<action-suffix separator="." ordering="suffix">
  <suffix name="LongTapMenu" label="Users closed a tab by long tap menu"/>
  <affected-action name="AnAction"/>
  <affected-action name="MobileMenuCloseTab"/>
</action-suffix>

</actions>
""".strip()

XML_WITH_WRONG_LINE_BREAK = """
<actions>
<action name="Action1">
  <owner>[email protected]</owner>
  <description>Description1</description>
</action>

<action name="Action2" not_user_triggered="true">
  <obsolete>Obsolete text</obsolete>
  <owner>[email protected]</owner>
  <description>Description2</description>
</action>
<action name="Action3">
  <obsolete>Obsolete text</obsolete>
  <owner>[email protected]</owner>

  <owner>[email protected]</owner>
  <description>Description3</description>
</action>

<action-suffix separator="." ordering="suffix">
  <suffix name="AppMenu" label="Users opened a new incognito tab by app menu"/>
  <suffix name="LongTapMenu"
      label="Users opened a new incognito tab by long tap menu"/>
</action-suffix>

<action-suffix separator="." ordering="suffix">
  <suffix name="LongTapMenu"
      label="Users closed a tab by long tap menu"/>
  <affected-action name="AnAction"/>
  <affected-action name="MobileMenuCloseTab"/>
</action-suffix>
</actions>
""".strip()

XML_WITH_WRONG_ORDER = """
<actions>

<action name="Action2" not_user_triggered="true">
  <obsolete>Obsolete text</obsolete>
  <owner>[email protected]</owner>
  <description>Description2</description>
</action>

<action name="Action1">
  <owner>[email protected]</owner>
  <description>Description1</description>
</action>

<action name="Action3">
  <obsolete>Obsolete text</obsolete>
  <owner>[email protected]</owner>
  <owner>[email protected]</owner>
  <description>Description3</description>
</action>

<action-suffix separator="." ordering="suffix">
  <suffix name="LongTapMenu"
      label="Users opened a new incognito tab by long tap menu"/>
  <suffix name="AppMenu" label="Users opened a new incognito tab by app menu"/>
</action-suffix>

<action-suffix separator="." ordering="suffix">
  <suffix name="LongTapMenu" label="Users closed a tab by long tap menu"/>
  <affected-action name="MobileMenuCloseTab"/>
  <affected-action name="AnAction"/>
</action-suffix>

</actions>
""".strip()

XML_WITH_WRONG_CHILDREN_ORDER = """
<actions>

<action name="Action1">
  <description>Description1</description>
  <owner>[email protected]</owner>
</action>

<action name="Action2" not_user_triggered="true">
  <owner>[email protected]</owner>
  <obsolete>Obsolete text</obsolete>
  <description>Description2</description>
</action>

<action name="Action3">
  <obsolete>Obsolete text</obsolete>
  <owner>[email protected]</owner>
  <description>Description3</description>
  <owner>[email protected]</owner>
</action>

<action-suffix separator="." ordering="suffix">
  <suffix name="AppMenu" label="Users opened a new incognito tab by app menu"/>
  <suffix name="LongTapMenu"
      label="Users opened a new incognito tab by long tap menu"/>
</action-suffix>

<action-suffix separator="." ordering="suffix">
  <affected-action name="AnAction"/>
  <affected-action name="MobileMenuCloseTab"/>
  <suffix name="LongTapMenu" label="Users closed a tab by long tap menu"/>
</action-suffix>

</actions>
""".strip()

XML_WITH_WRONG_ATTRIBUTE_ORDER = """
<actions>

<action name="Action1">
  <owner>[email protected]</owner>
  <description>Description1</description>
</action>

<action not_user_triggered="true" name="Action2">
  <obsolete>Obsolete text</obsolete>
  <owner>[email protected]</owner>
  <description>Description2</description>
</action>

<action name="Action3">
  <obsolete>Obsolete text</obsolete>
  <owner>[email protected]</owner>
  <owner>[email protected]</owner>
  <description>Description3</description>
</action>

<action-suffix ordering="suffix" separator=".">
  <suffix name="AppMenu" label="Users opened a new incognito tab by app menu"/>
  <suffix name="LongTapMenu"
      label="Users opened a new incognito tab by long tap menu"/>
</action-suffix>

<action-suffix separator="." ordering="suffix">
  <suffix label="Users closed a tab by long tap menu" name="LongTapMenu"/>
  <affected-action name="AnAction"/>
  <affected-action name="MobileMenuCloseTab"/>
</action-suffix>

</actions>
""".strip()


class ActionXmlTest(unittest.TestCase):
  def __init__(self, *args, **kwargs):
    super(ActionXmlTest, self).__init__(*args, **kwargs)
    self.maxDiff = None

  @parameterized.expand([
      # Test prettify already pretty XML to verify the pretty-printed version
      # is the same.
      ('AlreadyPrettyXml', PRETTY_XML, PRETTY_XML),
      ('Indent', XML_WITH_WRONG_INDENT, PRETTY_XML),
      ('SingleLine', XML_WITH_WRONG_SINGLELINE, PRETTY_XML),
      ('LineBreak', XML_WITH_WRONG_LINE_BREAK, PRETTY_XML),
      ('Order', XML_WITH_WRONG_ORDER, PRETTY_XML),
      # The children of <action> should be sorted in the order of <obsolete>,
      # <owner> and <description>
      ('ChildrenOrder', XML_WITH_WRONG_CHILDREN_ORDER, PRETTY_XML),
  ])
  def testPrettify(self, _, input_xml, expected_xml):
    result = actions_model.PrettifyTree(xml.dom.minidom.parseString(input_xml))
    self.assertMultiLineEqual(result.strip(), expected_xml)

  @parameterized.expand([
      ('BadAttributeBoolean', PRETTY_XML, 'true', 'hello', 'hello'),
      ('BadSuffixNameWithSpace', PRETTY_XML, 'AppMenu', 'App Menu', 'App Menu'),
      ('BadAffectedActionNameWithSpace', PRETTY_XML, 'AnAction', 'An Action',
       'An Action'),
      ('SuffixWithBadSeparator', PRETTY_XML, '.', '-', '-'),
      ('BadOrdering_IllegalWord', PRETTY_XML, 'ordering="suffix"',
       'ordering="hello"', 'hello'),
  ])
  def testRegex(self, _, pretty_input_xml, original_string, bad_string,
                error_string):
    BAD_XML = pretty_input_xml.replace(original_string, bad_string)
    with self.assertRaises(ValueError) as context:
      actions_model.PrettifyTree(xml.dom.minidom.parseString(BAD_XML))
    self.assertIn(error_string, str(context.exception))
    self.assertIn('does not match regex', str(context.exception))


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