# -*- coding: UTF-8 -*- from __future__ import absolute_import import struct import sys import tempfile import unittest import six from mock import Mock, patch from nose.tools import * # pylint: disable=wildcard-import, unused-wildcard-import from behave.formatter._registry import make_formatters from behave.formatter import pretty from behave.formatter.base import StreamOpener from behave.model import Tag, Feature, Scenario, Step from behave.model_core import Status from behave.matchers import Match class TestGetTerminalSize(unittest.TestCase): def setUp(self): try: self.ioctl_patch = patch("fcntl.ioctl") self.ioctl = self.ioctl_patch.start() except ImportError: self.ioctl_patch = None self.ioctl = None self.zero_struct = struct.pack("HHHH", 0, 0, 0, 0) def tearDown(self): if self.ioctl_patch: self.ioctl_patch.stop() def test_windows_fallback(self): # pylint: disable=no-self-use platform = sys.platform sys.platform = "windows" eq_(pretty.get_terminal_size(), (80, 24)) sys.platform = platform def test_termios_fallback(self): # pylint: disable=no-self-use try: import termios return except ImportError: pass eq_(pretty.get_terminal_size(), (80, 24)) def test_exception_in_ioctl(self): try: import termios except ImportError: return def raiser(*args, **kwargs): # pylint: disable=unused-argument raise Exception("yeehar!") self.ioctl.side_effect = raiser eq_(pretty.get_terminal_size(), (80, 24)) self.ioctl.assert_called_with(0, termios.TIOCGWINSZ, self.zero_struct) def test_happy_path(self): try: import termios except ImportError: return self.ioctl.return_value = struct.pack("HHHH", 17, 23, 5, 5) eq_(pretty.get_terminal_size(), (23, 17)) self.ioctl.assert_called_with(0, termios.TIOCGWINSZ, self.zero_struct) def test_zero_size_fallback(self): try: import termios except ImportError: return self.ioctl.return_value = self.zero_struct eq_(pretty.get_terminal_size(), (80, 24)) self.ioctl.assert_called_with(0, termios.TIOCGWINSZ, self.zero_struct) def _tf(): """Open a temp file that looks a bunch like stdout.""" if six.PY3: # in python3 it's got an encoding and accepts new-style strings return tempfile.TemporaryFile(mode="w", encoding="UTF-8") # pre-python3 it's not got an encoding and accepts encoded data # (old-style strings) return tempfile.TemporaryFile(mode="w") class FormatterTests(unittest.TestCase): formatter_name = "plain" # SANE DEFAULT, overwritten by concrete classes def setUp(self): self.config = Mock() self.config.color = True self.config.outputs = [StreamOpener(stream=sys.stdout)] self.config.format = [self.formatter_name] _line = 0 @property def line(self): self._line += 1 return self._line def _formatter(self, file_object, config): # pylint: disable=no-self-use stream_opener = StreamOpener(stream=file_object) f = make_formatters(config, [stream_opener])[0] f.uri("<string>") return f def _feature(self, keyword=u"k\xe9yword", name=u"name", tags=None, location=u"location", # pylint: disable=unused-argument description=None, scenarios=None, background=None): if tags is None: tags = [u"spam", u"ham"] if description is None: description = [u"description"] if scenarios is None: scenarios = [] line = self.line tags = [Tag(name, line) for name in tags] return Feature("<string>", line, keyword, name, tags=tags, description=description, scenarios=scenarios, background=background) def _scenario(self, keyword=u"k\xe9yword", name=u"name", tags=None, steps=None): if tags is None: tags = [] if steps is None: steps = [] line = self.line tags = [Tag(name, line) for name in tags] return Scenario("<string>", line, keyword, name, tags=tags, steps=steps) def _step(self, keyword=u"k\xe9yword", step_type="given", name=u"name", text=None, table=None): line = self.line return Step("<string>", line, keyword, step_type, name, text=text, table=table) def _match(self, arguments=None): # pylint: disable=no-self-use def dummy(): pass return Match(dummy, arguments) def test_feature(self): # this test does not actually check the result of the formatting; it # just exists to make sure that formatting doesn't explode in the face of # unicode and stuff p = self._formatter(_tf(), self.config) f = self._feature() p.feature(f) def test_scenario(self): p = self._formatter(_tf(), self.config) f = self._feature() p.feature(f) s = self._scenario() p.scenario(s) def test_step(self): p = self._formatter(_tf(), self.config) f = self._feature() p.feature(f) scenario = self._scenario() p.scenario(scenario) s = self._step() p.step(s) p.match(self._match([])) s.status = Status.passed p.result(s) class TestPretty(FormatterTests): formatter_name = "pretty" class TestPlain(FormatterTests): formatter_name = "plain" class TestJson(FormatterTests): formatter_name = "json" class TestTagsCount(FormatterTests): formatter_name = "tags" def test_tag_counts(self): p = self._formatter(_tf(), self.config) s = self._scenario(tags=[u"ham", u"foo"]) f = self._feature(scenarios=[s]) # feature.tags= ham, spam p.feature(f) p.scenario(s) eq_(p.tag_counts, {"ham": [f, s], "spam": [f], "foo": [s]}) class MultipleFormattersTests(FormatterTests): formatters = [] def setUp(self): self.config = Mock() self.config.color = True self.config.outputs = [StreamOpener(stream=sys.stdout) for i in self.formatters] self.config.format = self.formatters def _formatters(self, file_object, config): # pylint: disable=no-self-use stream_opener = StreamOpener(stream=file_object) formatters = make_formatters(config, [stream_opener]) for f in formatters: f.uri("<string>") return formatters def test_feature(self): # this test does not actually check the result of the formatting; it # just exists to make sure that formatting doesn't explode in the face of # unicode and stuff formatters = self._formatters(_tf(), self.config) f = self._feature() for p in formatters: p.feature(f) def test_scenario(self): formatters = self._formatters(_tf(), self.config) f = self._feature() for p in formatters: p.feature(f) s = self._scenario() p.scenario(s) def test_step(self): formatters = self._formatters(_tf(), self.config) f = self._feature() for p in formatters: p.feature(f) scenario = self._scenario() p.scenario(scenario) s = self._step() p.step(s) p.match(self._match([])) s.status = Status.passed p.result(s) class TestPrettyAndPlain(MultipleFormattersTests): formatters = ["pretty", "plain"] class TestPrettyAndJSON(MultipleFormattersTests): formatters = ["pretty", "json"] class TestJSONAndPlain(MultipleFormattersTests): formatters = ["json", "plain"]