Skip to content

Commit 749f5b0

Browse files
authored
Merge pull request #296 from TeamWheelWizard/fix/installation-and-settings-ux
fix: Improve installation and settings UX
2 parents ff78cb4 + 922c2e1 commit 749f5b0

23 files changed

Lines changed: 477 additions & 201 deletions

File tree

WheelWizard.Test/Features/LinuxDolphinInstallerTests.cs

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,29 @@ public LinuxDolphinInstallerTests()
1717
}
1818

1919
[Fact]
20-
public void IsDolphinInstalledInFlatpak_ReturnsTrue_WhenFlatpakInfoExitCodeIsZero()
20+
public void IsDolphinInstalledInFlatpak_ReturnsTrue_WhenFlatpakListHasDolphin()
2121
{
22-
_processService.Run("flatpak", "info org.DolphinEmu.dolphin-emu").Returns(Ok(0));
22+
_processService.Run("flatpak", "list --app --columns=application", out var stdOut, out _).Returns(Ok(0)).AndDoes(callInfo => callInfo[2] = "Application ID\norg.DolphinEmu.dolphin-emu\n");
2323

2424
var result = _installer.IsDolphinInstalledInFlatpak();
2525

2626
Assert.True(result);
2727
}
2828

2929
[Fact]
30-
public void IsDolphinInstalledInFlatpak_ReturnsFalse_WhenFlatpakInfoExitCodeIsNonZero()
30+
public void IsDolphinInstalledInFlatpak_ReturnsFalse_WhenFlatpakListHasNoDolphin()
3131
{
32-
_processService.Run("flatpak", "info org.DolphinEmu.dolphin-emu").Returns(Ok(1));
32+
_processService.Run("flatpak", "list --app --columns=application", out var stdOut, out _).Returns(Ok(0)).AndDoes(callInfo => callInfo[2] = "Application ID\n");
33+
34+
var result = _installer.IsDolphinInstalledInFlatpak();
35+
36+
Assert.False(result);
37+
}
38+
39+
[Fact]
40+
public void IsDolphinInstalledInFlatpak_ReturnsFalse_WhenFlatpakListFails()
41+
{
42+
_processService.Run("flatpak", "list --app --columns=application", out _, out _).Returns(Ok(1));
3343

3444
var result = _installer.IsDolphinInstalledInFlatpak();
3545

@@ -77,13 +87,53 @@ public async Task InstallFlatpak_ReturnsSuccess_WhenInstallCompletesAndCommandBe
7787
Assert.True(result.IsSuccess);
7888
}
7989

