Skip to content

Commit

Permalink
Fix some unit tests (#340)
Browse files Browse the repository at this point in the history
* Make DI resettable; do so in tests.

* Fix one ability test; don't cancel entirely out of Mod.AssignID if the object already has an ID (it might have an ID but not be in the objects collection).

* Fix a formula test

* Min range should be the specified min range of the weapon

* Fix a different weapon range test that the previous change broke

* Fix a bunch of tests where the game/mod/DI was not initialized correctly

* Fix cloaking tests, but a few of them fail when run as part of the group of tests (but pass when run alone)
  • Loading branch information
ekolis authored Dec 1, 2024
1 parent 564c4a6 commit c9f226a
Show file tree
Hide file tree
Showing 16 changed files with 145 additions and 135 deletions.
8 changes: 3 additions & 5 deletions FrEee.Core.Domain/Extensions/ChecksExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,6 @@ from subcloak in gj.DefaultIfEmpty()
int obscurationLevel = 0;
if (sobj.CanBeObscured)
{
if (sobj.StarSystem.Name == "Citronelle")
{

}
var so = sys.GetEmpireAbilityValue(sobj.Owner, "System - Sight Obscuration");
obscurationLevel = new[]
{
Expand All @@ -144,7 +140,9 @@ from subcloak in gj.DefaultIfEmpty()
sec.GetEmpireAbilityValue(sobj.Owner, "Sector - Sight Obscuration"),
}.Max(a => a.ToInt());
}
return (cloaks.Any() || obscurationLevel > 0) && joined.All(j => j.CloakLevel > j.SensorLevel || obscurationLevel > j.SensorLevel);
return (cloaks.Any() || obscurationLevel > 0)
&& joined.All(j =>
j.CloakLevel > j.SensorLevel || obscurationLevel > j.SensorLevel);
}

/// <summary>
Expand Down
8 changes: 4 additions & 4 deletions FrEee.Core.Domain/Modding/Loaders/ComponentLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public override IEnumerable<IModObject> Load(Mod mod)
else
w.Targets = ParseWeaponTargets(rec.Get<string>("Weapon Target", c), @"\", rec);

w.MinRange = rec.Get<int>(new string[] { "Min Range", "Minimum Range", "Weapon Min Range", "Weapon Minimum Range" }, c) ?? 0;
w.MinRange = rec.Get<int>(new string[] { "Min Range", "Minimum Range", "Weapon Min Range", "Weapon Minimum Range" }, c) ?? 1;
w.MaxRange = rec.Get<int>(new string[] { "Max Range", "Maximum Range", "Weapon Max Range", "Weapon Maximum Range" }, c) ?? 20;
var dmgfield = rec.FindField(new string[] { "Damage", "Weapon Damage", "Damage At Rng", "Weapon Damage At Rng", "Damage At Range", "Weapon Damage At Range" }, ref index);
if (dmgfield.Value.StartsWith("="))
Expand All @@ -140,7 +140,7 @@ public override IEnumerable<IModObject> Load(Mod mod)
{
if (split[i].ToInt() == 0)
continue;
dict[i + 1] = split[i].ToInt();
dict[i + w.MinRange] = split[i].ToInt();
}

// HACK - SE4 doesn't explicitly specify damage at range zero so copy the damage at range one value
Expand All @@ -150,8 +150,8 @@ public override IEnumerable<IModObject> Load(Mod mod)
w.MinRange = 0;
}

w.MinRange = dict.Keys.Min();
w.MaxRange = dict.Keys.Max();
w.MinRange = dict.Where(q => q.Value != 0).Min(q => q.Key);
w.MaxRange = dict.Where(q => q.Value != 0).Max(q => q.Key);

w.Damage = dict.BuildMultiConditionalLessThanOrEqual(c, "range", 0);
}
Expand Down
86 changes: 46 additions & 40 deletions FrEee.Core.Domain/Modding/Mod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,62 +242,68 @@ public IEnumerable<string> DesignNamesFiles
}
}

