// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. import 'dart:math' show Random, pow; import 'package:collection/collection.dart'; import 'package:test/test.dart'; void main() { group('Iterable', () { group('of any', () { group('.whereNot', () { test('empty', () { expect(iterable([]).whereNot(unreachable), isEmpty); }); test('none', () { expect(iterable([1, 3, 5]).whereNot((e) => e.isOdd), isEmpty); }); test('all', () { expect(iterable([1, 3, 5]).whereNot((e) => e.isEven), iterable([1, 3, 5])); }); test('some', () { expect(iterable([1, 2, 3, 4]).whereNot((e) => e.isEven), iterable([1, 3])); }); }); group('.sorted', () { test('empty', () { expect(iterable([]).sorted(unreachable), []); }); test('singleton', () { expect(iterable([1]).sorted(unreachable), [1]); }); test('multiple', () { expect(iterable([5, 2, 4, 3, 1]).sorted(cmpInt), [1, 2, 3, 4, 5]); }); }); group('.shuffled', () { test('empty', () { expect(iterable([]).shuffled(), []); }); test('singleton', () { expect(iterable([1]).shuffled(), [1]); }); test('multiple', () { var input = iterable([1, 2, 3, 4, 5]); var copy = [...input]; var shuffled = input.shuffled(); expect(const UnorderedIterableEquality().equals(input, shuffled), isTrue); // Check that the original list isn't touched expect(input, copy); }); }); group('.sortedBy', () { test('empty', () { expect(iterable([]).sortedBy(unreachable), []); }); test('singleton', () { expect(iterable([1]).sortedBy(unreachable), [1]); }); test('multiple', () { expect(iterable([3, 20, 100]).sortedBy(toString), [100, 20, 3]); }); }); group('.sortedByCompare', () { test('empty', () { expect( iterable([]).sortedByCompare(unreachable, unreachable), []); }); test('singleton', () { expect(iterable([2]).sortedByCompare(unreachable, unreachable), [2]); }); test('multiple', () { expect( iterable([30, 2, 100]) .sortedByCompare(toString, cmpParseInverse), [100, 30, 2]); }); }); group('isSorted', () { test('empty', () { expect(iterable([]).isSorted(unreachable), true); }); test('single', () { expect(iterable([1]).isSorted(unreachable), true); }); test('same', () { expect(iterable([1, 1, 1, 1]).isSorted(cmpInt), true); expect(iterable([1, 0, 1, 0]).isSorted(cmpMod(2)), true); }); test('multiple', () { expect(iterable([1, 2, 3, 4]).isSorted(cmpInt), true); expect(iterable([4, 3, 2, 1]).isSorted(cmpIntInverse), true); expect(iterable([1, 2, 3, 0]).isSorted(cmpInt), false); expect(iterable([4, 1, 2, 3]).isSorted(cmpInt), false); expect(iterable([4, 3, 2, 1]).isSorted(cmpInt), false); }); }); group('.isSortedBy', () { test('empty', () { expect(iterable([]).isSortedBy(unreachable), true); }); test('single', () { expect(iterable([1]).isSortedBy(toString), true); }); test('same', () { expect(iterable([1, 1, 1, 1]).isSortedBy(toString), true); }); test('multiple', () { expect(iterable([1, 2, 3, 4]).isSortedBy(toString), true); expect(iterable([4, 3, 2, 1]).isSortedBy(toString), false); expect(iterable([1000, 200, 30, 4]).isSortedBy(toString), true); expect(iterable([1, 2, 3, 0]).isSortedBy(toString), false); expect(iterable([4, 1, 2, 3]).isSortedBy(toString), false); expect(iterable([4, 3, 2, 1]).isSortedBy(toString), false); }); }); group('.isSortedByCompare', () { test('empty', () { expect(iterable([]).isSortedByCompare(unreachable, unreachable), true); }); test('single', () { expect(iterable([1]).isSortedByCompare(toString, unreachable), true); }); test('same', () { expect(iterable([1, 1, 1, 1]).isSortedByCompare(toString, cmpParse), true); }); test('multiple', () { expect(iterable([1, 2, 3, 4]).isSortedByCompare(toString, cmpParse), true); expect( iterable([4, 3, 2, 1]) .isSortedByCompare(toString, cmpParseInverse), true); expect( iterable([1000, 200, 30, 4]) .isSortedByCompare(toString, cmpString), true); expect(iterable([1, 2, 3, 0]).isSortedByCompare(toString, cmpParse), false); expect(iterable([4, 1, 2, 3]).isSortedByCompare(toString, cmpParse), false); expect(iterable([4, 3, 2, 1]).isSortedByCompare(toString, cmpParse), false); }); }); group('.forEachIndexed', () { test('empty', () { iterable([]).forEachIndexed(unreachable); }); test('single', () { var log = []; iterable(['a']).forEachIndexed((i, s) { log ..add(i) ..add(s); }); expect(log, [0, 'a']); }); test('multiple', () { var log = []; iterable(['a', 'b', 'c']).forEachIndexed((i, s) { log ..add(i) ..add(s); }); expect(log, [0, 'a', 1, 'b', 2, 'c']); }); }); group('.forEachWhile', () { test('empty', () { iterable([]).forEachWhile(unreachable); }); test('single true', () { var log = []; iterable(['a']).forEachWhile((s) { log.add(s); return true; }); expect(log, ['a']); }); test('single false', () { var log = []; iterable(['a']).forEachWhile((s) { log.add(s); return false; }); expect(log, ['a']); }); test('multiple one', () { var log = []; iterable(['a', 'b', 'c']).forEachWhile((s) { log.add(s); return false; }); expect(log, ['a']); }); test('multiple all', () { var log = []; iterable(['a', 'b', 'c']).forEachWhile((s) { log.add(s); return true; }); expect(log, ['a', 'b', 'c']); }); test('multiple some', () { var log = []; iterable(['a', 'b', 'c']).forEachWhile((s) { log.add(s); return s != 'b'; }); expect(log, ['a', 'b']); }); }); group('.forEachIndexedWhile', () { test('empty', () { iterable([]).forEachIndexedWhile(unreachable); }); test('single true', () { var log = []; iterable(['a']).forEachIndexedWhile((i, s) { log ..add(i) ..add(s); return true; }); expect(log, [0, 'a']); }); test('single false', () { var log = []; iterable(['a']).forEachIndexedWhile((i, s) { log ..add(i) ..add(s); return false; }); expect(log, [0, 'a']); }); test('multiple one', () { var log = []; iterable(['a', 'b', 'c']).forEachIndexedWhile((i, s) { log ..add(i) ..add(s); return false; }); expect(log, [0, 'a']); }); test('multiple all', () { var log = []; iterable(['a', 'b', 'c']).forEachIndexedWhile((i, s) { log ..add(i) ..add(s); return true; }); expect(log, [0, 'a', 1, 'b', 2, 'c']); }); test('multiple some', () { var log = []; iterable(['a', 'b', 'c']).forEachIndexedWhile((i, s) { log ..add(i) ..add(s); return s != 'b'; }); expect(log, [0, 'a', 1, 'b']); }); }); group('.mapIndexed', () { test('empty', () { expect(iterable([]).mapIndexed(unreachable), isEmpty); }); test('multiple', () { expect(iterable(['a', 'b']).mapIndexed((i, s) => [i, s]), [ [0, 'a'], [1, 'b'] ]); }); }); group('.whereIndexed', () { test('empty', () { expect(iterable([]).whereIndexed(unreachable), isEmpty); }); test('none', () { var trace = []; int log(int a, int b) { trace ..add(a) ..add(b); return b; } expect( iterable([1, 3, 5, 7]) .whereIndexed((i, x) => log(i, x).isEven), isEmpty); expect(trace, [0, 1, 1, 3, 2, 5, 3, 7]); }); test('all', () { expect(iterable([1, 3, 5, 7]).whereIndexed((i, x) => x.isOdd), [1, 3, 5, 7]); }); test('some', () { expect(iterable([1, 3, 5, 7]).whereIndexed((i, x) => i.isOdd), [3, 7]); }); }); group('.whereNotIndexed', () { test('empty', () { expect(iterable([]).whereNotIndexed(unreachable), isEmpty); }); test('none', () { var trace = []; int log(int a, int b) { trace ..add(a) ..add(b); return b; } expect( iterable([1, 3, 5, 7]) .whereNotIndexed((i, x) => log(i, x).isOdd), isEmpty); expect(trace, [0, 1, 1, 3, 2, 5, 3, 7]); }); test('all', () { expect( iterable([1, 3, 5, 7]).whereNotIndexed((i, x) => x.isEven), [1, 3, 5, 7]); }); test('some', () { expect(iterable([1, 3, 5, 7]).whereNotIndexed((i, x) => i.isOdd), [1, 5]); }); }); group('.expandIndexed', () { test('empty', () { expect(iterable([]).expandIndexed(unreachable), isEmpty); }); test('empty result', () { expect(iterable(['a', 'b']).expandIndexed((i, v) => []), isEmpty); }); test('larger result', () { expect(iterable(['a', 'b']).expandIndexed((i, v) => ['$i', v]), ['0', 'a', '1', 'b']); }); test('varying result', () { expect( iterable(['a', 'b']) .expandIndexed((i, v) => i.isOdd ? ['$i', v] : []), ['1', 'b']); }); }); group('.reduceIndexed', () { test('empty', () { expect(() => iterable([]).reduceIndexed((i, a, b) => a), throwsStateError); }); test('single', () { expect(iterable([1]).reduceIndexed(unreachable), 1); }); test('multiple', () { expect( iterable([1, 4, 2]) .reduceIndexed((i, p, v) => p + (pow(i + 1, v) as int)), 1 + 16 + 9); }); }); group('.foldIndexed', () { test('empty', () { expect(iterable([]).foldIndexed(0, unreachable), 0); }); test('single', () { expect( iterable([1]).foldIndexed('x', (i, a, b) => '$a:$i:$b'), 'x:0:1'); }); test('mulitple', () { expect(iterable([1, 3, 9]).foldIndexed('x', (i, a, b) => '$a:$i:$b'), 'x:0:1:1:3:2:9'); }); }); group('.firstWhereOrNull', () { test('empty', () { expect(iterable([]).firstWhereOrNull(unreachable), null); }); test('none', () { expect(iterable([1, 3, 7]).firstWhereOrNull(isEven), null); }); test('single', () { expect(iterable([0, 1, 2]).firstWhereOrNull(isOdd), 1); }); test('first of multiple', () { expect(iterable([0, 1, 3]).firstWhereOrNull(isOdd), 1); }); }); group('.firstWhereIndexedOrNull', () { test('empty', () { expect(iterable([]).firstWhereIndexedOrNull(unreachable), null); }); test('none', () { expect( iterable([1, 3, 7]).firstWhereIndexedOrNull((i, x) => x.isEven), null); expect(iterable([1, 3, 7]).firstWhereIndexedOrNull((i, x) => i < 0), null); }); test('single', () { expect(iterable([0, 3, 6]).firstWhereIndexedOrNull((i, x) => x.isOdd), 3); expect( iterable([0, 3, 6]).firstWhereIndexedOrNull((i, x) => i == 1), 3); }); test('first of multiple', () { expect(iterable([0, 3, 7]).firstWhereIndexedOrNull((i, x) => x.isOdd), 3); expect( iterable([0, 3, 7]).firstWhereIndexedOrNull((i, x) => i.isEven), 0); }); }); group('.firstOrNull', () { test('empty', () { expect(iterable([]).firstOrNull, null); }); test('single', () { expect(iterable([1]).firstOrNull, 1); }); test('first of multiple', () { expect(iterable([1, 3, 5]).firstOrNull, 1); }); }); group('.lastWhereOrNull', () { test('empty', () { expect(iterable([]).lastWhereOrNull(unreachable), null); }); test('none', () { expect(iterable([1, 3, 7]).lastWhereOrNull(isEven), null); }); test('single', () { expect(iterable([0, 1, 2]).lastWhereOrNull(isOdd), 1); }); test('last of multiple', () { expect(iterable([0, 1, 3]).lastWhereOrNull(isOdd), 3); }); }); group('.lastWhereIndexedOrNull', () { test('empty', () { expect(iterable([]).lastWhereIndexedOrNull(unreachable), null); }); test('none', () { expect(iterable([1, 3, 7]).lastWhereIndexedOrNull((i, x) => x.isEven), null); expect(iterable([1, 3, 7]).lastWhereIndexedOrNull((i, x) => i < 0), null); }); test('single', () { expect( iterable([0, 3, 6]).lastWhereIndexedOrNull((i, x) => x.isOdd), 3); expect( iterable([0, 3, 6]).lastWhereIndexedOrNull((i, x) => i == 1), 3); }); test('last of multiple', () { expect( iterable([0, 3, 7]).lastWhereIndexedOrNull((i, x) => x.isOdd), 7); expect(iterable([0, 3, 7]).lastWhereIndexedOrNull((i, x) => i.isEven), 7); }); }); group('.lastOrNull', () { test('empty', () { expect(iterable([]).lastOrNull, null); }); test('single', () { expect(iterable([1]).lastOrNull, 1); }); test('last of multiple', () { expect(iterable([1, 3, 5]).lastOrNull, 5); }); }); group('.singleWhereOrNull', () { test('empty', () { expect(iterable([]).singleWhereOrNull(unreachable), null); }); test('none', () { expect(iterable([1, 3, 7]).singleWhereOrNull(isEven), null); }); test('single', () { expect(iterable([0, 1, 2]).singleWhereOrNull(isOdd), 1); }); test('multiple', () { expect(iterable([0, 1, 3]).singleWhereOrNull(isOdd), null); }); }); group('.singleWhereIndexedOrNull', () { test('empty', () { expect(iterable([]).singleWhereIndexedOrNull(unreachable), null); }); test('none', () { expect( iterable([1, 3, 7]).singleWhereIndexedOrNull((i, x) => x.isEven), null); expect(iterable([1, 3, 7]).singleWhereIndexedOrNull((i, x) => i < 0), null); }); test('single', () { expect( iterable([0, 3, 6]).singleWhereIndexedOrNull((i, x) => x.isOdd), 3); expect(iterable([0, 3, 6]).singleWhereIndexedOrNull((i, x) => i == 1), 3); }); test('multiple', () { expect( iterable([0, 3, 7]).singleWhereIndexedOrNull((i, x) => x.isOdd), null); expect( iterable([0, 3, 7]).singleWhereIndexedOrNull((i, x) => i.isEven), null); }); }); group('.singleOrNull', () { test('empty', () { expect(iterable([]).singleOrNull, null); }); test('single', () { expect(iterable([1]).singleOrNull, 1); }); test('multiple', () { expect(iterable([1, 3, 5]).singleOrNull, null); }); }); group('.lastBy', () { test('empty', () { expect(iterable([]).lastBy((dynamic _) {}), {}); }); test('single', () { expect(iterable([1]).lastBy(toString), { '1': 1, }); }); test('multiple', () { expect( iterable([1, 2, 3, 4, 5]).lastBy((x) => x.isEven), { false: 5, true: 4, }, ); }); }); group('.groupFoldBy', () { test('empty', () { expect(iterable([]).groupFoldBy(unreachable, unreachable), {}); }); test('single', () { expect(iterable([1]).groupFoldBy(toString, (p, v) => [p, v]), { '1': [null, 1] }); }); test('multiple', () { expect( iterable([1, 2, 3, 4, 5]).groupFoldBy( (x) => x.isEven, (p, v) => p == null ? '$v' : '$p:$v'), {true: '2:4', false: '1:3:5'}); }); }); group('.groupSetsBy', () { test('empty', () { expect(iterable([]).groupSetsBy(unreachable), {}); }); test('multiple same', () { expect(iterable([1, 1]).groupSetsBy(toString), { '1': {1} }); }); test('multiple', () { expect(iterable([1, 2, 3, 4, 5, 1]).groupSetsBy((x) => x % 3), { 1: {1, 4}, 2: {2, 5}, 0: {3} }); }); }); group('.groupListsBy', () { test('empty', () { expect(iterable([]).groupListsBy(unreachable), {}); }); test('multiple saame', () { expect(iterable([1, 1]).groupListsBy(toString), { '1': [1, 1] }); }); test('multiple', () { expect(iterable([1, 2, 3, 4, 5, 1]).groupListsBy((x) => x % 3), { 1: [1, 4, 1], 2: [2, 5], 0: [3] }); }); }); group('.splitBefore', () { test('empty', () { expect(iterable([]).splitBefore(unreachable), []); }); test('single', () { expect(iterable([1]).splitBefore(unreachable), [ [1] ]); }); test('no split', () { var trace = []; bool log(int x) { trace.add(x); return false; } expect(iterable([1, 2, 3]).splitBefore(log), [ [1, 2, 3] ]); expect(trace, [2, 3]); }); test('all splits', () { expect(iterable([1, 2, 3]).splitBefore((x) => true), [ [1], [2], [3] ]); }); test('some splits', () { expect(iterable([1, 2, 3]).splitBefore((x) => x.isEven), [ [1], [2, 3] ]); }); }); group('.splitBeforeIndexed', () { test('empty', () { expect(iterable([]).splitBeforeIndexed(unreachable), []); }); test('single', () { expect(iterable([1]).splitBeforeIndexed(unreachable), [ [1] ]); }); test('no split', () { var trace = []; bool log(int i, int x) { trace ..add('$i') ..add(x); return false; } expect(iterable([1, 2, 3]).splitBeforeIndexed(log), [ [1, 2, 3] ]); expect(trace, ['1', 2, '2', 3]); }); test('all splits', () { expect(iterable([1, 2, 3]).splitBeforeIndexed((i, x) => true), [ [1], [2], [3] ]); }); test('some splits', () { expect(iterable([1, 2, 3]).splitBeforeIndexed((i, x) => x.isEven), [ [1], [2, 3] ]); expect(iterable([1, 2, 3]).splitBeforeIndexed((i, x) => i.isEven), [ [1, 2], [3] ]); }); }); group('.splitAfter', () { test('empty', () { expect(iterable([]).splitAfter(unreachable), []); }); test('single', () { expect(iterable([1]).splitAfter((x) => false), [ [1] ]); expect(iterable([1]).splitAfter((x) => true), [ [1] ]); }); test('no split', () { var trace = []; bool log(int x) { trace.add(x); return false; } expect(iterable([1, 2, 3]).splitAfter(log), [ [1, 2, 3] ]); expect(trace, [1, 2, 3]); }); test('all splits', () { expect(iterable([1, 2, 3]).splitAfter((x) => true), [ [1], [2], [3] ]); }); test('some splits', () { expect(iterable([1, 2, 3]).splitAfter((x) => x.isEven), [ [1, 2], [3] ]); }); }); group('.splitAfterIndexed', () { test('empty', () { expect(iterable([]).splitAfterIndexed(unreachable), []); }); test('single', () { expect(iterable([1]).splitAfterIndexed((i, x) => true), [ [1] ]); expect(iterable([1]).splitAfterIndexed((i, x) => false), [ [1] ]); }); test('no split', () { var trace = []; bool log(int i, int x) { trace ..add('$i') ..add(x); return false; } expect(iterable([1, 2, 3]).splitAfterIndexed(log), [ [1, 2, 3] ]); expect(trace, ['0', 1, '1', 2, '2', 3]); }); test('all splits', () { expect(iterable([1, 2, 3]).splitAfterIndexed((i, x) => true), [ [1], [2], [3] ]); }); test('some splits', () { expect(iterable([1, 2, 3]).splitAfterIndexed((i, x) => x.isEven), [ [1, 2], [3] ]); expect(iterable([1, 2, 3]).splitAfterIndexed((i, x) => i.isEven), [ [1], [2, 3] ]); }); }); group('.splitBetween', () { test('empty', () { expect(iterable([]).splitBetween(unreachable), []); }); test('single', () { expect(iterable([1]).splitBetween(unreachable), [ [1] ]); }); test('no split', () { var trace = []; bool log(int x, int y) { trace.add([x, y]); return false; } expect(iterable([1, 2, 3]).splitBetween(log), [ [1, 2, 3] ]); expect(trace, [ [1, 2], [2, 3] ]); }); test('all splits', () { expect(iterable([1, 2, 3]).splitBetween((x, y) => true), [ [1], [2], [3] ]); }); test('some splits', () { expect(iterable([1, 2, 4]).splitBetween((x, y) => (x ^ y).isEven), [ [1, 2], [4] ]); }); }); group('.splitBetweenIndexed', () { test('empty', () { expect(iterable([]).splitBetweenIndexed(unreachable), []); }); test('single', () { expect(iterable([1]).splitBetweenIndexed(unreachable), [ [1] ]); }); test('no split', () { var trace = []; bool log(int i, int x, int y) { trace.add([i, x, y]); return false; } expect(iterable([1, 2, 3]).splitBetweenIndexed(log), [ [1, 2, 3] ]); expect(trace, [ [1, 1, 2], [2, 2, 3] ]); }); test('all splits', () { expect(iterable([1, 2, 3]).splitBetweenIndexed((i, x, y) => true), [ [1], [2], [3] ]); }); test('some splits', () { expect( iterable([1, 2, 4]) .splitBetweenIndexed((i, x, y) => (x ^ y).isEven), [ [1, 2], [4] ]); expect( iterable([1, 2, 4]) .splitBetweenIndexed((i, x, y) => (i ^ y).isEven), [ [1, 2], [4] ]); }); }); group('none', () { test('empty', () { expect(iterable([]).none(unreachable), true); }); test('single', () { expect(iterable([1]).none(isEven), true); expect(iterable([1]).none(isOdd), false); }); test('multiple', () { expect(iterable([1, 3, 5, 7, 9, 11]).none(isEven), true); expect(iterable([1, 3, 5, 7, 9, 10]).none(isEven), false); expect(iterable([0, 3, 5, 7, 9, 11]).none(isEven), false); expect(iterable([0, 2, 4, 6, 8, 10]).none(isEven), false); }); }); }); group('of nullable', () { group('.whereNotNull', () { test('empty', () { expect( iterable([]) .whereNotNull(), // ignore: deprecated_member_use_from_same_package isEmpty); }); test('single', () { expect( iterable([ null ]).whereNotNull(), // ignore: deprecated_member_use_from_same_package isEmpty); expect( iterable([ 1 ]).whereNotNull(), // ignore: deprecated_member_use_from_same_package [1]); }); test('multiple', () { expect( iterable([ 1, 3, 5 ]).whereNotNull(), // ignore: deprecated_member_use_from_same_package [1, 3, 5]); expect( iterable([ null, null, null ]).whereNotNull(), // ignore: deprecated_member_use_from_same_package isEmpty); expect( iterable([ 1, null, 3, null, 5 ]).whereNotNull(), // ignore: deprecated_member_use_from_same_package [1, 3, 5]); }); }); }); group('of number', () { group('.sum', () { test('empty', () { expect(iterable([]).sum, same(0)); expect(iterable([]).sum, same(0.0)); expect(iterable([]).sum, same(0)); }); test('single', () { expect(iterable([1]).sum, same(1)); expect(iterable([1.2]).sum, same(1.2)); expect(iterable([1]).sum, same(1)); expect(iterable([1.2]).sum, same(1.2)); }); test('multiple', () { expect(iterable([1, 2, 4]).sum, 7); expect(iterable([1.2, 3.5]).sum, 4.7); expect(iterable([1, 3, 5]).sum, same(9)); expect(iterable([1.2, 3.5]).sum, 4.7); expect(iterable([1.2, 2, 3.5]).sum, 6.7); }); }); group('average', () { test('empty', () { expect(() => iterable([]).average, throwsStateError); expect(() => iterable([]).average, throwsStateError); expect(() => iterable([]).average, throwsStateError); }); test('single', () { expect(iterable([4]).average, same(4.0)); expect(iterable([3.5]).average, 3.5); expect(iterable([4]).average, same(4.0)); expect(iterable([3.5]).average, 3.5); }); test('multiple', () { expect(iterable([1, 3, 5]).average, same(3.0)); expect(iterable([1, 3, 5, 9]).average, 4.5); expect(iterable([1.0, 3.0, 5.0, 9.0]).average, 4.5); expect(iterable([1, 3, 5, 9]).average, 4.5); }); }); group('.min', () { test('empty', () { expect(() => iterable([]).min, throwsStateError); expect(() => iterable([]).min, throwsStateError); expect(() => iterable([]).min, throwsStateError); }); test('single', () { expect(iterable([1]).min, 1); expect(iterable([1.0]).min, 1.0); expect(iterable([1.0]).min, 1.0); }); test('multiple', () { expect(iterable([3, 1, 2]).min, 1); expect(iterable([3.0, 1.0, 2.5]).min, 1.0); expect(iterable([3, 1, 2.5]).min, 1.0); }); test('nan', () { expect(iterable([3.0, 1.0, double.nan]).min, isNaN); expect(iterable([3.0, 1, double.nan]).min, isNaN); }); }); group('.minOrNull', () { test('empty', () { expect(iterable([]).minOrNull, null); expect(iterable([]).minOrNull, null); expect(iterable([]).minOrNull, null); }); test('single', () { expect(iterable([1]).minOrNull, 1); expect(iterable([1.0]).minOrNull, 1.0); expect(iterable([1.0]).minOrNull, 1.0); }); test('multiple', () { expect(iterable([3, 1, 2]).minOrNull, 1); expect(iterable([3.0, 1.0, 2.5]).minOrNull, 1.0); expect(iterable([3, 1, 2.5]).minOrNull, 1.0); }); test('nan', () { expect(iterable([3.0, 1.0, double.nan]).minOrNull, isNaN); expect(iterable([3.0, 1, double.nan]).minOrNull, isNaN); }); }); group('.max', () { test('empty', () { expect(() => iterable([]).max, throwsStateError); expect(() => iterable([]).max, throwsStateError); expect(() => iterable([]).max, throwsStateError); }); test('single', () { expect(iterable([1]).max, 1); expect(iterable([1.0]).max, 1.0); expect(iterable([1.0]).max, 1.0); }); test('multiple', () { expect(iterable([3, 1, 2]).max, 3); expect(iterable([3.0, 1.0, 2.5]).max, 3.0); expect(iterable([3, 1, 2.5]).max, 3); }); test('nan', () { expect(iterable([3.0, 1.0, double.nan]).max, isNaN); expect(iterable([3.0, 1, double.nan]).max, isNaN); }); }); group('.maxOrNull', () { test('empty', () { expect(iterable([]).maxOrNull, null); expect(iterable([]).maxOrNull, null); expect(iterable([]).maxOrNull, null); }); test('single', () { expect(iterable([1]).maxOrNull, 1); expect(iterable([1.0]).maxOrNull, 1.0); expect(iterable([1.0]).maxOrNull, 1.0); }); test('multiple', () { expect(iterable([3, 1, 2]).maxOrNull, 3); expect(iterable([3.0, 1.0, 2.5]).maxOrNull, 3.0); expect(iterable([3, 1, 2.5]).maxOrNull, 3); }); test('nan', () { expect(iterable([3.0, 1.0, double.nan]).maxOrNull, isNaN); expect(iterable([3.0, 1, double.nan]).maxOrNull, isNaN); }); }); }); group('of iterable', () { group('.flattened', () { var empty = iterable([]); test('empty', () { expect(iterable(>[]).flattened, []); }); test('multiple empty', () { expect(iterable([empty, empty, empty]).flattened, []); }); test('single value', () { expect( iterable([ iterable([1]) ]).flattened, [1]); }); test('multiple', () { expect( iterable([ iterable([1, 2]), empty, iterable([3, 4]) ]).flattened, [1, 2, 3, 4]); }); }); group('.flattenedToList', () { var empty = iterable([]); test('empty', () { expect(iterable(>[]).flattenedToList, []); }); test('multiple empty', () { expect(iterable([empty, empty, empty]).flattenedToList, []); }); test('single value', () { expect( iterable([ iterable([1]) ]).flattenedToList, [1]); }); test('multiple', () { expect( iterable([ iterable([1, 2]), empty, iterable([3, 4]) ]).flattenedToList, [1, 2, 3, 4]); }); }); group('.flattenedToSet', () { var empty = iterable([]); test('empty', () { expect(iterable(>[]).flattenedToSet, {}); }); test('multiple empty', () { expect(iterable([empty, empty, empty]).flattenedToSet, {}); }); test('single value', () { expect( iterable([ iterable([1]) ]).flattenedToSet, {1}); }); test('multiple', () { expect( iterable([ iterable([1, 2]), empty, iterable([3, 4]) ]).flattenedToSet, {1, 2, 3, 4}); expect( iterable([ iterable([1, 2, 3]), empty, iterable([2, 3, 4]) ]).flattenedToSet, {1, 2, 3, 4}); }); }); }); group('of comparable', () { group('.min', () { test('empty', () { expect(() => iterable([]).min, throwsStateError); }); test('single', () { expect(iterable(['a']).min, 'a'); }); test('multiple', () { expect(iterable(['c', 'a', 'b']).min, 'a'); }); }); group('.minOrNull', () { test('empty', () { expect(iterable([]).minOrNull, null); }); test('single', () { expect(iterable(['a']).minOrNull, 'a'); }); test('multiple', () { expect(iterable(['c', 'a', 'b']).minOrNull, 'a'); }); }); group('.max', () { test('empty', () { expect(() => iterable([]).max, throwsStateError); }); test('single', () { expect(iterable(['a']).max, 'a'); }); test('multiple', () { expect(iterable(['b', 'c', 'a']).max, 'c'); }); }); group('.maxOrNull', () { test('empty', () { expect(iterable([]).maxOrNull, null); }); test('single', () { expect(iterable(['a']).maxOrNull, 'a'); }); test('multiple', () { expect(iterable(['b', 'c', 'a']).maxOrNull, 'c'); }); }); }); group('.sorted', () { test('empty', () { expect(iterable([]).sorted(unreachable), []); expect(iterable([]).sorted(), []); }); test('singleton', () { expect(iterable(['a']).sorted(unreachable), ['a']); expect(iterable(['a']).sorted(), ['a']); }); test('multiple', () { expect(iterable(['5', '2', '4', '3', '1']).sorted(cmpParse), ['1', '2', '3', '4', '5']); expect( iterable(['5', '2', '4', '3', '1']).sorted(cmpParseInverse), ['5', '4', '3', '2', '1']); expect(iterable(['5', '2', '4', '3', '1']).sorted(), ['1', '2', '3', '4', '5']); // Large enough to trigger quicksort. var i256 = Iterable.generate(256, (i) => i ^ 0x55); var sorted256 = [...i256]..sort(); expect(i256.sorted(cmpInt), sorted256); }); }); group('.isSorted', () { test('empty', () { expect(iterable([]).isSorted(unreachable), true); expect(iterable([]).isSorted(), true); }); test('single', () { expect(iterable(['1']).isSorted(unreachable), true); expect(iterable(['1']).isSorted(), true); }); test('same', () { expect(iterable(['1', '1', '1', '1']).isSorted(cmpParse), true); expect(iterable(['1', '2', '0', '3']).isSorted(cmpStringLength), true); expect(iterable(['1', '1', '1', '1']).isSorted(), true); }); test('multiple', () { expect(iterable(['1', '2', '3', '4']).isSorted(cmpParse), true); expect(iterable(['1', '2', '3', '4']).isSorted(), true); expect(iterable(['4', '3', '2', '1']).isSorted(cmpParseInverse), true); expect(iterable(['1', '2', '3', '0']).isSorted(cmpParse), false); expect(iterable(['1', '2', '3', '0']).isSorted(), false); expect(iterable(['4', '1', '2', '3']).isSorted(cmpParse), false); expect(iterable(['4', '1', '2', '3']).isSorted(), false); expect(iterable(['4', '3', '2', '1']).isSorted(cmpParse), false); expect(iterable(['4', '3', '2', '1']).isSorted(), false); }); }); group('.sample', () { test('errors', () { expect(() => iterable([1]).sample(-1), throwsRangeError); }); test('empty', () { var empty = iterable([]); expect(empty.sample(0), []); expect(empty.sample(5), []); }); test('single', () { var single = iterable([1]); expect(single.sample(0), []); expect(single.sample(1), [1]); expect(single.sample(5), [1]); }); test('multiple', () { var multiple = iterable([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); expect(multiple.sample(0), []); var one = multiple.sample(1); expect(one, hasLength(1)); expect(one.first, inInclusiveRange(1, 10)); var some = multiple.sample(3); expect(some, hasLength(3)); expect(some[0], inInclusiveRange(1, 10)); expect(some[1], inInclusiveRange(1, 10)); expect(some[2], inInclusiveRange(1, 10)); expect(some[0], isNot(some[1])); expect(some[0], isNot(some[2])); expect(some[1], isNot(some[2])); var seen = {}; do { seen.addAll(multiple.sample(3)); } while (seen.length < 10); // Should eventually terminate. }); test('random', () { // Passing in a `Random` makes result deterministic. var multiple = iterable([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); var seed = 12345; var some = multiple.sample(5, Random(seed)); for (var i = 0; i < 10; i++) { var other = multiple.sample(5, Random(seed)); expect(other, some); } }); group('shuffles results', () { late Random random; late Iterable input; setUp(() async { input = iterable([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); random = Random(12345); }); test('for partial samples of input', () { var result = input.sample(9, random); expect(result.length, 9); expect(result.isSorted(cmpInt), isFalse); }); test('for complete samples of input', () { var result = input.sample(10, random); expect(result.length, 10); expect(result.isSorted(cmpInt), isFalse); expect( const UnorderedIterableEquality().equals(input, result), isTrue); }); test('for overlengthed samples of input', () { var result = input.sample(20, random); expect(result.length, 10); expect(result.isSorted(cmpInt), isFalse); expect( const UnorderedIterableEquality().equals(input, result), isTrue); }); }); }); group('.elementAtOrNull', () { test('empty', () async { expect(iterable([]).elementAtOrNull(0), isNull); }); test('negative index', () async { expect(() => iterable([1]).elementAtOrNull(-1), throwsA(isA())); }); test('index within range', () async { expect(iterable([1]).elementAtOrNull(0), 1); }); test('index too high', () async { expect(iterable([1]).elementAtOrNull(1), isNull); }); }); group('.slices', () { test('empty', () { expect(iterable([]).slices(1), []); }); test('with the same length as the iterable', () { expect(iterable([1, 2, 3]).slices(3), [ [1, 2, 3] ]); }); test('with a longer length than the iterable', () { expect(iterable([1, 2, 3]).slices(5), [ [1, 2, 3] ]); }); test('with a shorter length than the iterable', () { expect(iterable([1, 2, 3]).slices(2), [ [1, 2], [3] ]); }); test('with length divisible by the iterable\'s', () { expect(iterable([1, 2, 3, 4]).slices(2), [ [1, 2], [3, 4] ]); }); test('refuses negative length', () { expect(() => iterable([1]).slices(-1), throwsRangeError); }); test('refuses length 0', () { expect(() => iterable([1]).slices(0), throwsRangeError); }); test('regression #286, bug in slice', () { var l1 = [1, 2, 3, 4, 5, 6]; List l2 = l1.slice(1, 5); // (2..5) // This call would stack-overflow due to a lacking type promotion // which caused the extension method to keep calling itself, // instead of switching to the instance method on `ListSlice`. // // If successful, it would use the `2` argument as offset, instead // of the `1` offset from the `slice` call above. var l3 = l2.slice(2, 4); // (4..5) expect(l3, [4, 5]); expect(l3.toList(), [4, 5]); }); }); }); group('Comparator', () { test('.inverse', () { var cmpStringInv = cmpString.inverse; expect(cmpString('a', 'b'), isNegative); expect(cmpStringInv('a', 'b'), isPositive); expect(cmpString('aa', 'a'), isPositive); expect(cmpStringInv('aa', 'a'), isNegative); expect(cmpString('a', 'a'), isZero); expect(cmpStringInv('a', 'a'), isZero); }); test('.compareBy', () { var cmpByLength = cmpInt.compareBy((String s) => s.length); expect(cmpByLength('a', 'b'), 0); expect(cmpByLength('aa', 'b'), isPositive); expect(cmpByLength('b', 'aa'), isNegative); var cmpByInverseLength = cmpIntInverse.compareBy((String s) => s.length); expect(cmpByInverseLength('a', 'b'), 0); expect(cmpByInverseLength('aa', 'b'), isNegative); expect(cmpByInverseLength('b', 'aa'), isPositive); }); test('.then', () { var cmpLengthFirst = cmpStringLength.then(cmpString); var strings = ['a', 'aa', 'ba', 'ab', 'b', 'aaa']; strings.sort(cmpString); expect(strings, ['a', 'aa', 'aaa', 'ab', 'b', 'ba']); strings.sort(cmpLengthFirst); expect(strings, ['a', 'b', 'aa', 'ab', 'ba', 'aaa']); int cmpFirstLetter(String s1, String s2) => s1.runes.first - s2.runes.first; var cmpLetterLength = cmpFirstLetter.then(cmpStringLength); var cmpLengthLetter = cmpStringLength.then(cmpFirstLetter); strings = ['a', 'ab', 'b', 'ba', 'aaa']; strings.sort(cmpLetterLength); expect(strings, ['a', 'ab', 'aaa', 'b', 'ba']); strings.sort(cmpLengthLetter); expect(strings, ['a', 'b', 'ab', 'ba', 'aaa']); }); }); group('List', () { group('of any', () { group('.binarySearch', () { test('empty', () { expect([].binarySearch(1, unreachable), -1); }); test('single', () { expect([0].binarySearch(1, cmpInt), -1); expect([1].binarySearch(1, cmpInt), 0); expect([2].binarySearch(1, cmpInt), -1); }); test('multiple', () { expect([1, 2, 3, 4, 5, 6].binarySearch(3, cmpInt), 2); expect([6, 5, 4, 3, 2, 1].binarySearch(3, cmpIntInverse), 3); }); }); group('.binarySearchByCompare', () { test('empty', () { expect([].binarySearchByCompare(1, toString, cmpParse), -1); }); test('single', () { expect([0].binarySearchByCompare(1, toString, cmpParse), -1); expect([1].binarySearchByCompare(1, toString, cmpParse), 0); expect([2].binarySearchByCompare(1, toString, cmpParse), -1); }); test('multiple', () { expect( [1, 2, 3, 4, 5, 6].binarySearchByCompare(3, toString, cmpParse), 2); expect( [6, 5, 4, 3, 2, 1] .binarySearchByCompare(3, toString, cmpParseInverse), 3); }); }); group('.binarySearchBy', () { test('empty', () { expect([].binarySearchBy(1, toString), -1); }); test('single', () { expect([0].binarySearchBy(1, toString), -1); expect([1].binarySearchBy(1, toString), 0); expect([2].binarySearchBy(1, toString), -1); }); test('multiple', () { expect([1, 2, 3, 4, 5, 6].binarySearchBy(3, toString), 2); }); }); group('.lowerBound', () { test('empty', () { expect([].lowerBound(1, unreachable), 0); }); test('single', () { expect([0].lowerBound(1, cmpInt), 1); expect([1].lowerBound(1, cmpInt), 0); expect([2].lowerBound(1, cmpInt), 0); }); test('multiple', () { expect([1, 2, 3, 4, 5, 6].lowerBound(3, cmpInt), 2); expect([6, 5, 4, 3, 2, 1].lowerBound(3, cmpIntInverse), 3); expect([1, 2, 4, 5, 6].lowerBound(3, cmpInt), 2); expect([6, 5, 4, 2, 1].lowerBound(3, cmpIntInverse), 3); }); }); group('.lowerBoundByCompare', () { test('empty', () { expect([].lowerBoundByCompare(1, toString, cmpParse), 0); }); test('single', () { expect([0].lowerBoundByCompare(1, toString, cmpParse), 1); expect([1].lowerBoundByCompare(1, toString, cmpParse), 0); expect([2].lowerBoundByCompare(1, toString, cmpParse), 0); }); test('multiple', () { expect( [1, 2, 3, 4, 5, 6].lowerBoundByCompare(3, toString, cmpParse), 2); expect( [6, 5, 4, 3, 2, 1] .lowerBoundByCompare(3, toString, cmpParseInverse), 3); expect([1, 2, 4, 5, 6].lowerBoundByCompare(3, toString, cmpParse), 2); expect( [6, 5, 4, 2, 1].lowerBoundByCompare(3, toString, cmpParseInverse), 3); }); }); group('.lowerBoundBy', () { test('empty', () { expect([].lowerBoundBy(1, toString), 0); }); test('single', () { expect([0].lowerBoundBy(1, toString), 1); expect([1].lowerBoundBy(1, toString), 0); expect([2].lowerBoundBy(1, toString), 0); }); test('multiple', () { expect([1, 2, 3, 4, 5, 6].lowerBoundBy(3, toString), 2); expect([1, 2, 4, 5, 6].lowerBoundBy(3, toString), 2); }); }); group('sortRange', () { test('errors', () { expect(() => [1].sortRange(-1, 1, cmpInt), throwsArgumentError); expect(() => [1].sortRange(0, 2, cmpInt), throwsArgumentError); expect(() => [1].sortRange(1, 0, cmpInt), throwsArgumentError); }); test('empty range', () { [].sortRange(0, 0, unreachable); var list = [3, 2, 1]; list.sortRange(0, 0, unreachable); list.sortRange(3, 3, unreachable); expect(list, [3, 2, 1]); }); test('single', () { [1].sortRange(0, 1, unreachable); var list = [3, 2, 1]; list.sortRange(0, 1, unreachable); list.sortRange(1, 2, unreachable); list.sortRange(2, 3, unreachable); }); test('multiple', () { var list = [9, 8, 7, 6, 5, 4, 3, 2, 1]; list.sortRange(2, 5, cmpInt); expect(list, [9, 8, 5, 6, 7, 4, 3, 2, 1]); list.sortRange(4, 8, cmpInt); expect(list, [9, 8, 5, 6, 2, 3, 4, 7, 1]); list.sortRange(3, 6, cmpIntInverse); expect(list, [9, 8, 5, 6, 3, 2, 4, 7, 1]); }); }); group('.sortBy', () { test('empty', () { expect([]..sortBy(unreachable), []); }); test('singleton', () { expect([1]..sortBy(unreachable), [1]); }); test('multiple', () { expect([3, 20, 100]..sortBy(toString), [100, 20, 3]); }); group('range', () { test('errors', () { expect(() => [1].sortBy(toString, -1, 1), throwsArgumentError); expect(() => [1].sortBy(toString, 0, 2), throwsArgumentError); expect(() => [1].sortBy(toString, 1, 0), throwsArgumentError); }); test('empty', () { expect([5, 7, 4, 2, 3]..sortBy(unreachable, 2, 2), [5, 7, 4, 2, 3]); }); test('singleton', () { expect([5, 7, 4, 2, 3]..sortBy(unreachable, 2, 3), [5, 7, 4, 2, 3]); }); test('multiple', () { expect( [5, 7, 40, 2, 3]..sortBy((a) => '$a', 1, 4), [5, 2, 40, 7, 3]); }); }); }); group('.sortByCompare', () { test('empty', () { expect([]..sortByCompare(unreachable, unreachable), []); }); test('singleton', () { expect([2]..sortByCompare(unreachable, unreachable), [2]); }); test('multiple', () { expect([30, 2, 100]..sortByCompare(toString, cmpParseInverse), [100, 30, 2]); }); group('range', () { test('errors', () { expect(() => [1].sortByCompare(toString, cmpParse, -1, 1), throwsArgumentError); expect(() => [1].sortByCompare(toString, cmpParse, 0, 2), throwsArgumentError); expect(() => [1].sortByCompare(toString, cmpParse, 1, 0), throwsArgumentError); }); test('empty', () { expect( [3, 5, 7, 3, 1]..sortByCompare(unreachable, unreachable, 2, 2), [3, 5, 7, 3, 1]); }); test('singleton', () { expect( [3, 5, 7, 3, 1]..sortByCompare(unreachable, unreachable, 2, 3), [3, 5, 7, 3, 1]); }); test('multiple', () { expect( [3, 5, 7, 30, 1] ..sortByCompare(toString, cmpParseInverse, 1, 4), [3, 30, 7, 5, 1]); }); }); }); group('.shuffleRange', () { test('errors', () { expect(() => [1].shuffleRange(-1, 1), throwsArgumentError); expect(() => [1].shuffleRange(0, 2), throwsArgumentError); expect(() => [1].shuffleRange(1, 0), throwsArgumentError); }); test('empty range', () { expect([]..shuffleRange(0, 0), []); expect([1, 2, 3, 4]..shuffleRange(0, 0), [1, 2, 3, 4]); expect([1, 2, 3, 4]..shuffleRange(4, 4), [1, 2, 3, 4]); }); test('singleton range', () { expect([1, 2, 3, 4]..shuffleRange(0, 1), [1, 2, 3, 4]); expect([1, 2, 3, 4]..shuffleRange(3, 4), [1, 2, 3, 4]); }); test('multiple', () { var list = [1, 2, 3, 4, 5]; do { list.shuffleRange(0, 3); expect(list.getRange(3, 5), [4, 5]); expect(list.getRange(0, 3), unorderedEquals([1, 2, 3])); } while (const ListEquality().equals(list.sublist(0, 3), [1, 2, 3])); // Won't terminate if shuffle *never* moves a value. }); }); group('.reverseRange', () { test('errors', () { expect(() => [1].reverseRange(-1, 1), throwsArgumentError); expect(() => [1].reverseRange(0, 2), throwsArgumentError); expect(() => [1].reverseRange(1, 0), throwsArgumentError); }); test('empty range', () { expect([]..reverseRange(0, 0), []); expect([1, 2, 3, 4]..reverseRange(0, 0), [1, 2, 3, 4]); expect([1, 2, 3, 4]..reverseRange(4, 4), [1, 2, 3, 4]); }); test('singleton range', () { expect([1, 2, 3, 4]..reverseRange(0, 1), [1, 2, 3, 4]); expect([1, 2, 3, 4]..reverseRange(3, 4), [1, 2, 3, 4]); }); test('multiple', () { var list = [1, 2, 3, 4, 5]; list.reverseRange(0, 3); expect(list, [3, 2, 1, 4, 5]); list.reverseRange(3, 5); expect(list, [3, 2, 1, 5, 4]); list.reverseRange(0, 5); expect(list, [4, 5, 1, 2, 3]); }); }); group('.swap', () { test('errors', () { expect(() => [1].swap(0, 1), throwsArgumentError); expect(() => [1].swap(1, 1), throwsArgumentError); expect(() => [1].swap(1, 0), throwsArgumentError); expect(() => [1].swap(-1, 0), throwsArgumentError); }); test('self swap', () { expect([1]..swap(0, 0), [1]); expect([1, 2, 3]..swap(1, 1), [1, 2, 3]); }); test('actual swap', () { expect([1, 2, 3]..swap(0, 2), [3, 2, 1]); expect([1, 2, 3]..swap(2, 0), [3, 2, 1]); expect([1, 2, 3]..swap(2, 1), [1, 3, 2]); expect([1, 2, 3]..swap(1, 2), [1, 3, 2]); expect([1, 2, 3]..swap(0, 1), [2, 1, 3]); expect([1, 2, 3]..swap(1, 0), [2, 1, 3]); }); }); group('.slice', () { test('errors', () { expect(() => [1].slice(-1, 1), throwsArgumentError); expect(() => [1].slice(0, 2), throwsArgumentError); expect(() => [1].slice(1, 0), throwsArgumentError); var l = [1]; var slice = l.slice(0, 1); l.removeLast(); expect(() => slice.first, throwsConcurrentModificationError); }); test('empty', () { expect([].slice(0, 0), isEmpty); }); test('modify', () { var list = [1, 2, 3, 4, 5, 6, 7, 8, 9]; var slice = list.slice(2, 6); expect(slice, [3, 4, 5, 6]); slice.sort(cmpIntInverse); expect(slice, [6, 5, 4, 3]); expect(list, [1, 2, 6, 5, 4, 3, 7, 8, 9]); }); }); group('equals', () { test('empty', () { expect([].equals([]), true); }); test('non-empty', () { expect([1, 2.5, 'a'].equals([1.0, 2.5, 'a']), true); expect([1, 2.5, 'a'].equals([1.0, 2.5, 'b']), false); expect( [ [1] ].equals([ [1] ]), false); expect( [ [1] ].equals([ [1] ], const ListEquality()), true); }); }); group('.forEachIndexed', () { test('empty', () { [].forEachIndexed(unreachable); }); test('single', () { var log = []; ['a'].forEachIndexed((i, s) { log ..add(i) ..add(s); }); expect(log, [0, 'a']); }); test('multiple', () { var log = []; ['a', 'b', 'c'].forEachIndexed((i, s) { log ..add(i) ..add(s); }); expect(log, [0, 'a', 1, 'b', 2, 'c']); }); }); group('.forEachWhile', () { test('empty', () { [].forEachWhile(unreachable); }); test('single true', () { var log = []; ['a'].forEachWhile((s) { log.add(s); return true; }); expect(log, ['a']); }); test('single false', () { var log = []; ['a'].forEachWhile((s) { log.add(s); return false; }); expect(log, ['a']); }); test('multiple one', () { var log = []; ['a', 'b', 'c'].forEachWhile((s) { log.add(s); return false; }); expect(log, ['a']); }); test('multiple all', () { var log = []; ['a', 'b', 'c'].forEachWhile((s) { log.add(s); return true; }); expect(log, ['a', 'b', 'c']); }); test('multiple some', () { var log = []; ['a', 'b', 'c'].forEachWhile((s) { log.add(s); return s != 'b'; }); expect(log, ['a', 'b']); }); }); group('.forEachIndexedWhile', () { test('empty', () { [].forEachIndexedWhile(unreachable); }); test('single true', () { var log = []; ['a'].forEachIndexedWhile((i, s) { log ..add(i) ..add(s); return true; }); expect(log, [0, 'a']); }); test('single false', () { var log = []; ['a'].forEachIndexedWhile((i, s) { log ..add(i) ..add(s); return false; }); expect(log, [0, 'a']); }); test('multiple one', () { var log = []; ['a', 'b', 'c'].forEachIndexedWhile((i, s) { log ..add(i) ..add(s); return false; }); expect(log, [0, 'a']); }); test('multiple all', () { var log = []; ['a', 'b', 'c'].forEachIndexedWhile((i, s) { log ..add(i) ..add(s); return true; }); expect(log, [0, 'a', 1, 'b', 2, 'c']); }); test('multiple some', () { var log = []; ['a', 'b', 'c'].forEachIndexedWhile((i, s) { log ..add(i) ..add(s); return s != 'b'; }); expect(log, [0, 'a', 1, 'b']); }); }); group('.mapIndexed', () { test('empty', () { expect([].mapIndexed(unreachable), isEmpty); }); test('multiple', () { expect(['a', 'b'].mapIndexed((i, s) => [i, s]), [ [0, 'a'], [1, 'b'] ]); }); }); group('.whereIndexed', () { test('empty', () { expect([].whereIndexed(unreachable), isEmpty); }); test('none', () { var trace = []; int log(int a, int b) { trace ..add(a) ..add(b); return b; } expect([1, 3, 5, 7].whereIndexed((i, x) => log(i, x).isEven), isEmpty); expect(trace, [0, 1, 1, 3, 2, 5, 3, 7]); }); test('all', () { expect( [1, 3, 5, 7].whereIndexed((i, x) => x.isOdd), [1, 3, 5, 7]); }); test('some', () { expect([1, 3, 5, 7].whereIndexed((i, x) => i.isOdd), [3, 7]); }); }); group('.whereNotIndexed', () { test('empty', () { expect([].whereNotIndexed(unreachable), isEmpty); }); test('none', () { var trace = []; int log(int a, int b) { trace ..add(a) ..add(b); return b; } expect([1, 3, 5, 7].whereNotIndexed((i, x) => log(i, x).isOdd), isEmpty); expect(trace, [0, 1, 1, 3, 2, 5, 3, 7]); }); test('all', () { expect([1, 3, 5, 7].whereNotIndexed((i, x) => x.isEven), [1, 3, 5, 7]); }); test('some', () { expect([1, 3, 5, 7].whereNotIndexed((i, x) => i.isOdd), [1, 5]); }); }); group('.expandIndexed', () { test('empty', () { expect([].expandIndexed(unreachable), isEmpty); }); test('empty result', () { expect(['a', 'b'].expandIndexed((i, v) => []), isEmpty); }); test('larger result', () { expect(['a', 'b'].expandIndexed((i, v) => ['$i', v]), ['0', 'a', '1', 'b']); }); test('varying result', () { expect(['a', 'b'].expandIndexed((i, v) => i.isOdd ? ['$i', v] : []), ['1', 'b']); }); }); group('.elementAtOrNull', () { test('empty', () async { expect([].elementAtOrNull(0), isNull); }); test('negative index', () async { expect(() => [1].elementAtOrNull(-1), throwsA(isA())); }); test('index within range', () async { expect([1].elementAtOrNull(0), 1); }); test('index too high', () async { expect([1].elementAtOrNull(1), isNull); }); }); group('.slices', () { test('empty', () { expect([].slices(1), []); }); test('with the same length as the iterable', () { expect([1, 2, 3].slices(3), [ [1, 2, 3] ]); }); test('with a longer length than the iterable', () { expect([1, 2, 3].slices(5), [ [1, 2, 3] ]); }); test('with a shorter length than the iterable', () { expect([1, 2, 3].slices(2), [ [1, 2], [3] ]); }); test('with length divisible by the iterable\'s', () { expect([1, 2, 3, 4].slices(2), [ [1, 2], [3, 4] ]); }); test('refuses negative length', () { expect(() => [1].slices(-1), throwsRangeError); }); test('refuses length 0', () { expect(() => [1].slices(0), throwsRangeError); }); }); }); group('on comparable', () { group('.binarySearch', () { test('empty', () { expect([].binarySearch('1', unreachable), -1); expect([].binarySearch('1'), -1); }); test('single', () { expect(['0'].binarySearch('1', cmpString), -1); expect(['1'].binarySearch('1', cmpString), 0); expect(['2'].binarySearch('1', cmpString), -1); expect( ['0'].binarySearch( '1', ), -1); expect( ['1'].binarySearch( '1', ), 0); expect( ['2'].binarySearch( '1', ), -1); }); test('multiple', () { expect( ['1', '2', '3', '4', '5', '6'].binarySearch('3', cmpString), 2); expect(['1', '2', '3', '4', '5', '6'].binarySearch('3'), 2); expect( ['6', '5', '4', '3', '2', '1'].binarySearch('3', cmpParseInverse), 3); }); }); }); group('.lowerBound', () { test('empty', () { expect([].lowerBound('1', unreachable), 0); }); test('single', () { expect(['0'].lowerBound('1', cmpString), 1); expect(['1'].lowerBound('1', cmpString), 0); expect(['2'].lowerBound('1', cmpString), 0); expect(['0'].lowerBound('1'), 1); expect(['1'].lowerBound('1'), 0); expect(['2'].lowerBound('1'), 0); }); test('multiple', () { expect(['1', '2', '3', '4', '5', '6'].lowerBound('3', cmpParse), 2); expect(['1', '2', '3', '4', '5', '6'].lowerBound('3'), 2); expect( ['6', '5', '4', '3', '2', '1'].lowerBound('3', cmpParseInverse), 3); expect(['1', '2', '4', '5', '6'].lowerBound('3', cmpParse), 2); expect(['1', '2', '4', '5', '6'].lowerBound('3'), 2); expect(['6', '5', '4', '2', '1'].lowerBound('3', cmpParseInverse), 3); }); }); group('sortRange', () { test('errors', () { expect(() => [1].sortRange(-1, 1, cmpInt), throwsArgumentError); expect(() => [1].sortRange(0, 2, cmpInt), throwsArgumentError); expect(() => [1].sortRange(1, 0, cmpInt), throwsArgumentError); }); test('empty range', () { [].sortRange(0, 0, unreachable); var list = [3, 2, 1]; list.sortRange(0, 0, unreachable); list.sortRange(3, 3, unreachable); expect(list, [3, 2, 1]); }); test('single', () { [1].sortRange(0, 1, unreachable); var list = [3, 2, 1]; list.sortRange(0, 1, unreachable); list.sortRange(1, 2, unreachable); list.sortRange(2, 3, unreachable); }); test('multiple', () { var list = [9, 8, 7, 6, 5, 4, 3, 2, 1]; list.sortRange(2, 5, cmpInt); expect(list, [9, 8, 5, 6, 7, 4, 3, 2, 1]); list.sortRange(4, 8, cmpInt); expect(list, [9, 8, 5, 6, 2, 3, 4, 7, 1]); list.sortRange(3, 6, cmpIntInverse); expect(list, [9, 8, 5, 6, 3, 2, 4, 7, 1]); }); }); }); } /// Creates a plain iterable not implementing any other class. Iterable iterable(Iterable values) sync* { yield* values; } Never unreachable([_, __, ___]) => fail('Unreachable'); String toString(Object? o) => '$o'; /// Compares values equal if they have the same remainder mod [mod]. int Function(int, int) cmpMod(int mod) => (a, b) => a ~/ mod - b ~/ mod; /// Compares strings lexically. int cmpString(String a, String b) => a.compareTo(b); /// Compares strings by length. int cmpStringLength(String a, String b) => a.length - b.length; /// Compares strings by their integer numeral content. int cmpParse(String s1, String s2) => cmpInt(int.parse(s1), int.parse(s2)); /// Compares strings inversely by their integer numeral content. int cmpParseInverse(String s1, String s2) => cmpIntInverse(int.parse(s1), int.parse(s2)); /// Compares integers by size. int cmpInt(int a, int b) => a - b; /// Compares integers by inverse size. int cmpIntInverse(int a, int b) => b - a; /// Tests an integer for being even. bool isEven(int x) => x.isEven; /// Tests an integer for being odd. bool isOdd(int x) => x.isOdd;