tests/testthat/test-expressions.R

cat("\n===== Starting AFL geneeration tests =====\n")

test_that(echo("expression_parser handles basic inputs"), {
    expect_equal(scidb::expression_parser(1)$to_afl(), "1")
    expect_equal(scidb::expression_parser(-1)$to_afl(), "-1")
    expect_equal(scidb::expression_parser(a)$to_afl(), "a")
    expect_equal(scidb::expression_parser(A)$to_afl(), "A")
    expect_equal(scidb::expression_parser(1-1)$to_afl(), "0")
    expect_equal(scidb::expression_parser(2+1)$to_afl(), "3")
})

test_that(echo("Generate afl filter expressions from R expressions"), {
  expect_equal(scidb::filter_to_afl(a == TRUE), "a = true")
  expect_equal(scidb::filter_to_afl(a == !!T), "a = true")
  expect_equal(scidb::filter_to_afl(a == !!F), "a = false")
  
  expect_equal(scidb::filter_to_afl(a == 42), "a = 42")
  expect_equal(scidb::filter_to_afl(is.null(a)), "is_null(a)")
  expect_equal(scidb::filter_to_afl(is.null(a), b != 0), "is_null(a) and (b != 0)")
  
  expect_equal(scidb::filter_to_afl(a %in% c(1,2,3)), "((a = 1) or (a = 2)) or (a = 3)")
  
  expect_equal(scidb::filter_to_afl((a + b + "c") == 'value'), "((a + b) + 'c') = 'value'")
})

test_that(echo("Regular expressions"), {
  
  # Basic tests
  expect_equal(scidb::filter_to_afl(field %like% '.+a.+'),    "regex(field, '(?i).+a.+')")
  expect_equal(scidb::filter_to_afl(field %contains% 'a'),    "regex(field, '(?i).*a.*')")
  expect_equal(scidb::filter_to_afl(field %starts_with% 'a'), "regex(field, '(?i)a.*')")
  expect_equal(scidb::filter_to_afl(field %ends_with% 'a'),   "regex(field, '(?i).*a')")
  
  # Escape single-quote character in all regex operations
  expect_equal(scidb::filter_to_afl(field %like% "a'a"),        "regex(field, '(?i)a\\'a')")
  expect_equal(scidb::filter_to_afl(field %contains% "a'a"),   "regex(field, '(?i).*a\\'a.*')")
  expect_equal(scidb::filter_to_afl(field %starts_with% "a'a"), "regex(field, '(?i)a\\'a.*')")
  expect_equal(scidb::filter_to_afl(field %ends_with% "a'a"),   "regex(field, '(?i).*a\\'a')")
  
  # Escape double-quote character in all regex operations
  expect_equal(scidb::filter_to_afl(field %like% 'a"'),        "regex(field, '(?i)a\"')")
  expect_equal(scidb::filter_to_afl(field %contains% 'a"'),    "regex(field, '(?i).*a\".*')")
  expect_equal(scidb::filter_to_afl(field %starts_with% 'a"'), "regex(field, '(?i)a\".*')")
  expect_equal(scidb::filter_to_afl(field %ends_with% 'a"'),   "regex(field, '(?i).*a\"')")
  
  # Treat regex characters literally, requiring users to escape manually when
  # needing to match a literal prefix or suffix containing regex special characters
  # NOTE: this behavior differs from arrayop::aflutils$e_to_afl, which escapes some
  # special characters like * and \, but does not handle regex characters (e.g. '.')
  # TODO: add a literal(...) method to escape all regex characters, which users can
  # chain together with the regex methods; this would require an R-value RHS
  expect_equal(scidb::filter_to_afl(field %contains% ' a*b(par\\en)\\ '), "regex(field, '(?i).* a*b(par\\en)\\ .*')")
  expect_equal(scidb::filter_to_afl(field %starts_with% ' ]"[ '), "regex(field, '(?i) ]\"[ .*')")
  
  # Error regex_func mode
  expect_error(scidb::filter_to_afl(field %like% c('a', 'b')), "ERROR: cannot apply character operation 'stringr::str_detect'; RHS is not a scalar character")
  
})

test_that(echo("Simple type checking for raw values"), {
  # Numeric-only
  expect_error(scidb::filter_to_afl(a >= "foo"), "ERROR: cannot apply numeric operation '>='; RHS is not a scalar numeric")
  expect_error(scidb::filter_to_afl(a > "foo"), "ERROR: cannot apply numeric operation '>'; RHS is not a scalar numeric")
  expect_error(scidb::filter_to_afl(a <= "foo"), "ERROR: cannot apply numeric operation '<='; RHS is not a scalar numeric")
  expect_error(scidb::filter_to_afl(a < "foo"), "ERROR: cannot apply numeric operation '<'; RHS is not a scalar numeric")
  
  expect_error(scidb::filter_to_afl(paste0(a,3)), "ERROR: cannot apply character operation 'paste0'; RHS is not a scalar character")
  expect_error(scidb::filter_to_afl(str_detect(a,3)), "ERROR: cannot apply character operation 'stringr::str_detect'; RHS is not a scalar character")
})

