Recipe12.7.Generating Test-Entry Web CGI


Recipe 12.7. Generating Test-Entry Web CGI

Problem

You want to test a process in isolation by processing text data received from a form and converting that data into a physical message that can be sent to the process under test.

Solution

This server generator goes along with the client generator created in Recipe 12.6. This section shows only a piece of the solution, but the discussion gives further detail.

The generated server is a C++ CGI program. You need it to be C++ (or C) because the form input will be converted into a binary message that is laid out according to the C memory model. It assumes the C++ processes that ultimately consume these messages expect them as binary data. The generated CGI uses Enterprise Integration Technologies' libcgi (http://www.landfield.com/hypermail/source/libcgi/) to simplify the common CGI tasks such as query-string parsing:

<!DOCTYPE xslt [   <!--Used to control code intenting -->   <!ENTITY INDENT "    "> ]> <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">       <xsl:output method="text"/>   <xsl:strip-space elements="*"/>       <!--The message to generate data for. '*' for all -->   <xsl:param name="message" select=" '*' "/>   <!--The directory to generate code -->   <xsl:param name="generationDir" select=" 'src/' "/>   <!--The C++ header extension to use -->   <xsl:param name="headerExt" select=" '.h' "/>   <!--The C++ source extension to use -->   <xsl:param name="sourceExt" select=" '.C' "/>       <!--Key to locate data types by name -->   <xsl:key name="dataTypes" match="Structure" use="Name" />    <xsl:key name="dataTypes" match="Primitive" use="Name" />    <xsl:key name="dataTypes" match="Array" use="Name" />    <xsl:key name="dataTypes" match="Enumeration" use="Name" />           <!-- Top-level template determines which messages to process -->   <xsl:template match="/">      <xsl:choose>         <xsl:when test="$message = '*'">           <xsl:apply-templates select="*/Messages/*"/>         </xsl:when>         <xsl:when test="*/Messages/Message[Name=$message]">           <xsl:apply-templates select="*/Messages/Message[Name=$message]"/>         </xsl:when>         <xsl:otherwise>           <xsl:message terminate="yes">No such message name            [<xsl:value-of select="$message"/>]</xsl:message>         </xsl:otherwise>       </xsl:choose>   </xsl:template>     <!-- If the messages data type is contained in the repository, then generate data  wrapper header and source file for it --> <xsl:template match="Message">   <xsl:choose>     <xsl:when test="key('dataTypes',DataTypeName)">       <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="source">         <xsl:with-param name="msg" select="Name"/>       </xsl:apply-templates>     </xsl:when>     <xsl:otherwise>             <xsl:message>Message name [<xsl:value-of select="Name"/>] uses data             [<xsl:value-of select="DataTypeName"/>] that is not defined in the             repository.</xsl:message>     </xsl:otherwise>   </xsl:choose> </xsl:template>     <!-- We only generate source if a messages data type is a Structure. --> <!-- The only other typical message data type is XML. We don't          --> <!-- generate wrappers for XML payloads.                                          --> <xsl:template match="Structure" mode="source">   <xsl:param name="msg"/>       <xsl:document href="{concat($generationDir,Name,'CGI',$sourceExt)}">     <xsl:text> #include &lt;stdio.h&gt; #include "cgi.h" #include "</xsl:text> <xsl:value-of select="Name"/><xsl:value-of select="$headerExt"/> <xsl:text>"     void cgi_main(cgi_info *cgi) {     </xsl:text>     <xsl:value-of select="Name"/>      <xsl:text> data ;     form_entry* form_data = get_form_entries(cgi) ;    </xsl:text>      <xsl:for-each select="Members/Member">     <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="variables">       <xsl:with-param name="field" select="concat($msg,'_',Name)"/>       <xsl:with-param name="var" select="Name"/>     </xsl:apply-templates>    </xsl:for-each>       <xsl:for-each select="Members/Member">     <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="load">       <xsl:with-param name="field" select="concat('data.',Name)"/>       <xsl:with-param name="var" select="Name"/>     </xsl:apply-templates>   </xsl:for-each>     <xsl:text> &INDENT;//Enque data to the process being tested  &INDENT;enqueData(data) ;     }</xsl:text>    </xsl:document> </xsl:template>     <!-- Declare and initialize variables for each field --> <xsl:template match="Structure" mode="variables">   <xsl:param name="field"/>   <xsl:param name="var"/>       <xsl:for-each select="Members/Member">             <xsl:apply-templates select="key('dataTypes',DataTypeName)"                 mode="variables">               <xsl:with-param name="field" select="concat($field,'_',Name)"/>               <xsl:with-param name="var" select="$var"/>             </xsl:apply-templates>      </xsl:for-each> </xsl:template>     <xsl:template match="*" mode="variables">   <xsl:param name="field"/>   <xsl:param name="var"/>      <xsl:text>&INDENT;const char * </xsl:text>   <xsl:value-of select="$var"/>   <xsl:text> = parmval(form_data, "</xsl:text>   <xsl:value-of select="$field"/>   <xsl:text>");&#xa;</xsl:text> </xsl:template>     <!-- Initialize data form the converted value --> <xsl:template match="Structure" mode="load">   <xsl:param name="field"/>   <xsl:param name="var"/>   <xsl:for-each select="Members/Member">         <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="load">           <xsl:with-param name="field" select="concat($field,'.',Name)"/>           <xsl:with-param name="var" select="concat($field,'_',Name)"/>         </xsl:apply-templates>   </xsl:for-each> </xsl:template>     <xsl:template match="Primitive" mode="load">   <xsl:param name="field"/>   <xsl:param name="var"/>     <xsl:text>&INDENT;</xsl:text>   <xsl:value-of select="$field"/>     <xsl:text> = </xsl:text>   <xsl:value-of select="Name"/>   <xsl:text>(</xsl:text>   <xsl:value-of select="$var"/>   <xsl:text>);&#xa;</xsl:text> </xsl:template>    <xsl:template match="Enumeration" mode="load">  <xsl:param name="field"/>   <xsl:param name="var"/>     <xsl:text>&INDENT;</xsl:text>   <xsl:value-of select="$field"/>     <xsl:text> = Enum</xsl:text>   <xsl:value-of select="Name"/>   <xsl:text>NameToVal(</xsl:text>   <xsl:value-of select="$var"/>   <xsl:text>");&#xa;</xsl:text> </xsl:template>     </xsl:stylesheet>

