Tôi đang tự hỏi làm thế nào để kiểm tra chức năng sản xuất đồ họa. Tôi có một chức năng đơn giản âm mưu img
:Làm thế nào để kiểm tra đầu ra đồ họa của các chức năng?
img <- function() {
plot(1:10)
}
Trong gói của tôi, tôi muốn tạo một thử nghiệm đơn vị cho chức năng này sử dụng testthat
. Vì plot
và bạn bè của mình trong đồ họa cơ sở chỉ trả NULL
một đơn giản expect_identical
không đang làm việc:
library("testthat")
## example for a successful test
expect_identical(plot(1:10), img()) ## equal (as expected)
## example for a test failure
expect_identical(plot(1:10, col="red"), img()) ## DOES NOT FAIL!
# (because both return NULL)
Đầu tiên tôi nghĩ về âm mưu vào một tập tin và so sánh checksums md5 để đảm bảo rằng đầu ra của chức năng là như nhau:
md5plot <- function(expr) {
file <- tempfile(fileext=".pdf")
on.exit(unlink(file))
pdf(file)
expr
dev.off()
unname(tools::md5sum(file))
}
## example for a successful test
expect_identical(md5plot(img()),
md5plot(plot(1:10))) ## equal (as expected)
## example for a test failure
expect_identical(md5plot(img()),
md5plot(plot(1:10, col="red"))) ## not equal (as expected)
Điều đó hoạt động tốt trên Linux nhưng không hoạt động trên Windows. Đáng ngạc nhiên md5plot(plot(1:10))
kết quả trong một md5sum mới tại mỗi cuộc gọi. Bên cạnh vấn đề này, tôi cần tạo nhiều tệp tạm thời.
Tiếp theo, tôi đã sử dụng recordPlot
(trước tiên tạo thiết bị trống, hãy gọi hàm và ghi lại đầu ra của nó). Điều này hoạt động như mong đợi:
recPlot <- function(expr) {
pdf(NULL)
on.exit(dev.off())
dev.control(displaylist="enable")
expr
recordPlot()
}
## example for a successful test
expect_identical(recPlot(plot(1:10)),
recPlot(img())) ## equal (as expected)
## example for a test failure
expect_identical(recPlot(plot(1:10, col="red")),
recPlot(img())) ## not equal (as expected)
Có ai biết cách tốt hơn để kiểm tra đầu ra đồ họa của hàm không?
EDIT: liên quan đến các điểm @josilber hỏi trong nhận xét của anh ấy.
Trong khi cách tiếp cận recordPlot
hoạt động tốt bạn cần phải viết lại các chức năng toàn bộ âm mưu trong các thử nghiệm đơn vị. Điều đó trở nên phức tạp đối với các chức năng âm mưu phức tạp. Nó sẽ được tốt đẹp để có một phương pháp cho phép để lưu trữ một tập tin (*.RData
hoặc *.pdf
, ...), trong đó có một hình ảnh chống lại bạn có thể so sánh trong các thử nghiệm trong tương lai. Cách tiếp cận md5sum
không hoạt động vì md5sums khác nhau trên nền tảng khác nhau. Via recordPlot
bạn có thể tạo một tập tin *.RData
nhưng bạn không thể dựa vào định dạng của nó (từ recordPlot
trang thủ công):
Định dạng của lô ghi có thể thay đổi giữa các phiên bản R. lô ghi có thể không được sử dụng như một định dạng lưu trữ vĩnh viễn cho lô R.
Có lẽ nó sẽ có thể để lưu trữ một tập tin hình ảnh (*.png
, *.bmp
, vv), nhập nó và so sánh nó từng pixel một ...
EDIT2: Đoạn mã sau minh họa cho tài liệu tham khảo mong muốn cách tiếp cận tập tin bằng cách sử dụng svg như đầu ra. Đầu tiên là chức năng helper cần thiết:
## plot to svg and return file contant as character
plot_image <- function(expr) {
file <- tempfile(fileext=".svg")
on.exit(unlink(file))
svg(file)
expr
dev.off()
readLines(file)
}
## the IDs differ at each `svg` call, that's why we simple remove them
ignore_svg_id <- function(lines) {
gsub(pattern = "(xlink:href|id)=\"#?([a-z0-9]+)-?(?<![0-9])[0-9]+\"",
replacement = "\\1=\"\\2\"", x = lines, perl = TRUE)
}
## compare svg character vs reference
expect_image_equal <- function(object, expected, ...) {
stopifnot(is.character(expected) && file.exists(expected))
expect_equal(ignore_svg_id(plot_image(object)),
ignore_svg_id(readLines(expected)), ...)
}
## create reference image
create_reference_image <- function(expr, file) {
svg(file)
expr
dev.off()
}
Một thử nghiệm sẽ là:
create_reference_image(img(), "reference.svg")
## create tests
library("testthat")
expect_image_equal(img(), "reference.svg") ## equal (as expected)
expect_image_equal(plot(1:10, col="red"), "reference.svg") ## not equal (as expected)
Thật đáng buồn này không hoạt động trên nhiều nền tảng khác nhau.Thứ tự (và tên) của các yếu tố svg hoàn toàn khác nhau trên Linux và Windows.
Sự cố tương tự tồn tại cho png
, jpeg
và recordPlot
. Các tệp kết quả khác nhau trên tất cả các nền tảng.
Hiện tại, giải pháp làm việc duy nhất là cách tiếp cận recPlot
ở trên. Nhưng do đó Tôi cần phải viết lại toàn bộ các chức năng âm mưu trong các bài kiểm tra đơn vị của mình.
P.S .: Tôi hoàn toàn nhầm lẫn về các md5sums khác nhau trên Windows. Có vẻ như họ tùy thuộc vào thời gian tạo ra các tập tin tạm thời:
# on Windows
table(sapply(1:100, function(x)md5plot(plot(1:10))))
#4693c8bcf6b6cb78ce1fc7ca41831353 51e8845fead596c86a3f0ca36495eacb
# 40 60
Có vẻ như giải pháp 'recordPlot' của bạn hoạt động tốt cho trường hợp sử dụng của bạn, nhưng sau đó bạn hỏi ở cuối câu hỏi nếu có ai biết cách tốt hơn để kiểm tra. Bạn có thể giải thích về những gì bạn đang tìm kiếm, hay không, tại sao cách tiếp cận hiện tại của bạn với 'recordPlot' là không đủ? – josliber