test_that(echo("Generate complex filter expression with logical operators"), {
  # Basic cases
  expect_equal(scidb::filter_to_afl(a == 3, b == 'val'), "(a = 3) and (b = 'val')")
  expect_equal(scidb::filter_to_afl(a == 3 && b == 'val'), "(a = 3) and (b = 'val')")
  expect_equal(scidb::filter_to_afl(a == 3 & b == 'val'), "(a = 3) and (b = 'val')")
  expect_equal(scidb::filter_to_afl(a == 3 || b == 'val'), "(a = 3) or (b = 'val')")
  expect_equal(scidb::filter_to_afl(a == 3 | b == 'val'), "(a = 3) or (b = 'val')")
  
  # Handle logical operator precedence
  expect_equal(scidb::filter_to_afl(a == 3 && b == 4 && c == 5), "((a = 3) and (b = 4)) and (c = 5)")
  expect_equal(scidb::filter_to_afl(a == 3 || b == 4 || c == 5), "((a = 3) or (b = 4)) or (c = 5)")
  expect_equal(scidb::filter_to_afl(a == 3 && (b == 4 || c == 5)), "(a = 3) and ((b = 4) or (c = 5))")
  
  # Push down ! with equalities and inequalities
  expect_equal(scidb::filter_to_afl(!(a == 3)), "a != 3")
  expect_equal(scidb::filter_to_afl(!(!(a == 3))), "a = 3")
  expect_equal(scidb::filter_to_afl(!(a != 3)), "a = 3")
  expect_equal(scidb::filter_to_afl(!(a >= 3)), "a < 3")
  expect_equal(scidb::filter_to_afl(!(a > 3)), "a <= 3")
  expect_equal(scidb::filter_to_afl(!(a <= 3)), "a > 3")
  expect_equal(scidb::filter_to_afl(!(a < 3)), "a >= 3")
  
  # Push down ! via De Morgan's Laws, preserving unary composite-ness
  A <- scidb::expression_parser(!(a == 3 && b == 4 && c == 5))
  B <- scidb::expression_parser(!(a == 3 || b == 4 || c == 5))
  C <- scidb::expression_parser(!(a == 3 && (b == 4 || c == 5)))
  expect_equal(A$to_afl(), "((a != 3) or (b != 4)) or (c != 5)")
  expect_equal(B$to_afl(), "((a != 3) and (b != 4)) and (c != 5)")
  expect_equal(C$to_afl(), "(a != 3) or ((b != 4) and (c != 5))")
  
  expect_true(A$unary_composite())
  expect_true(B$unary_composite())
  expect_true(C$unary_composite())
  expect_equal(A$left()$to_afl(), "(a != 3) or (b != 4)")
  expect_equal(A$left()$left()$to_afl(), "a != 3")
  expect_equal(A$left()$right()$to_afl(), "b != 4")
  expect_equal(A$right()$to_afl(), "c != 5")
  expect_equal(B$left()$to_afl(), "(a != 3) and (b != 4)")
  expect_equal(B$left()$left()$to_afl(), "a != 3")
  expect_equal(B$left()$right()$to_afl(), "b != 4")
  expect_equal(B$right()$to_afl(), "c != 5")
  expect_equal(C$left()$to_afl(), "a != 3")
  expect_equal(C$right()$to_afl(), "(b != 4) and (c != 5)")
  expect_equal(C$right()$left()$to_afl(), "b != 4")
  expect_equal(C$right()$right()$to_afl(), "c != 5")
})

test_that(echo("Anonymous predicate filters"), {
  expect_equal(scidb::filter_to_afl(a = EQUALS(3)), "a = 3")
  expect_equal(scidb::filter_to_afl(a = IN(c(3,4,5))), "((a = 3) or (a = 4)) or (a = 5)")
  expect_equal(scidb::filter_to_afl(a = NOT_IN(c(3,4,5))), "((a != 3) and (a != 4)) and (a != 5)")
  expect_equal(scidb::filter_to_afl(a = IS_NULL()), "is_null(a)")
  expect_equal(scidb::filter_to_afl(a = NOT_NULL()), "not(is_null(a))")
  expect_equal(scidb::filter_to_afl(a = NEQ(3)), "a != 3")
  expect_equal(scidb::filter_to_afl(a = GEQ(3)), "a >= 3")
  expect_equal(scidb::filter_to_afl(a = GT(3)), "a > 3")
  expect_equal(scidb::filter_to_afl(a = LEQ(3)), "a <= 3")
  expect_equal(scidb::filter_to_afl(a = LT(3)), "a < 3")
  expect_equal(scidb::filter_to_afl(a = IN_RANGE(3,5)), "(a >= 3) and (a <= 5)")
  expect_equal(scidb::filter_to_afl(a = IN_RANGE_INCL(3,5)), "(a >= 3) and (a <= 5)")
  expect_equal(scidb::filter_to_afl(a = IN_RANGE_EXCL(3,5)), "(a > 3) and (a < 5)")
})

test_that(echo("Value substitution and symbol interpolation"), {
  # Value substitution
  value <- 3
  expect_equal(scidb::filter_to_afl(a == !!value), "a = 3")
  value <- c(3,4,5)
  expect_equal(scidb::filter_to_afl(a %in% !!value), "((a = 3) or (a = 4)) or (a = 5)")
  value <- "3"
  expect_equal(scidb::filter_to_afl(a == !!value), "a = '3'")
  
  # Symbol interpolation
  variable <- "a"
  # Style 1: !!rlang::sym() in expression
  expect_equal(scidb::filter_to_afl(!!rlang::sym(variable) %in% !!value), "a = '3'")
  # Style 2: !!key := 
  expect_equal(scidb::filter_to_afl(!!variable := !!value), "a = '3'")
})
Paradigm4/SciDBR documentation built on Nov. 9, 2023, 4:58 a.m.