Archive for the ‘Programming’ Category

Programmatically check Log4Net log

Monday, November 30th, 2009

Today I needed to create unit test that checked if the NHibernate query was generated optimally.

Good thing is that, in case of an inefficient query, NHibernate puts a warning using log4net:

log.Warn( "firstResult/maxResults specified with collection fetch; applying in memory!" );

I wanted something like this:

[Test]
public void FindUsersBy_QueryWithLimit_LimitsOnSQLSide()
{
    using (LogChecker logChecker
        = new LogChecker("NHibernate", Level.Warn))
    {
        // Execute query using NHibernate

        // ....

        Assert.IsEmpty(logChecker.Messages);
    }
}

The problem is that it’s not that easy to attach MemoryAppender for a duration of unit test to the specified logger.

Anyway here’s the code:

public class LogChecker : IDisposable
{
    readonly Logger _logger;
    readonly Level _previousLevel;
    readonly MemoryAppender _appender = new MemoryAppender();

    public LogChecker(string logName, Level levelToCheck)
    {
        _logger = (Logger)LogManager.GetLogger(logName).Logger;
        _logger.AddAppender(_appender);
        _previousLevel = _logger.Level;
        _logger.Level = levelToCheck;
    }

    public List<string> Messages
    {
        get
        {
            return new List<loggingEvent>(_appender.GetEvents())
                  .ConvertAll(x => x.RenderedMessage);
        }
    }

    public void Dispose()
    {
        _logger.Level = _previousLevel;
        _logger.RemoveAppender(_appender);
    }
};

Wrapper configuration with StructureMap

Thursday, November 5th, 2009

Recently we have some problems with StructureMap configuration.
We decided to move away from xml files and use registry configuration.

Imagine the following scenario, we have a service class (MyService), and decide
to create simple caching mechanism, by creating a wrapper class (MyServiceWithCaching)

public interface IService
{
	List<string> MyMethod();
}

// Original service
public class MyService : IService
{
	public List<string> MyMethod() { ... }
}

// Service that supports simple caching
public class MyServiceWithCaching : IService
{
	IService _service;
	List<string> _cached;

	// NOTE: We want IService to be injected by IoC container
  	public MyServiceWithCaching(IService service)
  	{
		_service =service;
  	}

	public List<string> MyMethod()
	{
		if (_cached == null)
		{
			// call original when no cached data
			_cached = _service.MyMethod();
		}
		return _cached;
	}
}

The problem is how to tell StructureMap that when somebody asks for IMyService it should get MyServiceWithCaching which should be injected with original MyService class.

It took us some time to figure this out and here’s the solution:

public class IocRegistry : Registry
{
    protected override void configure()
    {
        this.BuildInstancesOf<imyService>()
             .TheDefaultIs(
                Instance<imyService>()
                    .UsingConcreteType<myServiceWithCaching>()
                        .Child<imyService>()
                            .IsConcreteType<myService>()
            );

        base.configure();
    }
} ;

Unit testing is good

Friday, October 23rd, 2009

Recently Ayende added another spot-the-bug post. Usually I’m just curious how fast people find the problem.

Some time ago he also published few posts about how he does unit testing.

In “Scenario driven tests” he says that, as I understand it, it’s better to test a whole scenario not a single method. I’m okay with that, as long as scenarios are in reasonable numbers and code coverage is high.

In “Tests have got to justify themselves” he states “I am not using TDD.”

Well if you were you would not have so many problems in 10 lines of code, period.
You want to test scenarios ok, but when you’ve simple method with lots of logic, you are going to make a mistake at some point. Test it!

public static string FirstCharacters(this string self, int numOfChars)
{
    if (self == null)
        return "";
    if (self.Length < numOfChars)
        return self;
    return self
        .Replace(Environment.NewLine, " ")
        .Substring(0, numOfChars - 3) + "...";
}

Each of the following tests will fail:
(negative values are not reasonable input for such method, so we won’t go there)

[Test]
public void FirstCharacters_EmptyString_Truncates()
{
    Assert.AreEqual("", "".FirstCharacters(0));
}

[Test]
public void FirstCharacters_RegularString_Truncates()
{
    Assert.AreEqual("12345", "12345".FirstCharacters(5));
}

[Test]
public void Method_Condition_Result()
{
    Assert.AreEqual("...", "12345".FirstCharacters(2));
}

[Test]
public void FirstCharacters_StringWithNewLine_Truncates()
{
    Assert.AreEqual("  ", "rnrnrn".FirstCharacters(2));
}

[Test]
public void FirstCharacters_StringWithNewLine_Truncates2()
{
    Assert.AreEqual(" ...", "nnnnnnnn".FirstCharacters(4));
}

[Test]
public void FirstCharacters_StringWithNewLine_Truncates3()
{
    Assert.AreEqual("start end", "startnend".FirstCharacters(255));
}

