"""Pruebas del laboratorio. LAB_DIR dice qué carpeta se prueba: lab o solution.

Cada prueba de vector_tests corre en un proceso aparte y con sanitizers: una fuga o un
acceso inválido la hace fallar, aunque el resultado parezca correcto."""
import os
import pathlib
import re
import subprocess
import unittest

HERE = pathlib.Path(__file__).parent
LAB_DIR = os.environ.get("LAB_DIR", "solution")
BUILD = HERE / "build" / LAB_DIR
TEST_SOURCE = HERE / "lab" / "vector_tests.cpp"


def run(command):
    return subprocess.run(command, cwd=HERE, text=True, stdin=subprocess.DEVNULL,
                          stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=120)


def short_report(text):
    """Deja lo útil: la aserción, el tipo de error y las líneas de su código."""
    useful = [line for line in text.splitlines()
              if "Assertion" in line or "ERROR:" in line or "SUMMARY:" in line
              or "vector.h" in line or "vector_tests.cpp" in line]
    return "\n".join(useful[:12]) or text.strip()


class VectorTests(unittest.TestCase):
    pass


def make_test(name):
    def test(self):
        done = run([str(BUILD / "vector_tests"), name])
        self.assertEqual(done.returncode, 0, short_report(done.stdout))
    return test


for test_name in re.findall(r'\{"(\w+)", \w+\}', TEST_SOURCE.read_text(encoding="utf-8")):
    setattr(VectorTests, f"test_{test_name}", make_test(test_name))


class Measurement(unittest.TestCase):
    def test_counts_match_the_theory(self):
        done = run([str(BUILD / "growth_bench"), "--counts"])
        self.assertEqual(done.returncode, 0, short_report(done.stdout))
        rows = {}
        for line in done.stdout.splitlines():
            parts = line.split()
            if len(parts) == 3 and parts[0].isdigit():
                rows[int(parts[0])] = (int(parts[1]), int(parts[2]))
        for n, (doubling, step) in rows.items():
            power = 1
            total = 0
            while power < n:
                total += power
                power *= 2
            m = (n - 1) // 1000
            self.assertEqual(doubling, total, f"copias duplicando con n = {n}")
            self.assertEqual(step, 1000 * m * (m + 1) // 2, f"copias de a 1000 con n = {n}")
        self.assertEqual(len(rows), 4)

    def test_report_is_written(self):
        report = HERE / LAB_DIR / "report.md"
        self.assertTrue(report.exists(), f"falta {report.name}")
        self.assertNotIn("(escriban aquí)", report.read_text(encoding="utf-8"))


if __name__ == "__main__":
    unittest.main()
