mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-12 01:37:06 +02:00
buildServeArgs se disait pure, mais la traduction des drapeaux de chargement qu'elle appelle écrivait encore sur stderr quand un --load-mode dio tombait sur un moteur ancien. La note est désormais rendue comme les autres et affichée par cmdServe, au même rang qu'avant (avant la note NGL). - normalizeLoadFlags / downgradeLoadMode rendent la note au lieu de l'écrire. - Table de tests : cas dio sur moteur ancien (drapeau retiré, une note), et TestNormalizeLoadFlags vérifie que seul ce cas produit une note. - Aucun changement de ligne de commande. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
139 lines
4.9 KiB
Go
139 lines
4.9 KiB
Go
package loki
|
|
|
|
import (
|
|
"net"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestHasAnyFlag(t *testing.T) {
|
|
args := []string{"--parallel", "4", "--load-mode=mlock", "-fa"}
|
|
if !hasAnyFlag(args, "--parallel", "-np") {
|
|
t.Fatal("--parallel devrait être détecté")
|
|
}
|
|
// Forme --clé=valeur : le nom seul compte.
|
|
if !hasAnyFlag(args, "--load-mode") {
|
|
t.Fatal("--load-mode=… devrait être détecté")
|
|
}
|
|
if hasAnyFlag(args, "-ngl", "--n-gpu-layers") {
|
|
t.Fatal("-ngl absent, ne devrait pas être détecté")
|
|
}
|
|
// Une VALEUR qui ressemble à un drapeau ne doit pas compter comme tel :
|
|
// ici "-ngl" n'est pas posé, seul --parallel l'est.
|
|
if hasAnyFlag(nil, "--parallel") {
|
|
t.Fatal("liste vide : rien à détecter")
|
|
}
|
|
}
|
|
|
|
func TestNormalizeLoadFlags(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
in []string
|
|
supports bool
|
|
want []string
|
|
}{
|
|
{"moteur ancien : anciens drapeaux intacts", []string{"--mlock", "--no-mmap"}, false,
|
|
[]string{"--mlock", "--no-mmap"}},
|
|
// L'ancien --mlock gardait le mmap : « mlock » de llama.cpp le coupe.
|
|
{"mlock seul → mmap+mlock", []string{"--mlock", "-fa"}, true,
|
|
[]string{"-fa", "--load-mode", "mmap+mlock"}},
|
|
{"no-mmap traduit", []string{"--no-mmap"}, true,
|
|
[]string{"--load-mode", "none"}},
|
|
{"mmap traduit", []string{"--mmap"}, true,
|
|
[]string{"--load-mode", "mmap"}},
|
|
{"mlock + no-mmap → mlock", []string{"--no-mmap", "--mlock"}, true,
|
|
[]string{"--load-mode", "mlock"}},
|
|
{"moteur ancien : none retraduit", []string{"-fa", "--load-mode", "none"}, false,
|
|
[]string{"-fa", "--no-mmap"}},
|
|
{"moteur ancien : mlock retraduit", []string{"--load-mode", "mlock"}, false,
|
|
[]string{"--mlock", "--no-mmap"}},
|
|
{"moteur ancien : mmap+mlock retraduit", []string{"-lm", "mmap+mlock"}, false,
|
|
[]string{"--mlock"}},
|
|
{"moteur ancien : dio abandonné", []string{"--load-mode=dio", "-fa"}, false,
|
|
[]string{"-fa"}},
|
|
{"--load-mode explicite gagne, vieux drapeaux retirés",
|
|
[]string{"--mlock", "--load-mode", "dio"}, true,
|
|
[]string{"--load-mode", "dio"}},
|
|
{"rien à traduire", []string{"-fa", "--n-cpu-moe", "8"}, true,
|
|
[]string{"-fa", "--n-cpu-moe", "8"}},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
got, note := normalizeLoadFlags(c.in, c.supports)
|
|
if !reflect.DeepEqual(got, c.want) {
|
|
t.Fatalf("got %q, want %q", got, c.want)
|
|
}
|
|
// Seul « dio » abandonné sur un moteur ancien se signale : les autres
|
|
// traductions gardent le sens demandé.
|
|
if wantNote := c.name == "moteur ancien : dio abandonné"; (note != "") != wantNote {
|
|
t.Fatalf("note = %q, attendue : %v", note, wantNote)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNGLArgs(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
ngl string
|
|
fitsItself bool
|
|
want []string
|
|
wantNote bool
|
|
}{
|
|
// Le cas qui motive tout : defaultConfig() sème NGL=999 sur chaque
|
|
// installation neuve. Personne ne l'a choisi, et le moteur récent
|
|
// abandonne son calcul de VRAM dès qu'on lui impose un nombre.
|
|
{"sentinelle 999, moteur récent", "999", true, []string{"-ngl", "auto"}, true},
|
|
{"sentinelle 999, moteur ancien", "999", false, []string{"-ngl", "999"}, false},
|
|
{"nombre choisi, jamais touché", "28", true, []string{"-ngl", "28"}, false},
|
|
{"nombre choisi, moteur ancien", "28", false, []string{"-ngl", "28"}, false},
|
|
{"all reste all", "all", true, []string{"-ngl", "all"}, false},
|
|
{"auto : aucun drapeau", "auto", true, nil, false},
|
|
{"auto insensible à la casse", "AUTO", true, nil, false},
|
|
{"espaces parasites autour de la sentinelle", " 999 ", true, []string{"-ngl", "auto"}, true},
|
|
{"clé absente, moteur récent", "", true, []string{"-ngl", "auto"}, false},
|
|
{"clé absente, moteur ancien", "", false, []string{"-ngl", "999"}, false},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
got, note := nglArgs(c.ngl, c.fitsItself)
|
|
if !reflect.DeepEqual(got, c.want) {
|
|
t.Fatalf("args = %q, want %q", got, c.want)
|
|
}
|
|
if (note != "") != c.wantNote {
|
|
t.Fatalf("note = %q, en voulait-on une ? %v", note, c.wantNote)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestArgValueEtPortLibre(t *testing.T) {
|
|
if got := argValue([]string{"--port", "8080", "-c", "1", "--port", "9000"}, "--port"); got != "9000" {
|
|
t.Fatalf("la dernière occurrence doit gagner, got %q", got)
|
|
}
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, port, _ := net.SplitHostPort(ln.Addr().String())
|
|
if waitPortFree("0.0.0.0", port, 600*time.Millisecond) == nil {
|
|
t.Fatal("un port occupé doit être refusé")
|
|
}
|
|
ln.Close()
|
|
if err := waitPortFree("0.0.0.0", port, time.Second); err != nil {
|
|
t.Fatalf("un port libre doit passer : %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSlowKV(t *testing.T) {
|
|
for _, c := range []struct {
|
|
k, v string
|
|
lent bool
|
|
}{{"", "", false}, {"q8_0", "q8_0", false}, {"q4_0", "q4_0", false}, {"q8_0", "q4_0", true}, {"q5_1", "q5_1", true}} {
|
|
if slowKV(c.k, c.v) != c.lent {
|
|
t.Errorf("%s/%s : lent attendu %v", c.k, c.v, c.lent)
|
|
}
|
|
}
|
|
}
|