clog/testing/test_logger.py
theonepath e4446c90ec
Added pytest testing module
Added files which hold unit tests for each package component
respectfully. Unit testing makes use of the pytest library.

Majority of tests are self-explanatory, with wordy function headers to
help understand the purpose of the test. Note, test modules DO NOT
provide best practices and are separate from the standards set for the
remaining code base. For this reason, mypy and flake8 are told to ignore
testing modules.

pytest-cov is used to gain an idea of how much of the code base is being
reached by the unit tests. Coverage does not have to be 100%, and 100%
coverage does not indicate "bug-free" code. If any bugs are identified,
or further unit tests are possible to increase code base coverage,
create a new Issue, or PR request with the appropriate changes.
2022-05-12 13:49:32 +01:00

159 lines
5.9 KiB
Python

# flake8: noqa
import os
import re
import pytest
from clog import Logger
from utils.common import LogLevel
logger = Logger()
class TestLoggingBasics:
@pytest.mark.parametrize(
"value, expected", [
(Logger(), Logger()),
(logger, logger),
(Logger(), logger),
(logger, Logger())
]
)
def test_new_logger_instance(self, value, expected):
assert value is expected
def test_logger_file_default(self):
assert logger.log == os.path.realpath("dump.log")
def test_creation_of_new_logger(self):
assert logger is not Logger.new()
@pytest.mark.parametrize(
"value, expected",
[(".log", '.log'),
(".dump.log", ".dump.log"),
("./non-dir/log", "dump.log"),]
)
def test_using_different_log_file(self, value, expected):
global logger
logger = logger.new(out_f=value)
os.remove(logger.log)
assert Logger().log == os.path.realpath(expected).strip('"')
class TestLoggingSTD:
global logger
logger = logger.new()
@pytest.mark.parametrize(
"value, expected, s, e", [
(*("Normal text to STDOUT",) * 2, None, None),
(*("Normal test with a newline\nto STDOUT",) * 2, None, None),
(("Text", "with", "hyphens"), "Text-with-hyphens", "-", None),
("Text here...", "Text here...\r", None, '\r')
]
)
def test_message_written_to_stdout(self, value, expected, s, e, capture_stdpipe):
if isinstance(value, tuple):
logger.printLog(*value, level=LogLevel.NORMAL, sep=s, end=e)
else:
logger.printLog(value, level=LogLevel.NORMAL, sep=s, end=e)
assert capture_stdpipe['stdout'].rstrip('\n') == expected and capture_stdpipe['stderr'] == ""
@pytest.mark.parametrize(
"value, expected, s, e", [
(*("Normal text to STDOUT",) * 2, None, None),
(*("Normal test with a newline\nto STDOUT",) * 2, None, None),
(("Text", "with", "hyphens"), "Text-with-hyphens", "-", None),
("Text here...", "Text here...\r", None, '\r')
]
)
def test_message_written_to_stderr(self, value, expected, s, e, capture_stdpipe):
if isinstance(value, tuple):
logger.printLog(*value, level=LogLevel.DEBUG, sep=s, end=e)
else:
logger.printLog(value, level=LogLevel.DEBUG, sep=s, end=e)
assert capture_stdpipe['stderr'].rstrip('\n') == expected and capture_stdpipe['stdout'] == ""
def test_message_not_on_std_pipe(self, capture_stdpipe):
with open(os.path.devnull, 'w') as devnull:
Logger.printLog("Message isn't sent to STDOUT or STDERR", file=devnull)
assert capture_stdpipe['stdout'] == "" and capture_stdpipe['stderr'] == ""
@pytest.mark.skip
def test_when_file_descriptor_redirect_is_given(self):
### :@Ethan: OK this test requires a little explaining as to what's going on
# and why it's happening. Since this project was pulled out of another and
# became standalone, the original program which made use of this system was
# designed to prevent writes with ANSI escape sequences on PIPE when a file
# descriptor redirect was given from the command-line. That logic still exists
# and here we invoke that behavour for testing by running a bit of Python script
# from the terminal.
#
#
# :@UPDATE: if anyone knows how to test this behaviour by invoking that a TTY
# is present in a subprocess call, create an Issue or a PR with the appropriate
# code to get something like that working. I've spent way too much time on trying
# to prove the system works and behaves how it should, but you can confirm this
# happens by calling the `Logger.printLog()` method from an external script
# and performing a file descriptor redirect to a file. If you don't see ASNI
# escape sequences, the system works correctly, and you can call the module
# again without redirect to ensure the terminal gets coloured output on log levels
# `LogLevel.DEBUG` or higher.
...
def test_print_log_does_not_handle_string_paths(self, capture_stdpipe):
Logger.printLog("This fails correctly", file="bad_file.txt")
assert capture_stdpipe['stderr'] != ""
class TestLogging2File:
def test_print_log_2_file_handles_string_paths(self, capture_stdpipe):
Logger.printLog2File("Hello, World!", file=logger.log, mode='w')
assert capture_stdpipe['stdout'] == "" and capture_stdpipe['stderr'] == ""
def test_print_log_2_file_writes_to_file(self):
Logger.printLog2File("Message to log", file=logger.log, mode='w')
with open(logger.log, 'r') as f:
assert re.match('^.*Message to log$', f.read())
class TestPseudoLogs:
global logger
logger = logger.new()
@pytest.mark.parametrize(
"value", [Logger().debug, Logger().warn, Logger().error]
)
def test_pseudologs_are_instance_bound(self, value):
assert hasattr(value, '__self__')
@pytest.mark.parametrize(
"value, ln, lv", [
(logger.debug, 1, "DEBUG"),
(logger.warn, 2, "WARN"),
(logger.error, 3, "ERROR"),
]
)
def test_pseudologs_write_to_file(self, value, ln, lv):
value("Pseudolog to file")
with open(logger.log, 'r') as f:
assert re.match(f'^.*{lv}\\s+Pseudolog to file$', f.readlines()[ln])
@pytest.mark.parametrize(
"value, ln", [
(logger.debug, 4),
(logger.warn, 5),
(logger.error, 6),
]
)
def test_pseudologs_write_to_file_and_console(self, value, ln, capture_stdpipe):
value("Pseudolog to file and console", strace=False).withConsole()
with open(logger.log, 'r') as f:
assert re.match(f'^.*{capture_stdpipe["stderr"]}$', f.readlines()[ln])