context("periscope2 - logging functionality")


writeToConsole     <- periscope2:::writeToConsole
writeToFile        <- periscope2:::writeToFile
loglevels          <- periscope2:::loglevels
set_app_parameters <- periscope2:::set_app_parameters
test_file_name     <- file.path(tempdir(), c("1", "2", "3"))
test_file_log      <- file.path(tempdir(), "test_log.txt")

env_setup <- function() {
    test_env        <- new.env(parent = emptyenv())
    test_env$logged <- NULL

    mock_action <- function(msg, handler, ...) {
        if (length(list(...)) && "dry" %in% names(list(...)))
            return(TRUE)
        test_env$logged <- c(test_env$logged, msg)
    }

    mock_formatter <- function(record) {
        paste(record$levelname, record$logger, record$msg, sep = ":")
    }

    periscope2:::logReset()
    periscope2:::addHandler(mock_action,
                           formatter = mock_formatter)
    test_env
}

test_that("Handlers - addHandler(), getHandler() and removeHandler()", {
    periscope2:::logReset()
    periscope2:::basicConfig()

    # looking for handler in RootLogger
    expect_identical(periscope2:::getHandler("basic.stdout"),
                     periscope2:::getLogger()[["handlers"]][[1]])
    # looking for handler in object
    expect_identical(periscope2:::getLogger()$getHandler("basic.stdout"),
                     periscope2:::getLogger()[["handlers"]][[1]])

    # add handler
    periscope2:::addHandler(writeToConsole)

    expect_equal(length(with(periscope2:::getLogger(), names(handlers))), 2)
    expect_true("writeToConsole" %in% with(periscope2:::getLogger(), names(handlers)))

    # get handler
    expect_true(!is.null(periscope2:::getHandler("writeToConsole")))
    expect_true(!is.null(periscope2:::getHandler(writeToConsole)))

    # remove handler
    periscope2:::removeHandler("writeToConsole")
    expect_equal(length(with(periscope2:::getLogger(), names(handlers))), 1)
    expect_false("writeToConsole" %in% with(periscope2:::getLogger(), names(handlers)))

    expect_error(periscope2:::addHandler("handlerName"),
                 regexp = "No action for the handler provided",
                 fixed  = TRUE)
})

test_that("Handlers in Logger object - addHandler(), getHandler() and removeHandler()", {
    periscope2:::logReset()
    periscope2:::basicConfig()

    # add handler
    log <- periscope2:::getLogger("testLogger")
    log$addHandler(writeToConsole)
    expect_equal(length(with(log, names(handlers))), 1)

    # get handler
    expect_true(!is.null(log$getHandler("writeToConsole")))
    expect_true(!is.null(log$getHandler(writeToConsole)))

    # remove handler
    log$removeHandler(writeToConsole)
    expect_equal(length(with(log, names(handlers))), 0)
    expect_false("writeToConsole" %in% with(log, names(handlers)))
})

test_that("Levels - setLevel()", {
    periscope2:::logReset()
    periscope2:::basicConfig()

    expect_error(periscope2:::setLevel("INFO", NULL),
                 regexp = "NULL container provided: cannot set level for NULL container",
                 fixed  = TRUE)

    periscope2:::logReset()
    expect_equal(periscope2:::setLevel(TRUE), loglevels["NOTSET"]) # invalid level

    periscope2:::logReset()
    expect_equal(periscope2:::setLevel("INVALID"), loglevels["NOTSET"]) # invalid level
})

test_that("Levels in Logger object- setLevel() and getLevel()", {
    periscope2:::logReset()
    log <- periscope2:::getLogger("testLogger")

    # invalid level
    log$setLevel(150)
    expect_equal(log$getLevel(), loglevels["NOTSET"])

    log$setLevel(TRUE)
    expect_equal(log$getLevel(), loglevels["NOTSET"])

    log$setLevel("INVALID")
    expect_equal(log$getLevel(), loglevels["NOTSET"])
})

