R 生成的 PDF 具有不一致的 MD5 校验和
PDFs produced by R having inconsistent MD5 checksum
我正在使用 testthat 测试 R 包。为 S3 方法 plot.foo
编写测试非常令人头疼,因为它只是 returns NULL
,所以我决定将绘图保存到文件中并检查自上次以来是否已更改运行.
pdf(file='plot_foo.pdf')
plot.foo(bar)
dev.off()
tools::md5sum('plot_foo.pdf')
问题是每次我用相同的输入得到不同的结果。不过,输出看起来是一样的。
replicate(10, {
pdf(file='plot.pdf')
plot(1:10, 10:1)
dev.off()
Sys.sleep(1)
tools::md5sum('plot.pdf')
})
请注意,您需要在每次迭代之间等待一段时间,否则文件将完全相同,这让我怀疑某些基于时间的元数据已更改。
plot.pdf plot.pdf
"5a0c096fe088342bc3c3d5960c5da1c9" "40d93c26b4901aef55a32b75473d05d2"
plot.pdf plot.pdf
"9815c6d9b2e94cda763a486fcd2ddf08" "a8e8db82d06b79f98416fa034b5aee46"
plot.pdf plot.pdf
"c2770250dbef3b60706559114c434851" "91c8cf124eb61ddebd3edbbb2d01677f"
plot.pdf plot.pdf
"d1594bd83b97fc890410a4c305366682" "f05197f165ec04df3dac4664494f4617"
plot.pdf plot.pdf
"64427124c6a6454e8f0e5944de20be95" "ff1abf2b31dfe688cf8f5994e409cc6d"
如何强制 R 生成一致的 PDF?我暂时切换到 PostScript 以进行测试,但我更喜欢 PDF,因为它得到更好的支持(Windows 似乎没有内置的 PostScript 查看器),因此也可以用作文档。
虽然我认为它在某些方面有点粗糙,但我认为 vdiffr
会让你做你想做的事。
首先,我要创建一个包;现在是假的,但有必要,因为 vdiffr
只能在 tightly-controlled 环境中工作:使用 testthat
.
的包
usethis::create_package("~/Whosebug/nalzok")
setwd("~/Whosebug/nalzok")
usethis::use_testthat()
创建一个 test_something.R
测试文件。
context("basic plot tests")
baseplot1 <- function() hist(1:10)
vdiffr::expect_doppelganger("base 1", baseplot1)
(我假设 hist(1:10)
是相关且有趣的东西。基本图需要是一个函数,ggplot2
对象不需要;有关更多信息,请参阅文档。)
我原以为我可以直接调用vdiffr::expect_doppelganger
(因为大多数testthat::expect_*
函数通常可以),但它需要先"managed"(设置) .
vdiffr::manage_cases(".")
每张图片都需要 "verified"(人工),因此这将打开一个闪亮的应用程序,它遍历每个预期的分身:
验证后,每次 test
包时,它都会验证图像是否未更改:
devtools::test()
# Loading nalzok
# Testing nalzok
# v | OK F W S | Context
# v | 1 | basic plot tests
# == Results =====================================================================
# OK: 1
# Failed: 0
# Warnings: 0
# Skipped: 0
如果发生变化(可能将 hist(1:10)
更改为 hist(2:11)
),下一次测试将失败:
devtools::test()
# Loading nalzok
# Testing nalzok
# v | OK F W S | Context
# x | 0 1 | basic plot tests
# --------------------------------------------------------------------------------
# test_something.R:3: failure: (unknown)
# Figures don't match: base-1.svg
# --------------------------------------------------------------------------------
# == Results =====================================================================
# OK: 0
# Failed: 1
# Warnings: 0
# Skipped: 0
它通过为每个期望创建一个包含目录和 .svg
文件的 ./tests/testthat/figs/
目录来实现这一点,虽然您不需要与其交互,但它对 .../figs/
要version-controlled(你做version-control你包对吧?)。
一些注意事项,我想:
正在保存到.svg
个文件;如果您的 S3 plot.foo
函数不能很好地与 SVG 配合使用(会发生这种情况吗?我不知道),那么我(还)不知道如何处理它;
因为它使用 text-based SVG 格式,它会注意到点或框或其他东西是否发生了变化,但仅在一些基本公差范围内;例如,即使某些 meta-parameters(限制)被更改 足够 ,也会触发失败。这通常很好,因为我相信测试应该能够适应微小的变化(上游库等)。
hist(1:10) # pass
hist(1:10, xlim=c(0,10)) # pass, that's the default x-limit given the data
hist(1:10, xlim=c(0,10+1e-5)) # pass, close enough?
hist(1:10, xlim=c(0,10+1e-4)) # FAIL
我正在使用 testthat 测试 R 包。为 S3 方法 plot.foo
编写测试非常令人头疼,因为它只是 returns NULL
,所以我决定将绘图保存到文件中并检查自上次以来是否已更改运行.
pdf(file='plot_foo.pdf')
plot.foo(bar)
dev.off()
tools::md5sum('plot_foo.pdf')
问题是每次我用相同的输入得到不同的结果。不过,输出看起来是一样的。
replicate(10, {
pdf(file='plot.pdf')
plot(1:10, 10:1)
dev.off()
Sys.sleep(1)
tools::md5sum('plot.pdf')
})
请注意,您需要在每次迭代之间等待一段时间,否则文件将完全相同,这让我怀疑某些基于时间的元数据已更改。
plot.pdf plot.pdf
"5a0c096fe088342bc3c3d5960c5da1c9" "40d93c26b4901aef55a32b75473d05d2"
plot.pdf plot.pdf
"9815c6d9b2e94cda763a486fcd2ddf08" "a8e8db82d06b79f98416fa034b5aee46"
plot.pdf plot.pdf
"c2770250dbef3b60706559114c434851" "91c8cf124eb61ddebd3edbbb2d01677f"
plot.pdf plot.pdf
"d1594bd83b97fc890410a4c305366682" "f05197f165ec04df3dac4664494f4617"
plot.pdf plot.pdf
"64427124c6a6454e8f0e5944de20be95" "ff1abf2b31dfe688cf8f5994e409cc6d"
如何强制 R 生成一致的 PDF?我暂时切换到 PostScript 以进行测试,但我更喜欢 PDF,因为它得到更好的支持(Windows 似乎没有内置的 PostScript 查看器),因此也可以用作文档。
虽然我认为它在某些方面有点粗糙,但我认为 vdiffr
会让你做你想做的事。
首先,我要创建一个包;现在是假的,但有必要,因为
的包vdiffr
只能在 tightly-controlled 环境中工作:使用testthat
.usethis::create_package("~/Whosebug/nalzok") setwd("~/Whosebug/nalzok") usethis::use_testthat()
创建一个
test_something.R
测试文件。context("basic plot tests") baseplot1 <- function() hist(1:10) vdiffr::expect_doppelganger("base 1", baseplot1)
(我假设
hist(1:10)
是相关且有趣的东西。基本图需要是一个函数,ggplot2
对象不需要;有关更多信息,请参阅文档。)我原以为我可以直接调用
vdiffr::expect_doppelganger
(因为大多数testthat::expect_*
函数通常可以),但它需要先"managed"(设置) .vdiffr::manage_cases(".")
每张图片都需要 "verified"(人工),因此这将打开一个闪亮的应用程序,它遍历每个预期的分身:
验证后,每次
test
包时,它都会验证图像是否未更改:devtools::test() # Loading nalzok # Testing nalzok # v | OK F W S | Context # v | 1 | basic plot tests # == Results ===================================================================== # OK: 1 # Failed: 0 # Warnings: 0 # Skipped: 0
如果发生变化(可能将
hist(1:10)
更改为hist(2:11)
),下一次测试将失败:devtools::test() # Loading nalzok # Testing nalzok # v | OK F W S | Context # x | 0 1 | basic plot tests # -------------------------------------------------------------------------------- # test_something.R:3: failure: (unknown) # Figures don't match: base-1.svg # -------------------------------------------------------------------------------- # == Results ===================================================================== # OK: 0 # Failed: 1 # Warnings: 0 # Skipped: 0
它通过为每个期望创建一个包含目录和
.svg
文件的./tests/testthat/figs/
目录来实现这一点,虽然您不需要与其交互,但它对.../figs/
要version-controlled(你做version-control你包对吧?)。
一些注意事项,我想:
正在保存到
.svg
个文件;如果您的 S3plot.foo
函数不能很好地与 SVG 配合使用(会发生这种情况吗?我不知道),那么我(还)不知道如何处理它;因为它使用 text-based SVG 格式,它会注意到点或框或其他东西是否发生了变化,但仅在一些基本公差范围内;例如,即使某些 meta-parameters(限制)被更改 足够 ,也会触发失败。这通常很好,因为我相信测试应该能够适应微小的变化(上游库等)。
hist(1:10) # pass hist(1:10, xlim=c(0,10)) # pass, that's the default x-limit given the data hist(1:10, xlim=c(0,10+1e-5)) # pass, close enough? hist(1:10, xlim=c(0,10+1e-4)) # FAIL