| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- // Software License Agreement (BSD License)
- //
- // Copyright (c) 2010-2025, Deusty, LLC
- // All rights reserved.
- //
- // Redistribution and use of this software in source and binary forms,
- // with or without modification, are permitted provided that the following conditions are met:
- //
- // * Redistributions of source code must retain the above copyright notice,
- // this list of conditions and the following disclaimer.
- //
- // * Neither the name of Deusty nor the names of its contributors may be used
- // to endorse or promote products derived from this software without specific
- // prior written permission of Deusty, LLC.
- import XCTest
- import CocoaLumberjack
- @testable import Logging
- @testable import CocoaLumberjackSwiftLogBackend
- fileprivate final class MockDDLog: DDLog, @unchecked Sendable {
- struct LoggedMessage: Sendable, Equatable {
- let async: Bool
- let message: DDLogMessage
- }
- private(set) var loggedMessages = Array<LoggedMessage>()
- override func log(asynchronous: Bool, message logMessage: DDLogMessage) {
- super.log(asynchronous: asynchronous, message: logMessage)
- loggedMessages.append(LoggedMessage(async: asynchronous, message: logMessage))
- }
- }
- final class DDLogHandlerTests: XCTestCase {
- private var mockDDLog: MockDDLog!
- private var logSource: String { "CocoaLumberjackSwiftLogBackendTests" }
- override func setUp() {
- super.setUp()
- mockDDLog = MockDDLog()
- }
- override func tearDown() {
- mockDDLog = nil
- super.tearDown()
- }
- func testBootstrappingWithConvenienceMethod() throws {
- // It is important that this is the only test using the convenience method,
- // since another use of it will fail the precondition (multiple bootstrap calls)
- // All other tests must use `LoggingSystem.bootstrapInternal`.
- LoggingSystem.bootstrapWithCocoaLumberjack(for: mockDDLog)
- let logger = Logging.Logger(label: "TestLogger")
- let msg: Logging.Logger.Message = "test message"
- let logLine: UInt = #line + 1
- logger.info(msg)
- XCTAssertEqual(mockDDLog.loggedMessages.count, 1)
- let loggedMsg = try XCTUnwrap(mockDDLog.loggedMessages.first)
- XCTAssertTrue(loggedMsg.async)
- XCTAssertEqual(loggedMsg.message.message, String(describing: msg))
- XCTAssertEqual(loggedMsg.message.level, .info)
- XCTAssertEqual(loggedMsg.message.flag, .info)
- XCTAssertEqual(loggedMsg.message.file, #fileID)
- XCTAssertEqual(loggedMsg.message.function, #function)
- XCTAssertEqual(loggedMsg.message.line, logLine)
- XCTAssertNotNil(loggedMsg.message.swiftLogInfo)
- XCTAssertEqual(loggedMsg.message.swiftLogInfo, .init(logger: .init(label: logger.label,
- metadataSources: .init(logger: logger.handler.metadata,
- provider: logger.metadataProvider?.get())),
- message: .init(message: msg,
- level: .info,
- metadata: nil,
- source: logSource)))
- }
- func testBootstrappingWithExplicitMethod() throws {
- LoggingSystem.bootstrapInternal(DDLogHandler.handlerFactory(for: mockDDLog))
- let logger = Logging.Logger(label: "TestLogger")
- let msg: Logging.Logger.Message = "test message"
- let logLine: UInt = #line + 1
- logger.info(msg)
- XCTAssertEqual(mockDDLog.loggedMessages.count, 1)
- let loggedMsg = try XCTUnwrap(mockDDLog.loggedMessages.first)
- XCTAssertTrue(loggedMsg.async)
- XCTAssertEqual(loggedMsg.message.message, String(describing: msg))
- XCTAssertEqual(loggedMsg.message.level, .info)
- XCTAssertEqual(loggedMsg.message.flag, .info)
- XCTAssertEqual(loggedMsg.message.file, #fileID)
- XCTAssertEqual(loggedMsg.message.function, #function)
- XCTAssertEqual(loggedMsg.message.line, logLine)
- XCTAssertNotNil(loggedMsg.message.swiftLogInfo)
- XCTAssertEqual(loggedMsg.message.swiftLogInfo, .init(logger: .init(label: logger.label,
- metadataSources: .init(logger: logger.handler.metadata,
- provider: logger.metadataProvider?.get())),
- message: .init(message: msg,
- level: .info,
- metadata: nil,
- source: logSource)))
- }
- func testDefaults() throws {
- LoggingSystem.bootstrapInternal(DDLogHandler.handlerFactory())
- let logger = Logging.Logger(label: "TestLogger")
- XCTAssertEqual(logger.logLevel, .info)
- XCTAssertTrue(logger.handler is DDLogHandler)
- let ddLogHandler = try XCTUnwrap(logger.handler as? DDLogHandler)
- XCTAssertEqual(ddLogHandler.loggerInfo.label, logger.label)
- XCTAssertTrue(ddLogHandler.loggerInfo.metadataSources.logger.isEmpty)
- XCTAssertTrue(ddLogHandler.config.log === DDLog.sharedInstance)
- XCTAssertEqual(ddLogHandler.config.syncLogging.tresholdLevel, .error)
- XCTAssertEqual(ddLogHandler.config.syncLogging.metadataKey, DDLogHandler.defaultSynchronousLoggingMetadataKey)
- }
- func testLoggingAllLevels() throws {
- let syncTresholdLevel = Logging.Logger.Level.warning
- let syncLoggingMetadataKey: Logging.Logger.Metadata.Key = "test-log-sync"
- LoggingSystem.bootstrapInternal(DDLogHandler.handlerFactory(for: mockDDLog,
- loggingSynchronousAsOf: syncTresholdLevel,
- synchronousLoggingMetadataKey: syncLoggingMetadataKey))
- var logger = Logging.Logger(label: "TestLogger")
- logger.logLevel = .trace // enable all logs
- logger[metadataKey: "test-data"] = "test-value"
- XCTAssertEqual(logger.logLevel, .trace)
- XCTAssertEqual(logger[metadataKey: "test-data"], "test-value")
- let allLevels = Logging.Logger.Level.allCases
- let message1Meta: Logging.Logger.Metadata = ["msg-data": "msg-value"]
- let message2Meta = message1Meta.merging([syncLoggingMetadataKey: .stringConvertible(true)], uniquingKeysWith: { $1 })
- let logLine1: UInt = #line + 3
- let logLine2 = logLine1 + 1
- for level in allLevels {
- logger.log(level: level, "\(level)-msg", metadata: message1Meta)
- logger.log(level: level, "\(level)-msg-with-sync", metadata: message2Meta)
- }
- XCTAssertEqual(mockDDLog.loggedMessages.count, Logging.Logger.Level.allCases.count * 2)
- guard mockDDLog.loggedMessages.count >= Logging.Logger.Level.allCases.count * 2 else { return } // prevent test crashes
- zip(allLevels, stride(from: mockDDLog.loggedMessages.startIndex, to: mockDDLog.loggedMessages.endIndex, by: 2)).forEach {
- let level = $0.0
- let loggedMsg1 = mockDDLog.loggedMessages[$0.1]
- XCTAssertEqual(loggedMsg1.async, level < syncTresholdLevel)
- XCTAssertEqual(loggedMsg1.message.message, "\(level)-msg")
- XCTAssertEqual(loggedMsg1.message.level, level.ddLogLevelAndFlag.0)
- XCTAssertEqual(loggedMsg1.message.flag, level.ddLogLevelAndFlag.1)
- XCTAssertEqual(loggedMsg1.message.file, #fileID)
- XCTAssertEqual(loggedMsg1.message.function, #function)
- XCTAssertEqual(loggedMsg1.message.line, logLine1)
- XCTAssertNotNil(loggedMsg1.message.swiftLogInfo)
- XCTAssertEqual(loggedMsg1.message.swiftLogInfo, .init(logger: .init(label: logger.label,
- metadataSources: .init(logger: logger.handler.metadata,
- provider: logger.metadataProvider?.get())),
- message: .init(message: "\(level)-msg",
- level: level,
- metadata: message1Meta,
- source: logSource)))
- let loggedMsg2 = mockDDLog.loggedMessages[$0.1 + 1]
- XCTAssertFalse(loggedMsg2.async)
- XCTAssertEqual(loggedMsg2.message.message, "\(level)-msg-with-sync")
- XCTAssertEqual(loggedMsg2.message.level, level.ddLogLevelAndFlag.0)
- XCTAssertEqual(loggedMsg2.message.flag, level.ddLogLevelAndFlag.1)
- XCTAssertEqual(loggedMsg2.message.file, #fileID)
- XCTAssertEqual(loggedMsg2.message.function, #function)
- XCTAssertEqual(loggedMsg2.message.line, logLine2)
- XCTAssertNotNil(loggedMsg2.message.swiftLogInfo)
- XCTAssertEqual(loggedMsg2.message.swiftLogInfo, .init(logger: .init(label: logger.label,
- metadataSources: .init(logger: logger.handler.metadata,
- provider: logger.metadataProvider?.get())),
- message: .init(message: "\(level)-msg-with-sync",
- level: level,
- metadata: message2Meta,
- source: logSource)))
- }
- }
- }
|