Printing in WebBrowser control (custom header and footer)

Tuesday, September 29th, 2009

This is going to be a complicated one. Whole code/working sample is near the end of the article.

The Task:
Enable HTML printing using WebBrowser content but modify standard header and footer.

SampleApp

Unfortunately, when we print, nasty footer and header appear, and there is no way to get rid of them:
NastyFooter

ShowPrintDialog method comment explicitly says that header and footer can not be modified:
ShowPrintDialog

The Big Plan:
1. First we create IEPrinting.dll written in c++ and managed c++, it exposes one extremely simple helper class: PrintHelper:

// PrintHelper.h
#pragma once
using namespace System;
namespace IEPrinting
{
	public ref class PrintHelper
	{
		public:
			static void Print(IntPtr^ ptrIWebBrowser2, String^ header, String^ footer);
	};
}

2.
From now on things get complicated:

// This is the main DLL file.
#include "stdafx.h"
#include "PrintHelper.h"

namespace IEPrinting
{
#pragma unmanaged

int UnmanagedPrint(IWebBrowser2* webOC, BSTR header, BSTR footer)
{
	SAFEARRAYBOUND psabBounds[1];
	SAFEARRAY *psaHeadFoot;
	HRESULT hr = S_OK;

	// Variables needed to send IStream header to print operation.
	HGLOBAL hG = 0;
	IStream *pStream= NULL;
	IUnknown *pUnk = NULL;
	ULONG lWrote = 0;
	LPSTR sMem = NULL;

	// Initialize header and footer parameters to send to ExecWB().
	psabBounds[0].lLbound = 0;
	psabBounds[0].cElements = 3;
	psaHeadFoot = SafeArrayCreate(VT_VARIANT, 1, psabBounds);
	if (NULL == psaHeadFoot) {
		// Error handling goes here.
		goto cleanup;
	}
	VARIANT vHeadStr, vFootStr, vHeadTxtStream;
	long rgIndices;
	VariantInit(&vHeadStr);
	VariantInit(&vFootStr);
	VariantInit(&vHeadTxtStream);

	// Argument 1: Header
	vHeadStr.vt = VT_BSTR;
	vHeadStr.bstrVal = header;
	if (vHeadStr.bstrVal == NULL) {
		goto cleanup;
	}

	// Argument 2: Footer
	vFootStr.vt = VT_BSTR;
	vFootStr.bstrVal = footer;
	if (vFootStr.bstrVal == NULL) {
		goto cleanup;
	}

	// Argument 3: IStream containing header text. Outlook and Outlook
         // Express use this to print out the mail header.
	if ((sMem = (LPSTR)CoTaskMemAlloc(512)) == NULL) {
		goto cleanup;
	}
	// We must pass in a full HTML file here, otherwise this
         // becomes corrupted when we print.
	sprintf_s(sMem, 512, "<html><body><strong>Printed By:</strong>
	 Custom WebBrowser Host 1.0<p></body></html>");

	// Allocate an IStream for the LPSTR that we just created.
	hG = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, strlen(sMem));
	if (hG == NULL) {
		goto cleanup;
	}
	hr = CreateStreamOnHGlobal(hG, TRUE, &pStream);
	if (FAILED(hr)) {
		//ATLTRACE(_T("OnPrint::Failed to create stream from HGlobal: %lXn"), hr);
		goto cleanup;
	}
	hr = pStream->Write(sMem, strlen(sMem), &lWrote);
	if (SUCCEEDED(hr)) {
	    // Set the stream back to its starting position.
		LARGE_INTEGER pos;
		pos.QuadPart = 0;
		pStream->Seek((LARGE_INTEGER)pos, STREAM_SEEK_SET, NULL);
		hr = pStream->QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&pUnk));
		vHeadTxtStream.vt = VT_UNKNOWN;
		vHeadTxtStream.punkVal = pUnk;
	}

	rgIndices = 0;
	SafeArrayPutElement(psaHeadFoot, &rgIndices, static_cast<void *>(vHeadStr));
	rgIndices = 1;
	SafeArrayPutElement(psaHeadFoot, &rgIndices, static_cast<void *>(&vFootStr));
	rgIndices = 2;
	SafeArrayPutElement(psaHeadFoot, &rgIndices, static_cast<void *>(&vHeadTxtStream));

	//NOTE: Currently, the SAFEARRAY variant must be passed by using
	// the VT_BYREF vartype when you call the ExecWeb method.
	VARIANT vArg;
	VariantInit(&vArg);
	vArg.vt = VT_ARRAY | VT_BYREF;
	vArg.parray = psaHeadFoot;

	//OLECMDEXECOPT_PROMPTUSER
	hr = webOC->ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER, &vArg, NULL);

	if (FAILED(hr)) {
		//ATLTRACE(_T("DoPrint: Call to WebBrowser's ExecWB failed: %lXn"), hr);
		goto cleanup;
	}
	return 1;
	//WebBrowser control will clean up the SAFEARRAY after printing.
	cleanup:
	VariantClear(&vHeadStr);
	VariantClear(&vFootStr);
	VariantClear(&vHeadTxtStream);
	if (psaHeadFoot) {
		SafeArrayDestroy(psaHeadFoot);
	}
	if (sMem) {
		CoTaskMemFree(sMem);
	}
	if (hG != NULL) {
		GlobalFree(hG);
	}
	if (pStream != NULL) {
		pStream->Release();
		pStream = NULL;
	}
	//bHandled = TRUE;
	return 0;
}
#pragma managed

	void PrintHelper::Print(IntPtr^ ptrIWebBrowser2, String^ header,  String^ footer)
	{
		IWebBrowser2* pBrowser = (IWebBrowser2 *)ptrIWebBrowser2->ToPointer();

		IDispatch *pDisp;
		pBrowser->get_Document(&pDisp);

		IHTMLDocument2 *pDoc;
		pDisp->QueryInterface<ihtmldocument2>(&pDoc);

		IHTMLElement *body;
		pDoc->get_body(&body);

		BSTR p;
		body->get_innerHTML(&p);

		IntPtr pHeader = Runtime::InteropServices::Marshal::StringToBSTR(header);
		IntPtr pFooter = Runtime::InteropServices::Marshal::StringToBSTR(footer);

		UnmanagedPrint(pBrowser, (BSTR)pHeader.ToPointer(), (BSTR)pFooter.ToPointer());
	}
}

