| // Copyright (c) 2019, 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. |
| |
| // ignore_for_file: avoid_function_literals_in_foreach_calls |
| |
| import "package:test/test.dart"; |
| |
| import "package:typed_data/typed_data.dart"; |
| |
| /// The initial capacity of queues if the user doesn't specify one. |
| const capacity = 16; |
| |
| void main() { |
| group("Uint8Queue()", () { |
| test("creates an empty Uint8Queue", () { |
| expect(Uint8Queue(), isEmpty); |
| }); |
| |
| test("takes an initial capacity", () { |
| expect(Uint8Queue(100), isEmpty); |
| }); |
| }); |
| |
| group("add() adds an element to the end", () { |
| forEachInternalRepresentation((queue) { |
| queue.add(16); |
| expect(queue, equals(oneThrough(capacity))); |
| }); |
| }); |
| |
| group("addFirst() adds an element to the beginning", () { |
| forEachInternalRepresentation((queue) { |
| queue.addFirst(0); |
| expect(queue, equals([0, ...oneThrough(capacity - 1)])); |
| }); |
| }); |
| |
| group("removeFirst() removes an element from the beginning", () { |
| forEachInternalRepresentation((queue) { |
| expect(queue.removeFirst(), equals(1)); |
| expect(queue, equals(oneThrough(capacity - 1).skip(1))); |
| }); |
| |
| test("throws a StateError for an empty queue", () { |
| expect(Uint8Queue().removeFirst, throwsStateError); |
| }); |
| }); |
| |
| group("removeLast() removes an element from the end", () { |
| forEachInternalRepresentation((queue) { |
| expect(queue.removeLast(), equals(15)); |
| expect(queue, equals(oneThrough(capacity - 2))); |
| }); |
| |
| test("throws a StateError for an empty queue", () { |
| expect(Uint8Queue().removeLast, throwsStateError); |
| }); |
| }); |
| |
| group("removeRange()", () { |
| group("removes a prefix", () { |
| forEachInternalRepresentation((queue) { |
| queue.removeRange(0, 5); |
| expect(queue, equals(oneThrough(capacity - 1).skip(5))); |
| }); |
| }); |
| |
| group("removes a suffix", () { |
| forEachInternalRepresentation((queue) { |
| queue.removeRange(10, 15); |
| expect(queue, equals(oneThrough(capacity - 6))); |
| }); |
| }); |
| |
| group("removes from the middle", () { |
| forEachInternalRepresentation((queue) { |
| queue.removeRange(5, 10); |
| expect(queue, equals([1, 2, 3, 4, 5, 11, 12, 13, 14, 15])); |
| }); |
| }); |
| |
| group("removes everything", () { |
| forEachInternalRepresentation((queue) { |
| queue.removeRange(0, 15); |
| expect(queue, isEmpty); |
| }); |
| }); |
| |
| test("throws a RangeError for an invalid range", () { |
| expect(() => Uint8Queue().removeRange(0, 1), throwsRangeError); |
| }); |
| }); |
| |
| group("setRange()", () { |
| group("sets a range to the contents of an iterable", () { |
| forEachInternalRepresentation((queue) { |
| queue.setRange(5, 10, oneThrough(10).map((n) => 100 + n), 2); |
| expect(queue, |
| [1, 2, 3, 4, 5, 103, 104, 105, 106, 107, 11, 12, 13, 14, 15]); |
| }); |
| }); |
| |
| group("sets a range to the contents of a list", () { |
| forEachInternalRepresentation((queue) { |
| queue.setRange(5, 10, oneThrough(10).map((n) => 100 + n).toList(), 2); |
| expect(queue, |
| [1, 2, 3, 4, 5, 103, 104, 105, 106, 107, 11, 12, 13, 14, 15]); |
| }); |
| }); |
| |
| group( |
| "sets a range to a section of the same queue overlapping at the beginning", |
| () { |
| forEachInternalRepresentation((queue) { |
| queue.setRange(5, 10, queue, 2); |
| expect(queue, [1, 2, 3, 4, 5, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15]); |
| }); |
| }); |
| |
| group("sets a range to a section of the same queue overlapping at the end", |
| () { |
| forEachInternalRepresentation((queue) { |
| queue.setRange(5, 10, queue, 6); |
| expect(queue, [1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15]); |
| }); |
| }); |
| |
| test("throws a RangeError for an invalid range", () { |
| expect(() => Uint8Queue().setRange(0, 1, [1]), throwsRangeError); |
| }); |
| }); |
| |
| group("length returns the length", () { |
| forEachInternalRepresentation((queue) { |
| expect(queue.length, equals(15)); |
| }); |
| }); |
| |
| group("length=", () { |
| group("empties", () { |
| forEachInternalRepresentation((queue) { |
| queue.length = 0; |
| expect(queue, isEmpty); |
| }); |
| }); |
| |
| group("shrinks", () { |
| forEachInternalRepresentation((queue) { |
| queue.length = 5; |
| expect(queue, equals([1, 2, 3, 4, 5])); |
| }); |
| }); |
| |
| group("grows", () { |
| forEachInternalRepresentation((queue) { |
| queue.length = 20; |
| expect( |
| queue, |
| equals(oneThrough(capacity - 1) + |
| List.filled(20 - (capacity - 1), 0))); |
| }); |
| }); |
| |
| group("zeroes out existing data", () { |
| forEachInternalRepresentation((queue) { |
| queue.length = 0; |
| queue.length = 15; |
| expect(queue, equals([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])); |
| }); |
| }); |
| |
| test("throws a RangeError if length is less than 0", () { |
| expect(() => Uint8Queue().length = -1, throwsRangeError); |
| }); |
| }); |
| |
| group("[]", () { |
| group("returns individual entries", () { |
| forEachInternalRepresentation((queue) { |
| for (var i = 0; i < capacity - 1; i++) { |
| expect(queue[i], equals(i + 1)); |
| } |
| }); |
| }); |
| |
| test("throws a RangeError if the index is less than 0", () { |
| var queue = Uint8Queue.fromList([1, 2, 3]); |
| expect(() => queue[-1], throwsRangeError); |
| }); |
| |
| test( |
| "throws a RangeError if the index is greater than or equal to the " |
| "length", () { |
| var queue = Uint8Queue.fromList([1, 2, 3]); |
| expect(() => queue[3], throwsRangeError); |
| }); |
| }); |
| |
| group("[]=", () { |
| group("sets individual entries", () { |
| forEachInternalRepresentation((queue) { |
| for (var i = 0; i < capacity - 1; i++) { |
| queue[i] = 100 + i; |
| } |
| expect(queue, equals(List.generate(capacity - 1, (i) => 100 + i))); |
| }); |
| }); |
| |
| test("throws a RangeError if the index is less than 0", () { |
| var queue = Uint8Queue.fromList([1, 2, 3]); |
| expect(() { |
| queue[-1] = 0; |
| }, throwsRangeError); |
| }); |
| |
| test( |
| "throws a RangeError if the index is greater than or equal to the " |
| "length", () { |
| var queue = Uint8Queue.fromList([1, 2, 3]); |
| expect(() { |
| queue[3] = 4; |
| }, throwsRangeError); |
| }); |
| }); |
| |
| group("throws a modification error for", () { |
| Uint8Queue queue; |
| setUp(() { |
| queue = Uint8Queue.fromList([1, 2, 3]); |
| }); |
| |
| test("add", () { |
| expect(() => queue.forEach((_) => queue.add(4)), |
| throwsConcurrentModificationError); |
| }); |
| |
| test("addAll", () { |
| expect(() => queue.forEach((_) => queue.addAll([4, 5, 6])), |
| throwsConcurrentModificationError); |
| }); |
| |
| test("addFirst", () { |
| expect(() => queue.forEach((_) => queue.addFirst(0)), |
| throwsConcurrentModificationError); |
| }); |
| |
| test("removeFirst", () { |
| expect(() => queue.forEach((_) => queue.removeFirst()), |
| throwsConcurrentModificationError); |
| }); |
| |
| test("removeLast", () { |
| expect(() => queue.forEach((_) => queue.removeLast()), |
| throwsConcurrentModificationError); |
| }); |
| |
| test("length=", () { |
| expect(() => queue.forEach((_) => queue.length = 1), |
| throwsConcurrentModificationError); |
| }); |
| }); |
| } |
| |
| /// Runs [callback] in multiple tests, all with queues containing numbers from |
| /// one through 15 in various different internal states. |
| void forEachInternalRepresentation(void callback(Uint8Queue queue)) { |
| // Test with a queue whose internal table has plenty of room. |
| group("for a queue that's below capacity", () { |
| // Test with a queue whose elements are in one contiguous block, so `_head < |
| // _tail`. |
| test("with contiguous elements", () { |
| callback(Uint8Queue(capacity * 2)..addAll(oneThrough(capacity - 1))); |
| }); |
| |
| // Test with a queue whose elements are split across the ends of the table, |
| // so `_head > _tail`. |
| test("with an internal gap", () { |
| var queue = Uint8Queue(capacity * 2); |
| for (var i = capacity ~/ 2; i < capacity; i++) { |
| queue.add(i); |
| } |
| for (var i = capacity ~/ 2 - 1; i > 0; i--) { |
| queue.addFirst(i); |
| } |
| callback(queue); |
| }); |
| }); |
| |
| // Test with a queue whose internal table will need to expand if one more |
| // element is added. |
| group("for a queue that's at capacity", () { |
| test("with contiguous elements", () { |
| callback(Uint8Queue()..addAll(oneThrough(capacity - 1))); |
| }); |
| |
| test("with an internal gap", () { |
| var queue = Uint8Queue(); |
| for (var i = capacity ~/ 2; i < capacity; i++) { |
| queue.add(i); |
| } |
| for (var i = capacity ~/ 2 - 1; i > 0; i--) { |
| queue.addFirst(i); |
| } |
| callback(queue); |
| }); |
| }); |
| } |
| |
| /// Returns a list containing the integers from one through [n]. |
| List<int> oneThrough(int n) => List.generate(n, (i) => i + 1); |
| |
| /// Returns a matcher that expects that a closure throws a |
| /// [ConcurrentModificationError]. |
| final throwsConcurrentModificationError = |
| throwsA(const TypeMatcher<ConcurrentModificationError>()); |