108 lines
3.7 KiB
Go
108 lines
3.7 KiB
Go
package docs_test
|
|
|
|
import (
|
|
"os"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"halcyon/docs"
|
|
"halcyon/sim"
|
|
)
|
|
|
|
// portValues maps the names used in the manual and in examples/programs/ports.s
|
|
// to the simulator's real port numbers, so the documentation cannot drift.
|
|
var portValues = map[string]int{
|
|
"TURN": sim.PortTurn, "DAY": sim.PortDay, "TURNS": sim.PortTurns,
|
|
"POSX": sim.PortPosX, "POSY": sim.PortPosY, "POSZ": sim.PortPosZ,
|
|
"VELX": sim.PortVelX, "VELY": sim.PortVelY, "VELZ": sim.PortVelZ,
|
|
"THROTTLE": sim.PortThrottle, "AZIMUTH": sim.PortAzimuth, "PITCH": sim.PortPitch,
|
|
"FUEL": sim.PortFuel, "MASS": sim.PortMass,
|
|
"SELECT": sim.PortScanSelect, "NEAREST": sim.PortScanNearest,
|
|
"RELX": sim.PortScanRelX, "RELY": sim.PortScanRelY, "RELZ": sim.PortScanRelZ,
|
|
"RELVX": sim.PortScanRelVX, "RELVY": sim.PortScanRelVY, "RELVZ": sim.PortScanRelVZ,
|
|
"DIST": sim.PortScanDist,
|
|
"ORE0": sim.PortScanOre, "ORE1": sim.PortScanOre + 1, "ORE2": sim.PortScanOre + 2, "ORE3": sim.PortScanOre + 3,
|
|
"MINE": sim.PortMine, "CARGO": sim.PortCargo, "CARGOCAP": sim.PortCargoCap,
|
|
"CARGO0": sim.PortCargoOre, "CARGO1": sim.PortCargoOre + 1, "CARGO2": sim.PortCargoOre + 2, "CARGO3": sim.PortCargoOre + 3,
|
|
"STNX": sim.PortStnRelX, "STNY": sim.PortStnRelY, "STNZ": sim.PortStnRelZ,
|
|
"STNVX": sim.PortStnRelVX, "STNVY": sim.PortStnRelVY, "STNVZ": sim.PortStnRelVZ,
|
|
"SELL": sim.PortSell, "EARNED": sim.PortCredits,
|
|
"UPNEW": sim.PortUplinkNew, "UPLEN": sim.PortUplinkLen,
|
|
"MATHX": sim.PortMathX, "MATHY": sim.PortMathY, "MATHZ": sim.PortMathZ,
|
|
"ATAN2": sim.PortMathAtan2, "HYPOT": sim.PortMathHypot, "NORM3": sim.PortMathNorm3,
|
|
}
|
|
|
|
func section(t *testing.T, md, from, to string) string {
|
|
t.Helper()
|
|
a := strings.Index(md, from)
|
|
if a < 0 {
|
|
t.Fatalf("manual has no %q", from)
|
|
}
|
|
b := len(md)
|
|
if to != "" {
|
|
b = a + strings.Index(md[a:], to)
|
|
}
|
|
return md[a:b]
|
|
}
|
|
|
|
func TestAppendixBMatchesSimulator(t *testing.T) {
|
|
md := string(docs.Markdown)
|
|
rows := regexp.MustCompile(`(?m)^0x([0-9A-F]{2})\s+(\w+)\s`).FindAllStringSubmatch(
|
|
section(t, md, "# Appendix B", "# Appendix C"), -1)
|
|
seen := map[string]bool{}
|
|
for _, r := range rows {
|
|
n, _ := strconv.ParseInt(r[1], 16, 32)
|
|
want, ok := portValues[r[2]]
|
|
if !ok || int(n) != want {
|
|
t.Errorf("Appendix B: %s is 0x%s, simulator says %#x (known=%v)", r[2], r[1], want, ok)
|
|
}
|
|
seen[r[2]] = true
|
|
}
|
|
for name := range portValues {
|
|
if !seen[name] {
|
|
t.Errorf("Appendix B does not document port %s", name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func readExample(t *testing.T, name string) string {
|
|
t.Helper()
|
|
b, err := os.ReadFile("../examples/programs/" + name)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return strings.TrimRight(string(b), "\n")
|
|
}
|
|
|
|
func TestEquateFileMatchesSimulatorAndManual(t *testing.T) {
|
|
ports := readExample(t, "ports.s")
|
|
seen := map[string]bool{}
|
|
for _, m := range regexp.MustCompile(`(?m)^\s*\.equ\s+P_(\w+)\s+0x([0-9A-F]{2})\b`).FindAllStringSubmatch(ports, -1) {
|
|
n, _ := strconv.ParseInt(m[2], 16, 32)
|
|
if want, ok := portValues[m[1]]; !ok || int(n) != want {
|
|
t.Errorf("ports.s: P_%s = 0x%s, simulator says %#x (known=%v)", m[1], m[2], want, ok)
|
|
}
|
|
seen[m[1]] = true
|
|
}
|
|
for name := range portValues {
|
|
if !seen[name] {
|
|
t.Errorf("ports.s lacks P_%s", name)
|
|
}
|
|
}
|
|
// Appendix C prints that file.
|
|
if !strings.Contains(string(docs.Markdown), "```asm\n"+ports+"\n```") {
|
|
t.Error("Appendix C is out of date with examples/programs/ports.s")
|
|
}
|
|
}
|
|
|
|
func TestSampleProgramsMatchExamples(t *testing.T) {
|
|
md := string(docs.Markdown)
|
|
for _, name := range []string{"echo", "telemetry", "pursue"} {
|
|
if !strings.Contains(md, "```asm\n"+readExample(t, name+".s")+"\n```") {
|
|
t.Errorf("Chapter 9 listing of %s is out of date with examples/programs/%s.s", name, name)
|
|
}
|
|
}
|
|
}
|