Use approximate equality for float comparisons

This commit is contained in:
Daniel Wennberg
2025-09-18 12:34:32 -07:00
parent bb607e0999
commit 4af4e18725

View File

@@ -1371,6 +1371,9 @@ test "adjusted sizes" {
} }
test "face metrics" { test "face metrics" {
// The web canvas backend doesn't calculate face metrics, only cell metrics
if (options.backend != .web_canvas) return error.SkipZigTest;
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
const narrowFont = font.embedded.cozette; const narrowFont = font.embedded.cozette;
@@ -1403,80 +1406,108 @@ test "face metrics" {
.size_adjustment = .none, .size_adjustment = .none,
}); });
const narrowMetrics = (try c.getFace(narrowIndex)).getMetrics(); const narrowMetrics: font.Metrics.FaceMetrics = (try c.getFace(narrowIndex)).getMetrics();
const wideMetrics = (try c.getFace(wideIndex)).getMetrics(); const wideMetrics: font.Metrics.FaceMetrics = (try c.getFace(wideIndex)).getMetrics();
// Verify provided/measured metrics. Measured // Verify provided/measured metrics. Measured
// values are backend-dependent due to hinting. // values are backend-dependent due to hinting.
if (options.backend != .web_canvas) { const narrowMetricsExpected = font.Metrics.FaceMetrics{
try std.testing.expectEqual(font.Metrics.FaceMetrics{ .px_per_em = 16.0,
.px_per_em = 16.0, .cell_width = switch (options.backend) {
.cell_width = switch (options.backend) { .freetype,
.freetype, .fontconfig_freetype,
.fontconfig_freetype, .coretext_freetype,
.coretext_freetype, => 8.0,
=> 8.0, .coretext,
.coretext, .coretext_harfbuzz,
.coretext_harfbuzz, .coretext_noshape,
.coretext_noshape, => 7.3828125,
=> 7.3828125, .web_canvas => unreachable,
.web_canvas => unreachable, },
}, .ascent = 12.3046875,
.ascent = 12.3046875, .descent = -3.6953125,
.descent = -3.6953125, .line_gap = 0.0,
.line_gap = 0.0, .underline_position = -1.2265625,
.underline_position = -1.2265625, .underline_thickness = 1.2265625,
.underline_thickness = 1.2265625, .strikethrough_position = 6.15625,
.strikethrough_position = 6.15625, .strikethrough_thickness = 1.234375,
.strikethrough_thickness = 1.234375, .cap_height = 9.84375,
.cap_height = 9.84375, .ex_height = 7.3828125,
.ex_height = 7.3828125, .ascii_height = switch (options.backend) {
.ascii_height = switch (options.backend) { .freetype,
.freetype, .fontconfig_freetype,
.fontconfig_freetype, .coretext_freetype,
.coretext_freetype, => 18.0625,
=> 18.0625, .coretext,
.coretext, .coretext_harfbuzz,
.coretext_harfbuzz, .coretext_noshape,
.coretext_noshape, => 16.0,
=> 16.0, .web_canvas => unreachable,
.web_canvas => unreachable, },
}, };
}, narrowMetrics); const wideMetricsExpected = font.Metrics.FaceMetrics{
try std.testing.expectEqual(font.Metrics.FaceMetrics{ .px_per_em = 16.0,
.px_per_em = 16.0, .cell_width = switch (options.backend) {
.cell_width = switch (options.backend) { .freetype,
.freetype, .fontconfig_freetype,
.fontconfig_freetype, .coretext_freetype,
.coretext_freetype, => 10.0,
=> 10.0, .coretext,
.coretext, .coretext_harfbuzz,
.coretext_harfbuzz, .coretext_noshape,
.coretext_noshape, => 9.6,
=> 9.6, .web_canvas => unreachable,
.web_canvas => unreachable, },
}, .ascent = 14.72,
.ascent = 14.72, .descent = -3.52,
.descent = -3.52, .line_gap = 1.6,
.line_gap = 1.6, .underline_position = -1.6,
.underline_position = -1.6, .underline_thickness = 0.8,
.underline_thickness = 0.8, .strikethrough_position = 4.24,
.strikethrough_position = 4.24, .strikethrough_thickness = 0.8,
.strikethrough_thickness = 0.8, .cap_height = 11.36,
.cap_height = 11.36, .ex_height = 8.48,
.ex_height = 8.48, .ascii_height = switch (options.backend) {
.ascii_height = switch (options.backend) { .freetype,
.freetype, .fontconfig_freetype,
.fontconfig_freetype, .coretext_freetype,
.coretext_freetype, => 16.0,
=> 16.0, .coretext,
.coretext, .coretext_harfbuzz,
.coretext_harfbuzz, .coretext_noshape,
.coretext_noshape, => 15.472000000000001,
=> 15.472000000000001, .web_canvas => unreachable,
.web_canvas => unreachable, },
}, };
}, wideMetrics);
inline for (
.{ narrowMetricsExpected, wideMetricsExpected },
.{ narrowMetrics, wideMetrics },
) |metricsExpected, metricsActual| {
inline for (@typeInfo(font.Metrics.FaceMetrics).@"struct".fields) |field| {
const expected = @field(metricsExpected, field.name);
const actual = @field(metricsActual, field.name);
// Unwrap optional fields
const expectedValue, const actualValue = unwrap: switch (@typeInfo(field.type)) {
.optional => |Tinfo| {
if (expected) |expectedValue| {
const actualValue = actual orelse std.math.nan(Tinfo.child);
break :unwrap .{ expectedValue, actualValue };
}
// Null values can be compared directly
try std.testing.expectEqual(expected, actual);
continue;
},
else => break :unwrap .{ expected, actual },
};
// All non-null values are floats
const eps = std.math.floatEps(@TypeOf(actualValue - expectedValue));
try std.testing.expectApproxEqRel(
expectedValue,
actualValue,
std.math.sqrt(eps),
);
}
} }
// Verify estimated metrics. icWidth() should equal the smaller of // Verify estimated metrics. icWidth() should equal the smaller of