[Merge] lp:~karni/media-hub/media-hub-engine-refactor into lp:media-hub/stable
Michał Karnicki
michal.karnicki at gmail.com
Fri Feb 26 15:21:39 UTC 2016
Diff comments:
>
> === added file 'src/core/media/engine/codec.h'
> --- src/core/media/engine/codec.h 1970-01-01 00:00:00 +0000
> +++ src/core/media/engine/codec.h 2016-01-27 19:53:39 +0000
> @@ -0,0 +1,126 @@
> +/*
> + * Copyright © 2016 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY {} without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Michal Karnicki <michal.karnicki at canonical.com>
> + */
> +
> +#pragma once
Understood, fixing :)
> +
> +// TODO karni: replace following with <core/media/engine.h> once engine.h moved to include/core/media/
> +#include "engine.h"
> +
> +#include <core/dbus/codec.h>
Actually, in this case I believe it's in fact an external include?
> +
> +namespace core
> +{
> +namespace dbus
> +{
> +namespace helper
> +{
> +
> +template<>
> +struct TypeMapper<core::ubuntu::media::Engine::Volume>
> +{
> + constexpr static ArgumentType type_value()
> + {
> + return ArgumentType::floating_point;
> + }
> + constexpr static bool is_basic_type()
> + {
> + return false;
> + }
> + constexpr static bool requires_signature()
> + {
> + return false;
> + }
> +
> + static std::string signature()
> + {
> + static const std::string s = TypeMapper<double>::signature();
> + return s;
> + }
> +};
> +}
> +
> +template<>
> +struct Codec<core::ubuntu::media::Engine::Volume>
> +{
> + static void encode_argument(core::dbus::Message::Writer& out, const core::ubuntu::media::Engine::Volume& in)
> + {
> + out.push_floating_point(in.value);
> + }
> +
> + static void decode_argument(core::dbus::Message::Reader& out, core::ubuntu::media::Engine::Volume& in)
> + {
> + in = core::ubuntu::media::Engine::Volume(out.pop_floating_point());
> + }
> +};
> +
> +namespace helper
> +{
> +template<>
> +struct TypeMapper<core::ubuntu::media::Engine::State>
> +{
> + constexpr static ArgumentType type_value()
> + {
> + return ArgumentType::int16;
> + }
> + constexpr static bool is_basic_type()
> + {
> + return false;
> + }
> + constexpr static bool requires_signature()
> + {
> + return false;
> + }
> +
> + static std::string signature()
> + {
> + static const std::string s = TypeMapper<int16_t>::signature();
> + return s;
> + }
> +};
> +}
> +
> +template<>
> +struct Codec<core::ubuntu::media::Engine::State>
> +{
> + static void encode_argument(core::dbus::Message::Writer& out, const core::ubuntu::media::Engine::State& in)
> + {
> + out.push_int16(static_cast<std::int16_t>(in));
> + }
> +
> + static void decode_argument(core::dbus::Message::Reader& out, core::ubuntu::media::Engine::State& in)
> + {
> + in = static_cast<core::ubuntu::media::Engine::State>(out.pop_int16());
> + }
> +};
> +
> +template<>
> +struct Codec<std::chrono::duration<long, std::micro > >
> +{
> + static void encode_argument(core::dbus::Message::Writer& out, const std::chrono::duration<long, std::micro >& in)
> + {
> + out.push_int64(static_cast<std::int64_t>(in.count()));
> + }
> +
> + static void decode_argument(core::dbus::Message::Reader& out, std::chrono::duration<long, std::micro >& in)
> + {
> + in = std::chrono::microseconds{out.pop_int64()};
> + }
> +};
> +
> +}
> +}
>
> === added file 'src/core/media/engine/engine.h'
> --- src/core/media/engine/engine.h 1970-01-01 00:00:00 +0000
> +++ src/core/media/engine/engine.h 2016-01-27 19:53:39 +0000
> @@ -0,0 +1,95 @@
> +/*
> + * Copyright © 2016 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Michal Karnicki <michal.karnicki at canonical.com>
> + */
> +
> +#pragma once
Yes, fixing all in /engine subdir.
> +
> +// XXX karni: core/media/engine.h will be moved to include/core/media/dbus/engine.h
> +#include "../engine.h"
> +#include <core/media/player.h>
Aren't those in <> because these headers actually live under /includes and are installed appropriately for use by 3rd party devs? If so, should we use "" nevertheless? If you look at core/media/engine.h or core/media/player.cpp, they actually use same import with <>, I think that's for a reason.
> +#include <core/media/track.h>
> +
> +#include <core/dbus/bus.h>
> +#include <core/dbus/macros.h>
> +#include <core/dbus/object.h>
> +#include <core/dbus/skeleton.h>
> +#include <core/dbus/stub.h>
> +
> +#include <core/property.h>
> +
> +#include <memory>
> +
> +namespace core
> +{
> +namespace media
> +{
> +namespace engine
> +{
> +
> +namespace engine = core::media::engine;
> +namespace media = core::ubuntu::media;
> +
> +struct Engine
> +{
> + typedef engine::Engine Interface;
> +
> + static const std::string& name()
> + {
> + static const std::string s{"core.ubuntu.media.Service.Engine"};
> + return s;
> + }
> +
> + DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(OpenResourceForUri, Engine, 5000)
> + DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(OpenResourceForUriExtended, Engine, 5000)
> + DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(CreateVideoSink, Engine, 5000)
> + DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(Play, Engine, 5000)
> + DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(Stop, Engine, 5000)
> + DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(Pause, Engine, 5000)
> + DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(SeekTo, Engine, 5000)
> + DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(Reset, Engine, 5000)
> +
> + struct Properties
> + {
> + typedef std::tuple<media::Track::UriType, media::Track::MetaData> MetaDataTuple;
> +
> + DBUS_CPP_READABLE_PROPERTY_DEF(State, Engine, media::Engine::State)
> + DBUS_CPP_READABLE_PROPERTY_DEF(IsVideoSource, Engine, bool)
> + DBUS_CPP_READABLE_PROPERTY_DEF(IsAudioSource, Engine, bool)
> + DBUS_CPP_READABLE_PROPERTY_DEF(Position, Engine, uint64_t)
> + DBUS_CPP_READABLE_PROPERTY_DEF(Duration, Engine, uint64_t)
> + DBUS_CPP_WRITABLE_PROPERTY_DEF(Volume, Engine, media::Engine::Volume)
> + DBUS_CPP_WRITABLE_PROPERTY_DEF(AudioStreamRole, Engine, media::Player::AudioStreamRole)
> + DBUS_CPP_READABLE_PROPERTY_DEF(Orientation, Engine, media::Player::Orientation)
> + DBUS_CPP_WRITABLE_PROPERTY_DEF(Lifetime, Engine, media::Player::Lifetime)
> + DBUS_CPP_READABLE_PROPERTY_DEF(TrackMetaData, Engine, MetaDataTuple)
> + };
> +
> + struct Signals
> + {
> + DBUS_CPP_SIGNAL_DEF(SeekedTo, Engine, uint64_t)
> + DBUS_CPP_SIGNAL_DEF(AboutToFinish, Engine, void)
> + DBUS_CPP_SIGNAL_DEF(EndOfStream, Engine, void)
> + DBUS_CPP_SIGNAL_DEF(ClientDisconnected, Engine, void)
> + DBUS_CPP_SIGNAL_DEF(PlaybackStatusChanged, Engine, media::Player::PlaybackStatus)
> + DBUS_CPP_SIGNAL_DEF(VideoDimensionChanged, Engine, media::video::Dimensions)
> + DBUS_CPP_SIGNAL_DEF(Error, Engine, media::Player::Error)
> + };
> +};
> +
> +}
> +}
> +}
>
> === added file 'src/core/media/engine/engine_skeleton.cpp'
> --- src/core/media/engine/engine_skeleton.cpp 1970-01-01 00:00:00 +0000
> +++ src/core/media/engine/engine_skeleton.cpp 2016-01-27 19:53:39 +0000
> @@ -0,0 +1,652 @@
> +/**
> + * Copyright (C) 2016 Canonical Ltd
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Michal Karnicki <michal.karnicki at canonical.com>
> + */
> +
> +#include "engine_skeleton.h"
> +
> +#include "codec.h"
> +#include "engine_traits.h"
> +#include "../apparmor/ubuntu.h"
> +#include "../codec.h"
> +#include "../engine.h"
> +#include "../property_stub.h"
> +#include "../the_session_bus.h"
> +
> +#include <core/dbus/asio/executor.h>
> +#include <core/dbus/interfaces/properties.h>
> +#include <core/dbus/object.h>
> +#include <core/dbus/property.h>
> +#include <core/dbus/stub.h>
> +#include <core/dbus/types/variant.h>
> +
> +namespace dbus = core::dbus;
> +namespace engine = core::media::engine;
> +namespace media = core::ubuntu::media;
> +
> +namespace
> +{
> +const std::vector<std::string>& the_empty_array_of_invalidated_properties()
> +{
> + static const std::vector<std::string> v; return v;
> +}
> +}
> +
> +struct engine::EngineSkeleton::Private
> +{
> + Private(
> + const std::shared_ptr<media::Engine> engine,
> + const std::shared_ptr<core::dbus::Bus>& bus,
> + const std::shared_ptr<core::dbus::Object>& object
> + ) : impl(engine),
> + bus(bus),
> + object(object),
> + properties_changed(
> + object->get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>()
> + ),
> + properties
> + {
> + object->get_property<engine::Engine::Properties::State>(),
> + object->get_property<engine::Engine::Properties::IsVideoSource>(),
> + object->get_property<engine::Engine::Properties::IsAudioSource>(),
> + object->get_property<engine::Engine::Properties::Position>(),
> + object->get_property<engine::Engine::Properties::Duration>(),
> + object->get_property<engine::Engine::Properties::Volume>(),
> + object->get_property<engine::Engine::Properties::AudioStreamRole>(),
> + object->get_property<engine::Engine::Properties::Orientation>(),
> + object->get_property<engine::Engine::Properties::Lifetime>(),
> + object->get_property<engine::Engine::Properties::TrackMetaData>()
> + },
> + connections
> + {
> + properties.state->changed().connect([this](media::Engine::State value)
> + {
> + on_property_value_changed<engine::Engine::Properties::State>(value);
> + }),
> + properties.is_video_source->changed().connect([this](bool value)
> + {
> + on_property_value_changed<engine::Engine::Properties::IsVideoSource>(value);
> + }),
> + properties.is_audio_source->changed().connect([this](bool value)
> + {
> + on_property_value_changed<engine::Engine::Properties::IsAudioSource>(value);
> + }),
> + properties.position->changed().connect([this](uint64_t value)
> + {
> + on_property_value_changed<engine::Engine::Properties::Position>(value);
> + }),
> + properties.duration->changed().connect([this](uint64_t value)
> + {
> + on_property_value_changed<engine::Engine::Properties::Duration>(value);
> + }),
> + properties.volume->changed().connect([this](Volume value)
> + {
> + impl->volume().set(value);
> + }),
> + properties.audio_stream_role->changed().connect([this](media::Player::AudioStreamRole role)
> + {
> + impl->audio_stream_role().set(role);
> + }),
> + properties.orientation->changed().connect([this](media::Player::Orientation value)
> + {
> + on_property_value_changed<engine::Engine::Properties::Orientation>(value);
> + }),
> + properties.lifetime->changed().connect([this](media::Player::Lifetime value)
> + {
> + std::cout << "lifetime, not working" << std::endl;
> + impl->lifetime().set(value);
> + }),
> + properties.track_meta_data->changed().connect([this](std::tuple<media::Track::UriType, media::Track::MetaData> value)
Tried passing by reference, got compilation issue. Tells me I have to pass by value here (unless I made a mistake somewhere else..)
> + {
> + std::cout << "track_meta_data, not working" << std::endl;
> + on_property_value_changed<engine::Engine::Properties::TrackMetaData>(value);
> + })
> + },
> + signals
> + {
> + object->get_signal<engine::Engine::Signals::AboutToFinish>(),
> + object->get_signal<engine::Engine::Signals::SeekedTo>(),
> + object->get_signal<engine::Engine::Signals::EndOfStream>(),
> + object->get_signal<engine::Engine::Signals::ClientDisconnected>(),
> + object->get_signal<engine::Engine::Signals::PlaybackStatusChanged>(),
> + object->get_signal<engine::Engine::Signals::VideoDimensionChanged>(),
> + object->get_signal<engine::Engine::Signals::Error>()
> + }
> + {
> + // Hook-up properties from engine implementation to the skeleton.
> +
> + impl->state().changed().connect([this](media::Engine::State value)
> + {
> + // (*properties.state.get()).set(state);
> + on_property_value_changed<engine::Engine::Properties::State>(value);
> + });
> + impl->is_video_source().changed().connect([this](bool value)
> + {
> + on_property_value_changed<engine::Engine::Properties::IsVideoSource>(value);
> + });
> + impl->is_audio_source().changed().connect([this](bool value)
> + {
> + on_property_value_changed<engine::Engine::Properties::IsAudioSource>(value);
> + });
> + impl->position().changed().connect([this](uint64_t value)
> + {
> + on_property_value_changed<engine::Engine::Properties::Position>(value);
> + });
> + impl->duration().changed().connect([this](uint64_t value)
> + {
> + on_property_value_changed<engine::Engine::Properties::Duration>(value);
> + });
> + impl->orientation().changed().connect([this](media::Player::Orientation value)
> + {
> + on_property_value_changed<engine::Engine::Properties::Orientation>(value);
> + });
> + impl->track_meta_data().changed().connect([this](std::tuple<media::Track::UriType, media::Track::MetaData> value)
> + {
> + on_property_value_changed<engine::Engine::Properties::TrackMetaData>(value);
> + });
> +
> + // Hook-up signals from engine implementation to the skeleton.
> +
> + impl->about_to_finish_signal().connect([this](){
> + signals.about_to_finish();
> + });
> + impl->seeked_to_signal().connect([this](uint64_t ts){
> + signals.seeked_to(ts);
> + });
> + impl->client_disconnected_signal().connect([this](){
> + signals.client_disconnected();
> + });
> + impl->end_of_stream_signal().connect([this](){
> + signals.end_of_stream();
> + });
> + impl->playback_status_changed_signal().connect([this](media::Player::PlaybackStatus status){
> + signals.playback_status_changed(status);
> + });
> + impl->video_dimension_changed_signal().connect([this](media::video::Dimensions dimensions){
> + signals.video_dimension_changed(dimensions);
> + });
> + impl->error_signal().connect([this](media::Player::Error error){
> + signals.error(error);
> + });
> + }
> +
> + void handle_open_resource_for_uri(const core::dbus::Message::Ptr& msg) const {
> + try {
> + core::ubuntu::media::Track::UriType uri;
> + bool do_pipeline_reset{false};
> + msg->reader() >> uri >> do_pipeline_reset;
> + bool result = impl->open_resource_for_uri(uri, do_pipeline_reset);
> + auto reply = core::dbus::Message::make_method_return(msg);
> + reply->writer() << result;
> + bus->send(reply);
> + }
> + catch (...)
> + {
> + // TODO karni: Find appropriate dbus error title, as this will throw as well!
> + auto error = core::dbus::Message::make_error(msg, "Error", "Invocation of OpenResourceForUri failed");
> + bus->send(error);
> + return;
> + }
> + }
> +
> + void handle_create_video_sink(const core::dbus::Message::Ptr& msg) const {
> + try {
> + std::uint32_t texture_id;
> + msg->reader() >> texture_id;
> + impl->create_video_sink(texture_id);
> + auto reply = core::dbus::Message::make_method_return(msg);
> + bus->send(reply);
> + }
> + catch (...)
> + {
> + auto error = core::dbus::Message::make_error(msg, "Error", "Invocation of CreateVideoSink failed");
> + bus->send(error);
> + return;
> + }
> + }
> +
> + void handle_play(const core::dbus::Message::Ptr& msg) const
> + {
> + try
> + {
> + bool result = impl->play();
> + auto reply = core::dbus::Message::make_method_return(msg);
> + reply->writer() << result;
> + bus->send(reply);
> + }
> + catch (...)
> + {
> + auto error = core::dbus::Message::make_error(msg, "Error", "Invocation of Play failed");
> + bus->send(error);
> + return;
> + }
> + }
> +
> + void handle_stop(const core::dbus::Message::Ptr& msg)
> + {
> + try
> + {
> + bool result = impl->stop();
> + auto reply = core::dbus::Message::make_method_return(msg);
> + reply->writer() << result;
> + bus->send(reply);
> + }
> + catch (...)
> + {
> + auto error = core::dbus::Message::make_error(msg, "Error", "Invocation of Stop failed");
> + bus->send(error);
> + return;
> + }
> + }
> +
> + void handle_pause(const core::dbus::Message::Ptr& msg)
> + {
> + try
> + {
> + bool result = impl->pause();
> + auto reply = core::dbus::Message::make_method_return(msg);
> + reply->writer() << result;
> + bus->send(reply);
> + }
> + catch (...)
> + {
> + auto error = core::dbus::Message::make_error(msg, "Error", "Invocation of Pause failed");
> + bus->send(error);
> + return;
> + }
> + }
> +
> + void handle_seek_to(const core::dbus::Message::Ptr& msg)
> + {
> + try
> + {
> + std::chrono::microseconds ts;
> + msg->reader() >> ts;
> + bool result = impl->seek_to(ts);
> + auto reply = core::dbus::Message::make_method_return(msg);
> + reply->writer() << result;
> + bus->send(reply);
> + }
> + catch (...)
> + {
> + auto error = core::dbus::Message::make_error(msg, "Error", "Invocation of SeekTo failed");
> + bus->send(error);
> + return;
> + }
> + }
> +
> + void handle_reset(const core::dbus::Message::Ptr& msg)
> + {
> + try
> + {
> + impl->reset();
> + auto reply = core::dbus::Message::make_method_return(msg);
> + bus->send(reply);
> + }
> + catch (...)
> + {
> + auto error = core::dbus::Message::make_error(msg, "Error", "Invocation of Reset failed");
> + bus->send(error);
> + return;
> + }
> + }
> +
> + template<typename Property>
> + void on_property_value_changed(const typename Property::ValueType& value)
> + {
> + std::map<std::string, core::dbus::types::Variant> dict
> + {
> + {
> + Property::name(),
> + core::dbus::types::Variant::encode(value)
> + }
> + };
> +
> + properties_changed->emit(
> + std::make_tuple(
> + dbus::traits::Service<engine::Engine>::interface_name(),
> + dict,
> + the_empty_array_of_invalidated_properties()));
> + }
> +
> + const media::Engine::Ptr impl;
> + const dbus::Bus::Ptr bus;
> + const dbus::Object::Ptr object;
> + core::dbus::Signal
> + <
> + core::dbus::interfaces::Properties::Signals::PropertiesChanged,
> + core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType
> + >::Ptr properties_changed;
> +
> + // TODO karni: Our functional dependencies.
> + // apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
Yes, not yet. In my understanding these will eventually be used to verify apparmor permissions for handling uri's.
> + // apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
> +
> + struct
> + {
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::State>> state;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::IsVideoSource>> is_video_source;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::IsAudioSource>> is_audio_source;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Position>> position;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Duration>> duration;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Volume>> volume;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::AudioStreamRole>> audio_stream_role;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Orientation>> orientation;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Lifetime>> lifetime;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::TrackMetaData>> track_meta_data;
> + } properties;
> +
> + struct
> + {
> + core::ScopedConnection state;
> + core::ScopedConnection is_video_source;
> + core::ScopedConnection is_audio_source;
> + core::ScopedConnection position;
> + core::ScopedConnection duration;
> + core::ScopedConnection volume;
> + core::ScopedConnection audio_stream_role;
> + core::ScopedConnection orientation;
> + core::ScopedConnection lifetime;
> + core::ScopedConnection track_meta_data;
> + } connections;
> +
> + struct Signals
> + {
> + typedef core::dbus::Signal<engine::Engine::Signals::AboutToFinish, engine::Engine::Signals::AboutToFinish::ArgumentType> DBusAboutToFinishSignal;
> + typedef core::dbus::Signal<engine::Engine::Signals::SeekedTo, engine::Engine::Signals::SeekedTo::ArgumentType> DBusSeekedToSignal;
> + typedef core::dbus::Signal<engine::Engine::Signals::EndOfStream, engine::Engine::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;
> + typedef core::dbus::Signal<engine::Engine::Signals::ClientDisconnected, engine::Engine::Signals::ClientDisconnected::ArgumentType> DBusClientDisconnectedSignal;
> + typedef core::dbus::Signal<engine::Engine::Signals::PlaybackStatusChanged, engine::Engine::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;
> + typedef core::dbus::Signal<engine::Engine::Signals::VideoDimensionChanged, engine::Engine::Signals::VideoDimensionChanged::ArgumentType> DBusVideoDimensionChangedSignal;
> + typedef core::dbus::Signal<engine::Engine::Signals::Error, engine::Engine::Signals::Error::ArgumentType> DBusErrorSignal;
> +
> + Signals(const std::shared_ptr<DBusAboutToFinishSignal>& remote_about_to_finish,
> + const std::shared_ptr<DBusSeekedToSignal>& remote_seeked_to,
> + const std::shared_ptr<DBusEndOfStreamSignal>& remote_end_of_stream,
> + const std::shared_ptr<DBusClientDisconnectedSignal>& remote_client_disconnected,
> + const std::shared_ptr<DBusPlaybackStatusChangedSignal>& remote_playback_status_changed,
> + const std::shared_ptr<DBusVideoDimensionChangedSignal>& remote_video_dimension_changed,
> + const std::shared_ptr<DBusErrorSignal>& remote_error)
> + {
> + about_to_finish.connect([remote_about_to_finish]()
> + {
> + remote_about_to_finish->emit();
> + });
> +
> + seeked_to.connect([remote_seeked_to](std::uint64_t value)
> + {
> + remote_seeked_to->emit(value);
> + });
> +
> + end_of_stream.connect([remote_end_of_stream]()
> + {
> + remote_end_of_stream->emit();
> + });
> +
> + client_disconnected.connect([remote_client_disconnected]()
> + {
> + remote_client_disconnected->emit();
> + });
> +
> + playback_status_changed.connect([remote_playback_status_changed](const media::Player::PlaybackStatus& status)
> + {
> + remote_playback_status_changed->emit(status);
> + });
> +
> + video_dimension_changed.connect([remote_video_dimension_changed](const media::video::Dimensions& dimensions)
> + {
> + remote_video_dimension_changed->emit(dimensions);
> + });
> +
> + error.connect([remote_error](const media::Player::Error& e)
> + {
> + remote_error->emit(e);
> + });
> + }
> +
> + core::Signal<void> about_to_finish;
> + core::Signal<uint64_t> seeked_to;
> + core::Signal<void> end_of_stream;
> + core::Signal<void> client_disconnected;
> + core::Signal<media::Player::PlaybackStatus> playback_status_changed;
> + core::Signal<media::video::Dimensions> video_dimension_changed;
> + core::Signal<media::Player::Error> error;
> + } signals;
> +};
> +
> +engine::EngineSkeleton::EngineSkeleton(
> + const std::shared_ptr<media::Engine>& engine,
> + const std::shared_ptr<core::dbus::Bus>& bus,
> + const std::shared_ptr<core::dbus::Object>& object
> + ) : d(new Private(engine, bus, object)) // this instead of engine?
Removed the comment. An engine instance (say, SpotifyEngine, or a GStreamerEngine that has a spotify plugin) is passed into the EngineSkeleton, hence it should be 'engine' that is passed (that initializes engine impl in the Private).
> +{
> + auto open_resource_for_uri = std::bind(&Private::handle_open_resource_for_uri, d, std::placeholders::_1);
Yup! Thanks.
> + d->object->install_method_handler<engine::Engine::OpenResourceForUri>(open_resource_for_uri);
> +
> + auto create_video_sink = std::bind(&Private::handle_create_video_sink, d, std::placeholders::_1);
> + d->object->install_method_handler<engine::Engine::CreateVideoSink>(create_video_sink);
> +
> + auto play = std::bind(&Private::handle_play, d, std::placeholders::_1);
> + d->object->install_method_handler<engine::Engine::Play>(play);
> +
> + auto stop = std::bind(&Private::handle_stop, d, std::placeholders::_1);
> + d->object->install_method_handler<engine::Engine::Stop>(stop);
> +
> + auto pause = std::bind(&Private::handle_pause, d, std::placeholders::_1);
> + d->object->install_method_handler<engine::Engine::Pause>(pause);
> +
> + auto seek_to = std::bind(&Private::handle_seek_to, d, std::placeholders::_1);
> + d->object->install_method_handler<engine::Engine::SeekTo>(seek_to);
> +
> + auto reset = std::bind(&Private::handle_reset, d, std::placeholders::_1);
> + d->object->install_method_handler<engine::Engine::Reset>(reset);
> +}
> +
> +engine::EngineSkeleton::~EngineSkeleton()
> +{
> + d->object->uninstall_method_handler<engine::Engine::OpenResourceForUri>();
> + d->object->uninstall_method_handler<engine::Engine::CreateVideoSink>();
> + d->object->uninstall_method_handler<engine::Engine::Play>();
> + d->object->uninstall_method_handler<engine::Engine::Stop>();
> + d->object->uninstall_method_handler<engine::Engine::Pause>();
> + d->object->uninstall_method_handler<engine::Engine::SeekTo>();
> + d->object->uninstall_method_handler<engine::Engine::Reset>();
> +}
> +
> +const std::shared_ptr<media::Engine::MetaDataExtractor>& engine::EngineSkeleton::meta_data_extractor() const
> +{
> + throw std::logic_error{"not implemented"};
> +}
> +
> +bool engine::EngineSkeleton::open_resource_for_uri(
> + const media::Track::UriType& uri,
> + bool do_pipeline_reset)
> +{
> + return d->impl->open_resource_for_uri(uri, do_pipeline_reset);
> +}
> +
> +bool engine::EngineSkeleton::open_resource_for_uri(
> + const media::Track::UriType& uri,
> + const media::Player::HeadersType& headers)
> +{
> + return d->impl->open_resource_for_uri(uri, headers);
> +}
> +
> +void engine::EngineSkeleton::create_video_sink(std::uint32_t texture_id)
> +{
> + d->impl->create_video_sink(texture_id);
> +}
> +
> +bool engine::EngineSkeleton::play()
> +{
> + return d->impl->play();
> +}
> +
> +bool engine::EngineSkeleton::stop()
> +{
> + return d->impl->stop();
> +}
> +
> +bool engine::EngineSkeleton::pause()
> +{
> + return d->impl->pause();
> +}
> +
> +bool engine::EngineSkeleton::seek_to(const std::chrono::microseconds& ts)
> +{
> + return d->impl->seek_to(ts);
> +}
> +
> +void engine::EngineSkeleton::reset()
> +{
> + d->impl->reset();
> +}
> +
> +// TODO karni: complete engine.h interface implementation
> +
> +const core::Property<media::Engine::State>& engine::EngineSkeleton::state() const
> +{
> + return *d->properties.state;
> +}
> +
> +const core::Property<bool>& engine::EngineSkeleton::is_video_source() const
> +{
> + return *d->properties.is_video_source;
> +}
> +
> +const core::Property<bool>& engine::EngineSkeleton::is_audio_source() const
> +{
> + return *d->properties.is_audio_source;
> +}
> +
> +const core::Property<uint64_t>& engine::EngineSkeleton::position() const
> +{
> + return *d->properties.position;
> +}
> +
> +const core::Property<uint64_t>& engine::EngineSkeleton::duration() const
> +{
> + return *d->properties.duration;
> +}
> +
> +const core::Property<media::Engine::Volume>& engine::EngineSkeleton::volume() const
> +{
> + return *d->properties.volume;
> +}
> +
> +const core::Property<media::Player::AudioStreamRole>& engine::EngineSkeleton::audio_stream_role() const
> +{
> + return *d->properties.audio_stream_role;
> +}
> +const core::Property<media::Player::Orientation>& engine::EngineSkeleton::orientation() const
> +{
> + return *d->properties.orientation;
> +}
> +
> +const core::Property<media::Player::Lifetime>& engine::EngineSkeleton::lifetime() const
> +{
> + return *d->properties.lifetime;
> +}
> +
> +const core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>>& engine::EngineSkeleton::track_meta_data() const
> +{
> + return *d->properties.track_meta_data;
> +}
> +
> +core::Property<media::Engine::Volume>& engine::EngineSkeleton::volume()
> +{
> + return *d->properties.volume;
> +}
> +
> +core::Property<media::Player::AudioStreamRole>& engine::EngineSkeleton::audio_stream_role()
> +{
> + return *d->properties.audio_stream_role;
> +}
> +
> +core::Property<media::Player::Lifetime>& engine::EngineSkeleton::lifetime()
> +{
> + return *d->properties.lifetime;
> +}
> +
> +const core::Signal<void>& engine::EngineSkeleton::about_to_finish_signal() const
> +{
> + return d->signals.about_to_finish;
> +}
> +
> +core::Signal<void>& engine::EngineSkeleton::about_to_finish_signal()
> +{
> + return d->signals.about_to_finish;
> +}
> +
> +const core::Signal<uint64_t>& engine::EngineSkeleton::seeked_to_signal() const
> +{
> + return d->signals.seeked_to;
> +}
> +
> +core::Signal<uint64_t>& engine::EngineSkeleton::seeked_to_signal()
> +{
> + return d->signals.seeked_to;
> +}
> +
> +const core::Signal<void>& engine::EngineSkeleton::client_disconnected_signal() const
> +{
> + return d->signals.client_disconnected;
> +}
> +
> +core::Signal<void>& engine::EngineSkeleton::client_disconnected_signal()
> +{
> + return d->signals.client_disconnected;
> +}
> +
> +const core::Signal<void>& engine::EngineSkeleton::end_of_stream_signal() const
> +{
> + return d->signals.end_of_stream;
> +}
> +
> +core::Signal<void>& engine::EngineSkeleton::end_of_stream_signal()
> +{
> + return d->signals.end_of_stream;
> +}
> +
> +const core::Signal<media::Player::PlaybackStatus>& engine::EngineSkeleton::playback_status_changed_signal() const
> +{
> + return d->signals.playback_status_changed;
> +}
> +
> +core::Signal<media::Player::PlaybackStatus>& engine::EngineSkeleton::playback_status_changed_signal()
> +{
> + return d->signals.playback_status_changed;
> +}
> +
> +const core::Signal<media::video::Dimensions>& engine::EngineSkeleton::video_dimension_changed_signal() const
> +{
> + return d->signals.video_dimension_changed;
> +}
> +
> +core::Signal<media::video::Dimensions>& engine::EngineSkeleton::video_dimension_changed_signal()
> +{
> + return d->signals.video_dimension_changed;
> +}
> +
> +const core::Signal<media::Player::Error>& engine::EngineSkeleton::error_signal() const
> +{
> + return d->signals.error;
> +}
> +
> +core::Signal<media::Player::Error>& engine::EngineSkeleton::error_signal()
> +{
> + return d->signals.error;
> +}
>
> === added file 'src/core/media/engine/engine_stub.cpp'
> --- src/core/media/engine/engine_stub.cpp 1970-01-01 00:00:00 +0000
> +++ src/core/media/engine/engine_stub.cpp 2016-01-27 19:53:39 +0000
> @@ -0,0 +1,365 @@
> +/*
> + * Copyright © 2016 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY {} without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Michal Karnicki <michal.karnicki at canonical.com>
> + */
> +
> +#include "engine_stub.h"
> +
> +#include "codec.h" // Volume
I removed the comment, but yes - there's a Volume property, hence the needed codec, IIUC.
> +#include "engine.h"
> +// #include "engine_traits.h"
> +#include "../property_stub.h"
> +#include "../the_session_bus.h"
> +#include "../codec.h"
> +
> +#include <core/dbus/property.h>
> +#include <core/dbus/types/object_path.h>
> +
> +namespace dbus = core::dbus;
> +namespace engine = core::media::engine;
> +namespace media = core::ubuntu::media;
> +
> +struct engine::EngineStub::Private
> +{
> + Private(const std::shared_ptr<core::dbus::Bus>& bus,
> + const std::shared_ptr<core::dbus::Object>& object
> + ) : bus(bus),
> + object(object),
> + properties
> + {
> + object->get_property<engine::Engine::Properties::State>(),
> + object->get_property<engine::Engine::Properties::IsVideoSource>(),
> + object->get_property<engine::Engine::Properties::IsAudioSource>(),
> + object->get_property<engine::Engine::Properties::Position>(),
> + object->get_property<engine::Engine::Properties::Duration>(),
> + object->get_property<engine::Engine::Properties::Volume>(),
> + object->get_property<engine::Engine::Properties::AudioStreamRole>(),
> + object->get_property<engine::Engine::Properties::Orientation>(),
> + object->get_property<engine::Engine::Properties::Lifetime>(),
> + object->get_property<engine::Engine::Properties::TrackMetaData>()
> + },
> + signals
> + {
> + object->get_signal<engine::Engine::Signals::SeekedTo>(),
> + object->get_signal<engine::Engine::Signals::AboutToFinish>(),
> + object->get_signal<engine::Engine::Signals::EndOfStream>(),
> + object->get_signal<engine::Engine::Signals::ClientDisconnected>(),
> + object->get_signal<engine::Engine::Signals::PlaybackStatusChanged>(),
> + object->get_signal<engine::Engine::Signals::VideoDimensionChanged>(),
> + object->get_signal<engine::Engine::Signals::Error>()
> + }
> + {
> + }
> +
> + ~Private()
> + {
> + }
> +
> + std::shared_ptr<core::dbus::Bus> bus;
> + std::shared_ptr<core::dbus::Object> object;
> +
> + struct Properties
> + {
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::State>> state;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::IsVideoSource>> is_video_source;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::IsAudioSource>> is_audio_source;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Position>> position;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Duration>> duration;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Volume>> volume;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::AudioStreamRole>> audio_stream_role;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Orientation>> orientation;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Lifetime>> lifetime;
> + std::shared_ptr<core::dbus::Property<engine::Engine::Properties::TrackMetaData>> track_meta_data;
> + } properties;
> +
> + struct Signals
> + {
> + typedef core::dbus::Signal<engine::Engine::Signals::SeekedTo, engine::Engine::Signals::SeekedTo::ArgumentType> DBusSeekedToSignal;
> + typedef core::dbus::Signal<engine::Engine::Signals::AboutToFinish, engine::Engine::Signals::AboutToFinish::ArgumentType> DBusAboutToFinishSignal;
> + typedef core::dbus::Signal<engine::Engine::Signals::EndOfStream, engine::Engine::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;
> + typedef core::dbus::Signal<engine::Engine::Signals::ClientDisconnected, engine::Engine::Signals::ClientDisconnected::ArgumentType> DBusClientDisconnectedSignal;
> + typedef core::dbus::Signal<engine::Engine::Signals::PlaybackStatusChanged, engine::Engine::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;
> + typedef core::dbus::Signal<engine::Engine::Signals::VideoDimensionChanged, engine::Engine::Signals::VideoDimensionChanged::ArgumentType> DBusVideoDimensionChangedSignal;
> + typedef core::dbus::Signal<engine::Engine::Signals::Error, engine::Engine::Signals::Error::ArgumentType> DBusErrorSignal;
> +
> + Signals(const std::shared_ptr<DBusSeekedToSignal>& seeked_to,
> + const std::shared_ptr<DBusAboutToFinishSignal>& about_to_finish,
> + const std::shared_ptr<DBusEndOfStreamSignal>& end_of_stream,
> + const std::shared_ptr<DBusClientDisconnectedSignal> client_disconnected,
> + const std::shared_ptr<DBusPlaybackStatusChangedSignal>& playback_status_changed,
> + const std::shared_ptr<DBusVideoDimensionChangedSignal>& video_dimension_changed,
> + const std::shared_ptr<DBusErrorSignal>& error)
> + : seeked_to(),
> + about_to_finish(),
> + end_of_stream(),
> + client_disconnected(),
> + playback_status_changed(),
> + video_dimension_changed(),
> + error(),
> + dbus
> + {
> + seeked_to,
> + about_to_finish,
> + end_of_stream,
> + client_disconnected,
> + playback_status_changed,
> + video_dimension_changed,
> + error
> + }
> + {
> + dbus.seeked_to->connect([this](std::uint64_t value)
> + {
> + std::cout << "SeekedTo signal arrived via the bus." << std::endl;
> + this->seeked_to(value);
> + });
> +
> + dbus.about_to_finish->connect([this]()
> + {
> + std::cout << "AboutToFinish signal arrived via the bus." << std::endl;
> + this->about_to_finish();
> + });
> +
> + dbus.end_of_stream->connect([this]()
> + {
> + std::cout << "EndOfStream signal arrived via the bus." << std::endl;
> + this->end_of_stream();
> + });
> +
> + dbus.client_disconnected->connect([this]()
> + {
> + std::cout << "ClientDisconnected signal arrived via the bus." << std::endl;
> + this->client_disconnected();
> + });
> +
> + dbus.playback_status_changed->connect([this](const media::Player::PlaybackStatus& status)
I don't understand why I can't pass by reference in the other place, it doesn't build :|
> + {
> + std::cout << "PlaybackStatusChanged signal arrived via the bus (Status: " << status << ")" << std::endl;
> + this->playback_status_changed(status);
> + });
> +
> + dbus.video_dimension_changed->connect([this](const media::video::Dimensions dimensions)
> + {
> + std::cout << "VideoDimensionChanged signal arrived via the bus." << std::endl;
> + this->video_dimension_changed(dimensions);
> + });
> +
> + dbus.error->connect([this](const media::Player::Error& e)
> + {
> + std::cout << "Error signal arrived via the bus (Error: " << e << ")" << std::endl;
> + this->error(e);
> + });
> + }
> +
> + core::Signal<uint64_t> seeked_to;
> + core::Signal<void> about_to_finish;
> + core::Signal<void> end_of_stream;
> + core::Signal<void> client_disconnected;
> + core::Signal<media::Player::PlaybackStatus> playback_status_changed;
> + core::Signal<media::video::Dimensions> video_dimension_changed;
> + core::Signal<media::Player::Error> error;
> +
> + struct DBus
> + {
> + std::shared_ptr<DBusSeekedToSignal> seeked_to;
> + std::shared_ptr<DBusAboutToFinishSignal> about_to_finish;
> + std::shared_ptr<DBusEndOfStreamSignal> end_of_stream;
> + std::shared_ptr<DBusClientDisconnectedSignal> client_disconnected;
> + std::shared_ptr<DBusPlaybackStatusChangedSignal> playback_status_changed;
> + std::shared_ptr<DBusVideoDimensionChangedSignal> video_dimension_changed;
> + std::shared_ptr<DBusErrorSignal> error;
> + } dbus;
> + } signals;
> +};
> +
> +engine::EngineStub::EngineStub(
> + const std::shared_ptr<core::dbus::Bus>& bus,
> + const std::shared_ptr<core::dbus::Object>& object
> + ) : d(new Private{bus, object})
> +{
> +}
> +
> +engine::EngineStub::~EngineStub()
> +{
> +}
> +
> +const std::shared_ptr<media::Engine::MetaDataExtractor>& engine::EngineStub::meta_data_extractor() const
> +{
> + throw std::logic_error{"not implemented"};
> +}
> +
> +const core::Property<media::Engine::State>& engine::EngineStub::state() const
> +{
> + return *d->properties.state;
> +}
> +
> +bool engine::EngineStub::open_resource_for_uri(
> + const media::Track::UriType& uri,
> + bool do_pipeline_reset)
> +{
> + auto result = d->object->invoke_method_synchronously<engine::Engine::OpenResourceForUri, bool>(uri, do_pipeline_reset);
> + if (result.is_error())
> + throw std::runtime_error(result.error().print());
> + return result.value();
> +}
> +
> +bool engine::EngineStub::open_resource_for_uri(
> + const media::Track::UriType& uri,
> + const media::Player::HeadersType& headers)
> +{
> + auto result = d->object->invoke_method_synchronously<engine::Engine::OpenResourceForUriExtended, bool>(uri, headers);
> + if (result.is_error())
> + throw std::runtime_error(result.error().print());
> + return result.value();
> +}
> +
> +void engine::EngineStub::create_video_sink(std::uint32_t texture_id)
> +{
> + d->object->invoke_method_synchronously<engine::Engine::CreateVideoSink, void>(texture_id);
> +}
> +
> +bool engine::EngineStub::play()
> +{
> + auto result = d->object->invoke_method_synchronously<engine::Engine::Play, bool>();
> + if (result.is_error())
> + throw std::runtime_error(result.error().print());
> + return result.value();
> +}
> +
> +bool engine::EngineStub::stop()
> +{
> + auto result = d->object->invoke_method_synchronously<engine::Engine::Stop, bool>();
> + if (result.is_error())
> + throw std::runtime_error(result.error().print());
> + return result.value();
> +}
> +
> +bool engine::EngineStub::pause()
> +{
> + auto result = d->object->invoke_method_synchronously<engine::Engine::Pause, bool>();
> + if (result.is_error())
> + throw std::runtime_error(result.error().print());
> + return result.value();
> +}
> +
> +bool engine::EngineStub::seek_to(const std::chrono::microseconds& ts)
> +{
> + auto result = d->object->invoke_method_synchronously<engine::Engine::SeekTo, bool>(ts);
> + if (result.is_error())
> + throw std::runtime_error(result.error().print());
> + return result.value();
> +}
> +
> +void engine::EngineStub::reset()
> +{
> + auto result = d->object->invoke_method_synchronously<engine::Engine::Reset, void>();
> + if (result.is_error())
> + throw std::runtime_error(result.error().print());
> +}
> +
> +const core::Property<bool>& engine::EngineStub::is_video_source() const
> +{
> + return *d->properties.is_video_source;
> +}
> +
> +const core::Property<bool>& engine::EngineStub::is_audio_source() const
> +{
> + return *d->properties.is_audio_source;
> +}
> +
> +const core::Property<uint64_t>& engine::EngineStub::position() const
> +{
> + return *d->properties.position;
> +}
> +
> +const core::Property<uint64_t>& engine::EngineStub::duration() const
> +{
> + return *d->properties.duration;
> +}
> +
> +const core::Property<media::Engine::Volume>& engine::EngineStub::volume() const
> +{
> + return *d->properties.volume;
> +}
> +
> +const core::Property<media::Player::AudioStreamRole>& engine::EngineStub::audio_stream_role() const
> +{
> + return *d->properties.audio_stream_role;
> +}
> +
> +const core::Property<media::Player::Orientation>& engine::EngineStub::orientation() const
> +{
> + return *d->properties.orientation;
> +}
> +
> +const core::Property<media::Player::Lifetime>& engine::EngineStub::lifetime() const
> +{
> + return *d->properties.lifetime;
> +}
> +
> +const core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>>& engine::EngineStub::track_meta_data() const
> +{
> + return *d->properties.track_meta_data;
> +}
> +
> +core::Property<media::Engine::Volume>& engine::EngineStub::volume()
> +{
> + return *d->properties.volume;
> +}
> +
> +core::Property<media::Player::AudioStreamRole>& engine::EngineStub::audio_stream_role()
> +{
> + return *d->properties.audio_stream_role;
> +}
> +
> +core::Property<media::Player::Lifetime>& engine::EngineStub::lifetime()
> +{
> + return *d->properties.lifetime;
> +}
> +
> +const core::Signal<void>& engine::EngineStub::about_to_finish_signal() const
> +{
> + return d->signals.about_to_finish;
> +}
> +
> +const core::Signal<uint64_t>& engine::EngineStub::seeked_to_signal() const
> +{
> + return d->signals.seeked_to;
> +}
> +
> +const core::Signal<void>& engine::EngineStub::client_disconnected_signal() const
> +{
> + return d->signals.client_disconnected;
> +}
> +
> +const core::Signal<void>& engine::EngineStub::end_of_stream_signal() const
> +{
> + return d->signals.end_of_stream;
> +}
> +
> +const core::Signal<media::Player::PlaybackStatus>& engine::EngineStub::playback_status_changed_signal() const
> +{
> + return d->signals.playback_status_changed;
> +}
> +
> +const core::Signal<media::video::Dimensions>& engine::EngineStub::video_dimension_changed_signal() const
> +{
> + return d->signals.video_dimension_changed;
> +}
> +
> +const core::Signal<media::Player::Error>& engine::EngineStub::error_signal() const
> +{
> + return d->signals.error;
> +}
>
> === added file 'tests/unit-tests/test-dbus-engine.cpp'
> --- tests/unit-tests/test-dbus-engine.cpp 1970-01-01 00:00:00 +0000
> +++ tests/unit-tests/test-dbus-engine.cpp 2016-01-27 19:53:39 +0000
> @@ -0,0 +1,1322 @@
> +/*
> + * Copyright © 2016 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Michal Karnicki <michal.karnicki at canonical.com>
> + */
> +
> +#include "core/media/engine/codec.h"
> +#include "core/media/engine/engine_traits.h"
> +#include "core/media/engine/engine_skeleton.h"
> +#include "core/media/engine/engine_stub.h"
> +#include "core/media/engine.h"
> +
> +#include <core/media/player.h>
> +#include <core/media/track.h>
> +
> +#include <core/dbus/asio/executor.h>
> +#include <core/dbus/dbus.h>
> +#include <core/dbus/fixture.h>
> +#include <core/dbus/interfaces/properties.h>
> +#include <core/dbus/object.h>
> +#include <core/dbus/property.h>
> +#include <core/dbus/service.h>
> +
> +#include "sig_term_catcher.h"
> +#include "test_data.h"
> +
> +#include <core/testing/cross_process_sync.h>
> +#include <core/testing/fork_and_run.h>
> +
> +#include <gmock/gmock.h>
> +#include <gtest/gtest.h>
> +
> +#include <memory>
> +#include <system_error>
> +#include <thread>
> +
> +namespace dbus = core::dbus;
> +namespace engine = core::media::engine;
> +namespace media = core::ubuntu::media;
> +
> +namespace
> +{
> +using ::testing::_;
> +using ::testing::Return;
> +
> +auto session_bus_config_file =
> + dbus::Fixture::default_session_bus_config_file();
> + // core::testing::session_bus_configuration_file();
> +
> +auto system_bus_config_file =
> + dbus::Fixture::default_system_bus_config_file();
> + // core::testing::system_bus_configuration_file();
> +
> +class MockEngine : public media::Engine {
> +public:
> + MockEngine()
> + {
> + ON_CALL(*this, open_resource_for_uri(_, true))
> + .WillByDefault(Return(true));
> + ON_CALL(*this, create_video_sink(_))
> + .WillByDefault(Return());
> +
> + ON_CALL(*this, play())
> + .WillByDefault(Return(true));
> + ON_CALL(*this, stop())
> + .WillByDefault(Return(true));
> + ON_CALL(*this, pause())
> + .WillByDefault(Return(true));
> + ON_CALL(*this, seek_to(_))
> + .WillByDefault(Return(true));
> +
> + properties.volume.changed().connect([this](Volume volume)
> + {
> + std::cout << "mock engine volume changed to: " << volume.value << std::endl;
> + });
> + properties.audio_stream_role.changed().connect([this](media::Player::AudioStreamRole role)
> + {
> + std::cout << "mock engine audio_stream_role changed to: " << role << std::endl;
> + });
> + properties.lifetime.changed().connect([this](media::Player::Lifetime lifetime)
> + {
> + std::cout << "mock engine lifetime changed to: " << lifetime << std::endl;
> + });
> + }
> +
> + ~MockEngine()
> + {
> + }
> +
> + MOCK_CONST_METHOD0(meta_data_extractor, std::shared_ptr<media::Engine::MetaDataExtractor>&());
> +
> + MOCK_METHOD2(open_resource_for_uri, bool(const media::Track::UriType& uri, bool do_pipeline_reset));
> + MOCK_METHOD2(open_resource_for_uri, bool(const media::Track::UriType& uri, const media::Player::HeadersType& headers));
> +
> + MOCK_METHOD1(create_video_sink, void(uint32_t texture_id));
> +
> + MOCK_METHOD0(play, bool());
> + MOCK_METHOD0(pause, bool());
> + MOCK_METHOD0(stop, bool());
> + MOCK_METHOD1(seek_to, bool(const std::chrono::microseconds& ts));
> + MOCK_METHOD0(reset, void());
> +
> +/*
> +property set by:
> +
> +state server
> +is_video_source server
> +is_audio_source server
> +position server
> +duration server
> +volume client
> +audio_stream_role client
> +orientation server
> +lifetime client
> +track_meta_data server
> +*/
> +
> + // MOCK_CONST_METHOD0(state, core::Property<media::Engine::State>&());
These were more of notes which implementations I completed right below, so I forget none. Sorry about that, cleaned-up.
> + // MOCK_CONST_METHOD0(is_video_source, core::Property<bool>&());
> + // MOCK_CONST_METHOD0(is_audio_source, core::Property<bool>&());
> + // MOCK_CONST_METHOD0(position, core::Property<uint64_t>&());
> + // MOCK_CONST_METHOD0(duration, core::Property<uint64_t>&());
> + // MOCK_CONST_METHOD0(volume, core::Property<media::Engine::Volume>&());
> + // MOCK_CONST_METHOD0(audio_stream_role, core::Property<media::Player::AudioStreamRole>&());
> + // MOCK_CONST_METHOD0(orientation, core::Property<media::Player::Orientation>&());
> + // MOCK_CONST_METHOD0(lifetime, core::Property<media::Player::Lifetime>&());
> + // MOCK_CONST_METHOD0(track_meta_data, core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>>&());
> +
> + // MOCK_METHOD0(volume, core::Property<media::Engine::Volume>&());
> + // MOCK_METHOD0(audio_stream_role, core::Property<media::Player::AudioStreamRole>&());
> + // MOCK_METHOD0(lifetime, core::Property<media::Player::Lifetime>&());
> +
> + const core::Property<media::Engine::State>& state() const
> + {
> + return properties.state;
> + }
> +
> + core::Property<media::Engine::State>& state()
> + {
> + return properties.state;
> + }
> +
> + const core::Property<bool>& is_video_source() const
> + {
> + return properties.is_video_source;
> + }
> +
> + core::Property<bool>& is_video_source()
> + {
> + return properties.is_video_source;
> + }
> +
> + const core::Property<bool>& is_audio_source() const
> + {
> + return properties.is_audio_source;
> + }
> +
> + core::Property<bool>& is_audio_source()
> + {
> + return properties.is_audio_source;
> + }
> +
> + const core::Property<uint64_t>& position() const
> + {
> + return properties.position;
> + }
> +
> + core::Property<uint64_t>& position()
> + {
> + return properties.position;
> + }
> +
> + const core::Property<uint64_t>& duration() const
> + {
> + return properties.duration;
> + }
> +
> + core::Property<uint64_t>& duration()
> + {
> + return properties.duration;
> + }
> +
> + const core::Property<Volume>& volume() const
> + {
> + return properties.volume;
> + }
> +
> + core::Property<Volume>& volume()
> + {
> + return properties.volume;
> + }
> +
> + const core::Property<media::Player::AudioStreamRole>& audio_stream_role() const
> + {
> + return properties.audio_stream_role;
> + }
> +
> + core::Property<media::Player::AudioStreamRole>& audio_stream_role()
> + {
> + return properties.audio_stream_role;
> + }
> +
> + const core::Property<media::Player::Orientation>& orientation() const
> + {
> + return properties.orientation;
> + }
> +
> + core::Property<media::Player::Orientation>& orientation()
> + {
> + return properties.orientation;
> + }
> +
> + const core::Property<media::Player::Lifetime>& lifetime() const
> + {
> + return properties.lifetime;
> + }
> +
> + core::Property<media::Player::Lifetime>& lifetime()
> + {
> + return properties.lifetime;
> + }
> +
> + const core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>>& track_meta_data() const
> + {
> + return properties.track_meta_data;
> + }
> +
> + core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>>& track_meta_data()
> + {
> + return properties.track_meta_data;
> + }
> +
> + struct
> + {
> + // std::shared_ptr<core::dbus::Property<engine::Engine::Properties::State>> state;
> + core::Property<media::Engine::State> state;
> + // std::shared_ptr<core::dbus::Property<engine::Engine::Properties::IsVideoSource>> is_video_source;
> + core::Property<bool> is_video_source;
> + // std::shared_ptr<core::dbus::Property<engine::Engine::Properties::IsAudioSource>> is_audio_source;
> + core::Property<bool> is_audio_source;
> + // std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Position>> position;
> + core::Property<uint64_t> position;
> + // std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Duration>> duration;
> + core::Property<uint64_t> duration;
> + // std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Volume>> volume;
> + core::Property<Volume> volume;
> + // std::shared_ptr<core::dbus::Property<engine::Engine::Properties::AudioStreamRole>> audio_stream_role;
> + core::Property<media::Player::AudioStreamRole> audio_stream_role;
> + // std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Orientation>> orientation;
> + core::Property<media::Player::Orientation> orientation;
> + // std::shared_ptr<core::dbus::Property<engine::Engine::Properties::Lifetime>> lifetime;
> + core::Property<media::Player::Lifetime> lifetime;
> + // std::shared_ptr<core::dbus::Property<engine::Engine::Properties::TrackMetaData>> track_meta_data;
> + core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>> track_meta_data;
> +
> + } properties;
> +
> + const core::Signal<void>& about_to_finish_signal() const
> + {
> + return signals.about_to_finish;
> + }
> +
> + const core::Signal<uint64_t>& seeked_to_signal() const
> + {
> + return signals.seeked_to;
> + }
> +
> + const core::Signal<void>& end_of_stream_signal() const
> + {
> + return signals.end_of_stream;
> + }
> +
> + const core::Signal<void>& client_disconnected_signal() const
> + {
> + return signals.client_disconnected;
> + }
> +
> + const core::Signal<media::Player::PlaybackStatus>& playback_status_changed_signal() const
> + {
> + return signals.playback_status_changed;
> + }
> +
> + const core::Signal<media::video::Dimensions>& video_dimension_changed_signal() const
> + {
> + return signals.video_dimension_changed;
> + }
> +
> + const core::Signal<media::Player::Error>& error_signal() const
> + {
> + return signals.error;
> + }
> +
> + core::Signal<void>& about_to_finish_signal()
> + {
> + return signals.about_to_finish;
> + }
> +
> + core::Signal<uint64_t>& seeked_to_signal()
> + {
> + return signals.seeked_to;
> + }
> +
> + core::Signal<void>& end_of_stream_signal()
> + {
> + return signals.end_of_stream;
> + }
> +
> + core::Signal<void>& client_disconnected_signal()
> + {
> + return signals.client_disconnected;
> + }
> +
> + core::Signal<media::Player::PlaybackStatus>& playback_status_changed_signal()
> + {
> + return signals.playback_status_changed;
> + }
> +
> + core::Signal<media::video::Dimensions>& video_dimension_changed_signal()
> + {
> + return signals.video_dimension_changed;
> + }
> +
> + core::Signal<media::Player::Error>& error_signal()
> + {
> + return signals.error;
> + }
> +
> + struct Signals
> + {
> + core::Signal<void> about_to_finish;
> + core::Signal<uint64_t> seeked_to;
> + core::Signal<void> end_of_stream;
> + core::Signal<void> client_disconnected;
> + core::Signal<media::Player::PlaybackStatus> playback_status_changed;
> + core::Signal<media::video::Dimensions> video_dimension_changed;
> + core::Signal<media::Player::Error> error;
> + } signals;
> +};
> +
> +struct DBusEngine : public dbus::testing::Fixture
> +{
> + void test_method_forward(
> + std::function<void(std::shared_ptr<engine::EngineStub>&)> test_client,
> + std::function<void(std::shared_ptr<MockEngine>)> test_serice);
> +
> + void test_signal_callback(
> + std::function<void(
> + const std::shared_ptr<media::Engine>&)> emit_signal,
> + std::function<void(
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &signalled)> connect_signal);
> +
> + void test_property_forward_from_engine(
> + std::function<void(
> + const std::shared_ptr<media::Engine>&)> change_property,
> + std::function<void(
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &property_changed)> check_property);
> +
> + void test_property_forward_to_engine(
> + std::function<void(
> + const std::shared_ptr<engine::EngineStub>& stub)> change_property,
> + std::function<void(
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<media::Engine>& engine,
> + bool& changed,
> + core::testing::CrossProcessSync& property_has_changed)> track_property);
> +};
> +
> +void DBusEngine::test_method_forward(
> + std::function<void(std::shared_ptr<engine::EngineStub>&)> test_client,
> + std::function<void(std::shared_ptr<MockEngine>)> test_service)
> +{
> + const std::string service_name{"com.ubuntu.media.Service.Engine"};
> + const std::string path{"/com/ubuntu/media/Service/Engine"};
> +
> + core::testing::CrossProcessSync barrier;
> + core::testing::CrossProcessSync stop_barrier;
> +
> + auto client = core::posix::fork([this, service_name, path, &barrier, &stop_barrier, &test_client]()
> + {
> + barrier.wait_for_signal_ready_for(std::chrono::milliseconds{500});
> +
> + auto bus = session_bus();
> +
> + auto service = dbus::Service::use_service(bus, service_name);
> + auto object = service->object_for_path(dbus::types::ObjectPath{path});
> + auto stub = std::shared_ptr<engine::EngineStub>(new engine::EngineStub(bus, object));
> +
> + test_client(stub);
> +
> + stop_barrier.try_signal_ready_for(std::chrono::milliseconds{500});
> +
> + return ::testing::Test::HasFailure()
> + ? core::posix::exit::Status::failure
> + : core::posix::exit::Status::success;
> + }, core::posix::StandardStream::empty);
> +
> + auto service = core::posix::fork([this, service_name, path, &barrier, &stop_barrier, &test_service]()
> + {
> + core::testing::SigTermCatcher sc;
> +
> + auto bus = session_bus();
> + auto executor = dbus::asio::make_executor(bus);
> + bus->install_executor(executor);
> + std::thread t{[bus](){ bus->run(); }};
> +
> + MockEngine* mock_engine = new ::testing::NiceMock<MockEngine>();
> + auto mock_ptr = std::shared_ptr<MockEngine>(mock_engine);
> + auto engine_ptr = std::shared_ptr<media::Engine>(mock_ptr);
> + test_service(mock_ptr);
> +
> + dbus::Bus::RequestNameFlag flags{dbus::Bus::RequestNameFlag::do_not_queue};
> + auto service = dbus::Service::add_service(bus, service_name, flags);
> + auto object = service->add_object_for_path(dbus::types::ObjectPath{path});
> + auto skeleton = std::shared_ptr<core::media::engine::EngineSkeleton>(
> + new core::media::engine::EngineSkeleton(engine_ptr, bus, object));
> +
> + barrier.try_signal_ready_for(std::chrono::milliseconds{500});
> +
> + stop_barrier.wait_for_signal_ready_for(std::chrono::milliseconds{500});
> + bus->stop();
> + sc.wait_for_signal();
> +
> + if (t.joinable())
> + t.join();
> +
> + return ::testing::Test::HasFailure()
> + ? core::posix::exit::Status::failure
> + : core::posix::exit::Status::success;
> + }, core::posix::StandardStream::empty);
> +
> + auto client_result = client.wait_for(core::posix::wait::Flags::untraced);
> +
> + EXPECT_EQ(core::posix::wait::Result::Status::exited, client_result.status);
> + EXPECT_EQ(core::posix::exit::Status::success, client_result.detail.if_exited.status);
> +
> + service.send_signal_or_throw(core::posix::Signal::sig_term);
> + auto service_result = service.wait_for(core::posix::wait::Flags::untraced);
> +
> + EXPECT_EQ(core::posix::wait::Result::Status::exited, service_result.status);
> + EXPECT_EQ(core::posix::exit::Status::success, service_result.detail.if_exited.status);
> +}
> +
> +void DBusEngine::test_signal_callback(
> + std::function<void(
> + const std::shared_ptr<media::Engine>&)> emit_signal,
> + std::function<void(
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &signalled)> connect_signal)
> +{
> +const std::string service_name{"com.ubuntu.media.Service.Engine"};
> + const std::string path{"/com/ubuntu/media/Service/Engine"};
> +
> + core::testing::CrossProcessSync server_is_running;
> + core::testing::CrossProcessSync client_has_setup_signals_and_connections;
> +
> + auto client = [
> + this, &service_name, &path,
> + &server_is_running, &client_has_setup_signals_and_connections,
> + &connect_signal]()
> + {
> + auto bus = session_bus();
> + auto executor = dbus::asio::make_executor(bus);
> + bus->install_executor(executor);
> + std::thread t{[bus](){ bus->run(); }};
> +
> + EXPECT_EQ(std::uint32_t(1),
> + server_is_running.wait_for_signal_ready_for(std::chrono::milliseconds{500}));
> +
> + auto service = dbus::Service::use_service(bus, service_name);
> + auto object = service->object_for_path(dbus::types::ObjectPath{path});
> + auto stub = std::shared_ptr<engine::EngineStub>(new engine::EngineStub(bus, object));
> +
> + bool signalled{false};
> + connect_signal(bus, stub, signalled);
> +
> + client_has_setup_signals_and_connections.try_signal_ready_for(std::chrono::milliseconds{500});
> +
> + if (t.joinable())
> + t.join();
> +
> + EXPECT_TRUE(signalled);
> +
> + return ::testing::Test::HasFailure()
> + ? core::posix::exit::Status::failure
> + : core::posix::exit::Status::success;
> + };
> +
> + auto service = [
> + this, &service_name, &path,
> + &server_is_running, &client_has_setup_signals_and_connections,
> + &emit_signal]()
> + {
> + core::testing::SigTermCatcher sc;
> +
> + auto bus = session_bus();
> + auto executor = dbus::asio::make_executor(bus);
> + bus->install_executor(executor);
> + std::thread t{[bus](){ bus->run(); }};
> +
> + MockEngine* mock_engine = new ::testing::NiceMock<MockEngine>();
> + auto mock_ptr = std::shared_ptr<MockEngine>(mock_engine);
> + auto engine_ptr = std::shared_ptr<media::Engine>(mock_ptr);
> +
> + dbus::Bus::RequestNameFlag flags{dbus::Bus::RequestNameFlag::do_not_queue};
> + auto service = dbus::Service::add_service(bus, service_name, flags);
> + auto object = service->add_object_for_path(dbus::types::ObjectPath{path});
> + auto skeleton = std::shared_ptr<core::media::engine::EngineSkeleton>(
> + new core::media::engine::EngineSkeleton(engine_ptr, bus, object));
> +
> + server_is_running.try_signal_ready_for(std::chrono::milliseconds{1000});
> + EXPECT_EQ(std::uint32_t(1),
> + client_has_setup_signals_and_connections.wait_for_signal_ready_for(
> + std::chrono::milliseconds{500}));
> +
> + emit_signal(engine_ptr);
> +
> + sc.wait_for_signal();
> +
> + bus->stop();
> +
> + if (t.joinable())
> + t.join();
> +
> + return ::testing::Test::HasFailure()
> + ? core::posix::exit::Status::failure
> + : core::posix::exit::Status::success;
> + };
> +
> + EXPECT_EQ(core::testing::ForkAndRunResult::empty, core::testing::fork_and_run(service, client));
> +}
> +
> +void DBusEngine::test_property_forward_from_engine(
> + std::function<void(
> + const std::shared_ptr<media::Engine>&)> change_property,
> + std::function<void(
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &property_changed)> track_property)
> +{
> + const std::string service_name{"com.ubuntu.media.Service.Engine"};
> + const std::string path{"/com/ubuntu/media/Service/Engine"};
> +
> + core::testing::CrossProcessSync server_is_running;
> + core::testing::CrossProcessSync client_has_setup_properties;
> +
> + auto client = [
> + this, &service_name, &path,
> + &server_is_running, &client_has_setup_properties,
> + &track_property]()
> + {
> + auto bus = session_bus();
> + auto executor = dbus::asio::make_executor(bus);
> + bus->install_executor(executor);
> + std::thread t{[bus](){ bus->run(); }};
> +
> + EXPECT_EQ(std::uint32_t(1),
> + server_is_running.wait_for_signal_ready_for(std::chrono::milliseconds{500}));
> +
> + auto service = dbus::Service::use_service(bus, service_name);
> + auto object = service->object_for_path(dbus::types::ObjectPath{path});
> + auto stub = std::shared_ptr<engine::EngineStub>(new engine::EngineStub(bus, object));
> +
> + bool changed{false};
> + track_property(bus, stub, changed);
> +
> + client_has_setup_properties.try_signal_ready_for(std::chrono::milliseconds{500});
> +
> + if (t.joinable())
> + t.join();
> +
> + EXPECT_TRUE(changed);
> +
> + return ::testing::Test::HasFailure()
> + ? core::posix::exit::Status::failure
> + : core::posix::exit::Status::success;
> + };
> +
> + auto service = [
> + this, &service_name, &path,
> + &server_is_running, &client_has_setup_properties,
> + &change_property]()
> + {
> + core::testing::SigTermCatcher sc;
> +
> + auto bus = session_bus();
> + auto executor = dbus::asio::make_executor(bus);
> + bus->install_executor(executor);
> + std::thread t{[bus](){ bus->run(); }};
> +
> + MockEngine* mock_engine = new ::testing::NiceMock<MockEngine>();
> + auto mock_ptr = std::shared_ptr<MockEngine>(mock_engine);
> + auto engine_ptr = std::shared_ptr<media::Engine>(mock_ptr);
> +
> + dbus::Bus::RequestNameFlag flags{dbus::Bus::RequestNameFlag::do_not_queue};
> + auto service = dbus::Service::add_service(bus, service_name, flags);
> + auto object = service->add_object_for_path(dbus::types::ObjectPath{path});
> + auto skeleton = std::shared_ptr<core::media::engine::EngineSkeleton>(
> + new core::media::engine::EngineSkeleton(engine_ptr, bus, object));
> +
> + server_is_running.try_signal_ready_for(std::chrono::milliseconds{1000});
> + EXPECT_EQ(std::uint32_t(1),
> + client_has_setup_properties.wait_for_signal_ready_for(
> + std::chrono::milliseconds{500}));
> +
> + change_property(engine_ptr);
> +
> + sc.wait_for_signal();
> +
> + bus->stop();
> +
> + if (t.joinable())
> + t.join();
> +
> + return ::testing::Test::HasFailure()
> + ? core::posix::exit::Status::failure
> + : core::posix::exit::Status::success;
> + };
> +
> + EXPECT_EQ(core::testing::ForkAndRunResult::empty, core::testing::fork_and_run(service, client));
> +}
> +
> +void DBusEngine::test_property_forward_to_engine(
> + std::function<void(
> + const std::shared_ptr<engine::EngineStub>& stub)> change_property,
> + std::function<void(
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<media::Engine>& engine,
> + bool& changed,
> + core::testing::CrossProcessSync&)> track_property)
> +{
> + const std::string service_name{"com.ubuntu.media.Service.Engine"};
> + const std::string path{"/com/ubuntu/media/Service/Engine"};
> +
> + core::testing::CrossProcessSync server_is_running;
> + core::testing::CrossProcessSync property_has_changed;
> +
> + auto client = [
> + this, &service_name, &path,
> + &server_is_running, &property_has_changed,
> + &change_property]()
> + {
> + auto bus = session_bus();
> + auto executor = dbus::asio::make_executor(bus);
> + bus->install_executor(executor);
> + std::thread t{[bus](){ bus->run(); }};
> +
> + EXPECT_EQ(std::uint32_t(1),
> + server_is_running.wait_for_signal_ready_for(std::chrono::milliseconds{500}));
> +
> + auto service = dbus::Service::use_service(bus, service_name);
> + auto object = service->object_for_path(dbus::types::ObjectPath{path});
> + auto stub = std::shared_ptr<engine::EngineStub>(new engine::EngineStub(bus, object));
> +
> + change_property(stub);
> +
> + EXPECT_EQ(std::uint32_t(1),
> + property_has_changed.wait_for_signal_ready_for(std::chrono::milliseconds{500}));
> + bus->stop();
> +
> + if (t.joinable())
> + t.join();
> +
> + return ::testing::Test::HasFailure()
> + ? core::posix::exit::Status::failure
> + : core::posix::exit::Status::success;
> + };
> +
> + auto service = [
> + this, &service_name, &path,
> + &server_is_running, &property_has_changed,
> + &track_property]()
> + {
> + core::testing::SigTermCatcher sc;
> +
> + auto bus = session_bus();
> + auto executor = dbus::asio::make_executor(bus);
> + bus->install_executor(executor);
> + std::thread t{[bus](){ bus->run(); }};
> +
> + MockEngine* mock_engine = new ::testing::NiceMock<MockEngine>();
> + auto mock_ptr = std::shared_ptr<MockEngine>(mock_engine);
> + auto engine_ptr = std::shared_ptr<media::Engine>(mock_ptr);
> +
> + dbus::Bus::RequestNameFlag flags{dbus::Bus::RequestNameFlag::do_not_queue};
> + auto service = dbus::Service::add_service(bus, service_name, flags);
> + auto object = service->add_object_for_path(dbus::types::ObjectPath{path});
> + auto skeleton = std::shared_ptr<core::media::engine::EngineSkeleton>(
> + new core::media::engine::EngineSkeleton(engine_ptr, bus, object));
> +
> + bool changed{false};
> + track_property(bus, engine_ptr, changed, property_has_changed);
> +
> + server_is_running.try_signal_ready_for(std::chrono::milliseconds{1000});
> +
> + sc.wait_for_signal();
> +
> + EXPECT_EQ(true, changed);
> +
> + bus->stop();
> +
> + if (t.joinable())
> + t.join();
> +
> + return ::testing::Test::HasFailure()
> + ? core::posix::exit::Status::failure
> + : core::posix::exit::Status::success;
> + };
> +
> + EXPECT_EQ(core::testing::ForkAndRunResult::empty, core::testing::fork_and_run(service, client));
> +}
> +}
> +
> +namespace
> +{
> +template<typename T>
> +struct Expectation
> +{
> + Expectation(const T& expected_value) : expected_value(expected_value)
> + {
> + }
> +
> + bool satisfied() const
> + {
> + return triggered && current_value == expected_value;
> + }
> +
> + bool triggered = false;
> + T expected_value;
> + T current_value;
> +};
> +}
> +
> +TEST_F(DBusEngine, InvokingPlayForwardsCallToEngineImplementation)
> +{
> + auto test_client = [](const std::shared_ptr<engine::EngineStub> &stub) {
> + bool result{false};
> + EXPECT_NO_THROW(result = stub->play());
> + EXPECT_EQ(true, result);
> + };
> +
> + auto test_service = [](const std::shared_ptr<MockEngine> &mock_ptr) {
> + EXPECT_CALL(*(mock_ptr.get()), play()).Times(1);
> + };
> +
> + test_method_forward(test_client, test_service);
> +}
> +
> +TEST_F(DBusEngine, InvokingStopForwardsCallToEngineImplementation)
> +{
> + auto test_client = [](const std::shared_ptr<engine::EngineStub> &stub) {
> + bool result{false};
> + EXPECT_NO_THROW(result = stub->stop());
> + EXPECT_EQ(true, result);
> + };
> +
> + auto test_service = [](const std::shared_ptr<MockEngine> &mock_ptr) {
> + EXPECT_CALL(*(mock_ptr.get()), stop()).Times(1);
> + };
> +
> + test_method_forward(test_client, test_service);
> +}
> +
> +TEST_F(DBusEngine, InvokingPauseForwardsCallToEngineImplementation)
> +{
> + auto test_client = [](const std::shared_ptr<engine::EngineStub> &stub) {
> + bool result{false};
> + EXPECT_NO_THROW(result = stub->pause());
> + EXPECT_EQ(true, result);
> + };
> +
> + auto test_service = [](const std::shared_ptr<MockEngine> &mock_ptr) {
> + EXPECT_CALL(*(mock_ptr.get()), pause()).Times(1);
> + };
> +
> + test_method_forward(test_client, test_service);
> +}
> +
> +TEST_F(DBusEngine, InvokingSeekToForwardsCallToEngineImplementation)
> +{
> + const std::chrono::microseconds ts{42};
> +
> + auto test_client = [&ts](const std::shared_ptr<engine::EngineStub> &stub) {
> + bool result{false};
> + EXPECT_NO_THROW(result = stub->seek_to(ts));
> + EXPECT_EQ(true, result);
> + };
> +
> + auto test_service = [&ts](const std::shared_ptr<MockEngine> &mock_ptr) {
> + EXPECT_CALL(*(mock_ptr.get()), seek_to(ts)).Times(1);
> + };
> +
> + test_method_forward(test_client, test_service);
> +}
> +
> +TEST_F(DBusEngine, InvokingResetForwardsCallToEngineImplementation)
> +{
> + auto test_client = [](const std::shared_ptr<engine::EngineStub> &stub) {
> + EXPECT_NO_THROW(stub->reset());
> + };
> +
> + auto test_service = [](const std::shared_ptr<MockEngine> &mock_ptr) {
> + EXPECT_CALL(*(mock_ptr.get()), reset()).Times(1);
> + };
> +
> + test_method_forward(test_client, test_service);
> +}
> +
> +// TODO karni: this test is failing (method call throws)
> +TEST_F(DBusEngine, InvokingOpenResourceForUriForwardsCallToEngineImplementation)
> +{
> + const core::ubuntu::media::Track::UriType& uri{"provider://fake/track/uri/123"};
> + bool do_pipeline_reset = true;
> +
> + auto test_client = [&uri, &do_pipeline_reset](const std::shared_ptr<engine::EngineStub> &stub) {
> + bool result{false};
> + EXPECT_NO_THROW(result = stub->open_resource_for_uri(uri, do_pipeline_reset));
> + EXPECT_EQ(true, result);
> + };
> +
> + auto test_service = [&uri, &do_pipeline_reset](const std::shared_ptr<MockEngine> &mock_ptr) {
> + EXPECT_CALL(*(mock_ptr.get()), open_resource_for_uri(uri, do_pipeline_reset)).Times(1);
> + };
> +
> + test_method_forward(test_client, test_service);
> +}
> +
> +TEST_F(DBusEngine, InvokingCreateVideoSinkForwardsCallToEngineImplementation)
> +{
> + uint32_t texture_id{42};
> +
> + auto test_client = [&texture_id](const std::shared_ptr<engine::EngineStub> &stub) {
> + EXPECT_NO_THROW(stub->create_video_sink(texture_id));
> + };
> +
> + auto test_service = [&texture_id](const std::shared_ptr<MockEngine> &mock_ptr) {
> + EXPECT_CALL(*(mock_ptr.get()), create_video_sink(texture_id)).Times(1);
> + };
> +
> + test_method_forward(test_client, test_service);
> +}
> +
> +TEST_F(DBusEngine, EmittingAboutToFinishSignalForwardedToStub)
> +{
> + auto emit_signal = [](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Signal<void>& about_to_finish =
> + ((MockEngine*)(engine.get()))->about_to_finish_signal();
> + about_to_finish();
> + };
> +
> + auto connect_signal = [](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &signalled)
> + {
> + stub->about_to_finish_signal().connect([&bus, &signalled]()
> + {
> + signalled = true;
> + bus->stop();
> + });
> + };
> +
> + test_signal_callback(emit_signal, connect_signal);
> +}
> +
> +TEST_F(DBusEngine, EmittingSeekedToSignalForwardedToStub)
> +{
> + uint64_t value = 42;
> +
> + auto emit_signal = [&value](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Signal<uint64_t>& seeked_to =
> + ((MockEngine*)(engine.get()))->seeked_to_signal();
> + seeked_to(value);
> + };
> +
> + auto connect_signal = [&value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &signalled)
> + {
> + stub->seeked_to_signal().connect([&value, &bus, &signalled](uint64_t ts)
> + {
> + signalled = true;
> + EXPECT_EQ(value, ts);
> + bus->stop();
> + });
> + };
> +
> + test_signal_callback(emit_signal, connect_signal);
> +}
> +
> +TEST_F(DBusEngine, EmittingClientDisconnectedSignalForwardedToStub)
> +{
> + auto emit_signal = [](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Signal<void>& client_disconnected =
> + ((MockEngine*)(engine.get()))->client_disconnected_signal();
> + client_disconnected();
> + };
> +
> + auto connect_signal = [](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &signalled)
> + {
> + stub->client_disconnected_signal().connect([&bus, &signalled]()
> + {
> + signalled = true;
> + bus->stop();
> + });
> + };
> +
> + test_signal_callback(emit_signal, connect_signal);
> +}
> +
> +TEST_F(DBusEngine, EmittingEndOfStreamSignalForwardedToStub)
> +{
> + auto emit_signal = [](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Signal<void>& end_of_stream =
> + ((MockEngine*)(engine.get()))->end_of_stream_signal();
> + end_of_stream();
> + };
> +
> + auto connect_signal = [](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &signalled)
> + {
> + stub->end_of_stream_signal().connect([&bus, &signalled]()
> + {
> + signalled = true;
> + bus->stop();
> + });
> + };
> +
> + test_signal_callback(emit_signal, connect_signal);
> +}
> +
> +TEST_F(DBusEngine, EmittingPlayerStatusChangedSignalForwardedToStub)
> +{
> + media::Player::PlaybackStatus value{media::Player::PlaybackStatus::playing};
> +
> + auto emit_signal = [&value](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Signal<media::Player::PlaybackStatus>& playback_status_changed =
> + ((MockEngine*)(engine.get()))->playback_status_changed_signal();
> + playback_status_changed(value);
> + };
> +
> + auto connect_signal = [&value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &signalled)
> + {
> + stub->playback_status_changed_signal().connect([&value, &bus, &signalled](
> + media::Player::PlaybackStatus status)
> + {
> + signalled = true;
> + EXPECT_EQ(value, status);
> + bus->stop();
> + });
> + };
> +
> + test_signal_callback(emit_signal, connect_signal);
> +}
> +
> +TEST_F(DBusEngine, EmittingVideoDimensionChangedSignalForwardedToStub)
> +{
> + namespace video = core::ubuntu::media::video;
> + video::Width width{640};
> + video::Height height{480};
> + video::Dimensions value = std::tuple<video::Height, video::Width> {
> + height, width
> + };
> +
> + auto emit_signal = [&value](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Signal<video::Dimensions>& video_dimension_changed =
> + ((MockEngine*)(engine.get()))->video_dimension_changed_signal();
> + video_dimension_changed(value);
> + };
> +
> + auto connect_signal = [&value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &signalled)
> + {
> + stub->video_dimension_changed_signal().connect(
> + [&value, &bus, &signalled](video::Dimensions dimensions)
> + {
> + signalled = true;
> + EXPECT_EQ(value, dimensions);
> + bus->stop();
> + });
> + };
> +
> + test_signal_callback(emit_signal, connect_signal);
> +}
> +
> +TEST_F(DBusEngine, EmittingErrorSignalForwardedToStub)
> +{
> + using Error = media::Player::Error;
> +
> + Error error{Error::network_error};
> +
> + auto emit_signal = [&error](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Signal<media::Player::Error>& error_signal =
> + ((MockEngine*)(engine.get()))->error_signal();
> + error_signal(error);
> + };
> +
> + auto connect_signal = [&error](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &signalled)
> + {
> + stub->error_signal().connect([&error, &bus, &signalled](Error value)
> + {
> + signalled = true;
> + EXPECT_NE(Error::no_error, value);
> + EXPECT_EQ(error, value);
> + bus->stop();
> + });
> + };
> +
> + test_signal_callback(emit_signal, connect_signal);
> +}
> +
> +// Properties signalling value changed from skeleton to stub.
> +
> +TEST_F(DBusEngine, IsStatePropertyChangeForwarded)
> +{
> + media::Engine::State expected_value = media::Engine::State::playing;
> +
> + auto change_property = [&expected_value](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Property<media::Engine::State>& state =
> + ((MockEngine*)(engine.get()))->state();
> + state.set(expected_value);
> + };
> +
> + auto track_property = [&expected_value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &changed)
> + {
> + stub->state().changed().connect([&bus, &changed, &expected_value](media::Engine::State value){
> + changed = true;
> + EXPECT_EQ(expected_value, value);
> + bus->stop();
> + });
> + };
> +
> + test_property_forward_from_engine(change_property, track_property);
> +}
> +
> +TEST_F(DBusEngine, IsVideoSourcePropertyChangeForwarded)
> +{
> + bool expected_value = true;
> +
> + auto change_property = [&expected_value](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Property<bool>& is_video_source =
> + ((MockEngine*)(engine.get()))->is_video_source();
> + is_video_source.set(expected_value);
> + };
> +
> + auto track_property = [&expected_value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &changed)
> + {
> + stub->is_video_source().changed().connect([&bus, &changed, &expected_value](bool value){
> + changed = true;
> + EXPECT_EQ(expected_value, value);
> + bus->stop();
> + });
> + };
> +
> + test_property_forward_from_engine(change_property, track_property);
> +}
> +
> +TEST_F(DBusEngine, IsAudioSourcePropertyChangeForwarded)
> +{
> + bool expected_value = true;
> +
> + auto change_property = [&expected_value](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Property<bool>& is_audio_source =
> + ((MockEngine*)(engine.get()))->is_audio_source();
> + is_audio_source.set(expected_value);
> + };
> +
> + auto track_property = [&expected_value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &changed)
> + {
> + stub->is_audio_source().changed().connect([&bus, &changed, &expected_value](bool value){
> + changed = true;
> + EXPECT_EQ(expected_value, value);
> + bus->stop();
> + });
> + };
> +
> + test_property_forward_from_engine(change_property, track_property);
> +}
> +
> +TEST_F(DBusEngine, PositionPropertyChangeForwarded)
> +{
> + uint64_t expected_value = 1024;
> +
> + auto change_property = [&expected_value](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Property<uint64_t>& position =
> + ((MockEngine*)(engine.get()))->position();
> + position.set(expected_value);
> + };
> +
> + auto track_property = [&expected_value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &changed)
> + {
> + stub->position().changed().connect([&bus, &changed, &expected_value](uint64_t value){
> + changed = true;
> + EXPECT_EQ(expected_value, value);
> + bus->stop();
> + });
> + };
> +
> + test_property_forward_from_engine(change_property, track_property);
> +}
> +
> +TEST_F(DBusEngine, DurationPropertyChangeForwarded)
> +{
> + uint64_t expected_value = 2048;
> +
> + auto change_property = [&expected_value](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Property<uint64_t>& duration =
> + ((MockEngine*)(engine.get()))->duration();
> + duration.set(expected_value);
> + };
> +
> + auto track_property = [&expected_value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &changed)
> + {
> + stub->duration().changed().connect([&bus, &changed, &expected_value](uint64_t value){
> + changed = true;
> + EXPECT_EQ(expected_value, value);
> + bus->stop();
> + });
> + };
> +
> + test_property_forward_from_engine(change_property, track_property);
> +}
> +
> +TEST_F(DBusEngine, OrientationPropertyChangeForwardedFromEngine)
> +{
> + media::Player::Orientation expected_value{core::ubuntu::media::Player::Orientation::rotate90};
> +
> + auto change_property = [&expected_value](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Property<media::Player::Orientation>& orientation =
> + ((MockEngine*)(engine.get()))->orientation();
> + orientation.set(expected_value);
> + };
> +
> + auto track_property = [&expected_value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &changed)
> + {
> + stub->orientation().changed().connect([&bus, &changed, &expected_value](media::Player::Orientation value){
> + changed = true;
> + EXPECT_EQ(expected_value, value);
> + bus->stop();
> + });
> + };
> +
> + test_property_forward_from_engine(change_property, track_property);
> +}
> +
> +TEST_F(DBusEngine, DISABLED_TrackMetaDataPropertyChangeForwardedFromEngine)
> +{
> + std::tuple<media::Track::UriType, media::Track::MetaData> expected_value;
> +
> + auto change_property = [&expected_value](const std::shared_ptr<media::Engine>& engine)
> + {
> + core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>>& track_meta_data =
> + ((MockEngine*)(engine.get()))->track_meta_data();
> + track_meta_data.set(expected_value);
> + };
> +
> + auto track_property = [&expected_value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<engine::EngineStub>& stub,
> + bool &changed)
> + {
> + stub->track_meta_data().changed().connect([&bus, &changed, &expected_value]
> + (std::tuple<media::Track::UriType, media::Track::MetaData> value){
> + changed = true;
> + EXPECT_EQ(expected_value, value);
> + bus->stop();
> + });
> + };
> +
> + test_property_forward_from_engine(change_property, track_property);
> +}
> +
> +// Properties signalling value changed from stub to skeleton.
> +
> +TEST_F(DBusEngine, VolumePropertyChangeForwardedToEngine)
> +{
> + const media::Engine::Volume expected_value{0.5};
> +
> + auto change_property = [&expected_value](
> + const std::shared_ptr<engine::EngineStub>& stub)
> + {
> + EXPECT_NO_THROW(stub->volume().set(expected_value));
> + };
> +
> + auto track_property = [&expected_value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<media::Engine>& engine,
> + bool &changed,
> + core::testing::CrossProcessSync& property_has_changed)
> + {
> + core::Property<media::Engine::Volume>& volume = ((MockEngine*)(engine.get()))->volume();
> + volume.changed().connect([&bus, &expected_value, &changed, &property_has_changed] (media::Engine::Volume value)
> + {
> + changed = true;
> + EXPECT_EQ(expected_value, value);
> + property_has_changed.try_signal_ready_for(std::chrono::milliseconds{500});
> + bus->stop();
> + });
> + };
> +
> + test_property_forward_to_engine(change_property, track_property);
> +}
> +
> +TEST_F(DBusEngine, AudioStreamRolePropertyChangeForwardedToEngine)
> +{
> + media::Player::AudioStreamRole expected_value{media::Player::AudioStreamRole::multimedia};
> +
> + auto change_property = [&expected_value](
> + const std::shared_ptr<engine::EngineStub>& stub)
> + {
> + EXPECT_NO_THROW(stub->audio_stream_role().set(expected_value));
> + };
> +
> + auto track_property = [&expected_value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<media::Engine>& engine,
> + bool &changed,
> + core::testing::CrossProcessSync& property_has_changed)
> + {
> + core::Property<media::Player::AudioStreamRole>& audio_stream_role = ((MockEngine*)(engine.get()))->audio_stream_role();
> + audio_stream_role.changed().connect([&bus, &expected_value, &changed, &property_has_changed] (media::Player::AudioStreamRole value)
> + {
> + changed = true;
> + EXPECT_EQ(expected_value, value);
> + property_has_changed.try_signal_ready_for(std::chrono::milliseconds{500});
> + bus->stop();
> + });
> + };
> +
> + test_property_forward_to_engine(change_property, track_property);
> +}
> +
> +TEST_F(DBusEngine, DISABLED_LifetimePropertyChangeForwardedToEngine)
In total there's two tests disabled. The one earlier because TrackMetaData bit was not yet implemented, and this one because I couldn't get it to work over dbus (if I recall correctly, the test hung). I hard hard time understanding what's wrong, at which point I explained I wanted to move forward with the refactor to see how things fall into place, instead of focusing too much on details at the time.
> +{
> + media::Player::Lifetime expected_value{media::Player::Lifetime::normal};
> +
> + auto change_property = [&expected_value](
> + const std::shared_ptr<engine::EngineStub>& stub)
> + {
> + EXPECT_NO_THROW(stub->lifetime().set(expected_value));
> + };
> +
> + auto track_property = [&expected_value](
> + const std::shared_ptr<dbus::Bus>& bus,
> + const std::shared_ptr<media::Engine>& engine,
> + bool &changed,
> + core::testing::CrossProcessSync& property_has_changed)
> + {
> + core::Property<media::Player::Lifetime>& lifetime = ((MockEngine*)(engine.get()))->lifetime();
> + lifetime.changed().connect([&bus, &expected_value, &changed, &property_has_changed] (media::Player::Lifetime value)
> + {
> + std::cout << "XXX not reached" << std::flush << std::endl;
> + changed = true;
> + EXPECT_EQ(expected_value, value);
> + property_has_changed.try_signal_ready_for(std::chrono::milliseconds{500});
> + bus->stop();
> + });
> + };
> +
> + test_property_forward_to_engine(change_property, track_property);
> +}
--
https://code.launchpad.net/~karni/media-hub/media-hub-engine-refactor/+merge/282286
Your team Ubuntu Phablet Team is subscribed to branch lp:media-hub/stable.
More information about the Ubuntu-reviews
mailing list