// Copyright (c) 2013, 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:expect/expect.dart"; class MyEntry extends LinkedListEntry { final int value; MyEntry(int this.value); String toString() => value.toString(); int get hashCode => value.hashCode; bool operator ==(Object o) => o is MyEntry && value == o.value; } void testPreviousNext() { var list = LinkedList(); Expect.throws(() => list.first); Expect.throws(() => list.last); Expect.equals(0, list.length); for (int i = 0; i < 3; i++) { list.add(MyEntry(i)); } Expect.equals(3, list.length); var entry = list.first; Expect.isNull(entry.previous); Expect.equals(0, entry.value); entry = entry.next; Expect.equals(1, entry.value); entry = entry.next; Expect.equals(2, entry.value); Expect.isNull(entry.next); entry = entry.previous; Expect.equals(1, entry.value); entry = entry.previous; Expect.equals(0, entry.value); Expect.isNull(entry.previous); } void testUnlinked() { var unlinked = MyEntry(0); Expect.isNull(unlinked.previous); Expect.isNull(unlinked.next); var list = LinkedList(); list.add(unlinked); Expect.isNull(unlinked.previous); Expect.isNull(unlinked.next); list.remove(unlinked); Expect.isNull(unlinked.previous); Expect.isNull(unlinked.next); list.add(unlinked); list.add(MyEntry(1)); Expect.isNull(unlinked.previous); Expect.equals(1, unlinked.next.value); list.remove(unlinked); Expect.isNull(unlinked.previous); Expect.isNull(unlinked.next); list.add(unlinked); Expect.isNull(unlinked.next); Expect.equals(1, unlinked.previous.value); } void testInsert() { // Insert last. var list = LinkedList(); for (int i = 0; i < 10; i++) { list.add(MyEntry(i)); } Expect.equals(10, list.length); int i = 0; for (var entry in list) { Expect.equals(i, entry.value); i++; } Expect.equals(10, i); list.clear(); // Insert first. for (int i = 0; i < 10; i++) { list.addFirst(MyEntry(i)); } Expect.equals(10, list.length); i = 10; for (var entry in list) { Expect.equals(--i, entry.value); } Expect.equals(0, i); list.clear(); // Insert after. list.addFirst(MyEntry(0)); for (int i = 1; i < 10; i++) { list.last.insertAfter(MyEntry(i)); } Expect.equals(10, list.length); i = 0; for (var entry in list) { Expect.equals(i, entry.value); i++; } Expect.equals(10, i); list.clear(); // Insert before. list.addFirst(MyEntry(0)); for (int i = 1; i < 10; i++) { list.first.insertBefore(MyEntry(i)); } Expect.equals(10, list.length); i = 10; for (var entry in list) { Expect.equals(--i, entry.value); } Expect.equals(0, i); list.clear(); } void testRemove() { var list = LinkedList(); for (int i = 0; i < 10; i++) { list.add(MyEntry(i)); } Expect.equals(10, list.length); list.remove(list.skip(5).first); Expect.equals(9, list.length); int i = 0; for (var entry in list) { if (i == 5) i++; Expect.equals(i, entry.value); i++; } Expect.listEquals( [0, 1, 2, 3, 4, 6, 7, 8, 9], list.map((e) => e.value).toList()); for (int i = 0; i < 9; i++) { list.first.unlink(); } Expect.throws(() => list.first); Expect.equals(0, list.length); } void testContains() { var list = LinkedList(); var entry5 = MyEntry(5); // Empty lists contains nothing. Expect.isFalse(list.contains(null)); Expect.isFalse(list.contains(Object())); Expect.isFalse(list.contains(entry5)); // Works for singleton lists. list.add(MyEntry(0)); Expect.isTrue(list.contains(list.first)); Expect.isFalse(list.contains(null)); Expect.isFalse(list.contains(Object())); Expect.isFalse(list.contains(entry5)); // Works for larger lists. for (int i = 1; i < 10; i++) { list.add(MyEntry(i)); } for (var entry in list) { Expect.isTrue(list.contains(entry)); } Expect.isFalse(list.contains(Object())); Expect.isFalse(list.contains(null)); Expect.isFalse(list.contains(entry5)); // Based on identity, not equality. Expect.isTrue(list.elementAt(5) == entry5); } void testBadAdd() { var list1 = LinkedList(); list1.addFirst(MyEntry(0)); var list2 = LinkedList(); Expect.throws(() => list2.addFirst(list1.first)); Expect.throws(() => MyEntry(0).unlink()); } void testConcurrentModificationError() { test(function(LinkedList ll)) { var ll = LinkedList(); for (int i = 0; i < 10; i++) { ll.add(MyEntry(i)); } Expect.throws(() => function(ll), (e) => e is ConcurrentModificationError); } test((ll) { for (var x in ll) { ll.remove(x); } }); test((ll) { ll.forEach((x) { ll.remove(x); }); }); test((ll) { ll.any((x) { ll.remove(x); return false; }); }); test((ll) { ll.every((x) { ll.remove(x); return true; }); }); test((ll) { ll.fold(0, (x, y) { ll.remove(y); return x; }); }); test((ll) { ll.reduce((x, y) { ll.remove(y); return x; }); }); test((ll) { ll.where((x) { ll.remove(x); return true; }).forEach((_) {}); }); test((ll) { ll.map((x) { ll.remove(x); return x; }).forEach((_) {}); }); test((ll) { ll.expand((x) { ll.remove(x); return [x]; }).forEach((_) {}); }); test((ll) { ll.takeWhile((x) { ll.remove(x); return true; }).forEach((_) {}); }); test((ll) { ll.skipWhile((x) { ll.remove(x); return true; }).forEach((_) {}); }); test((ll) { bool first = true; ll.firstWhere((x) { ll.remove(x); if (!first) return true; return first = false; }); }); test((ll) { ll.lastWhere((x) { ll.remove(x); return true; }); }); test((ll) { bool first = true; ll.singleWhere((x) { ll.remove(x); if (!first) return false; return !(first = false); }); }); } void main() { testPreviousNext(); testUnlinked(); testInsert(); testRemove(); testContains(); testBadAdd(); testConcurrentModificationError(); }