flatbuffers & transcoders

This commit is contained in:
Ilia Denisov
2026-03-31 19:16:34 +02:00
committed by GitHub
parent 6e01d73a5e
commit f616e3f5ca
75 changed files with 10540 additions and 0 deletions
+355
View File
@@ -0,0 +1,355 @@
package transcoder
import (
"bytes"
"reflect"
"strconv"
"strings"
"testing"
model "galaxy/model/report"
fbs "galaxy/schema/fbs/report"
flatbuffers "github.com/google/flatbuffers/go"
"github.com/google/uuid"
)
func TestReportToPayloadAndPayloadToReportRoundTrip(t *testing.T) {
t.Parallel()
source := sampleReport()
payload, err := ReportToPayload(source)
if err != nil {
t.Fatalf("encode report payload: %v", err)
}
decoded, err := PayloadToReport(payload)
if err != nil {
t.Fatalf("decode report payload: %v", err)
}
expected := reportWireClone(t, source)
if !reflect.DeepEqual(expected, decoded) {
t.Fatalf("round-trip mismatch\nexpected: %#v\ndecoded: %#v", expected, decoded)
}
}
func TestReportToPayloadNilReport(t *testing.T) {
t.Parallel()
_, err := ReportToPayload(nil)
if err == nil {
t.Fatal("expected error for nil report")
}
}
func TestPayloadToReportEmptyData(t *testing.T) {
t.Parallel()
_, err := PayloadToReport(nil)
if err == nil {
t.Fatal("expected error for empty payload")
}
}
func TestPayloadToReportGarbageDataDoesNotPanic(t *testing.T) {
t.Parallel()
_, err := PayloadToReport([]byte{0x01, 0x02, 0x03})
if err == nil {
t.Fatal("expected error for malformed payload")
}
}
func TestPayloadToReportMissingRequiredLocalGroupID(t *testing.T) {
t.Parallel()
payload := buildReportPayload(func(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
class := builder.CreateString("frigate")
state := builder.CreateString("orbit")
fbs.LocalGroupStart(builder)
fbs.LocalGroupAddClass(builder, class)
fbs.LocalGroupAddState(builder, state)
localGroup := fbs.LocalGroupEnd(builder)
fbs.ReportStartLocalGroupVector(builder, 1)
builder.PrependUOffsetT(localGroup)
localGroups := builder.EndVector(1)
fbs.ReportStart(builder)
fbs.ReportAddLocalGroup(builder, localGroups)
return fbs.ReportEnd(builder)
})
_, err := PayloadToReport(payload)
if err == nil {
t.Fatal("expected error for missing local group id")
}
if !strings.Contains(err.Error(), "id is missing") {
t.Fatalf("unexpected error: %v", err)
}
}
func TestPayloadToReportOverflow(t *testing.T) {
t.Parallel()
if strconv.IntSize == 64 {
t.Skip("uint overflow from uint64 is not possible on 64-bit runtime")
}
maxUint := uint64(^uint(0))
overflowValue := maxUint + 1
payload := buildReportPayload(func(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
fbs.ReportStart(builder)
fbs.ReportAddTurn(builder, overflowValue)
return fbs.ReportEnd(builder)
})
_, err := PayloadToReport(payload)
if err == nil {
t.Fatal("expected overflow error")
}
if !strings.Contains(err.Error(), "overflows uint") {
t.Fatalf("unexpected error: %v", err)
}
}
func TestReportToPayloadDeterministicMapEncoding(t *testing.T) {
t.Parallel()
report := sampleReport()
firstPayload, err := ReportToPayload(report)
if err != nil {
t.Fatalf("encode report payload: %v", err)
}
for i := 0; i < 20; i++ {
nextPayload, nextErr := ReportToPayload(report)
if nextErr != nil {
t.Fatalf("encode report payload #%d: %v", i+2, nextErr)
}
if !bytes.Equal(firstPayload, nextPayload) {
t.Fatalf("payload differs between runs at iteration %d", i+2)
}
}
}
func TestReportToPayloadFloat32Quantization(t *testing.T) {
t.Parallel()
source := &model.Report{
Race: "Terrans",
Votes: model.Float(0.123456789),
LocalScience: []model.Science{
{
Name: "science-alpha",
Drive: model.Float(0.123456789),
},
},
}
payload, err := ReportToPayload(source)
if err != nil {
t.Fatalf("encode report payload: %v", err)
}
decoded, err := PayloadToReport(payload)
if err != nil {
t.Fatalf("decode report payload: %v", err)
}
wantVotes := model.Float(float64(float32(source.Votes.F())))
if decoded.Votes != wantVotes {
t.Fatalf("unexpected votes value: got=%v want=%v", decoded.Votes, wantVotes)
}
wantDrive := model.Float(float64(float32(source.LocalScience[0].Drive.F())))
if decoded.LocalScience[0].Drive != wantDrive {
t.Fatalf("unexpected drive value: got=%v want=%v", decoded.LocalScience[0].Drive, wantDrive)
}
if decoded.Votes == source.Votes {
t.Fatal("expected quantization for votes value")
}
}
func sampleReport() *model.Report {
originA := uint(11)
originB := uint(17)
rangeA := model.Float(6.5)
rangeB := model.Float(3.5)
fleetName := "Fleet-1"
return &model.Report{
Version: 2,
Turn: 7,
Width: 64,
Height: 48,
PlanetCount: 21,
Race: "Terrans",
RaceID: uuid.MustParse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"),
Votes: model.Float(7.5),
VoteFor: "Martians",
Player: []model.Player{
{
ID: uuid.MustParse("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"),
Name: "Terrans",
Drive: model.Float(1.5),
Weapons: model.Float(2.0),
Shields: model.Float(2.5),
Cargo: model.Float(3.0),
Population: model.Float(120.0),
Industry: model.Float(90.0),
Planets: 5,
Relation: "-",
Votes: model.Float(7.5),
Extinct: false,
},
},
LocalScience: []model.Science{
{Name: "fusion", Drive: model.Float(1.25), Weapons: model.Float(1.5), Shields: model.Float(1.75), Cargo: model.Float(2.0)},
},
OtherScience: []model.OtherScience{
{Race: "Martians", Science: model.Science{Name: "warp", Drive: model.Float(2.0), Weapons: model.Float(2.25), Shields: model.Float(2.5), Cargo: model.Float(2.75)}},
},
LocalShipClass: []model.ShipClass{
{Name: "frigate", Drive: model.Float(1.5), Armament: 4, Weapons: model.Float(2.0), Shields: model.Float(2.5), Cargo: model.Float(3.0), Mass: model.Float(9.5)},
},
OtherShipClass: []model.OthersShipClass{
{Race: "Martians", ShipClass: model.ShipClass{Name: "destroyer", Drive: model.Float(1.75), Armament: 6, Weapons: model.Float(2.25), Shields: model.Float(2.75), Cargo: model.Float(3.25), Mass: model.Float(10.5)}},
},
Battle: []uuid.UUID{
uuid.MustParse("11111111-1111-1111-1111-111111111111"),
uuid.MustParse("22222222-2222-2222-2222-222222222222"),
},
Bombing: []*model.Bombing{
{
PlanetOwnedID: uuid.MustParse("cccccccc-cccc-cccc-cccc-cccccccccccc"),
Number: 9,
Planet: "Nova",
Owner: "Terrans",
Attacker: "Martians",
Production: "SHIP",
Industry: model.Float(10.5),
Population: model.Float(8.5),
Colonists: model.Float(7.5),
Capital: model.Float(6.5),
Material: model.Float(5.5),
AttackPower: model.Float(4.5),
Wiped: false,
},
},
IncomingGroup: []model.IncomingGroup{
{Origin: 1, Destination: 2, Distance: model.Float(10.0), Speed: model.Float(2.0), Mass: model.Float(20.0)},
},
LocalPlanet: []model.LocalPlanet{
{
UninhabitedPlanet: model.UninhabitedPlanet{
UnidentifiedPlanet: model.UnidentifiedPlanet{X: model.Float(1.0), Y: model.Float(2.0), Number: 3},
Size: model.Float(50.0),
Name: "Terra",
Resources: model.Float(7.0),
Capital: model.Float(8.0),
Material: model.Float(9.0),
},
Industry: model.Float(10.0),
Population: model.Float(11.0),
Colonists: model.Float(12.0),
Production: "SHIP",
FreeIndustry: model.Float(13.0),
},
},
ShipProduction: []model.ShipProduction{
{Planet: 3, Class: "frigate", Cost: model.Float(4.0), ProdUsed: model.Float(2.0), Percent: model.Float(50.0), Free: model.Float(6.0)},
},
Route: []model.Route{
{Planet: 3, Route: map[uint]string{9: "MAT", 2: "CAP", 5: "EMP"}},
},
OtherPlanet: []model.OtherPlanet{
{
Owner: "Martians",
LocalPlanet: model.LocalPlanet{
UninhabitedPlanet: model.UninhabitedPlanet{
UnidentifiedPlanet: model.UnidentifiedPlanet{X: model.Float(4.0), Y: model.Float(5.0), Number: 6},
Size: model.Float(40.0),
Name: "Ares",
Resources: model.Float(6.0),
Capital: model.Float(7.0),
Material: model.Float(8.0),
},
Industry: model.Float(9.0),
Population: model.Float(10.0),
Colonists: model.Float(11.0),
Production: "MAT",
FreeIndustry: model.Float(12.0),
},
},
},
UninhabitedPlanet: []model.UninhabitedPlanet{
{UnidentifiedPlanet: model.UnidentifiedPlanet{X: model.Float(6.0), Y: model.Float(7.0), Number: 8}, Size: model.Float(30.0), Name: "Nadir", Resources: model.Float(5.0), Capital: model.Float(4.0), Material: model.Float(3.0)},
},
UnidentifiedPlanet: []model.UnidentifiedPlanet{
{X: model.Float(8.0), Y: model.Float(9.0), Number: 10},
},
LocalFleet: []model.LocalFleet{
{Name: "Fleet-1", Groups: 2, Destination: 4, Origin: &originA, Range: &rangeA, Speed: model.Float(2.0), State: "moving"},
},
LocalGroup: []model.LocalGroup{
{
OtherGroup: model.OtherGroup{
Number: 1,
Class: "frigate",
Tech: map[string]model.Float{"WEAPONS": model.Float(2.0), "DRIVE": model.Float(1.5), "SHIELDS": model.Float(1.75)},
Cargo: "MAT",
Load: model.Float(4.0),
Destination: 4,
Origin: &originB,
Range: &rangeB,
Speed: model.Float(2.5),
Mass: model.Float(12.0),
},
ID: uuid.MustParse("33333333-3333-3333-3333-333333333333"),
State: "in_orbit",
Fleet: &fleetName,
},
},
OtherGroup: []model.OtherGroup{
{Number: 2, Class: "scout", Tech: map[string]model.Float{"CARGO": model.Float(1.25), "DRIVE": model.Float(1.75)}, Cargo: "CAP", Load: model.Float(3.5), Destination: 5, Speed: model.Float(2.25), Mass: model.Float(8.5)},
},
UnidentifiedGroup: []model.UnidentifiedGroup{
{X: model.Float(10.0), Y: model.Float(11.0)},
},
OnPlanetGroupCache: map[uint][]int{
1: {2, 3},
},
InSpaceGroupRangeCache: map[int]map[uint]float64{
1: {4: 12.5},
},
}
}
func reportWireClone(t *testing.T, source *model.Report) *model.Report {
t.Helper()
data, err := source.MarshalBinary()
if err != nil {
t.Fatalf("marshal source report: %v", err)
}
result := new(model.Report)
if err := result.UnmarshalBinary(data); err != nil {
t.Fatalf("unmarshal source report clone: %v", err)
}
return result
}
func buildReportPayload(build func(*flatbuffers.Builder) flatbuffers.UOffsetT) []byte {
builder := flatbuffers.NewBuilder(256)
offset := build(builder)
fbs.FinishReportBuffer(builder, offset)
return builder.FinishedBytes()
}