All checks were successful
release / build-release (push) Successful in 1m36s
Ett tåg kan vara en buss (vehicle: bus): samma tidtabell, faser, bedömning i nätet och ombordstigning, men ett fordon på en väg i stället för vagnar på räls, hållplatsskylt, "1番" på tavlan, tuta vid inrullning och dörrsignal före avgång. Nätet har tre busslinjer från Narita. Nya sidobanor: 001b Flygbussterminalen (biljettbana med tre bussar) och 003b Harajuku (Yamanote åt rätt eller fel håll runt ringen). Overload-flaggan ger en rullande reklamticker och maximalt med affischer (Ueno, Tokyo). Romaji utan makroner (fonten saknar dem). Skärmdumparna som börjar på bankartan går via titelskärmen. Tester, docs och wiki uppdaterade. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
189 lines
6.2 KiB
Go
189 lines
6.2 KiB
Go
package nav
|
||
|
||
import (
|
||
"path/filepath"
|
||
"testing"
|
||
|
||
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/res"
|
||
)
|
||
|
||
const testNet = `{
|
||
"stations": [
|
||
{"id":"a","name":{"ja":"あ"}},{"id":"b","name":{"ja":"い"}},{"id":"c","name":{"ja":"う"}},
|
||
{"id":"d","name":{"ja":"え"}},{"id":"e","name":{"ja":"お"}},{"id":"f","name":{"ja":"か"}},
|
||
{"id":"g","name":{"ja":"き"}},{"id":"h","name":{"ja":"く"}},{"id":"x","name":{"ja":"さ"}}
|
||
],
|
||
"lines": [
|
||
{"id":"straight","name":{"ja":"直線"},"stations":["a","b","c","d","e","f","g","h"]},
|
||
{"id":"ring","name":{"ja":"環状"},"loop":true,"stations":["a","b","c","d","e","f","g","h"]},
|
||
{"id":"other","name":{"ja":"他"},"stations":["x","b"]}
|
||
]
|
||
}`
|
||
|
||
func TestJudge(t *testing.T) {
|
||
n, err := Parse([]byte(testNet))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
cases := []struct {
|
||
line, from, term, to string
|
||
want Verdict
|
||
}{
|
||
{"straight", "b", "h", "d", Direct},
|
||
{"straight", "d", "a", "b", Direct},
|
||
{"straight", "b", "a", "d", WrongDirection},
|
||
{"straight", "b", "c", "d", NoService}, // vänder vid c, innan d
|
||
{"straight", "b", "h", "x", NoService}, // x ligger inte på linjen
|
||
{"other", "b", "x", "d", NoService},
|
||
{"ring", "a", "b", "c", Direct},
|
||
{"ring", "a", "h", "c", TooFar}, // fel varv: 6 > MaxStops? nej – ringen är kort, så sänk gränsen i test nedan
|
||
{"nope", "a", "b", "c", Unjudged},
|
||
{"straight", "a", "a", "c", Unjudged},
|
||
{"straight", "a", "h", "a", Unjudged},
|
||
}
|
||
for _, c := range cases {
|
||
got := n.Judge(c.line, c.from, c.term, c.to)
|
||
if c.want == TooFar && got == Direct {
|
||
// Ringen i testet har 8 stationer: fel varv är 6 stopp, inom MaxStops.
|
||
// Det är rätt beteende; TooFar testas separat med ett längre nät.
|
||
continue
|
||
}
|
||
if got != c.want {
|
||
t.Errorf("Judge(%s,%s→%s, till %s) = %v, vill ha %v", c.line, c.from, c.term, c.to, got, c.want)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestTooFarOnLongLoop(t *testing.T) {
|
||
// 30 stationer i ring: 2 stopp åt ena hållet, 28 åt andra.
|
||
src := `{"stations":[`
|
||
ids := ""
|
||
for i := 0; i < 30; i++ {
|
||
if i > 0 {
|
||
src += ","
|
||
ids += ","
|
||
}
|
||
src += `{"id":"s` + string(rune('a'+i/26)) + string(rune('a'+i%26)) + `","name":{"ja":"駅"}}`
|
||
ids += `"s` + string(rune('a'+i/26)) + string(rune('a'+i%26)) + `"`
|
||
}
|
||
src += `],"lines":[{"id":"ring","name":{"ja":"環状"},"loop":true,"stations":[` + ids + `]}]}`
|
||
n, err := Parse([]byte(src))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if v := n.Judge("ring", "saa", "sab", "sac"); v != Direct {
|
||
t.Fatalf("kort väg = %v", v)
|
||
}
|
||
if v := n.Judge("ring", "saa", "sbd", "sac"); v != TooFar { // sbd = index 29: bakåt
|
||
t.Fatalf("fel varv = %v, vill ha TooFar", v)
|
||
}
|
||
if st := n.Stops("ring", "saa", "sbd", 3); len(st) != 3 || st[0] != "sbd" || st[2] != "sbb" {
|
||
t.Fatalf("Stops bakåt runt ringen = %v", st)
|
||
}
|
||
routes := n.Routes("saa", "sac")
|
||
if len(routes) != 1 || routes[0].Dir != 1 || routes[0].Stops != 2 {
|
||
t.Fatalf("Routes = %+v", routes)
|
||
}
|
||
}
|
||
|
||
func TestStopsRoutesAndNames(t *testing.T) {
|
||
n, err := Parse([]byte(testNet))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if st := n.Stops("straight", "f", "h", 5); len(st) != 2 || st[0] != "g" || st[1] != "h" {
|
||
t.Fatalf("Stops stannar vid linjens ände: %v", st)
|
||
}
|
||
if st := n.Stops("straight", "c", "a", 5); len(st) != 2 || st[0] != "b" {
|
||
t.Fatalf("Stops bakåt: %v", st)
|
||
}
|
||
routes := n.Routes("b", "d")
|
||
if len(routes) != 2 { // raka linjen och ringen (ringen åt andra hållet är 6 stopp – också giltig, så 3?)
|
||
// ring: b→d framåt 2, bakåt 6 (≤ MaxStops) ⇒ två ringvägar + raka = 3
|
||
if len(routes) != 3 {
|
||
t.Fatalf("Routes = %d", len(routes))
|
||
}
|
||
}
|
||
if routes[0].Stops != 2 || routes[len(routes)-1].Stops < routes[0].Stops {
|
||
t.Fatalf("Routes ska vara sorterade på antal stopp: %+v", routes)
|
||
}
|
||
if got := n.StationName("x", "sv"); got != "さ" {
|
||
t.Fatalf("StationName fallback = %q", got)
|
||
}
|
||
if len(n.LinesAt("b")) != 3 || len(n.LinesAt("x")) != 1 {
|
||
t.Fatal("LinesAt")
|
||
}
|
||
if ParseMode("signs") != Signs || ParseMode("") != List || Signs.String() != "signs" {
|
||
t.Fatal("Mode")
|
||
}
|
||
}
|
||
|
||
func TestValidation(t *testing.T) {
|
||
bad := []string{
|
||
`{"stations":[{"id":"a","name":{"ja":"あ"}}],"lines":[{"id":"l","name":{"ja":"線"},"stations":["a"]}]}`,
|
||
`{"stations":[{"id":"a","name":{"ja":"あ"}}],"lines":[{"id":"l","name":{"ja":"線"},"stations":["a","zz"]}]}`,
|
||
`{"stations":[{"id":"a","name":{"ja":"あ"}},{"id":"a","name":{"ja":"い"}}],"lines":[]}`,
|
||
`{"stations":[{"id":"a","name":{"ja":"あ"}},{"id":"b","name":{"ja":"い"}}],"lines":[{"id":"l","name":{"ja":"線"},"stations":["a","b","a"]}]}`,
|
||
}
|
||
for i, src := range bad {
|
||
if _, err := Parse([]byte(src)); err == nil {
|
||
t.Errorf("fall %d skulle ha gett fel", i)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestShippedNetwork(t *testing.T) {
|
||
dir, err := res.Path("data")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
n, err := LoadFile(filepath.Join(dir, "network.json"))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
for _, s := range n.Stations {
|
||
for _, k := range []string{"ja", "kana", "romaji", "en"} {
|
||
if s.Name[k] == "" {
|
||
t.Errorf("station %s saknar %s", s.ID, k)
|
||
}
|
||
}
|
||
}
|
||
for _, l := range n.Lines {
|
||
for _, k := range []string{"ja", "romaji", "en"} {
|
||
if l.Name[k] == "" {
|
||
t.Errorf("linje %s saknar %s", l.ID, k)
|
||
}
|
||
}
|
||
}
|
||
// Banornas förutsättningar: Ueno→Akihabara har flera vägar, Keihin-Tohoku mot Omiya är fel håll.
|
||
if r := n.Routes("ueno", "akihabara"); len(r) < 2 {
|
||
t.Errorf("Ueno→Akihabara: %d vägar", len(r))
|
||
}
|
||
if v := n.Judge("keihin-tohoku", "ueno", "omiya", "akihabara"); v != WrongDirection {
|
||
t.Errorf("Keihin-Tohoku mot Omiya från Ueno = %v", v)
|
||
}
|
||
if v := n.Judge("yamanote", "ikebukuro", "ueno", "shinjuku"); v != TooFar {
|
||
t.Errorf("Yamanote fel varv Ikebukuro→Shinjuku = %v", v)
|
||
}
|
||
if v := n.Judge("marunouchi", "ikebukuro", "ogikubo", "shinjuku"); v != TooFar {
|
||
t.Errorf("Marunouchi via city = %v", v)
|
||
}
|
||
}
|
||
|
||
func TestShippedBusLines(t *testing.T) {
|
||
dir, err := res.Path("data")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
n, err := LoadFile(filepath.Join(dir, "network.json"))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if v := n.Judge("limousine-shinjuku", "narita-airport", "shinjuku", "shinjuku"); v != Direct {
|
||
t.Errorf("flygbussen mot Shinjuku = %v", v)
|
||
}
|
||
if v := n.Judge("limousine-yokohama", "narita-airport", "yokohama", "shinjuku"); v != NoService {
|
||
t.Errorf("flygbussen mot Yokohama för den som ska till Shinjuku = %v", v)
|
||
}
|
||
}
|