Decision Table
Here is a basic table in FitNesse:
Example Decision Table
!|ExecuteBinaryOperator |
|lhs|rhs|operator|expected?|
|3 |4 |- |-1 |
|5 |6 |* |30 |
Since no table type is provided, this is a decision table. The basic mechanics for this table are:
- Find a class called ExecuteBinaryOperator
- For each row after the header row:
- Call setLhs, providing a string
- Call setRhs, providing a string
- Call setOperator, providing a string
- Call expected, which returns a string and compare the value returned to the value provided
Here’s cslim code that can handle this table:
ExecuteBinaryOperator.cpp
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include "RpnCalculator.h"
#include "OperatorFactory.h"
#include "Fixtures.h"
#include "SlimUtils.h"
struct ExecuteBinaryOperator {
ExecuteBinaryOperator() {
lastValue[0] = 0;
}
int execute() {
RpnCalculator calculator(factory);
calculator.enterNumber(lhs);
calculator.enterNumber(rhs);
calculator.executeOperator(op);
return calculator.getX();
}
static ExecuteBinaryOperator* From(void *fixtureStorage) {
return reinterpret_cast<ExecuteBinaryOperator*>(fixtureStorage);
}
OperatorFactory factory;
int lhs;
int rhs;
std::string op;
char lastValue[32];
};
extern "C" {
void* ExecuteBinaryOperator_Create(StatementExecutor* errorHandler, SlimList* args) {
return new ExecuteBinaryOperator;
}
void ExecuteBinaryOperator_Destroy(void* self) {
delete ExecuteBinaryOperator::From(self);
}
static char* setLhs(void* fixture, SlimList* args) {
ExecuteBinaryOperator *self = ExecuteBinaryOperator::From(fixture);
self->lhs = getFirstInt(args);
return self->lastValue;
}
static char* setRhs(void* fixture, SlimList* args) {
ExecuteBinaryOperator *self = ExecuteBinaryOperator::From(fixture);
self->rhs = getFirstInt(args);
return self->lastValue;
}
static char* setOperator(void *fixture, SlimList* args) {
ExecuteBinaryOperator *self = ExecuteBinaryOperator::From(fixture);
self->op = getFirstString(args);
return self->lastValue;
}
static char* expected(void* fixture, SlimList* args) {
ExecuteBinaryOperator *self = ExecuteBinaryOperator::From(fixture);
int result = self->execute();
snprintf(self->lastValue, sizeof(self->lastValue), "%d", result);
return self->lastValue;
}
SLIM_CREATE_FIXTURE(ExecuteBinaryOperator)
SLIM_FUNCTION(setLhs)
SLIM_FUNCTION(setRhs)
SLIM_FUNCTION(setOperator)
SLIM_FUNCTION(expected)
SLIM_END
}
Note that as with all fixtures, this code will need to be “registered” with cslim. The default installation of cslim provides a file called “Fixtures.c”. To register this class, you’d add:
Fixtures.c
#include "Fixtures.h"
SLIM_FIXTURES
SLIM_FIXTURE(ExecuteBinaryOperator)
SLIM_FIXTURE(ProgramTheCalculator)
SLIM_FIXTURE(SingleCharacterNameOperators)
SLIM_END
This particular Fixtures.c file registeres all thee fixtures mentioned on this page.
Script Table
Here is an example of a script table:
Example Script Table
!|script |ProgramTheCalculator |
|startProgramCalled|primeFactorsOfSum |
|addOperation |sum |
|addOperation |primeFactors |
|saveProgram |
|enter |4 |
|enter |13 |
|enter |7 |
|execute |primeFactorsOfSum |
|check |stackHas|3|then|2|then|2|then|2|is|true|
How can you tell it’s a script table? The word script on the first row.
And here’s an example fixture that can handle this table. Note, I used g++ 4.4 (or 4.5) and provided the command-line parameter “-std=c++0x”. This allows me to use the auto keyword in its new form:
ProgrammingTheCalculator.cpp
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include "RpnCalculator.h"
#include "OperatorFactory.h"
#include "SlimUtils.h"
#include "SlimList.h"
#include "Fixtures.h"
struct ProgramTheCalculator {
ProgramTheCalculator() : calculator(factory) {
}
static ProgramTheCalculator* From(void *fixtureStorage) {
return reinterpret_cast<ProgramTheCalculator*>(fixtureStorage);
}
OperatorFactory factory;
RpnCalculator calculator;
};
extern "C" {
void* ProgramTheCalculator_Create(StatementExecutor* errorHandler, SlimList* args) {
return new ProgramTheCalculator;
}
void ProgramTheCalculator_Destroy(void *fixture) {
delete ProgramTheCalculator::From(fixture);
}
static char* startProgramCalled(void *fixture, SlimList *args) {
auto *self = ProgramTheCalculator::From(fixture);
self->calculator.createProgramNamed(getFirstString(args));
return remove_const("");
}
static char* addOperation(void *fixture, SlimList *args) {
auto *self = ProgramTheCalculator::From(fixture);
self->calculator.addOperator(getFirstString(args));
return remove_const("");
}
static char* saveProgram(void *fixture, SlimList *args) {
auto *self = ProgramTheCalculator::From(fixture);
self->calculator.saveProgram();
return remove_const("");
}
static char* enter(void *fixture, SlimList *args) {
auto *self = ProgramTheCalculator::From(fixture);
self->calculator.enterNumber(getFirstInt(args));
return remove_const("");
}
static char* execute(void *fixture, SlimList *args) {
auto *self = ProgramTheCalculator::From(fixture);
self->calculator.executeOperator(getFirstString(args));
return remove_const("");
}
static char* stackHasThenThenThenIs(void *fixture, SlimList *args) {
auto *self = ProgramTheCalculator::From(fixture);
for(int i = 0; i < 4; ++i) {
if(self->calculator.getX() != getIntAt(args, i))
return remove_const("false");
self->calculator.executeOperator("drop");
}
return remove_const("true");
}
SLIM_CREATE_FIXTURE(ProgramTheCalculator)
SLIM_FUNCTION(startProgramCalled)
SLIM_FUNCTION(addOperation)
SLIM_FUNCTION(saveProgram)
SLIM_FUNCTION(enter)
SLIM_FUNCTION(execute)
SLIM_FUNCTION(stackHasThenThenThenIs)
SLIM_END
}
Query Table
Finally, the dreaded query table. The query table requires a bit more work to produce a result. So this section contains a table, a fixture and a utility class to help create the query results.
Example Query Table
!|Query: SingleCharacterNameOperators|
|op |
|+ |
|* |
|/ |
|! |
|- |
How can you tell this is a query table? The word “Query” in the first row.
Now the fixture to handle it:
SingleCharacterNameOperators.cpp
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <string>
#include <memory>
#include "RpnCalculator.h"
#include "OperatorFactory.h"
#include "Fixtures.h"
#include "SlimUtils.h"
#include "QueryResultAccumulator.h"
struct SingleCharacterNameOperators {
OperatorFactory factory;
RpnCalculator calculator;
QueryResultAccumulator accumulator;
SingleCharacterNameOperators() :
calculator(factory) {
}
~SingleCharacterNameOperators() {
}
static SingleCharacterNameOperators* From(void *fixtureStorage) {
return reinterpret_cast<SingleCharacterNameOperators*> (fixtureStorage);
}
void conditionallyAddOperatorNamed(const std::string &name) {
if (name.size() == 1) {
accumulator.addFieldNamedWithValue("op", name);
accumulator.finishCurrentObject();
}
}
char *buildResult() {
v_string names = calculator.allOperatorNames();
return buildResult(names);
}
char *buildResult(v_string &names) {
for (v_string::iterator iter = names.begin(); iter != names.end(); ++iter)
conditionallyAddOperatorNamed(*iter);
return accumulator.produceFinalResults();
}
};
extern "C" {
void* SingleCharacterNameOperators_Create(StatementExecutor* errorHandler,
SlimList* args) {
return new SingleCharacterNameOperators;
}
void SingleCharacterNameOperators_Destroy(void *fixture) {
delete SingleCharacterNameOperators::From(fixture);
}
static char* query(void *fixture, SlimList *args) {
auto *self = SingleCharacterNameOperators::From(fixture);
return self->buildResult();
}
SLIM_CREATE_FIXTURE(SingleCharacterNameOperators)
SLIM_FUNCTION(query)
SLIM_END
}
This code makes use of a utility class QueryResultAccumulator. This is available in source from github (warning, as of this writing it’s in a raw form):
QueryResultAccumulator.h
#pragma once
#ifndef QUERYRESULTACCUMULATOR_H_
#defineQUERYRESULTACCUMULATOR_H_
class SlimList;
#include <vector>
#include <string>
class QueryResultAccumulator {
public:
typedef std::vector<SlimList*> v_SlimList;
typedef v_SlimList::iterator iterator;
QueryResultAccumulator();
virtual ~QueryResultAccumulator();
void finishCurrentObject();
void addFieldNamedWithValue(const std::string &name, const std::string &value);
char *produceFinalResults();
private:
SlimList* allocate();
void releaseAll();
void setInitialConditions();
private:
v_SlimList created;
SlimList *list;
SlimList *currentObject;
int lastFieldCount;
int currentFieldCount;
char *result;
private:
QueryResultAccumulator(const QueryResultAccumulator&);
QueryResultAccumulator& operator=(const QueryResultAccumulator&);
};
#endif
QueryResultAccumulator.cpp
#include "QueryResultAccumulator.h"
#include "DifferentFieldCountsInObjects.h"
#include "InvalidStateException.h"
extern "C" {
#include "SlimList.h"
#include "SlimListSerializer.h"
}
QueryResultAccumulator::QueryResultAccumulator() : result(0) {
setInitialConditions();
}
QueryResultAccumulator::~QueryResultAccumulator() {
releaseAll();
SlimList_Release(result);
}
void QueryResultAccumulator::setInitialConditions() {
releaseAll();
list = allocate();
currentObject = allocate();
lastFieldCount = -1;
currentFieldCount = -1;
}
SlimList* QueryResultAccumulator::allocate() {
SlimList *list = SlimList_Create();
created.push_back(list);
return list;
}
void QueryResultAccumulator::releaseAll() {
for (iterator i = created.begin(); i != created.end(); ++i)
SlimList_Destroy(*i);
created.clear();
}
void QueryResultAccumulator::finishCurrentObject() {
if(lastFieldCount >= 0 && lastFieldCount != currentFieldCount)
throw DifferentFieldCountsInObjects(lastFieldCount, currentFieldCount);
SlimList_AddList(list, currentObject);
currentObject = allocate();
lastFieldCount = currentFieldCount;
currentFieldCount = -1;
}
void QueryResultAccumulator::addFieldNamedWithValue(const std::string &name, const std::string &value) {
SlimList *fieldList = allocate();
SlimList_AddString(fieldList, name.c_str());
SlimList_AddString(fieldList, value.c_str());
SlimList_AddList(currentObject, fieldList);
++currentFieldCount;
}
char* QueryResultAccumulator::produceFinalResults() {
if(currentFieldCount != -1)
throw InvalidStateException("Current object not written");
SlimList_Release(result);
result = SlimList_Serialize(list);
setInitialConditions();
return result;
}
Comments