gfe-relnote: For QUIC BBR tests, add the ability to save test results and compare with saved results. Test only, not protected.

Note for Chromium/Envoy merge: quic_test_output_impl.(h|cc) needs to implement two functions:
1) QuicSaveTestOutputImpl: Can be implemented as a call to QuicRecordTestOutputToFile.
2) QuicLoadTestOutputImpl: Can be implemented using platform-specific file read apis. For example: use base::ReadFileToString in Chromium.

Usage:
$ blaze build third_party/quic/core/congestion_control:bbr_sender_test

// Save test results(one file per test) to <some directory>
$ QUIC_TEST_OUTPUT_DIR=<some directory> blaze-bin/third_party/quic/core/congestion_control/bbr_sender_test --quic_bbr_test_regression_mode=record

// Compare with test results in <some directory>, test fails if e.g. a test takes longer than the duration recorded in test results.
$ QUIC_TEST_OUTPUT_DIR=<some directory> blaze-bin/third_party/quic/core/congestion_control/bbr_sender_test --quic_bbr_test_regression_mode=regress

PiperOrigin-RevId: 284566985
Change-Id: I9610766cde7d014d2ddcdb629cd83626362af8d6
diff --git a/quic/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc
index 005e11d..811161d 100644
--- a/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -18,6 +18,8 @@
 #include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/send_algorithm_test_result.pb.h"
+#include "net/third_party/quiche/src/quic/test_tools/send_algorithm_test_utils.h"
 #include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
 #include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
 #include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
@@ -28,6 +30,14 @@
 using testing::Ge;
 using testing::Le;
 
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    std::string,
+    quic_bbr2_test_regression_mode,
+    "",
+    "One of a) 'record' to record test result (one file per test), or "
+    "b) 'regress' to regress against recorded results, or "
+    "c) <anything else> for non-regression mode.");
+
 namespace quic {
 
 using CyclePhase = Bbr2ProbeBwMode::CyclePhase;
@@ -112,6 +122,37 @@
     // srtt. Individual test can enable it via QuicConnectionPeer::SetAckMode().
     SetQuicReloadableFlag(quic_enable_ack_decimation, false);
   }
+
+  void SetUp() override {
+    if (GetQuicFlag(FLAGS_quic_bbr2_test_regression_mode) == "regress") {
+      SendAlgorithmTestResult expected;
+      ASSERT_TRUE(LoadSendAlgorithmTestResult(&expected));
+      random_seed_ = expected.random_seed();
+    } else {
+      random_seed_ = QuicRandom::GetInstance()->RandUint64();
+    }
+    random_.set_seed(random_seed_);
+    QUIC_LOG(INFO) << "Using random seed: " << random_seed_;
+  }
+
+  ~Bbr2SimulatorTest() override {
+    const std::string regression_mode =
+        GetQuicFlag(FLAGS_quic_bbr2_test_regression_mode);
+    const QuicTime::Delta simulated_duration =
+        SimulatedNow() - QuicTime::Zero();
+    if (regression_mode == "record") {
+      RecordSendAlgorithmTestResult(random_seed_,
+                                    simulated_duration.ToMicroseconds());
+    } else if (regression_mode == "regress") {
+      CompareSendAlgorithmTestResult(simulated_duration.ToMicroseconds());
+    }
+  }
+
+  QuicTime SimulatedNow() const { return simulator_.GetClock()->Now(); }
+
+  simulator::Simulator simulator_;
+  uint64_t random_seed_;
+  SimpleRandom random_;
 };
 
 class Bbr2DefaultTopologyTest : public Bbr2SimulatorTest {
@@ -129,10 +170,6 @@
                            TestConnectionId(42)) {
     sender_ = SetupBbr2Sender(&sender_endpoint_);
 
-    uint64_t seed = QuicRandom::GetInstance()->RandUint64();
-    random_.set_seed(seed);
-    QUIC_LOG(INFO) << "Bbr2DefaultTopologyTest set up.  Seed: " << seed;
-
     simulator_.set_random_generator(&random_);
   }
 
@@ -274,8 +311,6 @@
     return false;
   }
 
-  QuicTime SimulatedNow() const { return simulator_.GetClock()->Now(); }
-
   const RttStats* rtt_stats() {
     return sender_endpoint_.connection()->sent_packet_manager().GetRttStats();
   }
@@ -291,11 +326,9 @@
            sender_connection_stats().packets_sent;
   }
 
-  simulator::Simulator simulator_;
   simulator::QuicEndpoint sender_endpoint_;
   simulator::QuicEndpoint receiver_endpoint_;
   Bbr2Sender* sender_;
-  SimpleRandom random_;
 
   std::unique_ptr<simulator::Switch> switch_;
   std::unique_ptr<simulator::TrafficPolicer> sender_policer_;
