Atspi & Optional 98/137998/8
authorMariusz Wachowicz <m.wachowicz@partner.samsung.com>
Mon, 10 Jul 2017 11:08:39 +0000 (13:08 +0200)
committerMariusz Wachowicz <m.wachowicz@partner.samsung.com>
Fri, 14 Jul 2017 14:09:50 +0000 (16:09 +0200)
adds Optional similar to Optional in C++17

Change-Id: I5ff35f2838de4bbc01738b2fe02f4e755ce54aca

CMakeLists.txt
packaging/org.tizen.universal-switch.spec
src/Atspi.cpp [new file with mode: 0644]
src/Atspi.hpp [new file with mode: 0644]
src/CMakeLists.txt
src/Optional.hpp [new file with mode: 0644]
src/universalswitch.cpp
tests/CMakeLists.txt
tests/OptionalTests.cpp [new file with mode: 0644]

index cb8b2f8..b5a2cd4 100644 (file)
@@ -18,6 +18,8 @@ pkg_check_modules(pkgs REQUIRED
     atspi-2
     capi-ui-efl-util
     efl-extension
+    gobject-2.0
+    eldbus
 )
 
 SET(COMMON_FLAGS "-fdiagnostics-color=always -fPIC")
index e17094c..9a404ae 100644 (file)
@@ -20,6 +20,8 @@ BuildRequires:  pkgconfig(sqlite3)
 BuildRequires:  pkgconfig(libtzplatform-config)
 BuildRequires:  pkgconfig(efl-extension)
 BuildRequires:  pkgconfig(capi-ui-efl-util)
+BuildRequires:  pkgconfig(gobject-2.0)
+BuildRequires:  pkgconfig(eldbus)
 #Required for tests
 BuildRequires:  net-config
 
