Skip to content
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
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jobs:
- name: Restore dependencies
run: dotnet restore ControlPad/ControlPad.csproj

- name: Run unit tests
run: dotnet test ControlPad.Tests/ControlPad.Tests.csproj -c Release

- name: Publish (framework-dependent)
run: dotnet publish ControlPad/ControlPad.csproj -c Release -r win-x64 --no-self-contained -o publish

Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
ControlPad/bin/
ControlPad/obj/
ControlPad/publish/
ControlPad.Tests/bin/
ControlPad.Tests/obj/

*.user
*.suo
Expand All @@ -13,4 +15,4 @@ ControlPad/publish/

# Installer output
Installer/Output/
Installer/redist/
Installer/redist/
30 changes: 30 additions & 0 deletions ControlPad.Tests/ActionTypeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using ControlPad;

namespace ControlPad.Tests
{
public class ActionTypeTests
{
[Fact]
public void Constructor_SetsTypeAndDescription()
{
var actionType = new ActionType(EActionType.OpenWebsite, "Open Website");

Assert.Equal(EActionType.OpenWebsite, actionType.Type);
Assert.Equal("Open Website", actionType.Description);
}

[Fact]
public void Enum_DefinesExpectedActionTypes()
{
var values = Enum.GetValues<EActionType>();

Assert.Contains(EActionType.MuteProcess, values);
Assert.Contains(EActionType.MuteMainAudio, values);
Assert.Contains(EActionType.MuteMic, values);
Assert.Contains(EActionType.OpenProcess, values);
Assert.Contains(EActionType.OpenWebsite, values);
Assert.Contains(EActionType.KeyPress, values);
Assert.Equal(6, values.Length);
}
}
}
39 changes: 39 additions & 0 deletions ControlPad.Tests/AudioStreamTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using ControlPad;

namespace ControlPad.Tests
{
public class AudioStreamTests
{
[Fact]
public void Constructor_UsesMicNameAsDisplayName_WhenMicNameProvided()
{
var stream = new AudioStream("spotify", "USB Mic");

Assert.Equal("USB Mic", stream.DisplayName);
}

[Fact]
public void Constructor_UsesProcessAsDisplayName_WhenOnlyProcessProvided()
{
var stream = new AudioStream("discord", null);

Assert.Equal("discord", stream.DisplayName);
}

[Fact]
public void Constructor_UsesMainAudio_WhenProcessAndMicAreNull()
{
var stream = new AudioStream(null, null);

Assert.Equal("Main Audio", stream.DisplayName);
}

[Fact]
public void Constructor_PrioritizesMicNameOverProcess_WhenBothProvided()
{
var stream = new AudioStream("game", "Headset Mic");

Assert.Equal("Headset Mic", stream.DisplayName);
}
}
}
40 changes: 40 additions & 0 deletions ControlPad.Tests/ButtonCategoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using ControlPad;

namespace ControlPad.Tests
{
public class ButtonCategoryTests
{
[Fact]
public void Constructor_InitializesNameIdAndEmptyActions()
{
var category = new ButtonCategory("Actions", 2);

Assert.Equal("Actions", category.Name);
Assert.Equal(2, category.Id);
Assert.NotNull(category.ButtonActions);
Assert.Empty(category.ButtonActions);
}

[Fact]
public void ToString_ReturnsName()
{
var category = new ButtonCategory("Macros", 9);

Assert.Equal("Macros", category.ToString());
}

[Fact]
public void ButtonActions_CanAddAndRemoveEntries()
{
var category = new ButtonCategory("Media", 1);
var actionType = new ActionType(EActionType.MuteMainAudio, "Mute");
var action = new ButtonAction(actionType) { ActionProperty = null };

category.ButtonActions.Add(action);
Assert.Single(category.ButtonActions);

category.ButtonActions.Remove(action);
Assert.Empty(category.ButtonActions);
}
}
}
32 changes: 32 additions & 0 deletions ControlPad.Tests/ControlPad.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

<ItemGroup>
<Compile Include="../ControlPad/AudioStream.cs" Link="Source/AudioStream.cs" />
<Compile Include="../ControlPad/ActionType.cs" Link="Source/ActionType.cs" />
<Compile Include="../ControlPad/ButtonAction.cs" Link="Source/ButtonAction.cs" />
<Compile Include="../ControlPad/ButtonCategory.cs" Link="Source/ButtonCategory.cs" />
<Compile Include="../ControlPad/Preset.cs" Link="Source/Preset.cs" />
<Compile Include="../ControlPad/SliderCategory.cs" Link="Source/SliderCategory.cs" />
<Compile Include="../ControlPad/Converters/SliderValueConverter.cs" Link="Source/SliderValueConverter.cs" />
<Compile Include="../ControlPad/Utils/IdAllocator.cs" Link="Source/IdAllocator.cs" />
</ItemGroup>

</Project>
47 changes: 47 additions & 0 deletions ControlPad.Tests/IdAllocatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using ControlPad.Utils;