If run against our repository, this XSLT transformation generates a cgi program for each message. For example:

#include <stdio.h> #include "cgi.h" #include "msg_ids.h" #include "AddStockOrderData.h"     void cgi_main(cgi_info *cgi) {     AddStockOrderData data ;     form_entry* form_data = get_form_entries(cgi) ;        const char * symbol = parmval(form_data, "ADD_STOCK_ORDER_symbol");     const char * quantity = parmval(form_data, "ADD_STOCK_ORDER_quantity");     const char * side = parmval(form_data, "ADD_STOCK_ORDER_side");     const char * type = parmval(form_data, "ADD_STOCK_ORDER_type");     const char * price = parmval(form_data, "ADD_STOCK_ORDER_price");     data.symbol = StkSymbol(symbol);     data.quantity = Shares(quantity);     data.side = EnumBuyOrSellNameToVal(side);     data.type = EnumOrderTypeNameToVal(type);     data.price = Real(price);         //Enque data to the process being tested      enqueData(ADD_STOCK_ORDER,data) ;     }

Discussion

The solution makes several assumptions. First, it assumes that the primitive types (e.g., Shares) are classes with constructors that convert from strings to the type's internal representation (e.g., int). Second, it assumes the existence of conversion functions for converting symbolic names of enumeration constants to their integral values. As you might guess, these functions can be generated easily from information in the repository. We leave that as an exercise. Third, the generated code assumes a function, called enqueData, that knows how to send a message to the correct process under test. The details of how this function interacts with the specific middleware and how it locates the particular queue vary from implementation to implementation.

The bottom line is that you have to tweak this code generator to suit your needs. However, once a sufficiently rich metadata repository is established, a significant amount of automation is possible. The functionality automated in this example and Recipe 12.6 could easily keep a programmer busy full time. This is especially true if the messages the application handles are in constant flux.




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net