diff --git a/.gitignore b/.gitignore index def066fe7a..614098c887 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ /.build /.swiftpm xcuserdata/ + +# SPM-generated .xcodedproj +Datadog.xcodeproj diff --git a/Datadog.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Datadog.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/Datadog.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index 9c30fc8ec8..2c6316d17f 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -87,6 +87,11 @@ 61133C6E2423990D00786299 /* DatadogExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C472423990D00786299 /* DatadogExtensions.swift */; }; 61133C702423993200786299 /* Datadog.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61133B82242393DE00786299 /* Datadog.framework */; }; 61133C712423993200786299 /* Datadog.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 61133B82242393DE00786299 /* Datadog.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 616A62D024349BA700D1BE12 /* ObjcExceptionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 616A626924335D4900D1BE12 /* ObjcExceptionHandler.m */; }; + 616A62D62434A3C500D1BE12 /* ObjcExceptionHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 616A626A24335D4900D1BE12 /* ObjcExceptionHandler.h */; }; + 61C363802436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C3637F2436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift */; }; + 61C3638324361BE200C4D4E6 /* DatadogPrivateMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C3638224361BE200C4D4E6 /* DatadogPrivateMocks.swift */; }; + 61C3638524361E9200C4D4E6 /* Globals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C3638424361E9200C4D4E6 /* Globals.swift */; }; 9E08587A242519FF001A3583 /* NetworkPathMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E085879242519FF001A3583 /* NetworkPathMonitor.swift */; }; /* End PBXBuildFile section */ @@ -212,6 +217,12 @@ 61133C452423990D00786299 /* SwiftExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftExtensions.swift; sourceTree = ""; }; 61133C462423990D00786299 /* TestsDirectory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestsDirectory.swift; sourceTree = ""; }; 61133C472423990D00786299 /* DatadogExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatadogExtensions.swift; sourceTree = ""; }; + 616A626924335D4900D1BE12 /* ObjcExceptionHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ObjcExceptionHandler.m; sourceTree = ""; }; + 616A626A24335D4900D1BE12 /* ObjcExceptionHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjcExceptionHandler.h; sourceTree = ""; }; + 616A62DF2434BEF200D1BE12 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; + 61C3637F2436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjcExceptionHandlerTests.swift; sourceTree = ""; }; + 61C3638224361BE200C4D4E6 /* DatadogPrivateMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatadogPrivateMocks.swift; sourceTree = ""; }; + 61C3638424361E9200C4D4E6 /* Globals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Globals.swift; sourceTree = ""; }; 9E085879242519FF001A3583 /* NetworkPathMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkPathMonitor.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -247,6 +258,7 @@ children = ( 61133B9C2423979B00786299 /* Datadog */, 61133C082423983800786299 /* DatadogObjc */, + 616A626824335D4900D1BE12 /* DatadogPrivate */, 61133C122423990D00786299 /* DatadogTests */, 61133C07242397F200786299 /* TargetSupport */, 61133B83242393DE00786299 /* Products */, @@ -363,6 +375,7 @@ 61133BB72423979B00786299 /* Utils */ = { isa = PBXGroup; children = ( + 61C3638424361E9200C4D4E6 /* Globals.swift */, 61133BB82423979B00786299 /* InternalLoggers.swift */, 61133BB92423979B00786299 /* CompilationConditions.swift */, 61133BBA2423979B00786299 /* SwiftExtensions.swift */, @@ -455,6 +468,7 @@ children = ( 61133C182423990D00786299 /* Datadog */, 61133C132423990D00786299 /* DatadogObjc */, + 61C3637E2436163400C4D4E6 /* DatadogPrivate */, 61133C422423990D00786299 /* Matchers */, 61133C442423990D00786299 /* Helpers */, ); @@ -495,6 +509,7 @@ 61133C1C2423990D00786299 /* UIKitMocks.swift */, 61133C1D2423990D00786299 /* DatadogObjcMocks.swift */, 61133C1F2423990D00786299 /* DatadogMocks.swift */, + 61C3638224361BE200C4D4E6 /* DatadogPrivateMocks.swift */, 61133C202423990D00786299 /* FoundationMocks.swift */, ); path = Mocks; @@ -615,6 +630,24 @@ name = Frameworks; sourceTree = ""; }; + 616A626824335D4900D1BE12 /* DatadogPrivate */ = { + isa = PBXGroup; + children = ( + 616A62DF2434BEF200D1BE12 /* module.modulemap */, + 616A626A24335D4900D1BE12 /* ObjcExceptionHandler.h */, + 616A626924335D4900D1BE12 /* ObjcExceptionHandler.m */, + ); + path = DatadogPrivate; + sourceTree = ""; + }; + 61C3637E2436163400C4D4E6 /* DatadogPrivate */ = { + isa = PBXGroup; + children = ( + 61C3637F2436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift */, + ); + path = DatadogPrivate; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -623,6 +656,7 @@ buildActionMask = 2147483647; files = ( 61133B93242393DE00786299 /* Datadog.h in Headers */, + 616A62D62434A3C500D1BE12 /* ObjcExceptionHandler.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -795,6 +829,8 @@ 61133BCC2423979B00786299 /* MobileDevice.swift in Sources */, 9E08587A242519FF001A3583 /* NetworkPathMonitor.swift in Sources */, 61133BCA2423979B00786299 /* EncodableValue.swift in Sources */, + 61C3638524361E9200C4D4E6 /* Globals.swift in Sources */, + 616A62D024349BA700D1BE12 /* ObjcExceptionHandler.m in Sources */, 61133BE62423979B00786299 /* LogSanitizer.swift in Sources */, 61133BDF2423979B00786299 /* SwiftExtensions.swift in Sources */, 61133BEA2423979B00786299 /* LogConsoleOutput.swift in Sources */, @@ -836,9 +872,11 @@ 61133C622423990D00786299 /* InternalLoggersTests.swift in Sources */, 61133C582423990D00786299 /* FileWriterTests.swift in Sources */, 61133C672423990D00786299 /* LogConsoleOutputTests.swift in Sources */, + 61C3638324361BE200C4D4E6 /* DatadogPrivateMocks.swift in Sources */, 61133C4C2423990D00786299 /* LogsMocks.swift in Sources */, 61133C542423990D00786299 /* NetworkConnectionInfoProviderTests.swift in Sources */, 61133C4A2423990D00786299 /* DDConfigurationTests.swift in Sources */, + 61C363802436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift in Sources */, 61133C602423990D00786299 /* HTTPHeadersTests.swift in Sources */, 61133C632423990D00786299 /* DatadogConfigurationTests.swift in Sources */, 61133C572423990D00786299 /* FileReaderTests.swift in Sources */, @@ -1042,6 +1080,7 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/DatadogPrivate"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -1066,6 +1105,7 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/DatadogPrivate"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/Datadog/DatadogPrivate/ObjcExceptionHandler.h b/Datadog/DatadogPrivate/ObjcExceptionHandler.h new file mode 100644 index 0000000000..bf5a9e3e75 --- /dev/null +++ b/Datadog/DatadogPrivate/ObjcExceptionHandler.h @@ -0,0 +1,18 @@ +/* +* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. +* This product includes software developed at Datadog (https://www.datadoghq.com/). +* Copyright 2019-2020 Datadog, Inc. +*/ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ObjcExceptionHandler : NSObject + +- (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error + NS_SWIFT_NAME(rethrowToSwift(tryBlock:)); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Datadog/DatadogPrivate/ObjcExceptionHandler.m b/Datadog/DatadogPrivate/ObjcExceptionHandler.m new file mode 100644 index 0000000000..61a0c28ea7 --- /dev/null +++ b/Datadog/DatadogPrivate/ObjcExceptionHandler.m @@ -0,0 +1,23 @@ +/* +* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. +* This product includes software developed at Datadog (https://www.datadoghq.com/). +* Copyright 2019-2020 Datadog, Inc. +*/ + +#import +#import "ObjcExceptionHandler.h" + +@implementation ObjcExceptionHandler + +- (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error { + @try { + tryBlock(); + return YES; + } + @catch (NSException *exception) { + *error = [[NSError alloc] initWithDomain:exception.name code:0 userInfo:exception.userInfo]; + return NO; + } +} + +@end diff --git a/Datadog/DatadogPrivate/include/SPMHeaders.h b/Datadog/DatadogPrivate/include/SPMHeaders.h new file mode 100644 index 0000000000..bc3e3ebc33 --- /dev/null +++ b/Datadog/DatadogPrivate/include/SPMHeaders.h @@ -0,0 +1,9 @@ +/* +* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. +* This product includes software developed at Datadog (https://www.datadoghq.com/). +* Copyright 2019-2020 Datadog, Inc. +*/ + + +// List of headers for SPM to include for `_Datadog_Private` module. +#import "../ObjcExceptionHandler.h" diff --git a/Datadog/DatadogPrivate/module.modulemap b/Datadog/DatadogPrivate/module.modulemap new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/Datadog/DatadogPrivate/module.modulemap @@ -0,0 +1 @@ + diff --git a/Datadog/DatadogPrivate/module.private.modulemap b/Datadog/DatadogPrivate/module.private.modulemap new file mode 100644 index 0000000000..c3ae441654 --- /dev/null +++ b/Datadog/DatadogPrivate/module.private.modulemap @@ -0,0 +1,14 @@ +/* + Module Map Language reference: https://clang.llvm.org/docs/Modules.html#module-map-language + */ +module _Datadog_Private { + /* + Each header exported from here must be also exposed to each dependency manager separately: + - For Carthage, add it to "Datadog > Build Phases > Headers > Project". + - For SPM, import it from `include/SPMHeaders.h`. + - Cocoapods `.podspec` is already configured to read this `module.modulemap`. + */ + + header "ObjcExceptionHandler.h" + export * +} diff --git a/DatadogSDK.podspec b/DatadogSDK.podspec index 185dca817c..92a82db894 100644 --- a/DatadogSDK.podspec +++ b/DatadogSDK.podspec @@ -16,7 +16,11 @@ Pod::Spec.new do |s| s.swift_version = '5.1' s.ios.deployment_target = '11.0' - s.source = { :git => 'https://github.com/DataDog/dd-sdk-ios.git', :tag => s.version.to_s } + s.source = { :git => "https://github.com/DataDog/dd-sdk-ios.git", :tag => s.version.to_s } - s.source_files = "Sources/Datadog/**/*.swift" + s.source_files = "Sources/Datadog/**/*.swift", "Datadog/DatadogPrivate/*.m" + s.preserve_paths = "Datadog/DatadogPrivate/*.{modulemap,h}" + s.pod_target_xcconfig = { + "SWIFT_INCLUDE_PATHS" => "$(PODS_ROOT)/DatadogSDK/Datadog/DatadogPrivate/** $(PODS_TARGET_SRCROOT)/DatadogSDK/Datadog/DatadogPrivate/**" + } end diff --git a/Package.swift b/Package.swift index 6abe78fdaf..38502fb642 100644 --- a/Package.swift +++ b/Package.swift @@ -20,10 +20,14 @@ let package = Package( targets: [ .target( name: "Datadog", - dependencies: []), + dependencies: ["_Datadog_Private"]), .target( name: "DatadogObjc", dependencies: ["Datadog"]), + .target( + name: "_Datadog_Private", + path: "Datadog/DatadogPrivate" + ), .testTarget( name: "DatadogTests", dependencies: ["Datadog", "DatadogObjc"]), diff --git a/Sources/Datadog/Core/Persistence/FileWriter.swift b/Sources/Datadog/Core/Persistence/FileWriter.swift index 03e3c24cc6..e20f11c2ea 100644 --- a/Sources/Datadog/Core/Persistence/FileWriter.swift +++ b/Sources/Datadog/Core/Persistence/FileWriter.swift @@ -51,13 +51,13 @@ internal final class FileWriter { let file = try orchestrator.getWritableFile(writeSize: UInt64(data.count)) if try file.size() == 0 { - try file.append { write in - write(data) + try file.append { (write: (Data) throws -> Void) in + try write(data) } } else { try file.append { write in - write(commaSeparatorData) - write(data) + try write(commaSeparatorData) + try write(data) } } } catch { diff --git a/Sources/Datadog/Core/Persistence/Files/File.swift b/Sources/Datadog/Core/Persistence/Files/File.swift index 3fcf2cc24b..70e05b9551 100644 --- a/Sources/Datadog/Core/Persistence/Files/File.swift +++ b/Sources/Datadog/Core/Persistence/Files/File.swift @@ -5,6 +5,7 @@ */ import Foundation +import _Datadog_Private /// Provides convenient interface for reading metadata and appending data to the file. internal protocol WritableFile { @@ -15,7 +16,7 @@ internal protocol WritableFile { func size() throws -> UInt64 /// Synchronously appends given data at the end of this file. - func append(transaction: ((Data) -> Void) -> Void) throws + func append(transaction: ((Data) throws -> Void) throws -> Void) throws } /// Provides convenient interface for reading contents and metadata of the file. @@ -42,27 +43,24 @@ internal struct File: WritableFile, ReadableFile { } /// Appends given data at the end of this file. - func append(transaction: ((Data) -> Void) -> Void) throws { + func append(transaction: ((Data) throws -> Void) throws -> Void) throws { let fileHandle = try FileHandle(forWritingTo: url) defer { fileHandle.closeFile() } - fileHandle.seekToEndOfFile() + + try objcExceptionHandler.rethrowToSwift { + fileHandle.seekToEndOfFile() + } // Writes given data at the end of the file. - func appendData(_ data: Data) { - /* - Apple documentation https://developer.apple.com/documentation/foundation/filehandle/1410936-write says - that `fileHandle.write()` raises an exception if no free space is left on the file system, - or if any other writing error occurs. Those are unchecked exceptions impossible to handle in Swift. - - It was already escalated to Apple in Swift open source project discussion: - https://forums.swift.org/t/pitch-replacement-for-filehandle/5177 - - Until better replacement is provided by Apple, the SDK sticks to this API. To mitigate the risk of - crashing client applications, other precautions are implemented carefuly. - */ - fileHandle.write(data) + func appendData(_ data: Data) throws { + try objcExceptionHandler.rethrowToSwift { + fileHandle.write(data) + } + } + + try transaction { chunkOfData in + try appendData(chunkOfData) } - transaction(appendData) } func read() throws -> Data { diff --git a/Sources/Datadog/Logs/LogOutputs/LogConsoleOutput.swift b/Sources/Datadog/Logs/LogOutputs/LogConsoleOutput.swift index f5a3802022..b54fefb1ec 100644 --- a/Sources/Datadog/Logs/LogOutputs/LogConsoleOutput.swift +++ b/Sources/Datadog/Logs/LogOutputs/LogConsoleOutput.swift @@ -11,11 +11,6 @@ internal protocol ConsoleLogFormatter { func format(log: Log) -> String } -/// Function printing `String` content to console. -internal var consolePrint: (String) -> Void = { content in - print(content) -} - /// `LogOutput` which prints logs to console. internal struct LogConsoleOutput: LogOutput { /// Time formatter used for `.short` output format. diff --git a/Sources/Datadog/Utils/Globals.swift b/Sources/Datadog/Utils/Globals.swift new file mode 100644 index 0000000000..a9f1e68bbd --- /dev/null +++ b/Sources/Datadog/Utils/Globals.swift @@ -0,0 +1,15 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + */ + +import _Datadog_Private + +/// Function printing `String` content to console. +internal var consolePrint: (String) -> Void = { content in + print(content) +} + +/// Exception handler rethrowing `NSExceptions` to Swift `NSError`. +internal var objcExceptionHandler = ObjcExceptionHandler() diff --git a/Tests/DatadogTests/Datadog/Core/Persistence/FileReaderTests.swift b/Tests/DatadogTests/Datadog/Core/Persistence/FileReaderTests.swift index d5c859d60c..ab3ea0a9a3 100644 --- a/Tests/DatadogTests/Datadog/Core/Persistence/FileReaderTests.swift +++ b/Tests/DatadogTests/Datadog/Core/Persistence/FileReaderTests.swift @@ -27,7 +27,7 @@ class FileReaderTests: XCTestCase { ) _ = try temporaryDirectory .createFile(named: .mockAnyFileName()) - .append { write in write("ABCD".utf8Data) } + .append { write in try write("ABCD".utf8Data) } let batch = reader.readNextBatch() @@ -46,13 +46,13 @@ class FileReaderTests: XCTestCase { queue: queue ) let file1 = try temporaryDirectory.createFile(named: dateProvider.currentDate().toFileName) - try file1.append { write in write("1".utf8Data) } + try file1.append { write in try write("1".utf8Data) } let file2 = try temporaryDirectory.createFile(named: dateProvider.currentDate().toFileName) - try file2.append { write in write("2".utf8Data) } + try file2.append { write in try write("2".utf8Data) } let file3 = try temporaryDirectory.createFile(named: dateProvider.currentDate().toFileName) - try file3.append { write in write("3".utf8Data) } + try file3.append { write in try write("3".utf8Data) } var batch: Batch batch = try reader.readNextBatch().unwrapOrThrow() diff --git a/Tests/DatadogTests/Datadog/Core/Persistence/FileWriterTests.swift b/Tests/DatadogTests/Datadog/Core/Persistence/FileWriterTests.swift index 7db124d515..a2d03f8f67 100644 --- a/Tests/DatadogTests/Datadog/Core/Persistence/FileWriterTests.swift +++ b/Tests/DatadogTests/Datadog/Core/Persistence/FileWriterTests.swift @@ -63,7 +63,7 @@ class FileWriterTests: XCTestCase { XCTAssertEqual(try temporaryDirectory.files()[0].read(), #"{"key1":"value1"}"#.utf8Data) // same content as before } - func testGivenErrorVerbosity_whenLogCannotBeEncoded_itPrintsError() throws { + func testGivenErrorVerbosity_whenDataCannotBeEncoded_itPrintsError() throws { let expectation = self.expectation(description: "write completed") let previousUserLogger = userLogger defer { userLogger = previousUserLogger } @@ -86,6 +86,32 @@ class FileWriterTests: XCTestCase { XCTAssertEqual(output.recordedLog?.message, "🔥 Failed to write log: failed to encode `FailingEncodable`.") } + func testGivenErrorVerbosity_whenIOExceptionIsThrown_itPrintsError() throws { + let expectation = self.expectation(description: "write completed") + let previousUserLogger = userLogger + defer { userLogger = previousUserLogger } + let previousObjcExceptionHandler = objcExceptionHandler + defer { objcExceptionHandler = previousObjcExceptionHandler } + + let output = LogOutputMock() + userLogger = Logger(logOutput: output, identifier: "sdk-user") + objcExceptionHandler = ObjcExceptionHandlerMock(throwingError: ErrorMock("I/O exception")) + + let writer = FileWriter( + orchestrator: .mockWriteToSingleFile(in: temporaryDirectory), + queue: queue, + maxWriteSize: .max + ) + + writer.write(value: ["whatever"]) + + waitForWritesCompletion(on: queue, thenFulfill: expectation) + waitForExpectations(timeout: 1, handler: nil) + + XCTAssertEqual(output.recordedLog?.level, .error) + XCTAssertEqual(output.recordedLog?.message, "🔥 Failed to write log: I/O exception") + } + private func waitForWritesCompletion(on queue: DispatchQueue, thenFulfill expectation: XCTestExpectation) { queue.async { expectation.fulfill() } } diff --git a/Tests/DatadogTests/Datadog/Core/Persistence/Files/FileTests.swift b/Tests/DatadogTests/Datadog/Core/Persistence/Files/FileTests.swift index 9d6d31f049..9f50b0a258 100644 --- a/Tests/DatadogTests/Datadog/Core/Persistence/Files/FileTests.swift +++ b/Tests/DatadogTests/Datadog/Core/Persistence/Files/FileTests.swift @@ -24,7 +24,7 @@ class FileTests: XCTestCase { let file = try temporaryDirectory.createFile(named: "file") try file.append { write in - write(Data([0x41, 0x41, 0x41, 0x41, 0x41])) // 5 bytes + try write(Data([0x41, 0x41, 0x41, 0x41, 0x41])) // 5 bytes } XCTAssertEqual( @@ -33,8 +33,8 @@ class FileTests: XCTestCase { ) try file.append { write in - write(Data([0x42, 0x42, 0x42, 0x42, 0x42])) // 5 bytes - write(Data([0x41, 0x41, 0x41, 0x41, 0x41])) // 5 bytes + try write(Data([0x42, 0x42, 0x42, 0x42, 0x42])) // 5 bytes + try write(Data([0x41, 0x41, 0x41, 0x41, 0x41])) // 5 bytes } XCTAssertEqual( @@ -51,7 +51,7 @@ class FileTests: XCTestCase { func testItReadsDataFromFile() throws { let file = try temporaryDirectory.createFile(named: "file") - try file.append { write in write("Hello 👋".utf8Data) } + try file.append { write in try write("Hello 👋".utf8Data) } XCTAssertEqual(try file.read().utf8String, "Hello 👋") } @@ -68,10 +68,10 @@ class FileTests: XCTestCase { func testItReturnsFileSize() throws { let file = try temporaryDirectory.createFile(named: "file") - try file.append { write in write(.mock(ofSize: 5)) } + try file.append { write in try write(.mock(ofSize: 5)) } XCTAssertEqual(try file.size(), 5) - try file.append { write in write(.mock(ofSize: 10)) } + try file.append { write in try write(.mock(ofSize: 10)) } XCTAssertEqual(try file.size(), 15) } } diff --git a/Tests/DatadogTests/Datadog/Core/Persistence/FilesOrchestratorTests.swift b/Tests/DatadogTests/Datadog/Core/Persistence/FilesOrchestratorTests.swift index 4dc6f719fb..988621933c 100644 --- a/Tests/DatadogTests/Datadog/Core/Persistence/FilesOrchestratorTests.swift +++ b/Tests/DatadogTests/Datadog/Core/Persistence/FilesOrchestratorTests.swift @@ -73,7 +73,7 @@ class FilesOrchestratorTests: XCTestCase { let chunkedData: [Data] = .mockChunksOf(totalSize: defaultWriteConditions.maxFileSize) let file1 = try orchestrator.getWritableFile(writeSize: defaultWriteConditions.maxFileSize) - try file1.append { write in chunkedData.forEach { chunk in write(chunk) } } + try file1.append { write in try chunkedData.forEach { chunk in try write(chunk) } } let file2 = try orchestrator.getWritableFile(writeSize: 1) XCTAssertNotEqual(file1.name, file2.name) @@ -132,15 +132,15 @@ class FilesOrchestratorTests: XCTestCase { // write 1MB to first file (1MB of directory size in total) let file1 = try orchestrator.getWritableFile(writeSize: oneMB) - try file1.append { write in write(.mock(ofSize: oneMB)) } + try file1.append { write in try write(.mock(ofSize: oneMB)) } // write 1MB to second file (2MB of directory size in total) let file2 = try orchestrator.getWritableFile(writeSize: oneMB) - try file2.append { write in write(.mock(ofSize: oneMB)) } + try file2.append { write in try write(.mock(ofSize: oneMB)) } // write 1MB to third file (3MB of directory size in total) let file3 = try orchestrator.getWritableFile(writeSize: oneMB + 1) // +1 byte to exceed the limit - try file3.append { write in write(.mock(ofSize: oneMB + 1)) } + try file3.append { write in try write(.mock(ofSize: oneMB + 1)) } XCTAssertEqual(try temporaryDirectory.files().count, 3) @@ -149,7 +149,7 @@ class FilesOrchestratorTests: XCTestCase { let file4 = try orchestrator.getWritableFile(writeSize: oneMB) XCTAssertEqual(try temporaryDirectory.files().count, 3) XCTAssertNil(try? temporaryDirectory.file(named: file1.name)) - try file4.append { write in write(.mock(ofSize: oneMB + 1)) } + try file4.append { write in try write(.mock(ofSize: oneMB + 1)) } _ = try orchestrator.getWritableFile(writeSize: oneMB) XCTAssertEqual(try temporaryDirectory.files().count, 3) diff --git a/Tests/DatadogTests/Datadog/Mocks/DatadogPrivateMocks.swift b/Tests/DatadogTests/Datadog/Mocks/DatadogPrivateMocks.swift new file mode 100644 index 0000000000..6480151157 --- /dev/null +++ b/Tests/DatadogTests/Datadog/Mocks/DatadogPrivateMocks.swift @@ -0,0 +1,23 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + */ + +import _Datadog_Private + +/* + A collection of mocks for `_Datadog_Private` module. + */ + +class ObjcExceptionHandlerMock: ObjcExceptionHandler { + let error: Error + + init(throwingError: Error) { + self.error = throwingError + } + + override func rethrowToSwift(tryBlock: @escaping () -> Void) throws { + throw error + } +} diff --git a/Tests/DatadogTests/DatadogPrivate/ObjcExceptionHandlerTests.swift b/Tests/DatadogTests/DatadogPrivate/ObjcExceptionHandlerTests.swift new file mode 100644 index 0000000000..dcbb1cc88b --- /dev/null +++ b/Tests/DatadogTests/DatadogPrivate/ObjcExceptionHandlerTests.swift @@ -0,0 +1,32 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2020 Datadog, Inc. + */ + +import XCTest +import _Datadog_Private + +class ObjcExceptionHandlerTests: XCTestCase { + private let exceptionHandler = ObjcExceptionHandler() + + func testGivenNonThrowingCode_itDoesNotThrow() throws { + var counter = 0 + try exceptionHandler.rethrowToSwift { counter += 1 } + XCTAssertEqual(counter, 1) + } + + func testGivenThrowingCode_itThrowsNSErrorToSwift() { + let nsException = NSException( + name: NSExceptionName(rawValue: "name"), + reason: "reason", + userInfo: ["user-info": "some"] + ) + + XCTAssertThrowsError(try exceptionHandler.rethrowToSwift { nsException.raise() }) { error in + XCTAssertEqual((error as NSError).domain, "name") + XCTAssertEqual((error as NSError).code, 0) + XCTAssertEqual((error as NSError).userInfo as? [String: String], ["user-info": "some"]) + } + } +} diff --git a/instrumented-tests/Benchmark/Benchmark.xcodeproj/project.pbxproj b/instrumented-tests/Benchmark/Benchmark.xcodeproj/project.pbxproj index e2da86bcdf..2e39a28e60 100644 --- a/instrumented-tests/Benchmark/Benchmark.xcodeproj/project.pbxproj +++ b/instrumented-tests/Benchmark/Benchmark.xcodeproj/project.pbxproj @@ -17,6 +17,8 @@ 61999EC924071A760094866C /* LoggingBenchmarkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61999EC824071A760094866C /* LoggingBenchmarkTests.swift */; }; 61AEBF062423D2AE00A8CB51 /* Datadog.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61AEBF052423D2AE00A8CB51 /* Datadog.framework */; }; 61AEBF072423D2AE00A8CB51 /* Datadog.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 61AEBF052423D2AE00A8CB51 /* Datadog.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 61C363CB2436674B00C4D4E6 /* _Datadog_Private.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61C363C22436650C00C4D4E6 /* _Datadog_Private.framework */; }; + 61C363CC2436674B00C4D4E6 /* _Datadog_Private.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 61C363C22436650C00C4D4E6 /* _Datadog_Private.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -37,6 +39,7 @@ dstSubfolderSpec = 10; files = ( 61AEBF072423D2AE00A8CB51 /* Datadog.framework in Embed Frameworks */, + 61C363CC2436674B00C4D4E6 /* _Datadog_Private.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -57,6 +60,7 @@ 613D7F5C240938560013B7DF /* LoggingIOBenchmarkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingIOBenchmarkTests.swift; sourceTree = ""; }; 61999EC824071A760094866C /* LoggingBenchmarkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingBenchmarkTests.swift; sourceTree = ""; }; 61AEBF052423D2AE00A8CB51 /* Datadog.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Datadog.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 61C363C22436650C00C4D4E6 /* _Datadog_Private.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = _Datadog_Private.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -65,6 +69,7 @@ buildActionMask = 2147483647; files = ( 61AEBF062423D2AE00A8CB51 /* Datadog.framework in Frameworks */, + 61C363CB2436674B00C4D4E6 /* _Datadog_Private.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -141,6 +146,7 @@ 61800F732405411400420E0D /* Frameworks */ = { isa = PBXGroup; children = ( + 61C363C22436650C00C4D4E6 /* _Datadog_Private.framework */, 61AEBF052423D2AE00A8CB51 /* Datadog.framework */, ); name = Frameworks; @@ -493,6 +499,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/../../Datadog/DatadogPrivate"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Benchmark.app/Benchmark"; @@ -520,6 +527,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/../../Datadog/DatadogPrivate"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Benchmark.app/Benchmark";