Browse Source

Fix logging logic for multithreaded testing

DEVSIX-9032
pull/37/head
Vitali Prudnikovich 3 months ago
parent
commit
5672a25c0f
  1. 3
      itext.tests/itext.barcodes.tests/Properties/AssemblyInfo.cs
  2. 3
      itext.tests/itext.layout.tests/Properties/AssemblyInfo.cs
  3. 120
      itext/itext.pdftest/itext/test/ITextTestLoggerFactory.cs
  4. 102
      itext/itext.pdftest/itext/test/LogListener.cs

3
itext.tests/itext.barcodes.tests/Properties/AssemblyInfo.cs

@ -1,3 +1,4 @@
using NUnit.Framework;
using System.Reflection;
using System.Runtime.InteropServices;
@ -18,6 +19,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyFileVersion("9.2.0.0")]
[assembly: AssemblyInformationalVersion("9.2.0-SNAPSHOT")]
[assembly: Parallelizable(ParallelScope.ContextMask)]
#if !NETSTANDARD2_0
[assembly: NUnit.Framework.Timeout(300000)]
#endif

3
itext.tests/itext.layout.tests/Properties/AssemblyInfo.cs

@ -1,3 +1,4 @@
using NUnit.Framework;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -19,6 +20,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyFileVersion("9.2.0.0")]
[assembly: AssemblyInformationalVersion("9.2.0-SNAPSHOT")]
[assembly: Parallelizable(ParallelScope.ContextMask)]
#if !NETSTANDARD2_0
[assembly: NUnit.Framework.Timeout(600000)]
#endif

120
itext/itext.pdftest/itext/test/ITextTestLoggerFactory.cs

