diff --git a/python-lib/cuddlefish/__init__.py b/python-lib/cuddlefish/__init__.py index d08aedb4e..87e868dac 100644 --- a/python-lib/cuddlefish/__init__.py +++ b/python-lib/cuddlefish/__init__.py @@ -855,7 +855,8 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, jid=jid, update_url=options.update_url, bootstrap=True, - enable_mobile=options.enable_mobile) + enable_mobile=options.enable_mobile, + harness_options=harness_options) if command == "xpi" and options.update_link: if not options.update_link.startswith("https"): diff --git a/python-lib/cuddlefish/prefs.py b/python-lib/cuddlefish/prefs.py index 4d3f4807d..6a5cc86da 100644 --- a/python-lib/cuddlefish/prefs.py +++ b/python-lib/cuddlefish/prefs.py @@ -85,7 +85,7 @@ # Point the url-classifier to a nonexistent local URL for fast failures. 'browser.safebrowsing.provider.0.gethashURL' : 'http://localhost/safebrowsing-dummy/gethash', 'browser.safebrowsing.provider.0.updateURL' : 'http://localhost/safebrowsing-dummy/update', - } +} # When launching a temporary new Thunderbird profile, use these preferences. # Note that these were taken from: @@ -139,4 +139,9 @@ 'mail.smtpservers' : "smtp1", 'mail.startup.enabledMailCheckOnce' : True, 'mailnews.start_page_override.mstone' : "ignore", - } +} + +DEFAULT_TEST_PREFS = { + 'general.useragent.locale': "en-US", + 'intl.locale.matchOS': "en-US" +} diff --git a/python-lib/cuddlefish/rdf.py b/python-lib/cuddlefish/rdf.py index fb9bb76f6..c80be82ed 100644 --- a/python-lib/cuddlefish/rdf.py +++ b/python-lib/cuddlefish/rdf.py @@ -5,6 +5,8 @@ import os import xml.dom.minidom import StringIO +import codecs +import glob RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" EM_NS = "http://www.mozilla.org/2004/em-rdf#" @@ -20,9 +22,10 @@ def __str__(self): # have .encoding hardwired to "ascii" and put only bytes in # the backing store, so we can't use them here). # - # The encoding= argument to dom.writexml() merely sets the XML header's - # encoding= attribute. It still writes unencoded unicode to the output file, - # so we have to encode it for real afterwards. + # The encoding= argument to dom.writexml() merely sets the + # XML header's encoding= attribute. It still writes unencoded + # unicode to the output file, so we have to encode it for + # real afterwards. # # Also see: https://bugzilla.mozilla.org/show_bug.cgi?id=567660 @@ -112,7 +115,12 @@ def remove(self, property): return True; -def gen_manifest(template_root_dir, target_cfg, jid, + def add_node(self, node): + top = self.dom.documentElement.getElementsByTagName("Description")[0]; + top.appendChild(node) + + +def gen_manifest(template_root_dir, target_cfg, jid, harness_options={}, update_url=None, bootstrap=True, enable_mobile=False): install_rdf = os.path.join(template_root_dir, "install.rdf") manifest = RDFManifest(install_rdf) @@ -121,13 +129,51 @@ def gen_manifest(template_root_dir, target_cfg, jid, manifest.set("em:id", jid) manifest.set("em:version", target_cfg.get('version', '1.0')) + + if "locale" in harness_options: + # addon_title -> + # addon_author -> + # addon_description -> + # addon_homepageURL -> + localizable_in = ["title", "author", "description", "homepage"] + localized_out = ["name", "creator", "description", "homepageURL"] + for lang in harness_options["locale"]: + desc = dom.createElement("Description") + + for value_in in localizable_in: + key_in = "extensions." + target_cfg.get("id", "") + "." + value_in + tag_out = localized_out[localizable_in.index(value_in)] + + if key_in in harness_options["locale"][lang]: + elem = dom.createElement("em:" + tag_out) + elem_value = harness_options["locale"][lang][key_in] + elem.appendChild(dom.createTextNode(elem_value)) + desc.appendChild(elem) + + # Don't add language if no localizeable field was localized + if desc.hasChildNodes(): + locale = dom.createElement("em:locale") + locale.appendChild(dom.createTextNode(lang)) + desc.appendChild(locale) + + localized = dom.createElement("em:localized") + localized.appendChild(desc) + manifest.add_node(localized) + manifest.set("em:name", target_cfg.get('title', target_cfg.get('fullName', target_cfg['name']))) manifest.set("em:description", target_cfg.get("description", "")) manifest.set("em:creator", target_cfg.get("author", "")) + + if target_cfg.get("homepage"): + manifest.set("em:homepageURL", target_cfg.get("homepage")) + else: + manifest.remove("em:homepageURL") + manifest.set("em:bootstrap", str(bootstrap).lower()) + # XPIs remain packed by default, but package.json can override that. The # RDF format accepts "true" as True, anything else as False. We expect # booleans in the .json file, not strings. @@ -136,7 +182,7 @@ def gen_manifest(template_root_dir, target_cfg, jid, for translator in target_cfg.get("translators", [ ]): elem = dom.createElement("em:translator"); elem.appendChild(dom.createTextNode(translator)) - dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem) + manifest.add_node(elem) for developer in target_cfg.get("developers", [ ]): elem = dom.createElement("em:developer"); @@ -146,7 +192,7 @@ def gen_manifest(template_root_dir, target_cfg, jid, for contributor in target_cfg.get("contributors", [ ]): elem = dom.createElement("em:contributor"); elem.appendChild(dom.createTextNode(contributor)) - dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem) + manifest.add_node(elem) if update_url: manifest.set("em:updateURL", update_url) @@ -169,7 +215,7 @@ def gen_manifest(template_root_dir, target_cfg, jid, if enable_mobile: target_app = dom.createElement("em:targetApplication") - dom.documentElement.getElementsByTagName("Description")[0].appendChild(target_app) + manifest.add_node(target_app) ta_desc = dom.createElement("Description") target_app.appendChild(ta_desc) @@ -186,11 +232,6 @@ def gen_manifest(template_root_dir, target_cfg, jid, elem.appendChild(dom.createTextNode("30.0a1")) ta_desc.appendChild(elem) - if target_cfg.get("homepage"): - manifest.set("em:homepageURL", target_cfg.get("homepage")) - else: - manifest.remove("em:homepageURL") - return manifest if __name__ == "__main__": diff --git a/python-lib/cuddlefish/runner.py b/python-lib/cuddlefish/runner.py index 6a5f881f0..534935d11 100644 --- a/python-lib/cuddlefish/runner.py +++ b/python-lib/cuddlefish/runner.py @@ -18,6 +18,7 @@ from cuddlefish.prefs import DEFAULT_THUNDERBIRD_PREFS from cuddlefish.prefs import DEFAULT_FENNEC_PREFS from cuddlefish.prefs import DEFAULT_NO_CONNECTIONS_PREFS +from cuddlefish.prefs import DEFAULT_TEST_PREFS # Used to remove noise from ADB output CLEANUP_ADB = re.compile(r'^(I|E)/(stdout|stderr|GeckoConsole)\s*\(\s*\d+\):\s*(.*)$') @@ -433,6 +434,9 @@ def run_app(harness_root_dir, manifest_rdf, harness_options, cmdargs = [] preferences = dict(DEFAULT_COMMON_PREFS) + if is_running_tests: + preferences.update(DEFAULT_TEST_PREFS) + if no_connections: preferences.update(DEFAULT_NO_CONNECTIONS_PREFS) diff --git a/python-lib/cuddlefish/tests/bug-661083-files/packages/noLocalization/locale/en-GB.properties b/python-lib/cuddlefish/tests/bug-661083-files/packages/noLocalization/locale/en-GB.properties new file mode 100644 index 000000000..5a57eb596 --- /dev/null +++ b/python-lib/cuddlefish/tests/bug-661083-files/packages/noLocalization/locale/en-GB.properties @@ -0,0 +1 @@ +some_key = some_value diff --git a/python-lib/cuddlefish/tests/bug-661083-files/packages/noLocalization/locale/en-US.properties b/python-lib/cuddlefish/tests/bug-661083-files/packages/noLocalization/locale/en-US.properties new file mode 100644 index 000000000..5a57eb596 --- /dev/null +++ b/python-lib/cuddlefish/tests/bug-661083-files/packages/noLocalization/locale/en-US.properties @@ -0,0 +1 @@ +some_key = some_value diff --git a/python-lib/cuddlefish/tests/bug-661083-files/packages/noLocalization/package.json b/python-lib/cuddlefish/tests/bug-661083-files/packages/noLocalization/package.json new file mode 100644 index 000000000..5b0bb85e3 --- /dev/null +++ b/python-lib/cuddlefish/tests/bug-661083-files/packages/noLocalization/package.json @@ -0,0 +1,11 @@ +{ + "name": "nolocalization", + "id": "jid1-TBF7sWF7yT6xSQ", + "license": "MPL 2.0", + "version": "0.1", + + "title": "tilteUnlocalized", + "author": "authorUnlocalized", + "description": "descriptionUnlocalized", + "homepage": "homepageUnlocalized" +} diff --git a/python-lib/cuddlefish/tests/bug-661083-files/packages/twoLanguages/locale/en-GB.properties b/python-lib/cuddlefish/tests/bug-661083-files/packages/twoLanguages/locale/en-GB.properties new file mode 100644 index 000000000..64d08ab8b --- /dev/null +++ b/python-lib/cuddlefish/tests/bug-661083-files/packages/twoLanguages/locale/en-GB.properties @@ -0,0 +1,4 @@ +extensions.jid1-TBF7sWF7yT6xSQ@jetpack.title = title-en-GB +extensions.jid1-TBF7sWF7yT6xSQ@jetpack.author = author-en-GB +extensions.jid1-TBF7sWF7yT6xSQ@jetpack.description = description-en-GB +extensions.jid1-TBF7sWF7yT6xSQ@jetpack.homepage = homepage-en-GB diff --git a/python-lib/cuddlefish/tests/bug-661083-files/packages/twoLanguages/locale/en-US.properties b/python-lib/cuddlefish/tests/bug-661083-files/packages/twoLanguages/locale/en-US.properties new file mode 100644 index 000000000..a742254ec --- /dev/null +++ b/python-lib/cuddlefish/tests/bug-661083-files/packages/twoLanguages/locale/en-US.properties @@ -0,0 +1,4 @@ +extensions.jid1-TBF7sWF7yT6xSQ@jetpack.title = title-en-US +extensions.jid1-TBF7sWF7yT6xSQ@jetpack.author = author-en-US +extensions.jid1-TBF7sWF7yT6xSQ@jetpack.description = description-en-US +extensions.jid1-TBF7sWF7yT6xSQ@jetpack.homepage = homepage-en-US diff --git a/python-lib/cuddlefish/tests/bug-661083-files/packages/twoLanguages/package.json b/python-lib/cuddlefish/tests/bug-661083-files/packages/twoLanguages/package.json new file mode 100644 index 000000000..e6a2f3194 --- /dev/null +++ b/python-lib/cuddlefish/tests/bug-661083-files/packages/twoLanguages/package.json @@ -0,0 +1,11 @@ +{ + "name": "nolocalization", + "id": "jid1-TBF7sWF7yT6xSQ@jetpack", + "license": "MPL 2.0", + "version": "0.1", + + "title": "tilteUnlocalized", + "author": "authorUnlocalized", + "description": "descriptionUnlocalized", + "homepage": "homepageUnlocalized" +} diff --git a/python-lib/cuddlefish/tests/test_linker.py b/python-lib/cuddlefish/tests/test_linker.py old mode 100755 new mode 100644 diff --git a/python-lib/cuddlefish/tests/test_rdf.py b/python-lib/cuddlefish/tests/test_rdf.py index 5d9254e4e..b4e389391 100644 --- a/python-lib/cuddlefish/tests/test_rdf.py +++ b/python-lib/cuddlefish/tests/test_rdf.py @@ -6,7 +6,7 @@ import xml.dom.minidom import os.path -from cuddlefish import rdf, packaging +from cuddlefish import rdf, packaging, property_parser parent = os.path.dirname test_dir = parent(os.path.abspath(__file__)) @@ -49,6 +49,52 @@ def testTitle(self): self.failUnlessEqual(m.get('em:name'), 'a long ' + n) self.failUnlessIn('a long ' + n + '', str(m), n) + def testLocalization(self): + # addon_title -> + # addon_author -> + # addon_description -> + # addon_homepageURL -> + localizable_in = ["title", "author", "description", "homepage"] + localized_out = ["name", "creator", "description", "homepageURL"] + + basedir = os.path.join(test_dir, "bug-661083-files/packages") + for n in ["noLocalization", "twoLanguages"]: + harness_options = { "locale" : {} } + pkgdir = os.path.join(basedir, n) + localedir = os.path.join(pkgdir, "locale") + files = os.listdir(localedir) + + for file in files: + filepath = os.path.join(localedir, file) + if os.path.isfile(filepath) and file.endswith(".properties"): + language = file[:-len(".properties")] + try: + parsed_file = property_parser.parse_file(filepath) + except property_parser.MalformedLocaleFileError, msg: + self.fail(msg) + + harness_options["locale"][language] = parsed_file + + cfg = packaging.get_config_in_dir(pkgdir) + m = rdf.gen_manifest(template_dir, cfg, 'JID', harness_options) + + if n == "noLocalization": + self.failIf("" in str(m)) + continue + + for lang in harness_options["locale"]: + rdfstr = str(m) + node = "" + lang + "" + self.failUnlessIn(node, rdfstr, n) + + for value_in in localizable_in: + key_in = "extensions." + m.get('em:id') + "." + value_in + tag_out = localized_out[localizable_in.index(value_in)] + if key_in in harness_options["locale"][lang]: + # E.g. "author-en-US" + node = "" + value_in + "-" + lang \ + + "" + self.failUnlessIn(node , rdfstr, n) if __name__ == '__main__': unittest.main() diff --git a/test/addons/manifest-localized/locale/en-US.properties b/test/addons/manifest-localized/locale/en-US.properties new file mode 100644 index 000000000..f0bb50477 --- /dev/null +++ b/test/addons/manifest-localized/locale/en-US.properties @@ -0,0 +1,4 @@ +extensions.manifest-localized@jetpack.title = title-en +extensions.manifest-localized@jetpack.author = author-en +extensions.manifest-localized@jetpack.description = description-en +extensions.manifest-localized@jetpack.homepage = homepage-en diff --git a/test/addons/manifest-localized/main.js b/test/addons/manifest-localized/main.js new file mode 100644 index 000000000..9ad71b7a2 --- /dev/null +++ b/test/addons/manifest-localized/main.js @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { Cu } = require('chrome'); +const self = require('sdk/self'); +const { AddonManager } = Cu.import('resource://gre/modules/AddonManager.jsm', {}); + +exports["test add-on manifest was localized"] = (assert, done) => { + AddonManager.getAddonByID(self.id, addon => { + assert.equal(addon.name, "title-en", "title was translated"); + assert.equal(addon.description, "description-en", "description was translated"); + assert.equal(addon.creator, "author-en", "author was translated"); + assert.equal(addon.homepageURL, "homepage-en", "homepage was translated"); + done(); + }); +}; + +require("sdk/test/runner").runTestsFromModule(module); diff --git a/test/addons/manifest-localized/package.json b/test/addons/manifest-localized/package.json new file mode 100644 index 000000000..61011ade3 --- /dev/null +++ b/test/addons/manifest-localized/package.json @@ -0,0 +1,10 @@ +{ + "name": "manifest-localized", + "id": "manifest-localized@jetpack", + "license": "MPL 2.0", + "version": "0.1", + "title": "Manifest Not Localized", + "author": "Manifest Not Localized", + "description": "Manifest Not Localized", + "homepage": "Manifest Not Localized" +}