TwitterLoggingServiceTests/TLSLoggingSwiftTests.swift (270 lines of code) (raw):
//
// TLSLoggingSwiftTests.swift
// TwitterLoggingService
//
// Created on 2/4/16.
// Copyright (c) 2016 Twitter, Inc.
//
import TwitterLoggingService
import XCTest
extension Notification.Name {
fileprivate static let LoggingSwiftTestOutputStreamNotification = Notification.Name(rawValue: "TLSLoggingSwiftTestOutputStreamNotification")
fileprivate static let LoggingSwiftTestDiscardedMessageNotification = Notification.Name(rawValue: "TLSLoggingSwiftTestDiscardedMessageNotification")
}
class TLSLoggingSwiftTestOutputStream : NSObject, TLSOutputStream
{
var shouldPrint: Bool = true
func tls_outputLogInfo(_ logInfo: TLSLogMessageInfo)
{
DispatchQueue.main.async(execute: {
if (self.shouldPrint) {
print(logInfo.composeFormattedMessage())
}
NotificationCenter.default.post(name: .LoggingSwiftTestOutputStreamNotification, object: logInfo)
})
}
}
class TLSLoggingSwiftTestMessageInfo : TLSLogMessageInfo
{
var didGetLevel: Bool = false
override var level: TLSLogLevel {
didGetLevel = true
return super.level
}
var didGetFile: Bool = false
override var file: String {
didGetFile = true
return super.file
}
var didGetFunction: Bool = false
override var function: String {
didGetFunction = true
return super.function
}
var didGetLine: Bool = false
override var line: Int {
didGetLine = true
return super.line
}
var didGetChannel: Bool = false
override var channel: String {
didGetChannel = true
return super.channel
}
var didGetContextObject: Bool = false
override var contextObject: Any? {
didGetContextObject = true
return super.contextObject
}
var didGetTimestamp: Bool = false
override var timestamp: Date {
didGetTimestamp = true
return super.timestamp
}
var didGetLogLifespan: Bool = false
override var logLifespan: TimeInterval {
didGetLogLifespan = true
return super.logLifespan
}
var didGetThreadId: Bool = false
override var threadId: UInt32 {
didGetThreadId = true
return super.threadId
}
var didGetMessage: Bool = false
override var message: String {
didGetMessage = true
return super.message
}
var didComposeFormattedMessage: Bool = false
var lastSeenComposeFormattedMessage: TLSComposeLogMessageInfoOptions = []
override func composeFormattedMessage(options: TLSComposeLogMessageInfoOptions = []) -> String {
didComposeFormattedMessage = true
lastSeenComposeFormattedMessage = options
return super.composeFormattedMessage(options: options)
}
var didComposeFileFunctionLineString: Bool = false
override func composeFileFunctionLineString() -> String
{
didComposeFileFunctionLineString = true
return super.composeFileFunctionLineString()
}
internal func reset()
{
didGetLevel = false
didGetChannel = false
didGetMessage = false
didGetThreadId = false
didGetTimestamp = false
didGetLogLifespan = false
didGetContextObject = false
didGetFile = false
didGetFunction = false
didGetLine = false
didComposeFormattedMessage = false
lastSeenComposeFormattedMessage = []
didComposeFileFunctionLineString = false
}
}
class TLSLoggingSwiftTestCrashlyticsOutputStream : TLSCrashlyticsOutputStream
{
var didOutputLogMessageToCrashlytics: Bool = false
override func outputLogMessage(toCrashlytics message: String)
{
didOutputLogMessageToCrashlytics = true
}
fileprivate var _discardLargeLogMessagesOverride:Bool = false
var discardLargeLogMessagesOverride: Bool
{
get {
return _discardLargeLogMessagesOverride
}
set {
_discardLargeLogMessagesOverride = newValue
}
}
override func discardLargeLogMessages() -> Bool
{
return self.discardLargeLogMessagesOverride
}
func reset()
{
_discardLargeLogMessagesOverride = false
didOutputLogMessageToCrashlytics = false
}
}
class TLSLoggingSwiftTestServiceDelegate : NSObject, TLSLoggingServiceDelegate
{
func tls_loggingService(_ service: TLSLoggingService, lengthToLogForMessageExceedingMaxSafeLength maxSafeLength: UInt, level: TLSLogLevel, channel: String, file: String, function: String, line: Int, contextObject: Any?, message: String) -> UInt {
DispatchQueue.main.async(execute: {
NotificationCenter.default.post(name: .LoggingSwiftTestDiscardedMessageNotification, object: nil)
})
return 0
}
}
class TLSLoggingSwiftTest: XCTestCase
{
let testOutputStream = TLSLoggingSwiftTestOutputStream()
override func setUp() {
super.setUp()
TLSLoggingService.sharedInstance().addOutputStream(testOutputStream)
}
override func tearDown() {
TLSLoggingService.sharedInstance().removeOutputStream(testOutputStream)
super.tearDown()
}
func setExpectationForLoggingLevel(_ level: TLSLogLevel) {
_ = expectation(forNotification: .LoggingSwiftTestOutputStreamNotification, object: nil) { note in
let messageInfo: TLSLogMessageInfo = note.object as! TLSLogMessageInfo
return messageInfo.level == level
}
}
func dummyLogMessageInfo(_ message: String = "Some Message") -> TLSLoggingSwiftTestMessageInfo {
return TLSLoggingSwiftTestMessageInfo(level: .error, file:#file, function:#function ,line:#line, channel: "SomeChannel", timestamp: Date(), logLifespan: 0.1, threadId: 1, threadName: TLSCurrentThreadName(), contextObject: nil, message: message)
}
func testSwiftLogging() {
let context = Date()
setExpectationForLoggingLevel(.error)
TLSLog.error("TestChannel", "Message with context: \(context)")
waitForExpectations(timeout: 10)
setExpectationForLoggingLevel(.warning)
TLSLog.warning("TestChannel", "Message with context: \(context)")
waitForExpectations(timeout: 10)
setExpectationForLoggingLevel(.information)
TLSLog.information("TestChannel", "Message with context: \(context)")
waitForExpectations(timeout: 10)
setExpectationForLoggingLevel(.alert)
TLSLog.log(.alert, "TestChannel", context, "Message with context: \(context)")
waitForExpectations(timeout: 10)
#if DEBUG
setExpectationForLoggingLevel(.debug)
TLSLog.debug("TestChannel", "Message with context: \(context)") // documentation explicitly states this only logs to debug builds
waitForExpectations(timeout: 10)
#endif
var delayedDate = Date()
func delayedEvaluation() -> String {
delayedDate = Date()
return delayedDate.description
}
Thread.sleep(forTimeInterval: 0.000001)
let dateBeforeLog = Date()
XCTAssert(delayedDate < dateBeforeLog)
TLSLog.information("TestChannel", delayedEvaluation())
XCTAssert(dateBeforeLog < delayedDate)
}
func testConsoleOutputStreams()
{
let messageInfo = self.dummyLogMessageInfo()
let NSLogOutputStream = TLSNSLogOutputStream()
NSLogOutputStream.tls_outputLogInfo(messageInfo)
XCTAssertTrue(messageInfo.didGetFile)
XCTAssertTrue(messageInfo.didGetFunction)
XCTAssertTrue(messageInfo.didGetLine)
XCTAssertTrue(messageInfo.didGetLevel)
XCTAssertTrue(messageInfo.didGetChannel)
XCTAssertTrue(messageInfo.didGetMessage)
XCTAssertTrue(messageInfo.didComposeFileFunctionLineString)
XCTAssertTrue(messageInfo.didComposeFormattedMessage)
messageInfo.reset()
let stdOutOutputStream = TLSStdErrOutputStream()
stdOutOutputStream.tls_outputLogInfo(messageInfo)
XCTAssertFalse(messageInfo.didGetFile) // caching prevents access
XCTAssertFalse(messageInfo.didGetFunction) // caching prevents access
XCTAssertFalse(messageInfo.didGetLine) // caching prevents access
XCTAssertTrue(messageInfo.didGetLevel)
XCTAssertTrue(messageInfo.didGetChannel)
XCTAssertTrue(messageInfo.didGetMessage)
XCTAssertTrue(messageInfo.didComposeFileFunctionLineString) // cached value, access will not reconstruct string
XCTAssertTrue(messageInfo.didComposeFormattedMessage) // does use "composeFormattedMessage"
messageInfo.reset()
}
func testCrashlyticsOutputStream()
{
var longMessage = "This is a long message that will exceed 16KB so that we can test that it will be discarded."
while (longMessage.count < 16 * 1024) {
longMessage += longMessage
}
let crashlyticsOutputStream = TLSLoggingSwiftTestCrashlyticsOutputStream()
let messageInfo = self.dummyLogMessageInfo()
let longMessageInfo = self.dummyLogMessageInfo(longMessage)
crashlyticsOutputStream.tls_outputLogInfo(messageInfo)
XCTAssertTrue(messageInfo.didGetMessage)
XCTAssertTrue(crashlyticsOutputStream.didOutputLogMessageToCrashlytics)
crashlyticsOutputStream.reset()
messageInfo.reset()
crashlyticsOutputStream.discardLargeLogMessagesOverride = true
crashlyticsOutputStream.tls_outputLogInfo(messageInfo)
XCTAssertFalse(messageInfo.didGetMessage) // cached
XCTAssertTrue(crashlyticsOutputStream.didOutputLogMessageToCrashlytics)
crashlyticsOutputStream.reset()
messageInfo.reset()
crashlyticsOutputStream.tls_outputLogInfo(longMessageInfo)
XCTAssertTrue(longMessageInfo.didGetMessage)
XCTAssertTrue(crashlyticsOutputStream.didOutputLogMessageToCrashlytics)
crashlyticsOutputStream.reset()
longMessageInfo.reset()
crashlyticsOutputStream.discardLargeLogMessagesOverride = true
crashlyticsOutputStream.tls_outputLogInfo(longMessageInfo)
XCTAssertFalse(longMessageInfo.didGetMessage) // cached
XCTAssertFalse(crashlyticsOutputStream.didOutputLogMessageToCrashlytics)
crashlyticsOutputStream.reset()
longMessageInfo.reset()
}
func testExtraLargeLogMessage()
{
var longMessage = "This is a long message that will exceed 16KB so that we can test that it will be discarded."
while (longMessage.count < 16 * 1024) {
longMessage += longMessage
}
var expectation: XCTestExpectation
let service = TLSLoggingService()
let delegate = TLSLoggingSwiftTestServiceDelegate()
let outputStream = TLSLoggingSwiftTestOutputStream()
service.delegate = delegate
outputStream.shouldPrint = false
service.addOutputStream(outputStream)
// will log
service.maximumSafeMessageLength = 0
expectation = self.expectation(forNotification: NSNotification.Name(rawValue: Notification.Name.LoggingSwiftTestOutputStreamNotification.rawValue), object: nil, handler: nil)
TLSLogString(service, TLSLogLevel.error, "AnyChannel", #file, #function, #line, nil, TLSLogMessageOptions(), longMessage)
self.waitForExpectations(timeout: 10, handler: nil)
// won't log
service.maximumSafeMessageLength = 16 * 1024
expectation = self.expectation(forNotification: NSNotification.Name(rawValue: Notification.Name.LoggingSwiftTestDiscardedMessageNotification.rawValue), object: nil, handler: nil)
TLSLogString(service, TLSLogLevel.error, "AnyChannel", #file, #function, #line, nil, TLSLogMessageOptions(), longMessage)
self.waitForExpectations(timeout: 10, handler: nil)
// just to avoid the compiler warning :(
// can't do `(void)expectation;` like in C/ObjC
if expectation.isKind(of: XCTestExpectation.self) {
}
}
}