blob: 14cc2d638973170e4d70ad8b16763095e03c668e [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>());