Files
astro/moon/svg/occultation_isochrone_test.go
T

90 lines
3.3 KiB
Go
Raw Normal View History

package svg
import (
"math"
"strings"
"testing"
"time"
"b612.me/astro/moon"
)
// 掩甚时刻等时线由路径选项请求,渲染器只在路径携带时绘制。
func TestPlanetOccultationSVGDrawsGreatestTimeIsochrones(t *testing.T) {
start := time.Date(2025, time.February, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)
render := func(step time.Duration) string {
t.Helper()
paths, err := moon.FindPlanetOccultationPaths(start, end, moon.OccultationSaturn, moon.OccultationPathOptions{
Step: 2 * time.Minute, DisableFootprints: true, GreatestTimeStep: step,
})
if err != nil || len(paths) == 0 {
t.Fatalf("no occultation path: %v", err)
}
if step > 0 && len(paths[0].GreatestTimeContours) == 0 {
t.Fatal("path carries no isochrones")
}
rendered, renderErr := PlanetOccultationPathSVG(paths[0], StarOccultationSVGOptions{
Width: 1200, Height: 800, Location: time.UTC,
})
if renderErr != nil {
t.Fatalf("render: %v", renderErr)
}
return rendered
}
withIsochrones := render(30 * time.Minute)
if !strings.Contains(withIsochrones, `class="greatest-time-isoline"`) {
t.Fatal("isochrone paths missing")
}
if !strings.Contains(withIsochrones, `class="greatest-time-isoline-label"`) {
t.Fatal("isochrone labels missing")
}
if strings.Contains(render(0), "greatest-time-isoline") {
t.Fatal("a zero step must not draw isochrones")
}
}
// 等时线也要走路径校验:坏支路必须在渲染前被拦住,而不是画出 NaN 折线。
func TestPlanetOccultationSVGRejectsBrokenGreatestTimeIsochrones(t *testing.T) {
paths, err := moon.FindPlanetOccultationPaths(
time.Date(2025, time.February, 1, 0, 0, 0, 0, time.UTC),
time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), moon.OccultationSaturn,
moon.OccultationPathOptions{Step: 2 * time.Minute, DisableFootprints: true, GreatestTimeStep: 30 * time.Minute},
)
if err != nil || len(paths) == 0 || len(paths[0].GreatestTimeContours) == 0 {
t.Fatalf("no isochrone path: %v", err)
}
options := StarOccultationSVGOptions{Width: 1200, Height: 800, Location: time.UTC}
render := func(mutate func(path *moon.PlanetOccultationPath)) error {
path := paths[0]
path.GreatestTimeContours = append([]moon.OccultationGreatestTimeContour(nil), path.GreatestTimeContours...)
mutate(&path)
_, renderErr := PlanetOccultationPathSVG(path, options)
return renderErr
}
if err := render(func(path *moon.PlanetOccultationPath) {}); err != nil {
t.Fatalf("valid isochrones rejected: %v", err)
}
if err := render(func(path *moon.PlanetOccultationPath) {
path.GreatestTimeContours[0].JDE = 0
}); err == nil {
t.Fatal("zero JDE accepted")
}
if err := render(func(path *moon.PlanetOccultationPath) {
path.GreatestTimeContours[0].Segments[0][0].Longitude = math.NaN()
}); err == nil {
t.Fatal("NaN longitude accepted")
}
if err := render(func(path *moon.PlanetOccultationPath) {
path.GreatestTimeContours[0].Segments[0] = path.GreatestTimeContours[0].Segments[0][:1]
}); err == nil {
t.Fatal("single-point branch accepted")
}
if err := render(func(path *moon.PlanetOccultationPath) {
path.GreatestTimeContours[0].Segments[0][0].Time = path.GreatestTimeContours[0].Segments[0][0].Time.Add(2 * time.Second)
}); err == nil {
t.Fatal("branch point with a mismatched instant accepted")
}
}