Port of schuchert.wikispaces.com


cpptraining.ExecutingBinaryOperators

cpptraining.ExecutingBinaryOperators

Background

These steps assuem you have already worked through the tutorial: Getting Started With FitNesse in C++.

Building the RpnCalcualtor

You can find the source for the RpnCalculator at two github locations:

Building the Basic Structure

[~/src]% mkdir waat 
[~/src]% cd waat
/Users/schuchert/src/waat
[~/src/waat]% mkdir workspace
[~/src/waat]% cd workspace 
/Users/schuchert/src/waat/workspace
git clone git://github.com/schuchert/RpnCalculatorInCpp.git
[~/src/waat/workspace]% git clone git://github.com/schuchert/RpnCalculatorInCpp.git
Initialized empty Git repository in /Users/schuchert/src/waat/workspace/RpnCalculatorInCpp/.git/
remote: Counting objects: 67, done.
remote: Compressing objects: 100% (65/65), done.
remote: Total 67 (delta 7), reused 0 (delta 0)
Receiving objects: 100% (67/67), 12.17 KiB, done.
Resolving deltas: 100% (7/7), done.
[~/src/waat/workspace]% ls
RpnCalculatorInCpp/
git clone git://github.com/schuchert/RpnCalculatorInCppTests.git
[~/src/waat/workspace]% git clone git://github.com/schuchert/RpnCalculatorInCppTests.git
Initialized empty Git repository in /Users/schuchert/src/waat/workspace/RpnCalculatorInCppTests/.git/
remote: Counting objects: 37, done.
remote: Compressing objects: 100% (36/36), done.
remote: Total 37 (delta 9), reused 0 (delta 0)
Receiving objects: 100% (37/37), 8.87 KiB, done.
Resolving deltas: 100% (9/9), done.
[~/src/waat/workspace]% ls
RpnCalculatorInCpp/        RpnCalculatorInCppTests/
[~/src/waat/workspace]% 

Configuring the Eclipse Project

In previous tutorial, you created a directory called ~/src/cslim and under that is CppUTest. To create this project, we’ll use the equivalent of environment variables in Eclipse.

You should see your tests pass. As of this writing, there are 70 tests:

..................................................

....................

OK (70 tests, 70 ran, 108 checks, 0 ignored, 0 filtered out, 8 ms)

Creating Your First Test Table

Since you’ve already worked through Getting Started With FitNesse in C++, you have FitNesse installed somewhere. Start your FitNesse instance. Here’s what it looks like on my computer:

[~]% cd src/cpp_fitnesse 
/Users/schuchert/src/cpp_fitnesse
[~/src/cpp_fitnesse]% java -jar fitnesse.jar -p 8080
FitNesse (v20100711) Started...
    port:              8080
    root page:         fitnesse.wiki.FileSystemPage at ./FitNesseRoot
    logger:            none
    authenticator:     fitnesse.authentication.PromiscuousAuthenticator
    html page factory: fitnesse.html.HtmlPageFactory
    page version expiration set to 14 days.

Creating Top-Level Test Suite

Now that you have FitNesse started, create a top-level page for all of your work.

!contents -R2 -g -p -f -h

!define TEST_SYSTEM {slim}
!define TEST_RUNNER {/Users/schuchert/src/waat/workspace/RpnCalculatorFixtures/Debug/RpnCalculatorFixtures}

!define COMMAND_PATTERN {%m}

The TEST_RUNNER makes reference to a project/executable we have not yet created. Don’t worry, it’ll be there soon.

Creating First Test

!|ExecuteBinaryOperator    |
|lhs|rhs|operator|expected?|
|3  |4  |-       |-1       |
|5  |6  |*       |30       |

This page makes reference to a fixture that does not yet exist. As in the previous step, don’t worry. That’s next.

Creating Fixture Project

Now you’re going to add a third project to contain your fixtures. This involves creating a project, linking to the calculator.

#include "SocketServer.h"
#include "SlimConnectionHandler.h"
#include "TcpComLink.h"
#include <stdlib.h>
#include <string.h>
#include "SlimList.h"
#include "SlimListDeserializer.h"
#include "StatementExecutor.h"
#include "ListExecutor.h"
#include "SlimListSerializer.h"

