Develop a custom comparer - Tutorial

Relevant for: GUI tests and components

This tutorial walks you step-by-step through the process of creating a custom comparer in C++ using Microsoft Visual Studio. The custom comparer you create is similar to the sample custom comparer provided with UFT. You can create your own custom comparers in a similar way. For details about the sample custom comparer, see Use the bitmap checkpoint custom comparer samples.

Note: For a task related to this tutorial, see Develop a custom comparer.

By following the instructions in this section, you create a COM object that:

  • Implements the CompareBitmaps method to receive two bitmaps to compare and a configuration string, compare the (size of) the two bitmaps, and return the necessary results.

  • Implements the GetDefaultConfigurationString method and the GetHelpFilename method, to return the information that UFT displays in the advanced settings in the Bitmap Checkpoint Properties dialog box.

  • Registers to the component category for UFT bitmap comparers.

When the design of your custom comparer is complete, you can install and register it and use it in UFT to run a bitmap checkpoint.

Note: Depending on the version of Microsoft Visual Studio that you use to perform the tutorial, the command names may be different.

Create a new ATL project—SampleCPPCustomComparer

  1. In Microsoft Visual Studio, select New > Project. The New Project dialog box opens.

  2. Select the ATL Project template, enter SampleCPPCustomComparer in the Name box for the project, and click OK. The New ATL Project wizard opens.

  3. In Application Settings, make sure that the Attributed option is not selected, and click Finish.

Back to top

Create a new class—CBitmapComparer

  1. In the class view, select the SampleCPPCustomComparer project, right-click, and select Add > Class. The Add Class dialog box opens.

  2. Select ATL Simple Object and click Add. The ATL Simple Object Wizard opens.

  3. In the Short name box, enter BitmapComparer. The wizard uses this name to create the names of the class, the interface, and the files that it creates.

  4. In the Type box, enter Sample Custom Comparer. This is the custom comparer name that UFT will display in the advanced settings in the Bitmap Checkpoint Properties dialog box and in the run results. For details, see Set the Custom Comparer Name - Optional.

  5. Click Finish. The wizard creates the necessary files for the class that you added, including .cpp and .h files with implementation of CBitmapComparer class.

Back to top

Implement the comparer interfaces for the CBitmap Comparer class

  1. In the class view, select CBitmapComparer, right-click, and select Add > Implement Interface. The Implement Interface wizard opens.

  2. In the Implement interface from option, select File. Browse to or enter the location of the UFT bitmap checkpoint comparer type library. The type library is located in: <UFT installation folder>\dat\BitmapCPCustomization\BitmapComparer.tlb.

    The wizard displays the interfaces available in the selected type library, IBitmapCompareConfiguration and IVerifyBitmap.

  3. Add both interfaces to the list of interfaces to implement, and click Finish.

    In the BitmapComparer.h file, the wizard adds the declarations, classes, and method stubs that are necessary to implement the interfaces. In subsequent steps you will need to add implementation to these method stubs.

    Note: In Microsoft Visual Studio 2005, the wizard generates the signature for the CompareBitmaps method in the IVerifyBitmap interface incorrectly. To enable your project to compile correctly, manually change the type of the last argument (pbMatch) from BOOL* to VARIANT_BOOL*.

