#!/usr/bin/python3

import re
import os
import logging
import sys

from e3.fs import mkdir, ls, find
from e3.os.process import Run
from e3.testsuite import Testsuite

from drivers import make_gnatcoll, TESTSUITE_ROOT_DIR
from drivers.basic import BasicTestDriver


class MyTestsuite(Testsuite):

    tests_subdir = "tests"
    test_driver_map = {
        "default": BasicTestDriver,
    }
    default_driver = "default"

    def add_options(self, parser):
        parser.add_argument(
            "--gcov",
            help="compute testsuite coverage of gnatcoll",
            default=False,
            action="store_true",
        )
        parser.add_argument(
            "--valgrind",
            help="check memory usage with Valgrind (memcheck tool)",
            action="store_true",
        )
        parser.add_argument(
            "--recompile",
            help="recompile production version of gnatcoll for testing",
            default=None,
            action="store_const",
            const="PROD",
        )
        parser.add_argument(
            "--debug",
            help="recompile debug version of gnatcoll for testing",
            dest="recompile",
            action="store_const",
            const="DEBUG",
        )

    def set_up(self):
        super().set_up()

        self.env.gcov = self.main.args.gcov
        self.env.valgrind = self.main.args.valgrind
        if self.main.args.gcov:
            work_dir = os.path.join(TESTSUITE_ROOT_DIR, "gcov")
            gpr_dir, src_dir, obj_dir = make_gnatcoll(work_dir, "DEBUG", gcov=True)
            self.env.gnatcoll_gpr_dir = gpr_dir
            self.env.gnatcoll_src_dir = src_dir
            self.env.gnatcoll_obj_dir = obj_dir
        else:
            self.env.gnatcoll_gpr_dir = None

        recompile_mode = self.main.args.recompile
        if recompile_mode:
            work_dir = os.path.join(TESTSUITE_ROOT_DIR, recompile_mode.lower())
            gpr_dir, _, _ = make_gnatcoll(
                work_dir, recompile_mode, gcov=False
            )
            self.env.gnatcoll_prod_gpr_dir = gpr_dir
            if self.env.gnatcoll_gpr_dir is None:
                self.env.gnatcoll_gpr_dir = gpr_dir
        else:
            self.env.gnatcoll_prod_gpr_dir = None

    def tear_down(self):
        if self.main.args.gcov:
            wd = TESTSUITE_ROOT_DIR

            # We need to call gcov on gcda present both in gnatcoll itself and
            # tests (for generics coverage).
            gcda_files = find(
                os.path.join(self.env.gnatcoll_obj_dir), "*.gcda"
            ) + find(os.path.join(self.env.working_dir), "*.gcda")
            mkdir(os.path.join(wd, "gcov", "results"))
            gcr = os.path.join(wd, "gcov", "results")
            Run(["gcov"] + gcda_files, cwd=os.path.join(wd, "gcov", "results"))
            total_sources = 0
            total_covered = 0

            for source_file in ls(
                os.path.join(self.env.gnatcoll_src_dir, "*", "*")
            ):
                base_file = os.path.basename(source_file)
                if not os.path.isfile(os.path.join(gcr, base_file + ".gcov")):
                    total = 1
                    covered = 0
                    with open(source_file) as fd:
                        total = len(
                            [
                                line
                                for line in fd
                                if line.strip() and not re.match(r" *--", line)
                            ]
                        )
                else:
                    with open(os.path.join(gcr, base_file + ".gcov")) as fd:
                        total = 0
                        covered = 0
                        for line in fd:
                            if re.match(r" *-:", line):
                                pass
                            elif re.match(r" *[#=]{5}:", line):
                                total += 1
                            else:
                                total += 1
                                covered += 1
                total_sources += total
                total_covered += covered

                logging.info(
                    "%6.2f %% %8d/%-8d %s",
                    float(covered) * 100.0 / float(total),
                    covered,
                    total,
                    os.path.basename(source_file),
                )

            logging.info(
                "%6.2f %% %8d/%-8d %s",
                float(total_covered) * 100.0 / float(total_sources),
                total_covered,
                total_sources,
                "TOTAL",
            )
        super().tear_down()


if __name__ == "__main__":
    sys.exit(MyTestsuite().testsuite_main())
