diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..538543949c53897cc695190b5ccd064e3fed9453
--- /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 54f71d4ac5fdcb51f61dd447d3441549ab493af5..4220519f3dbcc51ee27ad5e73ef732b287506277 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 0000000000000000000000000000000000000000..25620686923375c57894939e44ac0bbde2f3ed81
--- /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 9d08fb49dd3654e4188abeae810b3f22401d85b6..770d4b84bde51bd05779dbb92fb2ea3ba46a9da1 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 ad74b8bcb42d8d03479fe39541b67538a2ae860e..ec7fc8a3d7c487fe1490da13c8864678606e8402 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 52e054eed48fd6de96920f19889ee0254efecc7a..232862e24946f3a2d5b98b88e8086c7c607acb93 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>