build+ci: add code coverage support
This commit also adds GlobalConfigurationFixture to ensure that running
unit tests doesn't interact with system configuration (e.g., doesn't use
or alter user certificates).
Change-Id: I621ce14425a777c94d41b8132cd2858d91235b4d
Refs: #3808
diff --git a/.jenkins.d/10-build.sh b/.jenkins.d/10-build.sh
index 7f885fb..f0df3eb 100755
--- a/.jenkins.d/10-build.sh
+++ b/.jenkins.d/10-build.sh
@@ -27,7 +27,7 @@
# Configure/build in debug mode with tests
if [[ $JOB_NAME == *"code-coverage" ]]; then
- COVERAGE="" # TODO add code coverage support
+ COVERAGE="--with-coverage"
elif ! has OSX-10.9 $NODE_LABELS && ! has OSX-10.11 $NODE_LABELS; then
ASAN="--with-sanitizer=address"
fi
diff --git a/.jenkins.d/30-coverage.sh b/.jenkins.d/30-coverage.sh
index fc3cbc0..7637b61 100755
--- a/.jenkins.d/30-coverage.sh
+++ b/.jenkins.d/30-coverage.sh
@@ -6,11 +6,27 @@
set -x
-# TODO add code coverage support
-if false && [[ $JOB_NAME == *"code-coverage" ]]; then
+if [[ $JOB_NAME == *"code-coverage" ]]; then
gcovr --object-directory=build \
--output=build/coverage.xml \
- --filter="$PWD/(core|tools)" \
+ --exclude="$PWD/tests" \
--root=. \
--xml
+
+ # Generate also a detailed HTML output, but using lcov (better results)
+ lcov --quiet \
+ --capture --no-external \
+ --directory . \
+ --rc lcov_branch_coverage=1 \
+ --output-file build/coverage-with-tests.info
+
+ lcov --quiet \
+ --remove build/coverage-with-tests.info "$PWD/tests/*" \
+ --rc lcov_branch_coverage=1 \
+ --output-file build/coverage.info
+
+ genhtml --legend \
+ --rc genhtml_branch_coverage=1 \
+ build/coverage.info \
+ --output-directory build/coverage
fi
diff --git a/.waf-tools/coverage.py b/.waf-tools/coverage.py
new file mode 100644
index 0000000..ce92883
--- /dev/null
+++ b/.waf-tools/coverage.py
@@ -0,0 +1,22 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+
+from waflib import TaskGen, Logs
+
+def options(opt):
+ opt.add_option('--with-coverage', action='store_true', default=False, dest='with_coverage',
+ help='''Set compiler flags for gcc to enable code coverage information''')
+
+def configure(conf):
+ if conf.options.with_coverage:
+ if not conf.options.debug:
+ conf.fatal("Code coverage flags require debug mode compilation (add --debug)")
+ conf.check_cxx(cxxflags=['-fprofile-arcs', '-ftest-coverage', '-fPIC'],
+ linkflags=['-fprofile-arcs'], uselib_store='GCOV', mandatory=True)
+
+@TaskGen.feature('cxx','cc')
+@TaskGen.after('process_source')
+def add_coverage(self):
+ if getattr(self, 'use', ''):
+ self.use += ' GCOV'
+ else:
+ self.use = 'GCOV'
diff --git a/tests/global-configuration-fixture.cpp b/tests/global-configuration-fixture.cpp
new file mode 100644
index 0000000..35ceeed
--- /dev/null
+++ b/tests/global-configuration-fixture.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017, Regents of the University of California.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/version.hpp>
+#include <boost/filesystem.hpp>
+
+#include "test-common.hpp"
+
+namespace ndn {
+namespace tools {
+namespace tests {
+
+class GlobalConfigurationFixture : boost::noncopyable
+{
+public:
+ GlobalConfigurationFixture()
+ {
+ if (getenv("HOME") != nullptr) {
+ m_home = getenv("HOME");
+ }
+ if (getenv("NDN_CLIENT_PIB") != nullptr) {
+ m_pib = getenv("NDN_CLIENT_PIB");
+ }
+ if (getenv("NDN_CLIENT_TPM") != nullptr) {
+ m_tpm = getenv("NDN_CLIENT_TPM");
+ }
+
+ boost::filesystem::path dir(TMP_TESTS_PATH);
+ dir /= "test-home";
+ setenv("HOME", dir.generic_string().c_str(), 1);
+
+ if (exists(dir)) {
+ remove_all(dir);
+ }
+
+ setenv("NDN_CLIENT_PIB", ("pib-sqlite3:" + dir.string()).c_str(), 1);
+ setenv("NDN_CLIENT_TPM", ("tpm-file:" + dir.string()).c_str(), 1);
+ create_directories(dir);
+ }
+
+ ~GlobalConfigurationFixture()
+ {
+ if (!m_home.empty()) {
+ setenv("HOME", m_home.c_str(), 1);
+ }
+ if (!m_pib.empty()) {
+ setenv("NDN_CLIENT_PIB", m_pib.c_str(), 1);
+ }
+ if (!m_tpm.empty()) {
+ setenv("NDN_CLIENT_TPM", m_tpm.c_str(), 1);
+ }
+ }
+
+private:
+ std::string m_home;
+ std::string m_pib;
+ std::string m_tpm;
+};
+
+BOOST_GLOBAL_FIXTURE(GlobalConfigurationFixture)
+#if (BOOST_VERSION >= 105900)
+;
+#endif // BOOST_VERSION >= 105900
+
+} // namespace tests
+} // namespace tools
+} // namespace ndn
diff --git a/wscript b/wscript
index 07a7524..42cb690 100644
--- a/wscript
+++ b/wscript
@@ -9,7 +9,8 @@
def options(opt):
opt.load(['compiler_cxx', 'gnu_dirs'])
- opt.load(['default-compiler-flags', 'sanitizers', 'sphinx_build', 'boost'],
+ opt.load(['default-compiler-flags', 'coverage', 'sanitizers', 'boost',
+ 'sphinx_build'],
tooldir=['.waf-tools'])
opt.add_option('--with-tests', action='store_true', default=False,
@@ -33,6 +34,9 @@
boost_libs += ' unit_test_framework'
conf.check_boost(lib=boost_libs)
+ # Loading "late" to prevent tests from being compiled with profiling flags
+ conf.load('coverage')
+
conf.recurse('tools')
conf.load('sanitizers')