1
1
// This source file is part of the Swift.org open source project
2
2
//
3
- // Copyright (c) 2016, 2018 Apple Inc. and the Swift project authors
3
+ // Copyright (c) 2016, 2018, 2019 Apple Inc. and the Swift project authors
4
4
// Licensed under Apache License v2.0 with Runtime Library Exception
5
5
//
6
6
// See https://swift.org/LICENSE.txt for license information
7
7
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8
8
//
9
9
10
10
class TestFileHandle : XCTestCase {
11
- static var allTests : [ ( String , ( TestFileHandle ) -> ( ) throws -> ( ) ) ] {
12
- return [
13
- ( " test_constants " , test_constants) ,
14
- ( " test_nullDevice " , test_nullDevice) ,
15
- ( " test_truncateFile " , test_truncateFile)
16
- ]
11
+ var allHandles : [ FileHandle ] = [ ]
12
+ var allTemporaryFileURLs : [ URL ] = [ ]
13
+
14
+ let content : Data = {
15
+ return """
16
+ CHAPTER I.
17
+
18
+ The Author gives some account of himself and family--His first
19
+ inducements to travel--He is shipwrecked, and swims for his life--Gets
20
+ safe on shore in the country of Lilliput--Is made a prisoner, and
21
+ carried up the country
22
+
23
+ CHAPTER II.
24
+
25
+ The emperor of Lilliput, attended by several of the nobility, comes to
26
+ see the Author in his confinement--The emperor's person and habits
27
+ described--Learned men appointed to teach the Author their language--He
28
+ gains favor by his mild disposition--His pockets are searched, and his
29
+ sword and pistols taken from him
30
+
31
+ CHAPTER III.
32
+
33
+ The Author diverts the emperor, and his nobility of both sexes, in a
34
+ very uncommon manner--The diversions of the court of Lilliput
35
+ described--The Author has his liberty granted him upon certain
36
+ conditions
37
+
38
+ CHAPTER IV.
39
+
40
+ Mildendo, the metropolis of Lilliput, described, together with the
41
+ emperor's palace--A conversation between the Author and a principal
42
+ secretary concerning the affairs of that empire--The Author's offers to
43
+ serve the emperor in his wars
44
+
45
+ CHAPTER V.
46
+
47
+ The Author, by an extraordinary stratagem, prevents an invasion--A high
48
+ title of honor is conferred upon him--Ambassadors arrive from the
49
+ emperor of Blefuscu, and sue for peace
50
+ """ . data ( using: . utf8) !
51
+ } ( )
52
+
53
+ func createFileHandle( ) -> FileHandle {
54
+ let url = URL ( fileURLWithPath: NSTemporaryDirectory ( ) ) . appendingPathComponent ( ProcessInfo . processInfo. globallyUniqueString)
55
+
56
+ expectDoesNotThrow ( { try content. write ( to: url) } , " Couldn't write file at \( url. path) for testing " )
57
+
58
+ var fh : FileHandle ?
59
+ expectDoesNotThrow ( { fh = try FileHandle ( forReadingFrom: url) } , " Couldn't create file handle. " )
60
+
61
+ allHandles. append ( fh!)
62
+ allTemporaryFileURLs. append ( url)
63
+ return fh!
64
+ }
65
+
66
+ func createFileHandleForSeekErrors( ) -> FileHandle {
67
+ var fds : [ Int32 ] = [ - 1 , - 1 ]
68
+ fds. withUnsafeMutableBufferPointer { ( pointer) -> Void in
69
+ pipe ( pointer. baseAddress)
70
+ }
71
+
72
+ close ( fds [ 1 ] )
73
+
74
+ let fh = FileHandle ( fileDescriptor: fds [ 0 ] , closeOnDealloc: true )
75
+ allHandles. append ( fh)
76
+ return fh
77
+ }
78
+
79
+ let seekError = NSError ( domain: NSCocoaErrorDomain, code: NSFileReadUnknownError, userInfo: [ NSUnderlyingErrorKey: NSError ( domain: NSPOSIXErrorDomain, code: Int ( ESPIPE) , userInfo: [ : ] ) ] )
80
+
81
+ func createFileHandleForReadErrors( ) -> FileHandle {
82
+ // Create a file handle where calling read returns -1.
83
+ // Accomplish this by creating one for a directory.
84
+ let fd = open ( " . " , O_RDONLY)
85
+ expectTrue ( fd > 0 , " We must be able to open a fd to the current directory (.) " )
86
+ let fh = FileHandle ( fileDescriptor: fd, closeOnDealloc: true )
87
+ allHandles. append ( fh)
88
+ return fh
89
+ }
90
+
91
+ let readError = NSError ( domain: NSCocoaErrorDomain, code: NSFileReadUnknownError, userInfo: [ NSUnderlyingErrorKey: NSError ( domain: NSPOSIXErrorDomain, code: Int ( EISDIR) , userInfo: [ : ] ) ] )
92
+
93
+ override func tearDown( ) {
94
+ for handle in allHandles {
95
+ print ( " Closing \( handle) … " )
96
+ try ? handle. close ( )
97
+ }
98
+
99
+ for url in allTemporaryFileURLs {
100
+ print ( " Deleting \( url) … " )
101
+ try ? FileManager . default. removeItem ( at: url)
102
+ }
103
+
104
+ allHandles = [ ]
105
+ allTemporaryFileURLs = [ ]
106
+ }
107
+
108
+ func testHandleCreationAndCleanup( ) {
109
+ _ = createFileHandle ( )
110
+ _ = createFileHandleForSeekErrors ( )
111
+ _ = createFileHandleForReadErrors ( )
112
+ }
113
+
114
+ func testReadUpToCount( ) {
115
+ let handle = createFileHandle ( )
116
+
117
+ // Zero:
118
+ expectDoesNotThrow ( {
119
+ let zeroData = try handle. read ( upToCount: 0 )
120
+ expectEqual ( zeroData, nil , " Data should be nil " )
121
+ } , " Must not throw while reading zero data " )
122
+
123
+ // Max:
124
+ expectDoesNotThrow ( {
125
+ let maxData = try handle. read ( upToCount: Int . max)
126
+ expectEqual ( maxData, content, " Data should be equal to the content " )
127
+ } , " Must not throw while reading Int.max data " )
128
+
129
+ // EOF:
130
+ expectDoesNotThrow ( {
131
+ let eof = try handle. read ( upToCount: Int . max)
132
+ expectEqual ( eof, nil , " EOF should return nil " )
133
+ } , " Must not throw while reading EOF " )
134
+
135
+ // One byte at a time
136
+ let onesHandle = createFileHandle ( )
137
+ expectDoesNotThrow ( {
138
+ for index in content. indices {
139
+ let oneByteData = try onesHandle. read ( upToCount: 1 )
140
+ let expected = content [ index ..< content. index ( after: index) ]
141
+ expectEqual ( oneByteData, expected, " Read incorrect data at index \( index) " )
142
+ }
143
+ } , " Must not throw while reading one byte at a time " )
144
+
145
+ // EOF:
146
+ expectDoesNotThrow ( {
147
+ let eof = try handle. read ( upToCount: 1 )
148
+ expectEqual ( eof, nil , " EOF should return nil " )
149
+ } , " Must not throw while reading one-byte-at-a-time EOF " )
150
+
151
+ // Errors:
152
+ expectThrows ( readError, {
153
+ _ = try createFileHandleForReadErrors ( ) . read ( upToCount: 1 )
154
+ } , " Must throw when encountering a read error " )
155
+ }
156
+
157
+ func testReadToEnd( ) {
158
+ let handle = createFileHandle ( )
159
+
160
+ // To end:
161
+ expectDoesNotThrow ( {
162
+ let maxData = try handle. readToEnd ( )
163
+ expectEqual ( maxData, content, " Data to end should equal what was written out " )
164
+ } , " Must not throw while reading to end " )
165
+
166
+ // EOF:
167
+ expectDoesNotThrow ( {
168
+ let eof = try handle. readToEnd ( )
169
+ expectEqual ( eof, nil , " EOF should return nil " )
170
+ } , " Must not throw while reading EOF " )
171
+
172
+ // Errors:
173
+ expectThrows ( readError, {
174
+ _ = try createFileHandleForReadErrors ( ) . readToEnd ( )
175
+ } , " Must throw when encountering a read error " )
176
+ }
177
+
178
+ func testOffset( ) {
179
+ // One byte at a time:
180
+ let handle = createFileHandle ( )
181
+ var offset : UInt64 = 0
182
+
183
+ for index in content. indices {
184
+ expectDoesNotThrow ( { offset = try handle. offset ( ) } , " Reading the offset must not throw " )
185
+ expectEqual ( offset, UInt64 ( index) , " The offset must match " )
186
+ expectDoesNotThrow ( { _ = try handle. read ( upToCount: 1 ) } , " Advancing by reading must not throw " )
187
+ }
188
+
189
+ expectDoesNotThrow ( { offset = try handle. offset ( ) } , " Reading the offset at EOF must not throw " )
190
+ expectEqual ( offset, UInt64 ( content. count) , " The offset at EOF must be at the end " )
191
+
192
+ // Error:
193
+ expectThrows ( seekError, {
194
+ _ = try createFileHandleForSeekErrors ( ) . offset ( )
195
+ } , " Must throw when encountering a seek error " )
196
+ }
197
+
198
+ func createPipe( ) -> Pipe {
199
+ let pipe = Pipe ( )
200
+ allHandles. append ( pipe. fileHandleForWriting)
201
+ allHandles. append ( pipe. fileHandleForReading)
202
+ return pipe
203
+ }
204
+
205
+ func performWriteTest< T: DataProtocol > ( with data: T , expecting expectation: Data ? = nil ) {
206
+ let pipe = createPipe ( )
207
+ let writer = pipe. fileHandleForWriting
208
+ let reader = pipe. fileHandleForReading
209
+
210
+ expectDoesNotThrow ( { try writer. write ( contentsOf: data) } , " Writing must succeed " )
211
+ expectDoesNotThrow ( {
212
+ expectEqual ( try reader. read ( upToCount: data. count) , expectation ?? content, " The content must be the same " )
213
+ } , " Reading must succeed " )
214
+ }
215
+
216
+ func testWritingWithData( ) {
217
+ performWriteTest ( with: content)
218
+ }
219
+
220
+ func testWritingWithBuffer( ) {
221
+ content. withUnsafeBytes { ( buffer) in
222
+ performWriteTest ( with: buffer)
223
+ }
224
+ }
225
+
226
+ func testWritingWithMultiregionData( ) {
227
+ var expectation = Data ( )
228
+ expectation. append ( content)
229
+ expectation. append ( content)
230
+ expectation. append ( content)
231
+ expectation. append ( content)
232
+
233
+ content. withUnsafeBytes { ( buffer) in
234
+ let data1 = DispatchData ( bytes: buffer)
235
+ let data2 = DispatchData ( bytes: buffer)
236
+
237
+ var multiregion1 : DispatchData = . empty
238
+ multiregion1. append ( data1)
239
+ multiregion1. append ( data2)
240
+
241
+ var multiregion2 : DispatchData = . empty
242
+ multiregion2. append ( data1)
243
+ multiregion2. append ( data2)
244
+
245
+ var longMultiregion : DispatchData = . empty
246
+ longMultiregion. append ( multiregion1)
247
+ longMultiregion. append ( multiregion2)
248
+
249
+ expectTrue ( longMultiregion. regions. count > 0 , " The multiregion data must be actually composed of multiple regions " )
250
+
251
+ performWriteTest ( with: longMultiregion, expecting: expectation)
252
+ }
17
253
}
18
254
19
255
func test_constants( ) {
@@ -32,10 +268,10 @@ class TestFileHandle : XCTestCase {
32
268
XCTAssertEqual ( fh. readData ( ofLength: 15 ) . count, 0 )
33
269
fh. synchronizeFile ( )
34
270
35
- fh. write ( Data ( bytes : [ 1 , 2 ] ) )
271
+ fh. write ( Data ( [ 1 , 2 ] ) )
36
272
fh. seek ( toFileOffset: 0 )
37
273
XCTAssertEqual ( fh. availableData. count, 0 )
38
- fh. write ( Data ( bytes : [ 1 , 2 ] ) )
274
+ fh. write ( Data ( [ 1 , 2 ] ) )
39
275
fh. seek ( toFileOffset: 0 )
40
276
XCTAssertEqual ( fh. readDataToEndOfFile ( ) . count, 0 )
41
277
}
@@ -51,13 +287,13 @@ class TestFileHandle : XCTestCase {
51
287
fh. truncateFile ( atOffset: 100 )
52
288
XCTAssertEqual ( fh. offsetInFile, 100 )
53
289
54
- fh. write ( Data ( bytes : [ 1 , 2 ] ) )
290
+ fh. write ( Data ( [ 1 , 2 ] ) )
55
291
XCTAssertEqual ( fh. offsetInFile, 102 )
56
292
57
293
fh. seek ( toFileOffset: 4 )
58
294
XCTAssertEqual ( fh. offsetInFile, 4 )
59
295
60
- ( 0 ..< 20 ) . forEach { fh. write ( Data ( bytes : [ $0] ) ) }
296
+ ( 0 ..< 20 ) . forEach { fh. write ( Data ( [ $0] ) ) }
61
297
XCTAssertEqual ( fh. offsetInFile, 24 )
62
298
63
299
fh. seekToEndOfFile ( )
@@ -71,7 +307,22 @@ class TestFileHandle : XCTestCase {
71
307
72
308
let data = fh. readDataToEndOfFile ( )
73
309
XCTAssertEqual ( data. count, 10 )
74
- XCTAssertEqual ( data, Data ( bytes : [ 0 , 0 , 0 , 0 , 0 , 1 , 2 , 3 , 4 , 5 ] ) )
310
+ XCTAssertEqual ( data, Data ( [ 0 , 0 , 0 , 0 , 0 , 1 , 2 , 3 , 4 , 5 ] ) )
75
311
}
76
312
}
313
+
314
+ static var allTests : [ ( String , ( TestFileHandle ) -> ( ) throws -> ( ) ) ] {
315
+ return [
316
+ ( " testHandleCreationAndCleanup " , testHandleCreationAndCleanup) ,
317
+ ( " testReadUpToCount " , testReadUpToCount) ,
318
+ ( " testReadToEnd " , testReadToEnd) ,
319
+ ( " testOffset " , testOffset) ,
320
+ ( " testWritingWithData " , testWritingWithData) ,
321
+ ( " testWritingWithBuffer " , testWritingWithBuffer) ,
322
+ ( " testWritingWithMultiregionData " , testWritingWithMultiregionData) ,
323
+ ( " test_constants " , test_constants) ,
324
+ ( " test_nullDevice " , test_nullDevice) ,
325
+ ( " test_truncateFile " , test_truncateFile)
326
+ ]
327
+ }
77
328
}
0 commit comments