| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| /* |
| * Copyright (C) 2019, Google Inc. |
| * |
| * object.cpp - Object tests |
| */ |
| |
| #include <iostream> |
| |
| #include <libcamera/base/message.h> |
| #include <libcamera/base/object.h> |
| #include <libcamera/base/thread.h> |
| |
| #include "test.h" |
| |
| using namespace std; |
| using namespace libcamera; |
| |
| class InstrumentedObject : public Object |
| { |
| public: |
| enum Status { |
| NoMessage, |
| MessageReceived, |
| }; |
| |
| InstrumentedObject(Object *parent = nullptr) |
| : Object(parent), status_(NoMessage) |
| { |
| } |
| |
| Status status() const { return status_; } |
| void reset() { status_ = NoMessage; } |
| |
| protected: |
| void message(Message *msg) override |
| { |
| if (msg->type() == Message::ThreadMoveMessage) |
| status_ = MessageReceived; |
| |
| Object::message(msg); |
| } |
| |
| private: |
| Status status_; |
| }; |
| |
| class ObjectTest : public Test |
| { |
| protected: |
| int init() |
| { |
| /* |
| * Create a hierarchy of objects: |
| * A -> B -> C |
| * \->D |
| * E |
| */ |
| a_ = new InstrumentedObject(); |
| b_ = new InstrumentedObject(a_); |
| c_ = new InstrumentedObject(b_); |
| d_ = new InstrumentedObject(a_); |
| e_ = new InstrumentedObject(); |
| f_ = nullptr; |
| |
| return TestPass; |
| } |
| |
| int run() |
| { |
| /* Verify the parent-child relationships. */ |
| if (a_->parent() != nullptr || b_->parent() != a_ || |
| c_->parent() != b_ || d_->parent() != a_ || |
| e_->parent() != nullptr) { |
| cout << "Incorrect parent-child relationships" << endl; |
| return TestFail; |
| } |
| |
| /* |
| * Verify that moving an object with no parent to a different |
| * thread succeeds. |
| */ |
| e_->moveToThread(&thread_); |
| |
| if (e_->thread() != &thread_ || e_->thread() == Thread::current()) { |
| cout << "Failed to move object to thread" << endl; |
| return TestFail; |
| } |
| |
| /* |
| * Verify that moving an object with a parent to a different |
| * thread fails. This results in an undefined behaviour, the |
| * test thus depends on the internal implementation returning |
| * without performing any change. |
| */ |
| b_->moveToThread(&thread_); |
| |
| if (b_->thread() != Thread::current()) { |
| cout << "Moving object with parent to thread shouldn't succeed" << endl; |
| return TestFail; |
| } |
| |
| /* |
| * Verify that moving an object with children to a different |
| * thread moves all the children. |
| */ |
| a_->moveToThread(&thread_); |
| |
| if (a_->thread() != &thread_ || b_->thread() != &thread_ || |
| c_->thread() != &thread_ || d_->thread() != &thread_) { |
| cout << "Failed to move children to thread" << endl; |
| return TestFail; |
| } |
| |
| /* Verify that objects are bound to the thread of their parent. */ |
| f_ = new InstrumentedObject(d_); |
| |
| if (f_->thread() != &thread_) { |
| cout << "Failed to bind child to parent thread" << endl; |
| return TestFail; |
| } |
| |
| /* Verify that objects receive a ThreadMoveMessage when moved. */ |
| if (a_->status() != InstrumentedObject::MessageReceived || |
| b_->status() != InstrumentedObject::MessageReceived || |
| c_->status() != InstrumentedObject::MessageReceived || |
| d_->status() != InstrumentedObject::MessageReceived || |
| e_->status() != InstrumentedObject::MessageReceived) { |
| cout << "Moving object didn't deliver ThreadMoveMessage" << endl; |
| return TestFail; |
| } |
| |
| return TestPass; |
| } |
| |
| void cleanup() |
| { |
| delete a_; |
| delete b_; |
| delete c_; |
| delete d_; |
| delete e_; |
| delete f_; |
| } |
| |
| private: |
| InstrumentedObject *a_; |
| InstrumentedObject *b_; |
| InstrumentedObject *c_; |
| InstrumentedObject *d_; |
| InstrumentedObject *e_; |
| InstrumentedObject *f_; |
| |
| Thread thread_; |
| }; |
| |
| TEST_REGISTER(ObjectTest) |