-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBaselineSelector.cs
More file actions
202 lines (172 loc) · 8.25 KB
/
Copy pathBaselineSelector.cs
File metadata and controls
202 lines (172 loc) · 8.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
using AET.ModVerify.App.Resources.Baselines;
using AET.ModVerify.App.Settings;
using AET.ModVerify.Reporting.Baseline;
using AnakinRaW.ApplicationBase;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using PG.StarWarsGame.Engine;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace AET.ModVerify.App.Reporting;
internal sealed class BaselineSelector(AppSettingsBase settings, IServiceProvider services)
{
private readonly ILogger? _logger = services.GetService<ILoggerFactory>()?.CreateLogger(typeof(ModVerifyApplication));
private readonly IBaselineFactory _baselineFactory = services.GetRequiredService<IBaselineFactory>();
private bool IsCreatingBaseline => settings is AppBaselineSettings;
public BaselineCollection SelectBaselines(VerificationTarget verificationTarget)
{
var report = settings.ReportSettings;
var collected = new List<IdentifiedBaseline>();
var seenIdentifiers = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var path in report.BaselinePaths)
{
var entry = LoadExplicitBaseline(path);
if (seenIdentifiers.Add(entry.Identifier))
collected.Add(entry);
}
// In interactive mode, offer to discover a baseline near the target when none was supplied.
if (settings.IsInteractive && collected.Count == 0 && TryFindBaselineInteractive(verificationTarget, out var found))
collected.Add(found);
// Loading the engine's default baseline is meaningless when creating a baseline for the game itself
// (you'd be subtracting it from itself). Skip it in that case.
var defaultBaselineApplicable = !(IsCreatingBaseline && verificationTarget.IsGame);
if (report.UseDefaultBaseline)
{
if (!defaultBaselineApplicable)
{
_logger?.LogWarning(ModVerifyConstants.ConsoleEventId,
"Ignoring --useDefaultBaseline: it does not apply when creating a baseline for the game itself.");
}
else if (TryLoadEmbeddedBaseline(verificationTarget.Engine, out var defaultBaseline, out var defaultId))
{
collected.Add(new IdentifiedBaseline(defaultId, defaultBaseline, BaselineSource.EmbeddedDefault));
}
}
else if (settings.IsInteractive && defaultBaselineApplicable)
{
// In interactive mode, offer the embedded default independently of any locally
// discovered or explicitly supplied baselines — they're typically stacked.
if (TryPromptForEmbeddedBaseline(verificationTarget.Engine, out var defaultBaseline, out var defaultId))
collected.Add(new IdentifiedBaseline(defaultId, defaultBaseline, BaselineSource.EmbeddedDefault));
}
return new BaselineCollection(collected);
}
private IdentifiedBaseline LoadExplicitBaseline(string baselinePath)
{
try
{
return new IdentifiedBaseline(baselinePath, _baselineFactory.ParseBaseline(baselinePath), BaselineSource.File);
}
catch (InvalidBaselineException e)
{
using (ConsoleUtilities.HorizontalLineSeparatedBlock('*'))
{
Console.WriteLine($"The baseline '{baselinePath}' is not a valid baseline file: {e.Message}" +
$"{Environment.NewLine}Please generate a new baseline file or download the latest version." +
$"{Environment.NewLine}");
}
throw;
}
}
private bool TryFindBaselineInteractive(VerificationTarget verificationTarget, [NotNullWhen(true)] out IdentifiedBaseline? found)
{
// 1. Use a baseline found in the directory of the verification target.
// 2. Use a baseline found in the directory of the ModVerify executable.
// Ask the user if they want to use the located baseline file.
_logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Searching for local baseline files...");
if (!_baselineFactory.TryFindBaselineInDirectory(
verificationTarget.Location.TargetPath,
b => IsBaselineCompatible(b, verificationTarget),
out var baseline,
out var baselinePath))
{
if (!_baselineFactory.TryFindBaselineInDirectory(
Environment.CurrentDirectory,
b => IsBaselineCompatible(b, verificationTarget),
out baseline,
out baselinePath))
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("No baseline found locally.");
Console.ResetColor();
found = null;
return false;
}
}
if (ShouldUseBaseline(baseline, baselinePath))
{
found = new IdentifiedBaseline(baselinePath, baseline, BaselineSource.File);
return true;
}
found = null;
return false;
}
private bool TryLoadEmbeddedBaseline(GameEngineType engineType,
[NotNullWhen(true)] out VerificationBaseline? baseline,
[NotNullWhen(true)] out string? identifier)
{
baseline = null;
identifier = null;
// TODO: EAW currently not implemented
if (engineType == GameEngineType.Eaw)
return false;
try
{
baseline = LoadEmbeddedBaseline(engineType);
identifier = MakeDefaultIdentifier(engineType);
_logger?.LogInformation(ModVerifyConstants.ConsoleEventId,
"Applying default embedded baseline for engine '{Engine}'.", engineType);
return true;
}
catch (InvalidBaselineException)
{
throw new InvalidOperationException(
"Invalid baseline packed along ModVerify App. Please reach out to the creators. Thanks!");
}
}
private bool TryPromptForEmbeddedBaseline(GameEngineType engineType,
[NotNullWhen(true)] out VerificationBaseline? baseline,
[NotNullWhen(true)] out string? identifier)
{
baseline = null;
identifier = null;
// TODO: EAW currently not implemented
if (engineType == GameEngineType.Eaw)
return false;
var question = IsCreatingBaseline
? $"Apply the default baseline for engine '{engineType}' as a base? Findings already covered by it will be excluded from your new baseline."
: $"Do you want to load the default baseline for game engine '{engineType}'?";
if (!ConsoleUtilities.UserYesNoQuestion(question, defaultAnswer: true))
return false;
return TryLoadEmbeddedBaseline(engineType, out baseline, out identifier);
}
internal static VerificationBaseline LoadEmbeddedBaseline(GameEngineType engineType)
{
var baselineFileName = $"baseline-{engineType.ToString().ToLower()}.json";
var resourcePath = $"{typeof(BaselineResources).Namespace}.{baselineFileName}";
using var baselineStream = typeof(BaselineSelector).Assembly.GetManifestResourceStream(resourcePath)!;
return VerificationBaseline.FromJson(baselineStream);
}
internal static string MakeDefaultIdentifier(GameEngineType engineType)
=> $"<embedded-default:{engineType.ToString().ToLower()}>";
private static bool IsBaselineCompatible(VerificationBaseline baseline, VerificationTarget target)
{
return baseline.Target?.Engine == target.Engine;
}
private bool ShouldUseBaseline(VerificationBaseline baseline, string baselinePath)
{
var sb = new StringBuilder("Found baseline ");
if (baseline.Target is not null)
sb.Append($"for '{baseline.Target.Name}' ");
sb.Append($"at '{baselinePath}'.");
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(sb.ToString());
var question = IsCreatingBaseline
? "Use it as a base? Findings already covered by it will be excluded from your new baseline."
: "Do you want to use it?";
Console.ResetColor();
return ConsoleUtilities.UserYesNoQuestion(question, defaultAnswer: true);
}
}