linux - Fortran 在锁定环境中的基本单元测试

标签 linux unit-testing testing fortran legacy

尝试向大量现有 (Fortran 90) 代码添加一些基本单元测试的明智方法是什么,这些代码仅在锁定系统上开发,没有机会安装任何第 3 方框架。我几乎只局限于标准的 Linux 工具。目前,代码库使用非常有限的一组测试在整个系统级别进行测试,但这非常耗时(运行数天),因此在开发过程中很少使用

理想情况下,希望能够逐步将有针对性的测试添加到关键系统,而不是一次性彻底检查整个代码库。

采用下面的示例模块,并假设一个断言类型宏的实现,详见 Assert in Fortran

MODULE foo
    CONTAINS
    FUNCTION bar() RESULT (some_output)
        INTEGER :: some_output
        some_output = 0
    END FUNCTION bar
END MODULE foo

我想到了几种可能的方法,但实现这些方法可能存在我不知道的技术或行政挑战:

  1. 每个模块都有单独的测试模块,如下所示,并有一个主测试运行器来调用每个模块中的每个函数

    MODULE foo_test
        CONTAINS
        SUBROUTINE bar_test()
         ! ....
        END SUBROUTINE bar_test()
    END MODULE foo_test
    
  2. 与上述类似的方法,但每个测试都有单独的可执行文件。明显的好处是单一故障不会终止所有测试,但可能更难管理大量测试可执行文件,并且可能需要大量额外代码。

  3. 使用预处理器在每个模块中包含包含测试的主要功能,例如在 gfortran 中 Fortran 90 with C/C++ style macro (e.g. # define SUBNAME(x) s ## x)并使用构建脚本自动测试主代码文件中预处理器定界符之间存储的 main。

我已尝试使用一些现有的 Fortran 框架(如 > Why the unit test frameworks in Fortran rely on Ruby instead of Fortran itself? > 中所述),但对于这个特定项目,我无法在我使用的系统上安装其他工具。

最佳答案

在我看来,断言机制不是 Fortran 单元测试的主要关注点。正如您链接的答案中提到的,Fortran 存在多个单元测试框架,例如 funit 和 FRUIT。

不过,我认为,主要问题是依赖关系的解决。您可能有一个包含许多相互依赖的模块的庞大项目,并且您的测试应该涵盖使用许多其他模块的模块之一。因此,您需要找到这些依赖项并相应地构建单元测试。一切都归结为编译可执行文件,断言的优势非常有限,因为您无论如何都需要定义测试并自己进行比较。

我们正在使用 Waf 构建我们的 Fortran 应用程序, 它带有一个单元 testing utility itself .现在,我不知道您是否可以使用它,但唯一的要求是 Python,它应该可以在几乎任何平台上使用。一个缺点是,测试依赖于返回码,这不容易从 Fortran 获得,至少在 Fortran 2008 之前不能以可移植的方式获得,它建议在返回码中提供停止代码。所以我修改了我们项目中的成功检查。我希望测试写入一些字符串,并在输出中检查它,而不是检查返回码:

    def summary(bld):
            """
            Get the test results from last line of output::

                    Fortran applications can not return arbitrary return codes in
                    a standarized way, instead we use the last line of output to
                    decide the outcome of a test: It has to state "PASSED" to count
                    as a successful test.

                    Otherwise it is considered as a failed test. Non-Zero return codes
                    that might still happen are also considered as failures.

                    Display an execution summary:

                    def build(bld):
                            bld(features='cxx cxxprogram test', source='main.c', target='app')
                            from waflib.extras import utest_results
                            bld.add_post_fun(utest_results.summary)
            """
            from waflib import Logs
            import sys

            lst = getattr(bld, 'utest_results', [])

            # Check for the PASSED keyword in the last line of stdout, to
            # decide on the actual success/failure of the test.
            nlst = []
            for (f, code, out, err) in lst:
                    ncode = code
                    if not code:
                            if sys.version_info[0] > 2:
                                    lines = out.decode('ascii').splitlines()
                            else:
                                    lines = out.splitlines()
                            if lines:
                                    ncode = lines[-1].strip() != 'PASSED'
                            else:
                                    ncode = True
                    nlst.append([f, ncode, out, err])
            lst = nlst

我还按照惯例添加测试,在构建脚本中只需要提供一个目录,该目录中以 _test.f90 结尾的所有文件都将被假定为单元测试,我们将尝试构建并运行它们:

def utests(bld, use, path='utests'):
    """
    Define the unit tests from the programs found in the utests directory.
    """
    from waflib import Options
    for utest in bld.path.ant_glob(path + '/*_test.f90'):
            nprocs = search_procs_in_file(utest.abspath())
            if int(nprocs) > 0:
                    bld(
                        features = 'fc fcprogram test',
                        source = utest,
                        use = use,
                        ut_exec = [Options.options.mpicmd, '-n', nprocs,
                                   utest.change_ext('').abspath()],
                        target = utest.change_ext(''))
            else:
                    bld(
                        features = 'fc fcprogram test',
                        source = utest,
                        use = use,
                        target = utest.change_ext(''))

您可以在 Aotus library 中找到这样定义的单元测试. wscript 中使用了哪些通过:

from waflib.extras import utest_results
utest_results.utests(bld, 'aotus')

然后也可以只构建单元测试的子集,例如通过运行

./waf build --target=aot_table_test

在奥图斯。我们的测试范围有点小,但我认为这个基础设施展览会实际上相当不错。一个测试可以简单地使用项目中的所有模块,并且可以轻松地编译它而无需进一步的麻烦。

现在我不知道这是否适合你,但我会更多地考虑你的测试在你的构建环境中的集成,而不是断言的东西。在每个模块中都有一个测试例程绝对是个好主意,然后可以很容易地从测试程序中调用它。我会尝试针对每个要测试的模块一个可执行文件,其中每个模块当然可以包含多个测试。

关于linux - Fortran 在锁定环境中的基本单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32434904/

相关文章:

testing - testcafe - 无法点击禁用的输入

ruby - 所有规范的 rspec shared_context 和 include_context

Linux、LDAP、半径

linux - 在 Linux 中将一个串口连接到另一个串口

python - 修补在另一个函数中导​​入的函数

java - 从命令行运行 java 类,包括 jar 中的库

html - HTML CSS 的最佳测试浏览器?

linux - 命令在脚本中不起作用,但在 shell 中有效

linux - 我如何比较 3 个文件(以查看它们之间的共同点)?

javascript - E2E 测试、单元测试和使用 $httpBackend 模拟数据之间的混淆