Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for Stardew Valley diagnostic emitters #2697

Merged
merged 10 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Abstractions/NexusMods.Abstractions.Games/AGame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public GameInstallation InstallationFromLocatorResult(GameLocatorResult metadata
/// Returns a game specific version of the game, usually from the primary executable.
/// Usually used for game specific diagnostics.
/// </summary>
public virtual Version GetLocalVersion(GameInstallMetadata.ReadOnly installation)
public virtual Optional<Version> GetLocalVersion(GameInstallMetadata.ReadOnly installation)
{
try
{
Expand All @@ -112,7 +112,7 @@ public virtual Version GetLocalVersion(GameInstallMetadata.ReadOnly installation
}
catch (Exception)
{
return new Version(0, 0, 0, 0);
return Optional<Version>.None;
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/Games/NexusMods.Games.Larian/BaldursGate3/BaldursGate3.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using DynamicData.Kernel;
using Microsoft.Extensions.DependencyInjection;
using NexusMods.Abstractions.Diagnostics.Emitters;
using NexusMods.Abstractions.GameLocators;
Expand Down Expand Up @@ -46,7 +47,7 @@ public BaldursGate3(IServiceProvider provider) : base(provider)
_fs = provider.GetRequiredService<IFileSystem>();
}

public override Version GetLocalVersion(GameInstallMetadata.ReadOnly installation)
public override Optional<Version> GetLocalVersion(GameInstallMetadata.ReadOnly installation)
{
try
{
Expand All @@ -62,7 +63,7 @@ public override Version GetLocalVersion(GameInstallMetadata.ReadOnly installatio
}
catch (Exception)
{
return new Version(0, 0, 0, 0);
return Optional<Version>.None;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Bannerlord.ModuleManager;
using DynamicData.Kernel;
using FetchBannerlordVersion;
using Microsoft.Extensions.DependencyInjection;
using NexusMods.Abstractions.Diagnostics.Emitters;
Expand Down Expand Up @@ -82,7 +83,7 @@ public Bannerlord(IServiceProvider serviceProvider, LauncherManagerFactory launc

public override GamePath GetPrimaryFile(GameStore store) => GamePathProvier.PrimaryLauncherFile(store);

public override Version GetLocalVersion(GameInstallMetadata.ReadOnly installation)
public override Optional<Version> GetLocalVersion(GameInstallMetadata.ReadOnly installation)
{
// Note(sewer): Bannerlord can use prefixes on versions etc. ,we want to strip them out
// so we sanitize/parse with `ApplicationVersion`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public DependencyDiagnosticEmitter(

public async IAsyncEnumerable<Diagnostic> Diagnose(Loadout.ReadOnly loadout, [EnumeratorCancellation] CancellationToken cancellationToken)
{
var gameVersion = new SemanticVersion((loadout.InstallationInstance.Game as AGame)!.GetLocalVersion(loadout.Installation));
var gameVersion = Helpers.GetGameVersion(loadout);

if (!Helpers.TryGetSMAPI(loadout, out var smapi)) yield break;
if (!SMAPILoadoutItem.Version.TryGetValue(smapi, out var smapiStrVersion)) yield break;
Expand Down
23 changes: 23 additions & 0 deletions src/Games/NexusMods.Games.StardewValley/Emitters/Helpers.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
using System.Runtime.CompilerServices;
using DynamicData.Kernel;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.Diagnostics.Values;
using NexusMods.Abstractions.Games;
using NexusMods.Abstractions.Loadouts;
using NexusMods.Abstractions.Loadouts.Extensions;
using NexusMods.Abstractions.Resources;
using NexusMods.Abstractions.Telemetry;
using NexusMods.Extensions.BCL;
using NexusMods.Games.StardewValley.Models;
using StardewModdingAPI;
using StardewModdingAPI.Toolkit;
using StardewModdingAPI.Toolkit.Serialization.Models;

namespace NexusMods.Games.StardewValley.Emitters;
Expand All @@ -16,6 +20,25 @@ internal static class Helpers
public static readonly NamedLink NexusModsLink = new("Nexus Mods", NexusModsUrlBuilder.CreateGenericUri("https://nexusmods.com/stardewvalley"));
public static readonly NamedLink SMAPILink = new("Nexus Mods", NexusModsUrlBuilder.CreateDiagnosticUri(StardewValley.DomainStatic.Value, "2400"));

public static ISemanticVersion GetGameVersion(Loadout.ReadOnly loadout)
{
var game = (loadout.InstallationInstance.Game as AGame)!;
var localVersion = game.GetLocalVersion(loadout.Installation).Convert(static v => v.ToString());
var rawVersion = localVersion.ValueOr(() => loadout.GameVersion);

#if DEBUG
// NOTE(erri120): dumb hack for tests
var index = rawVersion.IndexOf(".stubbed", StringComparison.OrdinalIgnoreCase);
if (index != -1)
{
rawVersion = rawVersion.AsSpan()[..index].ToString();
}
#endif

var gameVersion = new SemanticVersion(rawVersion);
return gameVersion;
}

public static bool TryGetSMAPI(Loadout.ReadOnly loadout, out SMAPILoadoutItem.ReadOnly smapi)
{
var foundSMAPI = loadout.Items
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Net.Http.Json;
using System.Runtime.CompilerServices;
using DynamicData.Kernel;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.Diagnostics;
Expand Down Expand Up @@ -42,7 +43,7 @@ public async IAsyncEnumerable<Diagnostic> Diagnose(Loadout.ReadOnly loadout, [En
var gameToSMAPIMappings = await FetchGameToSMAPIMappings(cancellationToken);
if (gameToSMAPIMappings is null) yield break;

var gameVersion = new SemanticVersion((loadout.InstallationInstance.Game as AGame)!.GetLocalVersion(loadout.Installation));
var gameVersion = Helpers.GetGameVersion(loadout);
// var gameVersion = new SemanticVersion("1.6.12");

if (!Helpers.TryGetSMAPI(loadout, out var smapi))
Expand Down Expand Up @@ -187,14 +188,14 @@ public async IAsyncEnumerable<Diagnostic> Diagnose(Loadout.ReadOnly loadout, [En
/// <summary>
/// Returns the latest supported SMAPI version for <paramref name="gameVersion"/>.
/// </summary>
private static bool TryGetLastSupportedSMAPIVersion(
internal static bool TryGetLastSupportedSMAPIVersion(
GameToSMAPIMapping gameToSmapiMappings,
ISemanticVersion gameVersion,
[NotNullWhen(true)] out ISemanticVersion? supportedSMAPIVersion)
{
var found = gameToSmapiMappings
.OrderByDescending(static kv => kv.Key)
.SkipWhile(current => current.Key.CompareTo(gameVersion) >= 0)
.SkipWhile(current => current.Key.CompareTo(gameVersion) > 0)
.TryGetFirst(out var mapping);

if (!found)
Expand Down Expand Up @@ -245,7 +246,7 @@ static bool ComparisonPredicate(ISemanticVersion x, ISemanticVersion y)
private SMAPIToGameMapping? _smapiToGameMappings;
private GameToSMAPIMapping? _gameToSMAPIMappings;

private async Task<SMAPIToGameMapping?> FetchSMAPIToGameMappings(CancellationToken cancellationToken)
internal async Task<SMAPIToGameMapping?> FetchSMAPIToGameMappings(CancellationToken cancellationToken)
{
if (_smapiToGameMappings is not null) return _smapiToGameMappings;

Expand Down Expand Up @@ -276,7 +277,7 @@ static bool ComparisonPredicate(ISemanticVersion x, ISemanticVersion y)

}

private async Task<GameToSMAPIMapping?> FetchGameToSMAPIMappings(CancellationToken cancellationToken)
internal async Task<GameToSMAPIMapping?> FetchGameToSMAPIMappings(CancellationToken cancellationToken)
{
if (_gameToSMAPIMappings is not null) return _gameToSMAPIMappings;

Expand Down Expand Up @@ -319,7 +320,6 @@ private bool TryParseVersion(string input, [NotNullWhen(true)] out ISemanticVers

_logger.LogWarning("Serialization of JSON data at {Uri} failed and returned null", dataUri);
return null;

}
catch (Exception e)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using DynamicData.Kernel;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.Diagnostics;
using NexusMods.Abstractions.Diagnostics.Emitters;
Expand Down Expand Up @@ -59,7 +60,7 @@ public SMAPIModDatabaseCompatibilityDiagnosticEmitter(

public async IAsyncEnumerable<Diagnostic> Diagnose(Loadout.ReadOnly loadout, [EnumeratorCancellation] CancellationToken cancellationToken)
{
var gameVersion = new SemanticVersion((loadout.InstallationInstance.Game as AGame)!.GetLocalVersion(loadout.Installation));
var gameVersion = Helpers.GetGameVersion(loadout);

if (!Helpers.TryGetSMAPI(loadout, out var smapi)) yield break;
if (!SMAPILoadoutItem.Version.TryGetValue(smapi, out var smapiStrVersion)) yield break;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using DynamicData.Kernel;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.Diagnostics;
Expand Down Expand Up @@ -37,7 +38,7 @@ public VersionDiagnosticEmitter(

public async IAsyncEnumerable<Diagnostic> Diagnose(Loadout.ReadOnly loadout, [EnumeratorCancellation] CancellationToken cancellationToken)
{
var gameVersion = new SemanticVersion((loadout.InstallationInstance.Game as AGame)!.GetLocalVersion(loadout.Installation));
var gameVersion = Helpers.GetGameVersion(loadout);

if (!Helpers.TryGetSMAPI(loadout, out var smapi)) yield break;
if (!SMAPILoadoutItem.Version.TryGetValue(smapi, out var smapiStrVersion)) yield break;
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

4 changes: 2 additions & 2 deletions src/Games/NexusMods.Games.StardewValley/StardewValley.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public override Optional<GamePath> GetFallbackCollectionInstallDirectory()
return Optional<GamePath>.Create(path);
}

public override Version GetLocalVersion(GameInstallMetadata.ReadOnly installation)
public override Optional<Version> GetLocalVersion(GameInstallMetadata.ReadOnly installation)
{
try
{
Expand All @@ -92,7 +92,7 @@ public override Version GetLocalVersion(GameInstallMetadata.ReadOnly installatio
}
catch (Exception)
{
return new Version(0, 0, 0, 0);
return Optional<Version>.None;
}
}

Expand Down
16 changes: 16 additions & 0 deletions src/NexusMods.App.UI/DiagnosticSystem/Services.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.Extensions.DependencyInjection;
using NexusMods.Abstractions.Diagnostics;

namespace NexusMods.App.UI.DiagnosticSystem;

public static class Services
{
public static IServiceCollection AddDiagnosticWriter(this IServiceCollection serviceCollection)
{
return serviceCollection
.AddSingleton<IValueFormatter, LoadoutReferenceFormatter>()
.AddSingleton<IValueFormatter, NamedLinkFormatter>()
.AddSingleton<IValueFormatter, LoadoutItemGroupFormatter>()
.AddSingleton<IDiagnosticWriter, DiagnosticWriter>();
}
}
7 changes: 2 additions & 5 deletions src/NexusMods.App.UI/Services.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,8 @@ public static IServiceCollection AddUI(this IServiceCollection c)
.AddSingleton<IWorkspaceAttachmentsFactory, LoadoutAttachmentsFactory>()

// Diagnostics
.AddSingleton<IValueFormatter, LoadoutReferenceFormatter>()
.AddSingleton<IValueFormatter, NamedLinkFormatter>()
.AddSingleton<IValueFormatter, LoadoutItemGroupFormatter>()
.AddSingleton<IDiagnosticWriter, DiagnosticWriter>()

.AddDiagnosticWriter()

// Overlay Helpers
.AddHostedService<NexusLoginOverlayService>()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[Id] NexusMods.Games.StardewValley: 5
[Title] Disabled Dependency
[Summary] 'Farm Type Manager' requires 'Content Patcher' but it is disabled
[Details]
The mod **Farm Type Manager** requires **Content Patcher** to function, but **Content Patcher** is not enabled.


### How to Resolve
1. Enable **Content Patcher** in "Installed Mods"

### Technical Details
The `manifest.json` file included with **Farm Type Manager** lists **Content Patcher** as a requirement in order function.

The issue can arise in these scenarios:

1. **Disabled Mod**: The required mod is disabled in the loadout
2. **Incorrect Mod ID**: The manifest data for **Farm Type Manager** might be incorrect
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[Id] NexusMods.Games.StardewValley: 1
[Title] Missing Dependency
[Summary] 'Farm Type Manager' requires 'Content Patcher' which is not installed
[Details]
The mod **Farm Type Manager** requires **Content Patcher** to function, but **Content Patcher** is not installed.


### How to Resolve
1. Download **Content Patcher** from [Nexus Mods](https://nexusmods.com/stardewvalley/mods/1915?mtm_source=nexusmodsapp&mtm_campaign=diagnostics)
2. Add **Content Patcher** to the loadout.

### Technical Details
The `manifest.json` file included with **Farm Type Manager** lists a mod with the ID `Pathoschild.ContentPatcher` as a requirement or is using it as a framework in order function.

The issue can arise in these scenarios:

1. **Missing Installation**: The required mod is not installed
2. **Incorrect Mod ID**: The manifest data for **Farm Type Manager** might be incorrect

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Id] NexusMods.Games.StardewValley: 2
[Title] Outdated Dependency
[Summary] 'Farm Type Manager' requires an updated version of 'Content Patcher'
[Details]
The mod **Farm Type Manager** requires **Content Patcher** version 2.0.0 or higher to function, but an older version of **Content Patcher** (1.30.4) is installed.

### How to Resolve
1. Download the latest version of **Content Patcher** (version 2.0.0 or newer) from [Nexus Mods](https://nexusmods.com/stardewvalley/mods/1915?mtm_source=nexusmodsapp&mtm_campaign=diagnostics)
2. Add the latest version of **Content Patcher** to the loadout
3. Remove version 1.30.4 of **Content Patcher** from the loadout

### Technical Details
The `manifest.json` file included with **Farm Type Manager** lists **Content Patcher** as a requirement with a minimum version of 2.0.0.
Loading
Loading