#!/usr/bin/python  -tt
# vim: ai ts=4 sts=4 et sw=4
#
# This program is free software
#
# Copyright (C) 2010, 2011, 2012, 2013, 2014 Intel, Inc.
#
#    This program is free software; you can redistribute it and/or
#    modify it under the terms of the GNU General Public License
#    as published by the Free Software Foundation; version 2 of the License.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#

"""Tests for GitRepository func"""

import unittest
import types
import os
import shutil
import subprocess
import gbp.log as log
from collections import OrderedDict
from gbp.git.repository import GitRepository
from gbp.rpm import SpecFile


SPEC_TEST = """Name:    GBP-test\nSummary:    gbp SpecFile test\n
Version:    1.0\nRelease:    1\nLicense:    GPL v2\n
Source:    %{name}_%{version}.tar.gz\n
%description\n
gbp interface test about SpecFile
"""

class GitRepositoryTest(unittest.TestCase):
    """Tests for GitRepository func
    """
    def test_gbp_log(self):
        """test setup from gbp.log
        """
        log.setup('auto', log.DEBUG)
        auto_dict = OrderedDict(log.LOGGER.__dict__)
        self.assertEqual(auto_dict.get('level'), 10)

    def test_rev_parse(self):
        """Test rev_parse output
        """
        # create a git project
        prjdir = tempdir()
        mygit = GitRepository(prjdir)
        # get all tag names from git project
        tag_names = get_tag_names(prjdir, os.getcwd())
        # get the SHA` of specified tag
        sid = get_sign(prjdir, os.getcwd(), tag_names[0])
        # compare
        self.assertEqual(sid, mygit.rev_parse(tag_names[0]))
        shutil.rmtree(prjdir)

    def test_get_commit_info(self):
        """Test get_commit_info return data
        """
        # create a git project
        prjdir = tempdir()
        mygit = GitRepository(prjdir)
        # get the SHA` of specified tag
        sid = get_sign(prjdir, os.getcwd(), 'HEAD')
        # return the info of spcified sid
        commit = mygit.get_commit_info(sid)
        for k in commit:
            if not type(commit[k]) is types.StringType:
                commit[k] = str(type(commit[k]))
        self.assertEqual(commit,
                         OrderedDict({'body': '',
                'committer': "<class 'gbp.git.modifier.GitModifier'>",
                'author': "<class 'gbp.git.modifier.GitModifier'>",
                'patchname': '2.txt',
                'id': sid,
                'files': "<type 'collections.defaultdict'>",
                'subject': '2.txt'})
        )
        shutil.rmtree(prjdir)

    def test_checkout(self):
        """Test checkout() method
        """
        # create a git project
        prjdir = tempdir()
        mygit = GitRepository(prjdir)
        # get all tag names from git project
        tag_names = get_tag_names(prjdir, os.getcwd())
        # create a branch from the specified tag
        with Workspace(prjdir, os.getcwd()):
            mygit.checkout(tag_names[0])
            tag_commit = mygit.head
            runner('git checkout -b %s %s'\
                    % (tag_names[0], tag_names[0]), back = False)
            branch_commit = mygit.head
            runner('git checkout master', back = False)
        self.assertEqual(tag_commit, branch_commit)
        shutil.rmtree(prjdir)

    def test_clean(self):
        """Test clean() method
        """
        # create a git project
        prjdir = tempdir()
        mygit = GitRepository(prjdir)
        with Workspace(prjdir, os.getcwd()):
            old_list = os.listdir(prjdir)
            runner('mkdir test_clean', back = False)
            mygit.clean(directories = True, force = True)
            new_list = os.listdir(prjdir)
        self.assertEqual(old_list, new_list)
        shutil.rmtree(prjdir)


    def test_find_tag(self):
        """Test the return of describe
        """
        # create a git project
        prjdir = tempdir()
        mygit = GitRepository(prjdir)
        # get all names from git project
        tag_names = get_tag_names(prjdir, os.getcwd())
        # get info of find_tag
        find_tag = mygit.find_tag(tag_names[0])
        with Workspace(prjdir, os.getcwd()):
            output = runner('git describe --abbrev=0 %s'% tag_names[0])
        self.assertEqual(output.communicate()[0].split('\n')[0], find_tag)
        shutil.rmtree(prjdir)

    def test_describe(self):
        """Test the return of describe
        """
        # create a git project
        prjdir = tempdir()
        mygit = GitRepository(prjdir)
        # get all names from git project
        tag_names = get_tag_names(prjdir, os.getcwd())
        # get info of describe
        describe = mygit.describe(tag_names[0], \
                                  pattern = 'submit/*', \
                                  exact_match = True)
        with Workspace(prjdir, os.getcwd()):
            output = runner(
                   '''git describe --match "submit/*" --exact-match %s''' \
                    % tag_names[0])
        self.assertEqual(output.communicate()[0].split('\n')[0], describe)
        shutil.rmtree(prjdir)

    def test_git_getoutput(self):
        """Test the return of git_getoutput
        """
        # create a git project
        prjdir = tempdir()
        mygitout = GitgetoutputTest(prjdir)
        # get all names from git project
        tag_names = get_tag_names(prjdir, os.getcwd())
        # get the result of git_getoutput and result of assumed
        out_show, _ret_show = mygitout.getoutput_show(tag_names[0])
        output_show = mygitout.getoutput('git show -s --pretty=format:### %s'\
                                        % tag_names[0], prjdir)
        out_branch, _ret_branch = mygitout.getoutput_branch(tag_names[0])
        output_branch = mygitout.getoutput('git branch -a --contains %s'\
                                        % tag_names[0], prjdir)
        # compare
        self.assertEqual(out_show, output_show)
        self.assertEqual(out_branch, output_branch)
        shutil.rmtree(prjdir)

    def test_specfile(self):
        """test specFile from gbp.rpm
        """
        prjdir = tempdir()
        with Workspace(prjdir, os.getcwd()):
            runner('mkdir packaging', back = False)
            with Workspace('./packaging', os.getcwd()):
                with open('Jenkins_gbp_test.spec', 'w') as spec_file:
                    spec_file.write(SPEC_TEST)
                runner('git add .', back = False)
                runner('git commit -m"Test gbp SpecFile"', back = False)
            filename = os.path.join(prjdir, 'packaging/Jenkins_gbp_test.spec')
        spec = SpecFile(filename)
        self.assertEqual('GBP-test', spec.name)
        shutil.rmtree(prjdir)

    def test_get_remote_repo(self):
        """Test the return of get_remote_repo method
        """
        # create a bare project
        create_bare_git()
        mygit = GitRepository('/tmp/mygit')
        # return info of get_remote_repos
        remote_info = mygit.get_remote_repos()
        # return the assumd result
        with Workspace('/tmp/mygit', os.getcwd()):
            output = runner('git remote -v').communicate()[0]
        remotes = {}
        for rem in output.splitlines():
            name, url_type = rem.split('\t', 1)
            url, urltype = url_type.rsplit(' ', 1)
            urltype = urltype[1].strip('()')
            if not name in remotes:
                remotes[name] = []
            if urltype == 'fetch':
                remotes[name][0] = url
            else:
                remotes[name].append(url)
        self.assertEqual(remote_info, remotes)
        shutil.rmtree('/tmp/myproject.git')
        shutil.rmtree('/tmp/mygit')
        shutil.rmtree('/tmp/mygit-pull')

    @staticmethod
    def test_push():
        """Test push
        """
        # create a bare project and clone git project to local dir
        create_bare_git()
        mygit = GitRepository('/tmp/mygit')
        # three methods for pushing
        mygit.push(repo = 'origin', src = 'submit/1.txt', 
                    tags = True, force = True)
        mygit.push(repo = 'origin', src = 'master', 
                    dst = 'refs/heads/master', 
                    tags = True, force = True)
        mygit.push(repo = 'origin', src = ':submit/1.txt')
        shutil.rmtree('/tmp/myproject.git')
        shutil.rmtree('/tmp/mygit')
        shutil.rmtree('/tmp/mygit-pull')

    @staticmethod
    def test_pull():
        """Test pull
        """
        # create a bare project and clone git project to local dir
        create_bare_git()
        mygit = GitRepository('/tmp/mygit')
        # push mygit to myproject.git
        mygit.push(repo = 'origin', src = 'master', 
                   dst = 'refs/heads/master', 
                   tags = True, force = True)
        # pull from bare to mygit-pull
        mygit_pull = GitRepository('/tmp/mygit-pull')
        mygit_pull.fetch(tags = True)
        mygit_pull.pull()
        # pull in bare
        mygit_bare = GitRepository('/tmp/myproject.git')
        mygit_bare.fetch(all_remotes = True)
        shutil.rmtree('/tmp/myproject.git')
        shutil.rmtree('/tmp/mygit')
        shutil.rmtree('/tmp/mygit-pull')

