{"id":40245152,"url":"https://github.com/degawa/par-funnel","last_synced_at":"2026-01-20T00:09:36.904Z","repository":{"id":162806857,"uuid":"618403617","full_name":"degawa/par-funnel","owner":"degawa","description":"Fortran unit test parameterizer using namelist","archived":false,"fork":false,"pushed_at":"2024-12-27T06:39:32.000Z","size":142,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-12-27T07:27:16.748Z","etag":null,"topics":["fortran","fortran-library","fortran-package-manager","modern-fortran","namelist","test-runner","testing-tool","unit-test","unit-testing"],"latest_commit_sha":null,"homepage":"","language":"Fortran","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/degawa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-03-24T11:51:09.000Z","updated_at":"2024-12-27T06:38:46.000Z","dependencies_parsed_at":"2023-05-19T12:45:18.903Z","dependency_job_id":null,"html_url":"https://github.com/degawa/par-funnel","commit_stats":{"total_commits":92,"total_committers":1,"mean_commits":92.0,"dds":0.0,"last_synced_commit":"1d2643c6e74e877cf0b37b9e8fbae0a3b5a4ca23"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/degawa/par-funnel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/degawa%2Fpar-funnel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/degawa%2Fpar-funnel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/degawa%2Fpar-funnel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/degawa%2Fpar-funnel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/degawa","download_url":"https://codeload.github.com/degawa/par-funnel/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/degawa%2Fpar-funnel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28590676,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T00:01:37.455Z","status":"ssl_error","status_checked_at":"2026-01-19T23:58:17.328Z","response_time":67,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["fortran","fortran-library","fortran-package-manager","modern-fortran","namelist","test-runner","testing-tool","unit-test","unit-testing"],"created_at":"2026-01-20T00:09:36.097Z","updated_at":"2026-01-20T00:09:36.896Z","avatar_url":"https://github.com/degawa.png","language":"Fortran","funding_links":[],"categories":[],"sub_categories":[],"readme":"# par-funnel\nFortran unit test parameterizer using namelist\n\n## Motivation\nThere are some assertion libraries and unit test frameworks for Fortran:\n\n- [Assert](https://github.com/sourceryinstitute/assert)\n- [assert-fortran](https://github.com/alecksandr26/assert-fortran)\n- [naturalFRUIT](https://cibinjoseph.github.io/naturalFRUIT/index.html)\n- [pFUnit](https://github.com/Goddard-Fortran-Ecosystem/pFUnit)\n- [test-drive](https://github.com/fortran-lang/test-drive)\n- [TOAST](https://github.com/thomasms/toast)\n- [veggies](https://gitlab.com/everythingfunctional/veggies) and\n[garden](https://gitlab.com/everythingfunctional/garden)\n\nUnit tests are the key to keeping the software quality and must have maintainability. To test a procedure, passing a wide variety of argument combinations to the procedure requires writing tiny and similar procedures. This significantly decreases the maintainability of the unit tests. To solve this problem, unit test frameworks for other languages have the feature to parameterize unit tests, for example, the `[inlineData]` attribute of xUnit for C# and `mark.parametrize` of pytest for Python.\n\nUnit test frameworks for Fortran mentioned above do not provide parameterization of unit tests utilizing Fortran standard features only and do not support optional arguments. This library, par-funnel, aims to provide features that\n- making parameter (arguments) list for unit tests using namelist,\n- supporting optional arguments, and\n- handling results of parameterized tests.\n\nPar-funnel is not a unit test framework but is intended to be used with other unit test frameworks.\n\n## Getting started\n### Requirements\nPar-funnel has been tested only on Windows 10 but may also work on Linux/Mac OS.\nDue to the use of relatively new features, including object-oriented programming, a recent compiler is required to build par-funnel. The compilers and versions listed below have been used to develop par-funnel.\n\n- Modern Fortran compiler\n    - gfortran 11.2 bundled with [quickstart Fortran on Windows](https://github.com/LKedward/quickstart-fortran)\n    - Intel Fortran Classic 2021.5.0 Build 20211109_000000\n    - NAG Fortran 7.1 Build 7117\n- [Fortran Package Manager](https://github.com/fortran-lang/fpm) (fpm) 0.7.0 alpha\n    - Par-funnel is created as an fpm project.\n- [test-drive](https://github.com/fortran-lang/test-drive) 0.4.0\n    - Par-funnel provide an example of a collaboration with test-drive.\n- [FORD](https://github.com/Fortran-FOSS-Programmers/ford) (optional)\n\n### Get the code\nTo get the code, execute the following commnad:\n\n```console\ngit clone https://github.com/degawa/par-funnel.git\ncd par-funnel\n```\n\n### Build with fpm\nTo build the library using fpm, execute the following command:\n\n```console\nfpm build\n```\n\nThen, install the library using:\n\n```console\nfpm install --prefix path/to/your/libdir\n```\n\n### Reference from your project\nAdd the following `use` statement to modules or procedures calling par-funnel.\n\n```Fortran\nuse :: par_funnel\n```\n\nNote that the source file name is parFunnel.f90, but the module name is par_funnel.\n\n### Reference as a fpm project's dependency\nTo use par-funnel in your fpm project, add the following to the fpm.toml.\n\n```TOML\n[dependencies]\npar-funnel = {git = \"https://github.com/degawa/par-funnel.git\"}\n```\n\n## usage\nAs mentioned above, par-funnel provides three features:\n- Making an arguments list to pass to a procedure under test.\n- Supporting optional arguments.\n- Handling results of parameterized tests.\n\nThese are achieved by using the user-defined types provided by par-funnel.\n\nSee basic examples for an overview of the features.\n\n## Basic examples\n### test parameterization\nAn example of test parameterization using `test_parameter_type` can be found at `example/1.testParameter/doublify.f90`.\n\nA unit test using `test_parameter_type` is as follows:\n1. describe arguments and expected results for the procedure under test.\n2. allocate the `test_parameter_type` instance according to the arguments and expected results.\n3. declare namelists for the arguments and expected results.\n4. read the arguments and expected results from the namelists.\n5. execute the procedure under test with the arguments, and get the actual values.\n6. check the actual values compared to the expected results.\n\nParameterization can be done by repeating steps 3 through 6 with the `test_parameter_type` array.\n\nThe `new_test_parameter` returns a `test_parameter_type` instance that contains the namelists for arguments and expected results. In this example, a function under test is `doublify`, which returns the value of input multiplied by 2.\n```Fortran\nuse :: par_funnel\n\ntype(test_parameter_type), allocatable :: params(:)\nparams = [ \u0026\n         new_test_parameter(arguments='input=1', expected=\"output=2\") \u0026\n         , new_test_parameter(arguments='input=2', expected=\"output=4\") \u0026\n         ]\n```\n\nThen declares namelists for aruments and expected results.\n```Fortran\ninteger(int32) :: input, output\n\nnamelist /arguments/ input\nnamelist /expected/ output\n```\n\nIn the `do`-loop, using `case` as the loop counter, read namelists via the internal file.\n```Fortran\nread (unit=params(case)%arguments_namelist, nml=arguments)\nread (unit=params(case)%expected_namelist, nml=expected)\n```\n\nExecuting the procedure under test, `doublify`, with the arguments read from namelist, and then checking the result.\n```Fortran\ninteger(int32) :: expected, actual\n\nexpect = output\nactual = doublify(input)\n\nif (actual == expect) then\n...\n```\n\n#### note\n- The group name of the namelists must be `arguments` and `expected`.\n- Par-funnel cannot parse namelists.\n    - Blank spaces at both sides of `=` are not allowed.\n    - A blank space must separate variable groups, for example `new_test_parameter(arguments='input1=1 input2=2', expected=\"output=2\")`\n- Par-funnel calls each parameterized test executed in a unit test a \"test case.\"\n\n### work with optional arguments\nAn example of a parameterized test including an optional argument using the `arguments_presence_type` can be found at `example/2.argumentsPresence/int2Str.f90`.\n\nIn this example, a function under test is `int_to_str`, which converts an integer to a string and returns the converted string. `int_to_str` has two optional arguments, `format` and `less_digits`. If `format` is passed, the conversion is performed according to the format specified by `format.` If the digits specified by `format` is less than the integer to be converted and `less_digits` is present, `less_digits` is changed to `.true.`.\n\nThe namelist feature can automatically detect the presence of arguments, but the user must specify the optional arguments.\n\nThe `arguments_presence` returns an `arguments_presence_type` instance. Arguments are logical arrays representing the presence of optional arguments. The type-bound procedure `presented` find an argument name from the namelist of arguments stored in the `test_parameter_type` variable.\n```Fortran\nuse :: par_funnel\ntype(arguments_presence_type) :: arg_pres\narg_pres = arguments_presence([params(case)%presented(\"fmt\"), \u0026\n                               params(case)%presented(\"less_digits\")])\n```\n\nEvaluate the value of the `arguments_presence` instance, i.e., the presence of optional arguments, and call the procedure considering it.\n```Fortran\nif (arg_pres .has. [.false., .false.]) \u0026\n    act_string = int_to_str(input)\nif (arg_pres .has. [.true., .false.]) \u0026\n    act_string = int_to_str(input, fmt)\nif (arg_pres .has. [.false., .true.]) \u0026\n    act_string = int_to_str(input, less_digits=less_digits)\nif (arg_pres .has. [.true., .true.]) \u0026\n    act_string = int_to_str(input, fmt, less_digits)\n```\n\nIn the current implementaion, the `arguments_presence_type` is not necessary and can be substituted with an allocatable logical array. The `==` operator for logical arrays is also implemented for the use of logical arrays.\n```Fortran\nlogical, allocatable :: arg_pres(:)\n\n...\n\narg_pres = [params(case)%presented(\"fmt\"), \u0026\n            params(case)%presented(\"less_digits\")]\n\nif (arg_pres == [.false., .false.]) \u0026\n    act_string = int_to_str(input)\nif (arg_pres == [.true., .false.]) \u0026\n    act_string = int_to_str(input, fmt)\nif (arg_pres == [.false., .true.]) \u0026\n    act_string = int_to_str(input, less_digits=less_digits)\nif (arg_pres == [.true., .true.]) \u0026\n    act_string = int_to_str(input, fmt, less_digits)\n```\n\n\nThis approach produces more combinations when the number of optional arguments exceeds 2. Improvements and Efficient implementations are needed.\n\n#### note\n- `test_parameter_type` and `arguments_presence_type` can be handled together using `parameterization_spec_type`. An example can be found at `example/5.parameterizationSpec/parameterizationSpec.f90`.\n\n### handling results of test cases\nAn example of the `test_results_type` for gathering parameterized test cases can be found at `example/3.testResults/results.f90`.\n\nA parameterized test is expected not to stop when a test under a condition fails and continues with the remaining conditions. In such a case, it is necessary to gather the results of each test case.\n\nThe `test_results_type` is introduced to gather the results of a parameterized test. `test_results_type` must be declared as a variable and constructed according to `test_parameter_type`.\n```Fortran\nuse :: par_funnel\n\ntype(test_parameter_type), allocatable :: params(:)\ntype(test_results_type) :: results\n\n! construct test parameter and declare namelists\n\nresults = new_test_results_for(params)\n```\n\nIn the parameterized test loop, the test results are gathered by the type-bound procedure `check_test()` to pass a logical value representing the success/failure of a test case and a message corresponding to the test result.\n```Fortran\ndo case = 1, results%get_number_of_test_cases()\n    ! doing a test under a condition\n\n    if (.not. params(case)%presented(\"less_digits\")) then\n        cond = (act_string == trim(exp_string))\n        message = ...\n    else\n        cond = (act_string == trim(exp_string)) .and. (less_digits .eqv. exp_less_digits)\n        message = ...\n    end if\n\n    call results%check_test(case, cond, message)\nend do\n```\n\nSome type-bound procedures, such as `get_number_of_failed_cases`, `all_cases_successful`, and `get_summary_message`, are available to confirm a kind of test summary.\n```Fortran\nif (results%get_number_of_failed_cases() \u003e 0) then\n    print *, results%get_summary_message()\n    error stop\nend if\n```\n\n#### note\nIn this example, all test cases will pass, and no message will be output. Replacing `cond` with `.false.` in the `check_test` forces all test cases to fail and message output.\n\n## collaboration with unit test frameworks\nAgain I explain that par-funnel is not a unit test framework and is intended to be used in collaboration with other unit test frameworks. Par-funnel can work with a framework if it has an assertion procedure that takes a logical value meaning that a condition is satisfied or not, and an error message.\n\nAn example of collaboration with test-drive, a community-made unit test framework, can be found at `example/4.collabo/testdrive.f90`.\n\nA significant change from `example/3.testResults/results.f90` is to replace the checking results with a procedure call provided by test-drive.\n```diff\n- if (results%get_number_of_failed_cases() \u003e 0) then\n-     print *, results%get_summary_message()\n-     error stop\n- end if\n+ call check(error, results%all_cases_successful(), results%get_summary_message())\n```\n\nThe test-drive requires making at least one test suite gathering unit tests like the below:\n\n```Fortran\ntest_suite = [ \u0026\n             new_unittest(\"doublify(), it should return 2 when input 1\", \u0026\n                          doublify_should_return_2_when_input_1) \u0026\n             , new_unittest(\"doublify(), it should return 4 when input 2\", \u0026\n                            doublify_should_return_4_when_input_2) \u0026\n             , new_unittest(\"doublify(), it should return -2 when input -1\", \u0026\n                            doublify_should_return_minus_4_when_input_minus_1) \u0026\n             , new_unittest(\"doublify(), it should return 0 when input 0\", \u0026\n                            doublify_should_return_0_when_input_0) \u0026\n             ]\n```\n\nIf a procedure under test has many arguments, the number of unit tests increases, and with it, the source code becomes more bloated and messy. In addition, it isn't easy, especially for non-native English speakers, to find proper names that include the procedure name under tests and test conditions under the 63-character limit. par-funnel can solve these problems.\n\nParameterization of unit tests with par-funnel must be done manually and is more complicated than with other libraries, such as xUnit and pytest. It would be great if these could be improved.\n\n### API Document\nThe API documentation can be generated using [FORD](https://github.com/Fortran-FOSS-Programmers/ford).\n\n```console\nford api-doc-ford-settings.md\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdegawa%2Fpar-funnel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdegawa%2Fpar-funnel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdegawa%2Fpar-funnel/lists"}