diff --git a/test/intl402/Intl/getCanonicalLocales/canonicalized-tags.js b/test/intl402/Intl/getCanonicalLocales/canonicalized-tags.js
new file mode 100644
index 0000000000000000000000000000000000000000..cdfd19444ee20b92442a258e64638068e1859640
--- /dev/null
+++ b/test/intl402/Intl/getCanonicalLocales/canonicalized-tags.js
@@ -0,0 +1,69 @@
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-intl.getcanonicallocales
+description: >
+  Call Intl.getCanonicalLocales function with valid language tags.
+info: |
+  8.2.1 Intl.getCanonicalLocales (locales)
+    1. Let ll be ? CanonicalizeLocaleList(locales).
+    2. Return CreateArrayFromList(ll).
+
+  9.2.1 CanonicalizeLocaleList (locales)
+    ...
+    7. Repeat, while k < len
+      a. Let Pk be ToString(k).
+      b. Let kPresent be ? HasProperty(O, Pk).
+      c. If kPresent is true, then
+        i. Let kValue be ? Get(O, Pk).
+        ...
+        iii. Let tag be ? ToString(kValue).
+        ...
+        v. Let canonicalizedTag be CanonicalizeLanguageTag(tag).
+        vi. If canonicalizedTag is not an element of seen, append canonicalizedTag as the last element of seen.
+      ...
+includes: [testIntl.js]
+---*/
+
+var canonicalizedTags = {
+  "de": "de",
+  "DE-de": "de-DE",
+  "de-DE": "de-DE",
+  "cmn": "cmn",
+  "CMN-hANS": "cmn-Hans",
+  "cmn-hans-cn": "cmn-Hans-CN",
+  "es-419": "es-419",
+  "es-419-u-nu-latn": "es-419-u-nu-latn",
+  "cmn-hans-cn-u-ca-t-ca-x-t-u": "cmn-Hans-CN-t-ca-u-ca-x-t-u",
+  "de-gregory-u-ca-gregory": "de-gregory-u-ca-gregory",
+  "no-nyn": "nn",
+  "i-klingon": "tlh",
+  "sgn-GR": "gss",
+  "ji": "yi",
+  "de-DD": "de-DE",
+  "zh-hak-CN": "hak-CN",
+  "sgn-ils": "ils",
+  "in": "id",
+  "x-foo": "x-foo",
+  "sr-cyrl-ekavsk": "sr-Cyrl-ekavsk",
+  "en-ca-newfound": "en-CA-newfound",
+  "sl-rozaj-biske-1994": "sl-rozaj-biske-1994",
+  "da-u-attr": "da-u-attr",
+  "da-u-attr-co-search": "da-u-attr-co-search",
+};
+
+// make sure the data above is correct
+Object.getOwnPropertyNames(canonicalizedTags).forEach(function (tag) {
+  var canonicalizedTag = canonicalizedTags[tag];
+  assert(
+    isCanonicalizedStructurallyValidLanguageTag(canonicalizedTag),
+    "Test data \"" + canonicalizedTag + "\" is not canonicalized and structurally valid language tag."
+  );
+});
+
+Object.getOwnPropertyNames(canonicalizedTags).forEach(function (tag) {
+  var canonicalLocales = Intl.getCanonicalLocales(tag);
+  assert.sameValue(canonicalLocales.length, 1);
+  assert.sameValue(canonicalLocales[0], canonicalizedTags[tag]);
+});
diff --git a/test/intl402/Intl/getCanonicalLocales/canonicalized-unicode-ext-seq.js b/test/intl402/Intl/getCanonicalLocales/canonicalized-unicode-ext-seq.js
new file mode 100644
index 0000000000000000000000000000000000000000..7f4aa16e8f73a7399c676c3901e37628273c8ac2
--- /dev/null
+++ b/test/intl402/Intl/getCanonicalLocales/canonicalized-unicode-ext-seq.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-intl.getcanonicallocales
+description: >
+  Implementations are allowed to canonicalize extension subtag sequences.
+info: |
+  8.2.1 Intl.getCanonicalLocales (locales)
+    1. Let ll be ? CanonicalizeLocaleList(locales).
+    2. Return CreateArrayFromList(ll).
+
+  9.2.1 CanonicalizeLocaleList (locales)
+    ...
+    7. Repeat, while k < len
+      ...
+      c. If kPresent is true, then
+        ...
+        v. Let canonicalizedTag be CanonicalizeLanguageTag(tag).
+        ...
+
+  6.2.3 CanonicalizeLanguageTag (locale)
+    The specifications for extensions to BCP 47 language tags, such as
+    RFC 6067, may include canonicalization rules for the extension subtag
+    sequences they define that go beyond the canonicalization rules of
+    RFC 5646 section 4.5. Implementations are allowed, but not required,
+    to apply these additional rules.
+---*/
+
+var locale = "it-u-nu-latn-ca-gregory";
+
+// RFC 6067: The canonical order of keywords is in US-ASCII order by key.
+var sorted = "it-u-ca-gregory-nu-latn";
+
+var canonicalLocales = Intl.getCanonicalLocales(locale);
+assert.sameValue(canonicalLocales.length, 1);
+
+var canonicalLocale = canonicalLocales[0];
+assert((canonicalLocale === locale) || (canonicalLocale === sorted));
diff --git a/test/intl402/Intl/getCanonicalLocales/descriptor.js b/test/intl402/Intl/getCanonicalLocales/descriptor.js
new file mode 100644
index 0000000000000000000000000000000000000000..1cf400ce21dae34ab52c532057991fea479d1918
--- /dev/null
+++ b/test/intl402/Intl/getCanonicalLocales/descriptor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-intl.getcanonicallocales
+description: >
+  Intl.getCanonicalLocales property attributes.
+info: |
+  8.2.1 Intl.getCanonicalLocales (locales)
+
+  17 ECMAScript Standard Built-in Objects:
+    Every other data property described in clauses 18 through 26 and in
+    Annex B.2 has the attributes { [[Writable]]: true, [[Enumerable]]: false,
+    [[Configurable]]: true } unless otherwise specified.
+includes: [propertyHelper.js]
+---*/
+
+verifyNotEnumerable(Intl, "getCanonicalLocales");
+verifyWritable(Intl, "getCanonicalLocales");
+verifyConfigurable(Intl, "getCanonicalLocales");
diff --git a/test/intl402/Intl/getCanonicalLocales/elements-not-reordered.js b/test/intl402/Intl/getCanonicalLocales/elements-not-reordered.js
new file mode 100644
index 0000000000000000000000000000000000000000..3c99efa045308b53b7bd228cbce1c54c698ce308
--- /dev/null
+++ b/test/intl402/Intl/getCanonicalLocales/elements-not-reordered.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-intl.getcanonicallocales
+description: >
+  Language tags are not reordered.
+info: |
+  8.2.1 Intl.getCanonicalLocales (locales)
+    1. Let ll be ? CanonicalizeLocaleList(locales).
+    2. Return CreateArrayFromList(ll).
+
+  9.2.1 CanonicalizeLocaleList (locales)
+    ...
+    7. Repeat, while k < len
+      ...
+      c. If kPresent is true, then
+        ...
+        vi. If canonicalizedTag is not an element of seen, append canonicalizedTag as the last element of seen.
+        ...
+---*/
+
+var canonicalLocales = Intl.getCanonicalLocales(["zu", "af"]);
+
+assert.sameValue(canonicalLocales.length, 2);
+assert.sameValue(canonicalLocales[0], "zu");
+assert.sameValue(canonicalLocales[1], "af");
diff --git a/test/intl402/Intl/getCanonicalLocales/error-cases.js b/test/intl402/Intl/getCanonicalLocales/error-cases.js
index 2a68e1e3965d5f6e642aaf246170eb20387eebfd..046d6d425d66d386b0ae2b0fac3f9204b36bcc23 100644
--- a/test/intl402/Intl/getCanonicalLocales/error-cases.js
+++ b/test/intl402/Intl/getCanonicalLocales/error-cases.js
@@ -33,6 +33,7 @@ var typeErrorCases =
   [
     null,
     [null],
+    [undefined],
     [true],
     [NaN],
     [2],
diff --git a/test/intl402/Intl/getCanonicalLocales/invalid-tags.js b/test/intl402/Intl/getCanonicalLocales/invalid-tags.js
new file mode 100644
index 0000000000000000000000000000000000000000..e218f21f1b632c79340b426289ccf5a8e3ca82ef
--- /dev/null
+++ b/test/intl402/Intl/getCanonicalLocales/invalid-tags.js
@@ -0,0 +1,81 @@
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-intl.getcanonicallocales
+description: >
+  Throws a RangeError if the language tag is invalid.
+info: |
+  8.2.1 Intl.getCanonicalLocales (locales)
+    1. Let ll be ? CanonicalizeLocaleList(locales).
+    ...
+
+  9.2.1 CanonicalizeLocaleList (locales)
+    ...
+    7. Repeat, while k < len
+      ...
+      c. If kPresent is true, then
+        ...
+        iv. If IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception.
+        ...
+includes: [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
+  "*", // language range
+  "de-*", // language range
+  "中文", // non-ASCII letters
+  "en-ß", // non-ASCII letters
+  "ıd", // non-ASCII letters
+
+  // underscores in different parts of the language tag
+  "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",
+
+  "en\u0000", // null-terminator sequence
+  " en", // leading whitespace
+  "en ", // trailing whitespace
+  "it-IT-Latn", // country before script tag
+  "de-u", // incomplete Unicode extension sequences
+  "de-u-",
+  "de-u-ca-",
+  "de-u-ca-gregory-",
+  "si-x", // incomplete private-use tags
+  "x-",
+  "x-y-",
+];
+
+// make sure the data above is correct
+for (var i = 0; i < invalidLanguageTags.length; ++i) {
+  var invalidTag = invalidLanguageTags[i];
+  assert(
+    !isCanonicalizedStructurallyValidLanguageTag(invalidTag),
+    "Test data \"" + invalidTag + "\" is a canonicalized and structurally valid language tag."
+  );
+}
+
+for (var i = 0; i < invalidLanguageTags.length; ++i) {
+  var invalidTag = invalidLanguageTags[i];
+  assert.throws(RangeError, function() {
+    Intl.getCanonicalLocales(invalidTag)
+  }, "Language tag: " + invalidTag);
+}
diff --git a/test/intl402/Intl/getCanonicalLocales/locales-is-not-a-string.js b/test/intl402/Intl/getCanonicalLocales/locales-is-not-a-string.js
index 7b100ec26604c3b9522de051aa2ab0321314e10d..ee96616d7f7eacb6dfe15ec6737eb94fbcdc53f7 100644
--- a/test/intl402/Intl/getCanonicalLocales/locales-is-not-a-string.js
+++ b/test/intl402/Intl/getCanonicalLocales/locales-is-not-a-string.js
@@ -19,6 +19,7 @@ function assertArray(l, r) {
 }
 
 assertArray(gCL(), []);
+assertArray(gCL(undefined), []);
 assertArray(gCL(false), []);
 assertArray(gCL(true), []);
 assertArray(gCL(Symbol("foo")), []);
diff --git a/test/intl402/Intl/getCanonicalLocales/preferred-grandfathered.js b/test/intl402/Intl/getCanonicalLocales/preferred-grandfathered.js
new file mode 100644
index 0000000000000000000000000000000000000000..d140289e3bf81ea22d5f78328528ff9712f72f63
--- /dev/null
+++ b/test/intl402/Intl/getCanonicalLocales/preferred-grandfathered.js
@@ -0,0 +1,86 @@
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-intl.getcanonicallocales
+description: >
+  Call Intl.getCanonicalLocales function with grandfathered language tags.
+info: |
+  8.2.1 Intl.getCanonicalLocales (locales)
+    1. Let ll be ? CanonicalizeLocaleList(locales).
+    2. Return CreateArrayFromList(ll).
+
+  9.2.1 CanonicalizeLocaleList (locales)
+    ...
+    7. Repeat, while k < len
+      ...
+      c. If kPresent is true, then
+        ...
+        v. Let canonicalizedTag be CanonicalizeLanguageTag(tag).
+      ...
+
+  6.2.3 CanonicalizeLanguageTag ( locale )
+    The CanonicalizeLanguageTag abstract operation returns the canonical and case-regularized
+    form of the locale argument (which must be a String value that is a structurally valid
+    BCP 47 language tag as verified by the IsStructurallyValidLanguageTag abstract operation).
+    A conforming implementation shall take the steps specified in RFC 5646 section 4.5, or
+    successor, to bring the language tag into canonical form, and to regularize the case of
+    the subtags. Furthermore, a conforming implementation shall not take the steps to bring
+    a language tag into "extlang form", nor shall it reorder variant subtags.
+
+    The specifications for extensions to BCP 47 language tags, such as RFC 6067, may include
+    canonicalization rules for the extension subtag sequences they define that go beyond the
+    canonicalization rules of RFC 5646 section 4.5. Implementations are allowed, but not
+    required, to apply these additional rules.
+
+includes: [testIntl.js]
+---*/
+
+// Generated from http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
+// File-Date: 2017-08-15
+var canonicalizedTags = {
+  // Irregular tags.
+  "en-gb-oed": "en-GB-oxendict",
+  "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",
+  "sgn-be-fr": "sfb",
+  "sgn-be-nl": "vgt",
+  "sgn-ch-de": "sgg",
+
+  // Regular tags.
+  "art-lojban": "jbo",
+  "cel-gaulish": "cel-gaulish",
+  "no-bok": "nb",
+  "no-nyn": "nn",
+  "zh-guoyu": "cmn",
+  "zh-hakka": "hak",
+  "zh-min": "zh-min",
+  "zh-min-nan": "nan",
+  "zh-xiang": "hsn",
+};
+
+// make sure the data above is correct
+Object.getOwnPropertyNames(canonicalizedTags).forEach(function (tag) {
+  var canonicalizedTag = canonicalizedTags[tag];
+  assert(
+    isCanonicalizedStructurallyValidLanguageTag(canonicalizedTag),
+    "Test data \"" + canonicalizedTag + "\" is not canonicalized and structurally valid language tag."
+  );
+});
+
+Object.getOwnPropertyNames(canonicalizedTags).forEach(function (tag) {
+  var canonicalLocales = Intl.getCanonicalLocales(tag);
+  assert.sameValue(canonicalLocales.length, 1);
+  assert.sameValue(canonicalLocales[0], canonicalizedTags[tag]);
+});
diff --git a/test/intl402/Intl/getCanonicalLocales/preferred-variant.js b/test/intl402/Intl/getCanonicalLocales/preferred-variant.js
new file mode 100644
index 0000000000000000000000000000000000000000..bb2c4cd9e0dc1d72e818b3e13c3064bfbf9870e1
--- /dev/null
+++ b/test/intl402/Intl/getCanonicalLocales/preferred-variant.js
@@ -0,0 +1,58 @@
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-intl.getcanonicallocales
+description: >
+  Call Intl.getCanonicalLocales function with grandfathered language tags.
+info: |
+  8.2.1 Intl.getCanonicalLocales (locales)
+    1. Let ll be ? CanonicalizeLocaleList(locales).
+    2. Return CreateArrayFromList(ll).
+
+  9.2.1 CanonicalizeLocaleList (locales)
+    ...
+    7. Repeat, while k < len
+      ...
+      c. If kPresent is true, then
+        ...
+        v. Let canonicalizedTag be CanonicalizeLanguageTag(tag).
+      ...
+
+  6.2.3 CanonicalizeLanguageTag ( locale )
+    The CanonicalizeLanguageTag abstract operation returns the canonical and case-regularized
+    form of the locale argument (which must be a String value that is a structurally valid
+    BCP 47 language tag as verified by the IsStructurallyValidLanguageTag abstract operation).
+    A conforming implementation shall take the steps specified in RFC 5646 section 4.5, or
+    successor, to bring the language tag into canonical form, and to regularize the case of
+    the subtags. Furthermore, a conforming implementation shall not take the steps to bring
+    a language tag into "extlang form", nor shall it reorder variant subtags.
+
+    The specifications for extensions to BCP 47 language tags, such as RFC 6067, may include
+    canonicalization rules for the extension subtag sequences they define that go beyond the
+    canonicalization rules of RFC 5646 section 4.5. Implementations are allowed, but not
+    required, to apply these additional rules.
+
+includes: [testIntl.js]
+---*/
+
+// Generated from http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
+// File-Date: 2017-08-15
+var canonicalizedTags = {
+  "ja-latn-hepburn-heploc": "ja-Latn-alalc97",
+};
+
+// make sure the data above is correct
+Object.getOwnPropertyNames(canonicalizedTags).forEach(function (tag) {
+  var canonicalizedTag = canonicalizedTags[tag];
+  assert(
+    isCanonicalizedStructurallyValidLanguageTag(canonicalizedTag),
+    "Test data \"" + canonicalizedTag + "\" is not canonicalized and structurally valid language tag."
+  );
+});
+
+Object.getOwnPropertyNames(canonicalizedTags).forEach(function (tag) {
+  var canonicalLocales = Intl.getCanonicalLocales(tag);
+  assert.sameValue(canonicalLocales.length, 1);
+  assert.sameValue(canonicalLocales[0], canonicalizedTags[tag]);
+});