Browse Source

Refactor Runic.main tests to also cover juliac code paths

pull/104/head
Fredrik Ekre 1 year ago
parent
commit
b418141251
No known key found for this signature in database
GPG Key ID: DE82E6D5E364C0A2
  1. 2
      .github/workflows/Test.yml
  2. 366
      test/maintests.jl
  3. 392
      test/runtests.jl

2
.github/workflows/Test.yml

@ -35,6 +35,8 @@ jobs:
- uses: julia-actions/cache@v2 - uses: julia-actions/cache@v2
- uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-runtest@v1
- uses: julia-actions/julia-processcoverage@v1 - uses: julia-actions/julia-processcoverage@v1
with:
directories: "src,juliac"
- uses: codecov/codecov-action@v4 - uses: codecov/codecov-action@v4
with: with:
file: lcov.info file: lcov.info

366
test/maintests.jl

@ -0,0 +1,366 @@
# SPDX-License-Identifier: MIT
using Test: @test
using Runic: Runic
function maintests(f::R) where {R}
bad = "1+1"
good = "1 + 1\n"
# Utils
function cdtmp(f)
return mktempdir(tmp -> cd(f, tmp))
end
function runic(std_in::String)
return runic(String[], std_in)
end
function runic(argv::Vector{String} = String[], std_in::String = "")
rc, stdout_str, stderr_str = mktemp() do stdin_path, stdin
write(stdin_path, std_in)
mktemp() do stdout_path, stdout
mktemp() do stderr_path, stderr
rc = redirect_stdio(() -> f(copy(argv)); stdin, stdout, stderr)
close(stderr)
close(stdout)
return rc, read(stdout_path, String), read(stderr_path, String)
end
end
end
return rc, stdout_str, stderr_str
end
# runic --help
let (rc, fd1, fd2) = runic(["--help"])
@test rc == 0
@test occursin("Runic.main - format Julia source code", fd1)
@test isempty(fd2)
end
# runic --version
let (rc, fd1, fd2) = runic(["--version"])
@test rc == 0
@test occursin("runic version $(Runic.RUNIC_VERSION), julia version $(VERSION)", fd1)
@test isempty(fd2)
end
# runic <stdin >stdout
for argv in [
String[], ["-"],
["--output=-"], ["-o", "-"],
["--output=-", "-"], ["-o", "-", "-"],
]
rc, fd1, fd2 = runic(argv, bad)
@test rc == 0
@test occursin(good, fd1)
@test isempty(fd2)
end
# runic --output=out.jl <stdin
cdtmp() do
f_out = "out.jl"
for argv in [
["--output=$f_out"], ["-o", f_out],
["--output=$f_out", "-"], ["-o", f_out, "-"],
]
rm(f_out, force = true)
rc, fd1, fd2 = runic(argv, bad)
@test rc == 0
@test isempty(fd1)
@test isempty(fd2)
@test read(f_out, String) == good
end
end
# runic in.jl >stdout
cdtmp() do
f_in = "in.jl"
write(f_in, bad)
for argv in [[f_in], ["--output=-", f_in], ["-o", "-", f_in]]
rc, fd1, fd2 = runic(argv)
@test rc == 0
@test occursin(good, fd1)
@test isempty(fd2)
@test read(f_in, String) == bad
end
end
# runic --output=out.jl in.jl
cdtmp() do
f_in = "in.jl"
write(f_in, bad)
f_out = "out.jl"
for argv in [["--output=$f_out", f_in], ["-o", f_out, f_in]]
rm(f_out, force = true)
rc, fd1, fd2 = runic(argv)
@test rc == 0
@test isempty(fd1)
@test occursin("Formatting `in.jl` -> `out.jl` ...", fd2)
@test occursin("", fd2)
@test !occursin("", fd2)
@test read(f_out, String) == good
@test read(f_in, String) == bad
end
end
# runic --inplace in.jl (bad input)
cdtmp() do
f_in = "in.jl"
for argv in [["--inplace", f_in], ["-i", f_in]]
write(f_in, bad)
rc, fd1, fd2 = runic(argv)
@test rc == 0
@test isempty(fd1)
@test occursin("Formatting `in.jl` ...", fd2)
@test occursin("", fd2)
@test !occursin("", fd2)
@test read(f_in, String) == good
end
end
# runic --inplace in.jl (good input)
cdtmp() do
f_in = "in.jl"
for argv in [["--inplace", f_in], ["-i", f_in]]
write(f_in, good)
rc, fd1, fd2 = runic(argv)
@test rc == 0
@test isempty(fd1)
@test occursin("Formatting `in.jl` ...", fd2)
@test occursin("", fd2)
@test !occursin("", fd2)
@test read(f_in, String) == good
end
end
# runic --inplace in/
cdtmp() do
fgood = "good.jl"
mkdir("src")
fbad = joinpath("src", "bad.jl")
mkdir(".git")
gitfile = joinpath(".git", "git.jl")
write(gitfile, "this is not a Julia file")
markdownfile = "markdown.md"
write(markdownfile, "this is not a Julia file")
for argv in [["--inplace", "."], ["-i", "."], ["-i", ".", "src"]]
write(fgood, good)
write(fbad, bad)
rc, fd1, fd2 = runic(argv)
@test rc == 0
@test isempty(fd1)
@test occursin("Formatting `good.jl` ...", fd2)
@test occursin("Formatting `src/bad.jl` ...", fd2)
@test occursin("", fd2)
@test !occursin("", fd2)
@test !occursin("git.jl", fd2)
@test !occursin("markdown.jl", fd2)
@test read(fgood, String) == read(fbad, String) == good
end
end
# runic --check in.jl (bad input)
cdtmp() do
f_in = "in.jl"
for argv in [["--check", f_in], ["-c", f_in]]
write(f_in, bad)
rc, fd1, fd2 = runic(argv)
@test rc == 1
@test isempty(fd1)
@test occursin("Checking `in.jl` ...", fd2)
@test !occursin("", fd2)
@test occursin("", fd2)
@test read(f_in, String) == bad
end
end
# runic --check in.jl (good input)
cdtmp() do
f_in = "in.jl"
for argv in [["--check", f_in], ["-c", f_in]]
write(f_in, good)
rc, fd1, fd2 = runic(argv)
@test rc == 0
@test isempty(fd1)
@test occursin("Checking `in.jl` ...", fd2)
@test occursin("", fd2)
@test !occursin("", fd2)
@test read(f_in, String) == good
end
end
# runic --check in/
cdtmp() do
fgood = "good.jl"
mkdir("src")
fbad = joinpath("src", "bad.jl")
mkdir(".git")
gitfile = joinpath(".git", "git.jl")
write(gitfile, "this is not a Julia file")
markdownfile = "markdown.md"
write(markdownfile, "this is not a Julia file")
for argv in [["--check", "."], ["-c", "."]]
write(fgood, good)
write(fbad, bad)
rc, fd1, fd2 = runic(argv)
@test rc == 1
@test isempty(fd1)
@test occursin("Checking `good.jl` ...", fd2)
@test occursin("Checking `src/bad.jl` ...", fd2)
@test occursin("", fd2)
@test occursin("", fd2)
@test !occursin("git.jl", fd2)
@test !occursin("markdown.jl", fd2)
@test read(fgood, String) == good
@test read(fbad, String) == bad
end
end
# runic --check --diff in.jl
if Sys.which("git") !== nothing
cdtmp() do
f_in = "in.jl"
for argv in [["--check", "--diff", f_in], ["-c", "-d", f_in]]
write(f_in, bad)
rc, fd1, fd2 = runic(argv)
@test rc == 1
@test isempty(fd1)
@test occursin("Checking `in.jl` ...", fd2)
@test !occursin("", fd2)
@test occursin("", fd2)
@test occursin("diff --git", fd2)
@test occursin("-1+1", fd2)
@test occursin("+1 + 1", fd2)
@test read(f_in, String) == bad
end
end
end
# Error paths
# runic -o
let (rc, fd1, fd2) = runic(["-o"])
@test rc == 1
@test isempty(fd1)
@test occursin("expected output file argument after `-o`", fd2)
end
# runic in.jl -
let (rc, fd1, fd2) = runic(["in.jl", "-"])
@test rc == 1
@test isempty(fd1)
@test occursin("input `-` can not be used with multiple files", fd2)
end
# runic --inplace --check (TODO: perhaps this should be allowed?)
let (rc, fd1, fd2) = runic(["--inplace", "--check"])
@test rc == 1
@test isempty(fd1)
@test occursin("options `--inplace` and `--check` are mutually exclusive", fd2)
end
# runic --inplace --output=out.jl in.jl
let (rc, fd1, fd2) = runic(["--inplace", "--output=out.jl", "in.jl"])
@test rc == 1
@test isempty(fd1)
@test occursin("options `--inplace` and `--output` are mutually exclusive", fd2)
end
# runic --check --output=out.jl in.jl
let (rc, fd1, fd2) = runic(["--check", "--output=out.jl", "in.jl"])
@test rc == 1
@test isempty(fd1)
@test occursin("options `--check` and `--output` are mutually exclusive", fd2)
end
# runic --inplace
let (rc, fd1, fd2) = runic(["--inplace"])
@test rc == 1
@test isempty(fd1)
@test occursin("option `--inplace` can not be used together with stdin input", fd2)
end
# runic --output=out.jl in1.jl in2.jl
let (rc, fd1, fd2) = runic(["--output=out.jl", "in1.jl", "in2.jl"])
@test rc == 1
@test isempty(fd1)
@test occursin("option `--output` can not be used together with multiple input files", fd2)
end
# runic in1.jl in2.jl
let (rc, fd1, fd2) = runic(["in1.jl", "in2.jl"])
@test rc == 1
@test isempty(fd1)
@test occursin("option `--inplace` or `--check` required with multiple input files", fd2)
end
# runic --diff (with no git)
let (rc, fd1, fd2) = withenv(() -> runic(["--diff"]), "PATH" => "")
@test rc == 1
@test isempty(fd1)
@test occursin("option `--diff` requires `git` to be installed", fd2)
end
# runic in.jl (not readable)
cdtmp() do
f_in = "in.jl"
write(f_in, bad)
omode = filemode(f_in)
chmod(f_in, omode & (typemax(omode) 0o444))
rc, fd1, fd2 = runic([f_in])
chmod(f_in, omode)
@test rc == 1
@test isempty(fd1)
@test occursin("could not read input from file", fd2)
@test occursin("SystemError: opening file", fd2)
end
# runic doesntexist.jl
cdtmp() do
rc, fd1, fd2 = runic(["doesntexist.jl"])
@test rc == 1
@test isempty(fd1)
@test occursin("input file does not exist", fd2)
end
# runic -o in.jl in.jl
cdtmp() do
f_in = "in.jl"
write(f_in, bad)
rc, fd1, fd2 = runic(["-o", f_in, f_in])
@test rc == 1
@test isempty(fd1)
@test occursin("can not use same file for input and output", fd2)
end
# runic --check unparseable.jl
cdtmp() do
f_in = "in.jl"
write(f_in, "syntax error")
rc, fd1, fd2 = runic(["--check", f_in])
@test rc == 1
@test isempty(fd1)
@test occursin("failed to parse input", fd2)
end
# runic -o readonly.jl in.jl
return cdtmp() do
f_in = "in.jl"
write(f_in, bad)
f_out = "readonly.jl"
touch(f_out)
omode = filemode(f_out)
chmod(f_out, omode & (typemax(omode) 0o222))
rc, fd1, fd2 = runic(["-o", f_out, f_in])
chmod(f_out, omode)
@test rc == 1
@test isempty(fd1)
@test occursin("could not write to output file", fd2)
end
end
# rc = let argv = pushfirst!(copy(argv), "runic"), argc = length(argv) % Cint
# GC.@preserve argv begin
# argvptr = Base.unsafe_convert(Ptr{Ptr{UInt8}}, Base.cconvert(Ptr{Ptr{UInt8}}, argv))
# redirect_stdio(() -> Main.RunicC.main(argc, argvptr); stdin, stdout, stderr)
# end
# end

