Tuesday, January 26, 2010

Smashing The Stack (continued)

In the last post i looked at one way of handling exceptions that come out of C++ when wrapping classes to be called from C. On the upside this code looks very similar to how I'd wrap exception free code. On the downside the exception handling callbacks aren't very flexible and result in an uglier interface.

By restructuring the C interface slightly we can get what I think is a much cleaner interface.
In summary all C functions return an int indicating success/failure, this means the old return values must be passed in as a pointer. We also add a function to return the error message if the call fails. Anyway here's the new header

 1 #pragma once
 2
 3 typedef struct 
 4 {
 5   int x;
 6   int y;
 7 } CLocation;
 8
 9 typedef struct HMyMap HMyMap;
10
11 int MyMap_create( HMyMap**  );
12 void MyMap_destroy( HMyMap *h );
13
14 int MyMap_addKV( HMyMap *h, const char *k, CLocation v );
15 int MyMap_getV( HMyMap *h, const char *k, CLocation *v);
16 char* MyMap_getErrorMessage( HMyMap *h );

And heres example code using this:
#include "simpletest.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
  HMyMap *h;

  if( ! MyMap_create(&h) )
  {
    printf("ERROR: FAILURE TO CREATE\n");
    return 1;
  }

  CLocation loc = {2, 3};
  if( ! MyMap_addKV(h, "test", loc) )
  {
    printf("ERROR: FAILURE %s\n", MyMap_getErrorMessage(h) );
    return 1;
  }
  if( ! MyMap_addKV(h, "test", loc) )
  {
    printf("THIS SHOULD FAIL : FAILURE %s\n", MyMap_getErrorMessage(h) );
  }

  CLocation v;
  if( ! MyMap_getV(h, "test", &v) )
  {
    printf("ERROR: FAILURE %s\n", MyMap_getErrorMessage(h) );
    return 1;
  }
  printf("%s : (%d, %d)\n", "test", v.x, v.y );
  CLocation w;
  if( ! MyMap_getV(h, "monkey", &w) )
  {
    printf("THIS SHOULD FAIL: FAILURE %s\n", MyMap_getErrorMessage(h) );
  }

  MyMap_destroy(h);
  return 0;
}

And finally an extract from the implementation:
18 struct HMyMap : public MyMap
19 {
20   HMyMap() : MyMap(), error_message() {}
21   std::string error_message;
22 };
23
24 int MyMap_create( HMyMap**h )
25 {
26   *h = new HMyMap();
27   return 1;
28 }
29
30 void MyMap_destroy( HMyMap *h )
31 {
32   delete h;
33 }
34
35 int MyMap_addKV( HMyMap *h, const char *k, CLocation v )
36   try
37 {
38   h->addKV( k, pair_from_cloc(v) );
39   return 1;
40 }
41 catch( MyMap::Exception & e )
42 {
43   h->error_message = e.mesg_;
44   return 0;
45 }
46
47 int MyMap_getV( HMyMap *h, const char *k, CLocation *v)
48   try
49 {
50   *v = cloc_from_pair( h->getV(k) );
51   return 1;
52 }
53 catch( MyMap::Exception & e )
54 {
55   h->error_message = e.mesg_;
56   return 0;
57 }
58
59 char * MyMap_getErrorMessage( HMyMap * h )
60 {
61   return const_cast<char*>(h->error_message.c_str());
62 }

So not much trickier than the other implementation, and now that I've built them both I consider this way of doing the wrapping to be easier to work with.

Sorry theres not much discussion of the code this time. But hopefully it will still be helpful to someone.

Monday, January 18, 2010

Smashing The Stack

This is the first post in a "series" I'll be writing. It's primarily going to be a pointer to a post I find interesting on StackOverflow. Often this will be a post that I've answered or asked, but hey thats just my bias ;)
So this first article is about calling C++ functions and classes from C. So this solution covers the simple case. Usesr asked about how we can handle exceptions and allocating small objects on the stack rather than the heap. I'm going to show you a way that these can be addressed.
There's a few key issues.
  1. Any exception that hits the C++/C bridge will crash your application
  2. You can only catch exceptions in C++
  3. Catching _all_ exceptions by catch(...) can leave your application in an invalid/unrecoverable state. Some compilers (VC++) will allow catch(...) to catch things like segmentation faults etc. The only sane  thing to do in that case is immediately exit. 
  4. Other exception may be recoverable, and we need a way to handle this.
