summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorgfeher@chromium.org <gfeher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-21 08:43:18 +0000
committergfeher@chromium.org <gfeher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-21 08:43:18 +0000
commit4c4c45bff3cf5cf7a07cc271b8f479c197ff7b69 (patch)
tree07b85f4a2c449b0eb8b00d248c7891a96c45b292 /tools
parent4b2b8caad5782d22b9eada599b89fd2f8ff13729 (diff)
downloadchromium_src-4c4c45bff3cf5cf7a07cc271b8f479c197ff7b69.zip
chromium_src-4c4c45bff3cf5cf7a07cc271b8f479c197ff7b69.tar.gz
chromium_src-4c4c45bff3cf5cf7a07cc271b8f479c197ff7b69.tar.bz2
Generate html documentation for enterprise policies
Use the .json and .grd description of the policies to generate HTML markup that can be uploaded straight to dev.chromium.org. BUG=54712 TEST=DocWriterUnittest.* Review URL: http://codereview.chromium.org/3452006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60032 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools')
-rw-r--r--tools/grit/grit/format/policy_templates/policy_template_generator.py5
-rw-r--r--tools/grit/grit/format/policy_templates/writers/adml_writer.py6
-rw-r--r--tools/grit/grit/format/policy_templates/writers/doc_writer.py498
-rw-r--r--tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py358
-rw-r--r--tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py7
-rw-r--r--tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py27
-rw-r--r--tools/grit/grit/node/misc.py2
-rw-r--r--tools/grit/grit/test_suite_all.py8
-rw-r--r--tools/grit/grit/tool/build.py3
9 files changed, 892 insertions, 22 deletions
diff --git a/tools/grit/grit/format/policy_templates/policy_template_generator.py b/tools/grit/grit/format/policy_templates/policy_template_generator.py
index e4fbbfe..fd23a52 100644
--- a/tools/grit/grit/format/policy_templates/policy_template_generator.py
+++ b/tools/grit/grit/format/policy_templates/policy_template_generator.py
@@ -107,7 +107,12 @@ class PolicyTemplateGenerator:
if 'policies' in group:
# Iterate through all the policies in the current group.
for policy in group['policies']:
+ # Add messages to the policy.
self._AddMessagesToPolicy(policy)
+ # Store a reference to the group of the policy. This makes
+ # it easier to look up messages of the group when we have
+ # a policy, like in PListStringsWriter and DocWriter.
+ policy['parent'] = group
def _GetPoliciesForWriter(self, template_writer, group):
'''Filters the list of policies in a group for a writer.
diff --git a/tools/grit/grit/format/policy_templates/writers/adml_writer.py b/tools/grit/grit/format/policy_templates/writers/adml_writer.py
index c6af9f1..cc29fdb 100644
--- a/tools/grit/grit/format/policy_templates/writers/adml_writer.py
+++ b/tools/grit/grit/format/policy_templates/writers/adml_writer.py
@@ -29,10 +29,6 @@ class ADMLWriter(xml_formatted_writer.XMLFormattedWriter):
# describe the presentation of Policy-Groups and Policies.
_presentation_table_elem = None
- # The active Policy-Group. At any given point in time this contains the
- # Policy-Group that is processed.
- _active_policy_group = None
-
# The active ADML "presentation" element. At any given point in time this
# contains the "presentation" element for the Policy-Group that is processed.
_active_presentation_elem = None
@@ -200,8 +196,6 @@ class ADMLWriter(xml_formatted_writer.XMLFormattedWriter):
Args:
group: The Policy-Group to generate ADML elements for.
'''
- self._active_policy_group = group
-
# Add ADML "string" elements to the string-table that are required by a
# Policy-Group.
id = group['name']
diff --git a/tools/grit/grit/format/policy_templates/writers/doc_writer.py b/tools/grit/grit/format/policy_templates/writers/doc_writer.py
new file mode 100644
index 0000000..b632937
--- /dev/null
+++ b/tools/grit/grit/format/policy_templates/writers/doc_writer.py
@@ -0,0 +1,498 @@
+# Copyright (c) 2010 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 re
+
+from xml.dom import minidom
+from grit.format.policy_templates.writers import xml_formatted_writer
+
+
+def GetWriter(config, messages):
+ '''Factory method for creating DocWriter objects.
+ See the constructor of TemplateWriter for description of
+ arguments.
+ '''
+ return DocWriter(['mac', 'linux', 'win'], config, messages)
+
+
+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.
+ '''
+
+ # TODO(gfeher): This function is duplicated in PListWriter.
+ def _GetLocalizedPolicyMessage(self, policy, msg_id):
+ '''Looks up localized caption or description for a policy.
+ If the policy does not have the required message, then it is
+ inherited from the group.
+
+ Args:
+ policy: The data structure of the policy.
+ msg_id: Either 'caption' or 'desc'.
+
+ Returns:
+ The corresponding message for the policy.
+ '''
+ if msg_id in policy:
+ msg = policy[msg_id]
+ else:
+ msg = policy['parent'][msg_id]
+ return msg
+
+ 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['IDS_POLICY_DOC_' + msg_id.upper()]
+
+ def _MapListToString(self, item_map, items):
+ '''Creates a comma-separated list.
+
+ Args:
+ item_map: A dictionary containing all the elements of 'items' as
+ keys.
+ items: A list of arbitrary items.
+
+ Returns:
+ Looks up each item of 'items' in 'item_maps' and concatenates the
+ resulting items into a comma-separated list.
+ '''
+ return ', '.join([item_map[x] for x in items])
+
+ 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.
+ '''
+ # Iterate through all the URLs and replace them with links.
+ out = []
+ while True:
+ # Look for the first URL.
+ res = self._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 _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 != '':
+ attrs['style'] = 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 'enum' type policies.
+
+ Args:
+ parent: The DOM node for which the feature list will be added.
+ policy: The data structure of a policy.
+ '''
+ desc = self._GetLocalizedPolicyMessage(policy, 'desc')
+ # Replace URLs with links in the description.
+ self._AddTextWithLinks(parent, desc)
+ # Add list of enum items.
+ if policy['type'] == 'enum':
+ ul = self.AddElement(parent, 'ul')
+ for item in policy['items']:
+ self.AddElement(
+ ul, 'li', {}, '%s = %s' % (item['value'], item['caption']))
+
+ 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 = []
+ for key, value in policy['annotations']['features'].iteritems():
+ key_name = self._FEATURE_MAP[key]
+ if value == 0:
+ value_name = self._GetLocalizedMessage('not_supported')
+ else:
+ value_name = self._GetLocalizedMessage('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['annotations']['example_value']
+ self.AddElement(parent, 'dt', {}, 'Mac:')
+ mac = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre'])
+
+ 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 _AddListExampleWindows(self, parent, policy):
+ '''Adds an example value for Windows 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.
+ '''
+ example_value = policy['annotations']['example_value']
+ self.AddElement(parent, 'dt', {}, 'Windows:')
+ win = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre'])
+ win_text = []
+ cnt = 0
+ for item in example_value:
+ win_text.append(
+ '%s\\%s\\%d = "%s"' %
+ (self.config['win_reg_key_name'], policy['name'], cnt, item))
+ cnt = cnt + 1
+ self.AddText(win, '\n'.join(win_text))
+
+ def _AddListExampleLinux(self, parent, policy):
+ '''Adds an example value for 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 Linux example value
+ is generated.
+ '''
+ example_value = policy['annotations']['example_value']
+ self.AddElement(parent, 'dt', {}, 'Linux:')
+ linux = self._AddStyledElement(parent, 'dd', ['.monospace'])
+ linux_text = []
+ for item in example_value:
+ linux_text.append('"%s"' % item)
+ self.AddText(linux, '[%s]' % ', '.join(linux_text))
+
+
+ def _AddListExample(self, parent, policy):
+ '''Adds the example value of a 'list' policy to a DOM node. Example output:
+ <dl>
+ <dt>Windows:</dt>
+ <dd>
+ Software\Policies\Chromium\DisabledPlugins\0 = "Java"
+ Software\Policies\Chromium\DisabledPlugins\1 = "Shockwave Flash"
+ </dd>
+ <dt>Linux:</dt>
+ <dd>["Java", "Shockwave Flash"]</dd>
+ <dt>Mac:</dt>
+ <dd>
+ <array>
+ <string>Java</string>
+ <string>Shockwave Flash</string>
+ </array>
+ </dd>
+ </dl>
+
+ Args:
+ parent: The DOM node for which the example will be added.
+ policy: The data structure of a policy.
+ '''
+ example_value = policy['annotations']['example_value']
+ examples = self._AddStyledElement(parent, 'dl', ['dd dl'])
+ self._AddListExampleWindows(examples, policy)
+ self._AddListExampleLinux(examples, policy)
+ self._AddListExampleMac(examples, policy)
+
+ 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 /> (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['annotations']['example_value']
+ policy_type = policy['type']
+ if policy_type == 'main':
+ if example_value == True:
+ self.AddText(
+ parent, '0x00000001 (Windows), true (Linux), <true /> (Mac)')
+ elif example_value == False:
+ self.AddText(
+ parent, '0x00000000 (Windows), false (Linux), <false /> (Mac)')
+ else:
+ raise Exception('Expected boolean value.')
+ elif policy_type == 'string':
+ self.AddText(parent, '"%s"' % example_value)
+ elif policy_type == 'enum':
+ self.AddText(
+ parent,
+ '0x%08x (Windows), %d (Linux/Mac)' % (example_value, example_value))
+ elif policy_type == 'list':
+ self._AddListExample(parent, policy)
+ else:
+ raise Exception('Unknown policy type: ' + policy_type)
+
+ 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 _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.
+ '''
+ annotations = policy['annotations']
+
+ dl = self.AddElement(parent, 'dl')
+ self._AddPolicyAttribute(
+ dl,
+ 'data_type',
+ self._TYPE_MAP[policy['type']])
+ self._AddPolicyAttribute(
+ dl,
+ 'win_reg_loc',
+ self.config['win_reg_key_name'] + '\\' + policy['name'],
+ ['.monospace'])
+ self._AddPolicyAttribute(
+ dl,
+ 'mac_linux_pref_name',
+ policy['name'],
+ ['.monospace'])
+ self._AddPolicyAttribute(
+ dl,
+ 'supported_on_platforms',
+ self._MapListToString(self._PLATFORM_MAP, annotations['platforms']))
+ self._AddPolicyAttribute(
+ dl,
+ 'supported_in_products',
+ self._MapListToString(self._PRODUCT_MAP, annotations['products']))
+ dd = self._AddPolicyAttribute(dl, 'supported_features')
+ self._AddFeatures(dd, policy)
+ dd = self._AddPolicyAttribute(dl, 'description')
+ self._AddDescription(dd, policy)
+ dd = self._AddPolicyAttribute(dl, 'example_value')
+ self._AddExample(dd, policy)
+
+ def _AddPolicyNote(self, parent, policy):
+ '''If a policy has an additional web page assigned with it, then add
+ a link for that page.
+
+ Args:
+ policy: The data structure of the policy.
+ '''
+ if 'problem_href' not in policy['annotations']:
+ return
+ problem_href = policy['annotations']['problem_href']
+ div = self._AddStyledElement(parent, 'div', ['div.note'])
+ note = self._GetLocalizedMessage('note').replace('$6', problem_href)
+ self._AddTextWithLinks(div, note)
+
+ 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.
+ '''
+ caption = self._GetLocalizedPolicyMessage(policy, 'caption')
+ tr = self._AddStyledElement(parent, 'tr', ['tr'])
+ name_td = self._AddStyledElement(tr, 'td', ['td', 'td.left'])
+ self.AddElement(
+ name_td, 'a',
+ {'href': '#' + policy['name']}, policy['name'])
+ self._AddStyledElement(tr, 'td', ['td', 'td.right'], {}, 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.
+ '''
+ h2 = self.AddElement(parent, 'h2')
+ caption = self._GetLocalizedPolicyMessage(policy, 'caption')
+ self.AddElement(h2, 'a', {'name': policy['name']})
+ self.AddText(h2, policy['name'])
+ self.AddElement(parent, 'span', {}, caption)
+ self._AddPolicyNote(parent, policy)
+
+ self._AddPolicyDetails(parent, policy)
+ self.AddElement(
+ parent, 'a', {'href': '#top'},
+ self._GetLocalizedMessage('back_to_top'))
+
+ #
+ # Implementation of abstract methods of TemplateWriter:
+ #
+
+ def WritePolicy(self, policy):
+ self._AddPolicyRow(self._summary_tbody, policy)
+ self._AddPolicySection(self._details_div, policy)
+
+ def BeginPolicyGroup(self, group):
+ pass
+
+ def EndPolicyGroup(self):
+ pass
+
+ def BeginTemplate(self):
+ # Add a <div> for the summary section.
+ summary_div = self.AddElement(self._main_div, 'div')
+ self.AddElement(summary_div, 'a', {'name': 'top'})
+ self.AddElement(summary_div, 'br')
+ self._AddTextWithLinks(
+ summary_div,
+ self._GetLocalizedMessage('intro'))
+ 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')
+
+ def EndTemplate(self):
+ pass
+
+ def Prepare(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')
+
+ # Human-readable names of supported platforms.
+ self._PLATFORM_MAP = {
+ 'win': 'Windows',
+ 'mac': 'Mac',
+ 'linux': 'Linux',
+ }
+ # Human-readable names of supported products.
+ self._PRODUCT_MAP = {
+ 'chrome': self.config['app_name'],
+ 'chrome_frame': self.config['frame_name'],
+ }
+ # Human-readable names of supported features.
+ self._FEATURE_MAP = {
+ 'dynamic_refresh': 'Dynamic Policy Refresh'
+ }
+ # Human-readable names of types.
+ self._TYPE_MAP = {
+ 'string': 'String (REG_SZ)',
+ 'main': 'Boolean (REG_DWORD)',
+ 'enum': 'Integer (REG_DWORD)',
+ 'list': 'List of strings',
+ }
+ # 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 = {
+ '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': 'white-space: pre;',
+ 'div.note': 'border: 2px solid black; padding: 5px; margin: 5px;'
+ }
+
+ # A simple regexp to search for URLs. It is enough for now.
+ self._url_matcher = re.compile('(http://[^\\s]*[^\\s\\.])')
+
+ 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/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py b/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py
new file mode 100644
index 0000000..bbb2292
--- /dev/null
+++ b/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py
@@ -0,0 +1,358 @@
+# Copyright (c) 2010 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 grit.format.policy_templates.writers.doc_writer'''
+
+
+import os
+import re
+import sys
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), '../../../..'))
+
+import tempfile
+import unittest
+import StringIO
+from xml.dom import minidom
+
+from grit.format import rc
+from grit.format.policy_templates.writers import writer_unittest_common
+from grit.format.policy_templates.writers import doc_writer
+from grit import grd_reader
+from grit import util
+from grit.tool import build
+
+class MockMessageDictionary:
+ '''A mock dictionary passed to a writer as the dictionary of
+ localized messages.
+ '''
+
+ # Dictionary of messages.
+ msg_dict = {}
+
+ def __getitem__(self, msg_id):
+ '''Returns a message for an identifier. The identifier is transformed
+ back from IDS_POLICY_DOC... style names to the keys that the writer used.
+ If it is then key in self.msg_dict, then the message comes from there.
+ Otherwise the returned message is just the transformed version of the
+ identifier. This makes things more simple for testing.
+
+ Args:
+ msg_id: The message identifier.
+
+ Returns:
+ The mock message for msg_id.
+ '''
+ # Do some trickery to get the original message id issued in DocWriter.
+ expected_prefix = 'IDS_POLICY_DOC_'
+ assert msg_id.startswith(expected_prefix)
+ original_msg_id = msg_id[len(expected_prefix):].lower()
+
+ if original_msg_id in self.msg_dict:
+ return self.msg_dict[original_msg_id]
+ return '_test_' + original_msg_id
+
+
+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',
+ 'win_reg_key_name': 'MockKey',
+ },
+ messages=MockMessageDictionary())
+ self.writer.Prepare()
+
+ # 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>'
+ '<a name="top"/><br/>_test_intro<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 testGetLocalizedPolicyMessage(self):
+ # Test if the message inheritance logic works well in
+ # DocWriter.GetLocalizedPolicyMessage()
+ policy = {'message_id1': '1hello, world'}
+ self.assertEquals(
+ self.writer._GetLocalizedPolicyMessage(policy, 'message_id1'),
+ '1hello, world')
+ self.assertRaises(
+ KeyError,
+ self.writer._GetLocalizedPolicyMessage, policy, 'message_id2')
+ policy['parent'] = {'message_id3': '3hello, world'}
+ self.assertEquals(
+ self.writer._GetLocalizedPolicyMessage(policy, 'message_id3'),
+ '3hello, world')
+
+ def testGetLocalizedMessage(self):
+ # Test if localized messages are retrieved correctly.
+ self.writer.messages = {
+ 'IDS_POLICY_DOC_HELLO_WORLD': 'hello, vilag!'
+ }
+ self.assertEquals(
+ self.writer._GetLocalizedMessage('hello_world'),
+ 'hello, vilag!')
+
+ def testMapListToString(self):
+ # Test function DocWriter.MapListToString()
+ self.assertEquals(
+ self.writer._MapListToString({'a1': 'a2', 'b1': 'b2'}, ['a1', 'b1']),
+ 'a2, b2')
+ self.assertEquals(
+ self.writer._MapListToString({'a1': 'a2', 'b1': 'b2'}, []),
+ '')
+ result = self.writer._MapListToString(
+ {'a': '1', 'b': '2', 'c': '3', 'd': '4'}, ['b', 'd'])
+ expected_result = '2, 4'
+ self.assertEquals(
+ result,
+ expected_result)
+
+ 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 testAddDescription(self):
+ # Test if URLs are replaced and choices of 'enum' policies are listed
+ # correctly.
+ policy = {
+ 'type': '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>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.
+<ul><li>0 = Disable foo</li><li>2 = Solve your problem</li><li>5 = Enable bar</li></ul></root>''')
+
+ def testAddFeatures(self):
+ # Test if the list of features of a policy is handled correctly.
+ policy = {
+ 'annotations': {
+ 'features': {'spaceship_docking': 0, 'dynamic_refresh': 1}
+ }
+ }
+ self.writer._FEATURE_MAP = {
+ 'spaceship_docking': 'Spaceship Docking',
+ 'dynamic_refresh': 'Dynamic Refresh'
+ }
+ self.writer._AddFeatures(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(),
+ '<root>'
+ 'Dynamic Refresh: _test_supported, '
+ 'Spaceship Docking: _test_not_supported'
+ '</root>')
+
+ def testAddListExample(self):
+ policy = {
+ 'name': 'PolicyName',
+ 'annotations': {
+ 'example_value': ['Foo', 'Bar']
+ }
+ }
+ self.writer._AddListExample(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(),
+ '''<root><dl style="style_dd dl;"><dt>Windows:</dt><dd style="style_.monospace;style_.pre;">MockKey\\PolicyName\\0 = &quot;Foo&quot;
+MockKey\\PolicyName\\1 = &quot;Bar&quot;</dd><dt>Linux:</dt><dd style="style_.monospace;">[&quot;Foo&quot;, &quot;Bar&quot;]</dd><dt>Mac:</dt><dd style="style_.monospace;style_.pre;">&lt;array&gt;
+ &lt;string&gt;Foo&lt;/string&gt;
+ &lt;string&gt;Bar&lt;/string&gt;
+&lt;/array&gt;</dd></dl></root>''')
+
+ def testBoolExample(self):
+ # Test representation of boolean example values.
+ policy = {
+ 'name': 'PolicyName',
+ 'type': 'main',
+ 'annotations': {
+ 'example_value': True
+ }
+ }
+ e1 = self.writer.AddElement(self.doc_root, 'e1')
+ self.writer._AddExample(e1, policy)
+ self.assertEquals(
+ e1.toxml(),
+ '<e1>0x00000001 (Windows), true (Linux), &lt;true /&gt; (Mac)</e1>')
+
+ policy = {
+ 'name': 'PolicyName',
+ 'type': 'main',
+ 'annotations': {
+ 'example_value': False
+ }
+ }
+ e2 = self.writer.AddElement(self.doc_root, 'e2')
+ self.writer._AddExample(e2, policy)
+ self.assertEquals(
+ e2.toxml(),
+ '<e2>0x00000000 (Windows), false (Linux), &lt;false /&gt; (Mac)</e2>')
+
+ def testEnumExample(self):
+ # Test representation of 'enum' example values.
+ policy = {
+ 'name': 'PolicyName',
+ 'type': 'enum',
+ 'annotations': {
+ 'example_value': 16
+ }
+ }
+ self.writer._AddExample(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(),
+ '<root>0x00000010 (Windows), 16 (Linux/Mac)</root>')
+
+ def testStringExample(self):
+ # Test representation of 'string' example values.
+ policy = {
+ 'name': 'PolicyName',
+ 'type': 'string',
+ 'annotations': {
+ 'example_value': 'awesome-example'
+ }
+ }
+ self.writer._AddExample(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(),
+ '<root>&quot;awesome-example&quot;</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',
+ 'annotations': {
+ 'platforms': ['win'],
+ 'products': ['chrome'],
+ 'features': {'dynamic_refresh': 0},
+ '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 (REG_DWORD)</dd>'
+ '<dt style="style_dt;">_test_win_reg_loc</dt>'
+ '<dd style="style_.monospace;">MockKey\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_platforms</dt><dd>Windows</dd>'
+ '<dt style="style_dt;">_test_supported_in_products</dt><dd>Chrome</dd>'
+ '<dt style="style_dt;">_test_supported_features</dt>'
+ '<dd>Dynamic Policy Refresh: _test_not_supported</dd>'
+ '<dt style="style_dt;">_test_description</dt><dd>TestPolicyDesc</dd>'
+ '<dt style="style_dt;">_test_example_value</dt>'
+ '<dd>0x00000000 (Windows), false (Linux), &lt;false /&gt; (Mac)</dd>'
+ '</dl></root>')
+
+ def testAddPolicyNote(self):
+ # Test if nodes are correctly added to policies.
+ policy = {
+ 'annotations': {
+ 'problem_href': 'http://www.example.com/5'
+ }
+ }
+ self.writer.messages.msg_dict['note'] = '...$6...'
+ self.writer._AddPolicyNote(self.doc_root, policy)
+ self.assertEquals(
+ self.doc_root.toxml(),
+ '<root><div style="style_div.note;">...'
+ '<a href="http://www.example.com/5">http://www.example.com/5</a>'
+ '...</div></root>')
+
+ def testAddPolicyRow(self):
+ # Test if policies are correctly added to the summary table.
+ policy = {
+ 'name': 'PolicyName',
+ 'caption': 'PolicyCaption'
+ }
+ 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;">'
+ '<a href="#PolicyName">PolicyName</a>'
+ '</td>'
+ '<td style="style_td;style_td.right;">PolicyCaption</td>'
+ '</tr></root>')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py b/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py
index faec9f8..a42bb13 100644
--- a/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py
+++ b/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py
@@ -20,6 +20,7 @@ class PListStringsWriter(template_writer.TemplateWriter):
[lang].lproj subdirectories of the manifest bundle.
'''
+ # TODO(gfeher): This function is duplicated in DocWriter.
def _GetLocalizedPolicyMessage(self, policy, msg_id):
'''Looks up localized caption or description for a policy.
If the policy does not have the required message, then it is
@@ -35,7 +36,7 @@ class PListStringsWriter(template_writer.TemplateWriter):
if msg_id in policy:
msg = policy[msg_id]
else:
- msg = self._policy_group[msg_id]
+ msg = policy['parent'][msg_id]
return msg
def _AddToStringTable(self, item_name, caption, desc):
@@ -73,10 +74,10 @@ class PListStringsWriter(template_writer.TemplateWriter):
self._AddToStringTable(policy['name'], caption, desc)
def BeginPolicyGroup(self, group):
- self._policy_group = group
+ pass
def EndPolicyGroup(self):
- self._policy_group = None
+ pass
def BeginTemplate(self):
self._AddToStringTable(
diff --git a/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py b/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py
index 6b9a475..f038f2e 100644
--- a/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py
+++ b/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py
@@ -3,16 +3,15 @@
# found in the LICENSE file.
-from xml.dom.minidom import Document
-from xml.dom.minidom import Node
-from template_writer import TemplateWriter
+from xml.dom import minidom
+from grit.format.policy_templates.writers import template_writer
-class XMLFormattedWriter(TemplateWriter):
+class XMLFormattedWriter(template_writer.TemplateWriter):
'''Helper class for generating XML-based templates.
'''
- def AddElement(self, parent, name, attrs = {}, text = None):
+ def AddElement(self, parent, name, attrs=None, text=None):
'''
Adds a new XML Element as a child to an existing element or the Document.
@@ -27,14 +26,20 @@ class XMLFormattedWriter(TemplateWriter):
Returns:
The created new element.
'''
- if isinstance(parent, Document):
- doc = parent
- else:
- doc = parent.ownerDocument
+ if attrs == None:
+ attrs = {}
+
+ doc = parent.ownerDocument
element = doc.createElement(name)
for key, value in attrs.iteritems():
- element.attributes[key] = value
+ element.setAttribute(key, value)
if text:
- element.appendChild( doc.createTextNode(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))
diff --git a/tools/grit/grit/node/misc.py b/tools/grit/grit/node/misc.py
index e93133c..eb1e054 100644
--- a/tools/grit/grit/node/misc.py
+++ b/tools/grit/grit/node/misc.py
@@ -250,7 +250,7 @@ class GritNode(base.Node):
elif t == 'js_map_format':
from grit.format import js_map_format
return js_map_format.TopLevel()
- elif t in ['adm', 'plist', 'plist_strings', 'admx', 'adml']:
+ elif t in ('adm', 'plist', 'plist_strings', 'admx', 'adml', 'doc'):
from grit.format.policy_templates import template_formatter
return template_formatter.TemplateFormatter(t)
else:
diff --git a/tools/grit/grit/test_suite_all.py b/tools/grit/grit/test_suite_all.py
index 584eed3..e6691d5 100644
--- a/tools/grit/grit/test_suite_all.py
+++ b/tools/grit/grit/test_suite_all.py
@@ -52,6 +52,11 @@ class TestSuiteAll(unittest.TestSuite):
import plist_strings_writer_unittest
from grit.format.policy_templates.writers \
import adm_writer_unittest
+ from grit.format.policy_templates.writers \
+ import adml_writer_unittest
+ from grit.format.policy_templates.writers \
+ import admx_writer_unittest
+ from grit.format.policy_templates.writers import doc_writer_unittest
test_classes = [
base_unittest.NodeUnittest,
@@ -85,6 +90,9 @@ class TestSuiteAll(unittest.TestSuite):
plist_writer_unittest.PListWriterUnittest,
plist_strings_writer_unittest.PListStringsWriterUnittest,
adm_writer_unittest.AdmWriterUnittest,
+ admx_writer_unittest.AdmxWriterUnittest,
+ adml_writer_unittest.AdmlWriterUnittest,
+ doc_writer_unittest.DocWriterUnittest,
# add test classes here...
]
diff --git a/tools/grit/grit/tool/build.py b/tools/grit/grit/tool/build.py
index 771a737..c750705 100644
--- a/tools/grit/grit/tool/build.py
+++ b/tools/grit/grit/tool/build.py
@@ -181,7 +181,8 @@ are exported to translation interchange files (e.g. XMB files), etc.
if output.GetType() in ('rc_header', 'resource_map_header',
'resource_map_source', 'resource_file_map_source'):
encoding = 'cp1252'
- elif output.GetType() in ['js_map_format', 'plist', 'plist_strings']:
+ elif output.GetType() in ('js_map_format', 'plist', 'plist_strings',
+ 'doc'):
encoding = 'utf_8'
else:
# TODO(gfeher) modify here to set utf-8 encoding for admx/adml