text: # Licensed to Elasticsearch B.V. under one or more contributor
  # license agreements. See the NOTICE file distributed with
  # this work for additional information regarding copyright
  # ownership. Elasticsearch B.V. licenses this file to you under
  # the Apache License, Version 2.0 (the "License"); you may
  # not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #	http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing,
  # software distributed under the License is distributed on an
  # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  # KIND, either express or implied.  See the License for the
  # specific language governing permissions and limitations
  # under the License.
  import logging
  import os
  import subprocess
  import tempfile
  import unittest.mock as mock
  from unittest import TestCase
  
  from esrally.utils import io
  
  
  def mock_debian(args, fallback=None):
      if args[0] == "update-alternatives":
          return [
              "/usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java",
              "/usr/lib/jvm/java-7-oracle/jre/bin/java",
              "/usr/lib/jvm/java-8-oracle/jre/bin/java",
              "/usr/lib/jvm/java-9-openjdk-amd64/bin/java"
          ]
      else:
          return fallback
  
  
  def mock_red_hat(path):
      if path == "/etc/alternatives/java_sdk_1.8.0":
          return "/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.91-5.b14.fc23.x86_64"
      else:
          return None
  
  
  class IoTests(TestCase):
      def test_guess_java_home_on_mac_os_x(self):
          def runner(return_value):
              return lambda args, fallback=None: [return_value]
  
          java_home = io.guess_java_home(major_version=8, runner=runner("/Library/Java/JavaVirtualMachines/jdk1.8.0_74.jdk/Contents/Home"))
          self.assertEqual("/Library/Java/JavaVirtualMachines/jdk1.8.0_74.jdk/Contents/Home", java_home)
  
          java_home = io.guess_java_home(major_version=9, runner=runner("/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home"))
          self.assertEqual("/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home", java_home)
  
      def test_guess_java_home_on_debian(self):
          self.assertEqual("/usr/lib/jvm/java-9-openjdk-amd64", io.guess_java_home(major_version=9, runner=mock_debian))
          self.assertEqual("/usr/lib/jvm/java-8-oracle", io.guess_java_home(major_version=8, runner=mock_debian))
          self.assertEqual("/usr/lib/jvm/java-7-openjdk-amd64", io.guess_java_home(major_version=7, runner=mock_debian))
  
      @mock.patch("os.path.isdir")
      @mock.patch("os.path.islink")
      def test_guess_java_home_on_redhat(self, islink, isdir):
          def runner(args, fallback=None):
              return ["Unknown option --list"] if args[0] == "update-alternatives" else None
  
          islink.return_value = False
          isdir.return_value = True
  
          self.assertEqual("/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.91-5.b14.fc23.x86_64",
                           io.guess_java_home(major_version=8, runner=runner, read_symlink=mock_red_hat))
          # simulate not installed version
          self.assertIsNone(io.guess_java_home(major_version=7, runner=runner, read_symlink=mock_red_hat))
  
      def test_normalize_path(self):
          self.assertEqual("/already/a/normalized/path", io.normalize_path("/already/a/normalized/path"))
          self.assertEqual("/not/normalized", io.normalize_path("/not/normalized/path/../"))
          self.assertEqual(os.path.expanduser("~"), io.normalize_path("~/Documents/.."))
  
      def test_archive(self):
          self.assertTrue(io.is_archive("/tmp/some-archive.tar.gz"))
          self.assertTrue(io.is_archive("/tmp/some-archive.tgz"))
          # Rally does not recognize .7z
          self.assertFalse(io.is_archive("/tmp/some-archive.7z"))
          self.assertFalse(io.is_archive("/tmp/some.log"))
          self.assertFalse(io.is_archive("some.log"))
  
      def test_has_extension(self):
          self.assertTrue(io.has_extension("/tmp/some-archive.tar.gz", ".tar.gz"))
          self.assertFalse(io.has_extension("/tmp/some-archive.tar.gz", ".gz"))
          self.assertTrue(io.has_extension("/tmp/text.txt", ".txt"))
          # no extension whatsoever
          self.assertFalse(io.has_extension("/tmp/README", "README"))
  
  
  class TestDecompression:
      def test_decompresses_supported_file_formats(self):
          for ext in ["zip", "gz", "bz2", "tgz", "tar.bz2", "tar.gz"]:
              tmp_dir = tempfile.mkdtemp()
              archive_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources", f"test.txt.{ext}")
              decompressed_path = os.path.join(tmp_dir, "test.txt")
  
              io.decompress(archive_path, target_directory=tmp_dir)
  
              assert os.path.exists(decompressed_path) is True,\
                  f"Could not decompress [{archive_path}] to [{decompressed_path}] (target file does not exist)"
              assert self.read(decompressed_path) == "Sample text for DecompressionTests\n",\
                  f"Could not decompress [{archive_path}] to [{decompressed_path}] (target file is corrupt)"
  
      @mock.patch.object(io, "is_executable", return_value=False)
      def test_decompresses_supported_file_formats_with_lib_as_failover(self, mocked_is_executable):
          for ext in ["zip", "gz", "bz2", "tgz", "tar.bz2", "tar.gz"]:
              tmp_dir = tempfile.mkdtemp()
              archive_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources", f"test.txt.{ext}")
              decompressed_path = os.path.join(tmp_dir, "test.txt")
  
              logger = logging.getLogger("esrally.utils.io")
              with mock.patch.object(logger, "warning") as mocked_console_warn:
                  io.decompress(archive_path, target_directory=tmp_dir)
  
                  assert os.path.exists(decompressed_path) is True,\
                      f"Could not decompress [{archive_path}] to [{decompressed_path}] (target file does not exist)"
                  assert self.read(decompressed_path) == "Sample text for DecompressionTests\n",\
                      f"Could not decompress [{archive_path}] to [{decompressed_path}] (target file is corrupt)"
  
              if ext in ["bz2", "gz"]:
                  assert f"not found in PATH. Using standard library, decompression will take longer." in mocked_console_warn.call_args[0][0]
  
      @mock.patch("subprocess.run")
      def test_decompress_manually_external_fails_if_tool_missing(self, mocked_run):
          base_path_without_extension = "corpus"
          archive_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources", "test.txt.bz2")
          tmp_dir = tempfile.mkdtemp()
          decompressor_bin = "pbzip2"
          decompress_cmd = f"{decompressor_bin} -d -k -m10000 -c ${archive_path}"
          stderr_msg = "Error details here"
          expected_err = "Failed to decompress [%s] with [%s]. Error [%s]. Falling back to standard library."
          mocked_run.side_effect = subprocess.CalledProcessError(cmd=decompress_cmd, returncode=1, stderr=stderr_msg)
  
          logger = logging.getLogger("esrally.utils.io")
          with mock.patch.object(logger, "warning") as mocked_warn_logger:
              result = io._do_decompress_manually_external(tmp_dir, archive_path, base_path_without_extension, [decompressor_bin])
  
          mocked_warn_logger.assert_called_once_with(expected_err, archive_path, decompress_cmd, stderr_msg)
          assert result is False
  
      def read(self, f):
          with open(f, 'r') as content_file:
              return content_file.read()
  