Skip to content

Commit 3bef295

Browse files
authored
Merge pull request #555 from avinxshKD/julia-interop-tests
tests/julia: add interop and wire compatibility tests
2 parents cff8b68 + 0a2fa6a commit 3bef295

3 files changed

Lines changed: 282 additions & 0 deletions

File tree

tests/julia/runtests.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ using .Concore
88
include("test_config.jl")
99
include("test_sync.jl")
1010
include("test_protocol.jl")
11+
include("test_wire_compat.jl")
12+
include("test_interop.jl")
1113
end

tests/julia/test_interop.jl

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# File-backend round trips against Python and C++ bindings.
2+
@testset "Cross-language interop" begin
3+
4+
repo_root = normpath(joinpath(@__DIR__, "..", ".."))
5+
repo_include = replace(repo_root, "\\" => "/")
6+
python_path(path) = replace(path, "\\" => "/")
7+
8+
function reset_interop_state!()
9+
Concore.simtime = 0.0
10+
Concore.delay = 0.0
11+
Concore.s = ""
12+
Concore.olds = ""
13+
Concore.retrycount = 0
14+
end
15+
16+
function run_cmd(cmd; dir=repo_root)
17+
try
18+
return 0, strip(read(Cmd(cmd; dir=dir), String)), ""
19+
catch err
20+
return 1, "", sprint(showerror, err)
21+
end
22+
end
23+
24+
@testset "Python writes, Julia reads" begin
25+
python = Sys.which("python")
26+
python === nothing && (python = Sys.which("python3"))
27+
28+
if python === nothing
29+
@test_skip "python executable not found"
30+
else
31+
mktempdir() do dir
32+
mkpath(joinpath(dir, "out1"))
33+
34+
script = """
35+
import sys
36+
sys.path.insert(0, "$(python_path(repo_root))")
37+
import concore
38+
concore.delay = 0
39+
concore.simtime = 5.0
40+
concore.outpath = "$(python_path(joinpath(dir, "out")))"
41+
concore.write(1, "signal", [42.0, 3.0])
42+
"""
43+
44+
code, _out, err = run_cmd(`$python -c $script`)
45+
@test code == 0
46+
err != "" && @info err
47+
48+
old_inpath = Concore.inpath
49+
try
50+
reset_interop_state!()
51+
Concore.inpath = joinpath(dir, "out")
52+
result = Concore.concore_read(1, "signal", "[0.0, 0.0, 0.0]")
53+
54+
@test result == [42.0, 3.0]
55+
@test Concore.simtime == 5.0
56+
@test read(joinpath(dir, "out1", "signal"), String) == "[5.0, 42.0, 3.0]"
57+
finally
58+
Concore.inpath = old_inpath
59+
reset_interop_state!()
60+
end
61+
end
62+
end
63+
end
64+
65+
@testset "Julia writes, Python reads" begin
66+
python = Sys.which("python")
67+
python === nothing && (python = Sys.which("python3"))
68+
69+
if python === nothing
70+
@test_skip "python executable not found"
71+
else
72+
mktempdir() do dir
73+
old_outpath = Concore.outpath
74+
try
75+
reset_interop_state!()
76+
Concore.outpath = joinpath(dir, "out")
77+
Concore.simtime = 7.0
78+
Concore.concore_write(1, "signal", [11.0, 12.5])
79+
80+
script = """
81+
import sys
82+
sys.path.insert(0, "$(python_path(repo_root))")
83+
import concore
84+
concore.delay = 0
85+
concore.simtime = 0.0
86+
concore.inpath = "$(python_path(joinpath(dir, "out")))"
87+
value = concore.read(1, "signal", "[0.0, 0.0, 0.0]")
88+
assert value == [11.0, 12.5], value
89+
assert concore.simtime == 7.0, concore.simtime
90+
"""
91+
92+
code, _out, err = run_cmd(`$python -c $script`)
93+
@test code == 0
94+
err != "" && @info err
95+
finally
96+
Concore.outpath = old_outpath
97+
reset_interop_state!()
98+
end
99+
end
100+
end
101+
end
102+
103+
function cxx_compiler()
104+
for name in ("g++", "clang++")
105+
compiler = Sys.which(name)
106+
compiler !== nothing && return compiler
107+
end
108+
return nothing
109+
end
110+
111+
@testset "C++ writes, Julia reads" begin
112+
compiler = cxx_compiler()
113+
114+
if compiler === nothing
115+
@test_skip "C++ compiler not found"
116+
else
117+
mktempdir() do dir
118+
source = joinpath(dir, "write_signal.cpp")
119+
exe = joinpath(dir, Sys.iswindows() ? "write_signal.exe" : "write_signal")
120+
write(source, """
121+
#include <vector>
122+
#include "$repo_include/concore.hpp"
123+
124+
int main() {
125+
Concore c;
126+
c.delay = 0;
127+
c.simtime = 9.0;
128+
c.write_FM(1, "signal", std::vector<double>{13.0, 14.0});
129+
return 0;
130+
}
131+
""")
132+
mkpath(joinpath(dir, "out1"))
133+
134+
code, _out, err = run_cmd(`$compiler -std=c++11 -I$repo_root -o $exe $source`; dir=dir)
135+
@test code == 0
136+
err != "" && @info err
137+
138+
code, _out, err = run_cmd(`$exe`; dir=dir)
139+
@test code == 0
140+
err != "" && @info err
141+
142+
old_inpath = Concore.inpath
143+
try
144+
reset_interop_state!()
145+
Concore.inpath = joinpath(dir, "out")
146+
result = Concore.concore_read(1, "signal", "[0.0, 0.0, 0.0]")
147+
148+
@test result == [13.0, 14.0]
149+
@test Concore.simtime == 9.0
150+
finally
151+
Concore.inpath = old_inpath
152+
reset_interop_state!()
153+
end
154+
end
155+
end
156+
end
157+
158+
@testset "Julia writes, C++ reads" begin
159+
compiler = cxx_compiler()
160+
161+
if compiler === nothing
162+
@test_skip "C++ compiler not found"
163+
else
164+
mktempdir() do dir
165+
old_outpath = Concore.outpath
166+
try
167+
reset_interop_state!()
168+
Concore.outpath = joinpath(dir, "out")
169+
Concore.simtime = 10.0
170+
Concore.concore_write(1, "signal", [21.0, 22.0])
171+
mkpath(joinpath(dir, "in1"))
172+
cp(joinpath(dir, "out1", "signal"), joinpath(dir, "in1", "signal"))
173+
finally
174+
Concore.outpath = old_outpath
175+
end
176+
177+
source = joinpath(dir, "read_signal.cpp")
178+
exe = joinpath(dir, Sys.iswindows() ? "read_signal.exe" : "read_signal")
179+
write(source, """
180+
#include <cmath>
181+
#include <vector>
182+
#include "$repo_include/concore.hpp"
183+
184+
int main() {
185+
Concore c;
186+
c.delay = 0;
187+
c.simtime = 0.0;
188+
std::vector<double> value = c.read_FM(1, "signal", "[0.0, 0.0, 0.0]");
189+
if (value.size() != 2) return 1;
190+
if (std::fabs(value[0] - 21.0) > 1e-9) return 2;
191+
if (std::fabs(value[1] - 22.0) > 1e-9) return 3;
192+
if (std::fabs(c.simtime - 10.0) > 1e-9) return 4;
193+
return 0;
194+
}
195+
""")
196+
197+
code, _out, err = run_cmd(`$compiler -std=c++11 -I$repo_root -o $exe $source`; dir=dir)
198+
@test code == 0
199+
err != "" && @info err
200+
201+
code, _out, err = run_cmd(`$exe`; dir=dir)
202+
@test code == 0
203+
err != "" && @info err
204+
205+
reset_interop_state!()
206+
end
207+
end
208+
end
209+
210+
end

