@@ -151,8 +151,12 @@ test_that("de-hardcoded defaults match the previous behavior (regression)", {
151151
152152# --- constant memory --------------------------------------------------------
153153
154- test_that(" constant memory resolves to on" , {
155- cm <- .resolve_constant_memory(list (data.frame (a = 1 )), xl_properties())
154+ test_that(" constant memory is off for small data, on when asked for" , {
155+ # a one-cell frame saves nothing by streaming, so the default is off; the
156+ # request is what separates "nothing forbids it" from "it is worth it"
157+ small <- list (data.frame (a = 1 ))
158+ expect_equal(.resolve_constant_memory(small , xl_properties())$ on , 0L )
159+ cm <- .resolve_constant_memory(small , xl_properties(), request = TRUE )
156160 expect_equal(cm $ on , 1L )
157161 expect_equal(cm $ reasons , character (0 ))
158162 expect_equal(.properties_payload(xl_properties())$ constant_memory , 1L )
@@ -166,7 +170,8 @@ test_that("workbooks write the same content with constant memory on and off", {
166170 flag = c(TRUE , FALSE , NA ),
167171 when = as.Date(" 2020-01-01" ) + 0 : 2 ,
168172 stringsAsFactors = FALSE )
169- on_path <- write_tmp(list (D = xl_sheet(df , autofilter = TRUE , freeze = " A2" )))
173+ on_path <- write_tmp(list (D = xl_sheet(df , autofilter = TRUE , freeze = " A2" )),
174+ constant_memory = TRUE )
170175 # No feature writexl writes today turns the mode off, so drive the off path by
171176 # mocking the resolver. It must stay exercised: the C side and libxlsxwriter
172177 # behave differently with row streaming disabled, and a phase that needs the
@@ -276,3 +281,76 @@ test_that("the other custom property types are unaffected", {
276281 expect_match(cust , " <vt:bool>true" )
277282 expect_match(cust , " <vt:filetime>2020-01-02T00:00:00Z" , fixed = TRUE )
278283})
284+
285+ test_that(" the constant_memory decision follows its five rules in order" , {
286+ small <- list (data.frame (a = 1 : 5 ))
287+ # ~1.4M cells, comfortably past the default threshold at 110 B/cell
288+ big <- list (data.frame (matrix (1 , nrow = 130000 , ncol = 12 )))
289+ blocked <- list (list (merges = list (1 )))
290+ clear <- list (list (merges = NULL ))
291+
292+ # 1. FALSE wins outright, without consulting anything else
293+ # FALSE returns before the blacklist is even consulted, so it is silent too
294+ expect_silent(r <- .resolve_constant_memory(big , list (), blocked , FALSE ))
295+ expect_equal(r $ on , 0L )
296+ expect_match(r $ reasons , " constant_memory = FALSE" )
297+
298+ # 2. the blacklist is absolute -- TRUE cannot override it, only be told.
299+ # (These also emit the size suggestion, which its own test covers.)
300+ expect_equal(suppressMessages(
301+ .resolve_constant_memory(big , list (), blocked ))$ on , 0L )
302+ expect_warning(r <- suppressMessages(
303+ .resolve_constant_memory(big , list (), blocked , TRUE )), " cannot be honoured" )
304+ expect_equal(r $ on , 0L )
305+
306+ # 3. TRUE overrides the size estimate
307+ expect_equal(.resolve_constant_memory(small , list (), clear , TRUE )$ on , 1L )
308+
309+ # 4. size decides when nothing else does
310+ expect_equal(.resolve_constant_memory(small , list (), clear )$ on , 0L )
311+ expect_match(.resolve_constant_memory(small , list (), clear )$ reasons ,
312+ " below the .* threshold" )
313+ expect_equal(.resolve_constant_memory(big , list (), clear )$ on , 1L )
314+ # ... and the threshold is what moves that line
315+ expect_equal(.resolve_constant_memory(big , list (), clear , NA ,
316+ threshold = 4 * 1024 ^ 3 )$ on , 0L )
317+ })
318+
319+ test_that(" cells are counted across the whole workbook" , {
320+ # libxlsxwriter holds every sheet's cell table until close, so two sheets of
321+ # half the size must decide the same way as one of the full size
322+ half <- data.frame (matrix (1 , nrow = 65000 , ncol = 12 ))
323+ clear <- list (list (merges = NULL ))
324+ expect_equal(.resolve_constant_memory(list (half ), list (), clear )$ on , 0L )
325+ expect_equal(.resolve_constant_memory(list (half , half ), list (), clear )$ on , 1L )
326+ })
327+
328+ test_that(" the suggestion appears only when streaming would have mattered" , {
329+ small <- list (data.frame (a = 1 : 5 ))
330+ big <- list (data.frame (matrix (1 , nrow = 130000 , ncol = 12 )))
331+ blocked <- list (list (merges = list (1 )))
332+ # a small workbook blocked by a feature has nothing to act on, so says nothing
333+ expect_silent(.resolve_constant_memory(small , list (), blocked ))
334+ # a large one is told what it cost, and why
335+ expect_message(.resolve_constant_memory(big , list (), blocked ), " merged range" )
336+ expect_message(.resolve_constant_memory(big , list (), blocked ),
337+ " MB more memory" )
338+ })
339+
340+ test_that(" the decision is silent whenever there is nothing to report" , {
341+ # write_xlsx() is called constantly on small frames; a message on every one
342+ # would be noise, so only a refusal that cost something speaks
343+ small <- list (data.frame (a = 1 : 5 ))
344+ clear <- list (list (merges = NULL ))
345+ expect_silent(.resolve_constant_memory(small , list (), clear ))
346+ expect_silent(.resolve_constant_memory(small , list (), clear , TRUE ))
347+ expect_silent(.resolve_constant_memory(small , list (), clear , FALSE ))
348+ big <- list (data.frame (matrix (1 , nrow = 130000 , ncol = 12 )))
349+ expect_silent(.resolve_constant_memory(big , list (), clear ))
350+ })
351+
352+ test_that(" an invalid constant_memory request is refused" , {
353+ expect_error(.resolve_constant_memory(list (data.frame (a = 1 )), list (), NULL ,
354+ " yes" ),
355+ " must be a single logical" )
356+ })
0 commit comments