text: # -*- coding: UTF-8 -*-
  # pylint: disable=too-many-lines, line-too-long
  
  from __future__ import absolute_import, print_function, with_statement
  from collections import defaultdict
  from platform import python_implementation
  import os.path
  import sys
  import warnings
  import tempfile
  import unittest
  import six
  from six import StringIO
  
  from mock import Mock, patch
  from nose.tools import *  # pylint: disable=wildcard-import, unused-wildcard-import
  
  from behave import runner_util
  from behave.model import Table
  from behave.step_registry import StepRegistry
  from behave import parser, runner
  from behave.configuration import ConfigError
  from behave.formatter.base import StreamOpener
  
  
  # -- CONVENIENCE-ALIAS:
  _text = six.text_type
  
  
  class TestContext(unittest.TestCase):
      # pylint: disable=invalid-name, protected-access, no-self-use
  
      def setUp(self):
          r = Mock()
          self.config = r.config = Mock()
          r.config.verbose = False
          self.context = runner.Context(r)
  
      def test_user_mode_shall_restore_behave_mode(self):
          # -- CASE: No exception is raised.
          initial_mode = runner.Context.BEHAVE
          eq_(self.context._mode, initial_mode)
          with self.context.use_with_user_mode():
              eq_(self.context._mode, runner.Context.USER)
              self.context.thing = "stuff"
          eq_(self.context._mode, initial_mode)
  
      def test_user_mode_shall_restore_behave_mode_if_assert_fails(self):
          initial_mode = runner.Context.BEHAVE
          eq_(self.context._mode, initial_mode)
          try:
              with self.context.use_with_user_mode():
                  eq_(self.context._mode, runner.Context.USER)
                  assert False, "XFAIL"
          except AssertionError:
              eq_(self.context._mode, initial_mode)
  
      def test_user_mode_shall_restore_behave_mode_if_exception_is_raised(self):
          initial_mode = runner.Context.BEHAVE
          eq_(self.context._mode, initial_mode)
          try:
              with self.context.use_with_user_mode():
                  eq_(self.context._mode, runner.Context.USER)
                  raise RuntimeError("XFAIL")
          except RuntimeError:
              eq_(self.context._mode, initial_mode)
  
      def test_use_with_user_mode__shall_restore_initial_mode(self):
          # -- CASE: No exception is raised.
          # pylint: disable=protected-access
          initial_mode = runner.Context.BEHAVE
          self.context._mode = initial_mode
          with self.context.use_with_user_mode():
              eq_(self.context._mode, runner.Context.USER)
              self.context.thing = "stuff"
          eq_(self.context._mode, initial_mode)
  
      def test_use_with_user_mode__shall_restore_initial_mode_with_error(self):
          # -- CASE: Exception is raised.
          # pylint: disable=protected-access
          initial_mode = runner.Context.BEHAVE
          self.context._mode = initial_mode
          try:
              with self.context.use_with_user_mode():
                  eq_(self.context._mode, runner.Context.USER)
                  raise RuntimeError("XFAIL")
          except RuntimeError:
              eq_(self.context._mode, initial_mode)
  
      def test_use_with_behave_mode__shall_restore_initial_mode(self):
          # -- CASE: No exception is raised.
          # pylint: disable=protected-access
          initial_mode = runner.Context.USER
          self.context._mode = initial_mode
          with self.context._use_with_behave_mode():
              eq_(self.context._mode, runner.Context.BEHAVE)
              self.context.thing = "stuff"
          eq_(self.context._mode, initial_mode)
  
      def test_use_with_behave_mode__shall_restore_initial_mode_with_error(self):
          # -- CASE: Exception is raised.
          # pylint: disable=protected-access
          initial_mode = runner.Context.USER
          self.context._mode = initial_mode
          try:
              with self.context._use_with_behave_mode():
                  eq_(self.context._mode, runner.Context.BEHAVE)
                  raise RuntimeError("XFAIL")
          except RuntimeError:
              eq_(self.context._mode, initial_mode)
  
      def test_context_contains(self):
          eq_("thing" in self.context, False)
          self.context.thing = "stuff"
          eq_("thing" in self.context, True)
          self.context._push()
          eq_("thing" in self.context, True)
  
      def test_attribute_set_at_upper_level_visible_at_lower_level(self):
          self.context.thing = "stuff"
          self.context._push()
          eq_(self.context.thing, "stuff")
  
      def test_attribute_set_at_lower_level_not_visible_at_upper_level(self):
          self.context._push()
          self.context.thing = "stuff"
          self.context._pop()
          assert getattr(self.context, "thing", None) is None
  
      def test_attributes_set_at_upper_level_visible_at_lower_level(self):
          self.context.thing = "stuff"
          self.context._push()
          eq_(self.context.thing, "stuff")
          self.context.other_thing = "more stuff"
          self.context._push()
          eq_(self.context.thing, "stuff")
          eq_(self.context.other_thing, "more stuff")
          self.context.third_thing = "wombats"
          self.context._push()
          eq_(self.context.thing, "stuff")
          eq_(self.context.other_thing, "more stuff")
          eq_(self.context.third_thing, "wombats")
  
      def test_attributes_set_at_lower_level_not_visible_at_upper_level(self):
          self.context.thing = "stuff"
  
          self.context._push()
          self.context.other_thing = "more stuff"
  
          self.context._push()
          self.context.third_thing = "wombats"
          eq_(self.context.thing, "stuff")
          eq_(self.context.other_thing, "more stuff")
          eq_(self.context.third_thing, "wombats")
  
          self.context._pop()
          eq_(self.context.thing, "stuff")
          eq_(self.context.other_thing, "more stuff")
          assert getattr(self.context, "third_thing", None) is None, "%s is not None" % self.context.third_thing
  
          self.context._pop()
          eq_(self.context.thing, "stuff")
          assert getattr(self.context, "other_thing", None) is None, "%s is not None" % self.context.other_thing
          assert getattr(self.context, "third_thing", None) is None, "%s is not None" % self.context.third_thing
  
      def test_masking_existing_user_attribute_when_verbose_causes_warning(self):
          warns = []
  
          def catch_warning(*args, **kwargs):
              warns.append(args[0])
  
          old_showwarning = warnings.showwarning
          warnings.showwarning = catch_warning
  
          # pylint: disable=protected-access
          self.config.verbose = True
          with self.context.use_with_user_mode():
              self.context.thing = "stuff"
              self.context._push()
              self.context.thing = "other stuff"
  
          warnings.showwarning = old_showwarning
  
          print(repr(warns))
          assert warns, "warns is empty!"
          warning = warns[0]
          assert isinstance(warning, runner.ContextMaskWarning), "warning is not a ContextMaskWarning"
          info = warning.args[0]
          assert info.startswith("user code"), "%r doesn't start with 'user code'" % info
          assert "'thing'" in info, "%r not in %r" % ("'thing'", info)
          assert "tutorial" in info, '"tutorial" not in %r' % (info, )
  
      def test_masking_existing_user_attribute_when_not_verbose_causes_no_warning(self):
          warns = []
  
          def catch_warning(*args, **kwargs):
              warns.append(args[0])
  
          old_showwarning = warnings.showwarning
          warnings.showwarning = catch_warning
  
          # explicit
          # pylint: disable=protected-access
          self.config.verbose = False
          with self.context.use_with_user_mode():
              self.context.thing = "stuff"
              self.context._push()
              self.context.thing = "other stuff"
  
          warnings.showwarning = old_showwarning
  
          assert not warns
  
      def test_behave_masking_user_attribute_causes_warning(self):
          warns = []
  
          def catch_warning(*args, **kwargs):
              warns.append(args[0])
  
          old_showwarning = warnings.showwarning
          warnings.showwarning = catch_warning
  
          with self.context.use_with_user_mode():
              self.context.thing = "stuff"
          # pylint: disable=protected-access
          self.context._push()
          self.context.thing = "other stuff"
  
          warnings.showwarning = old_showwarning
  
          print(repr(warns))
          assert warns, "OOPS: warns is empty, but expected non-empty"
          warning = warns[0]
          assert isinstance(warning, runner.ContextMaskWarning), "warning is not a ContextMaskWarning"
          info = warning.args[0]
          assert info.startswith("behave runner"), "%r doesn't start with 'behave runner'" % info
          assert "'thing'" in info, "%r not in %r" % ("'thing'", info)
          filename = __file__.rsplit(".", 1)[0]
          if python_implementation() == "Jython":
              filename = filename.replace("$py", ".py")
          assert filename in info, "%r not in %r" % (filename, info)
  
      def test_setting_root_attribute_that_masks_existing_causes_warning(self):
          # pylint: disable=protected-access
          warns = []
  
          def catch_warning(*args, **kwargs):
              warns.append(args[0])
  
          old_showwarning = warnings.showwarning
          warnings.showwarning = catch_warning
  
          with self.context.use_with_user_mode():
              self.context._push()
              self.context.thing = "teak"
          self.context._set_root_attribute("thing", "oak")
  
          warnings.showwarning = old_showwarning
  
          print(repr(warns))
          assert warns
          warning = warns[0]
          assert isinstance(warning, runner.ContextMaskWarning)
          info = warning.args[0]
          assert info.startswith("behave runner"), "%r doesn't start with 'behave runner'" % info
          assert "'thing'" in info, "%r not in %r" % ("'thing'", info)
          filename = __file__.rsplit(".", 1)[0]
          if python_implementation() == "Jython":
              filename = filename.replace("$py", ".py")
          assert filename in info, "%r not in %r" % (filename, info)
  
      def test_context_deletable(self):
          eq_("thing" in self.context, False)
          self.context.thing = "stuff"
          eq_("thing" in self.context, True)
          del self.context.thing
          eq_("thing" in self.context, False)
  
      @raises(AttributeError)
      def test_context_deletable_raises(self):
          # pylint: disable=protected-access
          eq_("thing" in self.context, False)
          self.context.thing = "stuff"
          eq_("thing" in self.context, True)
          self.context._push()
          eq_("thing" in self.context, True)
          del self.context.thing
  
  class ExampleSteps(object):
      text = None
      table = None
  
      @staticmethod
      def step_passes(context):   # pylint: disable=unused-argument
          pass
  
      @staticmethod
      def step_fails(context):    # pylint: disable=unused-argument
          assert False, "XFAIL"
  
      @classmethod
      def step_with_text(cls, context):
          assert context.text is not None, "REQUIRE: multi-line text"
          cls.text = context.text
  
      @classmethod
      def step_with_table(cls, context):
          assert context.table, "REQUIRE: table"
          cls.table = context.table
  
      @classmethod
      def register_steps_with(cls, step_registry):
          # pylint: disable=bad-whitespace
          step_definitions = [
              ("step", "a step passes", cls.step_passes),
              ("step", "a step fails",  cls.step_fails),
              ("step", "a step with text",     cls.step_with_text),
              ("step", "a step with a table",  cls.step_with_table),
          ]
          for keyword, pattern, func in step_definitions:
              step_registry.add_step_definition(keyword, pattern, func)
  
  class TestContext_ExecuteSteps(unittest.TestCase):
      """
      Test the behave.runner.Context.execute_steps() functionality.
      """
      # pylint: disable=invalid-name, no-self-use
      step_registry = None
  
      def setUp(self):
          if not self.step_registry:
              # -- SETUP ONCE:
              self.step_registry = StepRegistry()
              ExampleSteps.register_steps_with(self.step_registry)
          ExampleSteps.text = None
          ExampleSteps.table = None
  
          runner_ = Mock()
          self.config = runner_.config = Mock()
          runner_.config.verbose = False
          runner_.config.stdout_capture = False
          runner_.config.stderr_capture = False
          runner_.config.log_capture = False
          runner_.step_registry = self.step_registry
  
          self.context = runner.Context(runner_)
          runner_.context = self.context
          self.context.feature = Mock()
          self.context.feature.parser = parser.Parser()
          self.context.runner = runner_
          # self.context.text = None
          # self.context.table = None
  
      def test_execute_steps_with_simple_steps(self):
          doc = u"""
  Given a step passes
  Then a step passes
  """.lstrip()
          with patch("behave.step_registry.registry", self.step_registry):
              result = self.context.execute_steps(doc)
              eq_(result, True)
  
      def test_execute_steps_with_failing_step(self):
          doc = u"""
  Given a step passes
  When a step fails
  Then a step passes
  """.lstrip()
          with patch("behave.step_registry.registry", self.step_registry):
              try:
                  result = self.context.execute_steps(doc)
              except AssertionError as e:
                  ok_("FAILED SUB-STEP: When a step fails" in _text(e))
  
      def test_execute_steps_with_undefined_step(self):
          doc = u"""
  Given a step passes
  When a step is undefined
  Then a step passes
  """.lstrip()
          with patch("behave.step_registry.registry", self.step_registry):
              try:
                  result = self.context.execute_steps(doc)
              except AssertionError as e:
                  ok_("UNDEFINED SUB-STEP: When a step is undefined" in _text(e))
  
      def test_execute_steps_with_text(self):
          doc = u'''
  Given a step passes
  When a step with text:
      """
      Lorem ipsum
      Ipsum lorem
      """
  Then a step passes
  '''.lstrip()
          with patch("behave.step_registry.registry", self.step_registry):
              result = self.context.execute_steps(doc)
              expected_text = "Lorem ipsum\nIpsum lorem"
              eq_(result, True)
              eq_(expected_text, ExampleSteps.text)
  
      def test_execute_steps_with_table(self):
          doc = u"""
  Given a step with a table:
      | Name  | Age |
      | Alice |  12 |
      | Bob   |  23 |
  Then a step passes
  """.lstrip()
          with patch("behave.step_registry.registry", self.step_registry):
              # pylint: disable=bad-whitespace, bad-continuation
              result = self.context.execute_steps(doc)
              expected_table = Table([u"Name", u"Age"], 0, [
                      [u"Alice", u"12"],
                      [u"Bob",   u"23"],
              ])
              eq_(result, True)
              eq_(expected_table, ExampleSteps.table)
  
      def test_context_table_is_restored_after_execute_steps_without_table(self):
          doc = u"""
  Given a step passes
  Then a step passes
  """.lstrip()
          with patch("behave.step_registry.registry", self.step_registry):
              original_table = "<ORIGINAL_TABLE>"
              self.context.table = original_table
              self.context.execute_steps(doc)
              eq_(self.context.table, original_table)
  
      def test_context_table_is_restored_after_execute_steps_with_table(self):
          doc = u"""
  Given a step with a table:
      | Name  | Age |
      | Alice |  12 |
      | Bob   |  23 |
  Then a step passes
  """.lstrip()
          with patch("behave.step_registry.registry", self.step_registry):
              original_table = "<ORIGINAL_TABLE>"
              self.context.table = original_table
              self.context.execute_steps(doc)
              eq_(self.context.table, original_table)
  
      def test_context_text_is_restored_after_execute_steps_without_text(self):
          doc = u"""
  Given a step passes
  Then a step passes
  """.lstrip()
          with patch("behave.step_registry.registry", self.step_registry):
              original_text = "<ORIGINAL_TEXT>"
              self.context.text = original_text
              self.context.execute_steps(doc)
              eq_(self.context.text, original_text)
  
      def test_context_text_is_restored_after_execute_steps_with_text(self):
          doc = u'''
  Given a step passes
  When a step with text:
      """
      Lorem ipsum
      Ipsum lorem
      """
  '''.lstrip()
          with patch("behave.step_registry.registry", self.step_registry):
              original_text = "<ORIGINAL_TEXT>"
              self.context.text = original_text
              self.context.execute_steps(doc)
              eq_(self.context.text, original_text)
  
  
      @raises(ValueError)
      def test_execute_steps_should_fail_when_called_without_feature(self):
          doc = u"""
  Given a passes
  Then a step passes
  """.lstrip()
          with patch("behave.step_registry.registry", self.step_registry):
              self.context.feature = None
              self.context.execute_steps(doc)
  
  
  def create_mock_config():
      config = Mock()
      config.steps_dir = "steps"
      config.environment_file = "environment.py"
      return config
  
  
  class TestRunner(object):
      # pylint: disable=invalid-name, no-self-use
  
      def test_load_hooks_execfiles_hook_file(self):
          with patch("behave.runner.exec_file") as ef:
              with patch("os.path.exists") as exists:
                  exists.return_value = True
                  base_dir = "fake/path"
                  hooks_path = os.path.join(base_dir, "environment.py")
  
                  r = runner.Runner(create_mock_config())
                  r.base_dir = base_dir
                  r.load_hooks()
  
                  exists.assert_called_with(hooks_path)
                  ef.assert_called_with(hooks_path, r.hooks)
  
      def test_run_hook_runs_a_hook_that_exists(self):
          config = Mock()
          r = runner.Runner(config)
          # XXX r.config = Mock()
          r.config.stdout_capture = False
          r.config.stderr_capture = False
          r.config.dry_run = False
          r.hooks["before_lunch"] = hook = Mock()
          args = (runner.Context(Mock()), Mock(), Mock())
          r.run_hook("before_lunch", *args)
  
          hook.assert_called_with(*args)
  
      def test_run_hook_does_not_runs_a_hook_that_exists_if_dry_run(self):
          r = runner.Runner(None)
          r.config = Mock()
          r.config.dry_run = True
          r.hooks["before_lunch"] = hook = Mock()
          args = (runner.Context(Mock()), Mock(), Mock())
          r.run_hook("before_lunch", *args)
  
          assert len(hook.call_args_list) == 0
  
      def test_setup_capture_creates_stringio_for_stdout(self):
          r = runner.Runner(Mock())
          r.config.stdout_capture = True
          r.config.log_capture = False
          r.context = Mock()
  
          r.setup_capture()
  
          assert r.capture_controller.stdout_capture is not None
          assert isinstance(r.capture_controller.stdout_capture, StringIO)
  
      def test_setup_capture_does_not_create_stringio_if_not_wanted(self):
          r = runner.Runner(Mock())
          r.config.stdout_capture = False
          r.config.stderr_capture = False
          r.config.log_capture = False
  
          r.setup_capture()
  
          assert r.capture_controller.stdout_capture is None
  
      @patch("behave.capture.LoggingCapture")
      def test_setup_capture_creates_memory_handler_for_logging(self, handler):
          r = runner.Runner(Mock())
          r.config.stdout_capture = False
          r.config.log_capture = True
          r.context = Mock()
  
          r.setup_capture()
  
          assert r.capture_controller.log_capture is not None
          handler.assert_called_with(r.config)
          r.capture_controller.log_capture.inveigle.assert_called_with()
  
      def test_setup_capture_does_not_create_memory_handler_if_not_wanted(self):
          r = runner.Runner(Mock())
          r.config.stdout_capture = False
          r.config.stderr_capture = False
          r.config.log_capture = False
  
          r.setup_capture()
  
          assert r.capture_controller.log_capture is None
  
      def test_start_stop_capture_switcheroos_sys_stdout(self):
          old_stdout = sys.stdout
          sys.stdout = new_stdout = Mock()
  
          r = runner.Runner(Mock())
          r.config.stdout_capture = True
          r.config.log_capture = False
          r.context = Mock()
  
          r.setup_capture()
          r.start_capture()
  
          eq_(sys.stdout, r.capture_controller.stdout_capture)
  
          r.stop_capture()
  
          eq_(sys.stdout, new_stdout)
  
          sys.stdout = old_stdout
  
      def test_start_stop_capture_leaves_sys_stdout_alone_if_off(self):
          r = runner.Runner(Mock())
          r.config.stdout_capture = False
          r.config.log_capture = False
  
          old_stdout = sys.stdout
  
          r.start_capture()
  
          eq_(sys.stdout, old_stdout)
  
          r.stop_capture()
  
          eq_(sys.stdout, old_stdout)
  
      def test_teardown_capture_removes_log_tap(self):
          r = runner.Runner(Mock())
          r.config.stdout_capture = False
          r.config.log_capture = True
  
          r.capture_controller.log_capture = Mock()
  
          r.teardown_capture()
  
          r.capture_controller.log_capture.abandon.assert_called_with()
  
      def test_exec_file(self):
          fn = tempfile.mktemp()
          with open(fn, "w") as f:
              f.write("spam = __file__\n")
          g = {}
          l = {}
          runner_util.exec_file(fn, g, l)
          assert "__file__" in l
          # pylint: disable=too-many-format-args
          assert "spam" in l, '"spam" variable not set in locals (%r)' % (g, l)
          # pylint: enable=too-many-format-args
          eq_(l["spam"], fn)
  
      def test_run_returns_true_if_everything_passed(self):
          r = runner.Runner(Mock())
          r.setup_capture = Mock()
          r.setup_paths = Mock()
          r.run_with_paths = Mock()
          r.run_with_paths.return_value = True
          assert r.run()
  
      def test_run_returns_false_if_anything_failed(self):
          r = runner.Runner(Mock())
          r.setup_capture = Mock()
          r.setup_paths = Mock()
          r.run_with_paths = Mock()
          r.run_with_paths.return_value = False
          assert not r.run()
  
  
  class TestRunWithPaths(unittest.TestCase):
      # pylint: disable=invalid-name, no-self-use
  
      def setUp(self):
          self.config = Mock()
          self.config.reporters = []
          self.config.logging_level = None
          self.config.logging_filter = None
          self.config.outputs = [Mock(), StreamOpener(stream=sys.stdout)]
          self.config.format = ["plain", "progress"]
          self.runner = runner.Runner(self.config)
          self.load_hooks = self.runner.load_hooks = Mock()
          self.load_step_definitions = self.runner.load_step_definitions = Mock()
          self.run_hook = self.runner.run_hook = Mock()
          self.run_step = self.runner.run_step = Mock()
          self.feature_locations = self.runner.feature_locations = Mock()
          self.calculate_summaries = self.runner.calculate_summaries = Mock()
  
          self.formatter_class = patch("behave.formatter.pretty.PrettyFormatter")
          formatter_class = self.formatter_class.start()
          formatter_class.return_value = self.formatter = Mock()
  
      def tearDown(self):
          self.formatter_class.stop()
  
      def test_loads_hooks_and_step_definitions(self):
          self.feature_locations.return_value = []
          self.runner.run_with_paths()
  
          assert self.load_hooks.called
          assert self.load_step_definitions.called
  
      def test_runs_before_all_and_after_all_hooks(self):
          # Make runner.feature_locations() and runner.run_hook() the same mock so
          # we can make sure things happen in the right order.
          self.runner.feature_locations = self.run_hook
          self.runner.feature_locations.return_value = []
          self.runner.context = Mock()
          self.runner.run_with_paths()
  
          eq_(self.run_hook.call_args_list, [
              ((), {}),
              (("before_all", self.runner.context), {}),
              (("after_all", self.runner.context), {}),
          ])
  
      @patch("behave.parser.parse_file")
      @patch("os.path.abspath")
      def test_parses_feature_files_and_appends_to_feature_list(self, abspath,
                                                                parse_file):
          feature_locations = ["one", "two", "three"]
          feature = Mock()
          feature.tags = []
          feature.__iter__ = Mock(return_value=iter([]))
          feature.run.return_value = False
          self.runner.feature_locations.return_value = feature_locations
          abspath.side_effect = lambda x: x.upper()
          self.config.lang = "fritz"
          self.config.format = ["plain"]
          self.config.outputs = [StreamOpener(stream=sys.stdout)]
          self.config.output.encoding = None
          self.config.exclude = lambda s: False
          self.config.junit = False
          self.config.summary = False
          parse_file.return_value = feature
  
          self.runner.run_with_paths()
  
          expected_parse_file_args = \
              [((x.upper(),), {"language": "fritz"}) for x in feature_locations]
          eq_(parse_file.call_args_list, expected_parse_file_args)
          eq_(self.runner.features, [feature] * 3)
  
  
  class FsMock(object):
      def __init__(self, *paths):
          self.base = os.path.abspath(".")
          self.sep = os.path.sep
  
          # This bit of gymnastics is to support Windows. We feed in a bunch of
          # paths in places using FsMock that assume that POSIX-style paths
          # work. This is faster than fixing all of those but at some point we
          # should totally do it properly with os.path.join() and all that.
          def full_split(path):
              bits = []
  
              while path:
                  path, bit = os.path.split(path)
                  bits.insert(0, bit)
  
              return bits
  
          paths = [os.path.join(self.base, *full_split(path)) for path in paths]
          print(repr(paths))
          self.paths = paths
          self.files = set()
          self.dirs = defaultdict(list)
          separators = [sep for sep in (os.path.sep, os.path.altsep) if sep]
          for path in paths:
              if path[-1] in separators:
                  self.dirs[path[:-1]] = []
                  d, p = os.path.split(path[:-1])
                  while d and p:
                      self.dirs[d].append(p)
                      d, p = os.path.split(d)
              else:
                  self.files.add(path)
                  d, f = os.path.split(path)
                  self.dirs[d].append(f)
          self.calls = []
  
      def listdir(self, dir):
          # pylint: disable=W0622
          #   W0622   Redefining built-in dir
          self.calls.append(("listdir", dir))
          return self.dirs.get(dir, [])
  
      def isfile(self, path):
          self.calls.append(("isfile", path))
          return path in self.files
  
      def isdir(self, path):
          self.calls.append(("isdir", path))
          return path in self.dirs
  
      def exists(self, path):
          self.calls.append(("exists", path))
          return path in self.dirs or path in self.files
  
      def walk(self, path, locations=None, followlinks=False):
          if locations is None:
              assert path in self.dirs, "%s not in %s" % (path, self.dirs)
              locations = []
          dirnames = []
          filenames = []
          for e in self.dirs[path]:
              if os.path.join(path, e) in self.dirs:
                  dirnames.append(e)
                  self.walk(os.path.join(path, e), locations)
              else:
                  filenames.append(e)
          locations.append((path, dirnames, filenames))
          return locations
  
      # utilities that we need
      # pylint: disable=no-self-use
      def dirname(self, path, orig=os.path.dirname):
          return orig(path)
  
      def abspath(self, path, orig=os.path.abspath):
          return orig(path)
  
      def join(self, x, y, orig=os.path.join):
          return orig(x, y)
  
      def split(self, path, orig=os.path.split):
          return orig(path)
  
      def splitdrive(self, path, orig=os.path.splitdrive):
          return orig(path)
  
  
  class TestFeatureDirectory(object):
      # pylint: disable=invalid-name, no-self-use
  
      def test_default_path_no_steps(self):
          config = create_mock_config()
          config.paths = []
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock()
  
          # will look for a "features" directory and not find one
          with patch("os.path", fs):
              assert_raises(ConfigError, r.setup_paths)
  
          ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls)
  
      def test_default_path_no_features(self):
          config = create_mock_config()
          config.paths = []
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock("features/steps/")
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  assert_raises(ConfigError, r.setup_paths)
  
      def test_default_path(self):
          config = create_mock_config()
          config.paths = []
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock("features/steps/", "features/foo.feature")
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  with r.path_manager:
                      r.setup_paths()
  
          eq_(r.base_dir, os.path.abspath("features"))
  
      def test_supplied_feature_file(self):
          config = create_mock_config()
          config.paths = ["foo.feature"]
          config.verbose = True
          r = runner.Runner(config)
          r.context = Mock()
  
          fs = FsMock("steps/", "foo.feature")
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  with r.path_manager:
                      r.setup_paths()
          ok_(("isdir", os.path.join(fs.base, "steps")) in fs.calls)
          ok_(("isfile", os.path.join(fs.base, "foo.feature")) in fs.calls)
  
          eq_(r.base_dir, fs.base)
  
      def test_supplied_feature_file_no_steps(self):
          config = create_mock_config()
          config.paths = ["foo.feature"]
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock("foo.feature")
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  with r.path_manager:
                      assert_raises(ConfigError, r.setup_paths)
  
      def test_supplied_feature_directory(self):
          config = create_mock_config()
          config.paths = ["spam"]
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock("spam/", "spam/steps/", "spam/foo.feature")
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  with r.path_manager:
                      r.setup_paths()
  
          ok_(("isdir", os.path.join(fs.base, "spam", "steps")) in fs.calls)
  
          eq_(r.base_dir, os.path.join(fs.base, "spam"))
  
      def test_supplied_feature_directory_no_steps(self):
          config = create_mock_config()
          config.paths = ["spam"]
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock("spam/", "spam/foo.feature")
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  assert_raises(ConfigError, r.setup_paths)
  
          ok_(("isdir", os.path.join(fs.base, "spam", "steps")) in fs.calls)
  
      def test_supplied_feature_directory_missing(self):
          config = create_mock_config()
          config.paths = ["spam"]
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock()
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  assert_raises(ConfigError, r.setup_paths)
  
  
  class TestFeatureDirectoryLayout2(object):
      # pylint: disable=invalid-name, no-self-use
  
      def test_default_path(self):
          config = create_mock_config()
          config.paths = []
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock(
              "features/",
              "features/steps/",
              "features/group1/",
              "features/group1/foo.feature",
          )
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  with r.path_manager:
                      r.setup_paths()
  
          eq_(r.base_dir, os.path.abspath("features"))
  
      def test_supplied_root_directory(self):
          config = create_mock_config()
          config.paths = ["features"]
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock(
              "features/",
              "features/group1/",
              "features/group1/foo.feature",
              "features/steps/",
          )
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  with r.path_manager:
                      r.setup_paths()
  
          ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls)
          eq_(r.base_dir, os.path.join(fs.base, "features"))
  
      def test_supplied_root_directory_no_steps(self):
          config = create_mock_config()
          config.paths = ["features"]
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock(
              "features/",
              "features/group1/",
              "features/group1/foo.feature",
          )
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  with r.path_manager:
                      assert_raises(ConfigError, r.setup_paths)
  
          ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls)
          eq_(r.base_dir, None)
  
  
      def test_supplied_feature_file(self):
          config = create_mock_config()
          config.paths = ["features/group1/foo.feature"]
          config.verbose = True
          r = runner.Runner(config)
          r.context = Mock()
  
          fs = FsMock(
              "features/",
              "features/group1/",
              "features/group1/foo.feature",
              "features/steps/",
          )
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  with r.path_manager:
                      r.setup_paths()
  
          ok_(("isdir", os.path.join(fs.base, "features", "steps"))  in fs.calls)
          ok_(("isfile", os.path.join(fs.base, "features", "group1", "foo.feature")) in fs.calls)
          eq_(r.base_dir, fs.join(fs.base, "features"))
  
      def test_supplied_feature_file_no_steps(self):
          config = create_mock_config()
          config.paths = ["features/group1/foo.feature"]
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock(
              "features/",
              "features/group1/",
              "features/group1/foo.feature",
          )
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  with r.path_manager:
                      assert_raises(ConfigError, r.setup_paths)
  
      def test_supplied_feature_directory(self):
          config = create_mock_config()
          config.paths = ["features/group1"]
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock(
              "features/",
              "features/group1/",
              "features/group1/foo.feature",
              "features/steps/",
          )
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  with r.path_manager:
                      r.setup_paths()
  
          ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls)
          eq_(r.base_dir, os.path.join(fs.base, "features"))
  
  
      def test_supplied_feature_directory_no_steps(self):
          config = create_mock_config()
          config.paths = ["features/group1"]
          config.verbose = True
          r = runner.Runner(config)
  
          fs = FsMock(
              "features/",
              "features/group1/",
              "features/group1/foo.feature",
          )
  
          with patch("os.path", fs):
              with patch("os.walk", fs.walk):
                  assert_raises(ConfigError, r.setup_paths)
  
          ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls)
  