@@ -786,10 +819,6 @@
             "Receiver multiplexer", receiver_endpoint_pointers);
     sender_1_ = SetupBbr2Sender(sender_endpoints_[0].get());
 
-    uint64_t seed = QuicRandom::GetInstance()->RandUint64();
-    random_.set_seed(seed);
-    QUIC_LOG(INFO) << "Bbr2MultiSenderTest set up.  Seed: " << seed;
-
     simulator_.set_random_generator(&random_);
   }
 
@@ -873,8 +902,6 @@
     }
   }
 
-  QuicTime SimulatedNow() const { return simulator_.GetClock()->Now(); }
-
   QuicConnection* sender_connection(size_t which) {
     return sender_endpoints_[which]->connection();
   }
@@ -888,12 +915,10 @@
            sender_connection_stats(which).packets_sent;
   }
 
-  simulator::Simulator simulator_;
   std::vector<std::unique_ptr<simulator::QuicEndpoint>> sender_endpoints_;
   std::vector<std::unique_ptr<simulator::QuicEndpoint>> receiver_endpoints_;
   std::unique_ptr<simulator::QuicEndpointMultiplexer> receiver_multiplexer_;
   Bbr2Sender* sender_1_;
-  SimpleRandom random_;
 
   std::unique_ptr<simulator::Switch> switch_;
   std::vector<std::unique_ptr<simulator::SymmetricLink>> network_links_;
diff --git a/quic/core/congestion_control/bbr_sender_test.cc b/quic/core/congestion_control/bbr_sender_test.cc
index 6acfe80..1f45089 100644
--- a/quic/core/congestion_control/bbr_sender_test.cc
+++ b/quic/core/congestion_control/bbr_sender_test.cc
@@ -20,6 +20,8 @@
 #include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/send_algorithm_test_result.pb.h"
+#include "net/third_party/quiche/src/quic/test_tools/send_algorithm_test_utils.h"
 #include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
 #include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
 #include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
