Mercurial > prosody-modules
diff mod_xmllang_check/test_xmpplang.lua @ 6401:de3dc4297035
mod_xmllang_check: Initial commit
| author | Waqas Hussain <waqas20@gmail.com> |
|---|---|
| date | Mon, 09 Feb 2026 18:34:40 -0500 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_xmllang_check/test_xmpplang.lua Mon Feb 09 18:34:40 2026 -0500 @@ -0,0 +1,195 @@ +#!/usr/bin/env lua +-- Test suite for xmpplang.lib.lua parser + +local script_dir = arg and arg[0] and arg[0]:match("^(.*)/[^/]+$") or "."; +package.path = script_dir .. "/?.lib.lua;" .. package.path; + +local match_xmpplang = require("xmpplang") + +local passed = 0 +local failed = 0 + +local function test(input, expected, description) + local result = match_xmpplang(input) + if result == expected then + passed = passed + 1 + print(string.format("PASS: %s (%s) -> %s", description, input, tostring(result))) + else + failed = failed + 1 + print(string.format("FAIL: %s (%s) -> expected %s, got %s", description, input, tostring(expected), tostring(result))) + end +end + +print("=== Basic language tags ===") +test("en", true, "2-letter language") +test("eng", true, "3-letter language") +test("abcd", true, "4-letter language") +test("abcde", true, "5-letter language") +test("abcdefgh", true, "8-letter language") +test("abcdefghi", false, "9-letter language (invalid)") +test("e", false, "1-letter language (invalid)") + +print("\n=== Language with script ===") +test("en-Latn", true, "language-script") +test("zh-Hans", true, "zh-Hans") +test("zh-Hant", true, "zh-Hant") +test("en-Latn-US", true, "language-script-region") + +print("\n=== Language with region ===") +test("en-US", true, "language-region (2 alpha)") +test("en-GB", true, "en-GB") +test("en-123", true, "language-region (3 digit)") +-- Note: en-1234 is valid because 1234 matches variant (digit + 3 alphanum) +test("en-1234", true, "en-1234 (parsed as language + variant)") +-- Note: en-ABC is valid because ABC matches extlang (3 alpha) +test("en-ABC", true, "en-ABC (parsed as language + extlang)") +-- Actually invalid subtags +test("en-AB", true, "en-AB (language + region)") +test("en-A", false, "en-A (single alpha - invalid)") +test("en-12", false, "en-12 (2 digits - invalid)") +test("en-ABCDE", true, "en-ABCDE (language + 5 alpha variant)") + +print("\n=== Language with extlang ===") +test("zh-cmn", true, "language-extlang") +test("zh-cmn-Hans", true, "language-extlang-script") +test("zh-cmn-Hant-HK", true, "language-extlang-script-region") +test("zh-yue", true, "zh-yue") +test("zh-cmn-yue", true, "language-extlang-extlang") +test("zh-cmn-yue-gan", true, "language-extlang-extlang-extlang (max)") +test("zh-cmn-yue-gan-hak", false, "4 extlangs (too many)") + +print("\n=== Variants ===") +test("en-US-12345", true, "5-char variant") +test("en-US-12345678", true, "8-char variant") +test("en-US-1abc", true, "digit+3alphanum variant") +test("en-US-rozaj", true, "rozaj variant") +test("sl-IT-nedis", true, "sl-IT-nedis") +test("en-US-abcd", false, "4-char alpha variant (invalid)") +test("en-US-123456789", false, "9-char variant (invalid)") + +print("\n=== Extensions ===") +test("en-a-bbb", true, "extension a") +test("en-a-bbb-ccc", true, "extension with multiple subtags") +test("en-US-a-bbb", true, "language-region-extension") +test("en-US-a-bbb-b-ccc", true, "multiple extensions") +test("en-u-co-phonebk", true, "unicode extension") +test("en-a-b", false, "extension subtag too short") +test("en-a-bbbbbbbbb", false, "extension subtag too long (9)") + +print("\n=== Privateuse ===") +test("x-private", true, "privateuse standalone") +test("x-a", true, "privateuse single char subtag") +test("x-abcdefgh", true, "privateuse 8-char subtag") +test("x-abc-def", true, "privateuse multiple subtags") +test("en-x-private", true, "language-privateuse") +test("en-US-x-private", true, "language-region-privateuse") +test("X-private", true, "uppercase X privateuse") +test("x-abcdefghi", false, "privateuse subtag too long (9)") + +print("\n=== Grandfathered irregular ===") +test("en-GB-oed", true, "en-GB-oed") +test("i-ami", true, "i-ami") +test("i-bnn", true, "i-bnn") +test("i-default", true, "i-default") +test("i-enochian", true, "i-enochian") +test("i-hak", true, "i-hak") +test("i-klingon", true, "i-klingon") +test("i-lux", true, "i-lux") +test("i-mingo", true, "i-mingo") +test("i-navajo", true, "i-navajo") +test("i-pwn", true, "i-pwn") +test("i-tao", true, "i-tao") +test("i-tay", true, "i-tay") +test("i-tsu", true, "i-tsu") +test("sgn-BE-FR", true, "sgn-BE-FR") +test("sgn-BE-NL", true, "sgn-BE-NL") +test("sgn-CH-DE", true, "sgn-CH-DE") +-- Case insensitive +test("I-AMI", true, "I-AMI (uppercase)") +test("En-Gb-Oed", true, "En-Gb-Oed (mixed case)") + +print("\n=== Grandfathered regular ===") +test("art-lojban", true, "art-lojban") +test("cel-gaulish", true, "cel-gaulish") +test("no-bok", true, "no-bok") +test("no-nyn", true, "no-nyn") +test("zh-guoyu", true, "zh-guoyu") +test("zh-hakka", true, "zh-hakka") +test("zh-min", true, "zh-min") +test("zh-min-nan", true, "zh-min-nan") +test("zh-xiang", true, "zh-xiang") +-- Case insensitive +test("ZH-MIN", true, "ZH-MIN (uppercase)") +test("Art-Lojban", true, "Art-Lojban (mixed case)") + +print("\n=== Encoding ===") +test("en.UTF-8", true, "language with encoding") +test("en-US.UTF-8", true, "language-region with encoding") +test("en-Latn-US.UTF-8", true, "language-script-region with encoding") +test("en.utf-8", true, "encoding lowercase") +test("en.Utf-8", true, "encoding mixed case") +test("en.UTF-16", false, "invalid encoding") + +print("\n=== Complex valid tags ===") +test("zh-cmn-Hans-CN", true, "zh-cmn-Hans-CN") +test("sr-Latn-RS", true, "sr-Latn-RS") +test("sl-rozaj-biske", true, "sl-rozaj-biske (multiple variants)") +test("de-CH-1901", true, "de-CH-1901") +test("en-US-u-islamcal", true, "en-US-u-islamcal") +test("zh-Hans-CN-u-nu-hanidec", true, "complex with extension") + +print("\n=== Invalid cases ===") +test("", false, "empty string") +test("-en", false, "leading hyphen") +test("en-", false, "trailing hyphen") +test("en--US", false, "double hyphen") +test("123", false, "numeric only") +test("en_US", false, "underscore separator") +test("en US", false, "space separator") +test("toolonglanguage", false, "language too long") + +print("\n=== Edge cases ===") +test("qaa", true, "qaa (reserved)") +test("qaa-Qaaa-QM-x-southern", true, "all private use subtags") +test("en-Latn-GB-boont-r-extended-sequence-x-private", true, "complex tag") + +print("\n=== Additional edge cases ===") +-- Encoding edge cases +test("en.UTF-8extra", false, "encoding with extra chars") +test("en.UTF", false, "partial encoding") +test("en.", false, "dot with nothing after") +test("en-US.UTF-8.extra", false, "double dot") + +-- Extension boundary cases +test("en-0-abc", true, "extension with digit singleton") +test("en-9-abc", true, "extension with digit 9 singleton") +test("en-a-12", true, "extension with 2 char subtag") +test("en-a-12345678", true, "extension with 8 char subtag") +test("en-a-ab-cd-ef", true, "extension with multiple short subtags") + +-- Privateuse boundary cases +test("x-1", true, "privateuse with single digit") +test("x-12345678", true, "privateuse with 8 chars") +test("x-a-b-c-d-e", true, "privateuse with many 1-char subtags") + +-- Language boundary cases (no extlang possible after 4+ alpha language) +-- These are invalid because 3-alpha "ext" doesn't match script/region/variant/etc. +test("abcd-ext", false, "4-letter language with 3-char subtag (invalid)") +test("abcde-ext", false, "5-letter language with 3-char subtag (invalid)") + +-- Grandfathered should not be prefix-matched +test("i-ami-extra", false, "grandfathered with extra (invalid)") +-- Note: zh-min-foo is valid as langtag (foo is second extlang in zh-min-foo) +test("zh-min-foo", true, "zh-min-foo (valid: language + extlang + extlang)") + +-- Variants cannot repeat in a way that produces invalid structure +test("en-rozaj-rozaj", true, "repeated variant (syntactically valid)") + +-- Script + region + variant combinations +test("en-Latn-US-rozaj", true, "all optional components") +test("en-US-Latn", false, "region before script (invalid order)") + +print(string.format("\n=== Results: %d passed, %d failed ===", passed, failed)) +if failed > 0 then + os.exit(1) +end