Move the function bodies for the comparer interface methods

  1. Open the BitmapComparer.h and BitmapComparer.cpp files.

  2. In BitmapComparer.h, create declarations for the bitmap checkpoint comparer interface methods (based on the function bodies that the wizard created): CompareBitmaps, GetDefaultConfigurationString, and GetHelpFilename.

  3. Move the function bodies that the wizard created for the bitmap checkpoint comparer interface methods from the BitmapComparer.h file to the BitmapComparer.cpp file.

    At the end of this step, BitmapComparer.cpp and BitmapComparer.h should contain the following code:

    // BitmapComparer.cpp : Implementation of CBitmapComparer
    #include "stdafx.h"
    #include "BitmapComparer.h"
    // CBitmapComparer
    // IBitmapCompareConfiguration Methods
    STDMETHODIMP CBitmapComparer::GetDefaultConfigurationString
                                            (BSTR * pbstrConfiguration)
    {
                  return E_NOTIMPL;
    }
    STDMETHODIMP CBitmapComparer::GetHelpFilename(BSTR * pbstrFilename)
    {
                  return E_NOTIMPL;
    }
    // IVerifyBitmap Methods
    STDMETHODIMP CBitmapComparer::CompareBitmaps
                            (IPictureDisp * pExpected, IPictureDisp * pActual, 
                            BSTR bstrConfiguration, BSTR * pbstrLog, 
                            IPictureDisp * * ppDiff, VARIANT_BOOL * pbMatch)
    {
                  return E_NOTIMPL;
    }
    
    // BitmapComparer.h : Declaration of the CBitmapComparer
    #pragma once
    #include "resource.h"       // main symbols
    #include "SampleCPPCustomComparer.h"
    // CBitmapComparer
    class ATL_NO_VTABLE CBitmapComparer :
            public CComObjectRootEx<CComSingleThreadModel>,
            public CComCoClass<CBitmapComparer, &CLSID_BitmapComparer>,
            public IDispatchImpl<IBitmapComparer, &IID_IBitmapComparer, 
                        &LIBID_SampleCustomComparerLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
            public IDispatchImpl<IBitmapCompareConfiguration, 
                        &__uuidof(IBitmapCompareConfiguration),
                        &LIBID_BitmapComparerLib, /* wMajor = */ 1, /*wMinor =*/ 0>,
            public IDispatchImpl<IVerifyBitmap, &__uuidof(IVerifyBitmap), 
                        &LIBID_BitmapComparerLib, /* wMajor = */ 1, /*wMinor =*/ 0>
    {
        public:
            CBitmapComparer()
            {
            }
            DECLARE_REGISTRY_RESOURCEID(IDR_BITMAPCOMPARER)
            BEGIN_COM_MAP(CBitmapComparer)
                COM_INTERFACE_ENTRY(IBitmapComparer)
                COM_INTERFACE_ENTRY2(IDispatch, IBitmapCompareConfiguration)
                COM_INTERFACE_ENTRY(IBitmapCompareConfiguration)
                COM_INTERFACE_ENTRY(IVerifyBitmap)
            END_COM_MAP()
            DECLARE_PROTECT_FINAL_CONSTRUCT()
            HRESULT FinalConstruct()
            {
                return S_OK;
            }
            void FinalRelease() {}
            // IBitmapCompareConfiguration Methods
    public:
            STDMETHOD(GetDefaultConfigurationString)(BSTR * pbstrConfiguration);
            STDMETHOD(GetHelpFilename)(BSTR * pbstrFilename);
            // IVerifyBitmap Methods
    public:
            STDMETHOD(CompareBitmaps)(IPictureDisp * pExpected,
                         IPictureDisp * pActual, BSTR bstrConfiguration, BSTR * pbstrLog,
                        IPictureDisp * * ppDiff, VARIANT_BOOL * pbMatch);
    };
    OBJECT_ENTRY_AUTO(__uuidof(BitmapComparer), CBitmapComparer)
    

Back to top

Implement the bitmap checkpoint comparer interface methods

In this tutorial, you implement a custom comparer similar to the sample custom comparer provided with UFT. For details about the sample custom comparer, see Use the bitmap checkpoint custom comparer samples.

When you create your own custom comparers, this is the step during which you design the custom comparer logic. You define the configuration input that it can receive, the algorithm that it uses to compare the bitmaps, and the output that it provides.

