-
Notifications
You must be signed in to change notification settings - Fork 2
Open
Description
A test in my project reports a memory leak in zdotenv.
Error:
[gpa] (err): memory address 0x7f5bfe2e0000 leaked:
/home/vo/.zvm/0.14.1/lib/std/mem/Allocator.zig:423:40: 0x106a787 in dupe__anon_3850 (test)
const new_buf = try allocator.alloc(T, m.len);
^
/home/vo/.cache/zig/p/zdotenv-0.0.0-AAAAAFEzAAD075plCeAiLUlPjxTYnzVmel3KKzK_RsCp/src/parser.zig:36:50: 0x112e492 in parse (test)
const d_val = try self.allocator.dupe(u8, value);
^
/home/vo/.cache/zig/p/zdotenv-0.0.0-AAAAAFEzAAD075plCeAiLUlPjxTYnzVmel3KKzK_RsCp/src/dotenv.zig:87:39: 0x112f11c in parseAndLoadEnv (test)
var env_map = try parser.parse();
^
/home/vo/.cache/zig/p/zdotenv-0.0.0-AAAAAFEzAAD075plCeAiLUlPjxTYnzVmel3KKzK_RsCp/src/dotenv.zig:80:33: 0x112fe0d in load (test)
try self.parseAndLoadEnv();
^
/home/vo/zig-blog/lib/src/env.zig:39:20: 0x11329dd in loadEnv__anon_32367 (test)
try dotenv.load();
^
/home/vo/zig-blog/lib/src/env.zig:101:28: 0x1134ceb in test.loadEnv functionality (test)
const env = try loadEnv(Env, testing_allocator);
^
/home/vo/.zvm/0.14.1/lib/compiler/test_runner.zig:126:29: 0x1104bda in mainServer (test)
test_fn.func() catch |err| switch (err) {
^
...
The use case tested is basic, here is the code:
const builtin = @import("builtin");
const std = @import("std");
const utils = @import("utils.zig");
const zdotenv = @import("zdotenv");
const Allocator = std.mem.Allocator;
const eql = std.mem.eql;
const expect = std.testing.expect;
const expectEqualStrings = std.testing.expectEqualStrings;
const getCwd = std.process.getCwd;
const getEnvVarOwned = std.process.getEnvVarOwned;
const parseFloat = std.fmt.parseFloat;
const parseInt = std.fmt.parseInt;
const printFileName = utils.printFileName;
const printTestName = utils.printTestName;
const testing_allocator = std.testing.allocator;
const Zdotenv = zdotenv.Zdotenv;
pub const Env = struct {
STRING_VAR: []const u8 = "foo",
INT_VAR: i32 = 123,
BOOL_VAR: bool = true,
FLOAT_VAR: f32 = 1.23,
DOUBLE_VAR: f64 = 1.23,
CHAR_VAR: u8 = 'a',
};
pub fn loadEnv(comptime T: type, allocator: Allocator) !T {
var env = T{}; // Start with defaults
var dotenv = try Zdotenv.init(allocator);
defer dotenv.deinit();
// Load .env file using zdotenv, print a warning if it doesn't exist
try dotenv.load();
// Load .env.testing if we're in a test context, print a warning if it doesn't exist
if (builtin.is_test) {
var path_buffer: [std.fs.max_path_bytes]u8 = undefined;
const cwd = try getCwd(path_buffer[0..]);
const absolute_path = try std.fmt.allocPrint(allocator, "{s}/.env.testing", .{cwd});
defer allocator.free(absolute_path);
try dotenv.loadFromFile(absolute_path);
}
inline for (@typeInfo(T).@"struct".fields) |field| {
const value = getEnvVarOwned(allocator, field.name) catch |err| {
if (err == error.EnvironmentVariableNotFound) {
comptime continue;
}
return err;
};
// getEnvVarOwned() leaves the caller responsibility to free the value, so we do that here.
defer allocator.free(value);
// Parse value based on field.type and set it
@field(env, field.name) = try parseValue(field.type, value, allocator);
}
return env;
}
fn parseValue(comptime T: type, value: []const u8, allocator: Allocator) !T {
switch (T) {
[]const u8 => {
// For strings, we need to duplicate since the env var is freed
return try allocator.dupe(u8, value);
},
i32, i64, i16, i8 => {
return try parseInt(T, value, 10);
},
f32, f64 => {
return try parseFloat(T, value);
},
bool => {
return eql(u8, value, "true") or eql(u8, value, "1");
},
u8 => {
return if (value.len > 0) value[0] else 0;
},
else => @compileError("Unsupported type: " ++ @typeName(T)),
}
}
test {
printFileName(@src());
}
test "loadEnv functionality" {
printTestName(@src());
// Load environment into struct (includes ".env" and ".env.testing" file loading)
const env = try loadEnv(Env, testing_allocator);
// Verify that .env.testing values are loaded (not .env values)
try expectEqualStrings("testing_string", env.STRING_VAR);
try expect(env.INT_VAR == 123);
try expect(env.BOOL_VAR == true);
try expect(env.FLOAT_VAR == 1.23);
try expect(env.DOUBLE_VAR == 1.234);
try expect(env.CHAR_VAR == 'T');
}
Metadata
Metadata
Assignees
Labels
No labels