devmil 4 лет назад
Родитель
Сommit
8702adfb91
2 измененных файлов с 317 добавлено и 14 удалено
  1. 18 14
      lib/util/circular_list.dart
  2. 299 0
      test/util/circular_list_test.dart

+ 18 - 14
lib/util/circular_list.dart

@@ -100,6 +100,9 @@ class CircularList<T> {
   /// Deletes item at [index].
   void remove(int index, [int count = 1]) {
     if (count > 0) {
+      if (index + count >= _length) {
+        count = _length - index;
+      }
       for (var i = index; i < _length - count; i++) {
         _array[_getCyclicIndex(i)] = _array[_getCyclicIndex(i + count)];
       }
@@ -109,6 +112,11 @@ class CircularList<T> {
 
   /// Inserts [item] at [index].
   void insert(int index, T item) {
+    if (index == 0 && _length >= _array.length) {
+      // when something is inserted at index 0 and the list is full then
+      // the new value immediately gets removed => nothing changes
+      return;
+    }
     for (var i = _length - 1; i >= index; i--) {
       _array[_getCyclicIndex(i + 1)] = _array[_getCyclicIndex(i)];
     }
@@ -124,20 +132,16 @@ class CircularList<T> {
 
   /// Inserts [items] at [index] in order.
   void insertAll(int index, List<T> items) {
-    for (var i = _length - 1; i >= index; i--) {
-      _array[_getCyclicIndex(i + 1)] = _array[_getCyclicIndex(i)];
-    }
-
-    for (var i = 0; i < items.length; i++) {
-      _array[_getCyclicIndex(index + i)] = items[i];
-    }
-
-    if (_length + items.length > _array.length) {
-      final countToTrim = _length + items.length - _array.length;
-      _startIndex += countToTrim;
-      length = _array.length;
-    } else {
-      _length += items.length;
+    for (var i = items.length - 1; i >= 0; i--) {
+      insert(index, items[i]);
+      // when the list is full then we have to move the index down
+      // as newly inserted values remove values with a lower index
+      if (_length >= _array.length) {
+        index--;
+        if (index < 0) {
+          return;
+        }
+      }
     }
   }
 

+ 299 - 0
test/util/circular_list_test.dart

@@ -0,0 +1,299 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:xterm/util/circular_list.dart';
+
+void main() {
+  group("CircularList Tests", () {
+    test("normal creation test", () {
+      final cl = CircularList<int>(1000);
+
+      expect(cl, isNotNull);
+      expect(cl.maxLength, 1000);
+    });
+
+    test("change max value", () {
+      final cl = CircularList<int>(2000);
+      expect(cl.maxLength, 2000);
+      cl.maxLength = 3000;
+      expect(cl.maxLength, 3000);
+    });
+
+    test("circle works", () {
+      final cl = CircularList<int>(10);
+      expect(cl.maxLength, 10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+
+      expect(cl.length, 10);
+      expect(cl[0], 0);
+      expect(cl[9], 9);
+
+      cl.push(10);
+
+      expect(cl.length, 10);
+      expect(cl[0], 1);
+      expect(cl[9], 10);
+    });
+
+    test("change max value after circle", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(15, (index) => index));
+
+      expect(cl.length, 10);
+      expect(cl[0], 5);
+      expect(cl[9], 14);
+
+      cl.maxLength = 20;
+
+      expect(cl.length, 10);
+      expect(cl[0], 5);
+      expect(cl[9], 14);
+
+      cl.pushAll(List<int>.generate(5, (index) => 15 + index));
+
+      expect(cl[0], 5);
+      expect(cl[9], 14);
+      expect(cl[14], 19);
+    });
+
+    test("setting the length erases trail", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+
+      expect(cl.length, 10);
+      expect(cl[0], 0);
+      expect(cl[9], 9);
+
+      cl.length = 5;
+
+      expect(cl.length, 5);
+      expect(cl[0], 0);
+      expect(() => cl[5], throwsRangeError);
+    });
+
+    test("foreach works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+
+      final collectedItems = List<int>.empty(growable: true);
+
+      cl.forEach((item) {
+        collectedItems.add(item);
+      });
+
+      expect(collectedItems.length, 10);
+      expect(collectedItems[0], 0);
+      expect(collectedItems[9], 9);
+    });
+
+    test("index operator set works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+
+      expect(cl.length, 10);
+      expect(cl[5], 5);
+
+      cl[5] = 50;
+
+      expect(cl[5], 50);
+    });
+
+    test("clear works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+      expect(cl[5], 5);
+
+      cl.clear();
+
+      expect(cl.length, 0);
+      expect(() => cl[5], throwsRangeError);
+    });
+
+    test("pop works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+      expect(cl.length, 10);
+      expect(cl[9], 9);
+
+      final val = cl.pop();
+
+      expect(val, 9);
+      expect(cl.length, 9);
+      expect(() => cl[9], throwsRangeError);
+      expect(cl[8], 8);
+    });
+
+    test("pop on empty throws", () {
+      final cl = CircularList<int>(10);
+      expect(() => cl.pop(), throwsA(anything));
+    });
+
+    test("remove one works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+      expect(cl.length, 10);
+      expect(cl[5], 5);
+
+      cl.remove(5);
+
+      expect(cl.length, 9);
+      expect(cl[5], 6);
+    });
+
+    test("remove multiple works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+      expect(cl.length, 10);
+      expect(cl[5], 5);
+
+      cl.remove(5, 3);
+
+      expect(cl.length, 7);
+      expect(cl[5], 8);
+    });
+
+    test("remove circle works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(15, (index) => index));
+      expect(cl.length, 10);
+      expect(cl[0], 5);
+
+      cl.remove(0, 9);
+
+      expect(cl.length, 1);
+      expect(cl[0], 14);
+    });
+
+    test("remove too much works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+      expect(cl.length, 10);
+      expect(cl[5], 5);
+
+      cl.remove(5, 10);
+
+      expect(cl.length, 5);
+      expect(cl[0], 0);
+    });
+
+    test("insert works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(5, (index) => index));
+      expect(cl.length, 5);
+      expect(cl[0], 0);
+      cl.insert(0, 100);
+
+      expect(cl.length, 6);
+      expect(cl[0], 100);
+      expect(cl[1], 0);
+    });
+
+    test("insert circular works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+      expect(cl.length, 10);
+      expect(cl[0], 0);
+      expect(cl[1], 1);
+      expect(cl[9], 9);
+
+      cl.insert(1, 100);
+
+      expect(cl.length, 10);
+      expect(cl[0], 100); //circle leads to 100 moving one index down
+      expect(cl[1], 1);
+    });
+
+    test("insert circular immediately remove works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+      expect(cl.length, 10);
+      expect(cl[0], 0);
+      expect(cl[1], 1);
+      expect(cl[9], 9);
+
+      cl.insert(0, 100);
+
+      expect(cl.length, 10);
+      expect(cl[0], 0); //the inserted 100 fell over immediately
+      expect(cl[1], 1);
+    });
+
+    test("insert all works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+      expect(cl.length, 10);
+      expect(cl[0], 0);
+      expect(cl[1], 1);
+      expect(cl[9], 9);
+
+      cl.insertAll(2, List<int>.generate(2, (index) => 20 + index));
+
+      expect(cl.length, 10);
+      expect(cl[0], 20);
+      expect(cl[1], 21);
+      expect(cl[3], 3);
+      expect(cl[9], 9);
+    });
+
+    test("trim start works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+      expect(cl.length, 10);
+      expect(cl[0], 0);
+      expect(cl[1], 1);
+      expect(cl[9], 9);
+
+      cl.trimStart(5);
+
+      expect(cl.length, 5);
+      expect(cl[0], 5);
+      expect(cl[1], 6);
+      expect(cl[4], 9);
+    });
+
+    test("trim start with more than length works", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+      expect(cl.length, 10);
+      expect(cl[0], 0);
+      expect(cl[1], 1);
+      expect(cl[9], 9);
+
+      cl.trimStart(15);
+
+      expect(cl.length, 0);
+    });
+
+    test("shift elements works", () {
+      final cl = CircularList<int>(20);
+      cl.pushAll(List<int>.generate(20, (index) => index));
+      expect(cl.length, 20);
+      expect(cl[0], 0);
+      expect(cl[1], 1);
+      expect(cl[9], 9);
+
+      cl.shiftElements(5, 3, 2);
+
+      expect(cl.length, 20);
+      expect(cl[0], 0); // untouched
+      expect(cl[1], 1); // untouched
+      expect(cl[5], 5); // moved
+      expect(cl[6], 6); // moved
+      expect(cl[7], 5); // moved (7) and target (5)
+      expect(cl[8], 6); // target (6)
+      expect(cl[9], 7); // target (7)
+      expect(cl[10], 10); // untouched
+      expect(cl[11], 11); // untouched
+    });
+
+    test("shift elements over bounds throws", () {
+      final cl = CircularList<int>(10);
+      cl.pushAll(List<int>.generate(10, (index) => index));
+      expect(cl.length, 10);
+      expect(cl[0], 0);
+      expect(cl[1], 1);
+      expect(cl[9], 9);
+
+      expect(() => cl.shiftElements(8, 2, 3), throwsA(anything));
+      expect(() => cl.shiftElements(2, 3, -3), throwsA(anything));
+    });
+  });
+}