Protocol v8 adds tenths of a °C and precipitation to the clock frame; warmth drains from insulation versus place temperature. Co-authored-by: Cursor <cursoragent@cursor.com>
344 lines
15 KiB
C#
344 lines
15 KiB
C#
namespace HSchool.Content.Tests;
|
|
|
|
public class PeopleDefTests
|
|
{
|
|
private readonly CatalogLoader _loader = new();
|
|
|
|
[Fact]
|
|
public void VanillaCore_LoadsPeopleDefs()
|
|
{
|
|
var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
|
|
var catalog = _loader.Load([CatalogLoader.CorePackId], PackDocuments.FromDirectory(CatalogLoader.CorePackId, root));
|
|
|
|
Assert.True(catalog.Skills.Count >= 10);
|
|
Assert.Equal(12, catalog.Traits.Count);
|
|
Assert.Equal(5, catalog.Needs.Count);
|
|
Assert.True(catalog.BodyAttributes.ContainsKey("Height"));
|
|
Assert.Equal(BodyAttributeKind.Number, catalog.BodyAttributes["Height"].Kind);
|
|
Assert.Equal(BodyAttributeKind.Choice, catalog.BodyAttributes["HairColor"].Kind);
|
|
Assert.All(catalog.Needs.Values, need => Assert.True(need.DecayPerHour > 0));
|
|
Assert.True(catalog.Needs["Sleep"].RestoredOffCampus);
|
|
Assert.True(catalog.Needs["Warmth"].RestoredOffCampus);
|
|
Assert.True(catalog.Needs["Warmth"].Environmental);
|
|
Assert.Equal(0.2f, catalog.Needs["Hunger"].DecayPerHour);
|
|
Assert.True(catalog.Skills["Communication"].Always);
|
|
Assert.True(catalog.Skills["Agility"].Always);
|
|
Assert.True(catalog.Skills["Pedagogy"].Work);
|
|
Assert.Equal(0.4f, catalog.Skills["English"].AdultChance);
|
|
Assert.Equal(
|
|
["RussianLanguage", "BelarusianLanguage", "UkrainianLanguage"],
|
|
catalog.Countries["Russia"].Names.Spoken);
|
|
Assert.Equal(0.6f, catalog.Countries["Russia"].Names.RelatedLanguageChance);
|
|
Assert.Equal(40, catalog.Countries["Russia"].Names.RelatedLanguageMax);
|
|
Assert.Contains(catalog.Subjects["ForeignLanguage"].Skills, share => share.Skill == "English");
|
|
Assert.False(catalog.Skills.ContainsKey("ForeignLanguage"));
|
|
Assert.True(catalog.Countries.ContainsKey("Russia"));
|
|
Assert.True(catalog.Countries["Russia"].Names.MaleGiven.Count >= 20);
|
|
Assert.Equal("Россия", catalog.Label("ru", catalog.Countries["Russia"]));
|
|
Assert.Equal("Russia", catalog.Label("en", catalog.Countries["Russia"]));
|
|
Assert.Equal("Усидчивый", catalog.Label("ru", catalog.Traits["Diligent"]));
|
|
Assert.True(catalog.Subjects.ContainsKey("PrimarySchool"));
|
|
Assert.NotNull(catalog.StaffingRules);
|
|
Assert.Equal(32, catalog.StaffingRules.PoolSize);
|
|
Assert.Equal(20, catalog.StaffingRules.BaseWeeklyHours);
|
|
Assert.Equal(4, catalog.StaffingRules.WeeksPerMonth);
|
|
Assert.Equal(36, catalog.StaffingRules.MaxWeeklyHours);
|
|
Assert.Equal("Начальные классы", catalog.Label("ru", catalog.Subjects["PrimarySchool"]));
|
|
Assert.Equal("Primary", catalog.Label("en", catalog.Subjects["PrimarySchool"]));
|
|
Assert.NotNull(catalog.DayFrame);
|
|
Assert.Equal(3, catalog.DayFrame.LongBreakAfter);
|
|
Assert.Equal("GymHall", catalog.Subjects["PhysicalEducation"].Room);
|
|
Assert.Equal("ComputerLab", catalog.Subjects["Informatics"].Room);
|
|
Assert.Null(catalog.Subjects["Mathematics"].Room);
|
|
}
|
|
|
|
[Fact]
|
|
public void Skill_InheritsRangeFromParent()
|
|
{
|
|
var catalog = _loader.Load(
|
|
[CatalogLoader.CorePackId],
|
|
[
|
|
PackDocuments.Def(
|
|
CatalogLoader.CorePackId,
|
|
"skills",
|
|
"base",
|
|
"""{ "defName": "AcademicBase", "abstract": true, "range": { "min": 0, "max": 100 }, "distribution": { "mean": 40, "stdDev": 10 } }"""),
|
|
PackDocuments.Def(
|
|
CatalogLoader.CorePackId,
|
|
"skills",
|
|
"math",
|
|
"""{ "defName": "Mathematics", "parent": "AcademicBase", "distribution": { "mean": 55, "stdDev": 12 } }"""),
|
|
]);
|
|
|
|
Assert.Equal(0, catalog.Skills["Mathematics"].Range.Min);
|
|
Assert.Equal(100, catalog.Skills["Mathematics"].Range.Max);
|
|
Assert.Equal(55, catalog.Skills["Mathematics"].Distribution?.Mean);
|
|
Assert.False(catalog.Skills["Mathematics"].Abstract);
|
|
Assert.True(catalog.Skills["AcademicBase"].Abstract);
|
|
}
|
|
|
|
[Fact]
|
|
public void TraitPatch_AddsAnIncompatibility()
|
|
{
|
|
var catalog = _loader.Load(
|
|
[CatalogLoader.CorePackId, "addon"],
|
|
[
|
|
PackDocuments.Def(CatalogLoader.CorePackId, "skills", "math", """{ "defName": "Mathematics" }"""),
|
|
PackDocuments.Def(
|
|
CatalogLoader.CorePackId,
|
|
"traits",
|
|
"kind",
|
|
"""{ "defName": "Kind", "weight": 1, "incompatible": [] }"""),
|
|
PackDocuments.Def(
|
|
CatalogLoader.CorePackId,
|
|
"traits",
|
|
"bully",
|
|
"""{ "defName": "Bully", "weight": 1 }"""),
|
|
PackDocuments.Patch(
|
|
"addon",
|
|
"kind",
|
|
"""{ "target": "Kind", "ops": [ { "op": "add", "path": "/incompatible/-", "value": "Bully" } ] }"""),
|
|
]);
|
|
|
|
Assert.Equal(["Bully"], catalog.Traits["Kind"].Incompatible);
|
|
Assert.Contains("Bully", catalog.TraitIncompatibilities("Kind"));
|
|
Assert.Contains("Kind", catalog.TraitIncompatibilities("Bully"));
|
|
}
|
|
|
|
[Fact]
|
|
public void UnknownBodyLimitAttribute_FailsTheCatalog()
|
|
{
|
|
var ex = Assert.Throws<ContentLoadException>(() => _loader.Load(
|
|
[CatalogLoader.CorePackId],
|
|
[
|
|
PackDocuments.Def(
|
|
CatalogLoader.CorePackId,
|
|
"skills",
|
|
"agility",
|
|
"""{ "defName": "Agility", "bodyLimits": [ { "attribute": "Missing", "value": "X", "max": 10 } ] }"""),
|
|
]));
|
|
|
|
Assert.Contains("Missing", ex.Message);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The derived build is computed from these two by name. A pack that renames them used to
|
|
/// load fine and hand every single person the fallback height and weight — and therefore the
|
|
/// same build, which then drives every body limit on every skill.
|
|
/// </summary>
|
|
[Fact]
|
|
public void BodyAttributesWithoutHeightOrWeight_FailTheCatalog()
|
|
{
|
|
var ex = Assert.Throws<ContentLoadException>(() => _loader.Load(
|
|
[CatalogLoader.CorePackId],
|
|
[
|
|
PackDocuments.Def(
|
|
CatalogLoader.CorePackId,
|
|
"bodies",
|
|
"stature",
|
|
"""{ "defName": "Stature", "kind": "number", "distributions": [ { "distribution": { "mean": 170, "stdDev": 8 } } ] }"""),
|
|
]));
|
|
|
|
Assert.Contains("Height", ex.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void UnknownBuildValue_FailsTheCatalog()
|
|
{
|
|
var ex = Assert.Throws<ContentLoadException>(() => _loader.Load(
|
|
[CatalogLoader.CorePackId],
|
|
[
|
|
PackDocuments.Def(
|
|
CatalogLoader.CorePackId,
|
|
"skills",
|
|
"agility",
|
|
"""{ "defName": "Agility", "bodyLimits": [ { "attribute": "Build", "value": "Huge", "max": 10 } ] }"""),
|
|
]));
|
|
|
|
Assert.Contains("Huge", ex.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void UnknownTraitIncompatibility_FailsTheCatalog()
|
|
{
|
|
var ex = Assert.Throws<ContentLoadException>(() => _loader.Load(
|
|
[CatalogLoader.CorePackId],
|
|
[
|
|
PackDocuments.Def(
|
|
CatalogLoader.CorePackId,
|
|
"traits",
|
|
"kind",
|
|
"""{ "defName": "Kind", "incompatible": ["Ghost"] }"""),
|
|
]));
|
|
|
|
Assert.Contains("Ghost", ex.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void Subject_UnknownSkill_FailsTheCatalog()
|
|
{
|
|
var ex = Assert.Throws<ContentLoadException>(() => _loader.Load(
|
|
[CatalogLoader.CorePackId],
|
|
[
|
|
PackDocuments.Def(CatalogLoader.CorePackId, "skills", "math", """{ "defName": "Mathematics" }"""),
|
|
PackDocuments.Def(
|
|
CatalogLoader.CorePackId,
|
|
"subjects",
|
|
"ghost",
|
|
"""
|
|
{
|
|
"defName": "GhostSubject",
|
|
"grades": { "min": 1, "max": 4 },
|
|
"hoursPerWeek": 2,
|
|
"skills": [{ "skill": "Missing", "share": 1 }]
|
|
}
|
|
"""),
|
|
]));
|
|
|
|
Assert.Contains("Missing", ex.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void OneWayIncompatibility_IsMutual()
|
|
{
|
|
var catalog = _loader.Load(
|
|
[CatalogLoader.CorePackId],
|
|
[
|
|
PackDocuments.Def(CatalogLoader.CorePackId, "traits", "a", """{ "defName": "Diligent", "weight": 1, "incompatible": ["Lazy"] }"""),
|
|
PackDocuments.Def(CatalogLoader.CorePackId, "traits", "b", """{ "defName": "Lazy", "weight": 1 }"""),
|
|
]);
|
|
|
|
Assert.Equal(["Lazy"], catalog.Traits["Diligent"].Incompatible);
|
|
Assert.Empty(catalog.Traits["Lazy"].Incompatible);
|
|
Assert.Contains("Lazy", catalog.TraitIncompatibilities("Diligent"));
|
|
Assert.Contains("Diligent", catalog.TraitIncompatibilities("Lazy"));
|
|
}
|
|
|
|
[Fact]
|
|
public void ExtraPack_AddsACountryAndTrait()
|
|
{
|
|
var catalog = _loader.Load(
|
|
[CatalogLoader.CorePackId, "addon"],
|
|
[
|
|
PackDocuments.Def(CatalogLoader.CorePackId, "traits", "kind", """{ "defName": "Kind", "weight": 1 }"""),
|
|
PackDocuments.Def(
|
|
CatalogLoader.CorePackId,
|
|
"climates",
|
|
"temperate",
|
|
"""{ "defName": "TemperateContinental", "monthlyNorms": [0,0,0,0,0,0,0,0,0,0,0,0] }"""),
|
|
PackDocuments.Def(
|
|
CatalogLoader.CorePackId,
|
|
"countries",
|
|
"russia",
|
|
"""
|
|
{
|
|
"defName": "Russia",
|
|
"climatePresets": ["TemperateContinental"],
|
|
"names": {
|
|
"maleGiven": [{ "form": "Иван" }],
|
|
"femaleGiven": [{ "form": "Анна", "declension": "a" }],
|
|
"surnames": [{ "male": "Иванов", "female": "Иванова" }]
|
|
}
|
|
}
|
|
"""),
|
|
PackDocuments.Def("addon", "traits", "stoic", """{ "defName": "Stoic", "weight": 3 }"""),
|
|
PackDocuments.Def("addon", "traits", "cheerful", """{ "defName": "Cheerful", "weight": 2 }"""),
|
|
PackDocuments.Def(
|
|
"addon",
|
|
"countries",
|
|
"nordic",
|
|
"""
|
|
{
|
|
"defName": "Nordic",
|
|
"climatePresets": ["TemperateContinental"],
|
|
"names": {
|
|
"maleGiven": [{ "form": "Lars", "declension": "indeclinable" }],
|
|
"femaleGiven": [{ "form": "Ingrid", "declension": "indeclinable" }],
|
|
"surnames": [{ "male": "Berg", "female": "Berg", "declension": "indeclinable" }]
|
|
}
|
|
}
|
|
"""),
|
|
PackDocuments.Locale("addon", "ru", """{ "Nordic": "Северный", "Stoic": "Стоик", "Cheerful": "Жизнерадостный" }"""),
|
|
]);
|
|
|
|
Assert.True(catalog.Countries.ContainsKey("Nordic"));
|
|
Assert.True(catalog.Traits.ContainsKey("Stoic"));
|
|
Assert.True(catalog.Traits.ContainsKey("Cheerful"));
|
|
Assert.Equal("Северный", catalog.Label("ru", catalog.Countries["Nordic"]));
|
|
Assert.Equal("Стоик", catalog.Label("ru", catalog.Traits["Stoic"]));
|
|
}
|
|
}
|
|
|
|
public class NameGrammarTests
|
|
{
|
|
[Fact]
|
|
public void HardGiven_FollowsTheConsonantModel()
|
|
{
|
|
var entry = new GivenNameEntry { Form = "Иван", Declension = NameGrammar.Hard };
|
|
|
|
Assert.Equal("Иван", NameGrammar.InflectGiven(entry, GrammaticalCase.Nominative, NameGrammar.Hard));
|
|
Assert.Equal("Ивана", NameGrammar.InflectGiven(entry, GrammaticalCase.Genitive, NameGrammar.Hard));
|
|
Assert.Equal("Ивану", NameGrammar.InflectGiven(entry, GrammaticalCase.Dative, NameGrammar.Hard));
|
|
Assert.Equal("Иваном", NameGrammar.InflectGiven(entry, GrammaticalCase.Instrumental, NameGrammar.Hard));
|
|
}
|
|
|
|
[Fact]
|
|
public void ExplicitTable_OverridesTheModel()
|
|
{
|
|
var entry = new GivenNameEntry
|
|
{
|
|
Form = "Любовь",
|
|
Declension = NameGrammar.Soft,
|
|
Cases = new CaseTable
|
|
{
|
|
Nom = "Любовь",
|
|
Gen = "Любови",
|
|
Dat = "Любови",
|
|
Acc = "Любовь",
|
|
Ins = "Любовью",
|
|
Pre = "Любови",
|
|
},
|
|
};
|
|
|
|
Assert.Equal("Любови", NameGrammar.InflectGiven(entry, GrammaticalCase.Genitive, NameGrammar.Soft));
|
|
Assert.Equal("Любовью", NameGrammar.InflectGiven(entry, GrammaticalCase.Instrumental, NameGrammar.Soft));
|
|
Assert.Equal("Любовя", NameGrammar.InflectGivenForm("Любовь", NameGrammar.Soft, GrammaticalCase.Genitive));
|
|
}
|
|
|
|
[Fact]
|
|
public void OvSurname_UsesGenderedEndings()
|
|
{
|
|
var entry = new SurnameEntry { Male = "Иванов", Female = "Иванова", Declension = NameGrammar.Ov };
|
|
|
|
Assert.Equal("Иванова", NameGrammar.InflectSurname(entry, female: false, GrammaticalCase.Genitive, NameGrammar.Ov));
|
|
Assert.Equal("Ивановой", NameGrammar.InflectSurname(entry, female: true, GrammaticalCase.Genitive, NameGrammar.Ov));
|
|
Assert.Equal("Иванову", NameGrammar.InflectSurname(entry, female: true, GrammaticalCase.Accusative, NameGrammar.Ov));
|
|
}
|
|
|
|
[Fact]
|
|
public void SlavicPatronymic_FromTheFathersName()
|
|
{
|
|
Assert.Equal("Иванович", NameGrammar.Patronymic("Иван", female: false, NameGrammar.SlavicPatronymic));
|
|
Assert.Equal("Ивановна", NameGrammar.Patronymic("Иван", female: true, NameGrammar.SlavicPatronymic));
|
|
Assert.Equal("Андреевич", NameGrammar.Patronymic("Андрей", female: false, NameGrammar.SlavicPatronymic));
|
|
Assert.Equal("Дмитриевич", NameGrammar.Patronymic("Дмитрий", female: false, NameGrammar.SlavicPatronymic));
|
|
Assert.Equal("Ильинична", NameGrammar.Patronymic("Илья", female: true, NameGrammar.SlavicPatronymic));
|
|
}
|
|
|
|
[Fact]
|
|
public void BodyBuild_ComesFromBmiBands()
|
|
{
|
|
Assert.Equal(BodyBuilds.Skinny, BodyBuilds.FromHeightAndWeight(170, 50));
|
|
Assert.Equal(BodyBuilds.Athletic, BodyBuilds.FromHeightAndWeight(170, 68));
|
|
Assert.Equal(BodyBuilds.Obese, BodyBuilds.FromHeightAndWeight(170, 90));
|
|
}
|
|
|
|
[Fact]
|
|
public void Patronymic_InflectsLikeAHardOrAStem()
|
|
{
|
|
Assert.Equal("Ивановича", NameGrammar.InflectPatronymic("Иванович", female: false, GrammaticalCase.Genitive));
|
|
Assert.Equal("Ивановны", NameGrammar.InflectPatronymic("Ивановна", female: true, GrammaticalCase.Genitive));
|
|
Assert.Equal("Ивановну", NameGrammar.InflectPatronymic("Ивановна", female: true, GrammaticalCase.Accusative));
|
|
}
|
|
}
|