Merge branch 'master' of git.irl.cs.ucla.edu:ndn/sync
diff --git a/doc/doxygen.conf b/doc/doxygen.conf
index 0989ca5..5408411 100644
--- a/doc/doxygen.conf
+++ b/doc/doxygen.conf
@@ -568,7 +568,7 @@
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = model helper
+INPUT                  = doc model helper
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
diff --git a/doc/doxygen_boost_dummy.h b/doc/doxygen_boost_dummy.h
new file mode 100644
index 0000000..6a409cc
--- /dev/null
+++ b/doc/doxygen_boost_dummy.h
@@ -0,0 +1 @@
+namespace boost { template<class T> class shared_ptr { T *dummy; }; }
diff --git a/model/sync-digest.cc b/model/sync-digest.cc
index 5ff9418..621b52b 100644
--- a/model/sync-digest.cc
+++ b/model/sync-digest.cc
@@ -120,6 +120,12 @@
   EVP_MD_CTX_destroy (m_context);
 }
 
+bool
+Digest::empty () const
+{
+  return m_buffer == 0;
+}
+
 void
 Digest::reset ()
 {
@@ -154,7 +160,8 @@
   if (m_buffer == 0)
     finalize ();
 
-  BOOST_ASSERT (sizeof (std::size_t) <= m_hashLength);
+  if (sizeof (std::size_t) > m_hashLength)
+    throw DigestCalculationError () << errinfo_at_line (__LINE__);
   
   // just getting first sizeof(std::size_t) bytes
   // not ideal, but should work pretty well
@@ -162,13 +169,10 @@
 }
 
 bool
