| // Copyright 2016 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 "remoting/client/client_telemetry_logger.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/containers/circular_deque.h" |
| #include "base/memory/ptr_util.h" |
| #include "remoting/protocol/connection_to_host.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| // Returns true if |actual| has all fields that |expected| has and the values |
| // also match. |actual| can have fields that |expected| doesn't have but not the |
| // other way around. |
| static bool Contains(const remoting::ChromotingEvent& actual, |
| const remoting::ChromotingEvent& expected) { |
| auto actual_dict = actual.CopyDictionaryValue(); |
| auto expected_dict = expected.CopyDictionaryValue(); |
| |
| base::DictionaryValue::Iterator expected_it(*expected_dict); |
| |
| while (!expected_it.IsAtEnd()) { |
| const std::string& key = expected_it.key(); |
| const base::Value* out_value = nullptr; |
| |
| if (!actual_dict->Get(key, &out_value) || |
| expected_it.value() != *out_value) { |
| return false; |
| } |
| expected_it.Advance(); |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| namespace remoting { |
| |
| // The fake log writer will pass the test IFF: |
| // 1. The caller logs an entry when the writer expects an entry to be logged. |
| // 2. The caller logs an entry with all key-value pairs found in the expected |
| // entry. |
| // 3. There are no more expected log entries when the writer destructs. |
| class FakeLogWriter : public ChromotingEventLogWriter { |
| public: |
| ~FakeLogWriter() override { |
| EXPECT_TRUE(expected_events_.empty()) << "Sent less logs than expected."; |
| } |
| |
| // Add the event to |expected_events_|. Log() will only succeed IFF the actual |
| // entry has all fields that the expected entry (first entry in |
| // |expected_events|) has and the values also match. The actual entry can have |
| // more fields in addition to those in the expected entry. |
| void AddExpectedEvent(const ChromotingEvent& entry); |
| |
| // ChromotingEventLogWriter overrides. |
| void Log(const ChromotingEvent& entry) override; |
| |
| private: |
| base::circular_deque<ChromotingEvent> expected_events_; |
| std::string auth_token_; |
| }; |
| |
| void FakeLogWriter::AddExpectedEvent(const ChromotingEvent& entry) { |
| expected_events_.push_back(entry); |
| } |
| |
| void FakeLogWriter::Log(const ChromotingEvent& entry) { |
| ASSERT_FALSE(expected_events_.empty()) |
| << "Trying to send more logs than expected"; |
| ASSERT_TRUE(Contains(entry, expected_events_.front())) |
| << "Unexpected log being sent."; |
| expected_events_.pop_front(); |
| } |
| |
| class ClientTelemetryLoggerTest : public testing::Test { |
| public: |
| // testing::Test override. |
| void SetUp() override; |
| |
| protected: |
| std::unique_ptr<FakeLogWriter> log_writer_; |
| std::unique_ptr<ClientTelemetryLogger> logger_; |
| }; |
| |
| void ClientTelemetryLoggerTest::SetUp() { |
| log_writer_ = std::make_unique<FakeLogWriter>(); |
| logger_ = std::make_unique<ClientTelemetryLogger>( |
| log_writer_.get(), ChromotingEvent::Mode::ME2ME, |
| ChromotingEvent::SessionEntryPoint::CONNECT_BUTTON); |
| } |
| |
| TEST_F(ClientTelemetryLoggerTest, LogSessionStateChange) { |
| ChromotingEvent event(ChromotingEvent::Type::SESSION_STATE); |
| event.SetEnum("session_state", ChromotingEvent::SessionState::CONNECTED); |
| event.SetEnum("connection_error", ChromotingEvent::ConnectionError::NONE); |
| log_writer_->AddExpectedEvent(event); |
| logger_->LogSessionStateChange(ChromotingEvent::SessionState::CONNECTED, |
| ChromotingEvent::ConnectionError::NONE); |
| |
| event.SetEnum("session_state", |
| ChromotingEvent::SessionState::CONNECTION_FAILED); |
| event.SetEnum("connection_error", |
| ChromotingEvent::ConnectionError::HOST_OFFLINE); |
| log_writer_->AddExpectedEvent(event); |
| logger_->LogSessionStateChange( |
| ChromotingEvent::SessionState::CONNECTION_FAILED, |
| ChromotingEvent::ConnectionError::HOST_OFFLINE); |
| } |
| |
| TEST_F(ClientTelemetryLoggerTest, LogStatistics) { |
| protocol::PerformanceTracker perf_tracker; |
| log_writer_->AddExpectedEvent( |
| ChromotingEvent(ChromotingEvent::Type::CONNECTION_STATISTICS)); |
| logger_->LogStatistics(perf_tracker); |
| } |
| |
| TEST_F(ClientTelemetryLoggerTest, SessionIdGeneration) { |
| ChromotingEvent any_event; |
| log_writer_->AddExpectedEvent(any_event); |
| log_writer_->AddExpectedEvent(any_event); |
| log_writer_->AddExpectedEvent(any_event); |
| logger_->LogSessionStateChange(ChromotingEvent::SessionState::CONNECTED, |
| ChromotingEvent::ConnectionError::NONE); |
| std::string last_id = logger_->session_id(); |
| |
| logger_->LogSessionStateChange(ChromotingEvent::SessionState::CLOSED, |
| ChromotingEvent::ConnectionError::NONE); |
| EXPECT_TRUE(logger_->session_id().empty()); |
| |
| logger_->LogSessionStateChange(ChromotingEvent::SessionState::CONNECTED, |
| ChromotingEvent::ConnectionError::NONE); |
| EXPECT_NE(last_id, logger_->session_id()); |
| } |
| |
| TEST_F(ClientTelemetryLoggerTest, SessionIdExpiration) { |
| log_writer_->AddExpectedEvent( |
| ChromotingEvent(ChromotingEvent::Type::SESSION_STATE)); |
| log_writer_->AddExpectedEvent( |
| ChromotingEvent(ChromotingEvent::Type::SESSION_ID_OLD)); |
| log_writer_->AddExpectedEvent( |
| ChromotingEvent(ChromotingEvent::Type::SESSION_ID_NEW)); |
| log_writer_->AddExpectedEvent( |
| ChromotingEvent(ChromotingEvent::Type::CONNECTION_STATISTICS)); |
| logger_->LogSessionStateChange(ChromotingEvent::SessionState::CONNECTED, |
| ChromotingEvent::ConnectionError::NONE); |
| std::string last_id = logger_->session_id(); |
| |
| // kMaxSessionIdAgeDays = 1. Fake the generation time to be 2 days ago and |
| // force it to expire. |
| logger_->SetSessionIdGenerationTimeForTest(base::TimeTicks::Now() - |
| base::Days(2)); |
| protocol::PerformanceTracker perf_tracker; |
| logger_->LogStatistics(perf_tracker); |
| EXPECT_NE(last_id, logger_->session_id()); |
| } |
| |
| } // namespace remoting |