| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- package main
- import (
- "os"
- "path/filepath"
- "strings"
- "testing"
- )
- // Profiles and definitions are the tool's own files, and a binary installed with
- // `go install` sits in $GOPATH/bin where neither of them exists. They need a
- // per-user directory, and they need the same one on Linux, macOS and Windows,
- // which is what os.UserConfigDir answers and a hardcoded ~/.config does not.
- func TestDataDirectoriesAreTheUserDirectory(t *testing.T) {
- userDir := t.TempDir()
- t.Cleanup(stubUserConfigDir(t, userDir))
- t.Cleanup(func() { profilesDirOverride = "" })
- t.Cleanup(func() { definitionsDirOverride = "" })
- base := filepath.Join(userDir, "qmk-rgb-tool")
- for _, tc := range []struct {
- name string
- got string
- want string
- }{
- {"definitions", definitionsPath(), filepath.Join(base, "definitions")},
- {"profiles", profilesPath(), filepath.Join(base, "profiles")},
- } {
- if tc.got != tc.want {
- t.Errorf("%sPath() = %q, want %q", tc.name, tc.got, tc.want)
- }
- }
- }
- // The directories used to be searched: next to the executable, then the working
- // directory, then the user's. Both reasons for that are gone, and a test has to
- // say so, because the search is the kind of thing that comes back.
- //
- // A search path for a definition hides the file `keyboard fetch` just wrote,
- // because a definitions directory beside the binary exists for as long as the
- // binary is run from a checkout, and it wins. A search path for a profile finds a
- // same-named file in a checkout and loads that one instead of the one the user
- // saved, so which file `load <name>` reads depends on where the command is run.
- func TestDataDirectoriesAreNotSearched(t *testing.T) {
- userDir := t.TempDir()
- t.Cleanup(stubUserConfigDir(t, userDir))
- t.Cleanup(func() { profilesDirOverride = "" })
- t.Cleanup(func() { definitionsDirOverride = "" })
- base := filepath.Join(userDir, "qmk-rgb-tool")
- // A repository checkout with both directories beside the binary, and the same
- // two in the working directory, so every step of the old order would match.
- here := t.TempDir()
- for _, dir := range []string{here, filepath.Join(here, "sub")} {
- for _, name := range []string{"definitions", "profiles"} {
- if err := os.MkdirAll(filepath.Join(dir, name), 0o755); err != nil {
- t.Fatal(err)
- }
- }
- }
- t.Chdir(here)
- if got, want := definitionsPath(), filepath.Join(base, "definitions"); got != want {
- t.Errorf("definitionsPath() = %q, want %q", got, want)
- }
- if got, want := profilesPath(), filepath.Join(base, "profiles"); got != want {
- t.Errorf("profilesPath() = %q, want %q", got, want)
- }
- }
- // A profile written is a profile that reads back, which a search path broke: the
- // save went to the user's directory and the read stopped at a checkout's.
- func TestProfileIsReadBackFromWhereItIsWritten(t *testing.T) {
- userDir := t.TempDir()
- t.Cleanup(stubUserConfigDir(t, userDir))
- t.Cleanup(func() { profilesDirOverride = "" })
- // A checkout's profiles directory, holding a same-named file with different
- // content: if this one is loaded, the read is still searching.
- here := t.TempDir()
- if err := os.MkdirAll(filepath.Join(here, "profiles"), 0o755); err != nil {
- t.Fatal(err)
- }
- if err := os.WriteFile(filepath.Join(here, "profiles", "lava.json"),
- []byte(`{"name":"lava","version":1,"zones":{"Backlight":{"effect":"wave"}}}`), 0o644); err != nil {
- t.Fatal(err)
- }
- t.Chdir(here)
- if err := (&Profile{Name: "lava", Version: 1}).Save(); err != nil {
- t.Fatalf("Save(): %v", err)
- }
- if _, err := os.Stat(filepath.Join(here, "profiles", "lava.json")); err != nil {
- t.Fatalf("Save() did not write into the working directory's profiles: %v", err)
- }
- loaded, err := LoadProfile("lava")
- if err != nil {
- t.Fatalf("LoadProfile(lava) after Save(): %v", err)
- }
- if zone, ok := loaded.Zones["Backlight"]; ok && zone.Effect != "" {
- t.Errorf("loaded the checkout's profile: effect = %q, want the saved one, which has no zones", zone.Effect)
- }
- }
- // A profile that is not there says where it looked, because "not found" with no
- // path is the one error a user cannot act on.
- func TestMissingProfileNamesTheDirectory(t *testing.T) {
- userDir := t.TempDir()
- t.Cleanup(stubUserConfigDir(t, userDir))
- t.Cleanup(func() { profilesDirOverride = "" })
- _, err := LoadProfile("nope")
- if err == nil {
- t.Fatal("LoadProfile(nope) = nil error, want one")
- }
- if !strings.Contains(err.Error(), filepath.Join("qmk-rgb-tool", "profiles")) {
- t.Errorf("LoadProfile(nope) = %q, want it to name the directory", err)
- }
- }
- // A list of profiles has no duplicates to fold, because there is one directory
- // they come from. The names are still the ones on disk, without the extension.
- func TestProfilesAreListedFromTheUserDirectory(t *testing.T) {
- userDir := t.TempDir()
- t.Cleanup(stubUserConfigDir(t, userDir))
- t.Cleanup(func() { profilesDirOverride = "" })
- dir := filepath.Join(userDir, "qmk-rgb-tool", "profiles")
- if err := os.MkdirAll(dir, 0o755); err != nil {
- t.Fatal(err)
- }
- for _, n := range []string{"lava", "wave"} {
- if err := os.WriteFile(filepath.Join(dir, n+".json"), []byte("{}"), 0o644); err != nil {
- t.Fatal(err)
- }
- }
- if err := os.WriteFile(filepath.Join(dir, "notes.txt"), []byte(""), 0o644); err != nil {
- t.Fatal(err)
- }
- got, err := ListProfiles()
- if err != nil {
- t.Fatalf("ListProfiles(): %v", err)
- }
- if len(got) != 2 {
- t.Fatalf("ListProfiles() = %v, want lava and wave", got)
- }
- found := map[string]bool{}
- for _, n := range got {
- found[n] = true
- }
- if !found["lava"] || !found["wave"] {
- t.Errorf("ListProfiles() = %v, want lava and wave", got)
- }
- }
- // A directory that is not there is not an error: a user who has never saved a
- // profile has none, and `list` says so by printing nothing.
- func TestListingWithoutAProfilesDirectory(t *testing.T) {
- userDir := t.TempDir()
- t.Cleanup(stubUserConfigDir(t, userDir))
- t.Cleanup(func() { profilesDirOverride = "" })
- got, err := ListProfiles()
- if err != nil {
- t.Fatalf("ListProfiles(): %v, want no error for a directory that does not exist", err)
- }
- if len(got) != 0 {
- t.Errorf("ListProfiles() = %v, want none", got)
- }
- }
- // The user directory is reported so the commands can say where a file came from.
- func TestDataDirectoryIsReportedForTheUser(t *testing.T) {
- userDir := t.TempDir()
- t.Cleanup(stubUserConfigDir(t, userDir))
- t.Cleanup(func() { definitionsDirOverride = "" })
- got := definitionsPath()
- if got == "" {
- t.Fatal("definitionsPath() = \"\", want a path")
- }
- if !filepath.IsAbs(got) {
- t.Errorf("definitionsPath() = %q, want an absolute path so it can be printed", got)
- }
- if !strings.Contains(describeDataDir(got), "your user directory") {
- t.Errorf("describeDataDir(%q) = %q, want it marked as the user's", got, describeDataDir(got))
- }
- }
- // stubUserConfigDir points the platform's configuration directory at a temp one.
- func stubUserConfigDir(t *testing.T, dir string) func() {
- t.Helper()
- original := userConfigDir
- userConfigDir = func() (string, error) { return dir, nil }
- return func() { userConfigDir = original }
- }
|