//[TestCase(1000, Ignore = "Only for performance tests")] public async Task SendSyncAndAsyncPerformance(int numOfRecipients) { // In this sample: // With 100,000 messages and 10 MaxNumOfSmtpClients async is about twice as fast as sync. var recipients = new List <Recipient>(); for (var i = 0; i < numOfRecipients; i++) { recipients.Add(new Recipient { Email = $"recipient-{i}@example.com", Name = $"Name of {i}" }); } var mmm = new MailMergeMessage("Async/Sync email test", "This is the plain text part for {Name} ({Email})") { Config = _settings.MessageConfig }; mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "{Name}", "{Email}")); var mms = new MailMergeSender { Config = _settings.SenderConfig }; mms.Config.MaxNumOfSmtpClients = 10; var sw = new Stopwatch(); sw.Start(); mms.Send(mmm, recipients); sw.Stop(); Console.WriteLine($"Time to send {recipients.Count} messages sync: {sw.ElapsedMilliseconds} milliseconds."); Console.WriteLine(); Assert.AreEqual(recipients.Count, _server.ReceivedEmail.Length); Assert.IsFalse(mms.IsBusy); sw.Reset(); _server.ClearReceivedEmail(); sw.Start(); int numOfSmtpClientsUsed = 0; mms.OnMergeComplete += (s, args) => { numOfSmtpClientsUsed = args.NumOfSmtpClientsUsed; }; await mms.SendAsync(mmm, recipients); sw.Stop(); Console.WriteLine( $"Time to send {recipients.Count} messages async: {sw.ElapsedMilliseconds} milliseconds."); // Note: With too many SmtpClients and small emails some of the clients will never de-queue from the ConcurrentQueue of MailMergeSender Console.WriteLine( $"{numOfSmtpClientsUsed} tasks (and SmtpClients) used for sending async\n(max {mms.Config.MaxNumOfSmtpClients} were configured)."); Assert.AreEqual(recipients.Count, _server.ReceivedEmail.Length); Assert.IsFalse(mms.IsBusy); mms.Dispose(); mmm.Dispose(); }
private void SendMail(EventHandler <MailSenderAfterSendEventArgs> onAfterSend = null, EventHandler <MailSenderSmtpClientEventArgs> onSmtpConnected = null, EventHandler <MailSenderSmtpClientEventArgs> onSmtpDisconnected = null, EventHandler <MailSenderSendFailureEventArgs> onSendFailure = null) { var data = new Dictionary <string, object> { { "MessageText", "This is just a sample plain text." }, { "Date", DateTime.Now } }; var mmm = new MailMergeMessage("Mailsubject sent on {Date}", "{MessageText}") { Config = _settings.MessageConfig }; mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "Test name", "*****@*****.**")); var mms = new MailMergeSender() { Config = _settings.SenderConfig }; mms.OnAfterSend += onAfterSend; mms.OnSmtpConnected += onSmtpConnected; mms.OnSmtpDisconnected += onSmtpDisconnected; mms.OnSendFailure += onSendFailure; mms.Send(mmm, (object)data); mms.Dispose(); }
public void TryToSendWhenSenderIsBusy() { var mmm = new MailMergeMessage("Subject", "plain text"); mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "Test name", "*****@*****.**")); mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "Test name 2", "*****@*****.**")); var mms = new MailMergeSender { IsBusy = true }; // single mail Assert.Throws <InvalidOperationException>(() => mms.Send(mmm, new object())); Assert.ThrowsAsync <InvalidOperationException>(async() => await mms.SendAsync(mmm, new object())); // several mails Assert.Throws <InvalidOperationException>(() => mms.Send(mmm, new Dictionary <string, string>())); Assert.ThrowsAsync <InvalidOperationException>(async() => await mms.SendAsync(mmm, new Dictionary <string, string>())); mms.IsBusy = false; mmm.Dispose(); mms.Dispose(); }
public async Task CancelSendOperation() { var mmm = new MailMergeMessage("Cancel immediately", "plain text"); mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "Test name", "*****@*****.**")); mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.From, "Test name 2", "*****@*****.**")); var mms = new MailMergeSender { Config = _settings.SenderConfig }; mms.Config.MaxNumOfSmtpClients = 1; mms.Config.SmtpClientConfig[0].MessageOutput = MessageOutput.SmtpServer; mms.Config.SmtpClientConfig[0].DelayBetweenMessages = 2000; var anyData = new[] { new { mailNo = 1 }, new { mailNo = 2 }, new { mailNo = 3 } }; var tasks = new[] { await Task.Factory.StartNew(async() => await mms.SendAsync(mmm, anyData)), Task.Factory.StartNew(() => mms.SendCancel()), Task.Factory.StartNew(() => mms.SendCancel()) // a second cancel operation will just return }; Assert.Throws <AggregateException>(() => { Task.WaitAll(tasks); }); Assert.AreEqual(0, _server.ReceivedEmailCount); mms.Dispose(); mmm.Dispose(); }
public void CancelSendOperationWithDelay() { var mmm = new MailMergeMessage("Cancel with delay", "plain text"); mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "Test name", "*****@*****.**")); mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.From, "Test name 2", "*****@*****.**")); var mms = new MailMergeSender { Config = _settings.SenderConfig }; mms.Config.MaxNumOfSmtpClients = 1; mms.Config.SmtpClientConfig[0].MessageOutput = MessageOutput.SmtpServer; mms.Config.SmtpClientConfig[0].DelayBetweenMessages = 2000; var anyData = new[] { new { mailNo = 1 }, new { mailNo = 2 }, new { mailNo = 3 } }; mms.SendCancel(500); Assert.ThrowsAsync <TaskCanceledException>(() => mms.SendAsync(mmm, anyData)); Assert.AreEqual(0, _server.ReceivedEmailCount); mmm.Dispose(); mms.Dispose(); }
public void TryToSendWithNullAsEnumerable() { var mms = new MailMergeSender(); var mmm = new MailMergeMessage(); // no need to fully prepare for this test Assert.Throws <ArgumentNullException>(() => mms.Send(null, (Dictionary <string, string>)null)); Assert.ThrowsAsync <ArgumentNullException>(async() => await mms.SendAsync(mmm, (Dictionary <string, string>)null)); mms.Dispose(); mmm.Dispose(); }
public void TryToSendWithNullMessage() { var mms = new MailMergeSender(); // single mail Assert.Throws <ArgumentNullException>(() => mms.Send(null, new object())); Assert.ThrowsAsync <ArgumentNullException>(async() => await mms.SendAsync(null, new object())); // several mails Assert.Throws <ArgumentNullException>(() => mms.Send(null, new Dictionary <string, string>())); Assert.ThrowsAsync <ArgumentNullException>(async() => await mms.SendAsync(null, new Dictionary <string, string>())); mms.Dispose(); }
[TestCase(false, true)] // no exception with null for MimeMessage must throw exception public void Send_With_And_Without_MailMergeMessageException(bool throwException, bool setMimeMessageToNull) { #region * Sync and Async preparation * const string theFormatError = "{causeFormatError}"; const string plainText = theFormatError + "This is the plain text part for {Name} ({Email})"; var mms = new MailMergeSender { Config = _settings.SenderConfig }; mms.Config.MaxNumOfSmtpClients = 1; // Event raising when getting the merged MimeMessage of the MailMergeMessage has failed. mms.OnMessageFailure += (mailMergeSender, messageFailureArgs) => { lock (_locker) { if (throwException) { return; } // Remove the cause of the exception and return corrected values // Note: changes of MailMergeMessage will affect als messages to be sent messageFailureArgs.MailMergeMessage.PlainText = plainText.Replace(theFormatError, string.Empty); // in production a try...catch... must be implemented if (setMimeMessageToNull) { messageFailureArgs.MimeMessage = null; } else { messageFailureArgs.MimeMessage = messageFailureArgs.MailMergeMessage.GetMimeMessage(messageFailureArgs.DataSource); } messageFailureArgs.ThrowException = throwException; } }; var recipients = new List <Recipient>(); for (var i = 0; i < 2; i++) { recipients.Add(new Recipient { Email = $"recipient-{i}@example.com", Name = $"Name of {i}" }); } var mmm = new MailMergeMessage("Message failure", plainText) { Config = _settings.MessageConfig }; mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "{Name}", "{Email}")); #endregion #region * Synchronous send methods * // send enumerable data mmm.PlainText = plainText; // set text from constant try { if (throwException) { Assert.Throws <MailMergeMessage.MailMergeMessageException>(() => mms.Send(mmm, recipients)); } else { if (setMimeMessageToNull) { Assert.Throws <MailMergeMessage.MailMergeMessageException>(() => mms.Send(mmm, recipients)); } else { Assert.DoesNotThrow(() => mms.Send(mmm, recipients)); } } } catch (Exception e) { Console.WriteLine(e.Message); } if (throwException) { Assert.AreEqual(0, _server.ReceivedEmailCount); } else { if (setMimeMessageToNull) { Assert.AreEqual(0, _server.ReceivedEmailCount); } else { Assert.AreEqual(recipients.Count, _server.ReceivedEmailCount); } } _server.ClearReceivedEmail(); // send single data item mmm.PlainText = plainText; // set text from constant try { if (throwException) { Assert.Throws <MailMergeMessage.MailMergeMessageException>(() => mms.Send(mmm, recipients[0])); } else { if (setMimeMessageToNull) { Assert.Throws <MailMergeMessage.MailMergeMessageException>(() => mms.Send(mmm, recipients[0])); } else { Assert.DoesNotThrow(() => mms.Send(mmm, recipients[0])); } } } catch (Exception e) { Console.WriteLine(e.Message); } if (throwException) { Assert.AreEqual(0, _server.ReceivedEmailCount); } else { if (setMimeMessageToNull) { Assert.AreEqual(0, _server.ReceivedEmailCount); } else { Assert.AreEqual(1, _server.ReceivedEmailCount); } } _server.ClearReceivedEmail(); #endregion #region * Async send methods * // send enumerable data mmm.PlainText = plainText; // set text from constant try { if (throwException) { Assert.ThrowsAsync <MailMergeMessage.MailMergeMessageException>(async() => await mms.SendAsync(mmm, recipients)); } else { if (setMimeMessageToNull) { Assert.ThrowsAsync <MailMergeMessage.MailMergeMessageException>(async() => await mms.SendAsync(mmm, recipients)); } else { Assert.DoesNotThrow(() => mms.Send(mmm, recipients)); } } } catch (Exception e) { Console.WriteLine(e.Message); } if (throwException) { Assert.AreEqual(0, _server.ReceivedEmailCount); } else { if (setMimeMessageToNull) { Assert.AreEqual(0, _server.ReceivedEmailCount); } else { Assert.AreEqual(recipients.Count, _server.ReceivedEmailCount); } } _server.ClearReceivedEmail(); // send single data item mmm.PlainText = plainText; // set text from constant try { if (throwException) { Assert.ThrowsAsync <MailMergeMessage.MailMergeMessageException>(async() => await mms.SendAsync(mmm, recipients[0])); } else { if (setMimeMessageToNull) { Assert.ThrowsAsync <MailMergeMessage.MailMergeMessageException>(async() => await mms.SendAsync(mmm, recipients[0])); } else { Assert.DoesNotThrow(() => mms.Send(mmm, recipients[0])); } } } catch (Exception e) { Console.WriteLine(e.Message); } if (throwException) { Assert.AreEqual(0, _server.ReceivedEmailCount); } else { if (setMimeMessageToNull) { Assert.AreEqual(0, _server.ReceivedEmailCount); } else { Assert.AreEqual(1, _server.ReceivedEmailCount); } } #endregion mms.Dispose(); mmm.Dispose(); }
public async Task AllSenderEventsMailMerge(string somePlaceholder, bool withParseFailure) { #region * Sync and Async preparation * var actualEvents = new ConcurrentStack <string>(); var expectedEvents = new ConcurrentStack <string>(); var mms = new MailMergeSender { Config = _settings.SenderConfig }; mms.Config.MaxNumOfSmtpClients = 1; // Event raising before merging starts mms.OnMergeBegin += (mailMergeSender, mergeBeginArgs) => { actualEvents.Push(nameof(mms.OnMergeBegin)); }; // Event raising when getting the merged MimeMessage of the MailMergeMessage has failed. mms.OnMessageFailure += (mailMergeSender, messageFailureArgs) => { actualEvents.Push(nameof(mms.OnMessageFailure)); }; // Event raising before sending a single mail message starts mms.OnBeforeSend += (smtpClient, beforeSendArgs) => { actualEvents.Push(nameof(mms.OnBeforeSend)); }; // Event raising right after the SmtpClient's connection to the server is up (but not yet authenticated). mms.OnSmtpConnected += (smtpClient, smtpClientArgs) => { actualEvents.Push(nameof(mms.OnSmtpConnected)); }; // Event raising after the SmtpClient has authenticated on the server. mms.OnSmtpAuthenticated += (smtpClient, smtpClientArgs) => { actualEvents.Push(nameof(mms.OnSmtpAuthenticated)); }; // Event raising after the SmtpClient has disconnected from the SMTP mail server. mms.OnSmtpDisconnected += (smtpClient, smtpClientArgs) => { actualEvents.Push(nameof(mms.OnSmtpDisconnected)); }; // Event raising if sending a single mail message fails mms.OnSendFailure += (smtpClient, sendFailureArgs) => { actualEvents.Push(nameof(mms.OnSendFailure)); }; // Event raising before sending a single mail message is finished mms.OnAfterSend += (smtpClient, afterSendArgs) => { actualEvents.Push(nameof(mms.OnAfterSend)); }; // Event raising each time before and after a single message was sent mms.OnMergeProgress += (mailMergeSender, progressArgs) => { actualEvents.Push(nameof(mms.OnMergeProgress)); }; // Event raising after merging is completed mms.OnMergeComplete += (mailMergeSender, completedArgs) => { actualEvents.Push(nameof(mms.OnMergeComplete)); }; var recipients = new List <Recipient>(); for (var i = 0; i < 1; i++) { recipients.Add(new Recipient { Email = $"recipient-{i}@example.com", Name = $"Name of {i}" }); } var mmm = new MailMergeMessage("Event tests" + somePlaceholder, "This is the plain text part for {Name} ({Email})") { Config = _settings.MessageConfig }; mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "{Name}", "{Email}")); var sequenceOfExpectedEvents = new List <string>(); if (withParseFailure) { sequenceOfExpectedEvents.Clear(); sequenceOfExpectedEvents.AddRange(new[] { nameof(mms.OnMergeBegin), nameof(mms.OnMergeProgress), nameof(mms.OnMessageFailure), nameof(mms.OnMergeProgress), nameof(mms.OnMergeComplete) }); } else { sequenceOfExpectedEvents.Clear(); sequenceOfExpectedEvents.AddRange(new[] { nameof(mms.OnMergeBegin), nameof(mms.OnMergeProgress), nameof(mms.OnBeforeSend), nameof(mms.OnSmtpConnected), nameof(mms.OnAfterSend), nameof(mms.OnMergeProgress), nameof(mms.OnSmtpDisconnected), nameof(mms.OnMergeComplete) }); } #endregion #region * Synchronous send method * try { mms.Send(mmm, recipients); } catch (Exception e) { Console.WriteLine(e.Message); } expectedEvents.Clear(); expectedEvents.PushRange(sequenceOfExpectedEvents.ToArray()); Assert.AreEqual(expectedEvents.Count, actualEvents.Count); // sequence of sync sending is predefined while (actualEvents.Count > 0) { expectedEvents.TryPop(out string expected); actualEvents.TryPop(out string actual); Assert.AreEqual(expected, actual); } #endregion #region * Async send method * actualEvents.Clear(); expectedEvents.Clear(); expectedEvents.PushRange(sequenceOfExpectedEvents.ToArray()); try { await mms.SendAsync(mmm, recipients); } catch (Exception e) { Console.WriteLine(e.Message); } Assert.AreEqual(expectedEvents.Count, actualEvents.Count); // sequence of async sending may be different from sync, but all events must exists var sortedActual = actualEvents.OrderBy(e => e).ToArray(); var sortedExpected = expectedEvents.OrderBy(e => e).ToArray(); for (var i = 0; i < sortedActual.Length; i++) { Assert.AreEqual(sortedExpected[i], sortedActual[i]); } #endregion mms.Dispose(); mmm.Dispose(); }