1package app23import (4 "slices"5 "testing"67 "github.com/FelineStateMachine/atlas/format/semconv"8 "github.com/FelineStateMachine/atlas/internal/app/cells"9)1011// The two halves of the grid's server side: what a request is allowed to put12// in the session, and what the session then narrows.13//14// They are one subject rather than two. A system value that reaches the record15// is a system the filter has to be able to divide by, because the seam divides16// by it whatever the server thinks -- so the refusal below and the predicate17// beneath it are the same invariant said at the two ends.1819// sphere is the flattening a planetary world declares: the Mars bundle's own20// attributes, which tests/cells reads back against the shipped bundle itself.21var sphere = map[string]string{22 semconv.KeyGeometrySurface: semconv.SurfaceSphere,23 semconv.KeyGeometryProjection: semconv.ProjectionEquirect,24 semconv.KeyGeometryEquirectPx: "0,0,8192,4096",25 semconv.KeyGeometryEquirectDeg: "-180,90,180,-90",26}2728// plane is a world that says nothing about what its picture is of, which is29// every game map: geohash divides it and nothing else offers to.30var plane = map[string]string{semconv.KeyIconOutset: semconv.OutsetDark}3132// anchoredPlane is the shipped bend-or declaration: a plane city anchored to33// real earth by a Mercator window, whose geohash is therefore REAL — base34// depth 4 and floor 6 from the window's own degree spans (internal/app/cells,35// GeohashBaseDepth; the same numbers the seam and the shared vectors hold).36var anchoredPlane = map[string]string{37 semconv.KeyGeometrySurface: semconv.SurfacePlane,38 semconv.KeyGeometryBody: "earth",39 semconv.KeyGeometryMercatorPx: "0,0,8192,8192",40 semconv.KeyGeometryMercatorDeg: "-121.48204667177453,44.17572950664809,-121.1529533282255,43.93922950850393",41}4243// worldOn is a world standing on the ground the carry vectors are read on:44// the top half of an 8192 world square, declared whole as the lens's45// surface (`test/sphere-8192x4096` in analysis/testdata/cells/grounds.json).46// Every question about a *place* -- as against a system's name -- needs one,47// because a hierarchy divides a rectangle and a world with no rectangle48// divides nothing.49func worldOn(attrs map[string]string) *worldModel {50 return &worldModel{51 Attrs: attrs,52 Grid: tileGrid{Size: 8192},53 Lenses: []payloadLens{{Name: "Surface", Surface: &cells.Rect{Width: 8192, Height: 4096}}},54 }55}5657func TestApplyGridRefusesASystemTheWorldDoesNotDivide(t *testing.T) {58 tests := []struct {59 name string60 attrs map[string]string61 held Grid62 sent string63 want Grid64 }{65 {66 // The address does not survive and the place does: the two67 // hierarchies share no boundaries, so what crosses is the ground68 // the reader was looking at, at the depth they were looking at it69 // (internal/app/cells, Equivalent -- held to the recorded carry70 // vectors by TestCyclingCarriesTheHeldPlace below).71 name: "a sphere takes S2, carrying the held place across",72 attrs: sphere, held: Grid{System: "geohash", Cell: "9q"},73 sent: "s2", want: Grid{System: "s2", Cell: "80b"},74 },75 {76 // The refusal keeps the record where it was rather than closing77 // the grid or resetting it: a request the application does not78 // understand is one it did not happen, and a reader who never79 // asked for this should not lose the cell they were standing in.80 name: "a plane refuses S2 and keeps what it had",81 attrs: plane, held: Grid{System: "geohash", Cell: "9q", Subgrid: 1},82 sent: "s2", want: Grid{System: "geohash", Cell: "9q", Subgrid: 1},83 },84 {85 name: "a sphere with no flattening refuses it too",86 attrs: map[string]string{semconv.KeyGeometrySurface: semconv.SurfaceSphere},87 held: Grid{}, sent: "s2", want: Grid{},88 },89 {90 name: "a world nobody could stand up refuses it",91 attrs: nil, held: Grid{System: "geohash"}, sent: "s2",92 want: Grid{System: "geohash"},93 },94 {95 name: "geohash divides anything, including a sphere",96 attrs: sphere, held: Grid{System: "s2", Cell: "47a1cb"},97 sent: "geohash", want: Grid{System: "geohash", Cell: "u2b"},98 },99 {100 name: "a system nobody has written is not a system",101 attrs: sphere, held: Grid{System: "geohash"}, sent: "h3",102 want: Grid{System: "geohash"},103 },104 {105 // Slugs are the registry's spelling and nothing else is a near106 // miss worth guessing at.107 name: "and neither is one spelled differently",108 attrs: sphere, held: Grid{System: "geohash"}, sent: "S2",109 want: Grid{System: "geohash"},110 },111 {112 name: "an empty value is the grid closing, which is not a system at all",113 attrs: sphere, held: Grid{System: "s2", Cell: "47a1cb", Subgrid: 1},114 sent: "", want: Grid{},115 },116 }117118 for _, test := range tests {119 t.Run(test.name, func(t *testing.T) {120 session := Session{Grid: test.held}121 held := &concernContext{session: &session, world: worldOn(test.attrs)}122 form := formValues{values: map[string][]string{"system": {test.sent}}}123 if err := applyGrid(held, form); err != nil {124 t.Fatalf("applyGrid: %v", err)125 }126 if session.Grid != test.want {127 t.Errorf("grid = %+v, want %+v", session.Grid, test.want)128 }129 })130 }131}132133// TestApplyGridStillOpensAndCyclesOnAnyWorld guards the two moves the refusal134// sits beside: neither is a destination, so neither can be refused. Opening135// always lands on the system that divides anything; cycling always lands on a136// system this world offers, which on most worlds is the one it started on.137func TestApplyGridStillOpensAndCyclesOnAnyWorld(t *testing.T) {138 for _, attrs := range []map[string]string{plane, sphere, nil} {139 session := Session{}140 held := &concernContext{session: &session, world: worldOn(attrs)}141 if err := applyGrid(held, formValues{values: map[string][]string{"system": {"toggle"}}}); err != nil {142 t.Fatalf("applyGrid: %v", err)143 }144 if session.Grid.System != defaultCellSystem {145 t.Errorf("toggling a grid open on %v opened %q", attrs, session.Grid.System)146 }147 offered := cells.ApplicableSystems(attrs)148 if err := applyGrid(held, formValues{values: map[string][]string{"system": {"cycle"}}}); err != nil {149 t.Fatalf("applyGrid: %v", err)150 }151 if !slices.Contains(offered, session.Grid.System) {152 t.Errorf("cycling on %v landed on %q, which it does not offer %v",153 attrs, session.Grid.System, offered)154 }155 // A world with one system has nowhere to step; a world with two steps.156 want := defaultCellSystem157 if len(offered) > 1 {158 want = offered[1]159 }160 if session.Grid.System != want {161 t.Errorf("cycling on %v landed on %q, want %q", attrs, session.Grid.System, want)162 }163 }164}165166// TestCyclingIsANoOpWithNoGridOpen: the button that would cycle is not on the167// page when there is no grid, and a request arriving anyway must not open one.168// ⌘G in the reference changed which system *would* divide the map and left the169// map alone; a record that spells "no grid" as "no system" has nothing to170// remember, so it does nothing instead of opening something nobody asked for.171func TestCyclingIsANoOpWithNoGridOpen(t *testing.T) {172 session := Session{Grid: Grid{Subgrid: 1}}173 held := &concernContext{session: &session, world: worldOn(sphere)}174 if err := applyGrid(held, formValues{values: map[string][]string{"system": {"cycle"}}}); err != nil {175 t.Fatalf("applyGrid: %v", err)176 }177 if (session.Grid != Grid{Subgrid: 1}) {178 t.Errorf("cycling with no grid open wrote %+v", session.Grid)179 }180}181182// TestCyclingCarriesTheHeldPlace holds the Go carry to the shared183// `equivalentCell` vectors -- the same language-neutral oracle the TypeScript184// lane answers to (analysis/testdata/cells/carry.json, family "carry", read185// on the ground `test/sphere-8192x4096`). These five are the carry seen from186// inside the application lane, where the answer lands in a session.187//188// The table is transcribed rather than read, because the application lane may189// not open a file (the hostenv rule, held by tools/depcheck) and a test file190// is still the application lane. The durable *reading* of this family is191// tests/cells/conformance_test.go, which loads carry.json and answers it192// through cells.Equivalent; the case names below are that file's own keys, so193// the two can be diffed by eye.194func TestCyclingCarriesTheHeldPlace(t *testing.T) {195 vectors := []struct {196 name string197 from, to string198 id, want string199 }{200 // The root carries to the root, which is the only address the two201 // hierarchies spell the same way.202 {"root-carries-to-root", "geohash", "s2", "", ""},203 // A level-2 geohash cell is 256x128 of the 8192x4096 world; the S2204 // cell holding its centre at the nearest area sits within a level205 // either way.206 {"geohash-m6-to-s2", "geohash", "s2", "m6", "213"},207 {"and-back-to-geohash", "s2", "geohash", "213", "m6"},208 {"geohash-m-to-s2", "geohash", "s2", "m", "24"},209 // A polar face carried onto a plane's halving: the face's centre is210 // the north pole itself.211 {"s2-face-to-geohash", "s2", "geohash", "5", "b"},212 }213214 for _, vector := range vectors {215 t.Run(vector.name, func(t *testing.T) {216 session := Session{Grid: Grid{System: vector.from, Cell: vector.id, Subgrid: 1}}217 held := &concernContext{session: &session, world: worldOn(sphere)}218 setGridSystem(held, vector.to)219 if session.Grid.System != vector.to || session.Grid.Cell != vector.want {220 t.Errorf("carrying %q from %s to %s = %+v, the vector records %q",221 vector.id, vector.from, vector.to, session.Grid, vector.want)222 }223 })224 }225}226227// The two containment cross-checks recorded beside the crossings: the carried228// cell holds the ground the old one was standing on, which is the whole of229// what "the same place at like precision" means.230func TestTheCarriedCellHoldsTheOldCentre(t *testing.T) {231 world := worldOn(sphere)232 ground := gridGround(&concernContext{session: &Session{}, world: world})233 mapping, mapped := cells.MappingOf(sphere)234 if !mapped {235 t.Fatal("the sphere declares no flattening")236 }237 // carried-cell-holds-the-old-centre: s2 "213" contains m6's centre.238 if !cells.S2Held(mapping, "213")(5504, -2752) {239 t.Error("the S2 cell m6 carried to does not hold m6's centre")240 }241 // round-trip-holds-the-s2-centre: geohash "m6" contains 213's centre.242 if !cells.GeohashHeld(ground, "m6")(5560.028045868422, -2708.7329376470884) {243 t.Error("the geohash cell 213 carried back to does not hold 213's centre")244 }245}246247// TestCyclingBackAndForthComesHome is the property the vectors record two248// halves of: a place carried across and back names the same ground again. It249// is not an identity in general -- the two hierarchies are not nested, and a250// cell near a boundary can come home one neighbour over -- which is why this251// asks for the ground rather than for the spelling: the carried cell holds252// the centre it was carried from, both ways.253func TestCyclingBackAndForthComesHome(t *testing.T) {254 world := worldOn(sphere)255 ground := gridGround(&concernContext{session: &Session{}, world: world})256 mapping, mapped := cells.MappingOf(sphere)257 if !mapped {258 t.Fatal("the sphere declares no flattening")259 }260 for _, hash := range []string{"m", "m6", "u2b", "9q"} {261 t.Run(hash, func(t *testing.T) {262 token := cells.Equivalent(ground, sphere, "geohash", "s2", hash)263 if token == "" {264 t.Fatalf("%q carried to nothing", hash)265 }266 extent := cells.GeohashExtent(ground, hash)267 x, y := (extent.MinX+extent.MaxX)/2, (extent.MinY+extent.MaxY)/2268 if !cells.S2Held(mapping, token)(x, y) {269 t.Errorf("%q carried to %q, which does not hold its centre", hash, token)270 }271 back := cells.Equivalent(ground, sphere, "s2", "geohash", token)272 cx, cy := cells.S2Center(mapping, ground, token)273 if !cells.GeohashHeld(ground, back)(cx, cy) {274 t.Errorf("%q carried back to %q, which does not hold %q's centre", token, back, token)275 }276 })277 }278}279280// TestNormalizingTheNavigatorsField is the input contract, per system: what281// the field keeps of a keystroke, and when the draft has become a place.282//283// It is the half of the reference that used to live in the browser. Every284// submission is a navigation for geohash -- the empty string is the root and285// every prefix is an address -- and a whole-or-nothing token for S2, where a286// partial spelling leaves the reader exactly where they were.287func TestNormalizingTheNavigatorsField(t *testing.T) {288 tests := []struct {289 name string290 held Grid291 sent string292 want Grid293 }{294 {"a hash arrives lowercased and stripped of what it cannot spell",295 Grid{System: "geohash"}, "M6X!", Grid{System: "geohash", Cell: "m6x"}},296 {"the letters the alphabet leaves out are not letters",297 Grid{System: "geohash"}, "aM6iLo", Grid{System: "geohash", Cell: "m6"}},298 {"and a hash past the floor is cut at it",299 Grid{System: "geohash"}, "m6w9", Grid{System: "geohash", Cell: "m6w"}},300 {"the root is a place, so an empty field is the whole ground",301 Grid{System: "geohash", Cell: "m6"}, "", Grid{System: "geohash"}},302 {"a token arrives lowercased and hex-only",303 Grid{System: "s2"}, "47A1 CB!", Grid{System: "s2", Cell: "47a1cb"}},304 {"the root is a place in every system",305 Grid{System: "s2", Cell: "47a1cb"}, "", Grid{System: "s2"}},306 {"half a token is a draft, and a draft moves nobody",307 Grid{System: "s2", Cell: "47a1cb"}, "4", Grid{System: "s2", Cell: "47a1cb"}},308 {"and so is a whole one that is not an address",309 Grid{System: "s2", Cell: "47a1cb"}, "47a1cbd5955ff", Grid{System: "s2", Cell: "47a1cb"}},310 // Text with nothing of the system's alphabet in it normalizes to311 // nothing, and nothing is the root -- which is the field emptying312 // itself in front of the reader and the map opening out, exactly as313 // the reference's `input` handler did it.314 {"text a system cannot spell at all empties the field to the root",315 Grid{System: "s2", Cell: "47a1cb"}, "xyz", Grid{System: "s2"}},316 {"a token past the floor clamps to it and comes back respelled",317 Grid{System: "s2"}, "47a1cbd595522b39", Grid{System: "s2", Cell: "47a1cb"}},318 // The field takes six characters, so the seventeen-character leaf319 // above arrives cut to six -- which is the same cell, spelled the way320 // the navigator would have spelled it.321 {"and one typed past the field's own length is cut first",322 Grid{System: "s2"}, "47a1cbd5", Grid{System: "s2", Cell: "47a1cb"}},323 {"a cell arriving at a closed grid opens it on the system that divides anything",324 Grid{Subgrid: 1}, "m6", Grid{System: "geohash", Cell: "m6", Subgrid: 1}},325 // Opening the grid with the G key starts the subdivision over; going326 // somewhere does not. A reader who put the mesh away and then picked a327 // cell off the map asked for the cell, not for the mesh back.328 {"and does not bring the subdivision back with it",329 Grid{}, "m6", Grid{System: "geohash", Cell: "m6"}},330 // There is no such thing as a draft at a closed grid: the system a331 // closed grid opens on is the one that never refuses a spelling.332 {"and whatever arrives at a closed grid is a geohash",333 Grid{}, "47a1cb", Grid{System: "geohash", Cell: "471"}},334 }335336 for _, test := range tests {337 t.Run(test.name, func(t *testing.T) {338 session := Session{Grid: test.held}339 held := &concernContext{session: &session, world: worldOn(sphere)}340 form := formValues{values: map[string][]string{"cell": {test.sent}}}341 if err := applyGrid(held, form); err != nil {342 t.Fatalf("applyGrid: %v", err)343 }344 if session.Grid != test.want {345 t.Errorf("grid = %+v, want %+v", session.Grid, test.want)346 }347 })348 }349}350351// TestTheNavigatorsFieldOnAnAnchoredCity is the input contract in real mode:352// the field keeps six characters, an address shorter than the base depth is a353// draft that moves nobody, and Escape from a base cell lands on the root --354// the three-character hemisphere between them is not a place the window's355// grid has. The addresses are the shared vectors' own (containment.json and356// input.json, the real-* cases).357func TestTheNavigatorsFieldOnAnAnchoredCity(t *testing.T) {358 tests := []struct {359 name string360 held Grid361 form map[string][]string362 want Grid363 }{364 {"a whole real address travels",365 Grid{System: "geohash"}, map[string][]string{"cell": {"9RCDXK!"}},366 Grid{System: "geohash", Cell: "9rcdxk"}},367 {"the field keeps six characters where a synthetic ground keeps three",368 Grid{System: "geohash"}, map[string][]string{"cell": {"9rcdxkpb"}},369 Grid{System: "geohash", Cell: "9rcdxk"}},370 {"a base cell is a place",371 Grid{System: "geohash"}, map[string][]string{"cell": {"9rcd"}},372 Grid{System: "geohash", Cell: "9rcd"}},373 {"a shorter spelling is a draft, and a draft moves nobody",374 Grid{System: "geohash", Cell: "9rcd"}, map[string][]string{"cell": {"9rc"}},375 Grid{System: "geohash", Cell: "9rcd"}},376 {"an empty field is still the root",377 Grid{System: "geohash", Cell: "9rcd"}, map[string][]string{"cell": {""}},378 Grid{System: "geohash"}},379 {"escape from a base cell lands on the root, not a hemisphere",380 Grid{System: "geohash", Cell: "9rcd", Subgrid: 1},381 map[string][]string{"ascend": {"1"}, "cell": {"9rcd"}},382 Grid{System: "geohash", Subgrid: 1}},383 {"and one layer down it drops a character as ever",384 Grid{System: "geohash", Cell: "9rcdx", Subgrid: 1},385 map[string][]string{"ascend": {"1"}, "cell": {"9rcdx"}},386 Grid{System: "geohash", Cell: "9rcd", Subgrid: 1}},387 }388 for _, test := range tests {389 t.Run(test.name, func(t *testing.T) {390 session := Session{Grid: test.held}391 held := &concernContext{session: &session, world: worldOn(anchoredPlane)}392 if err := applyGrid(held, formValues{values: test.form}); err != nil {393 t.Fatalf("applyGrid: %v", err)394 }395 if session.Grid != test.want {396 t.Errorf("grid = %+v, want %+v", session.Grid, test.want)397 }398 })399 }400}401402// TestAscendingTelescopesOutOfTheHeldSystem is the regression the S2 side was403// written for: Escape steps one LEVEL out, and only the address's own system404// knows what that is. A geohash drops a character; an S2 token does not --405// `47a1cb` telescopes out to `47a1`, and shortening it by a character would406// have landed the reader in a cell they never asked for.407func TestAscendingTelescopesOutOfTheHeldSystem(t *testing.T) {408 tests := []struct {409 name string410 held Grid411 want Grid412 }{413 {"a geohash drops its last character", Grid{System: "geohash", Cell: "m6x", Subgrid: 1},414 Grid{System: "geohash", Cell: "m6", Subgrid: 1}},415 {"down to the root, which keeps the grid open",416 Grid{System: "geohash", Cell: "m", Subgrid: 1}, Grid{System: "geohash", Subgrid: 1}},417 // The shared hierarchy vector, read the other way round:418 // parent("47a1ca4") is "47a1cb" (analysis/testdata/cells/hierarchy.json).419 // A truncation would have answered "47a1ca", one of its own siblings'420 // neighbours.421 {"an S2 token telescopes out to a token, not to a prefix",422 Grid{System: "s2", Cell: "47a1ca4", Subgrid: 1}, Grid{System: "s2", Cell: "47a1cb", Subgrid: 1}},423 // And the level after that is the same length again: an S2 address is424 // a Hilbert index, so the character count says nothing about depth.425 {"and out again, to an address the same length",426 Grid{System: "s2", Cell: "47a1cb", Subgrid: 1}, Grid{System: "s2", Cell: "47a1cc", Subgrid: 1}},427 {"and out again, to a shorter one", Grid{System: "s2", Cell: "47a1cc", Subgrid: 1},428 Grid{System: "s2", Cell: "47a1d", Subgrid: 1}},429 {"a face telescopes out to the root", Grid{System: "s2", Cell: "5", Subgrid: 1},430 Grid{System: "s2", Subgrid: 1}},431 // The root is the last address; the press after it closes the grid,432 // and takes nothing but the grid with it.433 {"the root closes the grid", Grid{System: "geohash", Subgrid: 1}, Grid{Subgrid: 1}},434 {"in every system", Grid{System: "s2", Subgrid: 1}, Grid{Subgrid: 1}},435 {"and a reader who put the subdivision away keeps it away",436 Grid{System: "geohash"}, Grid{}},437 {"a grid nobody opened has nothing to telescope out of", Grid{Subgrid: 1}, Grid{Subgrid: 1}},438 }439440 for _, test := range tests {441 t.Run(test.name, func(t *testing.T) {442 session := Session{Grid: test.held}443 held := &concernContext{session: &session, world: worldOn(sphere)}444 // The back button posts the held cell alongside the press, the way445 // the navigator's markup does; telescoping out must win over it.446 form := formValues{values: map[string][]string{447 "ascend": {"1"}, "cell": {test.held.Cell},448 }}449 if err := applyGrid(held, form); err != nil {450 t.Fatalf("applyGrid: %v", err)451 }452 if session.Grid != test.want {453 t.Errorf("grid = %+v, want %+v", session.Grid, test.want)454 }455 })456 }457}458459// TestOpeningTheGridDividesTheCellAgain: the G key starts the subdivision460// over, and closing the grid leaves the reader's own setting where it was.461func TestOpeningTheGridDividesTheCellAgain(t *testing.T) {462 session := Session{Grid: Grid{System: "geohash", Cell: "m6"}}463 held := &concernContext{session: &session, world: worldOn(plane)}464 toggle := formValues{values: map[string][]string{"system": {"toggle"}}}465466 if err := applyGrid(held, toggle); err != nil {467 t.Fatalf("applyGrid: %v", err)468 }469 // Closing takes the cell with it -- a cell nobody can see is not a place470 // anybody is standing -- and leaves the subdivision hidden.471 if (session.Grid != Grid{}) {472 t.Fatalf("closing wrote %+v", session.Grid)473 }474 if err := applyGrid(held, toggle); err != nil {475 t.Fatalf("applyGrid: %v", err)476 }477 if want := (Grid{System: defaultCellSystem, Subgrid: 1}); session.Grid != want {478 t.Errorf("opening wrote %+v, want %+v", session.Grid, want)479 }480}481482// TestTheCycleButtonIsOfferedOnlyWhereItLeadsSomewhere: a control that cycles483// between one thing is a control that does nothing, so a world offering one484// system does not carry it. The mark, the name and the next system are the485// registry's own spellings, because the seam draws the same words.486func TestTheCycleButtonIsOfferedOnlyWhereItLeadsSomewhere(t *testing.T) {487 tests := []struct {488 name string489 attrs map[string]string490 grid Grid491 want GridView492 }{493 {"a game map divides one way and says so quietly", plane,494 Grid{System: "geohash", Subgrid: 1},495 GridView{Enabled: true, System: "geohash", Name: "Geohash", Mark: "G", Length: 3,496 Subgrid: true}},497 // The field's maxlength is the ground's: a real geohash on an498 // earth-anchored plane is six characters, and the navigator says so.499 {"an earth-anchored city takes a whole real address", anchoredPlane,500 Grid{System: "geohash", Subgrid: 1},501 GridView{Enabled: true, System: "geohash", Name: "Geohash", Mark: "G", Length: 6,502 Subgrid: true}},503 {"a planet divides two ways and offers the step", sphere,504 Grid{System: "geohash", Cell: "m6", Subgrid: 1},505 GridView{Enabled: true, System: "geohash", Name: "Geohash", Mark: "G", Cell: "m6",506 Length: 3, Subgrid: true, Cycles: true, Next: "S2"}},507 {"and the step wraps back", sphere,508 Grid{System: "s2", Cell: "47a1cb", Subgrid: 1},509 GridView{Enabled: true, System: "s2", Name: "S2", Mark: "S2", Cell: "47a1cb",510 Length: 6, Subgrid: true, Cycles: true, Next: "Geohash"}},511 // A closed grid is a navigator nobody can see; there is no current512 // system to step from, so there is nothing for the button to say.513 {"a closed grid offers nothing at all", sphere, Grid{Subgrid: 1},514 GridView{Subgrid: true}},515 }516517 for _, test := range tests {518 t.Run(test.name, func(t *testing.T) {519 world := worldOn(test.attrs)520 want := test.want521 want.Offered = true522 if got := gridView(Session{Grid: test.grid}, world); got != want {523 t.Errorf("grid view = %+v, want %+v", got, want)524 }525 })526 }527}528529// TestHeldCellNarrowsForEverySystemTheSessionCanHold is the invariant the530// defect broke: whatever the session holds, the server narrows the same set531// the seam draws, or the count above the panel is a count of something else.532func TestHeldCellNarrowsForEverySystemTheSessionCanHold(t *testing.T) {533 // A whole-body world: the raster is the sphere, so the lens's surface and534 // the world square are the same 8192 x 4096.535 world := &worldModel{Attrs: sphere, Grid: tileGrid{Size: 8192}}536 lens := &payloadLens{Surface: &cells.Rect{X: 0, Y: 0, Width: 8192, Height: 4096}}537538 // Two probes. The middle of the raster is lat 0, lng 0 on the body --539 // S2 cell 100001 at the telescope's floor, and the corner where four540 // geohash quarters meet, which every one of them holds. The other is an541 // ordinary point well inside one quarter.542 middle := [2]float64{4096, -2048}543 inland := [2]float64{5000, -1000}544545 tests := []struct {546 name string547 grid Grid548 world *worldModel549 at [2]float64550 narrow bool // a predicate is expected at all551 inside bool // and this is what it says about the probe552 }{553 {"no grid narrows nothing", Grid{}, world, inland, false, false},554 {"an open grid with no cell narrows nothing", Grid{System: "s2"}, world, middle, false, false},555 {"a geohash cell holds its own quarter", Grid{System: "geohash", Cell: "u"}, world, inland, true, true},556 {"and not the quarter beside it", Grid{System: "geohash", Cell: "s"}, world, inland, true, false},557 {"the corner belongs to every quarter that touches it",558 Grid{System: "geohash", Cell: "7"}, world, middle, true, true},559 {"an S2 cell holds the point it was found from",560 Grid{System: "s2", Cell: "100001"}, world, middle, true, true},561 {"and not a point a level-10 cell away", Grid{System: "s2", Cell: "100001"}, world, inland, true, false},562 {"a face on the other side of the body holds neither",563 Grid{System: "s2", Cell: "9"}, world, middle, true, false},564 // Real mode: the held cell is a genuine WGS84 geohash and the565 // question is asked in degrees through the declared window. The566 // raster centre is 9rcd's ground; 9rcf overhangs the window's east567 // edge and holds a point OFF the raster, because the cell is the568 // planet's even where the picture stops.569 {"a real geohash cell holds the ground it names",570 Grid{System: "geohash", Cell: "9rcd"},571 &worldModel{Attrs: anchoredPlane, Grid: tileGrid{Size: 8192}},572 [2]float64{4096, -4096}, true, true},573 {"and not the window cell beside it",574 Grid{System: "geohash", Cell: "9rcf"},575 &worldModel{Attrs: anchoredPlane, Grid: tileGrid{Size: 8192}},576 [2]float64{4096, -4096}, true, false},577 {"a real cell holds real ground past the raster's edge",578 Grid{System: "geohash", Cell: "9rcf"},579 &worldModel{Attrs: anchoredPlane, Grid: tileGrid{Size: 8192}},580 [2]float64{9000, -4096}, true, true},581 {"a system this world does not divide narrows nothing",582 Grid{System: "s2", Cell: "100001"},583 &worldModel{Attrs: plane, Grid: tileGrid{Size: 8192}}, middle, false, false},584 {"and neither does one nobody has written",585 Grid{System: "h3", Cell: "8928308280fffff"}, world, middle, false, false},586 }587588 for _, test := range tests {589 t.Run(test.name, func(t *testing.T) {590 cell := heldCell(Session{Grid: test.grid}, test.world, lens)591 if (cell != nil) != test.narrow {592 t.Fatalf("narrowing = %v, want %v", cell != nil, test.narrow)593 }594 if cell == nil {595 return596 }597 if got := cell(test.at[0], test.at[1]); got != test.inside {598 t.Errorf("holds (%g, %g) = %v, want %v", test.at[0], test.at[1], got, test.inside)599 }600 })601 }602}