| ... | ... | @@ -1,8 +1,16 @@ |
| 1 | //! Tests for the C ABI. |
| 2 | //! Those tests are passing back and forth struct and values across C ABI |
| 3 | //! by combining Zig code here and its mirror in cfunc.c |
| 4 | //! To run all the tests on the tier 1 architecture you can use: |
| 5 | //! zig build test-c-abi -fqemu |
| 6 | //! To run the tests on a specific architecture: |
| 7 | //! zig test -fno-stage1 -lc main.zig cfuncs.c -target mips-linux --test-cmd qemu-mips --test-cmd-bin |
| 1 | 8 | const std = @import("std"); |
| 2 | 9 | const builtin = @import("builtin"); |
| 3 | 10 | const print = std.debug.print; |
| 4 | 11 | const expect = std.testing.expect; |
| 5 | | const has_i128 = builtin.cpu.arch != .i386 and !builtin.cpu.arch.isARM() and |
| 12 | const expectEqual = std.testing.expectEqual; |
| 13 | const has_i128 = builtin.cpu.arch != .x86 and !builtin.cpu.arch.isARM() and |
| 6 | 14 | !builtin.cpu.arch.isMIPS() and !builtin.cpu.arch.isPPC(); |
| 7 | 15 | |
| 8 | 16 | extern fn run_c_tests() void; |
| ... | ... | @@ -831,3 +839,162 @@ test "C ABI pointer sized float struct" { |
| 831 | 839 | try expect(x.x == 3); |
| 832 | 840 | try expect(x.y == 4); |
| 833 | 841 | } |
| 842 | |
| 843 | //=== Helpers for struct test ===// |
| 844 | pub inline fn expectOk(c_err: c_int) !void { |
| 845 | if (c_err != 0) { |
| 846 | std.debug.print("ABI mismatch on field v{d}.\n", .{c_err}); |
| 847 | return error.TestExpectedEqual; |
| 848 | } |
| 849 | } |
| 850 | |
| 851 | /// Tests for Double + Char struct |
| 852 | const DC = extern struct { v1: f64, v2: u8 }; |
| 853 | test "DC: Zig passes to C" { |
| 854 | if (builtin.target.cpu.arch == .x86_64 and builtin.target.os.tag != .windows) |
| 855 | return error.SkipZigTest; |
| 856 | if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest; |
| 857 | if (comptime builtin.cpu.arch.isRISCV()) return error.SkipZigTest; |
| 858 | if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; |
| 859 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 860 | try expectOk(c_assert_DC(.{ .v1 = -0.25, .v2 = 15 })); |
| 861 | } |
| 862 | test "DC: Zig returns to C" { |
| 863 | if (comptime builtin.cpu.arch.isRISCV()) return error.SkipZigTest; |
| 864 | if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; |
| 865 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 866 | try expectOk(c_assert_ret_DC()); |
| 867 | } |
| 868 | test "DC: C passes to Zig" { |
| 869 | if (builtin.target.cpu.arch == .x86_64 and builtin.target.os.tag != .windows) |
| 870 | return error.SkipZigTest; |
| 871 | if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest; |
| 872 | if (comptime builtin.cpu.arch.isRISCV()) return error.SkipZigTest; |
| 873 | if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; |
| 874 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 875 | try expectOk(c_send_DC()); |
| 876 | } |
| 877 | test "DC: C returns to Zig" { |
| 878 | if (comptime builtin.cpu.arch.isRISCV()) return error.SkipZigTest; |
| 879 | if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; |
| 880 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 881 | try expectEqual(c_ret_DC(), .{ .v1 = -0.25, .v2 = 15 }); |
| 882 | } |
| 883 | |
| 884 | pub extern fn c_assert_DC(lv: DC) c_int; |
| 885 | pub extern fn c_assert_ret_DC() c_int; |
| 886 | pub extern fn c_send_DC() c_int; |
| 887 | pub extern fn c_ret_DC() DC; |
| 888 | pub export fn zig_assert_DC(lv: DC) c_int { |
| 889 | var err: c_int = 0; |
| 890 | if (lv.v1 != -0.25) err = 1; |
| 891 | if (lv.v2 != 15) err = 2; |
| 892 | if (err != 0) std.debug.print("Received {}", .{lv}); |
| 893 | return err; |
| 894 | } |
| 895 | pub export fn zig_ret_DC() DC { |
| 896 | return .{ .v1 = -0.25, .v2 = 15 }; |
| 897 | } |
| 898 | |
| 899 | /// Tests for Char + Float + FloatRect struct |
| 900 | const CFF = extern struct { v1: u8, v2: f32, v3: f32 }; |
| 901 | |
| 902 | test "CFF: Zig passes to C" { |
| 903 | if (builtin.target.cpu.arch.isX86() and builtin.target.os.tag != .windows) |
| 904 | return error.SkipZigTest; |
| 905 | if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest; |
| 906 | if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; |
| 907 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 908 | try expectOk(c_assert_CFF(.{ .v1 = 39, .v2 = 0.875, .v3 = 1.0 })); |
| 909 | } |
| 910 | test "CFF: Zig returns to C" { |
| 911 | if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest; |
| 912 | if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; |
| 913 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 914 | try expectOk(c_assert_ret_CFF()); |
| 915 | } |
| 916 | test "CFF: C passes to Zig" { |
| 917 | if (builtin.target.cpu.arch.isX86() and builtin.target.os.tag != .windows) |
| 918 | return error.SkipZigTest; |
| 919 | if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest; |
| 920 | if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; |
| 921 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 922 | |
| 923 | try expectOk(c_send_CFF()); |
| 924 | } |
| 925 | test "CFF: C returns to Zig" { |
| 926 | // segfault on aarch64 and mips |
| 927 | if (builtin.target.cpu.arch == .aarch64) return error.SkipZigTest; |
| 928 | if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest; |
| 929 | if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; |
| 930 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 931 | try expectEqual(c_ret_CFF(), .{ .v1 = 39, .v2 = 0.875, .v3 = 1.0 }); |
| 932 | } |
| 933 | pub extern fn c_assert_CFF(lv: CFF) c_int; |
| 934 | pub extern fn c_assert_ret_CFF() c_int; |
| 935 | pub extern fn c_send_CFF() c_int; |
| 936 | pub extern fn c_ret_CFF() CFF; |
| 937 | pub export fn zig_assert_CFF(lv: CFF) c_int { |
| 938 | var err: c_int = 0; |
| 939 | if (lv.v1 != 39) err = 1; |
| 940 | if (lv.v2 != 0.875) err = 2; |
| 941 | if (lv.v3 != 1.0) err = 3; |
| 942 | if (err != 0) std.debug.print("Received {}", .{lv}); |
| 943 | return err; |
| 944 | } |
| 945 | pub export fn zig_ret_CFF() CFF { |
| 946 | return .{ .v1 = 39, .v2 = 0.875, .v3 = 1.0 }; |
| 947 | } |
| 948 | |
| 949 | /// Tests for Pointer + Double struct |
| 950 | const PD = extern struct { v1: ?*anyopaque, v2: f64 }; |
| 951 | |
| 952 | test "PD: Zig passes to C" { |
| 953 | if (builtin.target.cpu.arch.isX86() and builtin.target.os.tag != .windows) |
| 954 | return error.SkipZigTest; |
| 955 | if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest; |
| 956 | if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; |
| 957 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 958 | try expectOk(c_assert_PD(.{ .v1 = null, .v2 = 0.5 })); |
| 959 | } |
| 960 | test "PD: Zig returns to C" { |
| 961 | if (builtin.target.cpu.arch == .x86) return error.SkipZigTest; |
| 962 | if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; |
| 963 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 964 | try expectOk(c_assert_ret_PD()); |
| 965 | } |
| 966 | test "PD: C passes to Zig" { |
| 967 | if (builtin.target.cpu.arch.isX86() and builtin.target.os.tag != .windows) |
| 968 | return error.SkipZigTest; |
| 969 | if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest; |
| 970 | if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; |
| 971 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 972 | try expectOk(c_send_PD()); |
| 973 | } |
| 974 | test "PD: C returns to Zig" { |
| 975 | if (builtin.target.cpu.arch == .x86) return error.SkipZigTest; |
| 976 | if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; |
| 977 | if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; |
| 978 | try expectEqual(c_ret_PD(), .{ .v1 = null, .v2 = 0.5 }); |
| 979 | } |
| 980 | pub extern fn c_assert_PD(lv: PD) c_int; |
| 981 | pub extern fn c_assert_ret_PD() c_int; |
| 982 | pub extern fn c_send_PD() c_int; |
| 983 | pub extern fn c_ret_PD() PD; |
| 984 | pub export fn zig_c_assert_PD(lv: PD) c_int { |
| 985 | var err: c_int = 0; |
| 986 | if (lv.v1 != null) err = 1; |
| 987 | if (lv.v2 != 0.5) err = 2; |
| 988 | if (err != 0) std.debug.print("Received {}", .{lv}); |
| 989 | return err; |
| 990 | } |
| 991 | pub export fn zig_ret_PD() PD { |
| 992 | return .{ .v1 = null, .v2 = 0.5 }; |
| 993 | } |
| 994 | pub export fn zig_assert_PD(lv: PD) c_int { |
| 995 | var err: c_int = 0; |
| 996 | if (lv.v1 != null) err = 1; |
| 997 | if (lv.v2 != 0.5) err = 2; |
| 998 | if (err != 0) std.debug.print("Received {}", .{lv}); |
| 999 | return err; |
| 1000 | } |