void AddFixtures(StatementExecutor* executor);
char * handle_slim_message(char * message);

int connection_handler(int socket)
{
    int result = 0;
    TcpComLink * comLink = TcpComLink_Create(socket);
    SlimConnectionHandler* connection = SlimConnectionHandler_Create(&TcpComLink_send, &TcpComLink_recv, (void*)comLink);
    SlimConnectionHandler_RegisterSlimMessageHandler(connection, &handle_slim_message);

    result = SlimConnectionHandler_Run(connection);

    SlimConnectionHandler_Destroy(connection);
    TcpComLink_Destroy(comLink);
    
    return result;
}

static StatementExecutor * statementExecutor;
static ListExecutor * listExecutor;
int main(int ac, char** av)
{
    statementExecutor = StatementExecutor_Create();
    AddFixtures(statementExecutor);
    
    listExecutor = ListExecutor_Create(statementExecutor);
    
    SocketServer* server = SocketServer_Create();
    SocketServer_register_handler(server, &connection_handler);
        
    int result = SocketServer_Run(server, av[1]);
    
    SocketServer_Destroy(server);
    ListExecutor_Destroy(listExecutor);
    StatementExecutor_Destroy(statementExecutor);
    return result;
}

char * handle_slim_message(char * message)
{
    SlimList* instructions = SlimList_Deserialize(message);
    SlimList* results = ListExecutor_Execute(listExecutor, instructions);
    char * response = SlimList_Serialize(results);
    SlimList_Destroy(results);
    SlimList_Destroy(instructions);
    return response;
}

You’ll notice several warnings about unknown header files. Let’s fix that before moving on:

Next, create another new file called Fixtures.c:

#include "Fixtures.h"

SLIM_FIXTURES
  SLIM_FIXTURE(ExecuteBinaryOperator)
SLIM_END

I’m having you preemptively add in the name of a fixture you have yet to write.

Now it’s time to create the fixture. Since this is a mechanics tutorial, I’ll give you the fixture source code:

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
 
}

You’ll notice a few more warnings about unknown include files. You’ll add a few more include directories, this time under the C++ tab instead of the C Tab

Notice that there’s still one missing header file. This is in another library that I’ve written to make writing C++ cslim fixtures a bit easer:

git clone git://github.com/schuchert/CSlimCppExtensions.git
[~/src/waat/workspace]% git clone git://github.com/schuchert/CSlimCppExtensions.git
Initialized empty Git repository in /Users/schuchert/src/waat/workspace/CSlimCppExtensions/.git/
remote: Counting objects: 16, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 16 (delta 1), reused 0 (delta 0)
Receiving objects: 100% (16/16), 5.95 KiB, done.
Resolving deltas: 100% (1/1), done.
[~/src/waat/workspace]% 

Updating CSlim Library

/include/CSlim/SlimListSerializer.h

Add the following function declaration to the header file:

void SlimList_Release(char *serializedList);

/src/CSlim/SlimListSerializer.h

Add a function declaration to the source file:

void SlimList_Release(char *serializedList) {
  if(serializedList != 0)
    free(serializedList);
}

Now that you have the required missing project, you need to add it as a dependent project and include its header files in the RpnCalcualtorFixtures project:

Now if you try to build the project, it will compile but it will not link.

You should now be able to run this executable. If you do, you’ll see in red text:

getaddrinfo: nodename nor servname provided, or not known

Running the Test

You should be able to run your FitNesse test and get to green.

Working with a Script Table

Now it is time to program the calculator. First a test, then the fixture code.

The Test

Create the following test at: http://localhost:8080/RpnExamples.SumOfPrimesExample

!|script           |ProgramTheCalculator|
|startProgramCalled|sumOfPrimeFactors   |
|addOperation      |primeFactors        |
|addOperation      |sum                 |
|saveProgram                            |
|enter             |12                  |
|execute           |sumOfPrimeFactors   |
|check             |getX       |7       |

This creates a new operator called “sumOfPrimesFactors” and then executes it. To make this work, you’ll need to create a new fixture and register it.

