From 7086f63f0c4cfa8c189f97f36d915ba3e08538fb Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Fri, 18 Apr 2025 15:40:59 -0500 Subject: [PATCH 01/16] REPL completion fix on 1.12 --- src/RPrompt.jl | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/RPrompt.jl b/src/RPrompt.jl index 2d75e1d6..d34a2869 100644 --- a/src/RPrompt.jl +++ b/src/RPrompt.jl @@ -22,7 +22,6 @@ import ..RCall: REvalError, RParseEOF - function simple_showerror(io::IO, er) Base.with_output_color(:red, io) do io print(io, "ERROR: ") @@ -137,7 +136,7 @@ mutable struct RCompletionProvider <: LineEdit.CompletionProvider end # Julia PR #54311 (backported to 1.11) added the `hint` argument -if v"1.11.0-beta1.46" <= VERSION < v"1.12.0-DEV.0" || VERSION >= v"1.12.0-DEV.468" +@static if v"1.11.0-beta1.46" <= VERSION < v"1.12.0-DEV.0" || VERSION >= v"1.12.0-DEV.468" using REPL.REPLCompletions: bslash_completions else function bslash_completions(string::String, pos::Int, hint::Bool=false) @@ -145,6 +144,15 @@ else end end +# Julia PR 54800 messed up REPL completion, fix adapted from https://github.com/JuliaLang/IJulia.jl/pull/1147 +@static if VERSION < v"1.12.0-DEV.1716" + completion_text_ = REPLCompletions.completion_text +else + function completion_text_(c)::String + return REPLCompletions.named_completion(c).completion + end +end + function LineEdit.complete_line(c::RCompletionProvider, s; hint::Bool=false) buf = s.input_buffer partial = String(buf.data[1:buf.ptr-1]) @@ -152,7 +160,7 @@ function LineEdit.complete_line(c::RCompletionProvider, s; hint::Bool=false) full = LineEdit.input_string(s) ret, range, should_complete = bslash_completions(full, lastindex(partial), hint)[2] if length(ret) > 0 && should_complete - return map(REPLCompletions.completion_text, ret), partial[range], should_complete + return map(completion_text_, ret), partial[range], should_complete end # complete r From d23f948b06a82d93205e32b4f4c42d96dd92ad75 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Fri, 18 Apr 2025 23:09:48 +0000 Subject: [PATCH 02/16] Update src/RPrompt.jl Co-authored-by: Steven G. Johnson --- src/RPrompt.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/RPrompt.jl b/src/RPrompt.jl index d34a2869..83c931bc 100644 --- a/src/RPrompt.jl +++ b/src/RPrompt.jl @@ -145,12 +145,10 @@ else end # Julia PR 54800 messed up REPL completion, fix adapted from https://github.com/JuliaLang/IJulia.jl/pull/1147 -@static if VERSION < v"1.12.0-DEV.1716" - completion_text_ = REPLCompletions.completion_text +if isdefined(REPLCompletions, :named_completion) # julia#54800 (julia 1.12) + completion_text_(c) = REPLCompletions.named_completion(c).completion::String else - function completion_text_(c)::String - return REPLCompletions.named_completion(c).completion - end + completion_text_(c) = REPLCompletions.completion_text(c) end function LineEdit.complete_line(c::RCompletionProvider, s; hint::Bool=false) From 96b84bf7f64d6137be7e0fe79c8ab70498866f60 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Fri, 18 Apr 2025 18:11:52 -0500 Subject: [PATCH 03/16] version bump --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index d8c738f8..39d6a77d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "RCall" uuid = "6f49c342-dc21-5d91-9882-a32aef131414" authors = ["Douglas Bates ", "Randy Lai ", "Simon Byrne "] -version = "0.14.7" +version = "0.14.8" [deps] CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" From 9d9715b300a2b93d7ef409f0267ee3b1a19ac51e Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Fri, 18 Apr 2025 23:57:10 -0500 Subject: [PATCH 04/16] fix namespace test on 1.12 --- test/namespaces.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/namespaces.jl b/test/namespaces.jl index 8026aa19..562f1892 100644 --- a/test/namespaces.jl +++ b/test/namespaces.jl @@ -17,7 +17,8 @@ module NamespaceTests @test rcopy(rcall(MASS.ginv, RObject([1 2; 0 4]))) ≈ [1 -0.5; 0 0.25] @rimport MASS as mass @test rcopy(rcall(mass.ginv, RObject([1 2; 0 4]))) ≈ [1 -0.5; 0 0.25] - @rlibrary MASS + # explicit eval necessary as of Julia 1.12 because we're nested under an `if` + @eval(@rlibrary MASS) @test rcopy(rcall(ginv, RObject([1 2; 0 4]))) ≈ [1 -0.5; 0 0.25] end From 8618b4853f95663a2f34f5d1546433ac0dcbe19e Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Sun, 20 Apr 2025 05:10:34 +0000 Subject: [PATCH 05/16] test on beta --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88f26544..500797fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,7 @@ jobs: version: - 'min' - '1' + - 'beta' - 'nightly' R: - 'release' From ca9988d7832ae9d2c959294c620057a09d3077a3 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Sun, 20 Apr 2025 05:11:52 +0000 Subject: [PATCH 06/16] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 500797fa..a70eb882 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: version: - 'min' - '1' - - 'beta' + - 'pre' - 'nightly' R: - 'release' From 3c1cbc0cbffde0cf92d548fbda09c0b3e1aea78e Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Tue, 22 Apr 2025 15:17:03 -0500 Subject: [PATCH 07/16] add locking in completion generation --- src/RPrompt.jl | 92 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/src/RPrompt.jl b/src/RPrompt.jl index 83c931bc..21f37df6 100644 --- a/src/RPrompt.jl +++ b/src/RPrompt.jl @@ -28,6 +28,8 @@ function simple_showerror(io::IO, er) showerror(io, er) println(io) end + + return nothing end function parse_status(script::String) @@ -41,7 +43,7 @@ function parse_status(script::String) status = :error end end - status + return status end function repl_eval(script::String, stdout::IO, stderr::IO) @@ -72,6 +74,12 @@ function repl_eval(script::String, stdout::IO, stderr::IO) end end +@static if isdefined(LineEdit, :check_show_hints) + refresh_line_(s) = LineEdit.refresh_line(s) +else + refresh_line_(s) = LineEdit.check_show_hint(s) +end + function bracketed_paste_callback(s, o...) input = LineEdit.bracketed_paste(s) sbuffer = LineEdit.buffer(s) @@ -98,7 +106,7 @@ function bracketed_paste_callback(s, o...) # parse the input line by line while nextpos < m next_result = findnext("\n", input, nextpos + 1) - if next_result == nothing + if isnothing(next_result) nextpos = m else nextpos = next_result[1] @@ -110,7 +118,7 @@ function bracketed_paste_callback(s, o...) (nextpos == m && !endswith(input, '\n')) # error / continue and the end / at the end but no new line LineEdit.replace_line(s, input[oldpos:end]) - LineEdit.refresh_line(s) + refresh_line_(s) break elseif status == :incomplete && nextpos < m continue @@ -128,11 +136,28 @@ function bracketed_paste_callback(s, o...) end oldpos = nextpos + 1 end - LineEdit.refresh_line(s) + refresh_line_(s) end mutable struct RCompletionProvider <: LineEdit.CompletionProvider - r::REPL.LineEditREPL + repl::REPL.LineEditREPL + line_modify_lock::ReentrantLock + hint_generation_lock::ReentrantLock + function RCompletionProvider(repl::REPL.LineEditREPL) + mistate = repl.mistate + @static if hasfield(LineEdit.MIState, :hint_generation_lock) + hint_generation_lock = repl.mistate.hint_generation_lock + else + hint_generation_lock = ReentrantLock() + end + @static if hasfield(LineEdit.MIState, :line_modify_lock) + line_modify_lock = repl.mistate.line_modify_lock + else + line_modify_lock = ReentrantLock() + end + + return new(repl, hint_generation_lock, line_modify_lock) + end end # Julia PR #54311 (backported to 1.11) added the `hint` argument @@ -144,7 +169,7 @@ else end end -# Julia PR 54800 messed up REPL completion, fix adapted from https://github.com/JuliaLang/IJulia.jl/pull/1147 +# Julia PR 54800 messed up REPL completion, fix adapted from https://github.com/JuliaLang/IJulia.jl/pull/1147 if isdefined(REPLCompletions, :named_completion) # julia#54800 (julia 1.12) completion_text_(c) = REPLCompletions.named_completion(c).completion::String else @@ -152,27 +177,30 @@ else end function LineEdit.complete_line(c::RCompletionProvider, s; hint::Bool=false) - buf = s.input_buffer - partial = String(buf.data[1:buf.ptr-1]) - # complete latex - full = LineEdit.input_string(s) - ret, range, should_complete = bslash_completions(full, lastindex(partial), hint)[2] - if length(ret) > 0 && should_complete - return map(completion_text_, ret), partial[range], should_complete - end + @lock c.hint_generation_lock begin + buf = s.input_buffer + partial = String(buf.data[1:buf.ptr-1]) + # complete latex + full = LineEdit.input_string(s) + ret, range, should_complete = bslash_completions(full, lastindex(partial), hint)[2] + if length(ret) > 0 && should_complete + return map(completion_text_, ret), partial[range], should_complete + end - # complete r - utils = findNamespace("utils") - rcall_p(utils[".assignLinebuffer"], partial) - rcall_p(utils[".assignEnd"], length(partial)) - token = rcopy(rcall_p(utils[".guessTokenFromLine"])) - rcall_p(utils[".completeToken"]) - ret = rcopy(Array, rcall_p(utils[".retrieveCompletions"])) - if length(ret) > 0 - return ret, token, true - end + # complete r + utils = findNamespace("utils") + rcall_p(utils[".assignLinebuffer"], partial) + rcall_p(utils[".assignEnd"], length(partial)) + token = rcopy(rcall_p(utils[".guessTokenFromLine"])) + rcall_p(utils[".completeToken"]) + ret = rcopy(Array, rcall_p(utils[".retrieveCompletions"])) - return String[], "", false + if length(ret) > 0 + return ret, token, true + end + + return String[], "", false + end end function create_r_repl(repl, main) @@ -231,12 +259,16 @@ end function repl_init(repl) mirepl = isdefined(repl,:mi) ? repl.mi : repl - main_mode = mirepl.interface.modes[1] + if !isdefined(repl, :interface) + mirepl.interface = REPL.setup_interface(repl) + end + interface = mirepl.interface + main_mode = interface.modes[1] r_mode = create_r_repl(mirepl, main_mode) push!(mirepl.interface.modes,r_mode) r_prompt_keymap = Dict{Any,Any}( - '$' => function (s,args...) + '$' => function (s, args...) if isempty(s) || position(LineEdit.buffer(s)) == 0 buf = copy(LineEdit.buffer(s)) LineEdit.transition(s, r_mode) do @@ -249,12 +281,14 @@ function repl_init(repl) ) main_mode.keymap_dict = LineEdit.keymap_merge(main_mode.keymap_dict, r_prompt_keymap); - nothing + return nothing end function repl_inited(repl) mirepl = isdefined(repl,:mi) ? repl.mi : repl - any(:prompt in fieldnames(typeof(m)) && m.prompt == "R> " for m in mirepl.interface.modes) + interface = mirepl.interface + + return any(:prompt in fieldnames(typeof(m)) && m.prompt == "R> " for m in interface.modes) end end # module From daf8d1f28a5b338974cbc2777d9c8528fc9ab156 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Tue, 22 Apr 2025 15:25:10 -0500 Subject: [PATCH 08/16] woops --- src/RPrompt.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RPrompt.jl b/src/RPrompt.jl index 21f37df6..7e612b87 100644 --- a/src/RPrompt.jl +++ b/src/RPrompt.jl @@ -75,9 +75,9 @@ function repl_eval(script::String, stdout::IO, stderr::IO) end @static if isdefined(LineEdit, :check_show_hints) - refresh_line_(s) = LineEdit.refresh_line(s) -else refresh_line_(s) = LineEdit.check_show_hint(s) +else + refresh_line_(s) = LineEdit.refresh_line(s) end function bracketed_paste_callback(s, o...) From a80fd3784036ed8323a2b066abf4fba753c8ee6a Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Tue, 22 Apr 2025 16:06:38 -0500 Subject: [PATCH 09/16] remove stale compat shim --- src/RPrompt.jl | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/RPrompt.jl b/src/RPrompt.jl index 7e612b87..c15f0df3 100644 --- a/src/RPrompt.jl +++ b/src/RPrompt.jl @@ -258,14 +258,13 @@ function create_r_repl(repl, main) end function repl_init(repl) - mirepl = isdefined(repl,:mi) ? repl.mi : repl if !isdefined(repl, :interface) - mirepl.interface = REPL.setup_interface(repl) + repl.interface = REPL.setup_interface(repl) end - interface = mirepl.interface + interface = repl.interface main_mode = interface.modes[1] - r_mode = create_r_repl(mirepl, main_mode) - push!(mirepl.interface.modes,r_mode) + r_mode = create_r_repl(repl, main_mode) + push!(repl.interface.modes,r_mode) r_prompt_keymap = Dict{Any,Any}( '$' => function (s, args...) @@ -285,8 +284,7 @@ function repl_init(repl) end function repl_inited(repl) - mirepl = isdefined(repl,:mi) ? repl.mi : repl - interface = mirepl.interface + interface = repl.interface return any(:prompt in fieldnames(typeof(m)) && m.prompt == "R> " for m in interface.modes) end From 6c0eaa5dae538667f3f361cd79aec053f9f1b209 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Tue, 22 Apr 2025 16:11:47 -0500 Subject: [PATCH 10/16] ensure that mistate is init'd --- src/RPrompt.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/RPrompt.jl b/src/RPrompt.jl index c15f0df3..e89313b6 100644 --- a/src/RPrompt.jl +++ b/src/RPrompt.jl @@ -262,6 +262,7 @@ function repl_init(repl) repl.interface = REPL.setup_interface(repl) end interface = repl.interface + repl.mistate = @something(repl.mistate, LineEdit.init_state(REPL.terminal(repl), interface)) main_mode = interface.modes[1] r_mode = create_r_repl(repl, main_mode) push!(repl.interface.modes,r_mode) From 5e95d4caf1b90ce318db6a2c1060e6ed370d8c07 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Tue, 22 Apr 2025 16:22:06 -0500 Subject: [PATCH 11/16] shift logic location, make immutable --- src/RPrompt.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/RPrompt.jl b/src/RPrompt.jl index e89313b6..ed2cbdb5 100644 --- a/src/RPrompt.jl +++ b/src/RPrompt.jl @@ -139,12 +139,12 @@ function bracketed_paste_callback(s, o...) refresh_line_(s) end -mutable struct RCompletionProvider <: LineEdit.CompletionProvider +struct RCompletionProvider <: LineEdit.CompletionProvider repl::REPL.LineEditREPL line_modify_lock::ReentrantLock hint_generation_lock::ReentrantLock function RCompletionProvider(repl::REPL.LineEditREPL) - mistate = repl.mistate + repl.mistate = @something(repl.mistate, LineEdit.init_state(REPL.terminal(repl), interface)) @static if hasfield(LineEdit.MIState, :hint_generation_lock) hint_generation_lock = repl.mistate.hint_generation_lock else @@ -262,7 +262,6 @@ function repl_init(repl) repl.interface = REPL.setup_interface(repl) end interface = repl.interface - repl.mistate = @something(repl.mistate, LineEdit.init_state(REPL.terminal(repl), interface)) main_mode = interface.modes[1] r_mode = create_r_repl(repl, main_mode) push!(repl.interface.modes,r_mode) From c57bfac9243f0047bcdef74ad8ac767feecc7d3e Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Tue, 22 Apr 2025 16:51:01 -0500 Subject: [PATCH 12/16] d'oh --- src/RPrompt.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RPrompt.jl b/src/RPrompt.jl index ed2cbdb5..37fd871f 100644 --- a/src/RPrompt.jl +++ b/src/RPrompt.jl @@ -144,7 +144,7 @@ struct RCompletionProvider <: LineEdit.CompletionProvider line_modify_lock::ReentrantLock hint_generation_lock::ReentrantLock function RCompletionProvider(repl::REPL.LineEditREPL) - repl.mistate = @something(repl.mistate, LineEdit.init_state(REPL.terminal(repl), interface)) + repl.mistate = @something(repl.mistate, LineEdit.init_state(REPL.terminal(repl), repl.interface)) @static if hasfield(LineEdit.MIState, :hint_generation_lock) hint_generation_lock = repl.mistate.hint_generation_lock else From 81713c375d1ffcecd77ed6fbee3947f7b52d324e Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Tue, 22 Apr 2025 16:54:29 -0500 Subject: [PATCH 13/16] reduce timeout --- test/repl.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/repl.jl b/test/repl.jl index 6493c267..22d1f238 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -37,7 +37,7 @@ function read_repl(io::IO, x) cache = Ref{Any}("") read_task = @task cache[] = readuntil(io, x) t = Base.Timer((_) -> Base.throwto(read_task, - ErrorException("Expect \"$x\", but wait too long.")), 5) + ErrorException("Expect \"$x\", but wait too long.")), 2) schedule(read_task) fetch(read_task) close(t) From 163850247aaee6e317c8144970d35d50e472d071 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Tue, 22 Apr 2025 16:54:42 -0500 Subject: [PATCH 14/16] tinker --- test/repl.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/repl.jl b/test/repl.jl index 22d1f238..6dc39a8e 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -27,9 +27,7 @@ Base.link_pipe!(err, reader_supports_async=true, writer_supports_async=true) repl = REPL.LineEditREPL(FakeTerminal(input.out, output.in, err.in), true) -repltask = @async begin - REPL.run_repl(repl) -end +@async REPL.run_repl(repl; backend_on_current_task=false) send_repl(x, enter=true) = write(input, enter ? "$x\n" : x) From a09b32d8bab1e30a950e5cc10303c13082cc9adf Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Tue, 22 Apr 2025 17:02:47 -0500 Subject: [PATCH 15/16] local crash reproducibility --- test/repl.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/repl.jl b/test/repl.jl index 6dc39a8e..3338b748 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -27,7 +27,9 @@ Base.link_pipe!(err, reader_supports_async=true, writer_supports_async=true) repl = REPL.LineEditREPL(FakeTerminal(input.out, output.in, err.in), true) -@async REPL.run_repl(repl; backend_on_current_task=false) +@info "Starting REPL...." +Threads.@spawn REPL.run_repl(repl) +@info "REPL spawned" send_repl(x, enter=true) = write(input, enter ? "$x\n" : x) From 7e92356ad0eb6700bc359a29d864f9bb85777566 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Thu, 9 Oct 2025 01:20:53 -0500 Subject: [PATCH 16/16] jousting at windmills --- src/RPrompt.jl | 32 ++++++++++++++++++++++------- test/repl.jl | 5 ++++- test/runtests.jl | 53 ++++++++++++++++++++++++------------------------ 3 files changed, 56 insertions(+), 34 deletions(-) diff --git a/src/RPrompt.jl b/src/RPrompt.jl index 37fd871f..aa7f7b82 100644 --- a/src/RPrompt.jl +++ b/src/RPrompt.jl @@ -8,11 +8,17 @@ import ..RCall: libR, rparse_p, reval_p, + reval, findNamespace, + getNamespace, + rcall, + rlang, + rlang_p, rcall_p, rprint, rcopy, render, + sexp, protect, unprotect, prepare_inline_julia_code, @@ -177,9 +183,10 @@ else end function LineEdit.complete_line(c::RCompletionProvider, s; hint::Bool=false) + reval("library(utils)") @lock c.hint_generation_lock begin buf = s.input_buffer - partial = String(buf.data[1:buf.ptr-1]) + partial = String(take!(copy(buf))) # String(buf.data[1:buf.ptr-1]) # complete latex full = LineEdit.input_string(s) ret, range, should_complete = bslash_completions(full, lastindex(partial), hint)[2] @@ -188,12 +195,23 @@ function LineEdit.complete_line(c::RCompletionProvider, s; hint::Bool=false) end # complete r - utils = findNamespace("utils") - rcall_p(utils[".assignLinebuffer"], partial) - rcall_p(utils[".assignEnd"], length(partial)) - token = rcopy(rcall_p(utils[".guessTokenFromLine"])) - rcall_p(utils[".completeToken"]) - ret = rcopy(Array, rcall_p(utils[".retrieveCompletions"])) + # XXX As of Julia 1.12, this happens on a background thread + # and findNamespace + function pointers seems to be unsafe in that context, so we must + # use the slightly slower explicit language + + rcall_p(reval("utils:::.assignLinebuffer"), partial) + rcall_p(reval("utils:::.assignEnd"), length(partial)) + token = rcopy(reval("utils:::.guessTokenFromLine()")) + reval("utils:::.completeToken()") + ret = rcopy(Vector{String}, reval("utils:::.retrieveCompletions()"))::Vector{String} + + # faster way that doesn't seem to play nice with testing on Julia 1.12 + # utils = findNamespace("utils") + # rcall_p(utils[".assignLinebuffer"], partial) + # rcall_p(utils[".assignEnd"], length(partial)) + # token = rcopy(rcall_p(utils[".guessTokenFromLine"])) + # rcall_p(utils[".completeToken"]) + # ret = rcopy(Array, rcall_p(utils[".retrieveCompletions"])) if length(ret) > 0 return ret, token, true diff --git a/test/repl.jl b/test/repl.jl index 3338b748..e6a49224 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -36,9 +36,9 @@ send_repl(x, enter=true) = write(input, enter ? "$x\n" : x) function read_repl(io::IO, x) cache = Ref{Any}("") read_task = @task cache[] = readuntil(io, x) + schedule(read_task) t = Base.Timer((_) -> Base.throwto(read_task, ErrorException("Expect \"$x\", but wait too long.")), 2) - schedule(read_task) fetch(read_task) close(t) cache[] @@ -73,12 +73,15 @@ send_repl("\\alp\t", false) send_repl("\t", false) @test check_repl_stdout("α") +@info "linebreak" send_repl("") @test check_repl_stdout("\n") +@info "foo" send_repl("foo]") @test check_repl_stderr("unexpected") +@info "stop" send_repl("stop('something is wrong')") @test check_repl_stderr("something is wrong") diff --git a/test/runtests.jl b/test/runtests.jl index 715dae84..1463e3d0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,9 +6,9 @@ using TestSetExtensions using DataStructures: OrderedDict using RCall: RClass -@testset ExtendedTestSet "installation" begin - include("installation.jl") -end +# @testset ExtendedTestSet "installation" begin +# include("installation.jl") +# end # before RCall does anything const R_PPSTACKTOP_INITIAL = unsafe_load(cglobal((:R_PPStackTop, RCall.libR), Int)) @@ -52,20 +52,21 @@ println(R"l10n_info()") @test rcopy(Vector{String}, reval(".libPaths()")) == libpaths end - tests = ["basic", - "convert/base", - "convert/missing", - "convert/datetime", - "convert/dataframe", - "convert/categorical", - "convert/formula", - "convert/namedtuple", - "convert/tuple", - # "convert/axisarray", - "macros", - "namespaces", - "repl", - ] + # tests = ["basic", + # "convert/base", + # "convert/missing", + # "convert/datetime", + # "convert/dataframe", + # "convert/categorical", + # "convert/formula", + # "convert/namedtuple", + # "convert/tuple", + # # "convert/axisarray", + # "macros", + # "namespaces", + # "repl", + # ] + tests = ["repl"] for t in tests @eval @testset $t begin @@ -73,16 +74,16 @@ println(R"l10n_info()") end end - if Sys.islinux() - # the IJulia tests depend on the R graphics device being set up correctly, - # which is non trivial on non-linux headless devices (e.g. CI) - # it also uses the assumed path to Jupyter on unix - @testset "IJulia" begin - include("ijulia.jl") - end - end + # if Sys.islinux() + # # the IJulia tests depend on the R graphics device being set up correctly, + # # which is non trivial on non-linux headless devices (e.g. CI) + # # it also uses the assumed path to Jupyter on unix + # @testset "IJulia" begin + # include("ijulia.jl") + # end + # end - @info "" RCall.conda_provided_r + # @info "" RCall.conda_provided_r # make sure we're back where we started @test unsafe_load(cglobal((:R_PPStackTop, RCall.libR), Int)) == R_PPSTACKTOP_INITIAL