From 4820c0b81787e1a21d52b8c8efea935b5adf0c86 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Mon, 12 Jun 2017 22:17:01 +0100 Subject: [PATCH 1/2] Start work on command tests TODO: -[x] Test command separators -[x] Test command searching -[ ] Test command execution -[ ] Test command builders (properly) -[ ] Test ModuleClassBuilder (properly) -[ ] Test all features of CommandServiceConfig -[ ] Test type readers -[ ] Test preconditions -[ ] Test parameter preconditions -[ ] ???? --- Discord.Net.sln | 20 +++++++- src/Discord.Net.Commands/AssemblyInfo.cs | 3 +- .../CommandServiceConfigTests.Data.cs | 26 ++++++++++ .../CommandServiceConfigTests.cs | 50 +++++++++++++++++++ .../Discord.Net.Commands.Tests.csproj | 23 +++++++++ .../DummyCommandContext.cs | 20 ++++++++ .../Discord.Net.Commands.Tests/DummyModule.cs | 24 +++++++++ 7 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 test/Discord.Net.Commands.Tests/CommandServiceConfigTests.Data.cs create mode 100644 test/Discord.Net.Commands.Tests/CommandServiceConfigTests.cs create mode 100644 test/Discord.Net.Commands.Tests/Discord.Net.Commands.Tests.csproj create mode 100644 test/Discord.Net.Commands.Tests/DummyCommandContext.cs create mode 100644 test/Discord.Net.Commands.Tests/DummyModule.cs diff --git a/Discord.Net.sln b/Discord.Net.sln index a63606787..302ef9f57 100644 --- a/Discord.Net.sln +++ b/Discord.Net.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.26430.13 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.csproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" EndProject @@ -24,6 +24,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Tests", "test\D EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Webhook", "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj", "{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{01324441-C38B-4200-A675-0B7B32BA8CA8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net.Commands.Tests", "test\Discord.Net.Commands.Tests\Discord.Net.Commands.Tests.csproj", "{CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -130,6 +134,18 @@ Global {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x64.Build.0 = Release|Any CPU {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.ActiveCfg = Release|Any CPU {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.Build.0 = Release|Any CPU + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}.Debug|x64.ActiveCfg = Debug|Any CPU + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}.Debug|x64.Build.0 = Debug|Any CPU + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}.Debug|x86.ActiveCfg = Debug|Any CPU + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}.Debug|x86.Build.0 = Debug|Any CPU + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}.Release|Any CPU.Build.0 = Release|Any CPU + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}.Release|x64.ActiveCfg = Release|Any CPU + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}.Release|x64.Build.0 = Release|Any CPU + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}.Release|x86.ActiveCfg = Release|Any CPU + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -140,6 +156,8 @@ Global {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} {688FD1D8-7F01-4539-B2E9-F473C5D699C7} = {288C363D-A636-4EAE-9AC1-4698B641B26E} {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433} = {01324441-C38B-4200-A675-0B7B32BA8CA8} {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} + {CE02AD0A-B9D8-4C0A-B6F8-45A86D7DF3FF} = {01324441-C38B-4200-A675-0B7B32BA8CA8} EndGlobalSection EndGlobal diff --git a/src/Discord.Net.Commands/AssemblyInfo.cs b/src/Discord.Net.Commands/AssemblyInfo.cs index c6b5997b4..ae60f6200 100644 --- a/src/Discord.Net.Commands/AssemblyInfo.cs +++ b/src/Discord.Net.Commands/AssemblyInfo.cs @@ -1,3 +1,4 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Discord.Net.Tests")] \ No newline at end of file +[assembly: InternalsVisibleTo("Discord.Net.Tests")] +[assembly: InternalsVisibleTo("Discord.Net.Commands.Tests")] \ No newline at end of file diff --git a/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.Data.cs b/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.Data.cs new file mode 100644 index 000000000..46ac4f725 --- /dev/null +++ b/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.Data.cs @@ -0,0 +1,26 @@ +using Discord.Commands; +using System.Collections.Generic; +using System.Linq; + +namespace Discord.Net.Commands.Tests +{ + partial class CommandServiceConfigTests + { + public static IEnumerable SeparatorNodeTestData => _separatorNodeTestData; + private static readonly IEnumerable _separatorNodeTestData = new List + { + new CommandServiceConfig{ SeparatorChar = ' '}, + new CommandServiceConfig{ SeparatorChar = '_'}, + new CommandServiceConfig{ SeparatorChar = '.'}, + new CommandServiceConfig{ SeparatorChar = '\u200b'}, + new CommandServiceConfig{ SeparatorChar = '"'} + }.Select(x => new object[] { x, x.SeparatorChar }); + + public static IEnumerable DefaultAliases => _defaultAliases; + private static readonly IEnumerable _defaultAliases = new List + { + "debug ping", + "debug pong" + }; + } +} diff --git a/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.cs b/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.cs new file mode 100644 index 000000000..1c72148cc --- /dev/null +++ b/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.cs @@ -0,0 +1,50 @@ +using Discord.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Discord.Net.Commands.Tests +{ + public partial class CommandServiceConfigTests + { + [Theory] + [MemberData(nameof(SeparatorNodeTestData), MemberType = typeof(CommandServiceConfigTests))] + public async Task CommandSeparators(CommandServiceConfig config, char separatorChar) + { + var service = new CommandService(config); + var module = await service.AddModuleAsync(typeof(DummyModule)).ConfigureAwait(false); + + Assert.True(CommandsLoaded(service, separatorChar)); + + var dummyContext = new DummyCommandContext(); + + foreach (var _alias in DefaultAliases) + { + var alias = _alias.Replace(' ', separatorChar); + + var result = service.Search(dummyContext, alias); + Assert.True(result.IsSuccess, result.ErrorReason); + } + } + + public bool CommandsLoaded(CommandService service, char separatorChar) + { + if (!service.Commands.Any()) + return false; + + var loadedAliases = service.Commands.SelectMany(x => x.Aliases); + + foreach (var alias in DefaultAliases) + { + if (!loadedAliases.Contains(alias.Replace(' ', separatorChar))) + { + return false; + } + } + + return true; + } + } +} diff --git a/test/Discord.Net.Commands.Tests/Discord.Net.Commands.Tests.csproj b/test/Discord.Net.Commands.Tests/Discord.Net.Commands.Tests.csproj new file mode 100644 index 000000000..8031ff95b --- /dev/null +++ b/test/Discord.Net.Commands.Tests/Discord.Net.Commands.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp1.1 + $(PackageTargetFallback);portable-net45+win8+wp8+wpa81 + + + + + + + + + + + + + + + + + + diff --git a/test/Discord.Net.Commands.Tests/DummyCommandContext.cs b/test/Discord.Net.Commands.Tests/DummyCommandContext.cs new file mode 100644 index 000000000..113a28294 --- /dev/null +++ b/test/Discord.Net.Commands.Tests/DummyCommandContext.cs @@ -0,0 +1,20 @@ +using Discord.Commands; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Discord.Net.Commands.Tests +{ + class DummyCommandContext : ICommandContext + { + public IDiscordClient Client { get; set; } + + public IGuild Guild { get; set; } + + public IMessageChannel Channel { get; set; } + + public IUser User { get; set; } + + public IUserMessage Message { get; set; } + } +} diff --git a/test/Discord.Net.Commands.Tests/DummyModule.cs b/test/Discord.Net.Commands.Tests/DummyModule.cs new file mode 100644 index 000000000..a7ad635c6 --- /dev/null +++ b/test/Discord.Net.Commands.Tests/DummyModule.cs @@ -0,0 +1,24 @@ +using Discord.Commands; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Discord.Net.Commands.Tests +{ + [Group("debug")] + class DummyModule : ModuleBase + { + [Command("ping")] + public Task TestCommandAsync(string param) + { + return Task.Delay(0); + } + + [Command("pong")] + public Task AnotherCommandAsync(int param) + { + return Task.Delay(0); + } + } +} From 0c5b4f43ca51cbed940c89b4b82e0365e3ac391e Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Tue, 13 Jun 2017 17:17:37 +0100 Subject: [PATCH 2/2] Use Moq for mocking objects, write more tests --- .../CommandServiceConfigTests.Data.cs | 10 +++-- .../CommandServiceConfigTests.cs | 40 +++++++++-------- .../CommandServiceTests.cs | 45 +++++++++++++++++++ .../Discord.Net.Commands.Tests.csproj | 1 + .../DummyCommandContext.cs | 20 --------- .../Discord.Net.Commands.Tests/DummyModule.cs | 14 ++++++ 6 files changed, 88 insertions(+), 42 deletions(-) create mode 100644 test/Discord.Net.Commands.Tests/CommandServiceTests.cs delete mode 100644 test/Discord.Net.Commands.Tests/DummyCommandContext.cs diff --git a/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.Data.cs b/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.Data.cs index 46ac4f725..49cd0a7f7 100644 --- a/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.Data.cs +++ b/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.Data.cs @@ -16,11 +16,13 @@ namespace Discord.Net.Commands.Tests new CommandServiceConfig{ SeparatorChar = '"'} }.Select(x => new object[] { x, x.SeparatorChar }); - public static IEnumerable DefaultAliases => _defaultAliases; - private static readonly IEnumerable _defaultAliases = new List + public static IEnumerable CaseSensitivityTestData => _caseSensitivityTestData; + private static readonly IEnumerable _caseSensitivityTestData = new object[][] { - "debug ping", - "debug pong" + new object[]{ true, false }, + new object[]{ true, true }, + new object[]{ false, true }, + new object[]{ false, false } }; } } diff --git a/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.cs b/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.cs index 1c72148cc..12e0db7f2 100644 --- a/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.cs +++ b/test/Discord.Net.Commands.Tests/CommandServiceConfigTests.cs @@ -1,4 +1,5 @@ using Discord.Commands; +using Moq; using System; using System.Collections.Generic; using System.Linq; @@ -14,37 +15,40 @@ namespace Discord.Net.Commands.Tests public async Task CommandSeparators(CommandServiceConfig config, char separatorChar) { var service = new CommandService(config); - var module = await service.AddModuleAsync(typeof(DummyModule)).ConfigureAwait(false); + await service.AddModuleAsync(typeof(DummyModule)).ConfigureAwait(false); - Assert.True(CommandsLoaded(service, separatorChar)); + var contextMock = new Mock(); + var context = contextMock.Object; - var dummyContext = new DummyCommandContext(); - - foreach (var _alias in DefaultAliases) + foreach (var _name in DummyModule.CommandNames) { - var alias = _alias.Replace(' ', separatorChar); + var name = _name.Replace(' ', separatorChar); - var result = service.Search(dummyContext, alias); + var result = service.Search(context, name); Assert.True(result.IsSuccess, result.ErrorReason); } } - public bool CommandsLoaded(CommandService service, char separatorChar) + [Theory] + [MemberData(nameof(CaseSensitivityTestData), MemberType = typeof(CommandServiceConfigTests))] + public async Task CaseSensitivity(bool caseSensitive, bool upperCase) { - if (!service.Commands.Any()) - return false; + var service = new CommandService(new CommandServiceConfig { CaseSensitiveCommands = caseSensitive }); + await service.AddModuleAsync(typeof(DummyModule)).ConfigureAwait(false); - var loadedAliases = service.Commands.SelectMany(x => x.Aliases); + var contextMock = new Mock(); + var context = contextMock.Object; - foreach (var alias in DefaultAliases) + foreach (var _name in DummyModule.CommandNames) { - if (!loadedAliases.Contains(alias.Replace(' ', separatorChar))) - { - return false; - } - } + var name = upperCase ? _name.ToUpper() : _name; - return true; + var result = service.Search(context, name); + if (caseSensitive && upperCase) + Assert.False(result.IsSuccess, $"Searching for `{name}` returned successfully"); + else + Assert.True(result.IsSuccess, result.ErrorReason); + } } } } diff --git a/test/Discord.Net.Commands.Tests/CommandServiceTests.cs b/test/Discord.Net.Commands.Tests/CommandServiceTests.cs new file mode 100644 index 000000000..5a4d1bdcb --- /dev/null +++ b/test/Discord.Net.Commands.Tests/CommandServiceTests.cs @@ -0,0 +1,45 @@ +using Discord.Commands; +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Discord.Net.Commands.Tests +{ + class CommandServiceTests + { + [Fact] + public async Task CommandsLoad() + { + var service = new CommandService(); + + var module = await service.AddModuleAsync(typeof(DummyModule)).ConfigureAwait(false); + Assert.NotNull(module); + + var commandAliases = module.Commands.SelectMany(x => x.Aliases); + + foreach (var name in DummyModule.CommandNames) + { + Assert.True(commandAliases.Contains(name), $"The loaded module did not contain the command {name}"); + } + } + + [Fact] + public async Task MultipleLoadsThrows() + { + var service = new CommandService(); + + var module = await service.AddModuleAsync(typeof(DummyModule)).ConfigureAwait(false); + await Assert.ThrowsAsync(() => service.AddModuleAsync(typeof(DummyModule))) + .ConfigureAwait(false); + } + + [Fact] + public async Task InvalidTypeThrows() + { + var service = new CommandService(); + await Assert.ThrowsAsync(() => service.AddModuleAsync(typeof(CommandServiceTests))) + .ConfigureAwait(false); + } + } +} diff --git a/test/Discord.Net.Commands.Tests/Discord.Net.Commands.Tests.csproj b/test/Discord.Net.Commands.Tests/Discord.Net.Commands.Tests.csproj index 8031ff95b..4fdc21beb 100644 --- a/test/Discord.Net.Commands.Tests/Discord.Net.Commands.Tests.csproj +++ b/test/Discord.Net.Commands.Tests/Discord.Net.Commands.Tests.csproj @@ -12,6 +12,7 @@ + diff --git a/test/Discord.Net.Commands.Tests/DummyCommandContext.cs b/test/Discord.Net.Commands.Tests/DummyCommandContext.cs deleted file mode 100644 index 113a28294..000000000 --- a/test/Discord.Net.Commands.Tests/DummyCommandContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Discord.Commands; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Discord.Net.Commands.Tests -{ - class DummyCommandContext : ICommandContext - { - public IDiscordClient Client { get; set; } - - public IGuild Guild { get; set; } - - public IMessageChannel Channel { get; set; } - - public IUser User { get; set; } - - public IUserMessage Message { get; set; } - } -} diff --git a/test/Discord.Net.Commands.Tests/DummyModule.cs b/test/Discord.Net.Commands.Tests/DummyModule.cs index a7ad635c6..964741d02 100644 --- a/test/Discord.Net.Commands.Tests/DummyModule.cs +++ b/test/Discord.Net.Commands.Tests/DummyModule.cs @@ -20,5 +20,19 @@ namespace Discord.Net.Commands.Tests { return Task.Delay(0); } + + //NOTE: do not add this to CommandNames: it is intentional for this command to not be loaded! + [Command("doesNotLoad")] + private Task DoesNotLoadAsync() + { + return Task.Delay(0); + } + + public static IEnumerable CommandNames => _commandNames; + private static readonly IEnumerable _commandNames = new List + { + "debug ping", + "debug pong" + }; } }