Skip to content

Commit 6078b98

Browse files
authored
Convert Data/DataIO Tests to swift-testing (#1363)
* Convert DataIO tests * Convert Data tests * Move string path tests from Data IO suite to String suite * Cleanup span expectations * Fix build failure * Fix watchOS build failure * Move large data allocation tests into separate serialized suite
1 parent 4e0d7b9 commit 6078b98

File tree

3 files changed

+1012
-1041
lines changed

3 files changed

+1012
-1041
lines changed

Tests/FoundationEssentialsTests/DataIOTests.swift

Lines changed: 92 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -10,71 +10,63 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#if canImport(TestSupport)
14-
import TestSupport
15-
#endif
16-
17-
#if canImport(Glibc)
18-
@preconcurrency import Glibc
19-
#endif
13+
import Testing
2014

2115
#if FOUNDATION_FRAMEWORK
2216
@testable import Foundation
2317
#else
2418
@testable import FoundationEssentials
2519
#endif // FOUNDATION_FRAMEWORK
2620

27-
class DataIOTests : XCTestCase {
21+
private func generateTestData(count: Int = 16_777_216) -> Data {
22+
// Set a few bytes so we're sure to not be all zeros
23+
let buf = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: count)
24+
for i in 0..<15 {
25+
for j in 0..<128 {
26+
buf[j * 1024 + i] = UInt8.random(in: 1..<42)
27+
}
28+
}
29+
30+
return Data(bytesNoCopy: buf.baseAddress!, count: count, deallocator: .custom({ ptr, _ in
31+
ptr.deallocate()
32+
}))
33+
}
34+
35+
@Suite("Data I/O")
36+
private final class DataIOTests {
2837

2938
// MARK: - Helpers
3039

31-
func testURL() -> URL {
32-
// Generate a random file name
33-
URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent("testfile-\(UUID().uuidString)")
34-
}
40+
let url: URL
3541

36-
func generateTestData(count: Int = 16_777_216) -> Data {
37-
let memory = malloc(count)!
38-
let ptr = memory.bindMemory(to: UInt8.self, capacity: count)
39-
40-
// Set a few bytes so we're sure to not be all zeros
41-
let buf = UnsafeMutableBufferPointer(start: ptr, count: count)
42-
for i in 0..<15 {
43-
for j in 0..<128 {
44-
buf[j * 1024 + i] = UInt8.random(in: 1..<42)
45-
}
46-
}
47-
48-
return Data(bytesNoCopy: ptr, count: count, deallocator: .free)
42+
init() {
43+
// Generate a random file name
44+
url = URL.temporaryDirectory.appendingPathComponent("testfile-\(UUID().uuidString)")
4945
}
5046

51-
func writeAndVerifyTestData(to url: URL, writeOptions: Data.WritingOptions = [], readOptions: Data.ReadingOptions = []) throws {
47+
func writeAndVerifyTestData(to url: URL, writeOptions: Data.WritingOptions = [], readOptions: Data.ReadingOptions = [], sourceLocation: SourceLocation = #_sourceLocation) throws {
5248
let data = generateTestData()
5349
try data.write(to: url, options: writeOptions)
5450
let readData = try Data(contentsOf: url, options: readOptions)
55-
XCTAssertEqual(data, readData)
51+
#expect(data == readData, sourceLocation: sourceLocation)
5652
}
5753

58-
func cleanup(at url: URL) {
54+
deinit {
5955
do {
6056
try FileManager.default.removeItem(at: url)
6157
} catch {
6258
// Ignore
6359
}
6460
}
6561

66-
6762
// MARK: - Tests
6863

69-
func test_basicReadWrite() throws {
70-
let url = testURL()
64+
@Test func basicReadWrite() throws {
7165
try writeAndVerifyTestData(to: url)
72-
cleanup(at: url)
7366
}
7467

75-
func test_slicedReadWrite() throws {
68+
@Test func slicedReadWrite() throws {
7669
// Be sure to use progress reporting so we get tests of the chunking
77-
let url = testURL()
7870
let data = generateTestData()
7971
let slice = data[data.startIndex.advanced(by: 1 * 1024 * 1024)..<data.startIndex.advanced(by: 8 * 1024 * 1024)]
8072

@@ -87,76 +79,57 @@ class DataIOTests : XCTestCase {
8779
p.resignCurrent()
8880
#endif
8981
let readData = try Data(contentsOf: url, options: [])
90-
XCTAssertEqual(readData, slice)
91-
cleanup(at: url)
82+
#expect(readData == slice)
9283
}
9384

85+
#if !os(WASI)
9486
// Atomic writing is a very different code path
95-
func test_readWriteAtomic() throws {
96-
#if os(WASI)
97-
try XCTSkip("atomic writing is not supported on WASI")
98-
#else
99-
let url = testURL()
87+
@Test func readWriteAtomic() throws {
10088
// Perform an atomic write to a file that does not exist
10189
try writeAndVerifyTestData(to: url, writeOptions: [.atomic])
10290

10391
// Perform an atomic write to a file that already exists
10492
try writeAndVerifyTestData(to: url, writeOptions: [.atomic])
105-
106-
cleanup(at: url)
107-
#endif
10893
}
94+
#endif
10995

110-
func test_readWriteMapped() throws {
111-
let url = testURL()
96+
@Test func readWriteMapped() throws {
11297
try writeAndVerifyTestData(to: url, readOptions: [.mappedIfSafe])
113-
114-
cleanup(at: url)
11598
}
11699

117-
func test_writeFailure() throws {
118-
let url = testURL()
119-
100+
@Test func writeFailure() throws {
120101
let data = Data()
121102
try data.write(to: url)
122-
123-
#if FOUNDATION_FRAMEWORK
124-
XCTAssertThrowsError(try data.write(to: url, options: [.withoutOverwriting])) { e in
125-
XCTAssertEqual((e as NSError).code, NSFileWriteFileExistsError)
103+
104+
#expect {
105+
try data.write(to: url, options: [.withoutOverwriting])
106+
} throws: {
107+
($0 as? CocoaError)?.code == .fileWriteFileExists
126108
}
127-
#else
128-
XCTAssertThrowsError(try data.write(to: url, options: [.withoutOverwriting]))
129-
#endif
130109

131-
cleanup(at: url)
110+
try FileManager.default.removeItem(at: url)
132111

133112
// Make sure clearing the error condition allows the write to succeed
134113
try data.write(to: url, options: [.withoutOverwriting])
135-
136-
cleanup(at: url)
137114
}
138115

139116
#if FOUNDATION_FRAMEWORK
140117
// Progress is currently stubbed out for FoundationPreview
141-
func test_writeWithProgress() throws {
142-
let url = testURL()
143-
118+
@Test func writeWithProgress() throws {
144119
let p = Progress(totalUnitCount: 1)
145120
p.becomeCurrent(withPendingUnitCount: 1)
146121
try writeAndVerifyTestData(to: url)
147122
p.resignCurrent()
148123

149-
XCTAssertEqual(p.completedUnitCount, 1)
150-
XCTAssertEqual(p.fractionCompleted, 1.0, accuracy: 0.1)
151-
cleanup(at: url)
124+
#expect(p.completedUnitCount == 1)
125+
#expect(abs(p.fractionCompleted - 1.0) <= 0.1)
152126
}
153127
#endif
154128

155129
#if FOUNDATION_FRAMEWORK
156-
func test_writeWithAttributes() throws {
130+
@Test func writeWithAttributes() throws {
157131
let writeData = generateTestData()
158132

159-
let url = testURL()
160133
// Data doesn't have a direct API to write with attributes, but our I/O code has it. Use it via @testable interface here.
161134

162135
let writeAttrs: [String : Data] = [FileAttributeKey.hfsCreatorCode.rawValue : "abcd".data(using: .ascii)!]
@@ -166,146 +139,85 @@ class DataIOTests : XCTestCase {
166139
var readAttrs: [String : Data] = [:]
167140
let readData = try readDataFromFile(path: .url(url), reportProgress: false, options: [], attributesToRead: [FileAttributeKey.hfsCreatorCode.rawValue], attributes: &readAttrs)
168141

169-
XCTAssertEqual(writeData, readData)
170-
XCTAssertEqual(writeAttrs, readAttrs)
171-
172-
cleanup(at: url)
142+
#expect(writeData == readData)
143+
#expect(writeAttrs == readAttrs)
173144
}
174145
#endif
175146

176-
func test_emptyFile() throws {
147+
@Test func emptyFile() throws {
177148
let data = Data()
178-
let url = testURL()
179149
try data.write(to: url)
180150
let read = try Data(contentsOf: url, options: [])
181-
XCTAssertEqual(data, read)
182-
183-
cleanup(at: url)
151+
#expect(data == read)
184152
}
185153

186154
#if FOUNDATION_FRAMEWORK
187155
// String(contentsOf:) is not available outside the framework yet
188156
@available(*, deprecated)
189-
func test_emptyFileString() {
157+
@Test func emptyFileString() throws {
190158
let data = Data()
191-
let url = testURL()
192-
193-
do {
194-
try data.write(to: url)
195-
let readString = try String(contentsOf: url)
196-
XCTAssertEqual(readString, "")
197-
198-
let readStringWithEncoding = try String(contentsOf: url, encoding: String._Encoding.utf8)
199-
XCTAssertEqual(readStringWithEncoding, "")
200-
201-
cleanup(at: url)
202-
} catch {
203-
XCTFail("Could not read file: \(error)")
204-
}
205-
}
206-
#endif
207-
208-
func test_largeFile() throws {
209-
#if !os(watchOS)
210-
// More than 2 GB
211-
let size = 0x80010000
212-
let url = testURL()
213-
214-
let data = generateTestData(count: size)
215159

216160
try data.write(to: url)
217-
let read = try! Data(contentsOf: url, options: .mappedIfSafe)
218-
219-
// No need to compare the contents, but do compare the size
220-
XCTAssertEqual(data.count, read.count)
161+
let readString = try String(contentsOf: url)
162+
#expect(readString == "")
221163

222-
#if FOUNDATION_FRAMEWORK
223-
// Try the NSData path
224-
let readNS = try! NSData(contentsOf: url, options: .mappedIfSafe) as Data
225-
XCTAssertEqual(data.count, readNS.count)
226-
#endif
227-
228-
cleanup(at: url)
229-
#endif // !os(watchOS)
164+
let readStringWithEncoding = try String(contentsOf: url, encoding: .utf8)
165+
#expect(readStringWithEncoding == "")
230166
}
167+
#endif
231168

232-
func test_writeToSpecialFile() throws {
233-
#if !os(Linux) && !os(Windows)
234-
throw XCTSkip("This test is only supported on Linux and Windows")
235-
#else
169+
#if os(Linux) || os(Windows)
170+
@Test
171+
#else
172+
@Test(.disabled("This test is not applicable on this platform"))
173+
#endif
174+
func writeToSpecialFile() throws {
236175
#if os(Windows)
237176
let path = URL(filePath: "CON", directoryHint: .notDirectory)
238177
#else
239178
let path = URL(filePath: "/dev/stdout", directoryHint: .notDirectory)
240179
#endif
241-
XCTAssertNoThrow(try Data("Output to STDOUT\n".utf8).write(to: path))
242-
#endif
180+
#expect(throws: Never.self) {
181+
try Data("Output to STDOUT\n".utf8).write(to: path)
182+
}
243183
}
244184

245-
func test_zeroSizeFile() throws {
246-
#if !os(Linux) && !os(Android)
247-
throw XCTSkip("This test is only applicable on Linux")
248-
#else
185+
#if os(Linux) || os(Android)
186+
@Test
187+
#else
188+
@Test(.disabled("This test is not applicable on this platform"))
189+
#endif
190+
func zeroSizeFile() throws {
249191
// Some files in /proc report a file size of 0 bytes via a stat call
250192
// Ensure that these files can still be read despite appearing to be empty
251-
let maps = try String(contentsOfFile: "/proc/self/maps", encoding: String._Encoding.utf8)
252-
XCTAssertFalse(maps.isEmpty)
253-
#endif
193+
let maps = try String(contentsOfFile: "/proc/self/maps", encoding: .utf8)
194+
#expect(!maps.isEmpty)
254195
}
196+
}
255197

256-
// MARK: - String Path Tests
257-
func testStringDeletingLastPathComponent() {
258-
XCTAssertEqual("/a/b/c".deletingLastPathComponent(), "/a/b")
259-
XCTAssertEqual("".deletingLastPathComponent(), "")
260-
XCTAssertEqual("/".deletingLastPathComponent(), "/")
261-
XCTAssertEqual("q".deletingLastPathComponent(), "")
262-
XCTAssertEqual("/aaa".deletingLastPathComponent(), "/")
263-
XCTAssertEqual("/aaa/".deletingLastPathComponent(), "/")
264-
XCTAssertEqual("/a/b/c/".deletingLastPathComponent(), "/a/b")
265-
XCTAssertEqual("hello".deletingLastPathComponent(), "")
266-
XCTAssertEqual("hello/".deletingLastPathComponent(), "")
267-
XCTAssertEqual("/hello/".deletingLastPathComponent(), "/")
268-
XCTAssertEqual("hello///".deletingLastPathComponent(), "")
269-
XCTAssertEqual("a/".deletingLastPathComponent(), "")
270-
XCTAssertEqual("a/b".deletingLastPathComponent(), "a")
271-
XCTAssertEqual("a/b/".deletingLastPathComponent(), "a")
272-
XCTAssertEqual("a//b//".deletingLastPathComponent(), "a")
273-
}
274-
275-
func testAppendingPathComponent() {
276-
let comp = "test"
277-
XCTAssertEqual("/a/b/c".appendingPathComponent(comp), "/a/b/c/test")
278-
XCTAssertEqual("".appendingPathComponent(comp), "test")
279-
XCTAssertEqual("/".appendingPathComponent(comp), "/test")
280-
XCTAssertEqual("q".appendingPathComponent(comp), "q/test")
281-
XCTAssertEqual("/aaa".appendingPathComponent(comp), "/aaa/test")
282-
XCTAssertEqual("/a/b/c/".appendingPathComponent(comp), "/a/b/c/test")
283-
XCTAssertEqual("hello".appendingPathComponent(comp), "hello/test")
284-
XCTAssertEqual("hello/".appendingPathComponent(comp), "hello/test")
198+
extension LargeDataTests {
199+
// This test is placed in the LargeDataTests suite since it allocates an extremely large amount of memory for some devices
200+
#if !os(watchOS)
201+
@Test func readLargeFile() throws {
202+
let url = URL.temporaryDirectory.appendingPathComponent("testfile-\(UUID().uuidString)")
203+
defer { try? FileManager.default.removeItem(at: url) }
204+
// More than 2 GB
205+
let size = 0x80010000
285206

286-
XCTAssertEqual("hello/".appendingPathComponent("/test"), "hello/test")
287-
XCTAssertEqual("hello".appendingPathComponent("/test"), "hello/test")
288-
XCTAssertEqual("hello///".appendingPathComponent("///test"), "hello/test")
289-
XCTAssertEqual("hello".appendingPathComponent("test/"), "hello/test")
290-
XCTAssertEqual("hello".appendingPathComponent("test/test2"), "hello/test/test2")
291-
XCTAssertEqual("hello".appendingPathComponent("test/test2/"), "hello/test/test2")
292-
XCTAssertEqual("hello".appendingPathComponent("test///test2/"), "hello/test/test2")
293-
XCTAssertEqual("hello".appendingPathComponent("/"), "hello")
294-
XCTAssertEqual("//".appendingPathComponent("/"), "/")
295-
XCTAssertEqual("".appendingPathComponent(""), "")
296-
}
297-
298-
func testStringLastPathComponent() {
299-
XCTAssertEqual("/a/b/c".lastPathComponent, "c")
300-
XCTAssertEqual("".lastPathComponent, "")
301-
XCTAssertEqual("/".lastPathComponent, "/")
302-
XCTAssertEqual("q".lastPathComponent, "q")
303-
XCTAssertEqual("/aaa".lastPathComponent, "aaa")
304-
XCTAssertEqual("/a/b/c/".lastPathComponent, "c")
305-
XCTAssertEqual("hello".lastPathComponent, "hello")
306-
XCTAssertEqual("hello/".lastPathComponent, "hello")
307-
XCTAssertEqual("hello///".lastPathComponent, "hello")
308-
XCTAssertEqual("//a//".lastPathComponent, "a")
207+
let data = generateTestData(count: size)
208+
209+
try data.write(to: url)
210+
let read = try Data(contentsOf: url, options: .mappedIfSafe)
211+
212+
// No need to compare the contents, but do compare the size
213+
#expect(data.count == read.count)
214+
215+
#if FOUNDATION_FRAMEWORK
216+
// Try the NSData path
217+
let readNS = try NSData(contentsOf: url, options: .mappedIfSafe) as Data
218+
#expect(data.count == readNS.count)
219+
#endif
309220
}
221+
#endif
310222
}
311223

0 commit comments

Comments
 (0)