Creating the Fixture

Create a new source filed called ProgramTheCalcualtor.cpp. Here’s the source:

#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;
    char lastResult[32];
};

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 0;
}

static char* addOperation(void *fixture, SlimList *args) {
    auto *self = ProgramTheCalculator::From(fixture);
    self->calculator.addOperator(getFirstString(args));
    return 0;
}

static char* saveProgram(void *fixture, SlimList *args) {
    auto *self = ProgramTheCalculator::From(fixture);
    self->calculator.saveProgram();
    return 0;
}

static char* enter(void *fixture, SlimList *args) {
    auto *self = ProgramTheCalculator::From(fixture);
    self->calculator.enterNumber(getFirstInt(args));
    return 0;
}

static char* execute(void *fixture, SlimList *args) {
    auto *self = ProgramTheCalculator::From(fixture);
    self->calculator.executeOperator(getFirstString(args));
    return 0;
}

static char* getX(void *fixture, SlimList *args) {
    ProgramTheCalculator *self = ProgramTheCalculator::From(fixture);
    snprintf(self->lastResult, sizeof(self->lastResult), "%d", self->calculator.getX());
    return self->lastResult;
}

SLIM_CREATE_FIXTURE(ProgramTheCalculator)
    SLIM_FUNCTION(startProgramCalled)
    SLIM_FUNCTION(addOperation)
    SLIM_FUNCTION(saveProgram)
    SLIM_FUNCTION(enter)
    SLIM_FUNCTION(execute)
    SLIM_FUNCTION(getX)
SLIM_END

}

To get this working, you’ll need to register the fixture by updated Fixtures.c:

#include "Fixtures.h"

SLIM_FIXTURES
  SLIM_FIXTURE(ExecuteBinaryOperator)
  SLIM_FIXTURE(ProgramTheCalculator)
SLIM_END

Once you make these changes and rebuild, you should have a passing test.

The Query Table

Here is an example of a query table:

!|Query: AlphaNamedOperators|
|op                         |
|dup                        |
|if                         |
|ifElse                     |
|ndup                       |
|nop                        |
|primeFactors               |
|sum                        |
|swap                       |
|drop                       |

Create this page at: http://localhost:8080/RpnExamples.AlphaNamedOperatorsExample

Now you’ll need to create a fixture. Create a new source file called AlphaNamedOperators.cpp:

#include <ctype.h>
#include <stdlib.h>
#include <string>
#include "RpnCalculator.h"
#include "OperatorFactory.h"
#include "Fixtures.h"
#include "SlimUtils.h"
#include "QueryResultAccumulator.h"

struct AlphaNamedOperators {
    OperatorFactory factory;
    RpnCalculator calculator;
    QueryResultAccumulator accumulator;

    AlphaNamedOperators() :
        calculator(factory) {
    }

    ~AlphaNamedOperators() {
    }

    static AlphaNamedOperators* From(void *fixtureStorage) {
        return reinterpret_cast<AlphaNamedOperators*> (fixtureStorage);
    }

    void conditionallyAddOperatorNamed(const std::string &name) {
        if (isalpha(name[0])) {
            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* AlphaNamedOperators_Create(StatementExecutor* errorHandler,
        SlimList* args) {
    return new AlphaNamedOperators;
}

void AlphaNamedOperators_Destroy(void *fixture) {
    delete AlphaNamedOperators::From(fixture);
}

static char* query(void *fixture, SlimList *args) {
    auto *self = AlphaNamedOperators::From(fixture);
    return self->buildResult();
}

SLIM_CREATE_FIXTURE(AlphaNamedOperators)
    SLIM_FUNCTION(query)SLIM_END

}

To get this registered, you’ll have to update Fixtures.c:

#include "Fixtures.h"

SLIM_FIXTURES
  SLIM_FIXTURE(ExecuteBinaryOperator)
  SLIM_FIXTURE(ProgramTheCalculator)
  SLIM_FIXTURE(AlphaNamedOperators)
SLIM_END

Save and rebuild. You should now have a passing test. If you run the entire suite, all three tests should pass.


Comments

" Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.