测试 – 是否可以在Julia中组织完整性检查以便在加载包时轻松编译?

我正在开发一个需要快速运行且正确的软件包.我想只编写一个函数,但有两个“函数”版本:一个在检测到任何有趣的业务时立即停止,另一个尽可能快地运行.我的想法是在随机的输入样本上运行严格的函数版本,如果没有任何失败,请在整个输入集上运行快速版本.我绝对没有计算能力来对每一次输入进行每次检查,即使我可以睡得更轻松. (我对这个已经是解决验证问题的错误方法持开放态度,所以如果你有一个更好的组织方法,请随意建议,而不是对下面的具体改进.)

到目前为止,我已经解决了这个问题的临时解决方案,如下所示:

module Foo

export some_function

const using_removeable_assertions = true

macro RemoveablyAssert(the_condition)
    if using_removeable_assertions
        return esc(:($the_condition || error("Assertion failed.")))
    else
        return esc("")
    end
end

function some_function(x::Float64,y::Float64)
    #Say this should only be called on positive numbers
    @RemoveablyAssert x>0
    @RemoveablyAssert y>0
    (x + y/2.0)::Float64
end

end

using Foo

这样可行,

julia> some_function(1.0,1.0)
1.5

julia> some_function(1.0,-1.0)
ERROR: Assertion failed.
 in some_function at none:18

如果我重新加载我手动更改const的模块using_removeable_assertions = false,那么它不仅会跳过这些检查,

julia> some_function(1.0,1.0)
1.5

julia> some_function(1.0,-1.0)
0.5

但事实上,正如预期的那样,本机代码与我从未放过这些行的相同功能相同:

julia> @code_native some_function(1.0,1.0)
    .section    __TEXT,__text,regular,pure_instructions
Filename: none
Source line: 19
    pushq   %rbp
    movq    %rsp, %rbp
    movabsq $13174486592, %rax      ## imm = 0x31142B640
Source line: 19
    vmulsd  (%rax), %xmm1, %xmm1
    vaddsd  %xmm0, %xmm1, %xmm0
    popq    %rbp
    ret

例如.如果我定义

function some_function_2(x::Float64,y::Float64)
    (x + y/2.0)::Float64
end

然后

julia> @code_native some_function_2(1.0,1.0)
    .section    __TEXT,__text,regular,pure_instructions
Filename: none
Source line: 2
    pushq   %rbp
    movq    %rsp, %rbp
    movabsq $13174493536, %rax      ## imm = 0x31142D160
Source line: 2
    vmulsd  (%rax), %xmm1, %xmm1
    vaddsd  %xmm0, %xmm1, %xmm0
    popq    %rbp
    ret

所以:这些是解决方案的两个理想属性,

>打开/关闭一系列严格的健全性检查应该与翻转开关一样简单
>在“关闭”的情况下,本机代码应该完全清除对检查的任何引用;特别是,应该没有
关闭情况下的性能差异

但我想要的解决方案是手动少一点,更习惯或标准.

注意,手动定义每个函数的两个版本是不切实际的,因为维护它们会很麻烦.所以我考虑使用另一个为每个some_function生成some_function_safe的宏,然后只是手动运行安全检查,但我希望有一个更好的解决方案,类似于julia的命令行参数,但具体到模块.例如.使用[safemode = yes] Foo.

相关地,我想知道是否有一个很好的方法来处理多个检查“级别”,除了一些类似的黑客,例如定义全局“安全级别”,然后使用宏有条件地注入检查,前提是全局安全级别超过特定检查级别.如果上面的[safemode = yes]想法可以实现,那么肯定也可以实现,但我想知道是否有更好的方法来组织这些类型的检查.

(FWIW,我认为这些检查是对一个大型测试套件的补充,而不是替代它.单独的测试套件不会让我睡不着觉.另外,虽然我的例子是参数验证,但我想用这些检查不仅仅是前后条件,但也适用于所有类型的健全性检查,例如检查方法体中的不变量,并针对我已经制定和修复的错误进行特定检查,等等)

最佳答案 我为Julia的工作做了一些相似的事情,我有一个宏可以被完全删除,或者如果编译进去,检查一个我可以设置的掩码来控制哪些实际被检查.

就单元测试而言,我们一直在使用FactCheck.jl包.

我们计划设置代码进行代码覆盖率测试(我们不能将我们的内部代码放在GitHub上,设置起来很简单),我认为这对于单元测试来说是至关重要的,以确保合理的代码.

如果您的代码在GitHub上,我认为从工作服或codecov.io添加代码覆盖支持是微不足道的.

点赞