print(paste("Starting tests with Julia version", juliaVersion))
juliaCall("string", list(as.integer(1), "bla" = 23L))
"Eval with semicolon at end returns NULL")
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", {
stderroutput <- capture.output(type = "message", {
expect_match(stderroutput, "[2ua\n]+") # TODO problems with non-ascii-characters
ret <- juliaLet('println(22); @warn "This might be serious"; 17')
expect_match(stderroutput[1], "This might be serious")
test_that("A return value of nothing is not printed", {
test_that("Test loading and importing a complex package", {
test_that("Error when trying to import a non-existent package or module", {
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", {
expect(juliaCall("<", v1, v2), "Comparison must work")
test_that("Echo: empty R vector", {testEcho(c())})
expect(is.integer(juliaEval("1")), "1 is not integer")
expect(is.double(juliaEval("2^52")), "2^52 is not double") # TODO inexactness?
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("UInt32, Float16, and Float32 translated to doubles in R", {
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", {
test_that("Echo: matrix of Float64 in Julia", {
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 <- matrix(c("bla", NA, NA, "NA"), nrow = 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)))
x <- juliaEval("[1 missing 3; 3 missing 4 ]")
x <- juliaEval("[2^52 missing 3; 2^53 missing 4 ]")
x <- juliaEval("Union{Int32,Missing}[1 missing 3; 3 missing 4 ]")
x <- juliaEval("Union{Int16,Missing}[-1 missing 3; 3 missing 4 ]")
testEcho(matrix(c(1L, 2L, NA, NA), nrow = 2))
test_that("Echo: Double vector with missing values", {
x <- juliaEval("[1.0 missing 3.0; 3.0 missing 4.0 ]")
x <- juliaEval("Union{Float32,Missing}[1.0f0 missing 3.0f0; 3.0f0 missing 4.0f0 ]")
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),
dirtyNA <- readBin(dirtyNa, what = "double", endian = "little")
expect_true(juliaLet("ismissing(x[2])", x = c(1, dirtyNA)))
dirtyComplexNA <- complex(real = dirtyNA, imaginary = dirtyNA)
expect_true(juliaLet("ismissing(x[2]) && ismissing(x[3])", x = 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)))
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
expect_true(juliaLet("ismissing(x[2])", x = c(1+1i, NA)))
test_that("Missing values transferred as NA", {
test_that("Logical with missing values", {
juliaLet("x[1] && !x[2] && ismissing(x[3])", x = c(TRUE, FALSE, NA))
test_that("Complex with missing values", {
x <- juliaEval("[1+1im 1+2im; 0 missing]")
expect_equivalent(x, matrix(c(1+1i, 0, 1+2i, NA), nrow = 2))
x <- juliaEval("[1.0+im 2.0+im; 0.0 missing]")
expect_equal(x, matrix(c(1+1i, 0, 2+1i, NA), nrow = 2))
ptrs <- juliaCall("map", arrPtr, as.integer(1:3))
r <- juliaEval("global reftestvar = 1; Ref(reftestvar)")
test_that("Complex values are handled first class", {
testEcho(matrix(c(1, 0, 0, -1), ncol = 2))
expect(all(jla$eigvals(matrix(c(1, 0, 0, -1), ncol = 2),
expect(all(jla$eigmax(matrix(c(0, 1i, -1i, 0), ncol = 2)) == 1.0), "")
complexTypeParameters = c("Int8", "Int16", "Int32", "Int64",
for (complexPar in complexTypeParameters) {
juliaComplexType <- paste0("Complex{", complexPar, "}")
carr <- juliaEval(paste0(juliaComplexType, "[4+4im, 2-2im]"))
test_that("Echo: 1-element Int16 Vector", {
test_that("Echo: Single UInt128", {testEcho(juliaEval('UInt128(2)^100 +1'))})
test_that("Echo: 1-element UInt128 Vector", {
test_that("Echo: List with NULL elements", {
x <- juliaGet(juliaEcho(list(1, NULL, 3)))
test_that("Mutable struct usable by reference", {
expect_true(all(get("ref", jlRef) == get("ref", refEcho)))
expect_false(juliaLet("haskey(communicator.sharedheap, ref)", ref = ref,
test_that("Immutable struct usable by reference and translated", {
test_that("Currying in juliaFun works", {
test_that("Multidimensional object arrays work", {
x <- juliaLet("map(MultiTestStruct, c)", c = content)
x[3:4, as.integer(4:5), 1L] <- juliaEval("MultiTestStruct(20.0)")
expect_error(juliaEcho(y), regexp = "Incorrect dimensions")
# Must also work with dimensions of zero
x <- juliaEval("Array{MultiTestStruct}(undef, 0, 0, 0)")
test_that("Arrays with undef entries are translated", {
# multidimensional arrays with undefs and values
x <- juliaLet("x = Array{MutableTestStruct}(undef, 2, 3, 4);
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", {
expect_equal(as.character(juliaLet("String(x[1:5])", x = str)), "Hello")
strArr <- juliaLet("String[a,b]", a = str, b = str)
strArr <- juliaLet("String[a,b,c]", a = str, b = str, c = str)
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", {
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")
testEcho(juliaLet("collect(zip(x,y))", x = c(1L,2L, 3L), y = c(1,0,1)))
namedTuple <- juliaLet("y=2*x; z = 3*u + 1; (x=y, y=z)", x=2, u=4)
d <- juliaEval("Dict(:bla => 1.0, :blup => 3.0)")
d[juliaExpr(":hi"), juliaExpr(":hello")] <- c(15, 14)
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))
d <- juliaLet("Dict(zip(x, y))", x = list("bla"), y = list(1))
d <- juliaLet("Dict(zip(x, y))", x = list(), y = list())
expect_length(setdiff(juliaGet(juliaLet("setdiff(s1, s2)", s1 = s1, s2 = s2)),
test_that("Exotic objects handled gracefully", {
expect_warning({ y <- juliaEcho(list(x = x))},
regexp = "not translatable", all = FALSE)
# Test complex constructor with all kinds of types
test_that("Complex Julia object with different member type", {
rand(Float16, 2), rand(Float32, 2), rand(Float64, 2),
test_that("Private inner constructor is forged", {
test_that("Imported modules are printed", {
expect_match(regexp = 'Julia module "Pkg": [0-9]+ function.*',
itm1 <- juliaImport(module, all = FALSE)
'Julia module "Main.ImportTestModule1": 1 function.*'
itm1_2 <- juliaImport(".ImportTestModule1", all = FALSE)
test_that("Empty module does not cause problems", {
expect_length(ls(juliaImport(".EmptyTestModule")), 2) # (eval and include)
expect_length(ls(juliaImport(".EmptyTestModule2", all = FALSE)), 0)
test_that("Errors are handled gracefully", {
test_that("Vectors of objects can be accessed by index via proxy", {
expect_equal(x[[1]][x[[2]] == 5], 2) # logical indexing
# juliaGet version must behave in the same way as the proxy version
outputenv <- new.env(parent = emptyenv())
juliaEval('function testNestedFun(ts::Vector{TestStruct}) map(t -> t.f(), ts) end')
juliaEval('function testNestedFunNamed(;ts::Vector{TestStruct}) map(t -> t.f(), ts) end')
t <- juliaCall("testNestedFunNamed", ts =
# test repeated call of nested functions
juliaEval('function testNestedFun2(ts::Vector{TestStruct}) for i = 1:2 map(t -> t.f(), ts) end end')
juliaEval('function testNestedFunArgs(ts::Vector{TestStruct}, args...) map(t -> t.f(args...), ts) end')
list(juliaCall("TestStruct", function(x, y, z) {doOutput(x)}),
juliaCall("TestStruct", function(x, y, z) {doOutput(z); return(c(5,6,7))})),
juliaCall("TestStruct", function(x, y) {doOutput(y); return(list(5,6,7))})),
test_that("Callback function calling Julia erroring in Julia", {
g <- function(x) {cat("OK"); f(x); print("NotOK"); x}
output <- capture.output(expect_error(juliaCall("map", g, c(1,2,3))))
test_that("Builtin functions can be used as callback functions", {
test_that("Callback function might have error in R", {
expect_error(juliaCall("map", fnotOK, c(1,2,3)), class = "error")
test_that("Julia functions as members are transferred and usable in R", {
test_that("juliaCall checks given name before running", {
test_that("juliaCall can handle keyword arguments with key \"name\"", {
expect_equal(Pkg$PackageSpec(name = "RData", version = "v0.10")$name,
pkgspec <- juliaCall("Pkg.PackageSpec", name = "RData", version = "0.10")
test_that("Documentation example of juliaFun", {
test_that("Constructors stand for their types", {
test_that("Parametric types are imported", {
test_that("Starting and stopping Julia server" , {
# starting the server twice should give a warning and not do anything
# now the server should start successfully
if (Sys.info()["sysname"] == "Windows") {
paste0("library(JuliaConnectoR); juliaEval('global x = 1')"))),
# check that variable is set in this Julia session,
# i.e. that the two R processes share the same Julia process
test_that("Julia startup options can be modified", {
# set project directory via startup opts
projectPathWithSpaces <- file.path(tempdir(), "Dirname with Spaces")
specifyProjectOpts <- paste0('--project="', projectPathWithSpaces, '"')
regexp = "Dirname with Spaces", fixed = TRUE, all = FALSE)
# set two startup opts: project directory and deprecation warnings
juliaDepWarn <- 'Base.depwarn("there is a deprecation warning", :foo)'
test_that("JULIACONNECTOR_SERVER environment variable and Killing Julia works", {
# if JULIACONNECTOR_SERVER is used, the server must be started with
Sys.setenv("JULIACONNECTOR_SERVER" = paste("localhost", port, sep = ":"))
test_that("Error if Julia is not setup properly", {
expect_error(JuliaConnectoR:::runJuliaServer(16752), "No Julia executable file found")
test_that("Fallback on juliaup fails correctly", {
juliaCmdJuliaup <- file.path(Sys.getenv("HOME"), ".juliaup", "bin", "julia")
test_that("Circular references do not lead to a crash", {
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")
test_that("Anonymous functions can be transferred", {
af2 <- juliaEval("(args...; kwargs...) -> 19")
expect_equal(af2(list(), 3, bla = 2), 19)
afs <- juliaEval("TestAnonFunStruct(() -> 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.
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
expect_equal(juliaLet("Int(div(div(p,i1),i2))", p = p, i1 = i1, i2 = i2),
ref = i1Ref, communicator = pkgLocal$communicator))
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
expect_false(juliaLet("haskey(communicator.sharedheap, ref)", ref = i1Ref,
test_that("Serialized mutable struct can be restored", {
msg <- capture.output(type = "message", {
expect_match(msg, "external references", all = FALSE)
juliaCall("GC.gc") # copy of the serialized copy is cleaned up
test_that("Object with unexported function defined in different modules", {
# It doesn't matter whether SecondModule.f or FirstModule.f is used
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", {
test_that("Operators can be imported and used", {
suppressWarnings({jla <- juliaImport("LinearAlgebra", all = FALSE)})
suppressWarnings({base <- juliaImport("Base", all = FALSE)})
test_that("Boltzmann machine can be trained and used", {
Pkg.add(PackageSpec(name = "BoltzmannMachines", version = v"1.2"))
# a test data set from the BoltzmannMachines-package, just to have some data
dbm <- BM$fitdbm(x, epochs = 2L, nhiddens = c(4L,3L))
pretraining = list(BM$TrainLayer(nhidden = 4L),
# Use a trained model to generate samples
# Evaluate the model: Likelihood estimation ...
#  ... or exact calculation (possible for such a small model)
test_that("Display of default printing is limited to width", {
test_that("Data frame can be translated", {
skip_on_cran() # don't test IndexedTables on CRAN
} else if (juliaEval('VERSION < v"1.10"')) {
# Works also without column names (Issue #16)
df <- juliaCall("IndexedTables.table", 1:3, 11:13)
test_that("Attempt to translate Julia Table with incompatible types results in proper error", {
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", {
x2 <- juliaEval('map(i -> SubString(i, 2,3), ["asdf", "wert"])')
test_that("Broadcasting via dot syntax works", {
expect_equal(juliaGet(juliaCall("Statistics.mean.", arrofarrs, dims = 1L)),
# Test functions with multiple arguments
juliaEval("TestBroadcasting.my_mul.([1, 2, 3], [4, 5, 6])"))
# Check that dimension mismatch leads to an error
test_that("Environemnt variables for Julia can be set", {
# LD_LIBRARY_PATH can be set via JULIACONNECTOR_JULIAENV
test_that("Iris/Flux example from README works", {
#skip_if(Sys.info()["login"] %in% c("lenz", "Stefan Lenz", "selectstern", "lenzstef"))
skip("Skip Flux example because Julia version is too old")
package = "JuliaConnectoR", mustWork = TRUE))
irisExampleJl <- system.file("examples", "iris-example", "iris-example.jl",
package = "JuliaConnectoR", mustWork = TRUE)
irisExampleR <- system.file("examples",  "iris-example", "iris-example.R",
suppressWarnings(eval(parse(text = paste(irisExampleRCode, collapse = "\n")),
expect_true(juliaCall("accuracy", scriptEnv$model, scriptEnv$testdata) > 0.9)
Pkg$activate() # use default environment again
test_that("Boltzmann example from README works", {
'error = function(e) { paste("e: ",e$message) },',
'warning = function(w) { paste("w: ", w$message) })')
Pkg.add(PackageSpec(name = "BoltzmannMachines", version = v"1.3"))
bmExample <- sub("plot\\(.*", "", bmExample)
code <- paste(bmExample, collapse = "\n")
eval(parse(text = code), envir = bmScriptEnv)