text: import os
  import sys
  from unittest import mock
  
  import pytest
  from _pytest.main import ExitCode
  from _pytest.mark import EMPTY_PARAMETERSET_OPTION
  from _pytest.mark import MarkGenerator as Mark
  from _pytest.nodes import Collector
  from _pytest.nodes import Node
  
  
  class TestMark:
      @pytest.mark.parametrize("attr", ["mark", "param"])
      @pytest.mark.parametrize("modulename", ["py.test", "pytest"])
      def test_pytest_exists_in_namespace_all(self, attr, modulename):
          module = sys.modules[modulename]
          assert attr in module.__all__
  
      def test_pytest_mark_notcallable(self):
          mark = Mark()
          with pytest.raises(TypeError):
              mark()
  
      def test_mark_with_param(self):
          def some_function(abc):
              pass
  
          class SomeClass:
              pass
  
          assert pytest.mark.foo(some_function) is some_function
          assert pytest.mark.foo.with_args(some_function) is not some_function
  
          assert pytest.mark.foo(SomeClass) is SomeClass
          assert pytest.mark.foo.with_args(SomeClass) is not SomeClass
  
      def test_pytest_mark_name_starts_with_underscore(self):
          mark = Mark()
          with pytest.raises(AttributeError):
              mark._some_name
  
  
  def test_marked_class_run_twice(testdir, request):
      """Test fails file is run twice that contains marked class.
      See issue#683.
      """
      py_file = testdir.makepyfile(
          """
      import pytest
      @pytest.mark.parametrize('abc', [1, 2, 3])
      class Test1(object):
          def test_1(self, abc):
              assert abc in [1, 2, 3]
      """
      )
      file_name = os.path.basename(py_file.strpath)
      rec = testdir.inline_run(file_name, file_name)
      rec.assertoutcome(passed=6)
  
  
  def test_ini_markers(testdir):
      testdir.makeini(
          """
          [pytest]
          markers =
              a1: this is a webtest marker
              a2: this is a smoke marker
      """
      )
      testdir.makepyfile(
          """
          def test_markers(pytestconfig):
              markers = pytestconfig.getini("markers")
              print(markers)
              assert len(markers) >= 2
              assert markers[0].startswith("a1:")
              assert markers[1].startswith("a2:")
      """
      )
      rec = testdir.inline_run()
      rec.assertoutcome(passed=1)
  
  
  def test_markers_option(testdir):
      testdir.makeini(
          """
          [pytest]
          markers =
              a1: this is a webtest marker
              a1some: another marker
              nodescription
      """
      )
      result = testdir.runpytest("--markers")
      result.stdout.fnmatch_lines(
          ["*a1*this is a webtest*", "*a1some*another marker", "*nodescription*"]
      )
  
  
  def test_ini_markers_whitespace(testdir):
      testdir.makeini(
          """
          [pytest]
          markers =
              a1 : this is a whitespace marker
      """
      )
      testdir.makepyfile(
          """
          import pytest
  
          @pytest.mark.a1
          def test_markers():
              assert True
      """
      )
      rec = testdir.inline_run("--strict-markers", "-m", "a1")
      rec.assertoutcome(passed=1)
  
  
  def test_marker_without_description(testdir):
      testdir.makefile(
          ".cfg",
          setup="""
          [tool:pytest]
          markers=slow
      """,
      )
      testdir.makeconftest(
          """
          import pytest
          pytest.mark.xfail('FAIL')
      """
      )
      ftdir = testdir.mkdir("ft1_dummy")
      testdir.tmpdir.join("conftest.py").move(ftdir.join("conftest.py"))
      rec = testdir.runpytest("--strict-markers")
      rec.assert_outcomes()
  
  
  def test_markers_option_with_plugin_in_current_dir(testdir):
      testdir.makeconftest('pytest_plugins = "flip_flop"')
      testdir.makepyfile(
          flip_flop="""\
          def pytest_configure(config):
              config.addinivalue_line("markers", "flip:flop")
  
          def pytest_generate_tests(metafunc):
              try:
                  mark = metafunc.function.flipper
              except AttributeError:
                  return
              metafunc.parametrize("x", (10, 20))"""
      )
      testdir.makepyfile(
          """\
          import pytest
          @pytest.mark.flipper
          def test_example(x):
              assert x"""
      )
  
      result = testdir.runpytest("--markers")
      result.stdout.fnmatch_lines(["*flip*flop*"])
  
  
  def test_mark_on_pseudo_function(testdir):
      testdir.makepyfile(
          """
          import pytest
  
          @pytest.mark.r(lambda x: 0/0)
          def test_hello():
              pass
      """
      )
      reprec = testdir.inline_run()
      reprec.assertoutcome(passed=1)
  
  
  @pytest.mark.parametrize("option_name", ["--strict-markers", "--strict"])
  def test_strict_prohibits_unregistered_markers(testdir, option_name):
      testdir.makepyfile(
          """
          import pytest
          @pytest.mark.unregisteredmark
          def test_hello():
              pass
      """
      )
      result = testdir.runpytest(option_name)
      assert result.ret != 0
      result.stdout.fnmatch_lines(
          ["'unregisteredmark' not found in `markers` configuration option"]
      )
  
  
  @pytest.mark.parametrize(
      "spec",
      [
          ("xyz", ("test_one",)),
          ("xyz and xyz2", ()),
          ("xyz2", ("test_two",)),
          ("xyz or xyz2", ("test_one", "test_two")),
      ],
  )
  def test_mark_option(spec, testdir):
      testdir.makepyfile(
          """
          import pytest
          @pytest.mark.xyz
          def test_one():
              pass
          @pytest.mark.xyz2
          def test_two():
              pass
      """
      )
      opt, passed_result = spec
      rec = testdir.inline_run("-m", opt)
      passed, skipped, fail = rec.listoutcomes()
      passed = [x.nodeid.split("::")[-1] for x in passed]
      assert len(passed) == len(passed_result)
      assert list(passed) == list(passed_result)
  
  
  @pytest.mark.parametrize(
      "spec", [("interface", ("test_interface",)), ("not interface", ("test_nointer",))]
  )
  def test_mark_option_custom(spec, testdir):
      testdir.makeconftest(
          """
          import pytest
          def pytest_collection_modifyitems(items):
              for item in items:
                  if "interface" in item.nodeid:
                      item.add_marker(pytest.mark.interface)
      """
      )
      testdir.makepyfile(
          """
          def test_interface():
              pass
          def test_nointer():
              pass
      """
      )
      opt, passed_result = spec
      rec = testdir.inline_run("-m", opt)
      passed, skipped, fail = rec.listoutcomes()
      passed = [x.nodeid.split("::")[-1] for x in passed]
      assert len(passed) == len(passed_result)
      assert list(passed) == list(passed_result)
  
  
  @pytest.mark.parametrize(
      "spec",
      [
          ("interface", ("test_interface",)),
          ("not interface", ("test_nointer", "test_pass")),
          ("pass", ("test_pass",)),
          ("not pass", ("test_interface", "test_nointer")),
      ],
  )
  def test_keyword_option_custom(spec, testdir):
      testdir.makepyfile(
          """
          def test_interface():
              pass
          def test_nointer():
              pass
          def test_pass():
              pass
      """
      )
      opt, passed_result = spec
      rec = testdir.inline_run("-k", opt)
      passed, skipped, fail = rec.listoutcomes()
      passed = [x.nodeid.split("::")[-1] for x in passed]
      assert len(passed) == len(passed_result)
      assert list(passed) == list(passed_result)
  
  
  def test_keyword_option_considers_mark(testdir):
      testdir.copy_example("marks/marks_considered_keywords")
      rec = testdir.inline_run("-k", "foo")
      passed = rec.listoutcomes()[0]
      assert len(passed) == 1
  
  
  @pytest.mark.parametrize(
      "spec",
      [
          ("None", ("test_func[None]",)),
          ("1.3", ("test_func[1.3]",)),
          ("2-3", ("test_func[2-3]",)),
      ],
  )
  def test_keyword_option_parametrize(spec, testdir):
      testdir.makepyfile(
          """
          import pytest
          @pytest.mark.parametrize("arg", [None, 1.3, "2-3"])
          def test_func(arg):
              pass
      """
      )
      opt, passed_result = spec
      rec = testdir.inline_run("-k", opt)
      passed, skipped, fail = rec.listoutcomes()
      passed = [x.nodeid.split("::")[-1] for x in passed]
      assert len(passed) == len(passed_result)
      assert list(passed) == list(passed_result)
  
  
  @pytest.mark.parametrize(
      "spec",
      [
          (
              "foo or import",
              "ERROR: Python keyword 'import' not accepted in expressions passed to '-k'",
          ),
          ("foo or", "ERROR: Wrong expression passed to '-k': foo or"),
      ],
  )
  def test_keyword_option_wrong_arguments(spec, testdir, capsys):
      testdir.makepyfile(
          """
              def test_func(arg):
                  pass
          """
      )
      opt, expected_result = spec
      testdir.inline_run("-k", opt)
      out = capsys.readouterr().err
      assert expected_result in out
  
  
  def test_parametrized_collected_from_command_line(testdir):
      """Parametrized test not collected if test named specified
         in command line issue#649.
      """
      py_file = testdir.makepyfile(
          """
          import pytest
          @pytest.mark.parametrize("arg", [None, 1.3, "2-3"])
          def test_func(arg):
              pass
      """
      )
      file_name = os.path.basename(py_file.strpath)
      rec = testdir.inline_run(file_name + "::" + "test_func")
      rec.assertoutcome(passed=3)
  
  
  def test_parametrized_collect_with_wrong_args(testdir):
      """Test collect parametrized func with wrong number of args."""
      py_file = testdir.makepyfile(
          """
          import pytest
  
          @pytest.mark.parametrize('foo, bar', [(1, 2, 3)])
          def test_func(foo, bar):
              pass
      """
      )
  
      result = testdir.runpytest(py_file)
      result.stdout.fnmatch_lines(
          [
              'test_parametrized_collect_with_wrong_args.py::test_func: in "parametrize" the number of names (2):',
              "  ['foo', 'bar']",
              "must be equal to the number of values (3):",
              "  (1, 2, 3)",
          ]
      )
  
  
  def test_parametrized_with_kwargs(testdir):
      """Test collect parametrized func with wrong number of args."""
      py_file = testdir.makepyfile(
          """
          import pytest
  
          @pytest.fixture(params=[1,2])
          def a(request):
              return request.param
  
          @pytest.mark.parametrize(argnames='b', argvalues=[1, 2])
          def test_func(a, b):
              pass
      """
      )
  
      result = testdir.runpytest(py_file)
      assert result.ret == 0
  
  
  def test_parametrize_iterator(testdir):
      """parametrize should work with generators (#5354)."""
      py_file = testdir.makepyfile(
          """\
          import pytest
  
          def gen():
              yield 1
              yield 2
              yield 3
  
          @pytest.mark.parametrize('a', gen())
          def test(a):
              assert a >= 1
          """
      )
      result = testdir.runpytest(py_file)
      assert result.ret == 0
      # should not skip any tests
      result.stdout.fnmatch_lines(["*3 passed*"])
  
  
  class TestFunctional:
      def test_merging_markers_deep(self, testdir):
          # issue 199 - propagate markers into nested classes
          p = testdir.makepyfile(
              """
              import pytest
              class TestA(object):
                  pytestmark = pytest.mark.a
                  def test_b(self):
                      assert True
                  class TestC(object):
                      # this one didn't get marked
                      def test_d(self):
                          assert True
          """
          )
          items, rec = testdir.inline_genitems(p)
          for item in items:
              print(item, item.keywords)
              assert [x for x in item.iter_markers() if x.name == "a"]
  
      def test_mark_decorator_subclass_does_not_propagate_to_base(self, testdir):
          p = testdir.makepyfile(
              """
              import pytest
  
              @pytest.mark.a
              class Base(object): pass
  
              @pytest.mark.b
              class Test1(Base):
                  def test_foo(self): pass
  
              class Test2(Base):
                  def test_bar(self): pass
          """
          )
          items, rec = testdir.inline_genitems(p)
          self.assert_markers(items, test_foo=("a", "b"), test_bar=("a",))
  
      def test_mark_should_not_pass_to_siebling_class(self, testdir):
          """#568"""
          p = testdir.makepyfile(
              """
              import pytest
  
              class TestBase(object):
                  def test_foo(self):
                      pass
  
              @pytest.mark.b
              class TestSub(TestBase):
                  pass
  
  
              class TestOtherSub(TestBase):
                  pass
  
          """
          )
          items, rec = testdir.inline_genitems(p)
          base_item, sub_item, sub_item_other = items
          print(items, [x.nodeid for x in items])
          # new api segregates
          assert not list(base_item.iter_markers(name="b"))
          assert not list(sub_item_other.iter_markers(name="b"))
          assert list(sub_item.iter_markers(name="b"))
  
      def test_mark_decorator_baseclasses_merged(self, testdir):
          p = testdir.makepyfile(
              """
              import pytest
  
              @pytest.mark.a
              class Base(object): pass
  
              @pytest.mark.b
              class Base2(Base): pass
  
              @pytest.mark.c
              class Test1(Base2):
                  def test_foo(self): pass
  
              class Test2(Base2):
                  @pytest.mark.d
                  def test_bar(self): pass
          """
          )
          items, rec = testdir.inline_genitems(p)
          self.assert_markers(items, test_foo=("a", "b", "c"), test_bar=("a", "b", "d"))
  
      def test_mark_closest(self, testdir):
          p = testdir.makepyfile(
              """
              import pytest
  
              @pytest.mark.c(location="class")
              class Test:
                  @pytest.mark.c(location="function")
                  def test_has_own():
                      pass
  
                  def test_has_inherited():
                      pass
  
          """
          )
          items, rec = testdir.inline_genitems(p)
          has_own, has_inherited = items
          assert has_own.get_closest_marker("c").kwargs == {"location": "function"}
          assert has_inherited.get_closest_marker("c").kwargs == {"location": "class"}
          assert has_own.get_closest_marker("missing") is None
  
      def test_mark_with_wrong_marker(self, testdir):
          reprec = testdir.inline_runsource(
              """
                  import pytest
                  class pytestmark(object):
                      pass
                  def test_func():
                      pass
          """
          )
          values = reprec.getfailedcollections()
          assert len(values) == 1
          assert "TypeError" in str(values[0].longrepr)
  
      def test_mark_dynamically_in_funcarg(self, testdir):
          testdir.makeconftest(
              """
              import pytest
              @pytest.fixture
              def arg(request):
                  request.applymarker(pytest.mark.hello)
              def pytest_terminal_summary(terminalreporter):
                  values = terminalreporter.stats['passed']
                  terminalreporter._tw.line("keyword: %s" % values[0].keywords)
          """
          )
          testdir.makepyfile(
              """
              def test_func(arg):
                  pass
          """
          )
          result = testdir.runpytest()
          result.stdout.fnmatch_lines(["keyword: *hello*"])
  
      def test_no_marker_match_on_unmarked_names(self, testdir):
          p = testdir.makepyfile(
              """
              import pytest
              @pytest.mark.shouldmatch
              def test_marked():
                  assert 1
  
              def test_unmarked():
                  assert 1
          """
          )
          reprec = testdir.inline_run("-m", "test_unmarked", p)
          passed, skipped, failed = reprec.listoutcomes()
          assert len(passed) + len(skipped) + len(failed) == 0
          dlist = reprec.getcalls("pytest_deselected")
          deselected_tests = dlist[0].items
          assert len(deselected_tests) == 2
  
      def test_invalid_m_option(self, testdir):
          testdir.makepyfile(
              """
              def test_a():
                  pass
          """
          )
          result = testdir.runpytest("-m bogus/")
          result.stdout.fnmatch_lines(
              ["INTERNALERROR> Marker expression must be valid Python!"]
          )
  
      def test_keywords_at_node_level(self, testdir):
          testdir.makepyfile(
              """
              import pytest
              @pytest.fixture(scope="session", autouse=True)
              def some(request):
                  request.keywords["hello"] = 42
                  assert "world" not in request.keywords
  
              @pytest.fixture(scope="function", autouse=True)
              def funcsetup(request):
                  assert "world" in request.keywords
                  assert "hello" in  request.keywords
  
              @pytest.mark.world
              def test_function():
                  pass
          """
          )
          reprec = testdir.inline_run()
          reprec.assertoutcome(passed=1)
  
      def test_keyword_added_for_session(self, testdir):
          testdir.makeconftest(
              """
              import pytest
              def pytest_collection_modifyitems(session):
                  session.add_marker("mark1")
                  session.add_marker(pytest.mark.mark2)
                  session.add_marker(pytest.mark.mark3)
                  pytest.raises(ValueError, lambda:
                          session.add_marker(10))
          """
          )
          testdir.makepyfile(
              """
              def test_some(request):
                  assert "mark1" in request.keywords
                  assert "mark2" in request.keywords
                  assert "mark3" in request.keywords
                  assert 10 not in request.keywords
                  marker = request.node.get_closest_marker("mark1")
                  assert marker.name == "mark1"
                  assert marker.args == ()
                  assert marker.kwargs == {}
          """
          )
          reprec = testdir.inline_run("-m", "mark1")
          reprec.assertoutcome(passed=1)
  
      def assert_markers(self, items, **expected):
          """assert that given items have expected marker names applied to them.
          expected should be a dict of (item name -> seq of expected marker names)
  
          .. note:: this could be moved to ``testdir`` if proven to be useful
          to other modules.
          """
  
          items = {x.name: x for x in items}
          for name, expected_markers in expected.items():
              markers = {m.name for m in items[name].iter_markers()}
              assert markers == set(expected_markers)
  
      @pytest.mark.filterwarnings("ignore")
      def test_mark_from_parameters(self, testdir):
          """#1540"""
          testdir.makepyfile(
              """
              import pytest
  
              pytestmark = pytest.mark.skipif(True, reason='skip all')
  
              # skipifs inside fixture params
              params = [pytest.mark.skipif(False, reason='dont skip')('parameter')]
  
  
              @pytest.fixture(params=params)
              def parameter(request):
                  return request.param
  
  
              def test_1(parameter):
                  assert True
          """
          )
          reprec = testdir.inline_run()
          reprec.assertoutcome(skipped=1)
  
  
  class TestKeywordSelection:
      def test_select_simple(self, testdir):
          file_test = testdir.makepyfile(
              """
              def test_one():
                  assert 0
              class TestClass(object):
                  def test_method_one(self):
                      assert 42 == 43
          """
          )
  
          def check(keyword, name):
              reprec = testdir.inline_run("-s", "-k", keyword, file_test)
              passed, skipped, failed = reprec.listoutcomes()
              assert len(failed) == 1
              assert failed[0].nodeid.split("::")[-1] == name
              assert len(reprec.getcalls("pytest_deselected")) == 1
  
          for keyword in ["test_one", "est_on"]:
              check(keyword, "test_one")
          check("TestClass and test", "test_method_one")
  
      @pytest.mark.parametrize(
          "keyword",
          [
              "xxx",
              "xxx and test_2",
              "TestClass",
              "xxx and not test_1",
              "TestClass and test_2",
              "xxx and TestClass and test_2",
          ],
      )
      def test_select_extra_keywords(self, testdir, keyword):
          p = testdir.makepyfile(
              test_select="""
              def test_1():
                  pass
              class TestClass(object):
                  def test_2(self):
                      pass
          """
          )
          testdir.makepyfile(
              conftest="""
              import pytest
              @pytest.hookimpl(hookwrapper=True)
              def pytest_pycollect_makeitem(name):
                  outcome = yield
                  if name == "TestClass":
                      item = outcome.get_result()
                      item.extra_keyword_matches.add("xxx")
          """
          )
          reprec = testdir.inline_run(p.dirpath(), "-s", "-k", keyword)
          print("keyword", repr(keyword))
          passed, skipped, failed = reprec.listoutcomes()
          assert len(passed) == 1
          assert passed[0].nodeid.endswith("test_2")
          dlist = reprec.getcalls("pytest_deselected")
          assert len(dlist) == 1
          assert dlist[0].items[0].name == "test_1"
  
      def test_select_starton(self, testdir):
          threepass = testdir.makepyfile(
              test_threepass="""
              def test_one(): assert 1
              def test_two(): assert 1
              def test_three(): assert 1
          """
          )
          reprec = testdir.inline_run("-k", "test_two:", threepass)
          passed, skipped, failed = reprec.listoutcomes()
          assert len(passed) == 2
          assert not failed
          dlist = reprec.getcalls("pytest_deselected")
          assert len(dlist) == 1
          item = dlist[0].items[0]
          assert item.name == "test_one"
  
      def test_keyword_extra(self, testdir):
          p = testdir.makepyfile(
              """
             def test_one():
                 assert 0
             test_one.mykeyword = True
          """
          )
          reprec = testdir.inline_run("-k", "mykeyword", p)
          passed, skipped, failed = reprec.countoutcomes()
          assert failed == 1
  
      @pytest.mark.xfail
      def test_keyword_extra_dash(self, testdir):
          p = testdir.makepyfile(
              """
             def test_one():
                 assert 0
             test_one.mykeyword = True
          """
          )
          # with argparse the argument to an option cannot
          # start with '-'
          reprec = testdir.inline_run("-k", "-mykeyword", p)
          passed, skipped, failed = reprec.countoutcomes()
          assert passed + skipped + failed == 0
  
      def test_no_magic_values(self, testdir):
          """Make sure the tests do not match on magic values,
          no double underscored values, like '__dict__',
          and no instance values, like '()'.
          """
          p = testdir.makepyfile(
              """
              def test_one(): assert 1
          """
          )
  
          def assert_test_is_not_selected(keyword):
              reprec = testdir.inline_run("-k", keyword, p)
              passed, skipped, failed = reprec.countoutcomes()
              dlist = reprec.getcalls("pytest_deselected")
              assert passed + skipped + failed == 0
              deselected_tests = dlist[0].items
              assert len(deselected_tests) == 1
  
          assert_test_is_not_selected("__")
          assert_test_is_not_selected("()")
  
  
  class TestMarkDecorator:
      @pytest.mark.parametrize(
          "lhs, rhs, expected",
          [
              (pytest.mark.foo(), pytest.mark.foo(), True),
              (pytest.mark.foo(), pytest.mark.bar(), False),
              (pytest.mark.foo(), "bar", False),
              ("foo", pytest.mark.bar(), False),
          ],
      )
      def test__eq__(self, lhs, rhs, expected):
          assert (lhs == rhs) == expected
  
  
  @pytest.mark.parametrize("mark", [None, "", "skip", "xfail"])
  def test_parameterset_for_parametrize_marks(testdir, mark):
      if mark is not None:
          testdir.makeini(
              """
          [pytest]
          {}={}
          """.format(
                  EMPTY_PARAMETERSET_OPTION, mark
              )
          )
  
      config = testdir.parseconfig()
      from _pytest.mark import pytest_configure, get_empty_parameterset_mark
  
      pytest_configure(config)
      result_mark = get_empty_parameterset_mark(config, ["a"], all)
      if mark in (None, ""):
          # normalize to the requested name
          mark = "skip"
      assert result_mark.name == mark
      assert result_mark.kwargs["reason"].startswith("got empty parameter set ")
      if mark == "xfail":
          assert result_mark.kwargs.get("run") is False
  
  
  def test_parameterset_for_fail_at_collect(testdir):
      testdir.makeini(
          """
      [pytest]
      {}=fail_at_collect
      """.format(
              EMPTY_PARAMETERSET_OPTION
          )
      )
  
      config = testdir.parseconfig()
      from _pytest.mark import pytest_configure, get_empty_parameterset_mark
  
      pytest_configure(config)
  
      with pytest.raises(
          Collector.CollectError,
          match=r"Empty parameter set in 'pytest_configure' at line \d\d+",
      ):
          get_empty_parameterset_mark(config, ["a"], pytest_configure)
  
      p1 = testdir.makepyfile(
          """
          import pytest
  
          @pytest.mark.parametrize("empty", [])
          def test():
              pass
          """
      )
      result = testdir.runpytest(str(p1))
      result.stdout.fnmatch_lines(
          [
              "collected 0 items / 1 error",
              "* ERROR collecting test_parameterset_for_fail_at_collect.py *",
              "Empty parameter set in 'test' at line 3",
              "*= 1 error in *",
          ]
      )
      assert result.ret == ExitCode.INTERRUPTED
  
  
  def test_parameterset_for_parametrize_bad_markname(testdir):
      with pytest.raises(pytest.UsageError):
          test_parameterset_for_parametrize_marks(testdir, "bad")
  
  
  def test_mark_expressions_no_smear(testdir):
      testdir.makepyfile(
          """
          import pytest
  
          class BaseTests(object):
              def test_something(self):
                  pass
  
          @pytest.mark.FOO
          class TestFooClass(BaseTests):
              pass
  
          @pytest.mark.BAR
          class TestBarClass(BaseTests):
              pass
      """
      )
  
      reprec = testdir.inline_run("-m", "FOO")
      passed, skipped, failed = reprec.countoutcomes()
      dlist = reprec.getcalls("pytest_deselected")
      assert passed == 1
      assert skipped == failed == 0
      deselected_tests = dlist[0].items
      assert len(deselected_tests) == 1
  
      # todo: fixed
      # keywords smear - expected behaviour
      # reprec_keywords = testdir.inline_run("-k", "FOO")
      # passed_k, skipped_k, failed_k = reprec_keywords.countoutcomes()
      # assert passed_k == 2
      # assert skipped_k == failed_k == 0
  
  
  def test_addmarker_order():
      node = Node("Test", config=mock.Mock(), session=mock.Mock(), nodeid="Test")
      node.add_marker("foo")
      node.add_marker("bar")
      node.add_marker("baz", append=False)
      extracted = [x.name for x in node.iter_markers()]
      assert extracted == ["baz", "foo", "bar"]
  
  
  @pytest.mark.filterwarnings("ignore")
  def test_markers_from_parametrize(testdir):
      """#3605"""
      testdir.makepyfile(
          """
          import pytest
  
          first_custom_mark = pytest.mark.custom_marker
          custom_mark = pytest.mark.custom_mark
          @pytest.fixture(autouse=True)
          def trigger(request):
              custom_mark = list(request.node.iter_markers('custom_mark'))
              print("Custom mark %s" % custom_mark)
  
          @custom_mark("custom mark non parametrized")
          def test_custom_mark_non_parametrized():
              print("Hey from test")
  
          @pytest.mark.parametrize(
              "obj_type",
              [
                  first_custom_mark("first custom mark")("template"),
                  pytest.param( # Think this should be recommended way?
                      "disk",
                      marks=custom_mark('custom mark1')
                  ),
                  custom_mark("custom mark2")("vm"),  # Tried also this
              ]
          )
          def test_custom_mark_parametrized(obj_type):
              print("obj_type is:", obj_type)
      """
      )
  
      result = testdir.runpytest()
      result.assert_outcomes(passed=4)
  
  
  def test_pytest_param_id_requires_string():
      with pytest.raises(TypeError) as excinfo:
          pytest.param(id=True)
      msg, = excinfo.value.args
      assert msg == "Expected id to be a string, got <class 'bool'>: True"
  
  
  @pytest.mark.parametrize("s", (None, "hello world"))
  def test_pytest_param_id_allows_none_or_string(s):
      assert pytest.param(id=s)
  