Files
rond-with-you/internal/fog/render_diff_test.go
T
tamakyi 30c502ce7b 备份与迷雾的差异分析、合并执行,附属数据改挂坐标键
备份(.rondbackup)与迷雾(.fwss)各加一层「先看差异再动手」:

- 迷雾:上传先出差异页,说清叠加能补多少、整体替换会丢多少,并在真实底图上
  用绿/灰/红标出会新增 / 不变 / 会丢的区域
- 备份:与当前数据集按 UUID 逐条比对(rond 每次导出都会重排 Z_PK,编号认不出人),
  新增合入、冲突逐条选、仅站内有逐条勾选删除,全在一个事务里执行;
  合入的行写回 entity_raw,保证再传同一份备份时差异归零
- 地点按坐标对齐:站点的「补全地址」会把地点改名,拿名称当键会让一整个地点的
  到访全变成假冲突
- 附属数据(备注/结论/访客票/图片)从 dataset_id+src_pk 换成坐标键,换数据集不再失联;
  访客票没有 dataset_id,回填按活跃数据集认领
- 修正 contentHash 拿 Z_PK 当指纹的隐患,以及两处「src_pk 跨快照稳定」的错误注释

导出格式(.rondbackup / .fwss)保持原样,往返测试通过。
2026-09-19 16:04:00 +08:00

115 lines
3.8 KiB
Go

package fog
import (
"image"
"math"
"testing"
)
func bmOf(cells ...[2]int) []byte {
bm := make([]byte, bitmapSize)
for _, c := range cells {
bm[c[1]*8+c[0]/8] |= 0x80 >> (c[0] % 8)
}
return bm
}
func pixAt(img *image.NRGBA, px, py int) [4]byte {
i := (py*256 + px) * 4
return [4]byte{img.Pix[i], img.Pix[i+1], img.Pix[i+2], img.Pix[i+3]}
}
// z=18 时一格正好 16 像素,块 (100,200) 完整落在瓦片 (400,800) 里,
// 格 (col,row) 的中心像素是 (col*16+8, row*16+8)。
func TestRenderDiffTilePerCell(t *testing.T) {
key := [2]int{100, 200}
cur := map[[2]int][]byte{key: bmOf([2]int{0, 0}, [2]int{4, 0})}
inc := map[[2]int][]byte{key: bmOf([2]int{0, 0}, [2]int{1, 0})}
img := RenderDiffTile(cur, inc, 18, 400, 800, "wgs84")
if img == nil {
t.Fatal("瓦片不该为空")
}
if got := pixAt(img, 8, 8); got != DiffBothColor {
t.Errorf("两边都有的格应为灰色,实际 %v", got)
}
if got := pixAt(img, 24, 8); got != DiffGainColor {
t.Errorf("只有快照有的格应为绿色,实际 %v", got)
}
if got := pixAt(img, 72, 8); got != DiffLostColor {
t.Errorf("只有库里有、整体替换会丢的格应为红色,实际 %v", got)
}
if got := pixAt(img, 200, 200); got[3] != 0 {
t.Errorf("没被探索的格应保持透明,实际 %v", got)
}
}
func TestRenderDiffTileSingleSided(t *testing.T) {
key := [2]int{100, 200}
onlyCur := RenderDiffTile(map[[2]int][]byte{key: bmOf([2]int{3, 3})}, nil, 18, 400, 800, "wgs84")
if got := pixAt(onlyCur, 56, 56); got != DiffLostColor {
t.Errorf("快照里完全没有的块应整块为红,实际 %v", got)
}
onlyInc := RenderDiffTile(nil, map[[2]int][]byte{key: bmOf([2]int{3, 3})}, 18, 400, 800, "wgs84")
if got := pixAt(onlyInc, 56, 56); got != DiffGainColor {
t.Errorf("库里完全没有的块应整块为绿,实际 %v", got)
}
}
// z=10 时一格不足一像素,块只占 4x4 像素,按块内多数类涂色。
func TestRenderDiffTileCoarseZoomUsesDominantKind(t *testing.T) {
key := [2]int{100, 200}
cur := map[[2]int][]byte{key: bmOf([2]int{0, 0})}
inc := map[[2]int][]byte{key: bmOf([2]int{1, 1}, [2]int{2, 2}, [2]int{3, 3})}
img := RenderDiffTile(cur, inc, 10, 1, 3, "wgs84")
const step, scale = WorldCells >> 10, 256.0 / (WorldCells >> 10)
bx, by := float64(key[0]*BlockCells), float64(key[1]*BlockCells)
px := int(math.Floor((bx - 1*step) * scale))
py := int(math.Floor((by - 3*step) * scale))
if got := pixAt(img, px+1, py+1); got != DiffGainColor {
t.Errorf("三格新增、一格不变的块应以绿色为主,实际 %v", got)
}
if w := int(math.Ceil(float64(BlockCells) * scale)); w != 4 {
t.Fatalf("z=10 下块应占 4 像素,实际 %d", w)
}
}
func TestRenderDiffTileEdgeCases(t *testing.T) {
if RenderDiffTile(nil, nil, 18, 400, 800, "wgs84") == nil {
t.Error("两边都空也应返回一张空瓦片")
}
if RenderDiffTile(nil, nil, 23, 0, 0, "wgs84") != nil {
t.Error("超出缩放范围应返回 nil")
}
}
func TestDominantKind(t *testing.T) {
cases := []struct {
both, gain, lost int
want uint8
}{
{0, 0, 0, diffNone},
{5, 1, 0, diffBoth},
{1, 5, 0, diffGain},
{1, 0, 5, diffLost},
{2, 2, 0, diffGain}, // 平局让「会新增」胜出
{3, 1, 1, diffBoth},
}
for _, c := range cases {
if got := dominantKind(c.both, c.gain, c.lost); got != c.want {
t.Errorf("dominantKind(%d,%d,%d)=%d,期望 %d", c.both, c.gain, c.lost, got, c.want)
}
}
}
func TestBlockDiffCounts(t *testing.T) {
both, gain, lost := blockDiffCounts(bmOf([2]int{0, 0}, [2]int{1, 0}), bmOf([2]int{0, 0}, [2]int{2, 0}))
if both != 1 || gain != 1 || lost != 1 {
t.Errorf("一块里应各有一格:both=%d gain=%d lost=%d", both, gain, lost)
}
both, gain, lost = blockDiffCounts(nil, bmOf([2]int{5, 5}))
if both != 0 || gain != 1 || lost != 0 {
t.Errorf("只有快照有该块:both=%d gain=%d lost=%d", both, gain, lost)
}
}