annotate oscpack/tests/OscUnitTests.cpp @ 101:52e44ee1c791 tip master

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