package pzconfig import ( "encoding/json" "os" "path/filepath" "testing" ) // TestSetJavaMemoryKeepsOnlyXmx: -Xms равным -Xmx заставляет JVM занять весь // объём при старте, и на машине с небольшой ОЗУ сервер падает с «Failed to // allocate initial Java heap». Ставим только верхнюю границу. func TestSetJavaMemoryKeepsOnlyXmx(t *testing.T) { dir := t.TempDir() writeLauncher(t, dir, map[string]any{ "mainClass": "zombie/network/GameServer", "vmArgs": []any{ "-Djava.awt.headless=true", "-XX:+UseZGC", // Значения от прошлой установки должны замениться. "-Xms8g", "-Xmx8g", }, }) if err := SetJavaMemory(dir, "3g", ""); err != nil { t.Fatal(err) } args := launcherArgs(t, dir) var xms, xmx int for _, a := range args { switch { case len(a) > 4 && a[:4] == "-Xms": xms++ t.Errorf("в аргументах остался %s — JVM займёт всю память при старте", a) case a == "-Xmx3g": xmx++ } } if xms != 0 { t.Errorf("-Xms не должен добавляться, найдено вхождений: %d", xms) } if xmx != 1 { t.Errorf("-Xmx3g встречается %d раз, ожидалось 1: %v", xmx, args) } // Чужие аргументы должны остаться нетронутыми. if !contains(args, "-XX:+UseZGC") || !contains(args, "-Djava.awt.headless=true") { t.Errorf("потеряны исходные аргументы: %v", args) } } func TestSetJavaMemoryValidates(t *testing.T) { dir := t.TempDir() writeLauncher(t, dir, map[string]any{"vmArgs": []any{}}) for _, bad := range []string{"4", "4gb", "много", "-4g", "4g; rm -rf /"} { if err := SetJavaMemory(dir, bad, ""); err == nil { t.Errorf("значение %q принято, ожидалась ошибка", bad) } } // Пустые значения — «не трогать», это не ошибка. if err := SetJavaMemory(dir, "", ""); err != nil { t.Errorf("пустое значение вернуло ошибку: %v", err) } for _, good := range []string{"512m", "4g", "8G", "16384M"} { if err := SetJavaMemory(dir, good, ""); err != nil { t.Errorf("значение %q отклонено: %v", good, err) } } // JVM с -Xms больше -Xmx не стартует, поэтому ловим это заранее. if err := SetJavaMemory(dir, "2g", "4g"); err == nil { t.Error("начальный размер больше максимального принят, ожидалась ошибка") } if err := SetJavaMemory(dir, "4g", "2g"); err != nil { t.Errorf("корректная пара 2g/4g отклонена: %v", err) } } func writeLauncher(t *testing.T, dir string, doc map[string]any) { t.Helper() raw, err := json.MarshalIndent(doc, "", " ") if err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, javaLauncherFile), raw, 0o644); err != nil { t.Fatal(err) } } func launcherArgs(t *testing.T, dir string) []string { t.Helper() raw, err := os.ReadFile(filepath.Join(dir, javaLauncherFile)) if err != nil { t.Fatal(err) } var doc struct { VMArgs []string `json:"vmArgs"` } if err := json.Unmarshal(raw, &doc); err != nil { t.Fatal(err) } return doc.VMArgs } func contains(list []string, want string) bool { for _, item := range list { if item == want { return true } } return false } // TestSetJavaMemoryWritesXmsWhenAsked: минимальный размер кучи задаётся // отдельно и попадает в файл, только если его действительно указали. func TestSetJavaMemoryWritesXmsWhenAsked(t *testing.T) { dir := t.TempDir() writeLauncher(t, dir, map[string]any{"vmArgs": []any{"-XX:+UseZGC"}}) if err := SetJavaMemory(dir, "6g", "2g"); err != nil { t.Fatal(err) } args := launcherArgs(t, dir) if !contains(args, "-Xms2g") || !contains(args, "-Xmx6g") { t.Fatalf("границы кучи не выставлены: %v", args) } // Убрали минимум — из файла он тоже должен уйти. if err := SetJavaMemory(dir, "6g", ""); err != nil { t.Fatal(err) } args = launcherArgs(t, dir) for _, a := range args { if len(a) > 4 && a[:4] == "-Xms" { t.Errorf("после очистки минимума остался %s: %v", a, args) } } if !contains(args, "-Xmx6g") { t.Errorf("максимум потерян: %v", args) } } func TestParseMemory(t *testing.T) { cases := map[string]int64{ "": 0, "512m": 512 * 1024 * 1024, "4g": 4 * 1024 * 1024 * 1024, "8G": 8 * 1024 * 1024 * 1024, "1024k": 1024 * 1024, } for input, want := range cases { got, err := ParseMemory(input) if err != nil { t.Errorf("ParseMemory(%q): %v", input, err) continue } if got != want { t.Errorf("ParseMemory(%q) = %d, ожидалось %d", input, got, want) } } if _, err := ParseMemory("4гб"); err == nil { t.Error("значение с кириллицей принято") } }