Files
90CNServer/pvf/internal/pvf/string_reference_workspace_test.go
T
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

204 lines
6.6 KiB
Go

package pvf
import (
"context"
"encoding/binary"
"errors"
"os"
"reflect"
"testing"
)
func workspaceReferenceInput() StringReferenceInput {
tokens := []struct {
typ byte
offset int32
}{{3, 20}, {5, 30}, {7, 30}, {5, 30}, {10, -7}, {6, 20}, {8, 20}, {0, 111}}
raw := make([]byte, len(tokens)*5)
for i, token := range tokens {
raw[i*5] = token.typ
binary.LittleEndian.PutUint32(raw[i*5+1:], uint32(token.offset))
}
// An incomplete trailing token is ignored by the existing parser.
raw = append(raw, 7, 1)
return StringReferenceInput{raw: raw, nameOff: 20, pathOff: 20}
}
func TestStringReferenceWorkspaceReusesWithoutStaleReferences(t *testing.T) {
input := workspaceReferenceInput()
want := []StringReference{
{Offset: -7, Occurrences: 1, TokenTypes: []int32{10}},
{Offset: 20, Occurrences: 5, TokenTypes: []int32{3, 6, 8}, FileFields: []string{"name", "path"}},
{Offset: 30, Occurrences: 3, TokenTypes: []int32{5, 7}},
}
var workspace StringReferenceWorkspace
refs, err := workspace.References(context.Background(), input)
if err != nil || !reflect.DeepEqual(refs, want) {
t.Fatalf("unexpected references: %v, err=%v", refs, err)
}
storage := &refs[0]
workspace.Reset()
if len(workspace.refs) != 0 || len(workspace.byOffset) != 0 || cap(workspace.refs) == 0 {
t.Fatal("reset did not clear logical state and retain small storage")
}
refs, err = workspace.References(context.Background(), StringReferenceInput{nameOff: 30, pathOff: -7})
want = []StringReference{
{Offset: -7, Occurrences: 1, TokenTypes: []int32{}, FileFields: []string{"path"}},
{Offset: 30, Occurrences: 1, TokenTypes: []int32{}, FileFields: []string{"name"}},
}
// Empty slices may reuse prior capacity; only their contents are semantic.
if err != nil || len(refs) != len(want) {
t.Fatalf("second parse failed: refs=%d err=%v", len(refs), err)
}
for i, ref := range refs {
if ref.Offset != want[i].Offset || ref.Occurrences != want[i].Occurrences || len(ref.TokenTypes) != 0 || !reflect.DeepEqual(ref.FileFields, want[i].FileFields) {
t.Fatal("second parse retained stale references, token types or fields")
}
}
if storage != &refs[0] {
t.Fatal("small result storage was not reused")
}
// The original owned-result API must remain independent of workspace reuse
// and later calls, including nested slices.
owned, err := input.References(context.Background())
if err != nil {
t.Fatal(err)
}
other, err := input.References(context.Background())
if err != nil {
t.Fatal(err)
}
other[0].TokenTypes[0] = 99
other[1].FileFields[0] = "changed"
if owned[0].TokenTypes[0] != 10 || owned[1].FileFields[0] != "name" {
t.Fatal("owned results share nested storage")
}
}
func largeWorkspaceReferenceInput() StringReferenceInput {
raw := make([]byte, (maxRetainedStringReferences+10)*5)
for i := 0; i < len(raw)/5; i++ {
raw[i*5] = 7
binary.LittleEndian.PutUint32(raw[i*5+1:], uint32(i))
}
return StringReferenceInput{raw: raw, nameOff: -1, pathOff: -2}
}
func TestStringReferenceWorkspaceDiscardsOversizedStorage(t *testing.T) {
var workspace StringReferenceWorkspace
refs, err := workspace.References(context.Background(), largeWorkspaceReferenceInput())
if err != nil || len(refs) != maxRetainedStringReferences+12 {
t.Fatalf("large file was truncated: refs=%d err=%v", len(refs), err)
}
workspace.Reset()
if workspace.refs != nil || workspace.byOffset != nil {
t.Fatal("oversized workspace survived reset")
}
refs, err = workspace.References(context.Background(), StringReferenceInput{nameOff: 1, pathOff: 2})
if err != nil || len(refs) != 2 || cap(workspace.refs) > maxRetainedStringReferences {
t.Fatal("small parse did not recover after oversized result")
}
}
type workspaceCancelContext struct {
context.Context
checks int
}
func (ctx *workspaceCancelContext) Err() error {
ctx.checks++
if ctx.checks >= 6 {
return context.Canceled
}
return nil
}
func TestStringReferenceWorkspaceCancellationReleasesPartialResult(t *testing.T) {
var workspace StringReferenceWorkspace
ctx := &workspaceCancelContext{Context: context.Background()}
refs, err := workspace.References(ctx, largeWorkspaceReferenceInput())
if !errors.Is(err, context.Canceled) || refs != nil {
t.Fatalf("partial parse was published: refs=%d err=%v", len(refs), err)
}
if workspace.refs != nil || workspace.byOffset != nil {
t.Fatal("canceled parse retained oversized storage")
}
refs, err = workspace.References(context.Background(), workspaceReferenceInput())
if err != nil || len(refs) != 3 {
t.Fatal("workspace could not recover after cancellation")
}
canceled, cancel := context.WithCancel(context.Background())
cancel()
refs, err = workspace.References(canceled, workspaceReferenceInput())
if !errors.Is(err, context.Canceled) || refs != nil || len(workspace.refs) != 0 || len(workspace.byOffset) != 0 {
t.Fatal("already canceled parse retained previous logical state")
}
}
// Opt-in full-file comparison against the independently owned parser. It logs
// only counts and never archive paths, string-pool values or payload bytes.
func TestStringReferenceWorkspaceRealArchive(t *testing.T) {
if os.Getenv("PVF_REFERENCE_TEST") != "1" {
t.Skip("PVF_REFERENCE_TEST=1 required")
}
path := os.Getenv("PVF_TESTFILE")
if path == "" {
t.Fatal("PVF_TESTFILE required")
}
a, err := Open(path)
if err != nil {
t.Fatal("cannot open reference test archive")
}
ctx := context.Background()
scanner := a.NewStringReferenceScanner()
var workspace StringReferenceWorkspace
files, count := 0, 0
for {
index, input, ok, err := scanner.NextInput(ctx)
if err != nil {
t.Fatal("cannot read reference test input")
}
if !ok {
break
}
want, err := input.References(ctx)
if err != nil {
t.Fatalf("owned parser failed for file %d", index)
}
refs, err := workspace.References(ctx, input)
if err != nil || !equalWorkspaceReferences(refs, want) {
t.Fatalf("reference mismatch for file %d", index)
}
files++
count += len(refs)
workspace.Reset()
}
if files != int(a.FileCount()) {
t.Fatalf("missing files: got=%d want=%d", files, a.FileCount())
}
t.Logf("files=%d references=%d", files, count)
}
func equalWorkspaceReferences(left, right []StringReference) bool {
if len(left) != len(right) {
return false
}
for i, ref := range left {
other := right[i]
if ref.Offset != other.Offset || ref.Occurrences != other.Occurrences || len(ref.TokenTypes) != len(other.TokenTypes) || len(ref.FileFields) != len(other.FileFields) {
return false
}
for j, typ := range ref.TokenTypes {
if typ != other.TokenTypes[j] {
return false
}
}
for j, field := range ref.FileFields {
if field != other.FileFields[j] {
return false
}
}
}
return true
}