"""Comprueba los ejemplos: los programas con errores fallan como dicen los apuntes, y
todo compila sin advertencias."""
import pathlib
import subprocess
import unittest

HERE = pathlib.Path(__file__).parent
FLAGS = ["-Wall", "-Wextra", "-std=c++20", "-g", "-fsanitize=address,undefined", "-I."]
BUILD = HERE / "build"


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


class Examples(unittest.TestCase):
    def test_no_warnings(self):
        sources = sorted(path.name for path in HERE.glob("*.cpp"))
        for source in sources:
            with self.subTest(source=source):
                done = run(["g++", *FLAGS, "-fsyntax-only", source])
                self.assertEqual(done.stdout, "")
                self.assertEqual(done.returncode, 0)

    def test_claims(self):
        done = run(["build/claims_tests"])
        self.assertEqual(done.returncode, 0, done.stdout)
        self.assertTrue(done.stdout.endswith("ok\n"))

    def test_runtime_errors_are_caught_by_asan(self):
        for name in ["stale_reference", "push_in_range_for", "self_push"]:
            with self.subTest(program=name):
                target = BUILD / name
                compiled = run(["g++", *FLAGS, f"errors/{name}.cpp", "-o", str(target)])
                self.assertEqual(compiled.stdout, "", "debe compilar sin advertencias")
                done = run([str(target)])
                self.assertNotEqual(done.returncode, 0)
                self.assertIn("heap-use-after-free", done.stdout)

    def test_new_array_needs_default_constructor(self):
        done = run(["g++", "-std=c++20", "-fsyntax-only", "errors/no_default.cpp"])
        self.assertNotEqual(done.returncode, 0)
        self.assertIn("no matching function", done.stdout)

    def test_programs_finish_cleanly(self):
        for name in ["capacity_growth", "growth_count", "shrink_policies", "move_or_copy",
                     "strong_guarantee", "lifetimes", "stable_refs", "vector_facts",
                     "front_removal", "ring_trace", "copy_block"]:
            with self.subTest(program=name):
                done = run([f"build/{name}"])
                self.assertEqual(done.returncode, 0, done.stdout)

    def test_public_code_has_no_circular_buffer_class(self):
        # El buffer circular se implementa en el laboratorio de la semana 5.
        for path in list(HERE.glob("*.cpp")) + list(HERE.glob("*.h")) + list((HERE / "lab").glob("*")):
            if path.is_file():
                self.assertNotIn("ArrayDeque", path.read_text(encoding="utf-8"))


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