test_that("UpdateOptions - updateOptions()", {
    periscope2:::logReset()
    periscope2:::basicConfig()

    periscope2:::updateOptions.character("", level = "WARNING")
    expect_equal(periscope2:::getLogger()$getLevel(), loglevels["WARNING"])
})

test_that("LoggingToConsole", {
    periscope2:::logReset()
    periscope2:::basicConfig()

    periscope2:::getLogger()$setLevel("FINEST")
    periscope2:::addHandler(writeToConsole, level = "DEBUG")

    expect_equal(with(periscope2:::getLogger(), names(handlers)), c("basic.stdout", "writeToConsole"))
    expect_true(grepl(pattern = "DEBUG::log generated for testing",
                      x       = capture_output(logdebug("log generated for testing"), print = TRUE)))
    expect_true(grepl(pattern = "INFO::log generated for testing",
                      x       = capture_output(loginfo("log generated for testing"), print = TRUE)))

    succeed()
})

test_that("LoggingToFile", {
    periscope2:::logReset()
    unlink(test_file_name, force = TRUE)

    periscope2:::getLogger()$setLevel("FINEST")
    periscope2:::addHandler(writeToFile, file = test_file_name[[1]], level = "DEBUG")

    expect_equal(with(periscope2:::getLogger(), names(handlers)), c("writeToFile"))
    logerror("log generated for testing")
    logwarn("log generated for testing")
    loginfo("log generated for testing")
    logdebug("log generated for testing")
    periscope2:::logfinest("log generated for testing")
    periscope2:::logfiner("log generated for testing")
    periscope2:::logfine("log generated for testing")
    periscope2:::levellog("log generated for testing")

    succeed()
})

test_that("LoggingToFile in Logger object", {
    periscope2:::logReset()
    unlink(test_file_name, force = TRUE)

    log <- periscope2:::getLogger()
    log$setLevel("FINEST")
    log$addHandler(writeToFile, file = test_file_name[[1]], level = "DEBUG")
    expect_equal(with(log, names(handlers)), c("writeToFile"))

    log$error("log generated for testing")
    log$warn("log generated for testing")
    log$info("log generated for testing")
    log$debug("log generated for testing")
    log$finest("log generated for testing")
    log$finer("log generated for testing")
    log$fine("log generated for testing")

    succeed()
})

test_that("Msgcomposer - setMsgComposer(), resetMsgComposer()", {
    env <- env_setup()

    periscope2:::setMsgComposer(function(msg, ...) { paste(msg, "comp") })
    loginfo("test")

    expect_equal(env$logged, "INFO::test comp")
    expect_error(periscope2:::setMsgComposer(function(msgX, ...) { paste(msg, "comp") }),
                 regexp = "message composer(passed as composer_f) must be function  with signature function(msg, ...)",
                 fixed = TRUE)
    expect_error(periscope2:::setMsgComposer(container = NULL),
                 regexp = "NULL container provided: cannot set message composer for NULL container")

    # reset_composer
    env <- env_setup()
    periscope2:::resetMsgComposer()
    loginfo("test")

    expect_equal(env$logged, "INFO::test")
    expect_error(periscope2:::resetMsgComposer(NULL),
                 regexp = "NULL container provided: cannot reset message composer for NULL container")

    # set_sublogger_composer
    env <- env_setup()

    periscope2:::setMsgComposer(function(msg, ...) { paste(msg, "comp") }, container = "named")
    loginfo("test")
    loginfo("test", logger = "named")

    expect_equal(env$logged, c("INFO::test", "INFO:named:test comp"))
})

test_that("MsgComposer function - defaultMsgCompose()",{
    expect_equal(periscope2:::defaultMsgCompose(msg = "Message"), "Message")
    expect_error(periscope2:::defaultMsgCompose(msg = paste(rep(LETTERS, 316), collapse = ""), param = 1),
                 "'msg' length exceeds maximal format length 8192")
    expect_equal(periscope2:::defaultMsgCompose(msg = paste(rep(LETTERS, 316), collapse = "")),
                 paste(rep(LETTERS, 316), collapse = ""))
})

