Skip to content

Commit

Permalink
Refactor some logging tests
Browse files Browse the repository at this point in the history
* Port some logging tests to pytest parametrized functions
* Add nonascii tests to the parametrized tests.  This exposes a few
  places where we fail to do the right thig with nonascii values
  • Loading branch information
abadger committed Nov 28, 2017
1 parent c44ce57 commit 9e6c53d
Showing 1 changed file with 132 additions and 87 deletions.
219 changes: 132 additions & 87 deletions tests/test_logger.py
@@ -1,7 +1,11 @@
# coding: utf-8

import itertools
import re import re
import sys import sys


from six import StringIO import pytest
from six import StringIO, string_types


import twiggy as _twiggy import twiggy as _twiggy
from twiggy import logger, outputs, levels, filters from twiggy import logger, outputs, levels, filters
Expand All @@ -15,103 +19,144 @@
raise RuntimeError("unittest2 is required for Python < 2.7") raise RuntimeError("unittest2 is required for Python < 2.7")




class LoggerTestBase(object): @pytest.fixture(params=(logger.Logger, logger.InternalLogger))
"""common tests for loggers""" def test_logger(request):

output = outputs.ListOutput(close_atexit=False)
def test_fields_dict(self): if issubclass(request.param, logger.InternalLogger):
d = {42: 42} yield request.param(output=output)
log = self.log.fields_dict(d) else:
assert log is not self.log log = request.param()
assert log._fields == d emitters = log._emitters
assert log._fields is not d emitters['*'] = filters.Emitter(levels.DEBUG, None, output)
# we could do the same tests on options/min_level as done yield log
# in test_clone, but that's starting to get redundant

output.close()
def test_fields(self):
log = self.log.fields(a=42)
assert log is not self.log class TestLoggerFields(object):
assert log._fields == {'a': 42} TEST_FIELDS_DATA = (

{42: 42},
def test_name(self): {b'a': 42},
log = self.log.name('bob') {u'a': 42},
assert log is not self.log {b'a': b'b'},
assert log._fields == {'name': 'bob'} {u'a': u'b'},

{b'\xe4\xb8\xad': 42},
def test_options(self): {u'中': 42},
log = self.log.options(suppress_newlines=True) {b'a': b'\xe4\xb8\xad'},
assert log is not self.log {u'a': u'中'},
)

TEST_NAMES_DATA = (
('bob', {'name': 'bob'}),
(b'\xe4\xb8\xad', {'name': b'\xe4\xb8\xad'}),
(u'中', {'name': u'中'}),
)

@pytest.mark.parametrize('fields', TEST_FIELDS_DATA)
def test_fields_dict(self, test_logger, fields):
log = test_logger.fields_dict(fields)
assert log is not test_logger
assert log._fields == fields
assert log._fields is not fields

# Note: On python2, byte strings are valid keyword argument identifiers but not on python3
@pytest.mark.parametrize('fields', (d for d in TEST_FIELDS_DATA if
isinstance(list(d.keys())[0], string_types)))
def test_fields(self, test_logger, fields):
log = test_logger.fields(**fields)
assert log is not test_logger
assert log._fields == fields
assert log._fields is not fields

@pytest.mark.parametrize('name, expected', TEST_NAMES_DATA)
def test_name(self, test_logger, name, expected):
log = test_logger.name(name)
assert log is not test_logger
assert log._fields == expected


class TestOptions(object):
# More comprehensive options tests done in test_message
def test_options(self, test_logger):
log = test_logger.options(suppress_newlines=True)
assert log is not test_logger
assert log._options['suppress_newlines'] is True assert log._options['suppress_newlines'] is True


def test_bad_options(self): def test_bad_options(self, test_logger):
with self.assertRaises(ValueError): with pytest.raises(ValueError):
self.log.options(boom=True) test_logger.options(boom=True)


def test_trace(self): def test_trace(self, test_logger):
log = self.log.trace('error') log = test_logger.trace('error')
assert log is not self.log assert log is not test_logger
assert log._options['trace'] == 'error' assert log._options['trace'] == 'error'


