Add tests for reactive log printing.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
65797558f8
commit
44c1747127
6 changed files with 218 additions and 133 deletions
|
|
@ -5,9 +5,11 @@ import pytest
|
|||
import six
|
||||
from six.moves.queue import Queue
|
||||
|
||||
from compose.cli.log_printer import build_log_generator
|
||||
from compose.cli.log_printer import build_log_presenters
|
||||
from compose.cli.log_printer import build_no_log_generator
|
||||
from compose.cli.log_printer import consume_queue
|
||||
from compose.cli.log_printer import LogPrinter
|
||||
from compose.cli.log_printer import STOP
|
||||
from compose.cli.log_printer import QueueItem
|
||||
from compose.cli.log_printer import wait_on_exit
|
||||
from compose.container import Container
|
||||
from tests import mock
|
||||
|
|
@ -34,72 +36,73 @@ def output_stream():
|
|||
|
||||
@pytest.fixture
|
||||
def mock_container():
|
||||
def reader(*args, **kwargs):
|
||||
yield b"hello\nworld"
|
||||
return build_mock_container(reader)
|
||||
return mock.Mock(spec=Container, name_without_project='web_1')
|
||||
|
||||
|
||||
@pytest.mark.skipif(True, reason="wip")
|
||||
class TestLogPrinter(object):
|
||||
class TestLogPresenter(object):
|
||||
|
||||
def test_single_container(self, output_stream, mock_container):
|
||||
LogPrinter([mock_container], output=output_stream, log_args={'follow': True}).run()
|
||||
def test_monochrome(self, mock_container):
|
||||
presenters = build_log_presenters(['foo', 'bar'], True)
|
||||
presenter = presenters.next()
|
||||
actual = presenter.present(mock_container, "this line")
|
||||
assert actual == "web_1 | this line"
|
||||
|
||||
output = output_stream.getvalue()
|
||||
assert 'hello' in output
|
||||
assert 'world' in output
|
||||
# Call count is 2 lines + "container exited line"
|
||||
assert output_stream.flush.call_count == 3
|
||||
def test_polychrome(self, mock_container):
|
||||
presenters = build_log_presenters(['foo', 'bar'], False)
|
||||
presenter = presenters.next()
|
||||
actual = presenter.present(mock_container, "this line")
|
||||
assert '\033[' in actual
|
||||
|
||||
def test_single_container_without_stream(self, output_stream, mock_container):
|
||||
LogPrinter([mock_container], output=output_stream).run()
|
||||
|
||||
output = output_stream.getvalue()
|
||||
assert 'hello' in output
|
||||
assert 'world' in output
|
||||
# Call count is 2 lines
|
||||
assert output_stream.flush.call_count == 2
|
||||
def test_wait_on_exit():
|
||||
exit_status = 3
|
||||
mock_container = mock.Mock(
|
||||
spec=Container,
|
||||
name='cname',
|
||||
wait=mock.Mock(return_value=exit_status))
|
||||
|
||||
def test_monochrome(self, output_stream, mock_container):
|
||||
LogPrinter([mock_container], output=output_stream, monochrome=True).run()
|
||||
assert '\033[' not in output_stream.getvalue()
|
||||
expected = '{} exited with code {}\n'.format(mock_container.name, exit_status)
|
||||
assert expected == wait_on_exit(mock_container)
|
||||
|
||||
def test_polychrome(self, output_stream, mock_container):
|
||||
LogPrinter([mock_container], output=output_stream).run()
|
||||
assert '\033[' in output_stream.getvalue()
|
||||
|
||||
def test_build_no_log_generator(mock_container):
|
||||
mock_container.has_api_logs = False
|
||||
mock_container.log_driver = 'none'
|
||||
output, = build_no_log_generator(mock_container, None)
|
||||
assert "WARNING: no logs are available with the 'none' log driver\n" in output
|
||||
assert "exited with code" not in output
|
||||
|
||||
|
||||
class TestBuildLogGenerator(object):
|
||||
|
||||
def test_no_log_stream(self, mock_container):
|
||||
mock_container.log_stream = None
|
||||
mock_container.logs.return_value = iter([b"hello\nworld"])
|
||||
log_args = {'follow': True}
|
||||
|
||||
generator = build_log_generator(mock_container, log_args)
|
||||
assert generator.next() == "hello\n"
|
||||
assert generator.next() == "world"
|
||||
mock_container.logs.assert_called_once_with(
|
||||
stdout=True,
|
||||
stderr=True,
|
||||
stream=True,
|
||||
**log_args)
|
||||
|
||||
def test_with_log_stream(self, mock_container):
|
||||
mock_container.log_stream = iter([b"hello\nworld"])
|
||||
log_args = {'follow': True}
|
||||
|
||||
generator = build_log_generator(mock_container, log_args)
|
||||
assert generator.next() == "hello\n"
|
||||
assert generator.next() == "world"
|
||||
|
||||
def test_unicode(self, output_stream):
|
||||
glyph = u'\u2022'
|
||||
glyph = u'\u2022\n'
|
||||
mock_container.log_stream = iter([glyph.encode('utf-8')])
|
||||
|
||||
def reader(*args, **kwargs):
|
||||
yield glyph.encode('utf-8') + b'\n'
|
||||
|
||||
container = build_mock_container(reader)
|
||||
LogPrinter([container], output=output_stream).run()
|
||||
output = output_stream.getvalue()
|
||||
if six.PY2:
|
||||
output = output.decode('utf-8')
|
||||
|
||||
assert glyph in output
|
||||
|
||||
def test_wait_on_exit(self):
|
||||
exit_status = 3
|
||||
mock_container = mock.Mock(
|
||||
spec=Container,
|
||||
name='cname',
|
||||
wait=mock.Mock(return_value=exit_status))
|
||||
|
||||
expected = '{} exited with code {}\n'.format(mock_container.name, exit_status)
|
||||
assert expected == wait_on_exit(mock_container)
|
||||
|
||||
def test_generator_with_no_logs(self, mock_container, output_stream):
|
||||
mock_container.has_api_logs = False
|
||||
mock_container.log_driver = 'none'
|
||||
LogPrinter([mock_container], output=output_stream).run()
|
||||
|
||||
output = output_stream.getvalue()
|
||||
assert "WARNING: no logs are available with the 'none' log driver\n" in output
|
||||
assert "exited with code" not in output
|
||||
generator = build_log_generator(mock_container, {})
|
||||
assert generator.next() == glyph
|
||||
|
||||
|
||||
class TestConsumeQueue(object):
|
||||
|
|
@ -111,7 +114,7 @@ class TestConsumeQueue(object):
|
|||
|
||||
queue = Queue()
|
||||
error = Problem('oops')
|
||||
for item in ('a', None), ('b', None), (None, error):
|
||||
for item in QueueItem.new('a'), QueueItem.new('b'), QueueItem.exception(error):
|
||||
queue.put(item)
|
||||
|
||||
generator = consume_queue(queue, False)
|
||||
|
|
@ -122,7 +125,7 @@ class TestConsumeQueue(object):
|
|||
|
||||
def test_item_is_stop_without_cascade_stop(self):
|
||||
queue = Queue()
|
||||
for item in (STOP, None), ('a', None), ('b', None):
|
||||
for item in QueueItem.stop(), QueueItem.new('a'), QueueItem.new('b'):
|
||||
queue.put(item)
|
||||
|
||||
generator = consume_queue(queue, False)
|
||||
|
|
@ -131,7 +134,12 @@ class TestConsumeQueue(object):
|
|||
|
||||
def test_item_is_stop_with_cascade_stop(self):
|
||||
queue = Queue()
|
||||
for item in (STOP, None), ('a', None), ('b', None):
|
||||
for item in QueueItem.stop(), QueueItem.new('a'), QueueItem.new('b'):
|
||||
queue.put(item)
|
||||
|
||||
assert list(consume_queue(queue, True)) == []
|
||||
|
||||
def test_item_is_none_when_timeout_is_hit(self):
|
||||
queue = Queue()
|
||||
generator = consume_queue(queue, False)
|
||||
assert generator.next() is None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue