build+ci: enable AddressSanitizer and LeakSanitizer for unit tests

Refs: #2589, #4206

Change-Id: Ie9770134ff87fce438029b97c9ed61dbef3a71a3
diff --git a/.jenkins.d/20-build.sh b/.jenkins.d/20-build.sh
index 464a7ea..6306a52 100755
--- a/.jenkins.d/20-build.sh
+++ b/.jenkins.d/20-build.sh
@@ -6,8 +6,6 @@
 git submodule sync
 git submodule update
 
-COVERAGE=$( python -c "print '--with-coverage' if 'code-coverage' in '$JOB_NAME' else ''" )
-
 # Cleanup
 sudo ./waf -j1 --color=yes distclean
 
@@ -25,13 +23,16 @@
 # Cleanup
 sudo ./waf -j1 --color=yes distclean
 
+if [[ $JOB_NAME == *"code-coverage" ]]; then
+    COVERAGE="--with-coverage"
+elif [[ -n $BUILD_WITH_ASAN || -z $TRAVIS ]]; then
+    ASAN="--with-sanitizer=address"
+fi
+
 # Configure/build in optimized mode with tests
-./waf -j1 --color=yes configure --with-tests $COVERAGE
+./waf -j1 --color=yes configure --with-tests $COVERAGE $ASAN
 ./waf -j1 --color=yes build
 
 # (tests will be run against optimized version)
 
-./waf configure --color=yes --with-tests $COVERAGE
-
-./waf -j1 --color=yes
 sudo ./waf install --color=yes
diff --git a/.jenkins.d/30-tests.sh b/.jenkins.d/30-tests.sh
index 633dfa7..6be5a4a 100755
--- a/.jenkins.d/30-tests.sh
+++ b/.jenkins.d/30-tests.sh
@@ -4,4 +4,14 @@
 
 rm -Rf ~/.ndn
 
+ASAN_OPTIONS="color=always"
+ASAN_OPTIONS+=":detect_stack_use_after_return=true"
+ASAN_OPTIONS+=":check_initialization_order=true"
+ASAN_OPTIONS+=":strict_init_order=true"
+ASAN_OPTIONS+=":detect_invalid_pointer_pairs=1"
+ASAN_OPTIONS+=":detect_container_overflow=false"
+ASAN_OPTIONS+=":strict_string_checks=true"
+ASAN_OPTIONS+=":strip_path_prefix=${PWD}/"
+export ASAN_OPTIONS
+
 ./build/unit-tests-nlsr -l test_suite
diff --git a/.waf-tools/default-compiler-flags.py b/.waf-tools/default-compiler-flags.py
index 562d192..7544cec 100644
--- a/.waf-tools/default-compiler-flags.py
+++ b/.waf-tools/default-compiler-flags.py
@@ -60,7 +60,7 @@
             supportedFlags += [flag]
 
     self.end_msg(' '.join(supportedFlags))
-    self.env.CXXFLAGS = supportedFlags + self.env.CXXFLAGS
+    self.env.prepend_value('CXXFLAGS', supportedFlags)
 
 @Configure.conf
 def add_supported_linkflags(self, linkflags):
@@ -78,8 +78,7 @@
             supportedFlags += [flag]
 
     self.end_msg(' '.join(supportedFlags))
-    self.env.LINKFLAGS = supportedFlags + self.env.LINKFLAGS
-
+    self.env.prepend_value('LINKFLAGS', supportedFlags)
 
 class CompilerFlags(object):
     def getGeneralFlags(self, conf):
diff --git a/.waf-tools/sanitizers.py b/.waf-tools/sanitizers.py
new file mode 100644
index 0000000..a8fe55d
--- /dev/null
+++ b/.waf-tools/sanitizers.py
@@ -0,0 +1,22 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+
+def options(opt):
+    opt.add_option('--with-sanitizer', action='store', default='', dest='sanitizers',
+                   help='Comma-separated list of compiler sanitizers to enable [default=none]')
+
+def configure(conf):
+    for san in conf.options.sanitizers.split(','):
+        if not san:
+            continue
+
+        sanflag = '-fsanitize=%s' % san
+        conf.start_msg('Checking if compiler supports %s' % sanflag)
+
+        if conf.check_cxx(cxxflags=['-Werror', sanflag, '-fno-omit-frame-pointer'],
+                          linkflags=[sanflag], mandatory=False):
+            conf.end_msg('yes')
+            conf.env.append_unique('CXXFLAGS', [sanflag, '-fno-omit-frame-pointer'])
+            conf.env.append_unique('LINKFLAGS', [sanflag])
+        else:
+            conf.end_msg('no', color='RED')
+            conf.fatal('%s sanitizer is not supported by the current compiler' % san)
diff --git a/AUTHORS.md b/AUTHORS.md
index bc84cdf..9888bde 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -43,6 +43,9 @@
 
     * Junxiao Shi         <https://www.cs.arizona.edu/people/shijunxiao/>
 
