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;