diff options
author | gfeher@chromium.org <gfeher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-21 08:43:18 +0000 |
---|---|---|
committer | gfeher@chromium.org <gfeher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-21 08:43:18 +0000 |
commit | 4c4c45bff3cf5cf7a07cc271b8f479c197ff7b69 (patch) | |
tree | 07b85f4a2c449b0eb8b00d248c7891a96c45b292 /tools/grit | |
parent | 4b2b8caad5782d22b9eada599b89fd2f8ff13729 (diff) | |
download | chromium_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/grit')
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 = "Foo" +MockKey\\PolicyName\\1 = "Bar"</dd><dt>Linux:</dt><dd style="style_.monospace;">["Foo", "Bar"]</dd><dt>Mac:</dt><dd style="style_.monospace;style_.pre;"><array> + <string>Foo</string> + <string>Bar</string> +</array></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), <true /> (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), <false /> (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>"awesome-example"</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), <false /> (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 |