def test_debug(self):
self.log.debug('hi')
assert len(self.messages) == 1
m = self.messages.pop()
assert m.text == 'hi'
assert m.fields['level'] == levels.DEBUG

def test_info(self):
self.log.info('hi')
assert len(self.messages) == 1
m = self.messages.pop()
assert m.text == 'hi'
assert m.fields['level'] == levels.INFO

def test_notice(self):
self.log.notice('hi')
assert len(self.messages) == 1
m = self.messages.pop()
assert m.text == 'hi'
assert m.fields['level'] == levels.NOTICE

def test_warning(self):
self.log.warning('hi')
assert len(self.messages) == 1, self.messages
m = self.messages.pop()
assert m.text == 'hi'
assert m.fields['level'] == levels.WARNING

def test_error(self):
self.log.error('hi')
assert len(self.messages) == 1
m = self.messages.pop()
assert m.text == 'hi'
assert m.fields['level'] == levels.ERROR

def test_critical(self):
self.log.critical('hi')
assert len(self.messages) == 1
m = self.messages.pop()
assert m.text == 'hi'
assert m.fields['level'] == levels.CRITICAL


def test_logger_min_level(self): class TestLevels(object):
log = self.log.name('test_min_level') TEST_LEVELS_MSGS = (
(u'hi', u'hi'),
(b'hi', u'hi'),
(b'\xe4\xb8\xad', u'中'),
(u'中', u'中'),
)

TEST_LEVELS_LEVELS = (
('debug', levels.DEBUG),
('info', levels.INFO),
('notice', levels.NOTICE),
('warning', levels.WARNING),
('error', levels.ERROR),
('critical', levels.CRITICAL),
)

@pytest.fixture
def level_logger(self, test_logger):
if isinstance(test_logger, logger.InternalLogger):
messages = test_logger.output.messages
else:
emitter = None
for emitter in test_logger._emitters.values():
break
messages = emitter._output.messages

yield test_logger, messages

@pytest.mark.parametrize('level_params, msg_params',
itertools.product(TEST_LEVELS_LEVELS, TEST_LEVELS_MSGS))
def test_level_functions(self, level_logger, level_params, msg_params):
level, expected_level = level_params
msg, expected_msg = msg_params
test_logger, test_messages = level_logger

level_function = getattr(test_logger, level)
level_function(msg)
assert len(test_messages) == 1
m = test_messages.pop()
assert m.text == expected_msg
assert m.fields['level'] == expected_level

@pytest.mark.parametrize('msg, expected', TEST_LEVELS_MSGS)
def test_logger_min_level(self, level_logger, msg, expected):
test_logger, test_messages = level_logger

log = test_logger.name('test_min_level')
log.min_level = levels.WARNING log.min_level = levels.WARNING


log.warning('hi') log.warning(msg)
assert len(self.messages) == 1 assert len(test_messages) == 1
m = self.messages.pop() m = test_messages.pop()
assert m.text == 'hi' assert m.text == expected


log.error('hi') log.error(msg)
assert len(self.messages) == 1 assert len(test_messages) == 1
m = self.messages.pop() m = test_messages.pop()
assert m.text == 'hi' assert m.text == expected


log.info('hi') log.info(msg)
assert len(self.messages) == 0 assert len(test_messages) == 0




class InternalLoggerTest(LoggerTestBase, unittest.TestCase): class InternalLoggerTest(unittest.TestCase):


def setUp(self): def setUp(self):
self.output = outputs.ListOutput(close_atexit=False) self.output = outputs.ListOutput(close_atexit=False)
Expand Down Expand Up @@ -184,7 +229,7 @@ def cleanup(stderr, output):
assert "Traceback" in sio.getvalue() assert "Traceback" in sio.getvalue()




class LoggerTestCase(LoggerTestBase, unittest.TestCase): class LoggerTestCase(unittest.TestCase):


def setUp(self): def setUp(self):
self.log = logger.Logger() self.log = logger.Logger()
Expand Down

0 comments on commit 9e6c53d

Please sign in to comment.