/*==================================================================*\
| EXIP - Embeddable EXI Processor in C |
|--------------------------------------------------------------------|
| This work is licensed under BSD 3-Clause License |
| The full license terms and conditions are located in LICENSE.txt |
\===================================================================*/
/**
* @file check_strict_grammar.c
* @brief Tests decoding documents using strict grammar
*
* @date Mar 29, 2012
* @author Ken Bannister
* @author Rumen Kyusakov
* @version 0.5
* @par[Revision] $Id: check_strict_grammar.c 328 2013-10-30 16:00:10Z kjussakov $
*/
#include <stdio.h>
#include <stdlib.h>
#include <check.h>
#include "EXISerializer.h"
#include "EXIParser.h"
#include "stringManipulate.h"
#include "grammarGenerator.h"
#include "memManagement.h"
#define INPUT_BUFFER_SIZE 200
#define OUTPUT_BUFFER_SIZE 200
#define MAX_PATH_LEN 200
/* Location for external test data */
static char *dataDir;
struct appData
{
unsigned int eventCount;
unsigned short expectAttributeData;
AllocList allocList;
String eventCode;
String uri;
String localName;
};
typedef struct appData appData;
/* Helper functions */
size_t readFileInputStream(void* buf, size_t readSize, void* stream)
{
FILE *infile = (FILE*) stream;
return fread(buf, 1, readSize, infile);
}
size_t writeFileOutputStream(void* buf, size_t readSize, void* stream)
{
FILE *outfile = (FILE*) stream;
return fwrite(buf, 1, readSize, outfile);
}
static void parseSchema(const char* fileName, EXIPSchema* schema)
{
FILE *schemaFile;
BinaryBuffer buffer;
errorCode tmp_err_code = EXIP_UNEXPECTED_ERROR;
size_t pathlen = strlen(dataDir);
char exipath[MAX_PATH_LEN + strlen(fileName)];
memcpy(exipath, dataDir, pathlen);
exipath[pathlen] = '/';
memcpy(&exipath[pathlen+1], fileName, strlen(fileName)+1);
schemaFile = fopen(exipath, "rb" );
if(!schemaFile)
{
fail("Unable to open file %s", exipath);
}
else
{
//Get file length
fseek(schemaFile, 0, SEEK_END);
buffer.bufLen = ftell(schemaFile) + 1;
fseek(schemaFile, 0, SEEK_SET);
//Allocate memory
buffer.buf = (char *)malloc(buffer.bufLen);
if (!buffer.buf)
{
fclose(schemaFile);
fail("Memory allocation error!");
}
//Read file contents into buffer
fread(buffer.buf, buffer.bufLen, 1, schemaFile);
fclose(schemaFile);
buffer.bufContent = buffer.bufLen;
buffer.ioStrm.readWriteToStream = NULL;
buffer.ioStrm.stream = NULL;
tmp_err_code = generateSchemaInformedGrammars(&buffer, 1, SCHEMA_FORMAT_XSD_EXI, NULL, schema, NULL);
if(tmp_err_code != EXIP_OK)
{
fail("\n Error reading schema: %d", tmp_err_code);
}
free(buffer.buf);
}
}
/* Document callbacks */
static errorCode sample_fatalError(const errorCode code, const char* msg, void* app_data)
{
printf("\n%3d : FATAL ERROR: %s\n", code, msg);
return EXIP_HANDLER_STOP;
}
static errorCode sample_startDocument(void* app_data)
{
appData* appD = (appData*) app_data;
asciiToString("SD", &appD->eventCode, &appD->allocList, TRUE);
return EXIP_OK;
}
static errorCode sample_endDocument(void* app_data)
{
appData* appD = (appData*) app_data;
asciiToString("ED", &appD->eventCode, &appD->allocList, TRUE);
return EXIP_OK;
}
static errorCode sample_startElement(QName qname, void* app_data)
{
appData* appD = (appData*) app_data;
asciiToString("SE", &appD->eventCode, &appD->allocList, TRUE);
cloneStringManaged(qname.uri, &appD->uri, &appD->allocList);
cloneStringManaged(qname.localName, &appD->localName, &appD->allocList);
return EXIP_OK;
}
static errorCode sample_endElement(void* app_data)
{
appData* appD = (appData*) app_data;
asciiToString("EE", &appD->eventCode, &appD->allocList, TRUE);
return EXIP_OK;
}
static errorCode sample_attribute(QName qname, void* app_data)
{
appData* appD = (appData*) app_data;
asciiToString("AT", &appD->eventCode, &appD->allocList, TRUE);
/*
printString(qname.uri);
printf(" ");
printString(qname.localName);
printf("=\"");
*/
appD->expectAttributeData = 1;
return EXIP_OK;
}
static errorCode sample_stringData(const String value, void* app_data)
{
appData* appD = (appData*) app_data;
if(appD->expectAttributeData)
{
asciiToString("AT", &appD->eventCode, &appD->allocList, TRUE);
/*
printString(&value);
printf("\"\n");
*/
appD->expectAttributeData = 0;
}
else
{
asciiToString("CH", &appD->eventCode, &appD->allocList, TRUE);
/*
printString(&value);
printf("\n");
*/
}
return EXIP_OK;
}
static errorCode sample_decimalData(Decimal value, void* app_data)
{
appData* appD = (appData*) app_data;
if(appD->expectAttributeData)
{
asciiToString("AT", &appD->eventCode, &appD->allocList, TRUE);
/*
printf("%.1f\"\n", (double) value);
*/
appD->expectAttributeData = 0;
}
else
{
asciiToString("CH", &appD->eventCode, &appD->allocList, TRUE);
}
return EXIP_OK;
}
static errorCode sample_intData(Integer int_val, void* app_data)
{
appData* appD = (appData*) app_data;
/* char tmp_buf[30]; */
if(appD->expectAttributeData)
{
asciiToString("AT", &appD->eventCode, &appD->allocList, TRUE);
/*
sprintf(tmp_buf, "%lld", int_val);
printf("%s", tmp_buf);
printf("\"\n");
*/
appD->expectAttributeData = 0;
}
else
{
asciiToString("CH", &appD->eventCode, &appD->allocList, TRUE);
/*
sprintf(tmp_buf, "%lld", int_val);
printf("%s", tmp_buf);
printf("\n");
*/
}
return EXIP_OK;
}
static errorCode sample_floatData(Float fl_val, void* app_data)
{
appData* appD = (appData*) app_data;
/* char tmp_buf[30]; */
if(appD->expectAttributeData)
{
asciiToString("AT", &appD->eventCode, &appD->allocList, TRUE);
/*
sprintf(tmp_buf, "%lldE%d", fl_val.mantissa, fl_val.exponent);
printf("%s", tmp_buf);
printf("\"\n");
*/
appD->expectAttributeData = 0;
}
else
{
asciiToString("CH", &appD->eventCode, &appD->allocList, TRUE);
/*
printf("%3d CH ", appD->eventCount);
sprintf(tmp_buf, "%lldE%d", fl_val.mantissa, fl_val.exponent);
printf("%s", tmp_buf);
printf("\n");
*/
}
return EXIP_OK;
}
/* Tests */
/* Compares document structure, including minOccurs/maxOccurs. */
START_TEST (test_acceptance_for_A_01)
{
EXIPSchema schema;
FILE *infile;
Parser testParser;
char buf[INPUT_BUFFER_SIZE];
const char *schemafname = "testStates/acceptance-xsd.exi";
const char *exifname = "testStates/acceptance_a_01.exi";
char exipath[MAX_PATH_LEN + strlen(exifname)];
struct appData parsingData;
errorCode tmp_err_code = EXIP_UNEXPECTED_ERROR;
BinaryBuffer buffer;
buffer.buf = buf;
buffer.bufContent = 0;
buffer.bufLen = INPUT_BUFFER_SIZE;
// Parsing steps:
// I.A: First, read in the schema
parseSchema(schemafname, &schema);
// I.B: Define an external stream for the input to the parser if any
size_t pathlen = strlen(dataDir);
memcpy(exipath, dataDir, pathlen);
exipath[pathlen] = '/';
memcpy(&exipath[pathlen+1], exifname, strlen(exifname)+1);
infile = fopen(exipath, "rb" );
if(!infile)
fail("Unable to open file %s", exipath);
buffer.ioStrm.readWriteToStream = readFileInputStream;
buffer.ioStrm.stream = infile;
// II: Second, initialize the parser object
tmp_err_code = initParser(&testParser, buffer, &parsingData);
fail_unless (tmp_err_code == EXIP_OK, "initParser returns an error code %d", tmp_err_code);
// III: Initialize the parsing data and hook the callback handlers to the parser object
parsingData.eventCount = 0;
parsingData.expectAttributeData = 0;
if (EXIP_OK != initAllocList(&parsingData.allocList))
fail("Memory allocation error!");
testParser.handler.fatalError = sample_fatalError;
testParser.handler.error = sample_fatalError;
testParser.handler.startDocument = sample_startDocument;
testParser.handler.endDocument = sample_endDocument;
testParser.handler.startElement = sample_startElement;
testParser.handler.attribute = sample_attribute;
testParser.handler.stringData = sample_stringData;
testParser.handler.endElement = sample_endElement;
testParser.handler.decimalData = sample_decimalData;
testParser.handler.intData = sample_intData;
testParser.handler.floatData = sample_floatData;
// IV: Parse the header of the stream
tmp_err_code = parseHeader(&testParser, FALSE);
fail_unless (tmp_err_code == EXIP_OK, "parsing the header returns an error code %d", tmp_err_code);
tmp_err_code = setSchema(&testParser, &schema);
fail_unless (tmp_err_code == EXIP_OK, "setSchema() returns an error code %d", tmp_err_code);
// V: Parse the body of the EXI stream
while(tmp_err_code == EXIP_OK)
{
switch (parsingData.eventCount)
{
case 0:
fail_unless(stringEqualToAscii(parsingData.eventCode, "SD"));
break;
case 1:
fail_unless(stringEqualToAscii(parsingData.eventCode, "SE"));
fail_unless(stringEqualToAscii(parsingData.uri, "urn:foo"));
fail_unless(stringEqualToAscii(parsingData.localName, "A"));
break;
case 2:
fail_unless(stringEqualToAscii(parsingData.eventCode, "SE"));
fail_unless(stringEqualToAscii(parsingData.uri, "urn:foo"));
fail_unless(stringEqualToAscii(parsingData.localName, "AB"));
break;
case 3:
fail_unless(stringEqualToAscii(parsingData.eventCode, "CH"));
break;
case 4:
fail_unless(stringEqualToAscii(parsingData.eventCode, "EE"));
break;
case 5:
fail_unless(stringEqualToAscii(parsingData.eventCode, "SE"));
fail_unless(stringEqualToAscii(parsingData.uri, "urn:foo"));
fail_unless(stringEqualToAscii(parsingData.localName, "AC"));
break;
case 6:
fail_unless(stringEqualToAscii(parsingData.eventCode, "CH"));
break;
case 7:
fail_unless(stringEqualToAscii(parsingData.eventCode, "EE"));
break;
case 8:
fail_unless(stringEqualToAscii(parsingData.eventCode, "SE"));
fail_unless(stringEqualToAscii(parsingData.uri, "urn:foo"));
fail_unless(stringEqualToAscii(parsingData.localName, "AC"));
break;
case 9:
fail_unless(stringEqualToAscii(parsingData.eventCode, "CH"));
break;
case 10:
fail_unless(stringEqualToAscii(parsingData.eventCode, "EE"));
break;
case 11:
fail_unless(stringEqualToAscii(parsingData.eventCode, "SE"));
fail_unless(stringEqualToAscii(parsingData.uri, "urn:foo"));
fail_unless(stringEqualToAscii(parsingData.localName, "AD"));
break;
case 12:
fail_unless(stringEqualToAscii(parsingData.eventCode, "CH"));
break;
case 13:
fail_unless(stringEqualToAscii(parsingData.eventCode, "EE"));
break;
case 14:
fail_unless(stringEqualToAscii(parsingData.eventCode, "SE"));
fail_unless(stringEqualToAscii(parsingData.uri, "urn:foo"));
fail_unless(stringEqualToAscii(parsingData.localName, "AE"));
break;
case 15:
fail_unless(stringEqualToAscii(parsingData.eventCode, "CH"));
break;
case 16:
fail_unless(stringEqualToAscii(parsingData.eventCode, "EE"));
break;
case 17:
fail_unless(stringEqualToAscii(parsingData.eventCode, "EE"));
break;
case 18:
fail_unless(stringEqualToAscii(parsingData.eventCode, "ED"));
break;
default:
// Unexpected event count caught below.
break;
}
tmp_err_code = parseNext(&testParser);
parsingData.eventCount++;
}
fail_unless(stringEqualToAscii(parsingData.eventCode, "ED"));
fail_unless(parsingData.eventCount == 18,
"Unexpected event count: %u", parsingData.eventCount);
// VI: Free the memory allocated by the parser object
freeAllocList(&parsingData.allocList);
destroyParser(&testParser);
fclose(infile);
fail_unless (tmp_err_code == EXIP_PARSING_COMPLETE, "Error during parsing of the EXI body %d", tmp_err_code);
}
END_TEST
/* Verifies a single global element also used as a reference. */
START_TEST (test_acceptance_for_A_01_exip1)
{
EXIPSchema schema;
FILE *infile;
Parser testParser;
char buf[INPUT_BUFFER_SIZE];
const char *schemafname = "testStates/acceptance-xsd.exi";
const char *exifname = "testStates/acceptance_a_01a.exi";
char exipath[MAX_PATH_LEN + strlen(exifname)];
struct appData parsingData;
errorCode tmp_err_code = EXIP_UNEXPECTED_ERROR;
BinaryBuffer buffer;
buffer.buf = buf;
buffer.bufContent = 0;
buffer.bufLen = INPUT_BUFFER_SIZE;
// Parsing steps:
// I.A: First, read in the schema
parseSchema(schemafname, &schema);
// I.B: Define an external stream for the input to the parser if any
size_t pathlen = strlen(dataDir);
memcpy(exipath, dataDir, pathlen);
exipath[pathlen] = '/';
memcpy(&exipath[pathlen+1], exifname, strlen(exifname)+1);
infile = fopen(exipath, "rb" );
if(!infile)
fail("Unable to open file %s", exipath);
buffer.ioStrm.readWriteToStream = readFileInputStream;
buffer.ioStrm.stream = infile;
// II: Second, initialize the parser object
tmp_err_code = initParser(&testParser, buffer, &parsingData);
fail_unless (tmp_err_code == EXIP_OK, "initParser returns an error code %d", tmp_err_code);
// III: Initialize the parsing data and hook the callback handlers to the parser object
parsingData.eventCount = 0;
parsingData.expectAttributeData = 0;
if (EXIP_OK != initAllocList(&parsingData.allocList))
fail("Memory allocation error!");
testParser.handler.fatalError = sample_fatalError;
testParser.handler.error = sample_fatalError;
testParser.handler.startDocument = sample_startDocument;
testParser.handler.endDocument = sample_endDocument;
testParser.handler.startElement = sample_startElement;
testParser.handler.attribute = sample_attribute;
testParser.handler.stringData = sample_stringData;
testParser.handler.endElement = sample_endElement;
testParser.handler.decimalData = sample_decimalData;
testParser.handler.intData = sample_intData;
testParser.handler.floatData = sample_floatData;
// IV: Parse the header of the stream
tmp_err_code = parseHeader(&testParser, FALSE);
fail_unless (tmp_err_code == EXIP_OK, "parsing the header returns an error code %d", tmp_err_code);
tmp_err_code = setSchema(&testParser, &schema);
fail_unless (tmp_err_code == EXIP_OK, "setSchema() returns an error code %d", tmp_err_code);
// V: Parse the body of the EXI stream
while(tmp_err_code == EXIP_OK)
{
switch (parsingData.eventCount)
{
case 0:
fail_unless(stringEqualToAscii(parsingData.eventCode, "SD"));
break;
case 1:
fail_unless(stringEqualToAscii(parsingData.eventCode, "SE"));
fail_unless(stringEqualToAscii(parsingData.uri, "urn:foo"));
fail_unless(stringEqualToAscii(parsingData.localName, "AB"));
break;
case 2:
fail_unless(stringEqualToAscii(parsingData.eventCode, "CH"));
break;
case 3:
fail_unless(stringEqualToAscii(parsingData.eventCode, "EE"));
break;
case 4:
fail_unless(stringEqualToAscii(parsingData.eventCode, "ED"));
break;
default:
// Unexpected event count caught below.
break;
}
tmp_err_code = parseNext(&testParser);
parsingData.eventCount++;
}
fail_unless(stringEqualToAscii(parsingData.eventCode, "ED"));
fail_unless(parsingData.eventCount == 4,
"Unexpected event count: %u", parsingData.eventCount);
// VI: Free the memory allocated by the parser object
freeAllocList(&parsingData.allocList);
destroyParser(&testParser);
fclose(infile);
fail_unless (tmp_err_code == EXIP_PARSING_COMPLETE, "Error during parsing of the EXI body %d", tmp_err_code);
}
END_TEST
/*
* Verifies error when more elements than schema maxOccurs permits. Attempts
* to encode:
*
* <A xmlns='urn:foo'>
* <AB/><AC/><AC/><AC/>
* </A>
*/
START_TEST (test_acceptance_for_A_01b)
{
EXIPSchema schema;
EXIStream testStrm;
String uri;
String ln;
QName qname = {&uri, &ln, NULL};
char buf[OUTPUT_BUFFER_SIZE];
const char *schemafname = "testStates/acceptance-xsd.exi";
errorCode tmp_err_code = EXIP_UNEXPECTED_ERROR;
EXITypeClass valueType;
const String NS_STR = {"urn:foo", 7};
const String ELEM_A = {"A", 1};
const String ELEM_AB = {"AB", 2};
const String ELEM_AC = {"AC", 2};
const String CH = {"", 0};
BinaryBuffer buffer;
buffer.buf = buf;
buffer.bufLen = OUTPUT_BUFFER_SIZE;
buffer.bufContent = 0;
// Serialization steps:
// I.A: First, read in the schema
parseSchema(schemafname, &schema);
// I: First initialize the header of the stream
serialize.initHeader(&testStrm);
// II: Set any options in the header, if different from the defaults
testStrm.header.has_cookie = TRUE;
testStrm.header.has_options = TRUE;
testStrm.header.opts.valueMaxLength = 300;
testStrm.header.opts.valuePartitionCapacity = 50;
SET_STRICT(testStrm.header.opts.enumOpt);
// III: Define an external stream for the output if any
buffer.ioStrm.readWriteToStream = NULL;
buffer.ioStrm.stream = NULL;
// IV: Initialize the stream
tmp_err_code = serialize.initStream(&testStrm, buffer, &schema);
fail_unless (tmp_err_code == EXIP_OK, "initStream returns an error code %d", tmp_err_code);
// V: Start building the stream step by step: header, document, element etc...
tmp_err_code += serialize.exiHeader(&testStrm);
tmp_err_code += serialize.startDocument(&testStrm);
qname.uri = &NS_STR;
qname.localName = &ELEM_A;
tmp_err_code += serialize.startElement(&testStrm, qname, &valueType);
fail_unless (tmp_err_code == EXIP_OK, "serialization returns an error code %d", tmp_err_code);
qname.localName = &ELEM_AB;
tmp_err_code += serialize.startElement(&testStrm, qname, &valueType);
tmp_err_code += serialize.stringData(&testStrm, CH);
tmp_err_code += serialize.endElement(&testStrm);
fail_unless (tmp_err_code == EXIP_OK, "serialization returns an error code %d", tmp_err_code);
qname.localName = &ELEM_AC;
tmp_err_code += serialize.startElement(&testStrm, qname, &valueType);
tmp_err_code += serialize.stringData(&testStrm, CH);
tmp_err_code += serialize.endElement(&testStrm);
fail_unless (tmp_err_code == EXIP_OK, "serialization returns an error code %d", tmp_err_code);
tmp_err_code += serialize.startElement(&testStrm, qname, &valueType);
tmp_err_code += serialize.stringData(&testStrm, CH);
tmp_err_code += serialize.endElement(&testStrm);
fail_unless (tmp_err_code == EXIP_OK, "serialization returns an error code %d", tmp_err_code);
/* Expect failure when start third AC element */
tmp_err_code += serialize.startElement(&testStrm, qname, &valueType);
fail_unless (tmp_err_code == EXIP_INCONSISTENT_PROC_STATE,
"Expected EXIP_INCONSISTENT_PROC_STATE, but returns an error code %d", tmp_err_code);
// VI: Free the memory allocated by the EXI stream object
tmp_err_code = serialize.closeEXIStream(&testStrm);
}
END_TEST
/* START THE LKAB DEMO SUIT*/
/**********************************************************************/
/**********************************************************************/
/**********************************************************************/
/**********************************************************************/
typedef char error_code; // 0 - OK; Error otherwise
struct timestamp
{
unsigned int year;
unsigned int month;
unsigned int mday;
unsigned int hour;
unsigned int min;
unsigned int sec;
unsigned int msec;
};
typedef struct timestamp Timestamp;
enum ValueType
{
Bool =0,
Long =1
};
typedef enum ValueType ValueType;
enum Quality {
Good =0,
Good_LocalOverride =1,
Uncertain =2,
Uncertain_LastUsable =3,
Uncertain_SensorCal =4,
Uncertain_EGUExeeded =5,
Uncertain_SubNormal =6,
Bad =7,
Bad_ConfigError =8,
Bad_NotConnected =9,
Bad_DeviceFailure =10,
Bad_SensorFailure =11,
Bad_CommFailureLastKnownAvailable =12,
Bad_CommFailure =13,
Bad_OutOfService =14
};
typedef enum Quality Quality;
struct boolValue
{
Timestamp ts;
Quality quality;
unsigned char val;
};
typedef struct boolValue BoolValue;
struct processValueDesc
{
char name[50];
ValueType type;
unsigned char isReadOnly; // boolean
char description[150];
};
typedef struct processValueDesc ProcessValueDesc;
struct devDescribtion
{
char id[50];
char name[50];
char type[50];
char location[100];
ProcessValueDesc processValue;
};
typedef struct devDescribtion DevDescribtion;
extern const EXISerializer serialize;
const String NS_STR = {"http://imc-aesop.eu/lkab-demo", 29};
const String NS_EMPTY_STR = {NULL, 0};
const String ELEM_BOOL_VAL_STR = {"BoolValue", 9};
const String ELEM_QUAL_STR = {"quality", 7};
const String ELEM_TIMESTAMP_STR = {"timestamp", 9};
const String ELEM_VALUE_STR = {"value", 5};
const String ELEM_DEV_DESC_STR = {"DeviceDescription", 17};
const String ELEM_ID_STR = {"id", 2};
const String ELEM_NAME_STR = {"name", 4};
const String ELEM_TYPE_STR = {"type", 4};
const String ELEM_LOCATION_STR = {"location", 8};
const String ELEM_PROSS_VAL_STR = {"processValues", 13};
const String ELEM_IS_READONLY_STR = {"isReadOnly", 10};
const String ELEM_DESC_STR = {"description", 11};
static String ENUM_DATA_QUALITY[] = {{"Good", 4},
{"Good_LocalOverride", 18},
{"Uncertain", 9},
{"Uncertain_LastUsable", 20},
{"Uncertain_SensorCal", 19},
{"Uncertain_EGUExeeded", 20},
{"Uncertain_SubNormal", 19},
{"Bad", 3},
{"Bad_ConfigError", 15},
{"Bad_NotConnected", 16},
{"Bad_DeviceFailure", 17},
{"Bad_SensorFailure", 17},
{"Bad_CommFailureLastKnownAvailable", 33},
{"Bad_CommFailure", 15},
{"Bad_OutOfService", 16}};
static String ENUM_DATA_VAL_TYPE[] = {{"Bool", 4},
{"Long", 4}};
struct appDataLKAB
{
BoolValue val;
DevDescribtion devDesc;
unsigned int currElementNumber;
};
// Content Handler API
static errorCode lkab_fatalError(const errorCode code, const char* msg, void* app_data);
static errorCode lkab_startElement_io(QName qname, void* app_data);
static errorCode lkab_startElement_desc(QName qname, void* app_data);
static errorCode lkab_endElement(void* app_data);
static errorCode lkab_stringData_io(const String value, void* app_data);
static errorCode lkab_stringData_desc(const String value, void* app_data);
static errorCode lkab_booleanData_io(boolean bool_val, void* app_data);
static errorCode lkab_booleanData_desc(boolean bool_val, void* app_data);
static errorCode lkab_dateTimeData(EXIPDateTime dt_val, void* app_data);
/**
* @brief Reads the time-stamp and value data from a EXI message
*
* @param[in] buf A buffer containing the EXI stream
* @param[in] buf_size the size of the buffer in bytes
* @param[out] val a time-stamp and bool (digital I/O) value read from the message
* @return Error handling code
*/
static error_code parseIOMsg(char* buf, unsigned int buf_size, BoolValue *val);
/**
* @brief Reads a DevDescribtion EXI message
*
* @param[in] buf A buffer containing the EXI stream
* @param[in] buf_size the size of the buffer in bytes
* @param[out] devDesc the description of the device to be read
* @return Error handling code
*/
static error_code parseDevDescMsg(char* buf, unsigned int buf_size, DevDescribtion* devDesc);
/**
* @brief Serialize an I/O EXI message
*
* @param[in, out] buf An empty buffer for storing the EXI stream
* @param[in] buf_size the size of the buffer in bytes
* @param[out] msg_size the size of the serialized message in bytes
* @param[in] val the time-stamp and bool (digital I/O) value to be written to the message
* @return Error handling code
*/
static error_code serializeIOMsg(char* buf, unsigned int buf_size, unsigned int* msg_size, BoolValue val);
/**
* @brief Serialize a DevDescribtion EXI message
*
* @param[in, out] buf An empty buffer for storing the EXI stream
* @param[in] buf_size the size of the buffer in bytes
* @param[out] msg_size the size of the serialized message in bytes
* @param[in] devDesc the description of the device to be written
* @return Error handling code
*/
static error_code serializeDevDescMsg(char* buf, unsigned int buf_size, unsigned int* msg_size, DevDescribtion devDesc);
static error_code serializeIOMsg(char* buf, unsigned int buf_size, unsigned int* msg_size, BoolValue val)
{
EXIStream strm;
String uri;
String ln;
QName qname = {&uri, &ln, NULL};
errorCode tmp_err_code = EXIP_UNEXPECTED_ERROR;
BinaryBuffer buffer;
EXIPDateTime dt;
EXIPSchema lkab_schema;
const char *schemafname = "SchemaStrict/lkab-devices-xsd.exi";
EXITypeClass valueType;
buffer.buf = buf;
buffer.bufLen = buf_size;
buffer.bufContent = 0;
// Serialization steps:
// I.A: First, read in the schema
parseSchema(schemafname, &lkab_schema);
// I: First initialize the header of the stream
serialize.initHeader(&strm);
// II: Set any options in the header, if different from the defaults
strm.header.has_options = TRUE;
SET_STRICT(strm.header.opts.enumOpt);
// III: Define an external stream for the output if any
buffer.ioStrm.stream = NULL;
buffer.ioStrm.readWriteToStream = NULL;
// IV: Initialize the stream
tmp_err_code = serialize.initStream(&strm, buffer, &lkab_schema);
if(tmp_err_code != EXIP_OK)
return tmp_err_code;
// V: Start building the stream step by step: header, document, element etc...
tmp_err_code += serialize.exiHeader(&strm);
tmp_err_code += serialize.startDocument(&strm);
qname.uri = &NS_STR;
qname.localName = &ELEM_BOOL_VAL_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <BoolValue>
qname.uri = &NS_STR;
qname.localName = &ELEM_TIMESTAMP_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <timeStamp>
dt.presenceMask = FRACT_PRESENCE;
dt.dateTime.tm_year = val.ts.year;
dt.dateTime.tm_mon = val.ts.month;
dt.dateTime.tm_mday = val.ts.mday;
dt.dateTime.tm_hour = val.ts.hour;
dt.dateTime.tm_min = val.ts.min;
dt.dateTime.tm_sec = val.ts.sec;
dt.fSecs.value = val.ts.msec;
dt.fSecs.offset = 2;
tmp_err_code += serialize.dateTimeData(&strm, dt);
tmp_err_code += serialize.endElement(&strm); // </timeStamp>
qname.uri = &NS_STR;
qname.localName = &ELEM_QUAL_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <quality>
tmp_err_code += serialize.stringData(&strm, ENUM_DATA_QUALITY[val.quality]); // quality
tmp_err_code += serialize.endElement(&strm); // </quality>
qname.uri = &NS_STR;
qname.localName = &ELEM_VALUE_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <value>
tmp_err_code += serialize.booleanData(&strm, val.val);
tmp_err_code += serialize.endElement(&strm); // </value>
tmp_err_code += serialize.endElement(&strm); // </BoolValue>
tmp_err_code += serialize.endDocument(&strm);
if(tmp_err_code != EXIP_OK)
return tmp_err_code;
*msg_size = strm.context.bufferIndx + 1;
// VI: Free the memory allocated by the EXI stream object
tmp_err_code = serialize.closeEXIStream(&strm);
return EXIP_OK;
}
static error_code serializeDevDescMsg(char* buf, unsigned int buf_size, unsigned int* msg_size, DevDescribtion devDesc)
{
EXIStream strm;
String uri;
String ln;
String ch;
QName qname = {&uri, &ln, NULL};
errorCode tmp_err_code = EXIP_UNEXPECTED_ERROR;
BinaryBuffer buffer;
EXIPSchema lkab_schema;
const char *schemafname = "SchemaStrict/lkab-devices-xsd.exi";
EXITypeClass valueType;
buffer.buf = buf;
buffer.bufLen = buf_size;
buffer.bufContent = 0;
// Serialization steps:
// I.A: First, read in the schema
parseSchema(schemafname, &lkab_schema);
// I: First initialize the header of the stream
serialize.initHeader(&strm);
// II: Set any options in the header, if different from the defaults
strm.header.has_options = TRUE;
SET_STRICT(strm.header.opts.enumOpt);
// III: Define an external stream for the output if any
buffer.ioStrm.stream = NULL;
buffer.ioStrm.readWriteToStream = NULL;
// IV: Initialize the stream
tmp_err_code = serialize.initStream(&strm, buffer, &lkab_schema);
if(tmp_err_code != EXIP_OK)
return tmp_err_code;
// V: Start building the stream step by step: header, document, element etc...
tmp_err_code += serialize.exiHeader(&strm);
tmp_err_code += serialize.startDocument(&strm);
qname.uri = &NS_STR;
qname.localName = &ELEM_DEV_DESC_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <DeviceDescription>
qname.uri = &NS_STR;
qname.localName = &ELEM_ID_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <id>
ch.str = devDesc.id;
ch.length = strlen(devDesc.id);
tmp_err_code += serialize.stringData(&strm, ch); // device id
tmp_err_code += serialize.endElement(&strm); // </id>
qname.uri = &NS_STR;
qname.localName = &ELEM_NAME_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <name>
ch.str = devDesc.name;
ch.length = strlen(devDesc.name);
tmp_err_code += serialize.stringData(&strm, ch); // device name
tmp_err_code += serialize.endElement(&strm); // </name>
qname.uri = &NS_STR;
qname.localName = &ELEM_TYPE_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <type>
ch.str = devDesc.type;
ch.length = strlen(devDesc.type);
tmp_err_code += serialize.stringData(&strm, ch); // device type
tmp_err_code += serialize.endElement(&strm); // </type>
qname.uri = &NS_STR;
qname.localName = &ELEM_LOCATION_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <location>
ch.str = devDesc.location;
ch.length = strlen(devDesc.location);
tmp_err_code += serialize.stringData(&strm, ch); // device location
tmp_err_code += serialize.endElement(&strm); // </location>
qname.uri = &NS_STR;
qname.localName = &ELEM_PROSS_VAL_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <processValues>
qname.uri = &NS_STR;
qname.localName = &ELEM_NAME_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <name>
ch.str = devDesc.processValue.name;
ch.length = strlen(devDesc.processValue.name);
tmp_err_code += serialize.stringData(&strm, ch); // processValues name
tmp_err_code += serialize.endElement(&strm); // </name>
qname.uri = &NS_STR;
qname.localName = &ELEM_TYPE_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <type>
tmp_err_code += serialize.stringData(&strm, ENUM_DATA_VAL_TYPE[devDesc.processValue.type]); // processValues type
tmp_err_code += serialize.endElement(&strm); // </type>
qname.uri = &NS_STR;
qname.localName = &ELEM_IS_READONLY_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <isReadOnly>
tmp_err_code += serialize.booleanData(&strm, 0); // processValues isReadOnly
tmp_err_code += serialize.endElement(&strm); // </isReadOnly>
qname.uri = &NS_STR;
qname.localName = &ELEM_DESC_STR;
tmp_err_code += serialize.startElement(&strm, qname, &valueType); // <description>
ch.str = devDesc.processValue.description;
ch.length = strlen(devDesc.processValue.description);
tmp_err_code += serialize.stringData(&strm, ch); // processValues description
tmp_err_code += serialize.endElement(&strm); // </description>
tmp_err_code += serialize.endElement(&strm); // </processValues>
tmp_err_code += serialize.endElement(&strm); // </DeviceDescription>
tmp_err_code += serialize.endDocument(&strm);
if(tmp_err_code != EXIP_OK)
return tmp_err_code;
*msg_size = strm.context.bufferIndx + 1;
// VI: Free the memory allocated by the EXI stream object
tmp_err_code = serialize.closeEXIStream(&strm);
return EXIP_OK;
}
static error_code parseIOMsg(char* buf, unsigned int buf_size, BoolValue *val)
{
Parser lkabParser;
BinaryBuffer buffer;
errorCode tmp_err_code = EXIP_UNEXPECTED_ERROR;
struct appDataLKAB parsingData;
EXIPSchema lkab_schema;
const char *schemafname = "SchemaStrict/lkab-devices-xsd.exi";
buffer.buf = buf;
buffer.bufLen = buf_size;
buffer.bufContent = buf_size;
// Parsing steps:
// I.A: First, read in the schema
parseSchema(schemafname, &lkab_schema);
// I: First, define an external stream for the input to the parser if any
buffer.ioStrm.stream = NULL;
// II: Second, initialize the parser object
tmp_err_code = initParser(&lkabParser, buffer, &parsingData);
if(tmp_err_code != EXIP_OK)
return tmp_err_code;
// III: Initialize the parsing data and hook the callback handlers to the parser object
parsingData.currElementNumber = 0;
parsingData.val.quality = Good;
parsingData.val.val = 0;
parsingData.val.ts.year = 0;
parsingData.val.ts.month = 0;
parsingData.val.ts.mday = 0;
parsingData.val.ts.hour = 0;
parsingData.val.ts.min = 0;
parsingData.val.ts.sec = 0;
parsingData.val.ts.msec = 0;
lkabParser.handler.fatalError = lkab_fatalError;
lkabParser.handler.error = lkab_fatalError;
lkabParser.handler.startElement = lkab_startElement_io;
lkabParser.handler.stringData = lkab_stringData_io;
lkabParser.handler.endElement = lkab_endElement;
lkabParser.handler.booleanData = lkab_booleanData_io;
lkabParser.handler.dateTimeData = lkab_dateTimeData;
// IV: Parse the header of the stream
tmp_err_code = parseHeader(&lkabParser, FALSE);
tmp_err_code = setSchema(&lkabParser, &lkab_schema);
// V: Parse the body of the EXI stream
while(tmp_err_code == EXIP_OK)
{
tmp_err_code = parseNext(&lkabParser);
}
// VI: Free the memory allocated by the parser object
destroyParser(&lkabParser);
val->ts = parsingData.val.ts;
val->val = parsingData.val.val;
val->quality = parsingData.val.quality;
if(tmp_err_code == EXIP_PARSING_COMPLETE)
return EXIP_OK;
else
return tmp_err_code;
}
static error_code parseDevDescMsg(char* buf, unsigned int buf_size, DevDescribtion* devDesc)
{
Parser lkabParser;
BinaryBuffer buffer;
errorCode tmp_err_code = EXIP_UNEXPECTED_ERROR;
struct appDataLKAB parsingData;
EXIPSchema lkab_schema;
const char *schemafname = "SchemaStrict/lkab-devices-xsd.exi";
buffer.buf = buf;
buffer.bufLen = buf_size;
buffer.bufContent = buf_size;
// Parsing steps:
// I.A: First, read in the schema
parseSchema(schemafname, &lkab_schema);
// I: First, define an external stream for the input to the parser if any
buffer.ioStrm.stream = NULL;
// II: Second, initialize the parser object
tmp_err_code = initParser(&lkabParser, buffer, &parsingData);
if(tmp_err_code != EXIP_OK)
return tmp_err_code;
// III: Initialize the parsing data and hook the callback handlers to the parser object
parsingData.currElementNumber = 0;
parsingData.devDesc.id[0] = '\0';
parsingData.devDesc.location[0] = '\0';
parsingData.devDesc.name[0] = '\0';
parsingData.devDesc.type[0] = '\0';
parsingData.devDesc.processValue.description[0] = '\0';
parsingData.devDesc.processValue.name[0] = '\0';
parsingData.devDesc.processValue.isReadOnly = 0;
parsingData.devDesc.processValue.type = Bool;
lkabParser.handler.fatalError = lkab_fatalError;
lkabParser.handler.error = lkab_fatalError;
lkabParser.handler.startElement = lkab_startElement_desc;
lkabParser.handler.stringData = lkab_stringData_desc;
lkabParser.handler.endElement = lkab_endElement;
lkabParser.handler.booleanData = lkab_booleanData_desc;
// IV: Parse the header of the stream
tmp_err_code = parseHeader(&lkabParser, FALSE);
tmp_err_code = setSchema(&lkabParser, &lkab_schema);
// V: Parse the body of the EXI stream
while(tmp_err_code == EXIP_OK)
{
tmp_err_code = parseNext(&lkabParser);
}
// VI: Free the memory allocated by the parser object
destroyParser(&lkabParser);
strcpy(devDesc->id, parsingData.devDesc.id);
strcpy(devDesc->location, parsingData.devDesc.location);
strcpy(devDesc->name, parsingData.devDesc.name);
strcpy(devDesc->type, parsingData.devDesc.type);
strcpy(devDesc->processValue.description, parsingData.devDesc.processValue.description);
strcpy(devDesc->processValue.name, parsingData.devDesc.processValue.name);
devDesc->processValue.isReadOnly = parsingData.devDesc.processValue.isReadOnly;
devDesc->processValue.type = parsingData.devDesc.processValue.type;
if(tmp_err_code == EXIP_PARSING_COMPLETE)
return EXIP_OK;
else
return tmp_err_code;
}
static errorCode lkab_fatalError(const errorCode code, const char* msg, void* app_data)
{
return EXIP_HANDLER_STOP;
}
static errorCode lkab_startElement_io(QName qname, void* app_data)
{
struct appDataLKAB* appD = (struct appDataLKAB*) app_data;
QName expectedElem;
appD->currElementNumber += 1;
expectedElem.uri = &NS_STR;
switch(appD->currElementNumber)
{
case 1:
expectedElem.localName = &ELEM_BOOL_VAL_STR;
break;
case 2:
expectedElem.localName = &ELEM_TIMESTAMP_STR;
break;
case 3:
expectedElem.localName = &ELEM_QUAL_STR;
break;
case 4:
expectedElem.localName = &ELEM_VALUE_STR;
break;
default:
return EXIP_HANDLER_STOP;
break;
}
if(!stringEqual(*expectedElem.uri, *(qname.uri)) ||
!stringEqual(*expectedElem.localName, *(qname.localName)))
{
return EXIP_HANDLER_STOP;
}
return EXIP_OK;
}
static errorCode lkab_startElement_desc(QName qname, void* app_data)
{
struct appDataLKAB* appD = (struct appDataLKAB*) app_data;
QName expectedElem;
appD->currElementNumber += 1;
expectedElem.uri = &NS_STR;
switch(appD->currElementNumber)
{
case 1:
expectedElem.localName = &ELEM_DEV_DESC_STR;
break;
case 2:
expectedElem.localName = &ELEM_ID_STR;
break;
case 3:
expectedElem.localName = &ELEM_NAME_STR;
break;
case 4:
expectedElem.localName = &ELEM_TYPE_STR;
break;
case 5:
expectedElem.localName = &ELEM_LOCATION_STR;
break;
case 6:
expectedElem.localName = &ELEM_PROSS_VAL_STR;
break;
case 7:
expectedElem.localName = &ELEM_NAME_STR;
break;
case 8:
expectedElem.localName = &ELEM_TYPE_STR;
break;
case 9:
expectedElem.localName = &ELEM_IS_READONLY_STR;
break;
case 10:
expectedElem.localName = &ELEM_DESC_STR;
break;
default:
return EXIP_HANDLER_STOP;
break;
}
if(!stringEqual(*expectedElem.uri, *(qname.uri)) ||
!stringEqual(*expectedElem.localName, *(qname.localName)))
{
return EXIP_HANDLER_STOP;
}
return EXIP_OK;
}
static errorCode lkab_endElement(void* app_data)
{
return EXIP_OK;
}
static errorCode lkab_stringData_io(const String value, void* app_data)
{
struct appDataLKAB* appD = (struct appDataLKAB*) app_data;
if(appD->currElementNumber != 3)
return EXIP_HANDLER_STOP;
else
{
int i;
for(i = 0; i < 15; i++)
{
if(stringEqual(ENUM_DATA_QUALITY[i], value))
{
appD->val.quality = i;
return EXIP_OK;
}
}
return EXIP_HANDLER_STOP;
}
}
static errorCode lkab_stringData_desc(const String value, void* app_data)
{
struct appDataLKAB* appD = (struct appDataLKAB*) app_data;
switch(appD->currElementNumber)
{
case 2:
memcpy(appD->devDesc.id, value.str, sizeof(CharType)*value.length);
appD->devDesc.id[value.length] = '\0';
break;
case 3:
memcpy(appD->devDesc.name, value.str, sizeof(CharType)*value.length);
appD->devDesc.name[value.length] = '\0';
break;
case 4:
memcpy(appD->devDesc.type, value.str, sizeof(CharType)*value.length);
appD->devDesc.type[value.length] = '\0';
break;
case 5:
memcpy(appD->devDesc.location, value.str, sizeof(CharType)*value.length);
appD->devDesc.location[value.length] = '\0';
break;
case 7:
memcpy(appD->devDesc.processValue.name, value.str, sizeof(CharType)*value.length);
appD->devDesc.processValue.name[value.length] = '\0';
break;
case 8:
if(stringEqual(value, ENUM_DATA_VAL_TYPE[Bool]))
appD->devDesc.processValue.type = Bool;
else if(stringEqual(value, ENUM_DATA_VAL_TYPE[Long]))
appD->devDesc.processValue.type = Long;
else
return EXIP_HANDLER_STOP;
break;
case 10:
memcpy(appD->devDesc.processValue.description, value.str, sizeof(CharType)*value.length);
appD->devDesc.processValue.description[value.length] = '\0';
break;
default:
return EXIP_HANDLER_STOP;
break;
}
return EXIP_OK;
}
static errorCode lkab_booleanData_io(boolean bool_val, void* app_data)
{
struct appDataLKAB* appD = (struct appDataLKAB*) app_data;
if(appD->currElementNumber != 4)
return EXIP_HANDLER_STOP;
else
{
appD->val.val = bool_val;
}
return EXIP_OK;
}
static errorCode lkab_booleanData_desc(boolean bool_val, void* app_data)
{
struct appDataLKAB* appD = (struct appDataLKAB*) app_data;
if(appD->currElementNumber != 9)
return EXIP_HANDLER_STOP;
else
{
appD->devDesc.processValue.isReadOnly = bool_val;
}
return EXIP_OK;
}
static errorCode lkab_dateTimeData(EXIPDateTime dt_val, void* app_data)
{
struct appDataLKAB* appD = (struct appDataLKAB*) app_data;
appD->val.ts.year = dt_val.dateTime.tm_year;
appD->val.ts.month = dt_val.dateTime.tm_mon;
appD->val.ts.mday = dt_val.dateTime.tm_mday;
appD->val.ts.hour = dt_val.dateTime.tm_hour;
appD->val.ts.min = dt_val.dateTime.tm_min;
appD->val.ts.sec = dt_val.dateTime.tm_sec;
appD->val.ts.msec = dt_val.fSecs.value;
return EXIP_OK;
}
#define LKAB_BUFFER_SIZE 1000
START_TEST (test_lkab_demo_suit)
{
char lkab_buf[LKAB_BUFFER_SIZE];
unsigned int msg_size;
BoolValue bVal;
DevDescribtion devD;
error_code err;
memset(lkab_buf, '\0', LKAB_BUFFER_SIZE);
bVal.ts.hour = 0;
bVal.ts.mday = 0;
bVal.ts.min = 10;
bVal.ts.month = 0;
bVal.ts.msec = 115;
bVal.ts.sec = 10;
bVal.ts.year = 0;
bVal.quality = Good;
bVal.val = 1;
err = serializeIOMsg(lkab_buf, LKAB_BUFFER_SIZE, &msg_size, bVal);
fail_unless (err == EXIP_OK, "Error during serialization of IO Msg %d", err);
fail_if(msg_size == 0, "0 Length message size");
bVal.ts.hour = 10;
bVal.ts.mday = 30;
bVal.ts.min = 120;
bVal.ts.month = 44;
bVal.ts.msec = 125;
bVal.ts.sec = 110;
bVal.ts.year = 4;
bVal.val = 0;
bVal.quality = Bad;
err = parseIOMsg(lkab_buf, LKAB_BUFFER_SIZE, &bVal);
fail_unless (err == EXIP_OK, "Error during parsing of IO Msg %d", err);
fail_unless (bVal.quality == Good, "quality not correct");
fail_unless (bVal.val == 1, "value not correct");
fail_unless (bVal.ts.min == 10, "min not correct");
fail_unless (bVal.ts.sec == 10, "sec not correct");
fail_unless (bVal.ts.msec == 115, "msec not correct");
strcpy(devD.id, "AirPS");
strcpy(devD.location, "Grate Support Shaft");
strcpy(devD.name, "Pressure switch");
strcpy(devD.type, "Air pressure switch");
strcpy(devD.processValue.description, "This is an Air pressure switch ON/OFF value");
strcpy(devD.processValue.name, "Air pressure switch ON/OFF");
devD.processValue.isReadOnly = 0;
devD.processValue.type = Bool;
err = serializeDevDescMsg(lkab_buf, LKAB_BUFFER_SIZE, &msg_size, devD);
fail_unless (err == EXIP_OK, "Error during serialization of DevDesc Msg %d", err);
fail_if(msg_size == 0, "0 Length message size");
strcpy(devD.id, "00000000000");
strcpy(devD.location, "00000000000");
strcpy(devD.name, "00000000000");
strcpy(devD.type, "00000000000");
strcpy(devD.processValue.description, "00000000000");
strcpy(devD.processValue.name, "00000000000");
devD.processValue.isReadOnly = 1;
devD.processValue.type = Long;
err = parseDevDescMsg(lkab_buf, LKAB_BUFFER_SIZE, &devD);
fail_unless (err == EXIP_OK, "Error during parsing of DevDesc Msg %d", err);
fail_unless (devD.processValue.isReadOnly == 0, "isReadOnly not correct");
fail_unless (devD.processValue.type == Bool, "type not correct");
fail_unless (strcmp(devD.id, "AirPS") == 0, "id not correct");
fail_unless (strcmp(devD.location, "Grate Support Shaft") == 0, "location not correct");
fail_unless (strcmp(devD.name, "Pressure switch") == 0, "name not correct");
fail_unless (strcmp(devD.type, "Air pressure switch") == 0, "type not correct");
fail_unless (strcmp(devD.processValue.description, "This is an Air pressure switch ON/OFF value") == 0, "description not correct");
fail_unless (strcmp(devD.processValue.name, "Air pressure switch ON/OFF") == 0, "name not correct");
}
END_TEST
/**********************************************************************/
/**********************************************************************/
/* END THE LKAB DEMO SUIT*/
/* Test suite */
Suite* exip_suite(void)
{
Suite *s = suite_create("Strict Grammar");
{
TCase *tc_builtin = tcase_create ("Strict Grammar");
tcase_add_test (tc_builtin, test_acceptance_for_A_01);
tcase_add_test (tc_builtin, test_acceptance_for_A_01_exip1);
tcase_add_test (tc_builtin, test_acceptance_for_A_01b);
tcase_add_test (tc_builtin, test_lkab_demo_suit);
suite_add_tcase (s, tc_builtin);
}
return s;
}
int main (int argc, char *argv[])
{
if (argc < 2)
{
printf("ERR: Expected test data directory\n");
exit(1);
}
if (strlen(argv[1]) > MAX_PATH_LEN)
{
printf("ERR: Test data pathname too long: %u", (unsigned int) strlen(argv[1]));
exit(1);
}
dataDir = argv[1];
int number_failed;
Suite *s = exip_suite();
SRunner *sr = srunner_create (s);
#ifdef _MSC_VER
srunner_set_fork_status(sr, CK_NOFORK);
#endif
srunner_run_all (sr, CK_NORMAL);
number_failed = srunner_ntests_failed (sr);
srunner_free (sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}