--- /dev/null
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error("bad value: " + String(actual));
+}
+
+function shouldThrow(func, message) {
+ var error = null;
+ try {
+ func();
+ } catch (e) {
+ error = e;
+ }
+ if (!error)
+ throw new Error("not thrown.");
+ if (String(error) !== message)
+ throw new Error("bad error: " + String(error));
+}
+
+function toCodePoints(string) {
+ var result = [];
+ for (var codePoint of string) {
+ result.push(codePoint.codePointAt(0));
+ }
+ return result;
+}
+
+shouldBe(String.fromCodePoint(), "");
+shouldBe(String.fromCodePoint(0), "\0");
+shouldBe(String.fromCodePoint(0, 0), "\0\0");
+
+var tests = [
+ "",
+ "Hello",
+ "Cocoa",
+ "Cappuccino Cocoa",
+ "日本語",
+ "マルチバイト",
+ "吉野屋",
+ "𠮷野家", // Contain a surrogate pair.
+];
+
+for (var test of tests) {
+ shouldBe(String.fromCodePoint.apply(String, toCodePoints(test)), test);
+}
+
+function passThrough(codePoint) {
+ var string = String.fromCodePoint(codePoint);
+ shouldBe(string.codePointAt(0), codePoint);
+}
+
+var numberTests = [
+ [ 0x10FFFF, "\uDBFF\uDFFF" ],
+ [ 0x10FFFE, "\uDBFF\uDFFE" ],
+ [ 0xFFFF, "\uFFFF" ],
+ [ 0x10000, "\uD800\uDC00" ],
+ [ 0x10001, "\uD800\uDC01" ],
+ [ -0.0, "\u0000" ],
+ [ 0xD800, "\uD800" ],
+ [ 0xDC00, "\uDC00" ],
+];
+
+for (var test of numberTests) {
+ shouldBe(String.fromCodePoint(test[0]), test[1]);
+}
+
+shouldBe(String.fromCodePoint(0xD800, 0xDC00).codePointAt(0), 0x10000);
+
+// Non-character code points.
+for (var i = 0; i < 17; ++i) {
+ var plane = 0x10000 * i;
+ passThrough(plane + 0xFFFE);
+ passThrough(plane + 0xFFFF);
+}
+
+for (var start = 0xFDD0; start <= 0xFDEF; ++start) {
+ passThrough(start);
+}
+
+var invalidTests = [
+ -1,
+ 1.2,
+ 1.5,
+ 30.01,
+ -11.0,
+ NaN,
+ Number.Infinity,
+ -Number.Infinity,
+ 0x10FFFF + 1,
+ 0x7FFFFFFF,
+ 0x7FFFFFFF + 1,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF + 1,
+ 0x100000000 + 32, // String.fromCharCode(0x100000000 + 32) produces a space, but String.fromCodePoint should throw an error.
+ "Hello",
+ undefined,
+ {},
+];
+
+for (var test of invalidTests) {
+ shouldThrow(function () {
+ String.fromCodePoint(test);
+ }, "RangeError: Arguments contain a value that is out of range of code points");
+}
+
+// toNumber causes errors.
+shouldThrow(function () {
+ String.fromCodePoint(Symbol.iterator);
+}, "TypeError: Type error")
+
+var toNumberObject = {
+ valueOf() {
+ throw new Error("valueOf is called");
+ }
+};
+
+shouldThrow(function () {
+ String.fromCodePoint(toNumberObject);
+}, "Error: valueOf is called")
+
+shouldThrow(function () {
+ String.fromCodePoint(Symbol.iterator, toNumberObject);
+}, "TypeError: Type error")
+
+var convertAndPassTests = [
+ [ null, "\0" ],
+ [ [], "\0" ],
+ [ "0x41", "A" ],
+ [ "", "\0" ],
+ [ true, "\u0001" ],
+ [ false, "\u0000" ],
+];
+
+for (var test of convertAndPassTests) {
+ shouldBe(String.fromCodePoint(test[0]), test[1]);
+}