Here's the source of the C++ class we're going to access from C
 1 #pragma once
 2
 3 #include <iostream>
 4 #include <map>
 5
 6 class MyMap
 7 {
 8   public:
 9     struct Exception
10     {
11       explicit Exception(const std::string & mesg) : mesg_(mesg) {}
12       std::string mesg_;
13     };
14
15   void addKV( const std::string & key, const std::pair<int,int> & value );
16   std::pair<int,int> getV( const std::string & key ) const;
17   std::map<std::string,std::pair<int,int> > map_;
18 };

Heres what my C header looks like

 1 #pragma once
 2
 3 typedef struct
 4 {
 5   int x;
 6   int y;
 7 } CLocation;
 8
 9 typedef struct HMyMap HMyMap;
10
11 HMyMap* MyMap_create( void(*handler)(const char*) );
12 void MyMap_destroy( HMyMap *h );
13
14 void MyMap_addKV( HMyMap *h, const char *k, CLocation v );
15 CLocation MyMap_getV( HMyMap *h, const char *k );

And the implementation:

 1 extern "C"
 2 {
 3 #include "simpletest.h"
 4 }
 5 #include "MyClass.h"
 6
 7 std::pair<int,int> pair_from_cloc( const CLocation & loc )
 8 {
 9   return std::make_pair(loc.x,loc.y);
10 }
11
12 CLocation cloc_from_pair( const std::pair<int,int> & loc )
13 {
14   CLocation cloc = { loc.first, loc.second };
15   return cloc;
16 }
17
18 struct HMyMap : public MyMap
19 {
20   HMyMap( void(*eh)(const char*) ) : MyMap(), handler(eh) {}
21   void(*handler)(const char*);
22 };
23
24 HMyMap* MyMap_create( void(*eh)(const char*) )
25 {
26   return new HMyMap(eh);
27 }
28
29 void MyMap_destroy( HMyMap *h )
30 {
31   delete h;
32 }
33
34 void MyMap_addKV( HMyMap *h, const char *k, CLocation v )
35   try
36 {
37   h->addKV( k, pair_from_cloc(v) );
38 }
39 catch( MyMap::Exception & e )
40 {
41   if( ! h->handler ) throw;
42   h->handler( e.mesg_.c_str() );
43 }
44
45 CLocation MyMap_getV( HMyMap *h, const char *k )
46   try
47 {
48   return cloc_from_pair( h->getV(k) );
49 }
50 catch( MyMap::Exception & e )
51 {
52   if( ! h->handler ) throw;
53   h->handler( e.mesg_.c_str() );
54   return cloc_from_pair(std::make_pair<int,int>(0,0));
55 }
Things to note are exposing std::pair<int,int> as CLocation struct, (rather than opaque handle). Allowing specification of an exception handler in the HMyMap class.
I'm not 100% happy with the way the exception handling is done, and will show another option in another post.

Monday, January 11, 2010

Debugging non-xcode code in xcode

We have some C++ code that we build using makefile system. We even use this on OS X where we could build using XCode instead, but we prefer a uniformity of build methods.

Now when a nasty and hard to trace bug crops up on an OS X build box we could try to track down the bug using good old GDB. However while I feel everyone should be able to use GDB I get sick of constantly making GDB give me what info I want. XCodes built in debugger does this nicely - in fact its just a front end to GDB. The question is how do we get our non-xcode binary to be debugged under XCode.

Turns out its EASY. (Well 90% easy).

  1. Create an empty project. ( XCode > File > New Project... )
  2. Add your binary as the default executable. (XCode > Project > New Custom Executable... )


Now you should be able to run your app through the debugger. But how do you set breakpoints? You can add your source files to the project by dragging them from finder into the XCode project sidebar. Then double click the file to open it in the XCode editor, and click in the left side by the line numbers to set breakpoints.

This worked for me most of the time. However I still had some issues setting certain breakpoints. On clicking in certain files I would get the error message
"Warning - No location found for "foo.cpp:12"
The reason for this was the file was being built from a subdirectory - its real name was autogen/foo.cpp.
So to get around this we can set the breakpoint from the gdb console. (XCode > Run > Console )
Typing "break foo.cpp:12" into the console works, presumably due to the lack of quote marks around the file/line pair.

Thursday, December 24, 2009

What's new in saru

So to support some of our testing infrastructure I've added some features to saru. The ones of note are:

Support for skipped tests. These are tests that don't run for whatever reason. In our case it was some tests that aren't fully implemented, that we didn't want showing up as fails. Skipped tests now show up in their own count, and tests can have sub tests that are skipped.