+* University Pierre & Marie Curie, Sorbonne University
+
+    * Davide Pesavento    <https://www.linkedin.com/in/davidepesavento>
 
 ## Queries about NLSR
 
diff --git a/tests/test-sync-logic-handler.cpp b/tests/test-sync-logic-handler.cpp
index 6e47e1c..24bd065 100644
--- a/tests/test-sync-logic-handler.cpp
+++ b/tests/test-sync-logic-handler.cpp
@@ -158,13 +158,12 @@
 
     receiveUpdate(updateName, syncSeqNo, sync_hrdry);
 
-    std::vector<ndn::Interest>& interests = face->sentInterests;
-
-    std::vector<ndn::Interest>::iterator it = interests.begin();
-
     // In HR dry-state all LSA's should be published
-    BOOST_REQUIRE_EQUAL(interests.size(), 1);
-    BOOST_CHECK_EQUAL(it->getName().getPrefix(-1), updateName + "/");
+    const auto& it = std::find_if(face->sentInterests.begin(), face->sentInterests.end(),
+                     [updateName] (const ndn::Interest& interest) {
+                       return interest.getName().getPrefix(-1) == updateName + "/";
+                     });
+    BOOST_REQUIRE(it != face->sentInterests.end());
   }
 }
 
diff --git a/wscript b/wscript
index 961c327..1f25960 100644
--- a/wscript
+++ b/wscript
@@ -1,7 +1,7 @@
 # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 """
-Copyright (c) 2014-2016,  The University of Memphis,
+Copyright (c) 2014-2017,  The University of Memphis,
                           Regents of the University of California,
                           Arizona Board of Regents.
 
@@ -32,7 +32,7 @@
 
 def options(opt):
     opt.load(['compiler_cxx', 'gnu_dirs'])
-    opt.load(['default-compiler-flags', 'coverage',
+    opt.load(['default-compiler-flags', 'coverage', 'sanitizers',
               'boost',  'doxygen', 'sphinx_build'],
             tooldir=['.waf-tools'])
 
@@ -74,6 +74,8 @@
 
     conf.load('coverage')
 
+    conf.load('sanitizers')
+
     conf.define('DEFAULT_CONFIG_FILE', '%s/ndn/nlsr.conf' % conf.env['SYSCONFDIR'])
 
     conf.write_config_header('config.hpp')
@@ -94,8 +96,7 @@
                 int(VERSION_SPLIT[2]),
         VERSION_MAJOR=VERSION_SPLIT[0],
         VERSION_MINOR=VERSION_SPLIT[1],
-        VERSION_PATCH=VERSION_SPLIT[2],
-        )
+        VERSION_PATCH=VERSION_SPLIT[2])
 
     nlsr_objects = bld(
         target='nlsr-objects',
@@ -105,28 +106,24 @@
                                  excl=['src/main.cpp']),
         use='NDN_CXX BOOST LOG4CXX SYNC',
         includes='. src',
-        export_includes='. src',
-        )
+        export_includes='. src')
 
     nlsr = bld(
         target='bin/nlsr',
         features='cxx cxxprogram',
         source='src/main.cpp',
-        use='nlsr-objects',
-        )
+        use='nlsr-objects')
 
     nlsrc = bld(
         target='bin/nlsrc',
         features='cxx cxxprogram',
         source='tools/nlsrc.cpp',
-        use='nlsr-objects BOOST',
-        )
+        use='nlsr-objects BOOST')
 
     bld(features="subst",
         source='nlsr.conf',
         target='nlsr.conf.sample',
-        install_path="${SYSCONFDIR}/ndn",
-    )
+        install_path="${SYSCONFDIR}/ndn")
 
     if bld.env['WITH_TESTS']:
         bld.recurse('tests')
@@ -149,14 +146,13 @@
     version(bld)
 
     if not bld.env.DOXYGEN:
-        Logs.error("ERROR: cannot build documentation (`doxygen' is not found in $PATH)")
+        Logs.error("ERROR: cannot build documentation (`doxygen' not found in $PATH)")
     else:
         bld(features="subst",
             name="doxygen-conf",
             source="docs/doxygen.conf.in",
             target="docs/doxygen.conf",
-            VERSION=VERSION_BASE,
-            )
+            VERSION=VERSION_BASE)
 
         bld(features="doxygen",
             doxyfile='docs/doxygen.conf',
@@ -166,7 +162,7 @@
     version(bld)
 
     if not bld.env.SPHINX_BUILD:
-        bld.fatal("ERROR: cannot build documentation (`sphinx-build' is not found in $PATH)")
+        bld.fatal("ERROR: cannot build documentation (`sphinx-build' not found in $PATH)")
     else:
         bld(features="sphinx",
             outdir="docs",