def create_bare_git():
    """create a bare project
       clone git project to lcoal git
    """
    with Workspace('/tmp', os.getcwd()):
        runner('git init --bare myproject.git', back = False)
    # clone git project to lcoal dir
    runner('git clone /tmp/myproject.git /tmp/mygit', back = False)
    runner('git clone /tmp/myproject.git /tmp/mygit-pull', back = False)
    with Workspace('/tmp/mygit', os.getcwd()):
        with open('1.txt', 'w') as tmp_file:
            tmp_file.write('afdadfdfa')
        runner('git add 1.txt', back = False)
        runner('git commit -m"pull.txt"', back = False)
        runner('git tag "submit/1.txt" -m"submit/1.txt"', back = False)

def runner(command, back = True):
    """Wrapper for most of subprocess
       input:
           if back:
               only stdout
           else:
               both stdout and stderr
       return output
    """
    if back:
        return subprocess.Popen(command, stdout = subprocess.PIPE,
                    stderr = subprocess.STDOUT,
                    shell = True)
    else:
        return subprocess.Popen(command, 
                    shell = True, 
                    stdout = subprocess.PIPE).wait()

def get_sign(prjdir, current_dir, sign_type):
    """return the SHA1 of the sign_type
        sign_type is the tag name
    """
    with Workspace(prjdir, current_dir):
        output = runner('git rev-parse %s'% sign_type)
        sid = output.communicate()[0].split('\n')[0]
    return sid

