Download src/3rdParty/FastSignals/tests/libfastsignals_unit_tests/signal_tests.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 15.7 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/3rdParty/FastSignals/tests/libfastsignals_unit_tests/signal_tests.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/3rdParty/FastSignals/tests/libfastsignals_unit_tests/signal_tests.cpp
-
curl -L -o signal_tests.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/3rdParty/FastSignals/tests/libfastsignals_unit_tests/signal_tests.cpp
15.7 kB
| using namespace fastsignals; | |
| using namespace std::literals; | |
| namespace | |
| { | |
| template <class T> | |
| class any_of_combiner | |
| { | |
| public: | |
| static_assert(std::is_same_v<T, bool>); | |
| using result_type = bool; | |
| template <class TRef> | |
| void operator()(TRef&& value) | |
| { | |
| m_result = m_result || bool(value); | |
| } | |
| result_type get_value() const | |
| { | |
| return m_result; | |
| } | |
| private: | |
| result_type m_result = {}; | |
| }; | |
| } // namespace | |
| TEST_CASE("Can connect a few slots and emit", "[signal]") | |
| { | |
| signal<void(int)> valueChanged; | |
| int value1 = 0; | |
| int value2 = 0; | |
| valueChanged.connect([&value1](int value) { | |
| value1 = value; | |
| }); | |
| valueChanged.connect([&value2](int value) { | |
| value2 = value; | |
| }); | |
| REQUIRE(value1 == 0); | |
| REQUIRE(value2 == 0); | |
| valueChanged(10); | |
| REQUIRE(value1 == 10); | |
| REQUIRE(value2 == 10); | |
| } | |
| TEST_CASE("Can safely pass rvalues", "[signal]") | |
| { | |
| const std::string expected = "If the type T is a reference type, provides the member typedef type which is the type referred to by T. Otherwise type is T."; | |
| std::string passedValue = expected; | |
| signal<void(std::string)> valueChanged; | |
| std::string value1; | |
| std::string value2; | |
| valueChanged.connect([&value1](std::string value) { | |
| value1 = value; | |
| }); | |
| valueChanged.connect([&value2](std::string value) { | |
| value2 = value; | |
| }); | |
| valueChanged(std::move(passedValue)); | |
| REQUIRE(value1 == expected); | |
| REQUIRE(value2 == expected); | |
| } | |
| TEST_CASE("Can pass mutable ref", "[signal]") | |
| { | |
| const std::string expected = "If the type T is a reference type, provides the member typedef type which is the type referred to by T. Otherwise type is T."; | |
| signal<void(std::string&)> valueChanged; | |
| std::string passedValue; | |
| valueChanged.connect([expected](std::string& value) { | |
| value = expected; | |
| }); | |
| valueChanged(passedValue); | |
| REQUIRE(passedValue == expected); | |
| } | |
| TEST_CASE("Can disconnect slot with explicit call", "[signal]") | |
| { | |
| signal<void(int)> valueChanged; | |
| int value1 = 0; | |
| int value2 = 0; | |
| int value3 = 0; | |
| auto conn1 = valueChanged.connect([&value1](int value) { | |
| value1 = value; | |
| }); | |
| auto conn2 = valueChanged.connect([&value2](int value) { | |
| value2 = value; | |
| }); | |
| valueChanged.connect([&value3](int value) { | |
| value3 = value; | |
| }); | |
| REQUIRE(value1 == 0); | |
| REQUIRE(value2 == 0); | |
| REQUIRE(value3 == 0); | |
| valueChanged(10); | |
| REQUIRE(value1 == 10); | |
| REQUIRE(value2 == 10); | |
| REQUIRE(value3 == 10); | |
| conn2.disconnect(); | |
| valueChanged(-99); | |
| REQUIRE(value1 == -99); | |
| REQUIRE(value2 == 10); | |
| REQUIRE(value3 == -99); | |
| conn1.disconnect(); | |
| valueChanged(17); | |
| REQUIRE(value1 == -99); | |
| REQUIRE(value2 == 10); | |
| REQUIRE(value3 == 17); | |
| } | |
| TEST_CASE("Can disconnect slot with scoped_connection", "[signal]") | |
| { | |
| signal<void(int)> valueChanged; | |
| int value1 = 0; | |
| int value2 = 0; | |
| int value3 = 0; | |
| { | |
| scoped_connection conn1 = valueChanged.connect([&value1](int value) { | |
| value1 = value; | |
| }); | |
| { | |
| scoped_connection conn2 = valueChanged.connect([&value2](int value) { | |
| value2 = value; | |
| }); | |
| valueChanged.connect([&value3](int value) { | |
| value3 = value; | |
| }); | |
| REQUIRE(value1 == 0); | |
| REQUIRE(value2 == 0); | |
| REQUIRE(value3 == 0); | |
| valueChanged(10); | |
| REQUIRE(value1 == 10); | |
| REQUIRE(value2 == 10); | |
| REQUIRE(value3 == 10); | |
| } | |
| // conn2 disconnected. | |
| valueChanged(-99); | |
| REQUIRE(value1 == -99); | |
| REQUIRE(value2 == 10); | |
| REQUIRE(value3 == -99); | |
| } | |
| // conn1 disconnected. | |
| valueChanged(17); | |
| REQUIRE(value1 == -99); | |
| REQUIRE(value2 == 10); | |
| REQUIRE(value3 == 17); | |
| } | |
| TEST_CASE("Can disconnect all", "[signal]") | |
| { | |
| signal<void(int)> valueChanged; | |
| int value1 = 0; | |
| int value2 = 0; | |
| int value3 = 0; | |
| valueChanged.connect([&value1](int value) { | |
| value1 = value; | |
| }); | |
| valueChanged.connect([&value2](int value) { | |
| value2 = value; | |
| }); | |
| valueChanged.connect([&value3](int value) { | |
| value3 = value; | |
| }); | |
| REQUIRE(value1 == 0); | |
| REQUIRE(value2 == 0); | |
| REQUIRE(value3 == 0); | |
| valueChanged(63); | |
| REQUIRE(value1 == 63); | |
| REQUIRE(value2 == 63); | |
| REQUIRE(value3 == 63); | |
| valueChanged.disconnect_all_slots(); | |
| valueChanged(101); | |
| REQUIRE(value1 == 63); | |
| REQUIRE(value2 == 63); | |
| REQUIRE(value3 == 63); | |
| } | |
| TEST_CASE("Can disconnect inside slot", "[signal]") | |
| { | |
| signal<void(int)> valueChanged; | |
| int value1 = 0; | |
| int value2 = 0; | |
| int value3 = 0; | |
| connection conn2; | |
| valueChanged.connect([&value1](int value) { | |
| value1 = value; | |
| }); | |
| conn2 = valueChanged.connect([&](int value) { | |
| value2 = value; | |
| conn2.disconnect(); | |
| }); | |
| valueChanged.connect([&value3](int value) { | |
| value3 = value; | |
| }); | |
| REQUIRE(value1 == 0); | |
| REQUIRE(value2 == 0); | |
| REQUIRE(value3 == 0); | |
| valueChanged(63); | |
| REQUIRE(value1 == 63); | |
| REQUIRE(value2 == 63); | |
| REQUIRE(value3 == 63); | |
| valueChanged(101); | |
| REQUIRE(value1 == 101); | |
| REQUIRE(value2 == 63); // disconnected in slot. | |
| REQUIRE(value3 == 101); | |
| } | |
| TEST_CASE("Disconnects OK if signal dead first", "[signal]") | |
| { | |
| connection conn2; | |
| { | |
| scoped_connection conn1; | |
| { | |
| signal<void(int)> valueChanged; | |
| conn2 = valueChanged.connect([](int) { | |
| }); | |
| // Just unused. | |
| valueChanged.connect([](int) { | |
| }); | |
| conn1 = valueChanged.connect([](int) { | |
| }); | |
| } | |
| REQUIRE(conn2.connected()); | |
| REQUIRE(conn1.connected()); | |
| conn2.disconnect(); | |
| REQUIRE(!conn2.connected()); | |
| REQUIRE(conn1.connected()); | |
| } | |
| conn2.disconnect(); | |
| } | |
| TEST_CASE("Returns last called slot result with default combiner", "[signal]") | |
| { | |
| connection conn2; | |
| { | |
| scoped_connection conn1; | |
| { | |
| signal<int(int)> absSignal; | |
| conn2 = absSignal.connect([](int value) { | |
| return value * value; | |
| }); | |
| conn1 = absSignal.connect([](int value) { | |
| return abs(value); | |
| }); | |
| absSignal(-1); | |
| REQUIRE(absSignal(45) == 45); | |
| REQUIRE(absSignal(-1) == 1); | |
| REQUIRE(absSignal(-177) == 177); | |
| REQUIRE(absSignal(0) == 0); | |
| } | |
| REQUIRE(conn2.connected()); | |
| conn2.disconnect(); | |
| REQUIRE(!conn2.connected()); | |
| } | |
| conn2.disconnect(); | |
| REQUIRE(!conn2.connected()); | |
| } | |
| TEST_CASE("Works with custom any_of combiner", "[signal]") | |
| { | |
| using cancellable_signal = signal<bool(std::string), any_of_combiner>; | |
| cancellable_signal startRequested; | |
| auto conn1 = startRequested.connect([](std::string op) { | |
| return op == "1"; | |
| }); | |
| auto conn2 = startRequested.connect([](std::string op) { | |
| return op == "1" || op == "2"; | |
| }); | |
| REQUIRE(startRequested("0") == false); | |
| REQUIRE(startRequested("1") == true); | |
| REQUIRE(startRequested("2") == true); | |
| REQUIRE(startRequested("3") == false); | |
| conn1.disconnect(); | |
| conn2.disconnect(); | |
| REQUIRE(startRequested("0") == false); | |
| REQUIRE(startRequested("1") == false); | |
| REQUIRE(startRequested("2") == false); | |
| REQUIRE(startRequested("3") == false); | |
| } | |
| TEST_CASE("Can release scoped connection", "[signal]") | |
| { | |
| int value2 = 0; | |
| int value3 = 0; | |
| signal<void(int)> valueChanged; | |
| connection conn1; | |
| { | |
| scoped_connection conn2; | |
| scoped_connection conn3; | |
| conn2 = valueChanged.connect([&value2](int x) { | |
| value2 = x; | |
| }); | |
| conn3 = valueChanged.connect([&value3](int x) { | |
| value3 = x; | |
| }); | |
| valueChanged(42); | |
| REQUIRE(value2 == 42); | |
| REQUIRE(value3 == 42); | |
| REQUIRE(conn2.connected()); | |
| REQUIRE(conn3.connected()); | |
| REQUIRE(!conn1.connected()); | |
| conn1 = conn3.release(); | |
| REQUIRE(conn2.connected()); | |
| REQUIRE(!conn3.connected()); | |
| REQUIRE(conn1.connected()); | |
| valueChanged(144); | |
| REQUIRE(value2 == 144); | |
| REQUIRE(value3 == 144); | |
| } | |
| // conn2 disconnected, conn1 connected. | |
| valueChanged(17); | |
| REQUIRE(value2 == 144); | |
| REQUIRE(value3 == 17); | |
| REQUIRE(conn1.connected()); | |
| conn1.disconnect(); | |
| valueChanged(90); | |
| REQUIRE(value2 == 144); | |
| REQUIRE(value3 == 17); | |
| } | |
| TEST_CASE("Can use signal with more than one argument", "[signal]") | |
| { | |
| signal<void(int, std::string, std::vector<std::string>)> event; | |
| int value1 = 0; | |
| std::string value2; | |
| std::vector<std::string> value3; | |
| event.connect([&](int v1, const std::string& v2, const std::vector<std::string>& v3) { | |
| value1 = v1; | |
| value2 = v2; | |
| value3 = v3; | |
| }); | |
| event(9815, "using namespace std::literals!"s, std::vector{ "std::vector"s, "using namespace std::literals"s }); | |
| REQUIRE(value1 == 9815); | |
| REQUIRE(value2 == "using namespace std::literals!"s); | |
| REQUIRE(value3 == std::vector{ "std::vector"s, "using namespace std::literals"s }); | |
| } | |
| TEST_CASE("Can blocks slots using shared_connection_block", "[signal]") | |
| { | |
| bool callbackShouldBeCalled = true; | |
| bool callbackCalled = false; | |
| const int value = 123; | |
| signal<void(int)> event; | |
| auto conn = event.connect([&](int gotValue) { | |
| CHECK(gotValue == value); | |
| callbackCalled = true; | |
| if (!callbackShouldBeCalled) | |
| { | |
| FAIL("callback is blocked and should not be called"); | |
| } | |
| }, | |
| advanced_tag{}); | |
| event(value); | |
| REQUIRE(callbackCalled); | |
| shared_connection_block block(conn); | |
| callbackShouldBeCalled = false; | |
| callbackCalled = false; | |
| event(value); | |
| REQUIRE(!callbackCalled); | |
| block.unblock(); | |
| callbackShouldBeCalled = true; | |
| event(value); | |
| REQUIRE(callbackCalled); | |
| } | |
| TEST_CASE("Other slots are unaffected by the block", "[signal]") | |
| { | |
| bool callback1Called = false; | |
| bool callback2Called = false; | |
| const int value = 123; | |
| signal<void(int)> event; | |
| auto conn1 = event.connect([&](int gotValue) { | |
| CHECK(gotValue == value); | |
| callback1Called = true; | |
| }, | |
| advanced_tag{}); | |
| auto conn2 = event.connect([&](int) { | |
| callback2Called = true; | |
| FAIL("callback is blocked and should not be called"); | |
| }, | |
| advanced_tag{}); | |
| shared_connection_block block(conn2); | |
| event(value); | |
| REQUIRE(callback1Called); | |
| REQUIRE(!callback2Called); | |
| } | |
| TEST_CASE("Multiple blocks block until last one is unblocked", "[signal]") | |
| { | |
| bool callbackShouldBeCalled = false; | |
| bool callbackCalled = false; | |
| const int value = 123; | |
| signal<void(int)> event; | |
| auto conn = event.connect([&](int gotValue) { | |
| CHECK(gotValue == value); | |
| callbackCalled = true; | |
| if (!callbackShouldBeCalled) | |
| { | |
| FAIL("callback is blocked and should not be called"); | |
| } | |
| }, | |
| advanced_tag{}); | |
| shared_connection_block block1(conn); | |
| shared_connection_block block2(conn); | |
| event(value); | |
| REQUIRE(!callbackCalled); | |
| block1.unblock(); | |
| event(value); | |
| REQUIRE(!callbackCalled); | |
| block1.block(); | |
| block2.unblock(); | |
| event(value); | |
| REQUIRE(!callbackCalled); | |
| block1.unblock(); | |
| callbackShouldBeCalled = true; | |
| event(value); | |
| REQUIRE(callbackCalled); | |
| } | |
| TEST_CASE("Can copy and move shared_connection_block objects", "[signal]") | |
| { | |
| bool callbackShouldBeCalled = false; | |
| bool callbackCalled = false; | |
| const int value = 123; | |
| signal<void(int)> event; | |
| auto conn = event.connect([&](int gotValue) { | |
| CHECK(gotValue == value); | |
| callbackCalled = true; | |
| if (!callbackShouldBeCalled) | |
| { | |
| FAIL("callback is blocked and should not be called"); | |
| } | |
| }, | |
| advanced_tag{}); | |
| shared_connection_block block1(conn); | |
| shared_connection_block block2(block1); | |
| event(value); | |
| REQUIRE(block1.blocking()); | |
| REQUIRE(block2.blocking()); | |
| REQUIRE(!callbackCalled); | |
| shared_connection_block block3(std::move(block2)); | |
| event(value); | |
| REQUIRE(block1.blocking()); | |
| REQUIRE(!block2.blocking()); | |
| REQUIRE(block3.blocking()); | |
| REQUIRE(!callbackCalled); | |
| block2 = block3; | |
| event(value); | |
| REQUIRE(block1.blocking()); | |
| REQUIRE(block2.blocking()); | |
| REQUIRE(block3.blocking()); | |
| REQUIRE(!callbackCalled); | |
| block3 = std::move(block2); | |
| event(value); | |
| REQUIRE(block1.blocking()); | |
| REQUIRE(!block2.blocking()); | |
| REQUIRE(block3.blocking()); | |
| REQUIRE(!callbackCalled); | |
| block3 = shared_connection_block(conn, false); | |
| event(value); | |
| REQUIRE(block1.blocking()); | |
| REQUIRE(!block2.blocking()); | |
| REQUIRE(!block3.blocking()); | |
| REQUIRE(!callbackCalled); | |
| block1.unblock(); | |
| callbackShouldBeCalled = true; | |
| event(value); | |
| REQUIRE(!block1.blocking()); | |
| REQUIRE(!block2.blocking()); | |
| REQUIRE(!block3.blocking()); | |
| REQUIRE(callbackCalled); | |
| } | |
| TEST_CASE("Unblocks when shared_connection_block goes out of scope") | |
| { | |
| bool callbackCalled = false; | |
| const int value = 123; | |
| signal<void(int)> event; | |
| auto conn = event.connect([&](int gotValue) { | |
| CHECK(gotValue == value); | |
| callbackCalled = true; | |
| }, | |
| advanced_tag{}); | |
| callbackCalled = false; | |
| event(value); | |
| CHECK(callbackCalled); | |
| { | |
| callbackCalled = false; | |
| shared_connection_block block(conn); | |
| event(value); | |
| CHECK(!callbackCalled); | |
| { | |
| callbackCalled = false; | |
| shared_connection_block block2(conn); | |
| event(value); | |
| CHECK(!callbackCalled); | |
| } | |
| } | |
| callbackCalled = false; | |
| event(value); | |
| CHECK(callbackCalled); | |
| } | |
| TEST_CASE("Can disconnect advanced slot using advanced_scoped_connection", "[signal]") | |
| { | |
| signal<void(int)> valueChanged; | |
| int value1 = 0; | |
| int value2 = 0; | |
| int value3 = 0; | |
| { | |
| advanced_scoped_connection conn1 = valueChanged.connect([&value1](int value) { | |
| value1 = value; | |
| }, | |
| advanced_tag{}); | |
| { | |
| advanced_scoped_connection conn2 = valueChanged.connect([&value2](int value) { | |
| value2 = value; | |
| }, | |
| advanced_tag{}); | |
| valueChanged.connect([&value3](int value) { | |
| value3 = value; | |
| }); | |
| REQUIRE(value1 == 0); | |
| REQUIRE(value2 == 0); | |
| REQUIRE(value3 == 0); | |
| valueChanged(10); | |
| REQUIRE(value1 == 10); | |
| REQUIRE(value2 == 10); | |
| REQUIRE(value3 == 10); | |
| } | |
| // conn2 disconnected. | |
| valueChanged(-99); | |
| REQUIRE(value1 == -99); | |
| REQUIRE(value2 == 10); | |
| REQUIRE(value3 == -99); | |
| } | |
| // conn1 disconnected. | |
| valueChanged(17); | |
| REQUIRE(value1 == -99); | |
| REQUIRE(value2 == 10); | |
| REQUIRE(value3 == 17); | |
| } | |
| TEST_CASE("Can move signal", "[signal]") | |
| { | |
| signal<void()> src; | |
| int srcFireCount = 0; | |
| auto srcConn = src.connect([&srcFireCount] { | |
| ++srcFireCount; | |
| }); | |
| src(); | |
| REQUIRE(srcFireCount == 1); | |
| auto dst = std::move(src); | |
| int dstFireCount = 0; | |
| auto dstConn = dst.connect([&dstFireCount] { | |
| ++dstFireCount; | |
| }); | |
| dst(); | |
| REQUIRE(srcFireCount == 2); | |
| REQUIRE(dstFireCount == 1); | |
| srcConn.disconnect(); | |
| dstConn.disconnect(); | |
| dst(); | |
| REQUIRE(srcFireCount == 2); | |
| REQUIRE(dstFireCount == 1); | |
| } | |
| TEST_CASE("Can swap signals", "[signal]") | |
| { | |
| signal<void()> s1; | |
| signal<void()> s2; | |
| int s1FireCount = 0; | |
| int s2FireCount = 0; | |
| s1.connect([&s1FireCount] { | |
| ++s1FireCount; | |
| }); | |
| s2.connect([&s2FireCount] { | |
| ++s2FireCount; | |
| }); | |
| std::swap(s1, s2); | |
| s1(); | |
| REQUIRE(s1FireCount == 0); | |
| REQUIRE(s2FireCount == 1); | |
| s2(); | |
| REQUIRE(s1FireCount == 1); | |
| REQUIRE(s2FireCount == 1); | |
| } | |
| TEST_CASE("Signal can be destroyed inside its slot and will call the rest of its slots", "[signal]") | |
| { | |
| std::optional<signal<void()>> s; | |
| s.emplace(); | |
| s->connect([&] { | |
| s.reset(); | |
| }); | |
| bool called = false; | |
| s->connect([&] { | |
| called = true; | |
| }); | |
| (*s)(); | |
| CHECK(called); | |
| } | |
| TEST_CASE("Signal can be used as a slot for another signal", "[signal]") | |
| { | |
| signal<void()> s1; | |
| bool called = false; | |
| s1.connect([&] { | |
| called = true; | |
| }); | |
| signal<void()> s2; | |
| s2.connect(s1); | |
| s2(); | |
| CHECK(called); | |
| } | |
| // memory leak fix | |
| TEST_CASE("Releases lambda and its captured const data", "[signal]") | |
| { | |
| struct Captured | |
| { | |
| Captured(bool& released) | |
| : m_released(released) | |
| { | |
| } | |
| ~Captured() | |
| { | |
| m_released = true; | |
| } | |
| private: | |
| bool& m_released; | |
| }; | |
| bool released = false; | |
| { | |
| const auto captured = std::make_shared<Captured>(released); | |
| signal<void()> changeSignal; | |
| changeSignal.connect([captured]{}); | |
| } | |
| CHECK(released); | |
| } |