diff --git a/src/Atspi.cpp b/src/Atspi.cpp
new file mode 100644 (file)
index 0000000..8d4331f
--- /dev/null
@@ -0,0 +1,230 @@
+#include "Atspi.hpp"
+
+#include <Eldbus.h>
+#include <functional>
+
+#define GERROR_CHECK(error) \
+       if (error) \
+       { \
+               ERROR("Error: %s", error->message); \
+               g_error_free(error); \
+               error = nullptr; \
+       }
+
+namespace
+{
+       template<class ReturnType, class GetValueFunctionType, class InterfaceType>
+       Optional<ReturnType> getValueTemplateFunction(GetValueFunctionType getValueFunction, InterfaceType interface)
+       {
+               GError *error = nullptr;
+               ReturnType value = getValueFunction(interface, &error);
+               if (!error)
+                       return value;
+               else {
+                       GERROR_CHECK(error);
+                       return {};
+               }
+       }
+
+       bool _dbus_set_property_bool(Eldbus_Connection_Type connection_type,
+                                                                const char *bus,
+                                                                const char *path,
+                                                                const char *interface,
+                                                                const char *property,
+                                                                bool value)
+       {
+               eldbus_init();
+
+               Eldbus_Connection *conn = eldbus_connection_get(connection_type);
+               if (!conn) {
+                       ERROR("Connection to dbus failed");
+                       return false;
+               }
+
+               Eldbus_Object *dobj = eldbus_object_get(conn, bus, path);
+               eldbus_connection_unref(conn);
+               if (!dobj) {
+                       ERROR("Failed to create eldbus object");
+                       eldbus_shutdown();
+                       return false;
+               }
+
+               Eldbus_Proxy *proxy = eldbus_proxy_get(dobj, "org.freedesktop.DBus.Properties");
+               eldbus_object_unref(dobj);
+               if (!proxy) {
+                       ERROR("Failed to create proxy object for 'org.freedesktop.DBus.Properties'");
+                       eldbus_shutdown();
+                       return false;
+               }
+
+               Eldbus_Message *req = eldbus_proxy_method_call_new(proxy, "Set");
+               if (!req) {
+                       ERROR("Failed to create method call on org.freedesktop.DBus.Properties.Set");
+                       eldbus_shutdown();
+                       return false;
+               }
+               eldbus_message_ref(req);
+               bool ret = false;
+               if (eldbus_message_arguments_append(req, "ss", interface, property)) {
+                       Eldbus_Message_Iter *iter = eldbus_message_iter_container_new(eldbus_message_iter_get(req), 'v', "b");
+                       if (iter) {
+                               if (eldbus_message_iter_arguments_append(iter, "b", value)) {
+                                       if (eldbus_message_iter_container_close(eldbus_message_iter_get(req), iter)) {
+                                               eldbus_proxy_send(proxy, req, NULL, NULL, -1.0);
+                                               ret = true;
+                                       } else
+                                               ERROR("Failed to close variant iterator");
+                               } else
+                                       ERROR("Unable to append to variant iterator");
+                       } else
+                               ERROR("Unable to create variant iterator");
+               } else
+                       ERROR("Failed to append message args");
+               eldbus_message_unref(req);
+
+               eldbus_shutdown();
+               return ret;
+       }
+}
+
+bool Atspi::ConnectAtClient()
+{
+       auto error = atspi_init();
+       if (error != 0 || error != 1) {
+               ERROR("Error code %d", error);
+               return false;
+       }
+
+       auto dbusStatus = _dbus_set_property_bool(ELDBUS_CONNECTION_TYPE_SESSION, "org.a11y.Bus", "/org/a11y/bus", "org.a11y.Status", "IsEnabled", true);
+       if (!dbusStatus) {
+               ERROR("IsEnabled flag set to true procedure failed");
+               return false;
+       }
+
+       if (error == 1)
+               DEBUG("Atspi already initialized");
+       return true;
+}
+
+bool Atspi::DisconnectAtClient()
+{
+       auto dbusStatus = _dbus_set_property_bool(ELDBUS_CONNECTION_TYPE_SESSION, "org.a11y.Bus", "/org/a11y/bus", "org.a11y.Status", "IsEnabled", false);
+       if (!dbusStatus)
+               ERROR("IsEnabled flag set to false procedure failed");
+
+       auto error = atspi_exit();
+       if (error) {
+               ERROR("Memory leak detected. Error code %d", error);
+               return false;
+       }
+       return true;
+}
+
+Optional<std::shared_ptr<AtspiAction>> Atspi::getActionInterface(std::shared_ptr<AtspiAccessible> accessibleObj)
+{
+       auto action = atspi_accessible_get_action_iface(accessibleObj.get());
+       if (action)
+               return std::shared_ptr<AtspiAction> {action, g_object_unref};
+       DEBUG("Action interface is not available");
+       return {};
+}
+
+bool Atspi::doAction(std::shared_ptr<AtspiAction> accessibleObj, int action)
+{
+       GError *error = nullptr;
+       auto status = atspi_action_do_action(accessibleObj.get(), action, &error);
+       GERROR_CHECK(error);
+       if (status)
+               return true;
+
+       DEBUG("Action execution failure");
+       return false;
+}
+
+bool Atspi::doActionName(std::shared_ptr<AtspiAction> accessibleObj, const std::string &action)
+{
+       GError *error = nullptr;
+       auto status = atspi_action_do_action_name(accessibleObj.get(), action.c_str(), &error);
+       GERROR_CHECK(error)
+       if (status)
+               return true;
+
+       DEBUG("Action by name execution failure");
+       return false;
+}
+
+Optional<std::shared_ptr<AtspiAccessible>> Atspi::getObjectInRelation(std::shared_ptr<AtspiAccessible> accessibleObj, AtspiRelationType searchType)
+{
+       if (!accessibleObj) {
+               ERROR("accessibleObj is nullptr");
+               return {};
+       }
+
+       GError *error = nullptr;
+       auto relations = atspi_accessible_get_relation_set(accessibleObj.get(), &error);
+       GERROR_CHECK(error);
+       if (!relations) {
+               DEBUG("Relation set do not exist");
+               return {};
+       }
+
+       DEBUG("Relations found: %d", relations->len);
+       AtspiAccessible *relationObj = nullptr;
+       for (unsigned i = 0; i < relations->len; ++i) {
+               AtspiRelation *relation = g_array_index(relations, AtspiRelation *, i);
+               auto type = atspi_relation_get_relation_type(relation);
+               DEBUG("Relation type: %d", type);
+
+               if (type == searchType) {
+                       DEBUG("Searched relation found");
+                       relationObj = atspi_relation_get_target(relation, 0);
+                       break;
+               }
+       }
+       g_array_free(relations, TRUE);
+       if (relationObj)
+               return std::shared_ptr<AtspiAccessible> {relationObj, g_object_unref};
+       return {};
+}
+
+Optional<std::shared_ptr<AtspiValue>> Atspi::getValueInterface(std::shared_ptr<AtspiAccessible> accessibleObj)
+{
+       auto valueInterface = atspi_accessible_get_value_iface(accessibleObj.get());
+       if (valueInterface)
+               return std::shared_ptr<AtspiValue> {valueInterface, g_object_unref};
+       else {
+               DEBUG("Object %p do not has value interface", accessibleObj.get());
+               return {};
+       }
+}
+
+Optional<double> Atspi::getCurrentValue(std::shared_ptr<AtspiValue> valueInterface)
+{
+       return getValueTemplateFunction<double>(atspi_value_get_current_value, valueInterface.get());
+}
+
+bool Atspi::setCurrentValue(std::shared_ptr<AtspiValue> valueInterface, double newValue)
+{
+       GError *error = nullptr;
+       auto isSuccessful = atspi_value_set_current_value(valueInterface.get(), newValue, &error);
+       GERROR_CHECK(error);
+       if (isSuccessful)
+               return true;
+       else
+               return false;
+}
+
+Optional<double> Atspi::getMinimumIncrement(std::shared_ptr<AtspiValue> valueInterface)
+{
+       return getValueTemplateFunction<double>(atspi_value_get_minimum_increment, valueInterface.get());
+}
+
+Optional<double> Atspi::getMaximumValue(std::shared_ptr<AtspiValue> valueInterface)
+{
+       return getValueTemplateFunction<double>(atspi_value_get_maximum_value, valueInterface.get());
+}
+
+Optional<double> Atspi::getMinimumValue(std::shared_ptr<AtspiValue> valueInterface)
+{
+       return getValueTemplateFunction<double>(atspi_value_get_minimum_value, valueInterface.get());
+}
diff --git a/src/Atspi.hpp b/src/Atspi.hpp
new file mode 100644 (file)
index 0000000..9843638
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef ATSPI_ADAPTER_HPP
+#define ATSTI_ADAPTER_HPP
+
+#include "Optional.hpp"
+
+#include <atspi/atspi.h>
+#include <memory>
+
+namespace Atspi
+{
+       bool ConnectAtClient();
+       bool DisconnectAtClient();
+
+       Optional<std::shared_ptr<AtspiAction>> getActionInterface(std::shared_ptr<AtspiAccessible> accessibleObj);
+       bool doAction(std::shared_ptr<AtspiAction> accessibleObj, int action);
+       bool doActionName(std::shared_ptr<AtspiAction> accessibleObj, const std::string &action);
+
+       Optional<std::shared_ptr<AtspiAccessible>> getObjectInRelation(std::shared_ptr<AtspiAccessible> accessibleObj, AtspiRelationType searchType);
+
+       Optional<std::shared_ptr<AtspiValue>> getValueInterface(std::shared_ptr<AtspiAccessible> accessibleObj);
+       Optional<double> getCurrentValue(std::shared_ptr<AtspiValue> valueInterface);
+       bool setCurrentValue(std::shared_ptr<AtspiValue> valueInterface, double newValue);
+       Optional<double> getMinimumIncrement(std::shared_ptr<AtspiValue> valueInterface);
+       Optional<double> getMaximumValue(std::shared_ptr<AtspiValue> valueInterface);
+       Optional<double> getMinimumValue(std::shared_ptr<AtspiValue> valueInterface);
+}
+
+#endif
index 2fbdb0f..6dd5df8 100644 (file)
@@ -22,6 +22,7 @@ SET(SRCS
        ActivityChangeRequest.cpp
        AutoScanOnActivity.cpp
        MenuBuilder.cpp
+       Atspi.cpp
 )
 
 ADD_LIBRARY(universal-switch-obj OBJECT ${SRCS})
