#!/usr/bin/python

import os
import shutil
import sys
import tempfile

try:
    import subprocess
except ImportError:
    # Redhat 4 doesn't have subprocess. 77 means "skip test"
    sys.exit(77)

# if an exception occurs, this tmpdir won't be cleaned up, this is desired to aid in debugging
tmpdir = tempfile.mkdtemp()
mustache_file = os.path.realpath("../../modules/packages/vendored/yum.mustache")
yum_module = os.path.join(tmpdir, "yum")
shutil.copy(mustache_file, yum_module)

cwd = os.getcwd()
# Will cause the package module to call these instead.
os.environ['CFENGINE_TEST_RPM_CMD'] = cwd + "/mock_rpm"
os.environ['CFENGINE_TEST_YUM_CMD'] = cwd + "/mock_yum"
mock_log = cwd + "/yum_mock_output.log"
os.environ['CFENGINE_TEST_MOCK_LOG'] = mock_log
try:
    os.unlink(mock_log)
except OSError:
    pass

# operation       = file-install/repo-install/etc.
# input           = List of lines to give process on stdin.
# no_lines        = The number of lines that the output should have.
# expected_output = List of chunks that should appear somewhere in output.
# mock_lines      = The number of lines that mock_output should have.
# mock_output     = List of chunks that should appear somewhere in the mock log.
#                   The mock log is the command line(s) that the mock command
#                   was called with.
def check(operation, input, no_lines, expected_output, mock_lines, mock_output):
    process = subprocess.Popen(["/usr/bin/python", yum_module, operation],
                               stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    for line in input:
        process.stdin.write((line + "\n").encode("utf-8"))
    process.stdin.close()

    output = ""
    line_count = 0
    for line in process.stdout:
        output += line.decode("utf-8")
        line_count += 1

    for chunk in expected_output:
        if output.find(chunk) < 0:
            sys.stdout.write("Fail: Does not appear in output: \"" + chunk + "\"\n")
            sys.stdout.write("Actual output:\n\"" + output + "\"\n")
            return False

    if line_count != no_lines:
        sys.stdout.write("Fail: Incorrect number of lines returned: " + str(line_count)
                         + " != " + str(no_lines) + " (expected)\n")
        sys.stdout.write("Actual output:\n\"" + output + "\"\n")
        return False

    output = ""
    line_count = 0
    try:
        log = open(mock_log, "r")
        for line in log:
            output += line
            line_count += 1
        log.close()
    except IOError:
        pass

    for chunk in mock_output:
        if output.find(chunk) < 0:
            sys.stdout.write("Fail: Does not appear in mock log: \"" + chunk + "\"\n")
            sys.stdout.write("Actual output:\n\"" + output + "\"\n")
            return False

    if line_count != mock_lines:
        sys.stdout.write("Fail: Incorrect number of lines in mock log: " + str(line_count)
                         + " != " + str(mock_lines) + " (expected)\n")
        sys.stdout.write("Actual output:\n\"" + output + "\"\n")
        return False

    try:
        os.unlink(mock_log)
    except OSError:
        pass

    return True

assert check("supports-api-version", [], 1, ["1"], 0, [])

assert check("file-install", ["File=/path/to/pkg"], 0, [], 1, ["rpm --quiet --force -U /path/to/pkg"])
assert check("file-install", ["File=/path/to/pkg","File=/path/to/pkg2"], 0, [], 1, ["rpm --quiet --force -U /path/to/pkg /path/to/pkg2"])

assert check("repo-install", ["Name=a\nVersion=1\nArchitecture=x",
                              "Name=b\nArchitecture=y",
                              "Name=c\nVersion=3",
                              "Name=d",
                              "Name=upgrade\nVersion=2",
                              "Name=downgrade\nVersion=1",
                              "Name=disappearing-upgrade\nVersion=2",
                              "Name=arch-x86_64-exists",
                              "Name=arch-i386-exists",
                              "Name=arch-x86_64-exists\nVersion=2",
                              "Name=arch-i386-exists\nVersion=2",
                              ],
             0, [], 38, ["yum --quiet -y install a-1.x b.y c-3 d arch-x86_64-exists.x86_64 arch-i386-exists.i386",
                        "yum --quiet -y upgrade upgrade-2.x86_64",
                        "rpm --quiet -q upgrade-2.x86_64",
                        "yum --quiet -y upgrade downgrade-1.x86_64",
                        "rpm --quiet -q downgrade-1.x86_64",
                        "yum --quiet -y downgrade downgrade-1.x86_64",
                        "yum --quiet -y upgrade disappearing-upgrade-2.x86_64",
                        "rpm --quiet -q disappearing-upgrade-2.x86_64",
                        "yum --quiet -y downgrade disappearing-upgrade-2.x86_64",
                        "rpm --quiet -q disappearing-upgrade-2.x86_64",
                        "yum --quiet -y install disappearing-upgrade-2.x86_64",
                        "yum --quiet -y upgrade arch-x86_64-exists-2.x86_64",
                        "rpm --quiet -q arch-x86_64-exists-2.x86_64",
                        "yum --quiet -y upgrade arch-i386-exists-2.i386",
                        "rpm --quiet -q arch-i386-exists-2.i386"])

assert check("remove", ["Name=a\nVersion=1\nArchitecture=x",
                        "Name=b\nArchitecture=y",
                        "Name=c\nVersion=3",
                        "Name=d"],
             0, [], 1, ["yum --quiet -y remove a-1.x b.y c-3 d"])

assert check("list-updates", [], 18,
             ["Name=yum\nVersion=3.2.29-43.el6_5\nArchitecture=noarch",
              "Name=yum-plugin-security\nVersion=1.1.30-17.el6_5\nArchitecture=noarch",
              "Name=phonon-backend-gstreamer\nVersion=1:4.6.2-28.el6_5\nArchitecture=x86_64",
              "Name=java-1.6.0-openjdk\nVersion=1:1.6.0.0-5.1.13.3.el6_5\nArchitecture=x86_64",
              "Name=firefox\nVersion=24.5.0-1.el5.centos\nArchitecture=i386",
              "Name=firefox\nVersion=24.5.0-1.el5.centos\nArchitecture=x86_64"],
             1, ["yum --quiet -y check-update"])

assert check("list-updates-local", [], 18,
             ["Name=yum\nVersion=3.2.29-43.el6_5\nArchitecture=noarch",
              "Name=yum-plugin-security\nVersion=1.1.30-17.el6_5\nArchitecture=noarch",
              "Name=phonon-backend-gstreamer\nVersion=1:4.6.2-28.el6_5\nArchitecture=x86_64",
              "Name=java-1.6.0-openjdk\nVersion=1:1.6.0.0-5.1.13.3.el6_5\nArchitecture=x86_64",
              "Name=firefox\nVersion=24.5.0-1.el5.centos\nArchitecture=i386",
              "Name=firefox\nVersion=24.5.0-1.el5.centos\nArchitecture=x86_64"],
             1, ["yum --quiet -y -C check-update"])

assert check("list-installed", [], 6,
             ["Name=firefox\nVersion=24.5.0-1.el5.centos\nArchitecture=i386",
              "Name=yum\nVersion=3.2.29-43.el6_5\nArchitecture=noarch"],
             4, ["rpm -qa --qf Name=%{name}\nVersion=%{epoch}:%{version}-%{release}\nArchitecture=%{arch}\n"])

assert check("get-package-data", ["File=/path/to/pkg"], 4,
             ["PackageType=file\nName=file_pkg\nVersion=10.0\nArchitecture=x86_64"],
             4, ["rpm --qf Name=%{name}\nVersion=%{epoch}:%{version}-%{release}\nArchitecture=%{arch}\n -qp /path/to/pkg"])
assert check("get-package-data", ["File=repo_pkg"], 2,
             ["PackageType=repo\nName=repo_pkg"],
             0, [])
assert check("get-package-data", ["File=repo/pkg"], 2,
             ["PackageType=repo\nName=repo/pkg"],
             0, [])
shutil.rmtree(tmpdir)