90+
[Fact]
91+
public async Task InstallFlatpakDolphin_ReturnsFailure_WhenDolphinRemoteCommandFails()
92+
{
93+
_commandEnvironment.IsCommandAvailable("flatpak").Returns(true);
94+
_processService
95+
.Run("flatpak", "remote-add --if-not-exists --user dolphin https://flatpak.dolphin-emu.org/releases.flatpakrepo")
96+
.Returns(Ok(1));
97+
_processService
98+
.Run("flatpak", "remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo")
99+
.Returns(Ok(0));
100+
101+
var result = await _installer.InstallFlatpakDolphin();
102+
103+
Assert.True(result.IsFailure);
104+
Assert.Contains("exit code 1", result.Error.Message);
105+
}
106+
107+
[Fact]
108+
public async Task InstallFlatpakDolphin_ReturnsFailure_WhenFlathubRemoteCommandFails()
109+
{
110+
_commandEnvironment.IsCommandAvailable("flatpak").Returns(true);
111+
_processService
112+
.Run("flatpak", "remote-add --if-not-exists --user dolphin https://flatpak.dolphin-emu.org/releases.flatpakrepo")
113+
.Returns(Ok(0));
114+
_processService
115+
.Run("flatpak", "remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo")
116+
.Returns(Ok(1));
117+
118+
var result = await _installer.InstallFlatpakDolphin();
119+
120+
Assert.True(result.IsFailure);
121+
Assert.Contains("exit code 1", result.Error.Message);
122+
}
123+
80124
[Fact]
81125
public async Task InstallFlatpakDolphin_ReturnsFailure_WhenDolphinInstallCommandFails()
82126
{
83127
_commandEnvironment.IsCommandAvailable("flatpak").Returns(true);
84128
_processService
85-
.RunWithProgressAsync("pkexec", "flatpak --system install -y org.DolphinEmu.dolphin-emu", Arg.Any<IProgress<int>?>())
86-
.Returns(Task.FromResult<OperationResult<int>>(Ok(1)));
129+
.Run("flatpak", "remote-add --if-not-exists --user dolphin https://flatpak.dolphin-emu.org/releases.flatpakrepo")
130+
.Returns(Ok(0));
131+
_processService
132+
.Run("flatpak", "remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo")
133+
.Returns(Ok(0));
134+
_processService
135+
.RunWithProgressAsync("flatpak", "install --user -y dolphin org.DolphinEmu.dolphin-emu", Arg.Any<IProgress<int>?>())
136+
.Returns(Task.FromResult(Ok(1)));
87137

88138
var result = await _installer.InstallFlatpakDolphin();
89139

@@ -96,8 +146,14 @@ public async Task InstallFlatpakDolphin_ReturnsFailure_WhenWarmupLaunchFails()
96146
{
97147
_commandEnvironment.IsCommandAvailable("flatpak").Returns(true);
98148
_processService
99-
.RunWithProgressAsync("pkexec", "flatpak --system install -y org.DolphinEmu.dolphin-emu", Arg.Any<IProgress<int>?>())
100-
.Returns(Task.FromResult<OperationResult<int>>(Ok(0)));
149+
.Run("flatpak", "remote-add --if-not-exists --user dolphin https://flatpak.dolphin-emu.org/releases.flatpakrepo")
150+
.Returns(Ok(0));
151+
_processService
152+
.Run("flatpak", "remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo")
153+
.Returns(Ok(0));
154+
_processService
155+
.RunWithProgressAsync("flatpak", "install --user -y dolphin org.DolphinEmu.dolphin-emu", Arg.Any<IProgress<int>?>())
156+
.Returns(Task.FromResult(Ok(0)));
101157
_processService
102158
.LaunchAndStopAsync("flatpak", "run org.DolphinEmu.dolphin-emu", TimeSpan.FromSeconds(4))
103159
.Returns(Task.FromResult<OperationResult>(Fail("Launch failed")));
@@ -113,11 +169,17 @@ public async Task InstallFlatpakDolphin_ReturnsSuccess_WhenInstallAndWarmupSucce
113169
{
114170
_commandEnvironment.IsCommandAvailable("flatpak").Returns(true);
115171
_processService
116-
.RunWithProgressAsync("pkexec", "flatpak --system install -y org.DolphinEmu.dolphin-emu", Arg.Any<IProgress<int>?>())
117-
.Returns(Task.FromResult<OperationResult<int>>(Ok(0)));
172+
.Run("flatpak", "remote-add --if-not-exists --user dolphin https://flatpak.dolphin-emu.org/releases.flatpakrepo")
173+
.Returns(Ok(0));
174+
_processService
175+
.Run("flatpak", "remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo")
176+
.Returns(Ok(0));
177+
_processService
178+
.RunWithProgressAsync("flatpak", "install --user -y dolphin org.DolphinEmu.dolphin-emu", Arg.Any<IProgress<int>?>())
179+
.Returns(Task.FromResult(Ok(0)));
118180
_processService
119181
.LaunchAndStopAsync("flatpak", "run org.DolphinEmu.dolphin-emu", TimeSpan.FromSeconds(4))
120-
.Returns(Task.FromResult<OperationResult>(Ok()));
182+
.Returns(Task.FromResult(Ok()));
121183

122184
var result = await _installer.InstallFlatpakDolphin();
123185

WheelWizard.Test/Features/Settings/SettingsTests.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,23 @@ public class SettingsManagerTests
2020
[Fact]
2121
public void Get_Throws_WhenRequestedTypeDoesNotMatchSettingType()
2222
{
23-
var manager = CreateManager(new MockFileSystem(), out _, out _, out _);
23+
var manager = CreateManager(new MockFileSystem(), out _, out _);
2424

2525
Assert.Throws<InvalidOperationException>(() => manager.Get<int>(manager.WW_LANGUAGE));
2626
}
2727

2828
[Fact]
2929
public void Set_Throws_WhenProvidedValueIsNull()
3030
{
31-
var manager = CreateManager(new MockFileSystem(), out _, out _, out _);
31+
var manager = CreateManager(new MockFileSystem(), out _, out _);
3232

3333
Assert.Throws<ArgumentNullException>(() => manager.Set<string>(manager.WW_LANGUAGE, null!));
3434
}
3535

3636
[Fact]
3737
public void Set_ReturnsFalse_WhenValidationFails()
3838
{
39-
var manager = CreateManager(new MockFileSystem(), out _, out _, out _);
39+
var manager = CreateManager(new MockFileSystem(), out _, out _);
4040

4141
var result = manager.Set(manager.FOCUSED_USER, 99, skipSave: true);
4242

@@ -49,7 +49,7 @@ public void Set_ReturnsFalse_WhenValidationFails()
4949
[InlineData(2.01)]
5050
public void SavedWindowScale_RejectsValuesOutsideBounds(double scale)
5151
{
52-
var manager = CreateManager(new MockFileSystem(), out _, out _, out _);
52+
var manager = CreateManager(new MockFileSystem(), out _, out _);
5353

5454
var result = manager.Set(manager.SAVED_WINDOW_SCALE, scale, skipSave: true);
5555

@@ -62,7 +62,7 @@ public void SavedWindowScale_RejectsValuesOutsideBounds(double scale)
6262
[InlineData(2.01)]
6363
public void WindowScalePreview_RejectsValuesOutsideBounds(double scale)
6464
{
65-
var manager = CreateManager(new MockFileSystem(), out _, out _, out _);
65+
var manager = CreateManager(new MockFileSystem(), out _, out _);
6666

6767
var result = manager.Set(manager.WINDOW_SCALE, scale, skipSave: true);
6868

@@ -73,7 +73,7 @@ public void WindowScalePreview_RejectsValuesOutsideBounds(double scale)
7373
[Fact]
7474
public void ValidateCorePathSettings_ReturnsAllExpectedIssues_WhenDefaultsAreInvalid()
7575
{
76-
var manager = CreateManager(new RealFileSystem(), out _, out _, out _);
76+
var manager = CreateManager(new RealFileSystem(), out _, out _);
7777
#pragma warning disable CS0618
7878
SettingsRuntime.Initialize(manager);
7979
#pragma warning restore CS0618
@@ -91,7 +91,7 @@ public void ValidateCorePathSettings_ReturnsAllExpectedIssues_WhenDefaultsAreInv
9191
public void PathsSetupCorrectly_ReturnsTrue_WhenCorePathsAreValid()
9292
{
9393
var fileSystem = new MockFileSystem();
94-
var manager = CreateManager(fileSystem, out _, out _, out _);
94+
var manager = CreateManager(fileSystem, out _, out _);
9595
var userFolderPath = $"/wheelwizard-user-{Guid.NewGuid():N}";
9696
var gameFilePath = Path.Combine(userFolderPath, "game.iso");
9797
var dolphinLocation = SettingsTestUtils.GetValidDolphinLocation(fileSystem);
@@ -110,7 +110,7 @@ public void PathsSetupCorrectly_ReturnsTrue_WhenCorePathsAreValid()
110110
[Fact]
111111
public void LoadSettings_CallsUnderlyingManagersOnlyOnce()
112112
{
113-
var manager = CreateManager(new MockFileSystem(), out var whWzManager, out var dolphinManager, out _);
113+
var manager = CreateManager(new MockFileSystem(), out var whWzManager, out var dolphinManager);
114114

115115
manager.LoadSettings();
116116
manager.LoadSettings();
@@ -122,16 +122,13 @@ public void LoadSettings_CallsUnderlyingManagersOnlyOnce()
122122
private static SettingsManager CreateManager(
123123
IFileSystem fileSystem,
124124
out IWhWzSettingManager whWzSettingManager,
125-
out IDolphinSettingManager dolphinSettingManager,
126-
out ILinuxDolphinInstaller linuxDolphinInstaller
125+
out IDolphinSettingManager dolphinSettingManager
127126
)
128127
{
129128
whWzSettingManager = Substitute.For<IWhWzSettingManager>();
130129
dolphinSettingManager = Substitute.For<IDolphinSettingManager>();
131-
linuxDolphinInstaller = Substitute.For<ILinuxDolphinInstaller>();
132-
linuxDolphinInstaller.IsDolphinInstalledInFlatpak().Returns(true);
133130

134-
return new SettingsManager(whWzSettingManager, dolphinSettingManager, linuxDolphinInstaller, fileSystem);
131+
return new SettingsManager(whWzSettingManager, dolphinSettingManager, fileSystem);
135132
}
136133
}
137134

WheelWizard/Features/DolphinInstaller/LinuxDolphinInstaller.cs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,22 @@ public sealed class LinuxDolphinInstaller(ILinuxCommandEnvironment commandEnviro
1414
{
1515
public bool IsDolphinInstalledInFlatpak()
1616
{
17-
var processResult = processService.Run("flatpak", "info org.DolphinEmu.dolphin-emu");
18-
return processResult.IsSuccess && processResult.Value == 0;
17+
const string dolphinAppId = "org.DolphinEmu.dolphin-emu";
18+
var processResult = processService.Run("flatpak", "list --app --columns=application", out var stdOut, out _);
19+
20+
return processResult.IsSuccess && processResult.Value == 0 && stdOut
21+
.Split('\n')
22+
.Any(line => line == dolphinAppId);
1923
}
2024

2125
public bool IsDolphinInstalledNative()
2226
{
23-
return commandEnvironment.IsCommandAvailable("dolphin-emu");
27+
if (!commandEnvironment.IsCommandAvailable("dolphin-emu"))
28+
{
29+
return false;
30+
}
31+
var processResult = processService.Run("dolphin-emu", "--version");
32+
return processResult.IsSuccess && processResult.Value == 0;
2433
}
2534

2635
public bool IsFlatpakInstalled()
@@ -62,17 +71,34 @@ public async Task<OperationResult> InstallFlatpakDolphin(IProgress<int>? progres
6271
return installFlatpakResult;
6372
}
6473

74+
var addRemoteResult = processService.Run(
75+
"flatpak",
76+
"remote-add --if-not-exists --user dolphin https://flatpak.dolphin-emu.org/releases.flatpakrepo"
77+
);
78+
if (addRemoteResult.IsFailure)
79+
return addRemoteResult.Error;
80+
81+
if (addRemoteResult.Value != 0)
82+
return Fail($"Adding the Dolphin Flatpak remote failed with exit code {addRemoteResult.Value}.");
83+
84+
addRemoteResult = processService.Run(
85+
"flatpak",
86+
"remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo"
87+
);
88+
if (addRemoteResult.IsFailure)
89+
return addRemoteResult.Error;
90+
91+
if (addRemoteResult.Value != 0)
92+
return Fail($"Adding the Flathub Flatpak remote failed with exit code {addRemoteResult.Value}.");
93+
6594
var installDolphinResult = await processService.RunWithProgressAsync(
66-
"pkexec",
67-
"flatpak --system install -y org.DolphinEmu.dolphin-emu",
95+
"flatpak",
96+
"install --user -y dolphin org.DolphinEmu.dolphin-emu",
6897
progress
6998
);
7099
if (installDolphinResult.IsFailure)
71100
return installDolphinResult.Error;
72101

73-
if (installDolphinResult.Value is 126 or 127)
74-
return Fail("You need to be an administrator to install Dolphin via Flatpak.");
75-
76102
if (installDolphinResult.Value != 0)
77103
return Fail($"Dolphin installation failed with exit code {installDolphinResult.Value}.");
78104

WheelWizard/Features/DolphinInstaller/LinuxProcessService.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@ namespace WheelWizard.DolphinInstaller;
55

66
public interface ILinuxProcessService
77
{
8+
OperationResult<int> Run(string fileName, string arguments, out string stdOut, out string stdErr);
89
OperationResult<int> Run(string fileName, string arguments);
910
Task<OperationResult<int>> RunWithProgressAsync(string fileName, string arguments, IProgress<int>? progress = null);
1011
Task<OperationResult> LaunchAndStopAsync(string fileName, string arguments, TimeSpan duration);
1112
}
1213

1314
public sealed class LinuxProcessService : ILinuxProcessService
1415
{
15-
public OperationResult<int> Run(string fileName, string arguments)
16+
public OperationResult<int> Run(string fileName, string arguments, out string stdOut, out string stdErr)
1617
{
17-
return TryCatch(
18+
var localStdOut = "";
19+
var localStdErr = "";
20+
var result = TryCatch(
1821
() =>
1922
{
2023
var processInfo = new ProcessStartInfo
@@ -31,11 +34,22 @@ public OperationResult<int> Run(string fileName, string arguments)
3134
if (process == null)
3235
return -1;
3336

37+
localStdOut = process.StandardOutput.ReadToEnd();
38+
localStdErr = process.StandardError.ReadToEnd();
3439
process.WaitForExit();
3540
return process.ExitCode;
3641
},
3742
$"Failed to run process: {fileName} {arguments}"
3843
);
44+
45+
stdOut = localStdOut;
46+
stdErr = localStdErr;
47+
return result;
48+
}
49+
50+
public OperationResult<int> Run(string fileName, string arguments)
51+
{
52+
return Run(fileName, arguments, out _, out _);
3953
}
4054

4155
public async Task<OperationResult<int>> RunWithProgressAsync(string fileName, string arguments, IProgress<int>? progress = null)

WheelWizard/Features/Settings/SettingsManager.cs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ public class SettingsManager : ISettingsManager
1212
{
1313
private readonly IWhWzSettingManager _whWzSettingManager;
1414
private readonly IDolphinSettingManager _dolphinSettingManager;
15-
private readonly ILinuxDolphinInstaller _linuxDolphinInstaller;
1615
private readonly IFileSystem _fileSystem;
1716

1817
private readonly Setting _dolphinCompilationMode;
@@ -27,13 +26,11 @@ public class SettingsManager : ISettingsManager
2726
public SettingsManager(
2827
IWhWzSettingManager whWzSettingManager,
2928
IDolphinSettingManager dolphinSettingManager,
30-
ILinuxDolphinInstaller linuxDolphinInstaller,
3129
IFileSystem fileSystem
3230
)
3331
{
3432
_whWzSettingManager = whWzSettingManager;
3533
_dolphinSettingManager = dolphinSettingManager;
36-
_linuxDolphinInstaller = linuxDolphinInstaller;
3734
_fileSystem = fileSystem;
3835

3936
#region WhWz settings
@@ -48,12 +45,6 @@ IFileSystem fileSystem
4845

4946
if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX)
5047
{
51-
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
52-
return EnvHelper.IsValidUnixCommand(pathOrCommand);
53-
54-
if (PathManager.IsFlatpakDolphinFilePath(pathOrCommand) && !_linuxDolphinInstaller.IsDolphinInstalledInFlatpak())
55-
return false;
56-
5748
return EnvHelper.IsValidUnixCommand(pathOrCommand);
5849
}
5950

@@ -99,7 +90,7 @@ IFileSystem fileSystem
9990
return false;
10091

10192
// `~/.dolphin-emu` would be used if it exists
102-
if (!PathManager.IsFlatpakDolphinFilePath() && _fileSystem.Directory.Exists(PathManager.LinuxDolphinLegacyFolderPath))
93+
if (!PathManager.IsFlatpakDolphinFilePath(dolphinLocation) && _fileSystem.Directory.Exists(PathManager.LinuxDolphinLegacyFolderPath))
10394
return false;
10495

10596
return true;

WheelWizard/Resources/Languages/cs.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ cs:
161161
rooms: "Místnosti"
162162
miis: "Miiy"
163163
text:
164-
made_by_string: "Vytvořili: {$1} \\n a {$2}"
164+
made_by_string: |
165+
Vytvořili: {$1}
166+
a {$2}
165167
thanks_translators: "Díky moc všem překladatelům:"
166168
wh_wz_translation_percentage: "Překlady pro tento jazyk jsou z {$1}% kompletní"
167169
powered_gamebanana: "Poháněno společností GameBanana"
@@ -241,10 +243,10 @@ cs:
241243
rr_to_old:
242244
extra: "Tvoje verze Retro Rewind je moc stará na aktualizaci. Chtěl bys znovu nainstalovat Retro Rewind?"
243245
title: "Verze Retro Rewind je moc stará."
244-
rr_not_determent:
246+
rr_not_determined:
245247
title: "Stáhnout Retro Rewind"
246248
extra: "Tvojí verzi Retro Rewind se nepodařilo určit. Chtěl bys si stáhnout Retro Rewind?"
247-
dolphin_flatpack:
249+
dolphin_flatpak:
248250
title: "Instalace Dolphin Flatpak"
249251
install_mod:
250252
title: "Chceš stáhnout a nainstalovat mod: {$1}?"

0 commit comments

Comments
 (0)