From d41cd674c8c37f7814c4ddd78ca741e4a4a8374f Mon Sep 17 00:00:00 2001 From: hai007 Date: Mon, 18 Jul 2022 19:15:09 -0700 Subject: [PATCH] Add onSessionComplete method in ClientProxy to log session PiperOrigin-RevId: 461772377 --- connections/implementation/BUILD | 1 + .../analytics/analytics_recorder.cc | 65 +- .../analytics/analytics_recorder.h | 16 +- .../analytics/analytics_recorder_test.cc | 796 ++++++++++++++++++ connections/implementation/client_proxy.cc | 16 +- connections/implementation/client_proxy.h | 2 +- .../implementation/client_proxy_test.cc | 219 ++++- 7 files changed, 1090 insertions(+), 25 deletions(-) diff --git a/connections/implementation/BUILD b/connections/implementation/BUILD index 3d9d421a..4e329e19 100644 --- a/connections/implementation/BUILD +++ b/connections/implementation/BUILD @@ -234,6 +234,7 @@ cc_test( "//connections:core_types", "//connections/implementation/mediums", "//connections/implementation/proto:offline_wire_formats_cc_proto", + "//internal/analytics:event_logger", "//internal/platform:base", "//internal/platform:comm", "//internal/platform:logging", diff --git a/connections/implementation/analytics/analytics_recorder.cc b/connections/implementation/analytics/analytics_recorder.cc index 72a787ae..98dd2515 100644 --- a/connections/implementation/analytics/analytics_recorder.cc +++ b/connections/implementation/analytics/analytics_recorder.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -33,6 +34,7 @@ namespace analytics { namespace { const char kVersion[] = "v1.0.0"; +constexpr absl::string_view kOnStartClientSession = "OnStartClientSession"; } // namespace using ::location::nearby::analytics::proto::ConnectionsLog; @@ -88,21 +90,41 @@ using ::nearby::analytics::EventLogger; AnalyticsRecorder::AnalyticsRecorder(EventLogger *event_logger) : event_logger_(event_logger) { - started_client_session_time_ = SystemClock::ElapsedRealtime(); - NEARBY_LOGS(INFO) << "AnalyticsRecorder ctor event_logger_=" << event_logger_; - MutexLock lock(&mutex_); - if (CanRecordAnalyticsLocked("OnStartClientSession")) { - LogEvent(START_CLIENT_SESSION); - } + NEARBY_LOGS(INFO) << "Start AnalyticsRecorder ctor event_logger_=" + << event_logger_; + LogStartSession(); } AnalyticsRecorder::~AnalyticsRecorder() { + serial_executor_.Shutdown(); + ResetClientSessionLoggingResouces(); +} + +bool AnalyticsRecorder::IsSessionLogged() { MutexLock lock(&mutex_); + return session_was_logged_; +} + +void AnalyticsRecorder::ResetClientSessionLoggingResouces() { + MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << "Reset AnalyticsRecorder ctor event_logger_=" + << event_logger_; + incoming_connection_requests_.clear(); outgoing_connection_requests_.clear(); active_connections_.clear(); bandwidth_upgrade_attempts_.clear(); - serial_executor_.Shutdown(); + + client_session_ = nullptr; + session_was_logged_ = true; + start_client_session_was_logged_ = false; + current_strategy_ = + connections::Strategy::kNone; // Need to reset since the same strategy + // should be logged separately for + // different client sessions. + current_strategy_session_ = nullptr; + current_advertising_phase_ = nullptr; + current_discovery_phase_ = nullptr; } void AnalyticsRecorder::OnStartAdvertising( @@ -163,8 +185,7 @@ void AnalyticsRecorder::OnStartDiscovery( // Initialize and set a DiscoveryPhase. started_discovery_phase_time_ = SystemClock::ElapsedRealtime(); - current_discovery_phase_ = - std::make_unique(); + current_discovery_phase_ = std::make_unique(); absl::c_copy(mediums, RepeatedFieldBackInserter( current_discovery_phase_->mutable_medium())); // Set a DiscoveryMetadata. @@ -632,6 +653,26 @@ void AnalyticsRecorder::OnErrorCode(const ErrorCodeParams ¶ms) { }); } +void AnalyticsRecorder::LogStartSession() { + MutexLock lock(&mutex_); + if (start_client_session_was_logged_) { + NEARBY_LOGS(WARNING) + << "AnalyticsRecorder CanRecordAnalytics Unexpected call " + << kOnStartClientSession + << " after start client session has already been logged."; + return; + } + + session_was_logged_ = false; + if (CanRecordAnalyticsLocked(kOnStartClientSession)) { + client_session_ = + std::make_unique(); + started_client_session_time_ = SystemClock::ElapsedRealtime(); + start_client_session_was_logged_ = true; + LogEvent(START_CLIENT_SESSION); + } +} + void AnalyticsRecorder::LogSession() { MutexLock lock(&mutex_); if (!CanRecordAnalyticsLocked("LogSession")) { @@ -668,7 +709,7 @@ AnalyticsRecorder::BuildConnectionAttemptMetadataParams( } bool AnalyticsRecorder::CanRecordAnalyticsLocked( - const std::string &method_name) { + absl::string_view method_name) { NEARBY_LOGS(VERBOSE) << "AnalyticsRecorder LogEvent " << method_name << " is calling."; if (event_logger_ == nullptr) { @@ -681,6 +722,7 @@ bool AnalyticsRecorder::CanRecordAnalyticsLocked( << method_name << " after session has already been logged."; return false; } + return true; } @@ -697,6 +739,7 @@ void AnalyticsRecorder::LogClientSession() { << connections_log.DebugString(); event_logger_->Log(connections_log); + ResetClientSessionLoggingResouces(); }); } @@ -717,7 +760,7 @@ void AnalyticsRecorder::UpdateStrategySessionLocked( connections::Strategy strategy, SessionRole role) { // If we're not switching strategies, just update the current StrategySession // with the new role. - if (strategy == current_strategy_) { + if (strategy == current_strategy_ && current_strategy_session_ != nullptr) { if (absl::c_linear_search(current_strategy_session_->role(), role)) { // We've already acted as this role before, so make sure we've finished // recording the previous round. diff --git a/connections/implementation/analytics/analytics_recorder.h b/connections/implementation/analytics/analytics_recorder.h index c79d9a6c..1b4f4bf4 100644 --- a/connections/implementation/analytics/analytics_recorder.h +++ b/connections/implementation/analytics/analytics_recorder.h @@ -164,11 +164,17 @@ class AnalyticsRecorder { // Error Code void OnErrorCode(const ErrorCodeParams ¶ms); + // Log the start client session event with start client session logging + // resouces setup (e.g. client_session_, started_client_session_time_) + void LogStartSession() ABSL_LOCKS_EXCLUDED(mutex_); + // Invokes event_logger_.Log() at the end of life of client. Log action is // called in a separate thread to allow synchronous potentially lengthy // execution. void LogSession() ABSL_LOCKS_EXCLUDED(mutex_); + bool IsSessionLogged(); + private: // Tracks the chunks and duration of a Payload on a particular medium. class PendingPayload { @@ -267,7 +273,7 @@ class AnalyticsRecorder { outgoing_payloads_; }; - bool CanRecordAnalyticsLocked(const std::string &method_name) + bool CanRecordAnalyticsLocked(absl::string_view method_name) ABSL_SHARED_LOCKS_REQUIRED(mutex_); // Callbacks the ConnectionsLog proto byte array data to the EventLogger with @@ -314,6 +320,10 @@ class AnalyticsRecorder { bool erase_item = true) ABSL_SHARED_LOCKS_REQUIRED(mutex_); void FinishStrategySessionLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + // Reset the client cession's logging resources (e.g. current_strategy_, + // current_advertising_phase_, current_discovery_phase_, etc) + void ResetClientSessionLoggingResouces(); + location::nearby::proto::connections::ConnectionsStrategy StrategyToConnectionStrategy(connections::Strategy strategy); location::nearby::proto::connections::PayloadType @@ -328,10 +338,10 @@ class AnalyticsRecorder { Mutex mutex_; // ClientSession - std::unique_ptr client_session_ = - std::make_unique(); + std::unique_ptr client_session_; absl::Time started_client_session_time_; bool session_was_logged_ ABSL_GUARDED_BY(mutex_) = false; + bool start_client_session_was_logged_ ABSL_GUARDED_BY(mutex_) = false; // Current StrategySession connections::Strategy current_strategy_ ABSL_GUARDED_BY(mutex_) = diff --git a/connections/implementation/analytics/analytics_recorder_test.cc b/connections/implementation/analytics/analytics_recorder_test.cc index b5ad8faf..9380cc78 100644 --- a/connections/implementation/analytics/analytics_recorder_test.cc +++ b/connections/implementation/analytics/analytics_recorder_test.cc @@ -55,6 +55,7 @@ using ::location::nearby::proto::connections::LOCAL_DISCONNECTION; using ::location::nearby::proto::connections::Medium; using ::location::nearby::proto::connections::RESULT_ERROR; using ::location::nearby::proto::connections::RESULT_SUCCESS; +using ::location::nearby::proto::connections::START_CLIENT_SESSION; using ::location::nearby::proto::connections::START_STRATEGY_SESSION; using ::location::nearby::proto::connections::STOP_CLIENT_SESSION; using ::location::nearby::proto::connections::STOP_STRATEGY_SESSION; @@ -67,6 +68,7 @@ using ::location::nearby::proto::connections::WIFI_LAN_SOCKET_CREATION; using ::nearby::analytics::EventLogger; using ::testing::Contains; using ::protobuf_matchers::EqualsProto; +using ::testing::Not; using ::testing::proto::Partially; constexpr absl::Duration kDefaultTimeout = absl::Milliseconds(1000); @@ -76,6 +78,12 @@ class FakeEventLogger : public EventLogger { explicit FakeEventLogger(CountDownLatch& client_session_done_latch) : client_session_done_latch_(client_session_done_latch) {} + FakeEventLogger(CountDownLatch& client_session_done_latch, + CountDownLatch* start_client_session_done_latch_ptr) + : client_session_done_latch_(client_session_done_latch), + start_client_session_done_latch_ptr_( + start_client_session_done_latch_ptr) {} + void Log(const ::google::protobuf::MessageLite& message) override { auto connections_log = dynamic_cast(&message); if (connections_log == nullptr) { @@ -94,6 +102,10 @@ class FakeEventLogger : public EventLogger { if (event_type == STOP_CLIENT_SESSION) { client_session_done_latch_.CountDown(); } + if (start_client_session_done_latch_ptr_ != nullptr && + event_type == START_CLIENT_SESSION) { + start_client_session_done_latch_ptr_->CountDown(); + } } int GetLoggedClientSessionCount() const { @@ -108,14 +120,27 @@ class FakeEventLogger : public EventLogger { std::vector GetLoggedEventTypes() { return logged_event_types_; } + void SetClientSessionDoneLatch( + const CountDownLatch& client_session_done_latch) { + client_session_done_latch_ = client_session_done_latch; + } + + void SetStartClientSessionDoneLatchPtr( + CountDownLatch* start_client_session_done_latch_ptr) { + start_client_session_done_latch_ptr_ = start_client_session_done_latch_ptr; + } + private: int logged_client_session_count_ = 0; CountDownLatch& client_session_done_latch_; + CountDownLatch* start_client_session_done_latch_ptr_ = nullptr; ConnectionsLog::ClientSession logged_client_session_; ConnectionsLog::ErrorCode error_code_; std::vector logged_event_types_; }; +// Test if session_was_logged_ is reset by checking if LogSession can take +// effect again or not. TEST(AnalyticsRecorderTest, SessionOnlyLoggedOnceWorks) { CountDownLatch client_session_done_latch(1); FakeEventLogger event_logger(client_session_done_latch); @@ -941,6 +966,777 @@ TEST(AnalyticsRecorderTest, SetErrorCodeFieldsCorrectlyForCommonError) { )pb"))); } +TEST(AnalyticsRecorderTest, CheckIfSessionWasLogged) { + CountDownLatch client_session_done_latch(1); + FakeEventLogger event_logger(client_session_done_latch); + AnalyticsRecorder analytics_recorder(&event_logger); + + // LogSession to count down client_session_done_latch. + analytics_recorder.LogSession(); + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + EXPECT_TRUE(analytics_recorder.IsSessionLogged()); +} + +TEST(AnalyticsRecorderTest, ConstructAnalyticsRecorder) { + CountDownLatch client_session_done_latch(0); + CountDownLatch start_client_session_done_latch(1); + FakeEventLogger event_logger(client_session_done_latch, + &start_client_session_done_latch); + + // Call the constructor to count down the session_done_latch. + AnalyticsRecorder analytics_recorder(&event_logger); + ASSERT_TRUE(start_client_session_done_latch.Await(kDefaultTimeout).result()); + + std::vector event_types = event_logger.GetLoggedEventTypes(); + EXPECT_EQ(event_types.size(), 1); + EXPECT_THAT(event_types, Contains(START_CLIENT_SESSION).Times(1)); +} + +TEST(AnalyticsRecorderTest, + StartClientSessionOnlyLoggedOnceWorksAfterAnalyticsRecorderIsConstructed) { + CountDownLatch client_session_done_latch(0); + CountDownLatch start_client_session_done_latch(1); + FakeEventLogger event_logger(client_session_done_latch, + &start_client_session_done_latch); + + // Call the constructor to count down the start_client_session_done_latch. + AnalyticsRecorder analytics_recorder(&event_logger); + ASSERT_TRUE(start_client_session_done_latch.Await(kDefaultTimeout).result()); + + // Log start client session once. + EXPECT_THAT(event_logger.GetLoggedEventTypes(), + Contains(START_CLIENT_SESSION).Times(1)); + + // Reset the start_client_session_done_latch. However, LogStartSession cannot + // count down the start_client_session_done_latch. + CountDownLatch new_start_client_session_done_latch(1); + event_logger.SetStartClientSessionDoneLatchPtr( + &new_start_client_session_done_latch); + analytics_recorder.LogStartSession(); + ASSERT_FALSE( + new_start_client_session_done_latch.Await(kDefaultTimeout).result()); + + // No more start client session was logged. + EXPECT_THAT(event_logger.GetLoggedEventTypes(), + Contains(START_CLIENT_SESSION).Times(1)); +} + +TEST(AnalyticsRecorderTest, + CanLogStartClientSessionOnceAgainAfterSessionWasLogged) { + CountDownLatch client_session_done_latch(0); + CountDownLatch start_client_session_done_latch(1); + FakeEventLogger event_logger(client_session_done_latch, + &start_client_session_done_latch); + + // Call the constructor to count down the start_client_session_done_latch. + AnalyticsRecorder analytics_recorder(&event_logger); + ASSERT_TRUE(start_client_session_done_latch.Await(kDefaultTimeout).result()); + + // Log start client session once. + EXPECT_THAT(event_logger.GetLoggedEventTypes(), + Contains(START_CLIENT_SESSION).Times(1)); + + // Reset the client_session_done_latch. Call LogSession to count down the + // client_session_done_latch. + CountDownLatch new_client_session_done_latch(1); + event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); + analytics_recorder.LogSession(); + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + // Reset the start_client_session_done_latch. Call LogStartSession to count + // down the start_client_session_done_latch. + CountDownLatch new_start_client_session_done_latch(1); + event_logger.SetStartClientSessionDoneLatchPtr( + &new_start_client_session_done_latch); + analytics_recorder.LogStartSession(); + analytics_recorder.LogStartSession(); + analytics_recorder.LogStartSession(); + analytics_recorder.LogStartSession(); + ASSERT_TRUE( + new_start_client_session_done_latch.Await(kDefaultTimeout).result()); + + // Can log start client session once again. + EXPECT_THAT(event_logger.GetLoggedEventTypes(), + Contains(START_CLIENT_SESSION).Times(2)); +} + +TEST(AnalyticsRecorderTest, + ClearcIncomingConnectionRequestsAfterSessionWasLogged) { + connections::Strategy strategy = connections::Strategy::kP2pStar; + std::vector mediums = {BLE, BLUETOOTH}; + std::string endpoint_id_0 = "endpoint_id_0"; + + CountDownLatch client_session_done_latch(1); + FakeEventLogger event_logger(client_session_done_latch); + AnalyticsRecorder analytics_recorder(&event_logger); + + analytics_recorder.OnStartAdvertising(strategy, mediums); + analytics_recorder.OnConnectionRequestReceived(endpoint_id_0); + analytics_recorder.OnLocalEndpointAccepted(endpoint_id_0); + analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_0); + + // LogSession + analytics_recorder.LogSession(); // call ResetClientSessionLoggingResouces + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + EXPECT_THAT(event_logger.GetLoggedClientSession(), Partially(EqualsProto(R"pb( + strategy_session < + strategy: P2P_STAR + role: ADVERTISER + advertising_phase < + medium: BLE + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + received_connection_request < + local_response: ACCEPTED + remote_response: ACCEPTED + > + > + >)pb"))); + + // LogStartSession + CountDownLatch new_start_client_session_done_latch(1); + event_logger.SetStartClientSessionDoneLatchPtr( + &new_start_client_session_done_latch); + analytics_recorder.LogStartSession(); + ASSERT_TRUE( + new_start_client_session_done_latch.Await(kDefaultTimeout).result()); + + // LogSession again + CountDownLatch new_client_session_done_latch(1); + event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); + std::string endpoint_id_1 = "endpoint_id_1"; + analytics_recorder.OnConnectionRequestReceived(endpoint_id_1); + analytics_recorder.OnLocalEndpointAccepted(endpoint_id_1); + analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_1); + + analytics_recorder.LogSession(); + ASSERT_TRUE(new_client_session_done_latch.Await(kDefaultTimeout).result()); + + // - if the current_strategy_session_ and current_advertising_phase_ are not + // reset, the duplicate advertising_phase (with the additional + // received_connection_request) will append to the strategy_session) + EXPECT_THAT(event_logger.GetLoggedClientSession(), + Not(Partially(EqualsProto(R"pb( + strategy_session < + strategy: P2P_STAR + role: ADVERTISER + advertising_phase < + medium: BLE + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + received_connection_request < + local_response: ACCEPTED + remote_response: ACCEPTED + > + > + advertising_phase < + medium: BLE + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + received_connection_request < + local_response: ACCEPTED + remote_response: ACCEPTED + > + received_connection_request < + local_response: ACCEPTED + remote_response: ACCEPTED + > + > + >)pb")))); +} + +TEST(AnalyticsRecorderTest, + ClearcOutgoingConnectionRequestsAfterSessionWasLogged) { + connections::Strategy strategy = connections::Strategy::kP2pStar; + std::vector mediums = {BLE, BLUETOOTH}; + std::string endpoint_id_0 = "endpoint_id_0"; + + CountDownLatch client_session_done_latch(1); + FakeEventLogger event_logger(client_session_done_latch); + AnalyticsRecorder analytics_recorder(&event_logger); + + analytics_recorder.OnStartDiscovery(strategy, mediums); + + analytics_recorder.OnConnectionRequestSent(endpoint_id_0); + analytics_recorder.OnLocalEndpointAccepted(endpoint_id_0); + analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_0); + + // LogSession + analytics_recorder.LogSession(); // call ResetClientSessionLoggingResouces + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + EXPECT_THAT(event_logger.GetLoggedClientSession(), Partially(EqualsProto(R"pb( + strategy_session < + strategy: P2P_STAR + role: DISCOVERER + discovery_phase < + medium: BLE + medium: BLUETOOTH + discovery_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + sent_connection_request < + local_response: ACCEPTED + remote_response: ACCEPTED + > + > + >)pb"))); + + // LogStartSession + CountDownLatch new_start_client_session_done_latch(1); + event_logger.SetStartClientSessionDoneLatchPtr( + &new_start_client_session_done_latch); + analytics_recorder.LogStartSession(); + ASSERT_TRUE( + new_start_client_session_done_latch.Await(kDefaultTimeout).result()); + + // LogSession again + CountDownLatch new_client_session_done_latch(1); + event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); + std::string endpoint_id_1 = "endpoint_id_1"; + analytics_recorder.OnConnectionRequestSent(endpoint_id_1); + analytics_recorder.OnLocalEndpointAccepted(endpoint_id_1); + analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_1); + + analytics_recorder.LogSession(); + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + // - if the current_strategy_session_ and current_discovery_phase_ are + // not reset, the duplicate discovery_phase (with the additional + // sent_connection_request) will append to the strategy_session) + EXPECT_THAT(event_logger.GetLoggedClientSession(), + Not(Partially(EqualsProto(R"pb( + strategy_session < + strategy: P2P_STAR + role: DISCOVERER + discovery_phase < + medium: BLE + medium: BLUETOOTH + discovery_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + sent_connection_request < + local_response: ACCEPTED + remote_response: ACCEPTED + > + > + discovery_phase < + medium: BLE + medium: BLUETOOTH + discovery_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + sent_connection_request < + local_response: ACCEPTED + remote_response: ACCEPTED + > + sent_connection_request < + local_response: ACCEPTED + remote_response: ACCEPTED + > + > + >)pb")))); +} + +TEST(AnalyticsRecorderTest, ClearcActiveConnectionsAfterSessionWasLogged) { + connections::Strategy strategy = connections::Strategy::kP2pStar; + std::vector mediums = {BLE, BLUETOOTH}; + std::string endpoint_id = "endpoint_id"; + std::string connection_token = "connection_token"; + + CountDownLatch client_session_done_latch(1); + FakeEventLogger event_logger(client_session_done_latch); + AnalyticsRecorder analytics_recorder(&event_logger); + + analytics_recorder.OnStartAdvertising(strategy, mediums); + + analytics_recorder.OnConnectionEstablished(endpoint_id, BLUETOOTH, + connection_token); + + // LogSession + analytics_recorder.LogSession(); // call ResetClientSessionLoggingResouces + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + EXPECT_THAT(event_logger.GetLoggedClientSession(), Partially(EqualsProto(R"pb( + strategy_session < + strategy: P2P_STAR + role: ADVERTISER + advertising_phase < + medium: BLE + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + > + established_connection < + medium: BLUETOOTH + disconnection_reason: UNFINISHED + connection_token: "connection_token" + > + >)pb"))); + + // LogStartSession + CountDownLatch new_start_client_session_done_latch(1); + event_logger.SetStartClientSessionDoneLatchPtr( + &new_start_client_session_done_latch); + analytics_recorder.LogStartSession(); + ASSERT_TRUE( + new_start_client_session_done_latch.Await(kDefaultTimeout).result()); + + // LogSession again + CountDownLatch new_client_session_done_latch(1); + event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); + analytics_recorder.LogSession(); + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + // - if the current_strategy_session_ and advertising_phase_ are not + // reset, the duplicate advertising_phase_ (with the additional + // will append to the strategy_session), and the active connection (i.e. + // established_connection) will stay there. + EXPECT_THAT(event_logger.GetLoggedClientSession(), + Not(Partially(EqualsProto(R"pb( + strategy_session < + strategy: P2P_STAR + role: ADVERTISER + advertising_phase < + medium: BLE + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + > + advertising_phase < + medium: BLE + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + > + established_connection < + medium: BLUETOOTH + disconnection_reason: UNFINISHED + connection_token: "connection_token" + > + >)pb")))); +} + +TEST(AnalyticsRecorderTest, + ClearBandwidthUpgradeAttemptsAfterSessionWasLogged) { + connections::Strategy strategy = connections::Strategy::kP2pStar; + std::vector mediums = {BLE, BLUETOOTH}; + std::string endpoint_id = "endpoint_id"; + std::string endpoint_id_1 = "endpoint_id_1"; + std::string endpoint_id_2 = "endpoint_id_2"; + std::string connection_token = "connection_token"; + + CountDownLatch client_session_done_latch(1); + FakeEventLogger event_logger(client_session_done_latch); + AnalyticsRecorder analytics_recorder(&event_logger); + + analytics_recorder.OnStartAdvertising(strategy, mediums); + + analytics_recorder.OnBandwidthUpgradeStarted(endpoint_id, BLE, WIFI_LAN, + INCOMING, connection_token); + + analytics_recorder.OnBandwidthUpgradeStarted( + endpoint_id_1, BLUETOOTH, WIFI_LAN, INCOMING, connection_token); + // - Error to upgrade. + analytics_recorder.OnBandwidthUpgradeError(endpoint_id, WIFI_LAN_MEDIUM_ERROR, + WIFI_LAN_SOCKET_CREATION); + // - Success to upgrade. + analytics_recorder.OnBandwidthUpgradeSuccess(endpoint_id_1); + + // - Upgrade is unfinished. + analytics_recorder.OnBandwidthUpgradeStarted( + endpoint_id_2, BLUETOOTH, WIFI_LAN, INCOMING, connection_token); + + // LogSession + analytics_recorder.LogSession(); // call ResetClientSessionLoggingResouces + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + // - if the current_strategy_session_ and advertising_phase_ are not + // reset, the duplicate advertising_phase_, and the upgrade_attempts (i.e. + // bandwidth_upgrade_attempts_) will stay there. + EXPECT_THAT(event_logger.GetLoggedClientSession(), Partially(EqualsProto(R"pb( + strategy_session < + strategy: P2P_STAR + role: ADVERTISER + advertising_phase < + medium: BLE + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + > + upgrade_attempt < + direction: INCOMING + from_medium: BLE + to_medium: WIFI_LAN + upgrade_result: WIFI_LAN_MEDIUM_ERROR + error_stage: WIFI_LAN_SOCKET_CREATION + connection_token: "connection_token" + > + upgrade_attempt < + direction: INCOMING + from_medium: BLUETOOTH + to_medium: WIFI_LAN + upgrade_result: UPGRADE_RESULT_SUCCESS + error_stage: UPGRADE_SUCCESS + connection_token: "connection_token" + > + upgrade_attempt { + direction: INCOMING + from_medium: BLUETOOTH + to_medium: WIFI_LAN + upgrade_result: UNFINISHED_ERROR + error_stage: UPGRADE_UNFINISHED + connection_token: "connection_token" + } + >)pb"))); + + // LogStartSession + CountDownLatch new_start_client_session_done_latch(1); + event_logger.SetStartClientSessionDoneLatchPtr( + &new_start_client_session_done_latch); + analytics_recorder.LogStartSession(); + ASSERT_TRUE( + new_start_client_session_done_latch.Await(kDefaultTimeout).result()); + + // LogSession again + CountDownLatch new_client_session_done_latch(1); + event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); + analytics_recorder.LogSession(); + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + EXPECT_THAT(event_logger.GetLoggedClientSession(), + Not(Partially(EqualsProto(R"pb( + strategy_session < + strategy: P2P_STAR + role: ADVERTISER + advertising_phase < + medium: BLE + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + > + advertising_phase < + medium: BLE + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + > + upgrade_attempt < + direction: INCOMING + from_medium: BLE + to_medium: WIFI_LAN + upgrade_result: WIFI_LAN_MEDIUM_ERROR + error_stage: WIFI_LAN_SOCKET_CREATION + connection_token: "connection_token" + > + upgrade_attempt < + direction: INCOMING + from_medium: BLUETOOTH + to_medium: WIFI_LAN + upgrade_result: UPGRADE_RESULT_SUCCESS + error_stage: UPGRADE_SUCCESS + connection_token: "connection_token" + > + upgrade_attempt { + direction: INCOMING + from_medium: BLUETOOTH + to_medium: WIFI_LAN + upgrade_result: UNFINISHED_ERROR + error_stage: UPGRADE_UNFINISHED + connection_token: "connection_token" + } + >)pb")))); +} + +// Test if current_strategy_ is reset by checking if the same strategy would +// be logged for different client sessions or not. If yes, it should be logged. +// Otherwise, not. +TEST(AnalyticsRecorderTest, + CanLogSeparateStartStrategySessionForSameStrategyAfterSessionWasLogged) { + connections::Strategy strategy = connections::Strategy::kP2pStar; + + CountDownLatch client_session_done_latch(1); + FakeEventLogger event_logger(client_session_done_latch); + AnalyticsRecorder analytics_recorder(&event_logger); + + analytics_recorder.OnStartAdvertising(strategy, {BLUETOOTH}); + analytics_recorder.OnStopAdvertising(); + + // LogSession + analytics_recorder.LogSession(); + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + EXPECT_THAT(event_logger.GetLoggedEventTypes(), + Contains(START_STRATEGY_SESSION) + .Times(1)); // the same strategy session shouldn't be logged + // again with the same client sesssion. + + // LogStartSession + CountDownLatch new_start_client_session_done_latch(1); + event_logger.SetStartClientSessionDoneLatchPtr( + &new_start_client_session_done_latch); + analytics_recorder.LogStartSession(); + ASSERT_TRUE( + new_start_client_session_done_latch.Await(kDefaultTimeout).result()); + + // LogSession again + CountDownLatch new_client_session_done_latch(1); + event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); + analytics_recorder.OnStartAdvertising(strategy, {BLUETOOTH}); + analytics_recorder.OnStopAdvertising(); + + analytics_recorder.LogSession(); + ASSERT_TRUE(new_client_session_done_latch.Await(kDefaultTimeout).result()); + + EXPECT_THAT(event_logger.GetLoggedEventTypes(), + Contains(START_STRATEGY_SESSION).Times(2)); +} + +// Test if current_strategy_session_ is reset. If not, the same strategy session +// proto will be logged. +TEST(AnalyticsRecorderTest, + NotLogSameStrategySessionProtoAfterSessionWasLogged) { + connections::Strategy strategy = connections::Strategy::kP2pStar; + std::vector mediums = {BLE, BLUETOOTH}; + std::string endpoint_id = "endpoint_id"; + std::string connection_token = ""; + + CountDownLatch client_session_done_latch(1); + FakeEventLogger event_logger(client_session_done_latch); + AnalyticsRecorder analytics_recorder{&event_logger}; + + analytics_recorder.OnStartAdvertising( + strategy, mediums); // via OnStartAdvertising, current_strategy_session_ + // is set in UpdateStrategySessionLocked. + analytics_recorder.OnStopAdvertising(); + + // LogSession + analytics_recorder.LogSession(); + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + std::string strategy_session_proto = R"pb( + strategy_session < + strategy: P2P_STAR + role: ADVERTISER + advertising_phase < + medium: BLE + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + > + >)pb"; + + EXPECT_THAT(event_logger.GetLoggedClientSession(), + Partially(EqualsProto(strategy_session_proto))); + + // LogStartSession + CountDownLatch new_start_client_session_done_latch(1); + event_logger.SetStartClientSessionDoneLatchPtr( + &new_start_client_session_done_latch); + analytics_recorder.LogStartSession(); + ASSERT_TRUE( + new_start_client_session_done_latch.Await(kDefaultTimeout).result()); + + // LogSession again + // - if current_strategy_session_ is reset, the same strategy_session_proto + // will be logged. + CountDownLatch new_client_session_done_latch(1); + event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); + analytics_recorder.LogSession(); + ASSERT_TRUE(new_client_session_done_latch.Await(kDefaultTimeout).result()); + + EXPECT_THAT(event_logger.GetLoggedClientSession(), + Not(Partially(EqualsProto(strategy_session_proto)))); +} + +// Test if current_advertising_phase_ is reset. +TEST(AnalyticsRecorderTest, + NotLogDuplicateAdvertisingPhaseAfterSessionWasLogged) { + connections::Strategy strategy = connections::Strategy::kP2pStar; + + CountDownLatch client_session_done_latch(1); + FakeEventLogger event_logger(client_session_done_latch); + AnalyticsRecorder analytics_recorder(&event_logger); + + analytics_recorder.OnStartAdvertising( + strategy, {BLUETOOTH}); // set current_advertising_phase_ + analytics_recorder.OnStopAdvertising(); + + // LogSession + analytics_recorder.LogSession(); + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + EXPECT_THAT(event_logger.GetLoggedClientSession(), Partially(EqualsProto(R"pb( + strategy_session < + strategy: P2P_STAR + role: ADVERTISER + advertising_phase < + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + > + >)pb"))); + + // LogStartSession + CountDownLatch new_start_client_session_done_latch(1); + event_logger.SetStartClientSessionDoneLatchPtr( + &new_start_client_session_done_latch); + analytics_recorder.LogStartSession(); + ASSERT_TRUE( + new_start_client_session_done_latch.Await(kDefaultTimeout).result()); + + // LogSession again + // - if the current_strategy_session_ and current_advertising_phase_ are + // not reset, the same strategy_session with two same advertising_phase will + // be logged. + CountDownLatch new_client_session_done_latch(1); + event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); + analytics_recorder.LogSession(); + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + EXPECT_THAT(event_logger.GetLoggedClientSession(), + Not(Partially(EqualsProto(R"pb( + strategy_session < + strategy: P2P_STAR + role: ADVERTISER + advertising_phase < + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + > + advertising_phase < + medium: BLUETOOTH + advertising_metadata < + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + > + > + >)pb")))); +} + +// Test if current_discovery_phase_ is reset. +TEST(AnalyticsRecorderTest, + NotLogDuplicateDiscoveryPhaseAfterSessionWasLogged) { + connections::Strategy strategy = connections::Strategy::kP2pStar; + + CountDownLatch client_session_done_latch(1); + FakeEventLogger event_logger(client_session_done_latch); + AnalyticsRecorder analytics_recorder(&event_logger); + + analytics_recorder.OnStartDiscovery( + strategy, {BLUETOOTH}, /*is_extended_advertisement_supported=*/true, + /*connected_ap_frequency=*/1, + /*is_nfc_available=*/false); // set current_discovery_phase_ + analytics_recorder.OnStopDiscovery(); + analytics_recorder.OnEndpointFound(BLUETOOTH); + + // LogSession + analytics_recorder.LogSession(); + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + EXPECT_THAT(event_logger.GetLoggedClientSession(), Partially(EqualsProto(R"pb( + strategy_session < + strategy: P2P_STAR + role: DISCOVERER + discovery_phase < + medium: BLUETOOTH + discovery_metadata < + supports_extended_ble_advertisements: true + connected_ap_frequency: 1 + supports_nfc_technology: false + > + > + >)pb"))); + + // LogStartSession + CountDownLatch new_start_client_session_done_latch(1); + event_logger.SetStartClientSessionDoneLatchPtr( + &new_start_client_session_done_latch); + analytics_recorder.LogStartSession(); + ASSERT_TRUE( + new_start_client_session_done_latch.Await(kDefaultTimeout).result()); + + // LogSession again + // - if the current_strategy_session_ and current_discovery_phase_ are not + // reset, the same strategy_session with two same discovery_phase will be + // logged. + CountDownLatch new_client_session_done_latch(1); + event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); + analytics_recorder.LogSession(); + ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); + + EXPECT_THAT(event_logger.GetLoggedClientSession(), + Not(Partially(EqualsProto(R"pb( + strategy_session < + strategy: P2P_STAR + role: DISCOVERER + discovery_phase < + medium: BLUETOOTH + discovery_metadata < + supports_extended_ble_advertisements: true + connected_ap_frequency: 1 + supports_nfc_technology: false + > + > + discovery_phase < + medium: BLUETOOTH + discovery_metadata < + supports_extended_ble_advertisements: true + connected_ap_frequency: 1 + supports_nfc_technology: false + > + > + >)pb")))); +} + } // namespace } // namespace analytics } // namespace nearby diff --git a/connections/implementation/client_proxy.cc b/connections/implementation/client_proxy.cc index 632dbb26..47bac9ef 100644 --- a/connections/implementation/client_proxy.cc +++ b/connections/implementation/client_proxy.cc @@ -17,10 +17,12 @@ #include #include #include +#include #include #include #include +#include "internal/analytics/event_logger.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/strings/escaping.h" @@ -104,7 +106,6 @@ void ClientProxy::Reset() { StoppedDiscovery(); RemoveAllEndpoints(); ExitHighVisibilityMode(); - analytics_recorder_->LogSession(); } void ClientProxy::StartedAdvertising( @@ -143,7 +144,7 @@ void ClientProxy::StoppedAdvertising() { analytics_recorder_->OnStopAdvertising(); } // advertising_options_ is purposefully not cleared here. - ResetLocalEndpointIdIfNeeded(); + OnSessionComplete(); ExitHighVisibilityMode(); } @@ -182,7 +183,7 @@ void ClientProxy::StoppedDiscovery() { analytics_recorder_->OnStopDiscovery(); } // discovery_options_ is purposefully not cleared here. - ResetLocalEndpointIdIfNeeded(); + OnSessionComplete(); } bool ClientProxy::IsDiscoveringServiceId(const std::string& service_id) const { @@ -357,7 +358,7 @@ void ClientProxy::OnDisconnected(const std::string& endpoint_id, bool notify) { item->connection_listener.disconnected_cb({endpoint_id}); } connections_.erase(endpoint_id); - ResetLocalEndpointIdIfNeeded(); + OnSessionComplete(); } CancelEndpoint(endpoint_id); @@ -657,13 +658,16 @@ void ClientProxy::RemoveAllEndpoints() { // just remove without notifying. connections_.clear(); cancellation_flags_.clear(); - local_endpoint_id_.clear(); + OnSessionComplete(); } -void ClientProxy::ResetLocalEndpointIdIfNeeded() { +void ClientProxy::OnSessionComplete() { MutexLock lock(&mutex_); if (connections_.empty() && !IsAdvertising() && !IsDiscovering()) { local_endpoint_id_.clear(); + + analytics_recorder_->LogSession(); + analytics_recorder_->LogStartSession(); } } diff --git a/connections/implementation/client_proxy.h b/connections/implementation/client_proxy.h index b27fdf51..5a20592f 100644 --- a/connections/implementation/client_proxy.h +++ b/connections/implementation/client_proxy.h @@ -236,7 +236,7 @@ class ClientProxy final { }; void RemoveAllEndpoints(); - void ResetLocalEndpointIdIfNeeded(); + void OnSessionComplete(); bool ConnectionStatusesContains(const std::string& endpoint_id, Connection::Status status_to_match) const; void AppendConnectionStatus(const std::string& endpoint_id, diff --git a/connections/implementation/client_proxy_test.cc b/connections/implementation/client_proxy_test.cc index 14cd04ba..8107e12e 100644 --- a/connections/implementation/client_proxy_test.cc +++ b/connections/implementation/client_proxy_test.cc @@ -27,6 +27,7 @@ #include "absl/types/span.h" #include "connections/listeners.h" #include "connections/strategy.h" +#include "internal/analytics/event_logger.h" #include "internal/platform/byte_array.h" #include "internal/platform/feature_flags.h" #include "internal/platform/medium_environment.h" @@ -48,6 +49,13 @@ constexpr FeatureFlags::Flags kTestCases[] = { }, }; +class FakeEventLogger : public ::nearby::analytics::EventLogger { + public: + explicit FakeEventLogger() = default; + + void Log(const ::google::protobuf::MessageLite& message) override {} +}; + class ClientProxyTest : public ::testing::TestWithParam { protected: struct MockDiscoveryListener { @@ -107,7 +115,23 @@ class ClientProxyTest : public ::testing::TestWithParam { return endpoint; } - void StopAdvertising(ClientProxy* client) { client->StoppedAdvertising(); } + void StopAdvertising(ClientProxy* client) { + client->StoppedAdvertising(); + EXPECT_FALSE(client->IsAdvertising()); + } + + void OnAdvertisingConnectionInitiated(ClientProxy* client, + const Endpoint& endpoint) { + EXPECT_CALL(mock_advertising_connection_.initiated_cb, Call).Times(1); + const std::string auth_token{"auth_token"}; + const ByteArray raw_auth_token{auth_token}; + const std::string connection_token{"conntokn"}; + discovery_connection_info_.remote_endpoint_info = endpoint.info; + client->OnConnectionInitiated( + endpoint.id, discovery_connection_info_, connection_options_, + advertising_connection_listener_, connection_token); + EXPECT_TRUE(client->HasPendingConnectionToEndpoint(endpoint.id)); + } Endpoint StartDiscovery(ClientProxy* client, DiscoveryListener listener) { Endpoint endpoint{ @@ -119,6 +143,11 @@ class ClientProxyTest : public ::testing::TestWithParam { return endpoint; } + void StopDiscovery(ClientProxy* client) { + client->StoppedDiscovery(); + EXPECT_FALSE(client->IsDiscovering()); + } + void OnDiscoveryEndpointFound(ClientProxy* client, const Endpoint& endpoint) { EXPECT_CALL(mock_discovery_.endpoint_found_cb, Call).Times(1); client->OnEndpointFound(service_id_, endpoint.id, endpoint.info, medium_); @@ -214,6 +243,8 @@ class ClientProxyTest : public ::testing::TestWithParam { client->OnPayloadProgress(endpoint.id, {}); } + MockConnectionListener mock_advertising_connection_; + MockDiscoveryListener mock_discovery_; MockConnectionListener mock_discovery_connection_; MockPayloadListener mock_discovery_payload_; @@ -224,8 +255,10 @@ class ClientProxyTest : public ::testing::TestWithParam { }; Strategy strategy_{Strategy::kP2pPointToPoint}; const std::string service_id_{"service"}; - ClientProxy client1_; - ClientProxy client2_; + FakeEventLogger event_logger1_; + FakeEventLogger event_logger2_; + ClientProxy client1_{&event_logger1_}; + ClientProxy client2_{&event_logger2_}; std::string auth_token_ = "auth_token"; ByteArray raw_auth_token_ = ByteArray(auth_token_); ByteArray payload_bytes_{"bytes"}; @@ -234,7 +267,14 @@ class ClientProxyTest : public ::testing::TestWithParam { .raw_authentication_token = raw_auth_token_, .is_incoming_connection = true, }; - ConnectionListener advertising_connection_listener_; + ConnectionListener advertising_connection_listener_{ + .initiated_cb = mock_advertising_connection_.initiated_cb.AsStdFunction(), + }; + ConnectionResponseInfo discovery_connection_info_{ + .authentication_token = auth_token_, + .raw_authentication_token = raw_auth_token_, + .is_incoming_connection = false, + }; ConnectionListener discovery_connection_listener_{ .initiated_cb = mock_discovery_connection_.initiated_cb.AsStdFunction(), .accepted_cb = mock_discovery_connection_.accepted_cb.AsStdFunction(), @@ -686,6 +726,177 @@ TEST_F(ClientProxyTest, EndpointIdRotateWhenLowVizAdvertisementWithLowPower) { EXPECT_NE(advertising_endpoint_1.id, advertising_endpoint_2.id); } +TEST_F(ClientProxyTest, NotLogSessionForStoppedAdvertisingWithConnection) { + Endpoint advertising_endpoint = + StartAdvertising(&client1_, advertising_connection_listener_); + OnAdvertisingConnectionInitiated(&client1_, advertising_endpoint); + + StartDiscovery(&client2_, discovery_listener_); + OnDiscoveryEndpointFound(&client2_, advertising_endpoint); + OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint); + + // Before + EXPECT_TRUE(client1_.HasPendingConnectionToEndpoint( + advertising_endpoint.id)); // Connections are available + EXPECT_FALSE(client1_.IsDiscovering()); // No Discovery + EXPECT_TRUE(client1_.IsAdvertising()); // Advertising + EXPECT_FALSE(client1_.GetAnalyticsRecorder().IsSessionLogged()); + + // After + StopAdvertising(&client1_); // No Advertising + EXPECT_FALSE(client1_.GetAnalyticsRecorder().IsSessionLogged()); +} + +TEST_F(ClientProxyTest, + LogSessionForStoppedAdvertisingWhenNoConnectionsAndNoDiscovering) { + Endpoint advertising_endpoint = + StartAdvertising(&client1_, advertising_connection_listener_); + + // Before + EXPECT_FALSE(client1_.HasPendingConnectionToEndpoint( + advertising_endpoint.id)); // No Connections + EXPECT_FALSE(client1_.IsDiscovering()); // No Discovery + EXPECT_TRUE(client1_.IsAdvertising()); // Advertising + EXPECT_FALSE(client1_.GetAnalyticsRecorder().IsSessionLogged()); + + // After + StopAdvertising(&client1_); + EXPECT_TRUE(client1_.GetAnalyticsRecorder().IsSessionLogged()); +} + +TEST_F(ClientProxyTest, NotLogSessionForStoppedDiscoveryWithConnection) { + Endpoint advertising_endpoint = + StartAdvertising(&client1_, advertising_connection_listener_); + + StartDiscovery(&client2_, discovery_listener_); + OnDiscoveryEndpointFound(&client2_, advertising_endpoint); + + // Before + OnDiscoveryConnectionInitiated( + &client2_, advertising_endpoint); // Connections are available + EXPECT_FALSE(client2_.IsAdvertising()); // No Advertising + EXPECT_TRUE(client2_.IsDiscovering()); // Discovering + EXPECT_FALSE(client2_.GetAnalyticsRecorder().IsSessionLogged()); + + // After + StopDiscovery(&client2_); + EXPECT_FALSE(client2_.GetAnalyticsRecorder().IsSessionLogged()); +} + +TEST_F(ClientProxyTest, + NotLogSessionForStoppedDiscoveryWithoutConnectionsAndAdvertising) { + Endpoint advertising_endpoint = + StartAdvertising(&client1_, advertising_connection_listener_); + + StartDiscovery(&client2_, discovery_listener_); + + // Before + EXPECT_FALSE(client2_.IsAdvertising()); // No Advertising + EXPECT_TRUE(client2_.IsDiscovering()); // Discoverying + EXPECT_FALSE(client2_.HasPendingConnectionToEndpoint( + advertising_endpoint.id)); // No Connections + + // After + StopDiscovery(&client2_); + EXPECT_TRUE(client2_.GetAnalyticsRecorder().IsSessionLogged()); +} + +TEST_F(ClientProxyTest, LogSessionOnDisconnectedWithOneConnection) { + Endpoint advertising_endpoint = + StartAdvertising(&client1_, advertising_connection_listener_); + StartDiscovery(&client2_, discovery_listener_); + OnDiscoveryEndpointFound(&client2_, advertising_endpoint); + OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint); + + // Before + EXPECT_FALSE(client2_.IsAdvertising()); // No Advertising + StopDiscovery(&client2_); // No Discovery + EXPECT_TRUE(client2_.HasPendingConnectionToEndpoint( + advertising_endpoint.id)); // One Connection + + // After + OnDiscoveryConnectionDisconnected(&client2_, advertising_endpoint); + EXPECT_TRUE(client2_.GetAnalyticsRecorder().IsSessionLogged()); +} + +TEST_F(ClientProxyTest, + NotLogSessionOnDisconnectedWithoutConnectionsDiscoveringAdvertising) { + Endpoint advertising_endpoint = + StartAdvertising(&client1_, advertising_connection_listener_); + + // Before + EXPECT_FALSE(client2_.IsAdvertising()); // No Advertising + EXPECT_FALSE(client2_.IsDiscovering()); // No Discovery + EXPECT_FALSE(client2_.HasPendingConnectionToEndpoint( + advertising_endpoint.id)); // No Connections + + // After + client2_.OnDisconnected(advertising_endpoint.id, /*notify=*/false); + EXPECT_FALSE(client2_.GetAnalyticsRecorder().IsSessionLogged()); +} + +TEST_F(ClientProxyTest, NotLogSessionOnDisconnectedWhenMoreThanOneConnection) { + ClientProxy client3; + Endpoint advertising_endpoint_1 = + StartAdvertising(&client1_, advertising_connection_listener_); + Endpoint advertising_endpoint_2 = + StartAdvertising(&client2_, advertising_connection_listener_); + StartDiscovery(&client3, discovery_listener_); + + OnDiscoveryEndpointFound(&client3, advertising_endpoint_1); + OnDiscoveryConnectionInitiated(&client3, advertising_endpoint_1); + OnDiscoveryEndpointFound(&client3, advertising_endpoint_2); + OnDiscoveryConnectionInitiated(&client3, advertising_endpoint_2); + + // Before + // - More than one Connection + EXPECT_TRUE( + client3.HasPendingConnectionToEndpoint(advertising_endpoint_1.id)); + EXPECT_TRUE( + client3.HasPendingConnectionToEndpoint(advertising_endpoint_2.id)); + EXPECT_FALSE(client3.IsAdvertising()); // No Advertising + StopDiscovery(&client3); // No Discovery + + // After + client2_.OnDisconnected(advertising_endpoint_1.id, /*notify=*/false); + EXPECT_FALSE(client2_.GetAnalyticsRecorder().IsSessionLogged()); +} + +TEST_F(ClientProxyTest, + NotLogSessionOnDisconnectedForDiscoveringWithOnlyOneConnection) { + Endpoint advertising_endpoint = + StartAdvertising(&client1_, advertising_connection_listener_); + StartDiscovery(&client2_, discovery_listener_); + OnDiscoveryEndpointFound(&client2_, advertising_endpoint); + OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint); + + // Before + EXPECT_FALSE(client2_.IsAdvertising()); // No Advertising + EXPECT_TRUE(client2_.IsDiscovering()); // Discovering + EXPECT_TRUE(client2_.HasPendingConnectionToEndpoint( + advertising_endpoint.id)); // One Connection + + // After + OnDiscoveryConnectionDisconnected(&client2_, advertising_endpoint); + EXPECT_FALSE(client2_.GetAnalyticsRecorder().IsSessionLogged()); +} + +TEST_F(ClientProxyTest, LogSessionForResetClientProxy) { + Endpoint advertising_endpoint = + StartAdvertising(&client1_, advertising_connection_listener_); + StartDiscovery(&client2_, discovery_listener_); + OnDiscoveryEndpointFound(&client2_, advertising_endpoint); + OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint); + + EXPECT_FALSE(client1_.GetAnalyticsRecorder().IsSessionLogged()); + client1_.Reset(); + EXPECT_TRUE(client1_.GetAnalyticsRecorder().IsSessionLogged()); + + EXPECT_FALSE(client2_.GetAnalyticsRecorder().IsSessionLogged()); + client2_.Reset(); + EXPECT_TRUE(client2_.GetAnalyticsRecorder().IsSessionLogged()); +} + } // namespace } // namespace connections } // namespace nearby