mirror of
https://github.com/MaSzyna-EU07/StarterNG.git
synced 2026-10-16 23:49:19 +02:00
Move rolling stock and physics into the domain
VehicleDatabase becomes VehicleCatalog, Physics becomes VehiclePhysics, and the textures.txt and .fiz parsers move to Infrastructure. GameLibrary replaces the GameData singleton.
This commit is contained in:
@@ -92,6 +92,17 @@ public sealed class InMemoryFileSystem : IFileSystem
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> GetFilesRecursive(string path, string searchPattern)
|
||||
{
|
||||
string prefix = Normalize(path) + "/";
|
||||
var pattern = ToRegex(searchPattern);
|
||||
return _files.Keys
|
||||
.Where(key => key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(key => pattern.IsMatch(key[(key.LastIndexOf('/') + 1)..]))
|
||||
.OrderBy(key => key, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> GetDirectories(string path)
|
||||
{
|
||||
string prefix = Normalize(path) + "/";
|
||||
|
||||
48
StarterNG.Tests/Fakes/TestInstallation.cs
Normal file
48
StarterNG.Tests/Fakes/TestInstallation.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using StarterNG.Application;
|
||||
using StarterNG.Infrastructure.Adapters;
|
||||
using StarterNG.Infrastructure.Sceneries;
|
||||
using StarterNG.Infrastructure.Vehicles;
|
||||
|
||||
namespace StarterNG.Tests.Fakes;
|
||||
|
||||
/// <summary>
|
||||
/// The production object graph wired over an in-memory installation: the same
|
||||
/// composition as <c>AppServices</c>, with disk, clock and randomness replaced.
|
||||
/// </summary>
|
||||
public sealed class TestInstallation
|
||||
{
|
||||
public const string Root = "game";
|
||||
|
||||
public TestInstallation(InMemoryFileSystem? files = null, DateTime? now = null)
|
||||
{
|
||||
Files = files ?? new InMemoryFileSystem();
|
||||
Paths = new GamePaths(Root);
|
||||
Clock = new FixedClock(now ?? new DateTime(2026, 1, 1, 12, 0, 0));
|
||||
Random = new StubRandom();
|
||||
Log = new FileDiagnosticsLog(Files, Clock, Paths);
|
||||
Strings = new FakeStrings();
|
||||
|
||||
MiniTextures = new MiniTextureIndex(Files, Paths);
|
||||
Physics = new FizPhysicsRepository(Files, Paths, Log);
|
||||
Vehicles = new TexturesTxtVehicleRepository(Files, Paths, Log, new TexturesTxtParser());
|
||||
Sceneries = new SceneryRepository(Files, Paths, Log, new SceneryParser(Clock, Random));
|
||||
MissingAssets = new MissingAssetScanner(Files, Paths);
|
||||
Library = new GameLibrary(Vehicles, Sceneries, MiniTextures, Physics, Log);
|
||||
}
|
||||
|
||||
public InMemoryFileSystem Files { get; }
|
||||
public GamePaths Paths { get; }
|
||||
public FixedClock Clock { get; }
|
||||
public StubRandom Random { get; }
|
||||
public FileDiagnosticsLog Log { get; }
|
||||
public FakeStrings Strings { get; }
|
||||
public MiniTextureIndex MiniTextures { get; }
|
||||
public FizPhysicsRepository Physics { get; }
|
||||
public TexturesTxtVehicleRepository Vehicles { get; }
|
||||
public SceneryRepository Sceneries { get; }
|
||||
public MissingAssetScanner MissingAssets { get; }
|
||||
public GameLibrary Library { get; }
|
||||
|
||||
/// <summary>Path of a file inside the fake installation.</summary>
|
||||
public static string At(params string[] segments) => Path.Combine(new[] { Root }.Concat(segments).ToArray());
|
||||
}
|
||||
75
StarterNG.Tests/GameLibraryTests.cs
Normal file
75
StarterNG.Tests/GameLibraryTests.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using StarterNG.Application;
|
||||
using StarterNG.Tests.Fakes;
|
||||
|
||||
namespace StarterNG.Tests;
|
||||
|
||||
public class GameLibraryTests
|
||||
{
|
||||
private static TestInstallation Complete() =>
|
||||
new(new InMemoryFileSystem()
|
||||
.WithFile(TestInstallation.At("dynamic", "pkp", "ep07", "textures.txt"), "ep07-135.mat=ep07.e3d,ep07")
|
||||
.WithFile(TestInstallation.At("scenery", "kato.scn"), "//$n Katowice")
|
||||
.WithFile(TestInstallation.At("textures", "mini", "ep07.bmp"), "bitmap"));
|
||||
|
||||
[Fact]
|
||||
public void Loading_reads_both_the_rolling_stock_and_the_scenarios()
|
||||
{
|
||||
var library = Complete().Library;
|
||||
|
||||
library.Load();
|
||||
|
||||
Assert.True(library.Loaded);
|
||||
Assert.Single(library.Vehicles.Textures);
|
||||
Assert.Equal("Katowice", Assert.Single(library.Sceneries).Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Loading_reports_progress_through_to_done()
|
||||
{
|
||||
var phases = new List<LoadPhase>();
|
||||
var progress = new Progress<LoadStatus>(status => { lock (phases) phases.Add(status.Phase); });
|
||||
|
||||
Complete().Library.Load(progress);
|
||||
|
||||
SpinWait.SpinUntil(() => { lock (phases) return phases.Contains(LoadPhase.Done); }, TimeSpan.FromSeconds(2));
|
||||
lock (phases)
|
||||
{
|
||||
Assert.Contains(LoadPhase.Vehicles, phases);
|
||||
Assert.Contains(LoadPhase.Done, phases);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Loading_twice_does_not_duplicate_the_scenarios()
|
||||
{
|
||||
var library = Complete().Library;
|
||||
|
||||
library.Load();
|
||||
library.Load();
|
||||
|
||||
Assert.Single(library.Sceneries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reloading_picks_up_a_scenery_added_after_startup()
|
||||
{
|
||||
var installation = Complete();
|
||||
var library = installation.Library;
|
||||
library.Load();
|
||||
|
||||
installation.Files.WithFile(TestInstallation.At("scenery", "gdansk.scn"), "//$n Gdańsk");
|
||||
library.ReloadSceneries();
|
||||
|
||||
Assert.Equal(2, library.Sceneries.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void An_installation_with_no_rolling_stock_is_logged()
|
||||
{
|
||||
var installation = new TestInstallation();
|
||||
|
||||
installation.Library.Load();
|
||||
|
||||
Assert.Contains("textures.txt", installation.Log.Text);
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,10 @@ public class InstallationCheckTests
|
||||
[Fact]
|
||||
public void An_empty_folder_reports_every_missing_game_directory()
|
||||
{
|
||||
var strings = new FakeStrings().With("FaultNoDir", "missing {0}");
|
||||
var check = new InstallationCheck(new GamePaths("game"), new InMemoryFileSystem(), strings);
|
||||
var installation = new TestInstallation();
|
||||
var strings = installation.Strings.With("FaultNoDir", "missing {0}");
|
||||
var check = new InstallationCheck(installation.Paths, installation.Files, strings,
|
||||
installation.Physics, installation.Library);
|
||||
|
||||
var faults = check.Run();
|
||||
|
||||
@@ -29,9 +31,11 @@ public class InstallationCheckTests
|
||||
.WithDirectory(Path.Combine("game", "scenery"))
|
||||
.WithDirectory(Path.Combine("game", "textures"))
|
||||
.WithFile(Path.Combine("game", "data", "load_weights.txt"), "");
|
||||
var strings = new FakeStrings().With("FaultNoDir", "missing {0}");
|
||||
var installation = new TestInstallation(files);
|
||||
var strings = installation.Strings.With("FaultNoDir", "missing {0}");
|
||||
|
||||
var faults = new InstallationCheck(new GamePaths("game"), files, strings).Run();
|
||||
var faults = new InstallationCheck(installation.Paths, files, strings,
|
||||
installation.Physics, installation.Library).Run();
|
||||
|
||||
Assert.DoesNotContain(faults, fault => fault.StartsWith("missing "));
|
||||
Assert.DoesNotContain("FaultNoWeights", faults);
|
||||
|
||||
128
StarterNG.Tests/PhysicsAndAssetsTests.cs
Normal file
128
StarterNG.Tests/PhysicsAndAssetsTests.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using StarterNG.Tests.Fakes;
|
||||
|
||||
namespace StarterNG.Tests;
|
||||
|
||||
public class PhysicsAndAssetsTests
|
||||
{
|
||||
private static string Fiz(string maker, string vehicle, string name) =>
|
||||
TestInstallation.At("dynamic", maker, vehicle, name + ".fiz");
|
||||
|
||||
[Fact]
|
||||
public void A_fiz_yields_the_properties_the_starter_shows()
|
||||
{
|
||||
const string fiz = """
|
||||
Param: M=80000 Vmax=125
|
||||
Dimensions: L=15.9
|
||||
Load: LoadAccepted=Coal MaxLoad=40
|
||||
Engine: EngineType=DieselElectric
|
||||
""";
|
||||
var installation = new TestInstallation(new InMemoryFileSystem()
|
||||
.WithFile(Fiz("pkp", "sm42", "sm42"), fiz));
|
||||
|
||||
var physics = installation.Physics.For("pkp/sm42/", "sm42.e3d");
|
||||
|
||||
Assert.NotNull(physics);
|
||||
Assert.Equal(80000, physics!.Mass);
|
||||
Assert.Equal(125, physics.VMax);
|
||||
Assert.Equal(15.9, physics.Length);
|
||||
Assert.Equal("Coal", physics.LoadAccepted);
|
||||
Assert.Equal(40, physics.MaxLoad);
|
||||
Assert.True(physics.IsDieselEngine);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_vehicle_declaring_one_coupler_gets_the_same_at_both_ends()
|
||||
{
|
||||
var installation = new TestInstallation(new InMemoryFileSystem()
|
||||
.WithFile(Fiz("pkp", "111a", "111a"), "BuffCoupl1: AllowedFlag=3 ControlType=screw"));
|
||||
|
||||
var physics = installation.Physics.For("pkp/111a/", "111a")!;
|
||||
|
||||
Assert.Equal(3, physics.AllowedFlagA);
|
||||
Assert.Equal(3, physics.AllowedFlagB);
|
||||
Assert.Equal("screw", physics.ControlTypeB);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_negative_coupler_flag_is_encoded_as_disabled()
|
||||
{
|
||||
var installation = new TestInstallation(new InMemoryFileSystem()
|
||||
.WithFile(Fiz("pkp", "111a", "111a"), "BuffCoupl1: AllowedFlag=-3"));
|
||||
|
||||
Assert.Equal(131, installation.Physics.For("pkp/111a/", "111a")!.AllowedFlagA);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void An_include_is_followed_and_its_parameters_substituted()
|
||||
{
|
||||
var installation = new TestInstallation(new InMemoryFileSystem()
|
||||
.WithFile(Fiz("pkp", "ep07", "ep07"), "include common.fiz 84000 end")
|
||||
.WithFile(TestInstallation.At("dynamic", "pkp", "ep07", "common.fiz"), "Param: M=(1) Vmax=125"));
|
||||
|
||||
var physics = installation.Physics.For("pkp/ep07/", "ep07")!;
|
||||
|
||||
Assert.Equal(84000, physics.Mass);
|
||||
Assert.Equal(125, physics.VMax);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_dumb_variant_stands_in_when_the_model_has_no_fiz_of_its_own()
|
||||
{
|
||||
var installation = new TestInstallation(new InMemoryFileSystem()
|
||||
.WithFile(Fiz("pkp", "ep07", "ep07dumb"), "Param: M=1000"));
|
||||
|
||||
Assert.NotNull(installation.Physics.For("pkp/ep07/", "ep07"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void An_unknown_model_has_no_physics_and_an_empty_installation_indexes_nothing()
|
||||
{
|
||||
var installation = new TestInstallation();
|
||||
|
||||
Assert.Null(installation.Physics.For("pkp/x/", "nieznany"));
|
||||
installation.Physics.Preload();
|
||||
Assert.Equal(0, installation.Physics.IndexedCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Thumbnails_are_looked_up_by_name_with_a_fallback_to_other()
|
||||
{
|
||||
var installation = new TestInstallation(new InMemoryFileSystem()
|
||||
.WithFile(TestInstallation.At("textures", "mini", "ep07.bmp"), "bitmap")
|
||||
.WithFile(TestInstallation.At("textures", "mini", "other.bmp"), "bitmap"));
|
||||
|
||||
Assert.True(installation.MiniTextures.Has("EP07"));
|
||||
Assert.False(installation.MiniTextures.Has("nieznany"));
|
||||
Assert.EndsWith("ep07.bmp", installation.MiniTextures.PathFor("ep07"));
|
||||
Assert.EndsWith("other.bmp", installation.MiniTextures.PathFor("nieznany"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_livery_whose_skin_and_model_are_absent_is_reported()
|
||||
{
|
||||
var files = new InMemoryFileSystem()
|
||||
.WithFile(TestInstallation.At("dynamic", "pkp", "ep07", "textures.txt"),
|
||||
"ep07-135.mat=ep07.e3d,ep07");
|
||||
var installation = new TestInstallation(files);
|
||||
installation.Vehicles.Load(installation.Library.Vehicles);
|
||||
|
||||
var missing = installation.MissingAssets.Scan(installation.Library.Vehicles);
|
||||
|
||||
Assert.Contains(missing, line => line.Contains("no file:") && line.Contains("ep07-135"));
|
||||
Assert.Contains(missing, line => line.Contains("no model:") && line.Contains("ep07.e3d"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_skin_present_under_an_accepted_extension_is_not_reported()
|
||||
{
|
||||
var files = new InMemoryFileSystem()
|
||||
.WithFile(TestInstallation.At("dynamic", "pkp", "ep07", "textures.txt"),
|
||||
"ep07-135.mat=ep07.e3d,ep07")
|
||||
.WithFile(TestInstallation.At("dynamic", "pkp", "ep07", "ep07-135.bmp"), "bitmap")
|
||||
.WithFile(TestInstallation.At("dynamic", "pkp", "ep07", "ep07.t3d"), "model");
|
||||
var installation = new TestInstallation(files);
|
||||
installation.Vehicles.Load(installation.Library.Vehicles);
|
||||
|
||||
Assert.Empty(installation.MissingAssets.Scan(installation.Library.Vehicles));
|
||||
}
|
||||
}
|
||||
103
StarterNG.Tests/TexturesTxtParserTests.cs
Normal file
103
StarterNG.Tests/TexturesTxtParserTests.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using StarterNG.Infrastructure.Vehicles;
|
||||
|
||||
namespace StarterNG.Tests;
|
||||
|
||||
public class TexturesTxtParserTests
|
||||
{
|
||||
private static readonly TexturesTxtParser Parser = new();
|
||||
|
||||
[Fact]
|
||||
public void A_livery_line_yields_a_texture_with_its_model_and_thumbnail()
|
||||
{
|
||||
var entry = Parser.Parse("pkp/ep07/", new[] { "ep07-135.mat=ep07.e3d,ep07" });
|
||||
|
||||
var texture = Assert.Single(entry!.Textures);
|
||||
Assert.Equal("ep07-135", texture.Skinfile);
|
||||
Assert.Equal("pkp/ep07/", texture.Directory);
|
||||
Assert.Equal("ep07.e3d", texture.Model);
|
||||
Assert.Equal("ep07", texture.MiniRef);
|
||||
Assert.False(texture.Wreck);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_trailing_comment_is_read_as_the_credit_line()
|
||||
{
|
||||
var entry = Parser.Parse("pkp/ep07/", new[]
|
||||
{
|
||||
"ep07-135.mat=ep07.e3d,ep07 // 1.0,EP07-135,PKP Intercity,Warszawa,2019-04-01,Wrocław,Autor,Fotograf"
|
||||
});
|
||||
|
||||
var meta = Assert.Single(entry!.Textures).Meta;
|
||||
Assert.NotNull(meta);
|
||||
Assert.Equal("EP07-135", meta!.Vehicle);
|
||||
Assert.Equal("PKP Intercity", meta.Operator);
|
||||
Assert.Equal("Warszawa", meta.Depot);
|
||||
Assert.Equal("Autor", meta.TextureAuthor);
|
||||
Assert.Equal("Fotograf", meta.PhotoAuthor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Wreck_liveries_are_marked_so_the_depot_can_hide_them()
|
||||
{
|
||||
var entry = Parser.Parse("pkp/ep07/", new[] { "ep07wreck.mat=ep07.e3d,ep07" });
|
||||
|
||||
Assert.True(Assert.Single(entry!.Textures).Wreck);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_dollar_a_marker_archives_everything_after_it()
|
||||
{
|
||||
var entry = Parser.Parse("pkp/ep07/", new[]
|
||||
{
|
||||
"ep07-135.mat=ep07.e3d,ep07",
|
||||
"$a",
|
||||
"ep07-old.mat=ep07.e3d,ep07old"
|
||||
});
|
||||
|
||||
var groups = entry!.Groups;
|
||||
Assert.Contains(groups, group => group is { Mini: "ep07", Archived: false });
|
||||
Assert.Contains(groups, group => group is { Mini: "ep07old", Archived: true });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_caret_opens_a_fixed_set_of_the_following_vehicles()
|
||||
{
|
||||
var entry = Parser.Parse("pkp/en57/", new[]
|
||||
{
|
||||
"^3",
|
||||
"en57-a.mat=en57.e3d,en57",
|
||||
"en57-b.mat=en57.e3d,en57",
|
||||
"en57-c.mat=en57.e3d,en57"
|
||||
});
|
||||
|
||||
var set = Assert.Single(entry!.Sets);
|
||||
Assert.Equal(3, set.Count);
|
||||
Assert.Equal(3, set.TextureRefs.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Extra_equals_segments_become_alternative_models()
|
||||
{
|
||||
var entry = Parser.Parse("pkp/111a/", new[] { "111a.mat=111a.e3d,111a=111ab.e3d,111ab" });
|
||||
|
||||
var texture = Assert.Single(entry!.Textures);
|
||||
Assert.Equal("111a.e3d", texture.Model);
|
||||
Assert.Equal("111ab.e3d", Assert.Single(texture.Aliases).Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Comments_headers_and_blank_lines_are_ignored()
|
||||
{
|
||||
var entry = Parser.Parse("pkp/ep07/", new[] { "", "# nagłówek", "@ coś", "// komentarz", "bez znaku równości" });
|
||||
|
||||
Assert.Null(entry);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_category_marker_applies_to_the_liveries_after_it()
|
||||
{
|
||||
var entry = Parser.Parse("pkp/ep07/", new[] { "!=e", "ep07-135.mat=ep07.e3d,ep07" });
|
||||
|
||||
Assert.Equal("e", Assert.Single(entry!.Groups).Category);
|
||||
}
|
||||
}
|
||||
120
StarterNG.Tests/VehicleCatalogTests.cs
Normal file
120
StarterNG.Tests/VehicleCatalogTests.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using StarterNG.Domain.Vehicles;
|
||||
using StarterNG.Tests.Fakes;
|
||||
|
||||
namespace StarterNG.Tests;
|
||||
|
||||
public class VehicleCatalogTests
|
||||
{
|
||||
private static string TexturesFile(string maker, string vehicle) =>
|
||||
TestInstallation.At("dynamic", maker, vehicle, "textures.txt");
|
||||
|
||||
[Fact]
|
||||
public void The_catalogue_is_filled_from_every_vehicle_folder()
|
||||
{
|
||||
var files = new InMemoryFileSystem()
|
||||
.WithFile(TexturesFile("pkp", "ep07"), "ep07-135.mat=ep07.e3d,ep07")
|
||||
.WithFile(TexturesFile("pkp", "111a"), "111a-1.mat=111a.e3d,111a");
|
||||
var installation = new TestInstallation(files);
|
||||
|
||||
int liveries = installation.Vehicles.Load(installation.Library.Vehicles);
|
||||
|
||||
Assert.Equal(2, liveries);
|
||||
Assert.Equal(2, installation.Library.Vehicles.Textures.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_data_folder_is_recorded_the_way_the_scenery_format_spells_it()
|
||||
{
|
||||
var files = new InMemoryFileSystem()
|
||||
.WithFile(TexturesFile("pkp", "ep07"), "ep07-135.mat=ep07.e3d,ep07");
|
||||
var installation = new TestInstallation(files);
|
||||
installation.Vehicles.Load(installation.Library.Vehicles);
|
||||
|
||||
Assert.Equal("pkp/ep07/", Assert.Single(installation.Library.Vehicles.Textures).Directory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Wrecks_are_indexed_but_kept_out_of_the_offered_liveries()
|
||||
{
|
||||
var files = new InMemoryFileSystem()
|
||||
.WithFile(TexturesFile("pkp", "ep07"), "ep07wrak.mat=ep07.e3d,ep07\nep07-135.mat=ep07.e3d,ep07");
|
||||
var installation = new TestInstallation(files);
|
||||
installation.Vehicles.Load(installation.Library.Vehicles);
|
||||
|
||||
var catalog = installation.Library.Vehicles;
|
||||
Assert.Equal("ep07-135", Assert.Single(catalog.Textures).Skinfile);
|
||||
Assert.NotNull(catalog.TextureForSkin("ep07wrak"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_livery_is_found_by_its_skin_file_whatever_the_extension()
|
||||
{
|
||||
var files = new InMemoryFileSystem()
|
||||
.WithFile(TexturesFile("pkp", "ep07"), "ep07-135.mat=ep07.e3d,ep07");
|
||||
var installation = new TestInstallation(files);
|
||||
installation.Vehicles.Load(installation.Library.Vehicles);
|
||||
|
||||
Assert.NotNull(installation.Library.Vehicles.TextureForSkin("EP07-135.bmp"));
|
||||
Assert.Null(installation.Library.Vehicles.TextureForSkin("nieznany"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_star_category_is_derived_from_the_first_letter_of_the_thumbnail()
|
||||
{
|
||||
var catalog = new VehicleCatalog(new MiniStub());
|
||||
catalog.BeginLoad();
|
||||
catalog.Ingest(ParsedEntry("pkp/ep07/", "ep07-135.mat=ep07.e3d,ep07"));
|
||||
catalog.EndLoad();
|
||||
|
||||
Assert.Equal("E", Assert.Single(catalog.Textures).ResolvedCategory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Members_of_a_set_after_the_first_are_followers()
|
||||
{
|
||||
var files = new InMemoryFileSystem()
|
||||
.WithFile(TexturesFile("pkp", "en57"),
|
||||
"^2\nen57-a.mat=en57.e3d,en57\nen57-b.mat=en57.e3d,en57");
|
||||
var installation = new TestInstallation(files);
|
||||
installation.Vehicles.Load(installation.Library.Vehicles);
|
||||
var catalog = installation.Library.Vehicles;
|
||||
|
||||
var lead = catalog.TextureForSkin("en57-a")!;
|
||||
var follower = catalog.TextureForSkin("en57-b")!;
|
||||
|
||||
Assert.False(catalog.IsSetFollower(lead));
|
||||
Assert.True(catalog.IsSetFollower(follower));
|
||||
Assert.Equal(2, catalog.ResolveSet(lead)!.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_livery_falls_back_to_its_groups_thumbnail_when_its_own_is_missing()
|
||||
{
|
||||
var catalog = new VehicleCatalog(new MiniStub(known: "ep07"));
|
||||
catalog.BeginLoad();
|
||||
catalog.Ingest(ParsedEntry("pkp/ep07/", "ep07-135.mat=ep07.e3d,ep07,brakujacy"));
|
||||
catalog.EndLoad();
|
||||
|
||||
Assert.Equal("ep07", catalog.ResolveMiniName(Assert.Single(catalog.Textures)));
|
||||
}
|
||||
|
||||
/// <summary>Thumbnail index that knows about exactly one name.</summary>
|
||||
private sealed class MiniStub : StarterNG.Application.Abstractions.IMiniTextureIndex
|
||||
{
|
||||
private readonly string? _known;
|
||||
|
||||
public MiniStub(string? known = null) => _known = known;
|
||||
|
||||
public void Preload() { }
|
||||
|
||||
public bool Has(string? miniName) => miniName is not null && miniName == _known;
|
||||
|
||||
public string? PathFor(string? miniName) => Has(miniName) ? miniName : FallbackPath;
|
||||
|
||||
public string? FallbackPath => null;
|
||||
}
|
||||
|
||||
/// <summary>Builds an entry through the real parser, for catalogue-level tests.</summary>
|
||||
private static VehicleEntry ParsedEntry(string directory, params string[] lines) =>
|
||||
new StarterNG.Infrastructure.Vehicles.TexturesTxtParser().Parse(directory, lines)!;
|
||||
}
|
||||
Reference in New Issue
Block a user