From 933a4f85604e21445c9bac8272d64cf3e6f65e00 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Wed, 22 Apr 2020 16:28:51 -0400 Subject: rename to IrcSharp also tidy up formatting with vs tools --- .editorconfig | 57 ++++++++++++ IrcSharp.sln | 48 ++++++++++ IrcStates/Casemap.cs | 43 +++++++++ IrcStates/Channel.cs | 69 +++++++++++++++ IrcStates/Emit.cs | 6 ++ IrcStates/ISupport.cs | 7 ++ IrcStates/IrcStates.csproj | 21 +++++ IrcStates/Numeric.cs | 49 +++++++++++ IrcStates/Server.cs | 6 ++ IrcStates/Tests/Cap.cs | 9 ++ IrcStates/Tests/Casemap.cs | 9 ++ IrcStates/Tests/Channel.cs | 9 ++ IrcStates/Tests/Emit.cs | 9 ++ IrcStates/Tests/ISupport.cs | 10 +++ IrcStates/Tests/Mode.cs | 9 ++ IrcStates/Tests/Motd.cs | 9 ++ IrcStates/Tests/User.cs | 9 ++ IrcStates/Tests/Who.cs | 9 ++ IrcStates/User.cs | 28 ++++++ IrcTokens.sln | 31 ------- IrcTokens/Hostmask.cs | 30 +++++-- IrcTokens/Line.cs | 47 ++++++++-- IrcTokens/Protocol.cs | 2 + IrcTokens/StatefulDecoder.cs | 8 +- IrcTokens/StatefulEncoder.cs | 4 + IrcTokens/Tests/Format.cs | 150 ++++++++++++++++++++++++++++++++ IrcTokens/Tests/FormatTests.cs | 150 -------------------------------- IrcTokens/Tests/Hostmask.cs | 64 ++++++++++++++ IrcTokens/Tests/HostmaskTests.cs | 64 -------------- IrcTokens/Tests/Parser.cs | 57 ++++++++++++ IrcTokens/Tests/ParserTests.cs | 57 ------------ IrcTokens/Tests/StatefulDecoder.cs | 86 ++++++++++++++++++ IrcTokens/Tests/StatefulDecoderTests.cs | 87 ------------------ IrcTokens/Tests/StatefulEncoder.cs | 71 +++++++++++++++ IrcTokens/Tests/StatefulEncoderTests.cs | 72 --------------- IrcTokens/Tests/Tokenization.cs | 118 +++++++++++++++++++++++++ IrcTokens/Tests/TokenizationTests.cs | 118 ------------------------- Sample/Client.cs | 19 ++-- Sample/Program.cs | 8 +- Sample/Sample.csproj | 12 --- Sample/TokensSample.csproj | 12 +++ StatesSample/Program.cs | 12 +++ StatesSample/StatesSample.csproj | 12 +++ 43 files changed, 1087 insertions(+), 620 deletions(-) create mode 100644 .editorconfig create mode 100644 IrcSharp.sln create mode 100644 IrcStates/Casemap.cs create mode 100644 IrcStates/Channel.cs create mode 100644 IrcStates/Emit.cs create mode 100644 IrcStates/ISupport.cs create mode 100644 IrcStates/IrcStates.csproj create mode 100644 IrcStates/Numeric.cs create mode 100644 IrcStates/Server.cs create mode 100644 IrcStates/Tests/Cap.cs create mode 100644 IrcStates/Tests/Casemap.cs create mode 100644 IrcStates/Tests/Channel.cs create mode 100644 IrcStates/Tests/Emit.cs create mode 100644 IrcStates/Tests/ISupport.cs create mode 100644 IrcStates/Tests/Mode.cs create mode 100644 IrcStates/Tests/Motd.cs create mode 100644 IrcStates/Tests/User.cs create mode 100644 IrcStates/Tests/Who.cs create mode 100644 IrcStates/User.cs delete mode 100644 IrcTokens.sln create mode 100644 IrcTokens/Tests/Format.cs delete mode 100644 IrcTokens/Tests/FormatTests.cs create mode 100644 IrcTokens/Tests/Hostmask.cs delete mode 100644 IrcTokens/Tests/HostmaskTests.cs create mode 100644 IrcTokens/Tests/Parser.cs delete mode 100644 IrcTokens/Tests/ParserTests.cs create mode 100644 IrcTokens/Tests/StatefulDecoder.cs delete mode 100644 IrcTokens/Tests/StatefulDecoderTests.cs create mode 100644 IrcTokens/Tests/StatefulEncoder.cs delete mode 100644 IrcTokens/Tests/StatefulEncoderTests.cs create mode 100644 IrcTokens/Tests/Tokenization.cs delete mode 100644 IrcTokens/Tests/TokenizationTests.cs delete mode 100644 Sample/Sample.csproj create mode 100644 Sample/TokensSample.csproj create mode 100644 StatesSample/Program.cs create mode 100644 StatesSample/StatesSample.csproj diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c81e008 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,57 @@ + +[*.proto] +indent_style=tab +indent_size=tab +tab_width=4 + +[*.{asax,ascx,aspx,cs,cshtml,css,htm,html,js,jsx,master,razor,skin,ts,tsx,vb,xaml,xamlx,xoml}] +indent_style=space +indent_size=4 +tab_width=4 + +[*.{appxmanifest,axml,build,config,csproj,dbml,discomap,dtd,json,jsproj,lsproj,njsproj,nuspec,proj,props,resjson,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}] +indent_style=space +indent_size=2 +tab_width=2 + +[*] + +# Microsoft .NET properties +csharp_new_line_before_members_in_object_initializers=true +csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion +csharp_style_var_elsewhere=true:suggestion +csharp_style_var_for_built_in_types=true:suggestion +csharp_style_var_when_type_is_apparent=true:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators=never_if_unnecessary:none +dotnet_style_parentheses_in_other_binary_operators=never_if_unnecessary:none +dotnet_style_parentheses_in_relational_binary_operators=never_if_unnecessary:none +dotnet_style_predefined_type_for_locals_parameters_members=true:suggestion +dotnet_style_predefined_type_for_member_access=true:suggestion +dotnet_style_qualification_for_event=false:suggestion +dotnet_style_qualification_for_field=false:suggestion +dotnet_style_qualification_for_method=false:suggestion +dotnet_style_qualification_for_property=false:suggestion +dotnet_style_require_accessibility_modifiers=for_non_interface_members:suggestion + +# ReSharper inspection severities +resharper_arrange_redundant_parentheses_highlighting=hint +resharper_arrange_this_qualifier_highlighting=hint +resharper_arrange_type_member_modifiers_highlighting=hint +resharper_arrange_type_modifiers_highlighting=hint +resharper_built_in_type_reference_style_for_member_access_highlighting=hint +resharper_built_in_type_reference_style_highlighting=hint +resharper_redundant_base_qualifier_highlighting=warning +resharper_suggest_var_or_type_built_in_types_highlighting=hint +resharper_suggest_var_or_type_elsewhere_highlighting=hint +resharper_suggest_var_or_type_simple_types_highlighting=hint + +# ReSharper properties +resharper_csharp_insert_final_newline=true +resharper_int_align_assignments=true +resharper_int_align_switch_expressions=true +resharper_int_align_switch_sections=true +resharper_int_align_variables=true +resharper_keep_existing_embedded_arrangement=false +resharper_keep_existing_switch_expression_arrangement=false +resharper_place_simple_case_statement_on_same_line=if_owner_is_single_line +resharper_xmldoc_max_blank_lines_between_tags=1 diff --git a/IrcSharp.sln b/IrcSharp.sln new file mode 100644 index 0000000..030a2af --- /dev/null +++ b/IrcSharp.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IrcTokens", "IrcTokens\IrcTokens.csproj", "{9E812F45-B2CD-42D2-8378-EBEBF8697905}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TokensSample", "Sample\TokensSample.csproj", "{A45DA39B-6B47-4713-8049-3B36E0235B67}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IrcStates", "IrcStates\IrcStates.csproj", "{233E3CB4-61F1-4368-9139-7E9F4A58ED2D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatesSample", "StatesSample\StatesSample.csproj", "{BC9F6696-9D83-4F7A-9E15-CE4D3626C1AF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1A85EB22-D7B4-417F-AC3B-DAFD97DDEA08}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9E812F45-B2CD-42D2-8378-EBEBF8697905}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E812F45-B2CD-42D2-8378-EBEBF8697905}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E812F45-B2CD-42D2-8378-EBEBF8697905}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E812F45-B2CD-42D2-8378-EBEBF8697905}.Release|Any CPU.Build.0 = Release|Any CPU + {A45DA39B-6B47-4713-8049-3B36E0235B67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A45DA39B-6B47-4713-8049-3B36E0235B67}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A45DA39B-6B47-4713-8049-3B36E0235B67}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A45DA39B-6B47-4713-8049-3B36E0235B67}.Release|Any CPU.Build.0 = Release|Any CPU + {233E3CB4-61F1-4368-9139-7E9F4A58ED2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {233E3CB4-61F1-4368-9139-7E9F4A58ED2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {233E3CB4-61F1-4368-9139-7E9F4A58ED2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {233E3CB4-61F1-4368-9139-7E9F4A58ED2D}.Release|Any CPU.Build.0 = Release|Any CPU + {BC9F6696-9D83-4F7A-9E15-CE4D3626C1AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC9F6696-9D83-4F7A-9E15-CE4D3626C1AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC9F6696-9D83-4F7A-9E15-CE4D3626C1AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC9F6696-9D83-4F7A-9E15-CE4D3626C1AF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0B91F0EA-8564-4318-8EEC-ED0640475141} + EndGlobalSection +EndGlobal diff --git a/IrcStates/Casemap.cs b/IrcStates/Casemap.cs new file mode 100644 index 0000000..484c490 --- /dev/null +++ b/IrcStates/Casemap.cs @@ -0,0 +1,43 @@ +using System; + +namespace IrcStates +{ + public static class Casemap + { + public enum CaseMapping + { + Rfc1459, + Ascii + } + + private const string AsciiUpperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private const string AsciiLowerChars = "abcdefghijklmnopqrstuvwxyz"; + private const string Rfc1459UpperChars = AsciiUpperChars + @"[]~\"; + private const string Rfc1459LowerChars = AsciiLowerChars + @"{}^|"; + + private static string Replace(string s, string upper, string lower) + { + for (var i = 0; i < upper.Length; ++i) + { + s = s.Replace(upper[i], lower[i]); + } + + return s; + } + + public static string CaseFold(CaseMapping mapping, string s) + { + if (s != null) + { + return mapping switch + { + CaseMapping.Rfc1459 => Replace(s, Rfc1459UpperChars, Rfc1459LowerChars), + CaseMapping.Ascii => Replace(s, AsciiUpperChars, AsciiLowerChars), + _ => throw new ArgumentOutOfRangeException(nameof(mapping), mapping, null) + }; + } + + return string.Empty; + } + } +} diff --git a/IrcStates/Channel.cs b/IrcStates/Channel.cs new file mode 100644 index 0000000..1850e51 --- /dev/null +++ b/IrcStates/Channel.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace IrcStates +{ + public class Channel + { + public string Name { get; set; } + public string NameLower { get; set; } + public Dictionary Users { get; set; } + public string Topic { get; set; } + public string TopicSetter { get; set; } + public DateTime TopicTime { get; set; } + public DateTime Created { get; set; } + public Dictionary> ListModes { get; set; } + public Dictionary Modes { get; set; } + + public override string ToString() + { + return $"Channel(name={Name})"; + } + + public void SetName(string name, string nameLower) + { + Name = name; + NameLower = nameLower; + } + + public void AddMode(string ch, string param, bool listMode) + { + if (listMode) + { + if (!ListModes.ContainsKey(ch)) + { + ListModes[ch] = new List(); + } + + if (ListModes[ch].Contains(param)) + { + ListModes[ch].Add(param ?? string.Empty); + } + } + else + { + Modes[ch] = param; + } + } + + public void RemoveMode(string ch, string param) + { + if (ListModes.ContainsKey(ch)) + { + if (ListModes[ch].Contains(param)) + { + ListModes[ch].Remove(param); + if (!ListModes[ch].Any()) + { + ListModes.Remove(ch); + } + } + } + else if (Modes.ContainsKey(ch)) + { + Modes.Remove(ch); + } + } + } +} diff --git a/IrcStates/Emit.cs b/IrcStates/Emit.cs new file mode 100644 index 0000000..7fc41ae --- /dev/null +++ b/IrcStates/Emit.cs @@ -0,0 +1,6 @@ +namespace IrcStates +{ + public class Emit + { + } +} diff --git a/IrcStates/ISupport.cs b/IrcStates/ISupport.cs new file mode 100644 index 0000000..91e6d6d --- /dev/null +++ b/IrcStates/ISupport.cs @@ -0,0 +1,7 @@ +// ReSharper disable InconsistentNaming +namespace IrcStates +{ + public class ISupport + { + } +} diff --git a/IrcStates/IrcStates.csproj b/IrcStates/IrcStates.csproj new file mode 100644 index 0000000..7500c8b --- /dev/null +++ b/IrcStates/IrcStates.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.1 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + diff --git a/IrcStates/Numeric.cs b/IrcStates/Numeric.cs new file mode 100644 index 0000000..f5525f6 --- /dev/null +++ b/IrcStates/Numeric.cs @@ -0,0 +1,49 @@ +// ReSharper disable InconsistentNaming +namespace IrcStates +{ + public static class Numeric + { + public const string RPL_WELCOME = "001"; + public const string RPL_ISUPPORT = "005"; + public const string RPL_MOTD = "372"; + public const string RPL_MOTDSTART = "375"; + public const string RPL_UMODEIS = "221"; + public const string RPL_VISIBLEHOST = "396"; + + public const string RPL_CHANNELMODEIS = "324"; + public const string RPL_CREATIONTIME = "329"; + public const string RPL_TOPIC = "332"; + public const string RPL_TOPICWHOTIME = "333"; + + public const string RPL_WHOREPLY = "352"; + public const string RPL_WHOSPCRPL = "354"; + public const string RPL_ENDOFWHO = "315"; + public const string RPL_NAMREPLY = "353"; + public const string RPL_ENDOFNAMES = "366"; + + public const string RPL_BANLIST = "367"; + public const string RPL_ENDOFBANLIST = "368"; + public const string RPL_QUIETLIST = "728"; + public const string RPL_ENDOFQUIETLIST = "729"; + + public const string RPL_SASLSUCCESS = "903"; + public const string ERR_SASLFAIL = "904"; + public const string ERR_SASLTOOLONG = "905"; + public const string ERR_SASLABORTED = "906"; + public const string ERR_SASLALREADY = "907"; + public const string RPL_SASLMECHS = "908"; + + public const string RPL_WHOISUSER = "311"; + public const string RPL_WHOISSERVER = "312"; + public const string RPL_WHOISOPERATOR = "313"; + public const string RPL_WHOISIDLE = "317"; + public const string RPL_WHOISCHANNELS = "319"; + public const string RPL_WHOISACCOUNT = "330"; + public const string RPL_WHOISHOST = "378"; + public const string RPL_WHOISMODES = "379"; + public const string RPL_WHOISSECURE = "671"; + public const string RPL_ENDOFWHOIS = "318"; + + public const string ERR_NOSUCHCHANNEL = "403"; + } +} diff --git a/IrcStates/Server.cs b/IrcStates/Server.cs new file mode 100644 index 0000000..9f97f47 --- /dev/null +++ b/IrcStates/Server.cs @@ -0,0 +1,6 @@ +namespace IrcStates +{ + public class Server + { + } +} diff --git a/IrcStates/Tests/Cap.cs b/IrcStates/Tests/Cap.cs new file mode 100644 index 0000000..dcd0e50 --- /dev/null +++ b/IrcStates/Tests/Cap.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IrcStates.Tests +{ + [TestClass] + public class Cap + { + } +} diff --git a/IrcStates/Tests/Casemap.cs b/IrcStates/Tests/Casemap.cs new file mode 100644 index 0000000..fef9a31 --- /dev/null +++ b/IrcStates/Tests/Casemap.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IrcStates.Tests +{ + [TestClass] + public class Casemap + { + } +} diff --git a/IrcStates/Tests/Channel.cs b/IrcStates/Tests/Channel.cs new file mode 100644 index 0000000..0de5f37 --- /dev/null +++ b/IrcStates/Tests/Channel.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IrcStates.Tests +{ + [TestClass] + public class Channel + { + } +} diff --git a/IrcStates/Tests/Emit.cs b/IrcStates/Tests/Emit.cs new file mode 100644 index 0000000..f4495c5 --- /dev/null +++ b/IrcStates/Tests/Emit.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IrcStates.Tests +{ + [TestClass] + public class Emit + { + } +} diff --git a/IrcStates/Tests/ISupport.cs b/IrcStates/Tests/ISupport.cs new file mode 100644 index 0000000..aa6260a --- /dev/null +++ b/IrcStates/Tests/ISupport.cs @@ -0,0 +1,10 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +// ReSharper disable InconsistentNaming + +namespace IrcStates.Tests +{ + [TestClass] + public class ISupport + { + } +} diff --git a/IrcStates/Tests/Mode.cs b/IrcStates/Tests/Mode.cs new file mode 100644 index 0000000..e7b70f4 --- /dev/null +++ b/IrcStates/Tests/Mode.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IrcStates.Tests +{ + [TestClass] + public class Mode + { + } +} diff --git a/IrcStates/Tests/Motd.cs b/IrcStates/Tests/Motd.cs new file mode 100644 index 0000000..8ca7f07 --- /dev/null +++ b/IrcStates/Tests/Motd.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IrcStates.Tests +{ + [TestClass] + public class Motd + { + } +} diff --git a/IrcStates/Tests/User.cs b/IrcStates/Tests/User.cs new file mode 100644 index 0000000..540d31f --- /dev/null +++ b/IrcStates/Tests/User.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IrcStates.Tests +{ + [TestClass] + public class User + { + } +} diff --git a/IrcStates/Tests/Who.cs b/IrcStates/Tests/Who.cs new file mode 100644 index 0000000..359991c --- /dev/null +++ b/IrcStates/Tests/Who.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IrcStates.Tests +{ + [TestClass] + public class Who + { + } +} diff --git a/IrcStates/User.cs b/IrcStates/User.cs new file mode 100644 index 0000000..9bc5ce8 --- /dev/null +++ b/IrcStates/User.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace IrcStates +{ + public class User + { + private string NickName; + private string NickNameLower; + + public string UserName { get; set; } + public string HostName { get; set; } + public string RealName { get; set; } + public string Account { get; set; } + public string Away { get; set; } + public HashSet Channels { get; set; } + + public override string ToString() + { + return $"User(nickname={NickName})"; + } + + public void SetNickName(string nick, string nickLower) + { + NickName = nick; + NickNameLower = nickLower; + } + } +} diff --git a/IrcTokens.sln b/IrcTokens.sln deleted file mode 100644 index c9b3d28..0000000 --- a/IrcTokens.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30011.22 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IrcTokens", "IrcTokens\IrcTokens.csproj", "{9E812F45-B2CD-42D2-8378-EBEBF8697905}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "Sample\Sample.csproj", "{A45DA39B-6B47-4713-8049-3B36E0235B67}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9E812F45-B2CD-42D2-8378-EBEBF8697905}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9E812F45-B2CD-42D2-8378-EBEBF8697905}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9E812F45-B2CD-42D2-8378-EBEBF8697905}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9E812F45-B2CD-42D2-8378-EBEBF8697905}.Release|Any CPU.Build.0 = Release|Any CPU - {A45DA39B-6B47-4713-8049-3B36E0235B67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A45DA39B-6B47-4713-8049-3B36E0235B67}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A45DA39B-6B47-4713-8049-3B36E0235B67}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A45DA39B-6B47-4713-8049-3B36E0235B67}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {0B91F0EA-8564-4318-8EEC-ED0640475141} - EndGlobalSection -EndGlobal diff --git a/IrcTokens/Hostmask.cs b/IrcTokens/Hostmask.cs index 0b07f80..01fe7d5 100644 --- a/IrcTokens/Hostmask.cs +++ b/IrcTokens/Hostmask.cs @@ -5,29 +5,45 @@ namespace IrcTokens /// /// Represents the three parts of a hostmask. Parse with the constructor. /// - public class Hostmask + public class Hostmask : IEquatable { public string NickName { get; set; } public string UserName { get; set; } public string HostName { get; set; } - public override string ToString() => _source; + public override string ToString() + { + return _source; + } - public override int GetHashCode() => _source.GetHashCode(StringComparison.Ordinal); + public override int GetHashCode() + { + return _source.GetHashCode(StringComparison.Ordinal); + } - public override bool Equals(object obj) + public bool Equals(Hostmask other) { - if (obj == null || GetType() != obj.GetType()) + if (other == null) + { return false; + } - return _source == ((Hostmask) obj)._source; + return _source == other._source; + } + + public override bool Equals(object obj) + { + return Equals(obj as Hostmask); } private readonly string _source; public Hostmask(string source) { - if (source == null) return; + if (source == null) + { + return; + } _source = source; diff --git a/IrcTokens/Line.cs b/IrcTokens/Line.cs index 9056097..24efe4a 100644 --- a/IrcTokens/Line.cs +++ b/IrcTokens/Line.cs @@ -8,7 +8,7 @@ namespace IrcTokens /// /// Tools to represent, parse, and format IRC lines /// - public class Line + public class Line : IEquatable { public Dictionary Tags { get; set; } public string Source { get; set; } @@ -16,32 +16,52 @@ namespace IrcTokens public List Params { get; set; } private Hostmask _hostmask; - private readonly string _rawLine; public override string ToString() { var vars = new List(); if (Command != null) + { vars.Add($"command={Command}"); + } + if (Source != null) + { vars.Add($"source={Source}"); + } + if (Params != null && Params.Any()) + { vars.Add($"params=[{string.Join(",", Params)}]"); + } + if (Tags != null && Tags.Any()) + { vars.Add($"tags=[{string.Join(";", Tags.Select(kvp => $"{kvp.Key}={kvp.Value}"))}]"); + } return $"Line({string.Join(", ", vars)})"; } - public override int GetHashCode() => Format().GetHashCode(StringComparison.Ordinal); + public override int GetHashCode() + { + return Format().GetHashCode(StringComparison.Ordinal); + } - public override bool Equals(object obj) + public bool Equals(Line other) { - if (obj == null || GetType() != obj.GetType()) + if (other == null) + { return false; + } - return Format() == ((Line) obj).Format(); + return Format() == other.Format(); + } + + public override bool Equals(object obj) + { + return Equals(obj as Line); } public Hostmask Hostmask => @@ -56,9 +76,10 @@ namespace IrcTokens public Line(string line) { if (string.IsNullOrWhiteSpace(line)) + { throw new ArgumentNullException(nameof(line)); + } - _rawLine = line; string[] split; if (line.StartsWith('@')) @@ -97,7 +118,7 @@ namespace IrcTokens Params = line.Contains(' ', StringComparison.Ordinal) ? line.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList() - : new List {line}; + : new List { line }; if (Params[0].StartsWith(':')) { @@ -135,7 +156,9 @@ namespace IrcTokens } if (Source != null) + { outs.Add($":{Source}"); + } outs.Add(Command); @@ -147,14 +170,22 @@ namespace IrcTokens foreach (var p in Params) { if (p.Contains(' ', StringComparison.Ordinal)) + { throw new ArgumentException(@"non-last parameters cannot have spaces", p); + } + if (p.StartsWith(':')) + { throw new ArgumentException(@"non-last parameters cannot start with colon", p); + } } outs.AddRange(Params); if (string.IsNullOrWhiteSpace(last) || last.Contains(' ', StringComparison.Ordinal) || last.StartsWith(':')) + { last = $":{last}"; + } + outs.Add(last); } diff --git a/IrcTokens/Protocol.cs b/IrcTokens/Protocol.cs index 3769ea3..6ddb079 100644 --- a/IrcTokens/Protocol.cs +++ b/IrcTokens/Protocol.cs @@ -50,7 +50,9 @@ namespace IrcTokens } } else + { unescaped.Append(current); + } } return unescaped.ToString(); diff --git a/IrcTokens/StatefulDecoder.cs b/IrcTokens/StatefulDecoder.cs index 2304431..47106d9 100644 --- a/IrcTokens/StatefulDecoder.cs +++ b/IrcTokens/StatefulDecoder.cs @@ -18,8 +18,10 @@ namespace IrcTokens set { if (value != null) + { _encoding = Encoding.GetEncoding(value.CodePage, EncoderFallback.ExceptionFallback, DecoderFallback.ReplacementFallback); + } } } @@ -30,8 +32,10 @@ namespace IrcTokens set { if (value != null) - _encoding = Encoding.GetEncoding(value.CodePage, EncoderFallback.ReplacementFallback, + { + _fallback = Encoding.GetEncoding(value.CodePage, EncoderFallback.ReplacementFallback, DecoderFallback.ReplacementFallback); + } } } @@ -55,7 +59,9 @@ namespace IrcTokens public List Push(byte[] data) { if (data == null || data.Length == 0) + { return null; + } _buffer = _buffer.Concat(data).ToArray(); diff --git a/IrcTokens/StatefulEncoder.cs b/IrcTokens/StatefulEncoder.cs index 17295eb..c036400 100644 --- a/IrcTokens/StatefulEncoder.cs +++ b/IrcTokens/StatefulEncoder.cs @@ -16,8 +16,10 @@ namespace IrcTokens set { if (value != null) + { _encoding = Encoding.GetEncoding(value.CodePage, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback); + } } } @@ -52,7 +54,9 @@ namespace IrcTokens public void Push(Line line) { if (line == null) + { throw new ArgumentNullException(nameof(line)); + } PendingBytes = PendingBytes.Concat(Encoding.GetBytes($"{line.Format()}\r\n")).ToArray(); _bufferedLines.Enqueue(line); diff --git a/IrcTokens/Tests/Format.cs b/IrcTokens/Tests/Format.cs new file mode 100644 index 0000000..64f974a --- /dev/null +++ b/IrcTokens/Tests/Format.cs @@ -0,0 +1,150 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; + +namespace IrcTokens.Tests +{ + [TestClass] + public class Format + { + [TestMethod] + public void TestTags() + { + var line = new Line + { + Command = "PRIVMSG", + Params = new List { "#channel", "hello" }, + Tags = new Dictionary { { "id", "\\" + " " + ";" + "\r\n" } } + }.Format(); + + Assert.AreEqual("@id=\\\\\\s\\:\\r\\n PRIVMSG #channel hello", line); + } + + [TestMethod] + public void TestMissingTag() + { + var line = new Line + { + Command = "PRIVMSG", + Params = new List { "#channel", "hello" } + }.Format(); + + Assert.AreEqual("PRIVMSG #channel hello", line); + } + + [TestMethod] + public void TestNullTag() + { + var line = new Line + { + Command = "PRIVMSG", + Params = new List { "#channel", "hello" }, + Tags = new Dictionary { { "a", null } } + }.Format(); + + Assert.AreEqual("@a PRIVMSG #channel hello", line); + } + + [TestMethod] + public void TestEmptyTag() + { + var line = new Line + { + Command = "PRIVMSG", + Params = new List { "#channel", "hello" }, + Tags = new Dictionary { { "a", "" } } + }.Format(); + + Assert.AreEqual("@a PRIVMSG #channel hello", line); + } + + [TestMethod] + public void TestSource() + { + var line = new Line + { + Command = "PRIVMSG", + Params = new List { "#channel", "hello" }, + Source = "nick!user@host" + }.Format(); + + Assert.AreEqual(":nick!user@host PRIVMSG #channel hello", line); + } + + [TestMethod] + public void TestCommandLowercase() + { + var line = new Line { Command = "privmsg" }.Format(); + Assert.AreEqual("privmsg", line); + } + + [TestMethod] + public void TestCommandUppercase() + { + var line = new Line { Command = "PRIVMSG" }.Format(); + Assert.AreEqual("PRIVMSG", line); + } + + [TestMethod] + public void TestTrailingSpace() + { + var line = new Line + { + Command = "PRIVMSG", + Params = new List { "#channel", "hello world" } + }.Format(); + + Assert.AreEqual("PRIVMSG #channel :hello world", line); + } + + [TestMethod] + public void TestTrailingNoSpace() + { + var line = new Line + { + Command = "PRIVMSG", + Params = new List { "#channel", "helloworld" } + }.Format(); + + Assert.AreEqual("PRIVMSG #channel helloworld", line); + } + + [TestMethod] + public void TestTrailingDoubleColon() + { + var line = new Line + { + Command = "PRIVMSG", + Params = new List { "#channel", ":helloworld" } + }.Format(); + + Assert.AreEqual("PRIVMSG #channel ::helloworld", line); + } + + [TestMethod] + public void TestInvalidNonLastSpace() + { + Assert.ThrowsException(() => + { + new Line + { + Command = "USER", + Params = new List { "user", "0 *", "real name" } + }.Format(); + }); + } + + [TestMethod] + public void TestInvalidNonLastColon() + { + Assert.ThrowsException(() => + { + new Line + { + Command = "PRIVMSG", + Params = new List { ":#channel", "hello" } + }.Format(); + }); + } + } +} diff --git a/IrcTokens/Tests/FormatTests.cs b/IrcTokens/Tests/FormatTests.cs deleted file mode 100644 index a804c1d..0000000 --- a/IrcTokens/Tests/FormatTests.cs +++ /dev/null @@ -1,150 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; - -namespace IrcTokens.Tests -{ - [TestClass] - public class FormatTests - { - [TestMethod] - public void TestTags() - { - var line = new Line - { - Command = "PRIVMSG", - Params = new List {"#channel", "hello"}, - Tags = new Dictionary {{"id", "\\" + " " + ";" + "\r\n"}} - }.Format(); - - Assert.AreEqual("@id=\\\\\\s\\:\\r\\n PRIVMSG #channel hello", line); - } - - [TestMethod] - public void TestMissingTag() - { - var line = new Line - { - Command = "PRIVMSG", - Params = new List {"#channel", "hello"} - }.Format(); - - Assert.AreEqual("PRIVMSG #channel hello", line); - } - - [TestMethod] - public void TestNullTag() - { - var line = new Line - { - Command = "PRIVMSG", - Params = new List {"#channel", "hello"}, - Tags = new Dictionary {{"a", null}} - }.Format(); - - Assert.AreEqual("@a PRIVMSG #channel hello", line); - } - - [TestMethod] - public void TestEmptyTag() - { - var line = new Line - { - Command = "PRIVMSG", - Params = new List {"#channel", "hello"}, - Tags = new Dictionary {{"a", ""}} - }.Format(); - - Assert.AreEqual("@a PRIVMSG #channel hello", line); - } - - [TestMethod] - public void TestSource() - { - var line = new Line - { - Command = "PRIVMSG", - Params = new List {"#channel", "hello"}, - Source = "nick!user@host" - }.Format(); - - Assert.AreEqual(":nick!user@host PRIVMSG #channel hello", line); - } - - [TestMethod] - public void TestCommandLowercase() - { - var line = new Line {Command = "privmsg"}.Format(); - Assert.AreEqual("privmsg", line); - } - - [TestMethod] - public void TestCommandUppercase() - { - var line = new Line {Command = "PRIVMSG"}.Format(); - Assert.AreEqual("PRIVMSG", line); - } - - [TestMethod] - public void TestTrailingSpace() - { - var line = new Line - { - Command = "PRIVMSG", - Params = new List {"#channel", "hello world"} - }.Format(); - - Assert.AreEqual("PRIVMSG #channel :hello world", line); - } - - [TestMethod] - public void TestTrailingNoSpace() - { - var line = new Line - { - Command = "PRIVMSG", - Params = new List {"#channel", "helloworld"} - }.Format(); - - Assert.AreEqual("PRIVMSG #channel helloworld", line); - } - - [TestMethod] - public void TestTrailingDoubleColon() - { - var line = new Line - { - Command = "PRIVMSG", - Params = new List {"#channel", ":helloworld"} - }.Format(); - - Assert.AreEqual("PRIVMSG #channel ::helloworld", line); - } - - [TestMethod] - public void TestInvalidNonLastSpace() - { - Assert.ThrowsException(() => - { - new Line - { - Command = "USER", - Params = new List {"user", "0 *", "real name"} - }.Format(); - }); - } - - [TestMethod] - public void TestInvalidNonLastColon() - { - Assert.ThrowsException(() => - { - new Line - { - Command = "PRIVMSG", - Params = new List {":#channel", "hello"} - }.Format(); - }); - } - } -} diff --git a/IrcTokens/Tests/Hostmask.cs b/IrcTokens/Tests/Hostmask.cs new file mode 100644 index 0000000..51bc182 --- /dev/null +++ b/IrcTokens/Tests/Hostmask.cs @@ -0,0 +1,64 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IrcTokens.Tests +{ + [TestClass] + public class Hostmask + { + [TestMethod] + public void TestHostmask() + { + var hostmask = new IrcTokens.Hostmask("nick!user@host"); + Assert.AreEqual("nick", hostmask.NickName); + Assert.AreEqual("user", hostmask.UserName); + Assert.AreEqual("host", hostmask.HostName); + } + + [TestMethod] + public void TestNoHostName() + { + var hostmask = new IrcTokens.Hostmask("nick!user"); + Assert.AreEqual("nick", hostmask.NickName); + Assert.AreEqual("user", hostmask.UserName); + Assert.IsNull(hostmask.HostName); + } + + [TestMethod] + public void TestNoUserName() + { + var hostmask = new IrcTokens.Hostmask("nick@host"); + Assert.AreEqual("nick", hostmask.NickName); + Assert.IsNull(hostmask.UserName); + Assert.AreEqual("host", hostmask.HostName); + } + + [TestMethod] + public void TestOnlyNickName() + { + var hostmask = new IrcTokens.Hostmask("nick"); + Assert.AreEqual("nick", hostmask.NickName); + Assert.IsNull(hostmask.UserName); + Assert.IsNull(hostmask.HostName); + } + + [TestMethod] + public void TestHostmaskFromLine() + { + var line = new Line(":nick!user@host PRIVMSG #channel hello"); + var hostmask = new IrcTokens.Hostmask("nick!user@host"); + Assert.AreEqual(hostmask.ToString(), line.Hostmask.ToString()); + Assert.AreEqual("nick", line.Hostmask.NickName); + Assert.AreEqual("user", line.Hostmask.UserName); + Assert.AreEqual("host", line.Hostmask.HostName); + } + + [TestMethod] + public void TestEmptyHostmaskFromLine() + { + var line = new Line("PRIVMSG #channel hello"); + Assert.IsNull(line.Hostmask.HostName); + Assert.IsNull(line.Hostmask.UserName); + Assert.IsNull(line.Hostmask.NickName); + } + } +} diff --git a/IrcTokens/Tests/HostmaskTests.cs b/IrcTokens/Tests/HostmaskTests.cs deleted file mode 100644 index 78b8a54..0000000 --- a/IrcTokens/Tests/HostmaskTests.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace IrcTokens.Tests -{ - [TestClass] - public class HostmaskTests - { - [TestMethod] - public void TestHostmask() - { - var hostmask = new Hostmask("nick!user@host"); - Assert.AreEqual("nick", hostmask.NickName); - Assert.AreEqual("user", hostmask.UserName); - Assert.AreEqual("host", hostmask.HostName); - } - - [TestMethod] - public void TestNoHostName() - { - var hostmask = new Hostmask("nick!user"); - Assert.AreEqual("nick", hostmask.NickName); - Assert.AreEqual("user", hostmask.UserName); - Assert.IsNull(hostmask.HostName); - } - - [TestMethod] - public void TestNoUserName() - { - var hostmask = new Hostmask("nick@host"); - Assert.AreEqual("nick", hostmask.NickName); - Assert.IsNull(hostmask.UserName); - Assert.AreEqual("host", hostmask.HostName); - } - - [TestMethod] - public void TestOnlyNickName() - { - var hostmask = new Hostmask("nick"); - Assert.AreEqual("nick", hostmask.NickName); - Assert.IsNull(hostmask.UserName); - Assert.IsNull(hostmask.HostName); - } - - [TestMethod] - public void TestHostmaskFromLine() - { - var line = new Line(":nick!user@host PRIVMSG #channel hello"); - var hostmask = new Hostmask("nick!user@host"); - Assert.AreEqual(hostmask.ToString(), line.Hostmask.ToString()); - Assert.AreEqual("nick", line.Hostmask.NickName); - Assert.AreEqual("user", line.Hostmask.UserName); - Assert.AreEqual("host", line.Hostmask.HostName); - } - - [TestMethod] - public void TestEmptyHostmaskFromLine() - { - var line = new Line("PRIVMSG #channel hello"); - Assert.IsNull(line.Hostmask.HostName); - Assert.IsNull(line.Hostmask.UserName); - Assert.IsNull(line.Hostmask.NickName); - } - } -} diff --git a/IrcTokens/Tests/Parser.cs b/IrcTokens/Tests/Parser.cs new file mode 100644 index 0000000..df70b92 --- /dev/null +++ b/IrcTokens/Tests/Parser.cs @@ -0,0 +1,57 @@ +using IrcTokens.Tests.Data; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace IrcTokens.Tests +{ + [TestClass] + public class Parser + { + private static T LoadYaml(string path) + { + var deserializer = new DeserializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .Build(); + + return deserializer.Deserialize(File.ReadAllText(path)); + } + + [TestMethod] + public void TestSplit() + { + foreach (var test in LoadYaml("Tests/Data/msg-split.yaml").Tests) + { + var tokens = new Line(test.Input); + var atoms = test.Atoms; + + Assert.AreEqual(atoms.Verb.ToUpper(CultureInfo.InvariantCulture), tokens.Command, + $"command failed on: '{test.Input}'"); + Assert.AreEqual(atoms.Source, tokens.Source, $"source failed on: '{test.Input}'"); + CollectionAssert.AreEqual(atoms.Tags, tokens.Tags, $"tags failed on: '{test.Input}'"); + CollectionAssert.AreEqual(atoms.Params ?? new List(), tokens.Params, $"params failed on: '{test.Input}'"); + } + } + + [TestMethod] + public void TestJoin() + { + foreach (var test in LoadYaml("Tests/Data/msg-join.yaml").Tests) + { + var atoms = test.Atoms; + var line = new Line + { + Command = atoms.Verb, + Params = atoms.Params, + Source = atoms.Source, + Tags = atoms.Tags + }.Format(); + + Assert.IsTrue(test.Matches.Contains(line), test.Description); + } + } + } +} diff --git a/IrcTokens/Tests/ParserTests.cs b/IrcTokens/Tests/ParserTests.cs deleted file mode 100644 index ad734cf..0000000 --- a/IrcTokens/Tests/ParserTests.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using IrcTokens.Tests.Data; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; - -namespace IrcTokens.Tests -{ - [TestClass] - public class ParserTests - { - private static T LoadYaml(string path) - { - var deserializer = new DeserializerBuilder() - .WithNamingConvention(CamelCaseNamingConvention.Instance) - .Build(); - - return deserializer.Deserialize(File.ReadAllText(path)); - } - - [TestMethod] - public void TestSplit() - { - foreach (var test in LoadYaml("Tests/Data/msg-split.yaml").Tests) - { - var tokens = new Line(test.Input); - var atoms = test.Atoms; - - Assert.AreEqual(atoms.Verb.ToUpper(CultureInfo.InvariantCulture), tokens.Command, - $"command failed on: '{test.Input}'"); - Assert.AreEqual(atoms.Source, tokens.Source, $"source failed on: '{test.Input}'"); - CollectionAssert.AreEqual(atoms.Tags, tokens.Tags, $"tags failed on: '{test.Input}'"); - CollectionAssert.AreEqual(atoms.Params ?? new List(), tokens.Params, $"params failed on: '{test.Input}'"); - } - } - - [TestMethod] - public void TestJoin() - { - foreach (var test in LoadYaml("Tests/Data/msg-join.yaml").Tests) - { - var atoms = test.Atoms; - var line = new Line - { - Command = atoms.Verb, - Params = atoms.Params, - Source = atoms.Source ?? null, - Tags = atoms.Tags - }.Format(); - - Assert.IsTrue(test.Matches.Contains(line), test.Description); - } - } - } -} diff --git a/IrcTokens/Tests/StatefulDecoder.cs b/IrcTokens/Tests/StatefulDecoder.cs new file mode 100644 index 0000000..209a6cf --- /dev/null +++ b/IrcTokens/Tests/StatefulDecoder.cs @@ -0,0 +1,86 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Generic; +using System.Text; + +namespace IrcTokens.Tests +{ + [TestClass] + public class StatefulDecoder + { + private IrcTokens.StatefulDecoder _decoder; + + [TestInitialize] + public void TestInitialize() + { + _decoder = new IrcTokens.StatefulDecoder(); + } + + [TestMethod] + public void TestPartial() + { + var lines = _decoder.Push("PRIVMSG "); + Assert.AreEqual(0, lines.Count); + + lines = _decoder.Push("#channel hello\r\n"); + Assert.AreEqual(1, lines.Count); + + var line = new Line("PRIVMSG #channel hello"); + CollectionAssert.AreEqual(new List { line }, lines); + } + + [TestMethod] + public void TestMultiple() + { + var lines = _decoder.Push("PRIVMSG #channel1 hello\r\nPRIVMSG #channel2 hello\r\n"); + Assert.AreEqual(2, lines.Count); + + var line1 = new Line("PRIVMSG #channel1 hello"); + var line2 = new Line("PRIVMSG #channel2 hello"); + Assert.AreEqual(line1, lines[0]); + Assert.AreEqual(line2, lines[1]); + } + + [TestMethod] + public void TestEncoding() + { + var iso8859 = Encoding.GetEncoding("iso-8859-1"); + _decoder = new IrcTokens.StatefulDecoder { Encoding = iso8859 }; + var lines = _decoder.Push(iso8859.GetBytes("PRIVMSG #channel :hello Ç\r\n")); + var line = new Line("PRIVMSG #channel :hello Ç"); + Assert.IsTrue(line.Equals(lines[0])); + } + + [TestMethod] + public void TestEncodingFallback() + { + var latin1 = Encoding.GetEncoding("iso-8859-1"); + _decoder = new IrcTokens.StatefulDecoder { Encoding = null, Fallback = latin1 }; + var lines = _decoder.Push(latin1.GetBytes("PRIVMSG #channel hélló\r\n")); + Assert.AreEqual(1, lines.Count); + Assert.IsTrue(new Line("PRIVMSG #channel hélló").Equals(lines[0])); + } + + [TestMethod] + public void TestEmpty() + { + var lines = _decoder.Push(string.Empty); + Assert.IsNull(lines); + } + + [TestMethod] + public void TestBufferUnfinished() + { + _decoder.Push("PRIVMSG #channel hello"); + var lines = _decoder.Push(string.Empty); + Assert.IsNull(lines); + } + + [TestMethod] + public void TestClear() + { + _decoder.Push("PRIVMSG "); + _decoder.Clear(); + Assert.AreEqual(string.Empty, _decoder.Pending); + } + } +} diff --git a/IrcTokens/Tests/StatefulDecoderTests.cs b/IrcTokens/Tests/StatefulDecoderTests.cs deleted file mode 100644 index 3e6a078..0000000 --- a/IrcTokens/Tests/StatefulDecoderTests.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace IrcTokens.Tests -{ - [TestClass] - public class StatefulDecoderTests - { - private StatefulDecoder _decoder; - - [TestInitialize] - public void TestInitialize() - { - _decoder = new StatefulDecoder(); - } - - [TestMethod] - public void TestPartial() - { - var lines = _decoder.Push("PRIVMSG "); - Assert.AreEqual(0, lines.Count); - - lines = _decoder.Push("#channel hello\r\n"); - Assert.AreEqual(1, lines.Count); - - var line = new Line("PRIVMSG #channel hello"); - CollectionAssert.AreEqual(new List {line}, lines); - } - - [TestMethod] - public void TestMultiple() - { - var lines = _decoder.Push("PRIVMSG #channel1 hello\r\nPRIVMSG #channel2 hello\r\n"); - Assert.AreEqual(2, lines.Count); - - var line1 = new Line("PRIVMSG #channel1 hello"); - var line2 = new Line("PRIVMSG #channel2 hello"); - Assert.AreEqual(line1, lines[0]); - Assert.AreEqual(line2, lines[1]); - } - - [TestMethod] - public void TestEncoding() - { - var iso8859 = Encoding.GetEncoding("iso-8859-1"); - _decoder = new StatefulDecoder {Encoding = iso8859}; - var lines = _decoder.Push(iso8859.GetBytes("PRIVMSG #channel :hello Ç\r\n")); - var line = new Line("PRIVMSG #channel :hello Ç"); - Assert.IsTrue(line.Equals(lines[0])); - } - - [TestMethod] - public void TestEncodingFallback() - { - var latin1 = Encoding.GetEncoding("iso-8859-1"); - _decoder = new StatefulDecoder {Encoding = null, Fallback = latin1}; - var lines = _decoder.Push(latin1.GetBytes("PRIVMSG #channel hélló\r\n")); - Assert.AreEqual(1, lines.Count); - Assert.IsTrue(new Line("PRIVMSG #channel hélló").Equals(lines[0])); - } - - [TestMethod] - public void TestEmpty() - { - var lines = _decoder.Push(string.Empty); - Assert.IsNull(lines); - } - - [TestMethod] - public void TestBufferUnfinished() - { - _decoder.Push("PRIVMSG #channel hello"); - var lines = _decoder.Push(string.Empty); - Assert.IsNull(lines); - } - - [TestMethod] - public void TestClear() - { - _decoder.Push("PRIVMSG "); - _decoder.Clear(); - Assert.AreEqual(string.Empty, _decoder.Pending); - } - } -} diff --git a/IrcTokens/Tests/StatefulEncoder.cs b/IrcTokens/Tests/StatefulEncoder.cs new file mode 100644 index 0000000..e3ed70d --- /dev/null +++ b/IrcTokens/Tests/StatefulEncoder.cs @@ -0,0 +1,71 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text; + +namespace IrcTokens.Tests +{ + [TestClass] + public class StatefulEncoder + { + private IrcTokens.StatefulEncoder _encoder; + + [TestInitialize] + public void TestInitialize() + { + _encoder = new IrcTokens.StatefulEncoder(); + } + + [TestMethod] + public void TestPush() + { + var line = new Line("PRIVMSG #channel hello"); + _encoder.Push(line); + Assert.AreEqual("PRIVMSG #channel hello\r\n", _encoder.Pending()); + } + + [TestMethod] + public void TestPopPartial() + { + var line = new Line("PRIVMSG #channel hello"); + _encoder.Push(line); + _encoder.Pop("PRIVMSG #channel hello".Length); + Assert.AreEqual("\r\n", _encoder.Pending()); + } + + [TestMethod] + public void TestPopReturned() + { + var line = new Line("PRIVMSG #channel hello"); + _encoder.Push(line); + _encoder.Push(line); + var lines = _encoder.Pop("PRIVMSG #channel hello\r\n".Length); + Assert.AreEqual(1, lines.Count); + Assert.AreEqual(line, lines[0]); + } + + [TestMethod] + public void TestPopNoneReturned() + { + var line = new Line("PRIVMSG #channel hello"); + _encoder.Push(line); + var lines = _encoder.Pop(1); + Assert.AreEqual(0, lines.Count); + } + + [TestMethod] + public void TestClear() + { + _encoder.Push(new Line("PRIVMSG #channel hello")); + _encoder.Clear(); + Assert.AreEqual(string.Empty, _encoder.Pending()); + } + + [TestMethod] + public void TestEncoding() + { + var iso8859 = Encoding.GetEncoding("iso-8859-1"); + _encoder = new IrcTokens.StatefulEncoder { Encoding = iso8859 }; + _encoder.Push(new Line("PRIVMSG #channel :hello Ç")); + CollectionAssert.AreEqual(iso8859.GetBytes("PRIVMSG #channel :hello Ç\r\n"), _encoder.PendingBytes); + } + } +} diff --git a/IrcTokens/Tests/StatefulEncoderTests.cs b/IrcTokens/Tests/StatefulEncoderTests.cs deleted file mode 100644 index 477b38d..0000000 --- a/IrcTokens/Tests/StatefulEncoderTests.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Linq; -using System.Text; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace IrcTokens.Tests -{ - [TestClass] - public class StatefulEncoderTests - { - private StatefulEncoder _encoder; - - [TestInitialize] - public void TestInitialize() - { - _encoder = new StatefulEncoder(); - } - - [TestMethod] - public void TestPush() - { - var line = new Line("PRIVMSG #channel hello"); - _encoder.Push(line); - Assert.AreEqual("PRIVMSG #channel hello\r\n", _encoder.Pending()); - } - - [TestMethod] - public void TestPopPartial() - { - var line = new Line("PRIVMSG #channel hello"); - _encoder.Push(line); - _encoder.Pop("PRIVMSG #channel hello".Length); - Assert.AreEqual("\r\n", _encoder.Pending()); - } - - [TestMethod] - public void TestPopReturned() - { - var line = new Line("PRIVMSG #channel hello"); - _encoder.Push(line); - _encoder.Push(line); - var lines = _encoder.Pop("PRIVMSG #channel hello\r\n".Length); - Assert.AreEqual(1, lines.Count); - Assert.AreEqual(line, lines[0]); - } - - [TestMethod] - public void TestPopNoneReturned() - { - var line = new Line("PRIVMSG #channel hello"); - _encoder.Push(line); - var lines = _encoder.Pop(1); - Assert.AreEqual(0, lines.Count); - } - - [TestMethod] - public void TestClear() - { - _encoder.Push(new Line("PRIVMSG #channel hello")); - _encoder.Clear(); - Assert.AreEqual(string.Empty, _encoder.Pending()); - } - - [TestMethod] - public void TestEncoding() - { - var iso8859 = Encoding.GetEncoding("iso-8859-1"); - _encoder = new StatefulEncoder {Encoding = iso8859}; - _encoder.Push(new Line("PRIVMSG #channel :hello Ç")); - CollectionAssert.AreEqual(iso8859.GetBytes("PRIVMSG #channel :hello Ç\r\n"), _encoder.PendingBytes); - } - } -} diff --git a/IrcTokens/Tests/Tokenization.cs b/IrcTokens/Tests/Tokenization.cs new file mode 100644 index 0000000..c4970ed --- /dev/null +++ b/IrcTokens/Tests/Tokenization.cs @@ -0,0 +1,118 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Generic; + +namespace IrcTokens.Tests +{ + [TestClass] + public class Tokenization + { + [TestMethod] + public void TestTagsMissing() + { + var line = new Line("PRIVMSG #channel"); + Assert.IsNull(line.Tags); + } + + [TestMethod] + public void TestTagsMissingValue() + { + var line = new Line("@id= PRIVMSG #channel"); + Assert.AreEqual(string.Empty, line.Tags["id"]); + } + + [TestMethod] + public void TestTagsMissingEqual() + { + var line = new Line("@id PRIVMSG #channel"); + Assert.AreEqual(string.Empty, line.Tags["id"]); + } + + [TestMethod] + public void TestTagsUnescape() + { + var line = new Line(@"@id=1\\\:\r\n\s2 PRIVMSG #channel"); + Assert.AreEqual("1\\;\r\n 2", line.Tags["id"]); + } + + [TestMethod] + public void TestTagsOverlap() + { + var line = new Line(@"@id=1\\\s\\s PRIVMSG #channel"); + Assert.AreEqual("1\\ \\s", line.Tags["id"]); + } + + [TestMethod] + public void TestTagsLoneEndSlash() + { + var line = new Line("@id=1\\ PRIVMSG #channel"); + Assert.AreEqual("1", line.Tags["id"]); + } + + [TestMethod] + public void TestSourceWithoutTags() + { + var line = new Line(":nick!user@host PRIVMSG #channel"); + Assert.AreEqual("nick!user@host", line.Source); + } + + [TestMethod] + public void TestSourceWithTags() + { + var line = new Line("@id=123 :nick!user@host PRIVMSG #channel"); + Assert.AreEqual("nick!user@host", line.Source); + } + + [TestMethod] + public void TestSourceMissingWithoutTags() + { + var line = new Line("PRIVMSG #channel"); + Assert.IsNull(line.Source); + } + + [TestMethod] + public void TestSourceMissingWithTags() + { + var line = new Line("@id=123 PRIVMSG #channel"); + Assert.IsNull(line.Source); + } + + [TestMethod] + public void TestCommand() + { + var line = new Line("privmsg #channel"); + Assert.AreEqual("PRIVMSG", line.Command); + } + + [TestMethod] + public void TestParamsTrailing() + { + var line = new Line("PRIVMSG #channel :hello world"); + CollectionAssert.AreEqual(new List { "#channel", "hello world" }, line.Params); + } + + [TestMethod] + public void TestParamsOnlyTrailing() + { + var line = new Line("PRIVMSG :hello world"); + CollectionAssert.AreEqual(new List { "hello world" }, line.Params); + } + + [TestMethod] + public void TestParamsMissing() + { + var line = new Line("PRIVMSG"); + Assert.AreEqual("PRIVMSG", line.Command); + CollectionAssert.AreEqual(new List(), line.Params); + } + + [TestMethod] + public void TestAllTokens() + { + var line = new Line("@id=123 :nick!user@host PRIVMSG #channel :hello world"); + CollectionAssert.AreEqual(new Dictionary { { "id", "123" } }, line.Tags); + Assert.AreEqual("nick!user@host", line.Source); + Assert.AreEqual("PRIVMSG", line.Command); + CollectionAssert.AreEqual(new List { "#channel", "hello world" }, line.Params); + } + } +} diff --git a/IrcTokens/Tests/TokenizationTests.cs b/IrcTokens/Tests/TokenizationTests.cs deleted file mode 100644 index 6d8a69d..0000000 --- a/IrcTokens/Tests/TokenizationTests.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System.Collections.Generic; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace IrcTokens.Tests -{ - [TestClass] - public class TokenizationTests - { - [TestMethod] - public void TestTagsMissing() - { - var line = new Line("PRIVMSG #channel"); - Assert.IsNull(line.Tags); - } - - [TestMethod] - public void TestTagsMissingValue() - { - var line = new Line("@id= PRIVMSG #channel"); - Assert.AreEqual(string.Empty, line.Tags["id"]); - } - - [TestMethod] - public void TestTagsMissingEqual() - { - var line = new Line("@id PRIVMSG #channel"); - Assert.AreEqual(string.Empty, line.Tags["id"]); - } - - [TestMethod] - public void TestTagsUnescape() - { - var line = new Line(@"@id=1\\\:\r\n\s2 PRIVMSG #channel"); - Assert.AreEqual("1\\;\r\n 2", line.Tags["id"]); - } - - [TestMethod] - public void TestTagsOverlap() - { - var line = new Line(@"@id=1\\\s\\s PRIVMSG #channel"); - Assert.AreEqual("1\\ \\s", line.Tags["id"]); - } - - [TestMethod] - public void TestTagsLoneEndSlash() - { - var line = new Line("@id=1\\ PRIVMSG #channel"); - Assert.AreEqual("1", line.Tags["id"]); - } - - [TestMethod] - public void TestSourceWithoutTags() - { - var line = new Line(":nick!user@host PRIVMSG #channel"); - Assert.AreEqual("nick!user@host", line.Source); - } - - [TestMethod] - public void TestSourceWithTags() - { - var line = new Line("@id=123 :nick!user@host PRIVMSG #channel"); - Assert.AreEqual("nick!user@host", line.Source); - } - - [TestMethod] - public void TestSourceMissingWithoutTags() - { - var line = new Line("PRIVMSG #channel"); - Assert.IsNull(line.Source); - } - - [TestMethod] - public void TestSourceMissingWithTags() - { - var line = new Line("@id=123 PRIVMSG #channel"); - Assert.IsNull(line.Source); - } - - [TestMethod] - public void TestCommand() - { - var line = new Line("privmsg #channel"); - Assert.AreEqual("PRIVMSG", line.Command); - } - - [TestMethod] - public void TestParamsTrailing() - { - var line = new Line("PRIVMSG #channel :hello world"); - CollectionAssert.AreEqual(new List {"#channel", "hello world"}, line.Params); - } - - [TestMethod] - public void TestParamsOnlyTrailing() - { - var line = new Line("PRIVMSG :hello world"); - CollectionAssert.AreEqual(new List {"hello world"}, line.Params); - } - - [TestMethod] - public void TestParamsMissing() - { - var line = new Line("PRIVMSG"); - Assert.AreEqual("PRIVMSG", line.Command); - CollectionAssert.AreEqual(new List(), line.Params); - } - - [TestMethod] - public void TestAllTokens() - { - var line = new Line("@id=123 :nick!user@host PRIVMSG #channel :hello world"); - CollectionAssert.AreEqual(new Dictionary {{"id", "123"}}, line.Tags); - Assert.AreEqual("nick!user@host", line.Source); - Assert.AreEqual("PRIVMSG", line.Command); - CollectionAssert.AreEqual(new List {"#channel", "hello world"}, line.Params); - } - } -} diff --git a/Sample/Client.cs b/Sample/Client.cs index e9e286f..b756adf 100644 --- a/Sample/Client.cs +++ b/Sample/Client.cs @@ -1,10 +1,9 @@ -using System; +using IrcTokens; +using System; using System.Collections.Generic; using System.Net.Sockets; -using System.Text; -using IrcTokens; -namespace Sample +namespace TokensSample { public class Client { @@ -25,15 +24,15 @@ namespace Sample { _socket.Connect("127.0.0.1", 6667); - Send(new Line {Command = "USER", Params = new List {"username", "0", "*", "real name"}}); - Send(new Line {Command = "NICK", Params = new List {"statefulbot"}}); + Send(new Line { Command = "USER", Params = new List { "username", "0", "*", "real name" } }); + Send(new Line { Command = "NICK", Params = new List { "tokensbot" } }); while (true) { var bytesReceived = _socket.Receive(_bytes); var lines = _decoder.Push(_bytes); - if (lines.Count == 0) + if (bytesReceived == 0) { Console.WriteLine("! disconnected"); _socket.Shutdown(SocketShutdown.Both); @@ -47,10 +46,10 @@ namespace Sample switch (line.Command) { case "PING": - Send(new Line {Command = "PONG", Params = line.Params}); + Send(new Line { Command = "PONG", Params = line.Params }); break; case "001": - Send(new Line {Command = "JOIN", Params = new List {"#channel"}}); + Send(new Line { Command = "JOIN", Params = new List { "#channel" } }); break; } } @@ -62,7 +61,9 @@ namespace Sample Console.WriteLine($"> {line.Format()}"); _encoder.Push(line); while (_encoder.PendingBytes.Length > 0) + { _encoder.Pop(_socket.Send(_encoder.PendingBytes)); + } } } } diff --git a/Sample/Program.cs b/Sample/Program.cs index 6800179..eda312f 100644 --- a/Sample/Program.cs +++ b/Sample/Program.cs @@ -1,8 +1,8 @@ -using System; +using IrcTokens; +using System; using System.Collections.Generic; -using IrcTokens; -namespace Sample +namespace TokensSample { public class Program { @@ -17,7 +17,7 @@ namespace Sample var line2 = new Line { Command = "USER", - Params = new List {"user", "0", "*", "real name"} + Params = new List { "user", "0", "*", "real name" } }; Console.WriteLine(line2); Console.WriteLine(line2.Format()); diff --git a/Sample/Sample.csproj b/Sample/Sample.csproj deleted file mode 100644 index 7c66734..0000000 --- a/Sample/Sample.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - Exe - netcoreapp3.1 - - - - - - - diff --git a/Sample/TokensSample.csproj b/Sample/TokensSample.csproj new file mode 100644 index 0000000..7c66734 --- /dev/null +++ b/Sample/TokensSample.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp3.1 + + + + + + + diff --git a/StatesSample/Program.cs b/StatesSample/Program.cs new file mode 100644 index 0000000..8fc42b8 --- /dev/null +++ b/StatesSample/Program.cs @@ -0,0 +1,12 @@ +using System; + +namespace StatesSample +{ + public static class Program + { + private static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + } + } +} diff --git a/StatesSample/StatesSample.csproj b/StatesSample/StatesSample.csproj new file mode 100644 index 0000000..3107344 --- /dev/null +++ b/StatesSample/StatesSample.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp3.1 + + + + + + + -- cgit 1.4.1