Skip to content

Commit 9971d81

Browse files
committed
uname: test uname using system abstraction
1 parent ed3c6bf commit 9971d81

File tree

5 files changed

+132
-15
lines changed

5 files changed

+132
-15
lines changed

src/commands/uname.zig

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33

44
/// Is this command enabled for the current target?
55
pub const enabled: bool = switch (shared.target_os) {
6-
.linux => true,
7-
.macos => true,
6+
.linux, .macos => true,
87
.windows => false,
98
};
109

@@ -46,54 +45,53 @@ const impl = struct {
4645
const z: tracy.Zone = .begin(.{ .src = @src(), .name = command.name });
4746
defer z.end();
4847

49-
_ = system;
50-
5148
const options = try parseArguments(allocator, io, args, exe_path);
5249
log.debug("{}", .{options});
5350

54-
return performUname(allocator, io, options);
51+
return performUname(allocator, io, system, options);
5552
}
5653

5754
fn performUname(
5855
allocator: std.mem.Allocator,
5956
io: IO,
57+
system: System,
6058
options: UnameOptions,
6159
) !void {
6260
const z: tracy.Zone = .begin(.{ .src = @src(), .name = "perform uname" });
6361
defer z.end();
6462

6563
_ = allocator;
6664

67-
const utsname = std.posix.uname();
65+
const uname = system.uname();
6866

6967
var any_printed = false;
7068

7169
if (options.kernel_name) {
72-
try io.stdoutWriteAll(&utsname.sysname);
70+
try io.stdoutWriteAll(std.mem.sliceTo(&uname.sysname, 0));
7371
any_printed = true;
7472
}
7573

7674
if (options.node_name) {
7775
if (any_printed) try io.stdoutWriteByte(' ');
78-
try io.stdoutWriteAll(&utsname.nodename);
76+
try io.stdoutWriteAll(std.mem.sliceTo(&uname.nodename, 0));
7977
any_printed = true;
8078
}
8179

8280
if (options.kernel_release) {
8381
if (any_printed) try io.stdoutWriteByte(' ');
84-
try io.stdoutWriteAll(&utsname.release);
82+
try io.stdoutWriteAll(std.mem.sliceTo(&uname.release, 0));
8583
any_printed = true;
8684
}
8785

8886
if (options.kernel_version) {
8987
if (any_printed) try io.stdoutWriteByte(' ');
90-
try io.stdoutWriteAll(&utsname.version);
88+
try io.stdoutWriteAll(std.mem.sliceTo(&uname.version, 0));
9189
any_printed = true;
9290
}
9391

9492
if (options.machine) {
9593
if (any_printed) try io.stdoutWriteByte(' ');
96-
try io.stdoutWriteAll(&utsname.machine);
94+
try io.stdoutWriteAll(std.mem.sliceTo(&uname.machine, 0));
9795
any_printed = true;
9896
}
9997

@@ -117,13 +115,13 @@ const impl = struct {
117115

118116
if (options.os) {
119117
if (any_printed) try io.stdoutWriteByte(' ');
120-
try io.stdoutWriteAll(&utsname.sysname);
118+
try io.stdoutWriteAll(std.mem.sliceTo(&uname.sysname, 0));
121119
any_printed = true;
122120
}
123121

124122
if (target_has_domainname and options.domainname) {
125123
if (any_printed) try io.stdoutWriteByte(' ');
126-
try io.stdoutWriteAll(&utsname.domainname);
124+
try io.stdoutWriteAll(std.mem.sliceTo(&uname.domainname, 0));
127125
any_printed = true;
128126
}
129127

@@ -389,8 +387,29 @@ const impl = struct {
389387
try command.testVersion();
390388
}
391389

392-
// TODO: How do we test this without introducing the amount of complexity that https://github.com/leecannon/zsw does?
393-
// https://github.com/leecannon/zig-coreutils/issues/7
390+
test "uname" {
391+
var stdout = std.ArrayList(u8).init(std.testing.allocator);
392+
defer stdout.deinit();
393+
394+
try command.testExecute(&.{"-a"}, .{
395+
.stdout = stdout.writer().any(),
396+
.system_description = .{
397+
.uname = .{
398+
.sysname = "Linux",
399+
.nodename = "node",
400+
.release = "5.15.0-100-generic",
401+
.version = "Some version",
402+
.machine = "x86_64",
403+
.domainname = null,
404+
},
405+
},
406+
});
407+
408+
try std.testing.expectEqualStrings(
409+
"Linux node 5.15.0-100-generic Some version x86_64 Linux (none)\n",
410+
stdout.items,
411+
);
412+
}
394413
};
395414

396415
const Arg = @import("../Arg.zig");

src/system/System.zig

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,33 @@ pub const UserId = blk: {
6464
};
6565
};
6666

