annotate external/oscpack/tests/OscUnitTests.cpp @ 509:0284d2152e17

Add support for outputting featutes using OSC (for use with the Wekinator, etc).
author tomwalters@google.com
date Fri, 22 Jun 2012 12:22:08 +0000
parents
children
rev   line source
tomwalters@509 1 /*
tomwalters@509 2 oscpack -- Open Sound Control packet manipulation library
tomwalters@509 3 http://www.audiomulch.com/~rossb/oscpack
tomwalters@509 4
tomwalters@509 5 Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com>
tomwalters@509 6
tomwalters@509 7 Permission is hereby granted, free of charge, to any person obtaining
tomwalters@509 8 a copy of this software and associated documentation files
tomwalters@509 9 (the "Software"), to deal in the Software without restriction,
tomwalters@509 10 including without limitation the rights to use, copy, modify, merge,
tomwalters@509 11 publish, distribute, sublicense, and/or sell copies of the Software,
tomwalters@509 12 and to permit persons to whom the Software is furnished to do so,
tomwalters@509 13 subject to the following conditions:
tomwalters@509 14
tomwalters@509 15 The above copyright notice and this permission notice shall be
tomwalters@509 16 included in all copies or substantial portions of the Software.
tomwalters@509 17
tomwalters@509 18 Any person wishing to distribute modifications to the Software is
tomwalters@509 19 requested to send the modifications to the original developer so that
tomwalters@509 20 they can be incorporated into the canonical version.
tomwalters@509 21
tomwalters@509 22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
tomwalters@509 23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
tomwalters@509 24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
tomwalters@509 25 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
tomwalters@509 26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
tomwalters@509 27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
tomwalters@509 28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
tomwalters@509 29 */
tomwalters@509 30 #include "OscUnitTests.h"
tomwalters@509 31
tomwalters@509 32 #include <iostream>
tomwalters@509 33 #include <iomanip>
tomwalters@509 34
tomwalters@509 35 #include "osc/OscReceivedElements.h"
tomwalters@509 36 #include "osc/OscPrintReceivedElements.h"
tomwalters@509 37 #include "osc/OscOutboundPacketStream.h"
tomwalters@509 38
tomwalters@509 39
tomwalters@509 40 namespace osc{
tomwalters@509 41
tomwalters@509 42 static int passCount_=0, failCount_=0;
tomwalters@509 43
tomwalters@509 44 void PrintTestSummary()
tomwalters@509 45 {
tomwalters@509 46 std::cout << (passCount_+failCount_) << " tests run, " << passCount_ << " passed, " << failCount_ << " failed.\n";
tomwalters@509 47 }
tomwalters@509 48
tomwalters@509 49 void pass_equality( const char *slhs, const char *srhs, const char *file, int line )
tomwalters@509 50 {
tomwalters@509 51 ++passCount_;
tomwalters@509 52 std::cout << file << "(" << line << "): PASSED : " << slhs << " == " << srhs << "\n";
tomwalters@509 53 }
tomwalters@509 54
tomwalters@509 55 void fail_equality( const char *slhs, const char *srhs, const char *file, int line )
tomwalters@509 56 {
tomwalters@509 57 ++failCount_;
tomwalters@509 58 std::cout << file << "(" << line << "): FAILED : " << slhs << " != " << srhs << "\n";
tomwalters@509 59 }
tomwalters@509 60
tomwalters@509 61 template <typename T>
tomwalters@509 62 void assertEqual_( const T& lhs, const T& rhs, const char *slhs, const char *srhs, const char *file, int line )
tomwalters@509 63 {
tomwalters@509 64 if( lhs == rhs )
tomwalters@509 65 pass_equality( slhs, srhs, file, line );
tomwalters@509 66 else
tomwalters@509 67 fail_equality( slhs, srhs, file, line );
tomwalters@509 68 }
tomwalters@509 69
tomwalters@509 70 template <typename T>
tomwalters@509 71 void assertEqual_( const T* lhs, const T* rhs, const char *slhs, const char *srhs, const char *file, int line )
tomwalters@509 72 {
tomwalters@509 73 if( lhs == rhs )
tomwalters@509 74 pass_equality( slhs, srhs, file, line );
tomwalters@509 75 else
tomwalters@509 76 fail_equality( slhs, srhs, file, line );
tomwalters@509 77 }
tomwalters@509 78
tomwalters@509 79 template <>
tomwalters@509 80 void assertEqual_( const char* lhs, const char* rhs, const char *slhs, const char *srhs, const char *file, int line )
tomwalters@509 81 {
tomwalters@509 82 if( strcmp( lhs, rhs ) == 0 )
tomwalters@509 83 pass_equality( slhs, srhs, file, line );
tomwalters@509 84 else
tomwalters@509 85 fail_equality( slhs, srhs, file, line );
tomwalters@509 86 }
tomwalters@509 87
tomwalters@509 88
tomwalters@509 89 #define assertEqual( a, b ) assertEqual_( (a), (b), #a, #b, __FILE__, __LINE__ )
tomwalters@509 90
tomwalters@509 91 //---------------------------------------------------------------------------
tomwalters@509 92 char * AllocateAligned4( unsigned long size )
tomwalters@509 93 {
tomwalters@509 94 char *s = new char[ size + 4 ]; //allocate on stack to get 4 byte alignment
tomwalters@509 95 return (char*)((long)(s-1) & (~0x03L)) + 4;
tomwalters@509 96 }
tomwalters@509 97
tomwalters@509 98 // allocate a 4 byte aligned copy of s
tomwalters@509 99 char * NewMessageBuffer( const char *s, unsigned long length )
tomwalters@509 100 {
tomwalters@509 101 char *p = AllocateAligned4( length );
tomwalters@509 102 memcpy( p, s, length );
tomwalters@509 103 return p;
tomwalters@509 104 }
tomwalters@509 105
tomwalters@509 106 void test1()
tomwalters@509 107 {
tomwalters@509 108 const char s[] = "/test\0\0\0,fiT\0\0\0\0\0\0\0\0\0\0\0A";
tomwalters@509 109 char *buffer = NewMessageBuffer( s, sizeof(s)-1 );
tomwalters@509 110
tomwalters@509 111 // test argument iterator interface
tomwalters@509 112 bool unexpectedExceptionCaught = false;
tomwalters@509 113 try{
tomwalters@509 114 ReceivedMessage m( ReceivedPacket(buffer, sizeof(s)-1) );
tomwalters@509 115
tomwalters@509 116 assertEqual( strcmp( m.AddressPattern(), "/test" ), 0 );
tomwalters@509 117 assertEqual( strcmp( m.TypeTags(), "fiT" ), 0 );
tomwalters@509 118
tomwalters@509 119 ReceivedMessage::const_iterator i = m.ArgumentsBegin();
tomwalters@509 120 ++i;
tomwalters@509 121 ++i;
tomwalters@509 122 ++i;
tomwalters@509 123 assertEqual( i, m.ArgumentsEnd() );
tomwalters@509 124
tomwalters@509 125 i = m.ArgumentsBegin();
tomwalters@509 126 float f = (i++)->AsFloat();
tomwalters@509 127 (void)f;
tomwalters@509 128 int n = (i++)->AsInt32();
tomwalters@509 129 (void)n;
tomwalters@509 130 bool b = (i++)->AsBool();
tomwalters@509 131 (void)b;
tomwalters@509 132
tomwalters@509 133 i = m.ArgumentsBegin();
tomwalters@509 134 bool exceptionThrown = false;
tomwalters@509 135 try{
tomwalters@509 136 int n = (i++)->AsInt32();
tomwalters@509 137 (void)n;
tomwalters@509 138 }catch( Exception& ){
tomwalters@509 139 exceptionThrown = true;
tomwalters@509 140 }
tomwalters@509 141 assertEqual( exceptionThrown, true );
tomwalters@509 142
tomwalters@509 143 }catch( Exception& e ){
tomwalters@509 144 std::cout << "unexpected exception: " << e.what() << "\n";
tomwalters@509 145 unexpectedExceptionCaught = true;
tomwalters@509 146 }
tomwalters@509 147 assertEqual( unexpectedExceptionCaught, false );
tomwalters@509 148
tomwalters@509 149
tomwalters@509 150 // test argument stream interface
tomwalters@509 151 unexpectedExceptionCaught = false;
tomwalters@509 152 try{
tomwalters@509 153 ReceivedMessage m( ReceivedPacket(buffer, sizeof(s)-1) );
tomwalters@509 154 ReceivedMessageArgumentStream args = m.ArgumentStream();
tomwalters@509 155 assertEqual( args.Eos(), false );
tomwalters@509 156
tomwalters@509 157 float f;
tomwalters@509 158 long n;
tomwalters@509 159 bool b;
tomwalters@509 160 args >> f >> n >> b;
tomwalters@509 161
tomwalters@509 162 (void) f;
tomwalters@509 163 (void) n;
tomwalters@509 164 (void) b;
tomwalters@509 165
tomwalters@509 166 assertEqual( args.Eos(), true );
tomwalters@509 167
tomwalters@509 168 }catch( Exception& e ){
tomwalters@509 169 std::cout << "unexpected exception: " << e.what() << "\n";
tomwalters@509 170 unexpectedExceptionCaught = true;
tomwalters@509 171 }
tomwalters@509 172 assertEqual( unexpectedExceptionCaught, false );
tomwalters@509 173 }
tomwalters@509 174
tomwalters@509 175 //---------------------------------------------------------------------------
tomwalters@509 176
tomwalters@509 177
tomwalters@509 178 #define TEST2_PRINT( ss )\
tomwalters@509 179 {\
tomwalters@509 180 const char s[] = ss;\
tomwalters@509 181 ReceivedPacket p( NewMessageBuffer( s, sizeof(s)-1 ), sizeof(s)-1 ); \
tomwalters@509 182 ReceivedMessage m( p );\
tomwalters@509 183 std::cout << m << "\n";\
tomwalters@509 184 }
tomwalters@509 185
tomwalters@509 186 void test2()
tomwalters@509 187 {
tomwalters@509 188 bool unexpectedExceptionCaught = false;
tomwalters@509 189 try{
tomwalters@509 190 // 012301230 1 2 3
tomwalters@509 191 TEST2_PRINT( "/no_args\0\0\0\0" );
tomwalters@509 192
tomwalters@509 193 // 012301230 1 2 3 01 2 3
tomwalters@509 194 TEST2_PRINT( "/no_args\0\0\0\0,\0\0\0" );
tomwalters@509 195
tomwalters@509 196 // 01230123 012 3 0 1 2 3
tomwalters@509 197 TEST2_PRINT( "/an_int\0,i\0\0\0\0\0A" );
tomwalters@509 198 // 012301230 1 2 3 012 3 0 1 2 3
tomwalters@509 199 TEST2_PRINT( "/a_float\0\0\0\0,f\0\0\0\0\0\0" );
tomwalters@509 200 // 0123012301 2 3 012 3 012301230123
tomwalters@509 201 TEST2_PRINT( "/a_string\0\0\0,s\0\0hello world\0" );
tomwalters@509 202 // 01230123 012 3 0 1 2 3 0 1 2 3
tomwalters@509 203 TEST2_PRINT( "/a_blob\0,b\0\0\0\0\0\x4\x0\x1\x2\x3" );
tomwalters@509 204
tomwalters@509 205 // 0123012301 2 3 012 3 0 1 2 3 0 1 2 3
tomwalters@509 206 TEST2_PRINT( "/an_int64\0\0\0,h\0\0\0\0\0\0\0\0\0\x1" );
tomwalters@509 207 // 01230123012 3 012 3 0 1 2 3 0 1 2 3
tomwalters@509 208 TEST2_PRINT( "/a_timetag\0\0,t\0\0\0\0\0\0\0\0\0\x1" );
tomwalters@509 209 // 0123012301 2 3 012 3 0 1 2 3 0 1 2 3
tomwalters@509 210 TEST2_PRINT( "/a_double\0\0\0,d\0\0\0\0\0\0\0\0\0\0" );
tomwalters@509 211 // 0123012301 2 3 012 3 012301230123
tomwalters@509 212 TEST2_PRINT( "/a_symbol\0\0\0,S\0\0hello world\0" );
tomwalters@509 213 // 01230123 012 3 0 1 2 3
tomwalters@509 214 TEST2_PRINT( "/a_char\0,c\0\0\0\0\0A" );
tomwalters@509 215 // 012301230 1 2 3 012 3 0 1 2 3
tomwalters@509 216 TEST2_PRINT( "/a_color\0\0\0\0,r\0\0\0\0\0\0" );
tomwalters@509 217 // 012301230123012 3 012 3 0 1 2 3
tomwalters@509 218 TEST2_PRINT( "/a_midimessage\0\0,m\0\0\0\0\0\0" );
tomwalters@509 219 // 01230123 012 3
tomwalters@509 220 TEST2_PRINT( "/a_bool\0,T\0\0" );
tomwalters@509 221 // 01230123 012 3
tomwalters@509 222 TEST2_PRINT( "/a_bool\0,F\0\0" );
tomwalters@509 223 // 01230 1 2 3 012 3
tomwalters@509 224 TEST2_PRINT( "/Nil\0\0\0\0,N\0\0" );
tomwalters@509 225 // 01230 1 2 3 012 3
tomwalters@509 226 TEST2_PRINT( "/Inf\0\0\0\0,I\0\0" );
tomwalters@509 227
tomwalters@509 228 TEST2_PRINT( "/test\0\0\0,fiT\0\0\0\0\0\0\0\0\0\0\0A" );
tomwalters@509 229
tomwalters@509 230 bool exceptionThrown = false;
tomwalters@509 231 try{
tomwalters@509 232 TEST2_PRINT( "/a_char\0,x\0\0\0\0\0A" ); // unknown type tag 'x'
tomwalters@509 233 }catch( Exception& ){
tomwalters@509 234 exceptionThrown = true;
tomwalters@509 235 }
tomwalters@509 236 assertEqual( exceptionThrown, true );
tomwalters@509 237
tomwalters@509 238 }catch( Exception& e ){
tomwalters@509 239 std::cout << "unexpected exception: " << e.what() << "\n";
tomwalters@509 240 unexpectedExceptionCaught = true;
tomwalters@509 241 }
tomwalters@509 242 assertEqual( unexpectedExceptionCaught, false );
tomwalters@509 243 }
tomwalters@509 244
tomwalters@509 245 //-----------------------------------------------------------------------
tomwalters@509 246
tomwalters@509 247 // pack a message and then unpack it and check that the result is the same
tomwalters@509 248 // also print each message
tomwalters@509 249 // repeat the process inside a bundle
tomwalters@509 250
tomwalters@509 251 #define TEST_PACK_UNPACK0( addressPattern, argument, value, recieveGetter ) \
tomwalters@509 252 { \
tomwalters@509 253 memset( buffer, 0x74, bufferSize ); \
tomwalters@509 254 OutboundPacketStream ps( buffer, bufferSize ); \
tomwalters@509 255 ps << BeginMessage( addressPattern ) \
tomwalters@509 256 << argument \
tomwalters@509 257 << EndMessage;\
tomwalters@509 258 assertEqual( ps.IsReady(), true );\
tomwalters@509 259 ReceivedMessage m( ReceivedPacket(ps.Data(), ps.Size()) );\
tomwalters@509 260 std::cout << m << "\n";\
tomwalters@509 261 assertEqual( m.ArgumentsBegin()-> recieveGetter () , value );\
tomwalters@509 262 } \
tomwalters@509 263 { \
tomwalters@509 264 memset( buffer, 0x74, bufferSize ); \
tomwalters@509 265 OutboundPacketStream ps( buffer, bufferSize ); \
tomwalters@509 266 ps << BeginBundle( 1234 ) \
tomwalters@509 267 << BeginMessage( addressPattern ) \
tomwalters@509 268 << argument \
tomwalters@509 269 << EndMessage \
tomwalters@509 270 << EndBundle;\
tomwalters@509 271 assertEqual( ps.IsReady(), true );\
tomwalters@509 272 ReceivedBundle b( ReceivedPacket(ps.Data(), ps.Size()) );\
tomwalters@509 273 ReceivedMessage m( *b.ElementsBegin() );\
tomwalters@509 274 std::cout << m << "\n";\
tomwalters@509 275 assertEqual( m.ArgumentsBegin()-> recieveGetter () , value );\
tomwalters@509 276 }
tomwalters@509 277
tomwalters@509 278 #define TEST_PACK_UNPACK( addressPattern, argument, type, recieveGetter ) \
tomwalters@509 279 { \
tomwalters@509 280 memset( buffer, 0x74, bufferSize ); \
tomwalters@509 281 OutboundPacketStream ps( buffer, bufferSize ); \
tomwalters@509 282 ps << BeginMessage( addressPattern ) \
tomwalters@509 283 << argument \
tomwalters@509 284 << EndMessage;\
tomwalters@509 285 assertEqual( ps.IsReady(), true );\
tomwalters@509 286 ReceivedMessage m( ReceivedPacket(ps.Data(), ps.Size()) );\
tomwalters@509 287 std::cout << m << "\n";\
tomwalters@509 288 assertEqual( m.ArgumentsBegin()-> recieveGetter () , ( type ) argument );\
tomwalters@509 289 } \
tomwalters@509 290 { \
tomwalters@509 291 memset( buffer, 0x74, bufferSize ); \
tomwalters@509 292 OutboundPacketStream ps( buffer, bufferSize ); \
tomwalters@509 293 ps << BeginBundle( 1234 ) \
tomwalters@509 294 << BeginMessage( addressPattern ) \
tomwalters@509 295 << argument \
tomwalters@509 296 << EndMessage \
tomwalters@509 297 << EndBundle;\
tomwalters@509 298 assertEqual( ps.IsReady(), true );\
tomwalters@509 299 ReceivedBundle b( ReceivedPacket(ps.Data(), ps.Size()) );\
tomwalters@509 300 ReceivedMessage m( *b.ElementsBegin() );\
tomwalters@509 301 std::cout << m << "\n";\
tomwalters@509 302 assertEqual( m.ArgumentsBegin()-> recieveGetter () , ( type ) argument );\
tomwalters@509 303 }
tomwalters@509 304
tomwalters@509 305 void test3()
tomwalters@509 306 {
tomwalters@509 307 int bufferSize = 1000;
tomwalters@509 308 char *buffer = AllocateAligned4( bufferSize );
tomwalters@509 309
tomwalters@509 310 // single message tests
tomwalters@509 311 // empty message
tomwalters@509 312 {
tomwalters@509 313 memset( buffer, 0x74, bufferSize );
tomwalters@509 314 OutboundPacketStream ps( buffer, bufferSize );
tomwalters@509 315 ps << BeginMessage( "/no_arguments" )
tomwalters@509 316 << EndMessage;
tomwalters@509 317 assertEqual( ps.IsReady(), true );
tomwalters@509 318 ReceivedMessage m( ReceivedPacket(ps.Data(), ps.Size()) );
tomwalters@509 319 std::cout << m << "\n";\
tomwalters@509 320 }
tomwalters@509 321
tomwalters@509 322 TEST_PACK_UNPACK( "/a_bool", true, bool, AsBool );
tomwalters@509 323 TEST_PACK_UNPACK( "/a_bool", false, bool, AsBool );
tomwalters@509 324 TEST_PACK_UNPACK( "/a_bool", (bool)1, bool, AsBool );
tomwalters@509 325
tomwalters@509 326 TEST_PACK_UNPACK0( "/nil", Nil, true, IsNil );
tomwalters@509 327 TEST_PACK_UNPACK0( "/inf", Infinitum, true, IsInfinitum );
tomwalters@509 328
tomwalters@509 329 TEST_PACK_UNPACK( "/an_int", (int32)1234, int32, AsInt32 );
tomwalters@509 330
tomwalters@509 331 TEST_PACK_UNPACK( "/a_float", 3.1415926f, float, AsFloat );
tomwalters@509 332
tomwalters@509 333 TEST_PACK_UNPACK( "/a_char", 'c', char, AsChar );
tomwalters@509 334
tomwalters@509 335 TEST_PACK_UNPACK( "/an_rgba_color", RgbaColor(0x22334455), uint32, AsRgbaColor );
tomwalters@509 336
tomwalters@509 337 TEST_PACK_UNPACK( "/a_midi_message", MidiMessage(0x7F), uint32, AsMidiMessage );
tomwalters@509 338
tomwalters@509 339 TEST_PACK_UNPACK( "/an_int64", (int64)(0xFFFFFFFF), int64, AsInt64 );
tomwalters@509 340
tomwalters@509 341 TEST_PACK_UNPACK( "/a_time_tag", TimeTag(0xFFFFFFFF), uint64, AsTimeTag );
tomwalters@509 342
tomwalters@509 343 TEST_PACK_UNPACK( "/a_double", (double)3.1415926, double, AsDouble );
tomwalters@509 344
tomwalters@509 345 // blob
tomwalters@509 346 {
tomwalters@509 347 char blobData[] = "abcd";
tomwalters@509 348 memset( buffer, 0x74, bufferSize );
tomwalters@509 349 OutboundPacketStream ps( buffer, bufferSize );
tomwalters@509 350 ps << BeginMessage( "/a_blob" )
tomwalters@509 351 << Blob( blobData, 4 )
tomwalters@509 352 << EndMessage;
tomwalters@509 353 assertEqual( ps.IsReady(), true );
tomwalters@509 354 ReceivedMessage m( ReceivedPacket(ps.Data(), ps.Size()) );
tomwalters@509 355 std::cout << m << "\n";
tomwalters@509 356
tomwalters@509 357 const void *value;
tomwalters@509 358 unsigned long size;
tomwalters@509 359 m.ArgumentsBegin()->AsBlob( value, size );
tomwalters@509 360 assertEqual( size, (unsigned long)4 );
tomwalters@509 361 assertEqual( (memcmp( value, blobData, 4 ) == 0), true );
tomwalters@509 362 }
tomwalters@509 363
tomwalters@509 364
tomwalters@509 365 TEST_PACK_UNPACK( "/a_string", "hello world", const char*, AsString );
tomwalters@509 366
tomwalters@509 367 TEST_PACK_UNPACK( "/a_symbol", Symbol("foobar"), const char*, AsSymbol );
tomwalters@509 368
tomwalters@509 369
tomwalters@509 370 // nested bundles, and multiple messages in bundles...
tomwalters@509 371
tomwalters@509 372 {
tomwalters@509 373 memset( buffer, 0x74, bufferSize );
tomwalters@509 374 OutboundPacketStream ps( buffer, bufferSize );
tomwalters@509 375 ps << BeginBundle()
tomwalters@509 376 << BeginMessage( "/message_one" ) << 1 << 2 << 3 << 4 << EndMessage
tomwalters@509 377 << BeginMessage( "/message_two" ) << 1 << 2 << 3 << 4 << EndMessage
tomwalters@509 378 << BeginMessage( "/message_three" ) << 1 << 2 << 3 << 4 << EndMessage
tomwalters@509 379 << BeginMessage( "/message_four" ) << 1 << 2 << 3 << 4 << EndMessage
tomwalters@509 380 << EndBundle;
tomwalters@509 381 assertEqual( ps.IsReady(), true );
tomwalters@509 382 ReceivedBundle b( ReceivedPacket(ps.Data(), ps.Size()) );
tomwalters@509 383 std::cout << b << "\n";
tomwalters@509 384 }
tomwalters@509 385 }
tomwalters@509 386
tomwalters@509 387
tomwalters@509 388 void RunUnitTests()
tomwalters@509 389 {
tomwalters@509 390 test1();
tomwalters@509 391 test2();
tomwalters@509 392 test3();
tomwalters@509 393 PrintTestSummary();
tomwalters@509 394 }
tomwalters@509 395
tomwalters@509 396 } // namespace osc
tomwalters@509 397
tomwalters@509 398
tomwalters@509 399 #ifndef NO_OSC_TEST_MAIN
tomwalters@509 400
tomwalters@509 401 int main(int argc, char* argv[])
tomwalters@509 402 {
tomwalters@509 403 (void)argc;
tomwalters@509 404 (void)argv;
tomwalters@509 405
tomwalters@509 406 osc::RunUnitTests();
tomwalters@509 407 }
tomwalters@509 408
tomwalters@509 409 #endif