--- /dev/null
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + 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));
+}
+
+var originalArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+
+var array = Array.from(originalArray.values());
+shouldBe(array.length, originalArray.length);
+for (var i = 0; i < array.length; ++i) {
+ shouldBe(array[i], originalArray[i]);
+}
+
+function createIterator(callback) {
+ var array = [0,1,2,3,4,5];
+ var iterator = array[Symbol.iterator]();
+ iterator.return = function () {
+ iterator.returned = true;
+ if (callback)
+ return callback(this);
+ return { done: true, value: undefined };
+ };
+ iterator.returned = false;
+ return iterator;
+}
+
+var iterator = createIterator();
+var result = Array.from(iterator);
+shouldBe(result.length, 6);
+for (var i = 0; i < 6; ++i) {
+ shouldBe(result[i], i);
+}
+shouldBe(iterator.returned, false);
+
+// mapFn raises an error.
+var iterator = createIterator();
+shouldThrow(function () {
+ var result = Array.from(iterator, function () {
+ throw new Error('map func');
+ });
+}, "Error: map func");
+shouldBe(iterator.returned, true);
+
+// mapFn raises an error and iterator.return also raises an error.
+var iterator = createIterator(function () {
+ throw new Error('iterator.return');
+});
+
+// An error raised in iterator.return is discarded.
+shouldThrow(function () {
+ var result = Array.from(iterator, function () {
+ throw new Error('map func');
+ });
+}, "Error: map func");
+shouldBe(iterator.returned, true);
+
+// iterable[Symbol.iterator] is not a function.
+shouldThrow(function () {
+ var iterator = [].values();
+ iterator[Symbol.iterator] = {};
+ Array.from(iterator);
+}, "TypeError: Array.from requires that the property of the first argument, items[Symbol.iterator], when exists, be a function");
+
+// iterable[Symbol.iterator] raises an error.
+shouldThrow(function () {
+ var iterable = [];
+ iterable[Symbol.iterator] = function () {
+ throw new Error("iterator");
+ };
+ Array.from(iterable);
+}, "Error: iterator");
+
+// iterable[Symbol.iterator] lookup is only once.
+(function () {
+ var iterable = [0, 1, 2, 3, 4, 5];
+ var count = 0;
+ var iteratorCallCount = 0;
+ Object.defineProperty(iterable, Symbol.iterator, {
+ get() {
+ ++count;
+ return function () {
+ ++iteratorCallCount;
+ return this.values();
+ };
+ }
+ });
+ var generated = Array.from(iterable);
+ shouldBe(generated.length, iterable.length);
+ for (var i = 0; i < iterable.length; ++i) {
+ shouldBe(generated[i], iterable[i]);
+ }
+ shouldBe(count, 1);
+ shouldBe(iteratorCallCount, 1);
+}());
+
+// The Symbol.iterator method of the iterator generated by iterable[Symbol.iterator] is not looked up.
+(function () {
+ var iterable = [0, 1, 2, 3, 4, 5];
+ var count = 0;
+ iterable[Symbol.iterator] = function () {
+ ++count;
+ var iterator = this.values();
+ Object.defineProperty(iterator, Symbol.iterator, {
+ get() {
+ throw new Error('iterator[@@iterator] is touched');
+ }
+ });
+ return iterator;
+ };
+ var generated = Array.from(iterable);
+ shouldBe(generated.length, iterable.length);
+ for (var i = 0; i < iterable.length; ++i) {
+ shouldBe(generated[i], iterable[i]);
+ }
+ shouldBe(count, 1);
+}());