avoid output under test

This commit is contained in:
Emil Lerch 2026-05-19 13:44:37 -07:00
parent 6c32e94ab4
commit 858cc6fad5

View file

@ -10,11 +10,24 @@
/// - Success rate for a given spending level /// - Success rate for a given spending level
/// - Percentile bands of portfolio value at each year (for charting) /// - Percentile bands of portfolio value at each year (for charting)
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin");
const log = std.log.scoped(.projections); const log = std.log.scoped(.projections);
const shiller = @import("../data/shiller.zig"); const shiller = @import("../data/shiller.zig");
const srf = @import("srf"); const srf = @import("srf");
const Date = @import("../Date.zig"); const Date = @import("../Date.zig");
/// `log.warn` wrapper that no-ops under `zig build test`. Used for
/// validation warnings emitted while parsing user-supplied
/// `projections.srf` records: the test suite intentionally feeds
/// invalid inputs to `validRetirementTarget` and the config-loader
/// to verify they're rejected, but the resulting stderr noise
/// pollutes test output. Keep using `log.warn` directly when the
/// warning is interesting in tests too.
fn warnUser(comptime fmt: []const u8, args: anytype) void {
if (builtin.is_test) return;
log.warn(fmt, args);
}
// Life events // Life events
/// A resolved event ready for the simulation loop. All age-based timing /// A resolved event ready for the simulation loop. All age-based timing
@ -522,7 +535,7 @@ pub fn parseProjectionsConfig(data: ?[]const u8) UserConfig {
if (amt >= 0) { if (amt >= 0) {
config.annual_contribution = amt; config.annual_contribution = amt;
} else { } else {
log.warn("projections: annual_contribution must be >= 0 (got {d}); ignoring record", .{amt}); warnUser("projections: annual_contribution must be >= 0 (got {d}); ignoring record", .{amt});
} }
} }
if (c.contribution_inflation_adjusted) |b| { if (c.contribution_inflation_adjusted) |b| {
@ -532,7 +545,7 @@ pub fn parseProjectionsConfig(data: ?[]const u8) UserConfig {
if (amt >= 0) { if (amt >= 0) {
config.target_spending = amt; config.target_spending = amt;
} else { } else {
log.warn("projections: target_spending must be >= 0 (got {d}); ignoring record", .{amt}); warnUser("projections: target_spending must be >= 0 (got {d}); ignoring record", .{amt});
} }
} }
if (c.target_spending_inflation_adjusted) |b| { if (c.target_spending_inflation_adjusted) |b| {
@ -580,7 +593,7 @@ pub fn parseProjectionsConfig(data: ?[]const u8) UserConfig {
// fall back to the default rule. Logged as a warning so the // fall back to the default rule. Logged as a warning so the
// user knows their override was ignored. // user knows their override was ignored.
if (annotation_count > 1) { if (annotation_count > 1) {
log.warn("projections: retirement_target set on multiple horizons; ignoring all annotations and using default promotion rule", .{}); warnUser("projections: retirement_target set on multiple horizons; ignoring all annotations and using default promotion rule", .{});
config.horizon_targets = @splat(0); config.horizon_targets = @splat(0);
config.horizon_age_targets = @splat(0); config.horizon_age_targets = @splat(0);
} }
@ -596,7 +609,7 @@ pub fn parseProjectionsConfig(data: ?[]const u8) UserConfig {
fn validRetirementTarget(raw: ?u8) ?u8 { fn validRetirementTarget(raw: ?u8) ?u8 {
const v = raw orelse return null; const v = raw orelse return null;
if (v == 90 or v == 95 or v == 99) return v; if (v == 90 or v == 95 or v == 99) return v;
log.warn("projections: retirement_target must be 90, 95, or 99 (got {d}); annotation ignored", .{v}); warnUser("projections: retirement_target must be 90, 95, or 99 (got {d}); annotation ignored", .{v});
return null; return null;
} }