diff --git a/src/Optional.hpp b/src/Optional.hpp
new file mode 100644 (file)
index 0000000..d5b825e
--- /dev/null
@@ -0,0 +1,145 @@
+#ifndef OPTIONAL_HPP
+#define OPTIONAL_HPP
+
+/*
+ * After project conversion to C++17 standard this template class will be deleted
+ *
+ * Allowed operations (note, to make code simplier, than original, value class must have accessible copy and move constructor):
+ *  - constructing empty (valueless) object
+ *  - copying Optional (with and without value)
+ *  - moving Optional (with and without value)
+ *  - querying, if Optional has value (via explicit operator bool), for example:
+ *        Optional<int> v = ...;
+ *        if (v) ... // if v has value, then do something
+ *  - accessing value (via operator *), for example:
+ *        Optional<int> v = ...;
+ *        auto z = *v; // z now has the same int, as v (copied)
+ *        auto &y = *v; // y now has REFERENCe to int inside v, so modying y modifies v
+ */
+
+#include "UniversalSwitchLog.hpp"
+
+#include <new>
+#include <utility>
+#include <type_traits>
+
+template <typename A>
+class Optional
+{
+       union {
+               A place;
+       };
+       bool hasValue = false;
+public:
+       Optional() {}
+
+       template < typename U = A, typename std::enable_if <
+                                  std::is_convertible < U &&, A >::value &&
+                                  std::is_constructible < A, U && >::value &&
+                                  !std::is_same<typename std::decay<U>::type, Optional<A>>::value
+                                  , int * >::type = nullptr > constexpr Optional(U && a)
+               : place(std::forward<U>(a)), hasValue(true)
+       {
+       }
+
+       template < typename U = A, typename std::enable_if <
+                                  !std::is_convertible < U &&, A >::value &&
+                                  std::is_constructible < A, U && >::value &&
+                                  !std::is_same<typename std::decay<U>::type, Optional<A>>::value
+                                  , int * >::type = nullptr > explicit constexpr Optional(U && a)
+               : place(std::forward<U>(a)), hasValue(true)
+       {
+       }
+
+       Optional(const Optional &v) : place(v.place), hasValue(v.hasValue)
+       {
+       }
+
+       Optional(Optional &&v) : place(std::move(v.place)), hasValue(v.hasValue)
+       {
+       }
+
+       ~Optional()
+       {
+               if (hasValue) {
+                       place.~A();
+               }
+       }
+
+       explicit operator bool () const
+       {
+               return hasValue;
+       }
+
+       A &operator * ()
+       {
+               ASSERT(hasValue, "Object is not initialized");
+               return place;
+       }
+
+       const A &operator * () const
+       {
+               ASSERT(hasValue, "Object is not initialized");
+               return place;
+       }
+
+       A *operator -> ()
+       {
+               ASSERT(hasValue, "Object is not initialized");
+               return &place;
+       }
+
+       const A *operator -> () const
+       {
+               ASSERT(hasValue, "Object is not initialized");
+               return &place;
+       }
+
+       Optional &operator = (const Optional &v)
+       {
+               if (this != &v) {
+                       if (hasValue != v.hasValue) {
+                               if (v.hasValue) new (&place) A(v.place);
+                               else place.~A();
+                               hasValue = v.hasValue;
+                       } else if (hasValue) {
+                               place.~A();
+                               new (&place) A(v.place);
+                       }
+               }
+               return *this;
+       }
+
+       Optional &operator = (Optional &&v)
+       {
+               if (this != &v) {
+                       if (hasValue != v.hasValue) {
+                               if (v.hasValue) new (&place) A(std::move(v.place));
+                               else place.~A();
+                               hasValue = v.hasValue;
+                       } else if (hasValue) {
+                               place.~A();
+                               new (&place) A(std::move(v.place));
+                       }
+               }
+               return *this;
+       }
+
+       template <typename U = A>
+       typename std::enable_if <
+       !std::is_same<typename std::decay<U>::type, Optional<A>>::value &&
+                       std::is_constructible<A, U>::value &&
+                       std::is_assignable<A &, U>::value &&
+                       (!std::is_scalar<A>::value || !std::is_same<typename std::decay<U>, A>::value)
+                       , Optional & >::type operator = (U && a)
+       {
+               if (hasValue)
+                       place.~A();
+               else
+                       hasValue = true;
+               new (&place) A(std::forward<U>(a));
+               return *this;
+       }
+};
+
+#endif
index 40589c6..e239707 100644 (file)
@@ -1,12 +1,11 @@
 #include "UniversalSwitchLog.hpp"
 #include "Window.hpp"
 #include "ScreenScannerManager.hpp"
