{"id":13741011,"url":"https://github.com/divvun/morph-test","last_synced_at":"2025-07-16T04:06:51.994Z","repository":{"id":16874890,"uuid":"19635314","full_name":"divvun/morph-test","owner":"divvun","description":"A python script to run tests for generation and analysis of a morphological transducer built using the Giella infrastructure. Works with Hfst, Xerox' fst tools, and with Foma.","archived":false,"fork":false,"pushed_at":"2021-03-16T11:40:31.000Z","size":50,"stargazers_count":2,"open_issues_count":2,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-08T21:35:06.424Z","etag":null,"topics":["minority-language","morphological-analysis","morphology","natural-language","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/divvun.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-05-10T06:45:52.000Z","updated_at":"2022-04-01T07:25:09.000Z","dependencies_parsed_at":"2022-07-09T07:46:22.973Z","dependency_job_id":null,"html_url":"https://github.com/divvun/morph-test","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/divvun/morph-test","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/divvun%2Fmorph-test","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/divvun%2Fmorph-test/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/divvun%2Fmorph-test/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/divvun%2Fmorph-test/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/divvun","download_url":"https://codeload.github.com/divvun/morph-test/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/divvun%2Fmorph-test/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265480756,"owners_count":23773781,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["minority-language","morphological-analysis","morphology","natural-language","python"],"created_at":"2024-08-03T04:00:54.487Z","updated_at":"2025-07-16T04:06:51.941Z","avatar_url":"https://github.com/divvun.png","language":"Python","readme":"# morph-test\nAn open-source test tool for rule-based FST moprhological analysers. `morph-test` is at the core of the morphological testing regime in the [GiellaLT](https://github.com/giellalt) infrastructure.\n\n# Installation\n\nClone or download and run as shown below.\n\n# Usage\n\n```\npython3 morph-test.py -h\nusage: morph-test.py [-h] [-c] [-o OUTPUT] [-q] [-i] [-s] [-l] [-f] [-p] [-S [SECTION]]\n                     [-t [TEST]] [-F [TRANSDUCER]] [-v] [--app [APP]]\n                     [--gen [GEN]] [--morph [MORPH]]\n                     test_file\n\nTest morphological transducers for consistency.\n\npositional arguments:\n  test_file             YAML file with test rules\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -c, --colour          Colours the output\n  -o OUTPUT, --output OUTPUT\n                        Desired output style: compact, terse, final, normal (Default: normal)\n  -q, --silent          Hide all output; exit code only\n  -i, --ignore-extra-analyses\n                        Ignore extra analyses when there are more than expected, will PASS\n                        if the expected one is found.\n  -s, --surface         Surface input/analysis tests only\n  -l, --lexical         Lexical input/generation tests only\n  -f, --hide-fails      Suppresses fails to make finding passes easier\n  -p, --hide-passes     Suppresses passes to make finding fails easier\n  -S [SECTION], --section [SECTION]\n                        The section to be used for testing (default is `hfst`)\n  -t [TEST], --test [TEST]\n                        Which test to run (Default: all). TEST = test ID, e.g.\n                        'Noun - gåetie' (remember quotes if the ID contains spaces)\n  -F [TRANSDUCER], --fallback [TRANSDUCER]\n                        Which fallback transducer to use (ignored, use --gen and --morph).\n  -v, --verbose         More verbose output.\n  --app [APP]           Override application used for test\n  --gen [GEN]           Override generation transducer used for test\n  --morph [MORPH]       Override morph transducer used for test\n\nWill run all tests in the test_file by default.\n```\n\n## Controlling output\n\n### Output modes\n\nThe different output modes (`-o OUTPUT, --output OUTPUT`) will render the as follows:\n\n- **normal** (default): all details, full report:\n\n```\n-----------------------------------------------------\nTest 0: Adjective - antisosiálla (Lexical/Generation)\n-----------------------------------------------------\n[1/7][PASS] antisosiálla+A+Attr =\u003e antisosiála\n[1/7][PASS] antisosiálla+A+Attr =\u003e antisosiálalasj\n[2/7][PASS] antisosiálla+A+Sg+Nom =\u003e antisosiálla\n[2/7][PASS] antisosiálla+A+Sg+Nom =\u003e antisosiálalasj\n[3/7][PASS] antisosiálla+A+Sg+Gen =\u003e antisosiála\n[3/7][PASS] antisosiálla+A+Sg+Gen =\u003e antisosiálalattja\n[4/7][PASS] antisosiálla+A+Der/Comp+A+Sg+Nom =\u003e antisosiálalabbo\n[4/7][PASS] antisosiálla+A+Der/Comp+A+Sg+Nom =\u003e antisosiálap\n[5/7][PASS] antisosiálla+A+Der/Superl+A+Sg+Nom =\u003e antisosiálamus\n[5/7][PASS] antisosiálla+A+Der/Superl+A+Sg+Nom =\u003e antisosiálalamos\n[6/7][PASS] antisosiálla+A+Der/AAdv+Adv =\u003e antisosiálalattjat\n[7/7][PASS] antisosiálla+A+Attr+Der/vuota+N+Sg+Nom =\u003e \u003cNo lexical/generation\u003e\n\nTest 0 - Passes: 12, Fails: 0, Total: 12\n----------------------------------------------------\nTest 1: Adjective - aspektuálla (Lexical/Generation)\n----------------------------------------------------\n[1/7][PASS] aspektuælla+A+Attr =\u003e aspektuella\n[1/7][PASS] aspektuælla+A+Attr =\u003e aspektuellalasj\n[1/7][FAIL] aspektuælla+A+Attr =\u003e Unexpected results: aspektuälla\n[2/7][PASS] aspektuælla+A+Sg+Nom =\u003e aspektuælla\n[2/7][PASS] aspektuælla+A+Sg+Nom =\u003e aspektuälla\n[2/7][PASS] aspektuælla+A+Sg+Nom =\u003e aspektuellalasj\n[3/7][PASS] aspektuælla+A+Sg+Acc =\u003e aspektuellav\n[3/7][PASS] aspektuælla+A+Sg+Acc =\u003e aspektuellalattjav\n[3/7][FAIL] aspektuælla+A+Sg+Acc =\u003e Unexpected results: aspektuällav\n[4/7][PASS] aspektuælla+A+Der/Comp+A+Sg+Nom =\u003e aspektuellalabbo\n[4/7][PASS] aspektuælla+A+Der/Comp+A+Sg+Nom =\u003e aspektuellap\n[4/7][FAIL] aspektuælla+A+Der/Comp+A+Sg+Nom =\u003e Unexpected results: aspektuällap\n[5/7][PASS] aspektuælla+A+Der/Superl+A+Sg+Nom =\u003e aspektuellamus\n[5/7][PASS] aspektuælla+A+Der/Superl+A+Sg+Nom =\u003e aspektuellalamos\n[5/7][FAIL] aspektuælla+A+Der/Superl+A+Sg+Nom =\u003e Unexpected results: aspektuällamus\n[6/7][PASS] aspektuælla+A+Der/AAdv+Adv =\u003e aspektuellalattjat\n[7/7][PASS] aspektuælla+A+Attr+Der/vuota+N+Sg+Nom =\u003e \u003cNo lexical/generation\u003e\n\nTest 1 - Passes: 13, Fails: 4, Total: 17\n--------------------------------------------------\nTest 2: Adjective - paralælla (Lexical/Generation)\n--------------------------------------------------\n[1/5][PASS] paralælla+A+Attr =\u003e paralellalasj\n[1/5][PASS] paralælla+A+Attr =\u003e paralella\n[1/5][FAIL] paralælla+A+Attr =\u003e Unexpected results: paralälla\n[2/5][PASS] paralælla+A+Sg+Nom =\u003e paralälla\n[2/5][PASS] paralælla+A+Sg+Nom =\u003e paralellalasj\n[2/5][PASS] paralælla+A+Sg+Nom =\u003e paralælla\n[3/5][PASS] paralælla+A+Sg+Gen =\u003e paralellalattja\n[3/5][PASS] paralælla+A+Sg+Gen =\u003e paralella\n[3/5][FAIL] paralælla+A+Sg+Gen =\u003e Unexpected results: paralälla\n[4/5][PASS] paralælla+A+Der/Comp+A+Sg+Nom =\u003e paralellap\n[4/5][PASS] paralælla+A+Der/Comp+A+Sg+Nom =\u003e paralellalabbo\n[4/5][FAIL] paralælla+A+Der/Comp+A+Sg+Nom =\u003e Unexpected results: paralällap\n[5/5][PASS] paralælla+A+Der/Superl+A+Sg+Nom =\u003e paralellamus\n[5/5][PASS] paralælla+A+Der/Superl+A+Sg+Nom =\u003e paralellalamos\n[5/5][FAIL] paralælla+A+Der/Superl+A+Sg+Nom =\u003e Unexpected results: paralällamus\n\nTest 2 - Passes: 11, Fails: 4, Total: 15\n---------------------------------------------------\nTest 3: Adjective - antisosiálla (Surface/Analysis)\n---------------------------------------------------\n[1/9][PASS] antisosiála =\u003e antisosiálla+A+Attr\n[1/9][PASS] antisosiála =\u003e antisosiálla+A+Sg+Gen\n[2/9][PASS] antisosiálalasj =\u003e antisosiálla+A+Attr\n[2/9][PASS] antisosiálalasj =\u003e antisosiálla+A+Sg+Nom\n[3/9][PASS] antisosiálla =\u003e antisosiálla+A+Sg+Nom\n[4/9][PASS] antisosiálalattja =\u003e antisosiálla+A+Sg+Gen\n[5/9][PASS] antisosiálalabbo =\u003e antisosiálla+A+Der/Comp+A+Sg+Nom\n[6/9][PASS] antisosiálap =\u003e antisosiálla+A+Der/Comp+A+Sg+Nom\n[7/9][PASS] antisosiálalamos =\u003e antisosiálla+A+Der/Superl+A+Sg+Nom\n[8/9][PASS] antisosiálamus =\u003e antisosiálla+A+Der/Superl+A+Sg+Nom\n[9/9][PASS] antisosiálalattjat =\u003e antisosiálla+A+Der/AAdv+Adv\n\nTest 3 - Passes: 11, Fails: 0, Total: 11\n--------------------------------------------------\nTest 4: Adjective - aspektuálla (Surface/Analysis)\n--------------------------------------------------\n[ 1/11][PASS] aspektuella =\u003e aspektuælla+A+Attr\n[ 2/11][PASS] aspektuellalasj =\u003e aspektuælla+A+Attr\n[ 2/11][PASS] aspektuellalasj =\u003e aspektuælla+A+Sg+Nom\n[ 3/11][PASS] aspektuælla =\u003e aspektuælla+A+Sg+Nom\n[ 4/11][PASS] aspektuälla =\u003e aspektuælla+A+Sg+Nom\n[ 5/11][PASS] aspektuellav =\u003e aspektuælla+A+Sg+Acc\n[ 6/11][PASS] aspektuellalattjav =\u003e aspektuælla+A+Sg+Acc\n[ 7/11][PASS] aspektuellalabbo =\u003e aspektuælla+A+Der/Comp+A+Sg+Nom\n[ 8/11][PASS] aspektuellap =\u003e aspektuælla+A+Der/Comp+A+Sg+Nom\n[ 9/11][PASS] aspektuellalamos =\u003e aspektuælla+A+Der/Superl+A+Sg+Nom\n[10/11][PASS] aspektuellamus =\u003e aspektuælla+A+Der/Superl+A+Sg+Nom\n[11/11][PASS] aspektuellalattjat =\u003e aspektuælla+A+Der/AAdv+Adv\n\nTest 4 - Passes: 12, Fails: 0, Total: 12\n------------------------------------------------\nTest 5: Adjective - paralælla (Surface/Analysis)\n------------------------------------------------\n[1/9][PASS] paralella =\u003e paralælla+A+Sg+Gen\n[1/9][PASS] paralella =\u003e paralælla+A+Attr\n[2/9][PASS] paralellalasj =\u003e paralælla+A+Sg+Nom\n[2/9][PASS] paralellalasj =\u003e paralælla+A+Attr\n[3/9][PASS] paralælla =\u003e paralælla+A+Sg+Nom\n[4/9][PASS] paralälla =\u003e paralælla+A+Sg+Nom\n[5/9][PASS] paralellalattja =\u003e paralælla+A+Sg+Gen\n[6/9][PASS] paralellalabbo =\u003e paralælla+A+Der/Comp+A+Sg+Nom\n[7/9][PASS] paralellap =\u003e paralælla+A+Der/Comp+A+Sg+Nom\n[8/9][PASS] paralellalamos =\u003e paralælla+A+Der/Superl+A+Sg+Nom\n[9/9][PASS] paralellamus =\u003e paralælla+A+Der/Superl+A+Sg+Nom\n\nTest 5 - Passes: 11, Fails: 0, Total: 11\nTotal passes: 70, Total fails: 8, Total: 78\n\n```\n- **compact**: Report the test names and result for each, and print a summary:\n\n```\n[PASS] Test 0: Adjective - antisosiálla (Lexical/Generation) 12/0/12\n[FAIL] Test 1: Adjective - aspektuálla (Lexical/Generation) 13/4/17\n[FAIL] Test 2: Adjective - paralælla (Lexical/Generation) 11/4/15\n[PASS] Test 3: Adjective - antisosiálla (Surface/Analysis) 11/0/11\n[PASS] Test 4: Adjective - aspektuálla (Surface/Analysis) 12/0/12\n[PASS] Test 5: Adjective - paralælla (Surface/Analysis) 11/0/11\nTotal passes: 70, Total fails: 8, Total: 78\n```\n- **terse**: print one `.` for each PASS, and one `!` for each fail, and report PASS or FAIL:\n\n```\n............\n..!.....!..!..!..\n..!.....!..!..!\n...........\n............\n...........\nFAIL\n```\n\n- **final**: only prints a summary of PASSed/FAILed/TOTAL tests:\n\n```\n70/8/78\n```\n\n### Verbose and silent modes\n\nVerbose mode (`-v, --verbose`) adds test run metadata:\n\n```\n`/usr/local/bin/hfst-optimized-lookup` will be used for parsing dictionaries.\nGenerating...\nMorphing...\nDone!\n[... test results ...]\n```\n\nSilent or quiet mode (`-q, --silent `) blocks all output, and only returns the exit value (`0` for `PASS`, `1` for `FAIL`).\n\n### Other output options\n\n`-f, --hide-fails` removes the fails from the output, to make finding passes easier.\n\n`-p, --hide-passes` removes passes from the output, to make finding fails easier.\n\n# Example test YAML file\n\nThe test files are written in [YAML](https://yaml.org) [syntax](https://en.wikipedia.org/wiki/YAML#Syntax), adhering to the following structure:\n\n```yaml\nConfig:\n  hfst:\n    Gen: ../../../src/generator-gt-norm.hfst\n    Morph: ../../../src/analyser-gt-norm.hfst\n  xerox:\n    Gen: ../../../src/generator-gt-norm.xfst\n    Morph: ../../../src/analyser-gt-norm.xfst\n    App: lookup\n\nTests:\n  Pronoun - juoga: # Indefinitive\n    juoga+Pron+Indef+Sg+Nom: juoga\n    juoga+Pron+Indef+Sg+Gen: juoŋga\n    juoga+Pron+Indef+Sg+Acc: juojddá\n    juoga+Pron+Indef+Sg+Ine: [juonná, juonne]\n    juoga+Pron+Indef+Sg+Ill: juosik\n    juoga+Pron+Indef+Sg+Ela: juosstá\n    juoga+Pron+Indef+Sg+Com: juojnak\n    juoga+Pron+Indef+Pl+Nom: []\n    juoga+Pron+Indef+Pl+Gen: []\n    juoga+Pron+Indef+Pl+Acc: juojddájt\n    juoga+Pron+Indef+Pl+Ine: []\n    juoga+Pron+Indef+Pl+Ill: []\n    juoga+Pron+Indef+Pl+Ela: []\n    juoga+Pron+Indef+Pl+Com: []\n    juoga+Pron+Indef+Ess: juonen\n```\n\nThe `Config` section contains defaults to be used with the test files; these can be overriden on the command line. After the `Config` section comes the `Tests` section. Each test must have a name (`Pronoun - juoga` in the example above), followed by the actual test data.\n\nEach line of test data contains:\n\n- **morphological analysis**: e.g. `juoga+Pron+Indef+Sg+Gen`\n- **separator**: `:`\n- **expected word form(s)**: `juoŋga`\n\nIf more than one word form is expected, they must be in a `[]` list, comma separated.\n\nIf **no** word form is expected, that must be denoted with an empty `[]`.\n\nComments can be freely added, marked with an initial `#`.\n\nA test run succeeds if all and only the listed word forms are generated, and the word forms listed only get the specified analyses.\n\nAll languages contain a certain amount of homonymy, which makes the `-i, --ignore-extra-analyses` option very useful: it makes the tests pass even if there are alternative analyses of a given word form. That is, homonym analyses won't destroy the test results.\n\n# License\n\nLicensed under [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/).\n\n# Contribution\n\nFork and PR on Github.\n\nUnless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed as above, without any additional terms or conditions.","funding_links":[],"categories":["Software"],"sub_categories":["Utilities"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdivvun%2Fmorph-test","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdivvun%2Fmorph-test","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdivvun%2Fmorph-test/lists"}