Merge "Set context to defined value OnRun"
diff --git a/README.md b/README.md
index bcfe5c6..839f9c8 100644
--- a/README.md
+++ b/README.md
@@ -77,7 +77,7 @@
         1. Step 1: Add Bazel distribution URI as a package source
            ```
            sudo apt install curl
-           curl -fsSL https://bazel.build/bazel-release.pub.gpg | apt-key add -
+           curl -fsSL https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
            echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
            ```
         2. Step 2: Install Bazel
diff --git a/aos/events/BUILD b/aos/events/BUILD
index 4194514..79d09e4 100644
--- a/aos/events/BUILD
+++ b/aos/events/BUILD
@@ -225,7 +225,10 @@
 cc_test(
     name = "pingpong_test",
     srcs = ["pingpong_test.cc"],
-    data = [":pingpong_config"],
+    data = [
+        ":multinode_pingpong_test_split_config",
+        ":pingpong_config",
+    ],
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":ping_lib",
diff --git a/aos/events/pingpong_test.cc b/aos/events/pingpong_test.cc
index 07340f7..9c874ef 100644
--- a/aos/events/pingpong_test.cc
+++ b/aos/events/pingpong_test.cc
@@ -79,5 +79,93 @@
   EXPECT_EQ(ping_count, 1001);
 }
 
+// Multi-node ping pong test.  This test carefully mirrors the structure of the
+// single node test above to help highlight the similarities and differences.
+class MultiNodePingPongTest : public ::testing::Test {
+ public:
+  MultiNodePingPongTest()
+      : config_(aos::configuration::ReadConfig(ArtifactPath(
+            "aos/events/multinode_pingpong_test_split_config.json"))),
+        event_loop_factory_(&config_.message()),
+        pi1_(
+            configuration::GetNode(event_loop_factory_.configuration(), "pi1")),
+        pi2_(
+            configuration::GetNode(event_loop_factory_.configuration(), "pi2")),
+        ping_event_loop_(event_loop_factory_.MakeEventLoop("ping", pi1_)),
+        ping_(ping_event_loop_.get()),
+        pong_event_loop_(event_loop_factory_.MakeEventLoop("pong", pi2_)),
+        pong_(pong_event_loop_.get()) {}
+
+  // Config and factory.
+  // The factory is a factory for connected event loops.  Each application needs
+  // a separate event loop (because you can't send a message to yourself in a
+  // single event loop).  The created event loops can then send messages to each
+  // other and trigger callbacks to be called, or fetchers to receive data.
+  aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
+  SimulatedEventLoopFactory event_loop_factory_;
+
+  // Convenience pointers for each Node.
+  const Node *pi1_;
+  const Node *pi2_;
+
+  // Event loop and app for Ping
+  std::unique_ptr<EventLoop> ping_event_loop_;
+  Ping ping_;
+
+  // Event loop and app for Pong
+  std::unique_ptr<EventLoop> pong_event_loop_;
+  Pong pong_;
+};
+
+// Tests that the number of pong messages matches the number of ping messages
+// (on both nodes this time)
+TEST_F(MultiNodePingPongTest, AlwaysReplies) {
+  // For grins, test that ping and pong appear on both nodes and match.
+  std::unique_ptr<EventLoop> pi1_test_event_loop =
+      event_loop_factory_.MakeEventLoop("test", pi1_);
+  std::unique_ptr<EventLoop> pi2_test_event_loop =
+      event_loop_factory_.MakeEventLoop("test", pi2_);
+
+  int pi1_ping_count = 0;
+  int pi2_ping_count = 0;
+  int pi1_pong_count = 0;
+  int pi2_pong_count = 0;
+
+  // Confirm that the ping value matches on both nodes.
+  pi1_test_event_loop->MakeWatcher(
+      "/test", [&pi1_ping_count](const examples::Ping &ping) {
+        EXPECT_EQ(ping.value(), pi1_ping_count + 1);
+        ++pi1_ping_count;
+      });
+  pi2_test_event_loop->MakeWatcher(
+      "/test", [&pi2_ping_count](const examples::Ping &ping) {
+        EXPECT_EQ(ping.value(), pi2_ping_count + 1);
+        ++pi2_ping_count;
+      });
+
+  // Confirm that the ping and pong counts both match, and the value also
+  // matches.
+  pi2_test_event_loop->MakeWatcher(
+      "/test", [&pi2_pong_count, &pi2_ping_count](const examples::Pong &pong) {
+        EXPECT_EQ(pong.value(), pi2_pong_count + 1);
+        ++pi2_pong_count;
+        EXPECT_EQ(pi2_ping_count, pi2_pong_count);
+      });
+  pi1_test_event_loop->MakeWatcher(
+      "/test", [&pi1_pong_count, &pi1_ping_count](const examples::Pong &pong) {
+        EXPECT_EQ(pong.value(), pi1_pong_count + 1);
+        ++pi1_pong_count;
+        EXPECT_EQ(pi1_ping_count, pi1_pong_count);
+      });
+
+  // Since forwarding takes "time", we need to run a bit longer to let pong
+  // finish the last cycle.
+  event_loop_factory_.RunFor(chrono::seconds(10) + chrono::milliseconds(5));
+
+  // We run at t=0 and t=10 seconds, which means we run 1 extra time.
+  EXPECT_EQ(pi1_ping_count, 1001);
+  EXPECT_EQ(pi2_ping_count, 1001);
+}
+
 }  // namespace testing
 }  // namespace aos