Главная » XSLT » Генерация CGI-сценария для обработки тестовых данных

0

Задача

Требуется автономно протестировать процесс путем обработки введенных в форму данных и преобразования их в физическое сообщение, которое можно послать тестируемому процессу.

Решение

Этот генератор сервера является дополнением к генератору клиента из рецеп­та 12.6. Сейчас мы приведем лишь часть решения, а детали отложим до раздела «Обсуждение».

Сгенерированный сервер – это CGI-программа на языке C++. Так приходит­ся поступать, потому что введенные в форму данные преобразуются в двоичное сообщение в формате, определяемом моделью памяти, характерной для C. Предпо­лагается, что написанные на C++ процессы, потребляющие сообщения, ожидают именно двоичных данных. В сгенерированной программе используется библиотека libcgi компании Enterprise Integration Technologies (http://www.landfield.com/ hypermail/source/libcgi/), которая решает типичные для CGI-программ задачи, к примеру, разбор строки запроса.

<!DOCTYPE xslt [

<!– Служит для управления отступами –> <!ENTITY INDENT "   ">

]>

<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="text"/> <xsl:strip-space elements="*"/>

<!– Для какого сообщения генерировать данные. ‘*’ – для всех –> <xsl:param name="message" select=" ‘*’ "/> <!– Каталог, в котором хранятся сгенерированные файлы —> <xsl:param name="generationDir" select=" ‘src/’ "/> <!– Расширение заголовочных файлов C++ –> <xsl:param name="headerExt" select=" ‘.h’ "/> <!– Расширение исходных файлов C++ –> <xsl:param name="sourceExt" select=" ‘.C’ "/>

<!– Ключ для поиска типов данных по имени –> <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" />

<!— Шаблон верхнего уровня определяет, какие сообщения обрабатывать –> <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>

<!— Если тип данных, представленных сообщением, хранится в репозитории, –>

<!– сгенерировать заголовочный и исходный файл для его обертки –> <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>

<!– Исходные файлы генерируются только, если тип данных в сообщении – Structure. Возможен также тип данных XML. Если полезная нагрузка представлена в формате XML, обертка не генерируется. –> <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;//Поместить данные в очередь к тестируемому процессу &INDENT;enqueData(data) ;

}</xsl:text>

</xsl:document>

</xsl:template>

<!– Объявляем и инициализируем переменные для каждого поля –> <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>

<!– Инициализируем данные для преобразованного значения –> <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>

Если применить это преобразование к нашему репозиторию, будут сгенериро­ваны CGI-программы для каждого сообщения. Например:

#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);

// Поместить данные в очередь к тестируемому процессу enqueData(ADD_STOCK_ORDER,data) ;

}

Обсуждение

В этом решении сделано несколько допущений. Во-первых, предполагается, что примитивные типы (например, Shares) – это классы, в которых имеется кон­структор, преобразующий строку во внутреннее представление (например, int). Во-вторых, предполагается, что существуют функции-конверторы символи­ческих имен перечисляемых констант в их целочисленное представление. Как вы, наверное, догадались, такие функции легко сгенерировать по информации, хра­нящейся в репозитории, оставляем эту задачу в качестве упражнения. В-третьих, предполагается, что существует функция enqueData, которая знает, как посы­лать сообщение нужному процессу. Детали того, как эта функция взаимодейству­ет с конкретным промежуточным слоем и находит подходящую очередь, зависят от реализации.

В общем, этот генератор еще следует «заточить» под собственные нужды. Од­нако, если в репозитории хранится достаточно информации, то автоматизировать можно многое. Без той автоматизации тестов, которая приведена в этом рецепте и в рецепте 12.6, программисту пришлось бы трудиться не покладая рук, особенно если состав и структура сообщений, обрабатываемых приложением, постоянно изменяются.

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

  • Комментарии