diff --git a/.gitignore b/.gitignore
index c1833bba173b6133b0970eb5dcd6cae4a4c69791..ff2696ed75fe48bd54e9d654d2c139a2d9fa1c6b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,8 @@
 *.swo
 *.swn
 *.swq
+*.swm
+*.swl
 
 # tar files
 *.tar
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 041c025c3bd1725bcecda9ebb02d959ff19cff07..8da46487de7eb56194909cbcb81a1262ff2a6444 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -76,6 +76,33 @@ endif()
 # whether to generate optimized kernels from NMODL
 set(USE_OPTIMIZED_KERNELS OFF CACHE BOOL "generate optimized code that vectorizes with the Intel compiler")
 
+# Only build modcc if it has not already been installed.
+# This is useful if cross compiling for KNL, when it is not desirable to compile
+# modcc with the same flags that are used for the KNL target.
+find_program(MODCC_BIN modcc)
+set(modcc "${MODCC_BIN}")
+set(use_external_modcc OFF BOOL)
+
+# the modcc executable was not found, so build our own copy
+if(MODCC_BIN STREQUAL "MODCC_BIN-NOTFOUND")
+    include(ExternalProject)
+    externalproject_add(modparser
+        PREFIX ${CMAKE_BINARY_DIR}/external
+        CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/external"
+                   "-DCMAKE_CXX_FLAGS=${SAVED_CXX_FLAGS}"
+                   "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
+        BINARY_DIR "${CMAKE_BINARY_DIR}/external/modparser"
+        STAMP_DIR  "${CMAKE_BINARY_DIR}/external/"
+        TMP_DIR    "${CMAKE_BINARY_DIR}/external/tmp"
+        SOURCE_DIR "${CMAKE_SOURCE_DIR}/external/modparser"
+    )
+    # Set up environment to use the version of modcc that is compiled
+    # as the ExternalProject above.
+    set(use_external_modcc ON)
+    set(modcc "${CMAKE_BINARY_DIR}/external/bin/modcc")
+endif()
+
+
 include_directories(${CMAKE_SOURCE_DIR}/external)
 include_directories(${CMAKE_SOURCE_DIR}/external/tclap/include)
 include_directories(${CMAKE_SOURCE_DIR}/include)
diff --git a/mechanisms/CMakeLists.txt b/mechanisms/CMakeLists.txt
index ace3ccda304be110d1cc65b215986759c88647b3..80925e24320fe3a01d0c2eee24e5494e58ecd14a 100644
--- a/mechanisms/CMakeLists.txt
+++ b/mechanisms/CMakeLists.txt
@@ -1,30 +1,3 @@
-
-# Only build modcc if it has not already been installed.
-# This is useful if cross compiling for KNL, when it is not desirable to compile
-# modcc with the same flags that are used for the KNL target.
-find_program(MODCC_BIN modcc)
-set(modcc "${MODCC_BIN}")
-set(use_external_modcc OFF BOOL)
-
-# the modcc executable was not found, so build our own copy
-if(MODCC_BIN STREQUAL "MODCC_BIN-NOTFOUND")
-    include(ExternalProject)
-    externalproject_add(modparser
-        PREFIX ${CMAKE_BINARY_DIR}/external
-        CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/external"
-                   "-DCMAKE_CXX_FLAGS=${SAVED_CXX_FLAGS}"
-                   "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
-        BINARY_DIR "${CMAKE_BINARY_DIR}/external/modparser"
-        STAMP_DIR  "${CMAKE_BINARY_DIR}/external/"
-        TMP_DIR    "${CMAKE_BINARY_DIR}/external/tmp"
-        SOURCE_DIR "${CMAKE_SOURCE_DIR}/external/modparser"
-    )
-    # Set up environment to use the version of modcc that is compiled
-    # as the ExternalProject above.
-    set(use_external_modcc ON)
-    set(modcc "${CMAKE_BINARY_DIR}/external/bin/modcc")
-endif()
-
 # the list of built-in mechanisms to be provided by default
 set(mechanisms pas hh expsyn exp2syn)
 
diff --git a/src/model.hpp b/src/model.hpp
index 9eacbbc93677e3f2453558bde390427650a73986..41fb391ca614f60c01e37b170ed0774fd03b0cbc 100644
--- a/src/model.hpp
+++ b/src/model.hpp
@@ -242,7 +242,6 @@ private:
     using event_queue_type = typename communicator_type::event_queue;
     util::double_buffer< std::vector<event_queue_type> > event_queues_;
 
