Files
h-school/tests/HSchool.Simulation.Tests/PeopleInSchoolTests.cs
T
2026-08-18 23:51:10 +03:00

201 lines
8.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Arch.Core;
using HSchool.Content;
using HSchool.People;
namespace HSchool.Simulation.Tests;
public class PeopleInSchoolTests
{
private static readonly DateTime Start = new(2012, 4, 3, 6, 0, 0, DateTimeKind.Utc);
[Fact]
public void InstallPeople_FillsHomeroomsAndJobs()
{
var (catalog, map) = Vanilla();
var demand = SchoolDemand.From(catalog, map);
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Slavic", Start);
using var school = School.Create(1, "Полная", Start, catalog, map);
school.InstallPeople(roster, seed: 1);
school.Tick(1d / 20d, 5d);
Assert.Same(roster, school.Roster);
Assert.Equal(11, roster.Classes.Count);
Assert.Equal(demand.Seats.Count, roster.People.Count(person => person.IsStudent));
Assert.Equal(demand.Staff.Count, roster.People.Count(person => person.IsStaff));
Assert.True(RosterFit.Matches(roster, demand));
var peopleQuery = new QueryDescription().WithAll<PersonIdentity, PersonNeeds, PersonRoles>();
var classesQuery = new QueryDescription().WithAll<ClassIdentity>();
Assert.Equal(roster.People.Count, school.World.CountEntities(in peopleQuery));
Assert.Equal(11, school.World.CountEntities(in classesQuery));
}
[Fact]
public void CoreNeeds_DoNotMoveOnTick()
{
var (catalog, map) = Vanilla();
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 2, "Slavic", Start);
using var school = School.Create(2, "Нужды", Start, catalog, map);
school.InstallPeople(roster, seed: 2);
for (var i = 0; i < 20; i++)
{
school.Tick(1d / 20d, 5d);
}
var hunger = new List<float>();
var query = new QueryDescription().WithAll<PersonNeeds>();
school.World.Query(in query, (ref PersonNeeds needs) => hunger.Add(needs.Values["Hunger"]));
Assert.NotEmpty(hunger);
Assert.All(hunger, value => Assert.Equal(1f, value));
}
[Fact]
public void NeedDecay_DropsWhenTheDefSaysSo()
{
var catalog = new CatalogLoader().Load(
[CatalogLoader.CorePackId],
[
new ContentDocument(
CatalogLoader.CorePackId,
"defs/needs/hunger.jsonc",
"""{ "defName": "Hunger", "initial": 1, "decayPerHour": 1, "min": 0, "max": 1 }"""),
]);
var world = World.Create();
try
{
world.Create(new PersonNeeds(new Dictionary<string, float>(StringComparer.Ordinal) { ["Hunger"] = 1f }));
NeedDecay.Apply(world, catalog, gameMinutes: 60);
var query = new QueryDescription().WithAll<PersonNeeds>();
world.Query(in query, (ref PersonNeeds needs) => Assert.Equal(0f, needs.Values["Hunger"]));
}
finally
{
World.Destroy(world);
}
}
[Fact]
public void SameSeed_InstallsTheSameNames()
{
var (catalog, map) = Vanilla();
var first = RosterGenerator.Generate(catalog, map, schoolSeed: 9, "Slavic", Start);
var second = RosterGenerator.Generate(catalog, map, schoolSeed: 9, "Slavic", Start);
using var a = School.Create(9, "А", Start, catalog, map);
using var b = School.Create(9, "Б", Start, catalog, map);
a.InstallPeople(first, 9);
b.InstallPeople(second, 9);
Assert.Equal(
first.People.Select(person => $"{person.Name.Surname} {person.Name.Given} {person.Name.Patronymic}"),
second.People.Select(person => $"{person.Name.Surname} {person.Name.Given} {person.Name.Patronymic}"));
}
[Fact]
public void TickPastFirstSeptember_GraduatesTheOldestYearAndKeepsCapacity()
{
var catalog = Catalog();
var map = Classrooms(4);
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Slavic", Start);
var demand = SchoolDemand.From(catalog, map);
var oldest = roster.Classes.Max(schoolClass => schoolClass.Year);
var graduated = roster.Classes
.Where(schoolClass => schoolClass.Year == oldest)
.SelectMany(schoolClass => schoolClass.PupilIds)
.ToHashSet(StringComparer.Ordinal);
using var school = School.Create(1, "Четыре кабинета", Start, catalog, map);
school.InstallPeople(roster, seed: 1, "Slavic");
var sept = new DateTime(2012, 9, 1, 0, 0, 0, DateTimeKind.Utc);
var changed = school.Tick((sept - Start).TotalMinutes / 5d, 5d);
Assert.True(changed);
Assert.True(school.Clock.Time >= sept);
Assert.NotNull(school.Roster);
Assert.True(RosterFit.Matches(school.Roster, demand));
Assert.Equal(roster.People.Count(person => person.IsStudent), school.Roster.People.Count(person => person.IsStudent));
Assert.DoesNotContain(school.Roster.People, person => graduated.Contains(person.Id));
Assert.Contains(school.Roster.Classes, schoolClass => schoolClass.Year == 1);
var peopleQuery = new QueryDescription().WithAll<PersonIdentity>();
var classesQuery = new QueryDescription().WithAll<ClassIdentity>();
Assert.Equal(school.Roster.People.Count, school.World.CountEntities(in peopleQuery));
Assert.Equal(school.Roster.Classes.Count, school.World.CountEntities(in classesQuery));
}
[Fact]
public void TickOnCreateDateOfFirstSeptember_DoesNotIntake()
{
var start = new DateTime(2012, 9, 1, 6, 0, 0, DateTimeKind.Utc);
var catalog = Catalog();
var map = Classrooms(4);
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 3, "Slavic", start);
using var school = School.Create(3, "Первое сентября", start, catalog, map);
school.InstallPeople(roster, seed: 3, "Slavic");
var years = roster.Classes.Select(schoolClass => schoolClass.Year).OrderBy(year => year).ToArray();
var changed = school.Tick(1d / 20d, 5d);
Assert.False(changed);
Assert.Same(roster, school.Roster);
Assert.Equal(years, school.Roster!.Classes.Select(schoolClass => schoolClass.Year).OrderBy(year => year));
}
private static DefCatalog Catalog()
{
var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
return new CatalogLoader().Load([CatalogLoader.CorePackId], PackDocumentsFrom(root));
}
private static MapLayout Classrooms(int count, int desks = 16)
{
var rooms = new RoomNode[count];
for (var i = 0; i < count; i++)
{
rooms[i] = new RoomNode
{
Id = $"classroom-{i:00}",
Def = "Classroom",
Building = "main",
Floor = "floor-1",
Label = $"{101 + i}",
Seats = desks,
};
}
return new MapLayout { Rooms = rooms };
}
private static (DefCatalog Catalog, MapLayout Map) Vanilla()
{
var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
var documents = PackDocumentsFrom(root);
var catalog = new CatalogLoader().Load([CatalogLoader.CorePackId], documents);
var map = CatalogLoader.LastDefaultMap([CatalogLoader.CorePackId], documents);
Assert.NotNull(map);
return (catalog, map);
}
private static List<ContentDocument> PackDocumentsFrom(string packRoot)
{
var documents = new List<ContentDocument>();
foreach (var path in Directory.EnumerateFiles(packRoot, "*.*", SearchOption.AllDirectories))
{
if (!path.EndsWith(".jsonc", StringComparison.OrdinalIgnoreCase)
&& !path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
{
continue;
}
var relative = Path.GetRelativePath(packRoot, path).Replace('\\', '/');
documents.Add(new ContentDocument(CatalogLoader.CorePackId, relative, File.ReadAllText(path)));
}
return documents;
}
}