mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:02:31 +00:00
c43b1d66b5
Add tests. Change-Id: If27b6c987c944ebb84d43acde012f5b44730c4e7 Reviewed-on: https://dart-review.googlesource.com/45501 Commit-Queue: Lasse R.H. Nielsen <lrn@google.com> Reviewed-by: Erik Ernst <eernst@google.com>
215 lines
5.5 KiB
Dart
215 lines
5.5 KiB
Dart
// Copyright (c) 2018, 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:expect/expect.dart";
|
|
import 'dart:collection';
|
|
import 'dart:convert' show json;
|
|
|
|
Map<String, dynamic> newJsonMap() => json.decode('{}');
|
|
Map<String, dynamic> newJsonMapCustomReviver() =>
|
|
json.decode('{}', reviver: (key, value) => value);
|
|
|
|
void main() {
|
|
test({});
|
|
test(new LinkedHashMap());
|
|
test(new HashMap());
|
|
test(new LinkedHashMap.identity());
|
|
test(new HashMap.identity());
|
|
test(new MapView(new HashMap()));
|
|
test(new MapBaseMap());
|
|
test(new MapMixinMap());
|
|
test(newJsonMap());
|
|
test(newJsonMapCustomReviver());
|
|
testNonNull(new SplayTreeMap());
|
|
testNonNull(new SplayTreeMap(Comparable.compare));
|
|
testNonNull(new MapView(new SplayTreeMap()));
|
|
}
|
|
|
|
void test(Map<Comparable, Object> map) {
|
|
testNonNull(map);
|
|
|
|
// Also works with null keys and values (omitted for splay-tree based maps)
|
|
map.clear();
|
|
map.update(null, unreachable, ifAbsent: () => null);
|
|
Expect.mapEquals({null: null}, map);
|
|
map.update(null, (v) => "$v", ifAbsent: unreachable);
|
|
Expect.mapEquals({null: "null"}, map);
|
|
map.update(null, (v) => null, ifAbsent: unreachable);
|
|
Expect.mapEquals({null: null}, map);
|
|
}
|
|
|
|
void testNonNull(Map<Comparable, Object> map) {
|
|
// Only use literal String keys since JSON maps only accept strings,
|
|
// and literals works with identity-maps, and it's comparable for SplayTreeMap
|
|
// maps.
|
|
Expect.mapEquals({}, map);
|
|
map.update("key1", unreachable, ifAbsent: () => 42);
|
|
Expect.mapEquals({"key1": 42}, map);
|
|
map.clear();
|
|
map["key1"] = 42;
|
|
map.update("key1", (v) => 1 + v, ifAbsent: unreachable);
|
|
Expect.mapEquals({"key1": 43}, map);
|
|
map.clear();
|
|
|
|
// Operations on maps with multiple elements.
|
|
var multi = {
|
|
"k1": 1,
|
|
"k2": 2,
|
|
"k3": 3,
|
|
"k4": 4,
|
|
"k5": 5,
|
|
"k6": 6,
|
|
"k7": 7,
|
|
"k8": 8,
|
|
"k9": 9,
|
|
"k10": 10,
|
|
};
|
|
map.addAll(multi);
|
|
Expect.mapEquals(multi, map);
|
|
map.update("k3", (v) => 13);
|
|
map.update("k6", (v) => 16);
|
|
map.update("k11", unreachable, ifAbsent: () => 21);
|
|
Expect.mapEquals({
|
|
"k1": 1,
|
|
"k2": 2,
|
|
"k3": 13,
|
|
"k4": 4,
|
|
"k5": 5,
|
|
"k6": 16,
|
|
"k7": 7,
|
|
"k8": 8,
|
|
"k9": 9,
|
|
"k10": 10,
|
|
"k11": 21,
|
|
}, map);
|
|
|
|
map.clear();
|
|
map.updateAll((k, v) => throw "unreachable");
|
|
Expect.mapEquals({}, map);
|
|
|
|
map.addAll(multi);
|
|
map.updateAll((k, v) => "$k:$v");
|
|
Expect.mapEquals({
|
|
"k1": "k1:1",
|
|
"k2": "k2:2",
|
|
"k3": "k3:3",
|
|
"k4": "k4:4",
|
|
"k5": "k5:5",
|
|
"k6": "k6:6",
|
|
"k7": "k7:7",
|
|
"k8": "k8:8",
|
|
"k9": "k9:9",
|
|
"k10": "k10:10",
|
|
}, map);
|
|
|
|
map.clear();
|
|
Expect.throws(
|
|
() => map.update("key1", unreachable, ifAbsent: () => throw "expected"),
|
|
(t) => t == "expected");
|
|
|
|
map["key1"] = 42;
|
|
Expect.throws(() => map.update("key1", (_) => throw "expected"),
|
|
(t) => t == "expected");
|
|
|
|
// No ifAbsent means throw if key not there.
|
|
Expect.throws(() => map.update("key-not", unreachable), (e) => e is Error);
|
|
|
|
Expect.throws(() => map.update("key1", null), (e) => e is Error);
|
|
|
|
// Works with null values.
|
|
map.clear();
|
|
map.update("key1", unreachable, ifAbsent: () => null);
|
|
Expect.mapEquals({"key1": null}, map);
|
|
map.update("key1", (v) => "$v", ifAbsent: unreachable);
|
|
Expect.mapEquals({"key1": "null"}, map);
|
|
map.update("key1", (v) => null, ifAbsent: unreachable);
|
|
Expect.mapEquals({"key1": null}, map);
|
|
}
|
|
|
|
// Slow implementation of Map based on MapBase.
|
|
abstract class MapBaseOperations<K, V> {
|
|
final List _keys = <K>[];
|
|
final List _values = <V>[];
|
|
int _modCount = 0;
|
|
|
|
V operator [](Object key) {
|
|
int index = _keys.indexOf(key);
|
|
if (index < 0) return null;
|
|
return _values[index];
|
|
}
|
|
|
|
Iterable<K> get keys => new TestKeyIterable<K>(this);
|
|
|
|
void operator []=(K key, V value) {
|
|
int index = _keys.indexOf(key);
|
|
if (index >= 0) {
|
|
_values[index] = value;
|
|
} else {
|
|
_modCount++;
|
|
_keys.add(key);
|
|
_values.add(value);
|
|
}
|
|
}
|
|
|
|
V remove(Object key) {
|
|
int index = _keys.indexOf(key);
|
|
if (index >= 0) {
|
|
var result = _values[index];
|
|
key = _keys.removeLast();
|
|
var value = _values.removeLast();
|
|
if (index != _keys.length) {
|
|
_keys[index] = key;
|
|
_values[index] = value;
|
|
}
|
|
_modCount++;
|
|
return result;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void clear() {
|
|
// Clear cannot be based on remove, since remove won't remove keys that
|
|
// are not equal to themselves. It will fail the testNaNKeys test.
|
|
_keys.clear();
|
|
_values.clear();
|
|
_modCount++;
|
|
}
|
|
}
|
|
|
|
class MapBaseMap<K, V> = MapBase<K, V> with MapBaseOperations<K, V>;
|
|
class MapMixinMap<K, V> = MapBaseOperations<K, V> with MapMixin<K, V>;
|
|
|
|
class TestKeyIterable<K> extends IterableBase<K> {
|
|
final _map;
|
|
TestKeyIterable(this._map);
|
|
int get length => _map._keys.length;
|
|
Iterator<K> get iterator => new TestKeyIterator<K>(_map);
|
|
}
|
|
|
|
class TestKeyIterator<K> implements Iterator<K> {
|
|
final _map;
|
|
final int _modCount;
|
|
int _index = 0;
|
|
var _current;
|
|
TestKeyIterator(map)
|
|
: _map = map,
|
|
_modCount = map._modCount;
|
|
|
|
bool moveNext() {
|
|
if (_modCount != _map._modCount) {
|
|
throw new ConcurrentModificationError(_map);
|
|
}
|
|
if (_index == _map._keys.length) {
|
|
_current = null;
|
|
return false;
|
|
}
|
|
_current = _map._keys[_index++];
|
|
return true;
|
|
}
|
|
|
|
K get current => _current;
|
|
}
|
|
|
|
Null unreachable([_, __]) => throw "unreachable";
|