Files
swift-nio/Tests/NIOFSIntegrationTests/BufferedReaderTests.swift
George Barnett a18bddb0ac Add back NIOFileSystem (#3380)
Motivation:

We accidentally removed the 'NIOFileSystem' module from the
'_NIOFileSystem' product in the last release.

Modifications:

- Rename 'NIOFileSystem' and 'NIOFileSystemFoundationCompat' to 'NIOFS'
  and 'NIOFSFoundationCompat'
- Add back 'NIOFileSystem' which re-exports '_NIOFileSystem' (there was
  no publicly available 'NIOFileSystemFoundationCompat' module to
  remove, only '_NIOFileSystemFoundationCompat').

Result:

Fewer breaks
2025-09-23 17:29:01 +01:00

282 lines
10 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2023 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 NIOCore
import NIOFS
import XCTest
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
final class BufferedReaderTests: XCTestCase {
func testBufferedReaderSizeAndCapacity() async throws {
let fs = FileSystem.shared
try await fs.withFileHandle(forReadingAt: #filePath) { handle in
var reader = handle.bufferedReader(capacity: .bytes(128 * 1024))
XCTAssertEqual(reader.count, 0)
XCTAssertEqual(reader.capacity, 128 * 1024)
let byte = try await reader.read(.bytes(1))
XCTAssertEqual(byte.readableBytes, 1)
// Buffer should be non-empty; there's more than one byte in this file.
XCTAssertGreaterThan(reader.count, 0)
// It should be no greater than the buffer capacity, however.
XCTAssertLessThanOrEqual(reader.count, reader.capacity)
}
}
func testBufferedReaderReadFixedSize() async throws {
let fs = FileSystem.shared
let path = try await fs.temporaryFilePath()
try await fs.withFileHandle(
forWritingAt: path,
options: .newFile(replaceExisting: false)
) { handle in
var writer = handle.bufferedWriter()
try await writer.write(contentsOf: repeatElement(0, count: 1024 * 1024))
try await writer.flush()
}
try await fs.withFileHandle(forReadingAt: path) { handle in
var reader = handle.bufferedReader()
let allTheBytes = try await reader.read(.bytes(1024 * 1024))
XCTAssertEqual(allTheBytes.readableBytes, 1024 * 1024)
let noBytes = try await reader.read(.bytes(1024))
XCTAssertEqual(noBytes.readableBytes, 0)
}
}
func testBufferedReaderMultipleReads() async throws {
let fs = FileSystem.shared
let path = try await fs.temporaryFilePath()
try await fs.withFileHandle(
forWritingAt: path,
options: .newFile(replaceExisting: false)
) { handle in
var writer = handle.bufferedWriter()
try await writer.write(contentsOf: repeatElement(0, count: 1024 * 1024))
try await writer.flush()
}
try await fs.withFileHandle(forReadingAt: path) { handle in
var reader = handle.bufferedReader()
var allTheBytes = [UInt8]()
while true {
let byte = try await reader.read(.bytes(100))
if byte.readableBytes == 0 { break }
allTheBytes.append(contentsOf: byte.readableBytesView)
}
XCTAssertEqual(allTheBytes.count, 1024 * 1024)
}
}
func testBufferedReaderReadingShort() async throws {
let fs = FileSystem.shared
try await fs.withFileHandle(forReadingAt: #filePath) { handle in
var reader = handle.bufferedReader(capacity: .bytes(128))
var buffer = ByteBuffer()
while true {
let chunk = try await reader.read(.bytes(128))
buffer.writeImmutableBuffer(chunk)
if chunk.readableBytes < 128 { break }
}
let info = try await handle.info()
XCTAssertEqual(Int64(buffer.readableBytes), info.size)
}
}
func testBufferedReaderReadWhile() async throws {
let fs = FileSystem.shared
let path = try await fs.temporaryFilePath()
try await fs.withFileHandle(
forWritingAt: path,
options: .newFile(replaceExisting: false)
) { handle in
var writer = handle.bufferedWriter()
for byte in UInt8.min...UInt8.max {
try await writer.write(contentsOf: repeatElement(byte, count: 1024))
}
try await writer.flush()
}
try await fs.withFileHandle(forReadingAt: path) { handle in
var reader = handle.bufferedReader()
let (zeros, isEOFZeros) = try await reader.read { $0 == 0 }
XCTAssertEqual(zeros, ByteBuffer(bytes: Array(repeating: 0, count: 1024)))
XCTAssertFalse(isEOFZeros)
let (onesAndTwos, isEOFOnesAndTwos) = try await reader.read { $0 < 3 }
var expectedOnesAndTwos = ByteBuffer()
expectedOnesAndTwos.writeRepeatingByte(1, count: 1024)
expectedOnesAndTwos.writeRepeatingByte(2, count: 1024)
XCTAssertEqual(onesAndTwos, expectedOnesAndTwos)
XCTAssertFalse(isEOFOnesAndTwos)
let (threesThroughNines, isEOFThreesThroughNines) = try await reader.read { $0 < 10 }
var expectedThreesThroughNines = ByteBuffer()
for byte in UInt8(3)...9 {
expectedThreesThroughNines.writeRepeatingByte(byte, count: 1024)
}
XCTAssertEqual(threesThroughNines, expectedThreesThroughNines)
XCTAssertFalse(isEOFThreesThroughNines)
let (theRest, isEOFTheRest) = try await reader.read { _ in true }
XCTAssertEqual(theRest.readableBytes, 246 * 1024)
XCTAssertTrue(isEOFTheRest)
}
}
func testBufferedReaderDropFixedSize() async throws {
let fs = FileSystem.shared
let path = try await fs.temporaryFilePath()
try await fs.withFileHandle(
forWritingAt: path,
options: .newFile(replaceExisting: false)
) { handle in
var writer = handle.bufferedWriter()
for byte in UInt8.min...UInt8.max {
try await writer.write(contentsOf: repeatElement(byte, count: 1000))
}
try await writer.flush()
}
try await fs.withFileHandle(forReadingAt: path) { handle in
var reader = handle.bufferedReader()
try await reader.drop(1000)
let ones = try await reader.read(.bytes(1000))
XCTAssertEqual(ones, ByteBuffer(repeating: 1, count: 1000))
try await reader.drop(1500)
let threes = try await reader.read(.bytes(500))
XCTAssertEqual(threes, ByteBuffer(repeating: 3, count: 500))
// More than remains in the file.
try await reader.drop(1_000_000)
let empty = try await reader.read(.bytes(1000))
XCTAssertEqual(empty.readableBytes, 0)
}
}
func testBufferedReaderDropWhile() async throws {
let fs = FileSystem.shared
let path = try await fs.temporaryFilePath()
try await fs.withFileHandle(
forWritingAt: path,
options: .newFile(replaceExisting: false)
) { handle in
var writer = handle.bufferedWriter()
for byte in UInt8.min...UInt8.max {
try await writer.write(contentsOf: repeatElement(byte, count: 1024))
}
try await writer.flush()
}
try await fs.withFileHandle(forReadingAt: path) { handle in
var reader = handle.bufferedReader()
try await reader.drop(while: { $0 < 255 })
let bytes = try await reader.read(.bytes(1024))
XCTAssertEqual(bytes, ByteBuffer(repeating: 255, count: 1024))
let empty = try await reader.read(.bytes(1))
XCTAssertEqual(empty.readableBytes, 0)
}
}
func testBufferedReaderReadingText() async throws {
let fs = FileSystem.shared
let path = try await fs.temporaryFilePath()
try await fs.withFileHandle(
forWritingAt: path,
options: .newFile(replaceExisting: false)
) { handle in
let text = """
Here's to the crazy ones, the misfits, the rebels, the troublemakers, \
the round pegs in the square holes, the ones who see things differently.
"""
var writer = handle.bufferedWriter()
try await writer.write(contentsOf: text.utf8)
try await writer.flush()
}
try await fs.withFileHandle(forReadingAt: path) { file in
var reader = file.bufferedReader()
var words = [String]()
func isWordIsh(_ byte: UInt8) -> Bool {
switch byte {
case UInt8(ascii: "a")...UInt8(ascii: "z"),
UInt8(ascii: "A")...UInt8(ascii: "Z"),
UInt8(ascii: "'"):
return true
default:
return false
}
}
repeat {
// Gobble up whitespace etc..
try await reader.drop(while: { !isWordIsh($0) })
// Read the next word.
var (characters, _) = try await reader.read(while: isWordIsh(_:))
if characters.readableBytes == 0 {
break // Done.
} else {
words.append(characters.readString(length: characters.readableBytes)!)
}
} while true
let expected: [String] = [
"Here's",
"to",
"the",
"crazy",
"ones",
"the",
"misfits",
"the",
"rebels",
"the",
"troublemakers",
"the",
"round",
"pegs",
"in",
"the",
"square",
"holes",
"the",
"ones",
"who",
"see",
"things",
"differently",
]
XCTAssertEqual(words, expected)
}
}
}