def get_tag_names(prjdir, current_dir):
    """return the lists of all tag names
    """
    with Workspace(prjdir, current_dir):
        output = runner('git tag')
        tag_names = output.communicate()[0].split('\n')
        tag_names.pop()
    return tag_names

def tempdir():
    """create a temporary git project
    """
    num = 1
    tmpdir = '/tmp/Jenkins_gbp_test%s'% num
    while True:
        tmpdir = '/tmp/Jenkins_gbp_test%s'% num
        num += 1
        if os.path.exists(tmpdir):
            continue
        else:
            os.makedirs(tmpdir)
            break
    with Workspace(tmpdir, os.getcwd()):
        runner('git init', back = False)
        for num in range(1, 3):
            file_name = '%s.txt'% num
            tag_name = 'submit/%s'% file_name
            tmp_file = os.path.join(tmpdir, file_name)
            with open(tmp_file, 'w') as tmp_file:
                tmp_file.write('abcdadfad')
            runner('git add %s'% file_name, back = False)
            runner('git commit -m"%s"'% file_name, back = False)
            runner('git tag "%s" -m "%s"'% (tag_name, tag_name), back = False)
    return tmpdir

class Workspace():
    """Turn to the directory
    """
    def __init__(self, tmp_path, path):
        self.path = path
        self.tmp_path = tmp_path

    def __enter__(self):
        if self.path != self.tmp_path:
            os.chdir(self.tmp_path)

    def __exit__(self, exception_type, exception_val, trace):
        os.chdir(self.path)

class GitgetoutputTest(GitRepository):
    """Wrapper the private method for git_getoutput
    """
    def getoutput_show(self, tag_name):
        """Return the result for git-show
        """
        out, ret = self._git_getoutput('show', \
                                       ['-s', '--pretty=format:###', tag_name])
        return out, ret

    def getoutput_branch(self, tag_name):
        """Return the result for git-branch
        """
        out, ret = self._git_getoutput('branch', ['-a', '--contains', tag_name])
        return out, ret

    @staticmethod
    def getoutput(command, prjdir):
        """Return the result
        """
        output = []
        with Workspace(prjdir, os.getcwd()):
            run = runner(command)
        while run.poll() == None:
            output += run.stdout.readlines()
        output += run.stdout.readlines()
        return output
