/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/cmdlineargs.jl
1 629 строк
69 KB
Sam Schweigel
Split images: write the heap image to the .ji (#61649)
06 авг 2026, 19:15
Не верифицирован
06 авг 2026, 19:15
ad84450
Код
Авторство
О чём код?
# This file is a part of Julia. License is MIT: https://julialang.org/license import Libdl # helper function for passing input to stdin # and returning the stdout result function writereadpipeline(input, exename; stderr=nothing) p = open(pipeline(exename; stderr), "w+") @async begin write(p.in, input) close(p.in) end return (read(p.out, String), success(p)) end # helper function for returning stderr and stdout # from running a command (ignoring failure status) function readchomperrors(exename::Cmd) out = Base.PipeEndpoint() err = Base.PipeEndpoint() p = run(exename, devnull, out, err, wait=false) o = @async(readchomp(out)) e = @async(readchomp(err)) return (success(p), fetch(o), fetch(e)) end # helper function for tests that expect successful command execution # logs detailed error information if the command fails function test_read_success(cmd::Cmd, expected_type::Type=String) success, out, err = readchomperrors(cmd) if !success println("---- Command failed: ") show(cmd) println("stdout:\n", out) println("stderr:\n", err) println("----") end @test success return expected_type == String ? out : parse(expected_type, out) end function format_filename(s) p = ccall(:jl_format_filename, Cstring, (Cstring,), s) r = unsafe_string(p) ccall(:free, Cvoid, (Cstring,), p) return r end # Returns true if the given command errors, but doesn't signal function errors_not_signals(cmd::Cmd) p = run(pipeline(ignorestatus(cmd); stdout=devnull, stderr=devnull)) return errors_not_signals(p) end function errors_not_signals(p::Base.Process) wait(p) return process_exited(p) && !Base.process_signaled(p) && !success(p) end let fn = format_filename("a%d %p %i %L %l %u z") hd = withenv("HOME" => nothing) do # get the homedir, as reported by uv_os_get_passwd, as used by jl_format_filename try homedir() catch ex (ex isa Base.IOError && ex.code == Base.UV_ENOENT) || rethrow(ex) "" end end @test startswith(fn, "a$hd ") @test endswith(fn, " z") @test !occursin('%', fn) @test occursin(" $(getpid()) ", fn) @test occursin(" $(Libc.gethostname()) ", fn) @test format_filename("%a%%b") == "a%b" end if Sys.isunix() @testset "SIGQUIT prints task backtraces" begin script = """ mutable struct RLimit cur::Int64 max::Int64 end const RLIMIT_CORE = 4 # from /usr/include/sys/resource.h ccall(:setrlimit, Cint, (Cint, Ref{RLimit}), RLIMIT_CORE, Ref(RLimit(0, 0))) write(stdout, "r") wait() """ exename = `$(Base.julia_cmd()) --startup-file=no --color=no` errp = PipeBuffer() # disable coredumps for this process p = open(pipeline(`$exename -e $script`, stderr=errp), "r") @test read(p, UInt8) == UInt8('r') # The process might ignore the first SIGQUIT, since it will try to then run cleanup, # which may fail for many reasons. # The process will not ignore the second SIGQUIT, but the kernel might ignore it. # So keep sending SIGQUIT every few seconds until the kernel delivers the second one # and `p` exits. t = Timer(0, interval=10) do t; Base.kill(p, Base.SIGQUIT); end wait(p) close(t) err_s = readchomp(errp) @test Base.process_signaled(p) && p.termsignal == Base.SIGQUIT @test occursin("==== Thread ", err_s) @test occursin("==== Done", err_s) end end @testset "julia_cmd" begin julia_basic = Base.julia_cmd() function get_julia_cmd(arg) io = Base.BufferStream() cmd = `$julia_basic $arg -e 'print(repr(Base.julia_cmd()))'` try run(pipeline(cmd, stdout=io, stderr=io)) catch closewrite(io) @error "cmd failed" cmd read(io, String) rethrow() end closewrite(io) return read(io, String) end opts = Base.JLOptions() for (arg, default) in ( # Use a Cmd to handle space nicely when # interpolating inside another Cmd. (`-C $(unsafe_string(opts.cpu_target))`, false), ("-J$(unsafe_string(opts.image_file))", false), ("--depwarn=yes", false), ("--depwarn=error", false), ("--depwarn=no", true), ("--check-bounds=yes", false), ("--check-bounds=no", false), ("--check-bounds=auto", true), ("--inline=no", false), ("--inline=yes", true), ("-O0", false), ("-O1", false), ("-O2", true), ("-O3", false), ("--min-optlevel=0", true), ("--min-optlevel=1", false), ("--min-optlevel=2", false), ("--min-optlevel=3", false), ("-g0", false), ("-g1", false), ("-g2", false), ("--compile=no", false), ("--compile=all", false), ("--compile=min", false), ("--compile=yes", true), ("--code-coverage=@", false), ("--code-coverage=user", false), ("--code-coverage=all", false), ("--code-coverage=none", true), ("--track-allocation=@", false), ("--track-allocation=user", false), ("--track-allocation=all", false), ("--track-allocation=none", true), ("--color=yes", false), ("--color=no", false), ("--startup-file=no", false), ("--startup-file=yes", true), ("--pkgimages=yes", true), ("--pkgimages=no", false), ) @testset "$arg" begin str = arg isa Cmd ? join(arg.exec, ' ') : arg if default @test !occursin(str, get_julia_cmd(arg)) else @test occursin(str, get_julia_cmd(arg)) end end end # Test empty `cpu_target` gives a helpful error message, issue #52209. io = IOBuffer() p = run(pipeline(`$(Base.julia_cmd(; cpu_target="")) --startup-file=no -e ''`; stderr=io); wait=false) wait(p) @test p.exitcode == 1 @test occursin("empty CPU name", String(take!(io))) # Test --cpu-target=help prints available targets and exits cleanly let v = readchomperrors(`$(Base.julia_cmd(; cpu_target="help"))`) @test v[1] == true # exits with 0 @test occursin("Available CPU targets:", v[2]) @test occursin("Host CPU:", v[2]) end end let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` # tests for handling of ENV errors let io = IOBuffer() v = writereadpipeline( "println(\"REPL: \", @which(less), @isdefined(InteractiveUtils))", setenv(`$exename -i -E '@assert isempty(LOAD_PATH); push!(LOAD_PATH, "@stdlib"); @isdefined InteractiveUtils'`, "JULIA_LOAD_PATH" => "", "JULIA_DEPOT_PATH" => ";:", "HOME" => homedir()); stderr=io) # @which is undefined @test_broken v == ("false\nREPL: InteractiveUtilstrue\n", true) stderr = String(take!(io)) @test_broken isempty(stderr) end let v = writereadpipeline("println(\"REPL: \", InteractiveUtils)", setenv(`$exename -i -e 'const InteractiveUtils = 3'`, "JULIA_LOAD_PATH" => ";;;:::", "JULIA_DEPOT_PATH" => ";;;:::", "HOME" => homedir())) # TODO: ideally, `@which`, etc. would still work, but Julia can't handle `using $InteractiveUtils` @test v == ("REPL: 3\n", true) end @testset let v = readchomperrors(`$exename -i -e ' empty!(LOAD_PATH) @eval Sys STDLIB=mktempdir() Base.unreference_module(Base.PkgId(Base.UUID(0xb77e0a4c_d291_57a0_90e8_8db25a27a240), "InteractiveUtils")) '`) # simulate not having a working version of InteractiveUtils, # make sure this is a non-fatal error and the REPL still loads @test v[1] @test isempty(v[2]) # Can't load REPL if it's outside the sysimg if we break the load path. # Need to rewrite this test nicer # ┌ Warning: REPL provider not available: using basic fallback # └ @ Base client.jl:459 @test_broken startswith(v[3], "┌ Warning: Failed to import InteractiveUtils into module Main\n") end real_threads = string(ccall(:jl_cpu_threads, Int32, ())) for nc in ("0", "-2", "x", "2x", " ", "") v = readchomperrors(setenv(`$exename -i -E 'Sys.CPU_THREADS'`, "JULIA_CPU_THREADS" => nc, "HOME" => homedir())) @test v == (true, real_threads, "WARNING: couldn't parse `JULIA_CPU_THREADS` environment variable. Defaulting Sys.CPU_THREADS to $real_threads.") end for nc in ("1", " 1 ", " +1 ", " 0x1 ") @testset let v = readchomperrors(setenv(`$exename -i -E 'Sys.CPU_THREADS'`, "JULIA_CPU_THREADS" => nc, "HOME" => homedir())) @test v[1] @test v[2] == "1" @test isempty(v[3]) end end @testset let v = readchomperrors(setenv(`$exename -e 0`, "JULIA_LLVM_ARGS" => "-print-options", "HOME" => homedir())) @test v[1] @test contains(v[2], r"print-options + = 1") @test contains(v[2], r"combiner-store-merge-dependence-limit + = 4") @test contains(v[2], r"enable-tail-merge + = 2") @test isempty(v[3]) end @testset let v = readchomperrors(setenv(`$exename -e 0`, "JULIA_LLVM_ARGS" => "-print-options -enable-tail-merge=1 -combiner-store-merge-dependence-limit=6", "HOME" => homedir())) @test v[1] @test contains(v[2], r"print-options + = 1") @test contains(v[2], r"combiner-store-merge-dependence-limit + = 6") @test contains(v[2], r"enable-tail-merge + = 1") @test isempty(v[3]) end if Base.libllvm_version < v"15" #LLVM over 15 doesn't care for multiple options @testset let v = readchomperrors(setenv(`$exename -e 0`, "JULIA_LLVM_ARGS" => "-print-options -enable-tail-merge=1 -enable-tail-merge=1", "HOME" => homedir())) @test !v[1] @test isempty(v[2]) @test v[3] == "julia: for the --enable-tail-merge option: may only occur zero or one times!" end end @testset "time-trace" begin mktempdir() do dir tracefile = joinpath(dir, "test_trace.json") # Use forward slashes on Windows to avoid LLVM command line parser issues with backslashes tracefile_arg = Sys.iswindows() ? replace(tracefile, "\\" => "/") : tracefile v = readchomperrors(setenv(`$exename -e "1+1"`, "JULIA_LLVM_ARGS" => "-time-trace -time-trace-file=$tracefile_arg", "HOME" => homedir())) @test v[1] @test isfile(tracefile) content = read(tracefile, String) @test startswith(content, "{\"traceEvents\":") end end end let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` # --version let v = split(read(`$exename -v`, String), "julia version ")[end] @test Base.VERSION_STRING == chomp(v) end @test read(`$exename -v`, String) == read(`$exename --version`, String) # --help let header = "\n julia [switches] -- [programfile] [args...]" @test startswith(read(`$exename -h`, String), header) @test startswith(read(`$exename --help`, String), header) end # Test to make sure that command line --help and --help-hidden do not return a description which is more than 100 characters wide @test isempty(filter(x->length(x) > 100, readlines(`$exename -h`))) @test isempty(filter(x->length(x) > 100, readlines(`$exename --help-hidden`))) # ~ expansion in --project and JULIA_PROJECT if !Sys.iswindows() let expanded = abspath(expanduser("~/foo/Project.toml")) @test expanded == readchomp(`$exename --project='~/foo' -e 'println(Base.active_project())'`) @test expanded == readchomp(`$exename -P '~/foo' -e 'println(Base.active_project())'`) @test expanded == readchomp(setenv(`$exename -e 'println(Base.active_project())'`, "JULIA_PROJECT" => "~/foo", "HOME" => homedir())) end end # handling of @projectname in --project and JULIA_PROJECT let expanded = abspath(Base.load_path_expand("@foo")) @test expanded == readchomp(`$exename --project='@foo' -e 'println(Base.active_project())'`) @test expanded == readchomp(`$exename -P '@foo' -e 'println(Base.active_project())'`) @test expanded == readchomp(addenv(`$exename -e 'println(Base.active_project())'`, "JULIA_PROJECT" => "@foo", "HOME" => homedir())) end # --project=@script handling let expanded = abspath(joinpath(@__DIR__, "project", "ScriptProject")) script = joinpath(expanded, "bin", "script.jl") # Check running julia with --project=@script both within and outside the script directory @testset "--@script from $name" for (name, dir) in [("project", expanded), ("outside", pwd())] @test joinpath(expanded, "Project.toml") == readchomp(Cmd(`$exename --project=@script $script`; dir)) @test joinpath(expanded, "Project.toml") == readchomp(Cmd(`$exename -P @script $script`; dir)) @test joinpath(expanded, "SubProject", "Project.toml") == readchomp(Cmd(`$exename --project=@script/../SubProject $script`; dir)) end end # handling of `@temp` in --project and JULIA_PROJECT @test tempdir() == readchomp(`$exename --project=@temp -e 'println(Base.active_project())'`)[1:lastindex(tempdir())] @test tempdir() == readchomp(`$exename -P @temp -e 'println(Base.active_project())'`)[1:lastindex(tempdir())] @test tempdir() == readchomp(addenv(`$exename -e 'println(Base.active_project())'`, "JULIA_PROJECT" => "@temp", "HOME" => homedir()))[1:lastindex(tempdir())] # --quiet, --banner let p = "print((Base.JLOptions().quiet, Base.JLOptions().banner))" @test read(`$exename -e $p`, String) == "(0, -1)" @test read(`$exename -q -e $p`, String) == "(1, 0)" @test read(`$exename --quiet -e $p`, String) == "(1, 0)" @test read(`$exename --banner=no -e $p`, String) == "(0, 0)" @test read(`$exename --banner=yes -e $p`, String) == "(0, 1)" @test read(`$exename --banner=short -e $p`, String) == "(0, 2)" @test read(`$exename -q --banner=no -e $p`, String) == "(1, 0)" @test read(`$exename -q --banner=yes -e $p`, String) == "(1, 1)" @test read(`$exename -q --banner=short -e $p`, String) == "(1, 2)" @test read(`$exename --banner=no -q -e $p`, String) == "(1, 0)" @test read(`$exename --banner=yes -q -e $p`, String) == "(1, 1)" @test read(`$exename --banner=short -q -e $p`, String) == "(1, 2)" end # --home @test "" == test_read_success(`$exename -H $(Sys.BINDIR)`) @test "" == test_read_success(`$exename --home=$(Sys.BINDIR)`) # --eval @test "" == test_read_success(`$exename -e "exit(0)"`) @test errors_not_signals(`$exename -e "exit(1)"`) @test "" == test_read_success(`$exename --eval="exit(0)"`) @test errors_not_signals(`$exename --eval="exit(1)"`) @test errors_not_signals(`$exename -e`) @test errors_not_signals(`$exename --eval`) # --eval --interactive (replaced --post-boot) @test "" == test_read_success(`$exename -i -e "exit(0)"`) @test errors_not_signals(`$exename -i -e "exit(1)"`) # issue #34924 @test "" == test_read_success(`$exename -e 'const LOAD_PATH=1'`) # --print @test read(`$exename -E "1+1"`, String) == "2\n" @test read(`$exename --print="1+1"`, String) == "2\n" @test errors_not_signals(`$exename -E`) @test errors_not_signals(`$exename --print`) # --load let testfile = tempname() try write(testfile, "testvar = :test\nprintln(\"loaded\")\n") @test read(`$exename -i --load=$testfile -e "println(testvar)"`, String) == "loaded\ntest\n" @test read(`$exename -i -L $testfile -e "println(testvar)"`, String) == "loaded\ntest\n" # multiple, combined @test read(```$exename -e 'push!(ARGS, "hi")' -E "1+1" -E "2+2" -L $testfile -E '3+3' -L $testfile -E 'pop!(ARGS)' -e 'show(ARGS); println()' 9 10 ```, String) == """ 2 4 loaded 6 loaded "hi" ["9", "10"] """ finally rm(testfile) end end # -L, --load requires an argument @test errors_not_signals(`$exename -L`) @test errors_not_signals(`$exename --load`) # --cpu-target (requires LLVM enabled) # Strictly test for failed error, not a segfault, since we had a false positive with just `success()` before. @test errors_not_signals(`$exename -C invalidtarget`) @test errors_not_signals(`$exename --cpu-target=invalidtarget`) # -t, --threads code = "print(Threads.threadpoolsize())" code2 = "print(Threads.maxthreadid())" cpu_threads = ccall(:jl_effective_threads, Int32, ()) @test string(cpu_threads) == read(`$exename --threads auto -e $code`, String) == read(`$exename --threads=auto -e $code`, String) == read(`$exename -tauto -e $code`, String) == read(`$exename -t auto -e $code`, String) for nt in (nothing, "1") withenv("JULIA_NUM_THREADS" => nt) do @test read(`$exename --threads=2 -e $code`, String) == read(`$exename -t 2 -e $code`, String) == "2" if nt === nothing @test read(`$exename -e $code2`, String) == "2" #default + interactive elseif nt == "1" @test read(`$exename -e $code2`, String) == "1" #if user asks for 1 give 1 end end end # We want to test oversubscription, but on manycore machines, this can # actually exhaust limited PID spaces cpu_threads = max(2*cpu_threads, min(50, 10*cpu_threads)) if Sys.WORD_SIZE == 32 cpu_threads = min(cpu_threads, 50) end @test read(`$exename -t $cpu_threads -e $code`, String) == string(cpu_threads) withenv("JULIA_NUM_THREADS" => string(cpu_threads)) do @test read(`$exename -e $code`, String) == string(cpu_threads) end @test errors_not_signals(`$exename -t 0`) @test errors_not_signals(`$exename -t -1`) # Combining --threads and --procs: --threads does propagate withenv("JULIA_NUM_THREADS" => nothing) do code = "print(sum(remotecall_fetch(Threads.threadpoolsize, x) for x in procs()))" @test read(`$exename -p2 -t2 -e $code`, String) == "6" end # Combining --threads and invalid -C should yield a decent error @test errors_not_signals(`$exename -t 2 -C invalidtarget`) # --procs @test readchomp(`$exename -q -p 2 -e "println(nworkers())"`) == "2" @test errors_not_signals(`$exename -p 0`) let p = run(`$exename --procs=1.0`, wait=false) wait(p) @test p.exitcode == 1 && p.termsignal == 0 end # FIXME: Issue #57103 --gcthreads does not have the same semantics # for Stock GC and MMTk, so the tests below are specific to the Stock GC @static if Base.USING_STOCK_GC # --gcthreads code = "print(Threads.ngcthreads())" cpu_threads = ccall(:jl_effective_threads, Int32, ()) @test string(cpu_threads) == read(`$exename --threads auto -e $code`, String) == read(`$exename --threads=auto -e $code`, String) == read(`$exename -tauto -e $code`, String) == read(`$exename -t auto -e $code`, String) for nt in (nothing, "1") withenv("JULIA_NUM_GC_THREADS" => nt) do @test read(`$exename --gcthreads=2 -e $code`, String) == "2" end withenv("JULIA_NUM_GC_THREADS" => nt) do @test read(`$exename --gcthreads=2,1 -e $code`, String) == "3" end end withenv("JULIA_NUM_GC_THREADS" => 2) do @test read(`$exename -e $code`, String) == "2" end withenv("JULIA_NUM_GC_THREADS" => "2,1") do @test read(`$exename -e $code`, String) == "3" end # invalid JULIA_NUM_GC_THREADS values must be rejected at startup # like `--gcthreads`, not crash the GC later (e.g. `=0` used to # underflow the mark-thread count and segfault at the first collection) for ngc in ("0", "-1", "abc", "32767", "2,2", "2,-1", "2,1x") withenv("JULIA_NUM_GC_THREADS" => ngc) do @test errors_not_signals(`$exename -e $code`) end end end # --machine-file # this does not check that machine file works, # only that the filename gets correctly passed to the option struct let fname = tempname() touch(fname) fname = realpath(fname) try @test readchomp(`$exename --machine-file $fname -e "println(unsafe_string(Base.JLOptions().machine_file))"`) == fname finally rm(fname) end end # -i, isinteractive @test readchomp(`$exename -E "isinteractive()"`) == "false" @test readchomp(`$exename -E "isinteractive()" -i`) == "true" # --color function color_cmd(; flag, no_color=nothing, force_color=nothing) cmd = `$exename --color=$flag -E "Base.have_color"` return addenv(cmd, "NO_COLOR" => no_color, "FORCE_COLOR" => force_color) end @test readchomp(color_cmd(flag="auto")) == "nothing" @test readchomp(color_cmd(flag="no")) == "false" @test readchomp(color_cmd(flag="yes")) == "true" @test errors_not_signals(color_cmd(flag="false")) @test errors_not_signals(color_cmd(flag="true")) @test readchomp(color_cmd(flag="auto", no_color="")) == "nothing" @test readchomp(color_cmd(flag="auto", no_color="1")) == "false" @test readchomp(color_cmd(flag="no", no_color="1")) == "false" @test readchomp(color_cmd(flag="yes", no_color="1")) == "true" @test readchomp(color_cmd(flag="auto", force_color="")) == "nothing" @test readchomp(color_cmd(flag="auto", force_color="1")) == "true" @test readchomp(color_cmd(flag="no", force_color="1")) == "false" @test readchomp(color_cmd(flag="yes", force_color="1")) == "true" @test readchomp(color_cmd(flag="auto", no_color="1", force_color="1")) == "true" @test readchomp(color_cmd(flag="no", no_color="1", force_color="1")) == "false" @test readchomp(color_cmd(flag="yes", no_color="1", force_color="1")) == "true" # --history-file @test readchomp(`$exename -E "Bool(Base.JLOptions().historyfile)" --history-file=yes`) == "true" @test readchomp(`$exename -E "Bool(Base.JLOptions().historyfile)" --history-file=no`) == "false" @test errors_not_signals(`$exename --history-file=false`) # --code-coverage mktempdir() do dir # don't inherit global coverage settings that could confuse these tests cov_exename = `$(Base.julia_cmd()[1]) --startup-file=no --color=no` helperdir = joinpath(@__DIR__, "testhelpers") inputfile = joinpath(helperdir, "coverage_file.jl") expected = replace(read(joinpath(helperdir, "coverage_file.info"), String), "<FILENAME>" => realpath(inputfile)) covfile = replace(joinpath(dir, "coverage.info"), "%" => "%%") @test !isfile(covfile) defaultcov = readchomp(`$cov_exename -E "Base.JLOptions().code_coverage != 0" -L $inputfile`) opts = Base.JLOptions() coverage_file = (opts.output_code_coverage != C_NULL) ? unsafe_string(opts.output_code_coverage) : "" @test !isfile(covfile) @test defaultcov == string(opts.code_coverage != 0 && (isempty(coverage_file) || occursin("%p", coverage_file))) @test readchomp(`$cov_exename -E "Base.JLOptions().code_coverage" -L $inputfile --code-coverage=$covfile --code-coverage=none`) == "0" @test !isfile(covfile) @test readchomp(`$cov_exename -E "Base.JLOptions().code_coverage" -L $inputfile --code-coverage=$covfile --code-coverage`) == "1" @test isfile(covfile) got = read(covfile, String) rm(covfile) @test occursin(expected, got) context=(expected, got) @test readchomp(`$cov_exename -E "Base.JLOptions().code_coverage" -L $inputfile --code-coverage=$covfile --code-coverage=user`) == "1" @test isfile(covfile) got = read(covfile, String) rm(covfile) @test occursin(expected, got) context=(expected, got) @test readchomp(`$cov_exename -E "Base.JLOptions().code_coverage" -L $inputfile --code-coverage=$covfile --code-coverage=all`) == "2" @test isfile(covfile) got = read(covfile, String) rm(covfile) @test occursin(expected, got) context=(expected, got) # Ask for coverage in specific file tfile = realpath(inputfile) @test readchomp(`$cov_exename -E "(Base.JLOptions().code_coverage, unsafe_string(Base.JLOptions().tracked_path))" -L $inputfile --code-coverage=$covfile --code-coverage=@$tfile`) == "(3, $(repr(tfile)))" @test isfile(covfile) got = read(covfile, String) rm(covfile) @test occursin(expected, got) context=(expected, got) # Ask for coverage in directory tdir = dirname(realpath(inputfile)) @test readchomp(`$cov_exename -E "(Base.JLOptions().code_coverage, unsafe_string(Base.JLOptions().tracked_path))" -L $inputfile --code-coverage=$covfile --code-coverage=@$tdir`) == "(3, $(repr(tdir)))" @test isfile(covfile) got = read(covfile, String) rm(covfile) @test occursin(expected, got) context=(expected, got) # Ask for coverage in current directory tdir = dirname(realpath(inputfile)) cd(tdir) do # there may be a trailing separator here so use rstrip @test readchomp(`$cov_exename -E "(Base.JLOptions().code_coverage, rstrip(unsafe_string(Base.JLOptions().tracked_path), Base.Filesystem.path_separator[1]))" -L $inputfile --code-coverage=$covfile --code-coverage=@`) == "(3, $(repr(tdir)))" end @test isfile(covfile) got = read(covfile, String) rm(covfile) @test occursin(expected, got) context=(expected, got) # Ask for coverage in relative directory tdir = dirname(realpath(inputfile)) cd(dirname(tdir)) do @test readchomp(`$cov_exename -E "(Base.JLOptions().code_coverage, unsafe_string(Base.JLOptions().tracked_path))" -L $inputfile --code-coverage=$covfile --code-coverage=@testhelpers`) == "(3, $(repr(tdir)))" end @test isfile(covfile) got = read(covfile, String) rm(covfile) @test occursin(expected, got) context=(expected, got) # Ask for coverage in relative directory with dot-dot notation tdir = dirname(realpath(inputfile)) cd(tdir) do @test readchomp(`$cov_exename -E "(Base.JLOptions().code_coverage, unsafe_string(Base.JLOptions().tracked_path))" -L $inputfile --code-coverage=$covfile --code-coverage=@../testhelpers`) == "(3, $(repr(tdir)))" end @test isfile(covfile) got = read(covfile, String) rm(covfile) @test occursin(expected, got) context=(expected, got) # Ask for coverage in a different directory tdir = mktempdir() # a dir that contains no code @test readchomp(`$cov_exename -E "(Base.JLOptions().code_coverage, unsafe_string(Base.JLOptions().tracked_path))" -L $inputfile --code-coverage=$covfile --code-coverage=@$tdir`) == "(3, $(repr(realpath(tdir))))" @test isfile(covfile) got = read(covfile, String) @test isempty(got) rm(covfile) function coverage_info_for(src::String) mktemp(dir) do srcfile, io write(io, src); close(io) outfile = tempname(dir, cleanup=false)*".info" run(`$cov_exename --code-coverage=$outfile $srcfile`) result = read(outfile, String) rm(outfile, force=true) result end end @test contains(coverage_info_for(""" function cov_bug(x, p) if p > 2 print("") # runs end if Base.compilerbarrier(:const, false) println("Does not run") end end function do_test() cov_bug(5, 3) end do_test() """), """ DA:2,1 DA:3,1 DA:5,1 DA:6,0 DA:9,1 DA:10,1 LH:5 LF:6 """) @test contains(coverage_info_for(""" function cov_bug() if Base.compilerbarrier(:const, true) if Base.compilerbarrier(:const, true) if Base.compilerbarrier(:const, false) println("Does not run") end else print("Does not run either") end else print("") end return nothing end cov_bug() """), """ DA:1,1 DA:2,1 DA:3,1 DA:4,1 DA:5,0 DA:8,0 DA:11,0 DA:13,1 LH:5 LF:8 """) end # --track-allocation alloc_exename = `$(Base.julia_cmd()) --startup-file=no --color=no --track-allocation=none` @test readchomp(`$alloc_exename -E "Base.JLOptions().malloc_log != 0"`) == "false" @test readchomp(`$alloc_exename -E "Base.JLOptions().malloc_log != 0" --track-allocation=none`) == "false" @test readchomp(`$alloc_exename -E "Base.JLOptions().malloc_log != 0" --track-allocation`) == "true" @test readchomp(`$alloc_exename -E "Base.JLOptions().malloc_log != 0" --track-allocation=user`) == "true" mktempdir() do dir helperdir = joinpath(@__DIR__, "testhelpers") inputfile = joinpath(dir, "allocation_file.jl") cp(joinpath(helperdir,"allocation_file.jl"), inputfile) pid = readchomp(`$alloc_exename -E "getpid()" -L $inputfile --track-allocation=user`) memfile = "$inputfile.$pid.mem" got = readlines(memfile) rm(memfile) @test popfirst!(got) == " 0 g(x) = x + 123456" @test popfirst!(got) == " - function f(x)" @test popfirst!(got) == " - []" if Sys.WORD_SIZE == 64 # P64 pools with 64 bit tags @test popfirst!(got) == " 16 Base.invokelatest(g, 0)" @test popfirst!(got) == " 32 Base.invokelatest(g, x)" elseif 12 == (() -> @allocated ccall(:jl_gc_allocobj, Ptr{Cvoid}, (Csize_t,), 8))() # See if we have a 12-byte pool with 32 bit tags (MAX_ALIGN = 4) @test popfirst!(got) == " 12 Base.invokelatest(g, 0)" @test popfirst!(got) == " 24 Base.invokelatest(g, x)" else # MAX_ALIGN >= 8 @test popfirst!(got) == " 8 Base.invokelatest(g, 0)" @test popfirst!(got) == " 32 Base.invokelatest(g, x)" end if Sys.WORD_SIZE == 64 @test popfirst!(got) == " 32 []" else @test popfirst!(got) == " 16 []" end @test popfirst!(got) == " - end" @test popfirst!(got) == " - f(1.23)" @test isempty(got) context=got end # --optimize @test readchomp(`$exename -E "Base.JLOptions().opt_level"`) == "2" @test readchomp(`$exename -E "Base.JLOptions().opt_level" -O`) == "3" @test readchomp(`$exename -E "Base.JLOptions().opt_level" --optimize`) == "3" @test readchomp(`$exename -E "Base.JLOptions().opt_level" -O0`) == "0" @test readchomp(`$exename -E "Base.JLOptions().opt_level_min"`) == "0" @test readchomp(`$exename -E "Base.JLOptions().opt_level_min" --min-optlevel=2`) == "2" # -g @test readchomp(`$exename -E "Base.JLOptions().debug_level" -g`) == "2" # --print-before/--print-after with pass names is broken on Windows due to no-gnu-unique issues if !Sys.iswindows() withenv("JULIA_LLVM_ARGS" => "--print-before=BeforeOptimization", "JULIA_OBJCACHE" => "0") do let code = readchomperrors(`$exename -g0 -E "@eval Int64(1)+Int64(1)"`) @test code[1] code = code[3] @test occursin("llvm.module.flags", code) @test !occursin("llvm.dbg.cu", code) @test !occursin("int.jl", code) @test !occursin("name: \"Int64\"", code) end let code = readchomperrors(`$exename -g1 -E "@eval Int64(1)+Int64(1)"`) @test code[1] code = code[3] @test occursin("llvm.module.flags", code) @test occursin("llvm.dbg.cu", code) # TODO: consider moving test to llvmpasses as this fails on some platforms # without clear reason @test_skip occursin("int.jl", code) @test !occursin("name: \"Int64\"", code) end let code = readchomperrors(`$exename -g2 -E "@eval Int64(1)+Int64(1)"`) @test code[1] code = code[3] @test occursin("llvm.module.flags", code) @test occursin("llvm.dbg.cu", code) # TODO: consider moving test to llvmpasses as this fails on some platforms # without clear reason @test_skip occursin("int.jl", code) @test occursin("name: \"Int64\"", code) end end end # --check-bounds let JL_OPTIONS_CHECK_BOUNDS_DEFAULT = 0, JL_OPTIONS_CHECK_BOUNDS_ON = 1, JL_OPTIONS_CHECK_BOUNDS_OFF = 2 exename_default_checkbounds = `$exename` filter!(a -> !startswith(a, "--check-bounds="), exename_default_checkbounds.exec) @test parse(Int, readchomp(`$exename_default_checkbounds -E "Int(Base.JLOptions().check_bounds)"`)) == JL_OPTIONS_CHECK_BOUNDS_DEFAULT @test parse(Int, readchomp(`$exename -E "Int(Base.JLOptions().check_bounds)" --check-bounds=auto`)) == JL_OPTIONS_CHECK_BOUNDS_DEFAULT @test parse(Int, readchomp(`$exename -E "Int(Base.JLOptions().check_bounds)" --check-bounds=yes`)) == JL_OPTIONS_CHECK_BOUNDS_ON @test parse(Int, readchomp(`$exename -E "Int(Base.JLOptions().check_bounds)" --check-bounds=no`)) == JL_OPTIONS_CHECK_BOUNDS_OFF end # check-bounds takes yes/no as argument @test errors_not_signals(`$exename -E "exit(0)" --check-bounds=false`) # --depwarn @test readchomp(`$exename --depwarn=no -E "Base.JLOptions().depwarn"`) == "0" @test readchomp(`$exename --depwarn=yes -E "Base.JLOptions().depwarn"`) == "1" @test errors_not_signals(`$exename --depwarn=false`) # test deprecated syntax @test errors_not_signals(`$exename -e "foo (x::Int) = x * x" --depwarn=error`) # test deprecated method @test errors_not_signals(`$exename -e " foo() = :foo; bar() = :bar @deprecate foo() bar() foo() " --depwarn=error`) # test deprecated bindings, #13269 let code = """ module Foo import Base: @deprecate_binding const NotDeprecated = true @deprecate_binding Deprecated NotDeprecated end Foo.Deprecated """ @test errors_not_signals(`$exename -E "$code" --depwarn=error`) @test readchomperrors(`$exename -E "$code" --depwarn=yes`) == (true, "true", "WARNING: Use of Foo.Deprecated is deprecated, use NotDeprecated instead.\n likely near none:8") @test readchomperrors(`$exename -E "$code" --depwarn=no`) == (true, "true", "") end # --inline @test readchomp(`$exename -E "Bool(Base.JLOptions().can_inline)"`) == "true" @test readchomp(`$exename --inline=yes -E "Bool(Base.JLOptions().can_inline)"`) == "true" @test readchomp(`$exename --inline=no -E "Bool(Base.JLOptions().can_inline)"`) == "false" # --inline takes yes/no as argument @test errors_not_signals(`$exename --inline=false`) # --polly @test readchomp(`$exename -E "Bool(Base.JLOptions().polly)"`) == "true" @test readchomp(`$exename --polly=yes -E "Bool(Base.JLOptions().polly)"`) == "true" @test readchomp(`$exename --polly=no -E "Bool(Base.JLOptions().polly)"`) == "false" # --polly takes yes/no as argument @test errors_not_signals(`$exename --polly=false`) # --fast-math let JL_OPTIONS_FAST_MATH_DEFAULT = 0, JL_OPTIONS_FAST_MATH_ON = 1, JL_OPTIONS_FAST_MATH_OFF = 2 @test parse(Int,readchomp(`$exename -E "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_DEFAULT @test parse(Int,readchomp(`$exename --math-mode=user -E "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_DEFAULT @test parse(Int,readchomp(`$exename --math-mode=ieee -E "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_OFF @test parse(Int,readchomp(`$exename --math-mode=fast -E "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_DEFAULT end let JL_OPTIONS_TASK_METRICS_OFF = 0, JL_OPTIONS_TASK_METRICS_ON = 1 @test parse(Int,readchomp(`$exename -E "Int(Base.JLOptions().task_metrics)"`)) == JL_OPTIONS_TASK_METRICS_OFF @test parse(Int, readchomp(`$exename --task-metrics=yes -E "Int(Base.JLOptions().task_metrics)"`)) == JL_OPTIONS_TASK_METRICS_ON @test !parse(Bool, readchomp(`$exename -E "current_task().metrics_enabled"`)) @test parse(Bool, readchomp(`$exename --task-metrics=yes -E "current_task().metrics_enabled"`)) end # --worker takes default / custom as argument (default/custom arguments # tested in test/parallel.jl) # shorten the worker timeout as this test relies on it timing out withenv("JULIA_WORKER_TIMEOUT" => "10") do @test errors_not_signals(`$exename --worker=true`) end # --trace-compile let io = IOBuffer() v = writereadpipeline( "foo(x) = begin Base.Experimental.@force_compile; x; end; foo(1)", `$exename --trace-compile=stderr -i`, stderr=io) _stderr = String(take!(io)) @test occursin("precompile(Tuple{typeof(Main.foo), Int", _stderr) end # --trace-compile-timing let io = IOBuffer() v = writereadpipeline( "foo(x) = begin Base.Experimental.@force_compile; x; end; foo(1)", `$exename --trace-compile=stderr --trace-compile-timing -i`, stderr=io) _stderr = String(take!(io)) @test occursin(" ms =# precompile(Tuple{typeof(Main.foo), Int", _stderr) end # Base.@trace_compile (local version of the 2 above args) let io = IOBuffer() v = writereadpipeline( """ f(x::Int) = 1 applyf(container) = f(container[1]) Base.@trace_compile @eval applyf([100]) Base.@trace_compile @eval applyf(Any[100]) f(::Bool) = 2 Base.@trace_compile @eval applyf([true]) Base.@trace_compile @eval applyf(Any[true]) """, `$exename -i`, stderr=io) _stderr = String(take!(io)) @test length(findall(r"precompile\(", _stderr)) == 5 @test length(findall(r" # recompile", _stderr)) == 1 end # --trace-dispatch let io = IOBuffer() v = writereadpipeline( "foo(x) = begin Base.Experimental.@force_compile; x; end; foo(1)", `$exename --trace-dispatch=stderr -i`, stderr=io) _stderr = String(take!(io)) @test occursin("precompile(Tuple{typeof(Main.foo), Int", _stderr) end # --trace-eval let # Test --trace-eval=loc (location only) mktempdir() do dir testfile = joinpath(dir, "test.jl") write(testfile, "x = 1 + 1\ny = x * 2") success, out, err = readchomperrors(`$exename --trace-eval=loc $testfile`) @test success @test occursin("eval: #=", err) @test !occursin("eval: \$(Expr(:toplevel", err) # Should not show full expressions end end let # Test --trace-eval=full (full expressions) mktempdir() do dir testfile = joinpath(dir, "test.jl") write(testfile, "x = 1 + 1\ny = x * 2") success, out, err = readchomperrors(`$exename --trace-eval=full $testfile`) @test success @test occursin("eval: \$(Expr(:toplevel", err) # Should show full expressions @test occursin("x = 1 + 1", err) end end let # Test --trace-eval=no (disabled) mktempdir() do dir testfile = joinpath(dir, "test.jl") write(testfile, "x = 1 + 1\ny = x * 2") success, out, err = readchomperrors(`$exename --trace-eval=no $testfile`) @test success @test !occursin("eval:", err) # Should not show any eval traces end end let # Test Base.TRACE_EVAL global control takes priority mktempdir() do dir testfile = joinpath(dir, "test.jl") write(testfile, """ Base.TRACE_EVAL = :full x = 1 + 1 Base.TRACE_EVAL = :no y = x * 2 """) success, out, err = readchomperrors(`$exename --trace-eval=loc $testfile`) # Command line says :loc, but code overrides @test success # Should show full expression for x = 1 + 1 (Base.TRACE_EVAL = :full) @test occursin("eval: \$(Expr(:toplevel", err) @test occursin("x = 1 + 1", err) # Should not show trace for y = x * 2 (Base.TRACE_EVAL = :no) lines = split(err, '\n') y_lines = filter(line -> occursin("y = x * 2", line), lines) @test length(y_lines) == 0 # No eval trace for y assignment end end # test passing arguments mktempdir() do dir testfile, io = mktemp(dir) # write a julia source file that just prints ARGS to stdout write(io, """ println(ARGS) """) close(io) mkpath(joinpath(dir, "config")) cp(testfile, joinpath(dir, "config", "startup.jl")) withenv("JULIA_DEPOT_PATH" => dir) do output = "[\"foo\", \"-bar\", \"--baz\"]" @test readchomp(`$exename $testfile foo -bar --baz`) == output @test readchomp(`$exename -- $testfile foo -bar --baz`) == output @test readchomp(`$exename -L $testfile -e 'exit(0)' -- foo -bar --baz`) == output @test readchomp(`$exename --startup-file=yes -e 'exit(0)' -- foo -bar --baz`) == output output = "[\"foo\", \"--\", \"-bar\", \"--baz\"]" @test readchomp(`$exename $testfile foo -- -bar --baz`) == output @test readchomp(`$exename -- $testfile foo -- -bar --baz`) == output @test readchomp(`$exename -L $testfile -e 'exit(0)' foo -- -bar --baz`) == output @test readchomp(`$exename -L $testfile -e 'exit(0)' -- foo -- -bar --baz`) == output @test readchomp(`$exename --startup-file=yes -e 'exit(0)' foo -- -bar --baz`) == output output = "String[]\nString[]" @test readchomp(`$exename -L $testfile $testfile`) == output @test readchomp(`$exename --startup-file=yes $testfile`) == output @test errors_not_signals(`$exename --foo $testfile`) end end # test the program name remains constant mktempdir() do dir # dir can be case-incorrect sometimes dir = realpath(dir) a = joinpath(dir, "a.jl") b = joinpath(dir, "b.jl") c = joinpath(dir, "config", "startup.jl") write(a, """ println(@__FILE__) println(PROGRAM_FILE) include(\"$(escape_string(b))\") """) write(b, """ println(@__FILE__) println(PROGRAM_FILE) """) mkpath(dirname(c)) cp(b, c) readsplit(cmd) = split(readchomp(cmd), '\n') withenv("JULIA_DEPOT_PATH" => dir) do @test readsplit(`$exename $a`) == [a, a, b, a] @test readsplit(`$exename -L $b -e 'exit(0)'`) == [b, ""] @test readsplit(`$exename -L $b $a`) == [b, a, a, a, b, a] @test readsplit(`$exename --startup-file=yes -e 'exit(0)'`) == [c, ""] @test readsplit(`$exename --startup-file=yes -L $b -e 'exit(0)'`) == [c, "", b, ""] @test readsplit(`$exename --startup-file=yes -L $b $a`) == [c, a, b, a, a, a, b, a] end end # issue #10562 @test readchomp(`$exename -e 'println(ARGS);' ''`) == "[\"\"]" # issue #12679 @test readchomperrors(`$exename --startup-file=no --compile=yes -ioo`) == (false, "", "ERROR: unknown option `-o`") @test readchomperrors(`$exename --startup-file=no -p`) == (false, "", "ERROR: option `-p/--procs` is missing an argument") @test readchomperrors(`$exename --startup-file=no --inline`) == (false, "", "ERROR: option `--inline` is missing an argument") @test readchomperrors(`$exename --startup-file=no -e "@show ARGS" -now -- julia RUN.jl`) == (false, "", "ERROR: unknown option `-n`") @test readchomperrors(`$exename --interactive=yes`) == (false, "", "ERROR: option `-i/--interactive` does not accept an argument") # --compiled-modules={yes|no} @test readchomp(`$exename -E "Bool(Base.JLOptions().use_compiled_modules)"`) == "true" @test readchomp(`$exename --compiled-modules=yes -E "Bool(Base.JLOptions().use_compiled_modules)"`) == "true" @test readchomp(`$exename --compiled-modules=no -E "Bool(Base.JLOptions().use_compiled_modules)"`) == "false" @test errors_not_signals(`$exename --compiled-modules=foo -e "exit(0)"`) # issue #12671, starting from a non-directory # rm(dir) fails on windows with Permission denied # and was an upstream bug in llvm <= v3.3 if !Sys.iswindows() && Base.libllvm_version > v"3.3" testdir = mktempdir() cd(testdir) do rm(testdir) @test Base.current_project() === nothing @test "" == test_read_success(`$exename -e "exit(0)"`) for load_path in ["", "@", "@."] withenv("JULIA_LOAD_PATH" => load_path) do @test "" == test_read_success(`$exename -e "exit(!(Base.load_path() == []))"`) end end end end end # Object file with multiple cpu targets @testset "Object file for multiple microarchitectures" begin julia_path = joinpath(Sys.BINDIR, Base.julia_exename()) outputo_file = tempname() write(outputo_file, "1") object_file = tempname() * ".o" # This is to test that even with `pkgimages=no`, we can create object file # with multiple cpu-targets # The cmd is checked for `--object-o` as soon as it is run. So, to avoid long # testing times, intentionally don't pass `--sysimage`; when we reach the # corresponding error, we know that `check_cmdline` has already passed let v = readchomperrors(`$julia_path --cpu-target='native;native' --output-o=$object_file $outputo_file --pkgimages=no`) @test v[1] == false @test v[2] == "" @test !contains(v[3], "More than one command line CPU targets specified") @test v[3] == "ERROR: File \"boot.jl\" not found" end # This is to test that with `pkgimages=yes`, multiple CPU targets are parsed. # We intentionally fail fast due to a lack of an `--output-o` flag. let v = readchomperrors(`$julia_path --cpu-target='native;native' --pkgimages=yes`) @test v[1] == false @test v[2] == "" @test contains(v[3], "More than one command line CPU targets specified") end # Testing this more precisely would be very platform and build system dependent and brittle. withenv("JULIA_CPU_TARGET" => "sysimage") do v = readchomp(`$julia_path -E "Sys.sysimage_target()"`) # Local builds will likely be "native" but CI shouldn't be. invalid_results = Base.get_bool_env("CI", false) ? ("", "native", "sysimage") : ("", "sysimage",) @test !in(v, invalid_results) end end # Find the path of libjulia (or libjulia-debug, as the case may be) # to use as a dummy shlib to open libjulia = if Base.DARWIN_FRAMEWORK abspath(Libdl.dlpath(Base.DARWIN_FRAMEWORK_NAME * (Base.isdebugbuild() ? "_debug" : ""))) else abspath(Libdl.dlpath(Base.isdebugbuild() ? "libjulia-debug" : "libjulia")) end # test error handling code paths of running --sysimage let exename = `$(Base.julia_cmd().exec[1]) -t 1` sysname = unsafe_string(Base.JLOptions().image_file) for nonexist_image in ( joinpath(@__DIR__, "nonexistent"), "$sysname.nonexistent", ) let err = Pipe(), p = run(pipeline(`$exename --sysimage=$nonexist_image`, stderr=err), wait=false) close(err.in) let s = read(err, String) @test occursin("ERROR: could not load library \"$nonexist_image\"\n", s) @test !occursin("Segmentation fault", s) @test !occursin("EXCEPTION_ACCESS_VIOLATION", s) end @test errors_not_signals(p) @test p.exitcode == 1 end end let err = Pipe(), p = run(pipeline(`$exename --sysimage=$libjulia`, stderr=err), wait=false) close(err.in) let s = read(err, String) @test s == "ERROR: Image file failed consistency check: maybe opened the wrong version?\n" end @test errors_not_signals(p) @test p.exitcode == 1 end end let exename = Base.julia_cmd() # --startup-file let JL_OPTIONS_STARTUPFILE_ON = 1, JL_OPTIONS_STARTUPFILE_OFF = 2 # `JULIA_DEPOT_PATH=$tmpdir` to avoid errors in the user startup.jl, which hangs the tests. Issue #17642 mktempdir() do tmpdir withenv("JULIA_DEPOT_PATH"=>tmpdir) do @test parse(Int,readchomp(`$exename -E "Base.JLOptions().startupfile" --startup-file=yes`)) == JL_OPTIONS_STARTUPFILE_ON end end @test parse(Int,readchomp(`$exename -E "Base.JLOptions().startupfile" --startup-file=no`)) == JL_OPTIONS_STARTUPFILE_OFF end @test errors_not_signals(`$exename --startup-file=false`) end # Make sure `julia --lisp` doesn't break run(pipeline(devnull, `$(joinpath(Sys.BINDIR, Base.julia_exename())) --lisp`, devnull)) # Test that `julia [some other option] --lisp` is disallowed @test readchomperrors(`$(joinpath(Sys.BINDIR, Base.julia_exename())) -Cnative --lisp`) == (false, "", "ERROR: --lisp must be specified as the first argument") # backtrace contains line number info (esp. on windows #17179) let # TODO: Make this safe in the presence of two single-thread threadpools with # --sysimage-native-code=no, though that option is deprecated. # see https://github.com/JuliaLang/julia/issues/57198 succ, out, bt = readchomperrors(`$(Base.julia_cmd()) --startup-file=no -E 'sqrt(-2)'`) @test !succ @test out == "" @test occursin(r"\.jl:(\d+)", bt) end # PR #23002 let exename = `$(Base.julia_cmd()) --startup-file=no` for (mac, flag, pfix, msg) in [("@test_nowarn", ``, "_1", ""), ("@test_warn", `--warn-overwrite=yes`, "_2", "\"WARNING: Method definition\"")] str = """ using Test try # issue #18725 $mac $msg @eval Main begin f18725$(pfix)(x) = 1 f18725$(pfix)(x) = 2 end @test Main.f18725$(pfix)(0) == 2 # PR #23030 $mac $msg @eval Main module Module23030$(pfix) f23030$(pfix)(x) = 1 f23030$(pfix)(x) = 2 end catch exit(-1) end exit(0) """ run(`$exename $flag -e $str`) end end # issue #6310 let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` @test writereadpipeline("2+2", exename) == ("4\n", true) @test writereadpipeline("2+2\n3+3\n4+4", exename) == ("4\n6\n8\n", true) @test writereadpipeline("", exename) == ("", true) @test writereadpipeline("print(2)", exename) == ("2", true) @test writereadpipeline("print(2)\nprint(3)", exename) == ("23", true) let infile = tempname() touch(infile) try @test read(pipeline(exename, stdin=infile), String) == "" write(infile, "(1, 2+3)") @test read(pipeline(exename, stdin=infile), String) == "(1, 5)\n" write(infile, "1+2\n2+2\n1-2\n") @test read(pipeline(exename, stdin=infile), String) == "3\n4\n-1\n" write(infile, "print(2)") @test read(pipeline(exename, stdin=infile), String) == "2" write(infile, "print(2)\nprint(3)") @test read(pipeline(exename, stdin=infile), String) == "23" finally rm(infile) end end end # incomplete inputs to stream REPL let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` in = Pipe(); out = Pipe(); err = Pipe() proc = run(pipeline(exename, stdin = in, stdout = out, stderr = err), wait=false) write(in, "f(\n") close(in) close(err.in) txt = readline(err) @test startswith(txt, r"ERROR: (syntax: incomplete|ParseError:)") end # uncaught errors in `-e` and script files must not leak driver frames # (exec_options, _start, eval/include machinery) into the printed backtrace let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` script = tempname() * ".jl" write(script, "f() = error(\"boom\")\nf()\n") for cmd in (`$exename -e 'f() = error("boom"); f()'`, `$exename $script`) (success, out, err) = readchomperrors(cmd) @test !success @test occursin("boom", err) @test occursin("top-level scope", err) @test !occursin("exec_options", err) @test !occursin("_start", err) @test !occursin("__script_entry", err) end rm(script; force=true) end # Issue #29855 for yn in ("no", "yes") exename = `$(Base.julia_cmd()) --inline=no --startup-file=no --color=no --inline=$yn` v = writereadpipeline("Base.julia_cmd()", exename) if yn == "no" @test occursin(r" --inline=no", v[1]) else @test !occursin(" --inline", v[1]) end @test v[2] end # issue #39259, shadowing `ARGS` @test "" == test_read_success(`$(Base.julia_cmd()) --startup-file=no -e 'ARGS=1'`) @testset "- as program file reads from stdin" begin for args in (`- foo bar`, `-- - foo bar`) cmd = `$(Base.julia_cmd()) --startup-file=no $(args)` io = IOBuffer() open(cmd, io; write=true) do proc write(proc, """ println(PROGRAM_FILE) println(@__FILE__) foreach(println, ARGS) """) end lines = collect(eachline(seekstart(io))) @test lines[1] == "-" @test lines[2] == "stdin" @test lines[3] == "foo" @test lines[4] == "bar" end end # FIXME: Issue #57103: MMTK currently does not use --heap-size-hint since it only # supports setting up a hard limit unlike the Stock GC # which takes it as a soft limit. For now, we skip the tests below for MMTk @static if Base.USING_STOCK_GC @testset "heap size hint" begin #heap-size-hint, we reserve 250 MB for non GC memory (llvm, etc.) @test readchomp(`$(Base.julia_cmd()) --startup-file=no --heap-size-hint=500M -e "println(@ccall jl_gc_get_max_memory()::UInt64)"`) == "$((500-250)*1024*1024)" mem = ccall(:uv_get_total_memory, UInt64, ()) cmem = ccall(:uv_get_constrained_memory, UInt64, ()) if cmem > 0 && cmem < mem mem = cmem end maxmem = parse(UInt64, readchomp(`$(Base.julia_cmd()) --startup-file=no --heap-size-hint=25% -e "println(@ccall jl_gc_get_max_memory()::UInt64)"`)) hint = max(mem÷4, 251*1024*1024) - 250*1024*1024 MAX32HEAP = 1536 * 1024 * 1024 if Int === Int32 && hint > MAX32HEAP hint = MAX32HEAP end @test abs(Float64(maxmem) - hint)/maxmem < 0.05 @test readchomp(`$(Base.julia_cmd()) --startup-file=no --heap-size-hint=10M -e "println(@ccall jl_gc_get_max_memory()::UInt64)"`) == "$(1*1024*1024)" end @testset "hard heap limit" begin cmd = `$(Base.julia_cmd()) --hard-heap-limit=30M -E "mutable struct ListNode; v::Int64; next::Union{ListNode, Nothing}; end\n l = ListNode(0, nothing); while true; l = ListNode(0, l); end"` @test !success(cmd) end end ## `Main.main` entrypoint # Basic usage @test readchomp(`$(Base.julia_cmd()) -e '(@main)(args) = println("hello")'`) == "hello" # Test ARGS with -e @test readchomp(`$(Base.julia_cmd()) -e '(@main)(args) = println(args)' a b`) == repr(["a", "b"]) # Test import from module @test readchomp(`$(Base.julia_cmd()) -e 'module Hello; export main; (@main)(args) = println("hello"); end; using .Hello'`) == "hello" @test readchomp(`$(Base.julia_cmd()) -e 'module Hello; export main; (@main)(args) = println("hello"); end; import .Hello'`) == "" # test --bug-report=rr if Sys.islinux() && Sys.ARCH in (:i686, :x86_64) # rr is only available on these platforms mktempdir() do temp_trace_dir cmd = setenv(`$(Base.julia_cmd()) --bug-report=rr-local -e 'exit()'`, "JULIA_RR_RECORD_ARGS" => "-n --nested=ignore", "_RR_TRACE_DIR" => temp_trace_dir) success, out, err = readchomperrors(cmd) # rr cannot read perf counters if running in containers, allow it to fail in this case allowed_failure = occursin("Unable to open performance counter", err) @testset let cmd=cmd, stdout=out, stderr=err @test success || allowed_failure end end end @testset "--heap-size-hint" begin exename = `$(Base.julia_cmd())` @test errors_not_signals(`$exename --heap-size-hint -e "exit(0)"`) @testset "--heap-size-hint=$str" for str in ["asdf","","0","1.2vb","b","GB","2.5GB̂","1.2gb2","42gigabytes","5gig","2GiB","NaNt"] @test errors_not_signals(`$exename --heap-size-hint=$str -e "exit(0)"`) end k = 1024 m = 1024k g = 1024m t = 1024g @testset "--heap-size-hint=$str" for (str, val) in [("1", 1), ("1e7", 1e7), ("2.5e7", 2.5e7), ("1MB", 1m), ("2.5g", 2.5g), ("1e4kB", 1e4k), ("1e100", typemax(UInt64)), ("1e500g", typemax(UInt64)), ("1e-12t", 1), ("500000000b", 500000000)] @test parse(UInt64,read(`$exename --heap-size-hint=$str -E "Base.JLOptions().heap_size_hint"`, String)) == val end end @testset "--hard-heap-limit" begin exename = `$(Base.julia_cmd())` @test errors_not_signals(`$exename --hard-heap-limit -e "exit(0)"`) @testset "--hard-heap-limit=$str" for str in ["asdf","","0","1.2vb","b","GB","2.5GB̂","1.2gb2","42gigabytes","5gig","2GiB","NaNt"] @test errors_not_signals(`$exename --hard-heap-limit=$str -e "exit(0)"`) end k = UInt64(1) << 10 m = UInt64(1) << 20 g = UInt64(1) << 30 t = UInt64(1) << 40 one_hundred_mb_strs_and_vals = [ ("100000000", 100000000), ("1e8", 1e8), ("100MB", 100m), ("100m", 100m), ("1e5kB", 1e5k), ] @testset "--hard-heap-limit=$str" for (str, val) in one_hundred_mb_strs_and_vals @test test_read_success(`$exename --hard-heap-limit=$str -E "Base.JLOptions().hard_heap_limit"`, UInt64) == val end two_and_a_half_gigabytes_strs_and_vals = [ ("2500000000", 2500000000), ("2.5e9", 2.5e9), ("2.5g", 2.5g), ("2.5GB", 2.5g), ("2.5e6mB", 2.5e6m), ] @testset "--hard-heap-limit=$str" for (str, val) in two_and_a_half_gigabytes_strs_and_vals @test test_read_success(`$exename --hard-heap-limit=$str -E "Base.JLOptions().hard_heap_limit"`, UInt64) == val end one_terabyte_strs_and_vals = [ ("1TB", 1t), ("1024GB", 1t), ] @testset "--hard-heap-limit=$str" for (str, val) in one_terabyte_strs_and_vals @test test_read_success(`$exename --hard-heap-limit=$str -E "Base.JLOptions().hard_heap_limit"`, UInt64) == val end end @testset "--heap-target-increment" begin exename = `$(Base.julia_cmd())` @test errors_not_signals(`$exename --heap-target-increment -e "exit(0)"`) @testset "--heap-target-increment=$str" for str in ["asdf","","0","1.2vb","b","GB","2.5GB̂","1.2gb2","42gigabytes","5gig","2GiB","NaNt"] @test errors_not_signals(`$exename --heap-target-increment=$str -e "exit(0)"`) end k = UInt64(1) << 10 m = UInt64(1) << 20 g = UInt64(1) << 30 t = UInt64(1) << 40 one_hundred_mb_strs_and_vals = [ ("100000000", 100000000), ("1e8", 1e8), ("100MB", 100m), ("100m", 100m), ("1e5kB", 1e5k), ] @testset "--heap-target-increment=$str" for (str, val) in one_hundred_mb_strs_and_vals @test test_read_success(`$exename --heap-target-increment=$str -E "Base.JLOptions().heap_target_increment"`, UInt64) == val end two_and_a_half_gigabytes_strs_and_vals = [ ("2500000000", 2500000000), ("2.5e9", 2.5e9), ("2.5g", 2.5g), ("2.5GB", 2.5g), ("2.5e6mB", 2.5e6m), ] @testset "--heap-target-increment=$str" for (str, val) in two_and_a_half_gigabytes_strs_and_vals @test test_read_success(`$exename --heap-target-increment=$str -E "Base.JLOptions().heap_target_increment"`, UInt64) == val end one_terabyte_strs_and_vals = [ ("1TB", 1t), ("1024GB", 1t), ] @testset "--heap-target-increment=$str" for (str, val) in one_terabyte_strs_and_vals @test test_read_success(`$exename --heap-target-increment=$str -E "Base.JLOptions().heap_target_increment"`, UInt64) == val end end @testset "--timeout-for-safepoint-straggler" begin exename = `$(Base.julia_cmd())` timeout = 120 @test test_read_success(`$exename --timeout-for-safepoint-straggler=$timeout -E "Base.JLOptions().timeout_for_safepoint_straggler_s"`, Int) == timeout end @testset "--strip-metadata" begin mktempdir() do dir @test "" == test_read_success(`$(Base.julia_cmd()) --strip-metadata -t1,0 --output-o $(dir)/sys.o.a -e 0`) if isfile(joinpath(dir, "sys.o.a")) Base.Linking.link_image(joinpath(dir, "sys.o.a"), joinpath(dir, "sys.so")) @test readchomp(`$(Base.julia_cmd()) -t1,0 -J $(dir)/sys.so -E 'hasmethod(sort, (Vector{Int},), (:dims,))'`) == "true" end end end # Build and use a system image, exercising both split (--output-o together with # --output-ji, heap goes into the .ji) and non-split (--output-o only, heap goes # into the .so) layouts, with --compress-sysimage on and off in each. #= These tests are disabled because they significantly increase CI time. @testset "system image: split=$split compress=$compress" for split in (true, false), compress in (true, false) mktempdir() do dir o_file = joinpath(dir, "sys.o.a") ji_file = joinpath(dir, "sys.ji") so_file = joinpath(dir, "sys.so") cmd = `$(Base.julia_cmd()) --strip-metadata -t1,0 --compress-sysimage=$(compress ? "yes" : "no") --output-o=$o_file` if split cmd = `$cmd --output-ji=$ji_file` end cmd = `$cmd -e 0` success, out, err = readchomperrors(cmd) @test success @test out == "" # Compression on/off must not trigger the non-split .ji warning here: # a native output is being produced (and, when split, paired with --output-ji). @test !occursin("--compress-sysimage=yes is unsupported", err) if isfile(o_file) @test isfile(ji_file) == split Base.Linking.link_image(o_file, so_file) @test readchomp(`$(Base.julia_cmd()) -t1,0 -J $so_file -E 'hasmethod(sort, (Vector{Int},), (:dims,))'`) == "true" end end end =# # Precompile and load a package, exercising the split pkgimage layout # (--pkgimages=yes: native code goes into the ocachefile, the heap stays in the # .ji) and the plain serialized .ji (--pkgimages=no), with --compress-sysimage # (which precompile workers inherit through julia_cmd) on and off in each. # Compression requires the split layout, so with --pkgimages=no the worker must # warn and emit an uncompressed heap. @testset "pkgimage: native=$native compress=$compress" for native in (true, false), compress in (true, false) mktempdir() do dir pkgdir = joinpath(dir, "CompressMe") mkpath(joinpath(pkgdir, "src")) write(joinpath(pkgdir, "Project.toml"), """ name = "CompressMe" uuid = "d1cd1848-32b7-4b19-a4d5-11c4de8b4381" version = "0.1.0" """) write(joinpath(pkgdir, "src", "CompressMe.jl"), """ module CompressMe f() = 42 end """) cmd = addenv(`$(Base.julia_cmd()) --pkgimages=$(native ? "yes" : "no") --compress-sysimage=$(compress ? "yes" : "no") --startup-file=no -E 'using CompressMe; CompressMe.f()'`, "JULIA_DEPOT_PATH" => joinpath(dir, "depot"), "JULIA_LOAD_PATH" => join((dir, "@stdlib"), Sys.iswindows() ? ";" : ":")) success, out, err = readchomperrors(cmd) @test success @test out == "42" @test occursin("--compress-sysimage=yes is unsupported", err) == (compress && !native) compiled = joinpath(dir, "depot", "compiled", "v$(VERSION.major).$(VERSION.minor)", "CompressMe") ji_file = only(filter(endswith(".ji"), readdir(compiled; join=true))) @test isfile(Base.ocachefile_from_cachefile(ji_file)) == native # The heap payload in the .ji must be zstd-compressed exactly when a # compressed split pkgimage was requested. open(ji_file) do io flags = Ref{UInt32}() checksum = Ref{UInt32}() dataendpos = Ref{Int64}() datastartpos = Ref{Int64}() err = ccall(:jl_read_verify_header, Cint, (Ptr{Cvoid}, Ptr{UInt32}, Ptr{UInt32}, Ptr{Int64}, Ptr{Int64}), io.ios, flags, checksum, dataendpos, datastartpos) @test err == 0 @test flags[] & Base.JI_FLAG_PKGIMAGE != 0 @test (flags[] & Base.JI_FLAG_SPLIT != 0) == native seek(io, datastartpos[]) zstd_magic = UInt8[0x28, 0xb5, 0x2f, 0xfd] @test (read(io, 4) == zstd_magic) == (native && compress) end # Load again in a fresh process from the existing cache, without # recompiling (which would rewrite the .ji, e.g. with a new build_id). cache_bytes = read(ji_file) success, out, err = readchomperrors(cmd) @test success @test out == "42" @test read(ji_file) == cache_bytes end end # https://github.com/JuliaLang/julia/issues/58229 Recursion in jitlinking with inline=no # Compiling a single entry point whose inferred call graph contains thousands of # CodeInstances used to overflow the stack in the JIT linker's recursive task # dispatcher (threshold ~4k with an 8MB stack); `--inline=no` keeps every callee as a # separately-linked function so they all land in one lookup batch. let n = 6000 code = """ f(x, ::Val{i}) where {i} = x + i @eval g(x) = +(\$((:(f(x, Val(\$i))) for i in 1:$n)...)) print(g(1)) """ @test test_read_success(`$(Base.julia_cmd()) --startup-file=no --inline=no -e $code`) == string(sum(1:n) + n) end # https://github.com/JuliaLang/julia/issues/59103 @test test_read_success(setenv(`$(Base.julia_cmd()) -g2 -e 'println("done")'`, "ENABLE_GDBLISTENER" => "1")) == "done"