tests[ChronoShare]: Create integrated tests for ChronoShare

Change-Id: If3f2a803b41dedb060971d07d9564e9ec5f77f4b
diff --git a/tests/integrated-tests/chronoshare.cpp b/tests/integrated-tests/chronoshare.cpp
new file mode 100644
index 0000000..415f24d
--- /dev/null
+++ b/tests/integrated-tests/chronoshare.cpp
@@ -0,0 +1,281 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+#define BOOST_TEST_MAIN 1
+#define BOOST_TEST_DYN_LINK 1
+#define BOOST_TEST_MODULE ChronoShare Integrated Tests (ChronoShare)
+
+#include "gui/chronosharegui.hpp"
+#include "test-common.hpp"
+#include "dummy-forwarder.hpp"
+
+#include <boost/make_shared.hpp>
+#include <boost/test/unit_test.hpp>
+#include <boost/bind.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/lexical_cast.hpp>
+#include <thread>
+#include <chrono>
+
+namespace ndn {
+namespace chronoshare {
+namespace tests {
+
+using namespace std;
+namespace fs = boost::filesystem;
+
+_LOG_INIT(Test.Integrated.ChronoShare);
+
+class ChronoShareFixture : public IdentityManagementFixture
+{
+public:
+  ChronoShareFixture()
+    : forwarder(m_io, m_keyChain)
+    , folder("sharefolder")
+    , dir1(fs::path(fs::path(UNIT_TEST_CONFIG_PATH) / "sharefolder1"))
+    , dir2(fs::path(fs::path(UNIT_TEST_CONFIG_PATH) / "sharefolder2"))
+    , argc(0)
+  {
+
+    if (fs::exists(dir1)) {
+      fs::remove_all(dir1);
+    }
+
+    if (fs::exists(dir2)) {
+      fs::remove_all(dir2);
+    }
+
+    fs::create_directory(dir1);
+    fs::create_directory(dir2);
+  }
+
+  ~ChronoShareFixture()
+  {
+    //delete app;
+    // cleanup
+    if (fs::exists(dir1)) {
+      fs::remove_all(dir1);
+    }
+
+    // cleanup
+    if (fs::exists(dir2)) {
+      fs::remove_all(dir2);
+    }
+  }
+
+  void
+  advanceClocks(std::chrono::seconds delay)
+  {
+    std::chrono::milliseconds step = delay;
+    step /= 50;
+    for (int i = 0; i < 50; ++i) {
+      std::this_thread::sleep_for(step);
+      m_io.poll();
+      m_io.reset();
+    }
+  }
+
+  void
+  create_file(const fs::path& ph, const std::string& contents)
+  {
+    std::ofstream f(ph.string().c_str());
+    if (!f) {
+      abort();
+    }
+    if (!contents.empty()) {
+      f << contents;
+    }
+  }
+
+  void
+  run()
+  {
+      std::this_thread::sleep_for(std::chrono::seconds(5));
+
+      _LOG_DEBUG("======created files===========");
+      create_file(dir1 / folder / "test.txt", "hello");
+
+      std::this_thread::sleep_for(std::chrono::seconds(10));
+
+
+      BOOST_REQUIRE_MESSAGE(fs::exists(dir2 / folder / "test.txt"), "user1 failed to notify user2 about test.txt");
+      BOOST_CHECK_EQUAL(fs::file_size(dir1 / folder / "test.txt"), fs::file_size(dir2 / folder / "test.txt"));
+
+
+      fs::path subdir1 = dir1 / folder / "sub";
+      fs::path subdir2 = dir2 / folder / "sub";
+      fs::create_directory(subdir1);
+      for (int i = 0; i < 10; i++) {
+        string filename = boost::lexical_cast<string>(i);
+        create_file(subdir1 / filename.c_str(), boost::lexical_cast<string>(i));
+      }
+
+      std::this_thread::sleep_for(std::chrono::seconds(10));
+      for (int i = 0; i < 10; i++) {
+        string filename = boost::lexical_cast<string>(i);
+        BOOST_REQUIRE_MESSAGE(fs::exists(subdir2 / filename), "user1 failed to notify user2 about"<< filename);
+      }
+
+      //=========check copy file to sub directory==========
+      fs::create_directory(dir1 / folder / "sub1");
+      fs::path subsubdir1 = dir1 / folder / "sub1" / "sub2";
+      fs::path subsubdir2 = dir2 / folder / "sub1" / "sub2";
+      fs::copy_directory(subdir1, subsubdir1);
+      for (int i = 0; i < 5; i++) {
+        string filename = boost::lexical_cast<string>(i);
+        fs::copy(subdir1 / filename.c_str(), subsubdir1 / filename.c_str());
+      }
+
+      std::this_thread::sleep_for(std::chrono::seconds(10));
+      // test.txt
+      // sub/0..9
+      // sub1/sub2/0..4
+      for (int i = 0; i < 5; i++) {
+        string filename = boost::lexical_cast<string>(i);
+        BOOST_REQUIRE_MESSAGE(fs::exists(subsubdir2 / filename), "user1 failed to notify user2 about"<< filename);
+      }
+
+      // =============== check remove files =========================
+      for (int i = 0; i < 7; i++) {
+        string filename = boost::lexical_cast<string>(i);
+        fs::remove(subdir1 / filename.c_str());
+      }
+
+      std::this_thread::sleep_for(std::chrono::seconds(10));
+      // test.txt
+      // sub/7..9
+      // sub1/sub2/0..4
+      for (int i = 0; i < 10; i++) {
+        string filename = boost::lexical_cast<string>(i);
+        if (i < 7)
+          BOOST_REQUIRE_MESSAGE(!fs::exists(subdir2 / filename), "user1 failed to notify user2 about"<< filename);
+        else
+          BOOST_REQUIRE_MESSAGE(fs::exists(subdir2 / filename), "user1 failed to notify user2 about"<< filename);
+      }
+
+      // =================== check remove files again, remove the whole dir this time
+      // ===================
+      // before remove check
+      for (int i = 0; i < 5; i++) {
+        string filename = boost::lexical_cast<string>(i);
+        BOOST_REQUIRE_MESSAGE(fs::exists(subsubdir2 / filename), "user1 failed to notify user2 about"<< filename);
+      }
+      fs::remove_all(subsubdir1);
+
+      std::this_thread::sleep_for(std::chrono::seconds(10));
+      // test.txt
+      // sub/7..9
+      BOOST_REQUIRE_MESSAGE(!fs::exists(subsubdir2), "user1 failed to notify user2 about sub1/sub2");
+
+      // =================== check rename files =======================
+      for (int i = 7; i < 10; i++) {
+        string filename = boost::lexical_cast<string>(i);
+        fs::rename(subdir1 / filename.c_str(), dir1 / folder / filename.c_str());
+      }
+      std::this_thread::sleep_for(std::chrono::seconds(10));
+      // test.txt
+      // 7
+      // 8
+      // 9
+      // sub
+      for (int i = 7; i < 10; i++) {
+        string filename = boost::lexical_cast<string>(i);
+
+        BOOST_REQUIRE_MESSAGE(!fs::exists(subdir2 / filename), "user1 failed to notify user2 about"<< filename);
+        BOOST_REQUIRE_MESSAGE(fs::exists(dir2 / folder / filename), "user1 failed to notify user2 about"<< filename);
+      }
+
+      create_file(dir1 / folder / "add-removal-check.txt", "add-removal-check");
+      std::this_thread::sleep_for(std::chrono::seconds(4));
+      BOOST_REQUIRE_MESSAGE(fs::exists(dir2 / folder / "add-removal-check.txt"),
+                            "user1 failed to notify user2 about add-removal-check.txt");
+
+      fs::remove(dir1 / folder / "add-removal-check.txt");
+      std::this_thread::sleep_for(std::chrono::seconds(4));
+      BOOST_REQUIRE_MESSAGE(!fs::exists(dir2 / folder / "add-removal-check.txt"),
+                            "user1 failed to notify user2 about add-removal-check.txt");
+
+      create_file(dir1 / folder / "add-removal-check.txt", "add-removal-check");
+      std::this_thread::sleep_for(std::chrono::seconds(4));
+      BOOST_REQUIRE_MESSAGE(fs::exists(dir2 / folder / "add-removal-check.txt"),
+                            "user1 failed to notify user2 about add-removal-check.txt");
+
+      fs::remove(dir1 / folder / "add-removal-check.txt");
+      std::this_thread::sleep_for(std::chrono::seconds(4));
+      BOOST_REQUIRE_MESSAGE(!fs::exists(dir2 / folder / "add-removal-check.txt"),
+                            "user1 failed to notify user2 about add-removal-check.txt");
+
+      create_file(dir1 / folder / "add-removal-check.txt", "add-removal-check");
+      std::this_thread::sleep_for(std::chrono::seconds(4));
+      BOOST_REQUIRE_MESSAGE(fs::exists(dir2 / folder / "add-removal-check.txt"),
+                            "user1 failed to notify user2 about add-removal-check.txt");
+
+      fs::remove(dir1 / folder / "add-removal-check.txt");
+      std::this_thread::sleep_for(std::chrono::seconds(4));
+      BOOST_REQUIRE_MESSAGE(!fs::exists(dir2 / folder / "add-removal-check.txt"),
+                            "user1 failed to notify user2 about add-removal-check.txt");
+
+      _LOG_DEBUG("======finish thread===========");
+  }
+
+public:
+  DummyForwarder forwarder;
+  std::string folder;
+  fs::path dir1;
+  fs::path dir2;
+
+  int argc;
+  //TestApp* app;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestChronoShare, ChronoShareFixture)
+
+BOOST_AUTO_TEST_CASE(Chronoshare)
+{
+  QApplication app(argc, nullptr);
+
+  // do not quit when last window closes
+  app.setQuitOnLastWindowClosed(false);
+  // invoke gui
+  ndn::chronoshare::ChronoShareGui gui1(QString::fromStdString(dir1.generic_string()),
+                                        QString::fromStdString("user1"),
+                                        QString::fromStdString("sharefolder"));
+
+  // invoke gui
+  ndn::chronoshare::ChronoShareGui gui2(QString::fromStdString(dir2.generic_string()),
+                                        QString::fromStdString("user2"),
+                                        QString::fromStdString("sharefolder"));
+
+  QTimer::singleShot(95000, &app, SLOT(quit()));
+
+  _LOG_DEBUG("run thread");
+  std::thread workThread(boost::bind(&ChronoShareFixture::run, this));
+
+  app.exec();
+  workThread.join();
+  _LOG_DEBUG("thread finished");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace chronoshare
+} // namespace ndn
diff --git a/tests/integrated-tests/fs-watcher.t.cpp b/tests/integrated-tests/fs-watcher.cpp
similarity index 95%
rename from tests/integrated-tests/fs-watcher.t.cpp
rename to tests/integrated-tests/fs-watcher.cpp
index 05f9764..c29da56 100644
--- a/tests/integrated-tests/fs-watcher.t.cpp
+++ b/tests/integrated-tests/fs-watcher.cpp
@@ -17,6 +17,9 @@
  *
  * See AUTHORS.md for complete list of ChronoShare authors and contributors.
  */
+#define BOOST_TEST_MAIN 1
+#define BOOST_TEST_DYN_LINK 1
+#define BOOST_TEST_MODULE ChronoShare Integrated Tests (FsWatcher)
 
 #include "fs-watcher.hpp"
 #include "test-common.hpp"
@@ -32,8 +35,7 @@
 #include <iostream>
 #include <thread>
 #include <set>
-
-#include "fs-watcher.t.hpp"
+#include <QtWidgets>
 
 using namespace std;
 namespace fs = boost::filesystem;
@@ -44,14 +46,6 @@
 namespace chronoshare {
 namespace tests {
 
-fsWatcherApp::fsWatcherApp(int& argc, char** argv)
-  : QCoreApplication(argc, argv)
-{
-  connect(this, SIGNAL(stopApp()), this, SLOT(quit()), Qt::QueuedConnection);
-}
-
-fsWatcherApp::~fsWatcherApp() = default;
-
 class TestFSWatcherFixture : public IdentityManagementFixture
 {
 public:
@@ -115,19 +109,21 @@
   void
   run()
   {
-    app = new fsWatcherApp(argc, nullptr);
+    QApplication app(argc, nullptr);
     new FsWatcher(m_io, dir.string().c_str(),
                   std::bind(&TestFSWatcherFixture::onChange, this, std::ref(files), _1),
                   std::bind(&TestFSWatcherFixture::onDelete, this, std::ref(files), _1),
-                  app);
-    app->exec();
+                  &app);
+
+    QTimer::singleShot(21000, &app, SLOT(quit()));
+    app.exec();
   }
 
 public:
   fs::path dir;
   set<string> files;
   int argc;
-  fsWatcherApp* app;
+  //fsWatcherApp* app;
 };
 
 BOOST_FIXTURE_TEST_SUITE(TestFsWatcher, TestFSWatcherFixture)
@@ -255,7 +251,7 @@
   this->advanceClocks(std::chrono::seconds(2));
   BOOST_CHECK(files.find("add-removal-check.txt") == files.end());
 
-  emit app->stopApp();
+  //emit app->stopApp();
 
   workThread.join();
 }
diff --git a/tests/integrated-tests/fs-watcher.t.hpp b/tests/integrated-tests/fs-watcher.t.hpp
deleted file mode 100644
index 40c524b..0000000
--- a/tests/integrated-tests/fs-watcher.t.hpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2013-2017, Regents of the University of California.
- *
- * This file is part of ChronoShare, a decentralized file sharing application over NDN.
- *
- * ChronoShare is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * ChronoShare 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 General Public License for more details.
- *
- * You should have received copies of the GNU General Public License along with
- * ChronoShare, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- *
- * See AUTHORS.md for complete list of ChronoShare authors and contributors.
- */
-
-#include <QtWidgets>
-
-namespace ndn {
-namespace chronoshare {
-namespace tests {
-
-class fsWatcherApp : public QCoreApplication
-{
-Q_OBJECT
-
-signals:
-  void
-  stopApp();
-
-public:
-  fsWatcherApp(int& argc, char** argv);
-  ~fsWatcherApp();
-};
-
-} // namespace tests
-} // namespace chronoshare
-} // namespace ndn
diff --git a/tests/integrated-tests/wscript b/tests/integrated-tests/wscript
new file mode 100644
index 0000000..3898a81
--- /dev/null
+++ b/tests/integrated-tests/wscript
@@ -0,0 +1,15 @@
+from waflib import Utils
+
+top = '..'
+
+def build(bld):
+
+    for i in bld.path.ant_glob(['*.cpp']):
+        name = str(i)[str.rfind(str(i),'/'):-len(".cpp")]
+        bld(features='qt5 cxx cxxprogram',
+            target=name,
+            source=[i],
+            use='chronoshare core-objects adhoc http_server chronoshare_gui boost-tests-base QT5CORE QT5WIDGETS',
+            includes='..',
+            defines=['UNIT_TEST_CONFIG_PATH=\"%s/tmp-files/\"' % (bld.bldnode)],
+            install_path=None)