#!/usr/bin/env python

# GStreamer QA system
#
#       insanity-dumpresults-json
#        - Output lava-dashboard compatible json.
#
# Copyright (c) 2008, Edward Hervey <bilboed@bilboed.com>
# Copyright (c) 2011, Collabora Ltd. <david.laban@collabora.co.uk>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser 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.

"""
Dumps the results of a test results DB
"""

import sys
import time
from optparse import OptionParser
from insanity.log import initLogging
import simplejson

# Since the dashboard model puts a limit on the length of attribute keys,
# we may need to truncate them.
MAX_ATTR_LEN = 32

def printTestRunInfo(db, testrunid, verbose=False):
    # id , date, nbtests, client
    cid, starttime, stoptime = db.getTestRun(testrunid)
    softname, clientname, clientuser = db.getClientInfoForTestRun(testrunid)
    nbtests = db.getNbTestsForTestrun(testrunid)
    nbfailed = db.getNbTestsForTestrun(testrunid, failedonly=True)
    print "[% 3d]\tDate:%s\tNbTests:% 5d\tFailed:% 5d\tClient: %s/%s/%s" % (testrunid,
                                                                           time.ctime(starttime),
                                                                           nbtests,
                                                                           nbfailed,
                                                                           softname,
                                                                           clientname,
                                                                           clientuser)
def getMonitorsInfo(db, testid):
    # Return the union of all info about monitors for this test.
    ret = {"args": {}, "results": {}, "extras": {}, "outputfiles": {}}
    monitors = db.getMonitorsIDForTest(testid)

    for mid in monitors or []:
        tid,mtyp,args,results,resperc,extras,outputfiles = db.getFullMonitorInfo(mid)
        ret["args"].update(args)
        ret["results"].update(results)
        ret["extras"].update(extras)
        ret["outputfiles"].update(outputfiles)
    return ret

def getTestInfo(db, testid):
    data = {}
    trid, ttype, args, checks, resperc, extras, outputfiles, parentid, ismon, isscen = db.getFullTestInfo(testid)

    if resperc == None:
        # test didn't end in the database
        return {}

    if resperc == 100:
        data["result"] = "pass"
    else:
        data["result"] = "fail"

    media_name = args.get("uri", "").rpartition("/")[-1]
    if media_name:
        data["test_case_id"] = "%s.%s" % (ttype, media_name)
    else:
        data["test_case_id"] = ttype

    monitors = getMonitorsInfo(db, testid)
    if monitors["args"].get("debug-level", "") == "5":
        data["test_case_id"] += (".rerun")

    logfile = monitors["outputfiles"].get("gst-log-file", "")
    if logfile:
        data["log_filename"] = logfile

    # Flatten extra information into the attributes dict for debugging.
    attributes = {}
    for k, v in args.items():
        attributes[("arg." + k)[:MAX_ATTR_LEN]] = str(v)
    for k, v in checks:
        attributes[("check." + k)[:MAX_ATTR_LEN]] = str(v)
    for k, v in extras:
        attributes[("extra." + k)[:MAX_ATTR_LEN]] = str(v)
    for k, v in outputfiles.items():
        attributes[("out." + k)[:MAX_ATTR_LEN]] = str(v)
    for k1, subdict in monitors.items():
        for k2, v in subdict.items():
            attributes[("monitor.%s.%s" % (k1, k2))[:MAX_ATTR_LEN]] = str(v)
    data["attributes"] = attributes

    # monitors
    return data

def getTestRun(db, testrunid, failedonly=False, hidescenarios=False):
    # let's output everything !
    cid, starttime, stoptime = db.getTestRun(testrunid)
    softname, clientname, clientuser = db.getClientInfoForTestRun(testrunid)
    environ = db.getEnvironmentForTestRun(testrunid)
    tests = db.getTestsForTestRun(testrunid, withscenarios=not hidescenarios,
                                  failedonly=failedonly)

    test_results = []
    for testid in tests:
        data = getTestInfo(db, testid)
        test_results.append(data)

    return test_results

def printTestRuns(db, testrunids, failedonly=False, hidescenarios=False):
    output = {}
    test_results = []

    for testrunid in testrunids:
        result = getTestRun(db, testrunid, failedonly, hidescenarios)
        test_results.extend(result)

    output["test_results"] = test_results

    simplejson.dump(output, sys.stdout)
    print # Newline at end of file.

if __name__ == "__main__":
    usage = "usage: %prog database [options]"
    parser = OptionParser(usage=usage)
    parser.add_option("-l", "--list", dest="list",
                      help="List the available test runs with summary",
                      action="store_true",
                      default=False)
    parser.add_option("-a", "--all", dest="all",
                      help="Dump all testruns.",
                      action="store_true",
                      default=False)
    parser.add_option("-t", "--testrun", dest="testrun",
                      help="Specify a testrun id",
                      type=int,
                      default=-1)
    parser.add_option("-f", "--failed", dest="failed",
                      help="Only show failed tests",
                      action="store_true", default=False)
    parser.add_option("-x", "--hidescenarios", dest="hidescenarios",
                      help="Do not show scenarios",
                      action="store_true", default=False)
    parser.add_option("-m", "--mysql", dest="usemysql",
                      default=False, action="store_true",
                      help="Connect to a MySQL database for storage")
    (options, args) = parser.parse_args(sys.argv[1:])
    if not options.usemysql and len(args) != 1:
        print >> sys.stderr, "You need to specify a database file !"
        parser.print_help()
        sys.exit(1)
    initLogging()
    if options.usemysql:
        from insanity.storage.mysql import MySQLStorage
        if len(args):
            kw = MySQLStorage.parse_uri(args[0])
            db = MySQLStorage(async=False, **kw)
        else:
            # use default values
            db = MySQLStorage(async=False)
    else:
        from insanity.storage.sqlite import SQLiteStorage
        db = SQLiteStorage(path=args[0], async=False)
    if options.list:
        # list all available test runs
        testruns = db.listTestRuns()
        for runid in testruns:
            printTestRunInfo(db, runid)
    else:
        testruns = db.listTestRuns()
        if options.testrun and not options.all:
            if not options.testrun in testruns:
                print >> sys.stderr, "Specified testrunid not available !"
                parser.print_help()
                sys.exit(1)
            printTestRuns(db, [options.testrun], options.failed, options.hidescenarios)
        else:
            if not testruns:
                print >> sys.stderr, "This file contains no test runs."
                sys.exit(1)
            printTestRuns(db, testruns, options.failed, options.hidescenarios)

