@@ -256,6 +256,80 @@ class URLSessionHelperTests: XCTestCase {
256256 )
257257 }
258258
259+ func testTempFileRemovedAfterMultipartUpload( ) async throws {
260+ stub ( condition: isPath ( " /upload " ) ) { _ in
261+ HTTPStubsResponse ( data: " success " . data ( using: . utf8) !, statusCode: 200 , headers: nil )
262+ }
263+
264+ // Create a large file which will be uploaded
265+ let file = try self . createLargeFile ( megaBytes: 100 )
266+ defer {
267+ try ? FileManager . default. removeItem ( at: file)
268+ }
269+
270+ // Capture a list of files in temp dirs, before calling the upload function.
271+ let tempFilesBeforeUpload = existingTempFiles ( )
272+
273+ // Perform upload HTTP request
274+ let builder = try HTTPRequestBuilder ( url: URL ( string: " https://wordpress.org/upload " ) !)
275+ . method ( . post)
276+ . body ( form: [ MultipartFormField ( fileAtPath: file. path, name: " file " , filename: " file.txt " , mimeType: " text/plain " ) ] )
277+ let _ = await session. perform ( request: builder, errorType: TestError . self)
278+
279+ // Capture a list of files in the temp dirs, after calling the upload function.
280+ let tempFilesAfterUpload = existingTempFiles ( )
281+
282+ // There should be no new files after the HTTP request returns. This assertion relies on an implementation detail
283+ // where the multipart form content is put into a file in temp dirs.
284+ let newFiles = tempFilesAfterUpload. subtracting ( tempFilesBeforeUpload)
285+ XCTAssertEqual ( newFiles. count, 0 )
286+ }
287+
288+ func testTempFileRemovedAfterMultipartUploadError( ) async throws {
289+ stub ( condition: isPath ( " /upload " ) ) { _ in
290+ HTTPStubsResponse ( error: URLError ( . networkConnectionLost) )
291+ }
292+
293+ // Create a large file which will be uploaded
294+ let file = try self . createLargeFile ( megaBytes: 100 )
295+ defer {
296+ try ? FileManager . default. removeItem ( at: file)
297+ }
298+
299+ // Capture a list of files in temp dirs, before calling the upload function.
300+ let tempFilesBeforeUpload = existingTempFiles ( )
301+
302+ // Perform upload HTTP request
303+ let builder = try HTTPRequestBuilder ( url: URL ( string: " https://wordpress.org/upload " ) !)
304+ . method ( . post)
305+ . body ( form: [ MultipartFormField ( fileAtPath: file. path, name: " file " , filename: " file.txt " , mimeType: " text/plain " ) ] )
306+ let _ = await session. perform ( request: builder, errorType: TestError . self)
307+
308+ // Capture a list of files in the temp dirs, after calling the upload function.
309+ let tempFilesAfterUpload = existingTempFiles ( )
310+
311+ // There should be no new files after the HTTP request returns. This assertion relies on an implementation detail
312+ // where the multipart form content is put into a file in temp dirs.
313+ let newFiles = tempFilesAfterUpload. subtracting ( tempFilesBeforeUpload)
314+ XCTAssertEqual ( newFiles. count, 0 )
315+ }
316+
317+ private func existingTempFiles( ) -> Set < String > {
318+ let fm = FileManager . default
319+ let enumerators = [
320+ fm. enumerator ( atPath: NSTemporaryDirectory ( ) ) ,
321+ fm. enumerator ( atPath: fm. temporaryDirectory. path)
322+ ] . compactMap { $0 }
323+
324+ var result : Set < String > = [ ]
325+ for enumerator in enumerators {
326+ while let file = enumerator. nextObject ( ) as? String {
327+ result. insert ( file)
328+ }
329+ }
330+ return result
331+ }
332+
259333 private func createLargeFile( megaBytes: Int ) throws -> URL {
260334 let fileManager = FileManager . default
261335 let file = try fileManager. url ( for: . documentDirectory, in: . userDomainMask, appropriateFor: nil , create: true )
0 commit comments