You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
7.0 KiB
241 lines
7.0 KiB
// License: Apache 2.0. See LICENSE file in root directory.
|
|
// Copyright(c) 2023 Intel Corporation. All Rights Reserved.
|
|
|
|
#include <unit-tests/catch.h>
|
|
#include <rsutils/easylogging/easyloggingpp.h>
|
|
#include <rsutils/signal.h>
|
|
#include <ostream>
|
|
|
|
|
|
// Many of the ideas behind testing this came from:
|
|
// https://github.com/tarqd/slimsig/blob/master/test/test.cpp
|
|
|
|
|
|
TEST_CASE( "signal", "[signal]" )
|
|
{
|
|
SECTION( "should trigger bound member function slots" )
|
|
{
|
|
struct C
|
|
{
|
|
bool bound_slot_triggered = false;
|
|
void bound_slot() { bound_slot_triggered = true; }
|
|
} obj;
|
|
rsutils::signal<> signal;
|
|
signal.add( std::bind( &C::bound_slot, &obj ) );
|
|
signal.raise();
|
|
CHECK( obj.bound_slot_triggered );
|
|
}
|
|
SECTION( "should trigger functor slots" )
|
|
{
|
|
static bool functor_slot_triggered = false;
|
|
struct C
|
|
{
|
|
void operator()() { functor_slot_triggered = true; }
|
|
} obj;
|
|
rsutils::signal<> signal;
|
|
signal.add( obj ); // makes a copy of obj!
|
|
signal.raise();
|
|
CHECK( functor_slot_triggered );
|
|
}
|
|
SECTION( "should trigger lambda slots" )
|
|
{
|
|
bool fired = false;
|
|
rsutils::signal<> signal;
|
|
signal.add( [&] { fired = true; } );
|
|
signal.raise();
|
|
CHECK( fired );
|
|
}
|
|
}
|
|
|
|
|
|
TEST_CASE( "raise()", "[signal]" )
|
|
{
|
|
SECTION( "should NOT perfectly forward r-value references" )
|
|
{
|
|
// Explanation: passing r-value refs (&&) will cause the first slot to be constructed
|
|
// with SECTION -- essentially moving SECTION -- and the second slot to get nothing!
|
|
rsutils::signal< std::string > signal;
|
|
std::string str( "hello world" );
|
|
signal.add( []( std::string str ) { CHECK( str == "hello world" ); } );
|
|
signal.add( []( std::string str ) { CHECK( str == "hello world" ); } );
|
|
signal.raise( std::move( str ) );
|
|
}
|
|
SECTION( "should not copy references" )
|
|
{
|
|
rsutils::signal< std::string & > signal;
|
|
std::string str( "hello world" );
|
|
signal.add(
|
|
[]( std::string & str )
|
|
{
|
|
CHECK( str == "hello world" );
|
|
str = "hola mundo";
|
|
} );
|
|
signal.add( []( std::string & str ) { CHECK( str == "hola mundo" ); } );
|
|
signal.raise( str );
|
|
}
|
|
SECTION( "should be re-entrant" )
|
|
{
|
|
unsigned count = 0;
|
|
rsutils::signal<> signal;
|
|
signal.add(
|
|
[&]
|
|
{
|
|
++count;
|
|
if( count == 1 )
|
|
{
|
|
signal.add( [&] { ++count; } );
|
|
signal.raise();
|
|
};
|
|
} );
|
|
signal.raise();
|
|
CHECK( count == 3 );
|
|
}
|
|
}
|
|
|
|
|
|
TEST_CASE( "size()", "[signal]" )
|
|
{
|
|
SECTION( "should return the subscription count" )
|
|
{
|
|
rsutils::signal<> signal;
|
|
signal.add( [] {} );
|
|
CHECK( signal.size() == 1 );
|
|
}
|
|
SECTION( "should return the correct count when adding slots during iteration" )
|
|
{
|
|
rsutils::signal<> signal;
|
|
signal.add(
|
|
[&]
|
|
{
|
|
signal.add( [] {} );
|
|
CHECK( signal.size() == 2 );
|
|
} );
|
|
signal.raise();
|
|
CHECK( signal.size() == 2 );
|
|
}
|
|
}
|
|
|
|
|
|
TEST_CASE( "subscription", "[signal]" )
|
|
{
|
|
rsutils::signal<> signal;
|
|
|
|
SECTION( "cancel() should disconnect the slot" )
|
|
{
|
|
bool fired = false;
|
|
auto subscription = signal.subscribe( [&] { fired = true; } );
|
|
subscription.cancel();
|
|
CHECK( signal.size() == 0 );
|
|
signal.raise();
|
|
CHECK_FALSE( fired );
|
|
}
|
|
SECTION( "cancel() should not throw if already disconnected" )
|
|
{
|
|
auto subscription = signal.subscribe( [] {} );
|
|
subscription.cancel();
|
|
subscription.cancel();
|
|
CHECK( signal.size() == 0 );
|
|
}
|
|
SECTION( "should automatically remove the slot" )
|
|
{
|
|
bool fired = false;
|
|
auto fn = [&] { fired = true; };
|
|
{
|
|
auto subscription = signal.subscribe( fn );
|
|
}
|
|
CHECK( signal.size() == 0 );
|
|
signal.raise();
|
|
CHECK_FALSE( fired );
|
|
}
|
|
SECTION( "unless detached" )
|
|
{
|
|
bool fired = false;
|
|
auto fn = [&] { fired = true; };
|
|
{
|
|
signal.subscribe( fn ).detach();
|
|
}
|
|
CHECK( signal.size() == 1 );
|
|
signal.raise();
|
|
CHECK( fired );
|
|
}
|
|
SECTION( "should still be valid if the signal is destroyed" )
|
|
{
|
|
rsutils::subscription subscription;
|
|
{
|
|
rsutils::signal<> scoped_signal;
|
|
subscription = scoped_signal.subscribe( [] {} );
|
|
}
|
|
}
|
|
SECTION( "replacing subscription with another should cancel the previous" )
|
|
{
|
|
std::string val;
|
|
auto subscription = signal.subscribe( [&val] { val += '1'; } );
|
|
CHECK( signal.size() == 1 );
|
|
signal.raise();
|
|
CHECK( val == "1" );
|
|
subscription = signal.subscribe( [&val] { val += '2'; } );
|
|
CHECK( signal.size() == 1 );
|
|
signal.raise();
|
|
CHECK( val == "12" );
|
|
subscription = {};
|
|
CHECK( signal.size() == 0 );
|
|
}
|
|
SECTION( "detach() followed by cancel()" )
|
|
{
|
|
std::string val;
|
|
auto subscription = signal.subscribe( [&val] { val += '1'; } );
|
|
CHECK( signal.size() == 1 );
|
|
subscription.detach(); // should no longer be valid
|
|
CHECK( signal.size() == 1 );
|
|
subscription.cancel(); // make sure!
|
|
CHECK( signal.size() == 1 ); // nothing should have happened!
|
|
}
|
|
}
|
|
|
|
|
|
TEST_CASE( "stress test", "[signal]" )
|
|
{
|
|
rsutils::signal< int & > signal;
|
|
constexpr size_t N = 100000;
|
|
|
|
// add subscriptions
|
|
{
|
|
auto before = std::chrono::high_resolution_clock::now();
|
|
for( int expected = 0; expected < N; ++expected )
|
|
{
|
|
signal.add(
|
|
[expected]( int & i )
|
|
{
|
|
CHECK( i == expected );
|
|
++i;
|
|
} );
|
|
}
|
|
auto after = std::chrono::high_resolution_clock::now();
|
|
std::cout << " took " << ((after - before).count() / 1e9) << " seconds to add" << std::endl;
|
|
CHECK( signal.size() == N );
|
|
int i = 0;
|
|
before = std::chrono::high_resolution_clock::now();
|
|
signal.raise( i );
|
|
after = std::chrono::high_resolution_clock::now();
|
|
std::cout << " took " << ((after - before).count() / 1e9) << " seconds to raise" << std::endl;
|
|
}
|
|
// remove all but last
|
|
{
|
|
for( int pos = 0; pos < N - 1; ++pos )
|
|
{
|
|
CAPTURE( pos );
|
|
CHECK( signal.remove( pos ) == true );
|
|
}
|
|
CHECK( signal.size() == 1 );
|
|
int i = int( N ) - 1; // the only one left
|
|
signal.raise( i );
|
|
}
|
|
// add one -> should iterate AFTER the last
|
|
{
|
|
auto slot_id = signal.add( []( int & i ) { i = -25; } );
|
|
int i = int( N ) - 1; // the only one left
|
|
signal.raise( i );
|
|
CHECK( i == -25 );
|
|
}
|
|
}
|