blob: dd94220194b1ed6870d756383b7b485160f53390 [file] [log] [blame]
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
library quiver.collection.multimap_test;
import 'package:quiver/collection.dart';
import 'package:test/test.dart';
void main() {
group('Multimap', () {
test('should be a list-backed multimap', () {
var map = new Multimap();
expect(map is ListMultimap, true);
});
});
group('Multimap.fromIterable', () {
test('should default to the identity for key and value', () {
var map = new Multimap<int, int>.fromIterable([1, 2, 1]);
expect(map.asMap(), {
1: [1, 1],
2: [2],
});
});
test('should allow setting value', () {
var i = 0;
var map = new Multimap<int, String>.fromIterable([1, 2, 1],
value: (x) => '$x:${i++}');
expect(map.asMap(), {
1: ['1:0', '1:2'],
2: ['2:1'],
});
});
test('should allow setting key', () {
var map =
new Multimap<String, int>.fromIterable([1, 2, 1], key: (x) => '($x)');
expect(map.asMap(), {
'(1)': [1, 1],
'(2)': [2],
});
});
test('should allow setting both key and value', () {
var i = 0;
var map = new Multimap<int, String>.fromIterable([1, 2, 1],
key: (x) => -x, value: (x) => '$x:${i++}');
expect(map.asMap(), {
-1: ['1:0', '1:2'],
-2: ['2:1'],
});
});
});
group('Multimap asMap() view', () {
var mmap;
var map;
setUp(() {
mmap = new Multimap()..add('k1', 'v1')..add('k1', 'v2')..add('k2', 'v3');
map = mmap.asMap();
});
test('operator[]= should throw UnsupportedError', () {
expect(() => map['k1'] = [1, 2, 3], throwsUnsupportedError);
});
test('containsKey() should return false for missing key', () {
expect(map.containsKey('k3'), isFalse);
});
test('containsKey() should return true for key in map', () {
expect(map.containsKey('k1'), isTrue);
});
test('containsValue() should return false for missing value', () {
expect(map.containsValue('k3'), isFalse);
});
test('containsValue() should return true for value in map', () {
expect(map.containsValue('v1'), isTrue);
});
test('forEach should iterate over all key-value pairs', () {
var results = [];
map.forEach((k, v) => results.add(new Pair(k, v)));
expect(
results,
unorderedEquals([
new Pair('k1', ['v1', 'v2']),
new Pair('k2', ['v3'])
]));
});
test('isEmpty should return whether the map contains key-value pairs', () {
expect(map.isEmpty, isFalse);
expect(map.isNotEmpty, isTrue);
expect(new Multimap().asMap().isEmpty, isTrue);
expect(new Multimap().asMap().isNotEmpty, isFalse);
});
test('length should return the number of key-value pairs', () {
expect(new Multimap().asMap().length, equals(0));
expect(map.length, equals(2));
});
test('addAll(Map m) should throw UnsupportedError', () {
expect(
() => map.addAll({
'k1': [1, 2, 3]
}),
throwsUnsupportedError);
});
test('putIfAbsent() should throw UnsupportedError', () {
var map = new Multimap().asMap();
expect(() => map.putIfAbsent('k1', () => [1]), throwsUnsupportedError);
});
});
group('ListMultimap', () {
test('should initialize empty', () {
var map = new ListMultimap();
expect(map.isEmpty, true);
expect(map.isNotEmpty, false);
});
test('should not be empty after adding', () {
var map = new ListMultimap<String, String>()..add('k', 'v');
expect(map.isEmpty, false);
expect(map.isNotEmpty, true);
});
test('should return the number of keys as length', () {
var map = new ListMultimap<String, String>();
expect(map.length, 0);
map..add('k1', 'v1')..add('k1', 'v2')..add('k2', 'v3');
expect(map.length, 2);
});
test('should return an empty iterable for unmapped keys', () {
var map = new ListMultimap<String, String>();
expect(map['k1'], []);
});
test('should support adding values for unmapped keys', () {
var map = new ListMultimap<String, String>()..['k1'].add('v1');
expect(map['k1'], ['v1']);
});
test('should support adding multiple values for unmapped keys', () {
var map = new ListMultimap<String, String>()..['k1'].addAll(['v1', 'v2']);
expect(map['k1'], ['v1', 'v2']);
});
test('should support inserting values for unmapped keys', () {
var map = new ListMultimap<String, String>()..['k1'].insert(0, 'v1');
expect(map['k1'], ['v1']);
});
test('should support inserting multiple values for unmapped keys', () {
var map = new ListMultimap<String, String>()
..['k1'].insertAll(0, ['v1', 'v2']);
expect(map['k1'], ['v1', 'v2']);
});
test('should support inserting multiple values for unmapped keys', () {
var map = new ListMultimap<String, String>()..['k1'].length = 2;
expect(map['k1'], [null, null]);
});
test('should return unmapped iterables that stay in sync on add', () {
var map = new ListMultimap<String, String>();
List values1 = map['k1'];
List values2 = map['k1'];
values1.add('v1');
expect(map['k1'], ['v1']);
expect(values2, ['v1']);
});
test('should return unmapped iterables that stay in sync on addAll', () {
var map = new ListMultimap<String, String>();
List values1 = map['k1'];
List values2 = map['k1'];
values1.addAll(['v1', 'v2']);
expect(map['k1'], ['v1', 'v2']);
expect(values2, ['v1', 'v2']);
});
test('should support adding duplicate values for a key', () {
var map = new ListMultimap<String, String>()
..add('k', 'v1')
..add('k', 'v1');
expect(map['k'], ['v1', 'v1']);
});
test(
'should support adding duplicate values for a key when initialized '
'from an iterable', () {
var map = new ListMultimap<String, String>.fromIterable(['k', 'k'],
value: (x) => 'v1');
expect(map['k'], ['v1', 'v1']);
});
test('should support adding multiple keys', () {
var map = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
expect(map['k1'], ['v1', 'v2']);
expect(map['k2'], ['v3']);
});
test('should support adding multiple values at once', () {
var map = new ListMultimap<String, String>()
..addValues('k1', ['v1', 'v2']);
expect(map['k1'], ['v1', 'v2']);
});
test('should support adding multiple values at once for existing keys', () {
var map = new ListMultimap<String, String>()
..add('k1', 'v1')
..addValues('k1', ['v1', 'v2']);
expect(map['k1'], ['v1', 'v1', 'v2']);
});
test('should support adding from another multimap', () {
var from = new ListMultimap<String, String>()
..addValues('k1', ['v1', 'v2'])
..add('k2', 'v3');
var map = new ListMultimap<String, String>()..addAll(from);
expect(map['k1'], ['v1', 'v2']);
expect(map['k2'], ['v3']);
});
test('should support adding from another multimap with existing keys', () {
var from = new ListMultimap<String, String>()
..addValues('k1', ['v1', 'v2'])
..add('k2', 'v3');
var map = new ListMultimap<String, String>()
..add('k1', 'v0')
..add('k2', 'v3')
..addAll(from);
expect(map['k1'], ['v0', 'v1', 'v2']);
expect(map['k2'], ['v3', 'v3']);
});
test('should return its keys', () {
var map = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
expect(map.keys, unorderedEquals(['k1', 'k2']));
});
test('should return its values', () {
var map = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
expect(map.values, unorderedEquals(['v1', 'v2', 'v3']));
});
test('should support duplicate values', () {
var map = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v1');
expect(map.values, unorderedEquals(['v1', 'v2', 'v1']));
});
test('should return an ordered list of values', () {
var map = new ListMultimap<String, String>()
..add('k', 'v1')
..add('k', 'v2');
expect(map['k'], ['v1', 'v2']);
});
test('should reflect changes to underlying list', () {
var map = new ListMultimap<String, String>()
..add('k', 'v1')
..add('k', 'v2');
map['k'].add('v3');
map['k'].remove('v2');
expect(map['k'], ['v1', 'v3']);
});
test('should return whether it contains a key', () {
var map = new ListMultimap<String, String>()
..add('k', 'v1')
..add('k', 'v2');
expect(map.containsKey('j'), false);
expect(map.containsKey('k'), true);
});
test('should return whether it contains a value', () {
var map = new ListMultimap<String, String>()
..add('k', 'v1')
..add('k', 'v2');
expect(map.containsValue('v0'), false);
expect(map.containsValue('v1'), true);
});
test('should remove specified key-value associations', () {
var map = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
expect(map.remove('k1', 'v0'), false);
expect(map.remove('k1', 'v1'), true);
expect(map['k1'], ['v2']);
expect(map.containsKey('k2'), true);
});
test('should remove a key when all associated values are removed', () {
var map = new ListMultimap<String, String>()
..add('k1', 'v1')
..remove('k1', 'v1');
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.remove', () {
var map = new ListMultimap<String, String>()..add('k1', 'v1');
map['k1'].remove('v1');
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.removeAt', () {
var map = new ListMultimap<String, String>()..add('k1', 'v1');
map['k1'].removeAt(0);
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.removeAt', () {
var map = new ListMultimap<String, String>()..add('k1', 'v1');
map['k1'].removeLast();
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.removeRange', () {
var map = new ListMultimap<String, String>()..add('k1', 'v1');
map['k1'].removeRange(0, 1);
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.removeWhere', () {
var map = new ListMultimap<String, String>()..add('k1', 'v1');
map['k1'].removeWhere((_) => true);
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.replaceRange', () {
var map = new ListMultimap<String, String>()..add('k1', 'v1');
map['k1'].replaceRange(0, 1, []);
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.retainWhere', () {
var map = new ListMultimap<String, String>()..add('k1', 'v1');
map['k1'].retainWhere((_) => false);
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.clear', () {
var map = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2');
map['k1'].clear();
expect(map.containsKey('k1'), false);
});
test('should remove all values for a key', () {
var map = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
expect(map.removeAll('k1'), ['v1', 'v2']);
expect(map.containsKey('k1'), false);
expect(map.containsKey('k2'), true);
});
test('should clear underlying iterable on remove', () {
var map = new ListMultimap<String, String>()..add('k1', 'v1');
List values = map['k1'];
expect(map.removeAll('k1'), ['v1']);
expect(values, []);
});
test('should return an empty iterable on removeAll of unmapped key', () {
var map = new ListMultimap<String, String>();
var removed = map.removeAll('k1');
expect(removed, []);
});
test('should be uncoupled from the iterable returned by removeAll', () {
var map = new ListMultimap<String, String>()..add('k1', 'v1');
var removed = map.removeAll('k1');
removed.add('v2');
map.add('k1', 'v3');
expect(removed, ['v1', 'v2']);
expect(map['k1'], ['v3']);
});
test('should clear the map', () {
var map = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3')
..clear();
expect(map.isEmpty, true);
expect(map.containsKey('k1'), false);
expect(map.containsKey('k2'), false);
});
test('should clear underlying iterables on clear', () {
var map = new ListMultimap<String, String>()..add('k1', 'v1');
List values = map['k1'];
map.clear();
expect(values, []);
});
test('should not add mappings on lookup of unmapped keys', () {
var map = new ListMultimap<String, String>()..['k1'];
expect(map.containsKey('k1'), false);
});
test('should not remove mappings on clearing mapped values', () {
var map = new ListMultimap<String, String>()
..add('k1', 'v1')
..['v1'].clear();
expect(map.containsKey('k1'), true);
});
test('should return a map view', () {
var mmap = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
Map map = mmap.asMap();
expect(map.keys, unorderedEquals(['k1', 'k2']));
expect(map.values, hasLength(2));
expect(map.values, anyElement(unorderedEquals(['v1', 'v2'])));
expect(map.values, anyElement(unorderedEquals(['v3'])));
expect(map['k1'], ['v1', 'v2']);
expect(map['k2'], ['v3']);
});
test('should return an empty iterable on map view unmapped key', () {
Map map = new ListMultimap<String, String>().asMap();
expect(map['k1'], []);
});
test('should allow addition via unmapped key lookup on map view', () {
var mmap = new ListMultimap<String, String>();
Map map = mmap.asMap();
map['k1'].add('v1');
map['k2'].addAll(['v1', 'v2']);
expect(mmap['k1'], ['v1']);
expect(mmap['k2'], ['v1', 'v2']);
});
test('should reflect additions to iterables returned by map view', () {
var mmap = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2');
Map map = mmap.asMap();
map['k1'].add('v3');
expect(mmap['k1'], ['v1', 'v2', 'v3']);
});
test('should reflect removals of keys in returned map view', () {
var mmap = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2');
Map map = mmap.asMap();
map.remove('k1');
expect(mmap.containsKey('k1'), false);
});
test('should reflect clearing of returned map view', () {
var mmap = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
Map map = mmap.asMap();
map.clear();
expect(mmap.isEmpty, true);
});
test('should support iteration over all {key, value} pairs', () {
Set s = new Set();
new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3')
..forEach((k, v) => s.add(new Pair(k, v)));
expect(
s,
unorderedEquals([
new Pair('k1', 'v1'),
new Pair('k1', 'v2'),
new Pair('k2', 'v3')
]));
});
test('should support iteration over all {key, Iterable<value>} pairs', () {
Map map = new Map();
var mmap = new ListMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3')
..forEachKey((k, v) => map[k] = v);
expect(map.length, mmap.length);
expect(map['k1'], ['v1', 'v2']);
expect(map['k2'], ['v3']);
});
test(
'should support operations on empty map views without breaking delegate synchronization',
() {
var mmap = new ListMultimap<String, String>();
List x = mmap['k1'];
List y = mmap['k1'];
List z = mmap['k1'];
List w = mmap['k1'];
mmap['k1'].add('v1');
expect(mmap['k1'], ['v1']);
x.add('v2');
expect(mmap['k1'], ['v1', 'v2']);
y.addAll(['v3', 'v4']);
expect(mmap['k1'], ['v1', 'v2', 'v3', 'v4']);
z.insert(0, 'v0');
expect(mmap['k1'], ['v0', 'v1', 'v2', 'v3', 'v4']);
w.insertAll(5, ['v5', 'v6']);
expect(mmap['k1'], ['v0', 'v1', 'v2', 'v3', 'v4', 'v5', 'v6']);
});
});
group('SetMultimap', () {
test('should initialize empty', () {
var map = new SetMultimap<String, String>();
expect(map.isEmpty, true);
expect(map.isNotEmpty, false);
});
test('should not be empty after adding', () {
var map = new SetMultimap<String, String>()..add('k', 'v');
expect(map.isEmpty, false);
expect(map.isNotEmpty, true);
});
test('should return the number of keys as length', () {
var map = new SetMultimap<String, String>();
expect(map.length, 0);
map..add('k1', 'v1')..add('k1', 'v2')..add('k2', 'v3');
expect(map.length, 2);
});
test('should return an empty iterable for unmapped keys', () {
var map = new SetMultimap<String, String>();
expect(map['k1'], []);
});
test('should support adding values for unmapped keys', () {
var map = new SetMultimap<String, String>()..['k1'].add('v1');
expect(map['k1'], ['v1']);
});
test('should support adding multiple values for unmapped keys', () {
var map = new SetMultimap<String, String>()..['k1'].addAll(['v1', 'v2']);
expect(map['k1'], unorderedEquals(['v1', 'v2']));
});
test('should return unmapped iterables that stay in sync on add', () {
var map = new SetMultimap<String, String>();
Set values1 = map['k1'];
Set values2 = map['k1'];
values1.add('v1');
expect(map['k1'], ['v1']);
expect(values2, ['v1']);
});
test('should return unmapped iterables that stay in sync on addAll', () {
var map = new SetMultimap<String, String>();
Set values1 = map['k1'];
Set values2 = map['k1'];
values1.addAll(['v1', 'v2']);
expect(map['k1'], unorderedEquals(['v1', 'v2']));
expect(values2, unorderedEquals(['v1', 'v2']));
});
test('should not support adding duplicate values for a key', () {
var map = new SetMultimap<String, String>()
..add('k', 'v1')
..add('k', 'v1');
expect(map['k'], ['v1']);
});
test(
'should not support adding duplicate values for a key when '
'initialized from an iterable', () {
var map = new SetMultimap<String, String>.fromIterable(['k', 'k'],
value: (x) => 'v1');
expect(map['k'], ['v1']);
});
test('should support adding multiple keys', () {
var map = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
expect(map['k1'], unorderedEquals(['v1', 'v2']));
expect(map['k2'], ['v3']);
});
test('should support adding multiple values at once', () {
var map = new SetMultimap<String, String>()
..addValues('k1', ['v1', 'v2']);
expect(map['k1'], ['v1', 'v2']);
});
test('should support adding multiple values at once for existing keys', () {
var map = new SetMultimap<String, String>()
..add('k1', 'v0')
..addValues('k1', ['v1', 'v2']);
expect(map['k1'], unorderedEquals(['v0', 'v1', 'v2']));
});
test('should support adding multiple values for existing (key,value)', () {
var map = new SetMultimap<String, String>()
..add('k1', 'v1')
..addValues('k1', ['v1', 'v2']);
expect(map['k1'], unorderedEquals(['v1', 'v2']));
});
test('should support adding from another multimap', () {
var from = new SetMultimap<String, String>()
..addValues('k1', ['v1', 'v2'])
..add('k2', 'v3');
var map = new SetMultimap<String, String>()..addAll(from);
expect(map['k1'], unorderedEquals(['v1', 'v2']));
expect(map['k2'], ['v3']);
});
test('should support adding from another multimap with existing keys', () {
var from = new SetMultimap<String, String>()
..addValues('k1', ['v1', 'v2'])
..add('k2', 'v3');
var map = new SetMultimap<String, String>()
..add('k1', 'v0')
..add('k2', 'v3')
..addAll(from);
expect(map['k1'], unorderedEquals(['v0', 'v1', 'v2']));
expect(map['k2'], ['v3']);
});
test('should return its keys', () {
var map = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
expect(map.keys, unorderedEquals(['k1', 'k2']));
});
test('should return its values', () {
var map = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
expect(map.values, unorderedEquals(['v1', 'v2', 'v3']));
});
test('should support duplicate values', () {
var map = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v1');
expect(map.values, unorderedEquals(['v1', 'v2', 'v1']));
});
test('should return an ordered list of values', () {
var map = new SetMultimap<String, String>()
..add('k', 'v1')
..add('k', 'v2');
expect(map['k'], unorderedEquals(['v1', 'v2']));
});
test('should reflect changes to underlying set', () {
var map = new SetMultimap<String, String>()
..add('k', 'v1')
..add('k', 'v2');
map['k'].add('v3');
map['k'].remove('v2');
expect(map['k'], unorderedEquals(['v1', 'v3']));
});
test('should return whether it contains a key', () {
var map = new SetMultimap<String, String>()
..add('k', 'v1')
..add('k', 'v2');
expect(map.containsKey('j'), false);
expect(map.containsKey('k'), true);
});
test('should return whether it contains a value', () {
var map = new SetMultimap<String, String>()
..add('k', 'v1')
..add('k', 'v2');
expect(map.containsValue('v0'), false);
expect(map.containsValue('v1'), true);
});
test('should remove specified key-value associations', () {
var map = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
expect(map.remove('k1', 'v0'), false);
expect(map.remove('k1', 'v1'), true);
expect(map['k1'], ['v2']);
expect(map.containsKey('k2'), true);
});
test('should remove a key when all associated values are removed', () {
var map = new SetMultimap<String, String>()
..add('k1', 'v1')
..remove('k1', 'v1');
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.remove', () {
var map = new SetMultimap<String, String>()..add('k1', 'v1');
map['k1'].remove('v1');
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.removeAll', () {
var map = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2');
map['k1'].removeAll(['v1', 'v2']);
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.removeWhere', () {
var map = new SetMultimap<String, String>()..add('k1', 'v1');
map['k1'].removeWhere((_) => true);
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.retainAll', () {
var map = new SetMultimap<String, String>()..add('k1', 'v1');
map['k1'].retainAll([]);
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.retainWhere', () {
var map = new SetMultimap<String, String>()..add('k1', 'v1');
map['k1'].retainWhere((_) => false);
expect(map.containsKey('k1'), false);
});
test(
'should remove a key when all associated values are removed '
'via the underlying iterable.clear', () {
var map = new SetMultimap<String, String>()..add('k1', 'v1');
map['k1'].clear();
expect(map.containsKey('k1'), false);
});
test('should remove all values for a key', () {
var map = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
expect(map.removeAll('k1'), unorderedEquals(['v1', 'v2']));
expect(map.containsKey('k1'), false);
expect(map.containsKey('k2'), true);
});
test('should clear underlying iterable on remove', () {
var map = new SetMultimap<String, String>()..add('k1', 'v1');
Set values = map['k1'];
expect(map.removeAll('k1'), ['v1']);
expect(values, []);
});
test('should return an empty iterable on removeAll of unmapped key', () {
var map = new SetMultimap<String, String>();
var removed = map.removeAll('k1');
expect(removed, []);
});
test('should be uncoupled from the iterable returned by removeAll', () {
var map = new SetMultimap<String, String>()..add('k1', 'v1');
var removed = map.removeAll('k1');
removed.add('v2');
map.add('k1', 'v3');
expect(removed, unorderedEquals(['v1', 'v2']));
expect(map['k1'], ['v3']);
});
test('should clear the map', () {
var map = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3')
..clear();
expect(map.isEmpty, true);
expect(map.containsKey('k1'), false);
expect(map.containsKey('k2'), false);
});
test('should clear underlying iterables on clear', () {
var map = new SetMultimap<String, String>()..add('k1', 'v1');
Set values = map['k1'];
map.clear();
expect(values, []);
});
test('should not add mappings on lookup of unmapped keys', () {
var map = new SetMultimap<String, String>()..['k1'];
expect(map.containsKey('k1'), false);
});
test('should not remove mappings on clearing mapped values', () {
var map = new SetMultimap<String, String>()
..add('k1', 'v1')
..['v1'].clear();
expect(map.containsKey('k1'), true);
});
test('should return a map view', () {
var mmap = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
Map map = mmap.asMap();
expect(map.keys, unorderedEquals(['k1', 'k2']));
expect(map['k1'], ['v1', 'v2']);
expect(map['k2'], ['v3']);
});
test('should return an empty iterable on map view unmapped key', () {
Map map = new SetMultimap<String, String>().asMap();
expect(map['k1'], []);
});
test('should allow addition via unmapped key lookup on map view', () {
var mmap = new SetMultimap<String, String>();
Map map = mmap.asMap();
map['k1'].add('v1');
map['k2'].addAll(['v1', 'v2']);
expect(mmap['k1'], ['v1']);
expect(mmap['k2'], unorderedEquals(['v1', 'v2']));
});
test('should reflect additions to iterables returned by map view', () {
var mmap = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2');
Map map = mmap.asMap();
map['k1'].add('v3');
expect(mmap['k1'], unorderedEquals(['v1', 'v2', 'v3']));
});
test('should reflect additions to iterables returned by map view', () {
var mmap = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2');
Map map = mmap.asMap();
map['k1'].add('v3');
expect(mmap['k1'], unorderedEquals(['v1', 'v2', 'v3']));
});
test('should reflect removals of keys in returned map view', () {
var mmap = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2');
Map map = mmap.asMap();
map.remove('k1');
expect(mmap.containsKey('k1'), false);
});
test('should reflect clearing of returned map view', () {
var mmap = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3');
Map map = mmap.asMap();
map.clear();
expect(mmap.isEmpty, true);
});
test('should support iteration over all {key, value} pairs', () {
Set s = new Set();
new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3')
..forEach((k, v) => s.add(new Pair(k, v)));
expect(
s,
unorderedEquals([
new Pair('k1', 'v1'),
new Pair('k1', 'v2'),
new Pair('k2', 'v3')
]));
});
test('should support iteration over all {key, Iterable<value>} pairs', () {
Map map = new Map();
var mmap = new SetMultimap<String, String>()
..add('k1', 'v1')
..add('k1', 'v2')
..add('k2', 'v3')
..forEachKey((k, v) => map[k] = v);
expect(map.length, mmap.length);
expect(map['k1'], unorderedEquals(['v1', 'v2']));
expect(map['k2'], unorderedEquals(['v3']));
});
test(
'should support operations on empty map views without breaking '
'delegate synchronization', () {
var mmap = new SetMultimap<String, String>();
Set x = mmap['k1'];
Set y = mmap['k1'];
mmap['k1'].add('v0');
x.add('v1');
y.addAll(['v2', 'v3']);
expect(mmap['k1'], unorderedEquals(['v0', 'v1', 'v2', 'v3']));
});
});
}
class Pair {
final x;
final y;
Pair(this.x, this.y);
bool operator ==(other) {
if (x != other.x) return false;
return equals(y).matches(other.y, {});
}
String toString() => "($x, $y)";
}