392
test/runtests.jl

@ -1455,359 +1455,61 @@ end
end end
end end
@testset "Runic.main" begin module RunicMain1
bad = "1+1" using Test: @testset
good = "1 + 1\n" using Runic: main
include("maintests.jl")
# Utils @testset "Runic.main (JIT compiled)" begin
function cdtmp(f) maintests(main)
return mktempdir(tmp -> cd(f, tmp))
end
function runic(std_in::String)
return runic(String[], std_in)
end
function runic(argv::Vector{String} = String[], std_in::String = "")
rc, stdout_str, stderr_str = mktemp() do stdin_path, stdin
write(stdin_path, std_in)
mktemp() do stdout_path, stdout
mktemp() do stderr_path, stderr
rc = redirect_stdio(() -> Runic.main(copy(argv)); stdin, stdout, stderr)
close(stderr)
close(stdout)
return rc, read(stdout_path, String), read(stderr_path, String)
end
end
end
return rc, stdout_str, stderr_str
end
# runic --help
let (rc, fd1, fd2) = runic(["--help"])
@test rc == 0
@test occursin("Runic.main - format Julia source code", fd1)
@test isempty(fd2)
end
# runic --version
let (rc, fd1, fd2) = runic(["--version"])
@test rc == 0
@test occursin("runic version $(Runic.RUNIC_VERSION), julia version $(VERSION)", fd1)
@test isempty(fd2)
end
# runic <stdin >stdout
for argv in [
String[], ["-"],
["--output=-"], ["-o", "-"],
["--output=-", "-"], ["-o", "-", "-"],
]
rc, fd1, fd2 = runic(argv, bad)
@test rc == 0
@test occursin(good, fd1)
@test isempty(fd2)
end
# runic --output=out.jl <stdin
cdtmp() do
f_out = "out.jl"
for argv in [
["--output=$f_out"], ["-o", f_out],
["--output=$f_out", "-"], ["-o", f_out, "-"],
]
rm(f_out, force = true)
rc, fd1, fd2 = runic(argv, bad)
@test rc == 0
@test isempty(fd1)
@test isempty(fd2)
@test read(f_out, String) == good
end
end
# runic in.jl >stdout
cdtmp() do
f_in = "in.jl"
write(f_in, bad)
for argv in [[f_in], ["--output=-", f_in], ["-o", "-", f_in]]
rc, fd1, fd2 = runic(argv)
@test rc == 0
@test occursin(good, fd1)
@test isempty(fd2)
@test read(f_in, String) == bad
end
end
# runic --output=out.jl in.jl
cdtmp() do
f_in = "in.jl"
write(f_in, bad)
f_out = "out.jl"
for argv in [["--output=$f_out", f_in], ["-o", f_out, f_in]]
rm(f_out, force = true)
rc, fd1, fd2 = runic(argv)
@test rc == 0
@test isempty(fd1)
@test occursin("Formatting `in.jl` -> `out.jl` ...", fd2)
@test occursin("", fd2)
@test !occursin("", fd2)
@test read(f_out, String) == good
@test read(f_in, String) == bad
end
end
# runic --inplace in.jl (bad input)
cdtmp() do
f_in = "in.jl"
for argv in [["--inplace", f_in], ["-i", f_in]]
write(f_in, bad)
rc, fd1, fd2 = runic(argv)
@test rc == 0
@test isempty(fd1)
@test occursin("Formatting `in.jl` ...", fd2)
@test occursin("", fd2)
@test !occursin("", fd2)
@test read(f_in, String) == good
end
end
# runic --inplace in.jl (good input)
cdtmp() do
f_in = "in.jl"
for argv in [["--inplace", f_in], ["-i", f_in]]
write(f_in, good)
rc, fd1, fd2 = runic(argv)
@test rc == 0
@test isempty(fd1)
@test occursin("Formatting `in.jl` ...", fd2)
@test occursin("", fd2)
@test !occursin("", fd2)
@test read(f_in, String) == good
end
end
# runic --inplace in/
cdtmp() do
fgood = "good.jl"
mkdir("src")
fbad = joinpath("src", "bad.jl")
mkdir(".git")
gitfile = joinpath(".git", "git.jl")
write(gitfile, "this is not a Julia file")
markdownfile = "markdown.md"
write(markdownfile, "this is not a Julia file")
for argv in [["--inplace", "."], ["-i", "."], ["-i", ".", "src"]]
write(fgood, good)
write(fbad, bad)
rc, fd1, fd2 = runic(argv)
@test rc == 0
@test isempty(fd1)
@test occursin("Formatting `good.jl` ...", fd2)
@test occursin("Formatting `src/bad.jl` ...", fd2)
@test occursin("", fd2)
@test !occursin("", fd2)
@test !occursin("git.jl", fd2)
@test !occursin("markdown.jl", fd2)
@test read(fgood, String) == read(fbad, String) == good
end
end
# runic --check in.jl (bad input)
cdtmp() do
f_in = "in.jl"
for argv in [["--check", f_in], ["-c", f_in]]
write(f_in, bad)
rc, fd1, fd2 = runic(argv)
@test rc == 1
@test isempty(fd1)
@test occursin("Checking `in.jl` ...", fd2)
@test !occursin("", fd2)
@test occursin("", fd2)
@test read(f_in, String) == bad
end
end
# runic --check in.jl (good input)
cdtmp() do
f_in = "in.jl"
for argv in [["--check", f_in], ["-c", f_in]]
write(f_in, good)
rc, fd1, fd2 = runic(argv)
@test rc == 0
@test isempty(fd1)
@test occursin("Checking `in.jl` ...", fd2)
@test occursin("", fd2)
@test !occursin("", fd2)
@test read(f_in, String) == good
end
end
# runic --check in/
cdtmp() do
fgood = "good.jl"
mkdir("src")
fbad = joinpath("src", "bad.jl")
mkdir(".git")
gitfile = joinpath(".git", "git.jl")
write(gitfile, "this is not a Julia file")
markdownfile = "markdown.md"
write(markdownfile, "this is not a Julia file")
for argv in [["--check", "."], ["-c", "."]]
write(fgood, good)
write(fbad, bad)
rc, fd1, fd2 = runic(argv)
@test rc == 1
@test isempty(fd1)
@test occursin("Checking `good.jl` ...", fd2)
@test occursin("Checking `src/bad.jl` ...", fd2)
@test occursin("", fd2)
@test occursin("", fd2)
@test !occursin("git.jl", fd2)
@test !occursin("markdown.jl", fd2)
@test read(fgood, String) == good
@test read(fbad, String) == bad
end
end
# runic --check --diff in.jl
if Sys.which("git") !== nothing
cdtmp() do
f_in = "in.jl"
for argv in [["--check", "--diff", f_in], ["-c", "-d", f_in]]
write(f_in, bad)
rc, fd1, fd2 = runic(argv)
@test rc == 1
@test isempty(fd1)
@test occursin("Checking `in.jl` ...", fd2)
@test !occursin("", fd2)
@test occursin("", fd2)
@test occursin("diff --git", fd2)
@test occursin("-1+1", fd2)
@test occursin("+1 + 1", fd2)
@test read(f_in, String) == bad
end
end
end
# Error paths
# runic -o
let (rc, fd1, fd2) = runic(["-o"])
@test rc == 1
@test isempty(fd1)
@test occursin("expected output file argument after `-o`", fd2)
end
# runic in.jl -
let (rc, fd1, fd2) = runic(["in.jl", "-"])
@test rc == 1
@test isempty(fd1)
@test occursin("input `-` can not be used with multiple files", fd2)
end
# runic --inplace --check (TODO: perhaps this should be allowed?)
let (rc, fd1, fd2) = runic(["--inplace", "--check"])
@test rc == 1
@test isempty(fd1)
@test occursin("options `--inplace` and `--check` are mutually exclusive", fd2)
end
# runic --inplace --output=out.jl in.jl
let (rc, fd1, fd2) = runic(["--inplace", "--output=out.jl", "in.jl"])
@test rc == 1
@test isempty(fd1)
@test occursin("options `--inplace` and `--output` are mutually exclusive", fd2)
end
# runic --check --output=out.jl in.jl
let (rc, fd1, fd2) = runic(["--check", "--output=out.jl", "in.jl"])
@test rc == 1
@test isempty(fd1)
@test occursin("options `--check` and `--output` are mutually exclusive", fd2)
end
# runic --inplace
let (rc, fd1, fd2) = runic(["--inplace"])
@test rc == 1
@test isempty(fd1)
@test occursin("option `--inplace` can not be used together with stdin input", fd2)
end
# runic --output=out.jl in1.jl in2.jl
let (rc, fd1, fd2) = runic(["--output=out.jl", "in1.jl", "in2.jl"])
@test rc == 1
@test isempty(fd1)
@test occursin("option `--output` can not be used together with multiple input files", fd2)
end
# runic in1.jl in2.jl
let (rc, fd1, fd2) = runic(["in1.jl", "in2.jl"])
@test rc == 1
@test isempty(fd1)
@test occursin("option `--inplace` or `--check` required with multiple input files", fd2)
end
# runic --diff (with no git)
let (rc, fd1, fd2) = withenv(() -> runic(["--diff"]), "PATH" => "")
@test rc == 1
@test isempty(fd1)
@test occursin("option `--diff` requires `git` to be installed", fd2)
end end
end
# runic in.jl (not readable) module RunicMain2
cdtmp() do using Test: @testset, @test
f_in = "in.jl" if VERSION > v"1.12-"
write(f_in, bad) include("../juliac/runicc.jl")
omode = filemode(f_in) include("maintests.jl")
chmod(f_in, omode & (typemax(omode) 0o444)) function juliac_main(argv)
rc, fd1, fd2 = runic([f_in]) pushfirst!(argv, "runic")
chmod(f_in, omode) juliac_main = @cfunction(RunicC.main, Cint, (Cint, Ptr{Ptr{UInt8}}))
@test rc == 1 return @ccall $juliac_main(length(argv)::Cint, argv::Ptr{Ptr{UInt8}})::Cint
@test isempty(fd1)
@test occursin("could not read input from file", fd2)
@test occursin("SystemError: opening file", fd2)
end end
@testset "RunicC.main (JIT compiled, prefs: juliac = false)" begin
# runic doesntexist.jl @test !Runic.juliac
cdtmp() do maintests(juliac_main)
rc, fd1, fd2 = runic(["doesntexist.jl"])
@test rc == 1
@test isempty(fd1)
@test occursin("input file does not exist", fd2)
end end
# runic -o in.jl in.jl
cdtmp() do
f_in = "in.jl"
write(f_in, bad)
rc, fd1, fd2 = runic(["-o", f_in, f_in])
@test rc == 1
@test isempty(fd1)
@test occursin("can not use same file for input and output", fd2)
end end
end
# runic --check unparseable.jl module RunicMain3
cdtmp() do if VERSION > v"1.12-"
f_in = "in.jl" julia_cmd = Base.julia_cmd()
write(f_in, "syntax error") juliac_dir = joinpath(@__DIR__, "..", "juliac")
rc, fd1, fd2 = runic(["--check", f_in]) # Instantiate
@test rc == 1 run(
@test isempty(fd1) addenv(
@test occursin("failed to parse input", fd2) setenv(`make JULIA=$(julia_cmd[1]) Manifest.toml`; dir = juliac_dir),
"JULIA_LOAD_PATH" => nothing,
"JULIA_PROJECT" => nothing,
)
)
# Run tests
code = """
using Test: @testset, @test
include("runicc.jl")
include("../test/maintests.jl")
function juliac_main(argv)
pushfirst!(argv, "runic")
juliac_main = @cfunction(RunicC.main, Cint, (Cint, Ptr{Ptr{UInt8}}))
@ccall \$juliac_main(length(argv)::Cint, argv::Ptr{Ptr{UInt8}})::Cint
end
@testset "RunicC.main (JIT compiled, prefs: juliac = true)" begin
@test Runic.juliac
maintests(juliac_main)
end end
"""
# runic -o readonly.jl in.jl run(setenv(`$(julia_cmd) --project -e $(code)`; dir = juliac_dir))
cdtmp() do
f_in = "in.jl"
write(f_in, bad)
f_out = "readonly.jl"
touch(f_out)
omode = filemode(f_out)
chmod(f_out, omode & (typemax(omode) 0o222))
rc, fd1, fd2 = runic(["-o", f_out, f_in])
chmod(f_out, omode)
@test rc == 1
@test isempty(fd1)
@test occursin("could not write to output file", fd2)
end end
end end
const share_julia = joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia") const share_julia = joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia")

Loading…
Cancel
Save