diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000000..b9a9d80675 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,17 @@ +codecov: + strict_yaml_branch: master +coverage: + status: + project: + default: + threshold: 0 + if_no_uploads: failure + if_ci_failed: failure + patch: off + changes: + default: + if_no_uploads: failure + if_ci_failed: failure + only_pulls: yes +comment: + layout: "header, diff, changes" diff --git a/.gitignore b/.gitignore index 5dee86e7ae..90b3b65a9c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ packages/* *.user *.dmb *.int +/TestResults diff --git a/TGServiceInterface/ChatSetupInfo.cs b/TGServiceInterface/ChatSetupInfo.cs index c34d6aea8e..20e522cba4 100644 --- a/TGServiceInterface/ChatSetupInfo.cs +++ b/TGServiceInterface/ChatSetupInfo.cs @@ -1,235 +1,235 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using System.Web.Script.Serialization; - -namespace TGServiceInterface -{ - /// - /// For setting up authentication no matter the chat provider - /// - [DataContract] - [KnownType(typeof(IRCSetupInfo))] - [KnownType(typeof(DiscordSetupInfo))] - public class ChatSetupInfo - { - const int AdminListIndex = 0; - const int AdminModeIndex = 1; - const int AdminChannelIndex = 2; - const int DevChannelIndex = 3; - const int WDChannelIndex = 4; - const int GameChannelIndex = 5; - const int ProviderIndex = 6; - const int EnabledIndex = 7; - /// - /// Starting index of which child classes should use to write their custom data to - /// - protected const int BaseIndex = 8; - /// - /// Set to if a child constructor should use the baseInfo parameter of to initialize it's property fields, otherwise - /// - protected readonly bool InitializeFields; - - /// - /// Raw access to the underlying data - /// - [DataMember] +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Web.Script.Serialization; + +namespace TGServiceInterface +{ + /// + /// For setting up authentication no matter the chat provider + /// + [DataContract] + [KnownType(typeof(IRCSetupInfo))] + [KnownType(typeof(DiscordSetupInfo))] + public class ChatSetupInfo + { + const int AdminListIndex = 0; + const int AdminModeIndex = 1; + const int AdminChannelIndex = 2; + const int DevChannelIndex = 3; + const int WDChannelIndex = 4; + const int GameChannelIndex = 5; + const int ProviderIndex = 6; + const int EnabledIndex = 7; + /// + /// Starting index of which child classes should use to write their custom data to + /// + protected const int BaseIndex = 8; + /// + /// Set to if a child constructor should use the baseInfo parameter of to initialize it's property fields, otherwise + /// + protected readonly bool InitializeFields; + + /// + /// Raw access to the underlying data + /// + [DataMember] public IList DataFields { get; protected set; } - /// - /// Constructs a from optional - /// - /// The that this is for - /// Optional past data - /// The number of fields in this chat provider - protected internal ChatSetupInfo(ChatProvider provider, ChatSetupInfo baseInfo, int numFields) - { - numFields += BaseIndex; - InitializeFields = baseInfo == null || baseInfo.DataFields.Count != numFields; - - if (InitializeFields) - { - DataFields = new List(numFields); - for (var I = 0; I < numFields; ++I) - DataFields.Add(null); - - AdminList = new List(); - AdminChannels = new List(); - DevChannels = new List(); - GameChannels = new List(); - WatchdogChannels = new List(); - AdminsAreSpecial = false; - Enabled = false; - } - else - DataFields = baseInfo.DataFields; - Provider = provider; + /// + /// Constructs a from optional + /// + /// The that this is for + /// Optional past data + /// The number of fields in this chat provider + protected internal ChatSetupInfo(ChatProvider provider, ChatSetupInfo baseInfo, int numFields) + { + numFields += BaseIndex; + InitializeFields = baseInfo == null || baseInfo.DataFields.Count != numFields; + + if (InitializeFields) + { + DataFields = new List(numFields); + for (var I = 0; I < numFields; ++I) + DataFields.Add(null); + + AdminList = new List(); + AdminChannels = new List(); + DevChannels = new List(); + GameChannels = new List(); + WatchdogChannels = new List(); + AdminsAreSpecial = false; + Enabled = false; + } + else + DataFields = baseInfo.DataFields; + Provider = provider; Specialize(true); //to check we have a valid provider - } - - /// - /// Recreates as the correct child - /// - /// If , is returned provided is a valid - /// A new based on the type - ChatSetupInfo Specialize(bool checkOnly) - { - switch (Provider) - { - case ChatProvider.IRC: - if (!checkOnly) - return new IRCSetupInfo(this); - break; - case ChatProvider.Discord: + } + + /// + /// Recreates as the correct child + /// + /// If , is returned provided is a valid + /// A new based on the type + ChatSetupInfo Specialize(bool checkOnly) + { + switch (Provider) + { + case ChatProvider.IRC: if (!checkOnly) - return new DiscordSetupInfo(this); - break; - default: - throw new Exception("Invalid provider!"); - } - return null; - } - - /// - /// Properly formats a name for the - /// - /// The to format - /// The formatted - protected virtual string SanitizeChannelName(string channel) - { - return Specialize(false).SanitizeChannelName(channel); - } - - /// - /// Sanitizes a list of - /// - /// An of strings - void SanitizeChannelNames(IList channelnames) - { - for (var I = 0; I < channelnames.Count; ++I) - - if (String.IsNullOrWhiteSpace(channelnames[I])) - { - channelnames.RemoveAt(I); - --I; - } - else - channelnames[I] = SanitizeChannelName(channelnames[I].Trim()); - } - - /// - /// Constructs a from a data list - /// - /// The data - public ChatSetupInfo(IList DeserializedData) - { - DataFields = DeserializedData; - Specialize(false); //ensure provider type is valid - } - /// - /// The list of admin entries - /// - public IList AdminList - { - get { return new JavaScriptSerializer().Deserialize>(DataFields[AdminListIndex]); } - set { DataFields[AdminListIndex] = new JavaScriptSerializer().Serialize(value); } - } - /// - /// If AdminList corresponds to a Provider specific recognization method - /// - public bool AdminsAreSpecial - { - get { return Convert.ToBoolean(DataFields[AdminModeIndex]); } - set { DataFields[AdminModeIndex] = Convert.ToString(value); } - } - /// - /// The channels from which admin commands/messages can be sent/received - /// - public IList AdminChannels - { - get { return new JavaScriptSerializer().Deserialize>(DataFields[AdminChannelIndex]); } - set - { - SanitizeChannelNames(value); - DataFields[AdminChannelIndex] = new JavaScriptSerializer().Serialize(value); - } - } - /// - /// The channels to which repo and compile messages are sent - /// - public IList DevChannels - { - get { return new JavaScriptSerializer().Deserialize>(DataFields[DevChannelIndex]); } - set - { - SanitizeChannelNames(value); - DataFields[DevChannelIndex] = new JavaScriptSerializer().Serialize(value); - } - } - /// - /// The channels to which watchdog messages are sent - /// - public IList WatchdogChannels - { - get { return new JavaScriptSerializer().Deserialize>(DataFields[WDChannelIndex]); } - set - { - SanitizeChannelNames(value); - DataFields[WDChannelIndex] = new JavaScriptSerializer().Serialize(value); - } - } - /// - /// The channels to which game messages are sent - /// - public IList GameChannels - { - get { return new JavaScriptSerializer().Deserialize>(DataFields[GameChannelIndex]); } - set - { - SanitizeChannelNames(value); - DataFields[GameChannelIndex] = new JavaScriptSerializer().Serialize(value); - } - } - /// - /// If this chat provider is enabled - /// - public bool Enabled - { - get { return Convert.ToBoolean(DataFields[EnabledIndex]); } - set { DataFields[EnabledIndex] = Convert.ToString(value); } - } - - /// - /// The type of provider - /// - public ChatProvider Provider - { - get { return (ChatProvider)Convert.ToInt32(DataFields[ProviderIndex]); } - set { DataFields[ProviderIndex] = Convert.ToString((int)value); } - } - } - - /// - /// Chat provider for IRC. Admin entries should be user nicknames in normal mode or required channel flags in special mode - /// - [DataContract] - public sealed class IRCSetupInfo : ChatSetupInfo - { - const int URLIndex = 0; - const int PortIndex = 1; - const int NickIndex = 2; - const int AuthTargetIndex = 3; - const int AuthMessageIndex = 4; - const int AuthLevelIndex = 5; + return new IRCSetupInfo(this); + break; + case ChatProvider.Discord: + if (!checkOnly) + return new DiscordSetupInfo(this); + break; + default: + throw new Exception("Invalid provider!"); + } + return null; + } + + /// + /// Properly formats a name for the + /// + /// The to format + /// The formatted + protected virtual string SanitizeChannelName(string channel) + { + return Specialize(false).SanitizeChannelName(channel); + } + + /// + /// Sanitizes a list of + /// + /// An of strings + void SanitizeChannelNames(IList channelnames) + { + for (var I = 0; I < channelnames.Count; ++I) + + if (String.IsNullOrWhiteSpace(channelnames[I])) + { + channelnames.RemoveAt(I); + --I; + } + else + channelnames[I] = SanitizeChannelName(channelnames[I].Trim()); + } + + /// + /// Constructs a from a data list + /// + /// The data + public ChatSetupInfo(IList DeserializedData) + { + DataFields = DeserializedData; + Specialize(false); //ensure provider type is valid + } + /// + /// The list of admin entries + /// + public IList AdminList + { + get { return new JavaScriptSerializer().Deserialize>(DataFields[AdminListIndex]); } + set { DataFields[AdminListIndex] = new JavaScriptSerializer().Serialize(value); } + } + /// + /// If AdminList corresponds to a Provider specific recognization method + /// + public bool AdminsAreSpecial + { + get { return Convert.ToBoolean(DataFields[AdminModeIndex]); } + set { DataFields[AdminModeIndex] = Convert.ToString(value); } + } + /// + /// The channels from which admin commands/messages can be sent/received + /// + public IList AdminChannels + { + get { return new JavaScriptSerializer().Deserialize>(DataFields[AdminChannelIndex]); } + set + { + SanitizeChannelNames(value); + DataFields[AdminChannelIndex] = new JavaScriptSerializer().Serialize(value); + } + } + /// + /// The channels to which repo and compile messages are sent + /// + public IList DevChannels + { + get { return new JavaScriptSerializer().Deserialize>(DataFields[DevChannelIndex]); } + set + { + SanitizeChannelNames(value); + DataFields[DevChannelIndex] = new JavaScriptSerializer().Serialize(value); + } + } + /// + /// The channels to which watchdog messages are sent + /// + public IList WatchdogChannels + { + get { return new JavaScriptSerializer().Deserialize>(DataFields[WDChannelIndex]); } + set + { + SanitizeChannelNames(value); + DataFields[WDChannelIndex] = new JavaScriptSerializer().Serialize(value); + } + } + /// + /// The channels to which game messages are sent + /// + public IList GameChannels + { + get { return new JavaScriptSerializer().Deserialize>(DataFields[GameChannelIndex]); } + set + { + SanitizeChannelNames(value); + DataFields[GameChannelIndex] = new JavaScriptSerializer().Serialize(value); + } + } + /// + /// If this chat provider is enabled + /// + public bool Enabled + { + get { return Convert.ToBoolean(DataFields[EnabledIndex]); } + set { DataFields[EnabledIndex] = Convert.ToString(value); } + } + + /// + /// The type of provider + /// + public ChatProvider Provider + { + get { return (ChatProvider)Convert.ToInt32(DataFields[ProviderIndex]); } + set { DataFields[ProviderIndex] = Convert.ToString((int)value); } + } + } + + /// + /// Chat provider for IRC. Admin entries should be user nicknames in normal mode or required channel flags in special mode + /// + [DataContract] + public sealed class IRCSetupInfo : ChatSetupInfo + { + const int URLIndex = 0; + const int PortIndex = 1; + const int NickIndex = 2; + const int AuthTargetIndex = 3; + const int AuthMessageIndex = 4; + const int AuthLevelIndex = 5; const int FieldsLen = 6; - /// - /// Construct IRC setup info from optional generic info. Defaults to TGS3 on rizons IRC server - /// - /// Optional generic info - public IRCSetupInfo(ChatSetupInfo baseInfo = null) : base(ChatProvider.IRC, baseInfo, FieldsLen) - { - if (!InitializeFields) + /// + /// Construct IRC setup info from optional generic info. Defaults to TGS3 on rizons IRC server + /// + /// Optional generic info + public IRCSetupInfo(ChatSetupInfo baseInfo = null) : base(ChatProvider.IRC, baseInfo, FieldsLen) + { + if (!InitializeFields) return; Nickname = "TGS3"; @@ -239,106 +239,106 @@ namespace TGServiceInterface AuthMessage = ""; AdminsAreSpecial = true; AuthLevel = IRCMode.Op; - } - - /// - protected override string SanitizeChannelName(string working) - { - if (working[0] != '#') - return "#" + working; - return working; - } - - /// - /// The port of the IRC server - /// - public ushort Port - { - get { return Convert.ToUInt16(DataFields[BaseIndex + PortIndex]); } - set { DataFields[BaseIndex + PortIndex] = value.ToString(); } - } - /// - /// The URL of the IRC server - /// - public string URL - { - get { return DataFields[BaseIndex + URLIndex]; } - set { DataFields[BaseIndex + URLIndex] = value; } - } - /// - /// The nickname of the IRC bot - /// - public string Nickname - { - get { return DataFields[BaseIndex + NickIndex]; } - set { DataFields[BaseIndex + NickIndex] = value; } - } - /// - /// The target for sending authentication messages - /// - public string AuthTarget - { - get { return DataFields[BaseIndex + AuthTargetIndex]; } - set { DataFields[BaseIndex + AuthTargetIndex] = value; } - } - /// - /// The authentication message - /// - public string AuthMessage - { - get { return DataFields[BaseIndex + AuthMessageIndex]; } - set { DataFields[BaseIndex + AuthMessageIndex] = value; } - } - /// - /// The minimum mode required to use admin bot commands when in special auth mode - /// - public IRCMode AuthLevel - { - get { return (IRCMode)Convert.ToInt32(DataFields[BaseIndex + AuthLevelIndex]); } - set { DataFields[BaseIndex + AuthLevelIndex] = Convert.ToString((int)value); } - } - } - - /// - /// Chat provider for Discord. Admin entires should be user ids in normal mode or group ids in special mode - /// - [DataContract] - public sealed class DiscordSetupInfo : ChatSetupInfo - { - const int BotTokenIndex = 0; + } + + /// + protected override string SanitizeChannelName(string working) + { + if (working[0] != '#') + return "#" + working; + return working; + } + + /// + /// The port of the IRC server + /// + public ushort Port + { + get { return Convert.ToUInt16(DataFields[BaseIndex + PortIndex]); } + set { DataFields[BaseIndex + PortIndex] = value.ToString(); } + } + /// + /// The URL of the IRC server + /// + public string URL + { + get { return DataFields[BaseIndex + URLIndex]; } + set { DataFields[BaseIndex + URLIndex] = value; } + } + /// + /// The nickname of the IRC bot + /// + public string Nickname + { + get { return DataFields[BaseIndex + NickIndex]; } + set { DataFields[BaseIndex + NickIndex] = value; } + } + /// + /// The target for sending authentication messages + /// + public string AuthTarget + { + get { return DataFields[BaseIndex + AuthTargetIndex]; } + set { DataFields[BaseIndex + AuthTargetIndex] = value; } + } + /// + /// The authentication message + /// + public string AuthMessage + { + get { return DataFields[BaseIndex + AuthMessageIndex]; } + set { DataFields[BaseIndex + AuthMessageIndex] = value; } + } + /// + /// The minimum mode required to use admin bot commands when in special auth mode + /// + public IRCMode AuthLevel + { + get { return (IRCMode)Convert.ToInt32(DataFields[BaseIndex + AuthLevelIndex]); } + set { DataFields[BaseIndex + AuthLevelIndex] = Convert.ToString((int)value); } + } + } + + /// + /// Chat provider for Discord. Admin entires should be user ids in normal mode or group ids in special mode + /// + [DataContract] + public sealed class DiscordSetupInfo : ChatSetupInfo + { + const int BotTokenIndex = 0; const int FieldsLen = 1; - /// - /// Construct Discord setup info from optional generic info. Default is not a valid discord bot tokent - /// - /// Optional generic info - public DiscordSetupInfo(ChatSetupInfo baseInfo = null) : base(ChatProvider.Discord, baseInfo, FieldsLen) - { - if (!InitializeFields) - return; - BotToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; //needless to say, this is fake - } - /// - protected override string SanitizeChannelName(string working) - { - working = working.Replace("<", "").Replace(">", "").Replace("&", ""); //filter out some stuff that can come in the copypasta - try - { - Convert.ToUInt64(working); - } - catch - { - throw new Exception("Invalid Discord channel ID!"); - } - return working; - } - - /// - /// The Discord bot token to use. See https://discordapp.com/developers/applications/me for registering bot accounts - /// - public string BotToken - { - get { return DataFields[BaseIndex + BotTokenIndex]; } - set { DataFields[BaseIndex + BotTokenIndex] = value; } - } - } -} + /// + /// Construct Discord setup info from optional generic info. Default is not a valid discord bot tokent + /// + /// Optional generic info + public DiscordSetupInfo(ChatSetupInfo baseInfo = null) : base(ChatProvider.Discord, baseInfo, FieldsLen) + { + if (!InitializeFields) + return; + BotToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; //needless to say, this is fake + } + /// + protected override string SanitizeChannelName(string working) + { + working = working.Replace("<", "").Replace(">", "").Replace("&", ""); //filter out some stuff that can come in the copypasta + try + { + Convert.ToUInt64(working); + } + catch + { + throw new Exception("Invalid Discord channel ID!"); + } + return working; + } + + /// + /// The Discord bot token to use. See https://discordapp.com/developers/applications/me for registering bot accounts + /// + public string BotToken + { + get { return DataFields[BaseIndex + BotTokenIndex]; } + set { DataFields[BaseIndex + BotTokenIndex] = value; } + } + } +} diff --git a/TGServiceTests/TempDirectoryRequiredTest.cs b/TGServiceTests/TempDirectoryRequiredTest.cs index a4ef44cf09..f3dcacd255 100644 --- a/TGServiceTests/TempDirectoryRequiredTest.cs +++ b/TGServiceTests/TempDirectoryRequiredTest.cs @@ -6,6 +6,7 @@ namespace TGServiceTests /// /// To be the parent of test classes that required a temporary directory /// + [TestClass] public class TempDirectoryRequiredTest { /// diff --git a/TGStationServer3.sln b/TGStationServer3.sln index 5e501864f6..2999ef57e4 100644 --- a/TGStationServer3.sln +++ b/TGStationServer3.sln @@ -75,8 +75,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{081BB0BB ProjectSection(SolutionItems) = preProject Tools\build_byond.sh = Tools\build_byond.sh Tools\Config.dm = Tools\Config.dm + Tools\CoverageExclusions.runsettings = Tools\CoverageExclusions.runsettings Tools\DMAPITravisTester.dme = Tools\DMAPITravisTester.dme Tools\Doxyfile = Tools\Doxyfile + Tools\GenCodeCovXML.ps1 = Tools\GenCodeCovXML.ps1 Tools\install_byond.sh = Tools\install_byond.sh Tools\Test.dm = Tools\Test.dm Tools\TGS3Build.ps1 = Tools\TGS3Build.ps1 diff --git a/Tools/CoverageExclusions.runsettings b/Tools/CoverageExclusions.runsettings new file mode 100644 index 0000000000..92eee55060 --- /dev/null +++ b/Tools/CoverageExclusions.runsettings @@ -0,0 +1,28 @@ + + + + + + + + + + + + + .*\\TGServiceTests\\.* + + + + + True + True + True + False + + + + + + + \ No newline at end of file diff --git a/Tools/GenCodeCovXML.ps1 b/Tools/GenCodeCovXML.ps1 new file mode 100644 index 0000000000..b0403e8f48 --- /dev/null +++ b/Tools/GenCodeCovXML.ps1 @@ -0,0 +1,15 @@ +$coverageFilePath = Resolve-Path -path "TestResults\*\*.coverage" + +$coverageFilePath = $coverageFilePath.ToString() + +Write-Host "Running CodeCoverage.exe..." +&"C:\Program Files (x86)\Microsoft Visual Studio\Community\Team Tools\Dynamic Code Coverage Tools\CodeCoverage.exe" analyze /output:coverage.coveragexml "$coverageFilePath" + +rm -r TestResults + +Write-Host "Downloading PathCapitalizationCorrector v0.1.4..." +appveyor DownloadFile https://github.com/Cyberboss/PathCapitalizationCorrector/releases/download/0.1.4/PathCapitalizationCorrector.exe + +Write-Host "Fixing Window's terrible case ignorance..." +&"./PathCapitalizationCorrector.exe" coverage.coveragexml +codecov -f coverage.coveragexml diff --git a/appveyor.yml b/appveyor.yml index 261a73153e..0ad02e2883 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,8 +34,8 @@ after_build: - ps: if($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[TGSDeploy\]"){$env:TGSDeploy = "Do it."} - ps: $env:TGSVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("$env:APPVEYOR_BUILD_FOLDER/TGServerService/bin/Release/TGServerService.exe").FileVersion test_script: - - OpenCover.Console.exe -register:user -target:"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\MSTest.exe" -targetargs:"/testcontainer:"".\TGServiceTests\bin\Release\TGServiceTests.dll" -filter:"+[TG*]* -[TGServiceTests*]*" -output:".\TGSCoverage.xml" - - codecov -f "TGSCoverage.xml" + - vstest.console /logger:Appveyor "TGServiceTests\bin\Release\TGServiceTests.dll" /Enablecodecoverage /Settings:"Tools/CoverageExclusions.runsettings" /inIsolation /Platform:x64 + - powershell -Command "Tools/GenCodeCovXML.ps1" deploy: - provider: GitHub release: "tgstation-server-v$(TGSVersion)"