156 lines
5.7 KiB
Go
156 lines
5.7 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/config"
|
|
"git.stormux.org/storm/cthulhu/internal/datetime"
|
|
"git.stormux.org/storm/cthulhu/internal/input"
|
|
"git.stormux.org/storm/cthulhu/internal/preferences"
|
|
)
|
|
|
|
func TestDateTimeFormats(t *testing.T) {
|
|
// datetime initializes libc's locale before tests run. Use a child process
|
|
// so these fixed English expectations do not depend on the build locale.
|
|
if os.Getenv("CTHULHU_DATE_TIME_TEST_CHILD") != "1" {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
command := exec.CommandContext(ctx, os.Args[0], "-test.run=^TestDateTimeFormats$", "-test.count=1")
|
|
command.Env = append(os.Environ(), "LC_ALL=C", "CTHULHU_DATE_TIME_TEST_CHILD=1")
|
|
if output, err := command.CombinedOutput(); err != nil {
|
|
t.Fatalf("C-locale date/time test: %v\n%s", err, output)
|
|
}
|
|
return
|
|
}
|
|
now := time.Date(2026, 9, 17, 19, 5, 0, 0, time.UTC)
|
|
for _, tc := range []struct {
|
|
command input.Command
|
|
format, want string
|
|
}{
|
|
{input.PresentTime, "locale", "19:05:00"},
|
|
{input.PresentTime, "12-hour", "07:05 PM"},
|
|
{input.PresentTime, "12-hour-seconds", "07:05:00 PM"},
|
|
{input.PresentTime, "24-hour-seconds", "19:05:00"},
|
|
{input.PresentTime, "24-hour-seconds-words", "19 hours, 05 minutes and 00 seconds"},
|
|
{input.PresentTime, "24-hour", "19:05"},
|
|
{input.PresentTime, "24-hour-words", "19 hours and 05 minutes"},
|
|
{input.PresentDate, "locale", "09/17/26"},
|
|
{input.PresentDate, "numbers-dm", "17/09"},
|
|
{input.PresentDate, "numbers-md", "09/17"},
|
|
{input.PresentDate, "numbers-dmy", "17/09/2026"},
|
|
{input.PresentDate, "numbers-mdy", "09/17/2026"},
|
|
{input.PresentDate, "numbers-ymd", "2026/09/17"},
|
|
{input.PresentDate, "full-dm", "Thursday, 17 September"},
|
|
{input.PresentDate, "full-md", "Thursday, September 17"},
|
|
{input.PresentDate, "full-dmy", "Thursday, 17 September, 2026"},
|
|
{input.PresentDate, "full-mdy", "Thursday, September 17, 2026"},
|
|
{input.PresentDate, "full-ymd", "2026. September 17, Thursday"},
|
|
{input.PresentDate, "abbreviated-dm", "Thu, 17 Sep"},
|
|
{input.PresentDate, "abbreviated-md", "Thu, Sep 17"},
|
|
{input.PresentDate, "abbreviated-dmy", "Thu, 17 Sep, 2026"},
|
|
{input.PresentDate, "abbreviated-mdy", "Thu, Sep 17, 2026"},
|
|
{input.PresentDate, "abbreviated-ymd", "2026. Sep 17, Thu"},
|
|
} {
|
|
settings := config.DefaultSettings()
|
|
if tc.command == input.PresentTime {
|
|
settings.TimeFormat = tc.format
|
|
} else {
|
|
settings.DateFormat = tc.format
|
|
}
|
|
queue := &fakeSpeechQueue{spoken: make(chan string, 1), interrupt: make(chan struct{}, 1)}
|
|
presentDateTime(queue, settings, tc.command, now)
|
|
if got := <-queue.spoken; got != tc.want {
|
|
t.Errorf("%s: %q want %q", tc.format, got, tc.want)
|
|
}
|
|
if len(queue.interrupt) != 1 {
|
|
t.Fatal("command did not interrupt")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDateTimeOptionCounts(t *testing.T) {
|
|
now := time.Date(2026, 9, 17, 19, 5, 0, 0, time.UTC)
|
|
timeOptions := datetime.TimeOptions(now)
|
|
dateOptions := datetime.DateOptions(now)
|
|
wantTime := []string{
|
|
"locale", "12-hour", "12-hour-seconds", "24-hour-seconds",
|
|
"24-hour-seconds-words", "24-hour", "24-hour-words",
|
|
}
|
|
wantDate := []string{
|
|
"locale", "numbers-dm", "numbers-md", "numbers-dmy", "numbers-mdy",
|
|
"numbers-ymd", "full-dm", "full-md", "full-dmy", "full-mdy",
|
|
"full-ymd", "abbreviated-dm", "abbreviated-md", "abbreviated-dmy",
|
|
"abbreviated-mdy", "abbreviated-ymd",
|
|
}
|
|
if len(timeOptions) != len(wantTime) {
|
|
t.Fatalf("time options: got %d, want %d", len(timeOptions), len(wantTime))
|
|
}
|
|
if len(dateOptions) != len(wantDate) {
|
|
t.Fatalf("date options: got %d, want %d", len(dateOptions), len(wantDate))
|
|
}
|
|
for _, test := range []struct {
|
|
name string
|
|
options []datetime.Option
|
|
expected []string
|
|
}{
|
|
{"time", timeOptions, wantTime},
|
|
{"date", dateOptions, wantDate},
|
|
} {
|
|
for index, option := range test.options {
|
|
if option.Value != test.expected[index] {
|
|
t.Fatalf("%s option %d = %q, want %q", test.name, index,
|
|
option.Value, test.expected[index])
|
|
}
|
|
}
|
|
}
|
|
for _, option := range append(timeOptions, dateOptions...) {
|
|
if option.Value == "" || option.Label == "" {
|
|
t.Fatalf("empty option: %+v", option)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDateTimePreferencesLiveAndSaved(t *testing.T) {
|
|
settings := config.DefaultSettings()
|
|
settings.Path = filepath.Join(t.TempDir(), "settings.toml")
|
|
live := &liveSettings{}
|
|
live.Store(settings)
|
|
worker := newPreferencesWorker(slog.New(slog.DiscardHandler), settings, nil)
|
|
worker.live = live
|
|
values := preferences.FromSettings(settings)
|
|
values.TimeFormat, values.DateFormat = "24-hour-words", "abbreviated-mdy"
|
|
response := worker.apply(context.Background(), preferences.Request{Values: values, Save: true, Close: true})
|
|
if !response.Accepted || !response.Close {
|
|
t.Fatalf("apply: %+v", response)
|
|
}
|
|
loaded, err := config.Load(settings.Path)
|
|
if err != nil || loaded.TimeFormat != "24-hour-words" || loaded.DateFormat != "abbreviated-mdy" {
|
|
t.Fatalf("saved formats: %+v %v", loaded, err)
|
|
}
|
|
queue := &fakeSpeechQueue{spoken: make(chan string, 1)}
|
|
presentDateTime(queue, live.Load(), input.PresentTime, time.Date(2026, 9, 17, 0, 1, 0, 0, time.Local))
|
|
if got := <-queue.spoken; got != "00 hours and 01 minutes" {
|
|
t.Fatalf("live time: %s", got)
|
|
}
|
|
for _, field := range []string{"time", "date"} {
|
|
bad := values
|
|
if field == "time" {
|
|
bad.TimeFormat = "invalid"
|
|
} else {
|
|
bad.DateFormat = "invalid"
|
|
}
|
|
if worker.apply(context.Background(), preferences.Request{Values: bad, Save: true}).Accepted {
|
|
t.Fatal("invalid format accepted")
|
|
}
|
|
}
|
|
if live.Load().TimeFormat != "24-hour-words" || live.Load().DateFormat != "abbreviated-mdy" {
|
|
t.Fatal("invalid draft changed live settings")
|
|
}
|
|
}
|