3.
Now we need to generate C# version of the IWebBrowser2 COM interface:
It can be generated from idl -> tlb -> dll and the referenced from the WindowsForms app.

midl ExDisp.Idl /tlb ExDisp.tlb
pause
tlbimp ExDisp.tlb /out:ExDisp.dll
pause

4.
Then we create WindowsForms project, and create custom control inheriting
from WebBrowser control.
We need this to access IWebBrowser2 interface (defined in ExDisp.dll):

using ExDisp;
using WebBrowser = System.Windows.Forms.WebBrowser;

namespace WindowsFormsApplication1.MyBrowser
{
    public class MyWebBrowser : WebBrowser
    {
        public IWebBrowser2 axIWebBrowser2;

        protected override void AttachInterfaces(object nativeActiveXObject)
        {
            base.AttachInterfaces(nativeActiveXObject);
            this.axIWebBrowser2 = (IWebBrowser2) nativeActiveXObject;
        }

        protected override void DetachInterfaces()
        {
            base.DetachInterfaces();
            this.axIWebBrowser2 = null;
        }
    };
}

5.
Finally we create a Form and add the printing code there:

private void _btnPrint_Click(object sender, EventArgs e)
{
    IntPtr ptr = Marshal.GetComInterfaceForObject(
        webBrowser1.axIWebBrowser2,
        typeof(IWebBrowser2));
    PrintHelper.Print(ptr, "this is my header", "this is my footer");
}

Modified the header and footer:
NiceFooter

The Zip:
IEPrinting

References:
http://support.microsoft.com/kb/267240
http://thedotnet.com/nntp/97691/showpost.aspx

How to test email sending?

Friday, September 25th, 2009

Nearly every web application these days sends some notifications to its users.

Many times before, I came across people who said that it’s not possible to test such things.
Well, they are wrong!

There is a very cool project on sourceforge called nDumbster:
http://ndumbster.sourceforge.net

nDumbster is a simple fake SMTP server designed especially to enable unit testing.

[TestFixture]
public class SmtpClientTest
{
    private const int _port = 25;
    private SimpleSmtpServer _smtpServer;

    [SetUp]
    public void SetUp()
    {
        _smtpServer = SimpleSmtpServer.Start(_port);
    }

    [TearDown]
    public void TearDown()
    {
        _smtpServer.Stop();
    }

    [Test]
    public void SendMessage_SendsMessage()
    {
        Mail.Text("Some tex")
            .Subject("Some subject")
            .From(new MailBox("alice@mail.com", "Alice"))
            .To(new MailBox("bob@mail.com", "Bob"))
            .UsingNewSmtp()
            .Server("localhost")
            .OnPort(_port)
            .Send();

        Assert.AreEqual(1, _smtpServer.ReceivedEmailCount);
        SmtpMessage mail = _smtpServer.ReceivedEmail[0];
        Assert.AreEqual(""Alice" <alice@mail.com>", mail.Headers["From"]);
        Assert.AreEqual(""Bob" <bob@mail.com>", mail.Headers["To"]);
        Assert.AreEqual("Some subject", mail.Headers["Subject"]);
        Assert.AreEqual("Some text", mail.Body);
    }
};