Logging of test history into a sqlite database. Now all test results and test output is stored into a sqlite database. We use this to create pass/fail charts for the tests. This has been invaluable in tracking down regressions and intermittent failures.

Thursday, December 10, 2009

Merging Git Repositories

There's a bunch of ways of merging git repositories. Here's the one that I find easiest. Say I've developed some test features in directory/repository A that I want to start to use in directory/repository B


First I package everything into a temporary directory inside A in preparation for moving

cd /path/to/A     
mkdir A_merge     
git mv * A_merge     
git mv .gitignore A_merge/.gitignore
git commit -m"Preparation for merging A into B"


Then I create a branch in B for merging into
cd /path/to/B
git checkout -b merge_A_into_B
Next get the stuff from A's master branch and merge with the current branch.
git fetch file:///path/to/A 'refs/heads/*:refs/remotes/A/*'
git merge A/master
All the stuff that you need should now be in /path/to/B/A_merge.
Move this directory and its contents to whereever you want and commit.

Thursday, October 29, 2009

Multiple Tests in a single saru file

Testing with saru is supposed to be easy to do in any language.
So the interface to support multiple tests from a single file has to be easy...

All you need do is print the right stuff to stdout and stderr and return the right value and you're done.

In the single test case all that mattered was the return value, everything else was just informational in the case of failure.

So what should the output look like to make multiple tests work... well here's a sample.

STDOUT
test_00_dummy_pass: OK
test_01_dummy_fail: FAILED
1/2

STDERR
<@saru start test_00_dummy_pass @>
Some info about the dummy_pass test
<@saru end test_00_dummy_pass @>
<@saru start test_01_dummy_fail @>
This test fails 
And so this message will appear in the test output
<@saru end test_01_dummy_fail @>

Whipping up a python script that prints these outputs and running it through
saru-run-test suite .
gives the following results
test.py::test_00_dummy_pass : OK
test.py::test_01_dummy_fail : FAILED???
==MESSAGE==

==STDERR==
This test fails 
And so this message will appear in the test output


1 / 2

Admitadly the output is not that pretty, and the mechanism is not prefectly robust.
But it has done everything I need from running multiple tests from a single file.

Of course you'd probably want to write a helper library to get that outputting correct.
And to help you use "good testing practices" like fixtures.
Some of these helper libraries already exist. The C++ one is already part of saru, as a pure header. There is a python helper library that will be added shortly, and a PHP library that is in development.

I might look at how to use the C++ library in a future post.

Wednesday, October 21, 2009

Testing with saru

So everyone should be running tests on their code. We have hundreds and they're never enough. But how to run and collate results from all these tests. There are plenty of testing frameworks out there, but each one seems married to a particular language. What if parts of your code are in python, parts in C++, parts in php etc. You've been doing the right thing and using "the right tool for the job" but now you have a mish-mash of code. That was the case I was in a while ago, and I decided that I'd be better off having a testing system that could test a bunch of languages. So I wrote saru.

saru is opensource (BSD) and is the simplest testing framework I could come up with.
So how do you use it?

Heres an example test in python
#!/bin/python
# SARU : tag example
import sys
print >> sys.stderr, "Log message"
sys.exit(1)

The same thing again in C++
// SARU : tag example
#include <iostream>
int main()
{
  std::cerr << "Log message" << std::endl ;
  return 1;
}
The convention is that tests are single applications that return 1 for failure and 0 for success. To distinguish test files from other files such as mocks, fixtures or other helper code, tests are tagged with a SARU tag. Now to run these tests
saru-run-tests suite
We get the following output:
example00.py : FAILED???
==MESSAGE==
saru-run-test : execution of test failed with error code 1
==STDERR==
Log message


example01.cpp : FAILED???
==MESSAGE==
saru-run-test : execution of test failed with error code 1
==STDERR==
Log message


0 / 2
Lets change both of those files to return 0 and rerun the tests and we should get
example00.py : OK
example01.cpp : OK
2 / 2
Now this should also catch and report compilation errors in the C++. Theres a bunch of stuff not explained here that I'll detail in following posts including
  1. How to make multiple tests in a single file
  2. How to specify compiler options for C++
  3. What would need to happen to make saru work on windows
  4. How to run subsets of tests
  5. How to extend saru to run other languages
  6. What are these saru logs?
  7. Things that still need to be done to make saru cooler