diff options
author | Norbert Lindenberg <ecmascript@lindenbergsoftware.com> | 2012-08-26 20:49:25 -0700 |
---|---|---|
committer | Norbert Lindenberg <ecmascript@lindenbergsoftware.com> | 2012-08-26 20:49:25 -0700 |
commit | 1af24250750e0a4318ec8efb467245fa0e86c215 (patch) | |
tree | 50a31abd8732055a1db32a63e5901f2037ed1d68 /test | |
parent | 8cad7d03cea197bd374b35f4af95a58c7b583538 (diff) |
Added new tests for chapters 6 and 9 of ECMAScript Internationalization API Specification.
- Removed a few old test cases that were redundant with new, more comprehensive ones.
- Added testIntl.js as standard include for all console tests in test262.py – see related bug 574.
- Added .jshintrc file for settings for the JSHint tool.
Diffstat (limited to 'test')
32 files changed, 1606 insertions, 56 deletions
diff --git a/test/harness/testIntl.js b/test/harness/testIntl.js new file mode 100644 index 000000000..251f0a2ca --- /dev/null +++ b/test/harness/testIntl.js @@ -0,0 +1,658 @@ +// Copyright 2011-2012 Norbert Lindenberg. All rights reserved. +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * This file contains shared functions for the tests in the conformance test + * suite for the ECMAScript Internationalization API. + * @author Norbert Lindenberg + */ + + +/** + * @description Calls the provided function for every service constructor in + * the Intl object, until f returns a falsy value. It returns the result of the + * last call to f, mapped to a boolean. + * @param {Function} f the function to call for each service constructor in + * the Intl object. + * @param {Function} Constructor the constructor object to test with. + * @result {Boolean} whether the test succeeded. + */ +function testWithIntlConstructors(f) { + var constructors = ["Collator", "NumberFormat", "DateTimeFormat"]; + return constructors.every(function (constructor) { + var Constructor = Intl[constructor]; + var result; + try { + result = f(Constructor); + } catch (e) { + e.message += " (Testing with " + constructor + ".)"; + throw e; + } + return result; + }); +} + + +/** + * Returns the name of the given constructor object, which must be one of + * Intl.Collator, Intl.NumberFormat, or Intl.DateTimeFormat. + * @param {object} Constructor a constructor + * @return {string} the name of the constructor + */ +function getConstructorName(Constructor) { + switch (Constructor) { + case Intl.Collator: + return "Collator"; + case Intl.NumberFormat: + return "NumberFormat"; + case Intl.DateTimeFormat: + return "DateTimeFormat"; + default: + $ERROR("test internal error: unknown Constructor"); + } +} + + +/** + * Taints a named data property of the given object by installing + * a setter that throws an exception. + * @param {object} obj the object whose data property to taint + * @param {string} property the property to taint + */ +function taintDataProperty(obj, property) { + Object.defineProperty(obj, property, { + set: function(value) { + $ERROR("Client code can adversely affect behavior: setter for " + property + "."); + }, + enumerable: false, + configurable: true + }); +} + + +/** + * Taints a named method of the given object by replacing it with a function + * that throws an exception. + * @param {object} obj the object whose method to taint + * @param {string} property the name of the method to taint + */ +function taintMethod(obj, property) { + Object.defineProperty(obj, property, { + value: function() { + $ERROR("Client code can adversely affect behavior: method " + property + "."); + }, + writable: true, + enumerable: false, + configurable: true + }); +} + + +/** + * Taints the given properties (and similarly named properties) by installing + * setters on Object.prototype that throw exceptions. + * @param {Array} properties an array of property names to taint + */ +function taintProperties(properties) { + properties.forEach(function (property) { + var adaptedProperties = [property, "__" + property, "_" + property, property + "_", property + "__"]; + adaptedProperties.forEach(function (property) { + taintDataProperty(Object.prototype, property); + }); + }); +} + + +/** + * Taints the Array object by creating a setter for the property "0" and + * replacing some key methods with functions that throw exceptions. + */ +function taintArray() { + taintDataProperty(Array.prototype, "0"); + taintMethod(Array.prototype, "indexOf"); + taintMethod(Array.prototype, "join"); + taintMethod(Array.prototype, "push"); + taintMethod(Array.prototype, "slice"); + taintMethod(Array.prototype, "sort"); +} + + +// auxiliary data for getLocaleSupportInfo +var languages = ["zh", "es", "en", "hi", "ur", "ar", "ja", "pa"]; +var scripts = ["Latn", "Hans", "Deva", "Arab", "Jpan", "Hant"]; +var countries = ["CN", "IN", "US", "PK", "JP", "TW", "HK", "SG"]; +var localeSupportInfo = {}; + + +/** + * Gets locale support info for the given constructor object, which must be one + * of Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat. + * @param {object} Constructor the constructor for which to get locale support info + * @return {object} locale support info with the following properties: + * supported: array of fully supported language tags + * byFallback: array of language tags that are supported through fallbacks + * unsupported: array of unsupported language tags + */ +function getLocaleSupportInfo(Constructor) { + var constructorName = getConstructorName(Constructor); + if (localeSupportInfo[constructorName] !== undefined) { + return localeSupportInfo[constructorName]; + } + + var allTags = []; + var i, j, k; + var language, script, country; + for (i = 0; i < languages.length; i++) { + language = languages[i]; + allTags.push(language); + for (j = 0; j < scripts.length; j++) { + script = scripts[j]; + allTags.push(language + "-" + script); + for (k = 0; k < countries.length; k++) { + country = countries[k]; + allTags.push(language + "-" + script + "-" + country); + } + } + for (k = 0; k < countries.length; k++) { + country = countries[k]; + allTags.push(language + "-" + country); + } + } + + var supported = []; + var byFallback = []; + var unsupported = []; + for (i = 0; i < allTags.length; i++) { + var request = allTags[i]; + var result = new Constructor([request], {localeMatcher: "lookup"}).resolvedOptions().locale; + if (request === result) { + supported.push(request); + } else if (request.indexOf(result) === 0) { + byFallback.push(request); + } else { + unsupported.push(request); + } + } + + localeSupportInfo[constructorName] = { + supported: supported, + byFallback: byFallback, + unsupported: unsupported + }; + + return localeSupportInfo[constructorName]; +} + + +/** + * @description Tests whether locale is a String value representing a + * structurally valid and canonicalized BCP 47 language tag, as defined in + * sections 6.2.2 and 6.2.3 of the ECMAScript Internationalization API + * Specification. + * @param {String} locale the string to be tested. + * @result {Boolean} whether the test succeeded. + */ +function isCanonicalizedStructurallyValidLanguageTag(locale) { + + /** + * Regular expression defining BCP 47 language tags. + * + * Spec: RFC 5646 section 2.1. + */ + var alpha = "[a-zA-Z]", + digit = "[0-9]", + alphanum = "(" + alpha + "|" + digit + ")", + regular = "(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)", + irregular = "(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)", + grandfathered = "(" + irregular + "|" + regular + ")", + privateuse = "(x(-[a-z0-9]{1,8})+)", + singleton = "(" + digit + "|[A-WY-Za-wy-z])", + extension = "(" + singleton + "(-" + alphanum + "{2,8})+)", + variant = "(" + alphanum + "{5,8}|(" + digit + alphanum + "{3}))", + region = "(" + alpha + "{2}|" + digit + "{3})", + script = "(" + alpha + "{4})", + extlang = "(" + alpha + "{3}(-" + alpha + "{3}){0,2})", + language = "(" + alpha + "{2,3}(-" + extlang + ")?|" + alpha + "{4}|" + alpha + "{5,8})", + langtag = language + "(-" + script + ")?(-" + region + ")?(-" + variant + ")*(-" + extension + ")*(-" + privateuse + ")?", + languageTag = "^(" + langtag + "|" + privateuse + "|" + grandfathered + ")$", + languageTagRE = new RegExp(languageTag, "i"); + var duplicateSingleton = "-" + singleton + "-(.*-)?\\1(?!" + alphanum + ")", + duplicateSingletonRE = new RegExp(duplicateSingleton, "i"), + duplicateVariant = "(" + alphanum + "{2,8}-)+" + variant + "-(" + alphanum + "{2,8}-)*\\3(?!" + alphanum + ")", + duplicateVariantRE = new RegExp(duplicateVariant, "i"); + + + /** + * Verifies that the given string is a well-formed BCP 47 language tag + * with no duplicate variant or singleton subtags. + * + * Spec: ECMAScript Internationalization API Specification, draft, 6.2.2. + */ + function isStructurallyValidLanguageTag(locale) { + if (!languageTagRE.test(locale)) { + return false; + } + locale = locale.split(/-x-/)[0]; + return !duplicateSingletonRE.test(locale) && !duplicateVariantRE.test(locale); + } + + + /** + * Mappings from complete tags to preferred values. + * + * Spec: IANA Language Subtag Registry. + */ + var __tagMappings = { + // property names must be in lower case; values in canonical form + + // grandfathered tags from IANA language subtag registry, file date 2011-08-25 + "art-lojban": "jbo", + "cel-gaulish": "cel-gaulish", + "en-gb-oed": "en-GB-oed", + "i-ami": "ami", + "i-bnn": "bnn", + "i-default": "i-default", + "i-enochian": "i-enochian", + "i-hak": "hak", + "i-klingon": "tlh", + "i-lux": "lb", + "i-mingo": "i-mingo", + "i-navajo": "nv", + "i-pwn": "pwn", + "i-tao": "tao", + "i-tay": "tay", + "i-tsu": "tsu", + "no-bok": "nb", + "no-nyn": "nn", + "sgn-be-fr": "sfb", + "sgn-be-nl": "vgt", + "sgn-ch-de": "sgg", + "zh-guoyu": "cmn", + "zh-hakka": "hak", + "zh-min": "zh-min", + "zh-min-nan": "nan", + "zh-xiang": "hsn", + // deprecated redundant tags from IANA language subtag registry, file date 2011-08-25 + "sgn-br": "bzs", + "sgn-co": "csn", + "sgn-de": "gsg", + "sgn-dk": "dsl", + "sgn-es": "ssp", + "sgn-fr": "fsl", + "sgn-gb": "bfi", + "sgn-gr": "gss", + "sgn-ie": "isg", + "sgn-it": "ise", + "sgn-jp": "jsl", + "sgn-mx": "mfs", + "sgn-ni": "ncs", + "sgn-nl": "dse", + "sgn-no": "nsl", + "sgn-pt": "psr", + "sgn-se": "swl", + "sgn-us": "ase", + "sgn-za": "sfs", + "zh-cmn": "cmn", + "zh-cmn-hans": "cmn-Hans", + "zh-cmn-hant": "cmn-Hant", + "zh-gan": "gan", + "zh-wuu": "wuu", + "zh-yue": "yue", + // deprecated variant with prefix from IANA language subtag registry, file date 2011-08-25 + "ja-latn-hepburn-heploc": "ja-Latn-alalc97" + }; + + + /** + * Mappings from non-extlang subtags to preferred values. + * + * Spec: IANA Language Subtag Registry. + */ + var __subtagMappings = { + // property names and values must be in canonical case + // language subtags with Preferred-Value mappings from IANA language subtag registry, file date 2011-08-25 + "in": "id", + "iw": "he", + "ji": "yi", + "jw": "jv", + "mo": "ro", + "ayx": "nun", + "cjr": "mom", + "cmk": "xch", + "drh": "khk", + "drw": "prs", + "gav": "dev", + "mst": "mry", + "myt": "mry", + "tie": "ras", + "tkk": "twm", + "tnf": "prs", + // region subtags with Preferred-Value mappings from IANA language subtag registry, file date 2011-08-25 + "BU": "MM", + "DD": "DE", + "FX": "FR", + "TP": "TL", + "YD": "YE", + "ZR": "CD" + }; + + + /** + * Mappings from extlang subtags to preferred values. + * + * Spec: IANA Language Subtag Registry. + */ + var __extlangMappings = { + // extlang subtags with Preferred-Value mappings from IANA language subtag registry, file date 2011-08-25 + // values are arrays with [0] the replacement value, [1] (if present) the prefix to be removed + "aao": ["aao", "ar"], + "abh": ["abh", "ar"], + "abv": ["abv", "ar"], + "acm": ["acm", "ar"], + "acq": ["acq", "ar"], + "acw": ["acw", "ar"], + "acx": ["acx", "ar"], + "acy": ["acy", "ar"], + "adf": ["adf", "ar"], + "ads": ["ads", "sgn"], + "aeb": ["aeb", "ar"], + "aec": ["aec", "ar"], + "aed": ["aed", "sgn"], + "aen": ["aen", "sgn"], + "afb": ["afb", "ar"], + "afg": ["afg", "sgn"], + "ajp": ["ajp", "ar"], + "apc": ["apc", "ar"], + "apd": ["apd", "ar"], + "arb": ["arb", "ar"], + "arq": ["arq", "ar"], + "ars": ["ars", "ar"], + "ary": ["ary", "ar"], + "arz": ["arz", "ar"], + "ase": ["ase", "sgn"], + "asf": ["asf", "sgn"], + "asp": ["asp", "sgn"], + "asq": ["asq", "sgn"], + "asw": ["asw", "sgn"], + "auz": ["auz", "ar"], + "avl": ["avl", "ar"], + "ayh": ["ayh", "ar"], + "ayl": ["ayl", "ar"], + "ayn": ["ayn", "ar"], + "ayp": ["ayp", "ar"], + "bbz": ["bbz", "ar"], + "bfi": ["bfi", "sgn"], + "bfk": ["bfk", "sgn"], + "bjn": ["bjn", "ms"], + "bog": ["bog", "sgn"], + "bqn": ["bqn", "sgn"], + "bqy": ["bqy", "sgn"], + "btj": ["btj", "ms"], + "bve": ["bve", "ms"], + "bvl": ["bvl", "sgn"], + "bvu": ["bvu", "ms"], + "bzs": ["bzs", "sgn"], + "cdo": ["cdo", "zh"], + "cds": ["cds", "sgn"], + "cjy": ["cjy", "zh"], + "cmn": ["cmn", "zh"], + "coa": ["coa", "ms"], + "cpx": ["cpx", "zh"], + "csc": ["csc", "sgn"], + "csd": ["csd", "sgn"], + "cse": ["cse", "sgn"], + "csf": ["csf", "sgn"], + "csg": ["csg", "sgn"], + "csl": ["csl", "sgn"], + "csn": ["csn", "sgn"], + "csq": ["csq", "sgn"], + "csr": ["csr", "sgn"], + "czh": ["czh", "zh"], + "czo": ["czo", "zh"], + "doq": ["doq", "sgn"], + "dse": ["dse", "sgn"], + "dsl": ["dsl", "sgn"], + "dup": ["dup", "ms"], + "ecs": ["ecs", "sgn"], + "esl": ["esl", "sgn"], + "esn": ["esn", "sgn"], + "eso": ["eso", "sgn"], + "eth": ["eth", "sgn"], + "fcs": ["fcs", "sgn"], + "fse": ["fse", "sgn"], + "fsl": ["fsl", "sgn"], + "fss": ["fss", "sgn"], + "gan": ["gan", "zh"], + "gom": ["gom", "kok"], + "gse": ["gse", "sgn"], + "gsg": ["gsg", "sgn"], + "gsm": ["gsm", "sgn"], + "gss": ["gss", "sgn"], + "gus": ["gus", "sgn"], + "hab": ["hab", "sgn"], + "haf": ["haf", "sgn"], + "hak": ["hak", "zh"], + "hds": ["hds", "sgn"], + "hji": ["hji", "ms"], + "hks": ["hks", "sgn"], + "hos": ["hos", "sgn"], + "hps": ["hps", "sgn"], + "hsh": ["hsh", "sgn"], + "hsl": ["hsl", "sgn"], + "hsn": ["hsn", "zh"], + "icl": ["icl", "sgn"], + "ils": ["ils", "sgn"], + "inl": ["inl", "sgn"], + "ins": ["ins", "sgn"], + "ise": ["ise", "sgn"], + "isg": ["isg", "sgn"], + "isr": ["isr", "sgn"], + "jak": ["jak", "ms"], + "jax": ["jax", "ms"], + "jcs": ["jcs", "sgn"], + "jhs": ["jhs", "sgn"], + "jls": ["jls", "sgn"], + "jos": ["jos", "sgn"], + "jsl": ["jsl", "sgn"], + "jus": ["jus", "sgn"], + "kgi": ["kgi", "sgn"], + "knn": ["knn", "kok"], + "kvb": ["kvb", "ms"], + "kvk": ["kvk", "sgn"], + "kvr": ["kvr", "ms"], + "kxd": ["kxd", "ms"], + "lbs": ["lbs", "sgn"], + "lce": ["lce", "ms"], + "lcf": ["lcf", "ms"], + "liw": ["liw", "ms"], + "lls": ["lls", "sgn"], + "lsg": ["lsg", "sgn"], + "lsl": ["lsl", "sgn"], + "lso": ["lso", "sgn"], + "lsp": ["lsp", "sgn"], + "lst": ["lst", "sgn"], + "lsy": ["lsy", "sgn"], + "ltg": ["ltg", "lv"], + "lvs": ["lvs", "lv"], + "lzh": ["lzh", "zh"], + "max": ["max", "ms"], + "mdl": ["mdl", "sgn"], + "meo": ["meo", "ms"], + "mfa": ["mfa", "ms"], + "mfb": ["mfb", "ms"], + "mfs": ["mfs", "sgn"], + "min": ["min", "ms"], + "mnp": ["mnp", "zh"], + "mqg": ["mqg", "ms"], + "mre": ["mre", "sgn"], + "msd": ["msd", "sgn"], + "msi": ["msi", "ms"], + "msr": ["msr", "sgn"], + "mui": ["mui", "ms"], + "mzc": ["mzc", "sgn"], + "mzg": ["mzg", "sgn"], + "mzy": ["mzy", "sgn"], + "nan": ["nan", "zh"], + "nbs": ["nbs", "sgn"], + "ncs": ["ncs", "sgn"], + "nsi": ["nsi", "sgn"], + "nsl": ["nsl", "sgn"], + "nsp": ["nsp", "sgn"], + "nsr": ["nsr", "sgn"], + "nzs": ["nzs", "sgn"], + "okl": ["okl", "sgn"], + "orn": ["orn", "ms"], + "ors": ["ors", "ms"], + "pel": ["pel", "ms"], + "pga": ["pga", "ar"], + "pks": ["pks", "sgn"], + "prl": ["prl", "sgn"], + "prz": ["prz", "sgn"], + "psc": ["psc", "sgn"], + "psd": ["psd", "sgn"], + "pse": ["pse", "ms"], + "psg": ["psg", "sgn"], + "psl": ["psl", "sgn"], + "pso": ["pso", "sgn"], + "psp": ["psp", "sgn"], + "psr": ["psr", "sgn"], + "pys": ["pys", "sgn"], + "rms": ["rms", "sgn"], + "rsi": ["rsi", "sgn"], + "rsl": ["rsl", "sgn"], + "sdl": ["sdl", "sgn"], + "sfb": ["sfb", "sgn"], + "sfs": ["sfs", "sgn"], + "sgg": ["sgg", "sgn"], + "sgx": ["sgx", "sgn"], + "shu": ["shu", "ar"], + "slf": ["slf", "sgn"], + "sls": ["sls", "sgn"], + "sqs": ["sqs", "sgn"], + "ssh": ["ssh", "ar"], + "ssp": ["ssp", "sgn"], + "ssr": ["ssr", "sgn"], + "svk": ["svk", "sgn"], + "swc": ["swc", "sw"], + "swh": ["swh", "sw"], + "swl": ["swl", "sgn"], + "syy": ["syy", "sgn"], + "tmw": ["tmw", "ms"], + "tse": ["tse", "sgn"], + "tsm": ["tsm", "sgn"], + "tsq": ["tsq", "sgn"], + "tss": ["tss", "sgn"], + "tsy": ["tsy", "sgn"], + "tza": ["tza", "sgn"], + "ugn": ["ugn", "sgn"], + "ugy": ["ugy", "sgn"], + "ukl": ["ukl", "sgn"], + "uks": ["uks", "sgn"], + "urk": ["urk", "ms"], + "uzn": ["uzn", "uz"], + "uzs": ["uzs", "uz"], + "vgt": ["vgt", "sgn"], + "vkk": ["vkk", "ms"], + "vkt": ["vkt", "ms"], + "vsi": ["vsi", "sgn"], + "vsl": ["vsl", "sgn"], + "vsv": ["vsv", "sgn"], + "wuu": ["wuu", "zh"], + "xki": ["xki", "sgn"], + "xml": ["xml", "sgn"], + "xmm": ["xmm", "ms"], + "xms": ["xms", "sgn"], + "yds": ["yds", "sgn"], + "ysl": ["ysl", "sgn"], + "yue": ["yue", "zh"], + "zib": ["zib", "sgn"], + "zlm": ["zlm", "ms"], + "zmi": ["zmi", "ms"], + "zsl": ["zsl", "sgn"], + "zsm": ["zsm", "ms"] + }; + + + /** + * Canonicalizes the given well-formed BCP 47 language tag, including regularized case of subtags. + * + * Spec: ECMAScript Internationalization API Specification, draft, 6.2.3. + * Spec: RFC 5646, section 4.5. + */ + function canonicalizeLanguageTag(locale) { + + // start with lower case for easier processing, and because most subtags will need to be lower case anyway + locale = locale.toLowerCase(); + + // handle mappings for complete tags + if (__tagMappings.hasOwnProperty(locale)) { + return __tagMappings[locale]; + } + + var subtags = locale.split("-"); + var i = 0; + + // handle standard part: all subtags before first singleton or "x" + while (i < subtags.length) { + var subtag = subtags[i]; + if (subtag.length === 1 && (i > 0 || subtag === "x")) { + break; + } else if (i !== 0 && subtag.length === 2) { + subtag = subtag.toUpperCase(); + } else if (subtag.length === 4) { + subtag = subtag[0].toUpperCase() + subtag.substring(1).toLowerCase(); + } + if (__subtagMappings.hasOwnProperty(subtag)) { + subtag = __subtagMappings[subtag]; + } else if (__extlangMappings.hasOwnProperty(subtag)) { + subtag = __extlangMappings[subtag][0]; + if (i === 1 && __extlangMappings[subtag][1] === subtags[0]) { + subtags.shift(); + i--; + } + } + subtags[i] = subtag; + i++; + } + var normal = subtags.slice(0, i).join("-"); + + // handle extensions + var extensions = []; + while (i < subtags.length && subtags[i] !== "x") { + var extensionStart = i; + i++; + while (i < subtags.length && subtags[i].length > 1) { + i++; + } + var extension = subtags.slice(extensionStart, i).join("-"); + extensions.push(extension); + } + extensions.sort(); + + // handle private use + var privateUse; + if (i < subtags.length) { + privateUse = subtags.slice(i).join("-"); + } + + // put everything back together + var canonical = normal; + if (extensions.length > 0) { + canonical += "-" + extensions.join("-"); + } + if (privateUse !== undefined) { + if (canonical.length > 0) { + canonical += "-" + privateUse; + } else { + canonical = privateUse; + } + } + + return canonical; + } + + return typeof locale === "string" && isStructurallyValidLanguageTag(locale) && + canonicalizeLanguageTag(locale) === locale; +} + diff --git a/test/suite/intl402/ch06/6.2/6.2.2_a.js b/test/suite/intl402/ch06/6.2/6.2.2_a.js new file mode 100644 index 000000000..144a1e7c9 --- /dev/null +++ b/test/suite/intl402/ch06/6.2/6.2.2_a.js @@ -0,0 +1,34 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that structurally valid language tags are accepted. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +var validLanguageTags = [ + "de", // ISO 639 language code + "de-DE", // + ISO 3166-1 country code + "DE-de", // tags are case-insensitive + "cmn", // ISO 639 language code + "cmn-Hans", // + script code + "CMN-hANS", // tags are case-insensitive + "cmn-hans-cn", // + ISO 3166-1 country code + "es-419", // + UN M.49 region code + "es-419-u-nu-latn-cu-bob", // + Unicode locale extension sequence + "i-klingon", // grandfathered tag + "cmn-hans-cn-t-ca-u-ca-x-t-u", // singleton subtags can also be used as private use subtags + "enochian-enochian", // language and variant subtags may be the same + "de-gregory-u-ca-gregory" // variant and extension subtags may be the same +]; + +testWithIntlConstructors(function (Constructor) { + validLanguageTags.forEach(function (tag) { + // this must not throw an exception for a valid language tag + var obj = new Constructor([tag]); + }); + return true; +}); + diff --git a/test/suite/intl402/ch06/6.2/6.2.2_b.js b/test/suite/intl402/ch06/6.2/6.2.2_b.js new file mode 100644 index 000000000..42d69e7c2 --- /dev/null +++ b/test/suite/intl402/ch06/6.2/6.2.2_b.js @@ -0,0 +1,41 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that language tags with "_" are not accepted. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +var invalidLanguageTags = [ + "de_DE", + "DE_de", + "cmn_Hans", + "cmn-hans_cn", + "es_419", + "es-419-u-nu-latn-cu_bob", + "i_klingon", + "cmn-hans-cn-t-ca-u-ca-x_t-u", + "enochian_enochian", + "de-gregory_u-ca-gregory" +]; + +testWithIntlConstructors(function (Constructor) { + invalidLanguageTags.forEach(function (tag) { + var error; + try { + // this must throw an exception for an invalid language tag + var obj = new Constructor([tag]); + } catch (e) { + error = e; + } + if (error === undefined) { + $ERROR("Invalid language tag " + tag + " was not rejected."); + } else if (error.name !== "RangeError") { + $ERROR("Invalid language tag " + tag + " was rejected with wrong error " + error.name + "."); + } + }); + return true; +}); + diff --git a/test/suite/intl402/ch06/6.2/6.2.2_c.js b/test/suite/intl402/ch06/6.2/6.2.2_c.js new file mode 100644 index 000000000..ff2f9ae95 --- /dev/null +++ b/test/suite/intl402/ch06/6.2/6.2.2_c.js @@ -0,0 +1,45 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that language tags with invalid subtag sequences are not accepted. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +var invalidLanguageTags = [ + "", // empty tag + "i", // singleton alone + "x", // private use without subtag + "u", // extension singleton in first place + "419", // region code in first place + "u-nu-latn-cu-bob", // extension sequence without language + "hans-cmn-cn", // "hans" could theoretically be a 4-letter language code, + // but those can't be followed by extlang codes. + "cmn-hans-cn-u-u", // duplicate singleton + "cmn-hans-cn-t-u-ca-u", // duplicate singleton + "de-gregory-gregory", // duplicate variant + "中文", // non-ASCII letters + "en-ß", // non-ASCII letters + "ıd" // non-ASCII letters +]; + +testWithIntlConstructors(function (Constructor) { + invalidLanguageTags.forEach(function (tag) { + var error; + try { + // this must throw an exception for an invalid language tag + var obj = new Constructor([tag]); + } catch (e) { + error = e; + } + if (error === undefined) { + $ERROR("Invalid language tag " + tag + " was not rejected."); + } else if (error.name !== "RangeError") { + $ERROR("Invalid language tag " + tag + " was rejected with wrong error " + error.name + "."); + } + }); + return true; +}); + diff --git a/test/suite/intl402/ch06/6.2/6.2.3.js b/test/suite/intl402/ch06/6.2/6.2.3.js new file mode 100644 index 000000000..ce273e198 --- /dev/null +++ b/test/suite/intl402/ch06/6.2/6.2.3.js @@ -0,0 +1,68 @@ +// Copyright 2011-2012 Norbert Lindenberg. All rights reserved. +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that language tags are canonicalized in return values. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +var canonicalizedTags = { + "de": ["de"], + "de-DE": ["de-DE", "de"], + "DE-de": ["de-DE", "de"], + "cmn": ["cmn"], + "CMN-hANS": ["cmn-Hans", "cmn"], + "cmn-hans-cn": ["cmn-Hans-CN", "cmn-Hans", "cmn"], + "es-419": ["es-419", "es"], + "es-419-u-nu-latn": ["es-419-u-nu-latn", "es-419", "es", "es-u-nu-latn"], + // -u-ca is incomplete, so it will not show up in resolvedOptions().locale + "cmn-hans-cn-u-ca-t-ca-x-t-u": ["cmn-Hans-CN-t-ca-u-ca-x-t-u", "cmn-Hans-CN-t-ca-x-t-u", "cmn-Hans-CN-t-ca-x-t", "cmn-Hans-CN-t-ca", "cmn-Hans-CN", "cmn-Hans", "cmn"], + "enochian-enochian": ["enochian-enochian", "enochian"], + "de-gregory-u-ca-gregory": ["de-gregory-u-ca-gregory", "de-gregory", "de-u-ca-gregory", "de"], + "no-nyn": ["nn"], + "i-klingon": ["tlh"], + "sgn-GR": ["gss"], + "ji": ["yi"], + "de-DD": ["de-DE", "de"], + "zh-hak-CN": ["hak-CN", "hak"], + "sgn-ils": ["ils"], + "in": ["id"] +}; + +// make sure the data above is correct +Object.getOwnPropertyNames(canonicalizedTags).forEach(function (tag) { + canonicalizedTags[tag].forEach(function (canonicalTag) { + if (!isCanonicalizedStructurallyValidLanguageTag(canonicalTag)) { + $ERROR("Test data \"" + canonicalTag + "\" is not canonicalized and structurally valid language tag."); + } + }); +}); + +// now the actual test +testWithIntlConstructors(function (Constructor) { + var defaultLocale = new Constructor().resolvedOptions().locale; + Object.getOwnPropertyNames(canonicalizedTags).forEach(function (tag) { + // use lookup locale matcher to keep the set of possible return values predictable + + // Variant 1: construct an object and see whether its locale is canonicalized. + // In this variant, shortened forms or the default locale may be returned + var object = new Constructor([tag], {localeMatcher: "lookup"}); + var locale = object.resolvedOptions().locale; + if (canonicalizedTags[tag].indexOf(locale) === -1 && locale !== defaultLocale) { + $ERROR("For " + tag + " got " + locale + "; expected one of " + + canonicalizedTags[tag].join(", ") + "."); + } + + // Variant 2: get the supported locales. If the tag is supported, it should be returned canonicalized but unshortened + var supported = Constructor.supportedLocalesOf([tag]); + if (supported.length > 0 && supported[0] !== canonicalizedTags[tag][0]) { + $ERROR("For " + tag + " got " + supported[0] + "; expected " + + canonicalizedTags[tag][0] + "."); + } + }); + return true; +}); + diff --git a/test/suite/intl402/ch06/6.2/6.2.4.js b/test/suite/intl402/ch06/6.2/6.2.4.js new file mode 100644 index 000000000..6ca47af14 --- /dev/null +++ b/test/suite/intl402/ch06/6.2/6.2.4.js @@ -0,0 +1,19 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that the default locale is a String value representing the + * structurally valid and canonicalized BCP 47 language tag. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +testWithIntlConstructors(function (Constructor) { + var defaultLocale = new Constructor().resolvedOptions().locale; + if (!isCanonicalizedStructurallyValidLanguageTag(defaultLocale)) { + $ERROR("Default locale \"" + defaultLocale + "\" is not canonicalized and structurally valid language tag."); + } + return true; +}); + diff --git a/test/suite/intl402/ch06/6.3/6.3.1_a.js b/test/suite/intl402/ch06/6.3/6.3.1_a.js new file mode 100644 index 000000000..01cc18b03 --- /dev/null +++ b/test/suite/intl402/ch06/6.3/6.3.1_a.js @@ -0,0 +1,25 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that well-formed currency codes are accepted. + * @author Norbert Lindenberg + */ + +var wellFormedCurrencyCodes = [ + "BOB", + "EUR", + "usd", // currency codes are case-insensitive + "XdR", + "xTs" +]; + +wellFormedCurrencyCodes.forEach(function (code) { + // this must not throw an exception for a valid currency code + var format = new Intl.NumberFormat(["de-de"], {style: "currency", currency: code}); + if (format.resolvedOptions().currency !== code.toUpperCase()) { + $ERROR("Currency " + code + " was not correctly accepted; turned into " + + format.resolvedOptions().currency + "."); + } +}); + diff --git a/test/suite/intl402/ch06/6.3/6.3.1_b.js b/test/suite/intl402/ch06/6.3/6.3.1_b.js new file mode 100644 index 000000000..90051b343 --- /dev/null +++ b/test/suite/intl402/ch06/6.3/6.3.1_b.js @@ -0,0 +1,34 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that invalid currency codes are not accepted. + * @author Norbert Lindenberg + */ + +var invalidCurrencyCodes = [ + "€", + "$", + "SFr.", + "DM", + "KR₩", + "702", + "ßP", + "ınr" +]; + +invalidCurrencyCodes.forEach(function (code) { + var error; + try { + // this must throw an exception for an invalid currency code + var format = new Intl.NumberFormat(["de-de"], {style: "currency", currency: code}); + } catch (e) { + error = e; + } + if (error === undefined) { + $ERROR("Invalid currency code " + code + " was not rejected."); + } else if (error.name !== "RangeError") { + $ERROR("Invalid currency code " + code + " was rejected with wrong error " + error.name + "."); + } +}); + diff --git a/test/suite/intl402/ch06/6.4/6.4_a.js b/test/suite/intl402/ch06/6.4/6.4_a.js new file mode 100644 index 000000000..05202353c --- /dev/null +++ b/test/suite/intl402/ch06/6.4/6.4_a.js @@ -0,0 +1,22 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that valid time zone names are accepted. + * @author Norbert Lindenberg + */ + +var validTimeZoneNames = [ + "UTC", + "utc" // time zone names are case-insensitive +]; + +validTimeZoneNames.forEach(function (name) { + // this must not throw an exception for a valid time zone name + var format = new Intl.DateTimeFormat(["de-de"], {timeZone: name}); + if (format.resolvedOptions().timeZone !== name.toUpperCase()) { + $ERROR("Time zone name " + name + " was not correctly accepted; turned into " + + format.resolvedOptions().timeZone + "."); + } +}); + diff --git a/test/suite/intl402/ch06/6.4/6.4_b.js b/test/suite/intl402/ch06/6.4/6.4_b.js new file mode 100644 index 000000000..847d804e3 --- /dev/null +++ b/test/suite/intl402/ch06/6.4/6.4_b.js @@ -0,0 +1,34 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that invalid time zone names are not accepted. + * @author Norbert Lindenberg + */ + +var invalidTimeZoneNames = [ + "", + "MEZ", // localized abbreviation + "Pacific Time", // localized long form + "cnsha", // BCP 47 time zone code + "invalid", // as the name says + "Europe/İstanbul", // non-ASCII letter + "asıa/baku", // non-ASCII letter + "europe/brußels" // non-ASCII letter +]; + +invalidTimeZoneNames.forEach(function (name) { + var error; + try { + // this must throw an exception for an invalid time zone name + var format = new Intl.DateTimeFormat(["de-de"], {timeZone: name}); + } catch (e) { + error = e; + } + if (error === undefined) { + $ERROR("Invalid time zone name " + name + " was not rejected."); + } else if (error.name !== "RangeError") { + $ERROR("Invalid time zone name " + name + " was rejected with wrong error " + error.name + "."); + } +}); + diff --git a/test/suite/intl402/ch06/6.4/6.4_c.js b/test/suite/intl402/ch06/6.4/6.4_c.js new file mode 100644 index 000000000..75bcdbd80 --- /dev/null +++ b/test/suite/intl402/ch06/6.4/6.4_c.js @@ -0,0 +1,36 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that additional time zone names, if accepted, are handled correctly. + * @author Norbert Lindenberg + */ + +// canonicalization specified in conformance clause +var additionalTimeZoneNames = { + "Etc/GMT": "UTC", + "Greenwich": "UTC", + "PRC": "Asia/Shanghai", + "AmErIcA/LoS_aNgElEs": "America/Los_Angeles", + "etc/gmt+7": "Etc/GMT+7" +}; + +Object.getOwnPropertyNames(additionalTimeZoneNames).forEach(function (name) { + var format, error; + try { + format = new Intl.DateTimeFormat([], {timeZone: name}); + } catch (e) { + error = e; + } + if (error === undefined) { + var actual = format.resolvedOptions().timeZone; + var expected = additionalTimeZoneNames.name; + if (actual !== expected) { + $ERROR("Time zone name " + name + " was accepted, but incorrectly canonicalized to " + + actual + "; expected " + expected + "."); + } + } else if (error.name !== "RangeError") { + $ERROR("Time zone name " + name + " was rejected with wrong error " + error.name + "."); + } +}); + diff --git a/test/suite/intl402/ch09/9.1/9.1_a.js b/test/suite/intl402/ch09/9.1/9.1_a.js new file mode 100644 index 000000000..b7358304b --- /dev/null +++ b/test/suite/intl402/ch09/9.1/9.1_a.js @@ -0,0 +1,18 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that default locale is available. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +testWithIntlConstructors(function (Constructor) { + var defaultLocale = new Constructor().resolvedOptions().locale; + var supportedLocales = Constructor.supportedLocalesOf([defaultLocale]); + if (supportedLocales.indexOf(defaultLocale) === -1) { + $ERROR("Default locale is not reported as available."); + } +}); + diff --git a/test/suite/intl402/ch09/9.1/9.1_b.js b/test/suite/intl402/ch09/9.1/9.1_b.js new file mode 100644 index 000000000..a0abc7fc1 --- /dev/null +++ b/test/suite/intl402/ch09/9.1/9.1_b.js @@ -0,0 +1,32 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that appropriate fallback locales are provided for + * supported locales. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +testWithIntlConstructors(function (Constructor) { + var info = getLocaleSupportInfo(Constructor); + var fallback; + info.supported.forEach(function (locale) { + var pos = locale.lastIndexOf("-"); + if (pos !== -1) { + fallback = locale.substring(0, pos); + if (info.supported.indexOf(fallback) === -1) { + $ERROR("Locale " + locale + " is supported, but fallback " + fallback + " isn't."); + } + } + var match = /([a-z]{2,3})(-[A-Z][a-z]{3})(-[A-Z]{2})/.exec(locale); + if (match !== null) { + fallback = match[1] + match[3]; + if (info.supported.indexOf(fallback) === -1) { + $ERROR("Locale " + locale + " is supported, but fallback " + fallback + " isn't."); + } + } + }); +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.1_1.js b/test/suite/intl402/ch09/9.2/9.2.1_1.js new file mode 100644 index 000000000..7386fdeda --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.1_1.js @@ -0,0 +1,23 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that canonicalization of locale lists treats undefined and empty lists the same. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +testWithIntlConstructors(function (Constructor) { + var supportedForUndefined = Constructor.supportedLocalesOf(undefined); + var supportedForEmptyList = Constructor.supportedLocalesOf([]); + if (supportedForUndefined.length !== supportedForEmptyList.length) { + $ERROR("Supported locales differ between undefined and empty list input."); + } + // we don't compare the elements because length should be 0 - let's just verify that + if (supportedForUndefined.length !== 0) { + $ERROR("Internal test error: Assumption about length being 0 is invalid."); + } + return true; +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.1_2.js b/test/suite/intl402/ch09/9.2/9.2.1_2.js new file mode 100644 index 000000000..e4fb3756d --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.1_2.js @@ -0,0 +1,21 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that the behavior of a List is not affected by adversarial + * changes to Array.prototype. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +taintArray(); + +testWithIntlConstructors(function (Constructor) { + var defaultLocale = new Constructor().resolvedOptions().locale; + var canonicalized = Constructor.supportedLocalesOf([defaultLocale, defaultLocale]); + if (canonicalized.length > 1) { + $ERROR("Canonicalization didn't remove duplicate language tags from locale list."); + } +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.1_3.js b/test/suite/intl402/ch09/9.2/9.2.1_3.js new file mode 100644 index 000000000..e403b1765 --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.1_3.js @@ -0,0 +1,87 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that a single string instead of a locale list is treated + * as the locale list containing that string. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +var validAndInvalidLanguageTags = [ + "de", // ISO 639 language code + "de-DE", // + ISO 3166-1 country code + "DE-de", // tags are case-insensitive + "cmn", // ISO 639 language code + "cmn-Hans", // + script code + "CMN-hANS", // tags are case-insensitive + "cmn-hans-cn", // + ISO 3166-1 country code + "es-419", // + UN M.49 region code + "es-419-u-nu-latn-cu-bob", // + Unicode locale extension sequence + "i-klingon", // grandfathered tag + "cmn-hans-cn-t-ca-u-ca-x-t-u", // singleton subtags can also be used as private use subtags + "enochian-enochian", // language and variant subtags may be the same + "de-gregory-u-ca-gregory", // variant and extension subtags may be the same + "de_DE", + "DE_de", + "cmn_Hans", + "cmn-hans_cn", + "es_419", + "es-419-u-nu-latn-cu_bob", + "i_klingon", + "cmn-hans-cn-t-ca-u-ca-x_t-u", + "enochian_enochian", + "de-gregory_u-ca-gregory", + "i", // singleton alone + "x", // private use without subtag + "u", // extension singleton in first place + "419", // region code in first place + "u-nu-latn-cu-bob", // extension sequence without language + "hans-cmn-cn", // "hans" could theoretically be a 4-letter language code, + // but those can't be followed by extlang codes. + "cmn-hans-cn-u-u", // duplicate singleton + "cmn-hans-cn-t-u-ca-u", // duplicate singleton + "de-gregory-gregory" // duplicate variant +]; + +testWithIntlConstructors(function (Constructor) { + validAndInvalidLanguageTags.forEach(function (locale) { + var obj1, obj2, locale1, locale2, error1, error2; + try { + obj1 = new Constructor(locale); + locale1 = obj1.resolvedOptions().locale; + } catch (e) { + error1 = e; + } + try { + obj2 = new Constructor([locale]); + locale2 = obj2.resolvedOptions().locale; + } catch (e) { + error2 = e; + } + + if ((error1 === undefined) !== (error2 === undefined)) { + if (error1 === undefined) { + $ERROR("Single locale string " + locale + + " was accepted, but locale list containing that string wasn't."); + } else { + $ERROR("Single locale string " + locale + + " was rejected, but locale list containing that string wasn't."); + } + } else if (error1 === undefined) { + if (locale1 !== locale2) { + $ERROR("Single locale string " + locale + " results in " + locale1 + + ", but locale list [" + locale + "] results in " + locale2 + "."); + } + } else { + if (error1.name !== error2.name) { + $ERROR("Single locale string " + locale + " results in error " + error1.name + + ", but locale list [" + locale + "] results in error " + error2.name + "."); + } + } + }); + + return true; +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.1_4.js b/test/suite/intl402/ch09/9.2/9.2.1_4.js new file mode 100644 index 000000000..b9bb9db79 --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.1_4.js @@ -0,0 +1,46 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that non-objects are converted to objects before canonicalization. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +testWithIntlConstructors(function (Constructor) { + // undefined is handled separately + + // null should result in a TypeError + var error; + try { + var supportedForNull = Constructor.supportedLocalesOf(null); + } catch (e) { + error = e; + } + if (error === undefined) { + $ERROR("Null as locale list was not rejected."); + } else if (error.name !== "TypeError") { + $ERROR("Null as locale list was rejected with wrong error " + error.name + "."); + } + + // let's use an empty list for comparison + var supportedForEmptyList = Constructor.supportedLocalesOf([]); + // we don't compare the elements because length should be 0 - let's just verify that + if (supportedForEmptyList.length !== 0) { + $ERROR("Internal test error: Assumption about length being 0 is invalid."); + } + + // most non-objects will be interpreted as empty lists because a missing length property is interpreted as 0 + var supportedForNumber = Constructor.supportedLocalesOf(5); + if (supportedForNumber.length !== supportedForEmptyList.length) { + $ERROR("Supported locales differ between numeric and empty list input."); + } + var supportedForBoolean = Constructor.supportedLocalesOf(true); + if (supportedForBoolean.length !== supportedForEmptyList.length) { + $ERROR("Supported locales differ between boolean and empty list input."); + } + + return true; +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.1_8_c_ii.js b/test/suite/intl402/ch09/9.2/9.2.1_8_c_ii.js new file mode 100644 index 000000000..8dfb9b7b3 --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.1_8_c_ii.js @@ -0,0 +1,30 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that values other than strings are not accepted as locales. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +var notStringOrObject = [undefined, null, true, false, 0, 5, -5, NaN]; + +testWithIntlConstructors(function (Constructor) { + notStringOrObject.forEach(function (value) { + var error; + try { + var supported = Constructor.supportedLocalesOf([value]); + } catch (e) { + error = e; + } + if (error === undefined) { + $ERROR("" + value + " as locale was not rejected."); + } else if (error.name !== "TypeError") { + $ERROR("" + value + " as locale was rejected with wrong error " + error.name + "."); + } + }); + + return true; +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.1_8_c_vi.js b/test/suite/intl402/ch09/9.2/9.2.1_8_c_vi.js new file mode 100644 index 000000000..ef78fa97b --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.1_8_c_vi.js @@ -0,0 +1,18 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that canonicalization of locale lists removes duplicate language tags. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +testWithIntlConstructors(function (Constructor) { + var defaultLocale = new Constructor().resolvedOptions().locale; + var canonicalized = Constructor.supportedLocalesOf([defaultLocale, defaultLocale]); + if (canonicalized.length > 1) { + $ERROR("Canonicalization didn't remove duplicate language tags from locale list."); + } +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.2.js b/test/suite/intl402/ch09/9.2/9.2.2.js new file mode 100644 index 000000000..cb74eaad2 --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.2.js @@ -0,0 +1,45 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that locales that are reported by resolvedOptions + * are also reported by supportedLocalesOf. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +testWithIntlConstructors(function (Constructor) { + var info = getLocaleSupportInfo(Constructor); + // this test should work equally for both matching algorithms + ["lookup", "best fit"].forEach(function (matcher) { + var supportedByConstructor = info.supported.concat(info.byFallback); + var supported = Constructor.supportedLocalesOf(supportedByConstructor, + {localeMatcher: matcher}); + // we could check the length first, but it's probably more interesting which locales are missing + var i = 0; + var limit = Math.min(supportedByConstructor.length, supported.length); + while (i < limit && supportedByConstructor[i] === supported[i]) { + i++; + } + if (i < supportedByConstructor.length) { + $ERROR("Locale " + supportedByConstructor[i] + + " is returned by resolvedOptions but not by supportedLocalesOf."); + } else if (i < supported.length) { + $ERROR("Locale " + supported[i] + + " is returned by supportedLocalesOf but not by resolvedOptions."); + } + }); + + // this test is only valid for lookup - best fit may find additional locales supported + var unsupportedByConstructor = info.unsupported; + var supported = Constructor.supportedLocalesOf(unsupportedByConstructor, + {localeMatcher: "lookup"}); + if (supported.length > 0) { + $ERROR("Locale " + supported[0] + + " is returned by supportedLocalesOf but not by resolvedOptions."); + } + + return true; +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.3_5.js b/test/suite/intl402/ch09/9.2/9.2.3_5.js new file mode 100644 index 000000000..8ae8a216b --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.3_5.js @@ -0,0 +1,22 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that the behavior of a Record is not affected by adversarial + * changes to Object.prototype. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +taintProperties(["locale", "extension", "extensionIndex"]); + +testWithIntlConstructors(function (Constructor) { + var locale = new Constructor(undefined, {localeMatcher: "lookup"}).resolvedOptions().locale; + if (!isCanonicalizedStructurallyValidLanguageTag(locale)) { + $ERROR("Constructor returns invalid locale " + locale + "."); + } + + return true; +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.5_11_g_ii_2.js b/test/suite/intl402/ch09/9.2/9.2.5_11_g_ii_2.js new file mode 100644 index 000000000..d4213d2da --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.5_11_g_ii_2.js @@ -0,0 +1,26 @@ +// Copyright 2011-2012 Norbert Lindenberg. All rights reserved. +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that missing Unicode extension values default to true for + * boolean keys. + * @author Norbert Lindenberg + */ + +var extensions = ["-u-co-phonebk-kn", "-u-kn-co-phonebk"]; +extensions.forEach(function (extension) { + var defaultLocale = new Intl.Collator().resolvedOptions().locale; + var collator = new Intl.Collator([defaultLocale + extension], {usage: "sort"}); + var locale = collator.resolvedOptions().locale; + var numeric = collator.resolvedOptions().numeric; + if (numeric !== undefined) { + if (numeric !== true) { + $ERROR("Default value for \"kn\" should be true, but is " + numeric + "."); + } + if (locale.indexOf("-kn") !== -1) { + $ERROR("\"kn\" is returned in locale, but shouldn't be."); + } + } +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.5_6.js b/test/suite/intl402/ch09/9.2/9.2.5_6.js new file mode 100644 index 000000000..4bc904ee1 --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.5_6.js @@ -0,0 +1,22 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that the behavior of a Record is not affected by adversarial + * changes to Object.prototype. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +taintProperties(["dataLocale", "nu", "ca", "co", "locale"]); + +testWithIntlConstructors(function (Constructor) { + var locale = new Constructor(undefined, {localeMatcher: "lookup"}).resolvedOptions().locale; + if (!isCanonicalizedStructurallyValidLanguageTag(locale)) { + $ERROR("Constructor returns invalid locale " + locale + "."); + } + + return true; +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.6_2.js b/test/suite/intl402/ch09/9.2/9.2.6_2.js new file mode 100644 index 000000000..8d4f7efa9 --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.6_2.js @@ -0,0 +1,27 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that the behavior of a List is not affected by adversarial + * changes to Array.prototype. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +taintArray(); + +testWithIntlConstructors(function (Constructor) { + // this test should work equally for both matching algorithms + ["lookup", "best fit"].forEach(function (matcher) { + var defaultLocale = new Constructor().resolvedOptions().locale; + var canonicalized = Constructor.supportedLocalesOf([defaultLocale, defaultLocale], + {localeMatcher: matcher}); + if (canonicalized.length > 1) { + $ERROR("Canonicalization with matcher " + matcher + " didn't remove duplicate language tags from locale list."); + } + }); + + return true; +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.6_4.js b/test/suite/intl402/ch09/9.2/9.2.6_4.js new file mode 100644 index 000000000..7c053a381 --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.6_4.js @@ -0,0 +1,23 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that LookupSupportedLocales returns an empty list when + * given an empty list. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +testWithIntlConstructors(function (Constructor) { + // this test should work equally for both matching algorithms + ["lookup", "best fit"].forEach(function (matcher) { + var supported = Constructor.supportedLocalesOf([], {localeMatcher: matcher}); + if (supported.length !== 0) { + $ERROR("SupportedLocales with matcher " + matcher + " returned a non-empty list for an empty list."); + } + }); + + return true; +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.6_4_b.js b/test/suite/intl402/ch09/9.2/9.2.6_4_b.js new file mode 100644 index 000000000..5b9a43157 --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.6_4_b.js @@ -0,0 +1,47 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that Unicode locale extension sequences do not affect + * whether a locale is considered supported, but are reported back. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +testWithIntlConstructors(function (Constructor) { + // this test should work equally for both matching algorithms + ["lookup", "best fit"].forEach(function (matcher) { + var info = getLocaleSupportInfo(Constructor); + var allLocales = info.supported.concat(info.byFallback, info.unsupported); + allLocales.forEach(function (locale) { + var validExtension = "-u-co-phonebk-nu-latn"; + var invalidExtension = "-u-nu-invalid"; + var supported1 = Constructor.supportedLocalesOf([locale], + {localeMatcher: matcher}); + var supported2 = Constructor.supportedLocalesOf([locale + validExtension], + {localeMatcher: matcher}); + var supported3 = Constructor.supportedLocalesOf([locale + invalidExtension], + {localeMatcher: matcher}); + if (supported1.length === 1) { + if (supported2.length !== 1 || supported3.length !== 1) { + $ERROR("Presence of Unicode locale extension sequence affects whether locale " + + locale + " is considered supported with matcher " + matcher + "."); + } + if (supported2[0] !== locale + validExtension || supported3[0] !== locale + invalidExtension) { + alert(locale + "; " + supported2[0] + "; " + supported3[0]); + $ERROR("Unicode locale extension sequence is not correctly returned for locale " + + locale + " with matcher " + matcher + "."); + } + } else { + if (supported2.length !== 0 || supported3.length !== 0) { + $ERROR("Presence of Unicode locale extension sequence affects whether locale " + + locale + " is considered supported with matcher " + matcher + "."); + } + } + }); + }); + + return true; +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.6_4_c.js b/test/suite/intl402/ch09/9.2/9.2.6_4_c.js new file mode 100644 index 000000000..de68a9b66 --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.6_4_c.js @@ -0,0 +1,32 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that LookupSupportedLocales includes the default locale + * and doesn't include the "no linguistic content" locale. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +testWithIntlConstructors(function (Constructor) { + // this test should work equally for both matching algorithms + ["lookup", "best fit"].forEach(function (matcher) { + var defaultLocale = new Constructor().resolvedOptions().locale; + var noLinguisticContent = "zxx"; + var supported = Constructor.supportedLocalesOf([defaultLocale, noLinguisticContent], + {localeMatcher: matcher}); + if (supported.indexOf(defaultLocale) === -1) { + $ERROR("SupportedLocales didn't return default locale with matcher " + matcher + "."); + } + if (supported.indexOf(noLinguisticContent) !== -1) { + $ERROR("SupportedLocales returned the \"no linguistic content\" locale with matcher " + matcher + "."); + } + if (supported.length > 1) { + $ERROR("SupportedLocales returned stray locales: " + supported.join(", ") + " with matcher " + matcher + "."); + } + }); + + return true; +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.8_1_c.js b/test/suite/intl402/ch09/9.2/9.2.8_1_c.js new file mode 100644 index 000000000..724db1edd --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.8_1_c.js @@ -0,0 +1,36 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that the option localeMatcher is processed correctly. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +testWithIntlConstructors(function (Constructor) { + var defaultLocale = new Constructor().resolvedOptions().locale; + + var validValues = [undefined, "lookup", "best fit", {toString: function () { return "lookup"; }}]; + validValues.forEach(function (value) { + var supported = Constructor.supportedLocalesOf([defaultLocale], {localeMatcher: value}); + }); + + var invalidValues = [null, 0, 5, NaN, true, false, "invalid"]; + invalidValues.forEach(function (value) { + var error; + try { + var supported = Constructor.supportedLocalesOf([defaultLocale], {localeMatcher: value}); + } catch (e) { + error = e; + } + if (error === undefined) { + $ERROR("Invalid localeMatcher value " + value + " was not rejected."); + } else if (error.name !== "RangeError") { + $ERROR("Invalid localeMatcher value " + value + " was rejected with wrong error " + error.name + "."); + } + }); + + return true; +}); + diff --git a/test/suite/intl402/ch09/9.2/9.2.8_4.js b/test/suite/intl402/ch09/9.2/9.2.8_4.js new file mode 100644 index 000000000..79c362f62 --- /dev/null +++ b/test/suite/intl402/ch09/9.2/9.2.8_4.js @@ -0,0 +1,35 @@ +// Copyright 2012 Mozilla Corporation. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * @description Tests that the array returned by SupportedLocales is extensible, + * but its properties are non-writable/non-configurable. + * @author Norbert Lindenberg + */ + +$INCLUDE("testIntl.js"); + +function testFrozenProperty(obj, property) { + var desc = Object.getOwnPropertyDescriptor(obj, property); + if (desc.writable) { + $ERROR("Property " + property + " of object returned by SupportedLocales is writable."); + } + if (desc.configurable) { + $ERROR("Property " + property + " of object returned by SupportedLocales is configurable."); + } +} + +testWithIntlConstructors(function (Constructor) { + var defaultLocale = new Constructor().resolvedOptions().locale; + var supported = Constructor.supportedLocalesOf([defaultLocale]); + if (!Object.isExtensible(supported)) { + $ERROR("Object returned by SupportedLocales is not extensible."); + } + for (var i = 0; i < supported.length; i++) { + testFrozenProperty(supported, i); + } + testFrozenProperty(supported, "length"); + + return true; +}); + diff --git a/test/suite/intl402/ch10/10.2/10.2.3.js b/test/suite/intl402/ch10/10.2/10.2.3.js deleted file mode 100644 index c281491a0..000000000 --- a/test/suite/intl402/ch10/10.2/10.2.3.js +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2012 Google Inc. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/** - * @description Tests the internal properties of Intl.Collator. - */ - -var defaultLocale = new Intl.Collator([]).resolvedOptions().locale; -var supportedLocales = Intl.Collator.supportedLocalesOf([defaultLocale]); - -if (supportedLocales.length < 1 || supportedLocales[0] !== defaultLocale) { - $ERROR('The default locale is not supported by Intl.Collator'); -} - -// FIXME: Find a way to check [[relevantExtensionKeys]] - -// FIXME: Find a way to check specified properties of [[localeData]] - diff --git a/test/suite/intl402/ch11/11.2/11.2.3.js b/test/suite/intl402/ch11/11.2/11.2.3.js deleted file mode 100644 index b0f30e495..000000000 --- a/test/suite/intl402/ch11/11.2/11.2.3.js +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2012 Google Inc. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/** - * @description Tests the internal properties of Intl.NumberFormat. - * @author: Roozbeh Pournader - */ - -var defaultLocale = new Intl.NumberFormat([]).resolvedOptions().locale; -var supportedLocales = Intl.NumberFormat.supportedLocalesOf([defaultLocale]); - -if (supportedLocales.length < 1 || supportedLocales[0] !== defaultLocale) { - $ERROR('The default locale is not supported by Intl.NumberFormat'); -} - -// FIXME: Find a way to check that [[relevantExtensionKeys]] === ['nu'] - -// FIXME: Find a way to check specified properties of [[localeData]] - diff --git a/test/suite/intl402/ch12/12.2/12.2.3.js b/test/suite/intl402/ch12/12.2/12.2.3.js deleted file mode 100644 index 333d97ae8..000000000 --- a/test/suite/intl402/ch12/12.2/12.2.3.js +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2012 Google Inc. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/** - * @description Tests the internal properties of Intl.DateTimeFormat. - * @author: Roozbeh Pournader - */ - -var defaultLocale = new Intl.DateTimeFormat([]).resolvedOptions().locale; -var supportedLocales = Intl.DateTimeFormat.supportedLocalesOf([defaultLocale]); - -if (supportedLocales.length < 1 || supportedLocales[0] !== defaultLocale) { - $ERROR('The default locale is not supported by Intl.DateTimeFormat'); -} - -// FIXME: Find a way to check that [[relevantExtensionKeys]] === ['ca', 'nu'] - -// FIXME: Find a way to check specified properties of [[localeData]] - |