Files
HellionChat/HellionChat/SelfTests/ConfigMigrationV27Step.cs
T
JonKazama-Hellion 89c73be71c feat(release): 2.0.0 -- nine cycles ship, and the config starts over
v1.6.0 through v1.15.0 were never published. The whole window layer was being
rewritten from ImGui defaults to custom drawing, and repo.json deliberately kept
its links on v1.5.6 so nobody could update into a half-finished state. That work
lands here in one release.

The config is not migrated, it is replaced. A config carried across nine cycles of
window rebuilds holds values chosen against surfaces that no longer exist, and
starting over is the only way to be sure every install is on the same defaults.
The message database is a separate file and is not touched. The old config is
copied to HellionChat.json.pre-2.0.0.bak first, so rolling back to 1.5.6 stays a
file copy rather than an evening of clicking settings back in.

Config schema 27. The reset runs from the constructor, before LoadAsync seeds the
default tabs, and the self-test now fails if the tab list is empty at /xlperf --
that ordering breaking would leave every user with no tabs and nothing else in the
plugin would notice.

Default layout gains an Emote tab: custom emotes, standard emotes and echo. A
tester asked for it, emotes get lost between system notices otherwise.

Manifest, README, roadmap and the four changelog consumers are on 2.0.0. The
release notes lead with what was fixed rather than what was redrawn -- retroactive
cleanup that could never be applied, a compaction that reported deleting nothing
while deleting everything, pinned tell tabs that came up empty for a session. And
with the one behaviour change users should know about: the channel grid is
authoritative over storage now, so anyone who had unticked channels while the
unknown-channel failsafe was on stores less than they did before.
2026-08-19 22:15:40 +02:00

115 lines
4.3 KiB
C#

using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.SelfTest;
namespace HellionChat.SelfTests;
// Pins the post-migration shape of the config. By /xlperf time the schema gate
// has stamped Config.Version and the 2.0.0 reset has run, so the fields below
// must carry valid values here. This probe never rewrites config; the reset
// itself is load-time.
internal sealed class ConfigMigrationV27Step : ISelfTestStep
{
public ConfigMigrationV27Step(Plugin plugin)
{
_ = plugin;
}
public string Name => "Hellion Chat - Config v27 migration";
public SelfTestStepResult RunStep()
{
if (Plugin.Config.Version != 27)
{
ImGui.Text($"Config.Version is {Plugin.Config.Version}, expected 27");
return SelfTestStepResult.Fail;
}
// v27 replaces the config rather than migrating it, and CreateFresh
// hands back an empty tab list. The seeding in LoadAsync is what fills
// it, and it runs in a different method than the reset does -- if that
// ordering ever breaks, every user comes out of the update with no tabs
// at all and nothing else in the plugin would notice.
if (Plugin.Config.Tabs.Count == 0)
{
ImGui.Text(
"No tabs at all. The v27 reset empties the list and LoadAsync is what "
+ "seeds the presets back -- reaching this point empty means it did not."
);
return SelfTestStepResult.Fail;
}
// What v26 exists to prevent: a tab whose name came from a conversation
// partner but is not marked as such, because screenshot mode reads that
// mark to decide whether the name may be shown. Anything still holding a
// tell binding or the temp flag should have been marked on load.
foreach (var tab in Plugin.Config.Tabs)
{
var looksLikeAPartner = tab.IsTempTab || tab.TellTarget?.IsSet() == true;
if (!looksLikeAPartner || tab.NameCameFromPartner)
continue;
ImGui.Text(
$"Tab '{tab.Name}' carries a tell binding but is not marked partner-named "
+ "— the v26 migration did not reach it, and screenshot mode will show it"
);
return SelfTestStepResult.Fail;
}
// The state the v24 migration exists to prevent: filter on, failsafe on,
// nothing picked. Under the corrected rule that combination stores no
// messages at all, so reaching /xlperf in it means the migration did not
// run.
if (
Privacy.StorageRule.ShouldDisableFilterOnV24(
Plugin.Config.PrivacyFilterEnabled,
Plugin.Config.PrivacyPersistUnknownChannels,
Plugin.Config.PrivacyPersistChannels.Count
)
)
{
ImGui.Text("Privacy filter is on with no channels picked — v24 migration did not run");
return SelfTestStepResult.Fail;
}
if (Plugin.Config.MaxParallelPopouts <= 0)
{
ImGui.Text(
$"Config.MaxParallelPopouts is {Plugin.Config.MaxParallelPopouts}, must be > 0"
);
return SelfTestStepResult.Fail;
}
if (Plugin.Config.SidebarAutoSwitchThresholdPx <= 0)
{
ImGui.Text(
$"Config.SidebarAutoSwitchThresholdPx is {Plugin.Config.SidebarAutoSwitchThresholdPx}, must be > 0"
);
return SelfTestStepResult.Fail;
}
if (!Enum.IsDefined(Plugin.Config.TellAutoOpenMode))
{
ImGui.Text($"Config.TellAutoOpenMode {Plugin.Config.TellAutoOpenMode} is out of range");
return SelfTestStepResult.Fail;
}
if (!Enum.IsDefined(Plugin.Config.MainWindowLayoutMode))
{
ImGui.Text(
$"Config.MainWindowLayoutMode {Plugin.Config.MainWindowLayoutMode} is out of range"
);
return SelfTestStepResult.Fail;
}
// Touch-tests: declaration proves the migration emitted these with
// defaults; reading them confirms the property is reachable.
_ = Plugin.Config.MainWindowOpen;
_ = Plugin.Config.SettingsWindowOpen;
_ = Plugin.Config.ScreenshotMode;
return SelfTestStepResult.Pass;
}
public void CleanUp() { }
}