blob: 82d96f6891b2eb5846faf5b203d759799670b8bc [file] [log] [blame]
// 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 Function(Uint8Queue queue) callback) {
// 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>());