Adds a CHPP loopback test

Bug: 165268537
Test: chre_chpp_linux_tests
Change-Id: I89a97ab1802ef55c5e5a283e18adee405a9addfc
diff --git a/chpp/Android.bp b/chpp/Android.bp
index d1cafc6..3727757 100644
--- a/chpp/Android.bp
+++ b/chpp/Android.bp
@@ -28,6 +28,8 @@
         "-DCHPP_SERVICE_ENABLED_WWAN",
         // clock_gettime() requires _POSIX_C_SOURCE >= 199309L
         "-D_POSIX_C_SOURCE=199309L",
+        // Required for pthread_setname_np()
+        "-D_GNU_SOURCE"
     ],
     conlyflags: ["-std=c11"],
     srcs: [
@@ -74,7 +76,10 @@
 
 cc_test_host {
     name: "chre_chpp_linux_tests",
-    srcs: ["test/transport_test.cpp"],
+    srcs: [
+        "test/loopback_test.cpp",
+        "test/transport_test.cpp",
+    ],
     static_libs: ["chre_chpp_linux"],
 }
 
diff --git a/chpp/platform/linux/include/chpp/platform/log.h b/chpp/platform/linux/include/chpp/platform/log.h
index 4913975..fbb7101 100644
--- a/chpp/platform/linux/include/chpp/platform/log.h
+++ b/chpp/platform/linux/include/chpp/platform/log.h
@@ -17,6 +17,7 @@
 #ifndef CHPP_LOG_H_
 #define CHPP_LOG_H_
 
+#include <pthread.h>
 #include <stdio.h>
 
 #ifdef __cplusplus
@@ -29,9 +30,13 @@
 
 // TODO: Should use PRIu8 etc. from inttypes.h instead of %d, etc. (add -Wall
 // and -Werror to cflags to catch these)
-#define CHPP_LINUX_LOG(level, color, fmt, ...)                         \
-  printf("\e[" color "m%s %s:%d\t" fmt "\e[0m\n", level, __FILENAME__, \
-         __LINE__, ##__VA_ARGS__)
+#define CHPP_LINUX_LOG(level, color, fmt, ...)                                 \
+  {                                                                            \
+    char name[16];                                                             \
+    pthread_getname_np(pthread_self(), name, 16);                              \
+    printf("\e[" color "m%s %s:%d\t (%s) " fmt "\e[0m\n", level, __FILENAME__, \
+           __LINE__, name, ##__VA_ARGS__);                                     \
+  }
 
 #define CHPP_LOGE(fmt, ...) CHPP_LINUX_LOG("E", "91", fmt, ##__VA_ARGS__)
 #define CHPP_LOGW(fmt, ...) CHPP_LINUX_LOG("W", "93", fmt, ##__VA_ARGS__)
diff --git a/chpp/platform/linux/include/chpp/platform/platform_link.h b/chpp/platform/linux/include/chpp/platform/platform_link.h
index f3343ff..2259008 100644
--- a/chpp/platform/linux/include/chpp/platform/platform_link.h
+++ b/chpp/platform/linux/include/chpp/platform/platform_link.h
@@ -57,6 +57,12 @@
   //! The temporary buffer to use to send data to the remote endpoint.
   uint8_t buf[CHPP_PLATFORM_LINK_TX_MTU_BYTES];
   size_t bufLen;
+
+  //! The string name of the linkSendThread.
+  const char *linkThreadName;
+
+  //! The string name of the CHPP work thread.
+  const char *workThreadName;
 };
 
 #ifdef __cplusplus
diff --git a/chpp/platform/linux/link.c b/chpp/platform/linux/link.c
index b101d61..03766b8 100644
--- a/chpp/platform/linux/link.c
+++ b/chpp/platform/linux/link.c
@@ -65,6 +65,9 @@
   chppNotifierInit(&params->notifier);
   pthread_create(&params->linkSendThread, NULL /* attr */, linkSendThread,
                  params);
+  if (params->linkThreadName != NULL) {
+    pthread_setname_np(params->linkSendThread, params->linkThreadName);
+  }
 }
 
 void chppPlatformLinkDeinit(struct ChppPlatformLinkParameters *params) {
diff --git a/chpp/test/loopback_test.cpp b/chpp/test/loopback_test.cpp
new file mode 100644
index 0000000..ff1d3d7
--- /dev/null
+++ b/chpp/test/loopback_test.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <thread>
+
+#include "chpp/app.h"
+#include "chpp/clients/loopback.h"
+#include "chpp/macros.h"
+#include "chpp/platform/log.h"
+#include "chpp/transport.h"
+
+namespace {
+
+void *workThread(void *arg) {
+  ChppTransportState *context = static_cast<ChppTransportState *>(arg);
+  pthread_setname_np(pthread_self(), context->linkParams.workThreadName);
+
+  chppWorkThreadStart(context);
+
+  return nullptr;
+}
+
+/*
+ * Test suite for the CHPP Loopback client/service
+ */
+class LoopbackTests : public testing::Test {
+ protected:
+  void SetUp() override {
+    memset(&mClientTransportContext.linkParams, 0,
+           sizeof(mClientTransportContext.linkParams));
+    memset(&mServiceTransportContext.linkParams, 0,
+           sizeof(mServiceTransportContext.linkParams));
+    // The linkSendThread in the link layer is a link "to" the remote end.
+    mServiceTransportContext.linkParams.linkThreadName = "Link to client";
+    mServiceTransportContext.linkParams.workThreadName = "Service work";
+    mClientTransportContext.linkParams.linkThreadName = "Link to service";
+    mClientTransportContext.linkParams.workThreadName = "Client work";
+
+    chppTransportInit(&mClientTransportContext, &mClientAppContext);
+    chppAppInit(&mClientAppContext, &mClientTransportContext);
+
+    chppTransportInit(&mServiceTransportContext, &mServiceAppContext);
+    chppAppInit(&mServiceAppContext, &mServiceTransportContext);
+
+    mClientTransportContext.linkParams.remoteTransportContext =
+        &mServiceTransportContext;
+    mServiceTransportContext.linkParams.remoteTransportContext =
+        &mClientTransportContext;
+
+    pthread_create(&mClientWorkThread, NULL, workThread,
+                   &mClientTransportContext);
+
+    // Wait a bit to emulate the scenario where the remote is not yet up
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+    pthread_create(&mServiceWorkThread, NULL, workThread,
+                   &mServiceTransportContext);
+    mClientTransportContext.linkParams.linkEstablished = true;
+    mServiceTransportContext.linkParams.linkEstablished = true;
+  }
+
+  void TearDown() override {
+    chppWorkThreadStop(&mClientTransportContext);
+    pthread_join(mClientWorkThread, NULL);
+
+    chppAppDeinit(&mClientAppContext);
+    chppTransportDeinit(&mClientTransportContext);
+
+    chppWorkThreadStop(&mServiceTransportContext);
+    pthread_join(mServiceWorkThread, NULL);
+
+    chppAppDeinit(&mServiceAppContext);
+    chppTransportDeinit(&mServiceTransportContext);
+  }
+
+  ChppTransportState mClientTransportContext = {};
+  ChppAppState mClientAppContext = {};
+
+  ChppTransportState mServiceTransportContext = {};
+  ChppAppState mServiceAppContext = {};
+
+  pthread_t mClientWorkThread;
+  pthread_t mServiceWorkThread;
+};
+
+TEST_F(LoopbackTests, SimpleStartStop) {
+  // Simple test to make sure start/stop work threads work,
+  // without crashes.
+  std::this_thread::sleep_for(std::chrono::seconds(1));
+}
+
+TEST_F(LoopbackTests, SimpleLoopback) {
+  // Wait for the reset to finish.
+  std::this_thread::sleep_for(std::chrono::seconds(1));
+
+  chppLoopbackClientInit(&mClientAppContext);
+
+  CHPP_LOGI("Starting loopback test ...");
+  uint8_t buf[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  struct ChppLoopbackTestResult result =
+      chppRunLoopbackTest(&mClientAppContext, buf, 10);
+  ASSERT_EQ(result.error, CHPP_APP_ERROR_NONE);
+
+  chppLoopbackClientDeinit();
+}
+
+}  // namespace