【问题标题】:Implementing `make check` or `make test`实施`make check`或`make test`
【发布时间】:2011-06-23 02:19:15
【问题描述】:

如何使用 Make 实现一个简单的回归测试框架? (如果这很重要,我正在使用 GNU Make。)

我当前的 makefile 看起来像这样(为简单起见进行了编辑):

OBJS = jscheme.o utility.o model.o read.o eval.o print.o

%.o : %.c jscheme.h
    gcc -c -o $@ $<

jscheme : $(OBJS)
    gcc -o $@ $(OBJS)

.PHONY : clean

clean :
    -rm -f jscheme $(OBJS)

我想要一组回归测试,例如expr.in 测试一个“好”的表达式和unrecognized.in 测试一个“坏”的表达式,expr.cmp & @ 987654325@ 是每个的预期输出。手动测试如下所示:

$ jscheme < expr.in > expr.out 2>&1
$ jscheme < unrecognized.in > unrecognized.out 2>&1
$ diff -q expr.out expr.cmp # identical
$ diff -q unrecognized.out unrecognized.cmp
Files unrecognized.out and unrecognized.cmp differ

我想在 makefile 中添加一组规则,如下所示:

TESTS = expr.test unrecognized.test

.PHONY test $(TESTS)

test : $(TESTS)

%.test : jscheme %.in %.cmp
    jscheme < [something.in] > [something.out] 2>&1
    diff -q [something.out] [something.cmp]

我的问题:
• 我在 [something] 占位符中放了什么?
• 有没有办法将来自diff 的消息替换为“测试expr 失败”的消息?

【问题讨论】:

  • 从哪里来的所有临时文件?有什么问题
  • @reinierpost:如果您有更好的方法来进行这些比较,请务必发布包含它们的答案 - 这正是我所要求的那种帮助.
  • 抱歉,我以为我取消了那个问题。你确实需要临时文件。

标签: testing makefile gnu-make regression-testing


【解决方案1】:

如问题中所述,您的原始方法是最好的。您的每个测试都采用一对预期输入和输出的形式。 Make 非常有能力遍历这些并运行测试;无需使用 shell for 循环。事实上,这样做你就失去了并行运行测试的机会,并且为自己创造了额外的工作来清理临时文件(这不是必需的)。

这是一个解决方案(以bc 为例):

SHELL := /bin/bash

all-tests := $(addsuffix .test, $(basename $(wildcard *.test-in)))

.PHONY : test all %.test

BC := /usr/bin/bc

test : $(all-tests)

%.test : %.test-in %.test-cmp $(BC)
    @$(BC) <$< 2>&1 | diff -q $(word 2, $?) - >/dev/null || \
    (echo "Test $@ failed" && exit 1)

all : test 
    @echo "Success, all tests passed."

该解决方案直接解决了您最初的问题:

  • 您要查找的占位符是$&lt;$(word 2, $?),分别对应于先决条件%.test-in%.test-cmp。与@reinierpost 评论相反,不需要临时文件。
  • 差异消息被隐藏并使用echo 替换。
  • 应使用 make -k 调用 makefile 以运行所有测试,无论单个测试是失败还是成功。
  • make -k all 只有在所有测试都成功时才会运行。

通过利用文件命名约定 (*.test-in) 和 GNU make functions for file names,在定义 all-tests 变量时,我们避免手动枚举每个测试。作为奖励,这意味着该解决方案可以开箱即用地扩展到数万个测试,因为在 GNU make 中变量的长度是 unlimited。这比基于 shell 的解决方案要好,后者一旦打开操作系统command line limit就会崩溃。

【讨论】:

【解决方案2】:

制作一个测试运行器脚本,它采用测试名称并从中推断输入文件名、输出文件名和样本数据:

#!/bin/sh
set -e
jscheme < $1.in > $1.out 2>&1
diff -q $1.out $1.cmp

那么,在你的Makefile

TESTS := expr unrecognised

.PHONY: test
test:
    for test in $(TESTS); do bash test-runner.sh $$test || exit 1; done

您也可以尝试实现类似automakesimple test framework

【讨论】:

  • 我喜欢 shell 循环的想法,而不是我最初的单独目标计划。 shell 脚本不是一个坏主意,但我想我会将整个内容合并到 makefile 中。
  • 我也真的不想打开整个自动工具的蠕虫罐头。
  • @jcsalomon:我不是说使用自动工具,而是研究测试系统的行为。如果你改变主意,我会把它留在这里:lrde.epita.fr/~adl/autotools.html。请记住避开Makefile 中的$ 标志。它抓住了我几次。
【解决方案3】:

我最终的结果是这样的:

TESTS = whitespace list boolean character \
    literal fixnum string symbol quote

.PHONY: clean test

test: $(JSCHEME)
    for t in $(TESTS); do \
        $(JSCHEME) < test/$$t.ss > test/$$t.out 2>&1; \
        diff test/$$t.out test/$$t.cmp > /dev/null || \
            echo Test $$t failed >&2; \
    done

它基于 Jack Kelly 的想法,包括 Jonathan Leffler 的提示。

【讨论】:

  • 你会希望那里有一个|| exit 1;否则你只是忽略了所有的测试失败。
  • @l0b0,我也不想在第一次失败后退出。告诉你什么:如果有任何测试失败,你会想出一个干净的方法来exit 1 - 但在所有测试运行之后,我会将我接受的答案更改为那个。
  • @ChandanChoudhury,您的问题值得单独发表; cmets 部分不是尝试诊断 Makefile 问题的有用地方。
【解决方案4】:

我将解决您关于差异的问题。你可以这样做:

差异文件 1 文件 2 > /dev/null || echo 测试 blah blah 失败 >&2

虽然您可能想使用 cmp 而不是 diff。

另一方面,您可能会发现继续采取行动会有所帮助 暴跌并使用automake。您的 Makefile.am(完整) 看起来像:

bin_PROGRAMS = jscheme jscheme_SOURCES = jscheme.c utility.c model.c read.c eval.c print.c jscheme.h TESTS = 测试脚本

您将免费获得很多非常好的目标,包括功能非常齐全的测试框架。

【讨论】:

    猜你喜欢
    • 2019-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-21
    • 2014-08-31
    • 2016-05-18
    相关资源
    最近更新 更多