#!/usr/bin/python
# vim:ts=4:sw=4:expandtab

# 
# Copyright (C) 2009 Red Hat, Inc.
#
# This file is part of IcedTea.
#
# IcedTea 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.
#
# IcedTea 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 IcedTea; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA.
#
# Linking this library statically or dynamically with other modules is
# making a combined work based on this library.  Thus, the terms and
# conditions of the GNU General Public License cover the whole
# combination.
#
# As a special exception, the copyright holders of this library give you
# permission to link this library with independent modules to produce an
# executable, regardless of the license terms of these independent
# modules, and to copy and distribute the resulting executable under
# terms of your choice, provided that you also meet, for each linked
# independent module, the terms and conditions of the license of that
# module.  An independent module is a module which is not derived from
# or based on this library.  If you modify this library, you may extend
# this exception to your version of the library, but you are not
# obligated to do so.  If you do not wish to do so, delete this
# exception statement from your version.
#

import os
import sys
import datetime
import subprocess
import smtplib

from email.mime.text import MIMEText


def generate_test_diff(old_file, new_file):
    """
    Returns a list of newly failed tests. 

    Given two jtreg output files old_file and new_file, the return value
    should be a list containing names of those tests which pass in old_file
    and fail in new_file
    eg: ['com/sun/java.sh']
    """

    # for jtreg, the reason for failure can change, which would generate a diff with new result as failed
    # so we create temporary files with the reason removed, and then diff them

    temp_ext = '.jtreg-differ.tmp'

    fixed_old_file = open(old_file+ temp_ext, 'w')
    original_old_file = open(old_file, 'r')
    for line in original_old_file:
        new_line = ' '.join(' '.join(line.split()).split()[:2])
        fixed_old_file.write(new_line + '\n')
    fixed_old_file.close()
    original_old_file.close()

    fixed_new_file = open(new_file + temp_ext , 'w')
    original_new_file = open(new_file, 'r')
    for line in original_new_file:
        new_line = ' '.join(' '.join(line.split()).split()[:2])
        fixed_new_file.write(new_line + '\n')
    fixed_new_file.close()
    original_old_file.close()


    # diff | grep ^+ | egrep (Failed|Error) | sort 

    diff = subprocess.Popen(['diff', '-bBdu', old_file + temp_ext, new_file + temp_ext], stdout=subprocess.PIPE)
    grep_additions = subprocess.Popen(['grep', '^+'], stdin=diff.stdout, stdout=subprocess.PIPE)
    grep_failed = subprocess.Popen(['egrep', '(Failed|Error)\.'], stdin=grep_additions.stdout, stdout=subprocess.PIPE)
    raw_output = grep_failed.communicate()[0]
    raw_diff = raw_output.split('\n')
    pretty_diff = [ line[1:].split()[0] for line in raw_diff if len(line.strip()) > 0]
    unique_list = list(set(pretty_diff))
    unique_list.sort()

    # remove temp files
    os.remove(old_file + temp_ext)
    os.remove(new_file + temp_ext)
    
    return unique_list


def get_recent_committers(repo_dir, first_changeset, last_changeset):
    """
    Get a list of recent commiters from repo

    repo_dir should be a directory containing a mercurial directory

    Return value should be a list with each entry containing a name and an 
    email, suitable for use in an email address. 
    eg:  [ 'IcedTea Tester <testresults@icedtea.classpath.org>' ]
    """

    original_dir = os.getcwd()
    os.chdir(repo_dir)
    cmd =  ['hg', 'log', '-r', first_changeset + ':' + last_changeset,
        '--template', '{author}\n']
    log = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output = log.communicate()[0]
    committers = set([])
    for author in output.split('\n'):
        if len(author.strip()) > 0:
            committers.add(author)

    if log.returncode != 0:
        raise IOError('hg log failed: ' + log.communicate()[1])

    os.chdir(original_dir)
    return committers