In the BitmapComparer.cpp file, add #include <atlstr.h>, and implement the bitmap checkpoint comparer interface methods as follows:

  • The GetDefaultConfigurationString method:

    STDMETHODIMP CBitmapComparer::GetDefaultConfigurationString
                                            (BSTR * pbstrConfiguration)
    {
            CComBSTR bsConfig("MaxSurfAreaDiff=140000");
            *pbstrConfiguration = bsConfig.Detach();
            return S_OK;
    }
  • The GetHelpFilename method:

    STDMETHODIMP CBitmapComparer::GetHelpFilename(BSTR * pbstrFilename)
    {
            CComBSTR bsFilename ("..\\samples\\BitmapCPSample\\CPPCustomComparer\\SampleComparerDetails.txt");
            *pbstrFilename = bsFilename.Detach();
            return S_OK;
    }
    

    Note: When the GetHelpFilename method returns a relative path, UFT searches for this path relative to <UFT installation folder>\bin. The implementation above instructs UFT to use the documentation file provided with the CPP sample custom comparer.

  • The CompareBitmaps method:

    STDMETHODIMP CBitmapComparer::CompareBitmaps
                                        (IPictureDisp * pExpected, IPictureDisp * pActual, 
                                        BSTR bstrConfiguration, BSTR * pbstrLog,
                                        IPictureDisp * * ppDiff, VARIANT_BOOL * pbMatch)
    {
            HRESULT hr = S_OK;
            if (!pExpected || !pActual)
                return S_FALSE;
            CComQIPtr<IPicture> picExp(pExpected);
            CComQIPtr<IPicture> picAct(pActual);
            // Try to get HBITMAP from IPicture
            HBITMAP HbmpExp, HbmpAct;
            hr = picExp->get_Handle((OLE_HANDLE*)&HbmpExp);
            if (FAILED(hr))
                return hr;
            hr = picAct->get_Handle((OLE_HANDLE*)&HbmpAct);
            if (FAILED(hr))
                return hr;
            BITMAP ExpBmp = {0};
            if( !GetObject(HbmpExp, sizeof(ExpBmp), &ExpBmp) )
                return E_FAIL;
            BITMAP ActBmp = {0};
            if( !GetObject(HbmpAct, sizeof(ActBmp), &ActBmp) )
                return E_FAIL;
            CString s, tol;
            tol = bstrConfiguration;
            int EPos = tol.ReverseFind('=);
            tol = tol.Right(tol.GetLength() - EPos - 1);
            int maxSurfaceAreaDiff = _ttoi(tol);
            // Set output parameters
            CComPtr<IPictureDisp> Diff(pActual);
            *ppDiff = Diff;
            int DiffPixelsNumber = abs (ExpBmp.bmHeight * ExpBmp.bmWidth - ActBmp.bmHeight * ActBmp.bmWidth);
            *pbMatch = DiffPixelsNumber <= maxSurfaceAreaDiff;
            s.Format(_T("The number of different pixels is: %d."), DiffPixelsNumber);
            CComBSTR bs (s);
            *pbstrLog = bs.Detach();
            return hr;
    }

Back to top

Design your custom comparer to register to the component category

For UFT to recognize the COM object that you create as a custom comparer, you must register it to the component category for UFT bitmap comparers. The component category ID is defined in <UFT installation folder>\dat\BitmapCPCustomization\ComponentCategory.h.

You can implement this registration in the DllRegisterServer and DllUnregisterServer methods in the SampleCPPCustomComparer.cpp file that the wizard created as part of your project. These methods are called when you run a DLL using the regsvr32.exe program.

  1. Add the <UFT installation folder>\dat\BitmapCPCustomization folder to your project's include path.

  2. Open the SampleCPPCustomComparer.cpp file and add the following line: #include "ComponentCategory.h"

  3. In the SampleCPPCustomComparer.cpp file, modify the DllRegisterServer and DllUnregisterServer methods created by the wizard, to contain the following code:

    STDAPI DllRegisterServer(void)
    {
             // registers object, typelib and all interfaces in typelib
             HRESULT hr = _AtlModule.DllRegisterServer();
            CComPtr<ICatRegister> spReg;
            hr = spReg.CoCreateInstance
                    (CLSID_StdComponentCategoriesMgr, 0, CLSCTX_INPROC);
            if (FAILED(hr))
                return hr;
            // register comparer to the UFT bitmap comparers category 
            CATID catid = CATID_QTPBitmapComparers;
            hr = spReg->RegisterClassImplCategories(CLSID_BitmapComparer, 1, &catid);
            return hr;
    }
    
    STDAPI DllUnregisterServer(void)
    {
            HRESULT hr = _AtlModule.DllUnregisterServer();
            CComPtr<ICatRegister> spReg;
            hr = spReg.CoCreateInstance
                    (CLSID_StdComponentCategoriesMgr, 0, CLSCTX_INPROC);
            if (FAILED(hr))
                return hr;
            // unregister comparer from the UFT bitmap comparers category
            CATID catid = CATID_QTPBitmapComparers;
            hr = spReg->UnRegisterClassImplCategories(CLSID_BitmapComparer, 1, &catid);
            return hr;
    }

    Note the second section in these methods, that handles registration to the component category for UFT bitmap comparers—CATID_QTPBitmapComparers.

Back to top

Compile and run your DLL

Use regsvr32.exe to register your DLL after it is compiled.Your custom comparer can now be used in UFT for bitmap checkpoints.

Back to top

Test your custom comparer

  1. Open UFT and create a bitmap checkpoint on the Windows Calculator application (Standard view).

    The advanced settings in the Bitmap Checkpoint Properties dialog box include the Comparer option, in which you can select the default UFT comparer or your sample custom comparer.

  2. Change the Calculator view to Scientific. The size of the calculator object is now larger. Run the checkpoint using the default UFT comparer. The checkpoint fails.

  3. Edit the checkpoint in the Bitmap Checkpoint Properties dialog box:

    • Make sure the Compare selection with runtime bitmap checkpoint mode is selected.

    • Click Advanced settings to open the Advanced Settings dialog box, and select Sample Custom Comparer in the Comparer Type box.

      In the Input box, you can see the default configuration string returned by the GetDefaultConfigurationString method: MaxSurfAreaDiff=140000

      In the Input box, you can see the default configuration string returned by the GetDefaultConfigurationString method: MaxSurfAreaDiff=140000

      If you click Details, the text file containing documentation for the sample custom comparer opens.

    The comparer you designed in this exercise checks how different the expected and actual bitmaps are in size, and fails the checkpoint if the difference is greater than the number of pixels defined in the configuration string.

    If you run the checkpoint using default MaxSurfAreaDiff value, the checkpoint passes, because the difference in the total size of the calculator object when it is set to different views is less than 140000 pixels (the difference is approximately 80000 pixels). If you set MaxSurfAreaDiff to 70000, the checkpoint fails.

    View the run results to see the text string and difference bitmap that your custom comparer provides to UFT after the comparison.

    Back to top