# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.


import collections
import itertools
import json
import logging
import time

from pylib.base import base_test_result


def GenerateResultsDict(test_run_results, global_tags=None):
    """Create a results dict from |test_run_results| suitable for writing to JSON.
    Args:
      test_run_results: a list of base_test_result.TestRunResults objects.
    Returns:
      A results dict that mirrors the one generated by
        base/test/launcher/test_results_tracker.cc:SaveSummaryAsJSON.
    """
    # Example json output.
    # {
    #   "global_tags": [],
    #   "all_tests": [
    #     "test1",
    #     "test2",
    #    ],
    #   "disabled_tests": [],
    #   "per_iteration_data": [
    #     {
    #       "test1": [
    #         {
    #           "status": "SUCCESS",
    #           "elapsed_time_ms": 1,
    #           "output_snippet": "",
    #           "output_snippet_base64": "",
    #           "losless_snippet": "",
    #         },
    #         ...
    #       ],
    #       "test2": [
    #         {
    #           "status": "FAILURE",
    #           "elapsed_time_ms": 12,
    #           "output_snippet": "",
    #           "output_snippet_base64": "",
    #           "losless_snippet": "",
    #         },
    #         ...
    #       ],
    #     },
    #     {
    #       "test1": [
    #         {
    #           "status": "SUCCESS",
    #           "elapsed_time_ms": 1,
    #           "output_snippet": "",
    #           "output_snippet_base64": "",
    #           "losless_snippet": "",
    #         },
    #       ],
    #       "test2": [
    #         {
    #           "status": "FAILURE",
    #           "elapsed_time_ms": 12,
    #           "output_snippet": "",
    #           "output_snippet_base64": "",
    #           "losless_snippet": "",
    #         },
    #       ],
    #     },
    #     ...
    #   ],
    # }

    all_tests = set()
    per_iteration_data = []
    test_run_links = {}

    for test_run_result in test_run_results:
        iteration_data = collections.defaultdict(list)
        if isinstance(test_run_result, list):
            results_iterable = itertools.chain(
                *(t.GetAll() for t in test_run_result)
            )
            for tr in test_run_result:
                test_run_links.update(tr.GetLinks())

        else:
            results_iterable = test_run_result.GetAll()
            test_run_links.update(test_run_result.GetLinks())

        for r in results_iterable:
            result_dict = {
                'status': r.GetType(),
                'elapsed_time_ms': r.GetDuration(),
                'output_snippet': r.GetLog(),
                'losless_snippet': True,
                'output_snippet_base64': '',
                'links': r.GetLinks(),
            }
            iteration_data[r.GetName()].append(result_dict)

        all_tests = all_tests.union(set(iteration_data.keys()))
        per_iteration_data.append(iteration_data)

    return {
        'global_tags': global_tags or [],
        'all_tests': sorted(list(all_tests)),
        # TODO(jbudorick): Add support for disabled tests within base_test_result.
        'disabled_tests': [],
        'per_iteration_data': per_iteration_data,
        'links': test_run_links,
    }


def GenerateJsonTestResultFormatDict(test_run_results, interrupted):
    """Create a results dict from |test_run_results| suitable for writing to JSON.

    Args:
      test_run_results: a list of base_test_result.TestRunResults objects.
      interrupted: True if tests were interrupted, e.g. timeout listing tests
    Returns:
      A results dict that mirrors the standard JSON Test Results Format.
    """

    tests = {}
    counts = {'PASS': 0, 'FAIL': 0, 'SKIP': 0, 'CRASH': 0, 'TIMEOUT': 0}

    for test_run_result in test_run_results:
        if isinstance(test_run_result, list):
            results_iterable = itertools.chain(
                *(t.GetAll() for t in test_run_result)
            )
        else:
            results_iterable = test_run_result.GetAll()

        for r in results_iterable:
            element = tests
            for key in r.GetName().split('.'):
                if key not in element:
                    element[key] = {}
                element = element[key]

            element['expected'] = 'PASS'

            if r.GetType() == base_test_result.ResultType.PASS:
                result = 'PASS'
            elif r.GetType() == base_test_result.ResultType.SKIP:
                result = 'SKIP'
            elif r.GetType() == base_test_result.ResultType.CRASH:
                result = 'CRASH'
            elif r.GetType() == base_test_result.ResultType.TIMEOUT:
                result = 'TIMEOUT'
            else:
                result = 'FAIL'

            if 'actual' in element:
                element['actual'] += ' ' + result
            else:
                counts[result] += 1
                element['actual'] = result
                if result == 'FAIL':
                    element['is_unexpected'] = True

            if r.GetDuration() != 0:
                element['time'] = r.GetDuration()

    # Fill in required fields.
    return {
        'interrupted': interrupted,
        'num_failures_by_type': counts,
        'path_delimiter': '.',
        'seconds_since_epoch': time.time(),
        'tests': tests,
        'version': 3,
    }


def GenerateJsonResultsFile(
    test_run_result, file_path, global_tags=None, **kwargs
):
    """Write |test_run_result| to JSON.

    This emulates the format of the JSON emitted by
    base/test/launcher/test_results_tracker.cc:SaveSummaryAsJSON.

    Args:
      test_run_result: a base_test_result.TestRunResults object.
      file_path: The path to the JSON file to write.
    """
    with open(file_path, 'w') as json_result_file:
        json_result_file.write(
            json.dumps(
                GenerateResultsDict(test_run_result, global_tags=global_tags),
                **kwargs,
            )
        )
        logging.info('Generated json results file at %s', file_path)


def GenerateJsonTestResultFormatFile(
    test_run_result, interrupted, file_path, **kwargs
):
    """Write |test_run_result| to JSON.

    This uses the official Chromium Test Results Format.

    Args:
      test_run_result: a base_test_result.TestRunResults object.
      interrupted: True if tests were interrupted, e.g. timeout listing tests
      file_path: The path to the JSON file to write.
    """
    with open(file_path, 'w') as json_result_file:
        json_result_file.write(
            json.dumps(
                GenerateJsonTestResultFormatDict(test_run_result, interrupted),
                **kwargs,
            )
        )
        logging.info('Generated json results file at %s', file_path)


def ParseResultsFromJson(json_results):
    """Creates a list of BaseTestResult objects from JSON.

    Args:
      json_results: A JSON dict in the format created by
                    GenerateJsonResultsFile.
    """

    def string_as_status(s):
        if s in base_test_result.ResultType.GetTypes():
            return s
        return base_test_result.ResultType.UNKNOWN

    results_list = []
    testsuite_runs = json_results['per_iteration_data']
    for testsuite_run in testsuite_runs:
        for test, test_runs in testsuite_run.items():
            results_list.extend(
                [
                    base_test_result.BaseTestResult(
                        test,
                        string_as_status(tr['status']),
                        duration=tr['elapsed_time_ms'],
                        log=tr.get('output_snippet'),
                    )
                    for tr in test_runs
                ]
            )
    return results_list
