166 lines
6.4 KiB
Go
166 lines
6.4 KiB
Go
|
|
package svg
|
||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"math"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"b612.me/astro/moon"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestFindLocalPlanetOccultationSVGsSaturnFiveContacts(t *testing.T) {
|
||
|
|
location := time.FixedZone("UTC+8", 8*3600)
|
||
|
|
diagrams, err := FindLocalPlanetOccultationSVGs(
|
||
|
|
time.Date(2025, time.February, 1, 0, 0, 0, 0, location),
|
||
|
|
time.Date(2025, time.February, 2, 0, 0, 0, 0, location),
|
||
|
|
moon.OccultationSaturn,
|
||
|
|
104.52219613, 55.25401991, 0,
|
||
|
|
moon.OccultationSearchOptions{},
|
||
|
|
LocalPlanetOccultationSVGOptions{Width: 920, Height: 700, Location: location},
|
||
|
|
)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("FindLocalPlanetOccultationSVGs() error = %v", err)
|
||
|
|
}
|
||
|
|
if len(diagrams) != 1 {
|
||
|
|
t.Fatalf("FindLocalPlanetOccultationSVGs() returned %d diagrams, want 1", len(diagrams))
|
||
|
|
}
|
||
|
|
diagram := diagrams[0]
|
||
|
|
for _, want := range []string{
|
||
|
|
`<svg`, `width="920"`, `height="700"`, "2025-02-01", "指定地点月掩土星",
|
||
|
|
"站心行星轨迹", "圆盘真实比例", "C1 外切始", "C2 内切始", "掩甚", "C3 内切终", "C4 外切终",
|
||
|
|
"C1-C4 接触阶段", "行星圆盘放大示意", "本地接触时刻", "白道", "土星环不参与接触计算",
|
||
|
|
`class="local-planet-track"`, `class="lunar-path-line"`, `class="overview-moon"`,
|
||
|
|
`class="overview-planet-disk"`, `<symbol id="le-moon"`, `href="#le-moon"`,
|
||
|
|
} {
|
||
|
|
if !strings.Contains(diagram, want) {
|
||
|
|
t.Fatalf("local planetary SVG missing %q", want)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if strings.Contains(diagram, "全球掩带") || strings.Contains(diagram, "全球中心线") {
|
||
|
|
t.Fatal("local planetary SVG unexpectedly contains global-path labels")
|
||
|
|
}
|
||
|
|
if got := strings.Count(diagram, `class="stage-moon"`); got != 5 {
|
||
|
|
t.Fatalf("stage Moon count = %d, want 5", got)
|
||
|
|
}
|
||
|
|
if got := strings.Count(diagram, `class="stage-planet-disk"`); got != 5 {
|
||
|
|
t.Fatalf("stage planet count = %d, want 5", got)
|
||
|
|
}
|
||
|
|
if err := validateXML(diagram); err != nil {
|
||
|
|
t.Fatalf("generated local planetary SVG is not valid XML: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestLocalPlanetOccultationSVGStageCentersPreserveDisplayedTangency(t *testing.T) {
|
||
|
|
event := localPlanetOccultationEventFrame{
|
||
|
|
label: "C2",
|
||
|
|
frame: moon.PlanetOccultationDiagramFrame{
|
||
|
|
SeparationArcsec: 970,
|
||
|
|
PositionAngleDeg: 90,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
x, y := localPlanetOccultationSVGStageCenter(event, 100, 100, 40, 6, 0.04)
|
||
|
|
if math.Abs(math.Hypot(x-100, y-100)-(40-6)) > 1e-12 {
|
||
|
|
t.Fatalf("C2 display distance = %.12f, want %.12f", math.Hypot(x-100, y-100), float64(40-6))
|
||
|
|
}
|
||
|
|
event.label = "C4"
|
||
|
|
x, y = localPlanetOccultationSVGStageCenter(event, 100, 100, 40, 6, 0.04)
|
||
|
|
if math.Abs(math.Hypot(x-100, y-100)-(40+6)) > 1e-12 {
|
||
|
|
t.Fatalf("C4 display distance = %.12f, want %.12f", math.Hypot(x-100, y-100), float64(40+6))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestLocalPlanetOccultationSVGEnglishAndCustomText(t *testing.T) {
|
||
|
|
info := localSaturnOccultation(t)
|
||
|
|
diagram, err := LocalPlanetOccultationSVG(info, LocalPlanetOccultationSVGOptions{
|
||
|
|
Language: "en",
|
||
|
|
Location: time.UTC,
|
||
|
|
Title: "Custom local planet title",
|
||
|
|
SummaryText: "Custom local planet summary",
|
||
|
|
GreatestText: "Custom local planet greatest",
|
||
|
|
OverviewTitle: "Custom local planet overview",
|
||
|
|
PhasePanelsTitle: "Custom local planet stages",
|
||
|
|
ContactsTitle: "Custom local planet contacts",
|
||
|
|
DirectionText: "Custom local planet direction",
|
||
|
|
FooterNote: "Custom local planet footer",
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("LocalPlanetOccultationSVG() error = %v", err)
|
||
|
|
}
|
||
|
|
for _, want := range []string{
|
||
|
|
"Custom local planet title", "Custom local planet summary", "Custom local planet greatest",
|
||
|
|
"Custom local planet overview", "Custom local planet stages", "Custom local planet contacts",
|
||
|
|
"Custom local planet direction", "Custom local planet footer",
|
||
|
|
"C1 external ingress", "C2 internal ingress", "Greatest", "C3 internal egress", "C4 external egress",
|
||
|
|
} {
|
||
|
|
if !strings.Contains(diagram, want) {
|
||
|
|
t.Fatalf("English local planetary SVG missing %q", want)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if err := validateXML(diagram); err != nil {
|
||
|
|
t.Fatalf("English local planetary SVG is not valid XML: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestLocalPlanetOccultationSVGRejectsInvalidInput(t *testing.T) {
|
||
|
|
info := localSaturnOccultation(t)
|
||
|
|
_, err := LocalPlanetOccultationSVG(info, LocalPlanetOccultationSVGOptions{Width: 1})
|
||
|
|
if !errors.Is(err, ErrInvalidLocalPlanetOccultationSVGOptions) {
|
||
|
|
t.Fatalf("invalid canvas error = %v, want ErrInvalidLocalPlanetOccultationSVGOptions", err)
|
||
|
|
}
|
||
|
|
info.ContactsComplete = false
|
||
|
|
_, err = LocalPlanetOccultationSVG(info, LocalPlanetOccultationSVGOptions{})
|
||
|
|
if !errors.Is(err, ErrInvalidLocalPlanetOccultationInfo) {
|
||
|
|
t.Fatalf("incomplete event error = %v, want ErrInvalidLocalPlanetOccultationInfo", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestLocalPlanetOccultationSVGRejectsTargetGeometryMismatch(t *testing.T) {
|
||
|
|
info := localSaturnOccultation(t)
|
||
|
|
info.Planet = moon.OccultationVenus
|
||
|
|
info.TargetID = "Venus"
|
||
|
|
_, err := LocalPlanetOccultationSVG(info, LocalPlanetOccultationSVGOptions{})
|
||
|
|
if !errors.Is(err, ErrInvalidLocalPlanetOccultationInfo) {
|
||
|
|
t.Fatalf("mismatched planet error = %v, want ErrInvalidLocalPlanetOccultationInfo", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestLocalPlanetOccultationSVGPartialEventUsesThreeStages(t *testing.T) {
|
||
|
|
events, err := moon.FindPlanetOccultations(
|
||
|
|
time.Date(2024, time.August, 21, 1, 30, 0, 0, time.UTC),
|
||
|
|
time.Date(2024, time.August, 21, 4, 0, 0, 0, time.UTC),
|
||
|
|
moon.OccultationSaturn, -30.072, -6.5, 0, moon.OccultationSearchOptions{},
|
||
|
|
)
|
||
|
|
if err != nil || len(events) != 1 {
|
||
|
|
t.Fatalf("FindPlanetOccultations() = %d events, %v; want one", len(events), err)
|
||
|
|
}
|
||
|
|
diagram, err := LocalPlanetOccultationSVG(events[0], LocalPlanetOccultationSVGOptions{Language: "en"})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("LocalPlanetOccultationSVG() error = %v", err)
|
||
|
|
}
|
||
|
|
if got := strings.Count(diagram, `class="stage-planet-disk"`); got != 3 {
|
||
|
|
t.Fatalf("partial event stage count = %d, want 3", got)
|
||
|
|
}
|
||
|
|
if strings.Contains(diagram, "C2 internal ingress") || strings.Contains(diagram, "C3 internal egress") {
|
||
|
|
t.Fatal("partial event SVG contains internal contacts")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func localSaturnOccultation(t *testing.T) moon.PlanetOccultationInfo {
|
||
|
|
t.Helper()
|
||
|
|
location := time.FixedZone("UTC+8", 8*3600)
|
||
|
|
events, err := moon.FindPlanetOccultations(
|
||
|
|
time.Date(2025, time.February, 1, 0, 0, 0, 0, location),
|
||
|
|
time.Date(2025, time.February, 2, 0, 0, 0, 0, location),
|
||
|
|
moon.OccultationSaturn, 104.52219613, 55.25401991, 0, moon.OccultationSearchOptions{},
|
||
|
|
)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("FindPlanetOccultations() error = %v", err)
|
||
|
|
}
|
||
|
|
if len(events) != 1 {
|
||
|
|
t.Fatalf("FindPlanetOccultations() returned %d events, want 1", len(events))
|
||
|
|
}
|
||
|
|
return events[0]
|
||
|
|
}
|