Add type-asserting wrapper constructors.
Closes #25
R=lrn@google.com
Review URL: https://codereview.chromium.org//1840573002 .
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bca49a..cde274e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,10 @@
-## 1.4.2
+## 1.5.0
+
+* Add `DelegatingIterable.typed()`, `DelegatingList.typed()`,
+ `DelegatingSet.typed()`, `DelegatingMap.typed()`, and
+ `DelegatingQueue.typed()` static methods. These wrap untyped instances of
+ these classes with the correct type parameter, and assert the types of values
+ as they're accessed.
* Fix the types for `binarySearch()` and `lowerBound()` so they no longer
require all arguments to be comparable.
diff --git a/lib/src/typed_wrappers.dart b/lib/src/typed_wrappers.dart
new file mode 100644
index 0000000..1dcb3b7
--- /dev/null
+++ b/lib/src/typed_wrappers.dart
@@ -0,0 +1,349 @@
+// Copyright (c) 2016, 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:collection";
+import "dart:math" as math;
+
+import "wrappers.dart";
+
+typedef F _UnaryFunction<E, F>(E argument);
+
+/// The base class for delegating, type-asserting iterables.
+///
+/// Subclasses can provide a [_base] that should be delegated to. Unlike
+/// [TypeSafeIterable], this allows the base to be created on demand.
+abstract class _TypeSafeIterableBase<E> implements Iterable<E> {
+ /// The base iterable to which operations are delegated.
+ Iterable get _base;
+
+ _TypeSafeIterableBase();
+
+ bool any(bool test(E element)) => _base.any(_validate(test));
+
+ bool contains(Object element) => _base.contains(element);
+
+ E elementAt(int index) => _base.elementAt(index) as E;
+
+ bool every(bool test(E element)) => _base.every(_validate(test));
+
+ Iterable/*<T>*/ expand/*<T>*/(Iterable/*<T>*/ f(E element)) =>
+ _base.expand(_validate(f));
+
+ E get first => _base.first as E;
+
+ E firstWhere(bool test(E element), {E orElse()}) =>
+ _base.firstWhere(_validate(test), orElse: orElse) as E;
+
+ /*=T*/ fold/*<T>*/(
+ /*=T*/ initialValue,
+ /*=T*/ combine(/*=T*/ previousValue, E element)) =>
+ _base.fold(initialValue,
+ (previousValue, element) => combine(previousValue, element as E));
+
+ void forEach(void f(E element)) => _base.forEach(_validate(f));
+
+ bool get isEmpty => _base.isEmpty;
+
+ bool get isNotEmpty => _base.isNotEmpty;
+
+ Iterator<E> get iterator => _base.map((element) => element as E).iterator;
+
+ String join([String separator = ""]) => _base.join(separator);
+
+ E get last => _base.last as E;
+
+ E lastWhere(bool test(E element), {E orElse()}) =>
+ _base.lastWhere(_validate(test), orElse: orElse) as E;
+
+ int get length => _base.length;
+
+ Iterable/*<T>*/ map/*<T>*/(/*=T*/ f(E element)) => _base.map(_validate(f));
+
+ E reduce(E combine(E value, E element)) =>
+ _base.reduce((value, element) => combine(value as E, element as E)) as E;
+
+ E get single => _base.single as E;
+
+ E singleWhere(bool test(E element)) =>
+ _base.singleWhere(_validate(test)) as E;
+
+ Iterable<E> skip(int n) => new TypeSafeIterable<E>(_base.skip(n));
+
+ Iterable<E> skipWhile(bool test(E value)) =>
+ new TypeSafeIterable<E>(_base.skipWhile(_validate(test)));
+
+ Iterable<E> take(int n) => new TypeSafeIterable<E>(_base.take(n));
+
+ Iterable<E> takeWhile(bool test(E value)) =>
+ new TypeSafeIterable<E>(_base.takeWhile(_validate(test)));
+
+ List<E> toList({bool growable: true}) =>
+ new TypeSafeList<E>(_base.toList(growable: growable));
+
+ Set<E> toSet() => new TypeSafeSet<E>(_base.toSet());
+
+ Iterable<E> where(bool test(E element)) =>
+ new TypeSafeIterable<E>(_base.where(_validate(test)));
+
+ String toString() => _base.toString();
+
+ /// Returns a version of [function] that asserts that its argument is an
+ /// instance of `E`.
+ _UnaryFunction/*<dynamic, F>*/ _validate/*<F>*/(/*=F*/ function(E value)) =>
+ (value) => function(value as E);
+}
+
+/// An [Iterable] that asserts the types of values in a base iterable.
+///
+/// This is instantiated using [DelegatingIterable.typed].
+class TypeSafeIterable<E> extends _TypeSafeIterableBase<E>
+ implements DelegatingIterable<E> {
+ final Iterable _base;
+
+ TypeSafeIterable(Iterable base) : _base = base;
+}
+
+/// A [List] that asserts the types of values in a base list.
+///
+/// This is instantiated using [DelegatingList.typed].
+class TypeSafeList<E> extends TypeSafeIterable<E> implements DelegatingList<E> {
+ TypeSafeList(List base) : super(base);
+
+ /// A [List]-typed getter for [_base].
+ List get _listBase => _base;
+
+ E operator [](int index) => _listBase[index] as E;
+
+ void operator []=(int index, E value) {
+ _listBase[index] = value;
+ }
+
+ void add(E value) {
+ _listBase.add(value);
+ }
+
+ void addAll(Iterable<E> iterable) {
+ _listBase.addAll(iterable);
+ }
+
+ Map<int, E> asMap() => new TypeSafeMap<int, E>(_listBase.asMap());
+
+ void clear() {
+ _listBase.clear();
+ }
+
+ void fillRange(int start, int end, [E fillValue]) {
+ _listBase.fillRange(start, end, fillValue);
+ }
+
+ Iterable<E> getRange(int start, int end) =>
+ new TypeSafeIterable<E>(_listBase.getRange(start, end));
+
+ int indexOf(E element, [int start = 0]) => _listBase.indexOf(element, start);
+
+ void insert(int index, E element) {
+ _listBase.insert(index, element);
+ }
+
+ void insertAll(int index, Iterable<E> iterable) {
+ _listBase.insertAll(index, iterable);
+ }
+
+ int lastIndexOf(E element, [int start]) =>
+ _listBase.lastIndexOf(element, start);
+
+ void set length(int newLength) {
+ _listBase.length = newLength;
+ }
+
+ bool remove(Object value) => _listBase.remove(value);
+
+ E removeAt(int index) => _listBase.removeAt(index) as E;
+
+ E removeLast() => _listBase.removeLast() as E;
+
+ void removeRange(int start, int end) {
+ _listBase.removeRange(start, end);
+ }
+
+ void removeWhere(bool test(E element)) {
+ _listBase.removeWhere(_validate(test));
+ }
+
+ void replaceRange(int start, int end, Iterable<E> iterable) {
+ _listBase.replaceRange(start, end, iterable);
+ }
+
+ void retainWhere(bool test(E element)) {
+ _listBase.retainWhere(_validate(test));
+ }
+
+ Iterable<E> get reversed => new TypeSafeIterable<E>(_listBase.reversed);
+
+ void setAll(int index, Iterable<E> iterable) {
+ _listBase.setAll(index, iterable);
+ }
+
+ void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
+ _listBase.setRange(start, end, iterable, skipCount);
+ }
+
+ void shuffle([math.Random random]) {
+ _listBase.shuffle(random);
+ }
+
+ void sort([int compare(E a, E b)]) {
+ if (compare == null) {
+ _listBase.sort();
+ } else {
+ _listBase.sort((a, b) => compare(a as E, b as E));
+ }
+ }
+
+ List<E> sublist(int start, [int end]) =>
+ new TypeSafeList<E>(_listBase.sublist(start, end));
+}
+
+/// A [Set] that asserts the types of values in a base set.
+///
+/// This is instantiated using [DelegatingSet.typed].
+class TypeSafeSet<E> extends TypeSafeIterable<E> implements DelegatingSet<E> {
+ TypeSafeSet(Set base) : super(base);
+
+ /// A [Set]-typed getter for [_base].
+ Set get _setBase => _base;
+
+ bool add(E value) => _setBase.add(value);
+
+ void addAll(Iterable<E> elements) {
+ _setBase.addAll(elements);
+ }
+
+ void clear() {
+ _setBase.clear();
+ }
+
+ bool containsAll(Iterable<Object> other) => _setBase.containsAll(other);
+
+ Set<E> difference(Set<E> other) =>
+ new TypeSafeSet<E>(_setBase.difference(other));
+
+ Set<E> intersection(Set<Object> other) =>
+ new TypeSafeSet<E>(_setBase.intersection(other));
+
+ E lookup(Object element) => _setBase.lookup(element) as E;
+
+ bool remove(Object value) => _setBase.remove(value);
+
+ void removeAll(Iterable<Object> elements) {
+ _setBase.removeAll(elements);
+ }
+
+ void removeWhere(bool test(E element)) {
+ _setBase.removeWhere(_validate(test));
+ }
+
+ void retainAll(Iterable<Object> elements) {
+ _setBase.retainAll(elements);
+ }
+
+ void retainWhere(bool test(E element)) {
+ _setBase.retainWhere(_validate(test));
+ }
+
+ Set<E> union(Set<E> other) => new TypeSafeSet<E>(_setBase.union(other));
+}
+
+/// A [Queue] that asserts the types of values in a base queue.
+///
+/// This is instantiated using [DelegatingQueue.typed].
+class TypeSafeQueue<E> extends TypeSafeIterable<E>
+ implements DelegatingQueue<E> {
+ TypeSafeQueue(Queue queue) : super(queue);
+
+ /// A [Queue]-typed getter for [_base].
+ Queue get _baseQueue => _base;
+
+ void add(E value) {
+ _baseQueue.add(value);
+ }
+
+ void addAll(Iterable<E> iterable) {
+ _baseQueue.addAll(iterable);
+ }
+
+ void addFirst(E value) {
+ _baseQueue.addFirst(value);
+ }
+
+ void addLast(E value) {
+ _baseQueue.addLast(value);
+ }
+
+ void clear() {
+ _baseQueue.clear();
+ }
+
+ bool remove(Object object) => _baseQueue.remove(object);
+
+ void removeWhere(bool test(E element)) {
+ _baseQueue.removeWhere(_validate(test));
+ }
+
+ void retainWhere(bool test(E element)) {
+ _baseQueue.retainWhere(_validate(test));
+ }
+
+ E removeFirst() => _baseQueue.removeFirst() as E;
+
+ E removeLast() => _baseQueue.removeLast() as E;
+}
+
+/// A [Map] that asserts the types of keys and values in a base map.
+///
+/// This is instantiated using [DelegatingMap.typed].
+class TypeSafeMap<K, V> implements DelegatingMap<K, V> {
+ /// The base map to which operations are delegated.
+ final Map _base;
+
+ TypeSafeMap(Map base) : _base = base;
+
+ V operator [](Object key) => _base[key] as V;
+
+ void operator []=(K key, V value) {
+ _base[key] = value;
+ }
+
+ void addAll(Map<K, V> other) {
+ _base.addAll(other);
+ }
+
+ void clear() {
+ _base.clear();
+ }
+
+ bool containsKey(Object key) => _base.containsKey(key);
+
+ bool containsValue(Object value) => _base.containsValue(value);
+
+ void forEach(void f(K key, V value)) {
+ _base.forEach((key, value) => f(key as K, value as V));
+ }
+
+ bool get isEmpty => _base.isEmpty;
+
+ bool get isNotEmpty => _base.isNotEmpty;
+
+ Iterable<K> get keys => new TypeSafeIterable<K>(_base.keys);
+
+ int get length => _base.length;
+
+ V putIfAbsent(K key, V ifAbsent()) => _base.putIfAbsent(key, ifAbsent) as V;
+
+ V remove(Object key) => _base.remove(key) as V;
+
+ Iterable<V> get values => new TypeSafeIterable<V>(_base.values);
+
+ String toString() => _base.toString();
+}
diff --git a/lib/src/wrappers.dart b/lib/src/wrappers.dart
index fcd3315..c447427 100644
--- a/lib/src/wrappers.dart
+++ b/lib/src/wrappers.dart
@@ -5,6 +5,7 @@
import "dart:collection";
import "dart:math" as math;
+import "typed_wrappers.dart";
import "unmodifiable_wrappers.dart";
typedef K _KeyForValue<K, V>(V value);
@@ -81,27 +82,53 @@
String toString() => _base.toString();
}
-/// Creates an [Iterable] that delegates all operations to a base iterable.
+/// An [Iterable] that delegates all operations to a base iterable.
///
-/// This class can be used hide non-`Iterable` methods of an iterable object,
+/// This class can be used to hide non-`Iterable` methods of an iterable object,
/// or it can be extended to add extra functionality on top of an existing
/// iterable object.
class DelegatingIterable<E> extends _DelegatingIterableBase<E> {
final Iterable<E> _base;
- /// Create a wrapper that forwards operations to [base].
+ /// Creates a wrapper that forwards operations to [base].
const DelegatingIterable(Iterable<E> base) : _base = base;
+
+ /// Creates a wrapper that asserts the types of values in [base].
+ ///
+ /// This soundly converts an [Iterable] without a generic type to an
+ /// `Iterable<E>` by asserting that its elements are instances of `E` whenever
+ /// they're accessed. If they're not, it throws a [CastError].
+ ///
+ /// This forwards all operations to [base], so any changes in [base] will be
+ /// reflected in [this]. If [base] is already an `Iterable<E>`, it's returned
+ /// unmodified.
+ static Iterable/*<E>*/ typed/*<E>*/(Iterable base) =>
+ base is Iterable/*<E>*/ ? base : new TypedIterable/*<E>*/(base);
}
-/// Creates a [List] that delegates all operations to a base list.
+/// A [List] that delegates all operations to a base list.
///
-/// This class can be used hide non-`List` methods of a list object,
-/// or it can be extended to add extra functionality on top of an existing
-/// list object.
+/// This class can be used to hide non-`List` methods of a list object, or it
+/// can be extended to add extra functionality on top of an existing list
+/// object.
class DelegatingList<E> extends DelegatingIterable<E> implements List<E> {
const DelegatingList(List<E> base) : super(base);
+ /// Creates a wrapper that asserts the types of values in [base].
+ ///
+ /// This soundly converts a [List] without a generic type to a `List<E>` by
+ /// asserting that its elements are instances of `E` whenever they're
+ /// accessed. If they're not, it throws a [CastError]. Note that even if an
+ /// operation throws a [CastError], it may still mutate the underlying
+ /// collection.
+ ///
+ /// This forwards all operations to [base], so any changes in [base] will be
+ /// reflected in [this]. If [base] is already a `List<E>`, it's returned
+ /// unmodified.
+ static List/*<E>*/ typed/*<E>*/(List base) =>
+ base is List/*<E>*/ ? base : new TypedList/*<E>*/(base);
+
List<E> get _listBase => _base;
E operator [](int index) => _listBase[index];
@@ -191,14 +218,27 @@
}
-/// Creates a [Set] that delegates all operations to a base set.
+/// A [Set] that delegates all operations to a base set.
///
-/// This class can be used hide non-`Set` methods of a set object,
-/// or it can be extended to add extra functionality on top of an existing
-/// set object.
+/// This class can be used to hide non-`Set` methods of a set object, or it can
+/// be extended to add extra functionality on top of an existing set object.
class DelegatingSet<E> extends DelegatingIterable<E> implements Set<E> {
const DelegatingSet(Set<E> base) : super(base);
+ /// Creates a wrapper that asserts the types of values in [base].
+ ///
+ /// This soundly converts a [Set] without a generic type to a `Set<E>` by
+ /// asserting that its elements are instances of `E` whenever they're
+ /// accessed. If they're not, it throws a [CastError]. Note that even if an
+ /// operation throws a [CastError], it may still mutate the underlying
+ /// collection.
+ ///
+ /// This forwards all operations to [base], so any changes in [base] will be
+ /// reflected in [this]. If [base] is already a `Set<E>`, it's returned
+ /// unmodified.
+ static Set/*<E>*/ typed/*<E>*/(Set base) =>
+ base is Set/*<E>*/ ? base : new TypedSet/*<E>*/(base);
+
Set<E> get _setBase => _base;
bool add(E value) => _setBase.add(value);
@@ -242,14 +282,28 @@
Set<E> toSet() => new DelegatingSet<E>(_setBase.toSet());
}
-/// Creates a [Queue] that delegates all operations to a base queue.
+/// A [Queue] that delegates all operations to a base queue.
///
-/// This class can be used hide non-`Queue` methods of a queue object,
-/// or it can be extended to add extra functionality on top of an existing
-/// queue object.
+/// This class can be used to hide non-`Queue` methods of a queue object, or it
+/// can be extended to add extra functionality on top of an existing queue
+/// object.
class DelegatingQueue<E> extends DelegatingIterable<E> implements Queue<E> {
const DelegatingQueue(Queue<E> queue) : super(queue);
+ /// Creates a wrapper that asserts the types of values in [base].
+ ///
+ /// This soundly converts a [Queue] without a generic type to a `Queue<E>` by
+ /// asserting that its elements are instances of `E` whenever they're
+ /// accessed. If they're not, it throws a [CastError]. Note that even if an
+ /// operation throws a [CastError], it may still mutate the underlying
+ /// collection.
+ ///
+ /// This forwards all operations to [base], so any changes in [base] will be
+ /// reflected in [this]. If [base] is already a `Queue<E>`, it's returned
+ /// unmodified.
+ static Queue/*<E>*/ typed/*<E>*/(Queue base) =>
+ base is Queue/*<E>*/ ? base : new TypedQueue/*<E>*/(base);
+
Queue<E> get _baseQueue => _base;
void add(E value) {
@@ -283,9 +337,9 @@
E removeLast() => _baseQueue.removeLast();
}
-/// Creates a [Map] that delegates all operations to a base map.
+/// A [Map] that delegates all operations to a base map.
///
-/// This class can be used hide non-`Map` methods of an object that extends
+/// This class can be used to hide non-`Map` methods of an object that extends
/// `Map`, or it can be extended to add extra functionality on top of an
/// existing map object.
class DelegatingMap<K, V> implements Map<K, V> {
@@ -293,6 +347,20 @@
const DelegatingMap(Map<K, V> base) : _base = base;
+ /// Creates a wrapper that asserts the types of keys and values in [base].
+ ///
+ /// This soundly converts a [Map] without generic types to a `Map<K, V>` by
+ /// asserting that its keys are instances of `E` and its values are instances
+ /// of `V` whenever they're accessed. If they're not, it throws a [CastError].
+ /// Note that even if an operation throws a [CastError], it may still mutate
+ /// the underlying collection.
+ ///
+ /// This forwards all operations to [base], so any changes in [base] will be
+ /// reflected in [this]. If [base] is already a `Map<K, V>`, it's returned
+ /// unmodified.
+ static Map/*<K, V>*/ typed/*<K, V>*/(Map base) =>
+ base is Map<K, V> ? base : new TypedMap<K, V>(base);
+
V operator [](Object key) => _base[key];
void operator []=(K key, V value) {
diff --git a/pubspec.yaml b/pubspec.yaml
index 6f1d641..5023dbe 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: collection
-version: 1.4.2
+version: 1.5.0
author: Dart Team <misc@dartlang.org>
description: Collections and utilities functions and classes related to collections.
homepage: https://www.github.com/dart-lang/collection
diff --git a/test/typed_wrapper/iterable_test.dart b/test/typed_wrapper/iterable_test.dart
new file mode 100644
index 0000000..3614ba4
--- /dev/null
+++ b/test/typed_wrapper/iterable_test.dart
@@ -0,0 +1,355 @@
+// Copyright (c) 2016, 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 "package:collection/collection.dart";
+import "package:test/test.dart";
+
+import '../utils.dart';
+
+void main() {
+ group("with valid types, forwards", () {
+ var wrapper;
+ var emptyWrapper;
+ var singleWrapper;
+ setUp(() {
+ wrapper = DelegatingIterable.typed/*<int>*/(
+ <Object>[1, 2, 3, 4, 5].map((i) => i));
+ emptyWrapper = DelegatingIterable.typed/*<int>*/(
+ <Object>[].map((i) => i));
+ singleWrapper = DelegatingIterable.typed/*<int>*/(
+ <Object>[1].map((i) => i));
+ });
+
+ test("any()", () {
+ expect(wrapper.any((i) => i > 3), isTrue);
+ expect(wrapper.any((i) => i > 5), isFalse);
+ });
+
+ test("contains()", () {
+ expect(wrapper.contains(2), isTrue);
+ expect(wrapper.contains(6), isFalse);
+ expect(wrapper.contains("foo"), isFalse);
+ });
+
+ test("elementAt()", () {
+ expect(wrapper.elementAt(1), equals(2));
+ expect(wrapper.elementAt(4), equals(5));
+ expect(() => wrapper.elementAt(5), throwsRangeError);
+ expect(() => wrapper.elementAt(-1), throwsRangeError);
+ });
+
+ test("every()", () {
+ expect(wrapper.every((i) => i < 6), isTrue);
+ expect(wrapper.every((i) => i > 3), isFalse);
+ });
+
+ test("expand()", () {
+ expect(wrapper.expand((i) => [i]), equals([1, 2, 3, 4, 5]));
+ expect(wrapper.expand((i) => [i, i]),
+ equals([1, 1, 2, 2, 3, 3, 4, 4, 5, 5]));
+ });
+
+ test("first", () {
+ expect(wrapper.first, equals(1));
+ expect(() => emptyWrapper.first, throwsStateError);
+ });
+
+ test("firstWhere()", () {
+ expect(wrapper.firstWhere((i) => i > 3), equals(4));
+ expect(() => wrapper.firstWhere((i) => i > 5), throwsStateError);
+ expect(wrapper.firstWhere((i) => i > 5, orElse: () => -1), equals(-1));
+ });
+
+ test("fold()", () {
+ expect(wrapper.fold("", (previous, i) => previous + i.toString()),
+ equals("12345"));
+ expect(emptyWrapper.fold(null, (previous, i) => previous + i), isNull);
+ });
+
+ test("forEach()", () {
+ var results = [];
+ wrapper.forEach(results.add);
+ expect(results, equals([1, 2, 3, 4, 5]));
+
+ emptyWrapper.forEach(expectAsync((_) {}, count: 0));
+ });
+
+ test("isEmpty", () {
+ expect(wrapper.isEmpty, isFalse);
+ expect(emptyWrapper.isEmpty, isTrue);
+ });
+
+ test("isNotEmpty", () {
+ expect(wrapper.isNotEmpty, isTrue);
+ expect(emptyWrapper.isNotEmpty, isFalse);
+ });
+
+ test("iterator", () {
+ var iterator = wrapper.iterator;
+ expect(iterator.current, isNull);
+ expect(iterator.moveNext(), isTrue);
+ expect(iterator.current, equals(1));
+ expect(iterator.moveNext(), isTrue);
+ expect(iterator.current, equals(2));
+ expect(iterator.moveNext(), isTrue);
+ expect(iterator.current, equals(3));
+ expect(iterator.moveNext(), isTrue);
+ expect(iterator.current, equals(4));
+ expect(iterator.moveNext(), isTrue);
+ expect(iterator.current, equals(5));
+ expect(iterator.moveNext(), isFalse);
+ expect(iterator.current, isNull);
+ });
+
+ test("join()", () {
+ expect(wrapper.join(), "12345");
+ expect(wrapper.join("-"), "1-2-3-4-5");
+ });
+
+ test("last", () {
+ expect(wrapper.last, equals(5));
+ expect(() => emptyWrapper.last, throwsStateError);
+ });
+
+ test("lastWhere()", () {
+ expect(wrapper.lastWhere((i) => i > 3), equals(5));
+ expect(() => wrapper.lastWhere((i) => i > 5), throwsStateError);
+ expect(wrapper.lastWhere((i) => i > 5, orElse: () => -1), equals(-1));
+ });
+
+ test("length", () {
+ expect(wrapper.length, equals(5));
+ expect(emptyWrapper.length, equals(0));
+ });
+
+ test("map()", () {
+ expect(wrapper.map((i) => i + 1), equals([2, 3, 4, 5, 6]));
+ expect(wrapper.map((i) => i / 2), equals([0.5, 1.0, 1.5, 2.0, 2.5]));
+ });
+
+ test("reduce()", () {
+ expect(wrapper.reduce((value, i) => value + i), equals(15));
+ expect(() => emptyWrapper.reduce((value, i) => value + i),
+ throwsStateError);
+ });
+
+ test("single", () {
+ expect(() => wrapper.single, throwsStateError);
+ expect(singleWrapper.single, equals(1));
+ });
+
+ test("singleWhere()", () {
+ expect(() => wrapper.singleWhere((i) => i.isOdd), throwsStateError);
+ expect(singleWrapper.singleWhere((i) => i.isOdd), equals(1));
+ expect(() => singleWrapper.singleWhere((i) => i.isEven),
+ throwsStateError);
+ });
+
+ test("skip()", () {
+ expect(wrapper.skip(3), equals([4, 5]));
+ expect(wrapper.skip(10), isEmpty);
+ expect(() => wrapper.skip(-1), throwsRangeError);
+ });
+
+ test("skipWhile()", () {
+ expect(wrapper.skipWhile((i) => i < 3), equals([3, 4, 5]));
+ expect(wrapper.skipWhile((i) => i < 10), isEmpty);
+ });
+
+ test("take()", () {
+ expect(wrapper.take(3), equals([1, 2, 3]));
+ expect(wrapper.take(10), equals([1, 2, 3, 4, 5]));
+ expect(() => wrapper.take(-1), throwsRangeError);
+ });
+
+ test("takeWhile()", () {
+ expect(wrapper.takeWhile((i) => i < 3), equals([1, 2]));
+ expect(wrapper.takeWhile((i) => i < 10), equals([1, 2, 3, 4, 5]));
+ });
+
+ test("toList()", () {
+ expect(wrapper.toList(), equals([1, 2, 3, 4, 5]));
+ expect(wrapper.toList(growable: false), equals([1, 2, 3, 4, 5]));
+ expect(() => wrapper.toList(growable: false).add(6),
+ throwsUnsupportedError);
+ });
+
+ test("toSet()", () {
+ expect(wrapper.toSet(), unorderedEquals([1, 2, 3, 4, 5]));
+ expect(DelegatingIterable.typed/*<int>*/(<Object>[1, 1, 2, 2]).toSet(),
+ unorderedEquals([1, 2]));
+ });
+
+ test("where()", () {
+ expect(wrapper.where((i) => i.isOdd), equals([1, 3, 5]));
+ expect(wrapper.where((i) => i.isEven), equals([2, 4]));
+ });
+
+ test("toString()", () {
+ expect(wrapper.toString(), equals("(1, 2, 3, 4, 5)"));
+ expect(emptyWrapper.toString(), equals("()"));
+ });
+ });
+
+ group("with invalid types", () {
+ var wrapper;
+ var singleWrapper;
+ setUp(() {
+ wrapper = DelegatingIterable.typed/*<int>*/(
+ <Object>["foo", "bar", "baz"].map((element) => element));
+ singleWrapper = DelegatingIterable.typed/*<int>*/(
+ <Object>["foo"].map((element) => element));
+ });
+
+ group("throws a CastError for", () {
+ test("any()", () {
+ expect(() => wrapper.any(expectAsync((_) => false, count: 0)),
+ throwsCastError);
+ });
+
+ test("elementAt()", () {
+ expect(() => wrapper.elementAt(1), throwsCastError);
+ });
+
+ test("every()", () {
+ expect(() => wrapper.every(expectAsync((_) => false, count: 0)),
+ throwsCastError);
+ });
+
+ test("expand()", () {
+ var lazy = wrapper.expand(expectAsync((_) => [], count: 0));
+ expect(() => lazy.first, throwsCastError);
+ });
+
+ test("first", () {
+ expect(() => wrapper.first, throwsCastError);
+ });
+
+ test("firstWhere()", () {
+ expect(() => wrapper.firstWhere(expectAsync((_) => false, count: 0)),
+ throwsCastError);
+ });
+
+ test("fold()", () {
+ expect(() => wrapper.fold(null, expectAsync((_, __) => null, count: 0)),
+ throwsCastError);
+ });
+
+ test("forEach()", () {
+ expect(() => wrapper.forEach(expectAsync((_) {}, count: 0)),
+ throwsCastError);
+ });
+
+ test("iterator", () {
+ var iterator = wrapper.iterator;
+ expect(iterator.current, isNull);
+ expect(() => iterator.moveNext(), throwsCastError);
+ });
+
+ test("last", () {
+ expect(() => wrapper.last, throwsCastError);
+ });
+
+ test("lastWhere()", () {
+ expect(() => wrapper.lastWhere(expectAsync((_) => false, count: 0)),
+ throwsCastError);
+ });
+
+ test("map()", () {
+ var lazy = wrapper.map(expectAsync((_) => null, count: 0));
+ expect(() => lazy.first, throwsCastError);
+ });
+
+ test("reduce()", () {
+ expect(() => wrapper.reduce(expectAsync((_, __) => null, count: 0)),
+ throwsCastError);
+ });
+
+ test("single", () {
+ expect(() => singleWrapper.single, throwsCastError);
+ });
+
+ test("singleWhere()", () {
+ expect(() {
+ singleWrapper.singleWhere(expectAsync((_) => false, count: 0));
+ }, throwsCastError);
+ });
+
+ test("skip()", () {
+ var lazy = wrapper.skip(1);
+ expect(() => lazy.first, throwsCastError);
+ });
+
+ test("skipWhile()", () {
+ var lazy = wrapper.skipWhile(expectAsync((_) => false, count: 0));
+ expect(() => lazy.first, throwsCastError);
+ });
+
+ test("take()", () {
+ var lazy = wrapper.take(1);
+ expect(() => lazy.first, throwsCastError);
+ });
+
+ test("takeWhile()", () {
+ var lazy = wrapper.takeWhile(expectAsync((_) => false, count: 0));
+ expect(() => lazy.first, throwsCastError);
+ });
+
+ test("toList()", () {
+ var list = wrapper.toList();
+ expect(() => list.first, throwsCastError);
+ });
+
+ test("toSet()", () {
+ var asSet = wrapper.toSet();
+ expect(() => asSet.first, throwsCastError);
+ });
+
+ test("where()", () {
+ var lazy = wrapper.where(expectAsync((_) => false, count: 0));
+ expect(() => lazy.first, throwsCastError);
+ });
+ });
+
+ group("doesn't throw a CastError for", () {
+ test("contains()", () {
+ expect(wrapper.contains(1), isFalse);
+ expect(wrapper.contains("foo"), isTrue);
+ });
+
+ test("elementAt()", () {
+ expect(() => wrapper.elementAt(-1), throwsRangeError);
+ expect(() => wrapper.elementAt(10), throwsRangeError);
+ });
+
+ test("isEmpty", () {
+ expect(wrapper.isEmpty, isFalse);
+ });
+
+ test("isNotEmpty", () {
+ expect(wrapper.isNotEmpty, isTrue);
+ });
+
+ test("join()", () {
+ expect(wrapper.join(), "foobarbaz");
+ });
+
+ test("length", () {
+ expect(wrapper.length, equals(3));
+ });
+
+ test("single", () {
+ expect(() => wrapper.single, throwsStateError);
+ });
+
+ test("skip()", () {
+ expect(() => wrapper.skip(-1), throwsRangeError);
+ });
+
+ test("toString()", () {
+ expect(wrapper.toString(), equals("(foo, bar, baz)"));
+ });
+ });
+ }, skip: "Re-enable this when test can run DDC (test#414).");
+}
diff --git a/test/typed_wrapper/list_test.dart b/test/typed_wrapper/list_test.dart
new file mode 100644
index 0000000..5dd81ca
--- /dev/null
+++ b/test/typed_wrapper/list_test.dart
@@ -0,0 +1,421 @@
+// Copyright (c) 2016, 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' as math;
+
+import "package:collection/collection.dart";
+import "package:test/test.dart";
+
+import '../utils.dart';
+
+void main() {
+ group("with valid types, forwards", () {
+ var wrapper;
+ var emptyWrapper;
+ setUp(() {
+ wrapper = DelegatingList.typed/*<int>*/(<Object>[1, 2, 3, 4, 5]);
+ emptyWrapper = DelegatingList.typed/*<int>*/(<Object>[]);
+ });
+
+ test("[]", () {
+ expect(wrapper[0], equals(1));
+ expect(wrapper[1], equals(2));
+ expect(() => wrapper[-1], throwsRangeError);
+ expect(() => wrapper[10], throwsRangeError);
+ });
+
+ test("[]=", () {
+ wrapper[1] = 10;
+ wrapper[3] = 15;
+ expect(wrapper, equals([1, 10, 3, 15, 5]));
+ expect(() => wrapper[-1] = 10, throwsRangeError);
+ expect(() => wrapper[10] = 10, throwsRangeError);
+ });
+
+ test("add()", () {
+ wrapper.add(6);
+ wrapper.add(7);
+ expect(wrapper, equals([1, 2, 3, 4, 5, 6, 7]));
+ });
+
+ test("addAll()", () {
+ wrapper.addAll([6, 7, 8]);
+ expect(wrapper, equals([1, 2, 3, 4, 5, 6, 7, 8]));
+ });
+
+ test("asMap()", () {
+ expect(wrapper.asMap(), equals({0: 1, 1: 2, 2: 3, 3: 4, 4: 5}));
+ });
+
+ test("clear()", () {
+ wrapper.clear();
+ expect(wrapper, isEmpty);
+ });
+
+ test("fillRange()", () {
+ wrapper.fillRange(2, 4);
+ expect(wrapper, equals([1, 2, null, null, 5]));
+
+ wrapper.fillRange(1, 2, 7);
+ expect(wrapper, equals([1, 7, null, null, 5]));
+
+ expect(() => wrapper.fillRange(-1, 2), throwsRangeError);
+ expect(() => wrapper.fillRange(1, 10), throwsRangeError);
+ expect(() => wrapper.fillRange(4, 2), throwsRangeError);
+ expect(() => wrapper.fillRange(10, 12), throwsRangeError);
+ });
+
+ test("getRange()", () {
+ expect(wrapper.getRange(2, 4), equals([3, 4]));
+ expect(wrapper.getRange(1, 2), equals([2]));
+
+ expect(() => wrapper.getRange(-1, 2), throwsRangeError);
+ expect(() => wrapper.getRange(1, 10), throwsRangeError);
+ expect(() => wrapper.getRange(4, 2), throwsRangeError);
+ expect(() => wrapper.getRange(10, 12), throwsRangeError);
+ });
+
+ test("indexOf()", () {
+ expect(wrapper.indexOf(4), equals(3));
+ expect(wrapper.indexOf(10), equals(-1));
+ expect(wrapper.indexOf(4, 2), equals(3));
+ expect(wrapper.indexOf(4, 4), equals(-1));
+ });
+
+ test("insert()", () {
+ wrapper.insert(3, 10);
+ expect(wrapper, equals([1, 2, 3, 10, 4, 5]));
+
+ wrapper.insert(0, 15);
+ expect(wrapper, equals([15, 1, 2, 3, 10, 4, 5]));
+
+ expect(() => wrapper.insert(-1, 0), throwsRangeError);
+ expect(() => wrapper.insert(10, 0), throwsRangeError);
+ });
+
+ test("insertAll()", () {
+ wrapper.insertAll(3, [10, 11, 12]);
+ expect(wrapper, equals([1, 2, 3, 10, 11, 12, 4, 5]));
+
+ wrapper.insertAll(0, [15, 16, 17]);
+ expect(wrapper, equals([15, 16, 17, 1, 2, 3, 10, 11, 12, 4, 5]));
+
+ expect(() => wrapper.insertAll(-1, []), throwsRangeError);
+ expect(() => wrapper.insertAll(100, []), throwsRangeError);
+ });
+
+ test("lastIndexOf()", () {
+ expect(wrapper.lastIndexOf(4), equals(3));
+ expect(wrapper.lastIndexOf(10), equals(-1));
+ expect(wrapper.lastIndexOf(4, 4), equals(3));
+ expect(wrapper.lastIndexOf(4, 2), equals(-1));
+ });
+
+ test("length=", () {
+ wrapper.length = 10;
+ expect(wrapper, equals([1, 2, 3, 4, 5, null, null, null, null, null]));
+
+ wrapper.length = 3;
+ expect(wrapper, equals([1, 2, 3]));
+ });
+
+ test("remove()", () {
+ expect(wrapper.remove(3), isTrue);
+ expect(wrapper, equals([1, 2, 4, 5]));
+
+ expect(wrapper.remove(3), isFalse);
+ expect(wrapper.remove("foo"), isFalse);
+ });
+
+ test("removeAt()", () {
+ expect(wrapper.removeAt(3), equals(4));
+ expect(wrapper, equals([1, 2, 3, 5]));
+
+ expect(() => wrapper.removeAt(-1), throwsRangeError);
+ expect(() => wrapper.removeAt(10), throwsRangeError);
+ });
+
+ test("removeLast()", () {
+ expect(wrapper.removeLast(), equals(5));
+ expect(wrapper, equals([1, 2, 3, 4]));
+
+ // See sdk#26087. We want this to pass with the current implementation and
+ // with the fix.
+ expect(() => emptyWrapper.removeLast(),
+ anyOf(throwsStateError, throwsRangeError));
+ });
+
+ test("removeRange()", () {
+ wrapper.removeRange(2, 4);
+ expect(wrapper, equals([1, 2, 5]));
+
+ expect(() => wrapper.removeRange(-1, 2), throwsRangeError);
+ expect(() => wrapper.removeRange(1, 10), throwsRangeError);
+ expect(() => wrapper.removeRange(4, 2), throwsRangeError);
+ expect(() => wrapper.removeRange(10, 12), throwsRangeError);
+ });
+
+ test("removeWhere()", () {
+ wrapper.removeWhere((i) => i.isOdd);
+ expect(wrapper, equals([2, 4]));
+ });
+
+ test("replaceRange()", () {
+ wrapper.replaceRange(2, 4, [10, 11, 12]);
+ expect(wrapper, equals([1, 2, 10, 11, 12, 5]));
+
+ expect(() => wrapper.replaceRange(-1, 2, []), throwsRangeError);
+ expect(() => wrapper.replaceRange(1, 10, []), throwsRangeError);
+ expect(() => wrapper.replaceRange(4, 2, []), throwsRangeError);
+ expect(() => wrapper.replaceRange(10, 12, []), throwsRangeError);
+ });
+
+ test("retainWhere()", () {
+ wrapper.retainWhere((i) => i.isOdd);
+ expect(wrapper, equals([1, 3, 5]));
+ });
+
+ test("reversed", () {
+ expect(wrapper.reversed, equals([5, 4, 3, 2, 1]));
+ });
+
+ test("setAll()", () {
+ wrapper.setAll(2, [10, 11]);
+ expect(wrapper, equals([1, 2, 10, 11, 5]));
+
+ expect(() => wrapper.setAll(-1, []), throwsRangeError);
+ expect(() => wrapper.setAll(10, []), throwsRangeError);
+ });
+
+ test("setRange()", () {
+ wrapper.setRange(2, 4, [10, 11, 12]);
+ expect(wrapper, equals([1, 2, 10, 11, 5]));
+
+ wrapper.setRange(2, 4, [10, 11, 12], 1);
+ expect(wrapper, equals([1, 2, 11, 12, 5]));
+
+ expect(() => wrapper.setRange(-1, 2, []), throwsRangeError);
+ expect(() => wrapper.setRange(1, 10, []), throwsRangeError);
+ expect(() => wrapper.setRange(4, 2, []), throwsRangeError);
+ expect(() => wrapper.setRange(10, 12, []), throwsRangeError);
+ expect(() => wrapper.setRange(2, 4, []), throwsStateError);
+ });
+
+ test("shuffle()", () {
+ wrapper.shuffle(new math.Random(1234));
+ expect(wrapper, equals([1, 2, 3, 4, 5]..shuffle(new math.Random(1234))));
+ });
+
+ test("sort()", () {
+ wrapper.sort((a, b) => b.compareTo(a));
+ expect(wrapper, equals([5, 4, 3, 2, 1]));
+
+ wrapper.sort();
+ expect(wrapper, equals([1, 2, 3, 4, 5]));
+ });
+
+ test("sublist()", () {
+ expect(wrapper.sublist(2), equals([3, 4, 5]));
+ expect(wrapper.sublist(2, 4), equals([3, 4]));
+ });
+ });
+
+ group("with invalid types", () {
+ var inner;
+ var wrapper;
+ setUp(() {
+ inner = <Object>["foo", "bar", "baz"];
+ wrapper = DelegatingList.typed/*<int>*/(inner);
+ });
+
+ group("throws a CastError for", () {
+ test("[]", () {
+ expect(() => wrapper[0], throwsCastError);
+ });
+
+ test("asMap()", () {
+ var map = wrapper.asMap();
+ expect(() => map[1], throwsCastError);
+ });
+
+ test("getRange()", () {
+ var lazy = wrapper.getRange(1, 2);
+ expect(() => lazy.first, throwsCastError);
+ });
+
+ test("removeAt()", () {
+ expect(() => wrapper.removeAt(2), throwsCastError);
+ });
+
+ test("removeLast()", () {
+ expect(() => wrapper.removeLast(), throwsCastError);
+ });
+
+ test("removeWhere()", () {
+ expect(() => wrapper.removeWhere(expectAsync((_) => false, count: 0)),
+ throwsCastError);
+ });
+
+ test("retainWhere()", () {
+ expect(() => wrapper.retainWhere(expectAsync((_) => false, count: 0)),
+ throwsCastError);
+ });
+
+ test("reversed", () {
+ var lazy = wrapper.reversed;
+ expect(() => lazy.first, throwsCastError);
+ });
+
+ test("sort()", () {
+ expect(() => wrapper.sort(expectAsync((_, __) => 0, count: 0)),
+ throwsCastError);
+ });
+
+ test("sublist()", () {
+ var list = wrapper.sublist(1);
+ expect(() => list[0], throwsCastError);
+ });
+ });
+
+ group("doesn't throw a CastError for", () {
+ test("[]", () {
+ expect(() => wrapper[-1], throwsRangeError);
+ expect(() => wrapper[10], throwsRangeError);
+ });
+
+ test("[]=", () {
+ wrapper[1] = 10;
+ expect(inner, equals(["foo", 10, "baz"]));
+ expect(() => wrapper[-1] = 10, throwsRangeError);
+ expect(() => wrapper[10] = 10, throwsRangeError);
+ });
+
+ test("add()", () {
+ wrapper.add(6);
+ wrapper.add(7);
+ expect(inner, equals(["foo", "bar", "baz", 6, 7]));
+ });
+
+ test("addAll()", () {
+ wrapper.addAll([6, 7, 8]);
+ expect(inner, equals(["foo", "bar", "baz", 6, 7, 8]));
+ });
+
+ test("clear()", () {
+ wrapper.clear();
+ expect(wrapper, isEmpty);
+ });
+
+ test("fillRange()", () {
+ wrapper.fillRange(1, 3, 7);
+ expect(inner, equals(["foo", 7, 7]));
+
+ expect(() => wrapper.fillRange(-1, 2), throwsRangeError);
+ expect(() => wrapper.fillRange(1, 10), throwsRangeError);
+ expect(() => wrapper.fillRange(4, 2), throwsRangeError);
+ expect(() => wrapper.fillRange(10, 12), throwsRangeError);
+ });
+
+ test("getRange()", () {
+ expect(() => wrapper.getRange(-1, 2), throwsRangeError);
+ expect(() => wrapper.getRange(1, 10), throwsRangeError);
+ expect(() => wrapper.getRange(4, 2), throwsRangeError);
+ expect(() => wrapper.getRange(10, 12), throwsRangeError);
+ });
+
+ test("indexOf()", () {
+ expect(wrapper.indexOf(4), equals(-1));
+ });
+
+ test("insert()", () {
+ wrapper.insert(0, 15);
+ expect(inner, equals([15, "foo", "bar", "baz"]));
+
+ expect(() => wrapper.insert(-1, 0), throwsRangeError);
+ expect(() => wrapper.insert(10, 0), throwsRangeError);
+ });
+
+ test("insertAll()", () {
+ wrapper.insertAll(0, [15, 16, 17]);
+ expect(inner, equals([15, 16, 17, "foo", "bar", "baz"]));
+
+ expect(() => wrapper.insertAll(-1, []), throwsRangeError);
+ expect(() => wrapper.insertAll(100, []), throwsRangeError);
+ });
+
+ test("lastIndexOf()", () {
+ expect(wrapper.lastIndexOf(4), equals(-1));
+ });
+
+ test("length=", () {
+ wrapper.length = 5;
+ expect(inner, equals(["foo", "bar", "baz", null, null]));
+
+ wrapper.length = 1;
+ expect(inner, equals(["foo"]));
+ });
+
+ test("remove()", () {
+ expect(wrapper.remove(3), isFalse);
+ expect(wrapper.remove("foo"), isTrue);
+ expect(inner, equals(["bar", "baz"]));
+ });
+
+ test("removeAt()", () {
+ expect(() => wrapper.removeAt(-1), throwsRangeError);
+ expect(() => wrapper.removeAt(10), throwsRangeError);
+ });
+
+ test("removeRange()", () {
+ wrapper.removeRange(1, 3);
+ expect(inner, equals(["foo"]));
+
+ expect(() => wrapper.removeRange(-1, 2), throwsRangeError);
+ expect(() => wrapper.removeRange(1, 10), throwsRangeError);
+ expect(() => wrapper.removeRange(4, 2), throwsRangeError);
+ expect(() => wrapper.removeRange(10, 12), throwsRangeError);
+ });
+
+ test("replaceRange()", () {
+ wrapper.replaceRange(1, 2, [10, 11, 12]);
+ expect(inner, equals(["foo", 10, 11, 12, "baz"]));
+
+ expect(() => wrapper.replaceRange(-1, 2, []), throwsRangeError);
+ expect(() => wrapper.replaceRange(1, 10, []), throwsRangeError);
+ expect(() => wrapper.replaceRange(4, 2, []), throwsRangeError);
+ expect(() => wrapper.replaceRange(10, 12, []), throwsRangeError);
+ });
+
+ test("setAll()", () {
+ wrapper.setAll(1, [10, 11]);
+ expect(inner, equals(["foo", 10, 11]));
+
+ expect(() => wrapper.setAll(-1, []), throwsRangeError);
+ expect(() => wrapper.setAll(10, []), throwsRangeError);
+ });
+
+ test("setRange()", () {
+ wrapper.setRange(1, 2, [10, 11, 12]);
+ expect(inner, equals(["foo", 10, "baz"]));
+
+ expect(() => wrapper.setRange(-1, 2, []), throwsRangeError);
+ expect(() => wrapper.setRange(1, 10, []), throwsRangeError);
+ expect(() => wrapper.setRange(4, 2, []), throwsRangeError);
+ expect(() => wrapper.setRange(10, 12, []), throwsRangeError);
+ expect(() => wrapper.setRange(1, 2, []), throwsStateError);
+ });
+
+ test("shuffle()", () {
+ wrapper.shuffle(new math.Random(1234));
+ expect(inner,
+ equals(["foo", "bar", "baz"]..shuffle(new math.Random(1234))));
+ });
+
+ test("sort()", () {
+ wrapper.sort();
+ expect(inner, equals(["bar", "baz", "foo"]));
+ });
+ });
+ }, skip: "Re-enable this when test can run DDC (test#414).");
+}
diff --git a/test/typed_wrapper/map_test.dart b/test/typed_wrapper/map_test.dart
new file mode 100644
index 0000000..3546ae8
--- /dev/null
+++ b/test/typed_wrapper/map_test.dart
@@ -0,0 +1,315 @@
+// Copyright (c) 2016, 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 "package:collection/collection.dart";
+import "package:test/test.dart";
+
+import '../utils.dart';
+
+void main() {
+ group("with valid types, forwards", () {
+ var wrapper;
+ var emptyWrapper;
+ setUp(() {
+ wrapper = DelegatingMap.typed/*<String, int>*/(
+ <Object, Object>{"foo": 1, "bar": 2, "baz": 3, "bang": 4});
+ emptyWrapper = DelegatingMap.typed/*<String, int>*/(<Object, Object>{});
+ });
+
+ test("[]", () {
+ expect(wrapper["foo"], equals(1));
+ expect(wrapper["bar"], equals(2));
+ expect(wrapper["qux"], isNull);
+ expect(wrapper[1], isNull);
+ });
+
+ test("[]=", () {
+ wrapper["foo"] = 5;
+ expect(wrapper, equals({"foo": 5, "bar": 2, "baz": 3, "bang": 4}));
+
+ wrapper["qux"] = 6;
+ expect(wrapper,
+ equals({"foo": 5, "bar": 2, "baz": 3, "bang": 4, "qux": 6}));
+ });
+
+ test("addAll()", () {
+ wrapper.addAll({"bar": 5, "qux": 6});
+ expect(wrapper,
+ equals({"foo": 1, "bar": 5, "baz": 3, "bang": 4, "qux": 6}));
+ });
+
+ test("clear()", () {
+ wrapper.clear();
+ expect(wrapper, isEmpty);
+ });
+
+ test("containsKey()", () {
+ expect(wrapper.containsKey("foo"), isTrue);
+ expect(wrapper.containsKey("qux"), isFalse);
+ expect(wrapper.containsKey(1), isFalse);
+ });
+
+ test("containsValue()", () {
+ expect(wrapper.containsValue(1), isTrue);
+ expect(wrapper.containsValue(7), isFalse);
+ expect(wrapper.containsValue("foo"), isFalse);
+ });
+
+ test("forEach()", () {
+ var results = [];
+ wrapper.forEach((key, value) => results.add([key, value]));
+ expect(results,
+ unorderedEquals([["foo", 1], ["bar", 2], ["baz", 3], ["bang", 4]]));
+
+ emptyWrapper.forEach(expectAsync((_, __) {}, count: 0));
+ });
+
+ test("isEmpty", () {
+ expect(wrapper.isEmpty, isFalse);
+ expect(emptyWrapper.isEmpty, isTrue);
+ });
+
+ test("isNotEmpty", () {
+ expect(wrapper.isNotEmpty, isTrue);
+ expect(emptyWrapper.isNotEmpty, isFalse);
+ });
+
+ test("keys", () {
+ expect(wrapper.keys, unorderedEquals(["foo", "bar", "baz", "bang"]));
+ expect(emptyWrapper.keys, isEmpty);
+ });
+
+ test("length", () {
+ expect(wrapper.length, equals(4));
+ expect(emptyWrapper.length, equals(0));
+ });
+
+ test("putIfAbsent()", () {
+ expect(wrapper.putIfAbsent("foo", expectAsync(() => null, count: 0)),
+ equals(1));
+
+ expect(wrapper.putIfAbsent("qux", () => 6), equals(6));
+ expect(wrapper,
+ equals({"foo": 1, "bar": 2, "baz": 3, "bang": 4, "qux": 6}));
+ });
+
+ test("remove()", () {
+ expect(wrapper.remove("foo"), equals(1));
+ expect(wrapper, equals({"bar": 2, "baz": 3, "bang": 4}));
+
+ expect(wrapper.remove("foo"), isNull);
+ expect(wrapper.remove(3), isNull);
+ });
+
+ test("values", () {
+ expect(wrapper.values, unorderedEquals([1, 2, 3, 4]));
+ expect(emptyWrapper.values, isEmpty);
+ });
+
+ test("toString()", () {
+ expect(wrapper.toString(), allOf([
+ startsWith("{"),
+ contains("foo: 1"),
+ contains("bar: 2"),
+ contains("baz: 3"),
+ contains("bang: 4"),
+ endsWith("}")
+ ]));
+ });
+ });
+
+ group("with invalid key types", () {
+ var inner;
+ var wrapper;
+ setUp(() {
+ inner = <Object, Object>{1: 1, 2: 2, 3: 3, 4: 4};
+ wrapper = DelegatingMap.typed/*<String, int>*/(inner);
+ });
+
+ group("throws a CastError for", () {
+ test("forEach()", () {
+ expect(() => wrapper.forEach(expectAsync((_, __) {}, count: 0)),
+ throwsCastError);
+ });
+
+ test("keys", () {
+ var lazy = wrapper.keys;
+ expect(() => lazy.first, throwsCastError);
+ });
+ });
+
+ group("doesn't throw a CastError for", () {
+ test("[]", () {
+ expect(wrapper["foo"], isNull);
+ expect(wrapper[1], equals(1));
+ expect(wrapper[7], isNull);
+ });
+
+ test("[]=", () {
+ wrapper["foo"] = 5;
+ expect(inner, equals({"foo": 5, 1: 1, 2: 2, 3: 3, 4: 4}));
+ });
+
+ test("addAll()", () {
+ wrapper.addAll({"foo": 1, "bar": 2});
+ expect(inner, equals({"foo": 1, "bar": 2, 1: 1, 2: 2, 3: 3, 4: 4}));
+ });
+
+ test("clear()", () {
+ wrapper.clear();
+ expect(wrapper, isEmpty);
+ });
+
+ test("containsKey()", () {
+ expect(wrapper.containsKey(1), isTrue);
+ expect(wrapper.containsKey(7), isFalse);
+ expect(wrapper.containsKey("foo"), isFalse);
+ });
+
+ test("containsValue()", () {
+ expect(wrapper.containsValue(1), isTrue);
+ expect(wrapper.containsValue(7), isFalse);
+ expect(wrapper.containsValue("foo"), isFalse);
+ });
+
+ test("isEmpty", () {
+ expect(wrapper.isEmpty, isFalse);
+ });
+
+ test("isNotEmpty", () {
+ expect(wrapper.isNotEmpty, isTrue);
+ });
+
+ test("length", () {
+ expect(wrapper.length, equals(4));
+ });
+
+ test("putIfAbsent()", () {
+ expect(wrapper.putIfAbsent("foo", () => 1), equals(1));
+ expect(inner, equals({"foo": 1, 1: 1, 2: 2, 3: 3, 4: 4}));
+ });
+
+ test("remove()", () {
+ expect(wrapper.remove(1), equals(1));
+ expect(inner, equals({2: 2, 3: 3, 4: 4}));
+
+ expect(wrapper.remove("foo"), isNull);
+ expect(wrapper.remove(7), isNull);
+ });
+
+ test("values", () {
+ expect(wrapper.values, unorderedEquals([1, 2, 3, 4]));
+ });
+
+ test("toString()", () {
+ expect(wrapper.toString(), allOf([
+ startsWith("{"),
+ contains("1: 1"),
+ contains("2: 2"),
+ contains("3: 3"),
+ contains("4: 4"),
+ endsWith("}")
+ ]));
+ });
+ });
+ }, skip: "Re-enable this when test can run DDC (test#414).");
+
+ group("with invalid value types", () {
+ var inner;
+ var wrapper;
+ setUp(() {
+ inner = <Object, Object>{"foo": "bar", "baz": "bang"};
+ wrapper = DelegatingMap.typed/*<String, int>*/(inner);
+ });
+
+ group("throws a CastError for", () {
+ test("forEach()", () {
+ expect(() => wrapper.forEach(expectAsync((_, __) {}, count: 0)),
+ throwsCastError);
+ });
+
+ test("[]", () {
+ expect(() => wrapper["foo"], throwsCastError);
+ expect(wrapper["qux"], isNull);
+ });
+
+ test("putIfAbsent()", () {
+ expect(() => wrapper.putIfAbsent("foo", () => 1), throwsCastError);
+ });
+
+ test("remove()", () {
+ expect(() => wrapper.remove("foo"), throwsCastError);
+ });
+
+ test("values", () {
+ var lazy = wrapper.values;
+ expect(() => lazy.first, throwsCastError);
+ });
+ });
+
+ group("doesn't throw a CastError for", () {
+ test("[]=", () {
+ wrapper["foo"] = 5;
+ expect(inner, equals({"foo": 5, "baz": "bang"}));
+ });
+
+ test("addAll()", () {
+ wrapper.addAll({"foo": 1, "qux": 2});
+ expect(inner, equals({"foo": 1, "baz": "bang", "qux": 2}));
+ });
+
+ test("clear()", () {
+ wrapper.clear();
+ expect(wrapper, isEmpty);
+ });
+
+ test("containsKey()", () {
+ expect(wrapper.containsKey("foo"), isTrue);
+ expect(wrapper.containsKey(1), isFalse);
+ expect(wrapper.containsKey("qux"), isFalse);
+ });
+
+ test("containsValue()", () {
+ expect(wrapper.containsValue("bar"), isTrue);
+ expect(wrapper.containsValue(1), isFalse);
+ expect(wrapper.containsValue("foo"), isFalse);
+ });
+
+ test("isEmpty", () {
+ expect(wrapper.isEmpty, isFalse);
+ });
+
+ test("isNotEmpty", () {
+ expect(wrapper.isNotEmpty, isTrue);
+ });
+
+ test("keys", () {
+ expect(wrapper.keys, unorderedEquals(["foo", "baz"]));
+ });
+
+ test("length", () {
+ expect(wrapper.length, equals(2));
+ });
+
+ test("putIfAbsent()", () {
+ expect(wrapper.putIfAbsent("qux", () => 1), equals(1));
+ expect(inner, equals({"foo": "bar", "baz": "bang", "qux": 1}));
+ });
+
+ test("remove()", () {
+ expect(wrapper.remove("qux"), isNull);
+ expect(wrapper.remove(7), isNull);
+ });
+
+ test("toString()", () {
+ expect(wrapper.toString(), allOf([
+ startsWith("{"),
+ contains("foo: bar"),
+ contains("baz: bang"),
+ endsWith("}")
+ ]));
+ });
+ });
+ }, skip: "Re-enable this when test can run DDC (test#414).");
+}
diff --git a/test/typed_wrapper/queue_test.dart b/test/typed_wrapper/queue_test.dart
new file mode 100644
index 0000000..dc93321
--- /dev/null
+++ b/test/typed_wrapper/queue_test.dart
@@ -0,0 +1,141 @@
+// Copyright (c) 2016, 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:collection";
+
+import "package:collection/collection.dart";
+import "package:test/test.dart";
+
+import '../utils.dart';
+
+void main() {
+ group("with valid types, forwards", () {
+ var wrapper;
+ var emptyWrapper;
+ setUp(() {
+ wrapper = DelegatingQueue.typed/*<int>*/(
+ new Queue<Object>.from([1, 2, 3, 4, 5]));
+ emptyWrapper = DelegatingQueue.typed/*<int>*/(new Queue<Object>());
+ });
+
+ test("add()", () {
+ wrapper.add(6);
+ wrapper.add(7);
+ expect(wrapper, equals([1, 2, 3, 4, 5, 6, 7]));
+ });
+
+ test("addAll()", () {
+ wrapper.addAll([6, 7, 8]);
+ expect(wrapper, equals([1, 2, 3, 4, 5, 6, 7, 8]));
+ });
+
+ test("addFirst()", () {
+ wrapper.addFirst(6);
+ wrapper.addFirst(7);
+ expect(wrapper, equals([7, 6, 1, 2, 3, 4, 5]));
+ });
+
+ test("addLast()", () {
+ wrapper.addLast(6);
+ wrapper.addLast(7);
+ expect(wrapper, equals([1, 2, 3, 4, 5, 6, 7]));
+ });
+
+ test("clear()", () {
+ wrapper.clear();
+ expect(wrapper, isEmpty);
+ });
+
+ test("remove()", () {
+ expect(wrapper.remove(3), isTrue);
+ expect(wrapper, equals([1, 2, 4, 5]));
+
+ expect(wrapper.remove(3), isFalse);
+ expect(wrapper.remove("foo"), isFalse);
+ });
+
+ test("removeWhere()", () {
+ wrapper.removeWhere((i) => i.isOdd);
+ expect(wrapper, equals([2, 4]));
+ });
+
+ test("retainWhere()", () {
+ wrapper.retainWhere((i) => i.isOdd);
+ expect(wrapper, equals([1, 3, 5]));
+ });
+
+ test("removeLast()", () {
+ expect(wrapper.removeLast(), equals(5));
+ expect(wrapper, equals([1, 2, 3, 4]));
+
+ expect(() => emptyWrapper.removeLast(), throwsStateError);
+ });
+
+ test("removeFirst()", () {
+ expect(wrapper.removeFirst(), equals(1));
+ expect(wrapper, equals([2, 3, 4, 5]));
+
+ expect(() => emptyWrapper.removeFirst(), throwsStateError);
+ });
+ });
+
+ group("with invalid types", () {
+ var inner;
+ var wrapper;
+ setUp(() {
+ inner = new Queue<Object>.from(["foo", "bar", "baz"]);
+ wrapper = DelegatingQueue.typed/*<int>*/(inner);
+ });
+
+ group("throws a CastError for", () {
+ test("removeLast()", () {
+ expect(() => wrapper.removeLast(), throwsCastError);
+ });
+
+ test("removeFirst()", () {
+ expect(() => wrapper.removeFirst(), throwsCastError);
+ });
+
+ test("removeWhere()", () {
+ expect(() => wrapper.removeWhere(expectAsync((_) => false, count: 0)),
+ throwsCastError);
+ });
+
+ test("retainWhere()", () {
+ expect(() => wrapper.retainWhere(expectAsync((_) => false, count: 0)),
+ throwsCastError);
+ });
+ });
+
+ group("doesn't throw a CastError for", () {
+ test("add()", () {
+ wrapper.add(6);
+ wrapper.add(7);
+ expect(inner, equals(["foo", "bar", "baz", 6, 7]));
+ });
+
+ test("addAll()", () {
+ wrapper.addAll([6, 7, 8]);
+ expect(inner, equals(["foo", "bar", "baz", 6, 7, 8]));
+ });
+
+ test("addFirst()", () {
+ wrapper.addFirst(6);
+ wrapper.addFirst(7);
+ expect(inner, equals([7, 6, "foo", "bar", "baz"]));
+ });
+
+ test("addLast()", () {
+ wrapper.addLast(6);
+ wrapper.addLast(7);
+ expect(inner, equals(["foo", "bar", "baz", 6, 7]));
+ });
+
+ test("clear()", () {
+ wrapper.clear();
+ expect(wrapper, isEmpty);
+ });
+ });
+ }, skip: "Re-enable this when test can run DDC (test#414).");
+}
diff --git a/test/typed_wrapper/set_test.dart b/test/typed_wrapper/set_test.dart
new file mode 100644
index 0000000..c3f84bc
--- /dev/null
+++ b/test/typed_wrapper/set_test.dart
@@ -0,0 +1,176 @@
+// Copyright (c) 2016, 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 "package:collection/collection.dart";
+import "package:test/test.dart";
+
+import '../utils.dart';
+
+void main() {
+ group("with valid types, forwards", () {
+ var wrapper;
+ var emptyWrapper;
+ setUp(() {
+ wrapper = DelegatingSet.typed/*<int>*/(
+ new Set<Object>.from([1, 2, 3, 4, 5]));
+ emptyWrapper = DelegatingSet.typed/*<int>*/(new Set<Object>());
+ });
+
+ test("add()", () {
+ wrapper.add(1);
+ wrapper.add(6);
+ expect(wrapper, unorderedEquals([1, 2, 3, 4, 5, 6]));
+ });
+
+ test("addAll()", () {
+ wrapper.addAll([1, 6, 7]);
+ expect(wrapper, unorderedEquals([1, 2, 3, 4, 5, 6, 7]));
+ });
+
+ test("clear()", () {
+ wrapper.clear();
+ expect(wrapper, isEmpty);
+ });
+
+ test("containsAll()", () {
+ expect(wrapper.containsAll([1, 3, 5]), isTrue);
+ expect(wrapper.containsAll([1, 3, 6]), isFalse);
+ expect(wrapper.containsAll([1, 3, "foo"]), isFalse);
+ });
+
+ test("difference()", () {
+ expect(wrapper.difference(new Set.from([1, 3, 6])),
+ unorderedEquals([2, 4, 5]));
+ });
+
+ test("intersection()", () {
+ expect(wrapper.intersection(new Set.from([1, 3, 6, "foo"])),
+ unorderedEquals([1, 3]));
+ });
+
+ test("lookup()", () {
+ expect(wrapper.lookup(1), equals(1));
+ expect(wrapper.lookup(7), isNull);
+ expect(wrapper.lookup("foo"), isNull);
+ });
+
+ test("remove()", () {
+ expect(wrapper.remove(3), isTrue);
+ expect(wrapper, unorderedEquals([1, 2, 4, 5]));
+
+ expect(wrapper.remove(3), isFalse);
+ expect(wrapper.remove("foo"), isFalse);
+ });
+
+ test("removeAll()", () {
+ wrapper.removeAll([1, 3, 6, "foo"]);
+ expect(wrapper, unorderedEquals([2, 4, 5]));
+ });
+
+ test("removeWhere()", () {
+ wrapper.removeWhere((i) => i.isOdd);
+ expect(wrapper, unorderedEquals([2, 4]));
+ });
+
+ test("retainAll()", () {
+ wrapper.retainAll([1, 3, 6, "foo"]);
+ expect(wrapper, unorderedEquals([1, 3]));
+ });
+
+ test("retainWhere()", () {
+ wrapper.retainWhere((i) => i.isOdd);
+ expect(wrapper, unorderedEquals([1, 3, 5]));
+ });
+
+ test("union()", () {
+ expect(wrapper.union(new Set.from([5, 6, 7])),
+ unorderedEquals([1, 2, 3, 4, 5, 6, 7]));
+ });
+ });
+
+ group("with invalid types", () {
+ var inner;
+ var wrapper;
+ setUp(() {
+ inner = new Set<Object>.from(["foo", "bar", "baz"]);
+ wrapper = DelegatingSet.typed/*<int>*/(inner);
+ });
+
+ group("throws a CastError for", () {
+ test("difference()", () {
+ var result = wrapper.difference(new Set.from([1, 3, 6]));
+ expect(() => result.first, throwsCastError);
+ });
+
+ test("intersection()", () {
+ var result = wrapper.intersection(new Set.from([1, 3, 6, "foo"]));
+ expect(() => result.first, throwsCastError);
+ });
+
+ test("lookup()", () {
+ expect(() => wrapper.lookup("foo"), throwsCastError);
+ });
+
+ test("removeWhere()", () {
+ expect(() => wrapper.removeWhere(expectAsync((_) => false, count: 0)),
+ throwsCastError);
+ });
+
+ test("retainWhere()", () {
+ expect(() => wrapper.retainWhere(expectAsync((_) => false, count: 0)),
+ throwsCastError);
+ });
+
+ test("union()", () {
+ var result = wrapper.union(new Set.from([5, 6, 7]));
+ expect(() => result.first, throwsCastError);
+ });
+ });
+
+ group("doesn't throw a CastError for", () {
+ test("add()", () {
+ wrapper.add(6);
+ expect(inner, unorderedEquals(["foo", "bar", "baz", 6]));
+ });
+
+ test("addAll()", () {
+ wrapper.addAll([6, 7]);
+ expect(inner, unorderedEquals(["foo", "bar", "baz", 6, 7]));
+ });
+
+ test("clear()", () {
+ wrapper.clear();
+ expect(wrapper, isEmpty);
+ });
+
+ test("containsAll()", () {
+ expect(wrapper.containsAll(["foo", "bar"]), isTrue);
+ expect(wrapper.containsAll(["foo", "bar", 1]), isFalse);
+ });
+
+ test("lookup()", () {
+ expect(wrapper.lookup(1), isNull);
+ expect(wrapper.lookup("zap"), isNull);
+ });
+
+ test("remove()", () {
+ expect(wrapper.remove("foo"), isTrue);
+ expect(inner, unorderedEquals(["bar", "baz"]));
+
+ expect(wrapper.remove(3), isFalse);
+ expect(wrapper.remove("foo"), isFalse);
+ });
+
+ test("removeAll()", () {
+ wrapper.removeAll([1, "foo", "baz"]);
+ expect(inner, unorderedEquals(["bar"]));
+ });
+
+ test("retainAll()", () {
+ wrapper.retainAll([1, "foo", "baz"]);
+ expect(inner, unorderedEquals(["foo", "baz"]));
+ });
+ });
+ }, skip: "Re-enable this when test can run DDC (test#414).");
+}
diff --git a/test/utils.dart b/test/utils.dart
new file mode 100644
index 0000000..d8ab082
--- /dev/null
+++ b/test/utils.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2016, 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 "package:test/test.dart";
+
+final Matcher throwsCastError = throwsA(new isInstanceOf<CastError>());