diff options
author | Ben Murdoch <benm@google.com> | 2014-04-10 11:53:13 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2014-04-10 11:53:13 +0100 |
commit | 790a254225fcf929fd6659323c1455dbd1167624 (patch) | |
tree | 2346d58f8a1c83d83320e5fe103037d04b0a5658 | |
parent | b9e867e779e33cb8710a262e84b289e93e8fa3fa (diff) | |
parent | fc300288d9f8b8f795aa8131cd0ab5f4cec154a4 (diff) | |
download | grit-790a254225fcf929fd6659323c1455dbd1167624.tar.gz |
Merge from Chromium at DEPS revision 262940
This commit was generated by merge_to_master.py.
Change-Id: I3590ff6b9511207e7c3fe2b8ae6eaa8e11342642
17 files changed, 524 insertions, 143 deletions
diff --git a/grit/format/data_pack.py b/grit/format/data_pack.py index 0cdbbd8..779a862 100755 --- a/grit/format/data_pack.py +++ b/grit/format/data_pack.py @@ -3,9 +3,9 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -'''Support for formatting a data pack file used for platform agnostic resource +"""Support for formatting a data pack file used for platform agnostic resource files. -''' +""" import collections import exceptions @@ -19,7 +19,6 @@ from grit import util from grit.node import include from grit.node import message from grit.node import structure -from grit.node import misc PACK_FILE_VERSION = 4 @@ -37,7 +36,7 @@ DataPackContents = collections.namedtuple( def Format(root, lang='en', output_dir='.'): - '''Writes out the data pack file format (platform agnostic resource file).''' + """Writes out the data pack file format (platform agnostic resource file).""" data = {} for node in root.ActiveDescendants(): with node: @@ -55,9 +54,9 @@ def ReadDataPack(input_file): original_data = data # Read the header. - version, num_entries, encoding = struct.unpack("<IIB", data[:HEADER_LENGTH]) + version, num_entries, encoding = struct.unpack('<IIB', data[:HEADER_LENGTH]) if version != PACK_FILE_VERSION: - print "Wrong file version in ", input_file + print 'Wrong file version in ', input_file raise WrongFileVersion resources = {} @@ -68,22 +67,21 @@ def ReadDataPack(input_file): data = data[HEADER_LENGTH:] kIndexEntrySize = 2 + 4 # Each entry is a uint16 and a uint32. for _ in range(num_entries): - id, offset = struct.unpack("<HI", data[:kIndexEntrySize]) + id, offset = struct.unpack('<HI', data[:kIndexEntrySize]) data = data[kIndexEntrySize:] - next_id, next_offset = struct.unpack("<HI", data[:kIndexEntrySize]) + next_id, next_offset = struct.unpack('<HI', data[:kIndexEntrySize]) resources[id] = original_data[offset:next_offset] return DataPackContents(resources, encoding) def WriteDataPackToString(resources, encoding): - """Write a map of id=>data into a string in the data pack format and return - it.""" + """Returns a string with a map of id=>data in the data pack format.""" ids = sorted(resources.keys()) ret = [] # Write file header. - ret.append(struct.pack("<IIB", PACK_FILE_VERSION, len(ids), encoding)) + ret.append(struct.pack('<IIB', PACK_FILE_VERSION, len(ids), encoding)) HEADER_LENGTH = 2 * 4 + 1 # Two uint32s and one uint8. # Each entry is a uint16 + a uint32s. We have one extra entry for the last @@ -93,10 +91,10 @@ def WriteDataPackToString(resources, encoding): # Write index. data_offset = HEADER_LENGTH + index_length for id in ids: - ret.append(struct.pack("<HI", id, data_offset)) + ret.append(struct.pack('<HI', id, data_offset)) data_offset += len(resources[id]) - ret.append(struct.pack("<HI", 0, data_offset)) + ret.append(struct.pack('<HI', 0, data_offset)) # Write data. for id in ids: @@ -105,39 +103,78 @@ def WriteDataPackToString(resources, encoding): def WriteDataPack(resources, output_file, encoding): - """Write a map of id=>data into output_file as a data pack.""" + """Writes a map of id=>data into output_file as a data pack.""" content = WriteDataPackToString(resources, encoding) - with open(output_file, "wb") as file: + with open(output_file, 'wb') as file: file.write(content) -def RePack(output_file, input_files): - """Write a new data pack to |output_file| based on a list of filenames - (|input_files|)""" +def RePack(output_file, input_files, whitelist_file=None): + """Write a new data pack file by combining input pack files. + + Args: + output_file: path to the new data pack file. + input_files: a list of paths to the data pack files to combine. + whitelist_file: path to the file that contains the list of resource IDs + that should be kept in the output file or None to include + all resources. + + Raises: + KeyError: if there are duplicate keys or resource encoding is + inconsistent. + """ + input_data_packs = [ReadDataPack(filename) for filename in input_files] + whitelist = None + if whitelist_file: + whitelist = util.ReadFile(whitelist_file, util.RAW_TEXT).strip().split('\n') + whitelist = map(int, whitelist) + resources, encoding = RePackFromDataPackStrings(input_data_packs, whitelist) + WriteDataPack(resources, output_file, encoding) + + +def RePackFromDataPackStrings(inputs, whitelist): + """Returns a data pack string that combines the resources from inputs. + + Args: + inputs: a list of data pack strings that need to be combined. + whitelist: a list of resource IDs that should be kep in the output string + or None to include all resources. + + Returns: + DataPackContents: a tuple containing the new combined data pack and its + encoding. + + Raises: + KeyError: if there are duplicate keys or resource encoding is + inconsistent. + """ resources = {} encoding = None - for filename in input_files: - new_content = ReadDataPack(filename) - + for content in inputs: # Make sure we have no dups. - duplicate_keys = set(new_content.resources.keys()) & set(resources.keys()) - if len(duplicate_keys) != 0: - raise exceptions.KeyError("Duplicate keys: " + str(list(duplicate_keys))) + duplicate_keys = set(content.resources.keys()) & set(resources.keys()) + if duplicate_keys: + raise exceptions.KeyError('Duplicate keys: ' + str(list(duplicate_keys))) # Make sure encoding is consistent. if encoding in (None, BINARY): - encoding = new_content.encoding - elif new_content.encoding not in (BINARY, encoding): - raise exceptions.KeyError("Inconsistent encodings: " + - str(encoding) + " vs " + - str(new_content.encoding)) - - resources.update(new_content.resources) + encoding = content.encoding + elif content.encoding not in (BINARY, encoding): + raise exceptions.KeyError('Inconsistent encodings: ' + str(encoding) + + ' vs ' + str(content.encoding)) + + if whitelist: + whitelisted_resources = dict([(key, content.resources[key]) + for key in content.resources.keys() + if key in whitelist]) + resources.update(whitelisted_resources) + else: + resources.update(content.resources) # Encoding is 0 for BINARY, 1 for UTF8 and 2 for UTF16 if encoding is None: encoding = BINARY - WriteDataPack(resources, output_file, encoding) + return DataPackContents(resources, encoding) # Temporary hack for external programs that import data_pack. @@ -157,14 +194,14 @@ def main(): data = ReadDataPack(sys.argv[1]) print data.encoding for (resource_id, text) in data.resources.iteritems(): - print "%s: %s" % (resource_id, text) + print '%s: %s' % (resource_id, text) else: # Just write a simple file. - data = { 1: "", 4: "this is id 4", 6: "this is id 6", 10: "" } - WriteDataPack(data, "datapack1.pak", UTF8) - data2 = { 1000: "test", 5: "five" } - WriteDataPack(data2, "datapack2.pak", UTF8) - print "wrote datapack1 and datapack2 to current directory." + data = {1: '', 4: 'this is id 4', 6: 'this is id 6', 10: ''} + WriteDataPack(data, 'datapack1.pak', UTF8) + data2 = {1000: 'test', 5: 'five'} + WriteDataPack(data2, 'datapack2.pak', UTF8) + print 'wrote datapack1 and datapack2 to current directory.' if __name__ == '__main__': diff --git a/grit/format/data_pack_unittest.py b/grit/format/data_pack_unittest.py index d210c99..f6e9edc 100644 --- a/grit/format/data_pack_unittest.py +++ b/grit/format/data_pack_unittest.py @@ -28,10 +28,37 @@ class FormatDataPackUnittest(unittest.TestCase): '\x0a\x00\x3f\x00\x00\x00' # index entry 10 '\x00\x00\x3f\x00\x00\x00' # extra entry for the size of last 'this is id 4this is id 6') # data - input = { 1: "", 4: "this is id 4", 6: "this is id 6", 10: "" } + input = {1: '', 4: 'this is id 4', 6: 'this is id 6', 10: ''} output = data_pack.WriteDataPackToString(input, data_pack.UTF8) self.failUnless(output == expected) + def testRePackUnittest(self): + expected_with_whitelist = { + 1: 'Never gonna', 10: 'give you up', 20: 'Never gonna let', + 30: 'you down', 40: 'Never', 50: 'gonna run around and', + 60: 'desert you'} + expected_without_whitelist = { + 1: 'Never gonna', 10: 'give you up', 20: 'Never gonna let', 65: 'Close', + 30: 'you down', 40: 'Never', 50: 'gonna run around and', 4: 'click', + 60: 'desert you', 6: 'chirr', 32: 'oops, try again', 70: 'Awww, snap!'} + inputs = [{1: 'Never gonna', 4: 'click', 6: 'chirr', 10: 'give you up'}, + {20: 'Never gonna let', 30: 'you down', 32: 'oops, try again'}, + {40: 'Never', 50: 'gonna run around and', 60: 'desert you'}, + {65: 'Close', 70: 'Awww, snap!'}] + whitelist = [1, 10, 20, 30, 40, 50, 60] + inputs = [data_pack.DataPackContents(input, data_pack.UTF8) for input + in inputs] + + # RePack using whitelist + output, _ = data_pack.RePackFromDataPackStrings(inputs, whitelist) + self.assertDictEqual(expected_with_whitelist, output, + 'Incorrect resource output') + + # RePack a None whitelist + output, _ = data_pack.RePackFromDataPackStrings(inputs, None) + self.assertDictEqual(expected_without_whitelist, output, + 'Incorrect resource output') + if __name__ == '__main__': unittest.main() diff --git a/grit/format/policy_templates/policy_template_generator.py b/grit/format/policy_templates/policy_template_generator.py index 3fad8ca..11d097e 100644 --- a/grit/format/policy_templates/policy_template_generator.py +++ b/grit/format/policy_templates/policy_template_generator.py @@ -5,7 +5,6 @@ import copy -import types class PolicyTemplateGenerator: @@ -104,20 +103,6 @@ class PolicyTemplateGenerator: }) return result - def _PrintPolicyValue(self, item): - '''Produces a string representation for a policy value. Taking care to print - dictionaries in a sorted order.''' - if type(item) == types.StringType: - str_val = "'%s'" % item - elif isinstance(item, dict): - str_val = "{"; - for it in sorted(item.iterkeys()): - str_val += "\'%s\': %s, " % (it, self._PrintPolicyValue(item[it])) - str_val = str_val.rstrip(", ") + "}"; - else: - str_val = str(item) - return str_val; - 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. @@ -137,8 +122,6 @@ class PolicyTemplateGenerator: # Iterate through all the items of an enum-type policy, and add captions. for item in policy['items']: item['caption'] = self._ImportMessage(item['caption']) - elif policy['type'] == 'dict' and 'example_value' in policy: - policy['example_value'] = self._PrintPolicyValue(policy['example_value']) if policy['type'] != 'group': if not 'label' in policy: # If 'label' is not specified, then it defaults to 'caption': diff --git a/grit/format/policy_templates/writers/adml_writer_unittest.py b/grit/format/policy_templates/writers/adml_writer_unittest.py index 41757d9..3600005 100644 --- a/grit/format/policy_templates/writers/adml_writer_unittest.py +++ b/grit/format/policy_templates/writers/adml_writer_unittest.py @@ -18,7 +18,7 @@ from grit.format.policy_templates.writers import adml_writer from grit.format.policy_templates.writers import xml_writer_base_unittest -class AdmlWriterTest(xml_writer_base_unittest.XmlWriterBaseTest): +class AdmlWriterUnittest(xml_writer_base_unittest.XmlWriterBaseTest): def setUp(self): config = { diff --git a/grit/format/policy_templates/writers/admx_writer_unittest.py b/grit/format/policy_templates/writers/admx_writer_unittest.py index 9a2a58e..c99131f 100644 --- a/grit/format/policy_templates/writers/admx_writer_unittest.py +++ b/grit/format/policy_templates/writers/admx_writer_unittest.py @@ -19,7 +19,7 @@ from grit.format.policy_templates.writers import xml_writer_base_unittest from xml.dom import minidom -class AdmxWriterTest(xml_writer_base_unittest.XmlWriterBaseTest): +class AdmxWriterUnittest(xml_writer_base_unittest.XmlWriterBaseTest): def _CreateDocumentElement(self): dom_impl = minidom.getDOMImplementation('') diff --git a/grit/format/policy_templates/writers/doc_writer.py b/grit/format/policy_templates/writers/doc_writer.py index ee45cd5..7cca976 100644 --- a/grit/format/policy_templates/writers/doc_writer.py +++ b/grit/format/policy_templates/writers/doc_writer.py @@ -234,41 +234,32 @@ class DocWriter(xml_formatted_writer.XMLFormattedWriter): if self.IsPolicySupportedOnPlatform(policy, 'mac'): self._AddListExampleMac(examples, policy) - def _PythonDictionaryToMacDictionary(self, dictionary, indent=''): - '''Converts a python dictionary to an equivalent XML plist. - - Returns a list of lines, with one dictionary entry per line.''' - result = [indent + '<dict>'] - indent += ' ' - for k in sorted(dictionary.keys()): - v = dictionary[k] - result.append('%s<key>%s</key>' % (indent, k)) - value_type = type(v) - if value_type == bool: - result.append('%s<%s/>' % (indent, 'true' if v else 'false')) - elif value_type == int: - result.append('%s<integer>%s</integer>' % (indent, v)) - elif value_type == str: - result.append('%s<string>%s</string>' % (indent, v)) - elif value_type == dict: - result += self._PythonDictionaryToMacDictionary(v, indent) - elif value_type == list: - array = [] - if len(v) != 0: - if type(v[0]) == str: - array = ['%s <string>%s</string>' % (indent, x) for x in v] - elif type(v[0]) == dict: - for x in v: - array += self._PythonDictionaryToMacDictionary(x, indent + ' ') - else: - raise Exception('Must be list of string or dict.') - result.append('%s<array>' % indent) - result += array - result.append('%s</array>' % indent) - else: - raise Exception('Invalid example value type %s' % value_type) - result.append(indent[2:] + '</dict>') - return result + 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, 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' policy to a DOM node. @@ -282,7 +273,7 @@ class DocWriter(xml_formatted_writer.XMLFormattedWriter): self.AddElement(parent, 'dt', {}, 'Mac:') mac = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre']) mac_text = ['<key>%s</key>' % (policy['name'])] - mac_text += self._PythonDictionaryToMacDictionary(example_value) + mac_text += self._PythonObjectToPlist(example_value) self.AddText(mac, '\n'.join(mac_text)) def _AddDictionaryExampleWindows(self, parent, policy): diff --git a/grit/format/policy_templates/writers/ios_plist_writer.py b/grit/format/policy_templates/writers/ios_plist_writer.py new file mode 100644 index 0000000..1da64aa --- /dev/null +++ b/grit/format/policy_templates/writers/ios_plist_writer.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# Copyright (c) 2014 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 xml.dom import minidom +from grit.format.policy_templates.writers import plist_writer + + +# This writer outputs a Property List with an example for each of the policies +# supported on iOS. This plist can be pushed to Chrome on iOS via the MDM API +# introduced in iOS 7. + +CHROME_POLICY_COMMENT = '''\ + ChromePolicy is the preferred key to configure Chrome. + Each of the keys in this <dict> configures a Chrome policy. + All of the Chrome policies are configured with an example + value below. + Note that it's not necessary to configure all of them. ''' + +ENCODED_CHROME_POLICY_COMMENT = '''\ + EncodedChromePolicy contains a Property List file, encoded in Base64, + which contains the same policies that can go in ChromePolicy. + This key can be used by vendors that restrict the app configuration + types to strings. + The value of this string can be validated by running these + commands in Mac OS X: + + # (first, copy-paste the string into a file named "policy.plist") + # base64 -D < policy.plist > decoded_policy.plist + # plutil -lint decoded_policy.plist + + plutil should indicate that decoded_policy.plist is valid, + otherwise Chrome will reject the encoded string too. + + This command can be used to pretty-print the plist file: + + # plutil -convert xml1 decoded_policy.plist + + Note that <ChromePolicy> is the preferred key to configure Chrome. + If <ChromePolicy> is present then <EncodedChromePolicy> is ignored. ''' + +def GetWriter(config): + '''Factory method for creating IOSPlistWriter objects. + See the constructor of TemplateWriter for description of + arguments. + ''' + return IOSPlistWriter(['ios'], config) + + +class IOSPlistWriter(plist_writer.PListWriter): + '''Class for generating policy templates in the iOS plist format. + It is used by PolicyTemplateGenerator to write plist files. + ''' + + # Overridden. + def IsPolicySupported(self, policy): + # Output examples only for policies that are supported on iOS. + for support_on in policy['supported_on']: + if ('ios' in support_on['platforms'] and + support_on['until_version'] == '' and + super(IOSPlistWriter, self).IsPolicySupported(policy)): + return True + return False + + def _WriteValue(self, parent, value): + if type(value) == bool: + self.AddElement(parent, 'true' if value else 'false') + elif type(value) == int: + self.AddElement(parent, 'integer', {}, str(value)) + elif type(value) == str: + self.AddElement(parent, 'string', {}, value) + elif type(value) == list: + array = self.AddElement(parent, 'array') + for element in value: + self._WriteValue(array, element) + elif type(value) == dict: + dic = self.AddElement(parent, 'dict') + for k, v in sorted(value.iteritems()): + self.AddElement(dic, 'key', {}, k) + self._WriteValue(dic, v) + else: + raise ValueError('Unsupported type in example value: ' + type(value)) + + # Overridden. + def WritePolicy(self, policy): + for dict in [self._dict, self._encoded_dict]: + self.AddElement(dict, 'key', {}, policy['name']) + self._WriteValue(dict, policy['example_value']) + + # Overridden. + # |self._plist| is created in super.Init(). + def BeginTemplate(self): + self._plist.attributes['version'] = '1.0' + self._root_dict = self.AddElement(self._plist, 'dict') + self.AddComment(self._root_dict, CHROME_POLICY_COMMENT) + self._dict = self._AddKeyValuePair(self._root_dict, 'ChromePolicy', 'dict') + + self._encoded_plist.attributes['version'] = '1.0' + self._encoded_dict = self.AddElement(self._encoded_plist, 'dict') + + # Overridden. + def EndTemplate(self): + # Add the "EncodedChromePolicy" entry. + encoded = base64.b64encode(self._encoded_doc.toxml()) + self.AddComment(self._root_dict, ENCODED_CHROME_POLICY_COMMENT) + self._AddStringKeyValuePair(self._root_dict, 'EncodedChromePolicy', encoded) + + # Overridden. + def Init(self): + super(IOSPlistWriter, self).Init() + # Create a secondary DOM for the EncodedChromePolicy Plist, which will be + # serialized and encoded in EndTemplate. + self._encoded_doc = self.CreatePlistDocument() + self._encoded_plist = self._encoded_doc.documentElement + + # Overridden. + def GetTemplateText(self): + return self.ToPrettyXml(self._doc, encoding='UTF-8') diff --git a/grit/format/policy_templates/writers/ios_plist_writer_unittest.py b/grit/format/policy_templates/writers/ios_plist_writer_unittest.py new file mode 100644 index 0000000..14a0cab --- /dev/null +++ b/grit/format/policy_templates/writers/ios_plist_writer_unittest.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +# Copyright (c) 2014 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.ios_plist_writer''' + + +import base64 +import functools +import os +import plistlib +import sys +if __name__ == '__main__': + sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..')) + +import unittest + +try: + import Cocoa +except: + Cocoa = None + +from grit.format.policy_templates.writers import writer_unittest_common + + +class IOSPListWriterUnittest(writer_unittest_common.WriterUnittestCommon): + '''Unit tests for IOSPListWriter.''' + + def _ParseWithPython(self, decode, text): + '''Parses a serialized Plist, using Python's plistlib. + + If |decode| is true then |text| is decoded as Base64 before being + deserialized as a Plist.''' + if decode: + text = base64.b64decode(text) + return plistlib.readPlistFromString(text) + + def _ParseWithCocoa(self, decode, text): + '''Parses a serialized Plist, using Cocoa's python bindings. + + If |decode| is true then |text| is decoded as Base64 before being + deserialized as a Plist.''' + if decode: + data = Cocoa.NSData.alloc().initWithBase64EncodedString_options_(text, 0) + else: + data = Cocoa.NSData.alloc().initWithBytes_length_(text, len(text)) + result = Cocoa.NSPropertyListSerialization. \ + propertyListFromData_mutabilityOption_format_errorDescription_( + data, Cocoa.NSPropertyListImmutable, None, None) + return result[0] + + def _VerifyGeneratedOutputWithParsers(self, + templates, + expected_output, + parse, + decode_and_parse): + # Generate the grit output for |templates|. + output = self.GetOutput( + self.PrepareTest(templates), + 'fr', + { '_chromium': '1', 'mac_bundle_id': 'com.example.Test' }, + 'ios_plist', + 'en') + + # Parse it as a Plist. + plist = parse(output) + self.assertEquals(len(plist), 2) + self.assertTrue('ChromePolicy' in plist) + self.assertTrue('EncodedChromePolicy' in plist) + + # Get the 2 expected fields. + chrome_policy = plist['ChromePolicy'] + encoded_chrome_policy = plist['EncodedChromePolicy'] + + # Verify the ChromePolicy. + self.assertEquals(chrome_policy, expected_output) + + # Decode the EncodedChromePolicy and verify it. + decoded_chrome_policy = decode_and_parse(encoded_chrome_policy) + self.assertEquals(decoded_chrome_policy, expected_output) + + def _VerifyGeneratedOutput(self, templates, expected): + # plistlib is available on all Python platforms. + parse = functools.partial(self._ParseWithPython, False) + decode_and_parse = functools.partial(self._ParseWithPython, True) + self._VerifyGeneratedOutputWithParsers( + templates, expected, parse, decode_and_parse) + + # The Cocoa bindings are available on Mac OS X only. + if Cocoa: + parse = functools.partial(self._ParseWithCocoa, False) + decode_and_parse = functools.partial(self._ParseWithCocoa, True) + self._VerifyGeneratedOutputWithParsers( + templates, expected, parse, decode_and_parse) + + def _MakeTemplate(self, name, type, example, extra=''): + return ''' + { + 'policy_definitions': [ + { + 'name': '%s', + 'type': '%s', + 'desc': '', + 'caption': '', + 'supported_on': ['ios:35-'], + 'example_value': %s, + %s + }, + ], + 'placeholders': [], + 'messages': {}, + } + ''' % (name, type, example, extra) + + def testEmpty(self): + templates = ''' + { + 'policy_definitions': [], + 'placeholders': [], + 'messages': {}, + } + ''' + expected = {} + self._VerifyGeneratedOutput(templates, expected) + + def testBoolean(self): + templates = self._MakeTemplate('BooleanPolicy', 'main', 'True') + expected = { + 'BooleanPolicy': True, + } + self._VerifyGeneratedOutput(templates, expected) + + def testString(self): + templates = self._MakeTemplate('StringPolicy', 'string', '"Foo"') + expected = { + 'StringPolicy': 'Foo', + } + self._VerifyGeneratedOutput(templates, expected) + + def testStringEnum(self): + templates = self._MakeTemplate( + 'StringEnumPolicy', 'string-enum', '"Foo"', + ''' + 'items': [ + { 'name': 'Foo', 'value': 'Foo', 'caption': '' }, + { 'name': 'Bar', 'value': 'Bar', 'caption': '' }, + ], + ''') + expected = { + 'StringEnumPolicy': 'Foo', + } + self._VerifyGeneratedOutput(templates, expected) + + def testInt(self): + templates = self._MakeTemplate('IntPolicy', 'int', '42') + expected = { + 'IntPolicy': 42, + } + self._VerifyGeneratedOutput(templates, expected) + + def testIntEnum(self): + templates = self._MakeTemplate( + 'IntEnumPolicy', 'int-enum', '42', + ''' + 'items': [ + { 'name': 'Foo', 'value': 100, 'caption': '' }, + { 'name': 'Bar', 'value': 42, 'caption': '' }, + ], + ''') + expected = { + 'IntEnumPolicy': 42, + } + self._VerifyGeneratedOutput(templates, expected) + + def testStringList(self): + templates = self._MakeTemplate('StringListPolicy', 'list', '["a", "b"]') + expected = { + 'StringListPolicy': [ "a", "b" ], + } + self._VerifyGeneratedOutput(templates, expected) + + def testListOfDictionary(self): + templates = self._MakeTemplate( + 'ManagedBookmarks', 'dict', + ''' + [ + { + "name": "Google Search", + "url": "www.google.com", + }, + { + "name": "Youtube", + "url": "www.youtube.com", + } + ] + ''') + expected = { + 'ManagedBookmarks': [ + { "name": "Google Search", "url": "www.google.com" }, + { "name": "Youtube", "url": "www.youtube.com" }, + ], + } + self._VerifyGeneratedOutput(templates, expected) + + +if __name__ == '__main__': + unittest.main() diff --git a/grit/format/policy_templates/writers/json_writer.py b/grit/format/policy_templates/writers/json_writer.py index 4739483..673bbf7 100644 --- a/grit/format/policy_templates/writers/json_writer.py +++ b/grit/format/policy_templates/writers/json_writer.py @@ -3,6 +3,8 @@ # 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 grit.format.policy_templates.writers import template_writer @@ -38,28 +40,10 @@ class JsonWriter(template_writer.TemplateWriter): return self.FlattenGroupsAndSortPolicies(policy_list) def WritePolicy(self, policy): - example_value = policy['example_value'] - if policy['type'] == 'string': - example_value_str = '"' + example_value + '"' - elif policy['type'] in ('int', 'int-enum', 'dict'): - example_value_str = str(example_value) - elif policy['type'] == 'list': - if example_value == []: - example_value_str = '[]' - else: - example_value_str = '["%s"]' % '", "'.join(example_value) - elif policy['type'] == 'main': - if example_value == True: - example_value_str = 'true' - else: - example_value_str = 'false' - elif policy['type'] == 'string-enum': - example_value_str = '"%s"' % example_value; - elif policy['type'] == 'external': + if policy['type'] == 'external': # This type can only be set through cloud policy. return - else: - raise Exception('unknown policy type %s:' % policy['type']) + 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: diff --git a/grit/format/policy_templates/writers/json_writer_unittest.py b/grit/format/policy_templates/writers/json_writer_unittest.py index 1281c19..00acde3 100644 --- a/grit/format/policy_templates/writers/json_writer_unittest.py +++ b/grit/format/policy_templates/writers/json_writer_unittest.py @@ -235,13 +235,13 @@ class JsonWriterUnittest(writer_unittest_common.WriterUnittestCommon): # Tests a policy group with a single policy of type 'dict'. example = { 'bool': True, - 'int': 10, - 'string': 'abc', - 'list': [1, 2, 3], 'dict': { 'a': 1, 'b': 2, - } + }, + 'int': 10, + 'list': [1, 2, 3], + 'string': 'abc', } grd = self.PrepareTest( '{' @@ -264,8 +264,8 @@ class JsonWriterUnittest(writer_unittest_common.WriterUnittestCommon): ' // Example Dictionary Policy\n' + HEADER_DELIMETER + ' // Example Dictionary Policy\n\n' - ' //"DictionaryPolicy": {\'bool\': True, \'dict\': {\'a\': 1, ' - '\'b\': 2}, \'int\': 10, \'list\': [1, 2, 3], \'string\': \'abc\'}\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) diff --git a/grit/format/policy_templates/writers/plist_writer.py b/grit/format/policy_templates/writers/plist_writer.py index 46b7ade..2297858 100644 --- a/grit/format/policy_templates/writers/plist_writer.py +++ b/grit/format/policy_templates/writers/plist_writer.py @@ -127,13 +127,16 @@ class PListWriter(xml_formatted_writer.XMLFormattedWriter): self._array = self._AddKeyValuePair(dict, 'pfm_subkeys', 'array') - def Init(self): + 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') - self._doc = dom_impl.createDocument(None, 'plist', doctype) + return dom_impl.createDocument(None, 'plist', doctype) + + def Init(self): + self._doc = self.CreatePlistDocument() self._plist = self._doc.documentElement def GetTemplateText(self): diff --git a/grit/format/policy_templates/writers/reg_writer.py b/grit/format/policy_templates/writers/reg_writer.py index 716cd74..beb1590 100644 --- a/grit/format/policy_templates/writers/reg_writer.py +++ b/grit/format/policy_templates/writers/reg_writer.py @@ -4,6 +4,8 @@ # found in the LICENSE file. +import json + from grit.format.policy_templates.writers import template_writer @@ -64,9 +66,10 @@ class RegWriter(template_writer.TemplateWriter): i = i + 1 else: self._StartBlock(key, None, list) - if policy['type'] in ('string', 'dict'): - escaped_str = self._EscapeRegString(str(example_value)) - example_value_str = '"' + escaped_str + '"' + if policy['type'] in ('string', 'string-enum', 'dict'): + example_value_str = json.dumps(example_value, sort_keys=True) + if policy['type'] == 'dict': + example_value_str = '"%s"' % example_value_str elif policy['type'] == 'main': if example_value == True: example_value_str = 'dword:00000001' @@ -74,8 +77,6 @@ class RegWriter(template_writer.TemplateWriter): example_value_str = 'dword:00000000' elif policy['type'] in ('int', 'int-enum'): example_value_str = 'dword:%08x' % example_value - elif policy['type'] == 'string-enum': - example_value_str = '"%s"' % example_value else: raise Exception('unknown policy type %s:' % policy['type']) diff --git a/grit/format/policy_templates/writers/reg_writer_unittest.py b/grit/format/policy_templates/writers/reg_writer_unittest.py index d84599c..d559c9f 100644 --- a/grit/format/policy_templates/writers/reg_writer_unittest.py +++ b/grit/format/policy_templates/writers/reg_writer_unittest.py @@ -216,13 +216,13 @@ class RegWriterUnittest(writer_unittest_common.WriterUnittestCommon): # Tests a policy group with a single policy of type 'dict'. example = { 'bool': True, - 'int': 10, - 'string': 'abc', - 'list': [1, 2, 3], 'dict': { 'a': 1, 'b': 2, - } + }, + 'int': 10, + 'list': [1, 2, 3], + 'string': 'abc', } grd = self.PrepareTest( '{' @@ -244,8 +244,8 @@ class RegWriterUnittest(writer_unittest_common.WriterUnittestCommon): '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\'}"']) + '"DictionaryPolicy"="{"bool": true, "dict": {"a": 1, ' + '"b": 2}, "int": 10, "list": [1, 2, 3], "string": "abc"}"']) self.CompareOutputs(output, expected_output) def testNonSupportedPolicy(self): diff --git a/grit/format/policy_templates/writers/xml_formatted_writer.py b/grit/format/policy_templates/writers/xml_formatted_writer.py index b28d8b6..dad3717 100644 --- a/grit/format/policy_templates/writers/xml_formatted_writer.py +++ b/grit/format/policy_templates/writers/xml_formatted_writer.py @@ -53,7 +53,11 @@ class XMLFormattedWriter(template_writer.TemplateWriter): attribute.value = value parent.setAttributeNode(attribute) - def ToPrettyXml(self, doc): + 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.: @@ -66,7 +70,7 @@ class XMLFormattedWriter(template_writer.TemplateWriter): # So we use the poor man's 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() + 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 = '' diff --git a/grit/format/repack.py b/grit/format/repack.py index e42acdb..337b7af 100755 --- a/grit/format/repack.py +++ b/grit/format/repack.py @@ -9,19 +9,29 @@ http://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedst for details about the file format. """ +import optparse import os import sys + if __name__ == '__main__': sys.path.append(os.path.join(os.path.dirname(__file__), '../..')) import grit.format.data_pack + def main(argv): - if len(argv) < 3: - print ("Usage:\n %s <output_filename> <input_file1> [input_file2] ... " % - argv[0]) - sys.exit(-1) - grit.format.data_pack.RePack(argv[1], argv[2:]) + parser = optparse.OptionParser('usage: %prog [options] <output_filename>' + '<input_file1> [input_file2] ...') + parser.add_option('--whitelist', action='store', dest='whitelist', + default=None, help='Full path to the whitelist used to' + 'filter output pak file resource IDs') + options, file_paths = parser.parse_args(argv) + + if len(file_paths) < 2: + parser.error('Please specify output and at least one input filenames') + + grit.format.data_pack.RePack(file_paths[0], file_paths[1:], + whitelist_file=options.whitelist) if '__main__' == __name__: - main(sys.argv) + main(sys.argv[1:]) diff --git a/grit/test_suite_all.py b/grit/test_suite_all.py index 3f5c978..71635fc 100644 --- a/grit/test_suite_all.py +++ b/grit/test_suite_all.py @@ -42,7 +42,10 @@ class TestSuiteAll(unittest.TestSuite): import grit.format.resource_map_unittest import grit.format.policy_templates.policy_template_generator_unittest import grit.format.policy_templates.writers.adm_writer_unittest + import grit.format.policy_templates.writers.adml_writer_unittest + import grit.format.policy_templates.writers.admx_writer_unittest import grit.format.policy_templates.writers.doc_writer_unittest + import grit.format.policy_templates.writers.ios_plist_writer_unittest import grit.format.policy_templates.writers.json_writer_unittest import grit.format.policy_templates.writers.plist_strings_writer_unittest import grit.format.policy_templates.writers.plist_writer_unittest @@ -97,8 +100,14 @@ class TestSuiteAll(unittest.TestSuite): PolicyTemplateGeneratorUnittest, grit.format.policy_templates.writers.adm_writer_unittest. AdmWriterUnittest, + grit.format.policy_templates.writers.adml_writer_unittest. + AdmlWriterUnittest, + grit.format.policy_templates.writers.admx_writer_unittest. + AdmxWriterUnittest, grit.format.policy_templates.writers.doc_writer_unittest. DocWriterUnittest, + grit.format.policy_templates.writers.ios_plist_writer_unittest. + IOSPListWriterUnittest, grit.format.policy_templates.writers.json_writer_unittest. JsonWriterUnittest, grit.format.policy_templates.writers.plist_strings_writer_unittest. diff --git a/grit/tool/build.py b/grit/tool/build.py index 2bb8085..87ee412 100644 --- a/grit/tool/build.py +++ b/grit/tool/build.py @@ -36,8 +36,10 @@ _format_modules = { 'resource_map_source': 'resource_map', 'resource_file_map_source': 'resource_map', } -_format_modules.update((type, 'policy_templates.template_formatter') - for type in 'adm plist plist_strings admx adml doc json reg'.split()) +_format_modules.update( + (type, 'policy_templates.template_formatter') for type in + [ 'adm', 'admx', 'adml', 'reg', 'doc', 'json', + 'plist', 'plist_strings', 'ios_plist' ]) def GetFormatter(type): |