Sys.unsetenv("R_TESTS") test_that("Some smoke tests", { expect_equal(juliaCall("prod", c(1,2,3)), 6) juliaEval("") juliaCall("string", list()) juliaCall("string", list(as.integer(1), "bla" = 23L)) juliaEval("String[]") expect(!is.null(juliaEval("using Random; Random.seed!(5)")), "Must be able to set random seed") expect(is.null(juliaEval("Random.seed!(5);")), "Eval with semicolon at end returns NULL") }) test_that("Output is transferred", { output <- capture_output({ juliaCall("println", as.integer(22)) }) expect_match(output, "^[\n]*22[\n]*$") output <- capture_output({ juliaCall("println", "hello world") }) expect_match(output, "^[\n]*hello world[\n]*$") }) test_that("Warnings are transferred", { output <- capture.output(type = "message", { juliaEval('@warn "This might be serious"') }) expect_match(output[1], "This might be serious") }) test_that("Warnings and normal output are both transferred and stable", { stdoutput <- capture_output({ stderroutput <- capture.output(type = "message", { juliaLet('for i = 1:100 println(join(rand(["1", "ä", "ö"], rand(1:100)))); println(stderr,join(rand(["2", "u", "a"], rand(1:100)))); end') ret <- juliaLet('println(22); 17') }) }) expect_match(stdoutput, "[1äö\n]+") expect_match(stderroutput, "[2ua\n]+") # TODO problems with non-ascii-characters stdoutput <- capture_output({ stderroutput <- capture.output(type = "message", { ret <- juliaLet('println(22); @warn "This might be serious"; 17') }) }) expect_equal(ret, 17) expect_match(stdoutput[1], "^[\n]*22[\n]*$") expect_match(stderroutput[1], "This might be serious") }) test_that("A return value of nothing is not printed", { output <- capture_output({ juliaEval("nothing") juliaCall("identity", NULL) juliaLet("x = y", y = NULL) }) expect_match(output, "^[\n]*$") }) test_that("Test loading and importing a complex package", { skip_on_cran() juliaEval('begin using Pkg; try @eval import StatsBase catch ex Pkg.add("StatsBase") end end ') # tests also trailing whitespace StatsBase <- juliaImport("StatsBase") expect_equivalent(juliaGet(StatsBase$mean_and_var(c(1,2,3))), list(2,1)) StatsBase$renyientropy(rnorm(100), 1) }) test_that("Error when trying to import a non-existent package or module", { expect_error(juliaImport("NonExistingPkg")) expect_error(capture.output({juliaImport(".NonExistingModule")}, type = "message")) }) test_that("Error when passing multiple strings to import or using", { expect_error(juliaImport(c("Pkg", "UUID")), regexp = "exactly one") }) test_that("Example for juliaEval runs", { v1 <- juliaExpr('v"1.0.5"') v2 <- juliaExpr('v"1.3.0"') expect(juliaCall("<", v1, v2), "Comparison must work") }) test_that("Echo: empty R vector", {testEcho(c())}) test_that("Echo: double", { expect(is.integer(juliaEcho(1L)), "Must be integer") testEcho(1L) }) test_that("Echo: Single Int", { testEcho(1L) expect(is.integer(juliaEval("1")), "1 is not integer") testEcho(juliaEval("1")) expect(is.double(juliaEval("2^52")), "2^52 is not double") # TODO inexactness? testEcho(juliaEval("2^52")) }) test_that("Echo: 1-element vector of Int in Julia", {testEcho(juliaEval("[1]"))}) test_that("Echo: Matrix of Int", {testEcho(matrix(1:6, nrow = 2))}) test_that("Echo: Single Float64", { testEcho(juliaEval("1.0")) testEcho(1) }) test_that("UInt32, Float16, and Float32 translated to doubles in R", { toTest <- c("Float16(1.5)", "Float16[]", "Float16[1.5]", "Float32(1.5)", "Float32[]", "Float32[1.5]", "UInt32(32)", "UInt32[]", "UInt32[32]") for (expr in toTest) { expect(is.double(juliaEval(expr)), "Must be double") } }) test_that("Echo: 0-element vector of Float64 in Julia", {testEcho(juliaEval("Float64[]"))}) test_that("Echo: 1-element vector of Float64 in Julia", {testEcho(juliaEval("[1.0]"))}) test_that("Echo: 2-element vector of Float64/double", { testEcho(c(1, 2)) testEcho("[1.0; 2.0]") }) test_that("Echo: matrix of Float64 in Julia", { testEcho(juliaEval("[1.0 2.0; 3.0 4.0]")) testEcho(matrix(c(1,2,3,4,4,5), nrow = 2)) }) test_that("Echo: 1-element vector of Float32 in Julia", {testEcho(juliaEval("[1.0f0]"))}) test_that("Echo: matrix of Float32 in Julia", {testEcho(juliaEval("[1.0f0 2.0f0; 3.0f0 4.0f0]"))}) test_that("Echo: 1-element vector of String in Julia", {testEcho(juliaEval('String["bla"]'))}) test_that("Echo: 0-element vector of String in Julia", {testEcho("String[]")}) test_that("Echo: 1-element vector of String in R", {testEcho("bla")}) test_that("Echo: 2-element vector of String in R", {testEcho(c("bla", "blup"))}) test_that("Echo: 2-element vector of String in Julia", {testEcho(juliaEval('String["bla", "blup"]'))}) test_that("Echo: String with missing values", { x <- c("Yes", "No", NA) y <- juliaEcho(x) expect_true(is.na(y[3])) x <- matrix(c("bla", NA, NA, "NA"), nrow = 2) y <- juliaEcho(x) expect_false(is.na(y[1,1])) expect_true(is.na(y[1,2])) expect_true(is.na(y[2,1])) expect_false(is.na(y[2,2])) }) test_that("Echo: Int vector with missing values", { expect_true(juliaLet("x[1] == 1 && ismissing(x[2]) && x[3] == 3", x = c(1L,NA, 3L))) expect_true(juliaLet("ismissing(x)", x = NA_integer_)) x <- juliaEval("[1 missing 3; 3 missing 4 ]") expect_type(x, "integer") expect_true(all(is.na(x[,2]))) expect_null(attr(x, "JLTYPE")) x <- juliaEval("[2^52 missing 3; 2^53 missing 4 ]") expect_type(x, "double") expect_true(all(is.na(x[,2]))) expect_equal(x[1,1], 2^52) expect_equal(x[2,1], 2^53) testEcho(x) x <- juliaEval("Union{Int32,Missing}[1 missing 3; 3 missing 4 ]") expect_type(x, "integer") expect_true(all(is.na(x[,2]))) expect_equal(x[1,1], 1) expect_equal(x[2,1], 3) testEcho(x) x <- juliaEval("Union{Int16,Missing}[-1 missing 3; 3 missing 4 ]") expect_type(x, "integer") expect_true(all(is.na(x[,2]))) expect_equal(x[1,1], -1) expect_equal(x[2,1], 3) testEcho(x) testEcho(c(1L, 2L, NA, NA)) testEcho(matrix(c(1L, 2L, NA, NA), nrow = 2)) }) test_that("Echo: Double vector with missing values", { expect_true(juliaLet("x[1] == 1.0 && ismissing(x[2]) && x[3] == 3.0", x = c(1,NA, 3))) expect_true(juliaLet("ismissing(x)", x = NA_integer_)) x <- juliaEval("[1.0 missing 3.0; 3.0 missing 4.0 ]") expect_type(x, "double") expect_true(all(is.na(x[,2]))) expect_null(attr(x, "JLTYPE")) x <- juliaEval("Union{Float32,Missing}[1.0f0 missing 3.0f0; 3.0f0 missing 4.0f0 ]") expect_true(all(is.na(x[,2]))) expect_equal(x[1,1], 1) expect_equal(x[2,1], 3) testEcho(x) testEcho(c(1, 2, NA, NA)) testEcho(matrix(c(1, 2, NA, NA), nrow = 2)) }) test_that("Dirty missing values are recognized", { dirtyNa <- c(as.raw(0xa2), as.raw(0x07), as.raw(0x00), as.raw(0x00), as.raw(0x00), as.raw(0x00), as.raw(0xf8), as.raw(0x7f)) dirtyNA <- readBin(dirtyNa, what = "double", endian = "little") expect_true(is.na(dirtyNA) && !is.nan(dirtyNA)) expect_true(juliaCall("ismissing", dirtyNA)) expect_true(juliaLet("ismissing(x[2])", x = c(1, dirtyNA))) echoDirtyNA <- juliaEcho(dirtyNA) expect_true(is.na(echoDirtyNA) && ! is.nan(echoDirtyNA)) dirtyComplexNA <- complex(real = dirtyNA, imaginary = dirtyNA) expect_true(is.na(dirtyComplexNA) && !is.nan(dirtyComplexNA)) expect_true(juliaCall("ismissing", dirtyComplexNA)) x <- c(1+1i, dirtyComplexNA, NA) expect_true(juliaLet("ismissing(x[2]) && ismissing(x[3])", x = x)) testEcho(x) # The following doesn't work: dirtNA coerced to NA+0i, not NA+NA*i: # expect_true(juliaLet("ismissing(x[2])", x = c(1+1i, dirtyNA))) echoDirtyComplexNA <- juliaEcho(dirtyComplexNA) expect_true(is.na(echoDirtyComplexNA) && !is.nan(echoDirtyComplexNA)) }) test_that("NaN and NA are handled differently", { # NaN in R is translated to a NaN Float64 value in Julia # NA in R is translated to a missing value in Julia x <- juliaEval("[1; missing; NaN]") expect_equal(x[1], 1) expect_true(is.na(x[2]) && !is.nan(x[2])) expect_true(is.nan(x[3])) y <- juliaEcho(x) expect_equal(y[1], 1) expect_true(is.na(y[2]) && !is.nan(y[2])) expect_true(is.nan(y[3])) expect_equal(juliaCall("typeof", c(1, NA)), juliaEval("Array{Union{Float64,Missing},1}")) expect_equal(juliaCall("typeof", c(1, NaN)), juliaEval("Array{Float64,1}")) expect_equal(juliaCall("typeof", c(1+1i, NA)), juliaEval("Array{Union{Complex{Float64},Missing},1}")) expect_equal(juliaCall("typeof", c(1+1i, NaN)), juliaEval("Array{Complex{Float64},1}")) expect_true(juliaLet("ismissing(x[2])", x = c(1+1i, NA))) }) test_that("Missing values transferred as NA", { expect_true(is.na(juliaEval("missing"))) expect_true(all(is.na(juliaEval("[missing; missing]")))) expect_true(all(is.na(juliaEval("[missing missing; missing missing]")))) }) test_that("Logical with missing values", { juliaLet("x[1] && !x[2] && ismissing(x[3])", x = c(TRUE, FALSE, NA)) x <- c(TRUE, FALSE, NA) testEcho(x) }) test_that("Complex with missing values", { juliaLet("x[1] == 1+1im && x[2] == 2+2im && ismissing(x[3])", x = c(1 +1i, 2+2i, NA)) x <- c(1 +1i, 2+2i, NA) testEcho(x) x <- juliaEval("[1+1im 1+2im; 0 missing]") expect_equivalent(x, matrix(c(1+1i, 0, 1+2i, NA), nrow = 2)) testEcho(x) x <- juliaEval("[1.0+im 2.0+im; 0.0 missing]") expect_equal(x, matrix(c(1+1i, 0, 2+1i, NA), nrow = 2)) expect_true(is.null(attr(x, "JLTYPE"))) testEcho(x) }) test_that("Echo: logical vectors", { testEcho(juliaEval('Bool[]')) testEcho(logical()) testEcho(TRUE) testEcho(FALSE) testEcho(c(TRUE, FALSE)) testEcho("Bool[true]") }) test_that("Echo: Pointers", { arrPtr <- juliaFun("Base.pointer", list(1,2,3)) ptr <- arrPtr(2L) expect_type(ptr, "raw") testEcho(ptr) ptrs <- juliaCall("map", arrPtr, as.integer(1:3)) testEcho(ptrs) }) test_that("Echo: Ref", { testEcho(juliaCall("Ref", list(1,2,3), 2L)) r <- juliaEval("global reftestvar = 1; Ref(reftestvar)") testEcho(r) }) test_that("Complex values are handled first class", { testEcho(1i) testEcho(juliaEval("1+im")) testEcho(juliaEval("[1+im]")) testEcho(juliaEval("[1+im; 2+im]")) testEcho(c(1+1i,2 + 2i)) suppressWarnings({jla <- juliaImport("LinearAlgebra")}) testEcho(matrix(c(1, 0, 0, -1), ncol = 2)) expect(all(jla$eigvals(matrix(c(1, 0, 0, -1), ncol = 2), matrix(c(0, 1, 1, 0), ncol = 2)) %in% c(1i, -1i)), "") expect(all(jla$eigmax(matrix(c(0, 1i, -1i, 0), ncol = 2)) == 1.0), "") complexTypeParameters = c("Int8", "Int16", "Int32", "Int64", "Float16", "Float32", "Float64") for (complexPar in complexTypeParameters) { juliaComplexType <- paste0("Complex{", complexPar, "}") c <- juliaEval(paste0(juliaComplexType, "(5-3im)")) expect_equivalent(c, 5 - 3i) carr <- juliaEval(paste0(juliaComplexType, "[4+4im, 2-2im]")) expect_equivalent(carr, c(4+4i, 2-2i)) if (complexPar == "Float64") { expect_null(attr(c, "JLTYPE")) } else { expect_true(juliaCall("==", juliaEval(attr(c, "JLTYPE")), juliaEval(juliaComplexType))) } } }) test_that("Echo: raw vector", { testEcho(as.raw(c(1,2,3))) testEcho(juliaEval("[0x01]")) testEcho(juliaEval("UInt8[]")) expect_equivalent(juliaEval("[0x01 0x02; 0x03 0x04]"), matrix(c(1,3,2,4), nrow = 2)) expect_equal(juliaCall("string", c(as.raw(0xca), as.raw(0xfe))), "UInt8[0xca, 0xfe]") }) test_that("Echo: raw vector", { testEcho(juliaEval("Int32[1, 2]")) testEcho(juliaEval("Int32[]")) testEcho(juliaEval("Int32[1]")) }) test_that("Echo: Single Int16", {testEcho(juliaEval('Int16(300)'))}) test_that("Echo: Int16 Vector", {testEcho(juliaEval('Int16[1,2,3]'))}) test_that("Echo: 1-element Int16 Vector", { expect_equal(juliaCall("string", juliaEval('Int16[300]')), "Int16[300]") }) test_that("Echo: Single UInt128", {testEcho(juliaEval('UInt128(2)^100 +1'))}) test_that("Echo: UInt128 Vector", {testEcho(juliaEval('UInt128[1,2,3]'))}) test_that("Echo: 1-element UInt128 Vector", { expect_equal(juliaCall("string", juliaEval('UInt128[1]')), "UInt128[0x00000000000000000000000000000001]") }) test_that("Echo: List with NULL elements", { x <- juliaGet(juliaEcho(list(1, NULL, 3))) expect_equal(x[[1]], 1) expect_null(x[[2]]) expect_equal(x[[3]], 3) expect_equal(length(x), 3) testEcho(juliaEval('[1, nothing, 3]')) x <- list("bla", NULL) expect_equal(x[[1]], "bla") expect_null(x[[2]]) x <- list(NULL, NULL) expect_null(x[[1]]) expect_null(x[[2]]) }) test_that("Mutable struct usable by reference", { juliaEval('mutable struct TestMutableStruct x::Int end') jlRef <- juliaEval("TestMutableStruct(1)") refEcho <- juliaEcho(jlRef) expect_true(juliaCall("==", jlRef, refEcho)) expect_true(all(get("ref", jlRef) == get("ref", refEcho))) expect_equal(refEcho$x, 1) refEcho$x <- 2L expect_equal(jlRef$x, 2) ref <- get("ref", jlRef) jlRef <- NULL refEcho <- NULL invisible(gc()) juliaEval("1") expect_false(juliaLet("haskey(communicator.sharedheap, ref)", ref = ref, communicator = JuliaConnectoR:::pkgLocal$communicator)) # Test behaviuor with juliaGet: # The reference must be attached jlRefEnv <- attr(juliaGet(juliaEval("TestMutableStruct(1)")), "JLREF") expect_true(is.environment(jlRefEnv)) expect_true(is.raw(jlRefEnv$ref)) }) test_that("Immutable struct usable by reference and translated", { juliaEval('struct TestImmutableStruct x::Int end') jlRef <- juliaEval("TestImmutableStruct(1)") expect_s3_class(jlRef, "JuliaStructProxy") refEcho <- juliaEcho(jlRef) expect_true(juliaCall("==", jlRef, refEcho)) expect_equal(refEcho$x, 1) ref <- get("ref", jlRef) expect_equal(juliaGet(jlRef)$x, 1) expect_false(juliaLet("haskey(communicator.sharedheap, ref)", ref = ref, communicator = JuliaConnectoR:::pkgLocal$communicator)) gc() juliaEval("1") }) test_that("Currying in juliaFun works", { plus1 <- juliaFun("+", 1) plus1(2) expect_equal(juliaCall("map", plus1, c(1,2,3)), c(2,3,4)) }) test_that("Multidimensional object arrays work", { juliaEval("struct MultiTestStruct f::Float64 end") content <- juliaEval("rand(4,5,6)") testEcho(content) x <- juliaLet("map(MultiTestStruct, c)", c = content) testEcho(x) expect_equivalent(dim(x), c(4,5,6)) expect_equivalent(juliaGet(x[1,1,1]), list(juliaGet(x[[1,1,1]]))) x[[1,2,3]] <- juliaEval("MultiTestStruct(17.0)") expect_equal(x[[1,2,3]]$f, 17) x[1,2,2] <- juliaEval("MultiTestStruct(19.0)") expect_equal(x[[1,2,2]]$f, 19) x[1,2,2] <- list(juliaEval("MultiTestStruct(19.0)")) expect_equal(x[[1,2,2]]$f, 19) expect_equal(dim(x[3:4, 4:5, 1]), c(2,2,1)) expect_equal(dim(x[3:4, as.integer(4:5), 1L]), c(2,2,1)) x[3:4, as.integer(4:5), 1L] <- juliaEval("MultiTestStruct(20.0)") expect_equal(juliaGet(x[[3, 4, 1]])$f, 20) expect_equal(juliaGet(x[[4, 5, 1]])$f, 20) y <- juliaGet(x) attr(y, "JLDIM") <- c(1L, 2L, 3L) expect_error(juliaEcho(y), regexp = "Incorrect dimensions") # Must also work with dimensions of zero testEcho(juliaEval("Matrix{MultiTestStruct}(undef, 0, 0)")) x <- juliaEval("Array{MultiTestStruct}(undef, 0, 0, 0)") testEcho(x) expect_equivalent(dim(x), c(0,0,0)) # Two dimensions content <- juliaEval("rand(2,3)") x <- juliaLet("map(MultiTestStruct, c)", c = content) x[[1,2]] <- juliaEval("MultiTestStruct(3.0)") expect_equal(x[[1,2]]$f, 3) }) test_that("Arrays with undef entries are translated", { juliaEval("mutable struct MutableTestStruct f::Float64 end") # all undefs testEcho(juliaEval("Vector{MutableTestStruct}(undef, 3)"), comparableInJulia = FALSE) # undefs and values juliaLet("x = Vector{MutableTestStruct}(undef, 3); x[1] = MutableTestStruct(x1); x[2] = MutableTestStruct(x2); x", x1 = 1.0, x2 = 2.0) testEcho(juliaEval("Vector{MutableTestStruct}(undef, 3)"), comparableInJulia = FALSE) # multidimensional arrays with undefs and values x <- juliaLet("x = Array{MutableTestStruct}(undef, 2, 3, 4); x[1,1,1] = MutableTestStruct(x1); x[2,2,2] = MutableTestStruct(x2); x", x1 = 1.0, x2 = 2.0) testEcho(x, comparableInJulia = FALSE) expect_equal(x[[1]]$f, 1) }) test_that("Undefined strings are transferred as empty strings", { expect_equal(juliaEval("Vector{String}(undef, 3)"), c("", "", "")) }) test_that("String with null can be translated back to Julia", { # einfacher string jlStrWithNul <- "String([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x5a, 0x65, 0x72, 0x6f, 0x57])" str <- juliaEval(jlStrWithNul) expect_true(is.raw(str)) expect_equal(as.character(juliaCall("typeof", str)), "String") expect_equal(as.character(juliaLet("String(x[1:5])", x = str)), "Hello") # string array strArr <- juliaLet("String[a,b]", a = str, b = str) expect_true(is.list(strArr)) strArr <- juliaLet("String[a,b,c]", a = str, b = str, c = str) expect_true(is.list(strArr)) expect_equal(juliaCall("typeof", strArr), juliaEval("Array{String,1}")) }) # Test Let test_that("Let: used like eval", { output <- capture_output({expect(is.null(juliaLet("print(1)")), "Failed")}) expect_equal(output, "1") }) test_that("Let: must error with no named argument", {expect_error(juliaLet("print(x)", 1), "")}) test_that("Let: basic echo", {expect(all(juliaLet("identity(x)", x=c(2, 3)) == c(2,3)), "Failed")}) test_that("Let: Simple example from documentation works", { expect_equal(capture_output({juliaLet('println(x)', x = 1)}), capture_output({juliaEval('let x = 1.0; println(x) end')})) }) test_that("Let: arguments without names not accepted", { expect_error(juliaLet("println(1)", 1), regexp = "names") expect_error(juliaLet("println(1)", x=1, 1, y=2), regexp = "names") }) #Test Pairs test_that("Echo: Pairs", { testEcho(juliaEval("(1 => 2.0)")) testEcho(juliaEval("1 => 2.0 => 3.0")) testEcho(juliaEval("[1 => 2.0, 2 => 3.0]")) }) # Test Tuples test_that("Echo: Tuples", { x <- juliaEval("(1, 17, 18)") expect_equal(x[[2]], 17) expect_equal(juliaGet(x[1]), juliaGet(juliaEval("(1,)"))) expect_length(x, 3) expect_length(x[1:2], 2) testEcho(juliaEval("(1, 2.0)")) testEcho(juliaEval("((1, 2.0), 3.0)")) testEcho(juliaLet("collect(zip(x,y))", x = c(1L,2L, 3L), y = c(1,0,1))) testEcho(juliaEval("(Ref(false), Ref(true))")) testEcho(juliaEval("Tuple{}()")) }) # Test Named Tuples test_that("Echo: Named Tuples", { namedTuple <- juliaLet("y=2*x; z = 3*u + 1; (x=y, y=z)", x=2, u=4) expect_s3_class(namedTuple, "JuliaStructProxy") expect_equal(namedTuple$y, 13) expect_type(juliaGet(namedTuple), "list") expect_equal(juliaGet(namedTuple)$y, 13) testEcho(namedTuple) }) # Test Module test_that("Echo: Module", { juliaEval("module TestModule end") testEcho(juliaEval("TestModule")) }) test_that("Echo: Symbol", { x <- juliaEval(":asymbol") expect_true(is.symbol(x)) expect_true(juliaCall("isa", as.symbol("s"), juliaExpr("Symbol"))) testEcho(x) }) # Test Dictionary test_that("Echo: Dictionary", { d <- juliaEval("Dict(:bla => 1.0, :blup => 3.0)") expect_equivalent(d[juliaExpr(":bla")][[1]], 1) expect_equal(d[juliaExpr(":bla"), juliaExpr(":bla")], as.list(c(1, 1))) expect_equal(d[[as.symbol("bla")]], 1) expect_equal(d[as.symbol("blup")],list(3)) d[juliaExpr(":blup")] <- 4 expect_equal(d[[juliaExpr(":blup")]], 4) d[[juliaExpr(":blup")]] <- 5 expect_equal(d[[juliaExpr(":blup")]], 5) d2 <- juliaGet(d) expect_setequal(d2[["keys"]], juliaGet(juliaEval("[:bla, :blup]"))) expect_setequal(d2[["values"]], list(1, 5)) d[juliaExpr(":hi"), juliaExpr(":hello")] <- c(15, 14) expect_equal(d[juliaExpr(":bla"), juliaExpr(":hi")], as.list(c(1, 15))) d[juliaExpr(":hi"), juliaExpr(":hello")] <- as.list(c(16, 17)) expect_equal(d[juliaExpr(":bla"), juliaExpr(":hello"), juliaExpr(":hi")], as.list(c(1, 17, 16))) expect_length(d[juliaExpr(":bla"), juliaExpr(":hi"), juliaExpr(":hello"), juliaExpr(":hello")], 4) d <- juliaLet("Dict(zip(x, y))", x = c("bla", "blup"), y = c(1,2)) expect_equal(d[["bla"]], 1) expect_equivalent(d["blup"], list(2)) d2 <- juliaGet(d) expect_setequal(d2[["keys"]], list("bla", "blup")) expect_setequal(d2[["values"]], list(1, 2)) d2 <- juliaGet(d) expect_setequal(d2[["keys"]], list("bla", "blup")) expect_setequal(d2[["values"]], list(1,2)) d$bla <- 17 expect_equal(d$bla, 17) d <- juliaLet("Dict(zip(x, y))", x = list("bla"), y = list(1)) expect_equal(length(juliaCall("keys", d)), 1) testEcho(d) d <- juliaLet("Dict(zip(x, y))", x = list(), y = list()) d2 <- juliaGet(d) expect_length(d2[["keys"]], 0) expect_length(d2[["values"]], 0) testEcho(d) }) # Test Set test_that("Echo: Set", { s1 <- juliaEval("Set([1; 2; 3; 4])") s2 <- juliaEval("Set([1; 2])") expect_length(setdiff(juliaGet(juliaEval("Set([1; 2; 3; 4])")), c(1,2,3,4)), 0) expect_length(setdiff(juliaGet(juliaLet("setdiff(s1, s2)", s1 = s1, s2 = s2)), c(3,4)), 0) testEcho(s1) }) # Test types with bitstypes test_that("Object with bitstype", { uuidregex <- '^[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}$' UUIDs <- juliaImport("UUIDs") expect_match(juliaCall("string", UUIDs$uuid4()), uuidregex) }) test_that("Exotic objects handled gracefully", { x <- quote(1 + 1) expect_type(x, "language") expect_warning({ y <- juliaEcho(list(x = x))}, regexp = "not translatable", all = FALSE) expect_null(y$x) }) # Test complex constructor with all kinds of types test_that("Complex Julia object with different member type", { juliaEval('struct TestTypeWithAllKindsOfStuff n::Nothing f16::Float16 f32::Float32 f64::Float64 b::Bool i8::Int8 ui8::UInt8 i16::Int16 ui16::UInt16 i32::Int32 ui32::UInt32 ch::Char i64::Int64 ui64::UInt64 i128::Int128 ui128::UInt128 f16vec::Vector{Float16} f32vec::Vector{Float32} f64vec::Vector{Float64} bvec::Vector{Bool} i8vec::Vector{Int8} ui8vec::Vector{UInt8} i16vec::Vector{Int16} ui16vec::Vector{UInt16} i32vec::Vector{Int32} ui32vec::Vector{UInt32} chvec::Vector{Char} i64vec::Vector{Int64} ui64vec::Vector{UInt64} i128vec::Vector{Int128} ui128vec::Vector{UInt128} end') juliaEval('function TestTypeWithAllKindsOfStuff() TestTypeWithAllKindsOfStuff( nothing, rand(Float16), rand(Float32), rand(Float64), rand(Bool), rand(Int8), rand(UInt8), rand(Int16), rand(UInt16), rand(Int32), rand(UInt32), rand(Char), rand(Int64), rand(UInt64), rand(Int128), rand(UInt128), rand(Float16, 2), rand(Float32, 2), rand(Float64, 2), rand(Bool, 2), rand(Int8, 2), rand(UInt8, 2), rand(Int16, 2), rand(UInt16, 2), rand(Int32, 2), rand(UInt32, 2), rand(Char, 2), rand(Int64, 2), rand(UInt64, 2), rand(Int128, 2), rand(UInt128, 2)) end') testEcho(juliaEval("TestTypeWithAllKindsOfStuff()")) }) test_that("Private inner constructor is forged", { juliaEval('struct MyPrivateX x::Int function MyPrivateX() new(5) end end') p <- juliaEval("MyPrivateX()") testEcho(p) }) test_that("Imported modules are printed", { # a stdlib module Pkg <- juliaImport("Pkg") expect_match(regexp = 'Julia module "Pkg": [0-9]+ function.*', capture.output(print(Pkg))[1]) # a user defined module module <- juliaEval('module ImportTestModule1 export f f(x)=1 end') itm1 <- juliaImport(module, all = FALSE) expectedOutput <- 'Julia module "Main.ImportTestModule1": 1 function.*' expect_match(capture.output(print(itm1))[1], regexp = expectedOutput) itm1_2 <- juliaImport(".ImportTestModule1", all = FALSE) expect_match(capture.output(print(itm1_2))[1], regexp = expectedOutput) }) test_that("Empty module does not cause problems", { juliaEval("module EmptyTestModule end") expect_length(ls(juliaImport(".EmptyTestModule")), 2) # (eval and include) juliaEval("module EmptyTestModule2 end") expect_length(ls(juliaImport(".EmptyTestModule2", all = FALSE)), 0) }) test_that("Errors are handled gracefully", { expect_error(juliaCall("sum", c(1,2,3, "bla"))) expect_error(juliaCall("thisisnotarealfunction", 100, 4)) expect_error(juliaCall("RConnector.thisisnotarealfunction", "haha")) expect_error(juliaCall("NotARealModule.thisisnotarealfunction", list(1,2,3))) }) test_that("Vectors of objects can be accessed by index via proxy", { x <- juliaEval("[ [1;2;3], [4;5;6], [7;8;9] ]") expect_s3_class(x, "JuliaArrayProxy") expect_equal(x[[1]][2], 2) expect_equal(x[[2]][2], 5) x[[1]][1] <- 17L expect_equal(x[[1]][1], 17) expect_equal(x[2:3][[1]][[1]], 4) x[2:3][[1]][[1]] <- 18L expect_equal(x[2:3][[1]][[1]], 18) expect_equal(x[[1]][x[[2]] == 5], 2) # logical indexing # juliaGet version must behave in the same way as the proxy version y <- juliaGet(x) expect_equal(x[[2]], y[[2]]) expect_equal(x[[1]][2], y[[1]][2]) expect_equal(x[[2]][2], y[[2]][2]) x[[1]][1] <- 17L y[[1]][1] <- 17L expect_equal(x[[1]][1], 17) expect_equal(y[[1]][1], 17) x[2:3][[1]][[1]] <- 18L y <- juliaGet(x) expect_equal(y[2:3][[1]][[1]], 18) expect_equal(x[2:3][[1]][1], y[2:3][[1]][1]) expect_equal(x[[2]], y[[2]]) # Multiple dimensions y <- juliaEval("map( x-> [1;x;3], rand(2,2,2))") expect_equivalent(y[c(1,2)][[1]], y[[1]]) expect_equivalent(y[c(1,3)][[2]], y[[3]]) y[c(1,3)] <- juliaEval("[[17.0], [18.0]]") expect_equivalent(juliaGet(y[1]), list(17)) expect_equivalent(juliaGet(y[3]), list(18)) expect_equivalent(juliaGet(y)[c(4,5)], juliaGet(y[c(4,5)])) }) test_that("Callback functions", { x <- juliaEcho(function() {}) expect_equal(typeof(x), "closure") expect_equal(x, function() {}) outputenv <- new.env(parent = emptyenv()) outputenv$output <- c() doOutput <- function(x) { outputenv$output <- c(outputenv$output, x) } # Nested callback functions juliaEval('struct TestStruct f::Function end') juliaEval('function testNestedFun(ts::Vector{TestStruct}) map(t -> t.f(), ts) end') t <- juliaCall("testNestedFun", list(juliaCall("TestStruct", function() {doOutput("a")}), juliaCall("TestStruct", function() {doOutput("b")}), juliaCall("TestStruct", function() {doOutput("c")}))) expect_equal(outputenv$output, c("a", "b", "c")) # as named arguments outputenv$output <- c() juliaEval('function testNestedFunNamed(;ts::Vector{TestStruct}) map(t -> t.f(), ts) end') t <- juliaCall("testNestedFunNamed", ts = list(juliaCall("TestStruct", function() {doOutput("x")}), juliaCall("TestStruct", function() {doOutput("y")}))) expect_equal(outputenv$output, c("x", "y")) # test repeated call of nested functions outputenv$output <- c() juliaEval('function testNestedFun2(ts::Vector{TestStruct}) for i = 1:2 map(t -> t.f(), ts) end end') t <- juliaCall("testNestedFun2", list(juliaCall("TestStruct", function() {doOutput(1)}), juliaCall("TestStruct", function() {doOutput(2)}))) expect_equal(outputenv$output, c(1,2,1,2)) # Test multiple arguments outputenv$output <- c() juliaEval('function testNestedFunArgs(ts::Vector{TestStruct}, args...) map(t -> t.f(args...), ts) end') t <- juliaCall("testNestedFunArgs", list(juliaCall("TestStruct", function(x, y, z) {doOutput(x)}), juliaCall("TestStruct", function(x, y, z) {doOutput(z); return(c(5,6,7))})), 1, 2, 3) expect_equal(outputenv$output, c(1,3)) outputenv$output <- c() juliaEval('function testNestedAndUnnested(f::Function, ts::Vector{TestStruct}, args...) testNestedFunArgs(ts, args...) f(args...) end') t <- juliaCall("testNestedAndUnnested", function(x, y) {doOutput(x)}, list(juliaCall("TestStruct", function(x,y) {doOutput(x)}), juliaCall("TestStruct", function(x, y) {doOutput(y); return(list(5,6,7))})), 17,18) expect_equal(outputenv$output, c(17, 18, 17)) }) test_that("Callback function calling Julia erroring in Julia", { f <- juliaEval('(x) -> error("Error")') g <- function(x) {cat("OK"); f(x); print("NotOK"); x} output <- capture.output(expect_error(juliaCall("map", g, c(1,2,3)))) expect_equal(output, "OK") }) test_that("Builtin functions can be used as callback functions", { expect_equal(juliaCall("map", sqrt, c(1,4,9)), c(1,2,3)) }) test_that("Callback function might have error in R", { fOK <- function(x) { return(x+1) } fnotOK <- function(x) { if (x == 2) { stop("No even numbers please") } else { return(1) } } expect_equal(juliaCall("map", fOK, c(1,2,3)), c(2,3,4)) expect_error(juliaCall("map", fnotOK, c(1,2,3)), class = "error") }) test_that("Julia functions as members are transferred and usable in R", { op1 <- juliaFun("+") juliaEval('struct FunTestStruct f::Function end') funTestStruct <- juliaCall("FunTestStruct", op1) expect_equal(funTestStruct$f(1,2), 3) }) test_that("juliaCall checks given name before running", { expect_error(juliaCall()) expect_error(juliaCall(1)) expect_error(juliaCall(c("bla", "bla"))) }) test_that("juliaCall can handle keyword arguments with key \"name\"", { Pkg <- juliaImport("Pkg") # indirect via created function object expect_equal(Pkg$PackageSpec(name = "RData", version = "v0.10")$name, "RData") # direct call of juliaCall pkgspec <- juliaCall("Pkg.PackageSpec", name = "RData", version = "0.10") expect_equal(pkgspec$name, "RData") }) test_that("Documentation example of juliaFun", { juliaSqrt <- juliaFun("sqrt") expect_equal(juliaSqrt(2), sqrt(2)) expect_equal(juliaCall("map", juliaSqrt, c(1,4,9)), c(1,2,3)) }) test_that("Constructors stand for their types", { juliaEval('module TestTypeModule export TestType struct TestType end end') TestTypeModule <- juliaImport(".TestTypeModule") expect_equal(as.character(juliaCall("typeof", TestTypeModule$TestType)), "DataType") }) test_that("Parametric types are imported", { juliaEval("module ParametricTypeTestModule export MyParametricType struct MyParametricType{T} i::T end end") ParametricTypeTestModule <- juliaImport(".ParametricTypeTestModule") expect_equal(ParametricTypeTestModule$MyParametricType(2)$i, 2) expect_equal(ParametricTypeTestModule$MyParametricType("bla")$i, "bla") }) test_that("Starting and stopping Julia server" , { stopJulia() port <- startJuliaServer() expect_match(Sys.getenv("JULIACONNECTOR_SERVER"), paste0("localhost:", port)) # starting the server twice should give a warning and not do anything expect_warning(startJuliaServer()) expect_match(Sys.getenv("JULIACONNECTOR_SERVER"), paste0("localhost:", port)) # now the server should start successfully expect_equal(juliaEval("1"), 1) # set a variable in a separate process: rPath <- file.path(R.home("bin"), "R") if (Sys.info()["sysname"] == "Windows") { rPath <- paste0(rPath, ".exe") } system2(rPath, c("-e", shQuote( paste0("library(JuliaConnectoR); juliaEval('global x = 1')"))), stdout = FALSE) # check that variable is set in this Julia session, # i.e. that the two R processes share the same Julia process expect_equal(juliaEval("x"), 1) stopJulia() expect_equal(Sys.getenv("JULIACONNECTOR_SERVER"), "") expect_null(JuliaConnectoR:::pkgLocal$con) }) test_that("Julia startup options can be modified", { stopJulia() oldJuliaOpts <- Sys.getenv("JULIACONNECTOR_JULIAOPTS") # set project directory via startup opts projectPathWithSpaces <- file.path(tempdir(), "Dirname with Spaces") dir.create(file.path(tempdir(), "Dirname with Spaces")) specifyProjectOpts <- paste0('--project="', projectPathWithSpaces, '"') Sys.setenv("JULIACONNECTOR_JULIAOPTS" = paste0('--project="', projectPathWithSpaces, '"')) expect_match(capture.output({juliaEval("import Pkg; Pkg.status()")}), regexp = "Dirname with Spaces", fixed = TRUE, all = FALSE) stopJulia() # set two startup opts: project directory and deprecation warnings Sys.setenv("JULIACONNECTOR_JULIAOPTS" = paste(specifyProjectOpts, "--depwarn=no")) juliaDepWarn <- 'Base.depwarn("there is a deprecation warning", :foo)' expect_length(capture.output({juliaEval(juliaDepWarn)}), 0) stopJulia() # clean up unlink(projectPathWithSpaces, recursive = TRUE) if (oldJuliaOpts == '') { Sys.unsetenv("JULIACONNECTOR_JULIAOPTS") } else { Sys.setenv("JULIACONNECTOR_JULIAOPTS" = oldJuliaOpts) } }) test_that("JULIACONNECTOR_SERVER environment variable and Killing Julia works", { # if JULIACONNECTOR_SERVER is used, the server must be started with # "keeprunning = true" to work. skip_on_cran() JuliaConnectoR:::stopJulia() oldJuliaConnectorServer <- Sys.getenv("JULIACONNECTOR_SERVER") # start new JuliaConnectoR server port <- JuliaConnectoR:::startJuliaServer(12980) expect_match(Sys.getenv("JULIACONNECTOR_SERVER"), paste0("localhost:", port)) # test with wrong variable Sys.setenv("JULIACONNECTOR_SERVER" = "wrong form") expect_error(JuliaConnectoR:::startJulia(), regexp = ":") # now for real Sys.setenv("JULIACONNECTOR_SERVER" = paste("localhost", port, sep = ":")) # test some command, if it works expect_equal(juliaCall("prod", c(1,2)), 2) # test killing JuliaConnectoR:::killJulia() if (oldJuliaConnectorServer == "") { Sys.unsetenv("JULIACONNECTOR_SERVER") } else { Sys.setenv("JULIACONNECTOR_SERVER" = oldJuliaConnectorServer) } }) test_that("Error if Julia is not setup properly", { oldJuliaBindir <- Sys.getenv("JULIA_BINDIR") Sys.setenv("JULIA_BINDIR" = "/this/isnot/a/directory/") expect_error(JuliaConnectoR:::runJuliaServer(16752), "No Julia executable file found") if (oldJuliaBindir == "") { Sys.unsetenv("JULIA_BINDIR") } else { Sys.setenv("JULIA_BINDIR" = oldJuliaBindir) } expect_equal(1, juliaEval("1")) }) test_that("Fallback on juliaup fails correctly", { skip_on_cran() juliaCmdJuliaup <- file.path(Sys.getenv("HOME"), ".juliaup", "bin", "julia") if (!file.exists(juliaCmdJuliaup)) { expect_error(JuliaConnectoR:::fallbackOnDefaultJuliaupPath(), regexp = "\\?`Julia-Setup`") } else { expect_equal(juliaCmdJuliaup, JuliaConnectoR:::fallbackOnDefaultJuliaupPath()) } }) test_that("Circular references do not lead to a crash", { definition <- 'mutable struct TestRecur r::Union{TestRecur, Int} end' try({juliaEval(definition)}, silent = TRUE) # (ignore redefinition error) r <- juliaEval("r1 = TestRecur(2); r2 = TestRecur(r1); r1.r = r2; r1") expect_match(capture.output({print(juliaEcho(r))}), regex = "circular reference", all = FALSE) expect_error(juliaEcho(juliaGet(r)), regex = "Circular reference") # reference only r <- juliaEval("r1 = TestRecur(2); r2 = TestRecur(r1); r1.r = r2; r1") expect_match(capture.output({print(juliaEcho(r))}), regex = "circular reference", all = FALSE) expect_equal(juliaCall("typeof", r$r$r), juliaCall("typeof", r)) }) test_that("Anonymous functions can be transferred", { af1 <- juliaEval("() -> 17") expect_equal(af1(), 17) expect_equal(juliaEcho(af1)(), 17) rm(af1) af2 <- juliaEval("(args...; kwargs...) -> 19") expect_equal(af2(list(), 3, bla = 2), 19) rm(af2) # Test cleaning of references invisible(gc(verbose = FALSE)) expect_gt(length(JuliaConnectoR:::pkgLocal$finalizedRefs), 0) juliaEval("1") expect_null(JuliaConnectoR:::pkgLocal$finalizedRefs) juliaEval("struct TestAnonFunStruct f::Function end") afs <- juliaEval("TestAnonFunStruct(() -> 20)") expect_equal(afs$f(), 20) }) test_that("Test BigInt: a Julia object with external pointers", { # Note: It is not really recommended to translate objects with # external pointers. Nevertheless, it should work and not cause a crash. i1 <- juliaGet(juliaCall("BigInt", 2147483647L)) i2 <- juliaGet(juliaCall("BigInt", 2147483647L)) p <- juliaGet(juliaCall("prod", list(i1, i2, i1))) p2 <- juliaGet(juliaCall("prod", list(i1, i2, i1))) juliaCall("GC.gc") # references in sharedheap must survive this jldiv <- juliaFun("div") expect_equal(juliaCall("Int", jldiv(jldiv(p,i1), i2)), 2147483647L) expect_equal(juliaLet("Int(div(div(p,i1),i2))", p = p, i1 = i1, i2 = i2), 2147483647) f <- juliaLet("p/i1/i2", p = p, i1 = i1, i2 = i2) expect_equal(juliaCall("Float64", f), 2147483647) i1Ref <- attr(i1, "JLREF")$ref expect_true(juliaLet("communicator.sharedheap[ref].refcount > 0", ref = i1Ref, communicator = pkgLocal$communicator)) i1 <- NULL invisible(gc()) juliaEval("1") # after one command, the references from R should be cleaned up # Multiple calls to the Julia garbage are necessary for a complete clean-up as # one finalization may make another object unreachable, which will then # be cleaned up in the next run. juliaCall("GC.gc") juliaCall("GC.gc") juliaCall("GC.gc") expect_false(juliaLet("haskey(communicator.sharedheap, ref)", ref = i1Ref, communicator = pkgLocal$communicator)) }) test_that("Serialized mutable struct can be restored", { juliaEval("mutable struct TestSerializationStruct i::Int end") x <- juliaGet(juliaEval("TestSerializationStruct(1)")) tmpfile <- tempfile() save("x", file = tmpfile) x <- NULL #juliaEval("println(RConnector.sharedheap)") invisible(gc()) juliaEval("1") juliaCall("GC.gc") load(tmpfile) msg <- capture.output(type = "message", { expect_equal(juliaEcho(x)$i, 1) }) expect_match(msg, "external references", all = FALSE) x <- NULL juliaEval("1") juliaCall("GC.gc") # copy of the serialized copy is cleaned up file.remove(tmpfile) }) test_that("Object with unexported function defined in different modules", { juliaEval(' module FirstModule struct FirstType f::Function end f(x::FirstType) = 1 end module SecondModule import ..FirstModule.f struct SecondType f::Function end f(x::SecondType) = 2 end #module') # It doesn't matter whether SecondModule.f or FirstModule.f is used ft1 <- juliaEval("FirstModule.FirstType(SecondModule.f)") ft2 <- juliaEval("SecondModule.SecondType(FirstModule.f)") expect_equal(juliaLet("x.f(x)", x = ft1), 1) expect_equal(juliaLet("x.f(x)", x = ft2), 2) expect_equal(juliaLet("x.f(y)", x = ft1, y = ft2), 2) expect_equal(juliaLet("x.f(y)", x = ft2, y = ft1), 1) }) test_that("AbstractArrays are transferred by reference and can be translated to struct", { a <- juliaEval('using SparseArrays A = sparse([1, 2, 3], [1, 2, 3], [0, 2, 0])') expect_s3_class(a, "JuliaArrayProxy") # indexing expect_equal(a[[2,2]], 2) # backtranslation expect_true(juliaCall("issparse", juliaGet(a))) testEcho(a) }) test_that("Operators can be imported and used", { suppressWarnings({jla <- juliaImport("LinearAlgebra", all = FALSE)}) expect_equal(jla$``(1,2), 2) suppressWarnings({base <- juliaImport("Base", all = FALSE)}) expect_true(base$``(c(1,2), c(1,2,3))) }) test_that("Boltzmann machine can be trained and used", { #skip_on_cran() juliaEval('using Pkg; try @eval import BoltzmannMachines catch ex Pkg.add(PackageSpec(name = "BoltzmannMachines", version = v"1.2")) end') # Set a random seed in Julia juliaEval("using Random; Random.seed!(5);") BM <- juliaImport("BoltzmannMachines") # a test data set from the BoltzmannMachines-package, just to have some data x <- BM$barsandstripes(50L, 4L) x # Train DBMs with dbm <- BM$fitdbm(x, epochs = 2L, nhiddens = c(4L,3L)) dbm dbm2 <- BM$fitdbm(x, epochs = 1L, pretraining = list(BM$TrainLayer(nhidden = 4L), BM$TrainLayer(nhidden = 3L))) dbm2 <- juliaGet(dbm2) # Use a trained model to generate samples gensamples <- BM$samples(dbm, 10L) expect_equal(nrow(gensamples), 10) # Evaluate the model: Likelihood estimation ... BM$loglikelihood(dbm2, x) # ... or exact calculation (possible for such a small model) BM$exactloglikelihood(dbm2, x) }) test_that("juliaPut", { x <- juliaPut(c(1,2,3)) expect_s3_class(x, "JuliaSimpleArrayProxy") expect_equal(x[[1]], 1) expect_equal(x[1:2], c(1,2)) expect_type(x[3], "double") x[2:3] <- 0 expect_equal(juliaCall("sum", x), 1) expect_error(juliaPut(juliaEval("(a=1,b=2,c=3)"))) x <- juliaPut("bla") expect_equal(as.character(juliaCall("typeof", x)), "String") # use on translated object x <- juliaGet(juliaEval("[(1,2), (2,3)]")) expect_s3_class(juliaPut(x), "JuliaArrayProxy") }) test_that("Display of default printing is limited to width", { x <- juliaEval("rand(100, 100)") y <- juliaPut(x) printOutput <- capture.output({print(y)}) expect_lte(nchar(printOutput[length(printOutput)]), getOption("width")) }) test_that("Error message for tables", { capture.output({JuliaConnectoR:::showUpdateTablesMsg()}) x <- juliaEcho(list(c(1,2), c(1,2,3))) expect_s3_class(x, "JuliaProxy") expect_error({as.data.frame(x)}) }) test_that("Data frame can be translated", { x <- data.frame(x = c(0, 2, 4), y = c("bla", "blup", "ha"), stringsAsFactors = FALSE) testEcho(x) y <- juliaEcho(x) x2 <- as.data.frame(juliaEcho(x)) expect_s3_class(x2, "data.frame") expect_equal(x2$x, c(0,2,4)) expect_equal(x2$y, c("bla", "blup", "ha")) testEcho(data.frame()) Pkg <- juliaImport("Pkg") if (juliaEval('VERSION < v"1.6"')) { subproject <- "1_0" } else if (juliaEval('VERSION < v"1.10"')) { subproject <- "1_6" } else { subproject <- "1_10" } Pkg$activate(system.file("examples", "IndexedTables-Project", subproject, package = "JuliaConnectoR")) Pkg$instantiate() juliaEval("import IndexedTables") x <- data.frame(x = c(0, 2, 4), y = c("bla", "blup", "ha"), stringsAsFactors = FALSE) y <- juliaCall("IndexedTables.table", x) expect_equal(as.data.frame(x), x) # Works also without column names (Issue #16) df <- juliaCall("IndexedTables.table", 1:3, 11:13) df <- as.data.frame(df) expect_equal(df[, 1], 1:3) expect_equal(df[, 2], 11:13) }) test_that("Attempt to translate Julia Table with incompatible types results in proper error", { juliaEval("import IndexedTables") x <- juliaEval('IndexedTables.table(["a", 1, 2], [1,2,3])') expect_error(as.data.frame(x), regexp = 'Column type "Any" cannot be translated') }) test_that("Julia types are converted to r compatible types in data frames", { x1 <- juliaEval("[1.0f0*im + 1.0f0, 0.0f0]") x2 <- juliaEval('map(i -> SubString(i, 2,3), ["asdf", "wert"])') x3 <- juliaEval('[true, false]') x4 <- juliaEval("['a', 'b']") x <- juliaLet('Dict(:x1 => x1, :x2 => x2, :x3 => x3, :x4 => x4)', x1 = x1, x2 = x2, x3 = x3, x4 = x4) x <- as.data.frame(x) expect_equivalent(x$x1, x1) expect_equal(x$x2, c("sd", "er")) expect_equal(x$x3, x3) expect_equivalent(x$x4, x4) }) test_that("Broadcasting via dot syntax works", { expect_equal(juliaCall("sin.", c(0, 1/3*pi, pi/2)), c(0, sqrt(3)/2, 1)) juliaEval("import Statistics") arrofarrs <- juliaEval("[[2;3], [4;5]]") expect_equal(juliaCall("Statistics.mean.", arrofarrs), c(2.5, 4.5)) expect_equal(juliaGet(juliaCall("Statistics.mean.", arrofarrs, dims = 1L)), juliaGet(juliaEval("[[2.5], [4.5]]"))) # Test functions with multiple arguments juliaEval("module TestBroadcasting function my_add(x,y) x+y end function my_mul(x,y) x*y end end") expect_equal(juliaCall("TestBroadcasting.my_add.", 10, c(1,2,3)), juliaEval("TestBroadcasting.my_add.(10, [1, 2, 3])")) expect_equal(juliaCall("TestBroadcasting.my_mul.", c(1,2,3), c(4,5,6)), juliaEval("TestBroadcasting.my_mul.([1, 2, 3], [4, 5, 6])")) # Check that dimension mismatch leads to an error expect_error(juliaCall("TestBroadcasting.my_mul.", c(1,2,3,4), c(5,6))) }) test_that("Environemnt variables for Julia can be set", { # prepare os <- Sys.info()['sysname'] oldJuliaEnv <- Sys.getenv("JULIACONNECTOR_JULIAENV") JuliaConnectoR:::stopJulia() # Test with one variable Sys.setenv(JULIACONNECTOR_JULIAENV = "TESTVARIABLE1=1") if (os == "Windows") { expect_warning(juliaEval("1+1")) } else { expect_equal(juliaEval('ENV["TESTVARIABLE1"]'), "1") } JuliaConnectoR:::stopJulia() # Test with two variables Sys.setenv(JULIACONNECTOR_JULIAENV = "TESTVARIABLE1=1; TESTVARIABLE2='abc'") if (os == "Windows") { expect_warning(juliaEval("1+1")) } else { expect_equal(juliaEval('ENV["TESTVARIABLE1"]'), "1") expect_equal(juliaEval('ENV["TESTVARIABLE2"]'), "abc") } JuliaConnectoR:::stopJulia() # LD_LIBRARY_PATH can be set via JULIACONNECTOR_JULIAENV Sys.setenv(JULIACONNECTOR_JULIAENV = "LD_LIBRARY_PATH='abc'") if (os == "Linux") { expect_match(JuliaConnectoR:::getJuliaEnv(), "LD_LIBRARY_PATH='abc'") } # clean up if (oldJuliaEnv == "") { Sys.unsetenv("JULIACONNECTOR_JULIAENV") } else { Sys.setenv(JULIACONNECTOR_JULIAENV = oldJuliaEnv) } }) test_that("Iris/Flux example from README works", { skip_on_cran() skip_on_covr() #skip_if(Sys.info()["login"] %in% c("lenz", "Stefan Lenz", "selectstern", "lenzstef")) cat("\nExecuting README examples...\n") if (juliaEval('VERSION < v"1.9"')) { skip("Skip Flux example because Julia version is too old") } else { projectFolder <- "project_1_9" } Pkg <- juliaImport("Pkg") Pkg$activate(system.file("examples", "iris-example", projectFolder, package = "JuliaConnectoR", mustWork = TRUE)) Pkg$instantiate() irisExampleJl <- system.file("examples", "iris-example", "iris-example.jl", package = "JuliaConnectoR", mustWork = TRUE) irisExampleJuliaCode <- readLines(irisExampleJl) juliaEval(paste(irisExampleJuliaCode, collapse = "\n")) expect_true(juliaEval("accuracy(model, testdata) > 0.9")) irisExampleR <- system.file("examples", "iris-example", "iris-example.R", package = "JuliaConnectoR", mustWork = TRUE) irisExampleRCode <- readLines(irisExampleR) scriptEnv <- new.env(emptyenv()) suppressWarnings(eval(parse(text = paste(irisExampleRCode, collapse = "\n")), envir = scriptEnv)) # Test that training the model worked print(juliaCall("accuracy", scriptEnv$model, scriptEnv$testdata)) expect_true(juliaCall("accuracy", scriptEnv$model, scriptEnv$testdata) > 0.9) Pkg$activate() # use default environment again }) test_that("Boltzmann example from README works", { skip_on_cran() returningError <- function(x) { paste0("tryCatch({", x , '},', 'error = function(e) { paste("e: ",e$message) },', 'warning = function(w) { paste("w: ", w$message) })') } juliaEval('import Pkg; try @eval import BoltzmannMachines catch ex Pkg.add(PackageSpec(name = "BoltzmannMachines", version = v"1.3")) end') boltzmannExampleR <- system.file("examples", "boltzmann-example.R", package = "JuliaConnectoR", mustWork = TRUE) bmExample <- readLines(boltzmannExampleR) bmExample <- sub("plot\\(.*", "", bmExample) bmScriptEnv <- new.env(emptyenv()) code <- paste(bmExample, collapse = "\n") code <- returningError(code) eval(parse(text = code), envir = bmScriptEnv) expect_s3_class(bmScriptEnv$dbm, "JuliaProxy") })