Nothing
# Knowledge tests
# ──────────────────────────────────────────────────────────────────────────────
# Knowledge generation using mini-dsl
# ──────────────────────────────────────────────────────────────────────────────
test_that("Knowledge object is created correctly using mini-DSL", {
kn <- knowledge(
tier(
1 ~ V1 + V2,
2 ~ V3,
3 ~ c(V4, V5)
),
V1 %!-->% V3,
V1 %-->% V2,
V2 %-->% V3
)
expect_equal(
kn$vars,
tibble::tibble(
var = c("V1", "V2", "V3", "V4", "V5"),
tier = c("1", "1", "2", "3", "3")
)
)
expect_equal(kn$frozen, FALSE)
expect_equal(
kn$edges,
tibble::tibble(
status = c("forbidden", "required", "required"),
from = c("V1", "V1", "V2"),
to = c("V3", "V2", "V3"),
tier_from = c("1", "1", "1"),
tier_to = c("2", "1", "2")
)
)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("1", "2", "3")
)
)
})
# seeding with data frame
test_that("seeding Knowledge object with a my_df, matrix, or tibble works", {
my_df <- data.frame(X1 = 1, X2 = 2, X3 = 3, X4 = 4, check.names = FALSE)
tbl <- tibble::as_tibble(my_df)
mat <- as.matrix(my_df)
kn <-
knowledge(
my_df,
tier(1 ~ X1, 2 ~ X2 + X3),
X1 %-->% X2
)
kn_tbl <- knowledge(
tbl,
tier(1 ~ X1, 2 ~ X2 + X3),
X1 %-->% X2
)
kn_mat <- knowledge(
mat,
tier(1 ~ X1, 2 ~ X2 + X3),
X1 %-->% X2
)
expect_equal(kn, kn_tbl)
expect_equal(kn, kn_mat)
expect_equal(kn$frozen, TRUE)
expect_equal(
kn$vars,
tibble::tibble(
var = c("X1", "X2", "X3", "X4"),
tier = c("1", "2", "2", NA_character_)
)
)
})
# ──────────────────────────────────────────────────────────────────────────────
# Tiers
# ──────────────────────────────────────────────────────────────────────────────
test_that("tier generation with named tiers using character names", {
kn <- knowledge(
tier(
"One" ~ V1 + V2,
"Two" ~ V3,
"Three" ~ V4 + V5
),
V1 %!-->% V3,
V1 %-->% V2,
V2 %-->% V3
)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("One", "Two", "Three")
)
)
})
test_that("tier generation with named tiers using symbols/expression", {
kn <- knowledge(
tier(
One ~ V1 + V2,
Two ~ V3,
Three ~ V4 + V5
),
V1 %!-->% V3,
V1 %-->% V2,
V2 %-->% V3
)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("One", "Two", "Three")
)
)
})
test_that("tier generation with named tiers using mix of integers, chars, and symbols", {
kn <- knowledge(
tier(
1 ~ V1 + V2,
Two ~ V3,
3 ~ V4 + V5,
"Four" ~ V6,
Five ~ V7 + V8 + V9
),
V1 %!-->% V3,
V1 %-->% V2,
V2 %-->% V3
)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("1", "Two", "3", "Four", "Five")
)
)
})
test_that("tier generation with negative numeric tiers errors", {
expect_error(
knowledge(
tier(
-1 ~ V1 + V2,
)
),
"`tier` must be a single non-empty label or a non-negative numeric literal.",
fixed = TRUE
)
})
######### Tier sorting works correctly ###########
test_that("tier sorts if all numeric", {
kn <- knowledge(
tier(
3 ~ V1 + V2,
1 ~ V3,
2 ~ V4 + V5
)
)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("1", "2", "3")
)
)
expect_equal(kn$vars$tier, c("1", "2", "2", "3", "3"))
kn <- knowledge(
tier(
10 ~ V1 + V2,
1 ~ V3,
2 ~ V4 + V5
)
)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("1", "2", "10")
)
)
expect_equal(kn$vars$tier, c("1", "2", "2", "10", "10"))
})
# ──────────────────────────────────────────────────────────────────────────────
# Tiers using verbs only
# ──────────────────────────────────────────────────────────────────────────────
test_that("tier generation using verbs only", {
kn <- knowledge() |>
add_tier(1) |>
add_tier(2, after = 1) |>
add_tier(3, after = 2) |>
add_to_tier(2 ~ V3)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("1", "2", "3")
)
)
})
test_that("tier generation using verbs only", {
kn <- knowledge() |>
add_tier(One) |>
add_tier(Two, after = One) |>
add_tier(Three, after = Two) |>
add_to_tier(Two ~ V3)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("One", "Two", "Three")
)
)
})
test_that("tier generation using verbs only", {
kn <- knowledge() |>
add_tier(One) |>
add_tier(Three, after = One) |>
add_tier(Two, before = Three) |>
add_to_tier(Two ~ V3)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("One", "Two", "Three")
)
)
})
test_that("tier generation using verbs only", {
kn <- knowledge() |>
add_tier(One) |>
add_tier(2, after = One) |>
add_tier(Three, after = 2) |>
add_to_tier(Three ~ V3)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("One", "2", "Three")
)
)
})
test_that("tier generation with verbs works", {
kn <- knowledge() |>
add_tier(1) |>
add_tier(3, after = 1) |>
add_tier(Two, before = 3) |>
add_tier(Two_and_a_Half, after = Two) |>
add_tier(2.75, before = 3)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("1", "Two", "Two_and_a_Half", "2.75", "3")
)
)
})
# ──────────────────────────────────────────────────────────────────────────────
# Tiers using verbs and mini-DSL
# ──────────────────────────────────────────────────────────────────────────────
test_that("tier generation with mixing DSL and verbs", {
kn <- knowledge(
tier(
1 ~ V1,
2 ~ V2
)
) |>
add_tier(3, after = 2) |>
add_to_tier(3 ~ V3)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("1", "2", "3")
)
)
})
test_that("tier generation with mixing DSL and verbs with symbols", {
kn <- knowledge(
tier(
1 ~ V1,
2 ~ V2
)
) |>
add_tier(Three, after = 2) |>
add_to_tier(Three ~ V3)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("1", "2", "Three")
)
)
})
test_that("tier generation with mixing DSL and verbs with symbols and chars", {
kn <- knowledge(
tier(
"One" ~ V1,
Three ~ V2
)
) |>
add_tier(Two, before = "Three") |>
add_to_tier("Two" ~ V3)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("One", "Two", "Three")
)
)
})
test_that("tier generation with mixing DSL and verbs with symbols and chars", {
kn <- knowledge(
tier(
"One" ~ V1,
Three ~ V2
)
) |>
add_tier(2, after = One) |>
add_to_tier(2 ~ V3)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("One", "2", "Three")
)
)
})
test_that("tier generation with mixing DSL and verbs with symbols and chars", {
kn <- knowledge(
tier(
"One" ~ V1,
Three ~ V2
)
) |>
add_tier(2, after = One) |>
add_to_tier(2 ~ V3)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("One", "2", "Three")
)
)
})
test_that("tier generation with mixing DSL and verbs with symbols and chars", {
kn <- knowledge(
tier(
"One" ~ V1,
Three ~ V2
)
) |>
add_tier(Two, before = Three) |>
add_to_tier(Two ~ V3)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("One", "Two", "Three")
)
)
})
test_that("tier tidyselect helpers works with +", {
kn1 <- knowledge(
tpc_example,
tier(
young ~ starts_with(c("child", "youth")),
old ~ starts_with("old")
)
)
kn2 <- knowledge(
tpc_example,
tier(
young ~ starts_with("child") + starts_with("youth"),
old ~ starts_with("old")
)
)
expect_equal(kn1, kn2)
kn3 <- knowledge(
tpc_example,
tier(
young ~ starts_with("child") + ends_with(c("3", "4")),
old ~ starts_with("old")
)
)
expect_equal(kn1, kn3)
})
# ──────────────────────────────────────────────────────────────────────────────
# Tiers using seq_tiers
# ──────────────────────────────────────────────────────────────────────────────
test_that("tier generation using seq_tiers", {
my_df <- as.data.frame(
matrix(
runif(10), # 10 random numbers in (0,1)
nrow = 1,
ncol = 10,
byrow = TRUE
)
)
names(my_df) <- paste0("X_", 1:10) # label the columns X_1 … X_10
kn <- knowledge(
my_df,
tier(
seq_tiers(
1:10,
ends_with("_{i}")
)
),
X_1 %-->% X_2
)
expect_equal(
kn$tiers,
tibble::tibble(
label = 1:10 |> as.character()
)
)
expect_equal(
kn$vars,
tibble::tibble(
var = paste0("X_", 1:10),
tier = 1:10 |> as.character()
)
)
})
test_that("tier generation using seq_tiers with labels", {
my_df <- data.frame(
X_1 = 1,
X_2 = 2,
tier3_A = 3,
Y5_ok = 4,
check.names = FALSE
)
kn <- knowledge(
my_df,
tier(
seq_tiers(1:2, ends_with("_{i}")), # X_1, X_2
seq_tiers(3, starts_with("tier{i}")), # tier3_…
seq_tiers(5, matches("Y{i}_")) # exact match
)
)
expect_equal(
kn$tiers,
tibble::tibble(
label = c("1", "2", "3", "5")
)
)
expect_equal(
kn$vars,
tibble::tibble(
var = c("X_1", "X_2", "tier3_A", "Y5_ok"),
tier = c(1, 2, 3, 5) |> as.character()
)
)
})
# ──────────────────────────────────────────────────────────────────────────────
# Tiers using 1:n
# ──────────────────────────────────────────────────────────────────────────────
test_that("tier generation using 1:n", {
my_df <- as.data.frame(
matrix(
runif(10), # 10 random numbers in (0,1)
nrow = 1,
ncol = 10,
byrow = TRUE
)
)
names(my_df) <- paste0("X_", 1:10) # label the columns X_1 … X_10
kn <-
knowledge(
my_df,
tier(
1:10
)
)
expect_equal(
kn$tiers,
tibble::tibble(
label = 1:10 |> as.character()
)
)
expect_equal(
kn$vars,
tibble::tibble(
var = paste0("X_", 1:10),
tier = 1:10 |> as.character()
)
)
})
# ──────────────────────────────────────────────────────────────────────────────
# Infix operators errors if my_df and variables don't match
# ──────────────────────────────────────────────────────────────────────────────
test_that("%-->% and %!-->% errors if my_df and variables don't match", {
my_df <- data.frame(V1 = 1, V2 = 2, check.names = FALSE)
expect_error(
knowledge(my_df, 1 %-->% V2),
"Required edge: no variables matched '1' from the left-hand side.",
fixed = TRUE
)
expect_error(
knowledge(my_df, V2 %-->% 1),
"Required edge: no variables matched '1' from the right-hand side.",
fixed = TRUE
)
expect_error(
knowledge(my_df, 1 %!-->% V2),
"Forbidden edge: no variables matched '1' from the left-hand side.",
fixed = TRUE
)
expect_error(
knowledge(my_df, V2 %!-->% 1),
"Forbidden edge: no variables matched '1' from the right-hand side.",
fixed = TRUE
)
})
test_that("forbidden and required errors when no from vars matched", {
my_df <- data.frame(V1 = 1, V2 = 2, check.names = FALSE)
expect_error(
knowledge(
my_df,
1 %!-->% V1
),
"Forbidden edge: no variables matched '1' from the left-hand side.",
fixed = TRUE
)
expect_error(
knowledge(
my_df,
1 %-->% V1
),
"Required edge: no variables matched '1' from the left-hand side.",
fixed = TRUE
)
})
test_that("forbidden and required errors when no to vars matched", {
my_df <- data.frame(V1 = 1, V2 = 2, check.names = FALSE)
expect_error(
knowledge(
my_df,
V1 %!-->% 1
),
"Forbidden edge: no variables matched '1' from the right-hand side.",
fixed = TRUE
)
expect_error(
knowledge(
my_df,
V1 %-->% 1
),
"Required edge: no variables matched '1' from the right-hand side.",
fixed = TRUE
)
})
# ──────────────────────────────────────────────────────────────────────────────
# Tier errors
# ──────────────────────────────────────────────────────────────────────────────
test_that("tier() errors if no formulas are supplied", {
my_df <- tibble::tibble(V1 = 1, V2 = 2)
expect_error(
knowledge(my_df, tier()),
"tier() needs at least one two-sided formula.",
fixed = TRUE
)
})
test_that("add_tier() errors for an empty tier label", {
kn <- knowledge()
expect_error(
add_tier(kn, ""), # invalid label
"`tier` must be a non-empty label.",
fixed = TRUE
)
})
test_that("add_tier() errors when the label already exists", {
kn <- knowledge() |> add_tier(1) # creates tier "1"
expect_error(
add_tier(kn, 1), # duplicate
"Tier label `1` already exists.",
fixed = TRUE
)
})
test_that("add_tier() errors if the anchor tier is missing", {
kn <- knowledge() |> add_tier("first") # only one tier so far
expect_error(
add_tier(kn, "second", after = "ghost"), # bad anchor
"`ghost` does not refer to an existing tier.",
fixed = TRUE
)
})
test_that("add_to_tier() errors when adding existing variable to another tier", {
expect_error(
knowledge(
tier(
One ~ V1 + V2,
2 ~ V3 + V4,
"Three" ~ V5
)
) |>
add_to_tier(One ~ V3 + V4),
"Cannot reassign variable(s) [V3, V4] to tier `One` using add_to_tier().",
fixed = TRUE
)
expect_error(
knowledge(
tier(
One ~ V1 + V2,
2 ~ V3 + V4,
"Three" ~ V5
)
) |>
add_to_tier(2 ~ V3 + V1),
"Cannot reassign variable(s) [V1] to tier `2` using add_to_tier().",
fixed = TRUE
)
})
test_that("add_to_tier() errors if no formulas are supplied", {
kn <- knowledge()
expect_error(
add_to_tier(kn), # no ...
"add_to_tier() needs at least one two-sided formula.",
fixed = TRUE
)
})
test_that("add_to_tier() errors when a non-formula argument is given", {
kn <- knowledge()
expect_error(
add_to_tier(kn, "oops"), # not a formula
"Each argument must be a two-sided formula.",
fixed = TRUE
)
})
test_that("add_to_tier() errors when the target tier does not exist", {
kn <- knowledge() # no tiers yet
expect_error(
add_to_tier(kn, ghost ~ V1), # lhs tier unknown
"Tier `ghost` does not exist. Create it first with add_tier().",
fixed = TRUE
)
})
test_that("add_to_tier() errors when RHS matches no variables", {
kn <- knowledge() |> add_tier("T1") # create tier
# tidyselect call that matches nothing because kn$vars is still empty
expect_error(
add_to_tier(kn, T1 ~ starts_with("foo")),
"matched no variables",
fixed = FALSE
)
})
test_that("tier throws error for one variable in two tiers", {
expect_error(
knowledge(
tier(
1 ~ V1 + V2,
Two ~ V1,
)
),
"Tier specification Two ~ V1 tries to re-assign variable(s) [V1] to a new tier.",
fixed = TRUE
)
})
test_that("tier throws error for using numeric vector without my_df", {
expect_error(
knowledge(
tier(
1:10
)
),
"Using tier(<numeric vector>) needs the data-frame columns first.",
fixed = TRUE
)
})
test_that("tier() errors when numeric vector length != ncol(my_df)", {
my_df <- data.frame(X1 = 1, X2 = 2, X3 = 3, X4 = 4, check.names = FALSE)
expect_error(
knowledge(
my_df,
tier(
1:10
)
),
"Tier vector length (10) must equal number of variables (4).",
fixed = TRUE
)
})
test_that("numeric-vector tier() errors on duplicate indices", {
my_df <- data.frame(A = 1, B = 2, C = 3, check.names = FALSE)
expect_error(
knowledge(
my_df,
tier(1:3), # first time: creates tiers 1,2,3
tier(1:3) # second time: should detect 1,2,3 already exist
),
"Tier index(es) 1, 2, 3 already exist.",
fixed = TRUE
)
})
test_that("tier() throws error when mispecifying tier", {
my_df <- data.frame(A = 1, B = 2, C = 3, check.names = FALSE)
expect_error(
knowledge(
my_df,
tier(2 ~ 1)
),
"Tier specification 2 ~ 1 matched no variables.",
fixed = TRUE
)
expect_error(
knowledge(
my_df,
tier(2 ~ X)
),
"Unknown variable(s): [X]
They are not present in the data frame provided to this Knowledge object.",
fixed = TRUE
)
expect_error(
knowledge(
data.frame(V1 = 1, V2 = 2, check.names = FALSE),
tier(
1 ~ V1,
"V2"
)
),
"Each tier() argument must be a two-sided formula.",
fixed = TRUE
)
# this throws some evaluation error
expect_error(
knowledge(
tier(
1 ~ V1,
V4
)
)
)
my_df <- data.frame(A = 1, B = 2, check.names = FALSE)
expect_error(
knowledge(
my_df,
tier(
1 ~ starts_with("Z")
)
),
"Tier specification 1 ~ starts_with(\"Z\") matched no variables.",
fixed = TRUE
)
})
test_that("numeric-vector tier() errors on duplicate indices", {
my_df <- data.frame(A = 1, B = 2, C = 3, check.names = FALSE)
expect_error(
knowledge(
my_df,
tier(1:3), # first time: creates tiers 1,2,3
tier(1:3) # second time: should detect 1,2,3 already exist
),
"Tier index(es) 1, 2, 3 already exist.",
fixed = TRUE
)
})
test_that("seq_tiers() in tier() errors when no variables match the pattern", {
my_df <- data.frame(A = 1, B = 2, C = 3, check.names = FALSE)
expect_error(
knowledge(
my_df,
tier(
# build a bundle that will match no columns
seq_tiers(1, starts_with("zzz"))
)
),
# literal match of the error message
"Pattern starts_with(\"zzz\") matched no variables.",
fixed = TRUE
)
})
test_that("tier() errors when two seq_tiers patterns overlap", {
my_df <- data.frame(
A = 1,
B = 2,
C = 3,
check.names = FALSE
)
expect_error(
knowledge(
my_df,
tier(
# seq_tiers(1:2, everything()) produces two formulas
# 1 ~ everything(), 2 ~ everything()
# so every column is matched twice -> should throw
seq_tiers(1:2, everything())
)
),
"Some variables matched by two patterns: A, B, C",
fixed = TRUE
)
})
test_that("seq_tiers() placeholder validation and numeric default branch", {
expect_error(
seq_tiers(1:2, foo), # no placeholder
"`vars` must contain the placeholder `i`",
fixed = TRUE
)
fml <- seq_tiers(1:2, c(i, 42))
expect_length(fml, 2)
rhs_txt <- rlang::expr_text(rlang::f_rhs(fml[[1]]))
expect_true(grepl("\\b1\\b", rhs_txt)) # i → "1"
expect_true(grepl("\\b42\\b", rhs_txt)) # 42 unchanged
})
test_that("add_tier() errors when both `before` and `after` are supplied", {
expect_error(
knowledge() |>
add_tier(2, before = 3, after = 1),
"Cannot supply both `before` and `after`.",
fixed = TRUE
)
})
test_that("add_tier() errors when both `before` and `after` are supplied for a labelled tier", {
expect_error(
knowledge() |>
add_tier(1) |>
add_tier(dumb_tier, before = 1, after = 1),
"Cannot supply both `before` and `after`.",
fixed = TRUE
)
})
test_that("add_tier() errors when either `before` or `after` is given but is not in kn$tiers", {
expect_error(
knowledge() |>
add_tier(One, before = Two),
"`before`/`after` cannot be used when there are no existing tiers.",
fixed = TRUE
)
})
test_that("add_to_tier() errors when tier input is bad", {
expect_error(
knowledge() |>
add_tier(NA),
"`tier` must be a single non-empty label or a non-negative numeric literal.",
fixed = TRUE
)
expect_error(
knowledge() |>
add_tier(NULL),
"`tier` must be a single non-empty label or a non-negative numeric literal.",
fixed = TRUE
)
})
test_that("add_tier() errors when no before or after is provided", {
expect_error(
knowledge() |>
add_tier(1) |>
add_tier(Two),
"Once the Knowledge object already has tiers, supply exactly one of `before` or `after`.",
fixed = TRUE
)
expect_error(
knowledge() |>
add_tier(1) |>
add_tier(Two, after = 1) |>
add_tier("Three"),
"Once the Knowledge object already has tiers, supply exactly one of `before` or `after`.",
fixed = TRUE
)
})
test_that("tier() attaches to an existing tier label", {
my_df <- tibble::tibble(V1 = 1, V2 = 2)
kn <- knowledge(
my_df,
tier(1 ~ V1), # creates tier "1" and assigns V1
tier(1 ~ V2) # same label, should hit the 'add_to_tier' path
)
# only one tier row exists and it is labelled "1"
expect_equal(kn$tiers$label, "1")
# both variables now belong to that tier
expect_setequal(
kn$vars |> dplyr::filter(tier == "1") |> dplyr::pull(var),
c("V1", "V2")
)
})
# ──────────────────────────────────────────────────────────────────────────────
# Tier violations
# ──────────────────────────────────────────────────────────────────────────────
test_that("reordering respects tier-violation rules", {
# only forbidden edge → any reorder is fine
kn <- knowledge(
tier(One ~ V1, Two ~ V2, Three ~ V3),
V2 %!-->% V3
)
expect_silent(reorder_tiers(kn, c("Three", "One", "Two")))
expect_silent(reposition_tier(kn, Three, before = One))
# required edge → illegal uphill move must error
kn2 <- knowledge(
tier(One ~ V1, Two ~ V2, Three ~ V3),
V2 %-->% V3 # V2 must stay *before* V1
)
expect_error(
reposition_tier(kn2, Three, after = One),
"Edge(s) violate tier ordering: V2 --> V3",
fixed = TRUE
)
expect_error(
reorder_tiers(kn2, c("One", "Three", "Two")),
"Edge(s) violate tier ordering: V2 --> V3",
fixed = TRUE
)
})
test_that("adding tier after required edge is provided will trigger tier violation error", {
expect_error(
knowledge(
V2 %-->% V1
) |>
add_tier(1) |>
add_tier(2, after = 1) |>
add_to_tier(2 ~ V2) |>
add_to_tier(1 ~ V1),
"Edge(s) violate tier ordering: V2 --> V1",
fixed = TRUE
)
})
# ──────────────────────────────────────────────────────────────────────────────
# Misc errors
# ──────────────────────────────────────────────────────────────────────────────
test_that("unfreeze() clears the frozen flag", {
my_df <- tibble::tibble(A = 1, B = 2)
kn_frozen <- knowledge(my_df) # passing a data frame sets frozen = TRUE
expect_true(kn_frozen$frozen)
kn_unfrozen <- unfreeze(kn_frozen)
expect_false(kn_unfrozen$frozen)
})
test_that("unfreeze() allows adding new variables", {
my_df <- tibble::tibble(A = 1, B = 2)
kn <- knowledge(my_df) |> unfreeze() # thaw the object
# add a new variable that wasn't in the original data frame
kn2 <- add_vars(kn, "C")
expect_setequal(kn2$vars$var, c("A", "B", "C"))
expect_false(kn2$frozen) # flag stays FALSE
})
test_that("knowledge() throws error when using another a not defined function", {
my_df <- data.frame(V1 = 1, V2 = 2, check.names = FALSE)
expect_error(
knowledge(
my_df,
musthave(V1 ~ 1)
),
"Only tier(), exogenous(), and infix edge operators (%-->%, %!-->%) are allowed.",
fixed = TRUE
)
expect_error(
knowledge(
makingmistakes(V1 ~ 1)
),
"Only tier(), exogenous(), and infix edge operators (%-->%, %!-->%) are allowed.",
fixed = TRUE
)
})
test_that("print.Knowledge() snapshot", {
local_edition(3) # enable v3 snapshotting
withr::with_options(
list(
crayon.enabled = FALSE, # strip colour codes
cli.num_colors = 1
),
{
kn <- knowledge(
tibble::tibble(V1 = 1, V2 = 2),
tier(1 ~ V1),
tier(2 ~ V2),
V1 %!-->% V2
)
expect_snapshot_output(print(kn), cran = FALSE)
}
)
})
test_that(".edge_verb() validates formula structure and matches", {
kn <- knowledge() # empty, so no vars are known yet
# not required or forbidden
expect_error(
.edge_verb(kn, "unknown", rlang::quo(V1 ~ V2)),
"`status` (edge type) must be 'required' or 'forbidden' for ",
fixed = TRUE
)
# not a two-sided formula
expect_error(
.edge_verb(kn, "forbidden", rlang::quo(V1)),
"Edge specification must be a two-sided formula",
fixed = TRUE
)
# both sides match zero vars: specific error branch
expect_error(
.edge_verb(
kn,
"forbidden",
rlang::quo(starts_with("Z") ~ starts_with("W"))
),
"Formula `starts_with(\"Z\") ~ starts_with(\"W\")` matched no variables.",
fixed = TRUE
)
})
test_that(".vars_from_spec() handles c(...) and symbol fallback paths", {
kn <- knowledge(tibble::tibble(V1 = 1, V2 = 2))
# unsupported argument inside c()
expect_error(
.vars_from_spec(kn, quote(c(V1, 42))),
"Unsupported argument in c\\(\\):",
perl = TRUE
)
# symbol resolves to a user-supplied character vector
local_vec <- c("V1", "V2")
expect_equal(
.vars_from_spec(kn, quote(local_vec)),
local_vec
)
})
# ──────────────────────────────────────────────────────────────────────────────
# + operator
# ──────────────────────────────────────────────────────────────────────────────
test_that("merge of numeric-looking tiers preserves left order", {
kn1 <- knowledge(tier(`1` ~ V1, `3` ~ V3))
kn2 <- knowledge(tier(`1` ~ V4, `2` ~ V2, `3` ~ V5))
kn12 <- kn1 + kn2
expect_equal(
kn12$tiers,
tiers_tbl("1", "3", "2") # order: left first, new labels appended
)
## duplicated variable: e1 wins because it’s listed first
kn1a <- knowledge(tier(`1` ~ V1))
kn2a <- knowledge(tier(`1` ~ V1_new))
expect_equal(
(kn1a + kn2a)$vars$var[1],
"V1" # takes first definition from kn1a
)
})
test_that("merge of arbitrary labels concatenates e1 order then new from e2", {
kn1 <- knowledge(tier(A ~ V1, AA ~ V3))
kn2 <- knowledge(tier(A ~ V4, B ~ V2))
kn12 <- kn1 + kn2
expect_equal(
kn12$tiers,
tiers_tbl("A", "AA", "B") # "B" appended after all of kn1’s labels
)
})
test_that("merge errors if resulting tiers violate required-edge order", {
kn_left <- knowledge(tier(One ~ V1))
kn_right <- knowledge(
tier(Two ~ V2),
V2 %-->% V1
)
expect_error(
kn_left + kn_right,
"Edge(s) violate tier ordering",
fixed = TRUE
)
})
test_that("merge errors if tiers overlap", {
kn1 <- knowledge(tier(One ~ V1, Two ~ V2))
kn2 <- knowledge(tier(Three ~ V2, Three ~ V3))
expect_error(
kn1 + kn2,
"Tier conflict detected for 1 variable",
fixed = TRUE
)
})
test_that("merge errors if required and forbidden edges overlap", {
kn1 <- knowledge(
V1 %!-->% V2,
V2 %!-->% V3
)
kn2 <- knowledge(
V1 %-->% V2,
V2 %-->% V3
)
expect_error(
kn1 + kn2,
"Edge(s) appear as both forbidden and required: V1 --> V2, V2 --> V3",
fixed = TRUE
)
})
# ──────────────────────────────────────────────────────────────────────────────
# deparse_knowledge()
# ──────────────────────────────────────────────────────────────────────────────
test_that("deparse_knowledge() emits minimal code for empty knowledge", {
kn <- knowledge()
code <- deparse_knowledge(kn)
expected <- "knowledge(\n)"
expect_equal(code, expected)
})
test_that("deparse_knowledge() includes data-frame name when provided", {
my_df <- data.frame(A = 1, B = 2)
kn <- knowledge(my_df, tier(1 ~ A, 2 ~ B))
code <- deparse_knowledge(kn, "my_df")
expected <- paste0(
"knowledge(my_df,",
"\n tier(",
"\n 1 ~ A,",
"\n 2 ~ B",
"\n )",
"\n)"
)
expect_equal(code, expected)
})
test_that("deparse_knowledge() groups multiple tiers into one tier() call", {
my_df <- data.frame(X = 1, Y = 2, Z = 3)
kn <- knowledge(
my_df,
tier(first ~ X + Y, second ~ Z)
)
code <- deparse_knowledge(kn, "my_df")
expect_true(grepl("tier\\(\\s*first ~ X \\+ Y,\\s*second ~ Z\\s*\\)", code))
})
test_that("deparse_knowledge() collapses forbidden edges by source", {
my_df <- data.frame(A = 1, B = 2, C = 3, D = 4)
kn <- knowledge(
my_df,
A %!-->% C,
A %!-->% D,
B %!-->% C
)
code <- deparse_knowledge(kn, "my_df")
# Should convert to
# A %!-->% c(C, D),
# B %!-->% C
expected_pattern <- "knowledge(my_df,\n A %!-->% c(C, D),\n B %!-->% C\n)"
expect_equal(code, expected_pattern)
})
test_that("deparse_knowledge() collapses required edges by source", {
my_df <- data.frame(P = 1, Q = 2, R = 3)
kn <- knowledge(
my_df,
P %-->% Q,
P %-->% R
)
code <- deparse_knowledge(kn, "my_df")
expected_pattern <- "knowledge(my_df,\n P %-->% c(Q, R)\n)"
expect_equal(code, expected_pattern)
})
test_that("deparse_knowledge() round-trips: eval(parse(code)) equals original", {
my_df <- data.frame(A = 1, B = 2, C = 3)
kn <- knowledge(
my_df,
tier(1 ~ A + B, 2 ~ C),
A %!-->% C,
B %-->% A
)
code <- deparse_knowledge(kn, "my_df")
kn2 <- eval(parse(text = code))
expect_equal(kn2, kn)
})
test_that("print and summary method for knowledge", {
my_df <- data.frame(A = 1, B = 2, C = 3, D = 4, E = 5, F = 6)
kn <- knowledge(
my_df,
tier(1 ~ A + B, 2 ~ C),
A %!-->% C,
B %-->% A
)
print(kn)
print(kn, wide = TRUE)
print(kn, compact = TRUE)
print(kn, wide = TRUE, compact = TRUE)
summary(kn)
expect_true(TRUE)
})
test_that("print and summary method for empty knowledge works", {
kn <- knowledge()
print(kn)
print(kn, wide = TRUE)
print(kn, compact = TRUE)
print(kn, wide = TRUE, compact = TRUE)
summary(kn)
expect_true(TRUE)
})
test_that("print and summary method for no tier knowledge works", {
kn <- knowledge(A %-->% B)
print(kn)
print(kn, wide = TRUE)
print(kn, compact = TRUE)
print(kn, wide = TRUE, compact = TRUE)
summary(kn)
expect_true(TRUE)
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.