summaryrefslogtreecommitdiff
path: root/chromium/components/policy/tools/template_writers
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/policy/tools/template_writers')
-rwxr-xr-xchromium/components/policy/tools/template_writers/PRESUBMIT.py25
-rwxr-xr-xchromium/components/policy/tools/template_writers/__init__.py8
-rwxr-xr-xchromium/components/policy/tools/template_writers/policy_template_generator.py321
-rwxr-xr-xchromium/components/policy/tools/template_writers/policy_template_generator_unittest.py654
-rwxr-xr-xchromium/components/policy/tools/template_writers/template_formatter.py274
-rwxr-xr-xchromium/components/policy/tools/template_writers/test_suite_all.py65
-rwxr-xr-xchromium/components/policy/tools/template_writers/writer_configuration.py133
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/__init__.py8
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/adm_writer.py314
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/adm_writer_unittest.py1470
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/adml_writer.py266
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/adml_writer_unittest.py521
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/admx_writer.py500
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/admx_writer_unittest.py700
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/android_policy_writer.py109
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/android_policy_writer_unittest.py104
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/chromeos_adml_writer.py33
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/chromeos_adml_writer_unittest.py109
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/chromeos_admx_writer.py38
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/chromeos_admx_writer_unittest.py151
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/doc_atomic_groups_writer.py96
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/doc_writer.py883
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/doc_writer_unittest.py1835
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/google_adml_writer.py36
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/google_adml_writer_unittest.py24
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/google_admx_writer.py36
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/google_admx_writer_unittest.py24
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/gpo_editor_writer.py128
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/ios_app_config_writer.py202
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py434
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/jamf_writer.py239
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/jamf_writer_unittest.py383
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/json_writer.py93
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/json_writer_unittest.py460
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/mock_writer.py29
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/plist_helper.py13
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/plist_strings_writer.py78
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/plist_strings_writer_unittest.py402
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/plist_writer.py156
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/plist_writer_unittest.py732
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/reg_writer.py108
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/reg_writer_unittest.py428
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/template_writer.py468
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/template_writer_unittest.py287
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/writer_unittest_common.py41
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/xml_formatted_writer.py90
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/xml_writer_base_unittest.py37
47 files changed, 13545 insertions, 0 deletions
diff --git a/chromium/components/policy/tools/template_writers/PRESUBMIT.py b/chromium/components/policy/tools/template_writers/PRESUBMIT.py
new file mode 100755
index 00000000000..e159b0d3b7a
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/PRESUBMIT.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+# 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.
+"""Template writers unittests presubmit script.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
+details on the presubmit API built into gcl.
+"""
+
+
+USE_PYTHON3 = True
+
+
+def RunUnittests(input_api, output_api):
+ return input_api.canned_checks.RunPythonUnitTests(input_api, output_api,
+ ['test_suite_all'])
+
+
+def CheckChangeOnUpload(input_api, output_api):
+ return RunUnittests(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ return RunUnittests(input_api, output_api)
diff --git a/chromium/components/policy/tools/template_writers/__init__.py b/chromium/components/policy/tools/template_writers/__init__.py
new file mode 100755
index 00000000000..abc93655ae4
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/__init__.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+'''Module template_writers
+'''
+
+pass
diff --git a/chromium/components/policy/tools/template_writers/policy_template_generator.py b/chromium/components/policy/tools/template_writers/policy_template_generator.py
new file mode 100755
index 00000000000..99a9e03c89f
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/policy_template_generator.py
@@ -0,0 +1,321 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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 copy
+import os
+import re
+import sys
+
+sys.path.insert(
+ 0,
+ os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
+ os.pardir, 'third_party', 'six', 'src'))
+
+import six
+
+
+def IsGroupOrAtomicGroup(policy):
+ return policy['type'] == 'group' or policy['type'] == 'atomic_group'
+
+
+class PolicyTemplateGenerator:
+ '''Generates template text for a particular platform.
+
+ This class is used to traverse a JSON structure from a .json template
+ definition metafile and merge GUI message string definitions that come
+ from a .grd resource tree onto it. After this, it can be used to output
+ this data to policy template files using TemplateWriter objects.
+ '''
+
+ def _ImportMessage(self, msg_txt):
+ msg_txt = six.ensure_text(msg_txt)
+ lines = msg_txt.split('\n')
+
+ # Strip any extra leading spaces, but keep useful indentation:
+ min_leading_spaces = min(list(self._IterateLeadingSpaces(lines)) or [0])
+ if min_leading_spaces > 0:
+ lstrip_pattern = re.compile('^[ ]{1,%s}' % min_leading_spaces)
+ lines = [lstrip_pattern.sub('', line) for line in lines]
+ # Strip all trailing spaces:
+ lines = [line.rstrip() for line in lines]
+ return "\n".join(lines)
+
+ def _IterateLeadingSpaces(self, lines):
+ '''Yields the number of leading spaces on each line, skipping lines which
+ have no leading spaces.'''
+ for line in lines:
+ match = re.search('^[ ]+', line)
+ if match:
+ yield len(match.group(0))
+
+ def __init__(self, config, policy_data):
+ '''Initializes this object with all the data necessary to output a
+ policy template.
+
+ Args:
+ config: Writer configuration.
+ policy_data: The list of defined policies and groups, as parsed from the
+ policy metafile. See
+ components/policy/resources/policy_templates.json
+ for description and content.
+ '''
+ # List of all the policies. Create a copy since the data is modified.
+ self._policy_data = copy.deepcopy(policy_data)
+ # Localized messages to be inserted to the policy_definitions structure:
+ self._messages = self._policy_data['messages']
+ self._config = config
+ for key in self._messages.keys():
+ self._messages[key]['text'] = self._ImportMessage(
+ self._messages[key]['text'])
+ self._AddGroups(self._policy_data['policy_definitions'])
+ self._AddAtomicGroups(self._policy_data['policy_definitions'],
+ self._policy_data['policy_atomic_group_definitions'])
+ self._policy_data[
+ 'policy_atomic_group_definitions'] = self._ExpandAtomicGroups(
+ self._policy_data['policy_definitions'],
+ self._policy_data['policy_atomic_group_definitions'])
+ self._ProcessPolicyList(
+ self._policy_data['policy_atomic_group_definitions'])
+ self._policy_data['policy_definitions'] = self._ExpandGroups(
+ self._policy_data['policy_definitions'])
+ self._policy_definitions = self._policy_data['policy_definitions']
+ self._ProcessPolicyList(self._policy_definitions)
+
+ def _ProcessProductPlatformString(self, product_platform_string):
+ '''Splits the |product_platform_string| string to product and a list of
+ platforms.'''
+ if '.' in product_platform_string:
+ product, platform = product_platform_string.split('.')
+ if platform == '*':
+ # e.g.: 'chrome.*:8-10'
+ platforms = ['linux', 'mac', 'win']
+ else:
+ # e.g.: 'chrome.win:-10'
+ platforms = [platform]
+ else:
+ # e.g.: 'chrome_frame:7-'
+ product, platform = {
+ 'android': ('chrome', 'android'),
+ 'webview_android': ('webview', 'android'),
+ 'ios': ('chrome', 'ios'),
+ 'chrome_os': ('chrome_os', 'chrome_os'),
+ 'chrome_frame': ('chrome_frame', 'win'),
+ }[product_platform_string]
+ platforms = [platform]
+ return product, platforms
+
+ def _ProcessSupportedOn(self, supported_on):
+ '''Parses and converts the string items of the list of supported platforms
+ into dictionaries.
+
+ Args:
+ supported_on: The list of supported platforms. E.g.:
+ ['chrome.win:8-10', 'chrome_frame:10-']
+
+ Returns:
+ supported_on: The list with its items converted to dictionaries. E.g.:
+ [{
+ 'product': 'chrome',
+ 'platform': 'win',
+ 'since_version': '8',
+ 'until_version': '10'
+ }, {
+ 'product': 'chrome_frame',
+ 'platform': 'win',
+ 'since_version': '10',
+ 'until_version': ''
+ }]
+ '''
+ result = []
+ for supported_on_item in supported_on:
+ product_platform_part, version_part = supported_on_item.split(':')
+ product, platforms = self._ProcessProductPlatformString(
+ product_platform_part)
+
+ since_version, until_version = version_part.split('-')
+ for platform in platforms:
+ result.append({
+ 'product': product,
+ 'platform': platform,
+ 'since_version': since_version,
+ 'until_version': until_version
+ })
+ return result
+
+ def _ProcessFutureOn(self, future_on):
+ '''Parses and converts the |future_on| strings into a list of dictionaries
+ contain product and platform string pair.
+
+ Args:
+ future_on: A list of platform strings. E.g.:
+ ['chrome.win', 'chromeos']
+ Returns:
+ future_on: A list of dictionaries. E.g.:
+ [{
+ 'product': 'chrome',
+ 'platform': 'win',
+ },{
+ 'product': 'chrome_os',
+ 'platform': 'chrome_os',
+ }]
+ '''
+ result = []
+ for future in future_on:
+ product, platforms = self._ProcessProductPlatformString(future)
+ for platform in platforms:
+ result.append({
+ 'product': product,
+ 'platform': platform,
+ })
+ return result
+
+ def _ProcessPolicy(self, policy):
+ '''Processes localized message strings in a policy or a group.
+ Also breaks up the content of 'supported_on' attribute into a list.
+
+ Args:
+ policy: The data structure of the policy or group, that will get message
+ strings here.
+ '''
+ if policy['type'] != 'atomic_group':
+ policy['desc'] = self._ImportMessage(policy['desc'])
+ policy['caption'] = self._ImportMessage(policy['caption'])
+ if 'label' in policy:
+ policy['label'] = self._ImportMessage(policy['label'])
+ if 'arc_support' in policy:
+ policy['arc_support'] = self._ImportMessage(policy['arc_support'])
+
+ if IsGroupOrAtomicGroup(policy):
+ self._ProcessPolicyList(policy['policies'])
+ elif policy['type'] in ('string-enum', 'int-enum', 'string-enum-list'):
+ # Iterate through all the items of an enum-type policy, and add captions.
+ for item in policy['items']:
+ item['caption'] = self._ImportMessage(item['caption'])
+ if 'supported_on' in item:
+ item['supported_on'] = self._ProcessSupportedOn(item['supported_on'])
+ if not IsGroupOrAtomicGroup(policy):
+ if not 'label' in policy:
+ # If 'label' is not specified, then it defaults to 'caption':
+ policy['label'] = policy['caption']
+ policy['supported_on'] = self._ProcessSupportedOn(
+ policy.get('supported_on', []))
+ policy['future_on'] = self._ProcessFutureOn(policy.get('future_on', []))
+
+ def _ProcessPolicyList(self, policy_list):
+ '''Adds localized message strings to each item in a list of policies and
+ groups. Also breaks up the content of 'supported_on' attributes into lists
+ of dictionaries.
+
+ Args:
+ policy_list: A list of policies and groups. Message strings will be added
+ for each item and to their child items, recursively.
+ '''
+ for policy in policy_list:
+ self._ProcessPolicy(policy)
+
+ def GetTemplateText(self, template_writer):
+ '''Generates the text of the template from the arguments given
+ to the constructor, using a given TemplateWriter.
+
+ Args:
+ template_writer: An object implementing TemplateWriter. Its methods
+ are called here for each item of self._policy_data.
+
+ Returns:
+ The text of the generated template.
+ '''
+ # Create a copy, so that writers can't screw up subsequent writers.
+ policy_data_copy = copy.deepcopy(self._policy_data)
+ return template_writer.WriteTemplate(policy_data_copy)
+
+
+ def _AddGroups(self, policy_list):
+ '''Adds a 'group' field, which is set to be the group's name, to the
+ policies that are part of a group.
+
+ Args:
+ policy_list: A list of policies and groups whose policies will have a
+ 'group' field added.
+ '''
+ groups = [policy for policy in policy_list if policy['type'] == 'group']
+ policy_lookup = {
+ policy['name']: policy
+ for policy in policy_list
+ if not IsGroupOrAtomicGroup(policy)
+ }
+ for group in groups:
+ for policy_name in group['policies']:
+ policy_lookup[policy_name]['group'] = group['name']
+
+ def _AddAtomicGroups(self, policy_list, policy_atomic_groups):
+ '''Adds an 'atomic_group' field to the policies that are part of an atomic
+ group.
+
+ Args:
+ policy_list: A list of policies and groups.
+ policy_atomic_groups: A list of policy atomic groups
+ '''
+ policy_lookup = {
+ policy['name']: policy
+ for policy in policy_list
+ if not IsGroupOrAtomicGroup(policy)
+ }
+ for group in policy_atomic_groups:
+ for policy_name in group['policies']:
+ policy_lookup[policy_name]['atomic_group'] = group['name']
+ break
+
+ def _ExpandAtomicGroups(self, policy_list, policy_atomic_groups):
+ '''Replaces policies names inside atomic group definitions for actual
+ policies definitions.
+
+ Args:
+ policy_list: A list of policies and groups.
+
+ Returns:
+ Modified policy_list
+ '''
+ policies = [
+ policy for policy in policy_list if not IsGroupOrAtomicGroup(policy)
+ ]
+ for group in policy_atomic_groups:
+ group['type'] = 'atomic_group'
+ expanded = self._ExpandGroups(policies + policy_atomic_groups)
+ expanded = [policy for policy in expanded if IsGroupOrAtomicGroup(policy)]
+ return copy.deepcopy(expanded)
+
+ def _ExpandGroups(self, policy_list):
+ '''Replaces policies names inside group definitions for actual policies
+ definitions. If policy does not belong to any group, leave it as is.
+
+ Args:
+ policy_list: A list of policies and groups.
+
+ Returns:
+ Modified policy_list
+ '''
+ groups = [policy for policy in policy_list if IsGroupOrAtomicGroup(policy)]
+ policies = {
+ policy['name']: policy
+ for policy in policy_list
+ if not IsGroupOrAtomicGroup(policy)
+ }
+ policies_in_groups = set()
+ result_policies = []
+ for group in groups:
+ group_policies = group['policies']
+ expanded_policies = [
+ policies[policy_name] for policy_name in group_policies
+ ]
+ assert policies_in_groups.isdisjoint(group_policies)
+ policies_in_groups.update(group_policies)
+ group['policies'] = expanded_policies
+ result_policies.append(group)
+
+ result_policies.extend([
+ policy for policy in policy_list if not IsGroupOrAtomicGroup(policy) and
+ policy['name'] not in policies_in_groups
+ ])
+ return result_policies
diff --git a/chromium/components/policy/tools/template_writers/policy_template_generator_unittest.py b/chromium/components/policy/tools/template_writers/policy_template_generator_unittest.py
new file mode 100755
index 00000000000..5beb6082a4d
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/policy_template_generator_unittest.py
@@ -0,0 +1,654 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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 os
+import sys
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../..'))
+
+import unittest
+
+import policy_template_generator
+from writers import mock_writer
+from writers import template_writer
+
+
+class PolicyTemplateGeneratorUnittest(unittest.TestCase):
+ '''Unit tests for policy_template_generator.py.'''
+
+ TEST_CONFIG = {
+ 'app_name': '_app_name',
+ 'frame_name': '_frame_name',
+ 'os_name': '_os_name',
+ }
+
+ TEST_POLICY_DATA = {
+ 'messages': {},
+ 'placeholders': [],
+ 'policy_definitions': [],
+ 'policy_atomic_group_definitions': [],
+ }
+
+ def do_test(self, policy_data, writer):
+ '''Executes a test case.
+
+ Creates and invokes an instance of PolicyTemplateGenerator with
+ the given arguments.
+
+ Notice: Plain comments are used in test methods instead of docstrings,
+ so that method names do not get overridden by the docstrings in the
+ test output.
+
+ Args:
+ policy_data: The list of policies and groups as it would be
+ loaded from policy_templates.json.
+ writer: A writer used for this test. It is usually derived from
+ mock_writer.MockWriter.
+ '''
+ writer.tester = self
+
+ policy_data = dict(self.TEST_POLICY_DATA, **policy_data)
+ policy_generator = policy_template_generator.PolicyTemplateGenerator(
+ self.TEST_CONFIG, policy_data)
+ res = policy_generator.GetTemplateText(writer)
+ writer.Test()
+ return res
+
+ def testSequence(self):
+ # Test the sequence of invoking the basic PolicyWriter operations,
+ # in case of empty input data structures.
+ class LocalMockWriter(mock_writer.MockWriter):
+
+ def __init__(self):
+ self.log = 'init;'
+
+ def Init(self):
+ self.log += 'prepare;'
+
+ def BeginTemplate(self):
+ self.log += 'begin;'
+
+ def EndTemplate(self):
+ self.log += 'end;'
+
+ def GetTemplateText(self):
+ self.log += 'get_text;'
+ return 'writer_result_string'
+
+ def Test(self):
+ self.tester.assertEquals(self.log, 'init;prepare;begin;end;get_text;')
+
+ result = self.do_test({}, LocalMockWriter())
+ self.assertEquals(result, 'writer_result_string')
+
+ def testEmptyGroups(self):
+ # Test that empty policy groups are not passed to the writer.
+ policies_mock = {
+ 'policy_definitions': [
+ {
+ 'name': 'Group1',
+ 'type': 'group',
+ 'policies': [],
+ 'desc': '',
+ 'caption': ''
+ },
+ {
+ 'name': 'Group2',
+ 'type': 'group',
+ 'policies': [],
+ 'desc': '',
+ 'caption': ''
+ },
+ {
+ 'name': 'Group3',
+ 'type': 'group',
+ 'policies': [],
+ 'desc': '',
+ 'caption': ''
+ },
+ ]
+ }
+
+ class LocalMockWriter(mock_writer.MockWriter):
+
+ def __init__(self):
+ self.log = ''
+
+ def BeginPolicyGroup(self, group):
+ self.log += '['
+
+ def EndPolicyGroup(self):
+ self.log += ']'
+
+ def Test(self):
+ self.tester.assertEquals(self.log, '')
+
+ self.do_test(policies_mock, LocalMockWriter())
+
+ def testGroups(self):
+ # Test that policy groups are passed to the writer in the correct order.
+ policies_mock = {
+ 'policy_definitions': [
+ {
+ 'name': 'Group1',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['TAG1'],
+ },
+ {
+ 'name': 'Group2',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['TAG2',],
+ },
+ {
+ 'name': 'Group3',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['TAG3'],
+ },
+ {
+ 'name': 'TAG1',
+ 'type': 'mock',
+ 'supported_on': [],
+ 'caption': '',
+ 'desc': ''
+ },
+ {
+ 'name': 'TAG2',
+ 'type': 'mock',
+ 'supported_on': [],
+ 'caption': '',
+ 'desc': ''
+ },
+ {
+ 'name': 'TAG3',
+ 'type': 'mock',
+ 'supported_on': [],
+ 'caption': '',
+ 'desc': ''
+ },
+ ]
+ }
+
+ class LocalMockWriter(mock_writer.MockWriter):
+
+ def __init__(self):
+ self.log = ''
+
+ def BeginPolicyGroup(self, group):
+ self.log += '[' + group['policies'][0]['name']
+
+ def EndPolicyGroup(self):
+ self.log += ']'
+
+ def Test(self):
+ self.tester.assertEquals(self.log, '[TAG1][TAG2][TAG3]')
+
+ self.do_test(policies_mock, LocalMockWriter())
+
+ def testPolicies(self):
+ # Test that policies are passed to the writer in the correct order.
+ policy_defs_mock = {
+ 'policy_definitions': [
+ {
+ 'name': 'Group1',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['Group1Policy1', 'Group1Policy2'],
+ },
+ {
+ 'name': 'Group2',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['Group2Policy3'],
+ },
+ {
+ 'name': 'Group1Policy1',
+ 'type': 'string',
+ 'supported_on': [],
+ 'caption': '',
+ 'desc': ''
+ },
+ {
+ 'name': 'Group1Policy2',
+ 'type': 'string',
+ 'supported_on': [],
+ 'caption': '',
+ 'desc': ''
+ },
+ {
+ 'name': 'Group2Policy3',
+ 'type': 'string',
+ 'supported_on': [],
+ 'caption': '',
+ 'desc': ''
+ },
+ ]
+ }
+
+ class LocalMockWriter(mock_writer.MockWriter):
+
+ def __init__(self):
+ self.policy_name = None
+ self.policy_list = []
+
+ def BeginPolicyGroup(self, group):
+ self.group = group
+
+ def EndPolicyGroup(self):
+ self.group = None
+
+ def WritePolicy(self, policy):
+ self.tester.assertEquals(policy['name'][0:6], self.group['name'])
+ self.policy_list.append(policy['name'])
+
+ def Test(self):
+ self.tester.assertEquals(
+ self.policy_list,
+ ['Group1Policy1', 'Group1Policy2', 'Group2Policy3'])
+
+ self.do_test(policy_defs_mock, LocalMockWriter())
+
+ def testIntEnumTexts(self):
+ # Test that GUI messages are assigned correctly to int-enums
+ # (aka dropdown menus).
+ policy_defs_mock = {
+ 'policy_definitions': [{
+ 'name':
+ 'Policy1',
+ 'type':
+ 'int-enum',
+ 'caption':
+ '',
+ 'desc':
+ '',
+ 'supported_on': [],
+ 'items': [
+ {
+ 'name': 'item1',
+ 'value': 0,
+ 'caption': 'string1',
+ 'desc': ''
+ },
+ {
+ 'name': 'item2',
+ 'value': 1,
+ 'caption': 'string2',
+ 'desc': ''
+ },
+ {
+ 'name': 'item3',
+ 'value': 3,
+ 'caption': 'string3',
+ 'desc': ''
+ },
+ ]
+ }]
+ }
+
+ class LocalMockWriter(mock_writer.MockWriter):
+
+ def WritePolicy(self, policy):
+ self.tester.assertEquals(policy['items'][0]['caption'], 'string1')
+ self.tester.assertEquals(policy['items'][1]['caption'], 'string2')
+ self.tester.assertEquals(policy['items'][2]['caption'], 'string3')
+
+ self.do_test(policy_defs_mock, LocalMockWriter())
+
+ def testStringEnumTexts(self):
+ # Test that GUI messages are assigned correctly to string-enums
+ # (aka dropdown menus).
+ policy_data_mock = {
+ 'policy_definitions': [{
+ 'name':
+ 'Policy1',
+ 'type':
+ 'string-enum',
+ 'caption':
+ '',
+ 'desc':
+ '',
+ 'supported_on': [],
+ 'items': [
+ {
+ 'name': 'item1',
+ 'value': 'one',
+ 'caption': 'string1',
+ 'desc': ''
+ },
+ {
+ 'name': 'item2',
+ 'value': 'two',
+ 'caption': 'string2',
+ 'desc': ''
+ },
+ {
+ 'name': 'item3',
+ 'value': 'three',
+ 'caption': 'string3',
+ 'desc': ''
+ },
+ ]
+ }]
+ }
+
+ class LocalMockWriter(mock_writer.MockWriter):
+
+ def WritePolicy(self, policy):
+ self.tester.assertEquals(policy['items'][0]['caption'], 'string1')
+ self.tester.assertEquals(policy['items'][1]['caption'], 'string2')
+ self.tester.assertEquals(policy['items'][2]['caption'], 'string3')
+
+ self.do_test(policy_data_mock, LocalMockWriter())
+
+ def testStringEnumTexts(self):
+ # Test that GUI messages are assigned correctly to string-enums
+ # (aka dropdown menus).
+ policy_data_mock = {
+ 'policy_definitions': [{
+ 'name':
+ 'Policy1',
+ 'type':
+ 'string-enum-list',
+ 'caption':
+ '',
+ 'desc':
+ '',
+ 'supported_on': [],
+ 'items': [
+ {
+ 'name': 'item1',
+ 'value': 'one',
+ 'caption': 'string1',
+ 'desc': ''
+ },
+ {
+ 'name': 'item2',
+ 'value': 'two',
+ 'caption': 'string2',
+ 'desc': ''
+ },
+ {
+ 'name': 'item3',
+ 'value': 'three',
+ 'caption': 'string3',
+ 'desc': ''
+ },
+ ]
+ }]
+ }
+
+ class LocalMockWriter(mock_writer.MockWriter):
+
+ def WritePolicy(self, policy):
+ self.tester.assertEquals(policy['items'][0]['caption'], 'string1')
+ self.tester.assertEquals(policy['items'][1]['caption'], 'string2')
+ self.tester.assertEquals(policy['items'][2]['caption'], 'string3')
+
+ self.do_test(policy_data_mock, LocalMockWriter())
+
+ def testWin7OnlyPolicy(self):
+ # Test that Win7 only policy is marked as windows policy with speicial flag.
+ policy_data_mock = {
+ 'policy_definitions': [{
+ 'name':
+ 'Policy1',
+ 'type':
+ 'string-enum-list',
+ 'caption':
+ '',
+ 'desc':
+ '',
+ 'supported_on': ['chrome.win7:2-'],
+ 'items': [{
+ 'name': 'item1',
+ 'value': 'one',
+ 'caption': 'string1',
+ 'desc': '',
+ 'supported_on': ['chrome.win7:2-'],
+ },]
+ }]
+ }
+
+ class LocalMockWriter(mock_writer.MockWriter):
+
+ def WritePolicy(self, policy):
+ self.tester.assertEquals(policy['supported_on'][0]['platform'], 'win7')
+ self.tester.assertEquals(
+ policy['items'][0]['supported_on'][0]['platform'], 'win7')
+
+ self.do_test(policy_data_mock, LocalMockWriter())
+
+ def testFutures(self):
+ # Test that 'future_on' tag has been processed successfully.
+ policy_data_mock = {
+ 'policy_definitions': [{
+ 'name': 'UnrelasedPolicy',
+ 'type': 'string',
+ 'caption': '',
+ 'desc': '',
+ 'future_on': ['chrome.*', 'chrome_os']
+ }, {
+ 'name':
+ 'PartiallyReleasedPolicy',
+ 'type':
+ 'string',
+ 'caption':
+ '',
+ 'desc':
+ '',
+ 'supported_on': ['chrome.win:2-', 'chrome.mac:2-', 'chrome_os:4-'],
+ 'future_on': ['chrome.linux', 'chrome_os'],
+ }, {
+ 'name': 'ReleasedPolicy',
+ 'type': 'string',
+ 'caption': '',
+ 'desc': '',
+ 'supported_on': ['chrome.*:2-', 'chrome_os:4-'],
+ }]
+ }
+
+ expected_future_on = {
+ 'UnrelasedPolicy': [{
+ 'product': 'chrome',
+ 'platform': 'linux'
+ }, {
+ 'product': 'chrome',
+ 'platform': 'mac'
+ }, {
+ 'product': 'chrome',
+ 'platform': 'win'
+ }, {
+ 'product': 'chrome_os',
+ 'platform': 'chrome_os'
+ }],
+ 'PartiallyReleasedPolicy': [{
+ 'product': 'chrome',
+ 'platform': 'linux'
+ }, {
+ 'product': 'chrome_os',
+ 'platform': 'chrome_os'
+ }],
+ 'ReleasedPolicy': [],
+ }
+
+ class LocalMockWriter(mock_writer.MockWriter):
+ def WritePolicy(self, policy):
+ self.tester.assertTrue(isinstance(policy['supported_on'], list))
+ self.tester.assertEquals(policy['future_on'],
+ expected_future_on[policy['name']])
+
+ self.do_test(policy_data_mock, LocalMockWriter())
+
+ def testPolicyFiltering(self):
+ # Test that policies are filtered correctly based on their annotations.
+ policy_data_mock = {
+ 'policy_definitions': [
+ {
+ 'name': 'Group1',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['Group1Policy1', 'Group1Policy2'],
+ },
+ {
+ 'name': 'Group2',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['Group2Policy3'],
+ },
+ {
+ 'name': 'SinglePolicy',
+ 'type': 'int',
+ 'caption': '',
+ 'desc': '',
+ 'supported_on': ['chrome.eee:8-']
+ },
+ {
+ 'name':
+ 'Group1Policy1',
+ 'type':
+ 'string',
+ 'caption':
+ '',
+ 'desc':
+ '',
+ 'supported_on': [
+ 'chrome.aaa:8-', 'chrome.bbb:8-', 'chrome.ccc:8-'
+ ]
+ },
+ {
+ 'name': 'Group1Policy2',
+ 'type': 'string',
+ 'caption': '',
+ 'desc': '',
+ 'supported_on': ['chrome.ddd:8-']
+ },
+ {
+ 'name': 'Group2Policy3',
+ 'type': 'string',
+ 'caption': '',
+ 'desc': '',
+ 'supported_on': ['chrome.eee:8-']
+ },
+ ]
+ }
+
+ # This writer accumulates the list of policies it is asked to write.
+ # This list is stored in the result_list member variable and can
+ # be used later for assertions.
+ class LocalMockWriter(mock_writer.MockWriter):
+
+ def __init__(self, platforms):
+ super(LocalMockWriter, self).__init__(platforms)
+ self.policy_name = None
+ self.result_list = []
+
+ def BeginPolicyGroup(self, group):
+ self.group = group
+ self.result_list.append('begin_' + group['name'])
+
+ def EndPolicyGroup(self):
+ self.result_list.append('end_group')
+ self.group = None
+
+ def WritePolicy(self, policy):
+ self.result_list.append(policy['name'])
+
+ def IsPolicySupported(self, policy):
+ # Call the original (non-mock) implementation of this method.
+ return template_writer.TemplateWriter.IsPolicySupported(self, policy)
+
+ local_mock_writer = LocalMockWriter(['eee'])
+ self.do_test(policy_data_mock, local_mock_writer)
+ # Test that only policies of platform 'eee' were written:
+ self.assertEquals(
+ local_mock_writer.result_list,
+ ['begin_Group2', 'Group2Policy3', 'end_group', 'SinglePolicy'])
+
+ local_mock_writer = LocalMockWriter(['ddd', 'bbb'])
+ self.do_test(policy_data_mock, local_mock_writer)
+ # Test that only policies of platforms 'ddd' and 'bbb' were written:
+ self.assertEquals(
+ local_mock_writer.result_list,
+ ['begin_Group1', 'Group1Policy1', 'Group1Policy2', 'end_group'])
+
+ def testSortingInvoked(self):
+ # Tests that policy-sorting happens before passing policies to the writer.
+ policy_data = {
+ 'policy_definitions': [
+ {
+ 'name': 'zp',
+ 'type': 'string',
+ 'supported_on': [],
+ 'caption': '',
+ 'desc': ''
+ },
+ {
+ 'name': 'ap',
+ 'type': 'string',
+ 'supported_on': [],
+ 'caption': '',
+ 'desc': ''
+ },
+ ]
+ }
+
+ class LocalMockWriter(mock_writer.MockWriter):
+
+ def __init__(self):
+ self.result_list = []
+
+ def WritePolicy(self, policy):
+ self.result_list.append(policy['name'])
+
+ def Test(self):
+ self.tester.assertEquals(self.result_list, ['ap', 'zp'])
+
+ self.do_test(policy_data, LocalMockWriter())
+
+ def testImportMessage_noIndentation(self):
+ message = '''
+Simple policy:
+
+Description of simple policy'''
+
+ policy_generator = policy_template_generator.PolicyTemplateGenerator(
+ self.TEST_CONFIG, self.TEST_POLICY_DATA)
+ self.assertEquals(message, policy_generator._ImportMessage(message))
+
+ def testImportMessage_withIndentation(self):
+ message = '''JSON policy:
+
+ JSON spec:
+ {
+ "key": {
+ "key2": "value"
+ }
+ }'''
+ imported_message = '''JSON policy:
+
+JSON spec:
+{
+ "key": {
+ "key2": "value"
+ }
+}'''
+
+ policy_generator = policy_template_generator.PolicyTemplateGenerator(
+ self.TEST_CONFIG, self.TEST_POLICY_DATA)
+ self.assertEquals(imported_message,
+ policy_generator._ImportMessage(message))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/template_formatter.py b/chromium/components/policy/tools/template_writers/template_formatter.py
new file mode 100755
index 00000000000..9af88164b66
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/template_formatter.py
@@ -0,0 +1,274 @@
+#!/usr/bin/env python3
+# 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.
+'''Takes translated policy_template.json files as input, applies template
+writers and emits various template and doc files (admx, html, json etc.).
+'''
+
+import argparse
+import codecs
+import collections
+import json
+import os
+import re
+import sys
+
+sys.path.insert(
+ 0,
+ os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
+ os.pardir, 'third_party', 'six', 'src'))
+
+import six
+
+import writer_configuration
+import policy_template_generator
+
+from writers import adm_writer, adml_writer, admx_writer, \
+ chromeos_admx_writer, chromeos_adml_writer, \
+ google_admx_writer, google_adml_writer, \
+ android_policy_writer, reg_writer, doc_writer, \
+ doc_atomic_groups_writer , json_writer, plist_writer, \
+ plist_strings_writer, ios_app_config_writer, jamf_writer
+
+
+def MacLanguageMap(lang):
+ '''Handles slightly different path naming convention for Macs:
+ - 'en-US' -> 'en'
+ - '-' -> '_'
+
+ Args:
+ lang: Language, e.g. 'en-US'.
+ '''
+ return 'en' if lang == 'en-US' else lang.replace('-', '_')
+
+
+'''Template writer descriptors.
+
+Members:
+ type: Writer type, e.g. 'admx'
+ is_per_language: Whether one file per language should be emitted.
+ encoding: Encoding of the output file.
+ language_map: Optional language mapping for file paths.
+ force_windows_line_ending: Forces output file to use Windows line ending.
+'''
+WriterDesc = collections.namedtuple('WriterDesc', [
+ 'type', 'is_per_language', 'encoding', 'language_map',
+ 'force_windows_line_ending'
+])
+
+_WRITER_DESCS = [
+ WriterDesc('adm', True, 'utf-16', None, True),
+ WriterDesc('adml', True, 'utf-16', None, True),
+ WriterDesc('admx', False, 'utf-16', None, True),
+ WriterDesc('google_adml', True, 'utf-8', None, True),
+ WriterDesc('google_admx', False, 'utf-8', None, True),
+ WriterDesc('chromeos_adml', True, 'utf-8', None, True),
+ WriterDesc('chromeos_admx', False, 'utf-8', None, True),
+ WriterDesc('android_policy', False, 'utf-8', None, False),
+ WriterDesc('reg', False, 'utf-16', None, False),
+ WriterDesc('doc', True, 'utf-8', None, False),
+ WriterDesc('doc_atomic_groups', True, 'utf-8', None, False),
+ WriterDesc('json', False, 'utf-8', None, False),
+ WriterDesc('plist', False, 'utf-8', None, False),
+ WriterDesc('plist_strings', True, 'utf-8', MacLanguageMap, False),
+ WriterDesc('jamf', False, 'utf-8', None, False),
+ WriterDesc('ios_app_config', False, 'utf-8', None, False),
+]
+
+# Template writers that are not per-language use policy_templates.json from
+# this language.
+_DEFAULT_LANGUAGE = 'en-US'
+
+
+def GetWriter(writer_type, config):
+ '''Returns the template writer for the given writer type.
+
+ Args:
+ writer_type: Writer type, e.g. 'admx'.
+ config: Writer configuration, see writer_configuration.py.
+ '''
+ return eval(writer_type + '_writer.GetWriter(config)')
+
+
+def _GetWriterConfiguration(grit_defines):
+ '''Returns the writer configuration based on grit defines.
+
+ Args:
+ grit_defines: Array of grit defines, see grit_rule.gni.
+ '''
+ # Build a dictionary from grit defines, which can be plain DEFs or KEY=VALUEs.
+ grit_defines_dict = {}
+ for define in grit_defines:
+ parts = define.split('=', 1)
+ grit_defines_dict[parts[0]] = parts[1] if len(parts) > 1 else 1
+ return writer_configuration.GetConfigurationForBuild(grit_defines_dict)
+
+
+def _ParseVersionFile(version_path):
+ '''Parse version file, return the version if it exists.
+
+ Args:
+ version_path: The path of Chrome VERSION file containing the major version
+ number.
+ '''
+ version = {}
+ with open(version_path) as fp:
+ for line in fp:
+ key, _, value = line.partition('=')
+ if key.strip() == 'MAJOR':
+ version['major'] = value.strip()
+ elif key.strip() == 'MINOR':
+ version['minor'] = value.strip()
+ elif key.strip() == 'BUILD':
+ version['build'] = value.strip()
+ elif key.strip() == 'PATCH':
+ version['patch'] = value.strip()
+
+ version_found = len(version) == 4
+ return version if version_found else None
+
+
+def _JsonToUtf8Encoding(data, ignore_dicts=False):
+ if six.PY2 and isinstance(data, unicode):
+ return data.encode('utf-8')
+ elif isinstance(data, list):
+ return [_JsonToUtf8Encoding(item, False) for item in data]
+ elif isinstance(data, dict):
+ return {
+ _JsonToUtf8Encoding(key): _JsonToUtf8Encoding(value)
+ for key, value in data.items()
+ }
+ return data
+
+
+def main():
+ '''Main policy template conversion script.
+ Usage: template_formatter
+ --translations <translations_path>
+ --languages <language_list>
+ [--adm <adm_path>]
+ ...
+ [--android_policy <android_policy_path>]
+ -D <grit_define>
+ -E <grit_env_variable>
+ -t <grit_target_platform>
+
+ Args:
+ translations: Absolute path of the translated policy_template.json
+ files. Must contain a ${lang} placeholder for the language.
+ languages: Comma-separated list of languages. Trailing commas are fine, e.g.
+ 'en,de,'
+ adm, adml, google_adml, doc, plist_string: Absolute path of the
+ corresponding file types. Must contain a ${lang} placeholder.
+ admx, google_admx, android_policy, reg, json, plist: Absolute path of the
+ corresponding file types. Must NOT contain a ${lang} placeholder. There
+ is only one output file, not one per language.
+ D: List of grit defines, used to assemble writer configuration.
+ E, t: Grit environment variables and target platform. Unused, but
+ grit_rule.gni adds them, so ArgumentParser must handle them.
+ '''
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--translations', dest='translations')
+ parser.add_argument('--languages', dest='languages')
+ parser.add_argument('--version_path', dest='version_path')
+ parser.add_argument('--adm', action='append', dest='adm')
+ parser.add_argument('--adml', action='append', dest='adml')
+ parser.add_argument('--admx', action='append', dest='admx')
+ parser.add_argument('--chromeos_adml', action='append', dest='chromeos_adml')
+ parser.add_argument('--chromeos_admx', action='append', dest='chromeos_admx')
+ parser.add_argument('--google_adml', action='append', dest='google_adml')
+ parser.add_argument('--google_admx', action='append', dest='google_admx')
+ parser.add_argument('--reg', action='append', dest='reg')
+ parser.add_argument('--doc', action='append', dest='doc')
+ parser.add_argument(
+ '--doc_atomic_groups', action='append', dest='doc_atomic_groups')
+ parser.add_argument('--local',
+ action='store_true',
+ help='If set, the documentation will be built so '
+ 'that links work locally in the generated path.')
+ parser.add_argument('--json', action='append', dest='json')
+ parser.add_argument('--plist', action='append', dest='plist')
+ parser.add_argument('--plist_strings', action='append', dest='plist_strings')
+ parser.add_argument('--jamf', action='append', dest='jamf')
+ parser.add_argument(
+ '--android_policy', action='append', dest='android_policy')
+ parser.add_argument(
+ '--ios_app_config', action='append', dest='ios_app_config')
+ parser.add_argument('-D', action='append', dest='grit_defines')
+ parser.add_argument('-E', action='append', dest='grit_build_env')
+ parser.add_argument('-t', action='append', dest='grit_target')
+ args = parser.parse_args()
+
+ _LANG_PLACEHOLDER = "${lang}"
+ assert _LANG_PLACEHOLDER in args.translations
+
+ languages = list(filter(bool, args.languages.split(',')))
+ assert _DEFAULT_LANGUAGE in languages
+
+ config = _GetWriterConfiguration(args.grit_defines)
+
+ version = _ParseVersionFile(args.version_path)
+ if version != None:
+ config['major_version'] = int(version['major'])
+ config['version'] = '.'.join([
+ version['major'], version['minor'], version['build'], version['patch']
+ ])
+ config['local'] = args.local
+
+ # For each language, load policy data once and run all writers on it.
+ for lang in languages:
+ # Load the policy data.
+ policy_templates_json_path = args.translations.replace(
+ _LANG_PLACEHOLDER, lang)
+ # Loads the localized policy json file which must be a valid json file
+ # encoded in utf-8.
+ with codecs.open(policy_templates_json_path, 'r', 'utf-8') as policy_file:
+ policy_data = json.loads(
+ policy_file.read(), object_hook=_JsonToUtf8Encoding)
+
+ # Preprocess the policy data.
+ policy_generator = policy_template_generator.PolicyTemplateGenerator(
+ config, policy_data)
+
+ for writer_desc in _WRITER_DESCS:
+ # For writer types that are not per language (e.g. admx), only do it once.
+ if (not writer_desc.is_per_language and lang != _DEFAULT_LANGUAGE):
+ continue
+
+ # Was the current writer type passed as argument, e.g. --admx <path>?
+ # Note that all paths are arrays and we loop over all of them.
+ output_paths = getattr(args, writer_desc.type, '')
+ if (not output_paths):
+ continue
+ for output_path in output_paths:
+ # Substitute language placeholder in output file.
+ if (writer_desc.is_per_language):
+ assert _LANG_PLACEHOLDER in output_path
+ mapped_lang = writer_desc.language_map(
+ lang) if writer_desc.language_map else lang
+ output_path = output_path.replace(_LANG_PLACEHOLDER, mapped_lang)
+ else:
+ assert _LANG_PLACEHOLDER not in output_path
+
+ # Run the template writer on th policy data.
+ writer = GetWriter(writer_desc.type, config)
+ output_data = policy_generator.GetTemplateText(writer)
+ # Make sure the file uses Windows line endings if needed. This is
+ # important here because codecs.open() opens files in binary more and
+ # will not do line ending conversion.
+ if writer_desc.force_windows_line_ending:
+ output_data = re.sub(r'([^\r])\n', r'\1\r\n', output_data)
+
+ # Make output directory if it doesn't exist yet.
+ output_dir = os.path.split(output_path)[0]
+ if not os.path.exists(output_dir):
+ os.makedirs(output_dir)
+
+ # Write output file.
+ with codecs.open(output_path, 'w', writer_desc.encoding) as output_file:
+ output_file.write(output_data)
+
+
+if '__main__' == __name__:
+ sys.exit(main())
diff --git a/chromium/components/policy/tools/template_writers/test_suite_all.py b/chromium/components/policy/tools/template_writers/test_suite_all.py
new file mode 100755
index 00000000000..8ca43de4571
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/test_suite_all.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+# 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.
+'''Unit test suite that collects template_writer tests.'''
+
+import os
+import sys
+import unittest
+
+
+class TestSuiteAll(unittest.TestSuite):
+
+ def __init__(self):
+ super(TestSuiteAll, self).__init__()
+ # Imports placed here to prevent circular imports.
+ # pylint: disable-msg=C6204
+ import policy_template_generator_unittest
+ import writers.adm_writer_unittest
+ import writers.adml_writer_unittest
+ import writers.admx_writer_unittest
+ import writers.android_policy_writer_unittest
+ import writers.chromeos_adml_writer_unittest
+ import writers.chromeos_admx_writer_unittest
+ import writers.doc_writer_unittest
+ import writers.google_adml_writer_unittest
+ import writers.google_admx_writer_unittest
+ import writers.ios_app_config_writer_unittest
+ import writers.jamf_writer_unittest
+ import writers.json_writer_unittest
+ import writers.plist_strings_writer_unittest
+ import writers.plist_writer_unittest
+ import writers.reg_writer_unittest
+ import writers.template_writer_unittest
+ import writers.xml_writer_base_unittest
+
+ test_classes = [
+ policy_template_generator_unittest.PolicyTemplateGeneratorUnittest,
+ writers.adm_writer_unittest.AdmWriterUnittest,
+ writers.adml_writer_unittest.AdmlWriterUnittest,
+ writers.admx_writer_unittest.AdmxWriterUnittest,
+ writers.android_policy_writer_unittest.AndroidPolicyWriterUnittest,
+ writers.chromeos_adml_writer_unittest.ChromeOsAdmlWriterUnittest,
+ writers.chromeos_admx_writer_unittest.ChromeOsAdmxWriterUnittest,
+ writers.doc_writer_unittest.DocWriterUnittest,
+ writers.google_adml_writer_unittest.GoogleAdmlWriterUnittest,
+ writers.google_admx_writer_unittest.GoogleAdmxWriterUnittest,
+ writers.ios_app_config_writer_unittest.IOSAppConfigWriterUnitTests,
+ writers.jamf_writer_unittest.JamfWriterUnitTests,
+ writers.json_writer_unittest.JsonWriterUnittest,
+ writers.plist_strings_writer_unittest.PListStringsWriterUnittest,
+ writers.plist_writer_unittest.PListWriterUnittest,
+ writers.reg_writer_unittest.RegWriterUnittest,
+ writers.template_writer_unittest.TemplateWriterUnittests,
+ writers.xml_writer_base_unittest.XmlWriterBaseTest,
+ # add test classes here, in alphabetical order...
+ ]
+
+ for test_class in test_classes:
+ self.addTest(unittest.makeSuite(test_class))
+
+
+if __name__ == '__main__':
+ test_result = unittest.TextTestRunner(verbosity=2).run(TestSuiteAll())
+ sys.exit(len(test_result.errors) + len(test_result.failures))
diff --git a/chromium/components/policy/tools/template_writers/writer_configuration.py b/chromium/components/policy/tools/template_writers/writer_configuration.py
new file mode 100755
index 00000000000..43cb02dde07
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writer_configuration.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+
+
+def GetConfigurationForBuild(defines):
+ '''Returns a configuration dictionary for the given build that contains
+ build-specific settings and information.
+
+ Args:
+ defines: Definitions coming from the build system.
+
+ Raises:
+ Exception: If 'defines' contains an unknown build-type.
+ '''
+ # The prefix of key names in config determines which writer will use their
+ # corresponding values:
+ # win: Both ADM and ADMX.
+ # mac: Only plist.
+ # admx: Only ADMX.
+ # adm: Only ADM.
+ # none/other: Used by all the writers.
+ # Google:Cat_Google references the external google.admx file.
+ # category_path_strings strings in curly braces are looked up from localized
+ # 'messages' in policy_templates.json.
+ if '_chromium' in defines:
+ config = {
+ 'build': 'chromium',
+ 'app_name': 'Chromium',
+ 'doc_url': 'https://chromeenterprise.google/policies/',
+ 'frame_name': 'Chromium Frame',
+ 'os_name': 'ChromiumOS',
+ 'webview_name': 'Chromium WebView',
+ 'win_config': {
+ 'win': {
+ 'reg_mandatory_key_name': 'Software\\Policies\\Chromium',
+ 'reg_recommended_key_name':
+ 'Software\\Policies\\Chromium\\Recommended',
+ 'mandatory_category_path': ['chromium'],
+ 'recommended_category_path': ['chromium_recommended'],
+ 'category_path_strings': {
+ 'chromium': 'Chromium',
+ 'chromium_recommended': 'Chromium - {doc_recommended}',
+ },
+ 'namespace': 'Chromium.Policies.Chromium',
+ },
+ 'chrome_os': {
+ 'reg_mandatory_key_name': 'Software\\Policies\\ChromiumOS',
+ 'reg_recommended_key_name':
+ 'Software\\Policies\\ChromiumOS\\Recommended',
+ 'mandatory_category_path': ['chromium_os'],
+ 'recommended_category_path': ['chromium_os_recommended'],
+ 'category_path_strings': {
+ 'chromium_os': 'ChromiumOS',
+ 'chromium_os_recommended': 'ChromiumOS - {doc_recommended}',
+ },
+ 'namespace': 'Chromium.Policies.ChromiumOS'
+ },
+ },
+ 'admx_prefix': 'chromium',
+ 'linux_policy_path': '/etc/chromium/policies/',
+ 'bundle_id': 'org.chromium',
+ }
+ elif '_google_chrome' in defines:
+ config = {
+ 'build': 'chrome',
+ 'app_name': 'Google Chrome',
+ 'doc_url': 'https://chromeenterprise.google/policies/',
+ 'frame_name': 'Google Chrome Frame',
+ 'os_name': 'Google ChromeOS',
+ 'webview_name': 'Android System WebView',
+ 'win_config': {
+ 'win': {
+ 'reg_mandatory_key_name':
+ 'Software\\Policies\\Google\\Chrome',
+ 'reg_recommended_key_name':
+ 'Software\\Policies\\Google\\Chrome\\Recommended',
+ 'mandatory_category_path':
+ ['Google:Cat_Google', 'googlechrome'],
+ 'recommended_category_path':
+ ['Google:Cat_Google', 'googlechrome_recommended'],
+ 'category_path_strings': {
+ 'googlechrome': 'Google Chrome',
+ 'googlechrome_recommended':
+ 'Google Chrome - {doc_recommended}'
+ },
+ 'namespace':
+ 'Google.Policies.Chrome',
+ },
+ 'chrome_os': {
+ 'reg_mandatory_key_name':
+ 'Software\\Policies\\Google\\ChromeOS',
+ 'reg_recommended_key_name':
+ 'Software\\Policies\\Google\\ChromeOS\\Recommended',
+ 'mandatory_category_path':
+ ['Google:Cat_Google', 'googlechromeos'],
+ 'recommended_category_path':
+ ['Google:Cat_Google', 'googlechromeos_recommended'],
+ 'category_path_strings': {
+ 'googlechromeos':
+ 'Google ChromeOS',
+ 'googlechromeos_recommended':
+ 'Google ChromeOS - {doc_recommended}'
+ },
+ 'namespace':
+ 'Google.Policies.ChromeOS',
+ },
+ },
+ # The string 'Google' is defined in google.adml for ADMX, but ADM
+ # doesn't support external references, so we define this map here.
+ 'adm_category_path_strings': {
+ 'Google:Cat_Google': 'Google'
+ },
+ 'admx_prefix': 'chrome',
+ 'admx_using_namespaces': {
+ 'Google': 'Google.Policies' # prefix: namespace
+ },
+ 'linux_policy_path': '/etc/opt/chrome/policies/',
+ 'bundle_id': 'com.google.chrome.ios',
+ }
+ else:
+ raise Exception('Unknown build')
+ if 'version' in defines:
+ config['version'] = defines['version']
+ if 'major_version' in defines:
+ config['major_version'] = defines['major_version']
+ config['win_supported_os'] = 'SUPPORTED_WIN7'
+ config['win_supported_os_win7'] = 'SUPPORTED_WIN7_ONLY'
+ if 'mac_bundle_id' in defines:
+ config['mac_bundle_id'] = defines['mac_bundle_id']
+ config['android_webview_restriction_prefix'] = 'com.android.browser:'
+ return config
diff --git a/chromium/components/policy/tools/template_writers/writers/__init__.py b/chromium/components/policy/tools/template_writers/writers/__init__.py
new file mode 100755
index 00000000000..fb050d1f24f
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/__init__.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+'''Module template_writers.writers
+'''
+
+pass
diff --git a/chromium/components/policy/tools/template_writers/writers/adm_writer.py b/chromium/components/policy/tools/template_writers/writers/adm_writer.py
new file mode 100755
index 00000000000..7c20a5a8e78
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/adm_writer.py
@@ -0,0 +1,314 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+
+from writers import gpo_editor_writer
+import re
+
+NEWLINE = '\r\n'
+POLICY_LIST_URL = '''https://cloud.google.com/docs/chrome-enterprise/policies/?policy='''
+
+
+def GetWriter(config):
+ '''Factory method for creating AdmWriter objects.
+ See the constructor of TemplateWriter for description of
+ arguments.
+ '''
+ return AdmWriter(['win', 'win7'], config)
+
+
+class IndentedStringBuilder:
+ '''Utility class for building text with indented lines.'''
+
+ def __init__(self):
+ self.lines = []
+ self.indent = ''
+
+ def AddLine(self, string='', indent_diff=0):
+ '''Appends a string with indentation and a linebreak to |self.lines|.
+
+ Args:
+ string: The string to print.
+ indent_diff: the difference of indentation of the printed line,
+ compared to the next/previous printed line. Increment occurs
+ after printing the line, while decrement occurs before that.
+ '''
+ indent_diff *= 2
+ if indent_diff < 0:
+ self.indent = self.indent[(-indent_diff):]
+ if string != '':
+ self.lines.append(self.indent + string)
+ else:
+ self.lines.append('')
+ if indent_diff > 0:
+ self.indent += ''.ljust(indent_diff)
+
+ def AddLines(self, other):
+ '''Appends the content of another |IndentedStringBuilder| to |self.lines|.
+ Indentation of the added lines will be the sum of |self.indent| and
+ their original indentation.
+
+ Args:
+ other: The buffer from which lines are copied.
+ '''
+ for line in other.lines:
+ self.AddLine(line)
+
+ def ToString(self):
+ '''Returns |self.lines| as text string.'''
+ return NEWLINE.join(self.lines)
+
+
+class AdmWriter(gpo_editor_writer.GpoEditorWriter):
+ '''Class for generating policy templates in Windows ADM format.
+ It is used by PolicyTemplateGenerator to write ADM files.
+ '''
+
+ TYPE_TO_INPUT = {
+ 'string': 'EDITTEXT',
+ 'int': 'NUMERIC',
+ 'string-enum': 'DROPDOWNLIST',
+ 'int-enum': 'DROPDOWNLIST',
+ 'list': 'LISTBOX',
+ 'string-enum-list': 'LISTBOX',
+ 'dict': 'EDITTEXT',
+ 'external': 'EDITTEXT'
+ }
+
+ def _Escape(self, string):
+ return string.replace('.', '_')
+
+ def _AddGuiString(self, name, value):
+ # The |name| must be escaped.
+ assert name == self._Escape(name)
+ # Escape newlines in the value.
+ value = value.replace('\n', '\\n')
+ if name in self.strings_seen:
+ err = ('%s was added as "%s" and now added again as "%s"' %
+ (name, self.strings_seen[name], value))
+ assert value == self.strings_seen[name], err
+ else:
+ self.strings_seen[name] = value
+ line = '%s="%s"' % (name, value)
+ self.strings.AddLine(line)
+
+ def _WriteSupported(self, builder, is_win7_only):
+ builder.AddLine('#if version >= 4', 1)
+ key = 'win_supported_os_win7' if is_win7_only else 'win_supported_os'
+ supported_on_text = self.config[key]
+ builder.AddLine('SUPPORTED !!' + supported_on_text)
+ builder.AddLine('#endif', -1)
+
+ def _WritePart(self, policy, key_name, builder):
+ '''Writes the PART ... END PART section of a policy.
+
+ Args:
+ policy: The policy to write to the output.
+ key_name: The registry key backing the policy.
+ builder: Builder to append lines to.
+ '''
+ policy_part_name = self._Escape(policy['name'] + '_Part')
+ self._AddGuiString(policy_part_name, policy['label'])
+
+ # Print the PART ... END PART section:
+ builder.AddLine()
+ adm_type = self.TYPE_TO_INPUT[policy['type']]
+ builder.AddLine('PART !!%s %s' % (policy_part_name, adm_type), 1)
+ if policy['type'] in ('list', 'string-enum-list'):
+ # Note that the following line causes FullArmor ADMX Migrator to create
+ # corrupt ADMX files. Please use admx_writer to get ADMX files.
+ builder.AddLine('KEYNAME "%s\\%s"' % (key_name, policy['name']))
+ builder.AddLine('VALUEPREFIX ""')
+ else:
+ builder.AddLine('VALUENAME "%s"' % policy['name'])
+ if policy['type'] == 'int':
+ # The default max for NUMERIC values is 9999 which is too small for us.
+ max = 2000000000
+ min = 0
+ if self.PolicyHasRestrictions(policy):
+ schema = policy['schema']
+ if 'minimum' in schema:
+ min = schema['minimum']
+ if 'maximum' in schema:
+ max = schema['maximum']
+ builder.AddLine('MIN %d MAX %d' % (min, max))
+ if policy['type'] in ('string', 'dict', 'external'):
+ # The default max for EDITTEXT values is 1023, which is too small for
+ # big JSON blobs and other string policies.
+ builder.AddLine('MAXLEN 1000000')
+ if policy['type'] in ('int-enum', 'string-enum'):
+ builder.AddLine('ITEMLIST', 1)
+ for item in policy['items']:
+ if policy['type'] == 'int-enum':
+ value_text = 'NUMERIC ' + str(item['value'])
+ else:
+ value_text = '"' + item['value'] + '"'
+ string_id = self._Escape(policy['name'] + '_' + item['name'] +
+ '_DropDown')
+ builder.AddLine('NAME !!%s VALUE %s' % (string_id, value_text))
+ self._AddGuiString(string_id, item['caption'])
+ builder.AddLine('END ITEMLIST', -1)
+ builder.AddLine('END PART', -1)
+
+ def PolicyHasRestrictions(self, policy):
+ if 'schema' in policy:
+ return any(keyword in policy['schema'] \
+ for keyword in ['minimum', 'maximum'])
+ return False
+
+ def _WritePolicy(self, policy, key_name, builder):
+ policy_name = self._Escape(policy['name'] + '_Policy')
+ self._AddGuiString(policy_name, policy['caption'])
+ builder.AddLine('POLICY !!%s' % policy_name, 1)
+ self._WriteSupported(builder, self.IsPolicyOnWin7Only(policy))
+ policy_explain_name = self._Escape(policy['name'] + '_Explain')
+ policy_explain = self._GetPolicyExplanation(policy)
+ self._AddGuiString(policy_explain_name, policy_explain)
+ builder.AddLine('EXPLAIN !!' + policy_explain_name)
+
+ if policy['type'] == 'main':
+ builder.AddLine('VALUENAME "%s"' % policy['name'])
+ builder.AddLine('VALUEON NUMERIC 1')
+ builder.AddLine('VALUEOFF NUMERIC 0')
+ else:
+ self._WritePart(policy, key_name, builder)
+
+ builder.AddLine('END POLICY', -1)
+ builder.AddLine()
+
+ def _GetPolicyExplanation(self, policy):
+ '''Returns the explanation for a given policy.
+ Includes a link to the relevant documentation on chromium.org.
+ '''
+ policy_desc = policy.get('desc')
+ reference_url = POLICY_LIST_URL + policy['name']
+ reference_link_text = self.GetLocalizedMessage('reference_link')
+ reference_link_text = reference_link_text.replace('$6', reference_url)
+
+ if policy_desc is not None:
+ policy_desc += '\n\n'
+ if (not policy.get('deprecated', False) and
+ not self._IsRemovedPolicy(policy)):
+ policy_desc += reference_link_text
+ return policy_desc
+ else:
+ return reference_link_text
+
+ def WriteComment(self, comment):
+ self.lines.AddLine('; ' + comment)
+
+ def WritePolicy(self, policy):
+ if self.CanBeMandatory(policy):
+ self._WritePolicy(policy, self.winconfig['reg_mandatory_key_name'],
+ self.policies)
+
+ def WriteRecommendedPolicy(self, policy):
+ self._WritePolicy(policy, self.winconfig['reg_recommended_key_name'],
+ self.recommended_policies)
+
+ def BeginPolicyGroup(self, group):
+ category_name = self._Escape(group['name'] + '_Category')
+ self._AddGuiString(category_name, group['caption'])
+ self.policies.AddLine('CATEGORY !!' + category_name, 1)
+
+ def EndPolicyGroup(self):
+ self.policies.AddLine('END CATEGORY', -1)
+ self.policies.AddLine('')
+
+ def BeginRecommendedPolicyGroup(self, group):
+ category_name = self._Escape(group['name'] + '_Category')
+ self._AddGuiString(category_name, group['caption'])
+ self.recommended_policies.AddLine('CATEGORY !!' + category_name, 1)
+
+ def EndRecommendedPolicyGroup(self):
+ self.recommended_policies.AddLine('END CATEGORY', -1)
+ self.recommended_policies.AddLine('')
+
+ def _CreateTemplate(self, category_path, key_name, policies):
+ '''Creates the whole ADM template except for the [Strings] section, and
+ returns it as an |IndentedStringBuilder|.
+
+ Args:
+ category_path: List of strings representing the category path.
+ key_name: Main registry key backing the policies.
+ policies: ADM code for all the policies in an |IndentedStringBuilder|.
+ '''
+ lines = IndentedStringBuilder()
+ for part in category_path:
+ lines.AddLine('CATEGORY !!' + part, 1)
+ lines.AddLine('KEYNAME "%s"' % key_name)
+ lines.AddLine()
+
+ lines.AddLines(policies)
+
+ for part in category_path:
+ lines.AddLine('END CATEGORY', -1)
+ lines.AddLine()
+
+ return lines
+
+ def BeginTemplate(self):
+ if self._GetChromiumVersionString() is not None:
+ self.WriteComment(self.config['build'] + ' version: ' + \
+ self._GetChromiumVersionString())
+ self._AddGuiString(self.config['win_supported_os'],
+ self.messages['win_supported_all']['text'])
+ self._AddGuiString(self.config['win_supported_os_win7'],
+ self.messages['win_supported_win7']['text'])
+ categories = self.winconfig['mandatory_category_path'] + \
+ self.winconfig['recommended_category_path']
+ strings = self.winconfig['category_path_strings'].copy()
+ if 'adm_category_path_strings' in self.config:
+ strings.update(self.config['adm_category_path_strings'])
+ for category in categories:
+ if (category in strings):
+ # Replace {...} by localized messages.
+ string = re.sub(r"\{(\w+)\}", \
+ lambda m: self.messages[m.group(1)]['text'], \
+ strings[category])
+ self._AddGuiString(category, string)
+ # All the policies will be written into self.policies.
+ # The final template text will be assembled into self.lines by
+ # self.EndTemplate().
+
+ def EndTemplate(self):
+ # Copy policies into self.lines.
+ policy_class = self.GetClass().upper()
+ for class_name in ['MACHINE', 'USER']:
+ if policy_class != 'BOTH' and policy_class != class_name:
+ continue
+ self.lines.AddLine('CLASS ' + class_name, 1)
+ self.lines.AddLines(
+ self._CreateTemplate(self.winconfig['mandatory_category_path'],
+ self.winconfig['reg_mandatory_key_name'],
+ self.policies))
+ self.lines.AddLines(
+ self._CreateTemplate(self.winconfig['recommended_category_path'],
+ self.winconfig['reg_recommended_key_name'],
+ self.recommended_policies))
+ self.lines.AddLine('', -1)
+ # Copy user strings into self.lines.
+ self.lines.AddLine('[Strings]')
+ self.lines.AddLines(self.strings)
+
+ def Init(self):
+ # String buffer for building the whole ADM file.
+ self.lines = IndentedStringBuilder()
+ # String buffer for building the strings section of the ADM file.
+ self.strings = IndentedStringBuilder()
+ # Map of strings seen, to avoid duplicates.
+ self.strings_seen = {}
+ # String buffer for building the policies of the ADM file.
+ self.policies = IndentedStringBuilder()
+ # String buffer for building the recommended policies of the ADM file.
+ self.recommended_policies = IndentedStringBuilder()
+ # Shortcut to platform-specific ADMX/ADM specific configuration.
+ assert len(self.platforms) == 2
+ self.winconfig = self.config['win_config'][self.platforms[0]]
+
+ def GetTemplateText(self):
+ return self.lines.ToString()
+
+ def GetClass(self):
+ return 'Both'
diff --git a/chromium/components/policy/tools/template_writers/writers/adm_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/adm_writer_unittest.py
new file mode 100755
index 00000000000..9d2b86d9e3a
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/adm_writer_unittest.py
@@ -0,0 +1,1470 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+'''Unit tests for writers.adm_writer'''
+
+import os
+import sys
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+import unittest
+
+from writers import writer_unittest_common
+
+MESSAGES = '''
+ {
+ 'win_supported_all': {
+ 'text': 'Microsoft Windows 7 or later', 'desc': 'blah'
+ },
+ 'win_supported_win7': {
+ 'text': 'Microsoft Windows 7', 'desc': 'blah'
+ },
+
+ 'doc_recommended': {
+ 'text': 'Recommended', 'desc': 'bleh'
+ },
+ 'doc_reference_link': {
+ 'text': 'Reference: $6', 'desc': 'bleh'
+ },
+ 'deprecated_policy_group_caption': {
+ 'text': 'Deprecated policies', 'desc': 'bleh'
+ },
+ 'deprecated_policy_group_desc': {
+ 'desc': 'bleh',
+ 'text': 'These policies are included here to make them easy to remove.'
+ },
+ 'deprecated_policy_desc': {
+ 'desc': 'bleh',
+ 'text': 'This policy is deprecated. blah blah blah'
+ },
+ 'removed_policy_group_caption': {
+ 'text': 'Removed policies', 'desc': 'bleh'
+ },
+ 'removed_policy_group_desc': {
+ 'desc': 'bleh',
+ 'text': 'These policies are included here to make them easy to remove.'
+ },
+ 'removed_policy_desc': {
+ 'desc': 'bleh',
+ 'text': 'This policy is removed. blah blah blah'
+ },
+ }'''
+
+
+class AdmWriterUnittest(writer_unittest_common.WriterUnittestCommon):
+ '''Unit tests for AdmWriter.'''
+
+ def ConstructOutput(self, classes, body, strings):
+ result = []
+ for clazz in classes:
+ result.append('CLASS ' + clazz)
+ result.append(body)
+ result.append(strings)
+ return ''.join(result)
+
+ def CompareOutputs(self, output, expected_output):
+ '''Compares the output of the adm_writer with its expected output.
+
+ Args:
+ output: The output of the adm writer.
+ expected_output: The expected output.
+
+ Raises:
+ AssertionError: if the two strings are not equivalent.
+ '''
+ self.assertEquals(output.strip(),
+ expected_output.strip().replace('\n', '\r\n'))
+
+ def testEmpty(self):
+ # Test PListWriter in case of empty polices.
+ policy_json = '''
+ {
+ 'policy_definitions': [],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ }, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"''')
+ self.CompareOutputs(output, expected_output)
+
+ def testVersionAnnotation(self):
+ # Test PListWriter in case of empty polices.
+ policy_json = '''
+ {
+ 'policy_definitions': [],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'version': '39.0.0.0'
+ }, 'adm')
+ expected_output = '; chromium version: 39.0.0.0\n' + \
+ self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"''')
+ self.CompareOutputs(output, expected_output)
+
+ def testMainPolicy(self):
+ # Tests a policy group with a single policy of type 'main'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'MainPolicy',
+ 'type': 'main',
+ 'supported_on': ['chrome.win:8-'],
+ 'features': { 'can_be_recommended': True },
+ 'caption': 'Caption of main.',
+ 'desc': 'Description of main.',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!Google:Cat_Google
+ CATEGORY !!googlechrome
+ KEYNAME "Software\\Policies\\Google\\Chrome"
+
+ POLICY !!MainPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!MainPolicy_Explain
+ VALUENAME "MainPolicy"
+ VALUEON NUMERIC 1
+ VALUEOFF NUMERIC 0
+ END POLICY
+
+ END CATEGORY
+ END CATEGORY
+
+ CATEGORY !!Google:Cat_Google
+ CATEGORY !!googlechrome_recommended
+ KEYNAME "Software\\Policies\\Google\\Chrome\\Recommended"
+
+ POLICY !!MainPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!MainPolicy_Explain
+ VALUENAME "MainPolicy"
+ VALUEON NUMERIC 1
+ VALUEOFF NUMERIC 0
+ END POLICY
+
+ END CATEGORY
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+Google:Cat_Google="Google"
+googlechrome="Google Chrome"
+googlechrome_recommended="Google Chrome - Recommended"
+MainPolicy_Policy="Caption of main."
+MainPolicy_Explain="Description of main.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=MainPolicy"''')
+ self.CompareOutputs(output, expected_output)
+
+ def testMainPolicyRecommendedOnly(self):
+ # Tests a policy group with a single policy of type 'main'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'MainPolicy',
+ 'type': 'main',
+ 'supported_on': ['chrome.win:8-'],
+ 'features': {
+ 'can_be_recommended': True,
+ 'can_be_mandatory': False
+ },
+ 'caption': 'Caption of main.',
+ 'desc': 'Description of main.',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!Google:Cat_Google
+ CATEGORY !!googlechrome
+ KEYNAME "Software\\Policies\\Google\\Chrome"
+
+ END CATEGORY
+ END CATEGORY
+
+ CATEGORY !!Google:Cat_Google
+ CATEGORY !!googlechrome_recommended
+ KEYNAME "Software\\Policies\\Google\\Chrome\\Recommended"
+
+ POLICY !!MainPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!MainPolicy_Explain
+ VALUENAME "MainPolicy"
+ VALUEON NUMERIC 1
+ VALUEOFF NUMERIC 0
+ END POLICY
+
+ END CATEGORY
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+Google:Cat_Google="Google"
+googlechrome="Google Chrome"
+googlechrome_recommended="Google Chrome - Recommended"
+MainPolicy_Policy="Caption of main."
+MainPolicy_Explain="Description of main.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=MainPolicy"''')
+ self.CompareOutputs(output, expected_output)
+
+ def testStringPolicy(self):
+ # Tests a policy group with a single policy of type 'string'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'StringPolicy',
+ 'type': 'string',
+ 'supported_on': ['chrome.win:8-'],
+ 'features': { 'can_be_recommended': True },
+ 'desc': """Description of group.
+With a newline.""",
+ 'caption': 'Caption of policy.',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ POLICY !!StringPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!StringPolicy_Explain
+
+ PART !!StringPolicy_Part EDITTEXT
+ VALUENAME "StringPolicy"
+ MAXLEN 1000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ POLICY !!StringPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!StringPolicy_Explain
+
+ PART !!StringPolicy_Part EDITTEXT
+ VALUENAME "StringPolicy"
+ MAXLEN 1000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+StringPolicy_Policy="Caption of policy."
+StringPolicy_Explain="Description of group.\\nWith a newline.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=StringPolicy"
+StringPolicy_Part="Caption of policy."
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testIntPolicy(self):
+ # Tests a policy group with a single policy of type 'int'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'IntPolicy',
+ 'type': 'int',
+ 'caption': 'Caption of policy.',
+ 'features': { 'can_be_recommended': True },
+ 'desc': 'Description of policy.',
+ 'supported_on': ['chrome.win:8-']
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ POLICY !!IntPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!IntPolicy_Explain
+
+ PART !!IntPolicy_Part NUMERIC
+ VALUENAME "IntPolicy"
+ MIN 0 MAX 2000000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ POLICY !!IntPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!IntPolicy_Explain
+
+ PART !!IntPolicy_Part NUMERIC
+ VALUENAME "IntPolicy"
+ MIN 0 MAX 2000000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+IntPolicy_Policy="Caption of policy."
+IntPolicy_Explain="Description of policy.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=IntPolicy"
+IntPolicy_Part="Caption of policy."
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testIntPolicyWithWin7(self):
+ # Tests a policy group with a single policy of type 'int' that is supported
+ # on Windows 7 only.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'IntPolicy',
+ 'type': 'int',
+ 'caption': 'Caption of policy.',
+ 'features': { 'can_be_recommended': True },
+ 'desc': 'Description of policy.',
+ 'supported_on': ['chrome.win7:8-'],
+ },
+ ],
+ 'placeholders': [],
+ 'policy_atomic_group_definitions': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ POLICY !!IntPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7_ONLY
+ #endif
+ EXPLAIN !!IntPolicy_Explain
+
+ PART !!IntPolicy_Part NUMERIC
+ VALUENAME "IntPolicy"
+ MIN 0 MAX 2000000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ POLICY !!IntPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7_ONLY
+ #endif
+ EXPLAIN !!IntPolicy_Explain
+
+ PART !!IntPolicy_Part NUMERIC
+ VALUENAME "IntPolicy"
+ MIN 0 MAX 2000000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+IntPolicy_Policy="Caption of policy."
+IntPolicy_Explain="Description of policy.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=IntPolicy"
+IntPolicy_Part="Caption of policy."
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testIntPolicyWithRange(self):
+ # Tests a policy group with a single policy of type 'int' with a min and
+ # max value.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'IntPolicy',
+ 'type': 'int',
+ 'schema': { 'type': 'integer', 'minimum': 5, 'maximum': 10 },
+ 'caption': 'Caption of policy.',
+ 'features': { 'can_be_recommended': True },
+ 'desc': 'Description of policy.',
+ 'supported_on': ['chrome.win:8-']
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ POLICY !!IntPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!IntPolicy_Explain
+
+ PART !!IntPolicy_Part NUMERIC
+ VALUENAME "IntPolicy"
+ MIN 5 MAX 10
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ POLICY !!IntPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!IntPolicy_Explain
+
+ PART !!IntPolicy_Part NUMERIC
+ VALUENAME "IntPolicy"
+ MIN 5 MAX 10
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+IntPolicy_Policy="Caption of policy."
+IntPolicy_Explain="Description of policy.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=IntPolicy"
+IntPolicy_Part="Caption of policy."
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testIntEnumPolicy(self):
+ # Tests a policy group with a single policy of type 'int-enum'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'EnumPolicy',
+ 'type': 'int-enum',
+ 'items': [
+ {
+ 'name': 'ProxyServerDisabled',
+ 'value': 0,
+ 'caption': 'Option1',
+ },
+ {
+ 'name': 'ProxyServerAutoDetect',
+ 'value': 1,
+ 'caption': 'Option2',
+ },
+ ],
+ 'desc': 'Description of policy.',
+ 'caption': 'Caption of policy.',
+ 'supported_on': ['chrome.win:8-'],
+ 'features': { 'can_be_recommended': True },
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!Google:Cat_Google
+ CATEGORY !!googlechrome
+ KEYNAME "Software\\Policies\\Google\\Chrome"
+
+ POLICY !!EnumPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!EnumPolicy_Explain
+
+ PART !!EnumPolicy_Part DROPDOWNLIST
+ VALUENAME "EnumPolicy"
+ ITEMLIST
+ NAME !!EnumPolicy_ProxyServerDisabled_DropDown VALUE NUMERIC 0
+ NAME !!EnumPolicy_ProxyServerAutoDetect_DropDown VALUE NUMERIC 1
+ END ITEMLIST
+ END PART
+ END POLICY
+
+ END CATEGORY
+ END CATEGORY
+
+ CATEGORY !!Google:Cat_Google
+ CATEGORY !!googlechrome_recommended
+ KEYNAME "Software\\Policies\\Google\\Chrome\\Recommended"
+
+ POLICY !!EnumPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!EnumPolicy_Explain
+
+ PART !!EnumPolicy_Part DROPDOWNLIST
+ VALUENAME "EnumPolicy"
+ ITEMLIST
+ NAME !!EnumPolicy_ProxyServerDisabled_DropDown VALUE NUMERIC 0
+ NAME !!EnumPolicy_ProxyServerAutoDetect_DropDown VALUE NUMERIC 1
+ END ITEMLIST
+ END PART
+ END POLICY
+
+ END CATEGORY
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+Google:Cat_Google="Google"
+googlechrome="Google Chrome"
+googlechrome_recommended="Google Chrome - Recommended"
+EnumPolicy_Policy="Caption of policy."
+EnumPolicy_Explain="Description of policy.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=EnumPolicy"
+EnumPolicy_Part="Caption of policy."
+EnumPolicy_ProxyServerDisabled_DropDown="Option1"
+EnumPolicy_ProxyServerAutoDetect_DropDown="Option2"
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testStringEnumPolicy(self):
+ # Tests a policy group with a single policy of type 'int-enum'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'EnumPolicy',
+ 'type': 'string-enum',
+ 'caption': 'Caption of policy.',
+ 'desc': 'Description of policy.',
+ 'items': [
+ {'name': 'ProxyServerDisabled', 'value': 'one',
+ 'caption': 'Option1'},
+ {'name': 'ProxyServerAutoDetect', 'value': 'two',
+ 'caption': 'Option2'},
+ ],
+ 'supported_on': ['chrome.win:8-'],
+ 'features': { 'can_be_recommended': True },
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!Google:Cat_Google
+ CATEGORY !!googlechrome
+ KEYNAME "Software\\Policies\\Google\\Chrome"
+
+ POLICY !!EnumPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!EnumPolicy_Explain
+
+ PART !!EnumPolicy_Part DROPDOWNLIST
+ VALUENAME "EnumPolicy"
+ ITEMLIST
+ NAME !!EnumPolicy_ProxyServerDisabled_DropDown VALUE "one"
+ NAME !!EnumPolicy_ProxyServerAutoDetect_DropDown VALUE "two"
+ END ITEMLIST
+ END PART
+ END POLICY
+
+ END CATEGORY
+ END CATEGORY
+
+ CATEGORY !!Google:Cat_Google
+ CATEGORY !!googlechrome_recommended
+ KEYNAME "Software\\Policies\\Google\\Chrome\\Recommended"
+
+ POLICY !!EnumPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!EnumPolicy_Explain
+
+ PART !!EnumPolicy_Part DROPDOWNLIST
+ VALUENAME "EnumPolicy"
+ ITEMLIST
+ NAME !!EnumPolicy_ProxyServerDisabled_DropDown VALUE "one"
+ NAME !!EnumPolicy_ProxyServerAutoDetect_DropDown VALUE "two"
+ END ITEMLIST
+ END PART
+ END POLICY
+
+ END CATEGORY
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+Google:Cat_Google="Google"
+googlechrome="Google Chrome"
+googlechrome_recommended="Google Chrome - Recommended"
+EnumPolicy_Policy="Caption of policy."
+EnumPolicy_Explain="Description of policy.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=EnumPolicy"
+EnumPolicy_Part="Caption of policy."
+EnumPolicy_ProxyServerDisabled_DropDown="Option1"
+EnumPolicy_ProxyServerAutoDetect_DropDown="Option2"
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testListPolicy(self):
+ # Tests a policy group with a single policy of type 'list'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'ListPolicy',
+ 'type': 'list',
+ 'supported_on': ['chrome.win:8-'],
+ 'features': { 'can_be_recommended': True },
+ 'desc': """Description of list policy.
+With a newline.""",
+ 'caption': 'Caption of list policy.',
+ 'label': 'Label of list policy.'
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s,
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ POLICY !!ListPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!ListPolicy_Explain
+
+ PART !!ListPolicy_Part LISTBOX
+ KEYNAME "Software\\Policies\\Chromium\\ListPolicy"
+ VALUEPREFIX ""
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ POLICY !!ListPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!ListPolicy_Explain
+
+ PART !!ListPolicy_Part LISTBOX
+ KEYNAME "Software\\Policies\\Chromium\\Recommended\\ListPolicy"
+ VALUEPREFIX ""
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+ListPolicy_Policy="Caption of list policy."
+ListPolicy_Explain="Description of list policy.\\nWith a newline.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=ListPolicy"
+ListPolicy_Part="Label of list policy."
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testStringEnumListPolicy(self):
+ # Tests a policy group with a single policy of type 'string-enum-list'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'ListPolicy',
+ 'type': 'string-enum-list',
+ 'supported_on': ['chrome.win:8-'],
+ 'features': { 'can_be_recommended': True },
+ 'desc': """Description of list policy.
+With a newline.""",
+ 'items': [
+ {'name': 'ProxyServerDisabled', 'value': 'one',
+ 'caption': 'Option1'},
+ {'name': 'ProxyServerAutoDetect', 'value': 'two',
+ 'caption': 'Option2'},
+ ],
+ 'caption': 'Caption of list policy.',
+ 'label': 'Label of list policy.'
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ POLICY !!ListPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!ListPolicy_Explain
+
+ PART !!ListPolicy_Part LISTBOX
+ KEYNAME "Software\\Policies\\Chromium\\ListPolicy"
+ VALUEPREFIX ""
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ POLICY !!ListPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!ListPolicy_Explain
+
+ PART !!ListPolicy_Part LISTBOX
+ KEYNAME "Software\\Policies\\Chromium\\Recommended\\ListPolicy"
+ VALUEPREFIX ""
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+ListPolicy_Policy="Caption of list policy."
+ListPolicy_Explain="Description of list policy.\\nWith a newline.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=ListPolicy"
+ListPolicy_Part="Label of list policy."
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testDictionaryPolicy(self):
+ # Tests a policy group with a single policy of type 'dict'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'DictionaryPolicy',
+ 'type': 'dict',
+ 'supported_on': ['chrome.win:8-'],
+ 'features': { 'can_be_recommended': True },
+ 'desc': 'Description of group.',
+ 'caption': 'Caption of policy.',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ POLICY !!DictionaryPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!DictionaryPolicy_Explain
+
+ PART !!DictionaryPolicy_Part EDITTEXT
+ VALUENAME "DictionaryPolicy"
+ MAXLEN 1000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ POLICY !!DictionaryPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!DictionaryPolicy_Explain
+
+ PART !!DictionaryPolicy_Part EDITTEXT
+ VALUENAME "DictionaryPolicy"
+ MAXLEN 1000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+DictionaryPolicy_Policy="Caption of policy."
+DictionaryPolicy_Explain="Description of group.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=DictionaryPolicy"
+DictionaryPolicy_Part="Caption of policy."
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testExternalPolicy(self):
+ # Tests a policy group with a single policy of type 'external'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'ExternalPolicy',
+ 'type': 'external',
+ 'supported_on': ['chrome.win:8-'],
+ 'features': { 'can_be_recommended': True },
+ 'desc': 'Description of group.',
+ 'caption': 'Caption of policy.',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ POLICY !!ExternalPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!ExternalPolicy_Explain
+
+ PART !!ExternalPolicy_Part EDITTEXT
+ VALUENAME "ExternalPolicy"
+ MAXLEN 1000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ POLICY !!ExternalPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!ExternalPolicy_Explain
+
+ PART !!ExternalPolicy_Part EDITTEXT
+ VALUENAME "ExternalPolicy"
+ MAXLEN 1000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+ExternalPolicy_Policy="Caption of policy."
+ExternalPolicy_Explain="Description of group.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=ExternalPolicy"
+ExternalPolicy_Part="Caption of policy."
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testNonSupportedPolicy(self):
+ # Tests a policy that is not supported on Windows, so it shouldn't
+ # be included in the ADM file.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'NonWinGroup',
+ 'type': 'group',
+ 'policies': ['NonWinPolicy'],
+ 'caption': 'Group caption.',
+ 'desc': 'Group description.',
+ },
+ {
+ 'name': 'NonWinPolicy',
+ 'type': 'list',
+ 'supported_on': ['chrome.linux:8-', 'chrome.mac:8-'],
+ 'caption': 'Caption of list policy.',
+ 'desc': 'Desc of list policy.',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testNonRecommendedPolicy(self):
+ # Tests a policy that is not recommended, so it should be included.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'MainPolicy',
+ 'type': 'main',
+ 'supported_on': ['chrome.win:8-'],
+ 'caption': 'Caption of main.',
+ 'desc': 'Description of main.',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!Google:Cat_Google
+ CATEGORY !!googlechrome
+ KEYNAME "Software\\Policies\\Google\\Chrome"
+
+ POLICY !!MainPolicy_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!MainPolicy_Explain
+ VALUENAME "MainPolicy"
+ VALUEON NUMERIC 1
+ VALUEOFF NUMERIC 0
+ END POLICY
+
+ END CATEGORY
+ END CATEGORY
+
+ CATEGORY !!Google:Cat_Google
+ CATEGORY !!googlechrome_recommended
+ KEYNAME "Software\\Policies\\Google\\Chrome\\Recommended"
+
+ END CATEGORY
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+Google:Cat_Google="Google"
+googlechrome="Google Chrome"
+googlechrome_recommended="Google Chrome - Recommended"
+MainPolicy_Policy="Caption of main."
+MainPolicy_Explain="Description of main.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=MainPolicy"''')
+ self.CompareOutputs(output, expected_output)
+
+ def testPolicyGroup(self):
+ # Tests a policy group that has more than one policies.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'Group1',
+ 'type': 'group',
+ 'desc': 'Description of group.',
+ 'caption': 'Caption of group.',
+ 'policies': ['Policy1', 'Policy2'],
+ },
+ {
+ 'name': 'Policy1',
+ 'type': 'list',
+ 'supported_on': ['chrome.win:8-'],
+ 'features': { 'can_be_recommended': True },
+ 'caption': 'Caption of policy1.',
+ 'desc': """Description of policy1.
+With a newline."""
+ },
+ {
+ 'name': 'Policy2',
+ 'type': 'string',
+ 'supported_on': ['chrome.win:8-'],
+ 'caption': 'Caption of policy2.',
+ 'desc': """Description of policy2.
+With a newline."""
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ CATEGORY !!Group1_Category
+ POLICY !!Policy1_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!Policy1_Explain
+
+ PART !!Policy1_Part LISTBOX
+ KEYNAME "Software\\Policies\\Chromium\\Policy1"
+ VALUEPREFIX ""
+ END PART
+ END POLICY
+
+ POLICY !!Policy2_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!Policy2_Explain
+
+ PART !!Policy2_Part EDITTEXT
+ VALUENAME "Policy2"
+ MAXLEN 1000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ CATEGORY !!Group1_Category
+ POLICY !!Policy1_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!Policy1_Explain
+
+ PART !!Policy1_Part LISTBOX
+ KEYNAME "Software\\Policies\\Chromium\\Recommended\\Policy1"
+ VALUEPREFIX ""
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+Group1_Category="Caption of group."
+Policy1_Policy="Caption of policy1."
+Policy1_Explain="Description of policy1.\\nWith a newline.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=Policy1"
+Policy1_Part="Caption of policy1."
+Policy2_Policy="Caption of policy2."
+Policy2_Explain="Description of policy2.\\nWith a newline.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=Policy2"
+Policy2_Part="Caption of policy2."
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testDuplicatedStringEnumPolicy(self):
+ # Verifies that duplicated enum constants with different descriptions are
+ # allowed.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'EnumPolicy.A',
+ 'type': 'string-enum',
+ 'caption': 'Caption of policy A.',
+ 'desc': 'Description of policy A.',
+ 'items': [
+ {'name': 'tls1.2', 'value': 'tls1.2', 'caption': 'tls1.2' },
+ ],
+ 'supported_on': ['chrome.win:39-'],
+ },
+ {
+ 'name': 'EnumPolicy.B',
+ 'type': 'string-enum',
+ 'caption': 'Caption of policy B.',
+ 'desc': 'Description of policy B.',
+ 'items': [
+ {'name': 'tls1.2', 'value': 'tls1.2', 'caption': 'tls1.2' },
+ ],
+ 'supported_on': ['chrome.win:39-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!Google:Cat_Google
+ CATEGORY !!googlechrome
+ KEYNAME "Software\\Policies\\Google\\Chrome"
+
+ POLICY !!EnumPolicy_A_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!EnumPolicy_A_Explain
+
+ PART !!EnumPolicy_A_Part DROPDOWNLIST
+ VALUENAME "EnumPolicy.A"
+ ITEMLIST
+ NAME !!EnumPolicy_A_tls1_2_DropDown VALUE "tls1.2"
+ END ITEMLIST
+ END PART
+ END POLICY
+
+ POLICY !!EnumPolicy_B_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!EnumPolicy_B_Explain
+
+ PART !!EnumPolicy_B_Part DROPDOWNLIST
+ VALUENAME "EnumPolicy.B"
+ ITEMLIST
+ NAME !!EnumPolicy_B_tls1_2_DropDown VALUE "tls1.2"
+ END ITEMLIST
+ END PART
+ END POLICY
+
+ END CATEGORY
+ END CATEGORY
+
+ CATEGORY !!Google:Cat_Google
+ CATEGORY !!googlechrome_recommended
+ KEYNAME "Software\\Policies\\Google\\Chrome\\Recommended"
+
+ END CATEGORY
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+Google:Cat_Google="Google"
+googlechrome="Google Chrome"
+googlechrome_recommended="Google Chrome - Recommended"
+EnumPolicy_A_Policy="Caption of policy A."
+EnumPolicy_A_Explain="Description of policy A.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=EnumPolicy.A"
+EnumPolicy_A_Part="Caption of policy A."
+EnumPolicy_A_tls1_2_DropDown="tls1.2"
+EnumPolicy_B_Policy="Caption of policy B."
+EnumPolicy_B_Explain="Description of policy B.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=EnumPolicy.B"
+EnumPolicy_B_Part="Caption of policy B."
+EnumPolicy_B_tls1_2_DropDown="tls1.2"
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testDeprecatedPolicy(self):
+ # Tests that a deprecated policy gets placed in the special
+ # 'DeprecatedPolicies' group.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'Policy1',
+ 'type': 'string',
+ 'deprecated': True,
+ 'features': { 'can_be_recommended': True },
+ 'supported_on': ['chrome.win:8-'],
+ 'caption': 'Caption of policy1.',
+ 'desc': """Description of policy1."""
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ CATEGORY !!DeprecatedPolicies_Category
+ POLICY !!Policy1_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!Policy1_Explain
+
+ PART !!Policy1_Part EDITTEXT
+ VALUENAME "Policy1"
+ MAXLEN 1000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ CATEGORY !!DeprecatedPolicies_Category
+ POLICY !!Policy1_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!Policy1_Explain
+
+ PART !!Policy1_Part EDITTEXT
+ VALUENAME "Policy1"
+ MAXLEN 1000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+DeprecatedPolicies_Category="Deprecated policies"
+Policy1_Policy="Caption of policy1."
+Policy1_Explain="This policy is deprecated. blah blah blah\\n\\n"
+Policy1_Part="Caption of policy1."
+''')
+ self.CompareOutputs(output, expected_output)
+
+ def testRemovedPolicy(self):
+ # Tests that a deprecated policy gets placed in the special
+ # 'RemovedPolicies' group.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'Policy1',
+ 'type': 'string',
+ 'deprecated': True,
+ 'features': { 'can_be_recommended': True },
+ 'supported_on': ['chrome.win:40-83'],
+ 'caption': 'Caption of policy1.',
+ 'desc': """Description of policy1."""
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': %s
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1',
+ 'major_version': 84}, 'adm')
+ expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+ CATEGORY !!chromium
+ KEYNAME "Software\\Policies\\Chromium"
+
+ CATEGORY !!RemovedPolicies_Category
+ POLICY !!Policy1_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!Policy1_Explain
+
+ PART !!Policy1_Part EDITTEXT
+ VALUENAME "Policy1"
+ MAXLEN 1000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ END CATEGORY
+
+ CATEGORY !!chromium_recommended
+ KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+ CATEGORY !!RemovedPolicies_Category
+ POLICY !!Policy1_Policy
+ #if version >= 4
+ SUPPORTED !!SUPPORTED_WIN7
+ #endif
+ EXPLAIN !!Policy1_Explain
+
+ PART !!Policy1_Part EDITTEXT
+ VALUENAME "Policy1"
+ MAXLEN 1000000
+ END PART
+ END POLICY
+
+ END CATEGORY
+
+ END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+RemovedPolicies_Category="Removed policies"
+Policy1_Policy="Caption of policy1."
+Policy1_Explain="This policy is removed. blah blah blah\\n\\n"
+Policy1_Part="Caption of policy1."
+''')
+ self.CompareOutputs(output, expected_output)
+
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/adml_writer.py b/chromium/components/policy/tools/template_writers/writers/adml_writer.py
new file mode 100755
index 00000000000..6f7233a0366
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/adml_writer.py
@@ -0,0 +1,266 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+
+from xml.dom import minidom
+from writers import gpo_editor_writer, xml_formatted_writer
+from writers.admx_writer import AdmxElementType
+import json
+import re
+
+
+def GetWriter(config):
+ '''Factory method for instanciating the ADMLWriter. Every Writer needs a
+ GetWriter method because the TemplateFormatter uses this method to
+ instantiate a Writer.
+ '''
+ return ADMLWriter(['win', 'win7'], config)
+
+
+class ADMLWriter(xml_formatted_writer.XMLFormattedWriter,
+ gpo_editor_writer.GpoEditorWriter):
+ ''' Class for generating an ADML policy template. It is used by the
+ PolicyTemplateGenerator to write the ADML file.
+ '''
+
+ # DOM root node of the generated ADML document.
+ _doc = None
+
+ # The string-table contains all ADML "string" elements.
+ _string_table_elem = None
+
+ # The presentation-table is the container for presentation elements, that
+ # describe the presentation of Policy-Groups and Policies.
+ _presentation_table_elem = None
+
+ def _AddString(self, id, text):
+ ''' Adds an ADML "string" element to _string_table_elem. The following
+ ADML snippet contains an example:
+
+ <string id="$(id)">$(text)</string>
+
+ Args:
+ id: ID of the newly created "string" element.
+ text: Value of the newly created "string" element.
+ '''
+ id = id.replace('.', '_')
+ if id in self.strings_seen:
+ assert text == self.strings_seen[id]
+ else:
+ self.strings_seen[id] = text
+ string_elem = self.AddElement(self._string_table_elem, 'string',
+ {'id': id})
+ string_elem.appendChild(self._doc.createTextNode(text))
+
+ def _GetAdmxElementType(self, policy):
+ '''Returns the ADMX element type for a particular Policy.'''
+ return AdmxElementType.GetType(policy, allow_multi_strings=False)
+
+ def WritePolicy(self, policy):
+ '''Generates the ADML elements for a Policy.
+ <stringTable>
+ ...
+ <string id="$(policy_group_name)">$(caption)</string>
+ <string id="$(policy_group_name)_Explain">$(description)</string>
+ </stringTable>
+
+ <presentationTables>
+ ...
+ <presentation id=$(policy_group_name)/>
+ </presentationTables>
+
+ Args:
+ policy: The Policy to generate ADML elements for.
+ '''
+ policy_name = policy['name']
+ policy_caption = policy.get('caption', policy_name)
+ policy_label = policy.get('label', policy_name)
+
+ policy_desc = policy.get('desc')
+ example_value_text = self._GetExampleValueText(policy)
+
+ if policy_desc is not None and self.HasExpandedPolicyDescription(policy):
+ policy_desc += '\n' + self.GetExpandedPolicyDescription(policy) + '\n'
+
+ if (policy_desc is not None and example_value_text is not None and
+ not self._IsRemovedPolicy(policy)):
+ policy_explain = policy_desc + '\n\n' + example_value_text
+ elif policy_desc is not None:
+ policy_explain = policy_desc
+ elif example_value_text is not None:
+ policy_explain = example_value_text
+ else:
+ # No explanation found at all.
+ policy_explain = policy_name
+
+ self._AddString(policy_name, policy_caption)
+ self._AddString(policy_name + '_Explain', policy_explain)
+ presentation_elem = self.AddElement(self._presentation_table_elem,
+ 'presentation', {'id': policy_name})
+
+ admx_element_type = self._GetAdmxElementType(policy)
+ if admx_element_type == AdmxElementType.MAIN:
+ pass
+ elif admx_element_type == AdmxElementType.STRING:
+ textbox_elem = self.AddElement(presentation_elem, 'textBox',
+ {'refId': policy_name})
+ label_elem = self.AddElement(textbox_elem, 'label')
+ label_elem.appendChild(self._doc.createTextNode(policy_label))
+ elif admx_element_type == AdmxElementType.MULTI_STRING:
+ # We currently also show a single-line textbox - see http://crbug/829328
+ textbox_elem = self.AddElement(presentation_elem, 'textBox',
+ {'refId': policy_name + '_Legacy'})
+ label_elem = self.AddElement(textbox_elem, 'label')
+ legacy_label = self._GetLegacySingleLineLabel(policy_label)
+ self._AddString(policy_name + '_Legacy', legacy_label)
+ label_elem.appendChild(self._doc.createTextNode(legacy_label))
+ # New multi-line textbox, easier to use than old single-line textbox:
+ multitextbox_elem = self.AddElement(presentation_elem, 'multiTextBox', {
+ 'refId': policy_name,
+ 'defaultHeight': '8'
+ })
+ multitextbox_elem.appendChild(self._doc.createTextNode(policy_label))
+ elif admx_element_type == AdmxElementType.INT:
+ textbox_elem = self.AddElement(presentation_elem, 'decimalTextBox',
+ {'refId': policy_name})
+ textbox_elem.appendChild(self._doc.createTextNode(policy_label + ':'))
+ elif admx_element_type == AdmxElementType.ENUM:
+ for item in policy['items']:
+ self._AddString(policy_name + "_" + item['name'], item['caption'])
+ dropdownlist_elem = self.AddElement(presentation_elem, 'dropdownList',
+ {'refId': policy_name})
+ dropdownlist_elem.appendChild(self._doc.createTextNode(policy_label))
+ elif admx_element_type == AdmxElementType.LIST:
+ self._AddString(policy_name + 'Desc', policy_caption)
+ listbox_elem = self.AddElement(presentation_elem, 'listBox',
+ {'refId': policy_name + 'Desc'})
+ listbox_elem.appendChild(self._doc.createTextNode(policy_label))
+ elif admx_element_type == AdmxElementType.GROUP:
+ pass
+ else:
+ raise Exception('Unknown element type %s.' % admx_element_type)
+
+ def BeginPolicyGroup(self, group):
+ '''Generates ADML elements for a Policy-Group. For each Policy-Group two
+ ADML "string" elements are added to the string-table. One contains the
+ caption of the Policy-Group and the other a description. A Policy-Group also
+ requires an ADML "presentation" element that must be added to the
+ presentation-table. The "presentation" element is the container for the
+ elements that define the visual presentation of the Policy-Goup's Policies.
+ The following ADML snippet shows an example:
+
+ Args:
+ group: The Policy-Group to generate ADML elements for.
+ '''
+ # Add ADML "string" elements to the string-table that are required by a
+ # Policy-Group.
+ self._AddString(group['name'] + '_group', group['caption'])
+
+ def _AddBaseStrings(self):
+ ''' Adds ADML "string" elements to the string-table that are referenced by
+ the ADMX file but not related to any specific Policy-Group or Policy.
+ '''
+ self._AddString(self.config['win_supported_os'],
+ self.messages['win_supported_all']['text'])
+ self._AddString(self.config['win_supported_os_win7'],
+ self.messages['win_supported_win7']['text'])
+ categories = self.winconfig['mandatory_category_path'] + \
+ self.winconfig['recommended_category_path']
+ strings = self.winconfig['category_path_strings']
+ for category in categories:
+ if (category in strings):
+ # Replace {...} by localized messages.
+ string = re.sub(r"\{(\w+)\}", \
+ lambda m: self.messages[m.group(1)]['text'], \
+ strings[category])
+ self._AddString(category, string)
+
+ def _GetExampleValueText(self, policy):
+ '''Generates a string that describes the example value, if needed.
+ Returns None if no string is needed. For instance, if the setting is a
+ boolean, the user can only select true or false, so example text is not
+ useful.'''
+ example_value = policy.get('example_value')
+ # If there is no example_value, we show nothing.
+ if not example_value:
+ return None
+
+ # Strings are simple - just return them as-is, on the same line.
+ if isinstance(example_value, str):
+ return self.GetLocalizedMessage('example_value') + ' ' + example_value
+
+ # Dicts are pretty simple - json.dumps them onto multiple lines.
+ if isinstance(example_value, dict):
+ value_as_text = json.dumps(example_value, indent=2)
+ return self.GetLocalizedMessage('example_value') + '\n\n' + value_as_text
+
+ # Lists are the more complicated - the example value we show the user
+ # depends on if they need to enter the list into a textbox (using JSON
+ # array syntax) or into a listbox (which doesn't need JSON array syntax,
+ # but does need exactly one entry per line).
+ if isinstance(example_value, list):
+ policy_type = policy.get('type')
+ if policy_type == 'dict':
+ # If the policy type is dict, that means they get to enter in the
+ # whole policy as JSON, including the JSON array square brackets:
+ value_as_text = json.dumps(example_value, indent=2)
+
+ elif policy_type is not None and 'list' in policy_type:
+ # But if the policy type is list, then they get to enter each item
+ # into a listbox, one item per line.
+ if isinstance(example_value[0], str):
+ # Items are strings. These don't need quotes when in a listbox.
+ value_as_text = '\n'.join([str(v) for v in example_value])
+ else:
+ # Items are dicts. We dump each item onto a single line, since the
+ # user has to enter one item per line into the listbox.
+ value_as_text = '\n'.join([json.dumps(v) for v in example_value])
+
+ else:
+ # Lists should be type 'dict', 'list', or something like '...enum-list'
+ raise Exception(
+ 'Unexpected policy type with list example value: %s' % policy_type)
+
+ return self.GetLocalizedMessage('example_value') + '\n\n' + value_as_text
+
+ # Other types - mostly booleans - we don't show example values.
+ return None
+
+ def _GetLegacySingleLineLabel(self, policy_label):
+ '''Generates a label for a legacy single-line textbox.'''
+ return (self.GetLocalizedMessage('legacy_single_line_label').replace(
+ '$6', policy_label))
+
+ def BeginTemplate(self):
+ dom_impl = minidom.getDOMImplementation('')
+ self._doc = dom_impl.createDocument(None, 'policyDefinitionResources', None)
+ if self._GetChromiumVersionString() is not None:
+ self.AddComment(self._doc.documentElement, self.config['build'] + \
+ ' version: ' + self._GetChromiumVersionString())
+ policy_definitions_resources_elem = self._doc.documentElement
+ policy_definitions_resources_elem.attributes['revision'] = '1.0'
+ policy_definitions_resources_elem.attributes['schemaVersion'] = '1.0'
+
+ self.AddElement(policy_definitions_resources_elem, 'displayName')
+ self.AddElement(policy_definitions_resources_elem, 'description')
+ resources_elem = self.AddElement(policy_definitions_resources_elem,
+ 'resources')
+ self._string_table_elem = self.AddElement(resources_elem, 'stringTable')
+ self._AddBaseStrings()
+ self._presentation_table_elem = self.AddElement(resources_elem,
+ 'presentationTable')
+
+ def Init(self):
+ # Map of all strings seen.
+ self.strings_seen = {}
+ # Shortcut to platform-specific ADMX/ADM specific configuration.
+ assert len(self.platforms) <= 2
+ self.winconfig = self.config['win_config'][self.platforms[0]]
+
+ def GetTemplateText(self):
+ # Using "toprettyxml()" confuses the Windows Group Policy Editor
+ # (gpedit.msc) because it interprets whitespace characters in text between
+ # the "string" tags. This prevents gpedit.msc from displaying the category
+ # names correctly.
+ return self._doc.toxml()
diff --git a/chromium/components/policy/tools/template_writers/writers/adml_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/adml_writer_unittest.py
new file mode 100755
index 00000000000..2f1afd14502
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/adml_writer_unittest.py
@@ -0,0 +1,521 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+"""Unittests for writers.adml_writer."""
+
+import os
+import sys
+import unittest
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+from writers import adml_writer
+from writers import xml_writer_base_unittest
+
+
+class AdmlWriterUnittest(xml_writer_base_unittest.XmlWriterBaseTest):
+
+ def setUp(self):
+ config = {
+ 'app_name': 'test',
+ 'build': 'test',
+ 'win_supported_os': 'SUPPORTED_TESTOS',
+ 'win_supported_os_win7': 'SUPPORTED_TESTOS_2',
+ 'win_config': {
+ 'win': {
+ 'mandatory_category_path': ['test_category'],
+ 'recommended_category_path': ['test_category_recommended'],
+ 'category_path_strings': {
+ 'test_category': 'TestCategory',
+ 'test_category_recommended': 'TestCategory - recommended',
+ },
+ },
+ 'chrome_os': {
+ 'mandatory_category_path': ['cros_test_category'],
+ 'recommended_category_path': ['cros_test_category_recommended'],
+ 'category_path_strings': {
+ 'cros_test_category':
+ 'CrOSTestCategory',
+ 'cros_test_category_recommended':
+ 'CrOSTestCategory - recommended',
+ },
+ },
+ },
+ }
+ self.writer = self._GetWriter(config)
+ self.writer.messages = {
+ 'win_supported_all': {
+ 'text': 'Supported on Test OS or higher',
+ 'desc': 'blah'
+ },
+ 'win_supported_win7': {
+ 'text': 'Supported on Test OS',
+ 'desc': 'blah'
+ },
+ 'doc_recommended': {
+ 'text': 'Recommended',
+ 'desc': 'bleh'
+ },
+ 'doc_example_value': {
+ 'text': 'Example value:',
+ 'desc': 'bluh'
+ },
+ 'doc_legacy_single_line_label': {
+ 'text': '$6 (deprecated)',
+ },
+ 'doc_schema_description_link': {
+ 'text': '''See $6'''
+ },
+ }
+ self.writer.Init()
+
+ def _GetWriter(self, config):
+ return adml_writer.GetWriter(config)
+
+ def GetCategory(self):
+ return "test_category"
+
+ def GetCategoryString(self):
+ return "TestCategory"
+
+ def _InitWriterForAddingPolicyGroups(self, writer):
+ '''Initialize the writer for adding policy groups. This method must be
+ called before the method "BeginPolicyGroup" can be called. It initializes
+ attributes of the writer.
+ '''
+ writer.BeginTemplate()
+
+ def _InitWriterForAddingPolicies(self, writer, policy):
+ '''Initialize the writer for adding policies. This method must be
+ called before the method "WritePolicy" can be called. It initializes
+ attributes of the writer.
+ '''
+ self._InitWriterForAddingPolicyGroups(writer)
+ policy_group = {
+ 'name': 'PolicyGroup',
+ 'caption': 'Test Caption',
+ 'desc': 'This is the test description of the test policy group.',
+ 'policies': policy,
+ }
+ writer.BeginPolicyGroup(policy_group)
+
+ string_elements = \
+ self.writer._string_table_elem.getElementsByTagName('string')
+ for elem in string_elements:
+ self.writer._string_table_elem.removeChild(elem)
+
+ def testEmpty(self):
+ self.writer.BeginTemplate()
+ self.writer.EndTemplate()
+ output = self.writer.GetTemplateText()
+ expected_output = (
+ '<?xml version="1.0" ?><policyDefinitionResources'
+ ' revision="1.0" schemaVersion="1.0"><displayName/><description/>'
+ '<resources><stringTable><string id="SUPPORTED_TESTOS">Supported on'
+ ' Test OS or higher</string>'
+ '<string id="SUPPORTED_TESTOS_2">Supported on Test OS</string>'
+ '<string id="' + self.GetCategory() + '">' + \
+ self.GetCategoryString() + '</string>'
+ '<string id="' + self.GetCategory() + '_recommended">' + \
+ self.GetCategoryString() + ' - recommended</string>'
+ '</stringTable><presentationTable/>'
+ '</resources></policyDefinitionResources>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testVersionAnnotation(self):
+ self.writer.config['version'] = '39.0.0.0'
+ self.writer.BeginTemplate()
+ self.writer.EndTemplate()
+ output = self.writer.GetTemplateText()
+ expected_output = (
+ '<?xml version="1.0" ?><policyDefinitionResources'
+ ' revision="1.0" schemaVersion="1.0"><!--test version: 39.0.0.0-->'
+ '<displayName/><description/><resources><stringTable>'
+ '<string id="SUPPORTED_TESTOS">Supported on'
+ ' Test OS or higher</string>'
+ '<string id="SUPPORTED_TESTOS_2">Supported on Test OS</string>'
+ '<string id="' + self.GetCategory() + '">' + \
+ self.GetCategoryString() + '</string>'
+ '<string id="' + self.GetCategory() + '_recommended">' + \
+ self.GetCategoryString() + ' - recommended</string>'
+ '</stringTable><presentationTable/>'
+ '</resources></policyDefinitionResources>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testPolicyGroup(self):
+ empty_policy_group = {
+ 'name':
+ 'PolicyGroup',
+ 'caption':
+ 'Test Group Caption',
+ 'desc':
+ 'This is the test description of the test policy group.',
+ 'policies': [
+ {
+ 'name': 'PolicyStub2',
+ 'type': 'main'
+ },
+ {
+ 'name': 'PolicyStub1',
+ 'type': 'main'
+ },
+ ],
+ }
+ self._InitWriterForAddingPolicyGroups(self.writer)
+ self.writer.BeginPolicyGroup(empty_policy_group)
+ self.writer.EndPolicyGroup()
+ # Assert generated string elements.
+ output = self.GetXMLOfChildren(self.writer._string_table_elem)
+ expected_output = (
+ '<string id="SUPPORTED_TESTOS">'
+ 'Supported on Test OS or higher</string>\n' + \
+ '<string id="SUPPORTED_TESTOS_2">Supported on Test OS</string>\n' + \
+ '<string id="' + self.GetCategory() + '">' + \
+ self.GetCategoryString() + '</string>\n'
+ '<string id="' + self.GetCategory() + '_recommended">' + \
+ self.GetCategoryString() + ' - recommended</string>\n'
+ '<string id="PolicyGroup_group">Test Group Caption</string>')
+ self.AssertXMLEquals(output, expected_output)
+ # Assert generated presentation elements.
+ output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+ expected_output = ''
+ self.AssertXMLEquals(output, expected_output)
+
+ def testMainPolicy(self):
+ main_policy = {
+ 'name': 'DummyMainPolicy',
+ 'type': 'main',
+ 'caption': 'Main policy caption',
+ 'desc': 'Main policy test description.'
+ }
+ self._InitWriterForAddingPolicies(self.writer, main_policy)
+ self.writer.WritePolicy(main_policy)
+ # Assert generated string elements.
+ output = self.GetXMLOfChildren(self.writer._string_table_elem)
+ expected_output = (
+ '<string id="DummyMainPolicy">Main policy caption</string>\n'
+ '<string id="DummyMainPolicy_Explain">'
+ 'Main policy test description.</string>')
+ self.AssertXMLEquals(output, expected_output)
+ # Assert generated presentation elements.
+ output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+ expected_output = '<presentation id="DummyMainPolicy"/>'
+ self.AssertXMLEquals(output, expected_output)
+
+ def testStringPolicy(self):
+ string_policy = {
+ 'name': 'StringPolicyStub',
+ 'type': 'string',
+ 'caption': 'String policy caption',
+ 'label': 'String policy label',
+ 'desc': 'This is a test description.',
+ 'supported_on': [{'platform': 'win'}, {'platform': 'chrome_os'}],
+ 'example_value': '01:23:45:67:89:ab',
+ }
+ self._InitWriterForAddingPolicies(self.writer, string_policy)
+ self.writer.WritePolicy(string_policy)
+ # Assert generated string elements.
+ output = self.GetXMLOfChildren(self.writer._string_table_elem)
+ expected_output = (
+ '<string id="StringPolicyStub">String policy caption</string>\n'
+ '<string id="StringPolicyStub_Explain">'
+ 'This is a test description.\n\n'
+ 'Example value: 01:23:45:67:89:ab</string>')
+ self.AssertXMLEquals(output, expected_output)
+ # Assert generated presentation elements.
+ output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+ expected_output = ('<presentation id="StringPolicyStub">\n'
+ ' <textBox refId="StringPolicyStub">\n'
+ ' <label>String policy label</label>\n'
+ ' </textBox>\n'
+ '</presentation>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testIntPolicy(self):
+ int_policy = {
+ 'name': 'IntPolicyStub',
+ 'type': 'int',
+ 'caption': 'Int policy caption',
+ 'label': 'Int policy label',
+ 'desc': 'This is a test description.',
+ }
+ self._InitWriterForAddingPolicies(self.writer, int_policy)
+ self.writer.WritePolicy(int_policy)
+ # Assert generated string elements.
+ output = self.GetXMLOfChildren(self.writer._string_table_elem)
+ expected_output = (
+ '<string id="IntPolicyStub">Int policy caption</string>\n'
+ '<string id="IntPolicyStub_Explain">'
+ 'This is a test description.</string>')
+ self.AssertXMLEquals(output, expected_output)
+ # Assert generated presentation elements.
+ output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+ expected_output = ('<presentation id="IntPolicyStub">\n'
+ ' <decimalTextBox refId="IntPolicyStub">'
+ 'Int policy label:</decimalTextBox>\n'
+ '</presentation>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testIntEnumPolicy(self):
+ enum_policy = {
+ 'name':
+ 'EnumPolicyStub',
+ 'type':
+ 'int-enum',
+ 'caption':
+ 'Enum policy caption',
+ 'label':
+ 'Enum policy label',
+ 'desc':
+ 'This is a test description.',
+ 'items': [
+ {
+ 'name': 'item 1',
+ 'value': 1,
+ 'caption': 'Caption Item 1',
+ },
+ {
+ 'name': 'item 2',
+ 'value': 2,
+ 'caption': 'Caption Item 2',
+ },
+ ],
+ }
+ self._InitWriterForAddingPolicies(self.writer, enum_policy)
+ self.writer.WritePolicy(enum_policy)
+ # Assert generated string elements.
+ output = self.GetXMLOfChildren(self.writer._string_table_elem)
+ expected_output = (
+ '<string id="EnumPolicyStub">Enum policy caption</string>\n'
+ '<string id="EnumPolicyStub_Explain">'
+ 'This is a test description.</string>\n'
+ '<string id="EnumPolicyStub_item 1">Caption Item 1</string>\n'
+ '<string id="EnumPolicyStub_item 2">Caption Item 2</string>')
+ self.AssertXMLEquals(output, expected_output)
+ # Assert generated presentation elements.
+ output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+ expected_output = ('<presentation id="EnumPolicyStub">\n'
+ ' <dropdownList refId="EnumPolicyStub">'
+ 'Enum policy label</dropdownList>\n'
+ '</presentation>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testStringEnumPolicy(self):
+ enum_policy = {
+ 'name':
+ 'EnumPolicyStub',
+ 'type':
+ 'string-enum',
+ 'caption':
+ 'Enum policy caption',
+ 'label':
+ 'Enum policy label',
+ 'desc':
+ 'This is a test description.',
+ 'items': [
+ {
+ 'name': 'item 1',
+ 'value': 'value 1',
+ 'caption': 'Caption Item 1',
+ },
+ {
+ 'name': 'item 2',
+ 'value': 'value 2',
+ 'caption': 'Caption Item 2',
+ },
+ ],
+ }
+ self._InitWriterForAddingPolicies(self.writer, enum_policy)
+ self.writer.WritePolicy(enum_policy)
+ # Assert generated string elements.
+ output = self.GetXMLOfChildren(self.writer._string_table_elem)
+ expected_output = (
+ '<string id="EnumPolicyStub">Enum policy caption</string>\n'
+ '<string id="EnumPolicyStub_Explain">'
+ 'This is a test description.</string>\n'
+ '<string id="EnumPolicyStub_item 1">Caption Item 1</string>\n'
+ '<string id="EnumPolicyStub_item 2">Caption Item 2</string>')
+ self.AssertXMLEquals(output, expected_output)
+ # Assert generated presentation elements.
+ output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+ expected_output = ('<presentation id="EnumPolicyStub">\n'
+ ' <dropdownList refId="EnumPolicyStub">'
+ 'Enum policy label</dropdownList>\n'
+ '</presentation>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testListPolicy(self):
+ list_policy = {
+ 'name': 'ListPolicyStub',
+ 'type': 'list',
+ 'caption': 'List policy caption',
+ 'label': 'List policy label',
+ 'desc': 'This is a test description.',
+ }
+ self._InitWriterForAddingPolicies(self.writer, list_policy)
+ self.writer.WritePolicy(list_policy)
+ # Assert generated string elements.
+ output = self.GetXMLOfChildren(self.writer._string_table_elem)
+ expected_output = (
+ '<string id="ListPolicyStub">List policy caption</string>\n'
+ '<string id="ListPolicyStub_Explain">'
+ 'This is a test description.</string>\n'
+ '<string id="ListPolicyStubDesc">List policy caption</string>')
+ self.AssertXMLEquals(output, expected_output)
+ # Assert generated presentation elements.
+ output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+ expected_output = (
+ '<presentation id="ListPolicyStub">\n'
+ ' <listBox refId="ListPolicyStubDesc">List policy label</listBox>\n'
+ '</presentation>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testStringEnumListPolicy(self):
+ list_policy = {
+ 'name':
+ 'ListPolicyStub',
+ 'type':
+ 'string-enum-list',
+ 'caption':
+ 'List policy caption',
+ 'label':
+ 'List policy label',
+ 'desc':
+ 'This is a test description.',
+ 'items': [
+ {
+ 'name': 'item 1',
+ 'value': 'value 1',
+ 'caption': 'Caption Item 1',
+ },
+ {
+ 'name': 'item 2',
+ 'value': 'value 2',
+ 'caption': 'Caption Item 2',
+ },
+ ],
+ }
+ self._InitWriterForAddingPolicies(self.writer, list_policy)
+ self.writer.WritePolicy(list_policy)
+ # Assert generated string elements.
+ output = self.GetXMLOfChildren(self.writer._string_table_elem)
+ expected_output = (
+ '<string id="ListPolicyStub">List policy caption</string>\n'
+ '<string id="ListPolicyStub_Explain">'
+ 'This is a test description.</string>\n'
+ '<string id="ListPolicyStubDesc">List policy caption</string>')
+ self.AssertXMLEquals(output, expected_output)
+ # Assert generated presentation elements.
+ output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+ expected_output = (
+ '<presentation id="ListPolicyStub">\n'
+ ' <listBox refId="ListPolicyStubDesc">List policy label</listBox>\n'
+ '</presentation>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testDictionaryPolicy(self, is_external=False):
+ dict_policy = {
+ 'name': 'DictionaryPolicyStub',
+ 'type': 'external' if is_external else 'dict',
+ 'caption': 'Dictionary policy caption',
+ 'label': 'Dictionary policy label',
+ 'desc': 'This is a test description.',
+ }
+ self._InitWriterForAddingPolicies(self.writer, dict_policy)
+ self.writer.WritePolicy(dict_policy)
+ # Assert generated string elements.
+ output = self.GetXMLOfChildren(self.writer._string_table_elem)
+ expected_output = (
+ '<string id="DictionaryPolicyStub">Dictionary policy caption</string>\n'
+ '<string id="DictionaryPolicyStub_Explain">'
+ 'This is a test description.\n'
+ 'See https://cloud.google.com/docs/chrome-enterprise/policies/?policy='
+ 'DictionaryPolicyStub\n</string>')
+ self.AssertXMLEquals(output, expected_output)
+ # Assert generated presentation elements.
+ output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+ expected_output = ('<presentation id="DictionaryPolicyStub">\n'
+ ' <textBox refId="DictionaryPolicyStub">\n'
+ ' <label>Dictionary policy label</label>\n'
+ ' </textBox>\n'
+ '</presentation>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testExternalPolicy(self):
+ self.testDictionaryPolicy(is_external=True)
+
+ def testPlatform(self):
+ # Test that the writer correctly chooses policies of platform Windows.
+ self.assertTrue(
+ self.writer.IsPolicySupported({
+ 'supported_on': [{
+ 'platform': 'win'
+ }, {
+ 'platform': 'aaa'
+ }]
+ }))
+ self.assertFalse(
+ self.writer.IsPolicySupported({
+ 'supported_on': [{
+ 'platform': 'mac',
+ }, {
+ 'platform': 'aaa'
+ }]
+ }))
+
+ def testStringEncodings(self):
+ enum_policy_a = {
+ 'name': 'EnumPolicy.A',
+ 'type': 'string-enum',
+ 'caption': 'Enum policy A caption',
+ 'label': 'Enum policy A label',
+ 'desc': 'This is a test description.',
+ 'items': [{
+ 'name': 'same_item',
+ 'value': '1',
+ 'caption': 'caption_a',
+ }],
+ }
+ enum_policy_b = {
+ 'name': 'EnumPolicy.B',
+ 'type': 'string-enum',
+ 'caption': 'Enum policy B caption',
+ 'label': 'Enum policy B label',
+ 'desc': 'This is a test description.',
+ 'items': [{
+ 'name': 'same_item',
+ 'value': '2',
+ 'caption': 'caption_b',
+ }],
+ }
+ self._InitWriterForAddingPolicies(self.writer, enum_policy_a)
+ self.writer.WritePolicy(enum_policy_a)
+ self.writer.WritePolicy(enum_policy_b)
+ # Assert generated string elements.
+ output = self.GetXMLOfChildren(self.writer._string_table_elem)
+ expected_output = (
+ '<string id="EnumPolicy_A">Enum policy A caption</string>\n'
+ '<string id="EnumPolicy_A_Explain">'
+ 'This is a test description.</string>\n'
+ '<string id="EnumPolicy_A_same_item">caption_a</string>\n'
+ '<string id="EnumPolicy_B">Enum policy B caption</string>\n'
+ '<string id="EnumPolicy_B_Explain">'
+ 'This is a test description.</string>\n'
+ '<string id="EnumPolicy_B_same_item">caption_b</string>\n')
+ self.AssertXMLEquals(output, expected_output)
+ # Assert generated presentation elements.
+ output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+ expected_output = ('<presentation id="EnumPolicy.A">\n'
+ ' <dropdownList refId="EnumPolicy.A">'
+ 'Enum policy A label</dropdownList>\n'
+ '</presentation>\n'
+ '<presentation id="EnumPolicy.B">\n'
+ ' <dropdownList refId="EnumPolicy.B">'
+ 'Enum policy B label</dropdownList>\n'
+ '</presentation>')
+ self.AssertXMLEquals(output, expected_output)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/admx_writer.py b/chromium/components/policy/tools/template_writers/writers/admx_writer.py
new file mode 100755
index 00000000000..d6dbf4fbc99
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/admx_writer.py
@@ -0,0 +1,500 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+
+from xml.dom import minidom
+from writers import gpo_editor_writer, xml_formatted_writer
+
+
+class AdmxElementType:
+ '''The different types of ADMX elements that can be used to display a policy.
+ This is related to the 'type' field in policy_templates.json, but there isn't
+ a perfect 1:1 mapping. This class is also used when writing ADML files, to
+ ensure that the ADML generated from policy_templates.json is compatible with
+ the ADMX generated from policy_templates.json"""
+ '''
+ MAIN = 1
+ STRING = 2
+ MULTI_STRING = 3
+ INT = 4
+ ENUM = 5
+ LIST = 6
+ GROUP = 7
+
+ @staticmethod
+ def GetType(policy, allow_multi_strings=False):
+ '''Returns the ADMX element type that should be used for the given policy.
+ This logic is shared between the ADMX writer and the ADML writer, to ensure
+ that the ADMX and ADML generated from policy_tempates.json are compatible.
+
+ Args:
+ policy: A dict describing the policy, as found in policy_templates.json.
+ allow_multi_strings: If true, the use of multi-line textbox elements is
+ allowed, so this function will sometimes return MULTI_STRING. If false
+ it falls back to single-line textboxes instead by returning STRING.
+
+ Returns:
+ One of the enum values of AdmxElementType.
+
+ Raises:
+ Exception: If policy['type'] is not recognized.
+ '''
+ policy_type = policy['type']
+ policy_example = policy.get('example_value')
+
+ # TODO(olsen): Some policies are defined in policy_templates.json as type
+ # string, but the string is actually a JSON object. We should change the
+ # schema so they are 'dict' or similar, but until then, we use this
+ # heuristic to decide whether they are actually JSON and so could benefit
+ # from being displayed to the user as a multi-line string:
+ if (policy_type == 'string' and allow_multi_strings and
+ policy_example is not None and policy_example.strip().startswith('{')):
+ return AdmxElementType.MULTI_STRING
+
+ admx_element_type = AdmxElementType._POLICY_TYPE_MAP.get(policy_type)
+ if admx_element_type is None:
+ raise Exception('Unknown policy type %s.' % policy_type)
+
+ if (admx_element_type == AdmxElementType.MULTI_STRING and
+ not allow_multi_strings):
+ return AdmxElementType.STRING
+
+ return admx_element_type
+
+
+AdmxElementType._POLICY_TYPE_MAP = {
+ 'main': AdmxElementType.MAIN,
+ 'string': AdmxElementType.STRING,
+ 'dict': AdmxElementType.MULTI_STRING,
+ 'external': AdmxElementType.MULTI_STRING,
+ 'int': AdmxElementType.INT,
+ 'int-enum': AdmxElementType.ENUM,
+ 'string-enum': AdmxElementType.ENUM,
+ 'list': AdmxElementType.LIST,
+ 'string-enum-list': AdmxElementType.LIST,
+ 'group': AdmxElementType.GROUP
+}
+
+
+def GetWriter(config):
+ '''Factory method for instanciating the ADMXWriter. Every Writer needs a
+ GetWriter method because the TemplateFormatter uses this method to
+ instantiate a Writer.
+ '''
+ return ADMXWriter(['win', 'win7'], config)
+
+
+class ADMXWriter(xml_formatted_writer.XMLFormattedWriter,
+ gpo_editor_writer.GpoEditorWriter):
+ '''Class for generating an ADMX policy template. It is used by the
+ PolicyTemplateGenerator to write the admx file.
+ '''
+
+ # DOM root node of the generated ADMX document.
+ _doc = None
+
+ # The ADMX "policies" element that contains the ADMX "policy" elements that
+ # are generated.
+ _active_policies_elem = None
+
+ def Init(self):
+ # Shortcut to platform-specific ADMX/ADM specific configuration.
+ assert len(self.platforms) <= 2
+ self.winconfig = self.config['win_config'][self.platforms[0]]
+
+ def _AdmlString(self, name):
+ '''Creates a reference to the named string in an ADML file.
+ Args:
+ name: Name of the referenced ADML string.
+ '''
+ name = name.replace('.', '_')
+ return '$(string.' + name + ')'
+
+ def _AdmlStringExplain(self, name):
+ '''Creates a reference to the named explanation string in an ADML file.
+ Args:
+ name: Name of the referenced ADML explanation.
+ '''
+ name = name.replace('.', '_')
+ return '$(string.' + name + '_Explain)'
+
+ def _AdmlPresentation(self, name):
+ '''Creates a reference to the named presentation element in an ADML file.
+ Args:
+ name: Name of the referenced ADML presentation element.
+ '''
+ return '$(presentation.' + name + ')'
+
+ def _AddPolicyNamespaces(self, parent, prefix, namespace):
+ '''Generates the ADMX "policyNamespace" element and adds the elements to the
+ passed parent element. The namespace of the generated ADMX document is
+ define via the ADMX "target" element. Used namespaces are declared with an
+ ADMX "using" element. ADMX "target" and "using" elements are children of the
+ ADMX "policyNamespace" element.
+
+ Args:
+ parent: The parent node to which all generated elements are added.
+ prefix: A logical name that can be used in the generated ADMX document to
+ refere to this namespace.
+ namespace: Namespace of the generated ADMX document.
+ '''
+ policy_namespaces_elem = self.AddElement(parent, 'policyNamespaces')
+ attributes = {
+ 'prefix': prefix,
+ 'namespace': namespace,
+ }
+ self.AddElement(policy_namespaces_elem, 'target', attributes)
+ if 'admx_using_namespaces' in self.config:
+ prefix_namespace_map = self.config['admx_using_namespaces']
+ for prefix in prefix_namespace_map:
+ attributes = {
+ 'prefix': prefix,
+ 'namespace': prefix_namespace_map[prefix],
+ }
+ self.AddElement(policy_namespaces_elem, 'using', attributes)
+ attributes = {
+ 'prefix': 'windows',
+ 'namespace': 'Microsoft.Policies.Windows',
+ }
+ self.AddElement(policy_namespaces_elem, 'using', attributes)
+
+ def _AddCategory(self, parent, name, display_name, parent_category_name=None):
+ '''Adds an ADMX category element to the passed parent node. The following
+ snippet shows an example of a category element where "chromium" is the value
+ of the parameter name:
+
+ <category displayName="$(string.chromium)" name="chromium"/>
+
+ Each parent node can have only one category with a given name. Adding the
+ same category again with the same attributes is ignored, but adding it
+ again with different attributes is an error.
+
+ Args:
+ parent: The parent node to which all generated elements are added.
+ name: Name of the category.
+ display_name: Display name of the category.
+ parent_category_name: Name of the parent category. Defaults to None.
+ '''
+ existing = list(
+ filter(lambda e: e.getAttribute('name') == name,
+ parent.getElementsByTagName('category')))
+ if existing:
+ assert len(existing) == 1
+ assert existing[0].getAttribute('name') == name
+ assert existing[0].getAttribute('displayName') == display_name
+ return
+ attributes = {
+ 'name': name,
+ 'displayName': display_name,
+ }
+ category_elem = self.AddElement(parent, 'category', attributes)
+ if parent_category_name:
+ attributes = {'ref': parent_category_name}
+ self.AddElement(category_elem, 'parentCategory', attributes)
+
+ def _AddCategories(self, categories):
+ '''Generates the ADMX "categories" element and adds it to the categories
+ main node. The "categories" element defines the category for the policies
+ defined in this ADMX document. Here is an example of an ADMX "categories"
+ element:
+
+ <categories>
+ <category displayName="$(string.googlechrome)" name="googlechrome">
+ <parentCategory ref="Google:Cat_Google"/>
+ </category>
+ </categories>
+
+ Args:
+ categories_path: The categories path e.g. ['google', 'googlechrome']. For
+ each level in the path a "category" element will be generated, unless
+ the level contains a ':', in which case it is treated as external
+ references and no element is generated. Except for the root level, each
+ level refers to its parent. Since the root level category has no parent
+ it does not require a parent reference.
+ '''
+ category_name = None
+ for category in categories:
+ parent_category_name = category_name
+ category_name = category
+ if (":" not in category_name):
+ self._AddCategory(self._categories_elem, category_name,
+ self._AdmlString(category_name), parent_category_name)
+
+ def _AddSupportedOn(self, parent, supported_os_list):
+ '''Generates the "supportedOn" ADMX element and adds it to the passed
+ parent node. The "supportedOn" element contains information about supported
+ Windows OS versions. The following code snippet contains an example of a
+ "supportedOn" element:
+
+ <supportedOn>
+ <definitions>
+ <definition name="$(supported_os)"
+ displayName="$(string.$(supported_os))"/>
+ </definitions>
+ ...
+ </supportedOn>
+
+ Args:
+ parent: The parent element to which all generated elements are added.
+ supported_os: List with all supported Win OSes.
+ '''
+ supported_on_elem = self.AddElement(parent, 'supportedOn')
+ definitions_elem = self.AddElement(supported_on_elem, 'definitions')
+ for supported_os in supported_os_list:
+ attributes = {
+ 'name': supported_os,
+ 'displayName': self._AdmlString(supported_os)
+ }
+ self.AddElement(definitions_elem, 'definition', attributes)
+
+ def _AddStringPolicy(self, parent, name, id=None):
+ '''Generates ADMX elements for a String-Policy and adds them to the
+ passed parent node.
+ '''
+ attributes = {
+ 'id': id or name,
+ 'valueName': name,
+ 'maxLength': '1000000',
+ }
+ self.AddElement(parent, 'text', attributes)
+
+ def _AddMultiStringPolicy(self, parent, name):
+ '''Generates ADMX elements for a multi-line String-Policy and adds them to
+ the passed parent node.
+ '''
+ # We currently also show a single-line textbox - see http://crbug/829328
+ self._AddStringPolicy(parent, name, id=name + '_Legacy')
+ attributes = {
+ 'id': name,
+ 'valueName': name,
+ 'maxLength': '1000000',
+ }
+ self.AddElement(parent, 'multiText', attributes)
+
+ def _AddIntPolicy(self, parent, policy):
+ '''Generates ADMX elements for an Int-Policy and adds them to the passed
+ parent node.
+ '''
+ #default max value for an integer
+ max = 2000000000
+ min = 0
+ if self.PolicyHasRestrictions(policy):
+ schema = policy['schema']
+ if 'minimum' in schema and schema['minimum'] >= 0:
+ min = schema['minimum']
+ if 'maximum' in schema and schema['maximum'] >= 0:
+ max = schema['maximum']
+ assert type(min) == int
+ assert type(max) == int
+ attributes = {
+ 'id': policy['name'],
+ 'valueName': policy['name'],
+ 'maxValue': str(max),
+ 'minValue': str(min),
+ }
+ self.AddElement(parent, 'decimal', attributes)
+
+ def _AddEnumPolicy(self, parent, policy):
+ '''Generates ADMX elements for an Enum-Policy and adds them to the
+ passed parent element.
+ '''
+ name = policy['name']
+ items = policy['items']
+ attributes = {
+ 'id': name,
+ 'valueName': name,
+ }
+ enum_elem = self.AddElement(parent, 'enum', attributes)
+ for item in items:
+ attributes = {'displayName': self._AdmlString(name + "_" + item['name'])}
+ item_elem = self.AddElement(enum_elem, 'item', attributes)
+ value_elem = self.AddElement(item_elem, 'value')
+ value_string = str(item['value'])
+ if policy['type'] == 'int-enum':
+ self.AddElement(value_elem, 'decimal', {'value': value_string})
+ else:
+ self.AddElement(value_elem, 'string', {}, value_string)
+
+ def _AddListPolicy(self, parent, key, name):
+ '''Generates ADMX XML elements for a List-Policy and adds them to the
+ passed parent element.
+ '''
+ attributes = {
+ # The ID must be in sync with ID of the corresponding element in the
+ # ADML file.
+ 'id': name + 'Desc',
+ 'valuePrefix': '',
+ 'key': key + '\\' + name,
+ }
+ self.AddElement(parent, 'list', attributes)
+
+ def _AddMainPolicy(self, parent):
+ '''Generates ADMX elements for a Main-Policy amd adds them to the
+ passed parent element.
+ '''
+ enabled_value_elem = self.AddElement(parent, 'enabledValue')
+ self.AddElement(enabled_value_elem, 'decimal', {'value': '1'})
+ disabled_value_elem = self.AddElement(parent, 'disabledValue')
+ self.AddElement(disabled_value_elem, 'decimal', {'value': '0'})
+
+ def PolicyHasRestrictions(self, policy):
+ if 'schema' in policy:
+ return any(keyword in policy['schema'] \
+ for keyword in ['minimum', 'maximum'])
+ return False
+
+ def _GetElements(self, policy_group_elem):
+ '''Returns the ADMX "elements" child from an ADMX "policy" element. If the
+ "policy" element has no "elements" child yet, a new child is created.
+
+ Args:
+ policy_group_elem: The ADMX "policy" element from which the child element
+ "elements" is returned.
+
+ Raises:
+ Exception: The policy_group_elem does not contain a ADMX "policy" element.
+ '''
+ if policy_group_elem.tagName != 'policy':
+ raise Exception('Expected a "policy" element but got a "%s" element' %
+ policy_group_elem.tagName)
+ elements_list = policy_group_elem.getElementsByTagName('elements')
+ if len(elements_list) == 0:
+ return self.AddElement(policy_group_elem, 'elements')
+ elif len(elements_list) == 1:
+ return elements_list[0]
+ else:
+ raise Exception('There is supposed to be only one "elements" node but'
+ ' there are %s.' % str(len(elements_list)))
+
+ def _GetAdmxElementType(self, policy):
+ '''Returns the ADMX element type for a particular Policy.'''
+ return AdmxElementType.GetType(policy, allow_multi_strings=False)
+
+ def _WritePolicy(self, policy, name, key, parent):
+ '''Generates ADMX elements for a Policy.'''
+ policies_elem = self._active_policies_elem
+ policy_name = policy['name']
+ attributes = {
+ 'name': name,
+ 'class': self.GetClass(policy),
+ 'displayName': self._AdmlString(policy_name),
+ 'explainText': self._AdmlStringExplain(policy_name),
+ 'presentation': self._AdmlPresentation(policy_name),
+ 'key': key,
+ }
+ is_win7_only = self.IsPolicyOnWin7Only(policy)
+ supported_key = ('win_supported_os_win7'
+ if is_win7_only else 'win_supported_os')
+ supported_on_text = self.config[supported_key]
+
+ # Store the current "policy" AMDX element in self for later use by the
+ # WritePolicy method.
+ policy_elem = self.AddElement(policies_elem, 'policy', attributes)
+ self.AddElement(policy_elem, 'parentCategory', {'ref': parent})
+ self.AddElement(policy_elem, 'supportedOn', {'ref': supported_on_text})
+
+ element_type = self._GetAdmxElementType(policy)
+ if element_type == AdmxElementType.MAIN:
+ self.AddAttribute(policy_elem, 'valueName', policy_name)
+ self._AddMainPolicy(policy_elem)
+ elif element_type == AdmxElementType.STRING:
+ parent = self._GetElements(policy_elem)
+ self._AddStringPolicy(parent, policy_name)
+ elif element_type == AdmxElementType.MULTI_STRING:
+ parent = self._GetElements(policy_elem)
+ self._AddMultiStringPolicy(parent, policy_name)
+ elif element_type == AdmxElementType.INT:
+ parent = self._GetElements(policy_elem)
+ self._AddIntPolicy(parent, policy)
+ elif element_type == AdmxElementType.ENUM:
+ parent = self._GetElements(policy_elem)
+ self._AddEnumPolicy(parent, policy)
+ elif element_type == AdmxElementType.LIST:
+ parent = self._GetElements(policy_elem)
+ self._AddListPolicy(parent, key, policy_name)
+ elif element_type == AdmxElementType.GROUP:
+ pass
+ else:
+ raise Exception('Unknown element type %s.' % element_type)
+
+ def WritePolicy(self, policy):
+ if self.CanBeMandatory(policy):
+ self._WritePolicy(policy, policy['name'],
+ self.winconfig['reg_mandatory_key_name'],
+ self._active_mandatory_policy_group_name)
+
+ def WriteRecommendedPolicy(self, policy):
+ self._WritePolicy(policy, policy['name'] + '_recommended',
+ self.winconfig['reg_recommended_key_name'],
+ self._active_recommended_policy_group_name)
+
+ def _BeginPolicyGroup(self, group, name, parent):
+ '''Generates ADMX elements for a Policy-Group.
+ '''
+ attributes = {
+ 'name': name,
+ 'displayName': self._AdmlString(group['name'] + '_group'),
+ }
+ category_elem = self.AddElement(self._categories_elem, 'category',
+ attributes)
+ attributes = {'ref': parent}
+ self.AddElement(category_elem, 'parentCategory', attributes)
+
+ def BeginPolicyGroup(self, group):
+ self._BeginPolicyGroup(group, group['name'],
+ self.winconfig['mandatory_category_path'][-1])
+ self._active_mandatory_policy_group_name = group['name']
+
+ def EndPolicyGroup(self):
+ self._active_mandatory_policy_group_name = \
+ self.winconfig['mandatory_category_path'][-1]
+
+ def BeginRecommendedPolicyGroup(self, group):
+ self._BeginPolicyGroup(group, group['name'] + '_recommended',
+ self.winconfig['recommended_category_path'][-1])
+ self._active_recommended_policy_group_name = group['name'] + '_recommended'
+
+ def EndRecommendedPolicyGroup(self):
+ self._active_recommended_policy_group_name = \
+ self.winconfig['recommended_category_path'][-1]
+
+ def BeginTemplate(self):
+ '''Generates the skeleton of the ADMX template. An ADMX template contains
+ an ADMX "PolicyDefinitions" element with four child nodes: "policies"
+ "policyNamspaces", "resources", "supportedOn" and "categories"
+ '''
+ dom_impl = minidom.getDOMImplementation('')
+ self._doc = dom_impl.createDocument(None, 'policyDefinitions', None)
+ if self._GetChromiumVersionString() is not None:
+ self.AddComment(self._doc.documentElement, self.config['build'] + \
+ ' version: ' + self._GetChromiumVersionString())
+ policy_definitions_elem = self._doc.documentElement
+
+ policy_definitions_elem.attributes['revision'] = '1.0'
+ policy_definitions_elem.attributes['schemaVersion'] = '1.0'
+
+ self._AddPolicyNamespaces(policy_definitions_elem,
+ self.config['admx_prefix'],
+ self.winconfig['namespace'])
+ self.AddElement(policy_definitions_elem, 'resources',
+ {'minRequiredRevision': '1.0'})
+ self._AddSupportedOn(
+ policy_definitions_elem,
+ [self.config['win_supported_os'], self.config['win_supported_os_win7']])
+ self._categories_elem = self.AddElement(policy_definitions_elem,
+ 'categories')
+ self._AddCategories(self.winconfig['mandatory_category_path'])
+ self._AddCategories(self.winconfig['recommended_category_path'])
+ self._active_policies_elem = self.AddElement(policy_definitions_elem,
+ 'policies')
+ self._active_mandatory_policy_group_name = \
+ self.winconfig['mandatory_category_path'][-1]
+ self._active_recommended_policy_group_name = \
+ self.winconfig['recommended_category_path'][-1]
+
+ def GetTemplateText(self):
+ return self.ToPrettyXml(self._doc)
+
+ def GetClass(self, policy):
+ return 'Both'
diff --git a/chromium/components/policy/tools/template_writers/writers/admx_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/admx_writer_unittest.py
new file mode 100755
index 00000000000..1543a016bb9
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/admx_writer_unittest.py
@@ -0,0 +1,700 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+"""Unittests for writers.admx_writer."""
+
+import os
+import sys
+import unittest
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+from writers import admx_writer
+from writers import xml_writer_base_unittest
+from xml.dom import minidom
+
+
+class AdmxWriterUnittest(xml_writer_base_unittest.XmlWriterBaseTest):
+
+ def _CreateDocumentElement(self):
+ dom_impl = minidom.getDOMImplementation('')
+ doc = dom_impl.createDocument(None, 'root', None)
+ return doc.documentElement
+
+ def setUp(self):
+ # Writer configuration. This dictionary contains parameter used by the ADMX
+ # Writer
+ config = {
+ 'win_supported_os': 'SUPPORTED_TESTOS',
+ 'win_supported_os_win7': 'SUPPORTED_TESTOS_2',
+ 'win_config': {
+ 'win': {
+ 'reg_mandatory_key_name':
+ 'Software\\Policies\\Test',
+ 'reg_recommended_key_name':
+ 'Software\\Policies\\Test\\Recommended',
+ 'mandatory_category_path': ['test_category'],
+ 'recommended_category_path': ['test_recommended_category'],
+ 'category_path_strings': {
+ 'test_category': 'TestCategory',
+ 'test_recommended_category': 'TestCategory - recommended',
+ },
+ 'namespace':
+ 'ADMXWriter.Test.Namespace',
+ },
+ 'chrome_os': {
+ 'reg_mandatory_key_name':
+ 'Software\\Policies\\CrOSTest',
+ 'reg_recommended_key_name':
+ 'Software\\Policies\\CrOSTest\\Recommended',
+ 'mandatory_category_path': ['cros_test_category'],
+ 'recommended_category_path': ['cros_test_recommended_category'],
+ 'category_path_strings': {
+ 'cros_test_category':
+ 'CrOSTestCategory',
+ 'cros_test_recommended_category':
+ 'CrOSTestCategory - recommended',
+ },
+ 'namespace':
+ 'ADMXWriter.Test.Namespace.ChromeOS',
+ },
+ },
+ 'admx_prefix': 'test_prefix',
+ 'build': 'test_product',
+ }
+ self.writer = self._GetWriter(config)
+ self.writer.Init()
+
+ def _GetWriter(self, config):
+ return admx_writer.GetWriter(config)
+
+ def _GetKey(self):
+ return "Test"
+
+ def _GetCategory(self):
+ return "test_category"
+
+ def _GetCategoryRec(self):
+ return "test_recommended_category"
+
+ def _GetNamespace(self):
+ return "ADMXWriter.Test.Namespace"
+
+ def _GetPoliciesElement(self, doc):
+ node_list = doc.getElementsByTagName('policies')
+ self.assertTrue(node_list.length == 1)
+ return node_list.item(0)
+
+ def _GetCategoriesElement(self, doc):
+ node_list = doc.getElementsByTagName('categories')
+ self.assertTrue(node_list.length == 1)
+ return node_list.item(0)
+
+ def testEmpty(self):
+ self.writer.BeginTemplate()
+ self.writer.EndTemplate()
+
+ output = self.writer.GetTemplateText()
+ expected_output = (
+ '<?xml version="1.0" ?>\n'
+ '<policyDefinitions revision="1.0" schemaVersion="1.0">\n'
+ ' <policyNamespaces>\n'
+ ' <target namespace="' + self._GetNamespace() + '"'
+ ' prefix="test_prefix"/>\n'
+ ' <using namespace="Microsoft.Policies.Windows" prefix="windows"/>\n'
+ ' </policyNamespaces>\n'
+ ' <resources minRequiredRevision="1.0"/>\n'
+ ' <supportedOn>\n'
+ ' <definitions>\n'
+ ' <definition displayName="'
+ '$(string.SUPPORTED_TESTOS)" name="SUPPORTED_TESTOS"/>\n'
+ ' <definition displayName="'
+ '$(string.SUPPORTED_TESTOS_2)" name="SUPPORTED_TESTOS_2"/>\n'
+ ' </definitions>\n'
+ ' </supportedOn>\n'
+ ' <categories>\n'
+ ' <category displayName="$(string.' + self._GetCategory() + ')"'
+ ' name="' + self._GetCategory() + '"/>\n'
+ ' <category displayName="$(string.' + self._GetCategoryRec() + ')"'
+ ' name="' + self._GetCategoryRec() + '"/>\n'
+ ' </categories>\n'
+ ' <policies/>\n'
+ '</policyDefinitions>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testEmptyVersion(self):
+ self.writer.config['version'] = '39.0.0.0'
+ self.writer.BeginTemplate()
+ self.writer.EndTemplate()
+
+ output = self.writer.GetTemplateText()
+ expected_output = (
+ '<?xml version="1.0" ?>\n'
+ '<policyDefinitions revision="1.0" schemaVersion="1.0">\n'
+ ' <!--test_product version: 39.0.0.0-->\n'
+ ' <policyNamespaces>\n'
+ ' <target namespace="' + self._GetNamespace() + '"'
+ ' prefix="test_prefix"/>\n'
+ ' <using namespace="Microsoft.Policies.Windows" prefix="windows"/>\n'
+ ' </policyNamespaces>\n'
+ ' <resources minRequiredRevision="1.0"/>\n'
+ ' <supportedOn>\n'
+ ' <definitions>\n'
+ ' <definition displayName="'
+ '$(string.SUPPORTED_TESTOS)" name="SUPPORTED_TESTOS"/>\n'
+ ' <definition displayName="'
+ '$(string.SUPPORTED_TESTOS_2)" name="SUPPORTED_TESTOS_2"/>\n'
+ ' </definitions>\n'
+ ' </supportedOn>\n'
+ ' <categories>\n'
+ ' <category displayName="$(string.' + self._GetCategory() + ')"'
+ ' name="' + self._GetCategory() + '"/>\n'
+ ' <category displayName="$(string.' + self._GetCategoryRec() + ')"'
+ ' name="' + self._GetCategoryRec() + '"/>\n'
+ ' </categories>\n'
+ ' <policies/>\n'
+ '</policyDefinitions>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testEmptyPolicyGroup(self):
+ empty_policy_group = {'name': 'PolicyGroup', 'policies': []}
+ # Initialize writer to write a policy group.
+ self.writer.BeginTemplate()
+ # Write policy group
+ self.writer.BeginPolicyGroup(empty_policy_group)
+ self.writer.EndPolicyGroup()
+
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = ''
+ self.AssertXMLEquals(output, expected_output)
+
+ output = self.GetXMLOfChildren(self._GetCategoriesElement(self.writer._doc))
+ expected_output = (
+ '<category displayName="$(string.' + self._GetCategory() + ')"'
+ ' name="' + self._GetCategory() + '"/>\n'
+ '<category displayName="$(string.' + self._GetCategoryRec() + ')"'
+ ' name="' + self._GetCategoryRec() + '"/>\n'
+ '<category displayName="$(string.PolicyGroup_group)"'
+ ' name="PolicyGroup">\n'
+ ' <parentCategory ref="' + self._GetCategory() + '"/>\n'
+ '</category>')
+
+ self.AssertXMLEquals(output, expected_output)
+
+ def testPolicyGroup(self):
+ empty_policy_group = {
+ 'name':
+ 'PolicyGroup',
+ 'policies': [
+ {
+ 'name': 'PolicyStub2',
+ 'type': 'main'
+ },
+ {
+ 'name': 'PolicyStub1',
+ 'type': 'main'
+ },
+ ]
+ }
+ # Initialize writer to write a policy group.
+ self.writer.BeginTemplate()
+ # Write policy group
+ self.writer.BeginPolicyGroup(empty_policy_group)
+ self.writer.EndPolicyGroup()
+
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = ''
+ self.AssertXMLEquals(output, expected_output)
+
+ output = self.GetXMLOfChildren(self._GetCategoriesElement(self.writer._doc))
+ expected_output = (
+ '<category displayName="$(string.' + self._GetCategory() + ')"'
+ ' name="' + self._GetCategory() + '"/>\n'
+ '<category displayName="$(string.' + self._GetCategoryRec() + ')"'
+ ' name="' + self._GetCategoryRec() + '"/>\n'
+ '<category displayName="$(string.PolicyGroup_group)"'
+ ' name="PolicyGroup">\n'
+ ' <parentCategory ref="' + self._GetCategory() + '"/>\n'
+ '</category>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def _initWriterForPolicy(self, writer, policy):
+ '''Initializes the writer to write the given policy next.
+ '''
+ policy_group = {'name': 'PolicyGroup', 'policies': [policy]}
+ writer.BeginTemplate()
+ writer.BeginPolicyGroup(policy_group)
+
+ def testMainPolicy(self):
+ main_policy = {
+ 'name': 'DummyMainPolicy',
+ 'type': 'main',
+ }
+
+ self._initWriterForPolicy(self.writer, main_policy)
+
+ self.writer.WritePolicy(main_policy)
+
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = (
+ '<policy class="' + self.writer.GetClass(main_policy) + '"'
+ ' displayName="$(string.DummyMainPolicy)"'
+ ' explainText="$(string.DummyMainPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="DummyMainPolicy"'
+ ' presentation="$(presentation.DummyMainPolicy)"'
+ ' valueName="DummyMainPolicy">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <enabledValue>\n'
+ ' <decimal value="1"/>\n'
+ ' </enabledValue>\n'
+ ' <disabledValue>\n'
+ ' <decimal value="0"/>\n'
+ ' </disabledValue>\n'
+ '</policy>')
+
+ self.AssertXMLEquals(output, expected_output)
+
+ def testRecommendedPolicy(self):
+ main_policy = {
+ 'name': 'DummyMainPolicy',
+ 'type': 'main',
+ }
+
+ policy_group = {
+ 'name': 'PolicyGroup',
+ 'policies': [main_policy],
+ }
+ self.writer.BeginTemplate()
+ self.writer.BeginRecommendedPolicyGroup(policy_group)
+
+ self.writer.WriteRecommendedPolicy(main_policy)
+
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = (
+ '<policy class="' + self.writer.GetClass(main_policy) + '"'
+ ' displayName="$(string.DummyMainPolicy)"'
+ ' explainText="$(string.DummyMainPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '\\Recommended"'
+ ' name="DummyMainPolicy_recommended"'
+ ' presentation="$(presentation.DummyMainPolicy)"'
+ ' valueName="DummyMainPolicy">\n'
+ ' <parentCategory ref="PolicyGroup_recommended"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <enabledValue>\n'
+ ' <decimal value="1"/>\n'
+ ' </enabledValue>\n'
+ ' <disabledValue>\n'
+ ' <decimal value="0"/>\n'
+ ' </disabledValue>\n'
+ '</policy>')
+
+ self.AssertXMLEquals(output, expected_output)
+
+ def testRecommendedOnlyPolicy(self):
+ main_policy = {
+ 'name': 'DummyMainPolicy',
+ 'type': 'main',
+ 'features': {
+ 'can_be_recommended': True,
+ 'can_be_mandatory': False,
+ }
+ }
+
+ policy_group = {
+ 'name': 'PolicyGroup',
+ 'policies': [main_policy],
+ }
+ self.writer.BeginTemplate()
+ self.writer.BeginRecommendedPolicyGroup(policy_group)
+
+ self.writer.WritePolicy(main_policy)
+ self.writer.WriteRecommendedPolicy(main_policy)
+
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = (
+ '<policy class="' + self.writer.GetClass(main_policy) + '"'
+ ' displayName="$(string.DummyMainPolicy)"'
+ ' explainText="$(string.DummyMainPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '\\Recommended"'
+ ' name="DummyMainPolicy_recommended"'
+ ' presentation="$(presentation.DummyMainPolicy)"'
+ ' valueName="DummyMainPolicy">\n'
+ ' <parentCategory ref="PolicyGroup_recommended"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <enabledValue>\n'
+ ' <decimal value="1"/>\n'
+ ' </enabledValue>\n'
+ ' <disabledValue>\n'
+ ' <decimal value="0"/>\n'
+ ' </disabledValue>\n'
+ '</policy>')
+
+ self.AssertXMLEquals(output, expected_output)
+
+ def testStringPolicy(self):
+ string_policy = {
+ 'name': 'SampleStringPolicy',
+ 'type': 'string',
+ }
+ self._initWriterForPolicy(self.writer, string_policy)
+
+ self.writer.WritePolicy(string_policy)
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = (
+ '<policy class="' + self.writer.GetClass(string_policy) + '"'
+ ' displayName="$(string.SampleStringPolicy)"'
+ ' explainText="$(string.SampleStringPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="SampleStringPolicy"'
+ ' presentation="$(presentation.SampleStringPolicy)">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <elements>\n'
+ ' <text id="SampleStringPolicy" maxLength="1000000"'
+ ' valueName="SampleStringPolicy"/>\n'
+ ' </elements>\n'
+ '</policy>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testIntPolicy(self):
+ int_policy = {
+ 'name': 'SampleIntPolicy',
+ 'type': 'int',
+ }
+ self._initWriterForPolicy(self.writer, int_policy)
+
+ self.writer.WritePolicy(int_policy)
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = (
+ '<policy class="' + self.writer.GetClass(int_policy) + '"'
+ ' displayName="$(string.SampleIntPolicy)"'
+ ' explainText="$(string.SampleIntPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="SampleIntPolicy"'
+ ' presentation="$(presentation.SampleIntPolicy)">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <elements>\n'
+ ' <decimal id="SampleIntPolicy" maxValue="2000000000" minValue="0" '
+ 'valueName="SampleIntPolicy"/>\n'
+ ' </elements>\n'
+ '</policy>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testIntPolicyWithWin7Only(self):
+ int_policy = {
+ 'name': 'SampleIntPolicy',
+ 'type': 'int',
+ 'supported_on': [{
+ 'platform': 'win7',
+ }]
+ }
+ self._initWriterForPolicy(self.writer, int_policy)
+
+ self.writer.WritePolicy(int_policy)
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = (
+ '<policy class="' + self.writer.GetClass(int_policy) + '"'
+ ' displayName="$(string.SampleIntPolicy)"'
+ ' explainText="$(string.SampleIntPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="SampleIntPolicy"'
+ ' presentation="$(presentation.SampleIntPolicy)">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS_2"/>\n'
+ ' <elements>\n'
+ ' <decimal id="SampleIntPolicy" maxValue="2000000000" minValue="0" '
+ 'valueName="SampleIntPolicy"/>\n'
+ ' </elements>\n'
+ '</policy>')
+ self.AssertXMLEquals(output, expected_output)
+
+
+ def testIntEnumPolicy(self):
+ enum_policy = {
+ 'name':
+ 'SampleEnumPolicy',
+ 'type':
+ 'int-enum',
+ 'items': [
+ {
+ 'name': 'item_1',
+ 'value': 0
+ },
+ {
+ 'name': 'item_2',
+ 'value': 1
+ },
+ ]
+ }
+
+ self._initWriterForPolicy(self.writer, enum_policy)
+ self.writer.WritePolicy(enum_policy)
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = (
+ '<policy class="' + self.writer.GetClass(enum_policy) + '"'
+ ' displayName="$(string.SampleEnumPolicy)"'
+ ' explainText="$(string.SampleEnumPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="SampleEnumPolicy"'
+ ' presentation="$(presentation.SampleEnumPolicy)">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <elements>\n'
+ ' <enum id="SampleEnumPolicy" valueName="SampleEnumPolicy">\n'
+ ' <item displayName="$(string.SampleEnumPolicy_item_1)">\n'
+ ' <value>\n'
+ ' <decimal value="0"/>\n'
+ ' </value>\n'
+ ' </item>\n'
+ ' <item displayName="$(string.SampleEnumPolicy_item_2)">\n'
+ ' <value>\n'
+ ' <decimal value="1"/>\n'
+ ' </value>\n'
+ ' </item>\n'
+ ' </enum>\n'
+ ' </elements>\n'
+ '</policy>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testStringEnumPolicy(self):
+ enum_policy = {
+ 'name':
+ 'SampleEnumPolicy',
+ 'type':
+ 'string-enum',
+ 'items': [
+ {
+ 'name': 'item_1',
+ 'value': 'one'
+ },
+ {
+ 'name': 'item_2',
+ 'value': 'two'
+ },
+ ]
+ }
+
+ # This test is different than the others because it also tests that space
+ # usage inside <string> nodes is correct.
+ dom_impl = minidom.getDOMImplementation('')
+ self.writer._doc = dom_impl.createDocument(None, 'policyDefinitions', None)
+ self.writer._active_policies_elem = self.writer._doc.documentElement
+ self.writer._active_mandatory_policy_group_name = 'PolicyGroup'
+ self.writer.WritePolicy(enum_policy)
+ output = self.writer.GetTemplateText()
+ expected_output = (
+ '<?xml version="1.0" ?>\n'
+ '<policyDefinitions>\n'
+ ' <policy class="' + self.writer.GetClass(enum_policy) + '"'
+ ' displayName="$(string.SampleEnumPolicy)"'
+ ' explainText="$(string.SampleEnumPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="SampleEnumPolicy"'
+ ' presentation="$(presentation.SampleEnumPolicy)">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <elements>\n'
+ ' <enum id="SampleEnumPolicy" valueName="SampleEnumPolicy">\n'
+ ' <item displayName="$(string.SampleEnumPolicy_item_1)">\n'
+ ' <value>\n'
+ ' <string>one</string>\n'
+ ' </value>\n'
+ ' </item>\n'
+ ' <item displayName="$(string.SampleEnumPolicy_item_2)">\n'
+ ' <value>\n'
+ ' <string>two</string>\n'
+ ' </value>\n'
+ ' </item>\n'
+ ' </enum>\n'
+ ' </elements>\n'
+ ' </policy>\n'
+ '</policyDefinitions>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testListPolicy(self):
+ list_policy = {
+ 'name': 'SampleListPolicy',
+ 'type': 'list',
+ }
+ self._initWriterForPolicy(self.writer, list_policy)
+ self.writer.WritePolicy(list_policy)
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = (
+ '<policy class="' + self.writer.GetClass(list_policy) + '"'
+ ' displayName="$(string.SampleListPolicy)"'
+ ' explainText="$(string.SampleListPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="SampleListPolicy"'
+ ' presentation="$(presentation.SampleListPolicy)">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <elements>\n'
+ ' <list id="SampleListPolicyDesc"'
+ ' key="Software\Policies\\' + self._GetKey() + '\SampleListPolicy"'
+ ' valuePrefix=""/>\n'
+ ' </elements>\n'
+ '</policy>')
+
+ self.AssertXMLEquals(output, expected_output)
+
+ def testStringEnumListPolicy(self):
+ list_policy = {
+ 'name':
+ 'SampleListPolicy',
+ 'type':
+ 'string-enum-list',
+ 'items': [
+ {
+ 'name': 'item_1',
+ 'value': 'one'
+ },
+ {
+ 'name': 'item_2',
+ 'value': 'two'
+ },
+ ]
+ }
+ self._initWriterForPolicy(self.writer, list_policy)
+ self.writer.WritePolicy(list_policy)
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = (
+ '<policy class="' + self.writer.GetClass(list_policy) + '"'
+ ' displayName="$(string.SampleListPolicy)"'
+ ' explainText="$(string.SampleListPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="SampleListPolicy"'
+ ' presentation="$(presentation.SampleListPolicy)">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <elements>\n'
+ ' <list id="SampleListPolicyDesc"'
+ ' key="Software\Policies\\' + self._GetKey() + '\SampleListPolicy"'
+ ' valuePrefix=""/>\n'
+ ' </elements>\n'
+ '</policy>')
+
+ self.AssertXMLEquals(output, expected_output)
+
+ def testDictionaryPolicy(self, is_external=False):
+ dict_policy = {
+ 'name': 'SampleDictionaryPolicy',
+ 'type': 'external' if is_external else 'dict',
+ }
+ self._initWriterForPolicy(self.writer, dict_policy)
+
+ self.writer.WritePolicy(dict_policy)
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = (
+ '<policy class="' + self.writer.GetClass(dict_policy) + '"'
+ ' displayName="$(string.SampleDictionaryPolicy)"'
+ ' explainText="$(string.SampleDictionaryPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="SampleDictionaryPolicy"'
+ ' presentation="$(presentation.SampleDictionaryPolicy)">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <elements>\n'
+ ' <text id="SampleDictionaryPolicy" maxLength="1000000"'
+ ' valueName="SampleDictionaryPolicy"/>\n'
+ ' </elements>\n'
+ '</policy>')
+ self.AssertXMLEquals(output, expected_output)
+
+ def testExternalPolicy(self):
+ self.testDictionaryPolicy(is_external=True)
+
+ def testPlatform(self):
+ # Test that the writer correctly chooses policies of platform Windows.
+ self.assertTrue(
+ self.writer.IsPolicySupported({
+ 'supported_on': [{
+ 'platform': 'win'
+ }, {
+ 'platform': 'aaa'
+ }]
+ }))
+ self.assertFalse(
+ self.writer.IsPolicySupported({
+ 'supported_on': [{
+ 'platform': 'mac'
+ }, {
+ 'platform': 'aaa'
+ }, {
+ 'platform': 'linux'
+ }]
+ }))
+
+ def testStringEncodings(self):
+ enum_policy_a = {
+ 'name': 'SampleEnumPolicy.A',
+ 'type': 'string-enum',
+ 'items': [{
+ 'name': 'tls1.2',
+ 'value': 'tls1.2'
+ }]
+ }
+ enum_policy_b = {
+ 'name': 'SampleEnumPolicy.B',
+ 'type': 'string-enum',
+ 'items': [{
+ 'name': 'tls1.2',
+ 'value': 'tls1.2'
+ }]
+ }
+
+ dom_impl = minidom.getDOMImplementation('')
+ self.writer._doc = dom_impl.createDocument(None, 'policyDefinitions', None)
+ self.writer._active_policies_elem = self.writer._doc.documentElement
+ self.writer._active_mandatory_policy_group_name = 'PolicyGroup'
+ self.writer.WritePolicy(enum_policy_a)
+ self.writer.WritePolicy(enum_policy_b)
+ output = self.writer.GetTemplateText()
+ expected_output = (
+ '<?xml version="1.0" ?>\n'
+ '<policyDefinitions>\n'
+ ' <policy class="' + self.writer.GetClass(enum_policy_a) + '"'
+ ' displayName="$(string.SampleEnumPolicy_A)"'
+ ' explainText="$(string.SampleEnumPolicy_A_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="SampleEnumPolicy.A"'
+ ' presentation="$(presentation.SampleEnumPolicy.A)">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <elements>\n'
+ ' <enum id="SampleEnumPolicy.A" valueName="SampleEnumPolicy.A">\n'
+ ' <item displayName="$(string.SampleEnumPolicy_A_tls1_2)">\n'
+ ' <value>\n'
+ ' <string>tls1.2</string>\n'
+ ' </value>\n'
+ ' </item>\n'
+ ' </enum>\n'
+ ' </elements>\n'
+ ' </policy>\n'
+ ' <policy class="' + self.writer.GetClass(enum_policy_b) + '"'
+ ' displayName="$(string.SampleEnumPolicy_B)"'
+ ' explainText="$(string.SampleEnumPolicy_B_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="SampleEnumPolicy.B"'
+ ' presentation="$(presentation.SampleEnumPolicy.B)">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <elements>\n'
+ ' <enum id="SampleEnumPolicy.B" valueName="SampleEnumPolicy.B">\n'
+ ' <item displayName="$(string.SampleEnumPolicy_B_tls1_2)">\n'
+ ' <value>\n'
+ ' <string>tls1.2</string>\n'
+ ' </value>\n'
+ ' </item>\n'
+ ' </enum>\n'
+ ' </elements>\n'
+ ' </policy>\n'
+ '</policyDefinitions>')
+ self.AssertXMLEquals(output, expected_output)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/android_policy_writer.py b/chromium/components/policy/tools/template_writers/writers/android_policy_writer.py
new file mode 100755
index 00000000000..5536a41fca3
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/android_policy_writer.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015 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.
+
+from writers import xml_formatted_writer
+from xml.dom import minidom
+from xml.sax import saxutils as xml_escape
+
+
+def GetWriter(config):
+ '''Factory method for creating AndroidPolicyWriter objects.
+ See the constructor of TemplateWriter for description of
+ arguments.
+ '''
+ return AndroidPolicyWriter(['android'], config)
+
+
+def _EscapeResource(resource):
+ '''Escape the resource for usage in an Android resource XML file.
+ This includes standard XML escaping as well as those specific to Android.
+ '''
+ if resource == None or type(resource) in (int, bool):
+ return str(resource)
+ return xml_escape.escape(
+ resource,
+ {
+ # Written order is matter to prevent "'" becomes "\\\\'" instead of
+ # "\\'".
+ "\\": "\\\\",
+ "'": "\\'",
+ '"': '\\"',
+ })
+
+
+class AndroidPolicyWriter(xml_formatted_writer.XMLFormattedWriter):
+ '''Outputs localized Android Resource XML files.
+ The policy strings are localized and exposed as string resources for
+ consumption through Android's App restriction Schema.
+ '''
+
+ # DOM root node of the generated XML document.
+ _doc = None
+ # The resources node contains all resource 'string' and 'string-array'
+ # elements.
+ _resources = None
+
+ def AddStringResource(self, name, string):
+ '''Add a string resource of the given name.
+ '''
+ string_node = self._doc.createElement('string')
+ string_node.setAttribute('name', name)
+ string_node.appendChild(self._doc.createTextNode(_EscapeResource(string)))
+ self._resources.appendChild(string_node)
+
+ def AddStringArrayResource(self, name, string_items):
+ '''Add a string-array resource of the given name and
+ elements from string_items.
+ '''
+ string_array_node = self._doc.createElement('string-array')
+ string_array_node.setAttribute('name', name)
+ self._resources.appendChild(string_array_node)
+ for item in string_items:
+ string_node = self._doc.createElement('item')
+ string_node.appendChild(self._doc.createTextNode(_EscapeResource(item)))
+ string_array_node.appendChild(string_node)
+
+ def PreprocessPolicies(self, policy_list):
+ return self.FlattenGroupsAndSortPolicies(policy_list)
+
+ def CanBeRecommended(self, policy):
+ return False
+
+ def WritePolicy(self, policy):
+ name = policy['name']
+ self.AddStringResource(name + 'Title', policy['caption'])
+
+ # Get the policy description.
+ description = policy['desc']
+ self.AddStringResource(name + 'Desc', description)
+
+ items = policy.get('items')
+ if items is not None:
+ items = [
+ item for item in items
+ if ('supported_on' not in item or
+ self.IsPolicyOrItemSupportedOnPlatform(item, 'android'))
+ ]
+ entries = [item['caption'] for item in items]
+ values = [item['value'] for item in items]
+ self.AddStringArrayResource(name + 'Entries', entries)
+ self.AddStringArrayResource(name + 'Values', values)
+
+ def BeginTemplate(self):
+ comment_text = 'DO NOT MODIFY THIS FILE DIRECTLY!\n' \
+ 'IT IS GENERATED FROM policy_templates.json.'
+ if self._GetChromiumVersionString():
+ comment_text += '\n' + self.config['build'] + ' version: '\
+ + self._GetChromiumVersionString()
+ comment_node = self._doc.createComment(comment_text)
+ self._doc.insertBefore(comment_node, self._resources)
+
+ def Init(self):
+ impl = minidom.getDOMImplementation()
+ self._doc = impl.createDocument(None, 'resources', None)
+ self._resources = self._doc.documentElement
+
+ def GetTemplateText(self):
+ return self.ToPrettyXml(self._doc)
diff --git a/chromium/components/policy/tools/template_writers/writers/android_policy_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/android_policy_writer_unittest.py
new file mode 100755
index 00000000000..a282bc5aab8
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/android_policy_writer_unittest.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015 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.
+'''Unit tests for writers.android_policy_writer'''
+
+import os
+import sys
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+import unittest
+from xml.dom import minidom
+
+from writers import writer_unittest_common
+from writers import android_policy_writer
+
+
+class AndroidPolicyWriterUnittest(writer_unittest_common.WriterUnittestCommon):
+ '''Unit tests to test assumptions in Android Policy Writer'''
+
+ def testPolicyWithoutItems(self):
+ # Test an example policy without items.
+ policy = {
+ 'name': '_policy_name',
+ 'caption': '_policy_caption',
+ 'desc': 'This is a long policy caption. More than one sentence '
+ 'in a single line because it is very important.\n'
+ 'Second line, also important'
+ }
+ writer = android_policy_writer.GetWriter({})
+ writer.Init()
+ writer.BeginTemplate()
+ writer.WritePolicy(policy)
+ self.assertEquals(
+ writer._resources.toxml(), '<resources>'
+ '<string name="_policy_nameTitle">_policy_caption</string>'
+ '<string name="_policy_nameDesc">This is a long policy caption. More '
+ 'than one sentence in a single line because it is very '
+ 'important.\nSecond line, also important'
+ '</string>'
+ '</resources>')
+
+ def testPolicyWithItems(self):
+ # Test an example policy without items.
+ policy = {
+ 'name':
+ '_policy_name',
+ 'caption':
+ '_policy_caption',
+ 'desc':
+ '_policy_desc_first.\nadditional line',
+ 'items': [{
+ 'caption': '_caption1',
+ 'value': '_value1',
+ }, {
+ 'caption': '_caption2',
+ 'value': '_value2',
+ },
+ {
+ 'caption': '_caption3',
+ 'value': '_value3',
+ 'supported_on': [{
+ 'platform': 'win'
+ }, {
+ 'platform': 'win7'
+ }]
+ },
+ {
+ 'caption':
+ '_caption4',
+ 'value':
+ '_value4',
+ 'supported_on': [{
+ 'platform': 'android'
+ }, {
+ 'platform': 'win7'
+ }]
+ }]
+ }
+ writer = android_policy_writer.GetWriter({})
+ writer.Init()
+ writer.BeginTemplate()
+ writer.WritePolicy(policy)
+ self.assertEquals(
+ writer._resources.toxml(), '<resources>'
+ '<string name="_policy_nameTitle">_policy_caption</string>'
+ '<string name="_policy_nameDesc">_policy_desc_first.\n'
+ 'additional line</string>'
+ '<string-array name="_policy_nameEntries">'
+ '<item>_caption1</item>'
+ '<item>_caption2</item>'
+ '<item>_caption4</item>'
+ '</string-array>'
+ '<string-array name="_policy_nameValues">'
+ '<item>_value1</item>'
+ '<item>_value2</item>'
+ '<item>_value4</item>'
+ '</string-array>'
+ '</resources>')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/chromeos_adml_writer.py b/chromium/components/policy/tools/template_writers/writers/chromeos_adml_writer.py
new file mode 100755
index 00000000000..72e4dfc5832
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/chromeos_adml_writer.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+# 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 base64
+
+from writers import adml_writer
+from writers.admx_writer import AdmxElementType
+
+
+def GetWriter(config):
+ '''Factory method for creating ADMLWriter objects for the Chrome OS platform.
+ See the constructor of TemplateWriter for description of arguments.
+ '''
+ return ChromeOSADMLWriter(['chrome_os'], config)
+
+
+class ChromeOSADMLWriter(adml_writer.ADMLWriter):
+ ''' Class for generating Chrome OS ADML policy templates. It is used by the
+ PolicyTemplateGenerator to write the ADML file.
+ '''
+
+ # Overridden.
+ # These ADML files are used to generate GPO for Active Directory managed
+ # Chrome OS devices.
+ def IsPolicySupported(self, policy):
+ return self.IsCrOSManagementSupported(policy, 'active_directory') and \
+ super(ChromeOSADMLWriter, self).IsPolicySupported(policy)
+
+ # Overridden.
+ def _GetAdmxElementType(self, policy):
+ return AdmxElementType.GetType(policy, allow_multi_strings=True)
diff --git a/chromium/components/policy/tools/template_writers/writers/chromeos_adml_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/chromeos_adml_writer_unittest.py
new file mode 100755
index 00000000000..f07d299270d
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/chromeos_adml_writer_unittest.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+# 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.
+"""Unittests for writers.chromeos_adml_writer."""
+
+import os
+import sys
+import unittest
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+from writers import chromeos_adml_writer
+from writers import adml_writer_unittest
+from writers.admx_writer import AdmxElementType
+
+
+class ChromeOsAdmlWriterUnittest(adml_writer_unittest.AdmlWriterUnittest):
+
+ # Overridden.
+ def _GetWriter(self, config):
+ return chromeos_adml_writer.GetWriter(config)
+
+ # Overridden
+ def GetCategory(self):
+ return "cros_test_category"
+
+ # Overridden
+ def GetCategoryString(self):
+ return "CrOSTestCategory"
+
+ # Overridden.
+ def testPlatform(self):
+ # Test that the writer correctly chooses policies of platform Chrome OS.
+ self.assertTrue(
+ self.writer.IsPolicySupported({
+ 'supported_on': [{
+ 'platform': 'chrome_os'
+ }, {
+ 'platform': 'aaa'
+ }]
+ }))
+ self.assertFalse(
+ self.writer.IsPolicySupported({
+ 'supported_on': [{
+ 'platform': 'win'
+ }, {
+ 'platform': 'aaa'
+ }]
+ }))
+
+ def testOnlySupportsAdPolicies(self):
+ # Tests whether only Active Directory managed policies are supported (Google
+ # cloud only managed polices are not put in the ADMX file).
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'supported_on': [{
+ 'product': 'chrome_os',
+ 'platform': 'chrome_os',
+ 'since_version': '8',
+ 'until_version': '',
+ }],
+ }
+ self.assertTrue(self.writer.IsPolicySupported(policy))
+
+ policy['supported_chrome_os_management'] = ['google_cloud']
+ self.assertFalse(self.writer.IsPolicySupported(policy))
+
+ policy['supported_chrome_os_management'] = ['active_directory']
+ self.assertTrue(self.writer.IsPolicySupported(policy))
+
+ # Overridden.
+ def testDictionaryPolicy(self, is_external=False):
+ dict_policy = {
+ 'name': 'DictionaryPolicyStub',
+ 'type': 'external' if is_external else 'dict',
+ 'caption': 'Dictionary policy caption',
+ 'label': 'Dictionary policy label',
+ 'desc': 'This is a test description.',
+ }
+ self._InitWriterForAddingPolicies(self.writer, dict_policy)
+ self.writer.WritePolicy(dict_policy)
+ # Assert generated string elements.
+ output = self.GetXMLOfChildren(self.writer._string_table_elem)
+ expected_output = (
+ '<string id="DictionaryPolicyStub">Dictionary policy caption</string>\n'
+ '<string id="DictionaryPolicyStub_Explain">'
+ 'This is a test description.\n'
+ 'See https://cloud.google.com/docs/chrome-enterprise/policies/?policy='
+ 'DictionaryPolicyStub\n</string>\n'
+ '<string id="DictionaryPolicyStub_Legacy">'
+ 'Dictionary policy label (deprecated)</string>')
+ self.AssertXMLEquals(output, expected_output)
+ # Assert generated presentation elements.
+ output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+ expected_output = (
+ '<presentation id="DictionaryPolicyStub">\n'
+ ' <textBox refId="DictionaryPolicyStub_Legacy">\n'
+ ' <label>Dictionary policy label (deprecated)</label>\n'
+ ' </textBox>\n'
+ ' <multiTextBox defaultHeight="8" refId="DictionaryPolicyStub">'
+ 'Dictionary policy label</multiTextBox>\n'
+ '</presentation>')
+ self.AssertXMLEquals(output, expected_output)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/chromeos_admx_writer.py b/chromium/components/policy/tools/template_writers/writers/chromeos_admx_writer.py
new file mode 100755
index 00000000000..c9f1c2c5052
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/chromeos_admx_writer.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+# 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 base64
+
+from writers import admx_writer
+from writers.admx_writer import AdmxElementType
+
+
+def GetWriter(config):
+ '''Factory method for creating ADMXWriter objects for the Chrome OS platform
+ See the constructor of TemplateWriter for description of arguments.
+ '''
+ return ChromeOSADMXWriter(['chrome_os'], config)
+
+
+class ChromeOSADMXWriter(admx_writer.ADMXWriter):
+ '''Class for generating Chrome OS policy templates in the ADMX format.
+ It is used by PolicyTemplateGenerator to write ADMX files.
+ '''
+
+ # Overridden.
+ def GetClass(self, policy):
+ is_device_only = 'device_only' in policy and policy['device_only']
+ return 'Machine' if is_device_only else 'User'
+
+ # Overridden.
+ # These ADMX templates are used to generate GPO for Active Directory managed
+ # Chrome OS devices.
+ def IsPolicySupported(self, policy):
+ return self.IsCrOSManagementSupported(policy, 'active_directory') and \
+ super(ChromeOSADMXWriter, self).IsPolicySupported(policy)
+
+ # Overridden.
+ def _GetAdmxElementType(self, policy):
+ return AdmxElementType.GetType(policy, allow_multi_strings=True)
diff --git a/chromium/components/policy/tools/template_writers/writers/chromeos_admx_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/chromeos_admx_writer_unittest.py
new file mode 100755
index 00000000000..b6b4f83a426
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/chromeos_admx_writer_unittest.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python3
+# 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.
+"""Unittests for writers.chromeos_admx_writer."""
+
+import os
+import sys
+import unittest
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+from writers import chromeos_admx_writer
+from writers import admx_writer_unittest
+from writers.admx_writer import AdmxElementType
+
+
+class ChromeOsAdmxWriterUnittest(admx_writer_unittest.AdmxWriterUnittest):
+
+ # Overridden.
+ def _GetWriter(self, config):
+ return chromeos_admx_writer.GetWriter(config)
+
+ # Overridden.
+ def _GetKey(self):
+ return "CrOSTest"
+
+ # Overridden.
+ def _GetCategory(self):
+ return "cros_test_category"
+
+ # Overridden.
+ def _GetCategoryRec(self):
+ return "cros_test_recommended_category"
+
+ # Overridden.
+ def _GetNamespace(self):
+ return "ADMXWriter.Test.Namespace.ChromeOS"
+
+ # Overridden.
+ def testPlatform(self):
+ # Test that the writer correctly chooses policies of platform Chrome OS.
+ self.assertTrue(
+ self.writer.IsPolicySupported({
+ 'supported_on': [{
+ 'platform': 'chrome_os'
+ }, {
+ 'platform': 'aaa'
+ }]
+ }))
+ self.assertFalse(
+ self.writer.IsPolicySupported({
+ 'supported_on': [{
+ 'platform': 'win'
+ }, {
+ 'platform': 'aaa'
+ }]
+ }))
+
+ def testUserPolicy(self):
+ self.doTestUserOrDevicePolicy(False)
+
+ def testDevicePolicy(self):
+ self.doTestUserOrDevicePolicy(True)
+
+ def doTestUserOrDevicePolicy(self, is_device_only):
+ # Tests whether CLASS attribute is 'User' for user policies and 'Machine'
+ # for device policies.
+ main_policy = {
+ 'name': 'DummyMainPolicy',
+ 'type': 'main',
+ 'device_only': is_device_only,
+ }
+
+ expected_class = 'Machine' if is_device_only else 'User'
+
+ self._initWriterForPolicy(self.writer, main_policy)
+ self.writer.WritePolicy(main_policy)
+
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = ('<policy class="' + expected_class + '"'
+ ' displayName="$(string.DummyMainPolicy)"'
+ ' explainText="$(string.DummyMainPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="DummyMainPolicy"'
+ ' presentation="$(presentation.DummyMainPolicy)"'
+ ' valueName="DummyMainPolicy">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <enabledValue>\n'
+ ' <decimal value="1"/>\n'
+ ' </enabledValue>\n'
+ ' <disabledValue>\n'
+ ' <decimal value="0"/>\n'
+ ' </disabledValue>\n'
+ '</policy>')
+
+ self.AssertXMLEquals(output, expected_output)
+
+ def testOnlySupportsAdPolicies(self):
+ # Tests whether only Active Directory managed policies are supported (Google
+ # cloud only managed polices are not put in the ADMX file).
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'supported_on': [{
+ 'product': 'chrome_os',
+ 'platform': 'chrome_os',
+ 'since_version': '8',
+ 'until_version': '',
+ }],
+ }
+ self.assertTrue(self.writer.IsPolicySupported(policy))
+
+ policy['supported_chrome_os_management'] = ['google_cloud']
+ self.assertFalse(self.writer.IsPolicySupported(policy))
+
+ policy['supported_chrome_os_management'] = ['active_directory']
+ self.assertTrue(self.writer.IsPolicySupported(policy))
+
+ #Overridden
+ def testDictionaryPolicy(self, is_external=False):
+ dict_policy = {
+ 'name': 'SampleDictionaryPolicy',
+ 'type': 'external' if is_external else 'dict',
+ }
+ self._initWriterForPolicy(self.writer, dict_policy)
+
+ self.writer.WritePolicy(dict_policy)
+ output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+ expected_output = (
+ '<policy class="' + self.writer.GetClass(dict_policy) + '"'
+ ' displayName="$(string.SampleDictionaryPolicy)"'
+ ' explainText="$(string.SampleDictionaryPolicy_Explain)"'
+ ' key="Software\\Policies\\' + self._GetKey() + '"'
+ ' name="SampleDictionaryPolicy"'
+ ' presentation="$(presentation.SampleDictionaryPolicy)">\n'
+ ' <parentCategory ref="PolicyGroup"/>\n'
+ ' <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+ ' <elements>\n'
+ ' <text id="SampleDictionaryPolicy_Legacy" maxLength="1000000"'
+ ' valueName="SampleDictionaryPolicy"/>\n'
+ ' <multiText id="SampleDictionaryPolicy" maxLength="1000000"'
+ ' valueName="SampleDictionaryPolicy"/>\n'
+ ' </elements>\n'
+ '</policy>')
+ self.AssertXMLEquals(output, expected_output)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/doc_atomic_groups_writer.py b/chromium/components/policy/tools/template_writers/writers/doc_atomic_groups_writer.py
new file mode 100755
index 00000000000..9f92347a6cb
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/doc_atomic_groups_writer.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+# Copyright 2019 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.
+
+from writers import doc_writer
+
+
+def GetWriter(config):
+ '''Factory method for creating DocAtomicGroupsWriter objects.
+ See the constructor of TemplateWriter for description of
+ arguments.
+ '''
+ return DocAtomicGroupsWriter(['*'], config)
+
+
+class DocAtomicGroupsWriter(doc_writer.DocWriter):
+ '''Class for generating atomic policy group templates in HTML format.
+ The intended use of the generated file is to upload it on
+ https://www.chromium.org, therefore its format has some limitations:
+ - No HTML and body tags.
+ - Restricted set of element attributes: for example no 'class'.
+ Because of the latter the output is styled using the 'style'
+ attributes of HTML elements. This is supported by the dictionary
+ self._STYLES[] and the method self._AddStyledElement(), they try
+ to mimic the functionality of CSS classes. (But without inheritance.)
+
+ This class is invoked by PolicyTemplateGenerator to create the HTML
+ files.
+ '''
+
+ def _AddPolicyRow(self, parent, policy):
+ '''Adds a row for the policy in the summary table.
+
+ Args:
+ parent: The DOM node of the summary table.
+ policy: The data structure of the policy.
+ '''
+ tr = self._AddStyledElement(parent, 'tr', ['tr'])
+ indent = 'padding-left: %dpx;' % (7 + self._indent_level * 14)
+ if policy['type'] != 'group':
+ # Normal policies get two columns with name and caption.
+ name_td = self._AddStyledElement(tr, 'td', ['td', 'td.left'],
+ {'style': indent})
+ policy_ref = './'
+ if self.config.get('local', False):
+ policy_ref = './chrome_policy_list.html'
+ self.AddElement(name_td, 'a', {'href': policy_ref + '#' + policy['name']},
+ policy['name'])
+ self._AddStyledElement(tr, 'td', ['td', 'td.right'], {},
+ policy['caption'])
+ else:
+ # Groups get one column with caption.
+ name_td = self._AddStyledElement(tr, 'td', ['td', 'td.left'], {
+ 'style': indent,
+ 'colspan': '2'
+ })
+ self.AddElement(name_td, 'a', {'name': policy['name']}, policy['caption'])
+
+ #
+ # Implementation of abstract methods of TemplateWriter:
+ #
+
+ def WritePolicy(self, policy):
+ self._AddPolicyRow(self._summary_tbody, policy)
+
+ def BeginTemplate(self):
+ self._BeginTemplate('group_intro', 'banner')
+
+ def WriteTemplate(self, template):
+ '''Writes the given template definition.
+
+ Args:
+ template: Template definition to write.
+
+ Returns:
+ Generated output for the passed template definition.
+ '''
+ self.messages = template['messages']
+ self.Init()
+
+ policies = self.PreprocessPolicies(
+ template['policy_atomic_group_definitions'])
+
+ self.BeginTemplate()
+ for policy in policies:
+ if policy['type'] != 'atomic_group':
+ continue
+ self.BeginPolicyGroup(policy)
+ for child_policy in policy['policies']:
+ # Nesting of groups is currently not supported.
+ self.WritePolicy(child_policy)
+ self.EndPolicyGroup()
+ self.EndTemplate()
+
+ return self.GetTemplateText()
diff --git a/chromium/components/policy/tools/template_writers/writers/doc_writer.py b/chromium/components/policy/tools/template_writers/writers/doc_writer.py
new file mode 100755
index 00000000000..c5a0104bc9e
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/doc_writer.py
@@ -0,0 +1,883 @@
+#!/usr/bin/env python3
+# Copyright 2019 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 json
+import re
+from xml.dom import minidom
+from xml.sax.saxutils import escape
+from writers import xml_formatted_writer
+
+
+def GetWriter(config):
+ '''Factory method for creating DocWriter objects.
+ See the constructor of TemplateWriter for description of
+ arguments.
+ '''
+ return DocWriter(['*'], config)
+
+
+class DocWriter(xml_formatted_writer.XMLFormattedWriter):
+ '''Class for generating policy templates in HTML format.
+ The intended use of the generated file is to upload it on
+ http://dev.chromium.org, therefore its format has some limitations:
+ - No HTML and body tags.
+ - Restricted set of element attributes: for example no 'class'.
+ Because of the latter the output is styled using the 'style'
+ attributes of HTML elements. This is supported by the dictionary
+ self._STYLES[] and the method self._AddStyledElement(), they try
+ to mimic the functionality of CSS classes. (But without inheritance.)
+
+ This class is invoked by PolicyTemplateGenerator to create the HTML
+ files.
+ '''
+
+ def _AddTextWithLinks(self, parent, text):
+ '''Parse a string for URLs and add it to a DOM node with the URLs replaced
+ with <a> HTML links.
+
+ Args:
+ parent: The DOM node to which the text will be added.
+ text: The string to be added.
+ '''
+ # A simple regexp to search for URLs. It is enough for now.
+ url_matcher = re.compile('(https?://[^\\s]*[^\\s\\.\\)\\"])')
+
+ # Iterate through all the URLs and replace them with links.
+ while True:
+ # Look for the first URL.
+ res = url_matcher.search(text)
+ if not res:
+ break
+ # Calculate positions of the substring of the URL.
+ url = res.group(0)
+ start = res.start(0)
+ end = res.end(0)
+ # Add the text prior to the URL.
+ self.AddText(parent, text[:start])
+ # Add a link for the URL.
+ self.AddElement(parent, 'a', {'href': url}, url)
+ # Drop the part of text that is added.
+ text = text[end:]
+ self.AddText(parent, text)
+
+ def _AddParagraphs(self, parent, text):
+ '''Break description into paragraphs and replace URLs with links.
+
+ Args:
+ parent: The DOM node to which the text will be added.
+ text: The string to be added.
+ '''
+ # Split text into list of paragraphs.
+ entries = text.split('\n\n')
+ for entry in entries:
+ # Create a new paragraph node.
+ paragraph = self.AddElement(parent, 'p')
+ # Insert text to the paragraph with processing the URLs.
+ self._AddTextWithLinks(paragraph, entry)
+
+ def _AddStyledElement(self, parent, name, style_ids, attrs=None, text=None):
+ '''Adds an XML element to a parent, with CSS style-sheets included.
+
+ Args:
+ parent: The parent DOM node.
+ name: Name of the element to add.
+ style_ids: A list of CSS style strings from self._STYLE[].
+ attrs: Dictionary of attributes for the element.
+ text: Text content for the element.
+ '''
+ if attrs == None:
+ attrs = {}
+
+ style = ''.join([self._STYLE[x] for x in style_ids])
+ if style != '':
+ # Apply the style specified by style_ids.
+ attrs['style'] = style + attrs.get('style', '')
+ return self.AddElement(parent, name, attrs, text)
+
+ def _AddDescription(self, parent, policy):
+ '''Adds a string containing the description of the policy. URLs are
+ replaced with links and the possible choices are enumerated in case
+ of 'string-enum' and 'int-enum' type policies.
+
+ Args:
+ parent: The DOM node for which the feature list will be added.
+ policy: The data structure of a policy.
+ '''
+ # Add description by paragraphs (URLs will be substituted by links).
+ self._AddParagraphs(parent, policy['desc'])
+ # Add list of enum items.
+ if policy['type'] in ('string-enum', 'int-enum', 'string-enum-list'):
+ ul = self.AddElement(parent, 'ul')
+ for item in policy['items']:
+ if policy['type'] == 'int-enum':
+ value_string = str(item['value'])
+ else:
+ value_string = '"%s"' % item['value']
+ self.AddElement(ul, 'li', {},
+ '%s = %s' % (value_string, item['caption']))
+
+ def _AddSchema(self, parent, schema):
+ '''Adds a schema to a DOM node.
+
+ Args:
+ parent: The DOM node for which the schema will be added.
+ schema: The schema of a policy.
+ '''
+ dd = self._AddPolicyAttribute(parent, 'schema', None,
+ ['.monospace', '.pre-wrap'])
+ # Explicitly specify separators since defaults depend on python version.
+ schema_json = json.dumps(schema,
+ indent=2,
+ sort_keys=True,
+ separators=(", ", ": "))
+ self.AddText(dd, schema_json)
+
+ def _AddFeatures(self, parent, policy):
+ '''Adds a string containing the list of supported features of a policy
+ to a DOM node. The text will look like as:
+ Feature_X: Yes, Feature_Y: No
+
+ Args:
+ parent: The DOM node for which the feature list will be added.
+ policy: The data structure of a policy.
+ '''
+ features = []
+ # The sorting is to make the order well-defined for testing.
+ keys = sorted(policy['features'].keys())
+ for key in keys:
+ key_name = self._FEATURE_MAP[key]
+ if policy['features'][key]:
+ value_name = self.GetLocalizedMessage('supported')
+ else:
+ value_name = self.GetLocalizedMessage('not_supported')
+ features.append('%s: %s' % (key_name, value_name))
+ self.AddText(parent, ', '.join(features))
+
+ def _AddListExampleMac(self, parent, policy):
+ '''Adds an example value for Mac of a 'list' policy to a DOM node.
+
+ Args:
+ parent: The DOM node for which the example will be added.
+ policy: A policy of type 'list', for which the Mac example value
+ is generated.
+ '''
+ example_value = policy['example_value']
+ self.AddElement(parent, 'dt', {}, 'Mac:')
+ mac = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre-wrap'])
+
+ mac_text = ['<array>']
+ for item in example_value:
+ mac_text.append(' <string>%s</string>' % item)
+ mac_text.append('</array>')
+ self.AddText(mac, '\n'.join(mac_text))
+
+ def _AddListExampleWindowsChromeOS(self, parent, policy, is_win):
+ '''Adds an example value for Windows or Chromium/Google Chrome OS of a
+ 'list' policy to a DOM node.
+
+ Args:
+ parent: The DOM node for which the example will be added.
+ policy: A policy of type 'list', for which the Windows example value
+ is generated.
+ is_win: True for Windows, False for Chromium/Google Chrome OS.
+ '''
+ example_value = policy['example_value']
+ os_header = self.GetLocalizedMessage('win_example_value') if is_win else \
+ self.GetLocalizedMessage('chrome_os_example_value')
+ self.AddElement(parent, 'dt', {}, os_header)
+ element = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre-wrap'])
+ element_text = []
+ cnt = 1
+ key_name = self._GetRegistryKeyName(policy, is_win)
+ for item in example_value:
+ element_text.append(
+ '%s\\%s\\%d = "%s"' % (key_name, policy['name'], cnt, item))
+ cnt = cnt + 1
+ self.AddText(element, '\n'.join(element_text))
+
+ def _GetRegistryKeyName(self, policy, is_win):
+ use_recommended_key = self.CanBeRecommended(policy) and not \
+ self.CanBeMandatory(policy)
+ platform = 'win' if is_win else 'chrome_os'
+ key = 'reg_recommended_key_name' if use_recommended_key else \
+ 'reg_mandatory_key_name'
+ return self.config['win_config'][platform][key]
+
+ def _GetOmaUriPath(self, policy):
+ product = 'googlechrome' if self.config['build'] == 'chrome' else 'chromium'
+ group = '~' + policy['group'] if 'group' in policy else ''
+ return '.\\Device\\Vendor\\MSFT\\Policy\\Config\\Chrome~Policy~%s%s\\%s' % (
+ product, group, policy['name'])
+
+ def _AddListExampleAndroidLinux(self, parent, policy):
+ '''Adds an example value for Android/Linux of a 'list' policy to a DOM node.
+
+ Args:
+ parent: The DOM node for which the example will be added.
+ policy: A policy of type 'list', for which the Android/Linux example value
+ is generated.
+ '''
+ example_value = policy['example_value']
+ self.AddElement(parent, 'dt', {}, 'Android/Linux:')
+ element = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre-wrap'])
+ self.AddText(
+ element,
+ '[\n%s\n]' % ',\n'.join(' "%s"' % item for item in example_value))
+
+ def _AddListExample(self, parent, policy):
+ r'''Adds the example value of a 'list' policy to a DOM node. Example output:
+ <dl>
+ <dt>Windows (Windows clients):</dt>
+ <dd>
+ Software\Policies\Chromium\URLAllowlist\0 = "www.example.com"
+ Software\Policies\Chromium\URLAllowlist\1 = "www.google.com"
+ </dd>
+ <dt>Windows (Chromium OS clients):</dt>
+ <dd>
+ Software\Policies\ChromiumOS\URLAllowlist\0 = "www.example.com"
+ Software\Policies\ChromiumOS\URLAllowlist\1 = "www.google.com"
+ </dd>
+ <dt>Android/Linux:</dt>
+ <dd>
+ [
+ "www.example.com",
+ "www.google.com"
+ ]
+ </dd>
+ <dt>Mac:</dt>
+ <dd>
+ <array>
+ <string>www.example.com</string>
+ <string>www.google.com</string>
+ </array>
+ </dd>
+ </dl>
+
+ Args:
+ parent: The DOM node for which the example will be added.
+ policy: The data structure of a policy.
+ '''
+ examples = self._AddStyledElement(parent, 'dl', ['dd dl'])
+ if self.IsPolicySupportedOnWindows(policy):
+ self._AddListExampleWindowsChromeOS(examples, policy, True)
+ if self.IsPolicyOrItemSupportedOnPlatform(
+ policy, 'chrome_os', management='active_directory'):
+ self._AddListExampleWindowsChromeOS(examples, policy, False)
+ if (self.IsPolicyOrItemSupportedOnPlatform(policy, 'android') or
+ self.IsPolicyOrItemSupportedOnPlatform(policy, 'linux')):
+ self._AddListExampleAndroidLinux(examples, policy)
+ if self.IsPolicyOrItemSupportedOnPlatform(policy, 'mac'):
+ self._AddListExampleMac(examples, policy)
+
+ def _PythonObjectToPlist(self, obj, indent=''):
+ '''Converts a python object to an equivalent XML plist.
+
+ Returns a list of lines.'''
+ obj_type = type(obj)
+ if obj_type == bool:
+ return ['%s<%s/>' % (indent, 'true' if obj else 'false')]
+ elif obj_type == int:
+ return ['%s<integer>%s</integer>' % (indent, obj)]
+ elif obj_type == str:
+ return ['%s<string>%s</string>' % (indent, escape(obj))]
+ elif obj_type == list:
+ result = ['%s<array>' % indent]
+ for item in obj:
+ result += self._PythonObjectToPlist(item, indent + ' ')
+ result.append('%s</array>' % indent)
+ return result
+ elif obj_type == dict:
+ result = ['%s<dict>' % indent]
+ for key in sorted(obj.keys()):
+ result.append('%s<key>%s</key>' % (indent + ' ', key))
+ result += self._PythonObjectToPlist(obj[key], indent + ' ')
+ result.append('%s</dict>' % indent)
+ return result
+ else:
+ raise Exception('Invalid object to convert: %s' % obj)
+
+ def _AddDictionaryExampleMac(self, parent, policy):
+ '''Adds an example value for Mac of a 'dict' or 'external' policy to a DOM
+ node.
+
+ Args:
+ parent: The DOM node for which the example will be added.
+ policy: A policy of type 'dict', for which the Mac example value
+ is generated.
+ '''
+ example_value = policy['example_value']
+ self.AddElement(parent, 'dt', {}, 'Mac:')
+ mac = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre-wrap'])
+ mac_text = ['<key>%s</key>' % (policy['name'])]
+ mac_text += self._PythonObjectToPlist(example_value)
+ self.AddText(mac, '\n'.join(mac_text))
+
+ def _AddDictionaryExampleWindowsChromeOS(self, parent, policy, is_win):
+ '''Adds an example value for Windows of a 'dict' or 'external' policy to a
+ DOM node.
+
+ Args:
+ parent: The DOM node for which the example will be added.
+ policy: A policy of type 'dict', for which the Windows example value
+ is generated.
+ '''
+ os_header = self.GetLocalizedMessage('win_example_value') if is_win else \
+ self.GetLocalizedMessage('chrome_os_example_value')
+ self.AddElement(parent, 'dt', {}, os_header)
+ element = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre-wrap'])
+ key_name = self._GetRegistryKeyName(policy, is_win)
+ # Explicitly specify separators since defaults depend on python version.
+ example = json.dumps(policy['example_value'],
+ indent=2,
+ sort_keys=True,
+ separators=(", ", ": "))
+ self.AddText(element, '%s\\%s = %s' % (key_name, policy['name'], example))
+
+ def _AddDictionaryExampleAndroidLinux(self, parent, policy):
+ '''Adds an example value for Android/Linux of a 'dict' or 'external' policy
+ to a DOM node.
+
+ Args:
+ parent: The DOM node for which the example will be added.
+ policy: A policy of type 'dict', for which the Android/Linux example value
+ is generated.
+ '''
+ self.AddElement(parent, 'dt', {}, 'Android/Linux:')
+ element = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre-wrap'])
+ # Explicitly specify separators since defaults depend on python version.
+ example = json.dumps(policy['example_value'],
+ indent=2,
+ sort_keys=True,
+ separators=(", ", ": "))
+ self.AddText(element, '%s: %s' % (policy['name'], example))
+
+ def _AddDictionaryExample(self, parent, policy):
+ '''Adds the example value of a 'dict' or 'external' policy to a DOM node.
+
+ Example output:
+ <dl>
+ <dt>Windows (Windows clients):</dt>
+ <dd>
+ Software\Policies\Chromium\ProxySettings = {
+ "ProxyMode": "direct"
+ }
+ </dd>
+ <dt>Windows (Chromium OS clients):</dt>
+ <dd>
+ Software\Policies\ChromiumOS\ProxySettings = {
+ "ProxyMode": "direct"
+ }
+ </dd>
+ <dt>Android/Linux:</dt>
+ <dd>
+ ProxySettings: {
+ "ProxyMode": "direct"
+ }
+ </dd>
+ <dt>Mac:</dt>
+ <dd>
+ <key>ProxySettings</key>
+ <dict>
+ <key>ProxyMode</key>
+ <string>direct</string>
+ </dict>
+ </dd>
+ </dl>
+
+ Args:
+ parent: The DOM node for which the example will be added.
+ policy: The data structure of a policy.
+ '''
+ examples = self._AddStyledElement(parent, 'dl', ['dd dl'])
+ if self.IsPolicySupportedOnWindows(policy):
+ self._AddDictionaryExampleWindowsChromeOS(examples, policy, True)
+ if self.IsPolicyOrItemSupportedOnPlatform(
+ policy, 'chrome_os', management='active_directory'):
+ self._AddDictionaryExampleWindowsChromeOS(examples, policy, False)
+ if (self.IsPolicyOrItemSupportedOnPlatform(policy, 'android') or
+ self.IsPolicyOrItemSupportedOnPlatform(policy, 'linux')):
+ self._AddDictionaryExampleAndroidLinux(examples, policy)
+ if self.IsPolicyOrItemSupportedOnPlatform(policy, 'mac'):
+ self._AddDictionaryExampleMac(examples, policy)
+
+ def _AddIntuneExample(self, parent, policy):
+ example_value = policy['example_value']
+ policy_type = policy['type']
+
+ container = self._AddStyledElement(parent, 'dl', [])
+ self.AddElement(container, 'dt', {}, 'Windows (Intune):')
+
+ if policy_type == 'main':
+ self._AddStyledElement(
+ container,
+ 'dd', ['.monospace', '.pre-wrap'],
+ text='<enabled/>' if example_value else '<disabled/>')
+ return
+
+ self._AddStyledElement(
+ container, 'dd', ['.monospace', '.pre-wrap'], text='<enabled/>')
+ if policy_type == 'list':
+ values = [
+ '%s&#xF000;%s' % (index, value)
+ for index, value in enumerate(example_value, start=1)
+ ]
+ self._AddStyledElement(
+ container,
+ 'dd', ['.monospace', '.pre-wrap'],
+ text='<data id="%s" value="%s"/>' % (policy['name'] + 'Desc',
+ '&#xF000;'.join(values)))
+ return
+ elif policy_type == 'int' or policy_type == 'int-enum':
+ self._AddStyledElement(
+ container,
+ 'dd', ['.monospace', '.pre-wrap'],
+ text='<data id="%s" value="%s"/>' % (policy['name'], example_value))
+ else:
+ self._AddStyledElement(
+ container,
+ 'dd', ['.monospace', '.pre-wrap'],
+ text='<data id="%s" value="%s"/>' % (policy['name'],
+ json.dumps(example_value)[1:-1]))
+ return
+
+ def _AddExample(self, parent, policy):
+ '''Adds the HTML DOM representation of the example value of a policy to
+ a DOM node. It is simple text for boolean policies, like
+ '0x00000001 (Windows), true (Linux), true (Android), <true /> (Mac)'
+ in case of boolean policies, but it may also contain other HTML elements.
+ (See method _AddListExample.)
+
+ Args:
+ parent: The DOM node for which the example will be added.
+ policy: The data structure of a policy.
+
+ Raises:
+ Exception: If the type of the policy is unknown or the example value
+ of the policy is out of its expected range.
+ '''
+ example_value = policy['example_value']
+ policy_type = policy['type']
+ if policy_type == 'main':
+ pieces = []
+ if self.IsPolicySupportedOnWindows(policy) or \
+ self.IsPolicyOrItemSupportedOnPlatform(policy, 'chrome_os',
+ management='active_directory'):
+ value = '0x00000001' if example_value else '0x00000000'
+ pieces.append(value + ' (Windows)')
+ if self.IsPolicyOrItemSupportedOnPlatform(policy, 'linux'):
+ value = 'true' if example_value else 'false'
+ pieces.append(value + ' (Linux)')
+ if self.IsPolicyOrItemSupportedOnPlatform(policy, 'android'):
+ value = 'true' if example_value else 'false'
+ pieces.append(value + ' (Android)')
+ if self.IsPolicyOrItemSupportedOnPlatform(policy, 'mac'):
+ value = '<true />' if example_value else '<false />'
+ pieces.append(value + ' (Mac)')
+ self.AddText(parent, ', '.join(pieces))
+ elif policy_type == 'string':
+ self.AddText(parent, '"%s"' % example_value)
+ elif policy_type in ('int', 'int-enum'):
+ pieces = []
+ if self.IsPolicySupportedOnWindows(policy) or \
+ self.IsPolicyOrItemSupportedOnPlatform(policy, 'chrome_os',
+ management='active_directory'):
+ pieces.append('0x%08x (Windows)' % example_value)
+ if self.IsPolicyOrItemSupportedOnPlatform(policy, 'linux'):
+ pieces.append('%d (Linux)' % example_value)
+ if self.IsPolicyOrItemSupportedOnPlatform(policy, 'android'):
+ pieces.append('%d (Android)' % example_value)
+ if self.IsPolicyOrItemSupportedOnPlatform(policy, 'mac'):
+ pieces.append('%d (Mac)' % example_value)
+ self.AddText(parent, ', '.join(pieces))
+ elif policy_type == 'string-enum':
+ self.AddText(parent, '"%s"' % (example_value))
+ elif policy_type in ('list', 'string-enum-list'):
+ self._AddListExample(parent, policy)
+ elif policy_type in ('dict', 'external'):
+ self._AddDictionaryExample(parent, policy)
+ else:
+ raise Exception('Unknown policy type: ' + policy_type)
+
+ if self.IsPolicySupportedOnWindows(policy):
+ self._AddIntuneExample(parent, policy)
+
+ def _AddPolicyAttribute(self,
+ dl,
+ term_id,
+ definition=None,
+ definition_style=None):
+ '''Adds a term-definition pair to a HTML DOM <dl> node. This method is
+ used by _AddPolicyDetails. Its result will have the form of:
+ <dt style="...">...</dt>
+ <dd style="...">...</dd>
+
+ Args:
+ dl: The DOM node of the <dl> list.
+ term_id: A key to self._STRINGS[] which specifies the term of the pair.
+ definition: The text of the definition. (Optional.)
+ definition_style: List of references to values self._STYLE[] that specify
+ the CSS stylesheet of the <dd> (definition) element.
+
+ Returns:
+ The DOM node representing the definition <dd> element.
+ '''
+ # Avoid modifying the default value of definition_style.
+ if definition_style == None:
+ definition_style = []
+ term = self.GetLocalizedMessage(term_id)
+ self._AddStyledElement(dl, 'dt', ['dt'], {}, term)
+ return self._AddStyledElement(dl, 'dd', definition_style, {}, definition)
+
+ def _AddSupportedOnList(self, parent, supported_on_list):
+ '''Creates a HTML list containing the platforms, products and versions
+ that are specified in the list of supported_on.
+
+ Args:
+ parent: The DOM node for which the list will be added.
+ supported_on_list: The list of supported products, as a list of
+ dictionaries.
+ '''
+ ul = self._AddStyledElement(parent, 'ul', ['ul'])
+ for supported_on in supported_on_list:
+ text = []
+ product = supported_on['product']
+ platform = supported_on['platform']
+ text.append(self._PRODUCT_MAP[product])
+ text.append('(%s)' % (self._PLATFORM_MAP[platform]))
+ if supported_on['since_version']:
+ since_version = self.GetLocalizedMessage('since_version')
+ text.append(since_version.replace('$6', supported_on['since_version']))
+ if supported_on['until_version']:
+ until_version = self.GetLocalizedMessage('until_version')
+ text.append(until_version.replace('$6', supported_on['until_version']))
+ # Add the list element:
+ self.AddElement(ul, 'li', {}, ' '.join(text))
+
+ def _AddRangeRestrictionsList(self, parent, schema):
+ '''Creates a HTML list containing range restrictions for an integer type
+ policy.
+
+ Args:
+ parent: The DOM node for which the list will be added.
+ schema: The schema of the policy.
+ '''
+ ul = self._AddStyledElement(parent, 'ul', ['ul'])
+ if 'minimum' in schema:
+ text_min = self.GetLocalizedMessage('range_minimum')
+ self.AddElement(ul, 'li', {}, text_min + str(schema['minimum']))
+ if 'maximum' in schema:
+ text_max = self.GetLocalizedMessage('range_maximum')
+ self.AddElement(ul, 'li', {}, text_max + str(schema['maximum']))
+
+ def _AddPolicyDetails(self, parent, policy):
+ '''Adds the list of attributes of a policy to the HTML DOM node parent.
+ It will have the form:
+ <dl>
+ <dt>Attribute:</dt><dd>Description</dd>
+ ...
+ </dl>
+
+ Args:
+ parent: A DOM element for which the list will be added.
+ policy: The data structure of the policy.
+ '''
+
+ dl = self.AddElement(parent, 'dl')
+ data_type = [self._TYPE_MAP[policy['type']]]
+ qualified_types = []
+ is_complex_policy = False
+ if (self.IsPolicyOrItemSupportedOnPlatform(policy, 'android') and
+ self._RESTRICTION_TYPE_MAP.get(policy['type'], None)):
+ qualified_types.append(
+ 'Android:%s' % self._RESTRICTION_TYPE_MAP[policy['type']])
+ if policy['type'] in ('dict', 'external', 'list'):
+ is_complex_policy = True
+ if ((self.IsPolicySupportedOnWindows(policy) or
+ self.IsPolicyOrItemSupportedOnPlatform(
+ policy, 'chrome_os', management='active_directory')) and
+ self._REG_TYPE_MAP.get(policy['type'], None)):
+ qualified_types.append('Windows:%s' % self._REG_TYPE_MAP[policy['type']])
+ if policy['type'] in ('dict', 'external'):
+ is_complex_policy = True
+ if qualified_types:
+ data_type.append('[%s]' % ', '.join(qualified_types))
+ if is_complex_policy:
+ data_type.append(
+ '(%s)' % self.GetLocalizedMessage('complex_policies_on_windows'))
+ self._AddPolicyAttribute(dl, 'data_type', ' '.join(data_type))
+ if self.IsPolicySupportedOnWindows(policy):
+ registry_key_name = self._GetRegistryKeyName(policy, True)
+ self._AddPolicyAttribute(dl, 'win_reg_loc',
+ registry_key_name + '\\' + policy['name'],
+ ['.monospace'])
+ self._AddPolicyAttribute(dl, 'oma_uri', self._GetOmaUriPath(policy),
+ ['.monospace'])
+
+ if self.IsPolicyOrItemSupportedOnPlatform(
+ policy, 'chrome_os', management='active_directory'):
+ key_name = self._GetRegistryKeyName(policy, False)
+ self._AddPolicyAttribute(dl, 'chrome_os_reg_loc',
+ key_name + '\\' + policy['name'], ['.monospace'])
+ if (self.IsPolicyOrItemSupportedOnPlatform(policy, 'linux') or
+ self.IsPolicyOrItemSupportedOnPlatform(policy, 'mac')):
+ self._AddPolicyAttribute(dl, 'mac_linux_pref_name', policy['name'],
+ ['.monospace'])
+ if self.IsPolicyOrItemSupportedOnPlatform(
+ policy, 'android', product='chrome'):
+ self._AddPolicyAttribute(dl, 'android_restriction_name', policy['name'],
+ ['.monospace'])
+ if self.IsPolicyOrItemSupportedOnPlatform(
+ policy, 'android', product='webview'):
+ restriction_prefix = self.config['android_webview_restriction_prefix']
+ self._AddPolicyAttribute(dl, 'android_webview_restriction_name',
+ restriction_prefix + policy['name'],
+ ['.monospace'])
+ dd = self._AddPolicyAttribute(dl, 'supported_on')
+ self._AddSupportedOnList(dd, policy['supported_on'])
+ dd = self._AddPolicyAttribute(dl, 'supported_features')
+ self._AddFeatures(dd, policy)
+ dd = self._AddPolicyAttribute(dl, 'description')
+ self._AddDescription(dd, policy)
+ if 'schema' in policy:
+ if self.SchemaHasRangeRestriction(policy['schema']):
+ dd = self._AddPolicyAttribute(dl, 'policy_restriction')
+ self._AddRangeRestrictionsList(dd, policy['schema'])
+ if 'arc_support' in policy:
+ dd = self._AddPolicyAttribute(dl, 'arc_support')
+ self._AddParagraphs(dd, policy['arc_support'])
+ if policy['type'] in ('dict', 'external') and 'schema' in policy:
+ self._AddSchema(dl, policy['schema'])
+ if 'validation_schema' in policy:
+ self._AddSchema(dl, policy['validation_schema'])
+ if 'description_schema' in policy:
+ self._AddSchema(dl, policy['description_schema'])
+ if 'url_schema' in policy:
+ dd = self._AddPolicyAttribute(dl, 'url_schema')
+ self._AddTextWithLinks(dd, policy['url_schema'])
+ if (self.IsPolicySupportedOnWindows(policy) or
+ self.IsPolicyOrItemSupportedOnPlatform(policy, 'linux') or
+ self.IsPolicyOrItemSupportedOnPlatform(policy, 'android') or
+ self.IsPolicyOrItemSupportedOnPlatform(policy, 'mac') or
+ self.IsPolicyOrItemSupportedOnPlatform(
+ policy, 'chrome_os', management='active_directory')):
+ # Don't add an example for Google cloud managed ChromeOS policies.
+ dd = self._AddPolicyAttribute(dl, 'example_value')
+ self._AddExample(dd, policy)
+ if 'atomic_group' in policy:
+ dd = self._AddPolicyAttribute(dl, 'policy_atomic_group')
+ policy_group_ref = './policy-list-3/atomic_groups'
+ if 'local' in self.config and self.config['local']:
+ policy_group_ref = './chrome_policy_atomic_groups_list.html'
+ self.AddText(dd, self.GetLocalizedMessage('policy_in_atomic_group') + ' ')
+ self.AddElement(dd, 'a',
+ {'href': policy_group_ref + '#' + policy['atomic_group']},
+ policy['atomic_group'])
+
+ def _AddPolicyRow(self, parent, policy):
+ '''Adds a row for the policy in the summary table.
+
+ Args:
+ parent: The DOM node of the summary table.
+ policy: The data structure of the policy.
+ '''
+ tr = self._AddStyledElement(parent, 'tr', ['tr'])
+ indent = 'padding-left: %dpx;' % (7 + self._indent_level * 14)
+ if policy['type'] != 'group':
+ # Normal policies get two columns with name and caption.
+ name_td = self._AddStyledElement(tr, 'td', ['td', 'td.left'],
+ {'style': indent})
+ self.AddElement(name_td, 'a', {'href': '#' + policy['name']},
+ policy['name'])
+ self._AddStyledElement(tr, 'td', ['td', 'td.right'], {},
+ policy['caption'])
+ else:
+ # Groups get one column with caption.
+ name_td = self._AddStyledElement(tr, 'td', ['td', 'td.left'], {
+ 'style': indent,
+ 'colspan': '2'
+ })
+ self.AddElement(name_td, 'a', {'href': '#' + policy['name']},
+ policy['caption'])
+
+ def _AddPolicySection(self, parent, policy):
+ '''Adds a section about the policy in the detailed policy listing.
+
+ Args:
+ parent: The DOM node of the <div> of the detailed policy list.
+ policy: The data structure of the policy.
+ '''
+ # Set style according to group nesting level.
+ indent = 'margin-left: %dpx' % (self._indent_level * 28)
+ if policy['type'] == 'group':
+ heading = 'h2'
+ else:
+ heading = 'h3'
+ parent2 = self.AddElement(parent, 'div', {'style': indent})
+
+ h2 = self.AddElement(parent2, heading)
+ self.AddElement(h2, 'a', {'name': policy['name']})
+ if policy['type'] != 'group':
+ # Normal policies get a full description.
+ policy_name_text = policy['name']
+ if 'deprecated' in policy and policy['deprecated'] == True:
+ policy_name_text += " ("
+ policy_name_text += self.GetLocalizedMessage('deprecated') + ")"
+ self.AddText(h2, policy_name_text)
+ self.AddElement(parent2, 'span', {}, policy['caption'])
+ self._AddPolicyDetails(parent2, policy)
+ else:
+ # Groups get a more compact description.
+ self.AddText(h2, policy['caption'])
+ self._AddStyledElement(parent2, 'div', ['div.group_desc'], {},
+ policy['desc'])
+ self.AddElement(parent2, 'a', {'href': '#top'},
+ self.GetLocalizedMessage('back_to_top'))
+
+ def SchemaHasRangeRestriction(self, schema):
+ if 'maximum' in schema:
+ return True
+ if 'minimum' in schema:
+ return schema['minimum'] != 0
+ return False
+
+ def _BeginTemplate(self, intro_message_id, banner_message_id):
+ # Add a <div> for the summary section.
+ if self._GetChromiumVersionString() is not None:
+ self.AddComment(self._main_div, self.config['build'] + \
+ ' version: ' + self._GetChromiumVersionString())
+
+ banner_div = self._AddStyledElement(self._main_div, 'div', ['div.banner'],
+ {}, '')
+ self._AddParagraphs(banner_div, self.GetLocalizedMessage(banner_message_id))
+ summary_div = self.AddElement(self._main_div, 'div')
+ self.AddElement(summary_div, 'a', {'name': 'top'})
+ self.AddElement(summary_div, 'br')
+ self._AddParagraphs(summary_div, self.GetLocalizedMessage(intro_message_id))
+ self.AddElement(summary_div, 'br')
+ self.AddElement(summary_div, 'br')
+ self.AddElement(summary_div, 'br')
+ # Add the summary table of policies.
+ summary_table = self._AddStyledElement(summary_div, 'table', ['table'])
+ # Add the first row.
+ thead = self.AddElement(summary_table, 'thead')
+ tr = self._AddStyledElement(thead, 'tr', ['tr'])
+ self._AddStyledElement(tr, 'td', ['td', 'td.left', 'thead td'], {},
+ self.GetLocalizedMessage('name_column_title'))
+ self._AddStyledElement(tr, 'td', ['td', 'td.right', 'thead td'], {},
+ self.GetLocalizedMessage('description_column_title'))
+ self._summary_tbody = self.AddElement(summary_table, 'tbody')
+
+ # Add a <div> for the detailed policy listing.
+ self._details_div = self.AddElement(self._main_div, 'div')
+
+ #
+ # Implementation of abstract methods of TemplateWriter:
+ #
+
+ def IsDeprecatedPolicySupported(self, policy):
+ return True
+
+ def WritePolicy(self, policy):
+ self._AddPolicyRow(self._summary_tbody, policy)
+ self._AddPolicySection(self._details_div, policy)
+
+ def BeginPolicyGroup(self, group):
+ self.WritePolicy(group)
+ self._indent_level += 1
+
+ def EndPolicyGroup(self):
+ self._indent_level -= 1
+
+ def BeginTemplate(self):
+ self._BeginTemplate('intro', 'banner')
+
+ def Init(self):
+ dom_impl = minidom.getDOMImplementation('')
+ self._doc = dom_impl.createDocument(None, 'html', None)
+ body = self.AddElement(self._doc.documentElement, 'body')
+ self._main_div = self.AddElement(body, 'div')
+ self._indent_level = 0
+
+ # Human-readable names of supported platforms.
+ self._PLATFORM_MAP = {
+ 'win': 'Windows',
+ 'mac': 'Mac',
+ 'linux': 'Linux',
+ 'chrome_os': self.config['os_name'],
+ 'android': 'Android',
+ 'win7': 'Windows 7',
+ 'ios': 'iOS',
+ }
+ # Human-readable names of supported products.
+ self._PRODUCT_MAP = {
+ 'chrome': self.config['app_name'],
+ 'chrome_frame': self.config['frame_name'],
+ 'chrome_os': self.config['os_name'],
+ 'webview': self.config['webview_name'],
+ }
+ # Human-readable names of supported features. Each supported feature has
+ # a 'doc_feature_X' entry in |self.messages|.
+ self._FEATURE_MAP = {}
+ for message in self.messages:
+ if message.startswith('doc_feature_'):
+ self._FEATURE_MAP[message[12:]] = self.messages[message]['text']
+ # Human-readable names of types.
+ self._TYPE_MAP = {
+ 'string': 'String',
+ 'int': 'Integer',
+ 'main': 'Boolean',
+ 'int-enum': 'Integer',
+ 'string-enum': 'String',
+ 'list': 'List of strings',
+ 'string-enum-list': 'List of strings',
+ 'dict': 'Dictionary',
+ 'external': 'External data reference',
+ }
+ self._REG_TYPE_MAP = {
+ 'string': 'REG_SZ',
+ 'int': 'REG_DWORD',
+ 'main': 'REG_DWORD',
+ 'int-enum': 'REG_DWORD',
+ 'string-enum': 'REG_SZ',
+ 'dict': 'REG_SZ',
+ 'external': 'REG_SZ',
+ }
+ self._RESTRICTION_TYPE_MAP = {
+ 'int-enum': 'choice',
+ 'string-enum': 'choice',
+ 'list': 'string',
+ 'string-enum-list': 'multi-select',
+ 'dict': 'string',
+ 'external': 'string',
+ }
+ # The CSS style-sheet used for the document. It will be used in Google
+ # Sites, which strips class attributes from HTML tags. To work around this,
+ # the style-sheet is a dictionary and the style attributes will be added
+ # "by hand" for each element.
+ self._STYLE = {
+ 'div.banner': 'background-color: rgb(244,204,204); font-size: x-large; '
+ 'border: 1px solid red; padding: 20px; '
+ 'text-align: center;',
+ 'table': 'border-style: none; border-collapse: collapse;',
+ 'tr': 'height: 0px;',
+ 'td': 'border: 1px dotted rgb(170, 170, 170); padding: 7px; '
+ 'vertical-align: top; width: 236px; height: 15px;',
+ 'thead td': 'font-weight: bold;',
+ 'td.left': 'width: 200px;',
+ 'td.right': 'width: 100%;',
+ 'dt': 'font-weight: bold;',
+ 'dd dl': 'margin-top: 0px; margin-bottom: 0px;',
+ '.monospace': 'font-family: monospace;',
+ '.pre-wrap': 'white-space: pre-wrap;',
+ 'div.note': 'border: 2px solid black; padding: 5px; margin: 5px;',
+ 'div.group_desc': 'margin-top: 20px; margin-bottom: 20px;',
+ 'ul': 'padding-left: 0px; margin-left: 0px;'
+ }
+
+ def GetTemplateText(self):
+ # Return the text representation of the main <div> tag.
+ return self._main_div.toxml()
+ # To get a complete HTML file, use the following.
+ # return self._doc.toxml()
diff --git a/chromium/components/policy/tools/template_writers/writers/doc_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/doc_writer_unittest.py
new file mode 100755
index 00000000000..8be4dd376c9
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/doc_writer_unittest.py
@@ -0,0 +1,1835 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+'''Unit tests for writers.doc_writer'''
+
+import json
+import os
+import sys
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+import unittest
+from xml.dom import minidom
+
+from writers import writer_unittest_common
+from writers import doc_writer
+
+
+class MockMessageDictionary:
+ '''A mock dictionary passed to a writer as the dictionary of
+ localized messages.
+ '''
+
+ # Dictionary of messages.
+ msg_dict = {}
+
+
+class DocWriterUnittest(writer_unittest_common.WriterUnittestCommon):
+ '''Unit tests for DocWriter.'''
+
+ def setUp(self):
+ # Create a writer for the tests.
+ self.writer = doc_writer.GetWriter(
+ config={
+ 'app_name': 'Chrome',
+ 'frame_name': 'Chrome Frame',
+ 'os_name': 'Chrome OS',
+ 'webview_name': 'WebView',
+ 'android_webview_restriction_prefix': 'mock.prefix:',
+ 'win_config': {
+ 'win': {
+ 'reg_mandatory_key_name': 'MockKey',
+ 'reg_recommended_key_name': 'MockKeyRec',
+ },
+ 'chrome_os': {
+ 'reg_mandatory_key_name': 'MockKeyCrOS',
+ 'reg_recommended_key_name': 'MockKeyCrOSRec',
+ },
+ },
+ 'build': 'test_product',
+ })
+ self.writer.messages = {
+ 'doc_back_to_top': {
+ 'text': '_test_back_to_top'
+ },
+ 'doc_complex_policies_on_windows': {
+ 'text': '_test_complex_policies_win'
+ },
+ 'doc_data_type': {
+ 'text': '_test_data_type'
+ },
+ 'doc_description': {
+ 'text': '_test_description'
+ },
+ 'doc_schema': {
+ 'text': '_test_schema'
+ },
+ 'doc_url_schema': {
+ 'text': '_test_url_schema'
+ },
+ 'doc_arc_support': {
+ 'text': '_test_arc_support'
+ },
+ 'doc_description_column_title': {
+ 'text': '_test_description_column_title'
+ },
+ 'doc_example_value': {
+ 'text': '_test_example_value'
+ },
+ 'doc_win_example_value': {
+ 'text': '_test_example_value_win'
+ },
+ 'doc_chrome_os_example_value': {
+ 'text': '_test_example_value_chrome_os'
+ },
+ 'doc_feature_dynamic_refresh': {
+ 'text': '_test_feature_dynamic_refresh'
+ },
+ 'doc_feature_can_be_recommended': {
+ 'text': '_test_feature_recommended'
+ },
+ 'doc_feature_can_be_mandatory': {
+ 'text': '_test_feature_mandatory'
+ },
+ 'doc_banner': {
+ 'text': '_test_banner'
+ },
+ 'doc_intro': {
+ 'text': '_test_intro'
+ },
+ 'doc_mac_linux_pref_name': {
+ 'text': '_test_mac_linux_pref_name'
+ },
+ 'doc_android_restriction_name': {
+ 'text': '_test_android_restriction_name'
+ },
+ 'doc_android_webview_restriction_name': {
+ 'text': '_test_android_webview_restriction_name'
+ },
+ 'doc_note': {
+ 'text': '_test_note'
+ },
+ 'doc_name_column_title': {
+ 'text': '_test_name_column_title'
+ },
+ 'doc_not_supported': {
+ 'text': '_test_not_supported'
+ },
+ 'doc_since_version': {
+ 'text': '..$6..'
+ },
+ 'doc_supported': {
+ 'text': '_test_supported'
+ },
+ 'doc_supported_features': {
+ 'text': '_test_supported_features'
+ },
+ 'doc_supported_on': {
+ 'text': '_test_supported_on'
+ },
+ 'doc_win_reg_loc': {
+ 'text': '_test_win_reg_loc'
+ },
+ 'doc_oma_uri': {
+ 'text': '_test_oma_uri'
+ },
+ 'doc_chrome_os_reg_loc': {
+ 'text': '_test_chrome_os_reg_loc'
+ },
+ 'doc_bla': {
+ 'text': '_test_bla'
+ },
+ 'doc_policy_atomic_group': {
+ 'text': '_test_policy_atomic_group'
+ },
+ 'doc_policy_in_atomic_group': {
+ 'text': '_test_policy_in_atomic_group'
+ }
+ }
+ self.writer.Init()
+
+ # It is not worth testing the exact content of style attributes.
+ # Therefore we override them here with shorter texts.
+ for key in self.writer._STYLE.keys():
+ self.writer._STYLE[key] = 'style_%s;' % key
+ # Add some more style attributes for additional testing.
+ self.writer._STYLE['key1'] = 'style1;'
+ self.writer._STYLE['key2'] = 'style2;'
+
+ # Create a DOM document for the tests.
+ dom_impl = minidom.getDOMImplementation('')
+ self.doc = dom_impl.createDocument(None, 'root', None)
+ self.doc_root = self.doc.documentElement
+
+ def testSkeleton(self):
+ # Test if DocWriter creates the skeleton of the document correctly.
+ self.writer.BeginTemplate()
+ self.assertEquals(
+ self.writer._main_div.toxml(), '<div>'
+ '<div style="style_div.banner;"><p>_test_banner</p></div>'
+ '<div>'
+ '<a name="top"/><br/><p>_test_intro</p><br/><br/><br/>'
+ '<table style="style_table;">'
+ '<thead><tr style="style_tr;">'
+ '<td style="style_td;style_td.left;style_thead td;">'
+ '_test_name_column_title'
+ '</td>'
+ '<td style="style_td;style_td.right;style_thead td;">'
+ '_test_description_column_title'
+ '</td>'
+ '</tr></thead>'
+ '<tbody/>'
+ '</table>'
+ '</div>'
+ '<div/>'
+ '</div>')
+
+ def testVersionAnnotation(self):
+ # Test if DocWriter creates the skeleton of the document correctly.
+ self.writer.config['version'] = '39.0.0.0'
+ self.writer.BeginTemplate()
+ self.assertEquals(
+ self.writer._main_div.toxml(), '<div>'
+ '<!--test_product version: 39.0.0.0-->'
+ '<div style="style_div.banner;"><p>_test_banner</p></div>'
+ '<div>'
+ '<a name="top"/><br/><p>_test_intro</p><br/><br/><br/>'
+ '<table style="style_table;">'
+ '<thead><tr style="style_tr;">'
+ '<td style="style_td;style_td.left;style_thead td;">'
+ '_test_name_column_title'
+ '</td>'
+ '<td style="style_td;style_td.right;style_thead td;">'
+ '_test_description_column_title'
+ '</td>'
+ '</tr></thead>'
+ '<tbody/>'
+ '</table>'
+ '</div>'
+ '<div/>'
+ '</div>')
+
+ def testGetLocalizedMessage(self):
+ # Test if localized messages are retrieved correctly.
+ self.writer.messages = {'doc_hello_world': {'text': 'hello, vilag!'}}
+ self.assertEquals(
+ self.writer.GetLocalizedMessage('hello_world'), 'hello, vilag!')
+
+ def testAddStyledElement(self):
+ # Test function DocWriter.AddStyledElement()
+
+ # Test the case of zero style.
+ e1 = self.writer._AddStyledElement(self.doc_root, 'z', [], {'a': 'b'},
+ 'text')
+ self.assertEquals(e1.toxml(), '<z a="b">text</z>')
+
+ # Test the case of one style.
+ e2 = self.writer._AddStyledElement(self.doc_root, 'z', ['key1'], {'a': 'b'},
+ 'text')
+ self.assertEquals(e2.toxml(), '<z a="b" style="style1;">text</z>')
+
+ # Test the case of two styles.
+ e3 = self.writer._AddStyledElement(self.doc_root, 'z', ['key1', 'key2'],
+ {'a': 'b'}, 'text')
+ self.assertEquals(e3.toxml(), '<z a="b" style="style1;style2;">text</z>')
+
+ def testAddDescriptionIntEnum(self):
+ # Test if URLs are replaced and choices of 'int-enum' policies are listed
+ # correctly.
+ policy = {
+ 'type':
+ 'int-enum',
+ 'items': [
+ {
+ 'value': 0,
+ 'caption': 'Disable foo'
+ },
+ {
+ 'value': 2,
+ 'caption': 'Solve your problem'
+ },
+ {
+ 'value': 5,
+ 'caption': 'Enable bar'
+ },
+ ],
+ 'desc':
+ '''This policy disables foo, except in case of bar.
+See http://policy-explanation.example.com for more details.
+'''
+ }
+ self.writer._AddDescription(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(),
+ '''<root><p>This policy disables foo, except in case of bar.
+See <a href="http://policy-explanation.example.com">http://policy-explanation.example.com</a> for more details.
+</p><ul><li>0 = Disable foo</li><li>2 = Solve your problem</li><li>5 = Enable bar</li></ul></root>'''
+ )
+
+ def testAddDescriptionStringEnum(self):
+ # Test if URLs are replaced and choices of 'int-enum' policies are listed
+ # correctly.
+ policy = {
+ 'type':
+ 'string-enum',
+ 'items': [
+ {
+ 'value': "one",
+ 'caption': 'Disable foo'
+ },
+ {
+ 'value': "two",
+ 'caption': 'Solve your problem'
+ },
+ {
+ 'value': "three",
+ 'caption': 'Enable bar'
+ },
+ ],
+ 'desc':
+ '''This policy disables foo, except in case of bar.
+See http://policy-explanation.example.com for more details.
+'''
+ }
+ self.writer._AddDescription(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(),
+ '''<root><p>This policy disables foo, except in case of bar.
+See <a href="http://policy-explanation.example.com">http://policy-explanation.example.com</a> for more details.
+</p><ul><li>&quot;one&quot; = Disable foo</li><li>&quot;two&quot; = Solve your problem</li><li>&quot;three&quot; = Enable bar</li></ul></root>'''
+ )
+
+ def testAddSchema(self):
+ # Test if the schema of a policy is handled correctly.
+ policy = {
+ 'type': 'dict',
+ 'schema': {
+ 'properties': {
+ 'foo': {
+ 'type': 'integer'
+ }
+ },
+ 'type': 'object'
+ }
+ }
+ self.writer._AddSchema(self.doc_root, policy['schema'])
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<dt style="style_dt;">_test_schema</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">{\n'
+ ' &quot;properties&quot;: {\n'
+ ' &quot;foo&quot;: {\n'
+ ' &quot;type&quot;: &quot;integer&quot;\n'
+ ' }\n'
+ ' }, \n'
+ ' &quot;type&quot;: &quot;object&quot;\n'
+ '}</dd></root>')
+
+ def testAddUrlSchema(self):
+ # Test if the expanded schema description of a policy is handled correctly.
+ policy = {'url_schema': 'https://example.com/details'}
+ self.writer._AddTextWithLinks(self.doc_root, policy['url_schema'])
+ self.assertEquals(
+ self.doc_root.toxml(),
+ '<root><a href="https://example.com/details">https://example.com/details</a></root>'
+ )
+
+ def testAddFeatures(self):
+ # Test if the list of features of a policy is handled correctly.
+ policy = {
+ 'features': {
+ 'spaceship_docking': False,
+ 'dynamic_refresh': True,
+ 'can_be_recommended': True,
+ }
+ }
+ self.writer._FEATURE_MAP = {
+ 'can_be_recommended': 'Can Be Recommended',
+ 'dynamic_refresh': 'Dynamic Refresh',
+ 'spaceship_docking': 'Spaceship Docking',
+ }
+ self.writer._AddFeatures(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ 'Can Be Recommended: _test_supported, '
+ 'Dynamic Refresh: _test_supported, '
+ 'Spaceship Docking: _test_not_supported'
+ '</root>')
+
+ def testAddListExample(self):
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'example_value': ['Foo', 'Bar'],
+ 'supported_on': [{
+ 'platform': 'win'
+ }, {
+ 'platform': 'mac'
+ }, {
+ 'platform': 'linux'
+ }, {
+ 'platform': 'chrome_os'
+ }]
+ }
+ self.writer._AddListExample(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<dl style="style_dd dl;">'
+ '<dt>_test_example_value_win</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ 'MockKey\\PolicyName\\1 = &quot;Foo&quot;\n'
+ 'MockKey\\PolicyName\\2 = &quot;Bar&quot;'
+ '</dd>'
+ '<dt>_test_example_value_chrome_os</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ 'MockKeyCrOS\\PolicyName\\1 = &quot;Foo&quot;\n'
+ 'MockKeyCrOS\\PolicyName\\2 = &quot;Bar&quot;'
+ '</dd>'
+ '<dt>Android/Linux:</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ '[\n'
+ ' &quot;Foo&quot;,\n'
+ ' &quot;Bar&quot;\n'
+ ']'
+ '</dd>'
+ '<dt>Mac:</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ '&lt;array&gt;\n'
+ ' &lt;string&gt;Foo&lt;/string&gt;\n'
+ ' &lt;string&gt;Bar&lt;/string&gt;\n'
+ '&lt;/array&gt;'
+ '</dd>'
+ '</dl>'
+ '</root>')
+
+ def testBoolExample(self):
+ # Test representation of boolean example values.
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'type':
+ 'main',
+ 'example_value':
+ True,
+ 'supported_on': [{
+ 'platform': 'win'
+ }, {
+ 'platform': 'mac'
+ }, {
+ 'platform': 'linux'
+ }, {
+ 'platform': 'android'
+ }]
+ }
+ e1 = self.writer.AddElement(self.doc_root, 'e1')
+ self.writer._AddExample(e1, policy)
+ self.assertEquals(
+ e1.toxml(), '<e1>0x00000001 (Windows),'
+ ' true (Linux), true (Android),'
+ ' &lt;true /&gt; (Mac)'
+ '<dl><dt>Windows (Intune):</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;enabled/&gt;</dd></dl>'
+ '</e1>')
+
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'type':
+ 'main',
+ 'example_value':
+ False,
+ 'supported_on': [{
+ 'platform': 'win'
+ }, {
+ 'platform': 'mac'
+ }, {
+ 'platform': 'linux'
+ }, {
+ 'platform': 'android'
+ }]
+ }
+ e2 = self.writer.AddElement(self.doc_root, 'e2')
+ self.writer._AddExample(e2, policy)
+ self.assertEquals(
+ e2.toxml(), '<e2>0x00000000 (Windows),'
+ ' false (Linux), false (Android),'
+ ' &lt;false /&gt; (Mac)'
+ '<dl><dt>Windows (Intune):</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;disabled/&gt;</dd></dl>'
+ '</e2>')
+
+ def testIntEnumExample(self):
+ # Test representation of 'int-enum' example values.
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'type':
+ 'int-enum',
+ 'example_value':
+ 16,
+ 'supported_on': [{
+ 'platform': 'win'
+ }, {
+ 'platform': 'mac'
+ }, {
+ 'platform': 'linux'
+ }, {
+ 'platform': 'android'
+ }]
+ }
+ self.writer._AddExample(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(),
+ '<root>0x00000010 (Windows), 16 (Linux), 16 (Android), 16 (Mac)'
+ '<dl><dt>Windows (Intune):</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;enabled/&gt;</dd>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;data id=&quot;PolicyName&quot; value=&quot;16&quot;/&gt;</dd></dl>'
+ '</root>')
+
+ def testStringEnumExample(self):
+ # Test representation of 'string-enum' example values.
+ policy = {
+ 'name': 'PolicyName',
+ 'type': 'string-enum',
+ 'example_value': "wacky",
+ 'supported_on': []
+ }
+ self.writer._AddExample(self.doc_root, policy)
+ self.assertEquals(self.doc_root.toxml(), '<root>&quot;wacky&quot;</root>')
+
+ def testListExample(self):
+ # Test representation of 'list' example values.
+ policy = {
+ 'name': 'PolicyName',
+ 'type': 'list',
+ 'example_value': ['one', 'two'],
+ 'supported_on': [{
+ 'platform': 'linux'
+ }]
+ }
+ self.writer._AddExample(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root><dl style="style_dd dl;">'
+ '<dt>Android/Linux:</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ '[\n'
+ ' &quot;one&quot;,\n'
+ ' &quot;two&quot;\n'
+ ']'
+ '</dd></dl></root>')
+
+ def testStringEnumListExample(self):
+ # Test representation of 'string-enum-list' example values.
+ policy = {
+ 'name': 'PolicyName',
+ 'type': 'string-enum-list',
+ 'example_value': ['one', 'two'],
+ 'supported_on': [{
+ 'platform': 'linux'
+ }]
+ }
+ self.writer._AddExample(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root><dl style="style_dd dl;">'
+ '<dt>Android/Linux:</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ '[\n'
+ ' &quot;one&quot;,\n'
+ ' &quot;two&quot;\n'
+ ']'
+ '</dd></dl></root>')
+
+ def testStringExample(self):
+ # Test representation of 'string' example values.
+ policy = {
+ 'name': 'PolicyName',
+ 'type': 'string',
+ 'example_value': 'awesome-example',
+ 'supported_on': []
+ }
+ self.writer._AddExample(self.doc_root, policy)
+ self.assertEquals(self.doc_root.toxml(),
+ '<root>&quot;awesome-example&quot;</root>')
+
+ def testIntExample(self):
+ # Test representation of 'int' example values.
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'type':
+ 'int',
+ 'example_value':
+ 26,
+ 'supported_on': [{
+ 'platform': 'win'
+ }, {
+ 'platform': 'mac'
+ }, {
+ 'platform': 'linux'
+ }, {
+ 'platform': 'android'
+ }]
+ }
+ self.writer._AddExample(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(),
+ '<root>0x0000001a (Windows), 26 (Linux), 26 (Android), 26 (Mac)'
+ '<dl><dt>Windows (Intune):</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;enabled/&gt;</dd>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;data id=&quot;PolicyName&quot; value=&quot;26&quot;/&gt;</dd></dl>'
+ '</root>')
+
+ def testAddPolicyAttribute(self):
+ # Test creating a policy attribute term-definition pair.
+ self.writer._AddPolicyAttribute(self.doc_root, 'bla', 'hello, world',
+ ['key1'])
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<dt style="style_dt;">_test_bla</dt>'
+ '<dd style="style1;">hello, world</dd>'
+ '</root>')
+
+ def testAddPolicyDetails(self):
+ # Test if the definition list (<dl>) of policy details is created correctly.
+ policy = {
+ 'type':
+ 'main',
+ 'name':
+ 'TestPolicyName',
+ 'caption':
+ 'TestPolicyCaption',
+ 'desc':
+ 'TestPolicyDesc',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'win',
+ 'since_version': '8',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'mac',
+ 'since_version': '8',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'linux',
+ 'since_version': '8',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'android',
+ 'since_version': '30',
+ 'until_version': '',
+ },
+ {
+ 'product': 'webview',
+ 'platform': 'android',
+ 'since_version': '47',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'chrome_os',
+ 'since_version': '55',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value':
+ False,
+ 'arc_support':
+ 'TestArcSupportNote'
+ }
+ self.writer._AddPolicyDetails(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root><dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>Boolean [Windows:REG_DWORD]</dd>'
+ '<dt style="style_dt;">_test_win_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKey\TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_oma_uri</dt>'
+ '<dd style="style_.monospace;">.\\Device\\Vendor\\MSFT\\Policy\\Config\\Chrome~Policy~chromium\\TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_chrome_os_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKeyCrOS\TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_mac_linux_pref_name</dt>'
+ '<dd style="style_.monospace;">TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_android_restriction_name</dt>'
+ '<dd style="style_.monospace;">TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_android_webview_restriction_name</dt>'
+ '<dd style="style_.monospace;">mock.prefix:TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome (Windows) ..8..</li>'
+ '<li>Chrome (Mac) ..8..</li>'
+ '<li>Chrome (Linux) ..8..</li>'
+ '<li>Chrome (Android) ..30..</li>'
+ '<li>WebView (Android) ..47..</li>'
+ '<li>Chrome (Chrome OS) ..55..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt><dd><p>TestPolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_arc_support</dt>'
+ '<dd><p>TestArcSupportNote</p></dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>0x00000000 (Windows), false (Linux),'
+ ' false (Android), &lt;false /&gt; (Mac)'
+ '<dl><dt>Windows (Intune):</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;disabled/&gt;</dd></dl>'
+ '</dd>'
+ '</dl></root>')
+
+ def testAddPolicyDetailsNoArcSupport(self):
+ # Test that the entire Android-on-Chrome-OS sub-section is left out when
+ # 'arc_support' is not specified.
+ policy = {
+ 'type':
+ 'main',
+ 'name':
+ 'TestPolicyName',
+ 'caption':
+ 'TestPolicyCaption',
+ 'desc':
+ 'TestPolicyDesc',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'linux',
+ 'since_version': '8',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value':
+ False
+ }
+ self.writer._AddPolicyDetails(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root><dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>Boolean</dd>'
+ '<dt style="style_dt;">_test_mac_linux_pref_name</dt>'
+ '<dd style="style_.monospace;">TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome (Linux) ..8..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt>'
+ '<dd><p>TestPolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>false (Linux)</dd>'
+ '</dl></root>')
+
+ def testAddDictPolicyDetails(self):
+ # Test if the definition list (<dl>) of policy details is created correctly
+ # for 'dict' policies.
+ policy = {
+ 'type':
+ 'dict',
+ 'name':
+ 'TestPolicyName',
+ 'caption':
+ 'TestPolicyCaption',
+ 'desc':
+ 'TestPolicyDesc',
+ 'schema': {
+ 'properties': {
+ 'foo': {
+ 'type': 'integer'
+ }
+ },
+ 'type': 'object'
+ },
+ 'url_schema':
+ 'https://example.com/details',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'win',
+ 'since_version': '8',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'mac',
+ 'since_version': '8',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'linux',
+ 'since_version': '8',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome_os',
+ 'platform': 'chrome_os',
+ 'since_version': '8',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value': {
+ 'foo': 123
+ }
+ }
+ self.writer._AddPolicyDetails(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root><dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>Dictionary [Windows:REG_SZ] (_test_complex_policies_win)</dd>'
+ '<dt style="style_dt;">_test_win_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKey\TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_oma_uri</dt>'
+ '<dd style="style_.monospace;">.\\Device\\Vendor\\MSFT\\Policy\\Config\\Chrome~Policy~chromium\\TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_chrome_os_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKeyCrOS\TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_mac_linux_pref_name</dt>'
+ '<dd style="style_.monospace;">TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome (Windows) ..8..</li>'
+ '<li>Chrome (Mac) ..8..</li>'
+ '<li>Chrome (Linux) ..8..</li>'
+ '<li>Chrome OS (Chrome OS) ..8..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt><dd><p>TestPolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_schema</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">{\n'
+ ' &quot;properties&quot;: {\n'
+ ' &quot;foo&quot;: {\n'
+ ' &quot;type&quot;: &quot;integer&quot;\n'
+ ' }\n'
+ ' }, \n'
+ ' &quot;type&quot;: &quot;object&quot;\n'
+ '}</dd>'
+ '<dt style="style_dt;">_test_url_schema</dt>'
+ '<dd><a href="https://example.com/details">https://example.com/details</a></dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>'
+ '<dl style="style_dd dl;">'
+ '<dt>_test_example_value_win</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ 'MockKey\TestPolicyName = {\n'
+ ' &quot;foo&quot;: 123\n'
+ '}'
+ '</dd>'
+ '<dt>_test_example_value_chrome_os</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ 'MockKeyCrOS\TestPolicyName = {\n'
+ ' &quot;foo&quot;: 123\n'
+ '}'
+ '</dd>'
+ '<dt>Android/Linux:</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ 'TestPolicyName: {\n'
+ ' &quot;foo&quot;: 123\n'
+ '}'
+ '</dd>'
+ '<dt>Mac:</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ '&lt;key&gt;TestPolicyName&lt;/key&gt;\n'
+ '&lt;dict&gt;\n'
+ ' &lt;key&gt;foo&lt;/key&gt;\n'
+ ' &lt;integer&gt;123&lt;/integer&gt;\n'
+ '&lt;/dict&gt;'
+ '</dd>'
+ '</dl>'
+ '<dl><dt>Windows (Intune):</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;enabled/&gt;</dd>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;data id=&quot;TestPolicyName&quot; value=&quot;&quot;foo&quot;: 123&quot;/&gt;</dd></dl>'
+ '</dd>'
+ '</dl></root>')
+
+ def testAddExternalPolicyDetails(self):
+ # Test if the definition list (<dl>) of policy details is created correctly
+ # for 'external' policies.
+ policy = {
+ 'type':
+ 'external',
+ 'name':
+ 'TestPolicyName',
+ 'caption':
+ 'TestPolicyCaption',
+ 'desc':
+ 'TestPolicyDesc',
+ 'description_schema': {
+ 'properties': {
+ 'url': {
+ 'type': 'string'
+ },
+ 'hash': {
+ 'type': 'string'
+ },
+ },
+ 'type': 'object'
+ },
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'win',
+ 'since_version': '8',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'mac',
+ 'since_version': '8',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'linux',
+ 'since_version': '8',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value': {
+ "url": "https://example.com/avatar.jpg",
+ "hash": "deadbeef",
+ },
+ }
+ self.writer.messages['doc_since_version'] = {'text': '...$6...'}
+ self.writer._AddPolicyDetails(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root><dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>External data reference [Windows:REG_SZ] (_test_complex_policies_win)</dd>'
+ '<dt style="style_dt;">_test_win_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKey\TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_oma_uri</dt>'
+ '<dd style="style_.monospace;">.\\Device\\Vendor\\MSFT\\Policy\\Config\\Chrome~Policy~chromium\\TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_mac_linux_pref_name</dt>'
+ '<dd style="style_.monospace;">TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome (Windows) ...8...</li>'
+ '<li>Chrome (Mac) ...8...</li>'
+ '<li>Chrome (Linux) ...8...</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt><dd><p>TestPolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_schema</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">{\n'
+ ' &quot;properties&quot;: {\n'
+ ' &quot;hash&quot;: {\n'
+ ' &quot;type&quot;: &quot;string&quot;\n'
+ ' }, \n'
+ ' &quot;url&quot;: {\n'
+ ' &quot;type&quot;: &quot;string&quot;\n'
+ ' }\n'
+ ' }, \n'
+ ' &quot;type&quot;: &quot;object&quot;\n'
+ '}</dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>'
+ '<dl style="style_dd dl;">'
+ '<dt>_test_example_value_win</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ 'MockKey\TestPolicyName = {\n'
+ ' &quot;hash&quot;: &quot;deadbeef&quot;, \n'
+ ' &quot;url&quot;: &quot;https://example.com/avatar.jpg&quot;\n'
+ '}'
+ '</dd>'
+ '<dt>Android/Linux:</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ 'TestPolicyName: {\n'
+ ' &quot;hash&quot;: &quot;deadbeef&quot;, \n'
+ ' &quot;url&quot;: &quot;https://example.com/avatar.jpg&quot;\n'
+ '}'
+ '</dd>'
+ '<dt>Mac:</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ '&lt;key&gt;TestPolicyName&lt;/key&gt;\n'
+ '&lt;dict&gt;\n'
+ ' &lt;key&gt;hash&lt;/key&gt;\n'
+ ' &lt;string&gt;deadbeef&lt;/string&gt;\n'
+ ' &lt;key&gt;url&lt;/key&gt;\n'
+ ' &lt;string&gt;https://example.com/avatar.jpg&lt;/string&gt;\n&lt;'
+ '/dict&gt;'
+ '</dd>'
+ '</dl>'
+ '<dl><dt>Windows (Intune):</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;enabled/&gt;</dd>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;data id=&quot;TestPolicyName&quot; value=&quot;&quot;url&quot;: &quot;https://example.com/avatar.jpg&quot;, &quot;hash&quot;: &quot;deadbeef&quot;&quot;/&gt;</dd></dl>'
+ '</dd>'
+ '</dl></root>')
+
+ def testAddPolicyDetailsRecommendedOnly(self):
+ policy = {
+ 'type':
+ 'main',
+ 'name':
+ 'TestPolicyName',
+ 'caption':
+ 'TestPolicyCaption',
+ 'desc':
+ 'TestPolicyDesc',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'win',
+ 'since_version': '8',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'mac',
+ 'since_version': '8',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'linux',
+ 'since_version': '8',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'android',
+ 'since_version': '30',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'chrome_os',
+ 'since_version': '53',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False,
+ 'can_be_mandatory': False,
+ 'can_be_recommended': True
+ },
+ 'example_value':
+ False
+ }
+ self.writer._AddPolicyDetails(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root><dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>Boolean [Windows:REG_DWORD]</dd>'
+ '<dt style="style_dt;">_test_win_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKeyRec\TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_oma_uri</dt>'
+ '<dd style="style_.monospace;">.\\Device\\Vendor\\MSFT\\Policy\\Config\\Chrome~Policy~chromium\\TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_chrome_os_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKeyCrOSRec\TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_mac_linux_pref_name</dt>'
+ '<dd style="style_.monospace;">TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_android_restriction_name</dt>'
+ '<dd style="style_.monospace;">TestPolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome (Windows) ..8..</li>'
+ '<li>Chrome (Mac) ..8..</li>'
+ '<li>Chrome (Linux) ..8..</li>'
+ '<li>Chrome (Android) ..30..</li>'
+ '<li>Chrome (Chrome OS) ..53..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>_test_feature_mandatory: _test_not_supported,'
+ ' _test_feature_recommended: _test_supported,'
+ ' _test_feature_dynamic_refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt><dd><p>TestPolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>0x00000000 (Windows), false (Linux),'
+ ' false (Android), &lt;false /&gt; (Mac)'
+ '<dl><dt>Windows (Intune):</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;disabled/&gt;</dd></dl>'
+ '</dd>'
+ '</dl></root>')
+
+ def testAddPolicyRow(self):
+ # Test if policies are correctly added to the summary table.
+ policy = {
+ 'name': 'PolicyName',
+ 'caption': 'PolicyCaption',
+ 'type': 'string',
+ }
+ self.writer._indent_level = 3
+ self.writer._AddPolicyRow(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root><tr style="style_tr;">'
+ '<td style="style_td;style_td.left;padding-left: 49px;">'
+ '<a href="#PolicyName">PolicyName</a>'
+ '</td>'
+ '<td style="style_td;style_td.right;">PolicyCaption</td>'
+ '</tr></root>')
+ self.setUp()
+ policy = {
+ 'name': 'PolicyName',
+ 'caption': 'PolicyCaption',
+ 'type': 'group',
+ }
+ self.writer._indent_level = 2
+ self.writer._AddPolicyRow(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root><tr style="style_tr;">'
+ '<td colspan="2" style="style_td;style_td.left;padding-left: 35px;">'
+ '<a href="#PolicyName">PolicyCaption</a>'
+ '</td>'
+ '</tr></root>')
+
+ def testAddPolicySection(self):
+ # Test if policy details are correctly added to the document.
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'caption':
+ 'PolicyCaption',
+ 'desc':
+ 'PolicyDesc',
+ 'type':
+ 'string',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'win',
+ 'since_version': '7',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'mac',
+ 'since_version': '7',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome_os',
+ 'platform': 'chrome_os',
+ 'since_version': '7',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value':
+ 'False'
+ }
+ self.writer._AddPolicySection(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<div style="margin-left: 0px">'
+ '<h3><a name="PolicyName"/>PolicyName</h3>'
+ '<span>PolicyCaption</span>'
+ '<dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>String [Windows:REG_SZ]</dd>'
+ '<dt style="style_dt;">_test_win_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKey\\PolicyName</dd>'
+ '<dt style="style_dt;">_test_oma_uri</dt>'
+ '<dd style="style_.monospace;">.\\Device\\Vendor\\MSFT\\Policy\\Config\\Chrome~Policy~chromium\\PolicyName</dd>'
+ '<dt style="style_dt;">_test_chrome_os_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKeyCrOS\\PolicyName</dd>'
+ '<dt style="style_dt;">_test_mac_linux_pref_name</dt>'
+ '<dd style="style_.monospace;">PolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome (Windows) ..7..</li>'
+ '<li>Chrome (Mac) ..7..</li>'
+ '<li>Chrome OS (Chrome OS) ..7..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt>'
+ '<dd><p>PolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>&quot;False&quot;'
+ '<dl><dt>Windows (Intune):</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;enabled/&gt;</dd>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;data id=&quot;PolicyName&quot; value=&quot;False&quot;/&gt;</dd></dl>'
+ '</dd>'
+ '</dl>'
+ '<a href="#top">_test_back_to_top</a>'
+ '</div>'
+ '</root>')
+ # Test for groups.
+ self.setUp()
+ policy['type'] = 'group'
+ self.writer._AddPolicySection(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<div style="margin-left: 0px">'
+ '<h2><a name="PolicyName"/>PolicyCaption</h2>'
+ '<div style="style_div.group_desc;">PolicyDesc</div>'
+ '<a href="#top">_test_back_to_top</a>'
+ '</div>'
+ '</root>')
+
+ def testAddPolicySectionWithAtomicGroup(self):
+ # Test if policy details are correctly added to the document.
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'caption':
+ 'PolicyCaption',
+ 'desc':
+ 'PolicyDesc',
+ 'type':
+ 'string',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'win',
+ 'since_version': '7',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'mac',
+ 'since_version': '7',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome_os',
+ 'platform': 'chrome_os',
+ 'since_version': '7',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value':
+ 'False',
+ 'atomic_group':
+ 'PolicyGroup'
+ }
+ self.writer._AddPolicySection(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<div style="margin-left: 0px">'
+ '<h3><a name="PolicyName"/>PolicyName</h3>'
+ '<span>PolicyCaption</span>'
+ '<dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>String [Windows:REG_SZ]</dd>'
+ '<dt style="style_dt;">_test_win_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKey\\PolicyName</dd>'
+ '<dt style="style_dt;">_test_oma_uri</dt>'
+ '<dd style="style_.monospace;">.\\Device\\Vendor\\MSFT\\Policy\\Config\\Chrome~Policy~chromium\\PolicyName</dd>'
+ '<dt style="style_dt;">_test_chrome_os_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKeyCrOS\\PolicyName</dd>'
+ '<dt style="style_dt;">_test_mac_linux_pref_name</dt>'
+ '<dd style="style_.monospace;">PolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome (Windows) ..7..</li>'
+ '<li>Chrome (Mac) ..7..</li>'
+ '<li>Chrome OS (Chrome OS) ..7..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt>'
+ '<dd><p>PolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>&quot;False&quot;'
+ '<dl><dt>Windows (Intune):</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;enabled/&gt;</dd>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;data id=&quot;PolicyName&quot; value=&quot;False&quot;/&gt;</dd></dl>'
+ '</dd>'
+ '<dt style="style_dt;">_test_policy_atomic_group</dt>'
+ '<dd>_test_policy_in_atomic_group <a href="./policy-list-3/atomic_groups#PolicyGroup">PolicyGroup</a></dd>'
+ '</dl>'
+ '<a href="#top">_test_back_to_top</a>'
+ '</div>'
+ '</root>')
+
+ def testAddPolicySectionForWindowsOnly(self):
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'caption':
+ 'PolicyCaption',
+ 'desc':
+ 'PolicyDesc',
+ 'type':
+ 'int',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'win',
+ 'since_version': '33',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value':
+ 123
+ }
+ self.writer._AddPolicySection(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<div style="margin-left: 0px">'
+ '<h3><a name="PolicyName"/>PolicyName</h3>'
+ '<span>PolicyCaption</span>'
+ '<dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>Integer [Windows:REG_DWORD]</dd>'
+ '<dt style="style_dt;">_test_win_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKey\\PolicyName</dd>'
+ '<dt style="style_dt;">_test_oma_uri</dt>'
+ '<dd style="style_.monospace;">.\\Device\\Vendor\\MSFT\\Policy\\Config\\Chrome~Policy~chromium\\PolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome (Windows) ..33..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt>'
+ '<dd><p>PolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>0x0000007b (Windows)'
+ '<dl><dt>Windows (Intune):</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;enabled/&gt;</dd>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;data id=&quot;PolicyName&quot; value=&quot;123&quot;/&gt;</dd></dl>'
+ '</dd>'
+ '</dl>'
+ '<a href="#top">_test_back_to_top</a>'
+ '</div>'
+ '</root>')
+
+ def testAddPolicySectionForWindows7Only(self):
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'caption':
+ 'PolicyCaption',
+ 'desc':
+ 'PolicyDesc',
+ 'type':
+ 'int',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'win7',
+ 'since_version': '33',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value':
+ 123
+ }
+ self.writer._AddPolicySection(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<div style="margin-left: 0px">'
+ '<h3><a name="PolicyName"/>PolicyName</h3>'
+ '<span>PolicyCaption</span>'
+ '<dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>Integer [Windows:REG_DWORD]</dd>'
+ '<dt style="style_dt;">_test_win_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKey\\PolicyName</dd>'
+ '<dt style="style_dt;">_test_oma_uri</dt>'
+ '<dd style="style_.monospace;">.\\Device\\Vendor\\MSFT\\Policy\\Config\\Chrome~Policy~chromium\\PolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome (Windows 7) ..33..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt>'
+ '<dd><p>PolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>0x0000007b (Windows)'
+ '<dl><dt>Windows (Intune):</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;enabled/&gt;</dd>'
+ '<dd style="style_.monospace;style_.pre-wrap;">&lt;data id=&quot;PolicyName&quot; value=&quot;123&quot;/&gt;</dd></dl>'
+ '</dd>'
+ '</dl>'
+ '<a href="#top">_test_back_to_top</a>'
+ '</div>'
+ '</root>')
+
+ def testAddPolicySectionForMacOnly(self):
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'caption':
+ 'PolicyCaption',
+ 'desc':
+ 'PolicyDesc',
+ 'type':
+ 'int',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'mac',
+ 'since_version': '33',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value':
+ 123
+ }
+ self.writer._AddPolicySection(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<div style="margin-left: 0px">'
+ '<h3><a name="PolicyName"/>PolicyName</h3>'
+ '<span>PolicyCaption</span>'
+ '<dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>Integer</dd>'
+ '<dt style="style_dt;">_test_mac_linux_pref_name</dt>'
+ '<dd style="style_.monospace;">PolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome (Mac) ..33..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt>'
+ '<dd><p>PolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>123 (Mac)</dd>'
+ '</dl>'
+ '<a href="#top">_test_back_to_top</a>'
+ '</div>'
+ '</root>')
+
+ def testAddPolicySectionForLinuxOnly(self):
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'caption':
+ 'PolicyCaption',
+ 'desc':
+ 'PolicyDesc',
+ 'type':
+ 'int',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'linux',
+ 'since_version': '33',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value':
+ 123
+ }
+ self.writer._AddPolicySection(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<div style="margin-left: 0px">'
+ '<h3><a name="PolicyName"/>PolicyName</h3>'
+ '<span>PolicyCaption</span>'
+ '<dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>Integer</dd>'
+ '<dt style="style_dt;">_test_mac_linux_pref_name</dt>'
+ '<dd style="style_.monospace;">PolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome (Linux) ..33..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt>'
+ '<dd><p>PolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>123 (Linux)</dd>'
+ '</dl>'
+ '<a href="#top">_test_back_to_top</a>'
+ '</div>'
+ '</root>')
+
+ def testAddPolicySectionForAndroidOnly(self):
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'caption':
+ 'PolicyCaption',
+ 'desc':
+ 'PolicyDesc',
+ 'type':
+ 'int',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'android',
+ 'since_version': '33',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value':
+ 123
+ }
+ self.writer._AddPolicySection(self.doc_root, policy)
+ self.assertTrue(
+ self.writer.IsPolicyOrItemSupportedOnPlatform(policy, 'android'))
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<div style="margin-left: 0px">'
+ '<h3><a name="PolicyName"/>PolicyName</h3>'
+ '<span>PolicyCaption</span>'
+ '<dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>Integer</dd>'
+ '<dt style="style_dt;">_test_android_restriction_name</dt>'
+ '<dd style="style_.monospace;">PolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome (Android) ..33..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt>'
+ '<dd><p>PolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>123 (Android)</dd>'
+ '</dl>'
+ '<a href="#top">_test_back_to_top</a>'
+ '</div>'
+ '</root>')
+
+ def testAddDictionaryExample(self):
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'caption':
+ 'PolicyCaption',
+ 'desc':
+ 'PolicyDesc',
+ 'type':
+ 'dict',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'win',
+ 'since_version': '7',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'mac',
+ 'since_version': '7',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'linux',
+ 'since_version': '7',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value': {
+ "ProxyMode": "direct",
+ "List": ["1", "2", "3"],
+ "True": True,
+ "False": False,
+ "Integer": 123,
+ "DictList": [
+ {
+ "A": 1,
+ "B": 2,
+ },
+ {
+ "C": 3,
+ "D": 4,
+ },
+ ],
+ },
+ }
+ self.writer._AddDictionaryExample(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<dl style="style_dd dl;">'
+ '<dt>_test_example_value_win</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">MockKey\PolicyName = {\n'
+ ' &quot;DictList&quot;: [\n'
+ ' {\n'
+ ' &quot;A&quot;: 1, \n'
+ ' &quot;B&quot;: 2\n'
+ ' }, \n'
+ ' {\n'
+ ' &quot;C&quot;: 3, \n'
+ ' &quot;D&quot;: 4\n'
+ ' }\n'
+ ' ], \n'
+ ' &quot;False&quot;: false, \n'
+ ' &quot;Integer&quot;: 123, \n'
+ ' &quot;List&quot;: [\n'
+ ' &quot;1&quot;, \n'
+ ' &quot;2&quot;, \n'
+ ' &quot;3&quot;\n'
+ ' ], \n'
+ ' &quot;ProxyMode&quot;: &quot;direct&quot;, \n'
+ ' &quot;True&quot;: true\n'
+ '}'
+ '</dd>'
+ '<dt>Android/Linux:</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">PolicyName: {\n'
+ ' &quot;DictList&quot;: [\n'
+ ' {\n'
+ ' &quot;A&quot;: 1, \n'
+ ' &quot;B&quot;: 2\n'
+ ' }, \n'
+ ' {\n'
+ ' &quot;C&quot;: 3, \n'
+ ' &quot;D&quot;: 4\n'
+ ' }\n'
+ ' ], \n'
+ ' &quot;False&quot;: false, \n'
+ ' &quot;Integer&quot;: 123, \n'
+ ' &quot;List&quot;: [\n'
+ ' &quot;1&quot;, \n'
+ ' &quot;2&quot;, \n'
+ ' &quot;3&quot;\n'
+ ' ], \n'
+ ' &quot;ProxyMode&quot;: &quot;direct&quot;, \n'
+ ' &quot;True&quot;: true\n'
+ '}'
+ '</dd>'
+ '<dt>Mac:</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ '&lt;key&gt;PolicyName&lt;/key&gt;\n'
+ '&lt;dict&gt;\n'
+ ' &lt;key&gt;DictList&lt;/key&gt;\n'
+ ' &lt;array&gt;\n'
+ ' &lt;dict&gt;\n'
+ ' &lt;key&gt;A&lt;/key&gt;\n'
+ ' &lt;integer&gt;1&lt;/integer&gt;\n'
+ ' &lt;key&gt;B&lt;/key&gt;\n'
+ ' &lt;integer&gt;2&lt;/integer&gt;\n'
+ ' &lt;/dict&gt;\n'
+ ' &lt;dict&gt;\n'
+ ' &lt;key&gt;C&lt;/key&gt;\n'
+ ' &lt;integer&gt;3&lt;/integer&gt;\n'
+ ' &lt;key&gt;D&lt;/key&gt;\n'
+ ' &lt;integer&gt;4&lt;/integer&gt;\n'
+ ' &lt;/dict&gt;\n'
+ ' &lt;/array&gt;\n'
+ ' &lt;key&gt;False&lt;/key&gt;\n'
+ ' &lt;false/&gt;\n'
+ ' &lt;key&gt;Integer&lt;/key&gt;\n'
+ ' &lt;integer&gt;123&lt;/integer&gt;\n'
+ ' &lt;key&gt;List&lt;/key&gt;\n'
+ ' &lt;array&gt;\n'
+ ' &lt;string&gt;1&lt;/string&gt;\n'
+ ' &lt;string&gt;2&lt;/string&gt;\n'
+ ' &lt;string&gt;3&lt;/string&gt;\n'
+ ' &lt;/array&gt;\n'
+ ' &lt;key&gt;ProxyMode&lt;/key&gt;\n'
+ ' &lt;string&gt;direct&lt;/string&gt;\n'
+ ' &lt;key&gt;True&lt;/key&gt;\n'
+ ' &lt;true/&gt;\n'
+ '&lt;/dict&gt;'
+ '</dd>'
+ '</dl>'
+ '</root>')
+
+ def testAddExternalExample(self):
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'caption':
+ 'PolicyCaption',
+ 'desc':
+ 'PolicyDesc',
+ 'type':
+ 'external',
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'win',
+ 'since_version': '7',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'mac',
+ 'since_version': '7',
+ 'until_version': '',
+ },
+ {
+ 'product': 'chrome',
+ 'platform': 'linux',
+ 'since_version': '7',
+ 'until_version': '',
+ }],
+ 'features': {
+ 'dynamic_refresh': False
+ },
+ 'example_value': {
+ "url": "https://example.com/avatar.jpg",
+ "hash": "deadbeef",
+ },
+ }
+ self.writer._AddDictionaryExample(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<dl style="style_dd dl;">'
+ '<dt>_test_example_value_win</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">MockKey\PolicyName = {\n'
+ ' &quot;hash&quot;: &quot;deadbeef&quot;, \n'
+ ' &quot;url&quot;: &quot;https://example.com/avatar.jpg&quot;\n'
+ '}'
+ '</dd>'
+ '<dt>Android/Linux:</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">PolicyName: {\n'
+ ' &quot;hash&quot;: &quot;deadbeef&quot;, \n'
+ ' &quot;url&quot;: &quot;https://example.com/avatar.jpg&quot;\n'
+ '}'
+ '</dd>'
+ '<dt>Mac:</dt>'
+ '<dd style="style_.monospace;style_.pre-wrap;">'
+ '&lt;key&gt;PolicyName&lt;/key&gt;\n'
+ '&lt;dict&gt;\n'
+ ' &lt;key&gt;hash&lt;/key&gt;\n'
+ ' &lt;string&gt;deadbeef&lt;/string&gt;\n'
+ ' &lt;key&gt;url&lt;/key&gt;\n'
+ ' &lt;string&gt;https://example.com/avatar.jpg&lt;/string&gt;\n'
+ '&lt;/dict&gt;'
+ '</dd>'
+ '</dl>'
+ '</root>')
+
+ def testParagraphs(self):
+ text = 'Paragraph 1\n\nParagraph 2\n\nParagraph 3'
+ self.writer._AddParagraphs(self.doc_root, text)
+ self.assertEquals(
+ self.doc_root.toxml(),
+ '<root><p>Paragraph 1</p><p>Paragraph 2</p><p>Paragraph 3</p></root>')
+
+ def testGoogleCloudChromeOsPolicies(self):
+ # Tests whether Chrome OS policies with management type 'google_cloud'
+ # don't print example values etc. since they are managed through Google's
+ # Admin console, not Active Directory GPO.
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'caption':
+ 'PolicyCaption',
+ 'desc':
+ 'PolicyDesc',
+ 'type':
+ 'int',
+ 'features': {},
+ 'example_value':
+ 42,
+ 'supported_on': [{
+ 'product': 'chrome_os',
+ 'platform': 'chrome_os',
+ 'since_version': '8',
+ 'until_version': '',
+ }],
+ 'supported_chrome_os_management': ['google_cloud']
+ }
+ self.writer._AddPolicySection(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<div style="margin-left: 0px">'
+ '<h3><a name="PolicyName"/>PolicyName</h3>'
+ '<span>PolicyCaption</span>'
+ '<dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>Integer</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome OS (Chrome OS) ..8..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd></dd>'
+ '<dt style="style_dt;">_test_description</dt>'
+ '<dd><p>PolicyDesc</p></dd>'
+ '</dl>'
+ '<a href="#top">_test_back_to_top</a>'
+ '</div>'
+ '</root>')
+
+ def testActiveDirectoryChromeOsPolicies(self):
+ # Tests whether Chrome OS policies with management type 'active_directory'
+ # print example values etc.
+ policy = {
+ 'name':
+ 'PolicyName',
+ 'caption':
+ 'PolicyCaption',
+ 'desc':
+ 'PolicyDesc',
+ 'type':
+ 'int',
+ 'features': {},
+ 'example_value':
+ 42,
+ 'supported_on': [{
+ 'product': 'chrome_os',
+ 'platform': 'chrome_os',
+ 'since_version': '8',
+ 'until_version': '',
+ }],
+ 'supported_chrome_os_management': ['active_directory']
+ }
+ self.writer._AddPolicySection(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(), '<root>'
+ '<div style="margin-left: 0px">'
+ '<h3><a name="PolicyName"/>PolicyName</h3>'
+ '<span>PolicyCaption</span>'
+ '<dl>'
+ '<dt style="style_dt;">_test_data_type</dt>'
+ '<dd>Integer [Windows:REG_DWORD]</dd>'
+ '<dt style="style_dt;">_test_chrome_os_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKeyCrOS\\PolicyName</dd>'
+ '<dt style="style_dt;">_test_supported_on</dt>'
+ '<dd>'
+ '<ul style="style_ul;">'
+ '<li>Chrome OS (Chrome OS) ..8..</li>'
+ '</ul>'
+ '</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd></dd>'
+ '<dt style="style_dt;">_test_description</dt>'
+ '<dd><p>PolicyDesc</p></dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>0x0000002a (Windows)</dd>'
+ '</dl>'
+ '<a href="#top">_test_back_to_top</a>'
+ '</div>'
+ '</root>')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/google_adml_writer.py b/chromium/components/policy/tools/template_writers/writers/google_adml_writer.py
new file mode 100755
index 00000000000..a126d34096b
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/google_adml_writer.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+# 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.
+
+from writers import template_writer
+
+
+def GetWriter(config):
+ '''Factory method for instanciating the GoogleADMLWriter. Every Writer needs a
+ GetWriter method because the TemplateFormatter uses this method to
+ instantiate a Writer.
+ '''
+ return GoogleADMLWriter(None, config) # platforms unused
+
+
+class GoogleADMLWriter(template_writer.TemplateWriter):
+ '''Simple writer that writes fixed google.adml files.
+ '''
+
+ def WriteTemplate(self, template):
+ '''Returns the contents of the google.adml file. It's independent of
+ policy_templates.json.
+ '''
+
+ return '''<?xml version="1.0" ?>
+<policyDefinitionResources revision="1.0" schemaVersion="1.0">
+ <displayName/>
+ <description/>
+ <resources>
+ <stringTable>
+ <string id="google">Google</string>
+ </stringTable>
+ </resources>
+</policyDefinitionResources>
+'''
diff --git a/chromium/components/policy/tools/template_writers/writers/google_adml_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/google_adml_writer_unittest.py
new file mode 100755
index 00000000000..3170f57f5f2
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/google_adml_writer_unittest.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+# 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.
+"""Unittests for writers.google_adml_writer."""
+
+import unittest
+from writers import google_adml_writer
+
+
+class GoogleAdmlWriterUnittest(unittest.TestCase):
+
+ def setUp(self):
+ self.writer = google_adml_writer.GetWriter(None) # Config unused
+
+ def testGoogleAdml(self):
+ output = self.writer.WriteTemplate(None) # Template unused
+
+ # No point to duplicate the full XML.
+ self.assertTrue('<string id="google">Google</string>' in output)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/google_admx_writer.py b/chromium/components/policy/tools/template_writers/writers/google_admx_writer.py
new file mode 100755
index 00000000000..e1f885ff600
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/google_admx_writer.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+# 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.
+
+from writers import template_writer
+
+
+def GetWriter(config):
+ '''Factory method for instanciating the GoogleADMXWriter. Every Writer needs a
+ GetWriter method because the TemplateFormatter uses this method to
+ instantiate a Writer.
+ '''
+ return GoogleADMXWriter(None, config) # platforms unused
+
+
+class GoogleADMXWriter(template_writer.TemplateWriter):
+ '''Simple writer that writes fixed google.admx files.
+ '''
+
+ def WriteTemplate(self, template):
+ '''Returns the contents of the google.admx file. It's independent of
+ policy_templates.json.
+ '''
+
+ return '''<?xml version="1.0" ?>
+<policyDefinitions revision="1.0" schemaVersion="1.0">
+ <policyNamespaces>
+ <target namespace="Google.Policies" prefix="Google"/>
+ </policyNamespaces>
+ <resources minRequiredRevision="1.0" />
+ <categories>
+ <category displayName="$(string.google)" name="Cat_Google"/>
+ </categories>
+</policyDefinitions>
+'''
diff --git a/chromium/components/policy/tools/template_writers/writers/google_admx_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/google_admx_writer_unittest.py
new file mode 100755
index 00000000000..f4f1e6dcdc2
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/google_admx_writer_unittest.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+# 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.
+"""Unittests for writers.google_admx_writer."""
+
+import unittest
+from writers import google_admx_writer
+
+
+class GoogleAdmxWriterUnittest(unittest.TestCase):
+
+ def setUp(self):
+ self.writer = google_admx_writer.GetWriter(None) # Config unused
+
+ def testGoogleAdmx(self):
+ output = self.writer.WriteTemplate(None) # Template unused
+
+ # No point to duplicate the full XML.
+ self.assertTrue('namespace="Google.Policies"' in output)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/gpo_editor_writer.py b/chromium/components/policy/tools/template_writers/writers/gpo_editor_writer.py
new file mode 100755
index 00000000000..ff36b98df5d
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/gpo_editor_writer.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 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.
+
+from writers import template_writer
+
+
+class GpoEditorWriter(template_writer.TemplateWriter):
+ '''Abstract class for ADM and ADMX writers.
+
+ It includes deprecated policies in its output, and places them in a dedicated
+ 'DeprecatedPolicies' group. Every deprecated policy has the same description.
+
+ It is a superclass for AdmWriter and AdmxWriter.
+ '''
+
+ def IsDeprecatedPolicySupported(self, policy):
+ # Include deprecated policies in the output.
+ return True
+
+ def IsVersionSupported(self, policy, supported_on):
+ # Include deprecated policies in the 'DeprecatedPolicies' group, even if
+ # they aren't supported anymore.
+ major_version = self._GetChromiumMajorVersion()
+ if not major_version:
+ return True
+
+ since_version = supported_on.get('since_version', None)
+
+ return not since_version or major_version >= int(since_version)
+
+ def IsPolicyOnWin7Only(self, policy):
+ ''' Returns true if the policy is supported on win7 only.'''
+ for suppported_on in policy.get('supported_on', []):
+ if 'win7' == suppported_on.get('platform', []):
+ return True
+ return False
+
+ def _IsRemovedPolicy(self, policy):
+ major_version = self._GetChromiumMajorVersion()
+ for supported_on in policy.get('supported_on', []):
+ if '*' in self.platforms or supported_on['platform'] in self.platforms:
+ until_version = supported_on.get('until_version', None)
+ if not until_version or major_version <= int(until_version):
+ # The policy is still supported, return False.
+ return False
+ # No platform+version combo supports this version, return True.
+ return True
+
+ def _FilterPolicies(self, predicate, policy_list):
+ filtered_policies = []
+ for policy in policy_list:
+ if policy['type'] == 'group':
+ for p in policy['policies']:
+ if predicate(p):
+ filtered_policies.append(p)
+ else:
+ if predicate(policy):
+ filtered_policies.append(policy)
+ return filtered_policies
+
+ def _RemovePoliciesFromList(self, policy_list, policies_to_remove):
+ '''Remove policies_to_remove from groups and the top-level list.'''
+ # We only compare the 'name' property.
+ policies_to_remove = set([p['name'] for p in policies_to_remove])
+
+ # Remove from top-level list.
+ policy_list[:] = [
+ p for p in policy_list if p['name'] not in policies_to_remove
+ ]
+
+ # Remove from groups.
+ for group in policy_list:
+ if group['type'] != 'group':
+ continue
+ group['policies'] = [
+ p for p in group['policies'] if p['name'] not in policies_to_remove
+ ]
+
+ # Remove empty groups.
+ policy_list[:] = [
+ p for p in policy_list if p['type'] != 'group' or p['policies']
+ ]
+
+ def _MovePolicyGroup(self, policy_list, predicate, policy_desc, group):
+ '''Remove policies from |policy_list| that satisfy |predicate| and add them
+ to |group|.'''
+ filtered_policies = self._FilterPolicies(predicate, policy_list)
+ self._RemovePoliciesFromList(policy_list, filtered_policies)
+
+ for p in filtered_policies:
+ p['desc'] = policy_desc
+
+ group['policies'] = filtered_policies
+
+ def PreprocessPolicies(self, policy_list):
+ '''Put policies under the DeprecatedPolicies/RemovedPolicies groups.'''
+ removed_policies_group = {
+ 'name': 'RemovedPolicies',
+ 'type': 'group',
+ 'caption': self.messages['removed_policy_group_caption']['text'],
+ 'desc': self.messages['removed_policy_group_desc']['text'],
+ 'policies': []
+ }
+ self._MovePolicyGroup(
+ policy_list,
+ lambda p: self._IsRemovedPolicy(p),
+ self.messages['removed_policy_desc']['text'],
+ removed_policies_group)
+
+ deprecated_policies_group = {
+ 'name': 'DeprecatedPolicies',
+ 'type': 'group',
+ 'caption': self.messages['deprecated_policy_group_caption']['text'],
+ 'desc': self.messages['deprecated_policy_group_desc']['text'],
+ 'policies': []
+ }
+ self._MovePolicyGroup(
+ policy_list,
+ lambda p: p.get('deprecated', False),
+ self.messages['deprecated_policy_desc']['text'],
+ deprecated_policies_group)
+
+ policy_list.append(deprecated_policies_group)
+ policy_list.append(removed_policies_group)
+
+ return super(GpoEditorWriter, self).SortPoliciesGroupsFirst(policy_list)
diff --git a/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer.py b/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer.py
new file mode 100755
index 00000000000..a5a61f9d404
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer.py
@@ -0,0 +1,202 @@
+#!/usr/bin/env python3
+# Copyright 2020 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.
+
+from xml.dom import minidom
+import json
+from writers import xml_formatted_writer
+
+_POLICY_TYPE_TO_XML_TAG = {
+ 'string': 'string',
+ 'int': 'integer',
+ 'int-enum': 'integer',
+ 'string-enum': 'string',
+ 'string-enum-list': 'stringArray',
+ 'main': 'boolean',
+ 'list': 'stringArray',
+ 'dict': 'string',
+}
+
+_POLICY_TYPE_TO_INPUT_TYPE = {
+ 'string': 'input',
+ 'int': 'input',
+ 'int-enum': 'select',
+ 'string-enum': 'select',
+ 'string-enum-list': 'multiselect',
+ 'main': 'checkbox',
+ 'list': 'list',
+ 'dict': 'input'
+}
+
+_JSON_SCHEMA_TYPES = [
+ "string", "number", "integer", "boolean", "null", "object", "array"
+]
+
+
+class Error(Exception):
+ pass
+
+
+def _ParseSchemaTypeValueToString(value, type):
+ '''Parses the value of a given JSON schema type to a string.
+ '''
+ if type not in _JSON_SCHEMA_TYPES:
+ raise Error('schema type "{}" not supported'.format(type))
+
+ if type == 'integer':
+ return '{0:d}'.format(value)
+
+ # Use the default string parser.
+ return str(value)
+
+
+def GetWriter(config):
+ '''Factory method for instanciating the IOSAppConfigWriter. Every Writer needs
+ a GetWriter method because the TemplateFormatter uses this method to
+ instantiate a Writer.
+ '''
+ return IOSAppConfigWriter(['ios'], config) # platforms unused
+
+
+class IOSAppConfigWriter(xml_formatted_writer.XMLFormattedWriter):
+ '''Simple writer that writes app_config.xml files.
+ '''
+
+ def _WritePolicyPresentation(self, policy, field_group):
+ element_type = _POLICY_TYPE_TO_INPUT_TYPE[policy['type']]
+ if element_type:
+ attributes = {'type': element_type, 'keyName': policy['name']}
+ field = self.AddElement(field_group, 'field', attributes)
+ self._AddLocalizedElement(field, 'label', policy['caption'])
+ self._AddLocalizedElement(field, 'description', policy['desc'])
+
+ if 'enum' in policy['type']:
+ options = self.AddElement(field, 'options', {})
+ for item in policy['items']:
+ self._AddLocalizedElement(
+ options, 'option', str(item['caption']), {
+ 'value':
+ _ParseSchemaTypeValueToString(item['value'],
+ policy['schema']['type'])
+ })
+
+ def _AddLocalizedElement(self,
+ parent,
+ element_type,
+ text,
+ attributes={},
+ localization={'value': 'en-US'}):
+ item = self.AddElement(parent, element_type, attributes)
+ localized = self.AddElement(item, 'language', localization)
+ self.AddText(localized, text)
+
+ def _WritePresentation(self, policy_list):
+ groups = [policy for policy in policy_list if policy['type'] == 'group']
+ policies_without_group = [
+ policy for policy in policy_list if policy['type'] != 'group'
+ ]
+ for policy in groups:
+ child_policies = self._GetPoliciesForWriter(policy)
+ if child_policies:
+ field_group = self.AddElement(self._presentation, 'fieldGroup', {})
+ self._AddLocalizedElement(field_group, 'name', policy['caption'])
+ for child_policy in child_policies:
+ self._WritePolicyPresentation(child_policy, field_group)
+ for policy in self._GetPoliciesForWriter(
+ {'policies': policies_without_group}):
+ self._WritePolicyPresentation(policy, self._presentation)
+
+ def _WritePolicyDefaultValue(self, parent, policy):
+ if 'default' in policy:
+ default_value = self.AddElement(parent, 'defaultValue', {})
+ value = self.AddElement(default_value, 'value', {})
+ if policy['type'] == 'main':
+ if policy['default'] == True:
+ self.AddText(value, 'true')
+ elif policy['default'] == False:
+ self.AddText(value, 'false')
+ elif policy['type'] in ['list', 'string-enum-list']:
+ for v in policy['default']:
+ if value == None:
+ value = self.AddElement(default_value, 'value', {})
+ self.AddText(value, v)
+ value = None
+ else:
+ self.AddText(value, policy['default'])
+
+ def _WritePolicyConstraint(self, parent, policy):
+ attrs = {'nullable': 'true'}
+ if 'schema' in policy:
+ if 'minimum' in policy['schema']:
+ attrs['min'] = _ParseSchemaTypeValueToString(
+ policy['schema']['minimum'], policy['schema']['type'])
+ if 'maximum' in policy['schema']:
+ attrs['max'] = _ParseSchemaTypeValueToString(
+ policy['schema']['maximum'], policy['schema']['type'])
+
+ constraint = self.AddElement(parent, 'constraint', attrs)
+ if 'enum' in policy['type']:
+ values_element = self.AddElement(constraint, 'values', {})
+ for v in policy['schema']['enum']:
+ value = self.AddElement(values_element, 'value', {})
+ self.AddText(value,
+ _ParseSchemaTypeValueToString(v, policy['schema']['type']))
+
+ def IsFuturePolicySupported(self, policy):
+ # For now, include all future policies in appconfig.xml.
+ return True
+
+ def CreateDocument(self):
+ dom_impl = minidom.getDOMImplementation('')
+ return dom_impl.createDocument('http://www.w3.org/2001/XMLSchema-instance',
+ 'managedAppConfiguration', None)
+
+ def WriteTemplate(self, template):
+ self.messages = template['messages']
+ self.Init()
+ template['policy_definitions'] = \
+ self.PreprocessPolicies(template['policy_definitions'])
+ self.BeginTemplate()
+ self.WritePolicies(template['policy_definitions'])
+ self._WritePresentation(template['policy_definitions'])
+ self.EndTemplate()
+
+ return self.GetTemplateText()
+
+ def BeginTemplate(self):
+ self._app_config.attributes[
+ 'xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance'
+ schema_location = 'https://storage.googleapis.com/appconfig-media/appconfigschema.xsd'
+ self._app_config.attributes[
+ 'xsi:noNamespaceSchemaLocation'] = schema_location
+
+ version = self.AddElement(self._app_config, 'version', {})
+ milestone = self.config['version'].split(".", 1)[0]
+ self.AddText(version, milestone)
+
+ bundle_id = self.AddElement(self._app_config, 'bundleId', {})
+ self.AddText(bundle_id, self.config['bundle_id'])
+ self._policies = self.AddElement(self._app_config, 'dict', {})
+ self._presentation = self.AddElement(self._app_config, 'presentation',
+ {'defaultLocale': 'en-US'})
+
+ def WritePolicy(self, policy):
+ element_type = _POLICY_TYPE_TO_XML_TAG[policy['type']]
+ if element_type:
+ attributes = {'keyName': policy['name']}
+ # Add a "<!--FUTURE POLICY-->" comment before future policies.
+ if 'future_on' in policy:
+ for config in policy['future_on']:
+ if config['platform'] == 'ios':
+ self.AddComment(self._policies, 'FUTURE POLICY')
+ policy_element = self.AddElement(self._policies, element_type, attributes)
+ self._WritePolicyDefaultValue(policy_element, policy)
+ self._WritePolicyConstraint(policy_element, policy)
+
+ def Init(self):
+ self._doc = self.CreateDocument()
+ self._app_config = self._doc.documentElement
+
+ def GetTemplateText(self):
+ return self.ToPrettyXml(self._doc)
diff --git a/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py
new file mode 100755
index 00000000000..8774f2e84a3
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py
@@ -0,0 +1,434 @@
+#!/usr/bin/env python3
+# Copyright 2020 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 os
+import sys
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+import json
+import unittest
+
+from writers import writer_unittest_common
+
+
+class IOSAppConfigWriterUnitTests(writer_unittest_common.WriterUnittestCommon):
+ '''Unit tests for IOSAppConfigWriter.'''
+
+ def _GetTestPolicyTemplate(self, policy_definitions):
+ return '''
+{
+ 'policy_definitions': %s,
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {},
+}
+''' % (policy_definitions)
+
+ def _GetExpectedOutput(self, version, policy_definition, policy_presentation):
+ if policy_definition:
+ definition = '<dict>\n %s\n </dict>' % policy_definition
+ else:
+ definition = '<dict/>'
+ if policy_presentation:
+ presentation = '<presentation defaultLocale="en-US">\n %s\n </presentation>' % policy_presentation
+ else:
+ presentation = '<presentation defaultLocale="en-US"/>'
+
+ return '''<?xml version="1.0" ?>
+<managedAppConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://storage.googleapis.com/appconfig-media/appconfigschema.xsd">
+ <version>%s</version>
+ <bundleId>com.google.chrome.ios</bundleId>
+ %s
+ %s
+</managedAppConfiguration>''' % (version, definition, presentation)
+
+ def testStringPolicy(self):
+ policy_definition = json.dumps([{
+ 'name': 'string policy',
+ 'type': 'string',
+ 'supported_on': ['ios:80-'],
+ 'caption': 'string caption',
+ 'desc': 'string description'
+ }])
+ policy_json = self._GetTestPolicyTemplate(policy_definition)
+ expected_configuration = '''<string keyName="string policy">
+ <constraint nullable="true"/>
+ </string>'''
+ expected_presentation = '''<field keyName="string policy" type="input">
+ <label>
+ <language value="en-US">string caption</language>
+ </label>
+ <description>
+ <language value="en-US">string description</language>
+ </description>
+ </field>'''
+ expected = self._GetExpectedOutput('83', expected_configuration,
+ expected_presentation)
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0'
+ }, 'ios_app_config')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testIntPolicy(self):
+ policy_definition = json.dumps([{
+ 'name': 'IntPolicy',
+ 'type': 'int',
+ 'supported_on': ['ios:80-'],
+ 'caption': 'int caption',
+ 'desc': 'int description'
+ }])
+ policy_json = self._GetTestPolicyTemplate(policy_definition)
+ expected_configuration = '''<integer keyName="IntPolicy">
+ <constraint nullable="true"/>
+ </integer>'''
+ expected_presentation = '''<field keyName="IntPolicy" type="input">
+ <label>
+ <language value="en-US">int caption</language>
+ </label>
+ <description>
+ <language value="en-US">int description</language>
+ </description>
+ </field>'''
+ expected = self._GetExpectedOutput('83', expected_configuration,
+ expected_presentation)
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0'
+ }, 'ios_app_config')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testIntEnumPolicy(self):
+ policy_definition = json.dumps([{
+ 'name':
+ 'IntEnumPolicy',
+ 'type':
+ 'int-enum',
+ 'supported_on': ['ios:80-'],
+ 'caption':
+ 'int-enum caption',
+ 'desc':
+ 'int-enum description',
+ 'schema': {
+ 'type': 'integer',
+ 'enum': [0, 1],
+ },
+ 'items': [{
+ 'name': 'item0',
+ 'value': 0,
+ 'caption': 'item 0',
+ }, {
+ 'name': 'item1',
+ 'value': 1,
+ 'caption': 'item 1',
+ }]
+ }])
+ policy_json = self._GetTestPolicyTemplate(policy_definition)
+ expected_configuration = '''<integer keyName="IntEnumPolicy">
+ <constraint nullable="true">
+ <values>
+ <value>0</value>
+ <value>1</value>
+ </values>
+ </constraint>
+ </integer>'''
+ expected_presentation = '''<field keyName="IntEnumPolicy" type="select">
+ <label>
+ <language value="en-US">int-enum caption</language>
+ </label>
+ <description>
+ <language value="en-US">int-enum description</language>
+ </description>
+ <options>
+ <option value="0">
+ <language value="en-US">item 0</language>
+ </option>
+ <option value="1">
+ <language value="en-US">item 1</language>
+ </option>
+ </options>
+ </field>'''
+ expected = self._GetExpectedOutput('83', expected_configuration,
+ expected_presentation)
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0'
+ }, 'ios_app_config')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testStringEnumPolicy(self):
+ policy_definition = json.dumps([{
+ 'name':
+ 'StringEnumPolicy',
+ 'type':
+ 'string-enum',
+ 'supported_on': ['ios:80-'],
+ 'caption':
+ 'string-enum caption',
+ 'desc':
+ 'string-enum description',
+ 'schema': {
+ 'type': 'string',
+ 'enum': ['0', '1'],
+ },
+ 'items': [{
+ 'name': 'item0',
+ 'value': '0',
+ 'caption': 'item 0',
+ }, {
+ 'name': 'item1',
+ 'value': '1',
+ 'caption': 'item 1',
+ }]
+ }])
+ policy_json = self._GetTestPolicyTemplate(policy_definition)
+ expected_configuration = '''<string keyName="StringEnumPolicy">
+ <constraint nullable="true">
+ <values>
+ <value>0</value>
+ <value>1</value>
+ </values>
+ </constraint>
+ </string>'''
+ expected_presentation = '''<field keyName="StringEnumPolicy" type="select">
+ <label>
+ <language value="en-US">string-enum caption</language>
+ </label>
+ <description>
+ <language value="en-US">string-enum description</language>
+ </description>
+ <options>
+ <option value="0">
+ <language value="en-US">item 0</language>
+ </option>
+ <option value="1">
+ <language value="en-US">item 1</language>
+ </option>
+ </options>
+ </field>'''
+ expected = self._GetExpectedOutput('83', expected_configuration,
+ expected_presentation)
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0'
+ }, 'ios_app_config')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testStringEnumListPolicy(self):
+ policy_definition = json.dumps([{
+ 'name':
+ 'StringEnumListPolicy',
+ 'type':
+ 'string-enum-list',
+ 'supported_on': ['ios:80-'],
+ 'caption':
+ 'string-enum-list caption',
+ 'desc':
+ 'string-enum-list description',
+ 'schema': {
+ 'type': 'string',
+ 'enum': ['0', '1'],
+ },
+ 'items': [{
+ 'name': 'item0',
+ 'value': '0',
+ 'caption': 'item 0',
+ }, {
+ 'name': 'item1',
+ 'value': '1',
+ 'caption': 'item 1',
+ }]
+ }])
+ policy_json = self._GetTestPolicyTemplate(policy_definition)
+ expected_configuration = '''<stringArray keyName="StringEnumListPolicy">
+ <constraint nullable="true">
+ <values>
+ <value>0</value>
+ <value>1</value>
+ </values>
+ </constraint>
+ </stringArray>'''
+ expected_presentation = '''<field keyName="StringEnumListPolicy" type="multiselect">
+ <label>
+ <language value="en-US">string-enum-list caption</language>
+ </label>
+ <description>
+ <language value="en-US">string-enum-list description</language>
+ </description>
+ <options>
+ <option value="0">
+ <language value="en-US">item 0</language>
+ </option>
+ <option value="1">
+ <language value="en-US">item 1</language>
+ </option>
+ </options>
+ </field>'''
+ expected = self._GetExpectedOutput('83', expected_configuration,
+ expected_presentation)
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0'
+ }, 'ios_app_config')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testBooleanPolicy(self):
+ policy_definition = json.dumps([{
+ 'name': 'BooleanPolicy',
+ 'type': 'main',
+ 'supported_on': ['ios:80-'],
+ 'caption': 'boolean caption',
+ 'desc': 'boolean description'
+ }])
+ policy_json = self._GetTestPolicyTemplate(policy_definition)
+ expected_configuration = '''<boolean keyName="BooleanPolicy">
+ <constraint nullable="true"/>
+ </boolean>'''
+ expected_presentation = '''<field keyName="BooleanPolicy" type="checkbox">
+ <label>
+ <language value="en-US">boolean caption</language>
+ </label>
+ <description>
+ <language value="en-US">boolean description</language>
+ </description>
+ </field>'''
+ expected = self._GetExpectedOutput('83', expected_configuration,
+ expected_presentation)
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0'
+ }, 'ios_app_config')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testListPolicy(self):
+ policy_definition = json.dumps([{
+ 'name': 'ListPolicy',
+ 'type': 'list',
+ 'supported_on': ['ios:80-'],
+ 'caption': 'list caption',
+ 'desc': 'list description'
+ }])
+ policy_json = self._GetTestPolicyTemplate(policy_definition)
+ expected_configuration = '''<stringArray keyName="ListPolicy">
+ <constraint nullable="true"/>
+ </stringArray>'''
+ expected_presentation = '''<field keyName="ListPolicy" type="list">
+ <label>
+ <language value="en-US">list caption</language>
+ </label>
+ <description>
+ <language value="en-US">list description</language>
+ </description>
+ </field>'''
+ expected = self._GetExpectedOutput('83', expected_configuration,
+ expected_presentation)
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0'
+ }, 'ios_app_config')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testDictPolicy(self):
+ policy_definition = json.dumps([{
+ 'name': 'DictPolicy',
+ 'type': 'dict',
+ 'supported_on': ['ios:80-'],
+ 'caption': 'dict caption',
+ 'desc': 'dict description'
+ }])
+ policy_json = self._GetTestPolicyTemplate(policy_definition)
+ # Dict policies are not supported by the appconfig.xml format, therefore
+ # they are treated as JSON strings.
+ expected_configuration = '''<string keyName="DictPolicy">
+ <constraint nullable="true"/>
+ </string>'''
+ expected_presentation = '''<field keyName="DictPolicy" type="input">
+ <label>
+ <language value="en-US">dict caption</language>
+ </label>
+ <description>
+ <language value="en-US">dict description</language>
+ </description>
+ </field>'''
+ expected = self._GetExpectedOutput('83', expected_configuration,
+ expected_presentation)
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0'
+ }, 'ios_app_config')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testFuturePolicy(self):
+ policy_definition = json.dumps([{
+ 'name': 'FuturePolicy',
+ 'type': 'string',
+ 'future_on': ['ios'],
+ 'caption': 'string caption',
+ 'desc': 'string description'
+ }])
+ policy_json = self._GetTestPolicyTemplate(policy_definition)
+ expected_configuration = '''<!--FUTURE POLICY-->
+ <string keyName="FuturePolicy">
+ <constraint nullable="true"/>
+ </string>'''
+ expected_presentation = '''<field keyName="FuturePolicy" type="input">
+ <label>
+ <language value="en-US">string caption</language>
+ </label>
+ <description>
+ <language value="en-US">string description</language>
+ </description>
+ </field>'''
+ expected = self._GetExpectedOutput('83', expected_configuration,
+ expected_presentation)
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0'
+ }, 'ios_app_config')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testPolicyWithGroup(self):
+ policy_definition = json.dumps([{
+ 'name': 'PolicyInGroup',
+ 'type': 'string',
+ 'supported_on': ['ios:80-'],
+ 'caption': 'string caption',
+ 'desc': 'string description'
+ }, {
+ 'name': 'DummyGroup',
+ 'type': 'group',
+ 'caption': 'Dummy Group',
+ 'desc': 'Dummy group for testing',
+ 'policies': ['PolicyInGroup']
+ }])
+ policy_json = self._GetTestPolicyTemplate(policy_definition)
+ expected_configuration = '''<string keyName="PolicyInGroup">
+ <constraint nullable="true"/>
+ </string>'''
+ expected_presentation = '''<fieldGroup>
+ <name>
+ <language value="en-US">Dummy Group</language>
+ </name>
+ <field keyName="PolicyInGroup" type="input">
+ <label>
+ <language value="en-US">string caption</language>
+ </label>
+ <description>
+ <language value="en-US">string description</language>
+ </description>
+ </field>
+ </fieldGroup>'''
+ expected = self._GetExpectedOutput('83', expected_configuration,
+ expected_presentation)
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0'
+ }, 'ios_app_config')
+ self.assertEquals(output.strip(), expected.strip())
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/jamf_writer.py b/chromium/components/policy/tools/template_writers/writers/jamf_writer.py
new file mode 100755
index 00000000000..f8379c0bca9
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/jamf_writer.py
@@ -0,0 +1,239 @@
+#!/usr/bin/env python3
+# Copyright 2020 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 copy
+import json
+
+from writers import template_writer
+
+
+def GetWriter(config):
+ '''Factory method for creating JamfWriter objects.
+ See the constructor of TemplateWriter for description of
+ arguments.
+ '''
+ return JamfWriter(['mac', 'ios'], config)
+
+
+class JamfWriter(template_writer.TemplateWriter):
+ '''Simple writer that writes a jamf.json file.
+ '''
+ MAX_RECURSIVE_FIELDS_DEPTH = 5
+ TYPE_TO_INPUT = {
+ 'string': 'string',
+ 'int': 'integer',
+ 'int-enum': 'integer',
+ 'string-enum': 'string',
+ 'string-enum-list': 'array',
+ 'main': 'boolean',
+ 'list': 'array',
+ 'dict': 'object',
+ 'external': 'object',
+ }
+
+ # Some policies are forced to a certain schema, so they bypass TYPE_TO_INPUT
+ POLICY_ID_TO_INPUT = {
+ 227: 'string', # ManagedBookmarks
+ 278: 'string', # ExtensionSettings
+ }
+
+
+ def WriteTemplate(self, template):
+ '''Writes the given template definition.
+
+ Args:
+ template: Template definition to write.
+
+ Returns:
+ Generated output for the passed template definition.
+ '''
+ self.messages = template['messages']
+ # Keep track of all items that can be referred to by an id.
+ # This is used for '$ref' fields in the policy templates.
+ ref_ids_schemas = {}
+ policies = []
+ for policy_def in template['policy_definitions']:
+ # Iterate over all policies, even the policies contained inside a policy
+ # group.
+ if policy_def['type'] == 'group':
+ policies += policy_def['policies']
+ else:
+ policies += [policy_def]
+
+ for policy in policies:
+ if policy['type'] == 'int-enum' or policy['type'] == 'string-enum':
+ self.RecordEnumIds(policy, ref_ids_schemas)
+ elif 'schema' in policy:
+ if 'id' in policy['schema']:
+ self.RecordKnownPropertyIds(policy['schema'], ref_ids_schemas)
+ if 'items' in policy['schema']:
+ self.RecordKnownPropertyIds(policy['schema']['items'],
+ ref_ids_schemas)
+ if 'properties' in policy['schema']:
+ self.RecordKnownPropertyIds(policy['schema']['properties'],
+ ref_ids_schemas)
+ if 'patternProperties' in policy['schema']:
+ self.RecordKnownPropertyIds(policy['schema']['patternProperties'],
+ ref_ids_schemas)
+
+ policies = [policy for policy in policies if self.IsPolicySupported(policy)]
+ output = {
+ 'title': self.config['bundle_id'],
+ 'version': self.config['version'].split(".", 1)[0],
+ 'description': self.config['app_name'],
+ 'options': {
+ 'remove_empty_properties': True
+ },
+ 'properties': {}
+ }
+
+ for policy in policies:
+ output['properties'][policy['name']] = {
+ 'title':
+ policy['name'],
+ 'description':
+ policy['caption'],
+ 'type':
+ self.TYPE_TO_INPUT[policy['type']],
+ 'links': [{
+ 'rel': self.messages['doc_policy_documentation']['text'],
+ 'href': self.config['doc_url'] + '#' + policy['name']
+ }]
+ }
+
+ policy_output = output['properties'][policy['name']]
+ if policy['id'] in self.POLICY_ID_TO_INPUT:
+ policy_output['type'] = self.POLICY_ID_TO_INPUT[policy['id']]
+
+ policy_type = policy_output['type']
+ if policy['type'] == 'int-enum' or policy['type'] == 'string-enum':
+ policy_output['options'] = {
+ 'enum_titles': [item['name'] for item in policy['items']]
+ }
+ policy_output['enum'] = [item['value'] for item in policy['items']]
+ elif policy['type'] == 'int' and 'schema' in policy:
+ if 'minimum' in policy['schema']:
+ policy_output['minimum'] = policy['schema']['minimum']
+ if 'maximum' in policy['schema']:
+ policy_output['maximum'] = policy['schema']['maximum']
+ elif policy['type'] == 'list':
+ policy_output['items'] = policy['schema']['items']
+ elif policy['type'] == 'string-enum-list' or policy[
+ 'type'] == 'int-enum-list':
+ policy_output['items'] = {
+ 'type': policy['schema']['items']['type'],
+ 'options': {
+ 'enum_titles': [item['name'] for item in policy['items']]
+ },
+ 'enum': [item['value'] for item in policy['items']]
+ }
+ elif policy_output['type'] == 'object' and policy['type'] != 'external':
+ policy_output['type'] = policy['schema']['type']
+ if policy_output['type'] == 'array':
+ policy_output['items'] = policy['schema']['items']
+ self.WriteRefItems(policy_output['items'], policy_output['items'], [],
+ ref_ids_schemas, set())
+ elif policy_output['type'] == 'object':
+ policy_output['properties'] = policy['schema']['properties']
+ self.WriteRefItems(policy_output['properties'],
+ policy_output['properties'], [], ref_ids_schemas,
+ set())
+
+ return json.dumps(output, indent=2, sort_keys=True, separators=(',', ': '))
+
+ def RecordEnumIds(self, policy, known_ids):
+ '''Writes the a dictionary mapping ids of enums that can be referred to by
+ '$ref' to their schema.
+
+ Args:
+ policy: The policy to scan for refs.
+ known_ids: The dictionary and output of all the known ids.
+ '''
+ if 'id' in policy['schema']:
+ known_ids[policy['schema']['id']] = {
+ 'type': policy['schema']['type'],
+ 'options': {
+ 'enum_titles': [item['name'] for item in policy['items']]
+ },
+ 'enum': [item['value'] for item in policy['items']]
+ }
+
+ def RecordKnownPropertyIds(self, obj, known_ids):
+ '''Writes the a dictionary mapping ids of schemas properties that can be
+ referred to by '$ref' to their schema.
+
+ Args:
+ obj: The object to scan for refs.
+ known_ids: The dictionary and output of all the known ids.
+ '''
+ if type(obj) is not dict:
+ return
+ if 'id' in obj:
+ known_ids[obj['id']] = obj
+ for value in obj.values():
+ self.RecordKnownPropertyIds(value, known_ids)
+
+ def WriteRefItems(self, root, obj, path_to_obj_parent, known_ids,
+ ids_in_ancestry):
+ '''Replaces all the '$ref' items by their actual value. Nested properties
+ are limited to a depth of MAX_RECURSIVE_FIELDS_DEPTH, after which the
+ recursive field is removed.
+
+ Args:
+ root: The root of the object tree to scan for refs.
+ obj: The current object being checked for ids.
+ path_to_obj_parent: A array of all the keys leading to the parent of |obj|
+ starting at |root|.
+ known_ids: The dictionary of all the known ids.
+ ids_in_ancestry: A list of ids found in the tree starting at root. Use to
+ keep nested fields in check.
+ '''
+ if type(obj) is not dict:
+ return
+ if 'id' in obj:
+ ids_in_ancestry.add(obj['id'])
+ # Make a copy of items since we are going to change |obj|.
+ for key, value in list(obj.items()):
+ if type(value) is not dict:
+ continue
+ if '$ref' in value:
+ # If the id is an ancestor, we have a nested field.
+ if value['$ref'] in ids_in_ancestry:
+ id = value['$ref']
+
+ last_obj = None
+ parent = root
+ grandparent = root
+
+ # Find the parent and grandparent of obj to create the |last_obj|
+ # which is the field where the nesting stops.
+ for i in range(0, len(path_to_obj_parent)):
+ if i + 1 < len(path_to_obj_parent):
+ grandparent = grandparent[path_to_obj_parent[i]]
+ else:
+ parent = grandparent[path_to_obj_parent[i]]
+ # Remove the link between grand parent and parent so we can have a
+ # copy of the object without nesting.
+ grandparent[path_to_obj_parent[i]] = None
+ del grandparent[path_to_obj_parent[i]]
+ # last_obj is a copy of the reference object without nesting.
+ last_obj = copy.deepcopy(known_ids[id])
+ # Re-establish the link between grand parent and parent.
+ grandparent[path_to_obj_parent[i]] = parent
+
+ del obj[key]
+ obj[key] = last_obj
+ # Create nested '$ref' objects with |last_obj| as the last object.
+ for count in range(1, self.MAX_RECURSIVE_FIELDS_DEPTH):
+ obj[key] = copy.deepcopy(known_ids[id])
+ obj_grandparent_ref = path_to_obj_parent[len(path_to_obj_parent) - 1]
+ else:
+ # If no nested field, simply assign the '$ref'.
+ obj[key] = dict(known_ids[value['$ref']])
+ self.WriteRefItems(root, obj[key], path_to_obj_parent + [key],
+ known_ids, ids_in_ancestry)
+ else:
+ self.WriteRefItems(root, value, path_to_obj_parent + [key], known_ids,
+ ids_in_ancestry)
diff --git a/chromium/components/policy/tools/template_writers/writers/jamf_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/jamf_writer_unittest.py
new file mode 100755
index 00000000000..1dc8fc5d114
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/jamf_writer_unittest.py
@@ -0,0 +1,383 @@
+#!/usr/bin/env python3
+# Copyright 2020 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 os
+import sys
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+import unittest
+
+import copy
+import json
+from writers import writer_unittest_common
+
+
+def _JsonFormat(input):
+ return json.dumps(input, indent=2, sort_keys=True, separators=(',', ': '))
+
+
+class JamfWriterUnitTests(writer_unittest_common.WriterUnittestCommon):
+ '''Unit tests for JamfWriter.'''
+
+ doc_url = 'https://chromeenterprise.google/policies/'
+
+ def _GetTestPolicyTemplate(self, policy_name, policy_type, schema_type,
+ policy_caption):
+ template = {
+ 'policy_definitions': [{
+ 'name':
+ policy_name,
+ 'id':
+ 1,
+ 'type':
+ policy_type,
+ 'supported_on': ['chrome.mac:*-'],
+ 'caption':
+ policy_caption,
+ 'desc':
+ '',
+ 'items': [{
+ 'name': 'title1',
+ 'value': 1,
+ 'caption': '',
+ 'type': 'integer'
+ }],
+ 'schema': {
+ 'type': schema_type,
+ 'id': 'enumid',
+ 'properties' if schema_type == 'object' else 'items': {
+ 'title':
+ 'title_obj' if schema_type == 'object' else 'title_array',
+ 'type': 'integer'
+ },
+ }
+ }],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {
+ 'doc_policy_documentation': {
+ 'text': 'Documentation for policy'
+ }
+ },
+ }
+ return _JsonFormat(template)
+
+ def _GetExpectedOutput(self, policy_name, policy_type, policy_caption,
+ initial_type, version):
+ output = {
+ 'description': 'Google Chrome',
+ 'options': {
+ 'remove_empty_properties': True
+ },
+ 'properties': {
+ policy_name: {
+ 'description':
+ policy_caption,
+ 'title':
+ policy_name,
+ 'type':
+ policy_type,
+ 'links': [{
+ 'rel': 'Documentation for policy',
+ 'href': self.doc_url + '#' + policy_name
+ }]
+ }
+ },
+ 'title': 'com.google.chrome.ios',
+ 'version': version
+ }
+ if initial_type == 'int-enum' or initial_type == 'string-enum':
+ output['properties'][policy_name]['enum'] = [1]
+ output['properties'][policy_name]['options'] = {'enum_titles': ['title1']}
+ if initial_type == 'string-enum-list' or initial_type == 'int-enum-list':
+ output['properties'][policy_name]['items'] = {
+ 'type': 'integer',
+ 'enum': [1],
+ 'options': {
+ 'enum_titles': ['title1']
+ }
+ }
+ elif policy_type == 'array':
+ output['properties'][policy_name]['items'] = {
+ 'type': 'integer',
+ 'title': 'title_array'
+ }
+ elif initial_type == 'dict':
+ output['properties'][policy_name]['properties'] = {
+ 'type': 'integer',
+ 'title': 'title_obj'
+ }
+
+ return _JsonFormat(output)
+
+ def testStringPolicy(self):
+ policy_json = self._GetTestPolicyTemplate('stringPolicy', 'string', '',
+ 'A string policy')
+ expected = self._GetExpectedOutput('stringPolicy', 'string',
+ 'A string policy', 'string', '83')
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0',
+ 'doc_url': self.doc_url
+ }, 'jamf')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testIntPolicy(self):
+ policy_json = self._GetTestPolicyTemplate('intPolicy', 'int', '',
+ 'An int policy')
+ expected = self._GetExpectedOutput('intPolicy', 'integer', 'An int policy',
+ 'int', '83')
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0',
+ 'doc_url': self.doc_url
+ }, 'jamf')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testIntPolicyWithMinAndMax(self):
+ template = {
+ 'policy_definitions': [{
+ 'name': 'intPolicyWithMinAndMax',
+ 'id': 1,
+ 'type': 'int',
+ 'supported_on': ['chrome.mac:*-'],
+ 'caption': 'An int policy with min and max',
+ 'desc': '',
+ 'schema': {
+ 'type': 'int',
+ 'minimum': 0,
+ 'maximum': 10
+ }
+ }],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {
+ 'doc_policy_documentation': {
+ 'text': 'Documentation for policy'
+ }
+ }
+ }
+ policy_json = _JsonFormat(template)
+
+ expected = {
+ 'description': 'Google Chrome',
+ 'options': {
+ 'remove_empty_properties': True
+ },
+ 'properties': {
+ 'intPolicyWithMinAndMax': {
+ 'description':
+ 'An int policy with min and max',
+ 'maximum':
+ 10,
+ 'minimum':
+ 0,
+ 'title':
+ 'intPolicyWithMinAndMax',
+ 'type':
+ 'integer',
+ 'links': [{
+ 'rel': 'Documentation for policy',
+ 'href': self.doc_url + '#' + 'intPolicyWithMinAndMax'
+ }]
+ }
+ },
+ 'title': 'com.google.chrome.ios',
+ 'version': '83'
+ }
+ expected_json = _JsonFormat(expected)
+
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0',
+ 'doc_url': self.doc_url
+ }, 'jamf')
+ self.assertEquals(output.strip(), expected_json.strip())
+
+ def testIntEnumPolicy(self):
+ policy_json = self._GetTestPolicyTemplate('intPolicy', 'int-enum', '',
+ 'An int-enum policy')
+ expected = self._GetExpectedOutput('intPolicy', 'integer',
+ 'An int-enum policy', 'int-enum', '83')
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0',
+ 'doc_url': self.doc_url
+ }, 'jamf')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testStringEnumPolicy(self):
+ policy_json = self._GetTestPolicyTemplate('stringPolicy', 'string-enum', '',
+ 'A string-enum policy')
+ expected = self._GetExpectedOutput('stringPolicy', 'string',
+ 'A string-enum policy', 'string-enum',
+ '83')
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0',
+ 'doc_url': self.doc_url
+ }, 'jamf')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testStringEnumListPolicy(self):
+ policy_json = self._GetTestPolicyTemplate('stringPolicy',
+ 'string-enum-list', '',
+ 'A string-enum-list policy')
+ expected = self._GetExpectedOutput('stringPolicy', 'array',
+ 'A string-enum-list policy',
+ 'string-enum-list', '83')
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0',
+ 'doc_url': self.doc_url
+ }, 'jamf')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testBooleanPolicy(self):
+ policy_json = self._GetTestPolicyTemplate('booleanPolicy', 'main', '',
+ 'A boolean policy')
+ expected = self._GetExpectedOutput('booleanPolicy', 'boolean',
+ 'A boolean policy', 'main', '83')
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0',
+ 'doc_url': self.doc_url
+ }, 'jamf')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testListPolicy(self):
+ policy_json = self._GetTestPolicyTemplate('listPolicy', 'list', '',
+ 'A list policy')
+ expected = self._GetExpectedOutput('listPolicy', 'array', 'A list policy',
+ 'list', '83')
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0',
+ 'doc_url': self.doc_url
+ }, 'jamf')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testDictPolicy(self):
+ policy_json = self._GetTestPolicyTemplate('dictPolicy', 'dict', 'object',
+ 'A dict policy')
+ expected = self._GetExpectedOutput('dictPolicy', 'object', 'A dict policy',
+ 'dict', '83')
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0',
+ 'doc_url': self.doc_url
+ }, 'jamf')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testArrayDictPolicy(self):
+ policy_json = self._GetTestPolicyTemplate('dictPolicy', 'dict', 'array',
+ 'A dict policy')
+ expected = self._GetExpectedOutput('dictPolicy', 'array', 'A dict policy',
+ 'dict', '83')
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0',
+ 'doc_url': self.doc_url
+ }, 'jamf')
+ self.assertEquals(output.strip(), expected.strip())
+
+ def testNestedArrayDict(self):
+ template = {
+ 'policy_definitions': [{
+ 'name': 'name',
+ 'id': 1,
+ 'type': 'dict',
+ 'supported_on': ['chrome.mac:*-'],
+ 'caption': 'caption',
+ 'desc': '',
+ 'schema': {
+ 'type': 'array',
+ 'items': {
+ 'title': 'title2',
+ 'id': 'id',
+ 'type': 'object',
+ 'properties': {
+ 'name': 'name',
+ 'children': {
+ 'type': 'array',
+ 'items': {
+ '$ref': 'id'
+ }
+ }
+ }
+ }
+ }
+ }],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {
+ 'doc_policy_documentation': {
+ 'text': 'Documentation for policy'
+ }
+ }
+ }
+ policy_json = _JsonFormat(template)
+
+ expected = {
+ 'description': 'Google Chrome',
+ 'options': {
+ 'remove_empty_properties': True
+ },
+ 'properties': {
+ 'name': {
+ 'description':
+ 'caption',
+ 'title':
+ 'name',
+ 'type':
+ 'array',
+ 'items': {
+ 'title': 'title2',
+ 'id': 'id',
+ 'type': 'object',
+ 'properties': {
+ 'name': 'name'
+ }
+ },
+ 'links': [{
+ 'rel': 'Documentation for policy',
+ 'href': self.doc_url + '#' + 'name'
+ }]
+ }
+ },
+ 'title': 'com.google.chrome.ios',
+ 'version': '83'
+ }
+
+ for i in range(0, 5):
+ expected['properties']['name']['items']['properties']['children'] = {
+ 'type': 'array',
+ 'items': copy.deepcopy(expected['properties']['name']['items'])
+ }
+
+ output_expected = _JsonFormat(expected)
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0',
+ 'doc_url': self.doc_url
+ }, 'jamf')
+ self.assertEquals(output.strip(), output_expected.strip())
+
+ def testExternalPolicy(self):
+ policy_json = self._GetTestPolicyTemplate('externalPolicy', 'external', '',
+ 'A external policy')
+ expected = self._GetExpectedOutput('externalPolicy', 'object',
+ 'A external policy', 'external', '83')
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'version': '83.0.4089.0',
+ 'doc_url': self.doc_url
+ }, 'jamf')
+ self.assertEquals(output.strip(), expected.strip())
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/json_writer.py b/chromium/components/policy/tools/template_writers/writers/json_writer.py
new file mode 100755
index 00000000000..7d5d166c7c9
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/json_writer.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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 json
+
+from textwrap import TextWrapper
+from writers import template_writer
+
+TEMPLATE_HEADER = """\
+// Policy template for Linux.
+// Uncomment the policies you wish to activate and change their values to
+// something useful for your case. The provided values are for reference only
+// and do not provide meaningful defaults!
+{"""
+
+HEADER_DELIMETER = """\
+ //-------------------------------------------------------------------------"""
+
+
+def GetWriter(config):
+ '''Factory method for creating JsonWriter objects.
+ See the constructor of TemplateWriter for description of
+ arguments.
+ '''
+ return JsonWriter(['linux'], config)
+
+
+class JsonWriter(template_writer.TemplateWriter):
+ '''Class for generating policy files in JSON format (for Linux). The
+ generated files will define all the supported policies with example values
+ set for them. This class is used by PolicyTemplateGenerator to write .json
+ files.
+ '''
+
+ def PreprocessPolicies(self, policy_list):
+ return self.FlattenGroupsAndSortPolicies(policy_list)
+
+ def WriteComment(self, comment):
+ self._out.append('// ' + comment)
+
+ def WritePolicy(self, policy):
+ example_value_str = json.dumps(policy['example_value'], sort_keys=True)
+
+ # Add comma to the end of the previous line.
+ if not self._first_written:
+ self._out[-2] += ','
+
+ if not self.CanBeMandatory(policy) and self.CanBeRecommended(policy):
+ line = ' // Note: this policy is supported only in recommended mode.'
+ self._out.append(line)
+ line = ' // The JSON file should be placed in %srecommended.' % \
+ self.config['linux_policy_path']
+ self._out.append(line)
+
+ line = ' // %s' % policy['caption']
+ self._out.append(line)
+ self._out.append(HEADER_DELIMETER)
+ description = policy['desc']
+ if self.HasExpandedPolicyDescription(policy):
+ description += ' ' + self.GetExpandedPolicyDescription(policy) + '\n'
+ description = self._text_wrapper.wrap(description)
+ self._out += description
+ line = ' //"%s": %s' % (policy['name'], example_value_str)
+ self._out.append('')
+ self._out.append(line)
+ self._out.append('')
+
+ self._first_written = False
+
+ def BeginTemplate(self):
+ if self._GetChromiumVersionString() is not None:
+ self.WriteComment(self.config['build'] + ''' version: ''' + \
+ self._GetChromiumVersionString())
+ self._out.append(TEMPLATE_HEADER)
+
+ def EndTemplate(self):
+ self._out.append('}')
+
+ def Init(self):
+ self._out = []
+ # The following boolean member is true until the first policy is written.
+ self._first_written = True
+ # Create the TextWrapper object once.
+ self._text_wrapper = TextWrapper(
+ initial_indent=' // ',
+ subsequent_indent=' // ',
+ break_long_words=False,
+ width=80)
+
+ def GetTemplateText(self):
+ return '\n'.join(self._out)
diff --git a/chromium/components/policy/tools/template_writers/writers/json_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/json_writer_unittest.py
new file mode 100755
index 00000000000..5f7c3ad9660
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/json_writer_unittest.py
@@ -0,0 +1,460 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+'''Unit tests for writers.json_writer'''
+
+import os
+import sys
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+import unittest
+
+from writers import writer_unittest_common
+
+TEMPLATE_HEADER = """\
+// Policy template for Linux.
+// Uncomment the policies you wish to activate and change their values to
+// something useful for your case. The provided values are for reference only
+// and do not provide meaningful defaults!
+{
+"""
+
+TEMPLATE_HEADER_WITH_VERSION = """\
+// chromium version: 39.0.0.0
+// Policy template for Linux.
+// Uncomment the policies you wish to activate and change their values to
+// something useful for your case. The provided values are for reference only
+// and do not provide meaningful defaults!
+{
+"""
+
+HEADER_DELIMETER = """\
+ //-------------------------------------------------------------------------
+"""
+
+MESSAGES = '''
+ {
+ 'doc_schema_description_link': {
+ 'text': 'See $6'
+ },
+ }'''
+
+
+class JsonWriterUnittest(writer_unittest_common.WriterUnittestCommon):
+ '''Unit tests for JsonWriter.'''
+
+ def CompareOutputs(self, output, expected_output):
+ '''Compares the output of the json_writer with its expected output.
+
+ Args:
+ output: The output of the json writer.
+ expected_output: The expected output.
+
+ Raises:
+ AssertionError: if the two strings are not equivalent.
+ '''
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testEmpty(self):
+ # Test the handling of an empty policy list.
+ policy_json = '''
+ {
+ "policy_definitions": [],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'json')
+ expected_output = TEMPLATE_HEADER + '}'
+ self.CompareOutputs(output, expected_output)
+
+ def testEmptyWithVersion(self):
+ # Test the handling of an empty policy list.
+ policy_json = '''
+ {
+ "policy_definitions": [],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'version': '39.0.0.0'
+ }, 'json')
+ expected_output = TEMPLATE_HEADER_WITH_VERSION + '}'
+ self.CompareOutputs(output, expected_output)
+
+ def testMainPolicy(self):
+ # Tests a policy group with a single policy of type 'main'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "MainPolicy",
+ "type": "main",
+ "caption": "Example Main Policy",
+ "desc": "Example Main Policy",
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": True
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'json')
+ expected_output = (
+ TEMPLATE_HEADER + ' // Example Main Policy\n' + HEADER_DELIMETER +
+ ' // Example Main Policy\n\n'
+ ' //"MainPolicy": true\n\n'
+ '}')
+ self.CompareOutputs(output, expected_output)
+
+ def testRecommendedOnlyPolicy(self):
+ # Tests a policy group with a single policy of type 'main'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "MainPolicy",
+ "type": "main",
+ "caption": "Example Main Policy",
+ "desc": "Example Main Policy",
+ "features": {
+ "can_be_recommended": True,
+ "can_be_mandatory": False
+ },
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": True
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'json')
+ expected_output = (
+ TEMPLATE_HEADER +
+ ' // Note: this policy is supported only in recommended mode.\n' +
+ ' // The JSON file should be placed in' +
+ ' /etc/opt/chrome/policies/recommended.\n' +
+ ' // Example Main Policy\n' + HEADER_DELIMETER +
+ ' // Example Main Policy\n\n'
+ ' //"MainPolicy": true\n\n'
+ '}')
+ self.CompareOutputs(output, expected_output)
+
+ def testStringPolicy(self):
+ # Tests a policy group with a single policy of type 'string'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "StringPolicy",
+ "type": "string",
+ "caption": "Example String Policy",
+ "desc": "Example String Policy",
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": "hello, world!"
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'json')
+ expected_output = (
+ TEMPLATE_HEADER + ' // Example String Policy\n' + HEADER_DELIMETER +
+ ' // Example String Policy\n\n'
+ ' //"StringPolicy": "hello, world!"\n\n'
+ '}')
+ self.CompareOutputs(output, expected_output)
+
+ def testIntPolicy(self):
+ # Tests a policy group with a single policy of type 'string'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "IntPolicy",
+ "type": "int",
+ "caption": "Example Int Policy",
+ "desc": "Example Int Policy",
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": 15
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'json')
+ expected_output = (
+ TEMPLATE_HEADER + ' // Example Int Policy\n' + HEADER_DELIMETER +
+ ' // Example Int Policy\n\n'
+ ' //"IntPolicy": 15\n\n'
+ '}')
+ self.CompareOutputs(output, expected_output)
+
+ def testIntEnumPolicy(self):
+ # Tests a policy group with a single policy of type 'int-enum'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "EnumPolicy",
+ "type": "int-enum",
+ "caption": "Example Int Enum",
+ "desc": "Example Int Enum",
+ "items": [
+ {"name": "ProxyServerDisabled", "value": 0, "caption": ""},
+ {"name": "ProxyServerAutoDetect", "value": 1, "caption": ""},
+ ],
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": 1
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'json')
+ expected_output = (
+ TEMPLATE_HEADER + ' // Example Int Enum\n' + HEADER_DELIMETER +
+ ' // Example Int Enum\n\n'
+ ' //"EnumPolicy": 1\n\n'
+ '}')
+ self.CompareOutputs(output, expected_output)
+
+ def testStringEnumPolicy(self):
+ # Tests a policy group with a single policy of type 'string-enum'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "EnumPolicy",
+ "type": "string-enum",
+ "caption": "Example String Enum",
+ "desc": "Example String Enum",
+ "items": [
+ {"name": "ProxyServerDisabled", "value": "one",
+ "caption": ""},
+ {"name": "ProxyServerAutoDetect", "value": "two",
+ "caption": ""},
+ ],
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": "one"
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'json')
+ expected_output = (
+ TEMPLATE_HEADER + ' // Example String Enum\n' + HEADER_DELIMETER +
+ ' // Example String Enum\n\n'
+ ' //"EnumPolicy": "one"\n\n'
+ '}')
+ self.CompareOutputs(output, expected_output)
+
+ def testListPolicy(self):
+ # Tests a policy group with a single policy of type 'list'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "ListPolicy",
+ "type": "list",
+ "caption": "Example List",
+ "desc": "Example List",
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": ["foo", "bar"]
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'json')
+ expected_output = (
+ TEMPLATE_HEADER + ' // Example List\n' + HEADER_DELIMETER +
+ ' // Example List\n\n'
+ ' //"ListPolicy": ["foo", "bar"]\n\n'
+ '}')
+ self.CompareOutputs(output, expected_output)
+
+ def testStringEnumListPolicy(self):
+ # Tests a policy group with a single policy of type 'string-enum-list'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "ListPolicy",
+ "type": "string-enum-list",
+ "caption": "Example List",
+ "desc": "Example List",
+ "items": [
+ {"name": "ProxyServerDisabled", "value": "one",
+ "caption": ""},
+ {"name": "ProxyServerAutoDetect", "value": "two",
+ "caption": ""},
+ ],
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": ["one", "two"]
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'json')
+ expected_output = (
+ TEMPLATE_HEADER + ' // Example List\n' + HEADER_DELIMETER +
+ ' // Example List\n\n'
+ ' //"ListPolicy": ["one", "two"]\n\n'
+ '}')
+ self.CompareOutputs(output, expected_output)
+
+ def testDictionaryPolicy(self):
+ # Tests a policy group with a single policy of type 'dict'.
+ example = {
+ 'bool': True,
+ 'dict': {
+ 'a': 1,
+ 'b': 2,
+ },
+ 'int': 10,
+ 'list': [1, 2, 3],
+ 'string': 'abc',
+ }
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "DictionaryPolicy",
+ "type": "dict",
+ "caption": "Example Dictionary Policy",
+ "desc": "Example Dictionary Policy",
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": ''' + str(example) + '''
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": %s,
+ }''' % MESSAGES
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'json')
+ expected_output = (
+ TEMPLATE_HEADER + ' // Example Dictionary Policy\n' + HEADER_DELIMETER
+ + ' // Example Dictionary Policy See '
+ 'https://cloud.google.com/docs/chrome-\n'
+ ' // enterprise/policies/?policy=DictionaryPolicy\n\n'
+ ' //"DictionaryPolicy": {"bool": true, "dict": {"a": 1, '
+ '"b": 2}, "int": 10, "list": [1, 2, 3], "string": "abc"}\n\n'
+ '}')
+ self.CompareOutputs(output, expected_output)
+
+ def testExternalPolicy(self):
+ # Tests a policy group with a single policy of type 'external'.
+ example = {
+ "url": "https://example.com/avatar.jpg",
+ "hash": "deadbeef",
+ }
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "ExternalPolicy",
+ "type": "external",
+ "caption": "Example External Policy",
+ "desc": "Example External Policy",
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": %s
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": %s,
+ }''' % (str(example), MESSAGES)
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'json')
+ expected_output = (
+ TEMPLATE_HEADER + ' // Example External Policy\n' + HEADER_DELIMETER +
+ ' // Example External Policy See '
+ 'https://cloud.google.com/docs/chrome-\n'
+ ' // enterprise/policies/?policy=ExternalPolicy\n\n'
+ ' //"ExternalPolicy": {"hash": "deadbeef", "url": "https://example.com/avatar.jpg"}\n\n'
+ '}')
+ self.CompareOutputs(output, expected_output)
+
+ def testNonSupportedPolicy(self):
+ # Tests a policy that is not supported on Linux, so it shouldn't
+ # be included in the JSON file.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "NonLinuxPolicy",
+ "type": "list",
+ "caption": "",
+ "desc": "",
+ "supported_on": ["chrome.mac:8-"],
+ "example_value": ["a"]
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'json')
+ expected_output = TEMPLATE_HEADER + '}'
+ self.CompareOutputs(output, expected_output)
+
+ def testPolicyGroup(self):
+ # Tests a policy group that has more than one policies.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "Group1",
+ "type": "group",
+ "caption": "",
+ "desc": "",
+ "policies": ["Policy1", "Policy2"],
+ },
+ {
+ "name": "Policy1",
+ "type": "list",
+ "caption": "Policy One",
+ "desc": "Policy One",
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": ["a", "b"]
+ },
+ {
+ "name": "Policy2",
+ "type": "string",
+ "caption": "Policy Two",
+ "desc": "Policy Two",
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": "c"
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'json')
+ expected_output = (
+ TEMPLATE_HEADER + ' // Policy One\n' + HEADER_DELIMETER +
+ ' // Policy One\n\n'
+ ' //"Policy1": ["a", "b"],\n\n'
+ ' // Policy Two\n' + HEADER_DELIMETER + ' // Policy Two\n\n'
+ ' //"Policy2": "c"\n\n'
+ '}')
+ self.CompareOutputs(output, expected_output)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/mock_writer.py b/chromium/components/policy/tools/template_writers/writers/mock_writer.py
new file mode 100755
index 00000000000..19e1d748908
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/mock_writer.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+
+from .template_writer import TemplateWriter
+
+
+class MockWriter(TemplateWriter):
+ '''Helper class for unit tests in policy_template_generator_unittest.py
+ '''
+
+ def __init__(self, platforms=[], config={}):
+ super(MockWriter, self).__init__(platforms, config)
+
+ def WritePolicy(self, policy):
+ pass
+
+ def BeginTemplate(self):
+ pass
+
+ def GetTemplateText(self):
+ pass
+
+ def IsPolicySupported(self, policy):
+ return True
+
+ def Test(self):
+ pass
diff --git a/chromium/components/policy/tools/template_writers/writers/plist_helper.py b/chromium/components/policy/tools/template_writers/writers/plist_helper.py
new file mode 100755
index 00000000000..e69f00dcb68
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/plist_helper.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+'''Common functions for plist_writer and plist_strings_writer.
+'''
+
+
+def GetPlistFriendlyName(name):
+ '''Transforms a string so that it will be suitable for use as
+ a pfm_name in the plist manifest file.
+ '''
+ return name.replace(' ', '_')
diff --git a/chromium/components/policy/tools/template_writers/writers/plist_strings_writer.py b/chromium/components/policy/tools/template_writers/writers/plist_strings_writer.py
new file mode 100755
index 00000000000..adfbc6d279e
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/plist_strings_writer.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+
+from writers import plist_helper
+from writers import template_writer
+
+
+def GetWriter(config):
+ '''Factory method for creating PListStringsWriter objects.
+ See the constructor of TemplateWriter for description of
+ arguments.
+ '''
+ return PListStringsWriter(['mac'], config)
+
+
+class PListStringsWriter(template_writer.TemplateWriter):
+ '''Outputs localized string table files for the Mac policy file.
+ These files are named Localizable.strings and they are in the
+ [lang].lproj subdirectories of the manifest bundle.
+ '''
+
+ def WriteComment(self, comment):
+ self._out.append('/* ' + comment + ' */')
+
+ def _AddToStringTable(self, item_name, caption, desc):
+ '''Add a title and a description of an item to the string table.
+
+ Args:
+ item_name: The name of the item that will get the title and the
+ description.
+ title: The text of the title to add.
+ desc: The text of the description to add.
+ '''
+ caption = caption.replace('"', '\\"')
+ caption = caption.replace('\n', '\\n')
+ desc = desc.replace('"', '\\"')
+ desc = desc.replace('\n', '\\n')
+ self._out.append('%s.pfm_title = \"%s\";' % (item_name, caption))
+ self._out.append('%s.pfm_description = \"%s\";' % (item_name, desc))
+
+ def PreprocessPolicies(self, policy_list):
+ return self.FlattenGroupsAndSortPolicies(policy_list)
+
+ def WritePolicy(self, policy):
+ '''Add strings to the stringtable corresponding a given policy.
+
+ Args:
+ policy: The policy for which the strings will be added to the
+ string table.
+ '''
+ desc = policy['desc']
+ if policy['type'] in ('int-enum', 'string-enum', 'string-enum-list'):
+ # Append the captions of enum items to the description string.
+ item_descs = []
+ for item in policy['items']:
+ item_descs.append(str(item['value']) + ' - ' + item['caption'])
+ desc = '\n'.join(item_descs) + '\n' + desc
+ if self.HasExpandedPolicyDescription(policy):
+ desc += '\n' + self.GetExpandedPolicyDescription(policy)
+
+ self._AddToStringTable(policy['name'], policy['label'], desc)
+
+ def BeginTemplate(self):
+ app_name = plist_helper.GetPlistFriendlyName(self.config['app_name'])
+ if self._GetChromiumVersionString() is not None:
+ self.WriteComment(self.config['build'] + ''' version: ''' + \
+ self._GetChromiumVersionString())
+ self._AddToStringTable(app_name, self.config['app_name'],
+ self.messages['mac_chrome_preferences']['text'])
+
+ def Init(self):
+ # A buffer for the lines of the string table being generated.
+ self._out = []
+
+ def GetTemplateText(self):
+ return '\n'.join(self._out)
diff --git a/chromium/components/policy/tools/template_writers/writers/plist_strings_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/plist_strings_writer_unittest.py
new file mode 100755
index 00000000000..91c39f431e3
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/plist_strings_writer_unittest.py
@@ -0,0 +1,402 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 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.
+'''Unit tests for writers.plist_strings_writer'''
+
+import os
+import sys
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+import unittest
+
+from writers import writer_unittest_common
+
+
+class PListStringsWriterUnittest(writer_unittest_common.WriterUnittestCommon):
+ '''Unit tests for PListStringsWriter.'''
+
+ def testEmpty(self):
+ # Test PListStringsWriter in case of empty polices.
+ policy_json = '''
+ {
+ 'policy_definitions': [],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {
+ 'mac_chrome_preferences': {
+ 'text': 'Chromium preferen"ces',
+ 'desc': 'blah'
+ }
+ }
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist_strings')
+ expected_output = ('Chromium.pfm_title = "Chromium";\n'
+ 'Chromium.pfm_description = "Chromium preferen\\"ces";')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testEmptyVersion(self):
+ # Test PListStringsWriter in case of empty polices.
+ policy_json = '''
+ {
+ 'policy_definitions': [],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {
+ 'mac_chrome_preferences': {
+ 'text': 'Chromium preferen"ces',
+ 'desc': 'blah'
+ }
+ }
+ }'''
+ output = self.GetOutput(
+ policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test',
+ 'version': '39.0.0.0'
+ }, 'plist_strings')
+ expected_output = ('/* chromium version: 39.0.0.0 */\n'
+ 'Chromium.pfm_title = "Chromium";\n'
+ 'Chromium.pfm_description = "Chromium preferen\\"ces";')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testMainPolicy(self):
+ # Tests a policy group with a single policy of type 'main'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'MainGroup',
+ 'type': 'group',
+ 'caption': 'Caption of main.',
+ 'desc': 'Description of main.',
+ 'policies': ['MainPolicy'],
+ },
+ {
+ 'name': 'MainPolicy',
+ 'type': 'main',
+ 'supported_on': ['chrome.mac:8-'],
+ 'caption': 'Caption of main policy.',
+ 'desc': 'Description of main policy.',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {
+ 'mac_chrome_preferences': {
+ 'text': 'Preferences of Google Chrome',
+ 'desc': 'blah'
+ }
+ }
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist_strings')
+ expected_output = (
+ 'Google_Chrome.pfm_title = "Google Chrome";\n'
+ 'Google_Chrome.pfm_description = "Preferences of Google Chrome";\n'
+ 'MainPolicy.pfm_title = "Caption of main policy.";\n'
+ 'MainPolicy.pfm_description = "Description of main policy.";')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testStringPolicy(self):
+ # Tests a policy group with a single policy of type 'string'. Also test
+ # inheriting group description to policy description.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'StringGroup',
+ 'type': 'group',
+ 'caption': 'Caption of group.',
+ 'desc': """Description of group.
+With a newline.""",
+ 'policies': ['StringPolicy'],
+ },
+ {
+ 'name': 'StringPolicy',
+ 'type': 'string',
+ 'caption': 'Caption of policy.',
+ 'desc': """Description of policy.
+With a newline.""",
+ 'supported_on': ['chrome.mac:8-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {
+ 'mac_chrome_preferences': {
+ 'text': 'Preferences of Chromium',
+ 'desc': 'blah'
+ }
+ }
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist_strings')
+ expected_output = ('Chromium.pfm_title = "Chromium";\n'
+ 'Chromium.pfm_description = "Preferences of Chromium";\n'
+ 'StringPolicy.pfm_title = "Caption of policy.";\n'
+ 'StringPolicy.pfm_description = '
+ '"Description of policy.\\nWith a newline.";')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testStringListPolicy(self):
+ # Tests a policy group with a single policy of type 'list'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'ListGroup',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['ListPolicy'],
+ },
+ {
+ 'name': 'ListPolicy',
+ 'type': 'list',
+ 'caption': 'Caption of policy.',
+ 'desc': """Description of policy.
+With a newline.""",
+ 'schema': {
+ 'type': 'array',
+ 'items': { 'type': 'string' },
+ },
+ 'supported_on': ['chrome.mac:8-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {
+ 'mac_chrome_preferences': {
+ 'text': 'Preferences of Chromium',
+ 'desc': 'blah'
+ }
+ }
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist_strings')
+ expected_output = ('Chromium.pfm_title = "Chromium";\n'
+ 'Chromium.pfm_description = "Preferences of Chromium";\n'
+ 'ListPolicy.pfm_title = "Caption of policy.";\n'
+ 'ListPolicy.pfm_description = '
+ '"Description of policy.\\nWith a newline.";')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testStringEnumListPolicy(self):
+ # Tests a policy group with a single policy of type 'string-enum-list'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'EnumGroup',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['EnumPolicy'],
+ },
+ {
+ 'name': 'EnumPolicy',
+ 'type': 'string-enum-list',
+ 'caption': 'Caption of policy.',
+ 'desc': """Description of policy.
+With a newline.""",
+ 'schema': {
+ 'type': 'array',
+ 'items': { 'type': 'string' },
+ },
+ 'items': [
+ {
+ 'name': 'ProxyServerDisabled',
+ 'value': 'one',
+ 'caption': 'Option1'
+ },
+ {
+ 'name': 'ProxyServerAutoDetect',
+ 'value': 'two',
+ 'caption': 'Option2'
+ },
+ ],
+ 'supported_on': ['chrome.mac:8-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {
+ 'mac_chrome_preferences': {
+ 'text': 'Preferences of Chromium',
+ 'desc': 'blah'
+ }
+ }
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist_strings')
+ expected_output = ('Chromium.pfm_title = "Chromium";\n'
+ 'Chromium.pfm_description = "Preferences of Chromium";\n'
+ 'EnumPolicy.pfm_title = "Caption of policy.";\n'
+ 'EnumPolicy.pfm_description = '
+ '"one - Option1\\ntwo - Option2\\n'
+ 'Description of policy.\\nWith a newline.";')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testIntEnumPolicy(self):
+ # Tests a policy group with a single policy of type 'int-enum'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'EnumGroup',
+ 'type': 'group',
+ 'desc': '',
+ 'caption': '',
+ 'policies': ['EnumPolicy'],
+ },
+ {
+ 'name': 'EnumPolicy',
+ 'type': 'int-enum',
+ 'desc': 'Description of policy.',
+ 'caption': 'Caption of policy.',
+ 'items': [
+ {
+ 'name': 'ProxyServerDisabled',
+ 'value': 0,
+ 'caption': 'Option1'
+ },
+ {
+ 'name': 'ProxyServerAutoDetect',
+ 'value': 1,
+ 'caption': 'Option2'
+ },
+ ],
+ 'supported_on': ['chrome.mac:8-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {
+ 'mac_chrome_preferences': {
+ 'text': 'Google Chrome preferences',
+ 'desc': 'blah'
+ }
+ }
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'mac_bundle_id': 'com.example.Test2'
+ }, 'plist_strings')
+ expected_output = (
+ 'Google_Chrome.pfm_title = "Google Chrome";\n'
+ 'Google_Chrome.pfm_description = "Google Chrome preferences";\n'
+ 'EnumPolicy.pfm_title = "Caption of policy.";\n'
+ 'EnumPolicy.pfm_description = '
+ '"0 - Option1\\n1 - Option2\\nDescription of policy.";\n')
+
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testStringEnumPolicy(self):
+ # Tests a policy group with a single policy of type 'string-enum'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'EnumGroup',
+ 'type': 'group',
+ 'desc': '',
+ 'caption': '',
+ 'policies': ['EnumPolicy'],
+ },
+ {
+ 'name': 'EnumPolicy',
+ 'type': 'string-enum',
+ 'desc': 'Description of policy.',
+ 'caption': 'Caption of policy.',
+ 'items': [
+ {
+ 'name': 'ProxyServerDisabled',
+ 'value': 'one',
+ 'caption': 'Option1'
+ },
+ {
+ 'name': 'ProxyServerAutoDetect',
+ 'value': 'two',
+ 'caption': 'Option2'
+ },
+ ],
+ 'supported_on': ['chrome.mac:8-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {
+ 'mac_chrome_preferences': {
+ 'text': 'Google Chrome preferences',
+ 'desc': 'blah'
+ }
+ }
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'mac_bundle_id': 'com.example.Test2'
+ }, 'plist_strings')
+ expected_output = (
+ 'Google_Chrome.pfm_title = "Google Chrome";\n'
+ 'Google_Chrome.pfm_description = "Google Chrome preferences";\n'
+ 'EnumPolicy.pfm_title = "Caption of policy.";\n'
+ 'EnumPolicy.pfm_description = '
+ '"one - Option1\\ntwo - Option2\\nDescription of policy.";\n')
+
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testNonSupportedPolicy(self):
+ # Tests a policy that is not supported on Mac, so its strings shouldn't
+ # be included in the plist string table.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'NonMacGroup',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['NonMacPolicy'],
+ },
+ {
+ 'name': 'NonMacPolicy',
+ 'type': 'string',
+ 'caption': '',
+ 'desc': '',
+ 'supported_on': ['chrome_os:8-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {
+ 'mac_chrome_preferences': {
+ 'text': 'Google Chrome preferences',
+ 'desc': 'blah'
+ }
+ }
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'mac_bundle_id': 'com.example.Test2'
+ }, 'plist_strings')
+ expected_output = (
+ 'Google_Chrome.pfm_title = "Google Chrome";\n'
+ 'Google_Chrome.pfm_description = "Google Chrome preferences";')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/plist_writer.py b/chromium/components/policy/tools/template_writers/writers/plist_writer.py
new file mode 100755
index 00000000000..e8666486457
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/plist_writer.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+
+from xml.dom import minidom
+from writers import plist_helper
+from writers import xml_formatted_writer
+
+# This writer outputs a Preferences Manifest file as documented at
+# https://developer.apple.com/library/mac/documentation/MacOSXServer/Conceptual/Preference_Manifest_Files
+
+
+def GetWriter(config):
+ '''Factory method for creating PListWriter objects.
+ See the constructor of TemplateWriter for description of
+ arguments.
+ '''
+ return PListWriter(['mac'], config)
+
+
+class PListWriter(xml_formatted_writer.XMLFormattedWriter):
+ '''Class for generating policy templates in Mac plist format.
+ It is used by PolicyTemplateGenerator to write plist files.
+ '''
+
+ STRING_TABLE = 'Localizable.strings'
+ TYPE_TO_INPUT = {
+ 'string': 'string',
+ 'int': 'integer',
+ 'int-enum': 'integer',
+ 'string-enum': 'string',
+ 'string-enum-list': 'array',
+ 'main': 'boolean',
+ 'list': 'array',
+ 'dict': 'dictionary',
+ 'external': 'dictionary',
+ }
+
+ def _AddKeyValuePair(self, parent, key_string, value_tag):
+ '''Adds a plist key-value pair to a parent XML element.
+
+ A key-value pair in plist consists of two XML elements next two each other:
+ <key>key_string</key>
+ <value_tag>...</value_tag>
+
+ Args:
+ key_string: The content of the key tag.
+ value_tag: The name of the value element.
+
+ Returns:
+ The XML element of the value tag.
+ '''
+ self.AddElement(parent, 'key', {}, key_string)
+ return self.AddElement(parent, value_tag)
+
+ def _AddStringKeyValuePair(self, parent, key_string, value_string):
+ '''Adds a plist key-value pair to a parent XML element, where the
+ value element contains a string. The name of the value element will be
+ <string>.
+
+ Args:
+ key_string: The content of the key tag.
+ value_string: The content of the value tag.
+ '''
+ self.AddElement(parent, 'key', {}, key_string)
+ self.AddElement(parent, 'string', {}, value_string)
+
+ def _AddRealKeyValuePair(self, parent, key_string, value_string):
+ '''Adds a plist key-value pair to a parent XML element, where the
+ value element contains a real number. The name of the value element will be
+ <real>.
+
+ Args:
+ key_string: The content of the key tag.
+ value_string: The content of the value tag.
+ '''
+ self.AddElement(parent, 'key', {}, key_string)
+ self.AddElement(parent, 'real', {}, value_string)
+
+ def _AddTargets(self, parent, policy):
+ '''Adds the following XML snippet to an XML element:
+ <key>pfm_targets</key>
+ <array>
+ <string>user-managed</string>
+ </array>
+
+ Args:
+ parent: The parent XML element where the snippet will be added.
+ '''
+ array = self._AddKeyValuePair(parent, 'pfm_targets', 'array')
+ if self.CanBeRecommended(policy):
+ self.AddElement(array, 'string', {}, 'user')
+ if self.CanBeMandatory(policy):
+ self.AddElement(array, 'string', {}, 'user-managed')
+
+ def PreprocessPolicies(self, policy_list):
+ return self.FlattenGroupsAndSortPolicies(policy_list)
+
+ def WritePolicy(self, policy):
+ policy_name = policy['name']
+ policy_type = policy['type']
+
+ dict = self.AddElement(self._array, 'dict')
+ self._AddStringKeyValuePair(dict, 'pfm_name', policy_name)
+ # Set empty strings for title and description. They will be taken by the
+ # OSX Workgroup Manager from the string table in a Localizable.strings file.
+ # Those files are generated by plist_strings_writer.
+ self._AddStringKeyValuePair(dict, 'pfm_description', '')
+ self._AddStringKeyValuePair(dict, 'pfm_title', '')
+ self._AddTargets(dict, policy)
+ self._AddStringKeyValuePair(dict, 'pfm_type',
+ self.TYPE_TO_INPUT[policy_type])
+ if policy_type in ('int-enum', 'string-enum'):
+ range_list = self._AddKeyValuePair(dict, 'pfm_range_list', 'array')
+ for item in policy['items']:
+ if policy_type == 'int-enum':
+ element_type = 'integer'
+ else:
+ element_type = 'string'
+ self.AddElement(range_list, element_type, {}, str(item['value']))
+ elif policy_type in ('list', 'string-enum-list'):
+ subkeys = self._AddKeyValuePair(dict, 'pfm_subkeys', 'array')
+ subkeys_dict = self.AddElement(subkeys, 'dict')
+ subkeys_type = self._AddKeyValuePair(subkeys_dict, 'pfm_type', 'string')
+ self.AddText(subkeys_type, 'string')
+
+ def BeginTemplate(self):
+ self._plist.attributes['version'] = '1'
+ dict = self.AddElement(self._plist, 'dict')
+ if self._GetChromiumVersionString() is not None:
+ self.AddComment(self._plist, self.config['build'] + ' version: ' + \
+ self._GetChromiumVersionString())
+ app_name = plist_helper.GetPlistFriendlyName(self.config['app_name'])
+ self._AddStringKeyValuePair(dict, 'pfm_name', app_name)
+ self._AddStringKeyValuePair(dict, 'pfm_description', '')
+ self._AddStringKeyValuePair(dict, 'pfm_title', '')
+ self._AddRealKeyValuePair(dict, 'pfm_version', '1')
+ self._AddStringKeyValuePair(dict, 'pfm_domain',
+ self.config['mac_bundle_id'])
+
+ self._array = self._AddKeyValuePair(dict, 'pfm_subkeys', 'array')
+
+ def CreatePlistDocument(self):
+ dom_impl = minidom.getDOMImplementation('')
+ doctype = dom_impl.createDocumentType(
+ 'plist', '-//Apple//DTD PLIST 1.0//EN',
+ 'http://www.apple.com/DTDs/PropertyList-1.0.dtd')
+ return dom_impl.createDocument(None, 'plist', doctype)
+
+ def Init(self):
+ self._doc = self.CreatePlistDocument()
+ self._plist = self._doc.documentElement
+
+ def GetTemplateText(self):
+ return self.ToPrettyXml(self._doc)
diff --git a/chromium/components/policy/tools/template_writers/writers/plist_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/plist_writer_unittest.py
new file mode 100755
index 00000000000..92d75ee7f8f
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/plist_writer_unittest.py
@@ -0,0 +1,732 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+'''Unit tests for writers.plist_writer'''
+
+import os
+import sys
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+import unittest
+
+from writers import writer_unittest_common
+
+
+class PListWriterUnittest(writer_unittest_common.WriterUnittestCommon):
+ '''Unit tests for PListWriter.'''
+
+ def _GetExpectedOutputs(self, product_name, bundle_id, policies):
+ '''Substitutes the variable parts into a plist template. The result
+ of this function can be used as an expected result to test the output
+ of PListWriter.
+
+ Args:
+ product_name: The name of the product, normally Chromium or Google Chrome.
+ bundle_id: The mac bundle id of the product.
+ policies: The list of policies.
+
+ Returns:
+ The text of a plist template with the variable parts substituted.
+ '''
+ return '''
+<?xml version="1.0" ?>
+<!DOCTYPE plist PUBLIC '-//Apple//DTD PLIST 1.0//EN' 'http://www.apple.com/DTDs/PropertyList-1.0.dtd'>
+<plist version="1">
+ <dict>
+ <key>pfm_name</key>
+ <string>%s</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_version</key>
+ <real>1</real>
+ <key>pfm_domain</key>
+ <string>%s</string>
+ <key>pfm_subkeys</key>
+ %s
+ </dict>
+</plist>''' % (product_name, bundle_id, policies)
+
+ def _GetExpectedOutputsWithVersion(self, product_name, bundle_id, policies,
+ version):
+ '''Substitutes the variable parts into a plist template. The result
+ of this function can be used as an expected result to test the output
+ of PListWriter.
+
+ Args:
+ product_name: The name of the product, normally Chromium or Google Chrome.
+ bundle_id: The mac bundle id of the product.
+ policies: The list of policies.
+
+ Returns:
+ The text of a plist template with the variable parts substituted.
+ '''
+ return '''
+<?xml version="1.0" ?>
+<!DOCTYPE plist PUBLIC '-//Apple//DTD PLIST 1.0//EN' 'http://www.apple.com/DTDs/PropertyList-1.0.dtd'>
+<plist version="1">
+ <dict>
+ <key>pfm_name</key>
+ <string>%s</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_version</key>
+ <real>1</real>
+ <key>pfm_domain</key>
+ <string>%s</string>
+ <key>pfm_subkeys</key>
+ %s
+ </dict>
+ <!--%s-->
+</plist>''' % (product_name, bundle_id, policies, version)
+
+ def testEmpty(self):
+ # Test PListWriter in case of empty polices.
+ policy_json = '''
+ {
+ 'policy_definitions': [],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {},
+ }'''
+
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs('Chromium', 'com.example.Test',
+ '<array/>')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testEmptyVersion(self):
+ # Test PListWriter in case of empty polices.
+ policy_json = '''
+ {
+ 'policy_definitions': [],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {},
+ }'''
+
+ output = self.GetOutput(
+ policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test',
+ 'version': '39.0.0.0'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputsWithVersion(
+ 'Chromium', 'com.example.Test', '<array/>',
+ 'chromium version: 39.0.0.0')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testMainPolicy(self):
+ # Tests a policy group with a single policy of type 'main'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'MainGroup',
+ 'type': 'group',
+ 'policies': ['MainPolicy'],
+ 'desc': '',
+ 'caption': '',
+ },
+ {
+ 'name': 'MainPolicy',
+ 'type': 'main',
+ 'desc': '',
+ 'caption': '',
+ 'supported_on': ['chrome.mac:8-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {}
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs(
+ 'Chromium', 'com.example.Test', '''<array>
+ <dict>
+ <key>pfm_name</key>
+ <string>MainPolicy</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_targets</key>
+ <array>
+ <string>user-managed</string>
+ </array>
+ <key>pfm_type</key>
+ <string>boolean</string>
+ </dict>
+ </array>''')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testRecommendedPolicy(self):
+ # Tests a policy group with a single policy of type 'main'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'MainGroup',
+ 'type': 'group',
+ 'policies': ['MainPolicy'],
+ 'desc': '',
+ 'caption': '',
+ },
+ {
+ 'name': 'MainPolicy',
+ 'type': 'main',
+ 'desc': '',
+ 'caption': '',
+ 'features': {
+ 'can_be_recommended' : True
+ },
+ 'supported_on': ['chrome.mac:8-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {}
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs(
+ 'Chromium', 'com.example.Test', '''<array>
+ <dict>
+ <key>pfm_name</key>
+ <string>MainPolicy</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_targets</key>
+ <array>
+ <string>user</string>
+ <string>user-managed</string>
+ </array>
+ <key>pfm_type</key>
+ <string>boolean</string>
+ </dict>
+ </array>''')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testRecommendedOnlyPolicy(self):
+ # Tests a policy group with a single policy of type 'main'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'MainGroup',
+ 'type': 'group',
+ 'policies': ['MainPolicy'],
+ 'desc': '',
+ 'caption': '',
+ },
+ {
+ 'name': 'MainPolicy',
+ 'type': 'main',
+ 'desc': '',
+ 'caption': '',
+ 'features': {
+ 'can_be_recommended' : True,
+ 'can_be_mandatory' : False
+ },
+ 'supported_on': ['chrome.mac:8-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {}
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs(
+ 'Chromium', 'com.example.Test', '''<array>
+ <dict>
+ <key>pfm_name</key>
+ <string>MainPolicy</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_targets</key>
+ <array>
+ <string>user</string>
+ </array>
+ <key>pfm_type</key>
+ <string>boolean</string>
+ </dict>
+ </array>''')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testStringPolicy(self):
+ # Tests a policy group with a single policy of type 'string'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'StringGroup',
+ 'type': 'group',
+ 'desc': '',
+ 'caption': '',
+ 'policies': ['StringPolicy'],
+ },
+ {
+ 'name': 'StringPolicy',
+ 'type': 'string',
+ 'supported_on': ['chrome.mac:8-'],
+ 'desc': '',
+ 'caption': '',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {},
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs(
+ 'Chromium', 'com.example.Test', '''<array>
+ <dict>
+ <key>pfm_name</key>
+ <string>StringPolicy</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_targets</key>
+ <array>
+ <string>user-managed</string>
+ </array>
+ <key>pfm_type</key>
+ <string>string</string>
+ </dict>
+ </array>''')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testListPolicy(self):
+ # Tests a policy group with a single policy of type 'list'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'ListGroup',
+ 'type': 'group',
+ 'desc': '',
+ 'caption': '',
+ 'policies': ['ListPolicy'],
+ },
+ {
+ 'name': 'ListPolicy',
+ 'type': 'list',
+ 'schema': {
+ 'type': 'array',
+ 'items': { 'type': 'string' },
+ },
+ 'supported_on': ['chrome.mac:8-'],
+ 'desc': '',
+ 'caption': '',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {},
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs(
+ 'Chromium', 'com.example.Test', '''<array>
+ <dict>
+ <key>pfm_name</key>
+ <string>ListPolicy</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_targets</key>
+ <array>
+ <string>user-managed</string>
+ </array>
+ <key>pfm_type</key>
+ <string>array</string>
+ <key>pfm_subkeys</key>
+ <array>
+ <dict>
+ <key>pfm_type</key>
+ <string>string</string>
+ </dict>
+ </array>
+ </dict>
+ </array>''')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testStringEnumListPolicy(self):
+ # Tests a policy group with a single policy of type 'string-enum-list'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'ListGroup',
+ 'type': 'group',
+ 'desc': '',
+ 'caption': '',
+ 'policies': ['ListPolicy'],
+ },
+ {
+ 'name': 'ListPolicy',
+ 'type': 'string-enum-list',
+ 'schema': {
+ 'type': 'array',
+ 'items': { 'type': 'string' },
+ },
+ 'items': [
+ {'name': 'ProxyServerDisabled', 'value': 'one', 'caption': ''},
+ {'name': 'ProxyServerAutoDetect', 'value': 'two', 'caption': ''},
+ ],
+ 'supported_on': ['chrome.mac:8-'],
+ 'supported_on': ['chrome.mac:8-'],
+ 'desc': '',
+ 'caption': '',
+ }
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {},
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs(
+ 'Chromium', 'com.example.Test', '''<array>
+ <dict>
+ <key>pfm_name</key>
+ <string>ListPolicy</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_targets</key>
+ <array>
+ <string>user-managed</string>
+ </array>
+ <key>pfm_type</key>
+ <string>array</string>
+ <key>pfm_subkeys</key>
+ <array>
+ <dict>
+ <key>pfm_type</key>
+ <string>string</string>
+ </dict>
+ </array>
+ </dict>
+ </array>''')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testIntPolicy(self):
+ # Tests a policy group with a single policy of type 'int'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'IntGroup',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['IntPolicy'],
+ },
+ {
+ 'name': 'IntPolicy',
+ 'type': 'int',
+ 'caption': '',
+ 'desc': '',
+ 'supported_on': ['chrome.mac:8-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {},
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs(
+ 'Chromium', 'com.example.Test', '''<array>
+ <dict>
+ <key>pfm_name</key>
+ <string>IntPolicy</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_targets</key>
+ <array>
+ <string>user-managed</string>
+ </array>
+ <key>pfm_type</key>
+ <string>integer</string>
+ </dict>
+ </array>''')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testIntEnumPolicy(self):
+ # Tests a policy group with a single policy of type 'int-enum'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'EnumGroup',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['EnumPolicy'],
+ },
+ {
+ 'name': 'EnumPolicy',
+ 'type': 'int-enum',
+ 'desc': '',
+ 'caption': '',
+ 'items': [
+ {'name': 'ProxyServerDisabled', 'value': 0, 'caption': ''},
+ {'name': 'ProxyServerAutoDetect', 'value': 1, 'caption': ''},
+ ],
+ 'supported_on': ['chrome.mac:8-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {},
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'mac_bundle_id': 'com.example.Test2'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs(
+ 'Google_Chrome', 'com.example.Test2', '''<array>
+ <dict>
+ <key>pfm_name</key>
+ <string>EnumPolicy</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_targets</key>
+ <array>
+ <string>user-managed</string>
+ </array>
+ <key>pfm_type</key>
+ <string>integer</string>
+ <key>pfm_range_list</key>
+ <array>
+ <integer>0</integer>
+ <integer>1</integer>
+ </array>
+ </dict>
+ </array>''')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testStringEnumPolicy(self):
+ # Tests a policy group with a single policy of type 'string-enum'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'EnumGroup',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['EnumPolicy'],
+ },
+ {
+ 'name': 'EnumPolicy',
+ 'type': 'string-enum',
+ 'desc': '',
+ 'caption': '',
+ 'items': [
+ {'name': 'ProxyServerDisabled', 'value': 'one', 'caption': ''},
+ {'name': 'ProxyServerAutoDetect', 'value': 'two', 'caption': ''},
+ ],
+ 'supported_on': ['chrome.mac:8-'],
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {},
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'mac_bundle_id': 'com.example.Test2'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs(
+ 'Google_Chrome', 'com.example.Test2', '''<array>
+ <dict>
+ <key>pfm_name</key>
+ <string>EnumPolicy</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_targets</key>
+ <array>
+ <string>user-managed</string>
+ </array>
+ <key>pfm_type</key>
+ <string>string</string>
+ <key>pfm_range_list</key>
+ <array>
+ <string>one</string>
+ <string>two</string>
+ </array>
+ </dict>
+ </array>''')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testDictionaryPolicy(self):
+ # Tests a policy group with a single policy of type 'dict'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'DictionaryGroup',
+ 'type': 'group',
+ 'desc': '',
+ 'caption': '',
+ 'policies': ['DictionaryPolicy'],
+ },
+ {
+ 'name': 'DictionaryPolicy',
+ 'type': 'dict',
+ 'supported_on': ['chrome.mac:8-'],
+ 'desc': '',
+ 'caption': '',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {},
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs(
+ 'Chromium', 'com.example.Test', '''<array>
+ <dict>
+ <key>pfm_name</key>
+ <string>DictionaryPolicy</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_targets</key>
+ <array>
+ <string>user-managed</string>
+ </array>
+ <key>pfm_type</key>
+ <string>dictionary</string>
+ </dict>
+ </array>''')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testExternalPolicy(self):
+ # Tests a policy group with a single policy of type 'dict'.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'ExternalGroup',
+ 'type': 'group',
+ 'desc': '',
+ 'caption': '',
+ 'policies': ['ExternalPolicy'],
+ },
+ {
+ 'name': 'ExternalPolicy',
+ 'type': 'external',
+ 'supported_on': ['chrome.mac:8-'],
+ 'desc': '',
+ 'caption': '',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {},
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'mac_bundle_id': 'com.example.Test'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs(
+ 'Chromium', 'com.example.Test', '''<array>
+ <dict>
+ <key>pfm_name</key>
+ <string>ExternalPolicy</string>
+ <key>pfm_description</key>
+ <string/>
+ <key>pfm_title</key>
+ <string/>
+ <key>pfm_targets</key>
+ <array>
+ <string>user-managed</string>
+ </array>
+ <key>pfm_type</key>
+ <string>dictionary</string>
+ </dict>
+ </array>''')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testNonSupportedPolicy(self):
+ # Tests a policy that is not supported on Mac, so it shouldn't
+ # be included in the plist file.
+ policy_json = '''
+ {
+ 'policy_definitions': [
+ {
+ 'name': 'NonMacGroup',
+ 'type': 'group',
+ 'caption': '',
+ 'desc': '',
+ 'policies': ['NonMacPolicy'],
+ },
+ {
+ 'name': 'NonMacPolicy',
+ 'type': 'string',
+ 'supported_on': ['chrome.linux:8-', 'chrome.win:7-'],
+ 'caption': '',
+ 'desc': '',
+ },
+ ],
+ 'policy_atomic_group_definitions': [],
+ 'placeholders': [],
+ 'messages': {},
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_google_chrome': '1',
+ 'mac_bundle_id': 'com.example.Test2'
+ }, 'plist')
+ expected_output = self._GetExpectedOutputs(
+ 'Google_Chrome', 'com.example.Test2', '''<array/>''')
+ self.assertEquals(output.strip(), expected_output.strip())
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/reg_writer.py b/chromium/components/policy/tools/template_writers/writers/reg_writer.py
new file mode 100755
index 00000000000..3fbd0a6b358
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/reg_writer.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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 json
+
+from writers import template_writer
+
+
+def GetWriter(config):
+ '''Factory method for creating RegWriter objects.
+ See the constructor of TemplateWriter for description of
+ arguments.
+ '''
+ return RegWriter(['win', 'win7'], config)
+
+
+class RegWriter(template_writer.TemplateWriter):
+ '''Class for generating policy example files in .reg format (for Windows).
+ The generated files will define all the supported policies with example
+ values set for them. This class is used by PolicyTemplateGenerator to
+ write .reg files.
+ '''
+
+ NEWLINE = '\r\n'
+
+ def _QuoteAndEscapeString(self, string):
+ assert isinstance(string, str)
+ return json.dumps(string)
+
+ def _StartBlock(self, key, suffix, list):
+ key = 'HKEY_LOCAL_MACHINE\\' + key
+ if suffix:
+ key = key + '\\' + suffix
+ if key != self._last_key.get(id(list), None):
+ list.append('')
+ list.append('[%s]' % key)
+ self._last_key[id(list)] = key
+
+ def PreprocessPolicies(self, policy_list):
+ return self.FlattenGroupsAndSortPolicies(policy_list,
+ self.GetPolicySortingKey)
+
+ def GetPolicySortingKey(self, policy):
+ '''Extracts a sorting key from a policy. These keys can be used for
+ list.sort() methods to sort policies.
+ See TemplateWriter.SortPoliciesGroupsFirst for usage.
+ '''
+ is_list = policy['type'] in ('list', 'string-enum-list')
+ # Lists come after regular policies.
+ return (is_list, policy['name'])
+
+ def _WritePolicy(self, policy, key, list):
+ example_value = policy['example_value']
+
+ if policy['type'] in ('list', 'string-enum-list'):
+ self._StartBlock(key, policy['name'], list)
+ i = 1
+ for item in example_value:
+ list.append('"%d"=%s' % (i, self._QuoteAndEscapeString(item)))
+ i = i + 1
+ else:
+ self._StartBlock(key, None, list)
+ if policy['type'] in ('string', 'string-enum'):
+ example_value_str = self._QuoteAndEscapeString(example_value)
+ elif policy['type'] in ('dict', 'external'):
+ example_value_str = self._QuoteAndEscapeString(
+ json.dumps(example_value, sort_keys=True))
+ elif policy['type'] in ('main', 'int', 'int-enum'):
+ example_value_str = 'dword:%08x' % int(example_value)
+ else:
+ raise Exception('unknown policy type %s:' % policy['type'])
+
+ list.append('"%s"=%s' % (policy['name'], example_value_str))
+
+ def WriteComment(self, comment):
+ self._prefix.append('; ' + comment)
+
+ def WritePolicy(self, policy):
+ if self.CanBeMandatory(policy):
+ self._WritePolicy(policy, self._winconfig['reg_mandatory_key_name'],
+ self._mandatory)
+
+ def WriteRecommendedPolicy(self, policy):
+ self._WritePolicy(policy, self._winconfig['reg_recommended_key_name'],
+ self._recommended)
+
+ def BeginTemplate(self):
+ pass
+
+ def EndTemplate(self):
+ pass
+
+ def Init(self):
+ self._mandatory = []
+ self._recommended = []
+ self._last_key = {}
+ self._prefix = []
+ self._winconfig = self.config['win_config']['win']
+
+ def GetTemplateText(self):
+ self._prefix.append('Windows Registry Editor Version 5.00')
+ if self._GetChromiumVersionString() is not None:
+ self.WriteComment(self.config['build'] + ' version: ' + \
+ self._GetChromiumVersionString())
+ all = self._prefix + self._mandatory + self._recommended
+ return self.NEWLINE.join(all)
diff --git a/chromium/components/policy/tools/template_writers/writers/reg_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/reg_writer_unittest.py
new file mode 100755
index 00000000000..7278f76b007
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/reg_writer_unittest.py
@@ -0,0 +1,428 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+'''Unit tests for writers.reg_writer'''
+
+import os
+import sys
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+import unittest
+
+from writers import writer_unittest_common
+
+
+class RegWriterUnittest(writer_unittest_common.WriterUnittestCommon):
+ '''Unit tests for RegWriter.'''
+
+ NEWLINE = '\r\n'
+
+ def CompareOutputs(self, output, expected_output):
+ '''Compares the output of the reg_writer with its expected output.
+
+ Args:
+ output: The output of the reg writer.
+ expected_output: The expected output.
+
+ Raises:
+ AssertionError: if the two strings are not equivalent.
+ '''
+ self.assertEquals(output.strip(), expected_output.strip())
+
+ def testEmpty(self):
+ # Test the handling of an empty policy list.
+ policy_json = '''
+ {
+ "policy_definitions": [],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {}
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ }, 'reg')
+ expected_output = 'Windows Registry Editor Version 5.00'
+ self.CompareOutputs(output, expected_output)
+
+ def testEmptyVersion(self):
+ # Test the handling of an empty policy list.
+ policy_json = '''
+ {
+ "policy_definitions": [],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {}
+ }'''
+ output = self.GetOutput(policy_json, {
+ '_chromium': '1',
+ 'version': '39.0.0.0'
+ }, 'reg')
+ expected_output = ('Windows Registry Editor Version 5.00\r\n'
+ '; chromium version: 39.0.0.0\r\n')
+ self.CompareOutputs(output, expected_output)
+
+ def testMainPolicy(self):
+ # Tests a policy group with a single policy of type 'main'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "MainPolicy",
+ "type": "main",
+ "features": { "can_be_recommended": True },
+ "caption": "",
+ "desc": "",
+ "supported_on": ["chrome.win:8-"],
+ "example_value": True
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'reg')
+ expected_output = self.NEWLINE.join([
+ 'Windows Registry Editor Version 5.00', '',
+ '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Google\\Chrome]',
+ '"MainPolicy"=dword:00000001', '',
+ '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Google\\Chrome\\Recommended]',
+ '"MainPolicy"=dword:00000001'
+ ])
+ self.CompareOutputs(output, expected_output)
+
+ def testRecommendedMainPolicy(self):
+ # Tests a policy group with a single policy of type 'main'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "MainPolicy",
+ "type": "main",
+ "features": {
+ "can_be_recommended": True,
+ "can_be_mandatory": False
+ },
+ "caption": "",
+ "desc": "",
+ "supported_on": ["chrome.win:8-"],
+ "example_value": True
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'reg')
+ expected_output = self.NEWLINE.join([
+ 'Windows Registry Editor Version 5.00', '',
+ '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Google\\Chrome\\Recommended]',
+ '"MainPolicy"=dword:00000001'
+ ])
+ self.CompareOutputs(output, expected_output)
+
+ def testStringPolicy(self):
+ # Tests a policy group with a single policy of type 'string'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "StringPolicy",
+ "type": "string",
+ "caption": "",
+ "desc": "",
+ "supported_on": ["chrome.win:8-"],
+ "example_value": "hello, world! \\\" \\\\"
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'reg')
+ expected_output = self.NEWLINE.join([
+ 'Windows Registry Editor Version 5.00', '',
+ '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Chromium]',
+ '"StringPolicy"="hello, world! \\\" \\\\"'
+ ])
+ self.CompareOutputs(output, expected_output)
+
+ def testIntPolicy(self):
+ # Tests a policy group with a single policy of type 'int'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "IntPolicy",
+ "type": "int",
+ "caption": "",
+ "desc": "",
+ "supported_on": ["chrome.win:8-"],
+ "example_value": 26
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'reg')
+ expected_output = self.NEWLINE.join([
+ 'Windows Registry Editor Version 5.00', '',
+ '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Chromium]',
+ '"IntPolicy"=dword:0000001a'
+ ])
+ self.CompareOutputs(output, expected_output)
+
+ def testIntEnumPolicy(self):
+ # Tests a policy group with a single policy of type 'int-enum'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "EnumPolicy",
+ "type": "int-enum",
+ "caption": "",
+ "desc": "",
+ "items": [
+ {"name": "ProxyServerDisabled", "value": 0, "caption": ""},
+ {"name": "ProxyServerAutoDetect", "value": 1, "caption": ""},
+ ],
+ "supported_on": ["chrome.win:8-"],
+ "example_value": 1
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'reg')
+ expected_output = self.NEWLINE.join([
+ 'Windows Registry Editor Version 5.00', '',
+ '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Google\\Chrome]',
+ '"EnumPolicy"=dword:00000001'
+ ])
+ self.CompareOutputs(output, expected_output)
+
+ def testStringEnumPolicy(self):
+ # Tests a policy group with a single policy of type 'string-enum'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "EnumPolicy",
+ "type": "string-enum",
+ "caption": "",
+ "desc": "",
+ "items": [
+ {"name": "ProxyServerDisabled", "value": "one", "caption": ""},
+ {"name": "ProxyServerAutoDetect", "value": "two","caption": ""},
+ ],
+ "supported_on": ["chrome.win:8-"],
+ "example_value": "two"
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'reg')
+ expected_output = self.NEWLINE.join([
+ 'Windows Registry Editor Version 5.00', '',
+ '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Google\\Chrome]',
+ '"EnumPolicy"="two"'
+ ])
+ self.CompareOutputs(output, expected_output)
+
+ def testListPolicy(self):
+ # Tests a policy group with a single policy of type 'list'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "ListPolicy",
+ "type": "list",
+ "caption": "",
+ "desc": "",
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": ["foo", "bar"]
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'reg')
+ expected_output = self.NEWLINE.join([
+ 'Windows Registry Editor Version 5.00', '',
+ '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Chromium\\ListPolicy]',
+ '"1"="foo"', '"2"="bar"'
+ ])
+
+ def testStringEnumListPolicy(self):
+ # Tests a policy group with a single policy of type 'string-enum-list'.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "ListPolicy",
+ "type": "string-enum-list",
+ "caption": "",
+ "desc": "",
+ "items": [
+ {"name": "ProxyServerDisabled", "value": "foo", "caption": ""},
+ {"name": "ProxyServerAutoDetect", "value": "bar","caption": ""},
+ ],
+ "supported_on": ["chrome.linux:8-"],
+ "example_value": ["foo", "bar"]
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'reg')
+ expected_output = self.NEWLINE.join([
+ 'Windows Registry Editor Version 5.00', '',
+ '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Chromium\\ListPolicy]',
+ '"1"="foo"', '"2"="bar"'
+ ])
+
+ def testDictionaryPolicy(self):
+ # Tests a policy group with a single policy of type 'dict'.
+ example = {
+ 'bool': True,
+ 'dict': {
+ 'a': 1,
+ 'b': 2,
+ },
+ 'int': 10,
+ 'list': [1, 2, 3],
+ 'string': 'abc',
+ }
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "DictionaryPolicy",
+ "type": "dict",
+ "caption": "",
+ "desc": "",
+ "supported_on": ["chrome.win:8-"],
+ "example_value": ''' + str(example) + '''
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'reg')
+ expected_output = self.NEWLINE.join([
+ 'Windows Registry Editor Version 5.00', '',
+ '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Chromium]',
+ '"DictionaryPolicy"="{\\"bool\\": true, '
+ '\\"dict\\": {\\"a\\": 1, \\"b\\": 2}, \\"int\\": 10, '
+ '\\"list\\": [1, 2, 3], \\"string\\": \\"abc\\"}"'
+ ])
+ self.CompareOutputs(output, expected_output)
+
+ def testExternalPolicy(self):
+ # Tests a policy group with a single policy of type 'external'.
+ example = {
+ 'url': "https://example.com/avatar.jpg",
+ 'hash': "deadbeef",
+ }
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "ExternalPolicy",
+ "type": "external",
+ "caption": "",
+ "desc": "",
+ "supported_on": ["chrome.win:8-"],
+ "example_value": %s
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }''' % str(example)
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'reg')
+ expected_output = self.NEWLINE.join([
+ 'Windows Registry Editor Version 5.00', '',
+ '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Chromium]',
+ '"ExternalPolicy"="{\\"hash\\": \\"deadbeef\\", '
+ '\\"url\\": \\"https://example.com/avatar.jpg\\"}"'
+ ])
+ self.CompareOutputs(output, expected_output)
+
+ def testNonSupportedPolicy(self):
+ # Tests a policy that is not supported on Windows, so it shouldn't
+ # be included in the .REG file.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "NonWindowsPolicy",
+ "type": "list",
+ "caption": "",
+ "desc": "",
+ "supported_on": ["chrome.mac:8-"],
+ "example_value": ["a"]
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'reg')
+ expected_output = self.NEWLINE.join(
+ ['Windows Registry Editor Version 5.00'])
+ self.CompareOutputs(output, expected_output)
+
+ def testPolicyGroup(self):
+ # Tests a policy group that has more than one policies.
+ policy_json = '''
+ {
+ "policy_definitions": [
+ {
+ "name": "Group1",
+ "type": "group",
+ "caption": "",
+ "desc": "",
+ "policies": ["Policy1", "Policy2"],
+ },
+ {
+ "name": "Policy1",
+ "type": "list",
+ "caption": "",
+ "desc": "",
+ "supported_on": ["chrome.win:8-"],
+ "example_value": ["a", "b"]
+ },
+ {
+ "name": "Policy2",
+ "type": "string",
+ "caption": "",
+ "desc": "",
+ "supported_on": ["chrome.win:8-"],
+ "example_value": "c"
+ },
+ ],
+ "policy_atomic_group_definitions": [],
+ "placeholders": [],
+ "messages": {},
+ }'''
+ output = self.GetOutput(policy_json, {'_chromium': '1'}, 'reg')
+ expected_output = self.NEWLINE.join([
+ 'Windows Registry Editor Version 5.00', '',
+ '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Chromium]', '"Policy2"="c"',
+ '', '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Chromium\\Policy1]',
+ '"1"="a"', '"2"="b"'
+ ])
+ self.CompareOutputs(output, expected_output)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/template_writer.py b/chromium/components/policy/tools/template_writers/writers/template_writer.py
new file mode 100755
index 00000000000..075a381aa2d
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/template_writer.py
@@ -0,0 +1,468 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+
+
+class TemplateWriter(object):
+ '''Abstract base class for writing policy templates in various formats.
+ The methods of this class will be called by PolicyTemplateGenerator.
+ '''
+ def __init__(self, platforms, config):
+ '''Initializes a TemplateWriter object.
+
+ Args:
+ platforms: List of platforms for which this writer can write policies.
+ config: A dictionary of information required to generate the template.
+ It contains some key-value pairs, including the following examples:
+ 'build': 'chrome' or 'chromium'
+ 'branding': 'Google Chrome' or 'Chromium'
+ 'mac_bundle_id': The Mac bundle id of Chrome. (Only set when building
+ for Mac.)
+ '''
+ self.platforms = platforms
+ self.config = config
+
+ def IsDeprecatedPolicySupported(self, policy):
+ '''Checks if the given deprecated policy is supported by the writer.
+
+ Args:
+ policy: The dictionary of the policy.
+
+ Returns:
+ True if the writer chooses to include the deprecated 'policy' in its
+ output.
+ '''
+ return False
+
+ def IsFuturePolicySupported(self, policy):
+ '''Checks if the given future policy is supported by the writer.
+
+ Args:
+ policy: The dictionary of the policy.
+
+ Returns:
+ True if the writer chooses to include the unreleased 'policy' in its
+ output.
+ '''
+ return False
+
+ def IsCloudOnlyPolicySupported(self, policy):
+ '''Checks if the given cloud only policy is supported by the writer.
+
+ Args:
+ policy: The dictionary of the policy.
+
+ Returns:
+ True if the writer chooses to include the cloud only 'policy' in its
+ output.
+ '''
+ return False
+
+ def IsInternalOnlyPolicySupported(self, policy):
+ '''Checks if the given internal policy is supported by the writer.
+
+ Args:
+ policy: The dictionary of the policy.
+
+ Returns:
+ True if the writer chooses to include the internal only 'policy' in its
+ output.
+ '''
+ return False
+
+ def IsPolicySupported(self, policy):
+ '''Checks if the given policy is supported by the writer.
+ In other words, the set of platforms supported by the writer
+ has a common subset with the set of platforms that support
+ the policy.
+
+ Args:
+ policy: The dictionary of the policy.
+
+ Returns:
+ True if the writer chooses to include 'policy' in its output.
+ '''
+ if ('deprecated' in policy and policy['deprecated'] is True
+ and not self.IsDeprecatedPolicySupported(policy)):
+ return False
+
+ if (self.IsCloudOnlyPolicy(policy)
+ and not self.IsCloudOnlyPolicySupported(policy)):
+ return False
+
+ if (self.IsInternalOnlyPolicy(policy)
+ and not self.IsInternalOnlyPolicySupported(policy)):
+ return False
+
+ for supported_on in policy['supported_on']:
+ if not self.IsVersionSupported(policy, supported_on):
+ continue
+ if '*' in self.platforms or supported_on['platform'] in self.platforms:
+ return True
+
+ if self.IsFuturePolicySupported(policy):
+ if '*' in self.platforms and policy['future_on']:
+ return True
+ for future in policy['future_on']:
+ if future['platform'] in self.platforms:
+ return True
+ return False
+
+ def GetPolicyFeature(self, policy, feature_name, value=None):
+ '''Returns policy feature with |feature_name| if exsits. Otherwise, returns
+ |value|.'''
+ return policy.get('features', {}).get(feature_name, value)
+
+ def CanBeRecommended(self, policy):
+ '''Checks if the given policy can be recommended.'''
+ return self.GetPolicyFeature(policy, 'can_be_recommended', False)
+
+ def CanBeMandatory(self, policy):
+ '''Checks if the given policy can be mandatory.'''
+ return self.GetPolicyFeature(policy, 'can_be_mandatory', True)
+
+ def IsCloudOnlyPolicy(self, policy):
+ '''Checks if the given policy is cloud only'''
+ return self.GetPolicyFeature(policy, 'cloud_only', False)
+
+ def IsInternalOnlyPolicy(self, policy):
+ '''Checks if the given policy is internal only'''
+ return self.GetPolicyFeature(policy, 'internal_only', False)
+
+ def IsPolicyOrItemSupportedOnPlatform(self,
+ item,
+ platform,
+ product=None,
+ management=None):
+ '''Checks if |item| is supported on |product| for |platform|. If
+ |product| is not specified, only the platform support is checked.
+ If |management| is specified, also checks for support for Chrome OS
+ management type.
+
+ Args:
+ item: The dictionary of the policy or item.
+ platform: The platform to check; one of
+ 'win', 'mac', 'linux', 'chrome_os', 'android'.
+ product: Optional product to check; one of
+ 'chrome', 'chrome_frame', 'chrome_os', 'webview'.
+ management: Optional Chrome OS management type to check; one of
+ 'active_directory', 'google_cloud'.
+ '''
+ if management and not self.IsCrOSManagementSupported(item, management):
+ return False
+
+ for supported_on in item['supported_on']:
+ if (platform == supported_on['platform']
+ and (not product or product in supported_on['product'])
+ and self.IsVersionSupported(item, supported_on)):
+ return True
+ if self.IsFuturePolicySupported(item):
+ if (product and {
+ 'platform': platform,
+ 'product': product
+ } in item.get('future_on', [])):
+ return True
+ if (not product and filter(lambda f: f['platform'] == platform,
+ item.get('future_on', []))):
+ return True
+ return False
+
+ def IsPolicySupportedOnWindows(self, policy, product=None):
+ ''' Checks if |policy| is supported on any Windows platform.
+
+ Args:
+ policy: The dictionary of the policy.
+ product: Optional product to check; one of
+ 'chrome', 'chrome_frame', 'chrome_os', 'webview'
+ '''
+ return (self.IsPolicyOrItemSupportedOnPlatform(policy, 'win', product)
+ or self.IsPolicyOrItemSupportedOnPlatform(policy, 'win7', product))
+
+ def IsCrOSManagementSupported(self, policy, management):
+ '''Checks whether |policy| supports the Chrome OS |management| type.
+
+ Args:
+ policy: The dictionary of the policy.
+ management: Chrome OS management type to check; one of
+ 'active_directory', 'google_cloud'.
+ '''
+ # By default, i.e. if supported_chrome_os_management is not set, all
+ # management types are supported.
+ return management in policy.get('supported_chrome_os_management',
+ ['active_directory', 'google_cloud'])
+
+ def IsVersionSupported(self, policy, supported_on):
+ '''Checks whether the policy is supported on current version'''
+ major_version = self._GetChromiumMajorVersion()
+ if not major_version:
+ return True
+
+ since_version = supported_on.get('since_version', None)
+ until_version = supported_on.get('until_version', None)
+
+ return ((not since_version or int(since_version) <= major_version)
+ and (not until_version or int(until_version) >= major_version))
+
+ def _GetChromiumVersionString(self):
+ '''Returns the Chromium version string stored in the environment variable
+ version (if it is set).
+
+ Returns: The Chromium version string or None if it has not been set.'''
+
+ return self.config.get('version', None)
+
+ def _GetChromiumMajorVersion(self):
+ ''' Returns the major version of Chromium if it exists
+ in config.
+ '''
+ return self.config.get('major_version', None)
+
+ def _GetPoliciesForWriter(self, group):
+ '''Filters the list of policies in the passed group that are supported by
+ the writer.
+
+ Args:
+ group: The dictionary of the policy group.
+
+ Returns: The list of policies of the policy group that are compatible
+ with the writer.
+ '''
+ if not 'policies' in group:
+ return []
+ result = []
+ for policy in group['policies']:
+ if self.IsPolicySupported(policy):
+ result.append(policy)
+ return result
+
+ def Init(self):
+ '''Initializes the writer. If the WriteTemplate method is overridden, then
+ this method must be called as first step of each template generation
+ process.
+ '''
+ pass
+
+ def WriteTemplate(self, template):
+ '''Writes the given template definition.
+
+ Args:
+ template: Template definition to write.
+
+ Returns:
+ Generated output for the passed template definition.
+ '''
+ self.messages = template['messages']
+ self.Init()
+ template['policy_definitions'] = \
+ self.PreprocessPolicies(template['policy_definitions'])
+ self.BeginTemplate()
+ self.WritePolicies(template['policy_definitions'])
+ self.EndTemplate()
+
+ return self.GetTemplateText()
+
+ def PreprocessPolicies(self, policy_list):
+ '''Preprocesses a list of policies according to a given writer's needs.
+ Preprocessing steps include sorting policies and stripping unneeded
+ information such as groups (for writers that ignore them).
+ Subclasses are encouraged to override this method, overriding
+ implementations may call one of the provided specialized implementations.
+ The default behaviour is to use SortPoliciesGroupsFirst().
+
+ Args:
+ policy_list: A list containing the policies to sort.
+
+ Returns:
+ The sorted policy list.
+ '''
+ return self.SortPoliciesGroupsFirst(policy_list)
+
+ def WritePolicies(self, policy_list):
+ '''Appends the template text corresponding to all the policies into the
+ internal buffer.
+
+ Args:
+ policy_list: A list containing the policies to write.
+ '''
+ for policy in policy_list:
+ if policy['type'] == 'group':
+ child_policies = list(self._GetPoliciesForWriter(policy))
+ child_recommended_policies = list(
+ filter(self.CanBeRecommended, child_policies))
+ if child_policies:
+ # Only write nonempty groups.
+ self.BeginPolicyGroup(policy)
+ for child_policy in child_policies:
+ # Nesting of groups is currently not supported.
+ self.WritePolicy(child_policy)
+ self.EndPolicyGroup()
+ if child_recommended_policies:
+ self.BeginRecommendedPolicyGroup(policy)
+ for child_policy in child_recommended_policies:
+ self.WriteRecommendedPolicy(child_policy)
+ self.EndRecommendedPolicyGroup()
+ elif self.IsPolicySupported(policy):
+ self.WritePolicy(policy)
+ if self.CanBeRecommended(policy):
+ self.WriteRecommendedPolicy(policy)
+
+ def WritePolicy(self, policy):
+ '''Appends the template text corresponding to a policy into the
+ internal buffer.
+
+ Args:
+ policy: The policy as it is found in the JSON file.
+ '''
+ raise NotImplementedError()
+
+ def WriteComment(self, comment):
+ '''Appends the comment to the internal buffer.
+
+ comment: The comment to be added.
+ '''
+ raise NotImplementedError()
+
+ def WriteRecommendedPolicy(self, policy):
+ '''Appends the template text corresponding to a recommended policy into the
+ internal buffer.
+
+ Args:
+ policy: The recommended policy as it is found in the JSON file.
+ '''
+ # TODO
+ #raise NotImplementedError()
+ pass
+
+ def BeginPolicyGroup(self, group):
+ '''Appends the template text corresponding to the beginning of a
+ policy group into the internal buffer.
+
+ Args:
+ group: The policy group as it is found in the JSON file.
+ '''
+ pass
+
+ def EndPolicyGroup(self):
+ '''Appends the template text corresponding to the end of a
+ policy group into the internal buffer.
+ '''
+ pass
+
+ def BeginRecommendedPolicyGroup(self, group):
+ '''Appends the template text corresponding to the beginning of a recommended
+ policy group into the internal buffer.
+
+ Args:
+ group: The recommended policy group as it is found in the JSON file.
+ '''
+ pass
+
+ def EndRecommendedPolicyGroup(self):
+ '''Appends the template text corresponding to the end of a recommended
+ policy group into the internal buffer.
+ '''
+ pass
+
+ def BeginTemplate(self):
+ '''Appends the text corresponding to the beginning of the whole
+ template into the internal buffer.
+ '''
+ raise NotImplementedError()
+
+ def EndTemplate(self):
+ '''Appends the text corresponding to the end of the whole
+ template into the internal buffer.
+ '''
+ pass
+
+ def GetTemplateText(self):
+ '''Gets the content of the internal template buffer.
+
+ Returns:
+ The generated template from the the internal buffer as a string.
+ '''
+ raise NotImplementedError()
+
+ def SortPoliciesGroupsFirst(self, policy_list):
+ '''Sorts a list of policies alphabetically. The order is the
+ following: first groups alphabetically by caption, then other policies
+ alphabetically by name. The order of policies inside groups is unchanged.
+
+ Args:
+ policy_list: The list of policies to sort. Sub-lists in groups will not
+ be sorted.
+ '''
+ policy_list.sort(key=self.GetPolicySortingKeyGroupsFirst)
+ return policy_list
+
+ def FlattenGroupsAndSortPolicies(self, policy_list, sorting_key=None):
+ '''Sorts a list of policies according to |sorting_key|, defaulting
+ to alphabetical sorting if no key is given. If |policy_list| contains
+ policies with type="group", it is flattened first, i.e. any groups' contents
+ are inserted into the list as first-class elements and the groups are then
+ removed.
+ '''
+ new_list = []
+ for policy in policy_list:
+ if policy['type'] == 'group':
+ for grouped_policy in policy['policies']:
+ new_list.append(grouped_policy)
+ else:
+ new_list.append(policy)
+ if sorting_key == None:
+ sorting_key = self.GetPolicySortingKeyName
+ new_list.sort(key=sorting_key)
+ return new_list
+
+ def GetPolicySortingKeyName(self, policy):
+ return policy['name']
+
+ def GetPolicySortingKeyGroupsFirst(self, policy):
+ '''Extracts a sorting key from a policy. These keys can be used for
+ list.sort() methods to sort policies.
+ See TemplateWriter.SortPolicies for usage.
+ '''
+ is_group = policy['type'] == 'group'
+ if is_group:
+ # Groups are sorted by caption.
+ str_key = policy['caption']
+ else:
+ # Regular policies are sorted by name.
+ str_key = policy['name']
+ # Groups come before regular policies.
+ return (not is_group, str_key)
+
+ def GetLocalizedMessage(self, msg_id):
+ '''Returns a localized message for this writer.
+
+ Args:
+ msg_id: The identifier of the message.
+
+ Returns:
+ The localized message.
+ '''
+ return self.messages['doc_' + msg_id]['text']
+
+ def HasExpandedPolicyDescription(self, policy):
+ '''Returns whether the policy has expanded documentation containing the link
+ to the documentation with schema and formatting.
+ '''
+ return (policy['type'] in ('dict', 'external') or 'url_schema' in policy
+ or 'validation_schema' in policy or 'description_schema' in policy)
+
+ def GetExpandedPolicyDescription(self, policy):
+ '''Returns the expanded description of the policy containing the link to the
+ documentation with schema and formatting.
+ '''
+ schema_description_link_text = self.GetLocalizedMessage(
+ 'schema_description_link')
+ url = None
+ if 'url_schema' in policy:
+ url = policy['url_schema']
+ if (policy['type'] in ('dict', 'external') or 'validation_schema' in policy
+ or 'description_schema' in policy):
+ url = (
+ 'https://cloud.google.com/docs/chrome-enterprise/policies/?policy=' +
+ policy['name'])
+ return schema_description_link_text.replace('$6', url) if url else ''
diff --git a/chromium/components/policy/tools/template_writers/writers/template_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/template_writer_unittest.py
new file mode 100755
index 00000000000..62c2e5e3fbc
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/template_writer_unittest.py
@@ -0,0 +1,287 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+'''Unit tests for writers.template_writer'''
+
+import os
+import sys
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+import unittest
+
+from writers import template_writer
+
+POLICY_DEFS = [
+ {
+ 'name': 'zp',
+ 'type': 'string',
+ 'caption': 'a1',
+ 'supported_on': []
+ },
+ {
+ 'type':
+ 'group',
+ 'caption':
+ 'z_group1_caption',
+ 'name':
+ 'group1',
+ 'policies': [{
+ 'name': 'z0',
+ 'type': 'string',
+ 'supported_on': []
+ }, {
+ 'name': 'a0',
+ 'type': 'string',
+ 'supported_on': []
+ }]
+ },
+ {
+ 'type': 'group',
+ 'caption': 'b_group2_caption',
+ 'name': 'group2',
+ 'policies': [{
+ 'name': 'q',
+ 'type': 'string',
+ 'supported_on': []
+ }],
+ }, {
+ 'name': 'ap',
+ 'type': 'string',
+ 'caption': 'a2',
+ 'supported_on': []
+ }
+]
+
+GROUP_FIRST_SORTED_POLICY_DEFS = [
+ {
+ 'type': 'group',
+ 'caption': 'b_group2_caption',
+ 'name': 'group2',
+ 'policies': [{
+ 'name': 'q',
+ 'type': 'string',
+ 'supported_on': []
+ }],
+ },
+ {
+ 'type':
+ 'group',
+ 'caption':
+ 'z_group1_caption',
+ 'name':
+ 'group1',
+ 'policies': [{
+ 'name': 'z0',
+ 'type': 'string',
+ 'supported_on': []
+ }, {
+ 'name': 'a0',
+ 'type': 'string',
+ 'supported_on': []
+ }]
+ },
+ {
+ 'name': 'ap',
+ 'type': 'string',
+ 'caption': 'a2',
+ 'supported_on': []
+ },
+ {
+ 'name': 'zp',
+ 'type': 'string',
+ 'caption': 'a1',
+ 'supported_on': []
+ },
+]
+
+IGNORE_GROUPS_SORTED_POLICY_DEFS = [
+ {
+ 'name': 'a0',
+ 'type': 'string',
+ 'supported_on': []
+ },
+ {
+ 'name': 'ap',
+ 'type': 'string',
+ 'caption': 'a2',
+ 'supported_on': []
+ },
+ {
+ 'name': 'q',
+ 'type': 'string',
+ 'supported_on': []
+ },
+ {
+ 'name': 'z0',
+ 'type': 'string',
+ 'supported_on': []
+ },
+ {
+ 'name': 'zp',
+ 'type': 'string',
+ 'caption': 'a1',
+ 'supported_on': []
+ },
+]
+
+
+class TemplateWriterUnittests(unittest.TestCase):
+ '''Unit tests for templater_writer.py.'''
+
+ def _IsPolicySupported(self,
+ platform,
+ version,
+ policy,
+ writer=template_writer.TemplateWriter):
+ tw = writer([platform], {'major_version': version})
+ if platform != '*':
+ self.assertEqual(
+ tw.IsPolicySupported(policy),
+ tw.IsPolicyOrItemSupportedOnPlatform(policy, platform))
+ return tw.IsPolicySupported(policy)
+
+ def testSortingGroupsFirst(self):
+ tw = template_writer.TemplateWriter(None, None)
+ sorted_list = tw.SortPoliciesGroupsFirst(POLICY_DEFS)
+ self.assertEqual(sorted_list, GROUP_FIRST_SORTED_POLICY_DEFS)
+
+ def testSortingIgnoreGroups(self):
+ tw = template_writer.TemplateWriter(None, None)
+ sorted_list = tw.FlattenGroupsAndSortPolicies(POLICY_DEFS)
+ self.assertEqual(sorted_list, IGNORE_GROUPS_SORTED_POLICY_DEFS)
+
+ def testPoliciesIsNotSupported(self):
+ tw = template_writer.TemplateWriter(None, None)
+ self.assertFalse(tw.IsPolicySupported({'deprecated': True}))
+ self.assertFalse(tw.IsPolicySupported({'features': {'cloud_only': True}}))
+ self.assertFalse(tw.IsPolicySupported({'features': {
+ 'internal_only': True
+ }}))
+
+ def testFuturePoliciesSupport(self):
+ class FutureWriter(template_writer.TemplateWriter):
+ def IsFuturePolicySupported(self, policy):
+ return True
+
+ expected_request_for_all_platforms = [[False, True, True],
+ [True, True, True]]
+ expected_request_for_all_win = [[False, False, True], [True, True, True]]
+ for i, writer in enumerate([template_writer.TemplateWriter, FutureWriter]):
+ for j, policy in enumerate([{
+ 'supported_on': [],
+ 'future_on': [{
+ 'product': 'chrome',
+ 'platform': 'win'
+ }, {
+ 'product': 'chrome',
+ 'platform': 'mac'
+ }]
+ }, {
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'mac'
+ }],
+ 'future_on': [{
+ 'product': 'chrome',
+ 'platform': 'win'
+ }]
+ }, {
+ 'supported_on': [{
+ 'product': 'chrome',
+ 'platform': 'win'
+ }, {
+ 'product': 'chrome',
+ 'platform': 'mac'
+ }],
+ 'future_on': []
+ }]):
+ self.assertEqual(expected_request_for_all_platforms[i][j],
+ self._IsPolicySupported('*', None, policy, writer))
+ self.assertEqual(
+ expected_request_for_all_win[i][j],
+ self._IsPolicySupported('win', None, policy, writer),
+ )
+
+ def testPoliciesIsSupportedOnCertainVersion(self):
+ platform = 'win'
+ policy = {
+ 'supported_on': [{
+ 'platform': 'win',
+ 'since_version': '11',
+ 'until_version': '12'
+ }]
+ }
+ self.assertFalse(self._IsPolicySupported(platform, 10, policy))
+ self.assertTrue(self._IsPolicySupported(platform, 11, policy))
+ self.assertTrue(self._IsPolicySupported(platform, 12, policy))
+ self.assertFalse(self._IsPolicySupported(platform, 13, policy))
+
+ policy = {
+ 'supported_on': [{
+ 'platform': 'win',
+ 'since_version': '11',
+ 'until_version': ''
+ }]
+ }
+ self.assertFalse(self._IsPolicySupported(platform, 10, policy))
+ self.assertTrue(self._IsPolicySupported(platform, 11, policy))
+ self.assertTrue(self._IsPolicySupported(platform, 12, policy))
+ self.assertTrue(self._IsPolicySupported(platform, 13, policy))
+
+ def testPoliciesIsSupportedOnMulitplePlatform(self):
+ policy = {
+ 'supported_on': [{
+ 'platform': 'win',
+ 'since_version': '12',
+ 'until_version': ''
+ }, {
+ 'platform': 'mac',
+ 'since_version': '11',
+ 'until_version': ''
+ }]
+ }
+ self.assertFalse(self._IsPolicySupported('win', 11, policy))
+ self.assertTrue(self._IsPolicySupported('mac', 11, policy))
+ self.assertTrue(self._IsPolicySupported('*', 11, policy))
+ self.assertFalse(self._IsPolicySupported('*', 10, policy))
+
+ def testHasExpandedPolicyDescriptionForUrlSchema(self):
+ policy = {'url_schema': 'https://example.com/details', 'type': 'list'}
+ tw = template_writer.TemplateWriter(None, None)
+ self.assertTrue(tw.HasExpandedPolicyDescription(policy))
+
+ def testHasExpandedPolicyDescriptionForJSONPolicies(self):
+ policy = {'name': 'PolicyName', 'type': 'dict'}
+ tw = template_writer.TemplateWriter(None, None)
+ self.assertTrue(tw.HasExpandedPolicyDescription(policy))
+
+ def testGetExpandedPolicyDescriptionForUrlSchema(self):
+ policy = {'type': 'integer', 'url_schema': 'https://example.com/details'}
+ tw = template_writer.TemplateWriter(None, None)
+ tw.messages = {
+ 'doc_schema_description_link': {
+ 'text': '''See $6'''
+ },
+ }
+ expanded_description = tw.GetExpandedPolicyDescription(policy)
+ self.assertEqual(expanded_description, 'See https://example.com/details')
+
+ def testGetExpandedPolicyDescriptionForJSONPolicies(self):
+ policy = {'name': 'PolicyName', 'type': 'dict'}
+ tw = template_writer.TemplateWriter(None, None)
+ tw.messages = {
+ 'doc_schema_description_link': {
+ 'text': '''See $6'''
+ },
+ }
+ expanded_description = tw.GetExpandedPolicyDescription(policy)
+ self.assertEqual(
+ expanded_description,
+ 'See https://cloud.google.com/docs/chrome-enterprise/policies/?policy=PolicyName'
+ )
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/policy/tools/template_writers/writers/writer_unittest_common.py b/chromium/components/policy/tools/template_writers/writers/writer_unittest_common.py
new file mode 100755
index 00000000000..d96bb7986d2
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/writer_unittest_common.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+# 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.
+'''Common tools for unit-testing writers.'''
+
+import unittest
+import policy_template_generator
+import template_formatter
+import textwrap
+import writer_configuration
+
+
+class WriterUnittestCommon(unittest.TestCase):
+ '''Common class for unittesting writers.'''
+
+ def GetOutput(self, policy_json, definitions, writer_type):
+ '''Generates an output of a writer.
+
+ Args:
+ policy_json: Raw policy JSON string.
+ definitions: Definitions to create writer configurations.
+ writer_type: Writer type (e.g. 'admx'), see template_formatter.py.
+
+ Returns:
+ The string of the template created by the writer.
+ '''
+
+ # Evaluate policy_json. For convenience, fix indentation in statements like
+ # policy_json = '''
+ # {
+ # ...
+ # }''')
+ start_idx = 1 if policy_json[0] == '\n' else 0
+ policy_data = eval(textwrap.dedent(policy_json[start_idx:]))
+
+ config = writer_configuration.GetConfigurationForBuild(definitions)
+ policy_generator = \
+ policy_template_generator.PolicyTemplateGenerator(config, policy_data)
+ writer = template_formatter.GetWriter(writer_type, config)
+ return policy_generator.GetTemplateText(writer)
diff --git a/chromium/components/policy/tools/template_writers/writers/xml_formatted_writer.py b/chromium/components/policy/tools/template_writers/writers/xml_formatted_writer.py
new file mode 100755
index 00000000000..a8815370472
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/xml_formatted_writer.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+
+from writers import template_writer
+
+
+class XMLFormattedWriter(template_writer.TemplateWriter):
+ '''Helper class for generating XML-based templates.
+ '''
+
+ def AddElement(self, parent, name, attrs=None, text=None):
+ '''
+ Adds a new XML Element as a child to an existing element or the Document.
+
+ Args:
+ parent: An XML element or the document, where the new element will be
+ added.
+ name: The name of the new element.
+ attrs: A dictionary of the attributes' names and values for the new
+ element.
+ text: Text content for the new element.
+
+ Returns:
+ The created new element.
+ '''
+ if attrs == None:
+ attrs = {}
+
+ doc = parent.ownerDocument
+ element = doc.createElement(name)
+ for key, value in sorted(attrs.items()):
+ element.setAttribute(key, value)
+ if text:
+ element.appendChild(doc.createTextNode(text))
+ parent.appendChild(element)
+ return element
+
+ def AddText(self, parent, text):
+ '''Adds text to a parent node.
+ '''
+ doc = parent.ownerDocument
+ parent.appendChild(doc.createTextNode(text))
+
+ def AddAttribute(self, parent, name, value):
+ '''Adds a new attribute to the parent Element. If an attribute with the
+ given name already exists then it will be replaced.
+ '''
+ doc = parent.ownerDocument
+ attribute = doc.createAttribute(name)
+ attribute.value = value
+ parent.setAttributeNode(attribute)
+
+ def AddComment(self, parent, comment):
+ '''Adds a comment node.'''
+ parent.appendChild(parent.ownerDocument.createComment(comment))
+
+ def ToPrettyXml(self, doc, **kwargs):
+ # return doc.toprettyxml(indent=' ')
+ # The above pretty-printer does not print the doctype and adds spaces
+ # around texts, e.g.:
+ # <string>
+ # value of the string
+ # </string>
+ # This is problematic both for the OSX Workgroup Manager (plist files) and
+ # the Windows Group Policy Editor (admx files). What they need instead:
+ # <string>value of string</string>
+ # So we use a hacky pretty printer here. It assumes that there are no
+ # mixed-content nodes.
+ # Get all the XML content in a one-line string.
+ xml = doc.toxml(**kwargs)
+ # Determine where the line breaks will be. (They will only be between tags.)
+ lines = xml[1:len(xml) - 1].split('><')
+ indent = ''
+ res = ''
+ # Determine indent for each line.
+ for i, line in enumerate(lines):
+ if line[0] == '/':
+ # If the current line starts with a closing tag, decrease indent before
+ # printing.
+ indent = indent[2:]
+ lines[i] = indent + '<' + line + '>'
+ if (line[0] not in ['/', '?', '!'] and '</' not in line and
+ line[len(line) - 1] != '/'):
+ # If the current line starts with an opening tag and does not conatin a
+ # closing tag, increase indent after the line is printed.
+ indent += ' '
+ # Reconstruct XML text from the lines.
+ return '\n'.join(lines)
diff --git a/chromium/components/policy/tools/template_writers/writers/xml_writer_base_unittest.py b/chromium/components/policy/tools/template_writers/writers/xml_writer_base_unittest.py
new file mode 100755
index 00000000000..40c2ff52bb5
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/writers/xml_writer_base_unittest.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3
+# Copyright (c) 2012 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.
+"""Unittests for writers.admx_writer."""
+
+import re
+import unittest
+
+
+class XmlWriterBaseTest(unittest.TestCase):
+ '''Base class for XML writer unit-tests.
+ '''
+
+ def GetXMLOfChildren(self, parent):
+ '''Returns the XML of all child nodes of the given parent node.
+ Args:
+ parent: The XML of the children of this node will be returned.
+
+ Return: XML of the chrildren of the parent node.
+ '''
+ raw_pretty_xml = ''.join(
+ child.toprettyxml(indent=' ') for child in parent.childNodes)
+ # Python 2.6.5 which is present in Lucid has bug in its pretty print
+ # function which produces new lines around string literals. This has been
+ # fixed in Precise which has Python 2.7.3 but we have to keep compatibility
+ # with both for now.
+ text_re = re.compile('>\n\s+([^<>\s].*?)\n\s*</', re.DOTALL)
+ return text_re.sub('>\g<1></', raw_pretty_xml)
+
+ def AssertXMLEquals(self, output, expected_output):
+ '''Asserts if the passed XML arguements are equal.
+ Args:
+ output: Actual XML text.
+ expected_output: Expected XML text.
+ '''
+ self.assertEquals(output.strip(), expected_output.strip())