From 137c5b5fd54659c7f704ef6d8714a9f25d806179 Mon Sep 17 00:00:00 2001
From: Ben Cumming <louncharf@gmail.com>
Date: Mon, 11 Sep 2017 09:49:09 +0200
Subject: [PATCH] Basic CI support with TravisCI (#340)

Add support for continuous integration with Travis CI.
This implements bare bones support that can be extended over time.

Travis CI test environments:

    All use gcc 5.
    Test the serial distributed back end with serial and cthread threading backends.
    Test mpi with cthread.
    The tbb test failed sporadically because CMake, so it is disabled for now.

The test script:

    Builds the unit tests, global_communication tests and miniapp.
    Asserts that all unit and global_communication tests pass.
    Asserts that the miniapp runs successfully.
        does not test miniapp output for now.

There is plenty of scope for improving the tests.
A key improvement will be to use validated output for the validation and miniapp
to provide some validation.

There were some small fixes required to make the tests pass on Travis

    communication/mpi.hpp now sets default size and rank values of 1 and 0 respectively
    to allow all unit tests to pass when built with MPI.
    The wrappers around MPI API calls use const_cast to support MPI implementations that
    are not "const aware".
    A missing header was added to tests/unit/test_range to make std::unordered_multimap
    available.`
---
 .travis.yml               | 30 ++++++++++++++++++
 CMakeLists.txt            |  8 +++--
 scripts/travis/build.sh   | 65 +++++++++++++++++++++++++++++++++++++++
 src/communication/mpi.cpp |  8 +++--
 src/communication/mpi.hpp | 14 ++++++---
 tests/unit/test_range.cpp |  1 +
 6 files changed, 116 insertions(+), 10 deletions(-)
 create mode 100644 .travis.yml
 create mode 100755 scripts/travis/build.sh

diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..53854394
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,30 @@
+language: cpp
+sudo: false
+os: linux
+dist: trusty
+compiler: gcc
+
+addons:
+    apt:
+        sources:
+            - ubuntu-toolchain-r-test
+        packages:
+            - g++-5
+            - openmpi-bin
+            - libopenmpi-dev
+
+env:
+    # test single node/rank with different threading back ends
+    - BUILD_NAME=serial   WITH_THREAD=serial   WITH_DISTRIBUTED=serial
+    - BUILD_NAME=cthread  WITH_THREAD=cthread  WITH_DISTRIBUTED=serial
+    # The tbb backend sometimes fails to build because CMake. Turn it off until our CMake TBB support is stable.
+    #- BUILD_NAME=tbb      WITH_THREAD=tbb      WITH_DISTRIBUTED=serial
+    # test mpi
+    - BUILD_NAME=mpi      WITH_THREAD=cthread  WITH_DISTRIBUTED=mpi
+
+before_install:
+    - CC=gcc-5
+    - CXX=g++-5
+
+script: source ./scripts/travis/build.sh
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 54f71d4a..4220519f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,9 +5,11 @@ project(cell_algorithms)
 enable_language(CXX)
 
 # Hide warnings about mixing old and new signatures for target_link_libraries.
-# These can't be avoided, because the FindCUDA packed provided by CMake uses
-# the old signature, while other packages use the new signature.
-cmake_policy(SET CMP0023 OLD)
+# These can't be avoided, because the FindCUDA packed provided by CMake before
+# version 3.9 uses the old signature, while other packages use the new signature.
+if ("${CMAKE_VERSION}" MATCHES "^3.[0-8].")
+    cmake_policy(SET CMP0023 OLD)
+endif()
 
 # save incoming CXX flags for forwarding to modcc external project
 set(SAVED_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
diff --git a/scripts/travis/build.sh b/scripts/travis/build.sh
new file mode 100755
index 00000000..25620686
--- /dev/null
+++ b/scripts/travis/build.sh
@@ -0,0 +1,65 @@
+RED='\033[0;31m'
+YELLOW='\033[0;33m'
+CLEAR='\033[0m'
+
+error()    {>&2 echo -e "${RED}ERROR${CLEAR}: $1"; exit 1;}
+progress() { echo; echo -e "${YELLOW}STATUS${CLEAR}: $1"; echo;}
+
+base_path=`pwd`
+build_path=build-${BUILD_NAME}
+
+#
+# print build-specific and useful information
+#
+progress "build environment"
+
+compiler_version=`${CXX} -dumpversion`
+cmake_version=`cmake --version | grep version | awk '{print $3}'`
+
+echo "compiler   : ${CXX} ${compiler_version}"
+echo "cmake      : ${cmake_version}"
+echo "build path : ${build_path}"
+echo "base path  : ${base_path}"
+
+if [[ "${WITH_DISTRIBUTED}" = "mpi" ]]; then
+    echo "mpi        : on"
+    export OMPI_CC=${CC}
+    export OMPI_CXX=${CXX}
+    CC="mpicc"
+    CXX="mpicxx"
+    launch="mpiexec -n 4"
+else
+    echo "mpi        : off"
+    launch=""
+fi
+
+#
+# make build path
+#
+mkdir -p $build_path
+cd $build_path
+
+#
+# run cmake
+#
+progress "Configuring with cmake"
+
+cmake_flags="-DNMC_WITH_ASSERTIONS=on -DNMC_THREADING_MODEL=${WITH_THREAD} -DNMC_DISTRIBUTED_MODEL=${WITH_DISTRIBUTED} ${CXX_FLAGS}"
+echo "cmake flags: ${cmake_flags}"
+cmake .. ${cmake_flags} || error "unable to configure cmake"
+
+export NMC_NUM_THREADS=2
+
+progress "Unit tests"
+make test.exe -j4  || error "building unit tests"
+./tests/test.exe --gtest_color=no || error "running unit tests"
+
+progress "Global communication tests"
+make global_communication.exe -j4          || error "building global communication tests"
+${launch} ./tests/global_communication.exe || error "running global communication tests"
+
+progress "Miniapp spike comparison test"
+make miniapp.exe -j4                         || error "building miniapp"
+${launch} ./miniapp/miniapp.exe -n 20 -t 100 || error "running miniapp"
+
+cd $base_path
diff --git a/src/communication/mpi.cpp b/src/communication/mpi.cpp
index 9d08fb49..770d4b84 100644
--- a/src/communication/mpi.cpp
+++ b/src/communication/mpi.cpp
@@ -8,8 +8,12 @@ namespace mpi {
 
 // global state
 namespace state {
-    int size = -1;
-    int rank = -1;
+    // TODO: in case MPI_Init is never called, this will mimic one rank with rank 0.
+    // This is not ideal: calls to MPI-dependent features such as reductions will
+    // still fail, however this allows us to run all the unit tests until the
+    // run-time executors are implemented.
+    int size = 1;
+    int rank = 0;
 } // namespace state
 
 void init(int *argc, char ***argv) {
diff --git a/src/communication/mpi.hpp b/src/communication/mpi.hpp
index ad74b8bc..ec7fc8a3 100644
--- a/src/communication/mpi.hpp
+++ b/src/communication/mpi.hpp
@@ -106,9 +106,11 @@ namespace mpi {
         std::vector<char> buffer(displs.back());
 
         PE("MPI", "Gather");
-        MPI_Gatherv(str.data(), counts[rank()], traits::mpi_type(),                  // send
-                    buffer.data(), counts.data(), displs.data(), traits::mpi_type(), // receive
-                    root, MPI_COMM_WORLD);
+        MPI_Gatherv(
+            // const_cast required for MPI implementations that don't use const* in their interfaces
+            const_cast<std::string::value_type*>(str.data()), counts[rank()], traits::mpi_type(), // send
+            buffer.data(), counts.data(), displs.data(), traits::mpi_type(),                      // receive
+            root, MPI_COMM_WORLD);
         PL(2);
 
         // Unpack the raw string data into a vector of strings.
@@ -136,7 +138,8 @@ namespace mpi {
         PE("MPI", "Allgatherv");
         MPI_Allgatherv(
             // send buffer
-            values.data(), counts[rank()], traits::mpi_type(),
+            // const_cast required for MPI implementations that don't use const* in their interfaces
+            const_cast<T*>(values.data()), counts[rank()], traits::mpi_type(),
             // receive buffer
             buffer.data(), counts.data(), displs.data(), traits::mpi_type(),
             MPI_COMM_WORLD
@@ -168,7 +171,8 @@ namespace mpi {
         PE("MPI", "Allgatherv-partition");
         MPI_Allgatherv(
             // send buffer
-            values.data(), counts[rank()], traits::mpi_type(),
+            // const_cast required for MPI implementations that don't use const* in their interfaces
+            const_cast<T*>(values.data()), counts[rank()], traits::mpi_type(),
             // receive buffer
             buffer.data(), counts.data(), displs.data(), traits::mpi_type(),
             MPI_COMM_WORLD
diff --git a/tests/unit/test_range.cpp b/tests/unit/test_range.cpp
index 52e054ee..232862e2 100644
--- a/tests/unit/test_range.cpp
+++ b/tests/unit/test_range.cpp
@@ -6,6 +6,7 @@
 #include <list>
 #include <numeric>
 #include <type_traits>
+#include <unordered_map>
 
 #ifdef NMC_HAVE_TBB
 #include <tbb/tbb_stddef.h>
-- 
GitLab