@@ -28,6 +30,14 @@
 using testing::Ge;
 using testing::Le;
 
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    std::string,
+    quic_bbr_test_regression_mode,
+    "",
+    "One of a) 'record' to record test result (one file per test), or "
+    "b) 'regress' to regress against recorded results, or "
+    "c) <anything else> for non-regression mode.");
+
 namespace quic {
 namespace test {
 
@@ -101,10 +111,30 @@
 
     clock_ = simulator_.GetClock();
     simulator_.set_random_generator(&random_);
+  }
 
-    uint64_t seed = QuicRandom::GetInstance()->RandUint64();
-    random_.set_seed(seed);
-    QUIC_LOG(INFO) << "BbrSenderTest simulator set up.  Seed: " << seed;
+  void SetUp() override {
+    if (GetQuicFlag(FLAGS_quic_bbr_test_regression_mode) == "regress") {
+      SendAlgorithmTestResult expected;
+      ASSERT_TRUE(LoadSendAlgorithmTestResult(&expected));
+      random_seed_ = expected.random_seed();
+    } else {
+      random_seed_ = QuicRandom::GetInstance()->RandUint64();
+    }
+    random_.set_seed(random_seed_);
+    QUIC_LOG(INFO) << "BbrSenderTest simulator set up.  Seed: " << random_seed_;
+  }
+
+  ~BbrSenderTest() {
+    const std::string regression_mode =
+        GetQuicFlag(FLAGS_quic_bbr_test_regression_mode);
+    const QuicTime::Delta simulated_duration = clock_->Now() - QuicTime::Zero();
+    if (regression_mode == "record") {
+      RecordSendAlgorithmTestResult(random_seed_,
+                                    simulated_duration.ToMicroseconds());
+    } else if (regression_mode == "regress") {
+      CompareSendAlgorithmTestResult(simulated_duration.ToMicroseconds());
+    }
   }
 
   simulator::Simulator simulator_;
@@ -118,6 +148,7 @@
   std::unique_ptr<simulator::SymmetricLink> competing_sender_link_;
   std::unique_ptr<simulator::SymmetricLink> receiver_link_;
 
+  uint64_t random_seed_;
   SimpleRandom random_;
 
   // Owned by different components of the connection.
diff --git a/quic/platform/api/quic_test_output.h b/quic/platform/api/quic_test_output.h
index dfdb865..f6bb443 100644
--- a/quic/platform/api/quic_test_output.h
+++ b/quic/platform/api/quic_test_output.h
@@ -10,6 +10,18 @@
 
 namespace quic {
 
+// Save |data| into ${QUIC_TEST_OUTPUT_DIR}/filename. If a file with the same
+// path already exists, overwrite it.
+inline void QuicSaveTestOutput(QuicStringPiece filename, QuicStringPiece data) {
+  QuicSaveTestOutputImpl(filename, data);
+}
+
+// Load the content of ${QUIC_TEST_OUTPUT_DIR}/filename into |*data|.
+// Return whether it is successfully loaded.
+inline bool QuicLoadTestOutput(QuicStringPiece filename, std::string* data) {
+  return QuicLoadTestOutputImpl(filename, data);
+}
+
 // Records a QUIC trace file(.qtr) into a directory specified by the
 // QUIC_TEST_OUTPUT_DIR environment variable.  Assumes that it's called from a
 // unit test.
diff --git a/quic/test_tools/send_algorithm_test_result.proto b/quic/test_tools/send_algorithm_test_result.proto
new file mode 100644
index 0000000..a836c47
--- /dev/null
+++ b/quic/test_tools/send_algorithm_test_result.proto
@@ -0,0 +1,15 @@
+// Copyright 2019 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package quic;
+
+message SendAlgorithmTestResult {
+  optional string test_name = 1;
+  optional uint64 random_seed = 2;
+  optional int64 simulated_duration_micros = 3;
+}
diff --git a/quic/test_tools/send_algorithm_test_utils.cc b/quic/test_tools/send_algorithm_test_utils.cc
new file mode 100644
index 0000000..122d6e5
--- /dev/null
+++ b/quic/test_tools/send_algorithm_test_utils.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2019 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/quic/test_tools/send_algorithm_test_utils.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_output.h"
+
+namespace quic {
+namespace test {
+
+bool LoadSendAlgorithmTestResult(SendAlgorithmTestResult* result) {
+  std::string test_result_file_content;
+  if (!QuicLoadTestOutput(GetSendAlgorithmTestResultFilename(),
+                          &test_result_file_content)) {
+    return false;
+  }
+  return result->ParseFromString(test_result_file_content);
+}
+
+void RecordSendAlgorithmTestResult(uint64_t random_seed,
+                                   int64_t simulated_duration_micros) {
+  SendAlgorithmTestResult result;
+  result.set_test_name(GetFullSendAlgorithmTestName());
+  result.set_random_seed(random_seed);
+  result.set_simulated_duration_micros(simulated_duration_micros);
+
+  QuicSaveTestOutput(GetSendAlgorithmTestResultFilename(),
+                     result.SerializeAsString());
+}
+
+void CompareSendAlgorithmTestResult(int64_t actual_simulated_duration_micros) {
+  SendAlgorithmTestResult expected;
+  ASSERT_TRUE(LoadSendAlgorithmTestResult(&expected));
+  QUIC_LOG(INFO) << "Loaded expected test result: "
+                 << expected.ShortDebugString();
+
+  EXPECT_GE(expected.simulated_duration_micros(),
+            actual_simulated_duration_micros);
+}
+
+std::string GetFullSendAlgorithmTestName() {
+  const auto* test_info =
+      ::testing::UnitTest::GetInstance()->current_test_info();
+  const std::string type_param =
+      test_info->type_param() ? test_info->type_param() : "";
+  const std::string value_param =
+      test_info->value_param() ? test_info->value_param() : "";
+  return QuicStrCat(test_info->test_suite_name(), ".", test_info->name(), "_",
+                    type_param, "_", value_param);
+}
+
+std::string GetSendAlgorithmTestResultFilename() {
+  return GetFullSendAlgorithmTestName() + ".test_result";
+}
+
+}  // namespace test
+}  // namespace quic
diff --git a/quic/test_tools/send_algorithm_test_utils.h b/quic/test_tools/send_algorithm_test_utils.h
new file mode 100644
index 0000000..c36218d
--- /dev/null
+++ b/quic/test_tools/send_algorithm_test_utils.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2019 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SEND_ALGORITHM_TEST_UTILS_H_
+#define QUICHE_QUIC_TEST_TOOLS_SEND_ALGORITHM_TEST_UTILS_H_
+
+#include "net/third_party/quiche/src/quic/test_tools/send_algorithm_test_result.pb.h"
+
+namespace quic {
+namespace test {
+
+bool LoadSendAlgorithmTestResult(SendAlgorithmTestResult* result);
+
+void RecordSendAlgorithmTestResult(uint64_t random_seed,
+                                   int64_t simulated_duration_micros);
+
+// Load the expected test result with LoadSendAlgorithmTestResult(), and compare
+// it with the actual results provided in the arguments.
+void CompareSendAlgorithmTestResult(int64_t actual_simulated_duration_micros);
+
+std::string GetFullSendAlgorithmTestName();
+
+std::string GetSendAlgorithmTestResultFilename();
+
+}  // namespace test
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_TEST_TOOLS_SEND_ALGORITHM_TEST_UTILS_H_