67+
pub const Uname = if (is_test) struct {
68+
sysname: [64:0]u8,
69+
nodename: [64:0]u8,
70+
release: [64:0]u8,
71+
version: [64:0]u8,
72+
machine: [64:0]u8,
73+
domainname: [64:0]u8,
74+
} else switch (target_os) {
75+
.linux, .macos => std.posix.utsname,
76+
.windows => @compileError("uname not supported on windows"),
77+
};
78+
79+
pub inline fn uname(system: System) Uname {
80+
if (is_test) {
81+
if (system._backend.uname) |uname_backend| {
82+
return uname_backend.uname();
83+
}
84+
85+
@panic("`uname` called with no uname backend configured");
86+
}
87+
88+
return switch (target_os) {
89+
.linux, .macos => std.posix.uname(),
90+
.windows => @panic("uname not supported on windows"),
91+
};
92+
}
93+
6794
pub const Dir = struct {
6895
_data: Data,
6996

src/system/backend/Description.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
time: TimeSource = .host,
55
file_system: ?*FileSystemDescription = null,
66
user_group: ?UserGroupDescription = null,
7+
uname: ?UnameDescription = null,
78

89
pub const FileSystemDescription = @import("FileSystem.zig").FileSystemDescription;
910
pub const TimeSource = @import("Time.zig").Source;
1011
pub const UserGroupDescription = @import("UserGroup.zig").UserGroupDescription;
12+
pub const UnameDescription = @import("Uname.zig").UnameDescription;

src/system/backend/TestBackend.zig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ allocator: std.mem.Allocator,
88
time: Time,
99
file_system: ?FileSystem,
1010
user_group: ?UserGroup,
11+
uname: ?Uname,
1112

1213
pub fn create(allocator: std.mem.Allocator, description: Description) !*TestBackend {
1314
const self = try allocator.create(TestBackend);
@@ -18,6 +19,7 @@ pub fn create(allocator: std.mem.Allocator, description: Description) !*TestBack
1819
.time = .{ .source = description.time },
1920
.file_system = null,
2021
.user_group = null,
22+
.uname = null,
2123
};
2224

2325
if (description.user_group) |ug_description| {
@@ -30,6 +32,11 @@ pub fn create(allocator: std.mem.Allocator, description: Description) !*TestBack
3032
try self.file_system.?.init(self, fs_description);
3133
}
3234

35+
if (description.uname) |uname_description| {
36+
self.uname = @as(Uname, undefined);
37+
self.uname.?.init(uname_description);
38+
}
39+
3340
return self;
3441
}
3542

@@ -43,6 +50,7 @@ pub const Description = @import("Description.zig");
4350
pub const FileSystem = @import("FileSystem.zig");
4451
const Time = @import("Time.zig");
4552
const UserGroup = @import("UserGroup.zig");
53+
const Uname = @import("Uname.zig");
4654
const System = @import("../System.zig");
4755

4856
const log = std.log.scoped(.system_test_backend);

src/system/backend/Uname.zig

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// SPDX-License-Identifier: MIT
2+
// SPDX-FileCopyrightText: 2025 Lee Cannon <leecannon@leecannon.xyz>
3+
4+
const Uname = @This();
5+
6+
uname_source: System.Uname,
7+
8+
pub fn init(self: *Uname, description: UnameDescription) void {
9+
@memset(std.mem.asBytes(self), 0);
10+
11+
if (description.sysname.len > self.uname_source.sysname.len) {
12+
@panic("UnameDescription.sysname is too long");
13+
}
14+
@memcpy(self.uname_source.sysname[0..description.sysname.len], description.sysname);
15+
16+
if (description.nodename.len > self.uname_source.nodename.len) {
17+
@panic("UnameDescription.nodename is too long");
18+
}
19+
@memcpy(self.uname_source.nodename[0..description.nodename.len], description.nodename);
20+
21+
if (description.release.len > self.uname_source.release.len) {
22+
@panic("UnameDescription.release is too long");
23+
}
24+
@memcpy(self.uname_source.release[0..description.release.len], description.release);
25+
26+
if (description.version.len > self.uname_source.version.len) {
27+
@panic("UnameDescription.version is too long");
28+
}
29+
@memcpy(self.uname_source.version[0..description.version.len], description.version);
30+
31+
if (description.machine.len > self.uname_source.machine.len) {
32+
@panic("UnameDescription.machine is too long");
33+
}
34+
@memcpy(self.uname_source.machine[0..description.machine.len], description.machine);
35+
36+
if (description.domainname) |domainname| {
37+
if (domainname.len > self.uname_source.domainname.len) {
38+
@panic("UnameDescription.domainname is too long");
39+
}
40+
@memcpy(self.uname_source.domainname[0..domainname.len], domainname);
41+
} else {
42+
std.mem.copyForwards(u8, &self.uname_source.domainname, "(none)");
43+
}
44+
}
45+
46+
pub inline fn uname(self: Uname) System.Uname {
47+
return self.uname_source;
48+
}
49+
50+
pub const UnameDescription = struct {
51+
sysname: []const u8,
52+
nodename: []const u8,
53+
release: []const u8,
54+
version: []const u8,
55+
machine: []const u8,
56+
domainname: ?[]const u8,
57+
};
58+
59+
const System = @import("../System.zig");
60+
61+
const std = @import("std");

0 commit comments

Comments
 (0)