-Digest::operator == (Digest &digest)
+Digest::operator == (const Digest &digest) const
 {
-  if (m_buffer == 0)
-    finalize ();
-
-  if (digest.m_buffer == 0)
-    digest.finalize ();
+  if (m_buffer == 0 || digest.m_buffer == 0)
+    throw DigestCalculationError () << errinfo_at_line (__LINE__);
   
   BOOST_ASSERT (m_hashLength == digest.m_hashLength);
 
@@ -221,6 +225,10 @@
 {
   string str;
   is >> str; // read string first
+
+  if (str.size () == 0)
+    throw DigestCalculationError () << errinfo_at_line (__LINE__);
+  
   // uint8_t padding = (3 - str.size () % 3) % 3;
   // for (uint8_t i = 0; i < padding; i++) str.push_back ('=');
 
diff --git a/model/sync-digest.h b/model/sync-digest.h
index f5c83f6..52072d2 100644
--- a/model/sync-digest.h
+++ b/model/sync-digest.h
@@ -42,6 +42,12 @@
   Digest ();
 
   /**
+   * @brief Check if digest is empty
+   */
+  bool
+  empty () const;
+  
+  /**
    * @brief Reset digest to the initial state
    */
   void
@@ -55,18 +61,29 @@
   /**
    * @brief Obtain a short version of the hash (just first sizeof(size_t) bytes
    *
-   * Side effect: Finalize will be called on `this'
+   * Side effect: finalize() will be called on `this'
    */
   std::size_t
   getHash ();
 
   /**
+   * @brief Finalize digest. All subsequent calls to "operator <<" will fire an exception
+   */
+  void
+  finalize ();
+
+  /**
    * @brief Compare two full digests
    *
    * Side effect: Finalize will be called on `this' and `digest'
    */
   bool
-  operator == (Digest &digest);
+  operator == (const Digest &digest) const;
+
+  bool
+  operator != (const Digest &digest) const
+  { return ! (*this == digest); }
+  
 
   /**
    * @brief Add existing digest to digest calculation
@@ -84,31 +101,18 @@
   inline Digest &
   operator << (const std::string &str);
 
+  /**
+   * @brief Add uint32_t value to digest calculation
+   * @param value uint32_t value to put into digest
+   */
   inline Digest &
   operator << (uint32_t value);
 
-  // /**
-  //  * @brief Add integer to digest calculation
-  //  * @param value the value to add to the digest
-  //  */
-  // template<class INT>
-  // inline Digest &
-  // operator << (INT value);
-
 private:
-  /**
-   * @brief Disabled copy operator 
-   */
   Digest &
   operator = (Digest &digest) { return *this; }
   
   /**
-   * @brief Finalize digest. All subsequent calls to "operator <<" will fire an exception
-   */
-  void
-  finalize ();
-
-  /**
    * @brief Add size bytes of buffer to the hash
    */
   void
diff --git a/model/sync-full-leaf.cc b/model/sync-full-leaf.cc
index f575056..ee1d3dc 100644
--- a/model/sync-full-leaf.cc
+++ b/model/sync-full-leaf.cc
@@ -37,7 +37,8 @@
 FullLeaf::updateDigest ()
 {
   m_digest.reset ();
-  m_digest << getInfo ().getDigest () << getSeq ().getDigest ();
+  m_digest << getInfo ().getDigest () << *getSeq ().getDigest ();
+  m_digest.finalize ();
 }
 
 // from Leaf
diff --git a/model/sync-full-state.cc b/model/sync-full-state.cc
index dcc4399..7e4a30f 100644
--- a/model/sync-full-state.cc
+++ b/model/sync-full-state.cc
@@ -91,7 +91,7 @@
   LeafContainer::iterator item = m_leaves.find (*info);
   if (item == m_leaves.end ())
     {
-      m_leaves.insert (make_shared<Leaf> (info, cref (seq)));
+      m_leaves.insert (make_shared<FullLeaf> (info, cref (seq)));
     }
   else
     {
diff --git a/model/sync-leaf.cc b/model/sync-leaf.cc
index 6ff48bc..6480480 100644
--- a/model/sync-leaf.cc
+++ b/model/sync-leaf.cc
@@ -30,6 +30,10 @@
 {
 }
 
+Leaf::~Leaf ()
+{
+}
+
 void
 Leaf::setSeq (const SeqNo &seq)
 {
diff --git a/model/sync-leaf.h b/model/sync-leaf.h
index c4da60c..037e4a5 100644
--- a/model/sync-leaf.h
+++ b/model/sync-leaf.h
@@ -41,7 +41,7 @@
    * @param seq  Initial sequence number of the pointer
    */
   Leaf (NameInfoConstPtr info, const SeqNo &seq);
-  virtual ~Leaf () { }
+  virtual ~Leaf ();
   
   /**
    * @brief Get name of the leaf
diff --git a/model/sync-seq-no.cc b/model/sync-seq-no.cc
new file mode 100644
index 0000000..31db4ae
--- /dev/null
+++ b/model/sync-seq-no.cc
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ *         卞超轶 Chaoyi Bian <bcy@pku.edu.cn>
+ *	   Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#include "sync-seq-no.h"
+#include <boost/make_shared.hpp>
+
+using namespace boost;
+
+namespace Sync {
+
+DigestConstPtr
+SeqNo::getDigest () const
+{
+  DigestPtr digest = make_shared<Digest> ();
+  *digest << m_session << m_seq;
+  digest->finalize ();
+  return digest;
+}
+
+} // Sync
diff --git a/model/sync-seq-no.h b/model/sync-seq-no.h
index fb9e6b8..354a1b6 100644
--- a/model/sync-seq-no.h
+++ b/model/sync-seq-no.h
@@ -29,14 +29,35 @@
 namespace Sync {
 
 /**
+ * @ingroup sync
  * @brief Sequence number abstraction
- *
- * 
  */
 class SeqNo
 {
 public:
   /**
+   * @brief Copy constructor
+   * @param seq sequence number object to copy from
+   */
+  SeqNo (const SeqNo &seq)
+  {
+    *this = seq;
+  }
+
+  /**
+   * @brief Assignment operator
+   * @param seq sequence number object to copy from
+   */
+  SeqNo &
+  operator = (const SeqNo &seq)
+  {
+    m_session = seq.m_session;
+    m_seq = seq.m_seq;
+
+    return *this;
+  }
+
+  /**
    * @brief Constructor with just sequence number. Session assumed to be zero
    * @param seq Sequence number
    */
@@ -55,7 +76,12 @@
     , m_seq (seq)
   { }
 
-  inline Digest
+  /**
+   * @brief Get sequence number digest
+   *
+   * Digest will be calculated every time it is requested
+   */
+  DigestConstPtr
   getDigest () const;
 
   /**
@@ -82,18 +108,17 @@
     return m_session == seq.m_session && m_seq == seq.m_seq;
   }
 
-  SeqNo &
-  operator = (const SeqNo &seq)
-  {
-    m_session = seq.m_session;
-    m_seq = seq.m_seq;
+  /**
+   * @brief Get session id
+   */
+  uint32_t getSession () const
+  { return m_session; }
 
-    return *this;
-  }
-  
-private:
-  inline void
-  updateDigest ();
+  /**
+   * @brief Get sequence number
+   */
+  uint32_t getSeq () const
+  { return m_seq; }
   
 private:
   /**
@@ -112,26 +137,8 @@
    * For now, wrapping sequence number after max to zero is not supported
    */
   uint32_t m_seq;
-
-  Digest m_digest;
 };
 
-
-void
-SeqNo::updateDigest ()
-{
-  m_digest.reset ();
-  m_digest << m_session << m_seq;
-}
-  
-Digest
-SeqNo::getDigest () const
-{
-  Digest digest;
-  return digest;
-}
-
-
 } // Sync
 
 #endif // SYNC_SEQ_NO_H
diff --git a/test/test.cc b/test/test.cc
new file mode 100644
index 0000000..55745d9
--- /dev/null
+++ b/test/test.cc
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ *         卞超轶 Chaoyi Bian <bcy@pku.edu.cn>
+ *	   Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#define BOOST_TEST_MODULE Digest
+#include <boost/test/unit_test.hpp>
+
diff --git a/test/test_digest.cc b/test/test_digest.cc
new file mode 100644
index 0000000..a1ae6a3
--- /dev/null
+++ b/test/test_digest.cc
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ *         卞超轶 Chaoyi Bian <bcy@pku.edu.cn>
+ *	   Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/output_test_stream.hpp> 
+using boost::test_tools::output_test_stream;
+
+#include "../model/sync-digest.h"
+#include <iostream>
+#include <sstream>
+
+using namespace Sync;
+using namespace std;
+using namespace boost;
+
+BOOST_AUTO_TEST_SUITE(DigestTestSuite)
+
+BOOST_AUTO_TEST_CASE (BasicTest)
+{
+  Digest d0;
+  BOOST_REQUIRE (d0.empty ());
+}
+
+BOOST_AUTO_TEST_CASE (DigestGenerationTest)
+{
+  Digest d1;
+  BOOST_CHECK_NO_THROW (d1 << "1\n");
+
+  // without explicit finalizing, Digest will not be complete and printing out will cause assert
+  BOOST_CHECK (d1.empty ());
+
+  // fix hash
+  BOOST_CHECK_NO_THROW (d1.getHash ());
+  BOOST_CHECK (!d1.empty ());
+  BOOST_CHECK (d1 == d1);
+
+  BOOST_CHECK_THROW (d1 << "2", DigestCalculationError);
+  
+  output_test_stream output;
+  BOOST_CHECK_NO_THROW (output << d1);
+  BOOST_CHECK (output.check_length (40,false) );
+  BOOST_CHECK (output.is_equal ("e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e", true));
+}
+
+BOOST_AUTO_TEST_CASE (DigestComparison)
+{
+  Digest d1;
+  BOOST_CHECK_NO_THROW (d1 << "1\n");
+  BOOST_CHECK_THROW (d1 == d1, DigestCalculationError);
+  BOOST_CHECK_NO_THROW (d1.getHash ());
+  BOOST_CHECK (d1 == d1);
+  
+  Digest d2;
+  BOOST_CHECK_NO_THROW (d2 << "2\n");
+  BOOST_CHECK_NO_THROW (d2.getHash ());
+  BOOST_CHECK (d1 != d2);
+  
+  Digest d3;
+  istringstream is (string ("e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e")); //real sha-1 for "1\n"
+  BOOST_CHECK_NO_THROW (is >> d3);
+  BOOST_CHECK (!d3.empty ());
+  BOOST_CHECK (d3 == d1);
+  BOOST_CHECK (d3 != d2);
+
+  istringstream is2 (string ("25fa44f2b31c1fb553b6021e7360d07d5d91ff5e")); // some fake hash
+  BOOST_CHECK_THROW (is2 >> d3, DigestCalculationError); // >> can be used only once
+
+  Digest d4;
+  BOOST_CHECK_THROW (is2 >> d4, DigestCalculationError); // is2 is now empty. empty >> is not allowed
+
+  istringstream is3 (string ("25fa44f2b31c1fb553b6021e7360d07d5d91ff5e")); // some fake hash
+  BOOST_CHECK_NO_THROW (is3 >> d4);
+  
+  BOOST_CHECK (d4 != d1);
+  BOOST_CHECK (d4 != d2);
+  BOOST_CHECK (d4 != d3);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/test/test_leaf.cc b/test/test_leaf.cc
new file mode 100644
index 0000000..d2c8289
--- /dev/null
+++ b/test/test_leaf.cc
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ *         卞超轶 Chaoyi Bian <bcy@pku.edu.cn>
+ *	   Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/output_test_stream.hpp> 
+using boost::test_tools::output_test_stream;
+
+#include <boost/make_shared.hpp>
+
+#include "../model/sync-full-leaf.h"
+#include "../model/sync-diff-leaf.h"
+#include "../model/sync-std-name-info.h"
+
+using namespace Sync;
+using namespace std;
+using namespace boost;
+
+BOOST_AUTO_TEST_SUITE(LeafTestSuite)
+
+BOOST_AUTO_TEST_CASE (LeafBase)
+{
+  NameInfoConstPtr name = StdNameInfo::FindOrCreate ("/test/name");
+  BOOST_CHECK (name != 0);
+
+  // find the same name
+  BOOST_CHECK (name.get () == StdNameInfo::FindOrCreate ("/test/name").get ());
+  BOOST_CHECK_EQUAL (name.use_count (), 2);
+
+  BOOST_CHECK_NO_THROW (DiffLeaf x (name, SeqNo (12)));
+  BOOST_CHECK_EQUAL (name.use_count (), 2);
+  
+  BOOST_CHECK_NO_THROW (DiffLeaf x (name));
+  BOOST_CHECK_EQUAL (name.use_count (), 2);
+
+  DiffLeaf updateLeaf (name, SeqNo (12));
+  BOOST_CHECK_EQUAL (name.use_count (), 3);
+
+  DiffLeaf removeLeaf (name);
+  BOOST_CHECK_EQUAL (name.use_count (), 4);
+
+  BOOST_CHECK_EQUAL (updateLeaf.getOperation (), UPDATE);
+  BOOST_CHECK_EQUAL (updateLeaf.getSeq ().getSession (), 0);
+  BOOST_CHECK_EQUAL (updateLeaf.getSeq ().getSeq (), 12);
+  
+  BOOST_CHECK_EQUAL (removeLeaf.getOperation (), REMOVE);
+  BOOST_CHECK_EQUAL (removeLeaf.getSeq ().getSession (), 0);
+  BOOST_CHECK_EQUAL (removeLeaf.getSeq ().getSeq (), 0);
+  
+  BOOST_REQUIRE_NO_THROW (FullLeaf x (name, SeqNo (12)));
+  FullLeaf fullLeaf (name, SeqNo (12));
+  BOOST_CHECK_EQUAL (name.use_count (), 5);
+}
+
+BOOST_AUTO_TEST_CASE (LeafDigest)
+{
+  BOOST_CHECK_EQUAL (StdNameInfo::FindOrCreate ("/test/name").use_count (), 2);
+  NameInfoConstPtr name = StdNameInfo::FindOrCreate ("/test/name");
+  FullLeaf fullLeaf (name, SeqNo (12));
+
+  // fullLeafDigest = hash ( hash(name), hash (session, seqNo) )
+  
+  // Digest manualDigest;
+
+  // Digest manualNameDigest;
+  // manualNameDigest << "/test/name";
+  // manualNameDigest.finalize ();
+
+  // Digest manualSeqNoDigest;
+  // manualSeqNoDigest << 0 << 12;
+  // manualSeqNoDigest.finalize ();
+
+  // manualDigest << manualNameDigest << manualSeqNoDigest;
+  // manualDigest.finalize ();
+
+  output_test_stream output;
+  output << fullLeaf.getDigest ();
+  BOOST_CHECK (output.is_equal ("991f8cf6262dfe0f519c63f6e9b92fe69e741a9b", true));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/testapp.cc b/test/testapp.cc
deleted file mode 100644
index 51921d5..0000000
--- a/test/testapp.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012 University of California, Los Angeles
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
- *         卞超轶 Chaoyi Bian <bcy@pku.edu.cn>
- *	   Alexander Afanasyev <alexander.afanasyev@ucla.edu>
- */
-
-#include "../model/sync-digest.h"
-#include <boost/lexical_cast.hpp>
-#include <iostream>
-#include <sstream>
-
-using namespace Sync;
-using namespace std;
-using namespace boost;
-
-int
-main (int argc, char **argv)
-{
-  Digest test;
-  test << "1\n";
-
-  // try
-  //   {
-  //     cout << "Trying to print without explicit getHash() call: ";
-  //     cout << test;
-  //     cout << "Failed (should be asserted)\n";
-  //   }
-  // catch (...)
-  //   {
-  //     cout << "OK (exception)\n";
-  //   }
-  
-  // without explicit finalizing, Digest will not be complete and printing out will cause assert
-  test.getHash ();
-
-  try
-    {
-      cout << "Hash for '1' is " << test << std::endl;
-    }
-  catch (...)
-    {
-      cout << "Hash calculation failed\n";
-    }
-
-  try
-    {
-      cout << "Trying to add data to hash after finalizing: ";
-      test << "2"; // should cause an assert
-      cout << "Failed (should be asserted)\n";
-    }
-  catch (...)
-    {
-      cout << "OK (exception)\n";
-    }
-
-  Digest test2;
-// #ifdef DIGEST_BASE64
-//   string testString = "sCYyTGkEsqnLS4jW1hyB0Q==";
-// #else
-  string testString = lexical_cast<string> (test); //"b026324c6904b2a9cb4b88d6d61c81d1";
-// #endif
-  cout << "Hash from string: " << testString << "\n";
-  istringstream is (testString);
-  is >> test2;
-
-  cout << "Result from hash: " << test2 << "\n";
-
-  cout << "Compare two hashes: " << (test == test2) << "\n";
-
-  Digest test3;
-  if (testString[0] != '1')
-    testString[0] = '1';
-  else
-    testString[0] = '2';
-  
-  istringstream is2(testString);
-  is2 >> test3;
-
-  cout << "Hash from string: " << test3 << "\n";
-  cout << "Compare two hashes: " << (test == test3) << "\n";
-  
-  return 0; 
-}
diff --git a/wscript b/wscript
index 1fc4e84..3ace7da 100644
--- a/wscript
+++ b/wscript
@@ -13,11 +13,12 @@
     conf.load("compiler_cxx")
     conf.check_cfg(atleast_pkgconfig_version='0.20')
     conf.check_cfg(package='openssl', args=['--cflags', '--libs'], uselib_store='SSL')
+    conf.check_cfg(package='libxml-2.0', args=['--cflags', '--libs'], uselib_store='XML')
     conf.define ('STANDALONE', 1)
     # conf.define ('DIGEST_BASE64', 1) # base64 is not working and probably will not work at all
 
     conf.load('boost')
-    conf.check_boost(lib='system iostreams')
+    conf.check_boost(lib='system iostreams test')
     
     conf.load('doxygen')
 
@@ -26,14 +27,17 @@
                features=['cxx', 'cxxshlib'],
                source = bld.path.ant_glob(['model/sync-*.cc',
                                            'helper/sync-*.cc']),
-               uselib = 'BOOST BOOST_IOSTREAMS SSL'
+               uselib = 'BOOST BOOST_IOSTREAMS SSL XML'
                )
 
-    bld.program (target="testapp",
-                 source = "test/testapp.cc",
+    # Unit tests
+    bld.program (target="unit-tests",
+                 source = bld.path.ant_glob(['test/**/*.cc']),
                  features=['cxx', 'cxxprogram'],
-                 use = 'sync')
+                 use = 'BOOST_TEST sync')
 
+
+# doxygen docs
 from waflib.Build import BuildContext
 class doxy (BuildContext):
     cmd = "doxygen"