1package tiles23import (4 "os"5 "path/filepath"6 "strings"7 "testing"8)910func TestLocalName(t *testing.T) {11 tests := []struct {12 name, volume, pyramid, want string13 }{14 {"the volume's own prefix comes off", "tunic", "tunic__world", "world"},15 {"a variant keeps what makes it one", "gta5", "gta5__los-santos__road", "los-santos__road"},16 {"an unprefixed pyramid is left alone", "tunic", "somebody-else", "somebody-else"},17 {"one underscore is not the separator", "tunic", "tunic_world", "tunic_world"},18 }19 for _, tt := range tests {20 t.Run(tt.name, func(t *testing.T) {21 if got := LocalName(tt.volume, tt.pyramid); got != tt.want {22 t.Errorf("LocalName(%q, %q) = %q, want %q", tt.volume, tt.pyramid, got, tt.want)23 }24 })25 }26}2728// TestStampPart pins the name a pyramid's derivation stamp weighs into a29// bundle's stamp under. It is one part per pyramid however many tiles it holds,30// which is what lets a rebuild notice an unchanged raster without reading one.31func TestStampPart(t *testing.T) {32 if got := StampPart("world"); got != "tiles/world" {33 t.Errorf("StampPart(\"world\") = %q", got)34 }35 if strings.ContainsAny(StampPart("world"), " \n") {36 t.Error("a part name may hold neither a space nor a newline; they separate the summed form")37 }38}3940func TestOpen(t *testing.T) {41 dir := t.TempDir()42 index := filepath.Join(dir, IndexName)43 write(t, index, `{"tileSize":256,"size":8192,"lenses":[44 {"sourcePath":"tunic/world/default-v2","assetPath":"tunic__world","stamp":"aaa",45 "minZoom":0,"maxZoom":7,"fullZoom":5,"sourceZoom":15,46 "grid":{"sourceZoom":13,"firstTile":4064},47 "formats":["jpg"],"interpolate":true,"background":"#000000"},48 {"sourcePath":"other/world/v1","assetPath":"tunic__world__aligned-other","stamp":"bbb",49 "alignedWith":"tunic/world/default-v2","name":"Other",50 "grid":{"sourceZoom":13,"firstTile":4064},"formats":["jpg"]}51 ]}`)52 write(t, filepath.Join(dir, "tunic__world", "0", "0", "0.jpg"), "a")53 write(t, filepath.Join(dir, "tunic__world", "10", "1", "2.jpg"), "b")54 write(t, filepath.Join(dir, "tunic__world", "2", "1", "2.jpg"), "c")5556 set, err := Open(index)57 if err != nil {58 t.Fatalf("open: %v", err)59 }60 if set.TileSize != 256 || set.Size != 8192 {61 t.Errorf("grid is %d/%d", set.TileSize, set.Size)62 }6364 native, ok := set.Native("tunic/world/default-v2")65 if !ok {66 t.Fatal("the native pyramid was not found")67 }68 if native.Name != "tunic__world" || native.Stamp != "aaa" || native.Window.FirstTile != 4064 {69 t.Errorf("native pyramid is %+v", native)70 }7172 aligned := set.Aligned("tunic/world/default-v2")73 if len(aligned) != 1 || aligned[0].LensName != "Other" {74 t.Errorf("aligned pyramids are %+v", aligned)75 }76 if _, ok := set.Native("nothing/here"); ok {77 t.Error("an underived tile set answered")78 }7980 t.Run("tiles come out in the order a bundle writes them", func(t *testing.T) {81 list, err := set.Tiles(native)82 if err != nil {83 t.Fatal(err)84 }85 want := []string{"0/0/0.jpg", "10/1/2.jpg", "2/1/2.jpg"}86 if len(list) != len(want) {87 t.Fatalf("%d tiles, want %d", len(list), len(want))88 }89 for i, name := range want {90 if list[i].Name != name {91 t.Errorf("tile %d is %q, want %q (a walk is lexical, so level 10 precedes level 2)",92 i, list[i].Name, name)93 }94 }95 })9697 t.Run("names", func(t *testing.T) {98 names := set.Names()99 if len(names) != 2 || names[0] != "tunic__world" {100 t.Errorf("names are %v", names)101 }102 })103}104105func TestOpenRefusals(t *testing.T) {106 tests := []struct {107 name, body, want string108 }{109 {"no grid", `{"lenses":[]}`, "declares no tile grid"},110 {"not JSON", `{`, "decode"},111 }112 for _, tt := range tests {113 t.Run(tt.name, func(t *testing.T) {114 path := filepath.Join(t.TempDir(), IndexName)115 write(t, path, tt.body)116 if _, err := Open(path); err == nil || !strings.Contains(err.Error(), tt.want) {117 t.Fatalf("Open = %v, want a refusal naming %q", err, tt.want)118 }119 })120 }121 t.Run("no register at all", func(t *testing.T) {122 if _, err := Open(filepath.Join(t.TempDir(), IndexName)); err == nil {123 t.Fatal("a missing register was accepted")124 }125 })126}127128func write(t *testing.T, path, body string) {129 t.Helper()130 if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {131 t.Fatal(err)132 }133 if err := os.WriteFile(path, []byte(body), 0o644); err != nil {134 t.Fatal(err)135 }136}