public void AssignID(IModObject mo, ICollection<string> used)
public void AssignID(IModObject mo, ICollection<string>? used = null)
{
if (mo.ModID != null)
return;
lock (locker)
{
var fullname = mo.GetType().Name + " " + mo.Name;
if (mo.Name != null && !used.Contains(fullname))
if (mo.ModID is null)
{
mo.ModID = fullname;
used.Add(mo.ModID);
}
else
{
// tack a number on
int lastnum;
string name;
if (mo.Name == null)
name = "Generic " + mo.GetType().Name;
else
name = mo.GetType().Name + " " + mo.Name;
var lastword = name.LastWord();
if (int.TryParse(lastword, out lastnum))
if (used is null)
{
// has a number, count from that number
used = Objects.Select(q => q.ModID).ToList();
}
else

var fullname = mo.GetType().Name + " " + mo.Name;
if (mo.Name != null && !used.Contains(fullname))
{
lastnum = -1; // no number, start from 1
mo.ModID = fullname;
used.Add(mo.ModID);
}
for (var num = lastnum + 1; num <= int.MaxValue; num++)
else
{
string exceptnum;
if (lastnum < 0 && num == 0)
// tack a number on
int lastnum;
string name;
if (mo.Name == null)
name = "Generic " + mo.GetType().Name;
else
name = mo.GetType().Name + " " + mo.Name;
var lastword = name.LastWord();
if (int.TryParse(lastword, out lastnum))
{
exceptnum = name;
num = 1;
// has a number, count from that number
}
else if (lastnum < 0)
exceptnum = name;
else
exceptnum = name.Substring(0, name.Length - lastword.Length - 1);
var withnextnum = exceptnum + " " + num;
if (!used.Contains(withnextnum))
{
mo.ModID = withnextnum;
used.Add(withnextnum);
break;
lastnum = -1; // no number, start from 1
}
for (var num = lastnum + 1; num <= int.MaxValue; num++)
{
string exceptnum;
if (lastnum < 0 && num == 0)
{
exceptnum = name;
num = 1;
}
else if (lastnum < 0)
exceptnum = name;
else
exceptnum = name.Substring(0, name.Length - lastword.Length - 1);
var withnextnum = exceptnum + " " + num;
if (!used.Contains(withnextnum))
{
mo.ModID = withnextnum;
used.Add(withnextnum);
break;
}
if (num == int.MaxValue)
throw new Exception("Can't assign mod ID to " + name + "; there's a gazillion other mod objects with that name.");
}
if (num == int.MaxValue)
throw new Exception("Can't assign mod ID to " + name + "; there's a gazillion other mod objects with that name.");
}
}

if (mo.ModID == null)
throw new Exception("Failed to assign mod ID to {0}: {1}".F(mo.GetType(), mo));
if (mo.ModID == null)
throw new Exception("Failed to assign mod ID to {0}: {1}".F(mo.GetType(), mo));
}

if (!objects.Contains(mo))
objects.Add(mo);
Expand Down
2 changes: 1 addition & 1 deletion FrEee.Core.Domain/Objects/GameState/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public Game()
/// <summary>
/// The galaxy in which this game is played.
/// </summary>
public Galaxy Galaxy { get; internal set; }
public Galaxy Galaxy { get; set; }

/// <summary>
/// The battles which have taken place this turn.
Expand Down
58 changes: 29 additions & 29 deletions FrEee.Core.Utility/Utility/DI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace FrEee.Utility
/// </summary>
public static class DI
{
private static readonly HostApplicationBuilder builder = Host.CreateApplicationBuilder();
private static HostApplicationBuilder builder = Host.CreateApplicationBuilder();

private static IHost? host;

Expand All @@ -27,20 +27,11 @@ public static void RegisterSingleton<TInterface, TImplementation>()
where TInterface : class
where TImplementation : class, TInterface
{
Task.Run(async () =>
if (host is not null)
{
if (host is not null)
{
await host.StopAsync();
}

builder.Services.AddSingleton<TInterface, TImplementation>();

if (host is not null)
{
await Run();
}
});
throw new InvalidOperationException("Can't register a service while the service host is running.");
}
builder.Services.AddSingleton<TInterface, TImplementation>();
}

/// <summary>
Expand All @@ -53,20 +44,11 @@ public static void RegisterTransient<TInterface, TImplementation>()
where TInterface : class
where TImplementation : class, TInterface
{
Task.Run(async () =>
if (host is not null)
{
if (host is not null)
{
await host.StopAsync();
}

builder.Services.AddTransient<TInterface, TImplementation>();

if (host is not null)
{
await Run();
}
});
throw new InvalidOperationException("Can't register a service while the service host is running.");
}
builder.Services.AddTransient<TInterface, TImplementation>();
}

/// <summary>
Expand All @@ -80,6 +62,21 @@ public async static Task Run()
await host.RunAsync();
}

/// <summary>
/// Resets the dependency injection host.
/// Unregisters all services.
/// </summary>
/// <returns></returns>
public async static Task Reset()
{
if (host is not null)
{
await host.StopAsync();
host = null;
builder = new HostApplicationBuilder();
}
}