tests/julia/test_wire_compat.jl

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Exact wire strings that must stay readable across Concore runtimes.
2+
@testset "Wire compatibility" begin
3+
4+
function reset_wire_state!()
5+
Concore.simtime = 0.0
6+
Concore.delay = 0.0
7+
Concore.s = ""
8+
Concore.olds = ""
9+
Concore.retrycount = 0
10+
end
11+
12+
@testset "Julia writes Python-style wire strings" begin
13+
cases = [
14+
(0.0, [0.0], "[0.0, 0.0]"),
15+
(0.0, [1.0, 2.0, 3.0], "[0.0, 1.0, 2.0, 3.0]"),
16+
(5.0, [42.0, 3.0], "[5.0, 42.0, 3.0]"),
17+
(2.0, [3.14, -1.5, 0.0], "[2.0, 3.14, -1.5, 0.0]"),
18+
]
19+
20+
mktempdir() do dir
21+
old_outpath = Concore.outpath
22+
Concore.outpath = joinpath(dir, "out")
23+
24+
try
25+
for (simtime, values, expected) in cases
26+
reset_wire_state!()
27+
Concore.simtime = simtime
28+
Concore.concore_write(1, "wire", values)
29+
content = read(joinpath(dir, "out1", "wire"), String)
30+
31+
@test content == expected
32+
@test !occursin('\n', content)
33+
@test !endswith(content, ",]")
34+
@test !endswith(content, ", ]")
35+
end
36+
finally
37+
Concore.outpath = old_outpath
38+
reset_wire_state!()
39+
end
40+
end
41+
end
42+
43+
@testset "Julia parses Python and C++ wire strings" begin
44+
@test Concore.safe_parse_list("[5.0, 42.0, 3.0]") == [5.0, 42.0, 3.0]
45+
@test Concore.safe_parse_list("[5,42,3]") == [5.0, 42.0, 3.0]
46+
@test Concore.safe_parse_list("[np.float64(5.0), np.float64(42.0)]") == [5.0, 42.0]
47+
@test Concore.safe_parse_list("np.array([1.0, 2.0, 3.0])") == [1.0, 2.0, 3.0]
48+
end
49+
50+
@testset "delta affects written simtime only" begin
51+
mktempdir() do dir
52+
old_outpath = Concore.outpath
53+
Concore.outpath = joinpath(dir, "out")
54+
55+
try
56+
reset_wire_state!()
57+
Concore.simtime = 5.0
58+
Concore.concore_write(1, "ym", [3.01]; delta=1)
59+
60+
content = read(joinpath(dir, "out1", "ym"), String)
61+
@test content == "[6.0, 3.01]"
62+
@test Concore.simtime == 5.0
63+
finally
64+
Concore.outpath = old_outpath
65+
reset_wire_state!()
66+
end
67+
end
68+
end
69+
70+
end

0 commit comments

Comments
 (0)