#!/usr/bin/env python3

# Characterization tests for the EXCEPTION_SUBS_MAP families via find_licenses.
# Copyright: 2026, Maximiliano Curia <maxy@debian.org>
#
# License: ISC
#  Permission to use, copy, modify, and/or distribute this software for any
#  purpose with or without fee is hereby granted, provided that the above
#  copyright notice and this permission notice appear in all copies.
#  .
#  THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
#  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
#  AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
#  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
#  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
#  OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
#  PERFORMANCE OF THIS SOFTWARE.

"""Pin the EXCEPTION_SUBS_MAP families end-to-end through find_licenses.

The five families keyed in licenses.EXCEPTION_SUBS_MAP (Apache, QPL, MIT/X11,
CC-BY, BSD) have their exception SUBS applied by find_licenses itself, via the
EXCEPTION_SUBS_MAP[family] lookup after detail resolution. The existing
TestSpdxExceptions cases drive those SUBS tables directly through the family
matchers (parse_apache, parse_qpl, parse_exceptions(..., SUBS)), which bypass
that lookup -- so nothing currently exercises the find_licenses path that the
Item-3 migration (EXCEPTION_SUBS_MAP -> per-row exception_subs) rewrites.

These cases feed a base licence text plus a real exception clause through the
whole find_licenses pipeline, so moving the map onto row data cannot drop or
mis-wire a family's exceptions silently.
"""

import json
import unittest
from pathlib import Path

from decopy import licenses

_LICENSES_DIR = Path("tests/testdata/licenses")
_EXCEPTIONS_DIR = Path("tests/testdata/exceptions")


def _license_text(spdx_id: str) -> str:
    data = json.loads((_LICENSES_DIR / f"{spdx_id}.json").read_text())
    return data.get("licenseText") or data.get("standardLicenseHeader") or ""


def _exception_text(filename: str) -> str:
    data = json.loads((_EXCEPTIONS_DIR / filename).read_text())
    return data["licenseExceptionText"]


def _detect(text: str) -> str:
    return " or ".join(sorted(licenses.find_licenses(licenses.clean_comments(text))))


class FindLicensesExceptionSubsTest(unittest.TestCase):
    # (family, base licence id, exception file, expected find_licenses output)
    CASES = (
        ("Apache", "Apache-2.0", "LLVM-exception.json", "Apache-2 with LLVM exception"),
        (
            "QPL",
            "QPL-1.0",
            "QPL-1.0-INRIA-2004-exception.json",
            "QPL-1 with QPL-INRIA-2004 exception",
        ),
        ("MIT/X11", "MIT", "fmt-exception.json", "Expat with fmt exception"),
        (
            "CC-BY",
            "CC-BY-SA-4.0",
            "GNOME-examples-exception.json",
            "CC-BY-SA-4 with GNOME-examples exception",
        ),
        (
            "BSD",
            "BSD-3-Clause",
            "PCRE2-exception.json",
            "BSD-3-clause with PCRE2 exception",
        ),
    )

    def test_exception_subs_applied_through_find_licenses(self):
        for family, base, exception_file, expected in self.CASES:
            with self.subTest(family=family):
                text = _license_text(base) + "\n\n" + _exception_text(exception_file)
                self.assertEqual(_detect(text), expected)


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