library(testit)
op = options(device = function(file = NULL, ...) {
pdf(file, ...)
dev.control('enable') # important! otherwise plots get discarded
})
evaluate = evaluate::evaluate
classes = function(x) vapply(x, function(x) class(x)[1], character(1))
# remove the blank plot
assert('blank plots are removed', {
res = evaluate('layout(t(1:2))')
(identical(classes(res), 'source'))
})
assert('plots generated by par(), palette() or layout() are removed', {
res = evaluate('par(mfrow = c(1, 2))\npie(islands)\nbarplot(islands)')
(identical(classes(res), rep(c('source', 'recordedplot'), c(3, 1))))
res = evaluate('layout(t(1:2))\npie(islands)\nbarplot(islands)')
(identical(classes(res), rep(c('source', 'recordedplot'), c(3, 1))))
res = evaluate('pie(islands)\nbarplot(islands)\npar(mfrow = c(1, 2))')
res = merge_low_plot(res)
(identical(classes(res), rep(c('source', 'recordedplot'), length = 5)))
res = evaluate('pie(islands)\npar(cex.main=1.2)\nbarplot(islands)')
res = merge_low_plot(res)
(identical(classes(res), c('source', 'recordedplot')[c(1, 2, 1, 1, 2)]))
res = evaluate('par(cex.main=1.2)\npalette(c("red","black"))\nbarplot(islands)')
(identical(classes(res), rep(c('source', 'recordedplot'), c(3, 1))))
})
assert('merge low-level changes', {
res = evaluate('plot(1)\npoints(1.1, 1.1)')
(classes(res) %==% rep(c('source', 'recordedplot'), 2))
(classes(merge_low_plot(res)) %==% rep(c('source', 'recordedplot'), c(2, 1)))
})
assert('captures grid graphics', {
res = evaluate('library(grid)
grid.newpage()
grid.rect(gp=gpar(fill="grey"))
grid.rect(gp=gpar(fill="red"))')
(classes(res) %==% c('source', 'recordedplot')[c(1, 1, 1, 2, 1, 2)])
res = merge_low_plot(res)
(identical(classes(res), rep(c('source', 'recordedplot'), c(4, 1))))
})
options(op)
# rmarkdown sets dev.args = list(pdf = list(useDingbats = FALSE)) when dev = 'pdf'
if (!has_error({png(); dev.off()})) {
assert('chunk_device() correctly opens the png device with dev.args', {
chunk_device(opts_chunk$merge(list(
dev = 'png', dev.args = list(pdf = list(useDingbats = FALSE))
)))
plot(1:10)
dev.off()
TRUE
})
}
if (requireNamespace("ragg", quietly = TRUE) &&
!has_error({ragg::agg_png(); dev.off()})) {
assert(
'chunk_device() correctly opens the ragg::agg_png device with dev.args',
{
chunk_device(opts_chunk$merge(list(
dev = 'ragg_png', dev.args = list(pdf = list(useDingbats = FALSE))
)))
plot(1:10)
dev.off()
TRUE
}
)
assert(
'ragg_png_dev correctly handles bg dev.arg into background arg',
{
chunk_device(opts_chunk$merge(list(
dev = 'ragg_png', dev.args = list(bg = "grey")
)))
plot(1:10)
dev.off()
TRUE
}
)
}
# should not error (find `pdf` correctly in grDevices, instead of the one
# defined below)
pdf = function() {}
do.call(pdf_null, list(7, 7))
dev.off()
gen_source = function(x) structure(x, class = 'source')
gen_plotrc = function(x) structure(factor(x), class = c('factor', 'recordedplot'))
assert('fig_before_code() moves plots before code blocks', {
res = list(
gen_source(1), gen_plotrc('a'), gen_plotrc('b'), gen_source(2), gen_source(3),
gen_plotrc('c'), gen_source(4), gen_plotrc('d')
)
(fig_before_code(res) %==% res[c(2, 3, 1, 4, 6, 5, 8, 7)])
})
assert('plots are rearrange based on fig.keep & fig.show options', {
res = list(gen_source(1), gen_source(2))
(rearrange_figs(res, 'high', NULL, 'asis') %==% res)
# only one plot to keep
res = c(evaluate('plot(1)'), list(gen_source(1)))
(rearrange_figs(res, 'high', NULL, 'asis') %==% res)
(rearrange_figs(res, 'all', NULL, 'asis') %==% res)
(rearrange_figs(res, 'last', NULL, 'asis') %==% res)
(rearrange_figs(res, 'first', NULL, 'asis') %==% res)
(rearrange_figs(res, 'index', 2, 'asis') %==% res)
# several plots
res = c(list(gen_source(1)), evaluate('plot(1)\npoints(1.1, 1.1)'),
list(gen_plotrc('b'), gen_source(2)))
(rearrange_figs(res, 'high', NULL, 'asis') %==% res[-3])
(rearrange_figs(res, 'all', NULL, 'asis') %==% res)
(rearrange_figs(res, 'all', NULL, 'hold') %==% res[c(1:2, 4, 7, 3, 5, 6)])
(rearrange_figs(res, 'last', NULL, 'asis') %==% res[c(-3, -5)])
(rearrange_figs(res, 'first', NULL, 'asis') %==% res[c(-5, -6)])
(rearrange_figs(res, 'none', NULL, 'asis') %==% res[c(-3, -5, -6)])
# correspond to options$fig.keep with numeric vector
(rearrange_figs(res, 'index', 1, 'asis') %==% res[c(-5, -6)])
(rearrange_figs(res, 'index', c(2, 3), 'asis') %==% res[c(-3)])
(rearrange_figs(res, 'index', c(2, 3), 'hold') %==% res[c(1:2, 4, 7, 5, 6)])
(rearrange_figs(res, 'index', c(1, 2, 3), 'asis') %==% res)
})
# should not error when a plot label contains special characters and sanitize=TRUE
if (xfun::loadable('tikzDevice') && Sys.which('pdflatex') != '' &&
(!is.na(Sys.getenv('CI', NA)) || Sys.getenv('USER') == 'yihui' || !xfun::is_macos())) {
knit('knit-tikzDevice.Rnw', quiet = TRUE)
unlink(c('*-tikzDictionary', 'figure', 'knit-tikzDevice.tex'), recursive = TRUE)
}
# https://github.com/yihui/knitr/issues/1166
knit(text = "\\Sexpr{include_graphics('myfigure.pdf', error = FALSE)}", quiet = TRUE)
assert('include_graphics() expands ~', {
path1 = "~/test.png"
(!has_warning(include_graphics("img/test.png", error = FALSE)))
(unclass(suppressWarnings(include_graphics(path1, error = FALSE))) %==% path.expand(path1))
})
with_par = function(expr, ...) {
# set par
op = graphics::par(...)
# reset on exit
on.exit(graphics::par(op))
# save changed state
global.pars = par(no.readonly = TRUE)
# reset par
graphics::par(op)
# simulate what happens when global.par = TRUE by restoring pars
par2(global.pars)
# evaluate in this state
force(expr)
}
assert("par2 correctly handles specific pars", {
(par2(NULL) %==% NULL)
# correctly changed
(with_par(par("col") %==% "red", col = "red"))
(with_par(par("cex") %==% 2, cex = 2))
# unchanged
old = par("fig")
(with_par(par("fig") %==% old, fig = old / 2))
old = par("fin")
(with_par(par("fin") %==% old, fin = old / 2))
old = par("pin")
(with_par(par("pin") %==% old, pin = old / 2))
old = par("usr")
(with_par(par("usr") %==% old, usr = old / 2))
old = par("ask")
(with_par(par("ask") %==% old, ask = !old))
# Does not work - something else is changing plt when setting everything
# old = par("plt")
# (with_par(par("plt") %==% old, plt = old / 2))
})
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.