diff --git a/itext.tests/itext.barcodes.tests/Properties/AssemblyInfo.cs b/itext.tests/itext.barcodes.tests/Properties/AssemblyInfo.cs index 7bf66bb33..7bacbaa8f 100644 --- a/itext.tests/itext.barcodes.tests/Properties/AssemblyInfo.cs +++ b/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 diff --git a/itext.tests/itext.layout.tests/Properties/AssemblyInfo.cs b/itext.tests/itext.layout.tests/Properties/AssemblyInfo.cs index 953ff2056..a13f0e934 100644 --- a/itext.tests/itext.layout.tests/Properties/AssemblyInfo.cs +++ b/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 diff --git a/itext/itext.pdftest/itext/test/ITextTestLoggerFactory.cs b/itext/itext.pdftest/itext/test/ITextTestLoggerFactory.cs index 7d693b2a5..336c4b6a7 100644 --- a/itext/itext.pdftest/itext/test/ITextTestLoggerFactory.cs +++ b/itext/itext.pdftest/itext/test/ITextTestLoggerFactory.cs @@ -21,39 +21,63 @@ along with this program. If not, see . */ 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 expectedTemplates = new Dictionary(); + private readonly IDictionary> expectedTemplates = new ConcurrentDictionary>(); - private readonly IList logEvents = new List(); - - public void SetExpectedTemplates(Dictionary expectedTemplates) { - this.expectedTemplates.Clear(); - foreach (KeyValuePair item in expectedTemplates) + private readonly IDictionary> logEvents = new ConcurrentDictionary>(); + + private static readonly int DEFAULT_THREAD_ID = 1; + private static bool threadsAware = false; + + public ITextTestLoggerFactory() + { + InitThreadAwareness(); + } + + public void SetExpectedTemplates(Dictionary expectedTemplates) { + var threadId = GetThreadID(); + + if (!this.expectedTemplates.ContainsKey(threadId)) { - this.expectedTemplates[item.Key] = item.Value; + this.expectedTemplates.Add(threadId, new Dictionary()); } + this.expectedTemplates[threadId] = expectedTemplates; } public IList GetLogEvents() { - return logEvents; + var threadId = GetThreadID(); + + return logEvents.ContainsKey(threadId) ? logEvents[threadId] : new List(); } 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()); + } + + 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(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func 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; diff --git a/itext/itext.pdftest/itext/test/LogListener.cs b/itext/itext.pdftest/itext/test/LogListener.cs index ec4ab82c4..3836777d9 100644 --- a/itext/itext.pdftest/itext/test/LogListener.cs +++ b/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 . */ + 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(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(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 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(testDetails); + + var attributes = LogListenerHelper.GetTestAttributes(testDetails); if (attributes.Length > 0) { - Dictionary expectedTemplates = new Dictionary(); - - for (int i = 0; i < attributes.Length; i++) { - LogMessageAttribute logMessage = attributes[i]; + var expectedTemplates = new Dictionary(); + 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; } }