diff --git a/.circleci/config.yml b/.circleci/config.yml index 533cd33a..ec341899 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,7 +11,7 @@ parameters: executors: apple-ci-arm-medium: macos: - xcode: 15.2.0 + xcode: 16.4.0 resource_class: macos.m1.medium.gen1 commands: @@ -35,13 +35,24 @@ commands: steps: - run: name: OktaSQLiteStorage unit tests" - command: set -o pipefail && xcodebuild -workspace "OktaLogger.xcworkspace" -scheme "OktaSQLiteStorageTests" -destination "platform=iOS Simulator,OS=latest,name=iPhone 15 Pro Max" test + command: set -o pipefail && xcodebuild -workspace "OktaLogger.xcworkspace" -scheme "OktaSQLiteStorageTests" -destination "platform=iOS Simulator,OS=latest,name=iPhone 16 Pro Max" test - run: name: OktaLogger unit tests" - command: set -o pipefail && xcodebuild -workspace "OktaLogger.xcworkspace" -scheme "OktaLoggerTests" -destination "platform=iOS Simulator,OS=latest,name=iPhone 15 Pro Max" test + command: set -o pipefail && xcodebuild -workspace "OktaLogger.xcworkspace" -scheme "OktaLoggerTests" -destination "platform=iOS Simulator,OS=latest,name=iPhone 16 Pro Max" test + - run: + when: always + name: Pack any xcresult, then remove. + command: | + pushd /Users/distiller/Library/Developer/Xcode/DerivedData/OktaLogger-brtczdmizdnmmgceswezmvcdqias/Logs/Test + find . -type d -name "*.xcresult" | xargs -I{} bash -c "tar zcf {}.tar.gz \"{}\" && rm -rf \"{}\"" + popd + mkdir artifacts + mv /Users/distiller/Library/Developer/Xcode/DerivedData/OktaLogger-brtczdmizdnmmgceswezmvcdqias/Logs/Test/*.tar.gz artifacts + - store_artifacts: + path: "artifacts" # - run: # name: OktaAnalytics unit tests" -# command: set -o pipefail && xcodebuild -workspace "OktaLogger.xcworkspace" -scheme "OktaAnalyticsTests" -destination "platform=iOS Simulator,OS=latest,name=iPhone 14" test +# command: set -o pipefail && xcodebuild -workspace "OktaLogger.xcworkspace" -scheme "OktaAnalyticsTests" -destination "platform=iOS Simulator,OS=latest,name=iPhone 16" test jobs: setup: executor: apple-ci-arm-medium @@ -78,12 +89,6 @@ jobs: run-on-non-main: <> workflows: - semgrep: - jobs: - - general-platform-helpers/job-semgrep-scan: - context: - - static-analysis - name: semgrep-scan build-test: jobs: - setup diff --git a/OktaLogger.podspec b/OktaLogger.podspec index aad31fda..ddd2d7d6 100644 --- a/OktaLogger.podspec +++ b/OktaLogger.podspec @@ -40,7 +40,7 @@ Pod::Spec.new do |s| instabugLogger.ios.source_files = [ 'Sources/OktaLogger/InstabugLogger/*' ] - instabugLogger.ios.dependency 'Instabug', '~> 13' + instabugLogger.ios.dependency 'Instabug', '~> 16' instabugLogger.dependency 'OktaLogger/Core' end diff --git a/Podfile b/Podfile index 44ee9bf8..13d86808 100644 --- a/Podfile +++ b/Podfile @@ -5,7 +5,7 @@ target 'OktaLogger' do pod 'Firebase/AnalyticsWithoutAdIdSupport' pod 'Firebase/Crashlytics', '~>11.3.0' pod 'CocoaLumberjack/Swift', '~>3.6.0' - pod 'Instabug', '13.3.0' + pod 'Instabug', '16.0.3' pod 'SwiftLint', '0.51' end diff --git a/Podfile.lock b/Podfile.lock index 32155f26..ccb28b1d 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -97,13 +97,13 @@ PODS: - GoogleUtilities/Privacy - GRDB.swift/SQLCipher (6.20.2): - SQLCipher (>= 3.4.2) - - Instabug (13.3.0) + - Instabug (16.0.3) - nanopb (3.30910.0): - nanopb/decode (= 3.30910.0) - nanopb/encode (= 3.30910.0) - nanopb/decode (3.30910.0) - nanopb/encode (3.30910.0) - - OktaAnalytics (2.1): + - OktaAnalytics (2.2): - AppCenter (~> 5) - Firebase/AnalyticsWithoutAdIdSupport - OktaLogger/Core (~> 1) @@ -122,7 +122,7 @@ PODS: - Firebase/Crashlytics (~> 11) - OktaLogger/Core - OktaLogger/InstabugLogger (1.3.20): - - Instabug (~> 13) + - Instabug (~> 16) - OktaLogger/Core - OktaSQLiteStorage (0.0.5): - GRDB.swift/SQLCipher (= 6.20.2) @@ -143,7 +143,7 @@ DEPENDENCIES: - Firebase/AnalyticsWithoutAdIdSupport - Firebase/Crashlytics (~> 11.3.0) - GRDB.swift/SQLCipher (= 6.20.2) - - Instabug (= 13.3.0) + - Instabug (= 16.0.3) - OktaAnalytics (from `.`) - OktaLogger (from `.`) - OktaLogger/Core (from `.`) @@ -199,16 +199,16 @@ SPEC CHECKSUMS: GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d GRDB.swift: 3eb7447131d897afb420d6877a45fd8bfca313c1 - Instabug: 4f26295103a330ec0236918359eef7ccaa74e2fa + Instabug: b6290ceceb5d98966aa6f10fbd7970026a916f65 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 - OktaAnalytics: 4a9b265fc7f7634aa564505446cb3dad8fa72b8c - OktaLogger: 25cb42a2ff6defbb234f1dd7c1f066011678c762 + OktaAnalytics: ef69be5de7b323a9dc21d76b27a7d5899104a114 + OktaLogger: 6dbe971237ce7a4ef1d82694acdd3af08d410ca3 OktaSQLiteStorage: 4761bb59ed2d6fc2ba6184a4fdd79f2fbeadc50c PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 SQLCipher: f2e96b3822e3006b379181a0e4fd145f6de29b56 SwiftLint: 1b7561918a19e23bfed960e40759086e70f4dba5 -PODFILE CHECKSUM: 5667fd7a1d4990d8e23a648ebfa747f6f3d8acbe +PODFILE CHECKSUM: ecdf063974058b530adeb0f4a28f92453ba0b076 COCOAPODS: 1.15.2 diff --git a/Sources/OktaLogger/FileLoggers/OktaLoggerFileLogger.swift b/Sources/OktaLogger/FileLoggers/OktaLoggerFileLogger.swift index 7e1c0b9c..b94fc86c 100644 --- a/Sources/OktaLogger/FileLoggers/OktaLoggerFileLogger.swift +++ b/Sources/OktaLogger/FileLoggers/OktaLoggerFileLogger.swift @@ -16,7 +16,7 @@ import LoggerCore #endif @objc -public class OktaLoggerFileLogger: OktaLoggerDestinationBase { +public class OktaLoggerFileLogger: OktaLoggerDestinationBase, FileLoggerDelegate { var delegate: FileLoggerDelegate @@ -43,10 +43,16 @@ public class OktaLoggerFileLogger: OktaLoggerDestinationBase { Log file path */ @objc + @available(*, deprecated, message: "Use directoryPath() instead.") public func logDirectoryAbsolutePath() -> String? { return delegate.directoryPath() } + @objc + func directoryPath() -> String? { + return delegate.directoryPath() + } + // MARK: Retrieve Logs /** Retrieve logs in the current thread. This method could be time consuming, @@ -89,7 +95,12 @@ public class OktaLoggerFileLogger: OktaLoggerDestinationBase { /** Translate log message into DDLog message */ + @available(*, deprecated, message: "Use log(_ level: OktaLoggerLogLevel, _ message: String) instead.") func log(level: OktaLoggerLogLevel, message: String) { delegate.log(level, message) } + + func log(_ level: OktaLoggerLogLevel, _ message: String) { + delegate.log(level, message) + } } diff --git a/Sources/OktaLogger/InstabugLogger/OktaLoggerInstabugLogger.swift b/Sources/OktaLogger/InstabugLogger/OktaLoggerInstabugLogger.swift index 56c28618..711d5971 100644 --- a/Sources/OktaLogger/InstabugLogger/OktaLoggerInstabugLogger.swift +++ b/Sources/OktaLogger/InstabugLogger/OktaLoggerInstabugLogger.swift @@ -10,7 +10,7 @@ * See the License for the specific language governing permissions and limitations under the License. */ -import Instabug +import InstabugSDK #if SWIFT_PACKAGE import LoggerCore #endif diff --git a/Tests/OktaLoggerTests/OktaLoggerFileLoggerTests.swift b/Tests/OktaLoggerTests/OktaLoggerFileLoggerTests.swift index 203639be..19000bc3 100644 --- a/Tests/OktaLoggerTests/OktaLoggerFileLoggerTests.swift +++ b/Tests/OktaLoggerTests/OktaLoggerFileLoggerTests.swift @@ -38,30 +38,32 @@ class OktaLoggerFileLoggerTests: XCTestCase { } // Verify that log files contains exactly 5 lines and purge logs - var receiveLogsExpectation = XCTestExpectation(description: "Should receive logs data") - var logs: [Data] = [] - testObject.getLogs { result in - logs = result - receiveLogsExpectation.fulfill() - } - wait(for: [receiveLogsExpectation], timeout: 10.0) - XCTAssertEqual(FileTestsHelper.countLines(logs[0]), 5) - testObject.purgeLogs() + var expectedMessages = ["1:log message", "2:log message", "3:log message", "4:log message", "5:log message"] + var expectation = XCTestExpectation(description: "Should receive logs: \(expectedMessages.joined(separator: ", "))") + pollForLogCompletion(delegate: testObject, expectedMessages: expectedMessages, expectation: expectation) + wait(for: [expectation], timeout: Double(expectedMessages.count) * 3.0) // Call log method 2 times + var logPaths = testObject.getLogPaths() + testObject.purgeLogs() + expectation = XCTestExpectation(description: "Logs should purge before second write") + pollForPurgeCompletion(urls: logPaths, expectation: expectation) + wait(for: [expectation], timeout: 10) + logger.debug(eventName: "AFTER_PURGE", message: "Debug log") logger.info(eventName: "AFTER_PURGE", message: "Debug log") // Verify that log files contains exactly 2 lines - receiveLogsExpectation = XCTestExpectation(description: "Should receive logs data") - logs = [] - testObject.getLogs { result in - logs = result - receiveLogsExpectation.fulfill() - } - wait(for: [receiveLogsExpectation], timeout: 10.0) - XCTAssertEqual(FileTestsHelper.countLines(logs[0]), 2) + expectedMessages = ["Debug log", "Debug log"] + expectation = XCTestExpectation(description: "Should receive logs: \(expectedMessages.joined(separator: ", "))") + pollForLogCompletion(delegate: testObject, expectedMessages: expectedMessages, expectation: expectation) + wait(for: [expectation], timeout: Double(expectedMessages.count) * 3.0) + + logPaths = testObject.getLogPaths() testObject.purgeLogs() + expectation = XCTestExpectation(description: "Logs should purge on completion") + pollForPurgeCompletion(urls: logPaths, expectation: expectation) + wait(for: [expectation], timeout: 10) } func testLumberjackFileLogger() { @@ -77,48 +79,41 @@ class OktaLoggerFileLoggerTests: XCTestCase { for i in 1...5 { testObject.log(.debug, "log \(i)") } - sleep(2) - // Verify that log files contains exactly 5 lines and purge logs - var receiveLogsExpectation = XCTestExpectation(description: "Should receive logs data") - var logs: [Data] = [] - testObject.getLogs { result in - logs = result - receiveLogsExpectation.fulfill() - } - wait(for: [receiveLogsExpectation], timeout: 10.0) - guard let testLog = logs.first else { - XCTFail("No logs") - return - } - XCTAssertEqual(FileTestsHelper.countLines(testLog), 5) + + // Verify that log files contains exactly 5 lines + var expectedMessages = ["log 1", "log 2", "log 3", "log 4", "log 5"] + var expectation = XCTestExpectation(description: "Should receive logs: \(expectedMessages.joined(separator: ", "))") + pollForLogCompletion(delegate: testObject, expectedMessages: expectedMessages, expectation: expectation) + wait(for: [expectation], timeout: Double(expectedMessages.count) * 3.0) // Verify that actual log files paths same as expected - var extectedPaths = FileTestsHelper.getPaths(testObject: testObject) + var expectedPaths = FileTestsHelper.getPaths(testObject: testObject) var actualPaths = testObject.getLogPaths() - XCTAssertEqual(actualPaths, extectedPaths) + XCTAssertEqual(actualPaths, expectedPaths) + testObject.purgeLogs() + expectation = XCTestExpectation(description: "Logs should purge before second write") + pollForPurgeCompletion(urls: expectedPaths, expectation: expectation) + wait(for: [expectation], timeout: 10) testObject.log(.debug, "After purge") testObject.log(.info, "After purge") - sleep(2) - receiveLogsExpectation = XCTestExpectation(description: "Should receive logs data") - testObject.getLogs { result in - logs = result - receiveLogsExpectation.fulfill() - } - wait(for: [receiveLogsExpectation], timeout: 10.0) - guard let logAfterPurge = logs.first else { - XCTFail("No logs after purge") - return - } - XCTAssertEqual(FileTestsHelper.countLines(logAfterPurge), 2) + // Verify that log files contains exactly 2 lines + expectedMessages = ["After purge", "After purge"] + expectation = XCTestExpectation(description: "Should receive logs: \(expectedMessages.joined(separator: ", "))") + pollForLogCompletion(delegate: testObject, expectedMessages: expectedMessages, expectation: expectation) + wait(for: [expectation], timeout: Double(expectedMessages.count) * 3.0) // Verify that actual log files paths same as expected - extectedPaths = FileTestsHelper.getPaths(testObject: testObject) + expectedPaths = FileTestsHelper.getPaths(testObject: testObject) actualPaths = testObject.getLogPaths() - XCTAssertEqual(actualPaths, extectedPaths) + XCTAssertEqual(actualPaths, expectedPaths) + testObject.purgeLogs() + expectation = XCTestExpectation(description: "Logs should purge on completion") + pollForPurgeCompletion(urls: expectedPaths, expectation: expectation) + wait(for: [expectation], timeout: 10) } func testLumberjackCustomNameMultipleFilesLogger() { @@ -141,38 +136,77 @@ class OktaLoggerFileLoggerTests: XCTestCase { } // Verify that log files contains exactly 5 lines and purge logs - var receiveLogsExpectation = XCTestExpectation(description: "Should receive logs data") - var logs: [Data] = [] - testObject.getLogs { result in - logs = result - receiveLogsExpectation.fulfill() - } - wait(for: [receiveLogsExpectation], timeout: 20.0) - for logData in logs { - XCTAssertEqual(FileTestsHelper.countLines(logData), 2) - } + var expectedMessages = ["log 1", "log 1", "log 2", "log 2", "log 3", "log 3", "log 4", "log 4", "log 5", "log 5"] + var expectation = XCTestExpectation(description: "Should receive logs: \(expectedMessages.joined(separator: ", "))") + pollForLogCompletion(delegate: testObject, expectedMessages: expectedMessages, expectation: expectation) + wait(for: [expectation], timeout: Double(expectedMessages.count) * 3.0) - // Verify that actual log files paths same as expected - var extectedPaths = Set(FileTestsHelper.getPaths(testObject: testObject, withArchived: true)) + // Verify that actual log files paths are same as expected + var expectedPaths = Set(FileTestsHelper.getPaths(testObject: testObject, withArchived: true)) var actualPaths = Set(testObject.getLogPaths()) - XCTAssertEqual(actualPaths, extectedPaths) + XCTAssertEqual(actualPaths, expectedPaths) testObject.purgeLogs() + expectation = XCTestExpectation(description: "Logs should purge") + pollForPurgeCompletion(urls: Array(expectedPaths), expectation: expectation) + wait(for: [expectation], timeout: 10) testObject.log(.debug, "After purge") testObject.log(.info, "After purge") - receiveLogsExpectation = XCTestExpectation(description: "Should receive logs data") - testObject.getLogs { result in - logs = result - receiveLogsExpectation.fulfill() - } - wait(for: [receiveLogsExpectation], timeout: 20.0) - XCTAssertEqual(FileTestsHelper.countLines(logs[0]), 2) + + expectedMessages = ["After purge", "After purge"] + expectation = XCTestExpectation(description: "Should receive logs: \(expectedMessages.joined(separator: ", "))") + pollForLogCompletion(delegate: testObject, expectedMessages: expectedMessages, expectation: expectation) + wait(for: [expectation], timeout: Double(expectedMessages.count) * 3.0) - // Verify that actual log files paths same as expected - extectedPaths = Set(FileTestsHelper.getPaths(testObject: testObject, withArchived: true)) + // Verify that actual log files paths are same as expected + expectedPaths = Set(FileTestsHelper.getPaths(testObject: testObject, withArchived: true)) actualPaths = Set(testObject.getLogPaths()) - XCTAssertEqual(actualPaths, extectedPaths) + XCTAssertEqual(actualPaths, expectedPaths) + testObject.purgeLogs() + expectation = XCTestExpectation(description: "Logs should purge") + pollForPurgeCompletion(urls: Array(expectedPaths), expectation: expectation) + wait(for: [expectation], timeout: 10) + } + + private func pollForLogCompletion(delegate: FileLoggerDelegate, expectedMessages: [String], expectation: XCTestExpectation) { + DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 3) { + delegate.getLogs { result in + let messages: [String] = result.flatMap { log -> [String] in + guard let logString = String(data: log, encoding: .utf8) else { + return [] + } + + return logString.split(separator: "\n").map { String($0) } + } + + if messages.count >= expectedMessages.count { + for (index, message) in messages.prefix(expectedMessages.count).enumerated() { + if !message.contains(expectedMessages[index]) { + XCTFail("Expected log message containing: \(expectedMessages[index]), but received: \(message)") + } + } + + expectation.fulfill() + return + } + + self.pollForLogCompletion(delegate: delegate, expectedMessages: expectedMessages, expectation: expectation) + } + } + } + + private func pollForPurgeCompletion(urls: [URL], expectation: XCTestExpectation) { + DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 3) { + let fileManager = FileManager.default + for url in urls { + if fileManager.fileExists(atPath: url.path) { + self.pollForPurgeCompletion(urls: urls, expectation: expectation) + return + } + } + expectation.fulfill() + } } } diff --git a/Tests/SQLiteStorageTests/SQLiteStorageTests.swift b/Tests/SQLiteStorageTests/SQLiteStorageTests.swift index 53dee25e..8a6be21d 100644 --- a/Tests/SQLiteStorageTests/SQLiteStorageTests.swift +++ b/Tests/SQLiteStorageTests/SQLiteStorageTests.swift @@ -65,7 +65,7 @@ final class SQLiteStorageTests: XCTestCase { } } - wait(for: [readFromDBExpectation, writeToDBExpectation], timeout: 1) + wait(for: [readFromDBExpectation, writeToDBExpectation], timeout: 3) } func testCreation_delegatedQueries() throws { @@ -104,7 +104,7 @@ final class SQLiteStorageTests: XCTestCase { } } - wait(for: [readFromDBExpectation, writeToDBExpectation], timeout: 1) + wait(for: [readFromDBExpectation, writeToDBExpectation], timeout: 10) } func testMigration() throws { @@ -143,7 +143,7 @@ final class SQLiteStorageTests: XCTestCase { } } - wait(for: [readFromDBExpectation, writeToDBExpectation], timeout: 1) + wait(for: [readFromDBExpectation, writeToDBExpectation], timeout: 3) } func testDBDowngrade() throws { @@ -168,7 +168,7 @@ final class SQLiteStorageTests: XCTestCase { } } - wait(for: [endOfAsycTest1], timeout: 1) + wait(for: [endOfAsycTest1], timeout: 3) let endOfAsycTest2 = expectation(description: "End of async test") @@ -196,7 +196,7 @@ final class SQLiteStorageTests: XCTestCase { } } - wait(for: [endOfAsycTest2], timeout: 1) + wait(for: [endOfAsycTest2], timeout: 3) } func testCreation_rawSQL_Enrypted() throws { @@ -242,7 +242,7 @@ final class SQLiteStorageTests: XCTestCase { } } - wait(for: [readFromDBExpectation, writeToDBExpectation], timeout: 1) + wait(for: [readFromDBExpectation, writeToDBExpectation], timeout: 6) } }