text: # coding=utf-8
  # Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
  # Licensed under the Apache License, Version 2.0 (see LICENSE).
  
  from __future__ import absolute_import, division, print_function, unicode_literals
  
  import os
  import re
  from builtins import open
  
  from pants.base.build_environment import get_buildroot
  from pants.util.contextutil import open_zip, temporary_dir
  from pants.util.dirutil import safe_rmtree
  from pants_test.pants_run_integration_test import PantsRunIntegrationTest
  
  
  def shared_artifacts(version, extra_jar=None):
    published_file_list = ['ivy-{0}.xml'.format(version),
                           'hello-greet-{0}.jar'.format(version),
                           'hello-greet-{0}.pom'.format(version),
                           'hello-greet-{0}-sources.jar'.format(version)]
    if extra_jar:
      published_file_list.append(extra_jar)
    return {'org/pantsbuild/testproject/publish/hello-greet/{0}'.format(version): published_file_list}
  
  
  # TODO: Right now some options are set via config and some via cmd-line flags. Normalize this?
  def publish_extra_config(unique_config):
    return {
      'GLOBAL': {
        # Turn off --verify-config as some scopes in pants.ini will not be
        # recognized due to the select few backend packages.
        'verify_config': False,
        'pythonpath': [
          'examples/src/python',
          'pants-plugins/src/python',
        ],
        'backend_packages': [
          'example.pants_publish_plugin',
          'internal_backend.repositories',
          'pants.backend.codegen',
          'pants.backend.jvm',
        ],
      },
      'publish.jar': {
        'publish_extras': {
          'extra_test_jar_example': unique_config,
        },
      },
    }
  
  
  class JarPublishIntegrationTest(PantsRunIntegrationTest):
    GOLDEN_DATA_DIR = 'tests/python/pants_test/tasks/jar_publish_resources/'
  
    # This is where all pushdb properties files will end up.
    @property
    def pushdb_root(self):
      return os.path.join(get_buildroot(), 'testprojects', 'ivy', 'pushdb')
  
    def setUp(self):
      # This attribute is required to see the full diff between ivy and pom files.
      self.maxDiff = None
      safe_rmtree(self.pushdb_root)
  
    def tearDown(self):
      safe_rmtree(self.pushdb_root)
  
    def test_scala_publish(self):
      unique_artifacts = {'org/pantsbuild/testproject/publish/jvm-example-lib_2.11/0.0.1-SNAPSHOT':
                          ['ivy-0.0.1-SNAPSHOT.xml',
                           'jvm-example-lib_2.11-0.0.1-SNAPSHOT.jar',
                           'jvm-example-lib_2.11-0.0.1-SNAPSHOT.pom',
                           'jvm-example-lib_2.11-0.0.1-SNAPSHOT-sources.jar'],
                          'org/pantsbuild/testproject/publish/hello/welcome_2.11/0.0.1-SNAPSHOT':
                          ['ivy-0.0.1-SNAPSHOT.xml',
                           'welcome_2.11-0.0.1-SNAPSHOT.jar',
                           'welcome_2.11-0.0.1-SNAPSHOT.pom',
                           'welcome_2.11-0.0.1-SNAPSHOT-sources.jar']}
      self.publish_test('testprojects/src/scala/org/pantsbuild/testproject/publish'
                        ':jvm-run-example-lib',
                        dict(list(unique_artifacts.items()) + list(shared_artifacts('0.0.1-SNAPSHOT').items())),
                        ['org.pantsbuild.testproject.publish/hello-greet/publish.properties',
                         'org.pantsbuild.testproject.publish/jvm-example-lib_2.11/publish.properties',
                         'org.pantsbuild.testproject.publish.hello/welcome_2.11/publish.properties'],
                        extra_options=['--doc-scaladoc-skip'],
                        assert_publish_config_contents=True)
  
    def test_java_publish(self):
      self.publish_test('testprojects/src/java/org/pantsbuild/testproject/publish/hello/greet',
                        shared_artifacts('0.0.1-SNAPSHOT'),
                        ['org.pantsbuild.testproject.publish/hello-greet/publish.properties'],)
  
    def test_protobuf_publish(self):
      unique_artifacts = {'org/pantsbuild/testproject/publish/protobuf/protobuf-java/0.0.1-SNAPSHOT':
                          ['ivy-0.0.1-SNAPSHOT.xml',
                           'protobuf-java-0.0.1-SNAPSHOT.jar',
                           'protobuf-java-0.0.1-SNAPSHOT.pom',
                           'protobuf-java-0.0.1-SNAPSHOT-sources.jar'],
                          'org/pantsbuild/testproject/protobuf/distance/0.0.1-SNAPSHOT/':
                          ['ivy-0.0.1-SNAPSHOT.xml',
                           'distance-0.0.1-SNAPSHOT.jar',
                           'distance-0.0.1-SNAPSHOT.pom',
                           'distance-0.0.1-SNAPSHOT-sources.jar']}
      self.publish_test('testprojects/src/java/org/pantsbuild/testproject/publish/protobuf'
                        ':protobuf-java',
                        unique_artifacts,
                        ['org.pantsbuild.testproject.publish.protobuf/protobuf-java/'
                         'publish.properties',
                         'org.pantsbuild.testproject.protobuf/distance/publish.properties'],
                        extra_options=['--doc-javadoc-skip'])
  
    def test_named_snapshot(self):
      name = "abcdef0123456789"
      self.publish_test('testprojects/src/java/org/pantsbuild/testproject/publish/hello/greet',
                        shared_artifacts(name),
                        ['org.pantsbuild.testproject.publish/hello-greet/publish.properties'],
                        extra_options=['--named-snapshot={}'.format(name)])
  
    def test_publish_override_flag_succeeds(self):
      override = "com.twitter.foo#baz=0.1.0"
      self.publish_test('testprojects/src/java/org/pantsbuild/testproject/publish/hello/greet',
                        shared_artifacts('0.0.1-SNAPSHOT'),
                        ['org.pantsbuild.testproject.publish/hello-greet/publish.properties'],
                        extra_options=['--override={}'.format(override)])
  
    # Collect all the common factors for running a publish_extras test, and execute the test.
    def publish_extras_runner(self, extra_config=None, artifact_name=None, success_expected=True):
      self.publish_test('testprojects/src/java/org/pantsbuild/testproject/publish/hello/greet',
                        shared_artifacts('0.0.1-SNAPSHOT', artifact_name),
                        ['org.pantsbuild.testproject.publish/hello-greet/publish.properties'],
                        extra_options=['--doc-javadoc-skip'],
                        extra_config=extra_config,
                        success_expected=success_expected)
    #
    # Run through all the permutations of the config parameters for publish_extras.
    #
  
    def test_publish_extras_name_classifier(self):
      self.publish_extras_runner(extra_config=publish_extra_config({
                                  'override_name': '{target_provides_name}-extra_example',
                                  'classifier': 'classy',
                                  }),
                                 artifact_name='hello-greet-extra_example-0.0.1-SNAPSHOT-classy.jar')
  
    def test_publish_extras_name(self):
      self.publish_extras_runner(extra_config=publish_extra_config({
                                  'override_name': '{target_provides_name}-extra_example',
                                  }),
                                 artifact_name='hello-greet-extra_example-0.0.1-SNAPSHOT.jar')
  
    def test_publish_extras_name_extension(self):
      self.publish_extras_runner(extra_config=publish_extra_config({
                                  'override_name': '{target_provides_name}-extra_example',
                                  'extension': 'zip'
                                  }),
                                 artifact_name='hello-greet-extra_example-0.0.1-SNAPSHOT.zip')
  
    def test_publish_extras_extension(self):
      self.publish_extras_runner(extra_config=publish_extra_config({
                                  'extension': 'zip'
                                  }),
                                 artifact_name='hello-greet-0.0.1-SNAPSHOT.zip')
  
    def test_publish_extras_extension_classifier(self):
      self.publish_extras_runner(extra_config=publish_extra_config({
                                  'classifier': 'classy',
                                  'extension': 'zip'
                                  }),
                                 artifact_name='hello-greet-0.0.1-SNAPSHOT-classy.zip')
  
    def test_publish_extras_classifier(self):
      self.publish_extras_runner(extra_config=publish_extra_config({
                                  'classifier': 'classy',
                                  }),
                                 artifact_name='hello-greet-0.0.1-SNAPSHOT-classy.jar')
  
    # This test doesn't specify a proper set of parameters that uniquely name the extra artifact, and
    # should fail with an error from pants.
    def test_publish_extras_invalid_args(self):
      self.publish_extras_runner(extra_config=publish_extra_config({
                                  'extension': 'jar',
                                  }),
                                 artifact_name='hello-greet-0.0.1-SNAPSHOT.jar',
                                 success_expected=False)
  
    def test_scala_publish_classifiers(self):
      self.publish_test('testprojects/src/scala/org/pantsbuild/testproject/publish/classifiers',
                        dict({
                          'org/pantsbuild/testproject/publish/classifiers_2.11/0.0.1-SNAPSHOT': [
                            'classifiers_2.11-0.0.1-SNAPSHOT.pom',
                            'ivy-0.0.1-SNAPSHOT.xml',
                          ]}),
                        [],
                        assert_publish_config_contents=True)
  
    def test_override_via_coord(self):
      self.publish_test(
        target='testprojects/src/scala/org/pantsbuild/testproject/publish/classifiers',
        artifacts=dict({'org/pantsbuild/testproject/publish/classifiers_2.11/1.2.3-SNAPSHOT': [
                          'classifiers_2.11-1.2.3-SNAPSHOT.pom',
                          'ivy-1.2.3-SNAPSHOT.xml',
                        ]}),
        pushdb_files=[],
        extra_options=['--override=org.pantsbuild.testproject.publish#classifiers_2.11=1.2.3'],
        assert_publish_config_contents=True)
  
    def test_override_via_address(self):
      target = 'testprojects/src/scala/org/pantsbuild/testproject/publish/classifiers'
      self.publish_test(
        target=target,
        artifacts=dict({'org/pantsbuild/testproject/publish/classifiers_2.11/1.2.3-SNAPSHOT': [
          'classifiers_2.11-1.2.3-SNAPSHOT.pom',
          'ivy-1.2.3-SNAPSHOT.xml',
        ]}),
        pushdb_files=[],
        extra_options=['--override={}=1.2.3'.format(target)],
        assert_publish_config_contents=True)
  
    def test_invalidate_resources(self):
      """Tests that resource changes invalidate publishes."""
      source_root = 'testprojects/src/java'
      target_relative_to_sourceroot = 'org/pantsbuild/testproject/publish/hello/greet'
      target = os.path.join(source_root, target_relative_to_sourceroot)
      resource_relative_to_sourceroot = os.path.join(target_relative_to_sourceroot, 'TEMP.txt')
      resource = os.path.join(source_root, resource_relative_to_sourceroot)
  
      with self.temporary_workdir() as workdir:
        def publish(resource_content):
          with temporary_dir() as publish_dir:
            with self.temporary_file_content(resource, resource_content):
              # Validate that the target depends on the relevant resource.
              self.assertIn(resource, self.run_pants(['filedeps', target]).stdout_data)
  
              pants_run = self.run_pants_with_workdir(['publish.jar',
                                                       '--local={}'.format(publish_dir),
                                                       '--named-snapshot=X',
                                                       '--no-dryrun',
                                                       target
                                                      ],
                                                      workdir=workdir)
              self.assert_success(pants_run)
            # Validate that the content in the resulting jar matches.
            jar = os.path.join(publish_dir,
                               'org/pantsbuild/testproject/publish/hello-greet/X/hello-greet-X.jar')
            with open_zip(jar, mode='r') as j:
              with j.open(resource_relative_to_sourceroot) as jar_entry:
                self.assertEqual(resource_content, jar_entry.read())
  
        # Publish the same target twice with different resource content.
        publish('one')
        publish('two')
  
    def publish_test(self, target, artifacts, pushdb_files, extra_options=None, extra_config=None,
                     extra_env=None, success_expected=True, assert_publish_config_contents=False):
      """Tests that publishing the given target results in the expected output.
  
      :param target: Target to test.
      :param artifacts: A map from directories to a list of expected filenames.
      :param pushdb_files: list of pushdb files that would be created if this weren't a local publish
      :param extra_options: Extra command-line options to the pants run.
      :param extra_config: Extra pants.ini configuration for the pants run.
      :param extra_env: Extra environment variables for the pants run.
      :param assert_publish_config_contents: Test the contents of the generated ivy and pom file.
             If set to True, compares the generated ivy.xml and pom files in
             tests/python/pants_test/tasks/jar_publish_resources/<package_name>/<artifact_name>/
      """
  
      with temporary_dir() as publish_dir:
        options = ['--local={}'.format(publish_dir),
                   '--no-dryrun',
                   '--force']
        if extra_options:
          options.extend(extra_options)
  
        pants_run = self.run_pants(['publish.jar'] + options + [target], config=extra_config,
                                   extra_env=extra_env)
        if success_expected:
          self.assert_success(pants_run, "'pants goal publish' expected success, but failed instead.")
        else:
          self.assert_failure(pants_run,
                              "'pants goal publish' expected failure, but succeeded instead.")
          return
  
        # New pushdb directory should be created for all artifacts.
        for pushdb_file in pushdb_files:
          pushdb_dir = os.path.dirname(os.path.join(self.pushdb_root, pushdb_file))
          self.assertTrue(os.path.exists(pushdb_dir))
  
        # But because we are doing local publishes, no pushdb files are created
        for pushdb_file in pushdb_files:
          self.assertFalse(os.path.exists(os.path.join(self.pushdb_root, pushdb_file)))
  
        for directory, artifact_list in artifacts.items():
          for artifact in artifact_list:
            artifact_path = os.path.join(publish_dir, directory, artifact)
            self.assertTrue(os.path.exists(artifact_path))
            if assert_publish_config_contents:
              if artifact.endswith('xml') or artifact.endswith('pom'):
                self.compare_file_contents(artifact_path, directory)
  
    def compare_file_contents(self, artifact_path, directory):
      """
      Tests the ivy.xml and pom
      :param artifact_path: Path of the artifact
      :param directory: Directory where the artifact resides.
      :return:
      """
      # Strip away the version number
      [package_dir, artifact_name, version] = directory.rsplit(os.path.sep, 2)
      file_name = os.path.basename(artifact_path)
      golden_file_nm = os.path.join(JarPublishIntegrationTest.GOLDEN_DATA_DIR,
                                    package_dir.replace(os.path.sep, '.'), artifact_name, file_name)
      with open(artifact_path, 'r') as test_file:
        generated_file = test_file.read()
        with open(golden_file_nm, 'r') as golden_file:
          golden_file_contents = golden_file.read()
          # Remove the publication sha attribute from ivy.xml
          if artifact_path.endswith('.xml'):
            generated_file = re.sub(r'publication=.*', '/>', generated_file)
        return self.assertMultiLineEqual(generated_file, golden_file_contents)
  