Add API to create new containers
[platform/core/security/vasum.git] / tests / unit_tests / server / ut-containers-manager.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Jan Olszak <j.olszak@samsung.com>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License
17  */
18
19
20 /**
21  * @file
22  * @author  Jan Olszak (j.olszak@samsung.com)
23  * @brief   Unit tests of the ContainersManager class
24  */
25
26 #include "config.hpp"
27 #include "ut.hpp"
28
29 #include "containers-manager.hpp"
30 #include "container-dbus-definitions.hpp"
31 #include "host-dbus-definitions.hpp"
32 #include "test-dbus-definitions.hpp"
33 // TODO: Switch to real power-manager dbus defs when they will be implemented in power-manager
34 #include "fake-power-manager-dbus-definitions.hpp"
35 #include "exception.hpp"
36
37 #include "dbus/connection.hpp"
38 #include "dbus/exception.hpp"
39 #include "utils/glib-loop.hpp"
40 #include "config/exception.hpp"
41 #include "utils/latch.hpp"
42 #include "utils/fs.hpp"
43 #include "utils/img.hpp"
44
45 #include <vector>
46 #include <map>
47 #include <memory>
48 #include <string>
49 #include <algorithm>
50 #include <functional>
51 #include <mutex>
52 #include <condition_variable>
53 #include <boost/filesystem.hpp>
54
55 using namespace security_containers;
56 using namespace config;
57 using namespace security_containers::utils;
58 using namespace dbus;
59
60 namespace {
61
62 const std::string TEST_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/test-daemon.conf";
63 const std::string TEST_DBUS_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/test-dbus-daemon.conf";
64 const std::string BUGGY_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/buggy-daemon.conf";
65 const std::string BUGGY_FOREGROUND_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/buggy-foreground-daemon.conf";
66 const std::string BUGGY_DEFAULTID_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/buggy-default-daemon.conf";
67 const std::string TEST_CONTAINER_CONF_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/containers/";
68 const std::string TEST_CONTAINER_LIBVIRT_CONF_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/libvirt-config/";
69 const std::string MISSING_CONFIG_PATH = "/this/is/a/missing/file/path/missing-daemon.conf";
70 const int EVENT_TIMEOUT = 5000;
71 const int TEST_DBUS_CONNECTION_CONTAINERS_COUNT = 3;
72 const std::string PREFIX_CONSOLE_NAME = "ut-containers-manager-console";
73 const std::string TEST_APP_NAME = "testapp";
74 const std::string TEST_MESSAGE = "testmessage";
75 const std::string FILE_CONTENT = "File content\n"
76                                  "Line 1\n"
77                                  "Line 2\n";
78 const std::string NON_EXISTANT_CONTAINER_ID = "NON_EXISTANT_CONTAINER_ID";
79
80 class DbusAccessory {
81 public:
82     static const int HOST_ID = 0;
83
84     typedef std::function<void(const std::string& argument,
85                                MethodResultBuilder::Pointer result
86                               )> TestApiMethodCallback;
87     typedef std::function<void()> AddContainerResultCallback;
88
89     typedef std::map<std::string, std::string> Dbuses;
90
91     DbusAccessory(int id)
92         : mId(id),
93           mClient(DbusConnection::create(acquireAddress())),
94           mNameAcquired(false),
95           mPendingDisconnect(false)
96     {
97     }
98
99     void setName(const std::string& name)
100     {
101         mClient->setName(name,
102                          std::bind(&DbusAccessory::onNameAcquired, this),
103                          std::bind(&DbusAccessory::onDisconnect, this));
104
105         if(!waitForName()) {
106             mClient.reset();
107             throw dbus::DbusOperationException("Could not acquire name.");
108         }
109     }
110
111     bool waitForName()
112     {
113         std::unique_lock<std::mutex> lock(mMutex);
114         mNameCondition.wait(lock, [this] {return mNameAcquired || mPendingDisconnect;});
115         return mNameAcquired;
116     }
117
118     void onNameAcquired()
119     {
120         std::unique_lock<std::mutex> lock(mMutex);
121         mNameAcquired = true;
122         mNameCondition.notify_one();
123     }
124
125     void onDisconnect()
126     {
127         std::unique_lock<std::mutex> lock(mMutex);
128         mPendingDisconnect = true;
129         mNameCondition.notify_one();
130     }
131
132     void signalSubscribe(const DbusConnection::SignalCallback& callback)
133     {
134         mClient->signalSubscribe(callback, isHost() ? api::host::BUS_NAME : api::container::BUS_NAME);
135     }
136
137     void emitSignal(const std::string& objectPath,
138                     const std::string& interface,
139                     const std::string& name,
140                     GVariant* parameters)
141     {
142         mClient->emitSignal(objectPath, interface, name, parameters);
143     }
144
145     void callMethodNotify()
146     {
147         GVariant* parameters = g_variant_new("(ss)", TEST_APP_NAME.c_str(), TEST_MESSAGE.c_str());
148         mClient->callMethod(api::container::BUS_NAME,
149                             api::container::OBJECT_PATH,
150                             api::container::INTERFACE,
151                             api::container::METHOD_NOTIFY_ACTIVE_CONTAINER,
152                             parameters,
153                             "()");
154     }
155
156     std::string callMethodMove(const std::string& dest, const std::string& path)
157     {
158         GVariant* parameters = g_variant_new("(ss)", dest.c_str(), path.c_str());
159         GVariantPtr result = mClient->callMethod(api::container::BUS_NAME,
160                                                  api::container::OBJECT_PATH,
161                                                  api::container::INTERFACE,
162                                                  api::container::METHOD_FILE_MOVE_REQUEST,
163                                                  parameters,
164                                                  "(s)");
165
166         const gchar* retcode = NULL;
167         g_variant_get(result.get(), "(&s)", &retcode);
168         return std::string(retcode);
169     }
170
171     void registerTestApiObject(const TestApiMethodCallback& callback)
172     {
173         auto handler = [callback](const std::string& objectPath,
174                           const std::string& interface,
175                           const std::string& methodName,
176                           GVariant* parameters,
177                           MethodResultBuilder::Pointer result) {
178             if (objectPath == testapi::OBJECT_PATH &&
179                 interface == testapi::INTERFACE &&
180                 methodName == testapi::METHOD) {
181                 const gchar* argument = NULL;
182                 g_variant_get(parameters, "(&s)", &argument);
183                 if (callback) {
184                     callback(argument, result);
185                 }
186             }
187         };
188         mClient->registerObject(testapi::OBJECT_PATH, testapi::DEFINITION, handler);
189     }
190
191     std::string testApiProxyCall(const std::string& target, const std::string& argument)
192     {
193         GVariant* parameters = g_variant_new("(s)", argument.c_str());
194         GVariantPtr result = proxyCall(target,
195                                        testapi::BUS_NAME,
196                                        testapi::OBJECT_PATH,
197                                        testapi::INTERFACE,
198                                        testapi::METHOD,
199                                        parameters);
200         const gchar* ret = NULL;
201         g_variant_get(result.get(), "(&s)", &ret);
202         return ret;
203     }
204
205
206     GVariantPtr proxyCall(const std::string& target,
207                           const std::string& busName,
208                           const std::string& objectPath,
209                           const std::string& interface,
210                           const std::string& method,
211                           GVariant* parameters)
212     {
213         GVariant* packedParameters = g_variant_new("(sssssv)",
214                                                    target.c_str(),
215                                                    busName.c_str(),
216                                                    objectPath.c_str(),
217                                                    interface.c_str(),
218                                                    method.c_str(),
219                                                    parameters);
220         GVariantPtr result = mClient->callMethod(isHost() ? api::host::BUS_NAME :
221                                                             api::container::BUS_NAME,
222                                                  isHost() ? api::host::OBJECT_PATH :
223                                                             api::container::OBJECT_PATH,
224                                                  isHost() ? api::host::INTERFACE :
225                                                             api::container::INTERFACE,
226                                                  api::METHOD_PROXY_CALL,
227                                                  packedParameters,
228                                                  "(v)");
229         GVariant* unpackedResult = NULL;
230         g_variant_get(result.get(), "(v)", &unpackedResult);
231         return GVariantPtr(unpackedResult, g_variant_unref);
232     }
233
234     Dbuses callMethodGetContainerDbuses()
235     {
236         assert(isHost());
237         Dbuses dbuses;
238         GVariantPtr result = mClient->callMethod(api::host::BUS_NAME,
239                                                  api::host::OBJECT_PATH,
240                                                  api::host::INTERFACE,
241                                                  api::host::METHOD_GET_CONTAINER_DBUSES,
242                                                  NULL,
243                                                  "(a{ss})");
244         GVariant* array = NULL;
245         g_variant_get(result.get(), "(*)", &array);
246         dbus::GVariantPtr autounref(array, g_variant_unref);
247         size_t count = g_variant_n_children(array);
248         for (size_t n = 0; n < count; ++n) {
249             const char* containerId = NULL;
250             const char* dbusAddress = NULL;
251             g_variant_get_child(array, n, "{&s&s}", &containerId, &dbusAddress);
252             dbuses.insert(Dbuses::value_type(containerId, dbusAddress));
253         }
254         return dbuses;
255     }
256
257     std::vector<std::string> callMethodGetContainerIds()
258     {
259         assert(isHost());
260         GVariantPtr result = mClient->callMethod(api::host::BUS_NAME,
261                                                  api::host::OBJECT_PATH,
262                                                  api::host::INTERFACE,
263                                                  api::host::METHOD_GET_CONTAINER_ID_LIST,
264                                                  NULL,
265                                                  "(as)");
266
267         GVariant* array = NULL;
268         g_variant_get(result.get(), "(*)", &array);
269
270         size_t arraySize = g_variant_n_children(array);
271         std::vector<std::string> containerIds;
272         for (size_t i = 0; i < arraySize; ++i) {
273             const char* id = NULL;
274             g_variant_get_child(array, i, "&s", &id);
275             containerIds.push_back(id);
276         }
277
278         g_variant_unref(array);
279         return containerIds;
280     }
281
282     std::string callMethodGetActiveContainerId()
283     {
284         assert(isHost());
285         GVariantPtr result = mClient->callMethod(api::host::BUS_NAME,
286                                                  api::host::OBJECT_PATH,
287                                                  api::host::INTERFACE,
288                                                  api::host::METHOD_GET_ACTIVE_CONTAINER_ID,
289                                                  NULL,
290                                                  "(s)");
291
292         const char* containerId = NULL;
293         g_variant_get(result.get(), "(&s)", &containerId);
294         return containerId;
295     }
296
297     void callMethodSetActiveContainer(const std::string& id)
298     {
299         assert(isHost());
300         GVariant* parameters = g_variant_new("(s)", id.c_str());
301         GVariantPtr result = mClient->callMethod(api::host::BUS_NAME,
302                                                  api::host::OBJECT_PATH,
303                                                  api::host::INTERFACE,
304                                                  api::host::METHOD_SET_ACTIVE_CONTAINER,
305                                                  parameters,
306                                                  "()");
307
308     }
309
310     void callAsyncMethodAddContainer(const std::string& id,
311                                      const AddContainerResultCallback& result)
312     {
313         auto asyncResult = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) {
314             BOOST_CHECK(g_variant_is_of_type(asyncMethodCallResult.get(), G_VARIANT_TYPE_UNIT));
315             result();
316         };
317
318         assert(isHost());
319         GVariant* parameters = g_variant_new("(s)", id.c_str());
320         mClient->callMethodAsync(api::host::BUS_NAME,
321                                  api::host::OBJECT_PATH,
322                                  api::host::INTERFACE,
323                                  api::host::METHOD_ADD_CONTAINER,
324                                  parameters,
325                                  "()",
326                                  asyncResult);
327     }
328
329 private:
330     const int mId;
331     DbusConnection::Pointer mClient;
332     bool mNameAcquired;
333     bool mPendingDisconnect;
334     std::mutex mMutex;
335     std::condition_variable mNameCondition;
336
337     bool isHost() const {
338         return mId == HOST_ID;
339     }
340
341     std::string acquireAddress() const
342     {
343         if (isHost()) {
344             return "unix:path=/var/run/dbus/system_bus_socket";
345         }
346         return "unix:path=/tmp/ut-containers-manager/console" + std::to_string(mId) +
347                "-dbus/dbus/system_bus_socket";
348     }
349 };
350
351 std::function<bool(const std::exception&)> expectedMessage(const std::string& message) {
352     return [=](const std::exception& e) {
353         return e.what() == message;
354     };
355 }
356
357 class FileCleanerRAII {
358 public:
359     FileCleanerRAII(const std::vector<std::string>& filePathsToClean):
360         mFilePathsToClean(filePathsToClean)
361     { }
362
363     ~FileCleanerRAII()
364     {
365         namespace fs = boost::filesystem;
366         for (const auto& file : mFilePathsToClean) {
367             fs::path f(file);
368             if (fs::exists(f)) {
369                 fs::remove(f);
370             }
371         }
372     }
373
374 private:
375     const std::vector<std::string> mFilePathsToClean;
376 };
377
378 struct Fixture {
379     security_containers::utils::ScopedGlibLoop mLoop;
380 };
381
382 } // namespace
383
384
385 BOOST_FIXTURE_TEST_SUITE(ContainersManagerSuite, Fixture)
386
387 BOOST_AUTO_TEST_CASE(ConstructorDestructorTest)
388 {
389     std::unique_ptr<ContainersManager> cm;
390     BOOST_REQUIRE_NO_THROW(cm.reset(new ContainersManager(TEST_CONFIG_PATH)));
391     BOOST_REQUIRE_NO_THROW(cm.reset());
392 }
393
394 BOOST_AUTO_TEST_CASE(BuggyConfigTest)
395 {
396     BOOST_REQUIRE_THROW(ContainersManager cm(BUGGY_CONFIG_PATH), ConfigException);
397 }
398
399 BOOST_AUTO_TEST_CASE(MissingConfigTest)
400 {
401     BOOST_REQUIRE_THROW(ContainersManager cm(MISSING_CONFIG_PATH), ConfigException);
402 }
403
404 BOOST_AUTO_TEST_CASE(StartAllTest)
405 {
406     ContainersManager cm(TEST_CONFIG_PATH);
407     BOOST_REQUIRE_NO_THROW(cm.startAll());
408     BOOST_CHECK(cm.getRunningForegroundContainerId() == "ut-containers-manager-console1");
409 }
410
411 BOOST_AUTO_TEST_CASE(BuggyForegroundTest)
412 {
413     ContainersManager cm(BUGGY_FOREGROUND_CONFIG_PATH);
414     BOOST_REQUIRE_NO_THROW(cm.startAll());
415     BOOST_CHECK(cm.getRunningForegroundContainerId() == "ut-containers-manager-console2");
416 }
417
418 BOOST_AUTO_TEST_CASE(BuggyDefaultTest)
419 {
420     BOOST_REQUIRE_THROW(ContainersManager cm(BUGGY_DEFAULTID_CONFIG_PATH),
421                         ContainerOperationException);
422 }
423
424 BOOST_AUTO_TEST_CASE(StopAllTest)
425 {
426     ContainersManager cm(TEST_CONFIG_PATH);
427     BOOST_REQUIRE_NO_THROW(cm.startAll());
428     BOOST_REQUIRE_NO_THROW(cm.stopAll());
429     BOOST_CHECK(cm.getRunningForegroundContainerId().empty());
430 }
431
432 BOOST_AUTO_TEST_CASE(DetachOnExitTest)
433 {
434     {
435         ContainersManager cm(TEST_CONFIG_PATH);
436         BOOST_REQUIRE_NO_THROW(cm.startAll());
437         cm.setContainersDetachOnExit();
438     }
439     {
440         ContainersManager cm(TEST_CONFIG_PATH);
441         BOOST_REQUIRE_NO_THROW(cm.startAll());
442     }
443 }
444
445 BOOST_AUTO_TEST_CASE(FocusTest)
446 {
447     ContainersManager cm(TEST_CONFIG_PATH);
448     BOOST_REQUIRE_NO_THROW(cm.startAll());
449     BOOST_REQUIRE_NO_THROW(cm.focus("ut-containers-manager-console2"));
450     BOOST_CHECK(cm.getRunningForegroundContainerId() == "ut-containers-manager-console2");
451     BOOST_REQUIRE_NO_THROW(cm.focus("ut-containers-manager-console1"));
452     BOOST_CHECK(cm.getRunningForegroundContainerId() == "ut-containers-manager-console1");
453     BOOST_REQUIRE_NO_THROW(cm.focus("ut-containers-manager-console3"));
454     BOOST_CHECK(cm.getRunningForegroundContainerId() == "ut-containers-manager-console3");
455 }
456
457 BOOST_AUTO_TEST_CASE(NotifyActiveContainerTest)
458 {
459     ContainersManager cm(TEST_DBUS_CONFIG_PATH);
460     cm.startAll();
461
462     Latch signalReceivedLatch;
463     std::map<int, std::vector<std::string>> signalReceivedSourcesMap;
464
465     std::map<int, std::unique_ptr<DbusAccessory>> dbuses;
466     for (int i = 1; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
467         dbuses[i] = std::unique_ptr<DbusAccessory>(new DbusAccessory(i));
468     }
469
470     auto handler = [](Latch& latch,
471                       std::vector<std::string>& receivedSignalSources,
472                       const std::string& /*senderBusName*/,
473                       const std::string& objectPath,
474                       const std::string& interface,
475                       const std::string& signalName,
476                       GVariant* parameters)
477         {
478             if (objectPath == api::container::OBJECT_PATH &&
479                 interface == api::container::INTERFACE &&
480                 signalName == api::container::SIGNAL_NOTIFICATION &&
481                 g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) {
482
483                 const gchar* container = NULL;
484                 const gchar* application = NULL;
485                 const gchar* message = NULL;
486                 g_variant_get(parameters, "(&s&s&s)", &container, &application, &message);
487                 receivedSignalSources.push_back(container);
488                 if (application == TEST_APP_NAME && message == TEST_MESSAGE) {
489                     latch.set();
490                 }
491             }
492         };
493
494     using namespace std::placeholders;
495     for (int i = 1; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
496         dbuses[i]->signalSubscribe(std::bind(handler,
497                                              std::ref(signalReceivedLatch),
498                                              std::ref(signalReceivedSourcesMap[i]),
499                                              _1, _2, _3, _4, _5));
500     }
501     for (auto& dbus : dbuses) {
502         dbus.second->callMethodNotify();
503     }
504
505     BOOST_CHECK(signalReceivedLatch.waitForN(dbuses.size() - 1, EVENT_TIMEOUT));
506     BOOST_CHECK(signalReceivedLatch.empty());
507
508     //check if there are no signals that was received more than once
509     for (const auto& source : signalReceivedSourcesMap[1]) {
510         BOOST_CHECK_EQUAL(std::count(signalReceivedSourcesMap[1].begin(),
511                                      signalReceivedSourcesMap[1].end(),
512                                      source), 1);
513     }
514     //check if all signals was received by active container
515     BOOST_CHECK_EQUAL(signalReceivedSourcesMap[1].size(), dbuses.size() - 1);
516     //check if no signals was received by inactive container
517     for (size_t i = 2; i <= dbuses.size(); ++i) {
518         BOOST_CHECK(signalReceivedSourcesMap[i].empty());
519     }
520
521     dbuses.clear();
522 }
523
524 BOOST_AUTO_TEST_CASE(DisplayOffTest)
525 {
526     ContainersManager cm(TEST_DBUS_CONFIG_PATH);
527     BOOST_REQUIRE_NO_THROW(cm.startAll());
528
529     std::vector<std::unique_ptr<DbusAccessory>> clients;
530     for (int i = 1; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
531         clients.push_back(std::unique_ptr<DbusAccessory>(new DbusAccessory(i)));
532     }
533
534     for (auto& client : clients) {
535         client->setName(fake_power_manager_api::BUS_NAME);
536     }
537
538     std::mutex Mutex;
539     std::unique_lock<std::mutex> Lock(Mutex);
540     std::condition_variable Condition;
541     auto cond = [&cm]() -> bool {
542         return cm.getRunningForegroundContainerId() == "ut-containers-manager-console1-dbus";
543     };
544
545     for (auto& client : clients) {
546         // TEST SWITCHING TO DEFAULT CONTAINER
547         // focus non-default container
548         BOOST_REQUIRE_NO_THROW(cm.focus("ut-containers-manager-console3-dbus"));
549
550         // emit signal from dbus connection
551         BOOST_REQUIRE_NO_THROW(client->emitSignal(fake_power_manager_api::OBJECT_PATH,
552                                                   fake_power_manager_api::INTERFACE,
553                                                   fake_power_manager_api::SIGNAL_DISPLAY_OFF,
554                                                   nullptr));
555
556         // check if default container has focus
557         BOOST_CHECK(Condition.wait_for(Lock, std::chrono::milliseconds(EVENT_TIMEOUT), cond));
558     }
559 }
560
561 BOOST_AUTO_TEST_CASE(MoveFileTest)
562 {
563     ContainersManager cm(TEST_DBUS_CONFIG_PATH);
564     cm.startAll();
565
566     Latch notificationLatch;
567     std::string notificationSource;
568     std::string notificationPath;
569     std::string notificationRetcode;
570
571     std::map<int, std::unique_ptr<DbusAccessory>> dbuses;
572     for (int i = 1; i <= 2; ++i) {
573         dbuses[i] = std::unique_ptr<DbusAccessory>(new DbusAccessory(i));
574     }
575
576     auto handler = [&](const std::string& /*senderBusName*/,
577                        const std::string& objectPath,
578                        const std::string& interface,
579                        const std::string& signalName,
580                        GVariant* parameters)
581         {
582             if (objectPath == api::container::OBJECT_PATH &&
583                 interface == api::container::INTERFACE &&
584                 signalName == api::container::SIGNAL_NOTIFICATION &&
585                 g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) {
586
587                 const gchar* source = NULL;
588                 const gchar* path = NULL;
589                 const gchar* retcode = NULL;
590                 g_variant_get(parameters, "(&s&s&s)", &source, &path, &retcode);
591
592                 notificationSource = source;
593                 notificationPath = path;
594                 notificationRetcode = retcode;
595                 notificationLatch.set();
596             }
597         };
598
599     // subscribe the second (destination) container for notifications
600     dbuses.at(2)->signalSubscribe(handler);
601
602     const std::string TMP = "/tmp";
603     const std::string NO_PATH = "path_doesnt_matter_here";
604     const std::string BUGGY_PATH = TMP + "/this_file_does_not_exist";
605     const std::string BUGGY_CONTAINER = "this-container-does-not-exist";
606     const std::string CONTAINER1 = "ut-containers-manager-console1-dbus";
607     const std::string CONTAINER2 = "ut-containers-manager-console2-dbus";
608     const std::string CONTAINER1PATH = TMP + "/" + CONTAINER1 + TMP;
609     const std::string CONTAINER2PATH = TMP + "/" + CONTAINER2 + TMP;
610
611     // sending to a non existing container
612     BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(BUGGY_CONTAINER, NO_PATH),
613                       api::container::FILE_MOVE_DESTINATION_NOT_FOUND);
614     BOOST_CHECK(notificationLatch.empty());
615
616     // sending to self
617     BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER1, NO_PATH),
618                       api::container::FILE_MOVE_WRONG_DESTINATION);
619     BOOST_CHECK(notificationLatch.empty());
620
621     // no permission to send
622     BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER2, "/etc/secret1"),
623                       api::container::FILE_MOVE_NO_PERMISSIONS_SEND);
624     BOOST_CHECK(notificationLatch.empty());
625
626     // no permission to receive
627     BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER2, "/etc/secret2"),
628                       api::container::FILE_MOVE_NO_PERMISSIONS_RECEIVE);
629     BOOST_CHECK(notificationLatch.empty());
630
631     // non existing file
632     BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER2, BUGGY_PATH),
633                       api::container::FILE_MOVE_FAILED);
634     BOOST_CHECK(notificationLatch.empty());
635
636     // a working scenario
637     namespace fs = boost::filesystem;
638     boost::system::error_code ec;
639     fs::remove_all(CONTAINER1PATH, ec);
640     fs::remove_all(CONTAINER2PATH, ec);
641     BOOST_REQUIRE(fs::create_directories(CONTAINER1PATH, ec));
642     BOOST_REQUIRE(fs::create_directories(CONTAINER2PATH, ec));
643     BOOST_REQUIRE(utils::saveFileContent(CONTAINER1PATH + "/file", FILE_CONTENT));
644
645     BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER2, TMP + "/file"),
646                       api::container::FILE_MOVE_SUCCEEDED);
647     BOOST_CHECK(notificationLatch.wait(EVENT_TIMEOUT));
648     BOOST_CHECK(notificationLatch.empty());
649     BOOST_CHECK_EQUAL(notificationSource, CONTAINER1);
650     BOOST_CHECK_EQUAL(notificationPath, TMP + "/file");
651     BOOST_CHECK_EQUAL(notificationRetcode, api::container::FILE_MOVE_SUCCEEDED);
652     BOOST_CHECK(!fs::exists(CONTAINER1PATH + "/file"));
653     BOOST_CHECK_EQUAL(utils::readFileContent(CONTAINER2PATH + "/file"), FILE_CONTENT);
654
655     fs::remove_all(CONTAINER1PATH, ec);
656     fs::remove_all(CONTAINER2PATH, ec);
657 }
658
659 BOOST_AUTO_TEST_CASE(AllowSwitchToDefaultTest)
660 {
661     ContainersManager cm(TEST_DBUS_CONFIG_PATH);
662     BOOST_REQUIRE_NO_THROW(cm.startAll());
663
664     std::vector<std::unique_ptr<DbusAccessory>> clients;
665     for (int i = 1; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
666         clients.push_back(std::unique_ptr<DbusAccessory>(new DbusAccessory(i)));
667     }
668
669     for (auto& client : clients) {
670         client->setName(fake_power_manager_api::BUS_NAME);
671     }
672
673     std::mutex condMutex;
674     std::unique_lock<std::mutex> condLock(condMutex);
675     std::condition_variable condition;
676     auto cond = [&cm]() -> bool {
677         return cm.getRunningForegroundContainerId() == "ut-containers-manager-console1-dbus";
678     };
679
680     for (auto& client : clients) {
681         // focus non-default container with allowed switching
682         BOOST_REQUIRE_NO_THROW(cm.focus("ut-containers-manager-console3-dbus"));
683
684         // emit signal from dbus connection
685         BOOST_REQUIRE_NO_THROW(client->emitSignal(fake_power_manager_api::OBJECT_PATH,
686                                                   fake_power_manager_api::INTERFACE,
687                                                   fake_power_manager_api::SIGNAL_DISPLAY_OFF,
688                                                   nullptr));
689
690         // check if default container has focus
691         BOOST_CHECK(condition.wait_for(condLock, std::chrono::milliseconds(EVENT_TIMEOUT), cond));
692
693         // focus non-default container with disabled switching
694         BOOST_REQUIRE_NO_THROW(cm.focus("ut-containers-manager-console2-dbus"));
695
696         // emit signal from dbus connection
697         BOOST_REQUIRE_NO_THROW(client->emitSignal(fake_power_manager_api::OBJECT_PATH,
698                                                   fake_power_manager_api::INTERFACE,
699                                                   fake_power_manager_api::SIGNAL_DISPLAY_OFF,
700                                                   nullptr));
701
702         // now default container should not be focused
703         BOOST_CHECK(!condition.wait_for(condLock, std::chrono::milliseconds(EVENT_TIMEOUT), cond));
704     }
705 }
706
707 BOOST_AUTO_TEST_CASE(ProxyCallTest)
708 {
709     ContainersManager cm(TEST_DBUS_CONFIG_PATH);
710     cm.startAll();
711
712     std::map<int, std::unique_ptr<DbusAccessory>> dbuses;
713     for (int i = 0; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
714         dbuses[i] = std::unique_ptr<DbusAccessory>(new DbusAccessory(i));
715     }
716
717     for (auto& dbus : dbuses) {
718         dbus.second->setName(testapi::BUS_NAME);
719
720         const int id = dbus.first;
721         auto handler = [id](const std::string& argument, MethodResultBuilder::Pointer result) {
722             if (argument.empty()) {
723                 result->setError("org.tizen.containers.Error.Test", "Test error");
724             } else {
725                 std::string ret = "reply from " + std::to_string(id) + ": " + argument;
726                 result->set(g_variant_new("(s)", ret.c_str()));
727             }
728         };
729         dbus.second->registerTestApiObject(handler);
730     }
731
732     // host -> container2
733     BOOST_CHECK_EQUAL("reply from 2: param1",
734                       dbuses.at(0)->testApiProxyCall("ut-containers-manager-console2-dbus",
735                                                      "param1"));
736
737     // host -> host
738     BOOST_CHECK_EQUAL("reply from 0: param2",
739                       dbuses.at(0)->testApiProxyCall("host",
740                                                      "param2"));
741
742     // container1 -> host
743     BOOST_CHECK_EQUAL("reply from 0: param3",
744                       dbuses.at(1)->testApiProxyCall("host",
745                                                      "param3"));
746
747     // container1 -> container2
748     BOOST_CHECK_EQUAL("reply from 2: param4",
749                       dbuses.at(1)->testApiProxyCall("ut-containers-manager-console2-dbus",
750                                                      "param4"));
751
752     // container2 -> container2
753     BOOST_CHECK_EQUAL("reply from 2: param5",
754                       dbuses.at(2)->testApiProxyCall("ut-containers-manager-console2-dbus",
755                                                      "param5"));
756
757     // host -> unknown
758     BOOST_CHECK_EXCEPTION(dbuses.at(0)->testApiProxyCall("unknown", "param"),
759                           DbusCustomException,
760                           expectedMessage("Unknown proxy call target"));
761
762     // forwarding error
763     BOOST_CHECK_EXCEPTION(dbuses.at(0)->testApiProxyCall("host", ""),
764                           DbusCustomException,
765                           expectedMessage("Test error"));
766
767     // forbidden call
768     BOOST_CHECK_EXCEPTION(dbuses.at(0)->proxyCall("host",
769                                               "org.fake",
770                                               "/a/b",
771                                               "c.d",
772                                               "foo",
773                                               g_variant_new("(s)", "arg")),
774                           DbusCustomException,
775                           expectedMessage("Proxy call forbidden"));
776 }
777
778 namespace {
779     const DbusAccessory::Dbuses EXPECTED_DBUSES_NO_DBUS = {
780         {"ut-containers-manager-console1", ""},
781         {"ut-containers-manager-console2", ""},
782         {"ut-containers-manager-console3", ""}};
783
784     const DbusAccessory::Dbuses EXPECTED_DBUSES_STOPPED = {
785         {"ut-containers-manager-console1-dbus", ""},
786         {"ut-containers-manager-console2-dbus", ""},
787         {"ut-containers-manager-console3-dbus", ""}};
788
789     const DbusAccessory::Dbuses EXPECTED_DBUSES_STARTED = {
790         {"ut-containers-manager-console1-dbus",
791          "unix:path=/tmp/ut-containers-manager/console1-dbus/dbus/system_bus_socket"},
792         {"ut-containers-manager-console2-dbus",
793          "unix:path=/tmp/ut-containers-manager/console2-dbus/dbus/system_bus_socket"},
794         {"ut-containers-manager-console3-dbus",
795          "unix:path=/tmp/ut-containers-manager/console3-dbus/dbus/system_bus_socket"}};
796 } // namespace
797
798 BOOST_AUTO_TEST_CASE(GetContainerDbusesTest)
799 {
800     DbusAccessory host(DbusAccessory::HOST_ID);
801     ContainersManager cm(TEST_DBUS_CONFIG_PATH);
802
803     BOOST_CHECK(EXPECTED_DBUSES_STOPPED == host.callMethodGetContainerDbuses());
804     cm.startAll();
805     BOOST_CHECK(EXPECTED_DBUSES_STARTED == host.callMethodGetContainerDbuses());
806     cm.stopAll();
807     BOOST_CHECK(EXPECTED_DBUSES_STOPPED == host.callMethodGetContainerDbuses());
808 }
809
810 BOOST_AUTO_TEST_CASE(GetContainerDbusesNoDbusTest)
811 {
812     DbusAccessory host(DbusAccessory::HOST_ID);
813     ContainersManager cm(TEST_CONFIG_PATH);
814     BOOST_CHECK(EXPECTED_DBUSES_NO_DBUS == host.callMethodGetContainerDbuses());
815     cm.startAll();
816     BOOST_CHECK(EXPECTED_DBUSES_NO_DBUS == host.callMethodGetContainerDbuses());
817     cm.stopAll();
818     BOOST_CHECK(EXPECTED_DBUSES_NO_DBUS == host.callMethodGetContainerDbuses());
819 }
820
821 BOOST_AUTO_TEST_CASE(ContainerDbusesSignalsTest)
822 {
823     Latch signalLatch;
824     DbusAccessory::Dbuses collectedDbuses;
825
826     DbusAccessory host(DbusAccessory::HOST_ID);
827
828     auto onSignal = [&] (const std::string& /*senderBusName*/,
829                          const std::string& objectPath,
830                          const std::string& interface,
831                          const std::string& signalName,
832                          GVariant* parameters) {
833         if (objectPath == api::host::OBJECT_PATH &&
834             interface == api::host::INTERFACE &&
835             signalName == api::host::SIGNAL_CONTAINER_DBUS_STATE) {
836
837             const gchar* containerId = NULL;
838             const gchar* dbusAddress = NULL;
839             g_variant_get(parameters, "(&s&s)", &containerId, &dbusAddress);
840
841             collectedDbuses.insert(DbusAccessory::Dbuses::value_type(containerId, dbusAddress));
842             signalLatch.set();
843         }
844     };
845
846     host.signalSubscribe(onSignal);
847
848     {
849         ContainersManager cm(TEST_DBUS_CONFIG_PATH);
850
851         BOOST_CHECK(signalLatch.empty());
852         BOOST_CHECK(collectedDbuses.empty());
853
854         cm.startAll();
855
856         BOOST_CHECK(signalLatch.waitForN(TEST_DBUS_CONNECTION_CONTAINERS_COUNT, EVENT_TIMEOUT));
857         BOOST_CHECK(signalLatch.empty());
858         BOOST_CHECK(EXPECTED_DBUSES_STARTED == collectedDbuses);
859         collectedDbuses.clear();
860     }
861
862     BOOST_CHECK(signalLatch.waitForN(TEST_DBUS_CONNECTION_CONTAINERS_COUNT, EVENT_TIMEOUT));
863     BOOST_CHECK(signalLatch.empty());
864     BOOST_CHECK(EXPECTED_DBUSES_STOPPED == collectedDbuses);
865 }
866
867
868 BOOST_AUTO_TEST_CASE(GetContainerIdsTest)
869 {
870     ContainersManager cm(TEST_DBUS_CONFIG_PATH);
871
872     DbusAccessory dbus(DbusAccessory::HOST_ID);
873
874     std::vector<std::string> containerIds = {"ut-containers-manager-console1-dbus",
875                                              "ut-containers-manager-console2-dbus",
876                                              "ut-containers-manager-console3-dbus"};
877     std::vector<std::string> returnedIds = dbus.callMethodGetContainerIds();
878
879     BOOST_CHECK(std::is_permutation(returnedIds.begin(),
880                                     returnedIds.end(),
881                                     containerIds.begin()));
882 }
883
884 BOOST_AUTO_TEST_CASE(GetActiveContainerIdTest)
885 {
886     ContainersManager cm(TEST_DBUS_CONFIG_PATH);
887     cm.startAll();
888
889     DbusAccessory dbus(DbusAccessory::HOST_ID);
890
891     std::vector<std::string> containerIds = {"ut-containers-manager-console1-dbus",
892                                              "ut-containers-manager-console2-dbus",
893                                              "ut-containers-manager-console3-dbus"};
894
895     for (std::string& containerId: containerIds){
896         cm.focus(containerId);
897         BOOST_CHECK(dbus.callMethodGetActiveContainerId() == containerId);
898     }
899
900     cm.stopAll();
901     BOOST_CHECK(dbus.callMethodGetActiveContainerId() == "");
902 }
903
904 BOOST_AUTO_TEST_CASE(SetActiveContainerTest)
905 {
906     ContainersManager cm(TEST_DBUS_CONFIG_PATH);
907     cm.startAll();
908
909     DbusAccessory dbus(DbusAccessory::HOST_ID);
910
911     std::vector<std::string> containerIds = {"ut-containers-manager-console1-dbus",
912                                              "ut-containers-manager-console2-dbus",
913                                              "ut-containers-manager-console3-dbus"};
914
915     for (std::string& containerId: containerIds){
916         BOOST_REQUIRE_NO_THROW(dbus.callMethodSetActiveContainer(containerId));
917         BOOST_CHECK(dbus.callMethodGetActiveContainerId() == containerId);
918     }
919
920     BOOST_REQUIRE_THROW(dbus.callMethodSetActiveContainer(NON_EXISTANT_CONTAINER_ID),
921                         DbusException);
922
923     cm.stopAll();
924     BOOST_REQUIRE_THROW(dbus.callMethodSetActiveContainer("ut-containers-manager-console1-dbus"),
925                         DbusException);
926 }
927
928 BOOST_AUTO_TEST_CASE(AddContainerTest)
929 {
930     const std::string newContainerId = "test1234";
931     const std::vector<std::string> newContainerConfigs = {
932         TEST_CONTAINER_CONF_PATH + newContainerId + ".conf",
933         TEST_CONTAINER_LIBVIRT_CONF_PATH + newContainerId + ".xml",
934         TEST_CONTAINER_LIBVIRT_CONF_PATH + newContainerId + "-network.xml",
935         TEST_CONTAINER_LIBVIRT_CONF_PATH + newContainerId + "-nwfilter.xml",
936     };
937     FileCleanerRAII cleaner(newContainerConfigs);
938
939     ContainersManager cm(TEST_DBUS_CONFIG_PATH);
940     cm.startAll();
941
942     Latch callDone;
943     auto resultCallback = [&]() {
944         callDone.set();
945     };
946
947     DbusAccessory dbus(DbusAccessory::HOST_ID);
948
949     // create new container
950     dbus.callAsyncMethodAddContainer(newContainerId, resultCallback);
951     callDone.wait(EVENT_TIMEOUT);
952
953     // focus new container
954     BOOST_REQUIRE_NO_THROW(cm.focus(newContainerId));
955     BOOST_CHECK(cm.getRunningForegroundContainerId() == newContainerId);
956 }
957
958 BOOST_AUTO_TEST_SUITE_END()