{"id":18854350,"url":"https://github.com/urbanjost/m_framework","last_synced_at":"2026-02-05T13:30:21.278Z","repository":{"id":158164087,"uuid":"628702784","full_name":"urbanjost/M_framework","owner":"urbanjost","description":"aggregate small modules used for unit testing, comparing results to expected values, logging, creating messages, ...","archived":false,"fork":false,"pushed_at":"2024-12-09T02:45:03.000Z","size":4616,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-30T18:27:45.730Z","etag":null,"topics":["fortran","fortran-package-manager","fpm","unit-testing"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/urbanjost.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-16T18:59:55.000Z","updated_at":"2024-12-09T02:45:07.000Z","dependencies_parsed_at":null,"dependency_job_id":"e8a67097-5237-4f80-a7ec-c19d49e96837","html_url":"https://github.com/urbanjost/M_framework","commit_stats":{"total_commits":89,"total_committers":1,"mean_commits":89.0,"dds":0.0,"last_synced_commit":"c785b73f60f503d0dfac7b15a41ec215a57cef3b"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/urbanjost%2FM_framework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/urbanjost%2FM_framework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/urbanjost%2FM_framework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/urbanjost%2FM_framework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/urbanjost","download_url":"https://codeload.github.com/urbanjost/M_framework/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239793065,"owners_count":19697893,"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":["fortran","fortran-package-manager","fpm","unit-testing"],"created_at":"2024-11-08T03:47:56.395Z","updated_at":"2026-02-05T13:30:21.197Z","avatar_url":"https://github.com/urbanjost.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![](docs/images/framework.gif)](https://urbanjost.github.io/M_framework/fpm-ford/index.html)\n# [M\\_framework](https://urbanjost.github.io/M_framework/man3.html)\n\n## Synopsis\nM\\_framework(3f) is an aggregate of Fortran modules useful for creating\nand performing unit tests for Fortran.\n\nThe support modules are useful on their own for terminal messages,\ncomparing expected values to results, writing logfiles and playback\njournals.\n\nIt supports easy integration with the fpm(1) \"test\" subcommand,\nin particular.\n\n + basic timing is included\n + messages can be composed almost like list-directed I/O when calling the\n   unit test procedures.\n + hooks are provided to external local applications\n   M\\_framework(3f) comes with a hook that allows calling your own programs to\n   integrate with local logging tools, report generators, spreadsheets or other\n   local infrastructure. The example program \"bookkeeper\" is included that\n   uses the hook to write example report data files:\n\n    + [CSV](https://urbanjost.github.io/M_framework/bookkeeper.csv),\n    + [HTML](https://urbanjost.github.io/M_framework/bookkeeper.html)\n    + [NAMELIST](https://urbanjost.github.io/M_framework/bookkeeper.nml)\n\n   The hook can call any local program with an interface similar to\n   \"bookkeeper\".  Modify the program for local needs such as sending e-mail\n   alerts and so on without having to change the tests.\n\n + designed for integration with fpm(1).\n   In conjunction with fpm(1) it is easy to run the tests with the --runner\n   option, allowing for integration with other utilities as well such as\n   the GNU debugger gdb(1), valgrind(1), and other tools.\n\n### programs included to generate skeleton test program\nExample programs are provided to create unit test skeleton programs to\nease usage.\n\n### Easily used with github CD/CI\nExample CD/CI scripts that can be used with github are in the .github/\ndirectory that assume your tests can by run by using \"fpm test\".\n\n```fortran\nprogram M_test_suite_M_intrinsics\nuse,intrinsic :: iso_fortran_env, only : \u0026\n\u0026 stderr=\u003eERROR_UNIT, stdin=\u003eINPUT_UNIT, stdout=\u003eOUTPUT_UNIT\n!\nuse M_framework, only : unit_test_start,unit_test,unit_test_end, \u0026\n                 unit_test_mode, unit_test_level, unit_test_stop\nuse M_framework, only : CHECK_PREFIX ! change column one of messages\n!\n!use M_mymodule ! load any modules you will be testing\nimplicit none\ndouble precision,allocatable :: expected(:), answers(:), input(:)\ndouble precision,parameter :: PI=atan(1.0d0)*4\n!! setup\n   !---------------------------------------------------\n   !OPTIONAL:\n   !  values used in prefix column for various messages\n   !  the default is to set them all to the basename of\n   !  the executable running tests, but they can be \n   !  altered. For example:\n   CHECK_PREFIX=prefix(                  \u0026\n    check_MSG    =  'check_msg:   ', \u0026\n    check        =  'check:       ', \u0026\n    check_START  =  'check_start: ', \u0026\n    check_STOP   =  'check_stop:  ', \u0026\n    check_END    =  'check_end:   '  \u0026\n   )\n   !---------------------------------------------------\n   !OPTIONAL: \n   ! the options available at run-time on the command\n   ! line can have their defaults selected. See the \n   ! man-page for the procedure for details.\n   call unit_check_mode(\n     ( keep_going=.true. ,\n     flags=[character(len=0) ::], \n     luns=[stdout], \n     command, \u0026\n     brief=.false. ,\n     interactive=.false. ,\n     CMDLINE='',\n     debug=.false. ,\n     match\n     )\n   !---------------------------------------------------\n!! test each subroutine\n   call test_sqrt()\n   call test_cos()\n   call test_sin()\n!! teardown\n   call unit_test_stop()\ncontains\nsubroutine test_sqrt()\ninteger :: i\n   call unit_test_start('sqrt',msg='calculate the square root')\n   input   =[1.0d0,4.0d0,9.0d0]\n   expected=[1.0d0,2.0d0,3.0d0]\n   answers=[(sqrt(input(i)),i=1,size(input))]\n   call unit_test('sqrt', all(expected.eq.answers),\u0026\n      \u0026 'check table of values')\n   call unit_test('sqrt', sqrt(25.0d0).eq.5.0d0,\u0026\n      \u0026 'got',sqrt(25.0d0),'expected',5.0d0)\n   call unit_test_end('sqrt',msg='')\nend subroutine test_sqrt\n\nsubroutine test_sin()\n   call unit_test_start('sin',msg='calculate the sine of a value')\n   call unit_test_end('sin',msg='')\nend subroutine test_sin\n\nsubroutine test_cos()\n   call unit_test_start('cos',msg='calculate the cosine of a value')\n   call unit_test_end('cos',msg='')\nend subroutine test_cos\n\nend program M_test_suite_M_intrinsics\n```\n\nExample output files from an fpm(1) package that uses\nM_framework illustrate the different types of output that can be \ngenerated.\n + [CSV](https://urbanjost.github.io/M_framework/bookkeeper.csv).\n   Generally. comma-separated files can be read directly into spreadsheet\n   programs, sqlite3, and several common databases.\n + [CSV runtimes](https://urbanjost.github.io/M_framework/bookkeeper_clicks.csv)\n   Another CSV file that is a record of the runtimes between a\n   unit test start and end.\n + [HTML](https://urbanjost.github.io/M_framework/bookkeeper.html)\n   An example of a formatted report that can be displayed in a browser.\n + [NAMELIST](https://urbanjost.github.io/M_framework/bookkeeper.nml).\n   Essentially this is a metafile that records the unit test calls.\n   It is very easy for a custom Fortran program to read back a NAMELIST\n   file and generate custom outputs instead of modifying bookkeeper(1).\n\nThe bookkeeper(1) program is an example program that is expected to\nbe customized. It provides for parsing the parameters passed to a\nM_frame external program.\n\n------------------------------------------------------------------------------------------\n\n## Supports FPM ![fpm](docs/images/fpm_logo.gif)\n\nThe impatient can try this, assuming git(1) and fpm(1) are installed.\n\nDownload the github repository and build it with fpm(1) \nand create a test fpm(1) project:\n\n```bash\n#!/bin/bash\n# first you need to install a few example programs\ncd /tmp\ngit clone https://github.com/urbanjost/M_framework.git\ncd M_framework\n# install the \"unit_test\", \"bookkeeper\", and \"test_suite\"\n# example programs; assuming the default install puts\n# them in your path:\nfpm install\n# \"fpm help install\" describes how to customize where the\n# programs are installed.\n#\n# go to your fpm package test/ directory.\n# here, we will make one up\nfpm new /tmp/tryit  # create test project\ncd /tmp/tryit/test\n# so lets say you plan on adding procedures \"a\",\"b\",and \"c\" to your src/tryit.f90\n# project code. Set up individual skeleton tests for each procedure.\nunit_test a b c                         # a file for each test\ntest_suite a b c \u003e test_suite_tryit.f90 # or a single file\ncd ..  # go to the top of the project \n# add M_framework to the developer dependencies\ncat \u003e\u003e fpm.toml \u003c\u003c\\EOF\n[dev-dependencies]\nM_framework    = { git = \"https://github.com/urbanjost/M_framework.git\" }\nEOF\n#\n# test the package \nfpm test\n# if will say the procedures are untested. Put real calls\n# in to unit_test(3f) and see how the default report \n# changes\n#\n# so now to run the default tests is as simple as\nfpm test\n# run just one test\nfpm test unit_test_a\n# run tests using globbing; eg. all tests beginning with \"unit\\_\"\nfpm test 'unit_*'\n# display help on the interactive command options\nfpm test -- --help\n\n# you can pass parameters and/or change the unit_test_mode(3f)\n# calls to change some of the test behavior\n```\nSo once you want to use this on your own projects, you would normally\njust add M\\_framework(3f) as a developer dependency\nin your fpm.toml project file and start making tests that call M\\_framework(3f).\n\nThe optional programs demonstrated (\"unit\\_test\", \"test\\_suite\") are just simple\nstand-alone programs that make skeleton programs to run tests that you can \ncustomize (and rename too to avoid confusion).\n```bash\n# some useful things to try. Check out the man-pages for all the unit_test_*(3f)\n# procedures.\n# Then look at\nunit_test --help\nfpm test -- help\n# run the demo bookkeeper script\nfpm test -- command=bookkeeper\n# and look at the bookkeeper*.* files in the top of the project\n```\n## Documentation   ![docs](docs/images/docs.gif)\n\n### User\n\n - An [index](https://urbanjost.github.io/M_framework/man3.html) to HTML versions\n   of the manpages\n\n - single pages that uses javascript to combine all the HTML descriptions\n   of the man-pages is at\n    + [BOOK_M_framework](https://urbanjost.github.io/M_framework/BOOK_M_framework.html)\n    + [BOOK_M_framework__verify](https://urbanjost.github.io/M_framework/BOOK_M_framework__verify.html)\n    + [BOOK_M_framework__approx](https://urbanjost.github.io/M_framework/BOOK_M_framework__approx.html)\n    + [BOOK_M_framework__journal](https://urbanjost.github.io/M_framework/BOOK_M_framework__journal.html)\n    + [BOOK_M_framework__msg](https://urbanjost.github.io/M_framework/BOOK_M_framework__msg.html)\n\n - man-pages in\n    + [manpages.zip](https://urbanjost.github.io/M_framework/manpages.zip)\n    + [manpages.tgz](https://urbanjost.github.io/M_framework/manpages.tgz)\n\n### Developer\n   + [ford-generated developers' document](https://urbanjost.github.io/M_framework/fpm-ford/index.html)\n   + [github action status](docs/STATUS.md)\n\n## Additional Directory Descriptions\n\n   - src/ is where the source for the M_framework(3f) module code resides\n   - docs/ contains HTML documentation and the manpage archives\n   - test/ contains simple test programs\n   - example/ has demos\n\n## References\n + See the .github directory in [easy](https://github.com/urbanjost/easy)\n + for examples of CD/CI scripts that assume your package can be tested with an \"fpm test\" command.\n + examples that use M_framework in github fpm packages:\n     * [M_strings](https://github.com/urbanjost/M_strings)\n     * [M_process](https://github.com/urbanjost/M_process)\n\n   These packages used a different reincarnation of the testing harness and are in the process of\n   being changed to use M_framework(3f) more appropriately, but still contain some useful examples.\n\n + [Fortran Package Manager](https://github.com/fortran-lang/fpm)\n + [fpm(1) registry](https://github.com/fortran-lang/fpm-registry)\n + [Fortran Wiki: unit testing list](https://fortranwiki.org/fortran/show/Unit+testing+frameworks)\n + [ford(1)](https://politicalphysicist.github.io/ford-fortran-documentation.html) for generating documentation for your projects\n\n## Note\nM\\_framework(3f) is subject to interface changes so the generally recommended\npractice of using a specific commit when using it an an external\nfpm(1) dependency is highly encouraged.\n\nAny feedback (features, performance, ease of use, ...) is appreciated,\nparticularly in the ongoing development phase.\n\u003c!--\n====================================================================================================\n\nDOES NOT WORK.\n\nIf you put that in the \"fpm.rsp\" file as:\n\n     @debug\n     options test '*' --verbose --runner 'gdb -ex run --args' -- --flags=9997,9998,9999 --luns=6\n\nYou can then just enter \"fpm @debug\"\n\n====================================================================================================\n--\u003e\n\n\u003c!--\n## Building the Module\nA conventional GNU/Linux or Unix install:\n\n```bash\n     git clone https://github.com/urbanjost/M_framework.git\n     cd M_framework/src\n     # change Makefile if not using one of the listed compilers\n\n     # for gfortran\n     make clean\n     make F90=gfortran gfortran\n\n     # for ifort\n     make clean\n     make F90=ifort ifort\n\n     # for nvfortran\n     make clean\n     make F90=nvfortran nvfortran\n```\nThis will compile the Fortran module and basic test\nprograms\n--\u003e\n\u003c!--\n  fpm test --compiler nvfortran --flag '-D__NVFORTRAN -Mbackslash'\n--\u003e\n\u003c!--\nNow add some real calls to unit_test(3f) in the new test files as you\ndevelop the new project. Make sure to run \"unit_test name\" when you add\na new public procedure.\n--\u003e\n\u003c!--\nI used the optional system command to place results into databases\nand SQLite3 files which was great for QA reports, but when using\n**M_framework** in a distributed environment I do not use that at all.\n\nSome of the tools I use like a script that runs nm(1) on the object files\nto create an initial scaffold calling each routine found is too specific\nto my particular platforms currently to distribute; but I find I am much\nmore likely to generate tests given a skeleton program that keeps reminding\nme everything is \"UNTESTED\".\n--\u003e\n\u003c!--\nCommand line options use the built-in NAMELIST-group\nSee the tests for M_time and M_strings for more elaborate examples.\n## Samples\n+ comparing files numerically\n+ comparing floating point scalar values and arrays\n+ using the many Fortran capabilities in expressions (any(3f), all(3f), pack(3f), merge(3f), ...)\n+ dealing with program execution being stopped\n--\u003e\n\u003c!--\nalthough there might be one procedure tested per file; or the tests\ncould even be internal to \"mymodule\", or the tests might call a system\ncommand that enters info into a database and so on this is a model that\nworks well for basic numeric procedures in particular.\n--\u003e\n\u003c!--\nThere can be a lot more to it than this, but even such a simple report\ngenerated with a simple command in a CLI or batch environment can be a\npowerful tool for ensuring any deviations in behavior are caught quickly\nand that testing becomes a natural part of the development cycle --\nmake a code change; run \"fpm test\"; correct any issues detected.\n\n## Making a skeleton test for each procedure\n\nThe first step is to make a skeleton for a test of each procedure.\nCreating unit tests can be cumbersome and the reward for creating them\nis often delayed until they uncover a problem you did not expect.\nSo to encourage taking the first step there is an included program\ncalled unit_test(1).\n\nInitially autodiscovery was used to look through the code for procedures\ncalled \"test_suite_NAME\" and a test program was created on the fly;\nwhere the test procedures were typically included in the same files as\nthe procedures they tested. But when many packages are used in the tests\nthis quickly leads to the core package having many external dependencies\nused only for testing. So although this approach works well in a stable\ncustom environment it complicates the distribution of fpm packages. And\nthe autodiscovery tools considered so far have a lot of infrastructure\nrequirements and/or are very system dependent. So the decision was made\nto just use standard Fortran programs that make it very easy to generate\ntest program skeletons instead.\n--\u003e\n\u003c!--\nOf course any user-supplied method such as a file listing what tests to\nexecute or skip or build the tests conditionally with a preprocessor or\nusing procedure pointers in clever ways to point to a no-op procedure\nare just a few examples of alternate methods.\n--\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Furbanjost%2Fm_framework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Furbanjost%2Fm_framework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Furbanjost%2Fm_framework/lists"}