+#include "Atspi.hpp"
 
 #include <tizen.h>
 #include <app.h>
 #include <Elementary.h>
-#include <vconf.h>
-
 
 static void _setting_time_lang_changed(app_event_info_h event_info, void *data)
 {
@@ -24,6 +23,7 @@ static void _setting_time_lang_changed(app_event_info_h event_info, void *data)
 bool app_create(void *data)
 {
        DEBUG("App create");
+       Atspi::ConnectAtClient();
 
        return true;
 }
@@ -31,6 +31,7 @@ bool app_create(void *data)
 void app_terminate(void *data)
 {
        DEBUG("app termination procedure");
+       Atspi::DisconnectAtClient();
 
        return;
 }
index ddf882e..8ec0a7e 100644 (file)
@@ -25,8 +25,8 @@ ENDFUNCTION()
 
 INSTALL(PROGRAMS VConf_init.sh DESTINATION ${TESTS_INSTALL_DIR})
 CREATE_TEST(VConfImplTests.cpp)
+CREATE_TEST(OptionalTests.cpp)
 
 CREATE_TEST(ConfigurationTests.cpp)
 CREATE_TEST(ActivityProcessingTests.cpp)
 CREATE_TEST(MenuBuilderTests.cpp)
-
diff --git a/tests/OptionalTests.cpp b/tests/OptionalTests.cpp
new file mode 100644 (file)
index 0000000..942e2df
--- /dev/null
@@ -0,0 +1,289 @@
+#include "Optional.hpp"
+
+#include <gtest/gtest.h>
+
+class OptionalTestsFixture : public ::testing::Test
+{
+public:
+       struct AA {
+               AA() = default;
+               int value = 10;
+       };
+
+       class ClassWithCounters
+       {
+       public:
+               ClassWithCounters(int a)
+                       : value(std::move(a))
+               {
+                       ++constructorCallsCount;
+               }
+
+               ClassWithCounters(const ClassWithCounters &other)
+                       : value(other.value)
+               {
+                       ++constructorCallsCount;
+               }
+
+               ClassWithCounters(ClassWithCounters &&other)
+                       : value(std::move(other.value))
+               {
+                       ++constructorCallsCount;
+               }
+
+               ClassWithCounters(const Optional<ClassWithCounters> &other)
+               {
+                       ++constructorCallsCount;
+                       if (other)
+                               value = other->value;
+               }
+
+               explicit ClassWithCounters(const AA &other)
+               {
+                       ++constructorCallsCount;
+                       value = other.value;
+               }
+
+               ~ClassWithCounters()
+               {
+                       ++destructorCallsCount;
+               }
+
+               ClassWithCounters &operator = (const ClassWithCounters &other)
+               {
+                       if (this != &other) {
+                               value = other.value;
+                       }
+                       return *this;
+               }
+
+               static int constructorCallsCount;
+               static int destructorCallsCount;
+               int value = 0;
+       };
+
+       void SetUp()
+       {
+               ClassWithCounters::constructorCallsCount = 0;
+               ClassWithCounters::destructorCallsCount = 0;
+       }
+
+       void SetExpects(int constructorCalls, int destructorCalls)
+       {
+               EXPECT_EQ(ClassWithCounters::constructorCallsCount, constructorCalls) << "Wrong number of constructor calls";
+               EXPECT_EQ(ClassWithCounters::destructorCallsCount, destructorCalls) << "Wrong number of destructor calls";
+       }
+
+};
+int OptionalTestsFixture::ClassWithCounters::constructorCallsCount = 0;
+int OptionalTestsFixture::ClassWithCounters::destructorCallsCount = 0;
+
+TEST(OptionalTests, OptionalToBoolConversionChecking)
+{
+       auto emptyOptional = Optional<int> {};
+       EXPECT_FALSE(emptyOptional) << "Optional should return false if not initialized";
+       auto notEmptyOptional = Optional<int> {5};
+       EXPECT_TRUE(notEmptyOptional) << "Optional should return true if initialized";
+}
+
+TEST(OptionalTests, ValueStorageChecking)
+{
+       auto a = Optional<int> {5};
+       EXPECT_EQ(*a, 5);
+       const auto b = Optional<int> {5};
+       EXPECT_EQ(*b, 5);
+       auto c = Optional<double> {5.0};
+       EXPECT_EQ(*c, 5.0);
+       auto d = Optional<std::string> {"TIZEN"};
+       EXPECT_EQ(*d, "TIZEN");
+}
+
+TEST(OptionalTests, ConstructorChecking_FromValue)
+{
+       Optional<int> a{5};
+       ASSERT_TRUE(a);
+       EXPECT_EQ(*a, 5);
+}
+
+TEST_F(OptionalTestsFixture, ConstructorChecking_FromTypeWithoutCast)
+{
+       auto u = AA {};
+       Optional<ClassWithCounters> a{u};
+       EXPECT_EQ(u.value, 10);
+       ASSERT_TRUE(a);
+       EXPECT_EQ(a->value, 10);
+       SetExpects(1, 0);
+}
+
+TEST(OptionalTests, CopyConstructorChecking_FromEmptyOptional)
+{
+       auto original = Optional<int> {};
+       auto copy = original;
+       ASSERT_FALSE(original);
+       ASSERT_FALSE(copy);
+}
+
+TEST(OptionalTests, CopyConstructorChecking_FromValidOptional)
+{
+       auto original = Optional<int> {5};
+       auto copy = original;
+       ASSERT_TRUE(original);
+       EXPECT_EQ(*original, 5);
+       ASSERT_TRUE(copy);
+       EXPECT_EQ(*copy, 5);
+}
+
+TEST(OptionalTests, MoveConstructorChecking_FromEmptyOptional)
+{
+       auto original = Optional<int> {};
+       auto copy{std::move(original)};
+       ASSERT_FALSE(original);
+       ASSERT_FALSE(copy);
+}
+
+TEST(OptionalTests, MoveConstructorChecking_FromValidOptional)
+{
+       auto original = Optional<int> {5};
+       auto copy{std::move(original)};
+       ASSERT_TRUE(copy);
+       ASSERT_EQ(*copy, 5);
+}
+
+TEST_F(OptionalTestsFixture, DestructorChecking_EmptyOptional)
+{
+       auto a = Optional<ClassWithCounters> {};
+       a.~Optional();
+       SetExpects(0, 0);
+}
+
+TEST_F(OptionalTestsFixture, DestructorChecking_ValidOptional)
+{
+       auto a = Optional<ClassWithCounters> {5};
+       a.~Optional();
+       SetExpects(1, 1);
+}
+
+TEST_F(OptionalTestsFixture, AssignmentOperatorChecking_SameObjectEmptyOptional)
+{
+       auto a = Optional<ClassWithCounters> {};
+       a = a;
+       ASSERT_FALSE(a);
+       SetExpects(0, 0);
+}
+
+TEST_F(OptionalTestsFixture, AssignmentOperatorChecking_SameObjectValidOptional)
+{
+       auto a = Optional<ClassWithCounters> {5};
+       a = a;
+       ASSERT_TRUE(a);
+       EXPECT_EQ(a->value, 5);
+       SetExpects(1, 0);
+}
+
+TEST_F(OptionalTestsFixture, AssignmentOperatorChecking_FromEmptyToValid)
+{
+       auto a = Optional<ClassWithCounters> {};
+       auto b = Optional<ClassWithCounters> {7};
+       b = a;
+       ASSERT_FALSE(a);
+       ASSERT_FALSE(b);
+       SetExpects(1, 1);
+}
+
+TEST_F(OptionalTestsFixture, AssignmentOperatorChecking_FromValidToValid)
+{
+       auto a = Optional<ClassWithCounters> {5};
+       auto b = Optional<ClassWithCounters> {7};
+       SetExpects(2, 0);
+       b = a;
+       ASSERT_TRUE(a);
+       EXPECT_EQ(a->value, 5);
+       ASSERT_TRUE(b);
+       EXPECT_EQ(b->value, 5);
+       SetExpects(3, 1);
+}
+
+TEST_F(OptionalTestsFixture, AssignmentOperatorChecking_FromValidToEmpty)
+{
+       auto a = Optional<ClassWithCounters> {5};
+       auto b = Optional<ClassWithCounters> {};
+       b = a;
+       ASSERT_TRUE(a);
+       EXPECT_EQ(a->value, 5);
+       ASSERT_TRUE(b);
+       EXPECT_EQ(b->value, 5);
+       SetExpects(2, 0);
+}
+
+TEST_F(OptionalTestsFixture, AssignmentMoveOperatorChecking_SameObjectEmptyOptional)
+{
+       auto a = Optional<ClassWithCounters> {};
+       a = std::move(a);
+       ASSERT_FALSE(a);
+       SetExpects(0, 0);
+}
+
+TEST_F(OptionalTestsFixture, AssignmentMoveOperatorChecking_SameObjectValidOptional)
+{
+       auto a = Optional<ClassWithCounters> {5};
+       a = std::move(a);
+       ASSERT_TRUE(a);
+       EXPECT_EQ(a->value, 5);
+       SetExpects(1, 0);
+}
+
+TEST_F(OptionalTestsFixture, AssignmentMoveOperatorChecking_FromEmptyToValid)
+{
+       auto a = Optional<ClassWithCounters> {};
+       auto b = Optional<ClassWithCounters> {7};
+       b = std::move(a);
+       ASSERT_FALSE(a);
+       ASSERT_FALSE(b);
+       SetExpects(1, 1);
+}
+
+TEST_F(OptionalTestsFixture, AssignmentMoveOperatorChecking_FromValidToValid)
+{
+       auto a = Optional<ClassWithCounters> {5};
+       auto b = Optional<ClassWithCounters> {7};
+       b = std::move(a);
+       ASSERT_TRUE(a);
+       EXPECT_EQ(a->value, 5);
+       ASSERT_TRUE(b);
+       EXPECT_EQ(b->value, 5);
+       SetExpects(3, 1);
+}
+
+TEST_F(OptionalTestsFixture, AssignmentMoveOperatorChecking_FromValidToEmpty)
+{
+       auto a = Optional<ClassWithCounters> {5};
+       auto b = Optional<ClassWithCounters> {};
+       b = std::move(a);
+       EXPECT_TRUE(a);
+       EXPECT_EQ(a->value, 5);
+       EXPECT_TRUE(b);
+       EXPECT_EQ(b->value, 5);
+       SetExpects(2, 0);
+}
+
+TEST_F(OptionalTestsFixture, ValueAssignmentOperatorChecking_FromValidToEmpty)
+{
+       auto a = ClassWithCounters {5};
+       auto b = Optional<ClassWithCounters> {};
+       b = a;
+       EXPECT_EQ(a.value, 5);
+       EXPECT_TRUE(b);
+       EXPECT_EQ(b->value, 5);
+       SetExpects(2, 0);
+}
+
+TEST_F(OptionalTestsFixture, ValueAssignmentOperatorChecking_FromValidToValid)
+{
+       auto a = ClassWithCounters {5};
+       auto b = Optional<ClassWithCounters> {7};
+       b = a;
+       EXPECT_EQ(a.value, 5);
+       EXPECT_TRUE(b);
+       EXPECT_EQ(b->value, 5);
+       SetExpects(3, 1);
+}