mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
config: theme loading unit tests
This commit is contained in:
@ -2675,6 +2675,8 @@ fn loadTheme(self: *Config, theme: []const u8) !void {
|
|||||||
self.* = new_config;
|
self.* = new_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Call this once after you are done setting configuration. This
|
||||||
|
/// is idempotent but will waste memory if called multiple times.
|
||||||
pub fn finalize(self: *Config) !void {
|
pub fn finalize(self: *Config) !void {
|
||||||
const alloc = self._arena.?.allocator();
|
const alloc = self._arena.?.allocator();
|
||||||
|
|
||||||
@ -2720,7 +2722,9 @@ pub fn finalize(self: *Config) !void {
|
|||||||
// to look up defaults which is kind of expensive. We only do this
|
// to look up defaults which is kind of expensive. We only do this
|
||||||
// on desktop.
|
// on desktop.
|
||||||
const wd_home = std.mem.eql(u8, "home", wd);
|
const wd_home = std.mem.eql(u8, "home", wd);
|
||||||
if (comptime !builtin.target.isWasm()) {
|
if ((comptime !builtin.target.isWasm()) and
|
||||||
|
(comptime !builtin.is_test))
|
||||||
|
{
|
||||||
if (self.command == null or wd_home) command: {
|
if (self.command == null or wd_home) command: {
|
||||||
// First look up the command using the SHELL env var if needed.
|
// First look up the command using the SHELL env var if needed.
|
||||||
// We don't do this in flatpak because SHELL in Flatpak is always
|
// We don't do this in flatpak because SHELL in Flatpak is always
|
||||||
@ -2790,7 +2794,9 @@ pub fn finalize(self: *Config) !void {
|
|||||||
if (std.mem.eql(u8, wd, "inherit")) self.@"working-directory" = null;
|
if (std.mem.eql(u8, wd, "inherit")) self.@"working-directory" = null;
|
||||||
|
|
||||||
// Default our click interval
|
// Default our click interval
|
||||||
if (self.@"click-repeat-interval" == 0) {
|
if (self.@"click-repeat-interval" == 0 and
|
||||||
|
(comptime !builtin.is_test))
|
||||||
|
{
|
||||||
self.@"click-repeat-interval" = internal_os.clickInterval() orelse 500;
|
self.@"click-repeat-interval" = internal_os.clickInterval() orelse 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3019,82 +3025,6 @@ pub const ChangeIterator = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const TestIterator = struct {
|
|
||||||
data: []const []const u8,
|
|
||||||
i: usize = 0,
|
|
||||||
|
|
||||||
pub fn next(self: *TestIterator) ?[]const u8 {
|
|
||||||
if (self.i >= self.data.len) return null;
|
|
||||||
const result = self.data[self.i];
|
|
||||||
self.i += 1;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
test "parse hook: invalid command" {
|
|
||||||
const testing = std.testing;
|
|
||||||
var cfg = try Config.default(testing.allocator);
|
|
||||||
defer cfg.deinit();
|
|
||||||
const alloc = cfg._arena.?.allocator();
|
|
||||||
|
|
||||||
var it: TestIterator = .{ .data = &.{"foo"} };
|
|
||||||
try testing.expect(try cfg.parseManuallyHook(alloc, "--command", &it));
|
|
||||||
try testing.expect(cfg.command == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "parse e: command only" {
|
|
||||||
const testing = std.testing;
|
|
||||||
var cfg = try Config.default(testing.allocator);
|
|
||||||
defer cfg.deinit();
|
|
||||||
const alloc = cfg._arena.?.allocator();
|
|
||||||
|
|
||||||
var it: TestIterator = .{ .data = &.{"foo"} };
|
|
||||||
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
|
|
||||||
try testing.expectEqualStrings("foo", cfg.@"initial-command".?);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "parse e: command and args" {
|
|
||||||
const testing = std.testing;
|
|
||||||
var cfg = try Config.default(testing.allocator);
|
|
||||||
defer cfg.deinit();
|
|
||||||
const alloc = cfg._arena.?.allocator();
|
|
||||||
|
|
||||||
var it: TestIterator = .{ .data = &.{ "echo", "foo", "bar baz" } };
|
|
||||||
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
|
|
||||||
try testing.expectEqualStrings("echo foo bar baz", cfg.@"initial-command".?);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "clone default" {
|
|
||||||
const testing = std.testing;
|
|
||||||
const alloc = testing.allocator;
|
|
||||||
|
|
||||||
var source = try Config.default(alloc);
|
|
||||||
defer source.deinit();
|
|
||||||
var dest = try source.clone(alloc);
|
|
||||||
defer dest.deinit();
|
|
||||||
|
|
||||||
// Should have no changes
|
|
||||||
var it = source.changeIterator(&dest);
|
|
||||||
try testing.expectEqual(@as(?Key, null), it.next());
|
|
||||||
|
|
||||||
// I want to do this but this doesn't work (the API doesn't work)
|
|
||||||
// try testing.expectEqualDeep(dest, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "changed" {
|
|
||||||
const testing = std.testing;
|
|
||||||
const alloc = testing.allocator;
|
|
||||||
|
|
||||||
var source = try Config.default(alloc);
|
|
||||||
defer source.deinit();
|
|
||||||
var dest = try source.clone(alloc);
|
|
||||||
defer dest.deinit();
|
|
||||||
dest.@"font-thicken" = true;
|
|
||||||
|
|
||||||
try testing.expect(source.changed(&dest, .@"font-thicken"));
|
|
||||||
try testing.expect(!source.changed(&dest, .@"font-size"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A config-specific helper to determine if two values of the same
|
/// A config-specific helper to determine if two values of the same
|
||||||
/// type are equal. This isn't the same as std.mem.eql or std.testing.equals
|
/// type are equal. This isn't the same as std.mem.eql or std.testing.equals
|
||||||
/// because we expect structs to implement their own equality.
|
/// because we expect structs to implement their own equality.
|
||||||
@ -5024,3 +4954,146 @@ test "test entryFormatter" {
|
|||||||
try p.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
try p.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||||
try std.testing.expectEqualStrings("a = 584y 49w 23h 34m 33s 709ms 551µs 615ns\n", buf.items);
|
try std.testing.expectEqualStrings("a = 584y 49w 23h 34m 33s 709ms 551µs 615ns\n", buf.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TestIterator = struct {
|
||||||
|
data: []const []const u8,
|
||||||
|
i: usize = 0,
|
||||||
|
|
||||||
|
pub fn next(self: *TestIterator) ?[]const u8 {
|
||||||
|
if (self.i >= self.data.len) return null;
|
||||||
|
const result = self.data[self.i];
|
||||||
|
self.i += 1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test "parse hook: invalid command" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var cfg = try Config.default(testing.allocator);
|
||||||
|
defer cfg.deinit();
|
||||||
|
const alloc = cfg._arena.?.allocator();
|
||||||
|
|
||||||
|
var it: TestIterator = .{ .data = &.{"foo"} };
|
||||||
|
try testing.expect(try cfg.parseManuallyHook(alloc, "--command", &it));
|
||||||
|
try testing.expect(cfg.command == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parse e: command only" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var cfg = try Config.default(testing.allocator);
|
||||||
|
defer cfg.deinit();
|
||||||
|
const alloc = cfg._arena.?.allocator();
|
||||||
|
|
||||||
|
var it: TestIterator = .{ .data = &.{"foo"} };
|
||||||
|
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
|
||||||
|
try testing.expectEqualStrings("foo", cfg.@"initial-command".?);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parse e: command and args" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var cfg = try Config.default(testing.allocator);
|
||||||
|
defer cfg.deinit();
|
||||||
|
const alloc = cfg._arena.?.allocator();
|
||||||
|
|
||||||
|
var it: TestIterator = .{ .data = &.{ "echo", "foo", "bar baz" } };
|
||||||
|
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
|
||||||
|
try testing.expectEqualStrings("echo foo bar baz", cfg.@"initial-command".?);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "clone default" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
var source = try Config.default(alloc);
|
||||||
|
defer source.deinit();
|
||||||
|
var dest = try source.clone(alloc);
|
||||||
|
defer dest.deinit();
|
||||||
|
|
||||||
|
// Should have no changes
|
||||||
|
var it = source.changeIterator(&dest);
|
||||||
|
try testing.expectEqual(@as(?Key, null), it.next());
|
||||||
|
|
||||||
|
// I want to do this but this doesn't work (the API doesn't work)
|
||||||
|
// try testing.expectEqualDeep(dest, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "changed" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
var source = try Config.default(alloc);
|
||||||
|
defer source.deinit();
|
||||||
|
var dest = try source.clone(alloc);
|
||||||
|
defer dest.deinit();
|
||||||
|
dest.@"font-thicken" = true;
|
||||||
|
|
||||||
|
try testing.expect(source.changed(&dest, .@"font-thicken"));
|
||||||
|
try testing.expect(!source.changed(&dest, .@"font-size"));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "theme loading" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var arena = ArenaAllocator.init(alloc);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc_arena = arena.allocator();
|
||||||
|
|
||||||
|
// Setup our test theme
|
||||||
|
var td = try internal_os.TempDir.init();
|
||||||
|
defer td.deinit();
|
||||||
|
{
|
||||||
|
var file = try td.dir.createFile("theme", .{});
|
||||||
|
defer file.close();
|
||||||
|
try file.writer().writeAll(@embedFile("testdata/theme_simple"));
|
||||||
|
}
|
||||||
|
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||||
|
const path = try td.dir.realpath("theme", &path_buf);
|
||||||
|
|
||||||
|
var cfg = try Config.default(alloc);
|
||||||
|
defer cfg.deinit();
|
||||||
|
var it: TestIterator = .{ .data = &.{
|
||||||
|
try std.fmt.allocPrint(alloc_arena, "--theme={s}", .{path}),
|
||||||
|
} };
|
||||||
|
try cfg.loadIter(alloc, &it);
|
||||||
|
try cfg.finalize();
|
||||||
|
|
||||||
|
try testing.expectEqual(Color{
|
||||||
|
.r = 0x12,
|
||||||
|
.g = 0x3A,
|
||||||
|
.b = 0xBC,
|
||||||
|
}, cfg.background);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "theme priority is lower than config" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var arena = ArenaAllocator.init(alloc);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc_arena = arena.allocator();
|
||||||
|
|
||||||
|
// Setup our test theme
|
||||||
|
var td = try internal_os.TempDir.init();
|
||||||
|
defer td.deinit();
|
||||||
|
{
|
||||||
|
var file = try td.dir.createFile("theme", .{});
|
||||||
|
defer file.close();
|
||||||
|
try file.writer().writeAll(@embedFile("testdata/theme_simple"));
|
||||||
|
}
|
||||||
|
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||||
|
const path = try td.dir.realpath("theme", &path_buf);
|
||||||
|
|
||||||
|
var cfg = try Config.default(alloc);
|
||||||
|
defer cfg.deinit();
|
||||||
|
var it: TestIterator = .{ .data = &.{
|
||||||
|
"--background=#ABCDEF",
|
||||||
|
try std.fmt.allocPrint(alloc_arena, "--theme={s}", .{path}),
|
||||||
|
} };
|
||||||
|
try cfg.loadIter(alloc, &it);
|
||||||
|
try cfg.finalize();
|
||||||
|
|
||||||
|
try testing.expectEqual(Color{
|
||||||
|
.r = 0xAB,
|
||||||
|
.g = 0xCD,
|
||||||
|
.b = 0xEF,
|
||||||
|
}, cfg.background);
|
||||||
|
}
|
||||||
|
2
src/config/testdata/theme_simple
vendored
Normal file
2
src/config/testdata/theme_simple
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# A simple theme
|
||||||
|
background = #123ABC
|
@ -112,7 +112,6 @@ pub fn open(
|
|||||||
path: []const u8,
|
path: []const u8,
|
||||||
file: std.fs.File,
|
file: std.fs.File,
|
||||||
} {
|
} {
|
||||||
|
|
||||||
// Absolute themes are loaded a different path.
|
// Absolute themes are loaded a different path.
|
||||||
if (std.fs.path.isAbsolute(theme)) {
|
if (std.fs.path.isAbsolute(theme)) {
|
||||||
const file: std.fs.File = try openAbsolute(
|
const file: std.fs.File = try openAbsolute(
|
||||||
|
Reference in New Issue
Block a user