Files
Omika/tests/Omika.Tests/Mobile/ModelePackMetadonneesTests.cs
2026-10-23 18:46:50 +02:00

159 lines
5.4 KiB
C#

using Omika.App.Local.Models;
using Omika.App.Local.Services;
namespace Omika.Tests.Mobile;
/// <summary>
/// Tests du jeton de cible des modèles multilingues : lecture du fichier
/// <c>modele.json</c> d'un sens, validation (un jeton malformé est refusé, jamais
/// ignoré en silence) et composition du texte effectivement envoyé au modèle.
///
/// <para>
/// Le pipeline ONNX complet n'est pas exerçable ici : il exige un vocabulaire
/// SentencePiece réel et le moteur d'inférence. La preuve de bout en bout se fait
/// avec <c>Omika.LanguesProbe traduire --dossier &lt;pack&gt;</c>, qui exécute le
/// vrai moteur sur un vrai pack.
/// </para>
/// </summary>
public class ModelePackMetadonneesTests
{
[Fact]
public void DepuisJson_JetonPresent_EstLuAvecSonModele()
{
string json = """
{
"version": 1,
"modeleSource": "Helsinki-NLP/opus-mt-tc-big-itc-itc",
"famille": "itc",
"jetonCible": ">>fra<<"
}
""";
ModelePackMetadonnees? metadonnees = ModelePackMetadonnees.DepuisJson(json);
Assert.NotNull(metadonnees);
Assert.True(metadonnees.AjouteUnJeton);
Assert.Equal(">>fra<<", metadonnees.JetonCible);
Assert.Equal("Helsinki-NLP/opus-mt-tc-big-itc-itc", metadonnees.ModeleSource);
Assert.Equal("itc", metadonnees.Famille);
Assert.Equal(1, metadonnees.Version);
}
[Fact]
public void DepuisJson_SansJeton_EstUnModeleDedie()
{
string json = """{ "modeleSource": "Helsinki-NLP/opus-mt-es-fr" }""";
ModelePackMetadonnees? metadonnees = ModelePackMetadonnees.DepuisJson(json);
Assert.NotNull(metadonnees);
Assert.False(metadonnees.AjouteUnJeton);
Assert.Equal(string.Empty, metadonnees.JetonCible);
}
[Fact]
public void DepuisJson_VersionAbsente_VautUn()
{
ModelePackMetadonnees? metadonnees = ModelePackMetadonnees.DepuisJson("""{ "jetonCible": ">>fra<<" }""");
Assert.NotNull(metadonnees);
Assert.Equal(1, metadonnees.Version);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void DepuisJson_ContenuVide_EstTraiteCommeAbsent(string? json)
{
Assert.Null(ModelePackMetadonnees.DepuisJson(json));
}
[Fact]
public void DepuisJson_JetonEntoureDEspaces_EstNettoye()
{
ModelePackMetadonnees? metadonnees = ModelePackMetadonnees.DepuisJson("""{ "jetonCible": " >>fra<< " }""");
Assert.NotNull(metadonnees);
Assert.Equal(">>fra<<", metadonnees.JetonCible);
}
[Fact]
public void DepuisJson_JetonAvecEspaceInterne_EstRefuse()
{
// Un jeton contenant un espace serait découpé par le tokeniseur : le modèle
// ne le reconnaîtrait pas et produirait sa langue par défaut, sans erreur.
FormatException ex = Assert.Throws<FormatException>(
() => ModelePackMetadonnees.DepuisJson("""{ "jetonCible": ">>fra<< x" }"""));
Assert.Contains("espace", ex.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void DepuisJson_JetonTropLong_EstRefuse()
{
string jeton = ">>" + new string('a', ModelePackMetadonnees.LongueurMaxJeton) + "<<";
FormatException ex = Assert.Throws<FormatException>(
() => ModelePackMetadonnees.DepuisJson($$"""{ "jetonCible": "{{jeton}}" }"""));
Assert.Contains("trop long", ex.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void DepuisJson_VersionFuture_EstRefusee()
{
// Une version inconnue peut changer le SENS du champ : la lire à l'aveugle
// reviendrait à deviner.
FormatException ex = Assert.Throws<FormatException>(
() => ModelePackMetadonnees.DepuisJson("""{ "version": 99, "jetonCible": ">>fra<<" }"""));
Assert.Contains("version", ex.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void DepuisJson_JsonInvalide_EstRefuse()
{
Assert.Throws<FormatException>(() => ModelePackMetadonnees.DepuisJson("{ pas du json"));
}
[Fact]
public void DepuisJson_ProprieteInconnue_EstIgnoree()
{
// Compatibilité ascendante : un pack plus récent peut ajouter des champs.
ModelePackMetadonnees? metadonnees = ModelePackMetadonnees.DepuisJson(
"""{ "jetonCible": ">>fra<<", "champFutur": 42 }""");
Assert.NotNull(metadonnees);
Assert.Equal(">>fra<<", metadonnees.JetonCible);
}
[Fact]
public void InsererJetonCible_AvecIdentifiant_LePlaceEnTete()
{
// Valeurs réelles d'opus-mt-tc-big-itc-itc : >>fra<< = 14666, et la suite
// d'identifiants du modèle (jamais ceux du .spm).
IReadOnlyList<int> ids = OnnxTranslationService.InsererJetonCible([6636, 5, 7228], 14666);
Assert.Equal([14666, 6636, 5, 7228], ids);
}
[Fact]
public void InsererJetonCible_SansIdentifiant_RendLaSequenceTelleQuelle()
{
IReadOnlyList<int> ids = OnnxTranslationService.InsererJetonCible([6636, 5], -1);
Assert.Equal([6636, 5], ids);
}
[Fact]
public void InsererJetonCible_NeModifiePasLaSequenceDOrigine()
{
int[] origine = [6636, 5];
IReadOnlyList<int> resultat = OnnxTranslationService.InsererJetonCible(origine, 14666);
Assert.Equal([6636, 5], origine);
Assert.NotSame(origine, resultat);
}
}