-    using spike_type = typename communicator_type::spike_type;
     using local_spike_store_type = thread_private_spike_store<time_type>;
     util::double_buffer< local_spike_store_type > local_spikes_;
 
diff --git a/tests/global_communication/CMakeLists.txt b/tests/global_communication/CMakeLists.txt
index fc8531dac8561fa748479af8d7633e9339c9a4f4..cc1e1af8b6567a2fc887815fa4eaeb25c561841e 100644
--- a/tests/global_communication/CMakeLists.txt
+++ b/tests/global_communication/CMakeLists.txt
@@ -3,6 +3,8 @@ set(HEADERS
 )
 set(COMMUNICATION_SOURCES
     test_exporter_spike_file.cpp
+    test_communicator.cpp
+
     # unit test driver
     test.cpp
 )
diff --git a/tests/global_communication/mpi_listener.hpp b/tests/global_communication/mpi_listener.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..968424c3e55f5da1dc81022ef75cf0de176a82fd
--- /dev/null
+++ b/tests/global_communication/mpi_listener.hpp
@@ -0,0 +1,145 @@
+#pragma once
+
+#include <cstdio>
+#include <ostream>
+#include <stdexcept>
+
+#include <communication/global_policy.hpp>
+
+#include "gtest.h"
+
+/// A specialized listener desinged for printing test results with MPI.
+///
+/// When tests are run with MPI, one instance of each test is run on
+/// each rank. The default behavior of Google Test is for each test
+/// instance to print to stdout. With more than one MPI rank, this creates
+/// the usual MPI mess of output.
+///
+/// This specialization has the first rank (rank 0) print to stdout, and all MPI
+/// ranks print their output to separate text files.
+/// For each test a message is printed showing
+///     - detailed messages about errors on rank 0
+///     - a head count of errors that occured on other MPI ranks
+
+class mpi_listener : public testing::EmptyTestEventListener {
+private:
+    using UnitTest = testing::UnitTest;
+    using TestCase = testing::TestCase;
+    using TestInfo = testing::TestInfo;
+    using TestPartResult = testing::TestPartResult;
+
+    int rank_;
+    int size_;
+    std::ofstream fid_;
+    char buffer_[1024];
+    bool test_case_failures_;
+    bool test_case_tests_;
+    int test_failures_;
+
+    bool does_print() const {
+        return rank_==0;
+    }
+
+    void print(const char* s) {
+        fid_ << s;
+        if (does_print()) {
+            std::cout << s;
+        }
+    }
+
+    void print(const std::string& s) {
+        print(s.c_str());
+    }
+
+    template <typename... Args>
+    void pprintf(const char* s, Args&&... args) {
+        snprintf(buffer_, sizeof(buffer_), s, std::forward<Args>(args)...);
+        print(buffer_);
+    }
+
+public:
+
+    mpi_listener(std::string f_base) {
+        rank_ = nest::mc::communication::global_policy::id();
+        size_ = nest::mc::communication::global_policy::size();
+
+        std::string fname = f_base + "_" + std::to_string(rank_) + ".txt";
+        fid_.open(fname);
+        if (!fid_.good()) {
+            throw std::runtime_error("could not open file " + fname + " for test output");
+        }
+    }
+
+    /// Messages that are printed at the start and end of the test program.
+    /// i.e. once only.
+    virtual void OnTestProgramStart(const UnitTest&) override {
+        pprintf("*** test output for rank %d of %d\n\n", rank_, size_);
+    }
+    virtual void OnTestProgramEnd(const UnitTest&) override {
+        pprintf("*** end test output for rank %d of %d\n", rank_, size_);
+    }
+
+    /// Messages that are printed at the start and end of each test case.
+    /// On startup a counter that counts the number of tests that fail in
+    /// this test case is initialized to zero, and will be incremented for each
+    /// test that fails.
+    virtual void OnTestCaseStart(const TestCase& test_case) override {
+        test_case_failures_ = 0;
+        test_case_tests_ = 0;
+    }
+    virtual void OnTestCaseEnd(const TestCase& test_case) override {
+        pprintf(
+            "TESTCASE %s : %2d:%-2d pass:fail of %2d tests\n\n",
+            test_case.name(),
+            test_case_failures_, test_case_tests_-test_case_failures_, test_case_tests_
+        );
+    }
+
+    // Called before a test starts.
+    virtual void OnTestStart(const TestInfo& test_info) override {
+        pprintf( "  TEST %s::%s\n", test_info.test_case_name(), test_info.name());
+        test_failures_ = 0;
+    }
+
+    // Called after a failed assertion or a SUCCEED() invocation.
+    virtual void OnTestPartResult(const TestPartResult& test_part_result) override {
+        const char* banner = "--------------------------------------------------------------------------------";
+
+        // indent all lines in the summary by 4 spaces
+        std::string summary = "    " + std::string(test_part_result.summary());
+        auto pos = summary.find("\n");
+        while (pos!=summary.size() && pos!=std::string::npos) {
+            summary.replace(pos, 1, "\n    ");
+            pos = summary.find("\n", pos+1);
+        }
+
+        pprintf(
+            "  LOCAL_%s\n    %s\n    %s:%d\n%s\n    %s\n",
+            test_part_result.failed() ? "FAIL" : "SUCCESS",
+            banner,
+            test_part_result.file_name(),
+            test_part_result.line_number(),
+            summary.c_str(),
+            banner
+        );
+
+        // note that there was a failure in this test case
+        if (test_part_result.failed()) {
+            test_failures_++;
+        }
+    }
+
+    // Called after a test ends.
+    virtual void OnTestEnd(const TestInfo& test_info) override {
+        test_case_tests_++;
+
+        // count the number of ranks that had errors
+        int global_errors =
+            nest::mc::communication::global_policy::sum(test_failures_>0 ? 1 : 0);
+        if (global_errors>0) {
+            test_case_failures_++;
+            pprintf("  GLOBAL_FAIL on %d ranks\n", global_errors);
+        }
+    }
+};
+
diff --git a/tests/global_communication/test.cpp b/tests/global_communication/test.cpp
index c67a065bd88ed85aa56e865b1d4f8f9fa3d5677e..640e4006973db9e65251e55b76132bbc8b908eea 100644
--- a/tests/global_communication/test.cpp
+++ b/tests/global_communication/test.cpp
@@ -5,15 +5,35 @@
 
 #include "gtest.h"
 