namespace ControlPad.Tests
{
public class IdAllocatorTests
{
[Fact]
public void GetFreeId_ReturnsZero_WhenCollectionEmpty()
{
var result = IdAllocator.GetFreeId(Array.Empty<int>(), x => x);

Assert.Equal(0, result);
}

[Fact]
public void GetFreeId_ReturnsOne_WhenZeroExists()
{
var result = IdAllocator.GetFreeId(new[] { 0 }, x => x);

Assert.Equal(1, result);
}

[Fact]
public void GetFreeId_FindsGapInSequence()
{
var result = IdAllocator.GetFreeId(new[] { 0, 2, 3 }, x => x);

Assert.Equal(1, result);
}

[Fact]
public void GetFreeId_IgnoresNegativeIds_AndReturnsZeroWhenMissing()
{
var result = IdAllocator.GetFreeId(new[] { -10, -1, 4, 7 }, x => x);

Assert.Equal(0, result);
}

[Fact]
public void GetFreeId_HandlesLargeNonSequentialNumbers()
{
var result = IdAllocator.GetFreeId(new[] { 0, 1, 2, 1000, 5000 }, x => x);

Assert.Equal(3, result);
}
}
}
56 changes: 56 additions & 0 deletions ControlPad.Tests/PresetTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.ComponentModel;
using ControlPad;

namespace ControlPad.Tests
{
public class PresetTests
{
[Fact]
public void Constructor_SetsInitialValues()
{
var preset = new Preset(2, "Gaming");

Assert.Equal(2, preset.Id);
Assert.Equal("Gaming", preset.Name);
}

[Fact]
public void Id_SetToDifferentValue_RaisesPropertyChanged()
{
var preset = new Preset(1, "Default");
PropertyChangedEventArgs? captured = null;
preset.PropertyChanged += (_, e) => captured = e;

preset.Id = 3;

Assert.NotNull(captured);
Assert.Equal(nameof(Preset.Id), captured!.PropertyName);
}

[Fact]
public void Name_SetToDifferentValue_RaisesPropertyChanged()
{
var preset = new Preset(1, "Default");
PropertyChangedEventArgs? captured = null;
preset.PropertyChanged += (_, e) => captured = e;

preset.Name = "Streaming";

Assert.NotNull(captured);
Assert.Equal(nameof(Preset.Name), captured!.PropertyName);
}

[Fact]
public void SettingSameValues_DoesNotRaisePropertyChanged()
{
var preset = new Preset(1, "Default");
int events = 0;
preset.PropertyChanged += (_, _) => events++;

preset.Id = 1;
preset.Name = "Default";

Assert.Equal(0, events);
}
}
}
58 changes: 58 additions & 0 deletions ControlPad.Tests/SliderCategoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Collections.ObjectModel;
using ControlPad;

namespace ControlPad.Tests
{
public class SliderCategoryTests
{
[Fact]
public void Constructor_InitializesNameIdAndEmptyAudioStreams()
{
var category = new SliderCategory("Voice", 12);

Assert.Equal("Voice", category.Name);
Assert.Equal(12, category.Id);
Assert.NotNull(category.AudioStreams);
Assert.Empty(category.AudioStreams);
}

[Fact]
public void ToString_ReturnsName()
{
var category = new SliderCategory("Music", 4);

Assert.Equal("Music", category.ToString());
}

[Fact]
public void ChangingIdNameAudioStreams_RaisesPropertyChangedForEach()
{
var category = new SliderCategory("Old", 1);
var changed = new List<string?>();
category.PropertyChanged += (_, e) => changed.Add(e.PropertyName);

category.Id = 5;
category.Name = "New";
category.AudioStreams = new ObservableCollection<AudioStream> { new AudioStream("spotify", null) };

Assert.Contains(nameof(SliderCategory.Id), changed);
Assert.Contains(nameof(SliderCategory.Name), changed);
Assert.Contains(nameof(SliderCategory.AudioStreams), changed);
}

[Fact]
public void SettingSameValues_DoesNotRaisePropertyChanged()
{
var category = new SliderCategory("Same", 1);
var sameCollection = category.AudioStreams;
int events = 0;
category.PropertyChanged += (_, _) => events++;

category.Id = 1;
category.Name = "Same";
category.AudioStreams = sameCollection;

Assert.Equal(0, events);
}
}
}
56 changes: 56 additions & 0 deletions ControlPad.Tests/SliderValueConverterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using ControlPad.Converters;

namespace ControlPad.Tests
{
public class SliderValueConverterTests
{
[Fact]
public void SliderToFloat_ReturnsZero_ForRawValueOne()
{
var result = SliderValueConverter.SliderToFloat(1, 1d);

Assert.Equal(0f, result);
}

[Fact]
public void SliderToFloat_ReturnsOne_ForRawValue1023_WithExponentOne()
{
var result = SliderValueConverter.SliderToFloat(1023, 1d);

Assert.Equal(1f, result, 5);
}

[Fact]
public void SliderToFloat_ClampsToZero_ForValuesBelowThreshold()
{
var result = SliderValueConverter.SliderToFloat(5, 1d);

Assert.Equal(0f, result);
}

[Fact]
public void SliderToFloat_AppliesExponentCurve()
{
var linear = SliderValueConverter.SliderToFloat(512, 1d);
var curved = SliderValueConverter.SliderToFloat(512, 2d);

Assert.True(curved < linear);
}

[Fact]
public void SliderToFloat_ClampsInputBelowRange()
{
var result = SliderValueConverter.SliderToFloat(-100, 1d);

Assert.Equal(0f, result);
}

[Fact]
public void SliderToFloat_ClampsInputAboveRange()
{
var result = SliderValueConverter.SliderToFloat(5000, 1d);

Assert.Equal(1f, result, 5);
}
}
}
Loading
Loading