tests/testthat/test-checkHzDepthLogic.R

context("SoilProfileCollection depth error checking")

## sample data
data(sp3)

expect_silent({depths(sp3) <- id ~ top + bottom})

test_that("hzDepthTests works as expected", {
  hdep <- horizonDepths(sp3)

  # vector of top and bottom depths -> 4 logical test results
  res <- hzDepthTests(sp3[[hdep[1]]], sp3[[hdep[2]]])
  expect_equal(names(res)[res], c("depthLogic","overlapOrGap")) # depthLogic & overlapOrGap errors
  expect_equal(length(res), 4)

  # mismatched lengths (top and bottom must have same number of values)
  expect_error(hzDepthTests(sp3[[hdep[1]]], sp3[[hdep[2]]][1]))
})

test_that("checkHzDepthLogic() works as expected", {

  # these data should be clean
  res <- checkHzDepthLogic(sp3)

  # result is an data.frame
  expect_true(inherits(res, 'data.frame'))

  # number of rows should match length(SPC)
  expect_true(nrow(res) == length(sp3))

  # all clear
  expect_true(all(!res$depthLogic))
  expect_true(all(!res$sameDepth))
  expect_true(all(!res$missingDepth))
  expect_true(all(!res$overlapOrGap))
  expect_true(all(res$valid))
  
  # works on a horizons()-like data.frame
  h <- horizons(sp3)
  h$foo <- h$id
  h$id <- NULL
  h$hzdept <- h$top
  h$top <- NULL
  h$hzdepb <- h$bottom
  h$bottom <- NULL
  expect_true(all(checkHzDepthLogic(h, c("hzdept","hzdepb"), "foo", fast = TRUE)$valid))
  expect_true(all(checkHzDepthLogic(h, c("hzdept","hzdepb"), "foo", fast = TRUE, byhz = TRUE)$valid))
})


test_that("preservation of hzID", {
  
  # modify hzID in-place
  hzID(sp3) <- sprintf("%04d", as.integer(sp3$hzID))
  
  res <- checkHzDepthLogic(sp3, byhz = TRUE, fast = FALSE)
  
  expect_true(
    all(hzID(sp3) == res$hzID)
  )
  
  res <- checkHzDepthLogic(sp3, byhz = TRUE, fast = TRUE)
  
  expect_true(
    all(hzID(sp3) == res$hzID)
  )
  
  # data.frame interface
  
  res <- checkHzDepthLogic(horizons(sp3), idname = idname(sp3),
                           hzdepths = horizonDepths(sp3),
                           byhz = TRUE, fast = FALSE)
  
  # data.frame interface always returns calculated integer hzID
  expect_true(
    all(1:nrow(sp3) == res$hzID)
  )
  
  res <- checkHzDepthLogic(horizons(sp3), idname = idname(sp3), 
                           hzdepths = horizonDepths(sp3),
                           byhz = TRUE, fast = TRUE)
  
  expect_true(
    all(1:nrow(sp3) == res$hzID)
  )
})

test_that("checkHzDepthLogic() depth logic errors", {

  # local copy
  x <- sp3[1, ]
  x$top[1] <- 10
  res <- checkHzDepthLogic(x)

  # errors only affect the first profile in this set
  expect_true(res$depthLogic[1])
  expect_false(res$valid[1])
  
  res2 <- checkHzDepthLogic(x, byhz = TRUE)
  expect_true(res2$depthLogic[1])
  expect_false(res2$valid[1])
})

test_that("checkHzDepthLogic() same top / bottom depths", {

  # local copy
  x <- sp3[7, ]
  x$bottom[3] <- x$top[3]
  res <- checkHzDepthLogic(x)

  # errors only affect the first profile in this set
  expect_true(res$sameDepth[1])
  expect_false(res$valid[1])
  
  res2 <- checkHzDepthLogic(x, byhz = TRUE)
  expect_true(res2$sameDepth[3])
  expect_false(res2$valid[3])
})


test_that("checkHzDepthLogic() NA in depths", {

  # local copy
  x <- sp3[4, ]
  x$bottom[3] <- NA
  res <- checkHzDepthLogic(x)

  # errors only affect the first profile in this set
  expect_true(res$missingDepth[1])
  expect_false(res$valid[1])
  
  res2 <- checkHzDepthLogic(x, byhz = TRUE)
  expect_true(res2$missingDepth[3])
  expect_false(res2$valid[3])
})


test_that("checkHzDepthLogic() gap", {

  # local copy
  x <- sp3[8, ]
  # create a gap
  x$top[4] <- 82
  res <- checkHzDepthLogic(x)

  # errors only affect the first profile in this set
  expect_true(res$overlapOrGap[1])
  expect_false(res$valid[1])
  
  # NOTE: OVERLAP OR GAP NOT MEANINGFUL FOR byhz=TRUE
  # result is NA
  res2 <- checkHzDepthLogic(x, byhz = TRUE)
  expect_true(is.na(res2$overlapOrGap[1]))
  expect_true(res2$valid[1])
})


test_that("checkHzDepthLogic() overlap", {

  # local copy
  x <- sp3[8, ]
  # create overlap
  x$top[4] <- 75
  res <- checkHzDepthLogic(x)

  # errors only affect the first profile in this set
  expect_true(res$overlapOrGap[1])
  expect_false(res$valid[1])
  
  # NOTE: OVERLAP OR GAP NOT MEANINGFUL FOR byhz=TRUE
  # result is NA
  res2 <- checkHzDepthLogic(x, byhz = TRUE)
  expect_true(is.na(res2$overlapOrGap[1]))
  expect_true(res2$valid[1])
})


test_that("splitLogicErrors", {
  data(sp4)
  depths(sp4) <- id ~ top + bottom

  # no errors (all list elements return NULL)
  expect_equal(unlist(splitLogicErrors(sp4)), c(NULL, NULL, NULL, NULL))

  # NA in top depth triggers depth logic and missing depth errors
  data(sp4)
  sp4$top[1] <- NA
  expect_message(depths(sp4) <- id ~ top + bottom)

  res <- splitLogicErrors(sp4)
  
  # the same profile occurs in two groups, since NA causes depth logic and missingDepth errors
  expect_true(profile_id(res$depthLogic) == profile_id(res$missingDepth))

  # interact = TRUE gets these in the same (interaction) group
  #  each SPC profile occurs once, name/number elements varies with your data
  #  (and whether or not you use split.default(..., drop = TRUE))
  res2 <- splitLogicErrors(sp4, interact = TRUE, sep = "_", drop = TRUE)
  expect_true(length(res2$depthLogic__missingDepth_) == 1)
})
ncss-tech/aqp documentation built on April 19, 2024, 5:38 p.m.