diff --git a/src/LogExpert.sln b/src/LogExpert.sln index 7fcaff98..04ba8137 100644 --- a/src/LogExpert.sln +++ b/src/LogExpert.sln @@ -58,6 +58,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "setup", "setup", "{C625E7C2 setup\LogExpertInstaller.iss = setup\LogExpertInstaller.iss EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegexColumnizer.UnitTests", "RegexColumnizer.UnitTests\RegexColumnizer.UnitTests.csproj", "{FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -358,6 +360,24 @@ Global {3D01E923-5219-488B-B0A7-98521841E680}.Release|Mixed Platforms.Build.0 = Release|Any CPU {3D01E923-5219-488B-B0A7-98521841E680}.Release|Win32.ActiveCfg = Release|Any CPU {3D01E923-5219-488B-B0A7-98521841E680}.Release|Win32.Build.0 = Release|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.Debug|Win32.ActiveCfg = Debug|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.Debug|Win32.Build.0 = Debug|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.DebugNoTimeout|Any CPU.ActiveCfg = Debug|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.DebugNoTimeout|Any CPU.Build.0 = Debug|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.DebugNoTimeout|Win32.ActiveCfg = Debug|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.DebugNoTimeout|Win32.Build.0 = Debug|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.Release|Any CPU.Build.0 = Release|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.Release|Win32.ActiveCfg = Release|Any CPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3}.Release|Win32.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/RegexColumnizer.UnitTests/Properties/AssemblyInfo.cs b/src/RegexColumnizer.UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..c42f87d7 --- /dev/null +++ b/src/RegexColumnizer.UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RegexColumnizer.UnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RegexColumnizer.UnitTests")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("FBFB598D-B94A-4AD3-A355-0D5A618CEEE3")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/src/RegexColumnizer.UnitTests/RegexColumnizer.UnitTests.csproj b/src/RegexColumnizer.UnitTests/RegexColumnizer.UnitTests.csproj new file mode 100644 index 00000000..f9a1b2d9 --- /dev/null +++ b/src/RegexColumnizer.UnitTests/RegexColumnizer.UnitTests.csproj @@ -0,0 +1,89 @@ + + + + + + Debug + AnyCPU + {FBFB598D-B94A-4AD3-A355-0D5A618CEEE3} + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Properties + RegexColumnizer.UnitTests + RegexColumnizer.UnitTests + v4.7.2 + 512 + + + true + + + ..\Solution Items\Key.snk + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll + + + ..\packages\Moq.4.18.4\lib\net462\Moq.dll + + + + ..\packages\NUnit.3.13.3\lib\net45\nunit.framework.dll + + + + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + + + + + + + + {e72c2bb1-34de-4d66-a830-9647c3837833} + ColumnizerLib + + + {b5a7dfa4-48a8-4616-8008-7441699ec946} + RegexColumnizer + + + + + 4.18.4 + + + 3.13.3 + + + 4.2.1 + + + + diff --git a/src/RegexColumnizer.UnitTests/RegexColumnizerTests.cs b/src/RegexColumnizer.UnitTests/RegexColumnizerTests.cs new file mode 100644 index 00000000..8f66f7de --- /dev/null +++ b/src/RegexColumnizer.UnitTests/RegexColumnizerTests.cs @@ -0,0 +1,67 @@ +using LogExpert; +using Moq; +using NUnit.Framework; + +namespace RegexColumnizer.UnitTests +{ + [TestFixture] + public class RegexColumnizerTests + { + + // The same amount of columns should be returned whether the line matches the regex or not. + [TestCase("5 test message", @"^(?'time'[\d]+)\s+(?'Message'.+)$", 2)] + [TestCase("Error in com.example.core", @"^(?'time'[\d]+)\s+(?'Message'.+)$", 2)] + public void SplitLine_ColumnCountMatches(string lineToParse, string regex, int expectedNumberOfColumns) + { + var columnizer = CreateInitializedColumnizer(regex); + + var testLogLine = new TestLogLine(4, lineToParse); + var parsedLogLine = columnizer.SplitLine(Mock.Of(), testLogLine); + + Assert.AreEqual(expectedNumberOfColumns, parsedLogLine.ColumnValues.Length); + } + + //Using "" for empty string since string.Empty can't be passed to the TestCase attribute. + [TestCase("5 test message", @"^(?'time'[\d]+)\s+(?'Message'.+)$", 0, "5")] + [TestCase("5 test message", @"^(?'time'[\d]+)\s+(?'Message'.+)$", 1, "test message")] + [TestCase("Error in com.example.core", @"^(?'time'[\d]+)\s+(?'Message'.+)$", 0, "")] // doesn't match regex so should be empty + [TestCase("Error in com.example.core", @"^(?'time'[\d]+)\s+(?'Message'.+)$", 1, "Error in com.example.core")] + public void SplitLine_ColumnValues(string lineToParse, string regex, int columnIndexToTest, + string expectedColumnValue) + { + var columnizer = CreateInitializedColumnizer(regex); + + var testLogLine = new TestLogLine(3, lineToParse); + var parsedLogLine = columnizer.SplitLine(Mock.Of(), testLogLine); + + Assert.AreEqual(expectedColumnValue, parsedLogLine.ColumnValues[columnIndexToTest].Text); + } + + private Regex1Columnizer CreateInitializedColumnizer(string regex) + { + var columnizerConfig = new RegexColumnizerConfig + { + Expression = regex, + Name = "Test regex" + }; + var columnizer = new Regex1Columnizer(); + columnizer.Init(columnizerConfig); + return columnizer; + } + + private class TestLogLine : ILogLine + { + public TestLogLine(int lineNumber, string fullLine) + { + LineNumber = lineNumber; + FullLine = fullLine; + } + + public string FullLine { get; set; } + public int LineNumber { get; set; } + public string Text { get; set; } + } + } + + +} \ No newline at end of file diff --git a/src/RegexColumnizer/Properties/AssemblyInfo.cs b/src/RegexColumnizer/Properties/AssemblyInfo.cs index d88885e5..87922f5e 100644 --- a/src/RegexColumnizer/Properties/AssemblyInfo.cs +++ b/src/RegexColumnizer/Properties/AssemblyInfo.cs @@ -11,5 +11,8 @@ [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] +//Allow the unit tests to use internal methods to make tests easier to read/write and understand +[assembly:InternalsVisibleTo("RegexColumnizer.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100619e9beea345a3bb5e15f55b29ddf40d96e9bb473ae58304fc63dfb3e9c94d8944bb7e45324ee0bef3e345dccba79b0bf64b85a128a7f261861899add639218ddaeb2acc6fcc746d6acb5bb212d375a0967756af192cfdb6cf0bff666a0fe535600abda860d3eafaff4ef1c9b5710181f72d996ca9c29ed64bae4a5fd916dea5")] + // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("b5a7dfa4-48a8-4616-8008-7441699ec946")] \ No newline at end of file diff --git a/src/RegexColumnizer/RegexColumnizer.cs b/src/RegexColumnizer/RegexColumnizer.cs index a5a4b937..723b21dc 100644 --- a/src/RegexColumnizer/RegexColumnizer.cs +++ b/src/RegexColumnizer/RegexColumnizer.cs @@ -71,6 +71,17 @@ public IColumnizedLogLine SplitLine(ILogLineColumnizerCallback callback, ILogLin Parent = logLine, FullValue = line.FullLine }; + + + //Fill other columns with empty string to avoid null pointer exceptions in unexpected places + for (var i = 0; i < columns.Length - 1; i++) + { + logLine.ColumnValues[i] = new Column + { + Parent = logLine, + FullValue = string.Empty + }; + } } } else @@ -170,7 +181,7 @@ public override string ToString() protected abstract string GetNameInternal(); - private void Init(RegexColumnizerConfig config) + internal void Init(RegexColumnizerConfig config) { Config = config;