| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| /* |
| * Copyright (C) 2019, Google Inc. |
| * |
| * signal.cpp - Signal test |
| */ |
| |
| #include <iostream> |
| #include <string.h> |
| |
| #include <libcamera/base/object.h> |
| #include <libcamera/base/signal.h> |
| |
| #include "test.h" |
| |
| using namespace std; |
| using namespace libcamera; |
| |
| static int valueStatic_ = 0; |
| |
| static void slotStatic(int value) |
| { |
| valueStatic_ = value; |
| } |
| |
| static int slotStaticReturn() |
| { |
| return 0; |
| } |
| |
| class SlotObject : public Object |
| { |
| public: |
| void slot() |
| { |
| valueStatic_ = 1; |
| } |
| }; |
| |
| class BaseClass |
| { |
| public: |
| /* |
| * A virtual function is required in the base class, otherwise the |
| * compiler will always store Object before BaseClass in memory. |
| */ |
| virtual ~BaseClass() |
| { |
| } |
| |
| unsigned int data_[32]; |
| }; |
| |
| class SlotMulti : public BaseClass, public Object |
| { |
| public: |
| void slot() |
| { |
| valueStatic_ = 1; |
| } |
| }; |
| |
| class SignalTest : public Test |
| { |
| protected: |
| void slotVoid() |
| { |
| called_ = true; |
| } |
| |
| void slotDisconnect() |
| { |
| called_ = true; |
| signalVoid_.disconnect(this, &SignalTest::slotDisconnect); |
| } |
| |
| void slotInteger1(int value) |
| { |
| values_[0] = value; |
| } |
| |
| void slotInteger2(int value) |
| { |
| values_[1] = value; |
| } |
| |
| void slotMultiArgs(int value, const std::string &name) |
| { |
| values_[2] = value; |
| name_ = name; |
| } |
| |
| int slotReturn() |
| { |
| return 0; |
| } |
| |
| int init() |
| { |
| return 0; |
| } |
| |
| int run() |
| { |
| /* ----------------- Signal -> !Object tests ---------------- */ |
| |
| /* Test signal emission and reception. */ |
| called_ = false; |
| signalVoid_.connect(this, &SignalTest::slotVoid); |
| signalVoid_.emit(); |
| |
| if (!called_) { |
| cout << "Signal emission test failed" << endl; |
| return TestFail; |
| } |
| |
| /* Test signal with parameters. */ |
| values_[2] = 0; |
| name_.clear(); |
| signalMultiArgs_.connect(this, &SignalTest::slotMultiArgs); |
| signalMultiArgs_.emit(42, "H2G2"); |
| |
| if (values_[2] != 42 || name_ != "H2G2") { |
| cout << "Signal parameters test failed" << endl; |
| return TestFail; |
| } |
| |
| /* Test signal connected to multiple slots. */ |
| memset(values_, 0, sizeof(values_)); |
| valueStatic_ = 0; |
| signalInt_.connect(this, &SignalTest::slotInteger1); |
| signalInt_.connect(this, &SignalTest::slotInteger2); |
| signalInt_.connect(&slotStatic); |
| signalInt_.emit(42); |
| |
| if (values_[0] != 42 || values_[1] != 42 || values_[2] != 0 || |
| valueStatic_ != 42) { |
| cout << "Signal multi slot test failed" << endl; |
| return TestFail; |
| } |
| |
| /* Test disconnection of a single slot. */ |
| memset(values_, 0, sizeof(values_)); |
| signalInt_.disconnect(this, &SignalTest::slotInteger2); |
| signalInt_.emit(42); |
| |
| if (values_[0] != 42 || values_[1] != 0 || values_[2] != 0) { |
| cout << "Signal slot disconnection test failed" << endl; |
| return TestFail; |
| } |
| |
| /* Test disconnection of a whole object. */ |
| memset(values_, 0, sizeof(values_)); |
| signalInt_.disconnect(this); |
| signalInt_.emit(42); |
| |
| if (values_[0] != 0 || values_[1] != 0 || values_[2] != 0) { |
| cout << "Signal object disconnection test failed" << endl; |
| return TestFail; |
| } |
| |
| /* Test disconnection of a whole signal. */ |
| memset(values_, 0, sizeof(values_)); |
| signalInt_.connect(this, &SignalTest::slotInteger1); |
| signalInt_.connect(this, &SignalTest::slotInteger2); |
| signalInt_.disconnect(); |
| signalInt_.emit(42); |
| |
| if (values_[0] != 0 || values_[1] != 0 || values_[2] != 0) { |
| cout << "Signal object disconnection test failed" << endl; |
| return TestFail; |
| } |
| |
| /* Test disconnection from slot. */ |
| signalVoid_.disconnect(); |
| signalVoid_.connect(this, &SignalTest::slotDisconnect); |
| |
| signalVoid_.emit(); |
| called_ = false; |
| signalVoid_.emit(); |
| |
| if (called_) { |
| cout << "Signal disconnection from slot test failed" << endl; |
| return TestFail; |
| } |
| |
| /* |
| * Test connecting to slots that return a value. This targets |
| * compilation, there's no need to check runtime results. |
| */ |
| signalVoid_.connect(slotStaticReturn); |
| signalVoid_.connect(this, &SignalTest::slotReturn); |
| |
| /* Test signal connection to a lambda. */ |
| int value = 0; |
| signalInt_.connect(this, [&](int v) { value = v; }); |
| signalInt_.emit(42); |
| |
| if (value != 42) { |
| cout << "Signal connection to lambda failed" << endl; |
| return TestFail; |
| } |
| |
| signalInt_.disconnect(this); |
| signalInt_.emit(0); |
| |
| if (value != 42) { |
| cout << "Signal disconnection from lambda failed" << endl; |
| return TestFail; |
| } |
| |
| /* ----------------- Signal -> Object tests ----------------- */ |
| |
| /* |
| * Test automatic disconnection on object deletion. Connect two |
| * signals to ensure all instances are disconnected. |
| */ |
| signalVoid_.disconnect(); |
| signalVoid2_.disconnect(); |
| |
| SlotObject *slotObject = new SlotObject(); |
| signalVoid_.connect(slotObject, &SlotObject::slot); |
| signalVoid2_.connect(slotObject, &SlotObject::slot); |
| delete slotObject; |
| valueStatic_ = 0; |
| signalVoid_.emit(); |
| signalVoid2_.emit(); |
| if (valueStatic_ != 0) { |
| cout << "Signal disconnection on object deletion test failed" << endl; |
| return TestFail; |
| } |
| |
| /* |
| * Test that signal deletion disconnects objects. This shall |
| * not generate any valgrind warning. |
| */ |
| Signal<> *dynamicSignal = new Signal<>(); |
| slotObject = new SlotObject(); |
| dynamicSignal->connect(slotObject, &SlotObject::slot); |
| delete dynamicSignal; |
| delete slotObject; |
| |
| /* |
| * Test that signal manual disconnection from Object removes |
| * the signal for the object. This shall not generate any |
| * valgrind warning. |
| */ |
| dynamicSignal = new Signal<>(); |
| slotObject = new SlotObject(); |
| dynamicSignal->connect(slotObject, &SlotObject::slot); |
| dynamicSignal->disconnect(slotObject); |
| delete dynamicSignal; |
| delete slotObject; |
| |
| /* |
| * Test that signal manual disconnection from all slots removes |
| * the signal for the object. This shall not generate any |
| * valgrind warning. |
| */ |
| dynamicSignal = new Signal<>(); |
| slotObject = new SlotObject(); |
| dynamicSignal->connect(slotObject, &SlotObject::slot); |
| dynamicSignal->disconnect(); |
| delete dynamicSignal; |
| delete slotObject; |
| |
| /* Exercise the Object slot code paths. */ |
| slotObject = new SlotObject(); |
| signalVoid_.connect(slotObject, &SlotObject::slot); |
| valueStatic_ = 0; |
| signalVoid_.emit(); |
| if (valueStatic_ == 0) { |
| cout << "Signal delivery for Object test failed" << endl; |
| return TestFail; |
| } |
| |
| delete slotObject; |
| |
| /* Test signal connection to a lambda. */ |
| slotObject = new SlotObject(); |
| value = 0; |
| signalInt_.connect(slotObject, [&](int v) { value = v; }); |
| signalInt_.emit(42); |
| |
| if (value != 42) { |
| cout << "Signal connection to Object lambda failed" << endl; |
| return TestFail; |
| } |
| |
| signalInt_.disconnect(slotObject); |
| signalInt_.emit(0); |
| |
| if (value != 42) { |
| cout << "Signal disconnection from Object lambda failed" << endl; |
| return TestFail; |
| } |
| |
| delete slotObject; |
| |
| /* --------- Signal -> Object (multiple inheritance) -------- */ |
| |
| /* |
| * Test automatic disconnection on object deletion. Connect two |
| * signals to ensure all instances are disconnected. |
| */ |
| signalVoid_.disconnect(); |
| signalVoid2_.disconnect(); |
| |
| SlotMulti *slotMulti = new SlotMulti(); |
| signalVoid_.connect(slotMulti, &SlotMulti::slot); |
| signalVoid2_.connect(slotMulti, &SlotMulti::slot); |
| delete slotMulti; |
| valueStatic_ = 0; |
| signalVoid_.emit(); |
| signalVoid2_.emit(); |
| if (valueStatic_ != 0) { |
| cout << "Signal disconnection on object deletion test failed" << endl; |
| return TestFail; |
| } |
| |
| /* |
| * Test that signal deletion disconnects objects. This shall |
| * not generate any valgrind warning. |
| */ |
| dynamicSignal = new Signal<>(); |
| slotMulti = new SlotMulti(); |
| dynamicSignal->connect(slotMulti, &SlotMulti::slot); |
| delete dynamicSignal; |
| delete slotMulti; |
| |
| /* Exercise the Object slot code paths. */ |
| slotMulti = new SlotMulti(); |
| signalVoid_.connect(slotMulti, &SlotMulti::slot); |
| valueStatic_ = 0; |
| signalVoid_.emit(); |
| if (valueStatic_ == 0) { |
| cout << "Signal delivery for Object test failed" << endl; |
| return TestFail; |
| } |
| |
| delete slotMulti; |
| |
| return TestPass; |
| } |
| |
| void cleanup() |
| { |
| } |
| |
| private: |
| Signal<> signalVoid_; |
| Signal<> signalVoid2_; |
| Signal<int> signalInt_; |
| Signal<int, const std::string &> signalMultiArgs_; |
| |
| bool called_; |
| int values_[3]; |
| std::string name_; |
| }; |
| |
| TEST_REGISTER(SignalTest) |