diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-14 15:16:55 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-14 15:16:55 +0200 |
| commit | 0b5d0eb92c5be2f0ddb2fa73990f31e5654e57fe (patch) | |
| tree | 358e331945b8206ed4a72703e3aedfe8ea7cdcdb /internal/kwcache/kwcache_test.go | |
| parent | 1d3f2d1e4c59867024470d3444e12698b7ebb22e (diff) | |
| download | krino-0.0.5.tar.gz krino-0.0.5.zip | |
krino: 0.0.5 — keyword cache, t and d in reviewv0.0.5
Diffstat (limited to 'internal/kwcache/kwcache_test.go')
| -rw-r--r-- | internal/kwcache/kwcache_test.go | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/internal/kwcache/kwcache_test.go b/internal/kwcache/kwcache_test.go new file mode 100644 index 0000000..8395b2d --- /dev/null +++ b/internal/kwcache/kwcache_test.go @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package kwcache + +import ( + "os" + "path/filepath" + "reflect" + "testing" +) + +var ( + a = ID{Dev: 1, Ino: 10, Size: 100, MTime: 1000} + b = ID{Dev: 1, Ino: 11, Size: 200, MTime: 2000} +) + +func saved(t *testing.T, c *Cache, present ...ID) string { + t.Helper() + path := filepath.Join(t.TempDir(), "sub", "dl.cache") + if err := c.Save(path, present); err != nil { + t.Fatal(err) + } + return path +} + +// TestLookupAfterReload: answers stored and saved come back after a Load +// with the same fingerprint, for any keywords the entry covers. +func TestLookupAfterReload(t *testing.T) { + c := New("fp") + c.Store(a, map[string]bool{"k:acme": true, "k:faktura": false, "k:nip": true}) + path := saved(t, c, a) + + got, err := Load(path, "fp") + if err != nil { + t.Fatal(err) + } + hits, ok := got.Lookup(a, []string{"k:faktura", "k:nip"}) + if !ok || !reflect.DeepEqual(hits, []bool{false, true}) { + t.Errorf("Lookup = %v, %v; want [false true], true", hits, ok) + } + if _, ok := got.Lookup(a, []string{"k:acme", "k:new"}); ok { + t.Error("a keyword the entry was never checked against must be a miss") + } + if _, ok := got.Lookup(b, []string{"k:acme"}); ok { + t.Error("a file never stored must be a miss") + } +} + +// TestLookupBeforeSave: what was stored in this run answers at once. +func TestLookupBeforeSave(t *testing.T) { + c := New("fp") + c.Store(a, map[string]bool{"k:acme": true}) + if hits, ok := c.Lookup(a, []string{"k:acme"}); !ok || !hits[0] { + t.Errorf("Lookup = %v, %v", hits, ok) + } +} + +// TestChangedFileMisses: a different size or modification time is a +// different ID, so nothing stored for the old one answers. +func TestChangedFileMisses(t *testing.T) { + c := New("fp") + c.Store(a, map[string]bool{"k:acme": true}) + for _, id := range []ID{{Dev: 1, Ino: 10, Size: 101, MTime: 1000}, {Dev: 1, Ino: 10, Size: 100, MTime: 1001}} { + if _, ok := c.Lookup(id, []string{"k:acme"}); ok { + t.Errorf("%+v answered from %+v's entry", id, a) + } + } +} + +// TestFingerprintMismatchDiscardsAll: a cache written under a different +// extractor fingerprint loads empty, without error. +func TestFingerprintMismatchDiscardsAll(t *testing.T) { + c := New("old") + c.Store(a, map[string]bool{"k:acme": true}) + path := saved(t, c, a) + got, err := Load(path, "new") + if err != nil { + t.Fatal(err) + } + if _, ok := got.Lookup(a, []string{"k:acme"}); ok { + t.Error("entry survived a fingerprint change") + } +} + +// TestSaveKeepsOnlyPresentFiles: entries for files not passed to Save, from +// this run or an earlier one, are dropped. +func TestSaveKeepsOnlyPresentFiles(t *testing.T) { + c := New("fp") + c.Store(a, map[string]bool{"k:acme": true}) + c.Store(b, map[string]bool{"k:acme": false}) + path := saved(t, c, a, b) + + next, err := Load(path, "fp") + if err != nil { + t.Fatal(err) + } + if err := next.Save(path, []ID{b}); err != nil { + t.Fatal(err) + } + last, err := Load(path, "fp") + if err != nil { + t.Fatal(err) + } + if _, ok := last.Lookup(a, []string{"k:acme"}); ok { + t.Error("a is gone from the directory but its entry was kept") + } + if hits, ok := last.Lookup(b, []string{"k:acme"}); !ok || hits[0] { + t.Errorf("b's entry lost across a reload: %v, %v", hits, ok) + } +} + +// TestStoreReplacesLoadedEntry: a file read again this run replaces what +// an earlier run stored for it. +func TestStoreReplacesLoadedEntry(t *testing.T) { + c := New("fp") + c.Store(a, map[string]bool{"k:acme": true}) + path := saved(t, c, a) + next, _ := Load(path, "fp") + next.Store(a, map[string]bool{"k:acme": false, "k:new": true}) + if hits, ok := next.Lookup(a, []string{"k:acme", "k:new"}); !ok || hits[0] || !hits[1] { + t.Errorf("Lookup = %v, %v; want the new entry", hits, ok) + } +} + +// TestSavePermissions: the directory is private and the file readable by +// its owner only, with no temporary file left behind. +func TestSavePermissions(t *testing.T) { + c := New("fp") + c.Store(a, map[string]bool{"k:acme": true}) + path := saved(t, c, a) + fi, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + if perm := fi.Mode().Perm(); perm != 0o600 { + t.Errorf("file mode %o, want 600", perm) + } + di, err := os.Stat(filepath.Dir(path)) + if err != nil { + t.Fatal(err) + } + if perm := di.Mode().Perm(); perm != 0o700 { + t.Errorf("directory mode %o, want 700", perm) + } + entries, _ := os.ReadDir(filepath.Dir(path)) + if len(entries) != 1 { + t.Errorf("directory holds %d entries, want only the cache", len(entries)) + } +} + +// TestLoadMissingAndCorrupt: no file is an empty cache and no error; an +// unreadable one is an empty cache and an error saying so. +func TestLoadMissingAndCorrupt(t *testing.T) { + dir := t.TempDir() + c, err := Load(filepath.Join(dir, "none.cache"), "fp") + if err != nil || c == nil { + t.Fatalf("missing: %v, %v", c, err) + } + bad := filepath.Join(dir, "bad.cache") + os.WriteFile(bad, []byte("{not json"), 0o600) + c, err = Load(bad, "fp") + if err == nil || c == nil { + t.Fatalf("corrupt: want an empty cache and an error, got %v, %v", c, err) + } + if _, ok := c.Lookup(a, []string{"k:acme"}); ok { + t.Error("corrupt cache answered") + } +} + +// TestSaveWithNothingWritesNothing: a cache that never held an entry, and +// has no file yet, creates none. +func TestSaveWithNothingWritesNothing(t *testing.T) { + path := filepath.Join(t.TempDir(), "sub", "dl.cache") + if err := New("fp").Save(path, []ID{a}); err != nil { + t.Fatal(err) + } + if _, err := os.Stat(filepath.Dir(path)); !os.IsNotExist(err) { + t.Errorf("Save created %s for an empty cache", filepath.Dir(path)) + } +} |