def get_changelog(repo_dir, first_changeset, last_changeset):
    """
    Get the command used to obtain a changelog and the changelog itself
    """
    original_dir = os.getcwd()
    os.chdir(repo_dir)
    cmd =  ['hg', 'log', '-r', first_changeset + ':' + last_changeset,
        '--template', 'Author: {author}\n{desc}\n\n']
    log = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output = log.communicate()[0]

    if log.returncode != 0:
        raise IOError('hg log failed: ' + log.communicate()[1])

    os.chdir(original_dir)
    return (cmd, output)


def send_email(sender, receivers, subject, text):
    """
    Send text as email to receivers
    """

    message = MIMEText(text)

    message['From'] = sender
    message['To'] = ', '.join(receivers)
    message['Subject'] = subject

    s = smtplib.SMTP()
    s.connect('smtp.corp.redhat.com', 25)
    s.sendmail(sender, receivers , message.as_string())
    s.close()


def main(args):
    """
    Send email to recent committers on any test regresssions

    Usage:
        """ + args[0] + """ project_name test_suite old_test_file new_test_file 
        old_version_file new_version_file hg_dir old_changeset new_changeset

    args:
        project_name: used in email subjects
        test_suite: jtreg test suite (jdk, hotspot or langtools)
        old_test_file: older jtreg output
        new_test_file: newer jtreg output
        old_version_file: file containing output of 'java -version' for older jtreg results
        new_version_file: file containing output of 'java -version' for newer jtreg results
        hg_dir: path to hg dir containing changesets
        old_changeset: changeset of the older build
        new_changeset: changset of the newer build
    """


    # check args
    if not (len(args) == 7 or len(args) == 10):
        sys.write(main.__doc__)
        exit(-1)

    project = args[1]

    test_type = args[2]
    test_types = ['jdk', 'hotspot', 'langtools']
    if not test_type in test_types:
        raise IOError('Invalid test type')

    older_file = args[3]
    latest_file = args[4]

    old_version_file = args[5]
    new_version_file = args[6]

    # if we have any information about repositories, use that too
    if (len(args) == 10):
        have_changesets = True
        hg_dir = args[7]
        old_changeset = args[8]
        new_changeset = args[9]
    else:
        have_changesets = False

    diff_sender = project + ' JTreg <omajid@redhat.com>'
    subject = 'Regression in ' + project + '\'s JTreg Results' + ' - ' + test_type
    message = ''

    # add version information
    message = message + 'Regression from\n'

    for line in open(old_version_file, 'r'):
        if len(line.strip()) == 0:
            continue
        message = message + line.strip() + '\n'

    message = message + '\n'

    message = message + 'to\n'
    for line in open(new_version_file, 'r'):
        if len(line.strip()) == 0:
            continue
        message = message + line.strip() + '\n'

    message = message + '\n'

    test_list = generate_test_diff(older_file, latest_file)

    # no emails on a clean diff
    if (len(test_list) == 0):
        print 'No regressions for ' + project + ' - ' + test_type
        sys.exit(0)

    # add list of regresssions
    message = message + 'Regressions:\n'
    for test in test_list:
        message = message + test + '\n'

    message = message + '\n\n'

    #if we have changesets, include that information
    if (have_changesets):
        if old_changeset == new_changeset:
            message = message + 'No Recent Commits\n'
            message = message + 'This is probably an issue in the build system.\n'
        else:    
            cmd, changelog =  get_changelog(hg_dir, old_changeset, new_changeset)
            message = message + 'Ouptut of ' + ' '.join(cmd[:4]) + '\n'
            message = message + changelog
            #message = message + 'Commiters for changesets ' + old_changeset + ':' + \
            #    new_changeset + ':\n'
            #committers = get_recent_committers(hg_dir, old_changeset, new_changeset)
            #for committer in committers: 
            #    message = message + committer + '\n'

    #diff_receivers = [ 'Omair <omajid@redhat.com>', 'IcedTea6 Testresults <testresults@icedtea.classpath.org>' ]
    diff_receivers = [ 'Omair <omajid@redhat.com>' ]

    send_email(diff_sender, diff_receivers, subject, message)

    # done


date = datetime.date.today()


if __name__ == '__main__':
    main(sys.argv)

# done