# Testing logging levels
test_that("DEBUG level shows all messages", {
    set_app_parameters(log_level = "DEBUG")
    periscope2:::addHandler(writeToConsole)
    expect_output(logdebug("debug message"), "DEBUG::debug message")
    expect_output(loginfo("info message"),   "INFO::info message")
    expect_output(logwarn("warn message"),   "WARNING::warn message")
    expect_output(logerror("error message"), "ERROR::error message")
})
I
test_that("INFO level filters DEBUG", {
    set_app_parameters(log_level = "INFO")
    periscope2:::addHandler(writeToConsole)
    expect_silent(logdebug("debug message"))
    expect_output(loginfo("info message"),   "INFO::info message")
    expect_output(logwarn("warn message"),   "WARNING::warn message")
    expect_output(logerror("error message"), "ERROR::error message")
})

test_that("WARNING level filters DEBUG and INFO", {
    set_app_parameters(log_level = "WARNING")
    periscope2:::addHandler(writeToConsole)
    expect_silent(logdebug("debug message"))
    expect_silent(loginfo("info message"))
    expect_output(logwarn("warn message"),   "WARNING::warn message")
    expect_output(logerror("error message"), "ERROR::error message")
})

test_that("ERROR level filters all but ERROR", {
    set_app_parameters(log_level = "ERROR")
    periscope2:::addHandler(writeToConsole)
    expect_silent(logdebug("debug message"))
    expect_silent(loginfo("info message"))
    expect_silent(logwarn("warn message"))
    expect_output(logerror("error message"), "ERROR::error message")
})

test_that("File logging level DEBUG", {
    unlink(test_file_log, force  = TRUE)
    set_app_parameters(log_level = "DEBUG")
    periscope2:::addHandler(writeToFile, file = test_file_log)

    expect_output(logdebug("debug message"), "debug message")
    expect_output(loginfo("info message"), "info message")
    expect_output(logwarn("warn message"), "warn message")
    expect_output(logerror("error message"), "error message")

    log_content <- readLines(test_file_log)
    expect_true(any(grepl("debug message", log_content)))
    expect_true(any(grepl("info message",  log_content)))
    expect_true(any(grepl("warn message",  log_content)))
    expect_true(any(grepl("error message", log_content)))
})

test_that("File logging level INFO", {
    unlink(test_file_log, force  = TRUE)
    set_app_parameters(log_level = "INFO")
    periscope2:::addHandler(writeToFile, file = test_file_log)

    logdebug("debug message")
    expect_output(loginfo("info message"), "info message")
    expect_output(logwarn("warn message"), "warn message")
    expect_output(logerror("error message"), "error message")

    log_content <- readLines(test_file_log)
    expect_false(any(grepl("debug message", log_content)))
    expect_true(any(grepl("info message",   log_content)))
    expect_true(any(grepl("warn message",   log_content)))
    expect_true(any(grepl("error message",  log_content)))
})

test_that("File logging level WARNING", {
    unlink(test_file_log, force  = TRUE)
    set_app_parameters(log_level = "WARNING")
    periscope2:::addHandler(writeToFile, file = test_file_log)

    logdebug("debug message")
    loginfo("info message")
    expect_output(logwarn("warn message"), "warn message")
    expect_output(logerror("error message"), "error message")

    log_content <- readLines(test_file_log)
    expect_false(any(grepl("debug message", log_content)))
    expect_false(any(grepl("info message",  log_content)))
    expect_true(any(grepl("warn message",   log_content)))
    expect_true(any(grepl("error message",  log_content)))
})

test_that("File logging level ERROR", {
    unlink(test_file_log, force  = TRUE)
    set_app_parameters(log_level = "ERROR")
    periscope2:::addHandler(writeToFile, file = test_file_log)

    logdebug("debug message")
    loginfo("info message")
    logwarn("warn message")
    expect_output(logerror("error message"), "error message")

    log_content <- readLines(test_file_log)
    expect_false(any(grepl("debug message", log_content)))
    expect_false(any(grepl("info message",  log_content)))
    expect_false(any(grepl("warn message",  log_content)))
    expect_true(any(grepl("error message",  log_content)))
})
