swift-nio/Tests/NIOPosixTests/CircularBufferTests.swift

1043 lines
38 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import XCTest
@testable import NIOCore
class CircularBufferTests: XCTestCase {
func testTrivial() {
var ring = CircularBuffer<Int>(initialCapacity: 8)
ring.append(1)
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(1, ring.removeFirst())
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
}
func testAddRemoveInALoop() {
var ring = CircularBuffer<Int>(initialCapacity: 8)
XCTAssertTrue(ring.isEmpty)
XCTAssertEqual(0, ring.count)
for f in 0..<1000 {
ring.append(f)
XCTAssertEqual(f, ring.removeFirst())
XCTAssertTrue(ring.isEmpty)
XCTAssertEqual(0, ring.count)
}
}
func testAddAllRemoveAll() {
var ring = CircularBuffer<Int>(initialCapacity: 8)
XCTAssertTrue(ring.isEmpty)
XCTAssertEqual(0, ring.count)
for f in 1..<100 {
ring.append(f)
XCTAssertEqual(f, ring.count)
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
}
for f in 1..<100 {
XCTAssertEqual(f, ring.removeFirst())
XCTAssertEqual(99 - f, ring.count)
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
}
XCTAssertTrue(ring.isEmpty)
}
func testRemoveAt() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
for idx in 0..<7 {
ring.prepend(idx)
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
}
XCTAssertEqual(7, ring.count)
_ = ring.remove(at: ring.index(ring.startIndex, offsetBy: 1))
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(6, ring.count)
XCTAssertEqual(0, ring.last)
}
func testRemoveAtLastPosition() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
for idx in 0..<7 {
ring.prepend(idx)
}
let last = ring.remove(at: ring.index(ring.endIndex, offsetBy: -1))
XCTAssertEqual(0, last)
XCTAssertEqual(1, ring.last)
}
func testRemoveAtTailIdx0() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
ring.prepend(99)
ring.prepend(98)
XCTAssertEqual(2, ring.count)
XCTAssertEqual(99, ring.remove(at: ring.index(ring.endIndex, offsetBy: -1)))
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertFalse(ring.isEmpty)
XCTAssertEqual(1, ring.count)
XCTAssertEqual(98, ring.last)
XCTAssertEqual(98, ring.first)
}
func testRemoveAtFirstPosition() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
for idx in 0..<7 {
ring.prepend(idx)
}
let first = ring.remove(at: ring.startIndex)
XCTAssertEqual(6, first)
XCTAssertEqual(5, ring.first)
}
func collectAllIndices<Element>(ring: CircularBuffer<Element>) -> [CircularBuffer<Element>.Index] {
return Array(ring.indices)
}
func collectAllIndices<Element>(ring: CircularBuffer<Element>, range: Range<CircularBuffer<Element>.Index>) -> [CircularBuffer<Element>.Index] {
var index: CircularBuffer<Element>.Index = range.lowerBound
var allIndices: [CircularBuffer<Element>.Index] = []
while index != range.upperBound {
allIndices.append(index)
index = ring.index(after: index)
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
}
return allIndices
}
func testHarderExpansion() {
var ring = CircularBuffer<Int>(initialCapacity: 3)
XCTAssertEqual(self.collectAllIndices(ring: ring),
self.collectAllIndices(ring: ring, range: ring.startIndex ..< ring.startIndex))
ring.append(1)
XCTAssertEqual(ring.count, 1)
XCTAssertEqual(ring[ring.startIndex], 1)
XCTAssertEqual(self.collectAllIndices(ring: ring),
self.collectAllIndices(ring: ring, range: ring.startIndex ..< ring.index(ring.startIndex,
offsetBy: 1)))
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
ring.append(2)
XCTAssertEqual(ring.count, 2)
XCTAssertEqual(ring[ring.startIndex], 1)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 1)], 2)
XCTAssertEqual(self.collectAllIndices(ring: ring),
self.collectAllIndices(ring: ring, range: ring.startIndex ..< ring.index(ring.startIndex,
offsetBy: 2)))
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
ring.append(3)
XCTAssertEqual(ring.count, 3)
XCTAssertEqual(ring[ring.startIndex], 1)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 1)], 2)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 2)], 3)
XCTAssertEqual(self.collectAllIndices(ring: ring),
self.collectAllIndices(ring: ring, range: ring.startIndex ..< ring.index(ring.startIndex,
offsetBy: 3)))
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(1, ring.removeFirst())
XCTAssertEqual(ring.count, 2)
XCTAssertEqual(ring[ring.startIndex], 2)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 1)], 3)
XCTAssertEqual(self.collectAllIndices(ring: ring),
self.collectAllIndices(ring: ring, range: ring.startIndex ..< ring.index(ring.startIndex,
offsetBy: 2)))
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(2, ring.removeFirst())
XCTAssertEqual(ring.count, 1)
XCTAssertEqual(ring[ring.startIndex], 3)
XCTAssertEqual(self.collectAllIndices(ring: ring),
self.collectAllIndices(ring: ring, range: ring.startIndex ..< ring.index(ring.startIndex,
offsetBy: 1)))
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
ring.append(5)
XCTAssertEqual(ring.count, 2)
XCTAssertEqual(ring[ring.startIndex], 3)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 1)], 5)
XCTAssertEqual(self.collectAllIndices(ring: ring),
self.collectAllIndices(ring: ring, range: ring.startIndex ..< ring.index(ring.startIndex,
offsetBy: 2)))
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
ring.append(6)
XCTAssertEqual(ring.count, 3)
XCTAssertEqual(ring[ring.startIndex], 3)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 1)], 5)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 2)], 6)
XCTAssertEqual(self.collectAllIndices(ring: ring),
self.collectAllIndices(ring: ring, range: ring.startIndex ..< ring.index(ring.startIndex,
offsetBy: 3)))
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
ring.append(7)
XCTAssertEqual(ring.count, 4)
XCTAssertEqual(ring[ring.startIndex], 3)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 1)], 5)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 2)], 6)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 3)], 7)
XCTAssertEqual(self.collectAllIndices(ring: ring),
self.collectAllIndices(ring: ring, range: ring.startIndex ..< ring.index(ring.startIndex,
offsetBy: 4)))
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
}
func testCollection() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
XCTAssertEqual(self.collectAllIndices(ring: ring),
self.collectAllIndices(ring: ring, range: ring.startIndex ..< ring.startIndex))
XCTAssertEqual(0, ring.distance(from: ring.startIndex, to: ring.endIndex))
XCTAssertEqual(0, ring.distance(from: ring.startIndex, to: ring.startIndex))
for idx in 0..<5 {
ring.append(idx)
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
}
XCTAssertFalse(ring.isEmpty)
XCTAssertEqual(5, ring.count)
XCTAssertEqual(self.collectAllIndices(ring: ring),
self.collectAllIndices(ring: ring, range: ring.startIndex ..< ring.index(ring.startIndex,
offsetBy: 5)))
XCTAssertEqual(ring.startIndex, ring.startIndex)
XCTAssertEqual(ring.endIndex, ring.index(ring.startIndex, offsetBy: 5))
XCTAssertEqual(ring.index(after: ring.index(ring.startIndex, offsetBy: 1)),
ring.index(ring.startIndex, offsetBy: 2))
XCTAssertEqual(ring.index(before: ring.index(ring.startIndex, offsetBy: 3)),
ring.index(ring.startIndex, offsetBy: 2))
let actualValues = [Int](ring)
let expectedValues = [0, 1, 2, 3, 4]
XCTAssertEqual(expectedValues, actualValues)
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
}
func testReplaceSubrange5ElementsWith1() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
for idx in 0..<50 {
ring.prepend(idx)
}
XCTAssertEqual(50, ring.count)
ring.replaceSubrange(ring.index(ring.startIndex, offsetBy: 20) ..< ring.index(ring.startIndex, offsetBy: 25),
with: [99])
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(ring.count, 46)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 19)], 30)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 20)], 99)
XCTAssertEqual(ring[ring.index(ring.startIndex, offsetBy: 21)], 24)
}
func testReplaceSubrangeAllElementsWithFewerElements() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
for idx in 0..<50 {
ring.prepend(idx)
}
XCTAssertEqual(50, ring.count)
ring.replaceSubrange(ring.startIndex..<ring.endIndex, with: [3,4])
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(2, ring.count)
XCTAssertEqual(3, ring.first)
XCTAssertEqual(4, ring.last)
}
func testReplaceSubrangeEmptyRange() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
for idx in 0..<50 {
ring.prepend(idx)
}
XCTAssertEqual(50, ring.count)
ring.replaceSubrange(ring.startIndex ..< ring.startIndex, with: [])
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(50, ring.count)
}
func testReplaceSubrangeWithSubrangeLargerThanTargetRange() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
for idx in 0..<5 {
ring.prepend(idx)
}
XCTAssertEqual(5, ring.count)
ring.replaceSubrange(ring.startIndex..<ring.endIndex, with: [10,11,12,13,14,15,16,17,18,19])
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(10, ring.count)
XCTAssertEqual(10, ring.first)
XCTAssertEqual(19, ring.last)
}
func testReplaceSubrangeSameSize() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
for idx in 0..<5 {
ring.prepend(idx)
}
XCTAssertEqual(5, ring.count)
XCTAssertEqual(4, ring.first)
XCTAssertEqual(0, ring.last)
var replacement = [Int]()
for idx in 0..<5 {
replacement.append(idx)
}
XCTAssertEqual(5, replacement.count)
XCTAssertEqual(0, replacement.first)
XCTAssertEqual(4, replacement.last)
ring.replaceSubrange(ring.startIndex..<ring.endIndex, with: replacement)
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(5, ring.count)
XCTAssertEqual(0, ring.first)
XCTAssertEqual(4, ring.last)
}
func testReplaceSubrangeReplaceBufferWithEmptyArray() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
for idx in 0..<5 {
ring.prepend(idx)
}
XCTAssertEqual(5, ring.count)
XCTAssertEqual(4, ring.first)
XCTAssertEqual(0, ring.last)
ring.replaceSubrange(ring.startIndex..<ring.endIndex, with: [])
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertTrue(ring.isEmpty)
}
func testRangeSubscriptExpanding() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
for idx in 0..<5 {
ring.prepend(idx)
}
XCTAssertEqual(5, ring.count)
let index = ring.firstIndex(of: 1)!
let originalCount = ring.count
XCTAssertEqual(ring[index..<ring.endIndex].count, 2)
ring[index..<ring.endIndex] = [10,11,12,13,14,15,16,17,18,19]
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(originalCount + 8, ring.count)
XCTAssertEqual(4, ring.first)
XCTAssertEqual(19, ring.last)
}
func testWeCanDistinguishBetweenEmptyAndFull() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
XCTAssertTrue(ring.isEmpty)
for idx in 0..<4 {
ring.append(idx)
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
}
XCTAssertEqual(4, ring.count)
XCTAssertFalse(ring.isEmpty)
}
func testExpandZeroBasedRingWorks() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
for idx in 0..<5 {
ring.append(idx)
}
for idx in 0..<5 {
XCTAssertEqual(idx, ring[ring.index(ring.startIndex, offsetBy: idx)])
}
}
func testExpandNonZeroBasedRingWorks() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
for idx in 0..<4 {
ring.append(idx)
}
/* the underlying buffer should now be filled from 0 to max */
for idx in 0..<4 {
XCTAssertEqual(idx, ring[ring.index(ring.startIndex, offsetBy: idx)])
}
XCTAssertEqual(0, ring.removeFirst())
/* now the first element is gone, ie. the ring starts at index 1 now */
for idx in 0..<3 {
XCTAssertEqual(idx + 1, ring[ring.index(ring.startIndex, offsetBy: idx)])
}
ring.append(4)
XCTAssertEqual(1, ring.first!)
/* now the last element should be at ring position 0 */
for idx in 0..<4 {
XCTAssertEqual(idx + 1, ring[ring.index(ring.startIndex, offsetBy: idx)])
}
/* and now we'll make it expand */
ring.append(5)
for idx in 0..<5 {
XCTAssertEqual(idx + 1, ring[ring.index(ring.startIndex, offsetBy: idx)])
}
}
func testEmptyingExpandedRingWorks() {
var ring = CircularBuffer<Int>(initialCapacity: 2)
for idx in 0..<4 {
ring.append(idx)
}
for idx in 0..<4 {
XCTAssertEqual(idx, ring[ring.index(ring.startIndex, offsetBy: idx)])
}
for idx in 0..<4 {
XCTAssertEqual(idx, ring.removeFirst())
}
XCTAssertTrue(ring.isEmpty)
XCTAssertNil(ring.first)
}
func testChangeElements() {
var ring = CircularBuffer<Int>(initialCapacity: 100)
for idx in 0..<50 {
ring.append(idx)
}
var changes: [(Int, Int)] = []
for (idx, element) in ring.enumerated() {
XCTAssertEqual(idx, element)
changes.append((idx, element * 2))
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
}
for change in changes {
ring[ring.index(ring.startIndex, offsetBy: change.0)] = change.1
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
}
for (idx, element) in ring.enumerated() {
XCTAssertEqual(idx * 2, element)
}
}
func testSliceTheRing() {
var ring = CircularBuffer<Int>(initialCapacity: 100)
for idx in 0..<50 {
ring.append(idx)
}
let slice = ring[ring.index(ring.startIndex, offsetBy: 25) ..< ring.index(ring.startIndex, offsetBy: 30)]
for (idx, element) in slice.enumerated() {
XCTAssertEqual(ring.index(ring.startIndex, offsetBy: idx + 25),
ring.index(ring.startIndex, offsetBy: element))
}
}
func testCount() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
ring.append(1)
XCTAssertEqual(1, ring.count)
ring.append(2)
XCTAssertEqual(2, ring.count)
XCTAssertEqual(1, ring.removeFirst())
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
ring.append(3)
XCTAssertEqual(2, ring.count)
XCTAssertEqual(2, ring.removeFirst())
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
ring.append(4)
XCTAssertEqual(2, ring.count)
XCTAssertEqual(3, ring.removeFirst())
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
ring.append(5)
XCTAssertEqual(3, ring.headBackingIndex)
XCTAssertEqual(1, ring.tailBackingIndex)
XCTAssertEqual(2, ring.count)
XCTAssertEqual(4, ring.removeFirst())
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(5, ring.removeFirst())
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(0, ring.count)
XCTAssertTrue(ring.isEmpty)
}
func testFirst() {
var ring = CircularBuffer<Int>(initialCapacity: 3)
XCTAssertNil(ring.first)
ring.append(1)
XCTAssertEqual(1, ring.first)
XCTAssertEqual(1, ring.removeFirst())
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertNil(ring.first)
}
func testLast() {
var ring = CircularBuffer<Int>(initialCapacity: 3)
XCTAssertNil(ring.last)
ring.prepend(1)
XCTAssertEqual(1, ring.last)
XCTAssertEqual(1, ring.removeLast())
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertNil(ring.last)
XCTAssertEqual(0, ring.count)
XCTAssertTrue(ring.isEmpty)
}
func testRemoveLast() {
var ring = CircularBuffer<Int>(initialCapacity: 3)
XCTAssertNil(ring.last)
ring.append(1)
ring.prepend(0)
XCTAssertEqual(1, ring.last)
XCTAssertEqual(1, ring.removeLast())
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(0, ring.last)
}
func testRemoveLastCountElements() {
var ring = CircularBuffer<Int>(initialCapacity: 3)
XCTAssertNil(ring.last)
ring.append(1)
ring.prepend(0)
XCTAssertEqual(1, ring.last)
ring.removeLast(2)
XCTAssertTrue(ring.isEmpty)
}
func testRemoveLastElements() {
var ring = CircularBuffer<Int>(initialCapacity: 10)
XCTAssertNil(ring.last)
for i in 0 ..< 20 {
ring.prepend(i)
}
XCTAssertEqual(20, ring.count)
XCTAssertEqual(19, ring.first)
XCTAssertEqual(0, ring.last)
ring.removeLast(10)
XCTAssertEqual(10, ring.count)
XCTAssertEqual(19, ring.first)
XCTAssertEqual(10, ring.last)
}
func testOperateOnBothSides() {
var ring = CircularBuffer<Int>(initialCapacity: 3)
XCTAssertNil(ring.last)
ring.prepend(1)
ring.prepend(2)
XCTAssertEqual(1, ring.last)
XCTAssertEqual(2, ring.first)
XCTAssertEqual(1, ring.removeLast())
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(2, ring.removeFirst())
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertNil(ring.last)
XCTAssertNil(ring.first)
XCTAssertEqual(0, ring.count)
XCTAssertTrue(ring.isEmpty)
}
func testPrependExpandBuffer() {
var ring = CircularBuffer<Int>(initialCapacity: 3)
for f in 1..<1000 {
ring.prepend(f)
XCTAssertEqual(f, ring.count)
}
for f in 1..<1000 {
XCTAssertEqual(f, ring.removeLast())
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
}
XCTAssertTrue(ring.isEmpty)
XCTAssertEqual(0, ring.count)
}
func testRemoveAllKeepingCapacity() {
var ring = CircularBuffer<Int>(initialCapacity: 2)
XCTAssertEqual(ring.capacity, 2)
ring.append(1)
ring.append(2)
// we're full so it will have doubled
XCTAssertEqual(ring.capacity, 4)
XCTAssertEqual(ring.count, 2)
ring.removeAll(keepingCapacity: true)
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(ring.capacity, 4)
XCTAssertEqual(ring.count, 0)
}
func testRemoveAllNotKeepingCapacity() {
var ring = CircularBuffer<Int>(initialCapacity: 2)
XCTAssertGreaterThanOrEqual(ring.capacity, 2)
ring.append(1)
ring.append(2)
// we're full so it will have doubled
XCTAssertEqual(ring.capacity, 4)
XCTAssertEqual(ring.count, 2)
ring.removeAll(keepingCapacity: false)
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
// 1 is the smallest capacity we have
XCTAssertEqual(ring.capacity, 1)
XCTAssertEqual(ring.count, 0)
ring.append(1)
XCTAssertEqual(ring.capacity, 2)
XCTAssertEqual(ring.count, 1)
ring.append(2)
XCTAssertEqual(ring.capacity, 4)
XCTAssertEqual(ring.count, 2)
ring.removeAll() // default should not keep capacity
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(ring.capacity, 1)
XCTAssertEqual(ring.count, 0)
}
func testBufferManaged() {
var ring = CircularBuffer<Int>(initialCapacity: 2)
ring.append(1)
XCTAssertEqual(ring.capacity, 2)
// Now we want to replace the last subrange with two elements. This should
// force an increase in size.
ring.replaceSubrange(ring.startIndex ..< ring.index(ring.startIndex, offsetBy: 1), with: [3, 4])
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(ring.capacity, 4)
}
func testDoesNotExpandCapacityNeedlesslyWhenInserting() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
let capacity0 = ring.capacity
XCTAssertGreaterThanOrEqual(capacity0, 4)
XCTAssertLessThan(capacity0, 16)
ring.insert(0, at: ring.startIndex)
let capacity1 = ring.capacity
XCTAssertEqual(capacity0, capacity1)
ring.insert(1, at: ring.startIndex)
let capacity2 = ring.capacity
XCTAssertEqual(capacity0, capacity2)
ring.insert(2, at: ring.startIndex)
let capacity3 = ring.capacity
XCTAssertEqual(capacity0, capacity3)
}
func testDoesNotExpandCapacityNeedlesslyWhenAppending() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
let capacity0 = ring.capacity
XCTAssertGreaterThanOrEqual(capacity0, 4)
XCTAssertLessThan(capacity0, 16)
ring.append(0)
let capacity1 = ring.capacity
XCTAssertEqual(capacity0, capacity1)
ring.append(1)
let capacity2 = ring.capacity
XCTAssertEqual(capacity0, capacity2)
ring.append(2)
let capacity3 = ring.capacity
XCTAssertEqual(capacity0, capacity3)
}
func testExpandRemoveAllKeepingAndNotKeepingCapacityAndExpandAgain() {
for shouldKeepCapacity in [false, true] {
var ring = CircularBuffer<Int>(initialCapacity: 4)
(0..<16).forEach { ring.append($0) }
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
(0..<4).forEach { _ in _ = ring.removeFirst() }
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
(16..<20).forEach { ring.append($0) }
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(Array(4..<20), Array(ring))
ring.removeAll(keepingCapacity: shouldKeepCapacity)
(0..<8).forEach { ring.append($0) }
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
(0..<4).forEach { _ in _ = ring.removeFirst() }
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
(8..<64).forEach { ring.append($0) }
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(Array(4..<64), Array(ring))
}
}
func testRemoveAllNilsOutTheContents() {
class Dummy {}
weak var dummy1: Dummy? = nil
weak var dummy2: Dummy? = nil
weak var dummy3: Dummy? = nil
weak var dummy4: Dummy? = nil
weak var dummy5: Dummy? = nil
weak var dummy6: Dummy? = nil
weak var dummy7: Dummy? = nil
weak var dummy8: Dummy? = nil
var ring: CircularBuffer<Dummy> = .init(initialCapacity: 4)
({
for _ in 0..<2 {
ring.append(Dummy())
}
dummy1 = ring.dropFirst(0).first
dummy2 = ring.dropFirst(1).first
XCTAssertNotNil(dummy1)
XCTAssertNotNil(dummy2)
_ = ring.removeFirst()
for _ in 2..<8 {
ring.append(Dummy())
}
dummy3 = ring.dropFirst(1).first
dummy4 = ring.dropFirst(2).first
dummy5 = ring.dropFirst(3).first
dummy6 = ring.dropFirst(4).first
dummy7 = ring.dropFirst(5).first
dummy8 = ring.dropFirst(6).first
XCTAssertNotNil(dummy3)
XCTAssertNotNil(dummy4)
XCTAssertNotNil(dummy5)
XCTAssertNotNil(dummy6)
XCTAssertNotNil(dummy7)
XCTAssertNotNil(dummy8)
})()
XCTAssertNotNil(dummy2)
XCTAssertNotNil(dummy3)
XCTAssertNotNil(dummy4)
XCTAssertNotNil(dummy5)
XCTAssertNotNil(dummy6)
XCTAssertNotNil(dummy7)
XCTAssertNotNil(dummy8)
ring.removeAll(keepingCapacity: true)
assert(dummy1 == nil, within: .seconds(1))
assert(dummy2 == nil, within: .seconds(1))
assert(dummy3 == nil, within: .seconds(1))
assert(dummy4 == nil, within: .seconds(1))
assert(dummy5 == nil, within: .seconds(1))
assert(dummy6 == nil, within: .seconds(1))
assert(dummy7 == nil, within: .seconds(1))
assert(dummy8 == nil, within: .seconds(1))
}
func testIntIndexing() {
var ring = CircularBuffer<Int>()
for i in 0 ..< 5 {
ring.append(i)
XCTAssertEqual(ring[offset: i], i)
}
XCTAssertEqual(ring[ring.startIndex], ring[offset :0])
XCTAssertEqual(ring[ring.index(before: ring.endIndex)], ring[offset: 4])
ring[offset: 1] = 10
XCTAssertEqual(ring[ring.index(after: ring.startIndex)], 10)
}
func testIndexDistance() {
var bufferOfBackingSize4 = CircularBuffer<Int>(initialCapacity: 4)
XCTAssertEqual(3, bufferOfBackingSize4.indexBeforeHeadIdx())
let index1 = CircularBuffer<Int>.Index(backingIndex: 0, backingCount: 4, backingIndexOfHead: 0)
let index2 = CircularBuffer<Int>.Index(backingIndex: 1, backingCount: 4, backingIndexOfHead: 0)
XCTAssertEqual(bufferOfBackingSize4.distance(from: index1, to: index2), 1)
bufferOfBackingSize4.append(1)
XCTAssertEqual(1, bufferOfBackingSize4.removeFirst())
XCTAssertEqual(1, bufferOfBackingSize4.headBackingIndex)
let index3 = CircularBuffer<Int>.Index(backingIndex: 2, backingCount: 4, backingIndexOfHead: 1)
let index4 = CircularBuffer<Int>.Index(backingIndex: 0, backingCount: 4, backingIndexOfHead: 1)
XCTAssertEqual(bufferOfBackingSize4.distance(from: index3, to: index4), 2)
let index5 = CircularBuffer<Int>.Index(backingIndex: 0, backingCount: 4, backingIndexOfHead: 1)
let index6 = CircularBuffer<Int>.Index(backingIndex: 2, backingCount: 4, backingIndexOfHead: 1)
XCTAssertEqual(bufferOfBackingSize4.distance(from: index5, to: index6), -2)
bufferOfBackingSize4.append(2)
bufferOfBackingSize4.append(3)
XCTAssertEqual(2, bufferOfBackingSize4.removeFirst())
XCTAssertEqual(3, bufferOfBackingSize4.removeFirst())
XCTAssertEqual(3, bufferOfBackingSize4.headBackingIndex)
let index7 = CircularBuffer<Int>.Index(backingIndex: 0, backingCount: 4, backingIndexOfHead: 3)
let index8 = CircularBuffer<Int>.Index(backingIndex: 2, backingCount: 4, backingIndexOfHead: 3)
XCTAssertEqual(bufferOfBackingSize4.distance(from: index7, to: index8), 2)
}
func testIndexAdvancing() {
var bufferOfBackingSize4 = CircularBuffer<Int>(initialCapacity: 4)
XCTAssertEqual(3, bufferOfBackingSize4.indexBeforeHeadIdx())
let index1 = CircularBuffer<Int>.Index(backingIndex: 0, backingCount: 4, backingIndexOfHead: 0)
let index2 = bufferOfBackingSize4.index(after: index1)
XCTAssertEqual(index2.backingIndex, 1)
XCTAssertEqual(index2.isIndexGEQHeadIndex, true)
bufferOfBackingSize4.append(1)
bufferOfBackingSize4.append(2)
XCTAssertEqual(1, bufferOfBackingSize4.removeFirst())
XCTAssertEqual(2, bufferOfBackingSize4.removeFirst())
XCTAssertEqual(2, bufferOfBackingSize4.headBackingIndex)
let index3 = CircularBuffer<Int>.Index(backingIndex: 3, backingCount: 4, backingIndexOfHead: 2)
let index4 = bufferOfBackingSize4.index(after: index3)
XCTAssertEqual(index4.backingIndex, 0)
XCTAssertEqual(index4.isIndexGEQHeadIndex, false)
bufferOfBackingSize4.prepend(3)
XCTAssertEqual(1, bufferOfBackingSize4.headBackingIndex)
let index5 = CircularBuffer<Int>.Index(backingIndex: 0, backingCount: 4, backingIndexOfHead: 1)
let index6 = bufferOfBackingSize4.index(before: index5)
XCTAssertEqual(index6.backingIndex, 3)
XCTAssertEqual(index6.isIndexGEQHeadIndex, true)
let index7 = CircularBuffer<Int>.Index(backingIndex: 2, backingCount: 4, backingIndexOfHead: 1)
let index8 = bufferOfBackingSize4.index(before: index7)
XCTAssertEqual(index8.backingIndex, 1)
XCTAssertEqual(index8.isIndexGEQHeadIndex, true)
}
func testPopFirst() {
var buf = CircularBuffer([1, 2, 3])
if let element = buf.popFirst() {
XCTAssertEqual(1, element)
} else {
XCTFail("popFirst didn't find first element")
}
if let element = buf.popFirst() {
XCTAssertEqual(2, element)
} else {
XCTFail("popFirst didn't find second element")
}
if let element = buf.popFirst() {
XCTAssertEqual(3, element)
} else {
XCTFail("popFirst didn't find third element")
}
XCTAssertNil(buf.popFirst())
XCTAssertTrue(buf.testOnly_verifyInvariantsForNonSlices())
}
func testSlicing() {
var buf = CircularBuffer<Int>()
for i in -4..<124 {
buf.append(i)
}
XCTAssertEqual(-4, buf.removeFirst())
XCTAssertEqual(-3, buf.removeFirst())
XCTAssertEqual(-2, buf.removeFirst())
XCTAssertEqual(-1, buf.removeFirst())
buf.append(124)
buf.append(125)
buf.append(126)
buf.append(127)
let buf2: CircularBuffer<Int> = buf[buf.index(buf.startIndex, offsetBy: 100) ..< buf.endIndex]
XCTAssertEqual(Array(100..<128), Array(buf2))
XCTAssertEqual(Array(100..<128), Array(buf[buf2.startIndex ..< buf2.endIndex]))
}
func testRemoveInMiddle() {
var buf = CircularBuffer<Int>(initialCapacity: 8)
for i in 0..<7 {
buf.append(i)
}
XCTAssertEqual(0, buf.removeFirst())
XCTAssertTrue(buf.testOnly_verifyInvariantsForNonSlices())
buf.append(7)
XCTAssertEqual(2, buf.remove(at: buf.index(buf.startIndex, offsetBy: 1)))
XCTAssertTrue(buf.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual([1, 3, 4, 5, 6, 7], Array(buf))
buf.removeAll(keepingCapacity: true)
XCTAssertTrue(buf.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual([], Array(buf))
}
func testLotsOfPrepending() {
var buf = CircularBuffer<Int>(initialCapacity: 8)
for i in (0..<128).reversed() {
buf.prepend(i)
}
XCTAssertEqual(Array(0..<128), Array(buf))
}
func testLotsOfInsertAtStart() {
var buf = CircularBuffer<Int>(initialCapacity: 8)
for i in (0..<128).reversed() {
buf.insert(i, at: buf.startIndex)
}
XCTAssertEqual(Array(0..<128), Array(buf))
}
func testLotsOfInsertAtEnd() {
var buf = CircularBuffer<Int>(initialCapacity: 8)
for i in (0..<128) {
buf.insert(i, at: buf.endIndex)
}
XCTAssertEqual(Array(0..<128), Array(buf))
}
func testPopLast() {
var buf = CircularBuffer<Int>(initialCapacity: 4)
buf.append(1)
XCTAssertEqual(1, buf.popLast())
XCTAssertNil(buf.popLast())
buf.append(1)
buf.append(2)
buf.append(3)
XCTAssertEqual(3, buf.popLast())
XCTAssertEqual(2, buf.popLast())
XCTAssertEqual(1, buf.popLast())
XCTAssertNil(buf.popLast())
}
func testModify() {
var buf = CircularBuffer<Int>(initialCapacity: 4)
buf.append(contentsOf: 0..<4)
XCTAssertEqual(Array(0..<4), Array(buf))
let secondIndex = buf.index(after: buf.startIndex)
buf.modify(secondIndex) { value in
XCTAssertEqual(value, 1)
value = 5
}
XCTAssertEqual([0, 5, 2, 3], Array(buf))
}
func testEquality() {
// Empty buffers
let emptyA = CircularBuffer<Int>()
let emptyB = CircularBuffer<Int>()
XCTAssertEqual(emptyA, emptyB)
var buffA = CircularBuffer<Int>()
var buffB = CircularBuffer<Int>()
var buffC = CircularBuffer<Int>()
var buffD = CircularBuffer<Int>()
buffA.append(contentsOf: 1...10)
buffB.append(contentsOf: 1...10)
buffC.append(contentsOf: 2...11) // Same count different values
buffD.append(contentsOf: 1...2) // Different count
XCTAssertEqual(buffA, buffB)
XCTAssertNotEqual(buffA, buffC)
XCTAssertNotEqual(buffA, buffD)
// Will make internal head/tail indexes different
var prependBuff = CircularBuffer<Int>()
var appendBuff = CircularBuffer<Int>()
for i in (1...100).reversed() {
prependBuff.prepend(i)
}
for i in 1...100 {
appendBuff.append(i)
}
// But the contents are still the same
XCTAssertEqual(prependBuff, appendBuff)
}
func testHash() {
let emptyA = CircularBuffer<Int>()
let emptyB = CircularBuffer<Int>()
XCTAssertEqual(Set([emptyA,emptyB]).count, 1)
var buffA = CircularBuffer<Int>()
var buffB = CircularBuffer<Int>()
buffA.append(contentsOf: 1...10)
buffB.append(contentsOf: 1...10)
XCTAssertEqual(Set([buffA,buffB]).count, 1)
buffB.append(123)
XCTAssertEqual(Set([buffA,buffB]).count, 2)
buffA.append(1)
XCTAssertEqual(Set([buffA,buffB]).count, 2)
// Will make internal head/tail indexes different
var prependBuff = CircularBuffer<Int>()
var appendBuff = CircularBuffer<Int>()
for i in (1...100).reversed() {
prependBuff.prepend(i)
}
for i in 1...100 {
appendBuff.append(i)
}
XCTAssertEqual(Set([prependBuff,appendBuff]).count, 1)
}
func testArrayLiteralInit() {
let empty: CircularBuffer<Int> = []
XCTAssert(empty.isEmpty)
let increasingInts: CircularBuffer = [1, 2, 3, 4, 5]
XCTAssertEqual(increasingInts.count, 5)
XCTAssert(zip(increasingInts, 1...5).allSatisfy(==))
let someIntsArray = [-9, 384, 2, 10, 0, 0, 0]
let someInts: CircularBuffer = [-9, 384, 2, 10, 0, 0, 0]
XCTAssertEqual(someInts.count, 7)
XCTAssert(zip(someInts, someIntsArray).allSatisfy(==))
}
func testFirstWorks() {
var buffer = CircularBuffer<Int>(initialCapacity: 4)
XCTAssertNil(buffer.first)
buffer.append(1)
XCTAssertEqual(1, buffer.first)
XCTAssertEqual(1, buffer.removeFirst())
XCTAssertNil(buffer.first)
for n in 0..<10 {
buffer.append(contentsOf: 0...n)
for i in 0...n {
XCTAssertEqual(i, buffer.first)
XCTAssertEqual(i, buffer.removeFirst())
}
XCTAssertNil(buffer.first)
}
}
func testReserveCapacityActuallyDoesSomething() {
var buffer = CircularBuffer<Int>(initialCapacity: 4)
XCTAssertEqual(buffer.capacity, 4)
buffer.reserveCapacity(16)
XCTAssertEqual(buffer.capacity, 16)
buffer.reserveCapacity(8)
XCTAssertEqual(buffer.capacity, 16)
buffer.reserveCapacity(20)
XCTAssertEqual(buffer.capacity, 32)
buffer.reserveCapacity(0)
XCTAssertEqual(buffer.capacity, 32)
}
}