1package gen23import (4 "context"5 "encoding/json"6 "testing"7)89// TestFieldGrow: a 3x3 of G=40 chunks centred on a G=300 field grown twice.10// The merged field passes every assertion; no old cell changes; ids11// continue; the result does not depend on jobs; a full field reports12// exhausted.13func TestFieldGrow(t *testing.T) {14 p := FieldParams{Seed: "grow", G: 300, Chunks: 3, ChunkG: 40, Band: 16, Kind: "scatter", Jobs: 1}15 base, err := GenerateField(context.Background(), p, InProcess)16 if err != nil {17 t.Fatal(err)18 }19 f := base.Manifest.Field20 if f.Origin != [2]int{(300 - (3*24 + 16)) / 2, (300 - (3*24 + 16)) / 2} || len(f.Occupied) != 9 || f.Grown != 0 {21 t.Fatalf("field record %+v", f)22 }23 if base.Manifest.G != 300 {24 t.Fatalf("G=%d", base.Manifest.G)25 }26 checkAssertions(t, base)27 state := base28 for k := 1; k <= 2; k++ {29 delta, err := GrowField(context.Background(), state, p, 1, InProcess)30 if err != nil {31 t.Fatalf("grow %d: %v", k, err)32 }33 if delta.Stats.Exhausted {34 t.Fatalf("grow %d: exhausted on a field with room", k)35 }36 if len(delta.Manifest.Tiles) == 0 || delta.Manifest.Tiles[0].ID != len(state.Manifest.Tiles) {37 t.Fatalf("grow %d: delta ids start at %d, state has %d tiles", k, delta.Manifest.Tiles[0].ID, len(state.Manifest.Tiles))38 }39 if delta.Manifest.Field.Grown != k || len(delta.Manifest.Field.SeedTiles) == 0 {40 t.Fatalf("grow %d: field record %+v", k, delta.Manifest.Field)41 }42 // no old cell changes43 for y, row := range state.Solution {44 for x := range row {45 if row[x] != '.' && delta.Solution[y][x] != row[x] {46 t.Fatalf("grow %d changed old cell (%d,%d)", k, x, y)47 }48 }49 }50 // every new tile overlaps something (old or new) by a whole line's worth51 for _, tp := range delta.Manifest.Tiles {52 ok := false53 for _, ov := range tp.Overlaps {54 if ov.Cells >= 4 {55 ok = true56 }57 }58 if !ok {59 t.Fatalf("grow %d: new tile %d has no pin-eligible overlap", k, tp.ID)60 }61 }62 merged, err := ApplyGrowth(state, delta)63 if err != nil {64 t.Fatal(err)65 }66 expect := 9 + 1667 if k == 2 {68 expect += 2469 }70 if len(merged.Manifest.Field.Occupied) != expect {71 t.Fatalf("grow %d: %d chunks occupied, want %d", k, len(merged.Manifest.Field.Occupied), expect)72 }73 checkAssertions(t, merged)74 t.Logf("grow %d: +%d tiles (%d total), %d chunks, coverage %.2f, %d ms", k, len(delta.Manifest.Tiles), len(merged.Manifest.Tiles), len(delta.ChunkStats), merged.Layout.Coverage, delta.Stats.Ms)75 state = merged76 }77}7879// TestFieldGrowDeterministic: a ring generated with jobs=1 and jobs=4 is80// byte-identical (on a small 2x2 base so the suite stays fast).81func TestFieldGrowDeterministic(t *testing.T) {82 p := FieldParams{Seed: "det", G: 200, Chunks: 2, ChunkG: 40, Band: 16, Kind: "scatter", Jobs: 1}83 base, err := GenerateField(context.Background(), p, InProcess)84 if err != nil {85 t.Fatal(err)86 }87 p4 := p88 p4.Jobs = 489 d1, err := GrowField(context.Background(), base, p, 1, InProcess)90 if err != nil {91 t.Fatal(err)92 }93 d4, err := GrowField(context.Background(), base, p4, 1, InProcess)94 if err != nil {95 t.Fatal(err)96 }97 if d1.Stats.Exhausted || len(d1.Manifest.Tiles) == 0 {98 t.Fatal("ring 1 generated nothing")99 }100 d1.Stats, d4.Stats = Stats{}, Stats{}101 d1.ChunkStats, d4.ChunkStats = nil, nil102 j1, _ := json.Marshal(d1)103 j4, _ := json.Marshal(d4)104 if string(j1) != string(j4) {105 t.Fatal("growth differs with jobs=4")106 }107}108109// TestFieldGrowExhausted: a field with no room around its grid reports110// exhausted and changes nothing.111func TestFieldGrowExhausted(t *testing.T) {112 p := FieldParams{Seed: "full", Chunks: 2, ChunkG: 40, Band: 16, Kind: "scatter", Jobs: 1}113 base, err := GenerateField(context.Background(), p, InProcess)114 if err != nil {115 t.Fatal(err)116 }117 delta, err := GrowField(context.Background(), base, p, 1, InProcess)118 if err != nil {119 t.Fatal(err)120 }121 if !delta.Stats.Exhausted || len(delta.Manifest.Tiles) != 0 || delta.Manifest.Field.Grown != 1 {122 t.Fatalf("want an empty exhausted delta, got %d tiles exhausted=%v grown=%d", len(delta.Manifest.Tiles), delta.Stats.Exhausted, delta.Manifest.Field.Grown)123 }124 merged, err := ApplyGrowth(base, delta)125 if err != nil {126 t.Fatal(err)127 }128 if len(merged.Manifest.Tiles) != len(base.Manifest.Tiles) {129 t.Fatal("exhausted growth changed the tile count")130 }131}