Move quic::test::CompareCharArraysWithHexError to Quiche

gfe-relnote: n/a, no functional change
PiperOrigin-RevId: 288560303
Change-Id: I82c3d1c60617d3c94abff40ea01ca5c8cecf95e2
diff --git a/common/test_tools/quiche_test_utils.cc b/common/test_tools/quiche_test_utils.cc
new file mode 100644
index 0000000..0a84635
--- /dev/null
+++ b/common/test_tools/quiche_test_utils.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/common/test_tools/quiche_test_utils.h"
+
+#include "net/third_party/quiche/src/common/platform/api/quiche_logging.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_test.h"
+
+#include <string>
+
+namespace {
+
+std::string HexDumpWithMarks(const char* data,
+                             int length,
+                             const bool* marks,
+                             int mark_length) {
+  static const char kHexChars[] = "0123456789abcdef";
+  static const int kColumns = 4;
+
+  const int kSizeLimit = 1024;
+  if (length > kSizeLimit || mark_length > kSizeLimit) {
+    QUICHE_LOG(ERROR) << "Only dumping first " << kSizeLimit << " bytes.";
+    length = std::min(length, kSizeLimit);
+    mark_length = std::min(mark_length, kSizeLimit);
+  }
+
+  std::string hex;
+  for (const char* row = data; length > 0;
+       row += kColumns, length -= kColumns) {
+    for (const char* p = row; p < row + 4; ++p) {
+      if (p < row + length) {
+        const bool mark =
+            (marks && (p - data) < mark_length && marks[p - data]);
+        hex += mark ? '*' : ' ';
+        hex += kHexChars[(*p & 0xf0) >> 4];
+        hex += kHexChars[*p & 0x0f];
+        hex += mark ? '*' : ' ';
+      } else {
+        hex += "    ";
+      }
+    }
+    hex = hex + "  ";
+
+    for (const char* p = row; p < row + 4 && p < row + length; ++p) {
+      hex += (*p >= 0x20 && *p <= 0x7f) ? (*p) : '.';
+    }
+
+    hex = hex + '\n';
+  }
+  return hex;
+}
+
+}  // namespace
+
+namespace quiche {
+namespace test {
+
+void CompareCharArraysWithHexError(const std::string& description,
+                                   const char* actual,
+                                   const int actual_len,
+                                   const char* expected,
+                                   const int expected_len) {
+  EXPECT_EQ(actual_len, expected_len);
+  const int min_len = std::min(actual_len, expected_len);
+  const int max_len = std::max(actual_len, expected_len);
+  std::unique_ptr<bool[]> marks(new bool[max_len]);
+  bool identical = (actual_len == expected_len);
+  for (int i = 0; i < min_len; ++i) {
+    if (actual[i] != expected[i]) {
+      marks[i] = true;
+      identical = false;
+    } else {
+      marks[i] = false;
+    }
+  }
+  for (int i = min_len; i < max_len; ++i) {
+    marks[i] = true;
+  }
+  if (identical)
+    return;
+  ADD_FAILURE() << "Description:\n"
+                << description << "\n\nExpected:\n"
+                << HexDumpWithMarks(expected, expected_len, marks.get(),
+                                    max_len)
+                << "\nActual:\n"
+                << HexDumpWithMarks(actual, actual_len, marks.get(), max_len);
+}
+
+}  // namespace test
+}  // namespace quiche