@ -21,39 +21,63 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using Microsoft.Extensions.Logging;
using NUnit.Framework;
namespace iText.Test
{
internal class ITextTestLoggerFactory : ILoggerFactory
{
private readonly Dictionary<String, Boolean> expectedTemplates = new Dictionary<String, Boolean>();
private readonly IDictionary<int, IDictionary<string, bool>> expectedTemplates = new ConcurrentDictionary<int, IDictionary<string, bool>>();
private readonly IList<ITextTestLogEvent> logEvents = new List<ITextTestLogEvent>();
public void SetExpectedTemplates(Dictionary<String, Boolean> expectedTemplates) {
this.expectedTemplates.Clear();
foreach (KeyValuePair<String, Boolean> item in expectedTemplates)
private readonly IDictionary<int, IList<ITextTestLogEvent>> logEvents = new ConcurrentDictionary<int, IList<ITextTestLogEvent>>();
private static readonly int DEFAULT_THREAD_ID = 1;
private static bool threadsAware = false;
public ITextTestLoggerFactory()
{
InitThreadAwareness();
}
public void SetExpectedTemplates(Dictionary<string, bool> expectedTemplates) {
var threadId = GetThreadID();
if (!this.expectedTemplates.ContainsKey(threadId))
{
this.expectedTemplates[item.Key] = item.Value;
this.expectedTemplates.Add(threadId, new Dictionary<string, bool>());
}
this.expectedTemplates[threadId] = expectedTemplates;
}
public IList<ITextTestLogEvent> GetLogEvents()
{
return logEvents;
var threadId = GetThreadID();
return logEvents.ContainsKey(threadId) ? logEvents[threadId] : new List<ITextTestLogEvent>();
}
public void Dispose()
{
expectedTemplates.Clear();
logEvents.Clear();
var threadId = GetThreadID();
if (expectedTemplates.ContainsKey(threadId))
{
expectedTemplates[threadId].Clear();
}
if (logEvents.ContainsKey(threadId))
{
logEvents[threadId].Clear();
}
}
public ILogger CreateLogger(string categoryName)
{
return new ITextTestLogger(categoryName, this);
}
@ -64,10 +88,10 @@ namespace iText.Test
throw new NotImplementedException();
}
private bool IsExpectedMessage(String message) {
if (message != null) {
foreach (var template in expectedTemplates) {
if (LogListenerHelper.EqualsMessageByTemplate(message, template.Key)) {
private bool IsExpectedMessage(string message, int threadId) {
if (message != null && expectedTemplates.ContainsKey(threadId)) {
foreach (var template in expectedTemplates[threadId].Keys) {
if (LogListenerHelper.EqualsMessageByTemplate(message, template)) {
return true;
}
}
@ -75,10 +99,10 @@ namespace iText.Test
return false;
}
private bool IsExpectedMessageQuiet(String message) {
if (message != null) {
foreach (String template in expectedTemplates.Keys) {
if (LogListenerHelper.EqualsMessageByTemplate(message, template) && expectedTemplates[template]) {
private bool IsExpectedMessageQuiet(string message, int threadId) {
if (message != null && expectedTemplates.ContainsKey(threadId)) {
foreach (var template in expectedTemplates[threadId].Keys) {
if (LogListenerHelper.EqualsMessageByTemplate(message, template) && expectedTemplates[threadId][template]) {
return true;
}
}
@ -88,7 +112,46 @@ namespace iText.Test
private void AddLogEvent(ITextTestLogEvent testLogEvent)
{
logEvents.Add(testLogEvent);
var threadId = GetThreadID();
if (!logEvents.ContainsKey(threadId))
{
logEvents.Add(threadId, new List<ITextTestLogEvent>());
}
logEvents[threadId].Add(testLogEvent);
}
private static void InitThreadAwareness()
{
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var asm in loadedAssemblies)
{
if (asm.IsDynamic || !asm.CodeBase.ToLower().EndsWith("tests.dll"))
{
continue;
}
var attrs = asm.GetCustomAttributes(true);
int parallel = attrs.Count(t => t is ParallelizableAttribute);
if (parallel > 0)
{
threadsAware = true;
return;
}
}
}
private static int GetThreadID()
{
if (threadsAware)
{
return Thread.CurrentThread.ManagedThreadId;
}
else
{
return DEFAULT_THREAD_ID;
}
}
public class ITextTestLogEvent
@ -102,35 +165,34 @@ namespace iText.Test
this.message = message;
}
}
private class ITextTestLogger : ILogger
internal class ITextTestLogger : ILogger
{
private readonly string categoryName;
private readonly ITextTestLoggerFactory factory;
private bool runInSilentMode = false;
private bool runInSilentMode;
private static readonly string TOKEN_ITEXT_SILENT_MODE = "ITEXT_SILENT_MODE";
private static readonly string ITEXT_LICENCING_PACKAGE = "iText.Licensing";
private static readonly string ITEXT_ACTIONS_PACKAGE = "iText.Commons.Actions.Processors";
public ITextTestLogger(string categoryName, ITextTestLoggerFactory factory)
{
this.categoryName = categoryName;
this.factory = factory;
SetupRunInSilentMode();
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (!factory.IsExpectedMessageQuiet(state.ToString()))
if (!factory.IsExpectedMessageQuiet(state.ToString(), GetThreadID()))
{
if (ShouldPrintMessage(categoryName))
{
Console.WriteLine( categoryName + ": " + state);
}
}
if (logLevel >= LogLevel.Warning || factory.IsExpectedMessage(state.ToString()))
if (logLevel >= LogLevel.Warning || factory.IsExpectedMessage(state.ToString(), GetThreadID()))
{
factory.AddLogEvent(new ITextTestLogEvent(logLevel, state.ToString()));
}
@ -161,7 +223,7 @@ namespace iText.Test
}
private void SetupRunInSilentMode() {
string envIsSilentModeEnabled = Environment.GetEnvironmentVariable(TOKEN_ITEXT_SILENT_MODE);
var envIsSilentModeEnabled = Environment.GetEnvironmentVariable(TOKEN_ITEXT_SILENT_MODE);
if (string.IsNullOrEmpty(envIsSilentModeEnabled)) {
runInSilentMode = false;
return;

102
itext/itext.pdftest/itext/test/LogListener.cs

@ -20,77 +20,92 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using iText.Commons;
using iText.IO;
using iText.Test.Attributes;
using Microsoft.Extensions.Logging;
using NUnit.Framework;
using NUnit.Framework.Interfaces;
namespace iText.Test {
namespace iText.Test
{
[AttributeUsage(AttributeTargets.Class)]
public class LogListener : TestActionAttribute {
public class LogListener : TestActionAttribute
{
private static readonly ITextTestLoggerFactory TEST_LOGGER_FACTORY;
private ILoggerFactory defaultLoggerFactory;
static LogListener() {
static LogListener()
{
TEST_LOGGER_FACTORY = new ITextTestLoggerFactory();
ITextLogManager.SetLoggerFactory(TEST_LOGGER_FACTORY);
}
public override void BeforeTest(ITest testDetails)
{
defaultLoggerFactory = ITextLogManager.GetLoggerFactory();
ITextLogManager.SetLoggerFactory(TEST_LOGGER_FACTORY);
Init(testDetails);
}
public override void AfterTest(ITest testDetails) {
public override void AfterTest(ITest testDetails)
{
CheckLogMessages(testDetails);
ITextLogManager.SetLoggerFactory(defaultLoggerFactory);
}
public override ActionTargets Targets {
public override ActionTargets Targets
{
get { return ActionTargets.Test; }
}
private void CheckLogMessages(ITest testDetails) {
int checkedMessages = 0;
LogMessageAttribute[] attributes = LogListenerHelper.GetTestAttributes<LogMessageAttribute>(testDetails);
if (attributes.Length > 0) {
for (int i = 0; i < attributes.Length; i++) {
LogMessageAttribute logMessage = attributes[i];
int foundCount = Contains(logMessage);
if (foundCount != logMessage.Count && !logMessage.Ignore) {
LogListenerHelper.FailWrongMessageCount(logMessage.Count, foundCount, logMessage.GetMessageTemplate(), testDetails);
} else {
checkedMessages += foundCount;
private void CheckLogMessages(ITest testDetails)
{
var checkedMessagesCount = 0;
var attributes = LogListenerHelper.GetTestAttributes<LogMessageAttribute>(testDetails);
if (attributes.Length > 0)
{
for (var i = 0; i < attributes.Length; i++)
{
var logMessage = attributes[i];
var foundCount = Contains(logMessage);
if (foundCount != logMessage.Count && !logMessage.Ignore)
{
LogListenerHelper.FailWrongMessageCount(logMessage.Count, foundCount,
logMessage.GetMessageTemplate(), testDetails);
}
else
{
checkedMessagesCount += foundCount;
}
}
}
if (GetSize() > checkedMessages) {
LogListenerHelper.FailWrongTotalCount(GetSize(), checkedMessages, testDetails);
if (GetLogEventsSize() > checkedMessagesCount)
{
LogListenerHelper.FailWrongTotalCount(GetLogEventsSize(), checkedMessagesCount, testDetails);
}
}
private int Contains(LogMessageAttribute loggingStatement) {
IList<ITextTestLoggerFactory.ITextTestLogEvent> eventList = TEST_LOGGER_FACTORY.GetLogEvents();
int index = 0;
for (int i = 0; i < eventList.Count; i++) {
if (IsLevelCompatible(loggingStatement.LogLevel, eventList[i].logLevel)
&& LogListenerHelper.EqualsMessageByTemplate(eventList[i].message, loggingStatement.GetMessageTemplate())) {
private int Contains(LogMessageAttribute loggingStatement)
{
var eventList = TEST_LOGGER_FACTORY.GetLogEvents();
var index = 0;
for (var i = 0; i < eventList.Count; i++)
{
if (IsLevelCompatible(loggingStatement.LogLevel, eventList[i].logLevel)
&& LogListenerHelper.EqualsMessageByTemplate(eventList[i].message,
loggingStatement.GetMessageTemplate()))
{
index++;
}
}
return index;
}
private bool IsLevelCompatible(int logMessageLevel, LogLevel eventLevel) {
switch (logMessageLevel) {
private bool IsLevelCompatible(int logMessageLevel, LogLevel eventLevel)
{
switch (logMessageLevel)
{
case LogLevelConstants.UNKNOWN:
return eventLevel >= LogLevel.Warning;
case LogLevelConstants.ERROR:
@ -106,22 +121,27 @@ namespace iText.Test {
}
}
private void Init(ITest testDetails) {
private void Init(ITest testDetails)
{
// Cleanup on Init. We can cleanup when finishing but there some exception may be thrown on checking messages.
TEST_LOGGER_FACTORY.Dispose();
LogMessageAttribute[] attributes = LogListenerHelper.GetTestAttributes<LogMessageAttribute>(testDetails);
var attributes = LogListenerHelper.GetTestAttributes<LogMessageAttribute>(testDetails);
if (attributes.Length > 0)
{
Dictionary<String, Boolean> expectedTemplates = new Dictionary<string, bool>();
for (int i = 0; i < attributes.Length; i++) {
LogMessageAttribute logMessage = attributes[i];
var expectedTemplates = new Dictionary<string, bool>();
for (var i = 0; i < attributes.Length; i++)
{
var logMessage = attributes[i];
expectedTemplates.Add(logMessage.GetMessageTemplate(), logMessage.QuietMode);
}
TEST_LOGGER_FACTORY.SetExpectedTemplates(expectedTemplates);
}
}
private int GetSize() {
private int GetLogEventsSize()
{
return TEST_LOGGER_FACTORY.GetLogEvents().Count;
}
}

Loading…
Cancel
Save