Skip to content

Commit 9e5d2fd

Browse files
gbaralditopolarity
andauthored
Port the tests from Base julia into JuliaC.jl (#90)
Co-authored-by: Cody Tapscott <[email protected]>
1 parent eb39268 commit 9e5d2fd

File tree

6 files changed

+281
-1
lines changed

6 files changed

+281
-1
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
.DS_Store
2-
*Manifest.toml
2+
*Manifest*.toml
33
.vscode
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name = "TrimmabilityProject"
2+
uuid = "a1b2c3d4-5678-90ab-cdef-1234567890ab"
3+
version = "0.1.0"
4+
5+
[deps]
6+
Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Test that various constructs support trimming
2+
module TrimmabilityProject
3+
4+
using Sockets
5+
6+
world::String = "world!"
7+
const str = OncePerProcess{String}() do
8+
return "Hello, " * world
9+
end
10+
11+
# Concrete type dispatch (no abstract types)
12+
struct Square
13+
side::Float64
14+
end
15+
struct Circle
16+
radius::Float64
17+
end
18+
area(s::Square) = s.side^2
19+
area(c::Circle) = pi*c.radius^2
20+
21+
function @main(args::Vector{String})::Cint
22+
println(Core.stdout, str())
23+
println(Core.stdout, PROGRAM_FILE)
24+
foreach(x->println(Core.stdout, x), args)
25+
26+
# test concrete type dispatch (not abstract type dispatch)
27+
println(Core.stdout, area(Circle(1)) + area(Square(2)))
28+
29+
arr = rand(10)
30+
sorted_arr = sort(arr)
31+
tot = sum(sorted_arr)
32+
tot = prod(sorted_arr)
33+
a = any(x -> x > 0, sorted_arr)
34+
b = all(x -> x >= 0, sorted_arr)
35+
c = map(x -> x^2, sorted_arr)
36+
d = mapreduce(x -> x^2, +, sorted_arr)
37+
# e = reduce(xor, rand(Int, 10))
38+
39+
try
40+
sock = connect("localhost", 4900)
41+
if isopen(sock)
42+
write(sock, "Hello")
43+
flush(sock)
44+
close(sock)
45+
end
46+
catch
47+
end
48+
49+
Base.donotdelete(reshape([1,2,3],:,1,1))
50+
51+
return 0
52+
end
53+
54+
end

test/c/capplication.c

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#include <stdio.h>
2+
#include <stdint.h>
3+
4+
#ifdef _WIN32
5+
#include <windows.h>
6+
typedef HMODULE lib_handle_t;
7+
#else
8+
#include <dlfcn.h>
9+
typedef void* lib_handle_t;
10+
#endif
11+
12+
// Cross-platform library loading functions
13+
static lib_handle_t load_library(const char* path) {
14+
#ifdef _WIN32
15+
return LoadLibraryA(path);
16+
#else
17+
return dlopen(path, RTLD_NOW | RTLD_GLOBAL);
18+
#endif
19+
}
20+
21+
static void* get_symbol(lib_handle_t handle, const char* name) {
22+
#ifdef _WIN32
23+
return (void*)GetProcAddress(handle, name);
24+
#else
25+
return dlsym(handle, name);
26+
#endif
27+
}
28+
29+
static void print_load_error(const char* context) {
30+
#ifdef _WIN32
31+
fprintf(stderr, "%s failed: error code %lu\n", context, GetLastError());
32+
#else
33+
fprintf(stderr, "%s failed: %s\n", context, dlerror());
34+
#endif
35+
}
36+
37+
// Struct definitions matching libsimple.jl
38+
typedef struct CVector_Float32 {
39+
int32_t length;
40+
float* data;
41+
} CVector_Float32;
42+
typedef struct CVectorPair_Float32 {
43+
CVector_Float32 from;
44+
CVector_Float32 to;
45+
} CVectorPair_Float32;
46+
typedef struct MyTwoVec {
47+
int32_t x;
48+
int32_t y;
49+
} MyTwoVec;
50+
51+
typedef float (*copyto_and_sum_t)(CVectorPair_Float32);
52+
typedef int32_t (*countsame_t)(MyTwoVec*, int32_t);
53+
54+
int main(int argc, char** argv) {
55+
if (argc < 2) {
56+
fprintf(stderr, "usage: %s <libpath>\n", argv[0]);
57+
return 2;
58+
}
59+
60+
lib_handle_t h = load_library(argv[1]);
61+
if (!h) {
62+
print_load_error("LoadLibrary/dlopen");
63+
return 3;
64+
}
65+
66+
// Test copyto_and_sum
67+
copyto_and_sum_t copyto_and_sum = (copyto_and_sum_t)get_symbol(h, "copyto_and_sum");
68+
if (!copyto_and_sum) {
69+
print_load_error("GetProcAddress/dlsym copyto_and_sum");
70+
return 4;
71+
}
72+
73+
CVectorPair_Float32 vecPair;
74+
float from_data[] = {1.0f, 2.0f, 3.0f};
75+
float to_data[] = {4.0f, 5.0f, 6.0f};
76+
vecPair.from.length = 3;
77+
vecPair.from.data = from_data;
78+
vecPair.to.length = 3;
79+
vecPair.to.data = to_data;
80+
81+
float sum = copyto_and_sum(vecPair);
82+
printf("Sum of copied values: %f\n", sum);
83+
if (sum < 5.9f || sum > 6.1f) {
84+
fprintf(stderr, "bad result from copyto_and_sum: %f (expected 6.0)\n", sum);
85+
return 5;
86+
}
87+
88+
// Test countsame
89+
countsame_t countsame = (countsame_t)get_symbol(h, "countsame");
90+
if (!countsame) {
91+
print_load_error("GetProcAddress/dlsym countsame");
92+
return 6;
93+
}
94+
95+
MyTwoVec list[] = {{1, 2}, {5, 5}, {3, 4}};
96+
int32_t count = countsame(list, 3);
97+
printf("Count of same vectors: %d\n", count);
98+
if (count != 1) {
99+
fprintf(stderr, "bad result from countsame: %d (expected 1)\n", count);
100+
return 7;
101+
}
102+
103+
return 0;
104+
}

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ const TEST_LIB_SRC = joinpath(TEST_LIB_PROJ, "src", "libtest.jl")
1212
include("utils.jl")
1313
include("programatic.jl")
1414
include("cli.jl")
15+
include("trimming.jl")

test/trimming.jl

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Trimming tests ported from Base Julia test/trimming/
2+
3+
const TRIM_PROJ = abspath(joinpath(@__DIR__, "TrimmabilityProject"))
4+
5+
@testset "Trimming: executable size check" begin
6+
# Uses the existing TEST_SRC (test.jl) hello world
7+
# Verifies the executable size stays reasonable
8+
outdir = mktempdir()
9+
exeout = joinpath(outdir, "hello")
10+
11+
img = JuliaC.ImageRecipe(
12+
file = TEST_SRC,
13+
output_type = "--output-exe",
14+
project = TEST_PROJ,
15+
trim_mode = "safe",
16+
verbose = true,
17+
)
18+
JuliaC.compile_products(img)
19+
link = JuliaC.LinkRecipe(image_recipe=img, outname=exeout)
20+
JuliaC.link_products(link)
21+
bun = JuliaC.BundleRecipe(link_recipe=link, output_dir=outdir)
22+
JuliaC.bundle_products(bun)
23+
24+
actual_exe = Sys.iswindows() ? joinpath(outdir, "bin", basename(exeout) * ".exe") : joinpath(outdir, "bin", basename(exeout))
25+
@test isfile(actual_exe)
26+
27+
# Test that the executable size stays reasonable (< 2.5MB for the executable itself)
28+
@test filesize(actual_exe) < 2_500_000
29+
30+
print_tree_with_sizes(outdir)
31+
end
32+
33+
@testset "Trimming: trimmability.jl (various constructs)" begin
34+
outdir = mktempdir()
35+
exeout = joinpath(outdir, "trimmability")
36+
37+
# Build trimmability project as executable - tests OncePerProcess, Sockets, sort, map, etc.
38+
img = JuliaC.ImageRecipe(
39+
file = TRIM_PROJ,
40+
output_type = "--output-exe",
41+
trim_mode = "safe",
42+
verbose = true,
43+
)
44+
JuliaC.compile_products(img)
45+
link = JuliaC.LinkRecipe(image_recipe=img, outname=exeout)
46+
JuliaC.link_products(link)
47+
bun = JuliaC.BundleRecipe(link_recipe=link, output_dir=outdir)
48+
JuliaC.bundle_products(bun)
49+
50+
actual_exe = Sys.iswindows() ? joinpath(outdir, "bin", basename(exeout) * ".exe") : joinpath(outdir, "bin", basename(exeout))
51+
@test isfile(actual_exe)
52+
53+
# Test output - the program should output:
54+
# 1. "Hello, world!" (from OncePerProcess)
55+
# 2. PROGRAM_FILE (the path to the executable)
56+
# 3. arg1
57+
# 4. arg2
58+
# 5. The sum_areas result: 4.0 + pi = 7.141592653589793
59+
output = readchomp(`$actual_exe arg1 arg2`)
60+
lines = split(output, '\n')
61+
@test length(lines) >= 5
62+
@test lines[1] == "Hello, world!"
63+
@test lines[2] == actual_exe # PROGRAM_FILE
64+
@test lines[3] == "arg1"
65+
@test lines[4] == "arg2"
66+
@test parse(Float64, lines[5]) (4.0 + pi)
67+
68+
print_tree_with_sizes(outdir)
69+
end
70+
71+
@testset "Trimming: libsimple.jl C application test" begin
72+
outdir = mktempdir()
73+
libout = joinpath(outdir, "libsimple")
74+
75+
# Build the libsimple library
76+
img = JuliaC.ImageRecipe(
77+
file = joinpath(@__DIR__, "libsimple.jl"),
78+
output_type = "--output-lib",
79+
project = TEST_LIB_PROJ,
80+
add_ccallables = true,
81+
trim_mode = "safe",
82+
verbose = true,
83+
)
84+
JuliaC.compile_products(img)
85+
link = JuliaC.LinkRecipe(image_recipe=img, outname=libout)
86+
JuliaC.link_products(link)
87+
bun = JuliaC.BundleRecipe(link_recipe=link, output_dir=outdir)
88+
JuliaC.bundle_products(bun)
89+
90+
# Library location differs by platform: bin/ on Windows, lib/ on Unix
91+
libdir = joinpath(outdir, Sys.iswindows() ? "bin" : "lib")
92+
libpath = joinpath(libdir, basename(libout) * "." * Base.BinaryPlatforms.platform_dlext())
93+
@test isfile(libpath)
94+
95+
# Compile and run the C application that uses libsimple
96+
# Use JuliaC's compiler (MinGW on Windows, system compiler on Unix)
97+
bindir = joinpath(outdir, "bin")
98+
mkpath(bindir)
99+
csrc = abspath(joinpath(@__DIR__, "c", "capplication.c"))
100+
exe = joinpath(bindir, Sys.iswindows() ? "capplication.exe" : "capplication")
101+
cc = JuliaC.get_compiler_cmd()
102+
103+
if Sys.islinux()
104+
run(`$cc -o $exe $csrc -ldl`)
105+
else
106+
run(`$cc -o $exe $csrc`)
107+
end
108+
109+
# Run the C application
110+
output = read(`$exe $libpath`, String)
111+
lines = split(strip(output), '\n')
112+
@test length(lines) == 2
113+
@test lines[1] == "Sum of copied values: 6.000000"
114+
@test lines[2] == "Count of same vectors: 1"
115+
end

0 commit comments

Comments
 (0)