-#include "../../src/communication/communicator.hpp"
-#include "../../src/communication/global_policy.hpp"
+#include "mpi_listener.hpp"
 
-int main(int argc, char **argv) {
-    ::testing::InitGoogleTest(&argc, argv);
+#include <communication/communicator.hpp>
+#include <communication/global_policy.hpp>
+#include <util/ioutil.hpp>
 
+using namespace nest;
+
+int main(int argc, char **argv) {
     // We need to set the communicator policy at the top level
     // this allows us to build multiple communicators in the tests
-    nest::mc::communication::global_policy_guard global_guard(argc, argv);
+    mc::communication::global_policy_guard global_guard(argc, argv);
+
+    // initialize google test environment
+    ::testing::InitGoogleTest(&argc, argv);
+
+    // set up a custom listener that prints messages in an MPI-friendly way
+    auto& listeners = testing::UnitTest::GetInstance()->listeners();
+    // first delete the original printer
+    delete listeners.Release(listeners.default_result_printer());
+    // now add our custom printer
+    listeners.Append(new mpi_listener("global_comms"));
+
+    // record the local return value for tests run on this mpi rank
+    //      0 : success
+    //      1 : failure
+    auto result = RUN_ALL_TESTS();
 
-    return RUN_ALL_TESTS();
+    // perform global collective, to ensure that all ranks return
+    // the same exit code
+    return mc::communication::global_policy::max(result);
 }
diff --git a/tests/global_communication/test_communicator.cpp b/tests/global_communication/test_communicator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1b50e1b037e70146272e8f3c2737ef45c50505b9
--- /dev/null
+++ b/tests/global_communication/test_communicator.cpp
@@ -0,0 +1,18 @@
+#include "gtest.h"
+
+#include <cstdio>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <communication/communicator.hpp>
+#include <communication/global_policy.hpp>
+
+using namespace nest::mc;
+
+using time_type = float;
+using communicator_type = communication::communicator<time_type, communication::global_policy>;
+
+TEST(communicator, setup) {
+}