test: add decl tests

This commit is contained in:
Nurul Huda (Apon) 2026-01-23 23:18:06 +06:00
parent c5051f9795
commit 0d18a9b332
2 changed files with 84 additions and 55 deletions

View file

@ -1,6 +1,6 @@
const std = @import("std"); const std = @import("std");
pub const Instant = @This(); const Instant = @This();
inner: *CInstant, inner: *CInstant,
epoch: i64, epoch: i64,
@ -28,7 +28,7 @@ pub fn from(text: []const u8) !Instant {
} }
/// Parse an ISO 8601 UTF-16 string (Temporal.Instant.from). /// Parse an ISO 8601 UTF-16 string (Temporal.Instant.from).
pub fn fromUtf16(text: []const u16) !Instant { fn fromUtf16(text: []const u16) !Instant {
const view = DiplomatString16View{ .data = text.ptr, .len = text.len }; const view = DiplomatString16View{ .data = text.ptr, .len = text.len };
return wrapInstant(temporal_rs_Instant_from_utf16(view)); return wrapInstant(temporal_rs_Instant_from_utf16(view));
} }
@ -103,7 +103,7 @@ pub fn toString(self: Instant, allocator: std.mem.Allocator, opts: ToStringOptio
} }
/// Convert to string using an explicit provider. /// Convert to string using an explicit provider.
pub fn toStringWithProvider(self: Instant, allocator: std.mem.Allocator, provider: *const Provider, opts: ToStringOptions) ![]u8 { fn toStringWithProvider(self: Instant, allocator: std.mem.Allocator, provider: *const Provider, opts: ToStringOptions) ![]u8 {
const zone_opt = if (opts.time_zone) |z| const zone_opt = if (opts.time_zone) |z|
TimeZone_option{ .ok = z, .is_ok = true } TimeZone_option{ .ok = z, .is_ok = true }
else else
@ -143,12 +143,12 @@ pub fn toZonedDateTimeISO(self: Instant, zone: TimeZone) !ZonedDateTimeHandle {
} }
/// Convert to ZonedDateTime using an explicit provider. /// Convert to ZonedDateTime using an explicit provider.
pub fn toZonedDateTimeIsoWithProvider(self: Instant, zone: TimeZone, provider: *const Provider) !ZonedDateTimeHandle { fn toZonedDateTimeIsoWithProvider(self: Instant, zone: TimeZone, provider: *const Provider) !ZonedDateTimeHandle {
return wrapZonedDateTime(temporal_rs_Instant_to_zoned_date_time_iso_with_provider(self.inner, zone, provider)); return wrapZonedDateTime(temporal_rs_Instant_to_zoned_date_time_iso_with_provider(self.inner, zone, provider));
} }
/// Clone the underlying instant. /// Clone the underlying instant.
pub fn clone(self: Instant) Instant { fn clone(self: Instant) Instant {
const ptr = temporal_rs_Instant_clone(self.inner); const ptr = temporal_rs_Instant_clone(self.inner);
return .{ .inner = ptr, .epoch = temporal_rs_Instant_epoch_milliseconds(ptr) }; return .{ .inner = ptr, .epoch = temporal_rs_Instant_epoch_milliseconds(ptr) };
} }
@ -221,12 +221,12 @@ fn parseDuration(text: []const u8) !DurationHandle {
// --- Public helper types ----------------------------------------------------- // --- Public helper types -----------------------------------------------------
pub const ToStringOptions = struct { const ToStringOptions = struct {
time_zone: ?TimeZone = null, time_zone: ?TimeZone = null,
rounding: ?ToStringRoundingOptions = null, rounding: ?ToStringRoundingOptions = null,
}; };
pub const DurationHandle = struct { const DurationHandle = struct {
ptr: *Duration, ptr: *Duration,
pub fn deinit(self: DurationHandle) void { pub fn deinit(self: DurationHandle) void {
@ -234,7 +234,7 @@ pub const DurationHandle = struct {
} }
}; };
pub const ZonedDateTimeHandle = struct { const ZonedDateTimeHandle = struct {
ptr: *ZonedDateTime, ptr: *ZonedDateTime,
pub fn deinit(self: ZonedDateTimeHandle) void { pub fn deinit(self: ZonedDateTimeHandle) void {
@ -245,21 +245,21 @@ pub const ZonedDateTimeHandle = struct {
// --- Extern types ------------------------------------------------------------ // --- Extern types ------------------------------------------------------------
const CInstant = opaque {}; const CInstant = opaque {};
pub const Duration = opaque {}; const Duration = opaque {};
pub const ZonedDateTime = opaque {}; const ZonedDateTime = opaque {};
pub const Provider = opaque {}; const Provider = opaque {};
pub const I128Nanoseconds = extern struct { high: u64, low: u64 }; const I128Nanoseconds = extern struct { high: u64, low: u64 };
pub const I128Nanoseconds_option = extern struct { ok: I128Nanoseconds, is_ok: bool }; const I128Nanoseconds_option = extern struct { ok: I128Nanoseconds, is_ok: bool };
pub const DiplomatStringView = extern struct { data: [*c]const u8, len: usize }; const DiplomatStringView = extern struct { data: [*c]const u8, len: usize };
pub const DiplomatString16View = extern struct { data: [*c]const u16, len: usize }; const DiplomatString16View = extern struct { data: [*c]const u16, len: usize };
pub const OptionStringView = extern struct { ok: DiplomatStringView, is_ok: bool }; const OptionStringView = extern struct { ok: DiplomatStringView, is_ok: bool };
pub const OptionU8 = extern struct { ok: u8, is_ok: bool }; const OptionU8 = extern struct { ok: u8, is_ok: bool };
pub const OptionU32 = extern struct { ok: u32, is_ok: bool }; const OptionU32 = extern struct { ok: u32, is_ok: bool };
pub const DiplomatWrite = extern struct { const DiplomatWrite = extern struct {
context: ?*anyopaque, context: ?*anyopaque,
buf: [*c]u8, buf: [*c]u8,
len: usize, len: usize,
@ -269,24 +269,24 @@ pub const DiplomatWrite = extern struct {
grow: ?*const fn (*DiplomatWrite, usize) bool, grow: ?*const fn (*DiplomatWrite, usize) bool,
}; };
pub const TimeZone = extern struct { const TimeZone = extern struct {
offset_minutes: i16, offset_minutes: i16,
resolved_id: usize, resolved_id: usize,
normalized_id: usize, normalized_id: usize,
is_iana_id: bool, is_iana_id: bool,
}; };
pub const TimeZone_option = extern struct { const TimeZone_option = extern struct {
ok: TimeZone, ok: TimeZone,
is_ok: bool, is_ok: bool,
}; };
pub const Precision = extern struct { const Precision = extern struct {
is_minute: bool, is_minute: bool,
precision: OptionU8, precision: OptionU8,
}; };
pub const Unit = enum(c_int) { const Unit = enum(c_int) {
Unit_Auto = 0, Unit_Auto = 0,
Unit_Nanosecond = 1, Unit_Nanosecond = 1,
Unit_Microsecond = 2, Unit_Microsecond = 2,
@ -300,9 +300,9 @@ pub const Unit = enum(c_int) {
Unit_Year = 10, Unit_Year = 10,
}; };
pub const Unit_option = extern struct { ok: Unit, is_ok: bool }; const Unit_option = extern struct { ok: Unit, is_ok: bool };
pub const RoundingMode = enum(c_int) { const RoundingMode = enum(c_int) {
RoundingMode_Ceil = 0, RoundingMode_Ceil = 0,
RoundingMode_Floor = 1, RoundingMode_Floor = 1,
RoundingMode_Expand = 2, RoundingMode_Expand = 2,
@ -314,29 +314,29 @@ pub const RoundingMode = enum(c_int) {
RoundingMode_HalfEven = 8, RoundingMode_HalfEven = 8,
}; };
pub const RoundingMode_option = extern struct { ok: RoundingMode, is_ok: bool }; const RoundingMode_option = extern struct { ok: RoundingMode, is_ok: bool };
pub const DifferenceSettings = extern struct { const DifferenceSettings = extern struct {
largest_unit: Unit_option, largest_unit: Unit_option,
smallest_unit: Unit_option, smallest_unit: Unit_option,
rounding_mode: RoundingMode_option, rounding_mode: RoundingMode_option,
increment: OptionU32, increment: OptionU32,
}; };
pub const RoundingOptions = extern struct { const RoundingOptions = extern struct {
largest_unit: Unit_option, largest_unit: Unit_option,
smallest_unit: Unit_option, smallest_unit: Unit_option,
rounding_mode: RoundingMode_option, rounding_mode: RoundingMode_option,
increment: OptionU32, increment: OptionU32,
}; };
pub const ToStringRoundingOptions = extern struct { const ToStringRoundingOptions = extern struct {
precision: Precision, precision: Precision,
smallest_unit: Unit_option, smallest_unit: Unit_option,
rounding_mode: RoundingMode_option, rounding_mode: RoundingMode_option,
}; };
pub const ErrorKind = enum(c_int) { const ErrorKind = enum(c_int) {
ErrorKind_Generic = 0, ErrorKind_Generic = 0,
ErrorKind_Type = 1, ErrorKind_Type = 1,
ErrorKind_Range = 2, ErrorKind_Range = 2,
@ -344,12 +344,12 @@ pub const ErrorKind = enum(c_int) {
ErrorKind_Assert = 4, ErrorKind_Assert = 4,
}; };
pub const TemporalError = extern struct { const TemporalError = extern struct {
kind: ErrorKind, kind: ErrorKind,
msg: OptionStringView, msg: OptionStringView,
}; };
pub const Sign = enum(c_int) { const Sign = enum(c_int) {
Sign_Positive = 1, Sign_Positive = 1,
Sign_Zero = 0, Sign_Zero = 0,
Sign_Negative = -1, Sign_Negative = -1,

View file

@ -10,14 +10,25 @@ pub const ZonedDateTime = @import("ZonedDateTime.zig");
const Temporal = @This(); const Temporal = @This();
fn assertDecls(comptime T: type, checks: anytype) !void { test "Temporal" {
const std = @import("std"); const std = @import("std");
inline for (checks) |check| {
const has = @hasDecl(T, check); const expected_scopes = .{
if (!std.mem.eql(u8, check, "valueOf")) { "Duration",
if (!has) std.log.err("Missing {s} method: {s}", .{ @typeName(T), check }); "Instant",
try std.testing.expect(has); "Now",
} "PlainDate",
"PlainDateTime",
"PlainMonthDay",
"PlainTime",
"PlainYearMonth",
"ZonedDateTime",
};
inline for (expected_scopes) |scope| {
const has = @hasDecl(Temporal, scope);
if (!has) std.log.err("Missing Temporal scope: {s}", .{scope});
try std.testing.expect(has);
} }
} }
@ -388,24 +399,42 @@ test "Temporal.ZonedDateTime" {
try assertDecls(ZonedDateTime, checks); try assertDecls(ZonedDateTime, checks);
} }
test "Temporal" { fn assertDecls(comptime T: type, checks: anytype) !void {
const std = @import("std"); const std = @import("std");
const typeInfo = @typeInfo(T);
const expected_scopes = .{ // Check: all items in checks exist
"Duration", inline for (checks) |check| {
"Instant", const should_ignore =
"Now", std.mem.eql(u8, check, "deinit") or
"PlainDate", std.mem.eql(u8, check, "valueOf");
"PlainDateTime",
"PlainMonthDay",
"PlainTime",
"PlainYearMonth",
"ZonedDateTime",
};
inline for (expected_scopes) |scope| { const has = @hasDecl(T, check);
const has = @hasDecl(Temporal, scope); if (!should_ignore) {
if (!has) std.log.err("Missing Temporal scope: {s}", .{scope}); if (!has) std.log.err("Missing {s} decl: {s}", .{ @typeName(T), check });
try std.testing.expect(has); try std.testing.expect(has);
}
}
// Check: no extraneous declarations beyond checks
if (typeInfo == .@"struct") {
const struct_info = typeInfo.@"struct";
inline for (struct_info.decls) |decl| {
// Allow deinit as extraneous
if (comptime std.mem.eql(u8, decl.name, "deinit")) continue;
var found = false;
inline for (checks) |check| {
if (std.mem.eql(u8, decl.name, check)) {
found = true;
break;
}
}
if (!found) {
std.log.err("Extraneous {s} decl: {s}", .{ @typeName(T), decl.name });
try std.testing.expect(false);
}
}
} }
} }