text: # Pylint doesn't play well with fixtures and dependency injection from pytest
  # pylint: disable=redefined-outer-name
  
  #
  # This test case tests the failure modes of loading a plugin
  # after it has already been discovered via it's origin.
  #
  
  import os
  import shutil
  import pytest
  
  from buildstream.exceptions import ErrorDomain, LoadErrorReason
  from buildstream.testing import cli  # pylint: disable=unused-import
  from buildstream import _yaml
  
  
  DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "loading")
  
  
  def update_project(project_path, updated_configuration):
      project_conf_path = os.path.join(project_path, "project.conf")
      project_conf = _yaml.roundtrip_load(project_conf_path)
  
      project_conf.update(updated_configuration)
  
      _yaml.roundtrip_dump(project_conf, project_conf_path)
  
  
  # Sets up the element.bst file so that it requires a source
  # or element plugin.
  #
  def setup_element(project_path, plugin_type, plugin_name):
      element_dir = os.path.join(project_path, "elements")
      element_path = os.path.join(element_dir, "element.bst")
      os.makedirs(element_dir, exist_ok=True)
  
      if plugin_type == "elements":
          element = {"kind": plugin_name}
      else:
          element = {"kind": "manual", "sources": [{"kind": plugin_name}]}
  
      _yaml.roundtrip_dump(element, element_path)
  
  
  # This function is used for pytest skipif() expressions.
  #
  # Tests which require our plugins in tests/plugins/pip-samples need
  # to check if these plugins are installed, they are only guaranteed
  # to be installed when running tox, but not when using pytest directly
  # to test that BuildStream works when integrated in your system.
  #
  def pip_sample_packages():
      import pkg_resources
  
      required = {"sample-plugins"}
      installed = {pkg.key for pkg in pkg_resources.working_set}  # pylint: disable=not-an-iterable
      missing = required - installed
  
      if missing:
          return False
  
      return True
  
  
  SAMPLE_PACKAGES_SKIP_REASON = """
  The sample plugins package used to test pip plugin origins is not installed.
  
  This is usually tested automatically with `tox`, if you are running
  `pytest` directly then you can install these plugins directly using pip.
  
  The plugins are located in the tests/plugins/sample-plugins directory
  of your BuildStream checkout.
  """
  
  
  ####################################################
  #                     Tests                        #
  ####################################################
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_nosetup(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(project, {"plugins": [{"origin": "local", "path": "plugins/nosetup", plugin_type: ["nosetup"]}]})
      setup_element(project, plugin_type, "nosetup")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "missing-setup-function")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_setup_not_function(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project,
          {"plugins": [{"origin": "local", "path": "plugins/setupnotfunction", plugin_type: ["setupnotfunction"]}]},
      )
      setup_element(project, plugin_type, "setupnotfunction")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "setup-is-not-function")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_setup_returns_not_type(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project,
          {
              "plugins": [
                  {"origin": "local", "path": "plugins/setupreturnsnottype", plugin_type: ["setupreturnsnottype"]}
              ]
          },
      )
      setup_element(project, plugin_type, "setupreturnsnottype")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "setup-returns-not-type")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_setup_returns_bad_type(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project,
          {
              "plugins": [
                  {"origin": "local", "path": "plugins/setupreturnsbadtype", plugin_type: ["setupreturnsbadtype"]}
              ]
          },
      )
      setup_element(project, plugin_type, "setupreturnsbadtype")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "setup-returns-bad-type")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_missing_min_version(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project,
          {
              "plugins": [
                  {
                      "origin": "local",
                      "path": os.path.join("plugins", plugin_type, "nominversion"),
                      plugin_type: ["nominversion"],
                  }
              ]
          },
      )
      setup_element(project, plugin_type, "nominversion")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "missing-min-version")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  @pytest.mark.parametrize("plugin", [("badstring"), ("number"), ("dict"), ("list")])
  def test_malformed_min_version(cli, datafiles, plugin_type, plugin):
      project = str(datafiles)
  
      update_project(
          project,
          {
              "plugins": [
                  {
                      "origin": "local",
                      "path": os.path.join("plugins", plugin_type, "malformedminversion"),
                      plugin_type: [plugin],
                  }
              ]
          },
      )
      setup_element(project, plugin_type, plugin)
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "malformed-min-version")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_incompatible_major_version(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project,
          {
              "plugins": [
                  {
                      "origin": "local",
                      "path": os.path.join("plugins", plugin_type, "incompatiblemajor"),
                      plugin_type: ["incompatiblemajor"],
                  }
              ]
          },
      )
      setup_element(project, plugin_type, "incompatiblemajor")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "incompatible-major-version")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_incompatible_minor_version(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project,
          {
              "plugins": [
                  {
                      "origin": "local",
                      "path": os.path.join("plugins", plugin_type, "incompatibleminor"),
                      plugin_type: ["incompatibleminor"],
                  }
              ]
          },
      )
      setup_element(project, plugin_type, "incompatibleminor")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "incompatible-minor-version")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_plugin_not_found(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      setup_element(project, plugin_type, "notfound")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "plugin-not-found")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_plugin_found(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project,
          {
              "plugins": [
                  {"origin": "local", "path": os.path.join("plugins", plugin_type, "found"), plugin_type: ["found"],}
              ]
          },
      )
      setup_element(project, plugin_type, "found")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_success()
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_deprecation_warnings(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project,
          {
              "plugins": [
                  {
                      "origin": "local",
                      "path": os.path.join("plugins", plugin_type, "deprecated"),
                      plugin_type: ["deprecated"],
                  }
              ]
          },
      )
      setup_element(project, plugin_type, "deprecated")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_success()
      assert "Here is some detail." in result.stderr
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_deprecation_warning_suppressed_by_origin(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project,
          {
              "plugins": [
                  {
                      "origin": "local",
                      "path": os.path.join("plugins", plugin_type, "deprecated"),
                      "allow-deprecated": True,
                      plugin_type: ["deprecated"],
                  }
              ]
          },
      )
      setup_element(project, plugin_type, "deprecated")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_success()
      assert "Here is some detail." not in result.stderr
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_deprecation_warning_suppressed_specifically(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project,
          {
              "plugins": [
                  {
                      "origin": "local",
                      "path": os.path.join("plugins", plugin_type, "deprecated"),
                      plugin_type: [{"kind": "deprecated", "allow-deprecated": True}],
                  }
              ]
          },
      )
      setup_element(project, plugin_type, "deprecated")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_success()
      assert "Here is some detail." not in result.stderr
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  @pytest.mark.skipif("not pip_sample_packages()", reason=SAMPLE_PACKAGES_SKIP_REASON)
  def test_pip_origin_load_success(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project, {"plugins": [{"origin": "pip", "package-name": "sample-plugins", plugin_type: ["sample"],}]},
      )
      setup_element(project, plugin_type, "sample")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_success()
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  @pytest.mark.skipif("not pip_sample_packages()", reason=SAMPLE_PACKAGES_SKIP_REASON)
  def test_pip_origin_with_constraints(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project,
          {
              "plugins": [
                  {"origin": "pip", "package-name": "sample-plugins>=1.0,<1.2.5,!=1.1.3", plugin_type: ["sample"],}
              ]
          },
      )
      setup_element(project, plugin_type, "sample")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_success()
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_pip_origin_package_not_found(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project, {"plugins": [{"origin": "pip", "package-name": "not-a-package", plugin_type: ["sample"],}]},
      )
      setup_element(project, plugin_type, "sample")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "package-not-found")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  @pytest.mark.skipif("not pip_sample_packages()", reason=SAMPLE_PACKAGES_SKIP_REASON)
  def test_pip_origin_plugin_not_found(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project, {"plugins": [{"origin": "pip", "package-name": "sample-plugins", plugin_type: ["notfound"],}]},
      )
      setup_element(project, plugin_type, "notfound")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "plugin-not-found")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  @pytest.mark.skipif("not pip_sample_packages()", reason=SAMPLE_PACKAGES_SKIP_REASON)
  def test_pip_origin_version_conflict(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project, {"plugins": [{"origin": "pip", "package-name": "sample-plugins>=1.4", plugin_type: ["sample"],}]},
      )
      setup_element(project, plugin_type, "sample")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "package-version-conflict")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  @pytest.mark.skipif("not pip_sample_packages()", reason=SAMPLE_PACKAGES_SKIP_REASON)
  def test_pip_origin_malformed_constraints(cli, datafiles, plugin_type):
      project = str(datafiles)
  
      update_project(
          project, {"plugins": [{"origin": "pip", "package-name": "sample-plugins>1.4,A", plugin_type: ["sample"],}]},
      )
      setup_element(project, plugin_type, "sample")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "package-malformed-requirement")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_junction_plugin_found(cli, datafiles, plugin_type):
      project = str(datafiles)
      subproject = os.path.join(project, "subproject")
  
      shutil.copytree(os.path.join(project, "plugins"), os.path.join(subproject, "plugins"))
  
      update_project(
          project, {"plugins": [{"origin": "junction", "junction": "subproject-junction.bst", plugin_type: ["found"],}]},
      )
      update_project(
          subproject,
          {
              "plugins": [
                  {"origin": "local", "path": os.path.join("plugins", plugin_type, "found"), plugin_type: ["found"],}
              ]
          },
      )
      setup_element(project, plugin_type, "found")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_success()
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_junction_plugin_not_found(cli, datafiles, plugin_type):
      project = str(datafiles)
      subproject = os.path.join(project, "subproject")
  
      shutil.copytree(os.path.join(project, "plugins"), os.path.join(subproject, "plugins"))
  
      # The toplevel says to search for the "notfound" plugin in the subproject
      #
      update_project(
          project,
          {"plugins": [{"origin": "junction", "junction": "subproject-junction.bst", plugin_type: ["notfound"],}]},
      )
  
      # The subproject only configures the "found" plugin
      #
      update_project(
          subproject,
          {
              "plugins": [
                  {"origin": "local", "path": os.path.join("plugins", plugin_type, "found"), plugin_type: ["found"],}
              ]
          },
      )
      setup_element(project, plugin_type, "notfound")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "junction-plugin-not-found")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_junction_deep_plugin_found(cli, datafiles, plugin_type):
      project = str(datafiles)
      subproject = os.path.join(project, "subproject")
      subsubproject = os.path.join(subproject, "subsubproject")
  
      shutil.copytree(os.path.join(project, "plugins"), os.path.join(subsubproject, "plugins"))
  
      update_project(
          project, {"plugins": [{"origin": "junction", "junction": "subproject-junction.bst", plugin_type: ["found"],}]},
      )
      update_project(
          subproject,
          {"plugins": [{"origin": "junction", "junction": "subsubproject-junction.bst", plugin_type: ["found"],}]},
      )
      update_project(
          subsubproject,
          {
              "plugins": [
                  {"origin": "local", "path": os.path.join("plugins", plugin_type, "found"), plugin_type: ["found"],}
              ]
          },
      )
      setup_element(project, plugin_type, "found")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_success()
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_junction_deep_plugin_not_found(cli, datafiles, plugin_type):
      project = str(datafiles)
      subproject = os.path.join(project, "subproject")
      subsubproject = os.path.join(subproject, "subsubproject")
  
      shutil.copytree(os.path.join(project, "plugins"), os.path.join(subsubproject, "plugins"))
  
      # The toplevel says to search for the "notfound" plugin in the subproject
      #
      update_project(
          project,
          {"plugins": [{"origin": "junction", "junction": "subproject-junction.bst", plugin_type: ["notfound"],}]},
      )
  
      # The subproject says to search for the "notfound" plugin in the subproject
      #
      update_project(
          subproject,
          {"plugins": [{"origin": "junction", "junction": "subsubproject-junction.bst", plugin_type: ["notfound"],}]},
      )
  
      # The subsubproject only configures the "found" plugin
      #
      update_project(
          subsubproject,
          {
              "plugins": [
                  {"origin": "local", "path": os.path.join("plugins", plugin_type, "found"), plugin_type: ["found"],}
              ]
          },
      )
      setup_element(project, plugin_type, "notfound")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "junction-plugin-load-error")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  @pytest.mark.skipif("not pip_sample_packages()", reason=SAMPLE_PACKAGES_SKIP_REASON)
  def test_junction_pip_plugin_found(cli, datafiles, plugin_type):
      project = str(datafiles)
      subproject = os.path.join(project, "subproject")
  
      shutil.copytree(os.path.join(project, "plugins"), os.path.join(subproject, "plugins"))
  
      update_project(
          project,
          {"plugins": [{"origin": "junction", "junction": "subproject-junction.bst", plugin_type: ["sample"],}]},
      )
      update_project(
          subproject, {"plugins": [{"origin": "pip", "package-name": "sample-plugins", plugin_type: ["sample"],}]},
      )
      setup_element(project, plugin_type, "sample")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_success()
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  @pytest.mark.skipif("not pip_sample_packages()", reason=SAMPLE_PACKAGES_SKIP_REASON)
  def test_junction_pip_plugin_version_conflict(cli, datafiles, plugin_type):
      project = str(datafiles)
      subproject = os.path.join(project, "subproject")
  
      shutil.copytree(os.path.join(project, "plugins"), os.path.join(subproject, "plugins"))
  
      update_project(
          project,
          {"plugins": [{"origin": "junction", "junction": "subproject-junction.bst", plugin_type: ["sample"],}]},
      )
      update_project(
          subproject, {"plugins": [{"origin": "pip", "package-name": "sample-plugins>=1.4", plugin_type: ["sample"],}]},
      )
      setup_element(project, plugin_type, "sample")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "junction-plugin-load-error")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_junction_full_path_found(cli, datafiles, plugin_type):
      project = str(datafiles)
      subproject = os.path.join(project, "subproject")
      subsubproject = os.path.join(subproject, "subsubproject")
  
      shutil.copytree(os.path.join(project, "plugins"), os.path.join(subsubproject, "plugins"))
  
      update_project(
          project,
          {
              "plugins": [
                  {
                      "origin": "junction",
                      "junction": "subproject-junction.bst:subsubproject-junction.bst",
                      plugin_type: ["found"],
                  }
              ]
          },
      )
      update_project(
          subsubproject,
          {
              "plugins": [
                  {"origin": "local", "path": os.path.join("plugins", plugin_type, "found"), plugin_type: ["found"],}
              ]
          },
      )
      setup_element(project, plugin_type, "found")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_success()
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize("plugin_type", [("elements"), ("sources")])
  def test_junction_full_path_not_found(cli, datafiles, plugin_type):
      project = str(datafiles)
      subproject = os.path.join(project, "subproject")
      subsubproject = os.path.join(subproject, "subsubproject")
  
      shutil.copytree(os.path.join(project, "plugins"), os.path.join(subsubproject, "plugins"))
  
      # The toplevel says to search for the "notfound" plugin in the subproject
      #
      update_project(
          project,
          {
              "plugins": [
                  {
                      "origin": "junction",
                      "junction": "subproject-junction.bst:subsubproject-junction.bst",
                      plugin_type: ["notfound"],
                  }
              ]
          },
      )
  
      # The subsubproject only configures the "found" plugin
      #
      update_project(
          subsubproject,
          {
              "plugins": [
                  {"origin": "local", "path": os.path.join("plugins", plugin_type, "found"), plugin_type: ["found"],}
              ]
          },
      )
      setup_element(project, plugin_type, "notfound")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.PLUGIN, "junction-plugin-not-found")
  
  
  @pytest.mark.datafiles(DATA_DIR)
  @pytest.mark.parametrize(
      "plugin_type,provenance",
      [("elements", "project.conf [line 12 column 2]"), ("sources", "project.conf [line 12 column 2]")],
  )
  def test_junction_invalid_full_path(cli, datafiles, plugin_type, provenance):
      project = str(datafiles)
  
      shutil.copy(os.path.join(project, "not-found-{}.conf".format(plugin_type)), os.path.join(project, "project.conf"))
      setup_element(project, plugin_type, "notfound")
  
      result = cli.run(project=project, args=["show", "element.bst"])
      result.assert_main_error(ErrorDomain.LOAD, LoadErrorReason.MISSING_FILE)
      assert provenance in result.stderr
  