/// <summary>
/// Gets an instance of a registered service.
/// </summary>
Expand All @@ -90,9 +87,12 @@ public static TInterface Get<TInterface>()
{
if (host is null)
{
throw new InvalidOperationException("Can't get items from DI without first calling Run.");
// start services (don't await, run in background)
Run();
}
var result = host.Services.GetService<TInterface>();

TInterface? result = host.Services.GetService<TInterface>();

return result ?? throw new InvalidOperationException($"No service of type {typeof(TInterface)} is registered.");
}
}
Expand Down
3 changes: 3 additions & 0 deletions FrEee.Root/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public static void ConfigureDI()
{
// TODO: load dependencies from configuration file in mod data so we can really modularize this thing!

// reset in case DI as already running (e.g. unit test suite)
DI.Reset();

// processes
DI.RegisterSingleton<ITurnProcessor, TurnProcessor>();
DI.RegisterSingleton<IBattleFactory, BattleFactory>();
Expand Down
9 changes: 9 additions & 0 deletions FrEee.Tests/Modding/FormulaTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using FrEee.Vehicles;
using FrEee.Vehicles.Types;
using FrEee.Utility;
using FrEee.Root;

namespace FrEee.Modding;

Expand All @@ -24,30 +25,38 @@ public class FormulaTest
[Test]
public void DynamicFormula()
{
Configuration.ConfigureDI();
var gal = new Game();
Empire emp = new Empire();
Mod.Current = new Mod();

var armor = new ComponentTemplate();
armor.Name = armor.ModID = "Armor";
armor.Size = 10;
armor.Durability = new ComputedFormula<int>("self.Size * 3", armor, true);
Mod.Current.ComponentTemplates.Add(armor);
Mod.Current.AssignID(armor);

var mount = new Mount();
mount.ModID = mount.Name = "Scale Mount";
mount.DurabilityPercent = 200;
mount.SizePercent = new ComputedFormula<int>("design.Hull.Size", mount, true);
Mod.Current.Mounts.Add(mount);
Mod.Current.AssignID(mount);

var hull = DIRoot.Hulls.Build(VehicleTypes.Ship);
hull.ModID = hull.Name = "Generic Hull";
hull.Size = 150;
Mod.Current.Hulls.Add(hull);
Mod.Current.AssignID(hull);

var design = DIRoot.Designs.Build(hull);
Game.Current.AssignID(design);
var mct = new MountedComponentTemplate(design, armor, mount);
design.Components.Add(mct);
mct.Container = design;
design.Owner = emp;

Assert.AreEqual(30, armor.Durability.Value); // 10 * 3
Assert.AreEqual(mct.Durability, 60); // 30 * 200%
Assert.AreEqual(15, mct.Size); // 10 * 150%
Expand Down
11 changes: 8 additions & 3 deletions FrEee.Tests/Objects/Abilities/AbilityTest.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.Drawing;
using FrEee.Extensions;
using FrEee.Modding;
using FrEee.Modding.Abilities;
using FrEee.Modding.Templates;
using FrEee.Objects.Civilization;
using FrEee.Objects.GameState;
using FrEee.Objects.Space;
using FrEee.Root;
using FrEee.Vehicles;
using FrEee.Vehicles.Types;
using NUnit.Framework;
Expand All @@ -25,16 +27,19 @@ public class AbilityTest
[SetUp]
public void SetUp()
{
// create galaxy
TestUtilities.CreateGalaxyWithMod();
// configure DI
Configuration.ConfigureDI();

// create game
TestUtilities.Initialize();

// create star system
sys = new(0);
Galaxy.Current.StarSystemLocations.Add(new(sys, new Point()));

// create stuff
emp = TestUtilities.CreateEmpire();
hull = TestUtilities.CreateHull(VehicleTypes.Ship, design.Name);
hull = TestUtilities.CreateHull(VehicleTypes.Ship);
design = TestUtilities.CreateDesign(emp, hull);
ship = TestUtilities.CreateVehicle(design, emp);
}
Expand Down
7 changes: 5 additions & 2 deletions FrEee.Tests/Objects/Orders/ConstructionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using FrEee.Objects.GameState;
using FrEee.Objects.Space;
using FrEee.Objects.Technology;
using FrEee.Root;
using FrEee.Utility;
using NUnit.Framework;

Expand All @@ -25,9 +26,11 @@ public class ConstructionTest
[OneTimeSetUp]
public static void ClassInit()
{
// initalize DI
Configuration.ConfigureDI();

// initialize galaxy
new Game();
new ModLoader().Load(null);
TestUtilities.Initialize();

// initialize empires
empire = new Empire();
Expand Down
Loading

0 comments on commit c9f226a

Please sign in to comment.