| /* | 
 |  * Copyright (C) 2018 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 "src/perfetto_cmd/rate_limiter.h" | 
 |  | 
 | #include <stdio.h> | 
 |  | 
 | #include "perfetto/ext/base/file_utils.h" | 
 | #include "perfetto/ext/base/scoped_file.h" | 
 | #include "perfetto/ext/base/temp_file.h" | 
 | #include "perfetto/ext/base/utils.h" | 
 |  | 
 | #include "test/gtest_and_gmock.h" | 
 |  | 
 | using testing::_; | 
 | using testing::Contains; | 
 | using testing::Invoke; | 
 | using testing::NiceMock; | 
 | using testing::Return; | 
 | using testing::StrictMock; | 
 |  | 
 | namespace perfetto { | 
 |  | 
 | namespace { | 
 |  | 
 | class MockRateLimiter : public RateLimiter { | 
 |  public: | 
 |   MockRateLimiter() : dir_(base::TempDir::Create()) { | 
 |     ON_CALL(*this, LoadState(_)) | 
 |         .WillByDefault(Invoke(this, &MockRateLimiter::LoadStateConcrete)); | 
 |     ON_CALL(*this, SaveState(_)) | 
 |         .WillByDefault(Invoke(this, &MockRateLimiter::SaveStateConcrete)); | 
 |   } | 
 |  | 
 |   virtual std::string GetStateFilePath() const { | 
 |     return std::string(dir_.path()) + "/.guardraildata"; | 
 |   } | 
 |  | 
 |   virtual ~MockRateLimiter() override { | 
 |     if (StateFileExists()) | 
 |       remove(GetStateFilePath().c_str()); | 
 |   } | 
 |  | 
 |   bool LoadStateConcrete(gen::PerfettoCmdState* state) { | 
 |     return RateLimiter::LoadState(state); | 
 |   } | 
 |  | 
 |   bool SaveStateConcrete(const gen::PerfettoCmdState& state) { | 
 |     return RateLimiter::SaveState(state); | 
 |   } | 
 |  | 
 |   MOCK_METHOD(bool, LoadState, (gen::PerfettoCmdState*), (override)); | 
 |   MOCK_METHOD(bool, SaveState, (const gen::PerfettoCmdState&), (override)); | 
 |  | 
 |  private: | 
 |   base::TempDir dir_; | 
 | }; | 
 |  | 
 | void WriteGarbageToFile(const std::string& path) { | 
 |   base::ScopedFile fd(base::OpenFile(path, O_WRONLY | O_CREAT, 0600)); | 
 |   constexpr char data[] = "Some random bytes."; | 
 |   if (base::WriteAll(fd.get(), data, sizeof(data)) != sizeof(data)) | 
 |     ADD_FAILURE() << "Could not write garbage"; | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, RoundTripState) { | 
 |   NiceMock<MockRateLimiter> limiter; | 
 |  | 
 |   gen::PerfettoCmdState input{}; | 
 |   gen::PerfettoCmdState output{}; | 
 |  | 
 |   input.set_total_bytes_uploaded(42); | 
 |   ASSERT_TRUE(limiter.SaveState(input)); | 
 |   ASSERT_TRUE(limiter.LoadState(&output)); | 
 |   ASSERT_EQ(output.total_bytes_uploaded(), 42u); | 
 |   ASSERT_EQ(output.session_state_size(), 0); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, FileIsSensiblyTruncated) { | 
 |   NiceMock<MockRateLimiter> limiter; | 
 |  | 
 |   gen::PerfettoCmdState input{}; | 
 |   gen::PerfettoCmdState output{}; | 
 |  | 
 |   input.set_total_bytes_uploaded(42); | 
 |   input.set_first_trace_timestamp(1); | 
 |   input.set_last_trace_timestamp(2); | 
 |  | 
 |   for (size_t i = 0; i < 100; ++i) { | 
 |     auto* session = input.add_session_state(); | 
 |     session->set_session_name("session_" + std::to_string(i)); | 
 |     session->set_total_bytes_uploaded(i * 100); | 
 |     session->set_last_trace_timestamp(i); | 
 |   } | 
 |  | 
 |   ASSERT_TRUE(limiter.SaveState(input)); | 
 |   ASSERT_TRUE(limiter.LoadState(&output)); | 
 |  | 
 |   ASSERT_EQ(output.total_bytes_uploaded(), 42u); | 
 |   ASSERT_EQ(output.first_trace_timestamp(), 1u); | 
 |   ASSERT_EQ(output.last_trace_timestamp(), 2u); | 
 |   ASSERT_LE(output.session_state_size(), 50); | 
 |   ASSERT_GE(output.session_state_size(), 5); | 
 |  | 
 |   { | 
 |     gen::PerfettoCmdState::PerSessionState session; | 
 |     session.set_session_name("session_99"); | 
 |     session.set_total_bytes_uploaded(99 * 100); | 
 |     session.set_last_trace_timestamp(99); | 
 |     ASSERT_THAT(output.session_state(), Contains(session)); | 
 |   } | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, LoadFromEmpty) { | 
 |   NiceMock<MockRateLimiter> limiter; | 
 |  | 
 |   gen::PerfettoCmdState input{}; | 
 |   input.set_total_bytes_uploaded(0); | 
 |   input.set_last_trace_timestamp(0); | 
 |   input.set_first_trace_timestamp(0); | 
 |   gen::PerfettoCmdState output{}; | 
 |  | 
 |   ASSERT_TRUE(limiter.SaveState(input)); | 
 |   ASSERT_TRUE(limiter.LoadState(&output)); | 
 |   ASSERT_EQ(output.total_bytes_uploaded(), 0u); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, LoadFromNoFileFails) { | 
 |   NiceMock<MockRateLimiter> limiter; | 
 |   gen::PerfettoCmdState output{}; | 
 |   ASSERT_FALSE(limiter.LoadState(&output)); | 
 |   ASSERT_EQ(output.total_bytes_uploaded(), 0u); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, LoadFromGarbageFails) { | 
 |   NiceMock<MockRateLimiter> limiter; | 
 |  | 
 |   WriteGarbageToFile(limiter.GetStateFilePath().c_str()); | 
 |  | 
 |   gen::PerfettoCmdState output{}; | 
 |   ASSERT_FALSE(limiter.LoadState(&output)); | 
 |   ASSERT_EQ(output.total_bytes_uploaded(), 0u); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, NotDropBox) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |  | 
 |   ASSERT_EQ(limiter.ShouldTrace({}), RateLimiter::kOkToTrace); | 
 |   ASSERT_TRUE(limiter.OnTraceDone({}, true, 10000)); | 
 |   ASSERT_FALSE(limiter.StateFileExists()); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, NotDropBox_FailedToTrace) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |  | 
 |   ASSERT_FALSE(limiter.OnTraceDone({}, false, 0)); | 
 |   ASSERT_FALSE(limiter.StateFileExists()); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_IgnoreGuardrails) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   args.allow_user_build_tracing = true; | 
 |   args.is_uploading = true; | 
 |   args.ignore_guardrails = true; | 
 |   args.current_time = base::TimeSeconds(41); | 
 |  | 
 |   EXPECT_CALL(limiter, SaveState(_)); | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kOkToTrace); | 
 |  | 
 |   EXPECT_CALL(limiter, SaveState(_)); | 
 |   ASSERT_TRUE(limiter.OnTraceDone(args, true, 42u)); | 
 |  | 
 |   gen::PerfettoCmdState output{}; | 
 |   ASSERT_TRUE(limiter.LoadStateConcrete(&output)); | 
 |   ASSERT_EQ(output.first_trace_timestamp(), 41u); | 
 |   ASSERT_EQ(output.last_trace_timestamp(), 41u); | 
 |   ASSERT_EQ(output.total_bytes_uploaded(), 42u); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_EmptyState) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   args.allow_user_build_tracing = true; | 
 |   args.is_uploading = true; | 
 |   args.current_time = base::TimeSeconds(10000); | 
 |  | 
 |   EXPECT_CALL(limiter, SaveState(_)); | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kOkToTrace); | 
 |  | 
 |   EXPECT_CALL(limiter, SaveState(_)); | 
 |   ASSERT_TRUE(limiter.OnTraceDone(args, true, 1024 * 1024)); | 
 |  | 
 |   gen::PerfettoCmdState output{}; | 
 |   ASSERT_TRUE(limiter.LoadStateConcrete(&output)); | 
 |   EXPECT_EQ(output.total_bytes_uploaded(), 1024u * 1024u); | 
 |   EXPECT_EQ(output.first_trace_timestamp(), 10000u); | 
 |   EXPECT_EQ(output.last_trace_timestamp(), 10000u); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_NormalUpload) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   gen::PerfettoCmdState input{}; | 
 |   input.set_first_trace_timestamp(10000); | 
 |   input.set_last_trace_timestamp(10000 + 60 * 10); | 
 |   input.set_total_bytes_uploaded(1024 * 1024 * 2); | 
 |   ASSERT_TRUE(limiter.SaveStateConcrete(input)); | 
 |  | 
 |   args.allow_user_build_tracing = true; | 
 |   args.is_uploading = true; | 
 |   args.current_time = base::TimeSeconds(input.last_trace_timestamp() + 60 * 10); | 
 |  | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kOkToTrace); | 
 |  | 
 |   EXPECT_CALL(limiter, SaveState(_)); | 
 |   ASSERT_TRUE(limiter.OnTraceDone(args, true, 1024 * 1024)); | 
 |  | 
 |   gen::PerfettoCmdState output{}; | 
 |   ASSERT_TRUE(limiter.LoadStateConcrete(&output)); | 
 |   EXPECT_EQ(output.total_bytes_uploaded(), 1024u * 1024u * 3); | 
 |   EXPECT_EQ(output.first_trace_timestamp(), input.first_trace_timestamp()); | 
 |   EXPECT_EQ(output.last_trace_timestamp(), | 
 |             static_cast<uint64_t>(args.current_time.count())); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_NormalUploadWithSessionName) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   gen::PerfettoCmdState input{}; | 
 |   input.set_first_trace_timestamp(10000); | 
 |   input.set_last_trace_timestamp(10000 + 60 * 10); | 
 |   input.set_total_bytes_uploaded(1024 * 1024 * 2); | 
 |   ASSERT_TRUE(limiter.SaveStateConcrete(input)); | 
 |  | 
 |   args.allow_user_build_tracing = true; | 
 |   args.is_uploading = true; | 
 |   args.unique_session_name = "foo"; | 
 |   args.current_time = base::TimeSeconds(input.last_trace_timestamp() + 60 * 10); | 
 |  | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kOkToTrace); | 
 |  | 
 |   EXPECT_CALL(limiter, SaveState(_)); | 
 |   ASSERT_TRUE(limiter.OnTraceDone(args, true, 1024 * 1024)); | 
 |  | 
 |   gen::PerfettoCmdState output{}; | 
 |   ASSERT_TRUE(limiter.LoadStateConcrete(&output)); | 
 |   EXPECT_EQ(output.total_bytes_uploaded(), 1024u * 1024u * 2); | 
 |   EXPECT_EQ(output.first_trace_timestamp(), input.first_trace_timestamp()); | 
 |   EXPECT_EQ(output.last_trace_timestamp(), | 
 |             static_cast<uint64_t>(args.current_time.count())); | 
 |   ASSERT_GE(output.session_state_size(), 1); | 
 |  | 
 |   { | 
 |     gen::PerfettoCmdState::PerSessionState session; | 
 |     session.set_session_name("foo"); | 
 |     session.set_total_bytes_uploaded(1024 * 1024); | 
 |     session.set_last_trace_timestamp( | 
 |         static_cast<uint64_t>(args.current_time.count())); | 
 |     ASSERT_THAT(output.session_state(), Contains(session)); | 
 |   } | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_FailedToLoadState) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   args.allow_user_build_tracing = true; | 
 |   args.is_uploading = true; | 
 |  | 
 |   WriteGarbageToFile(limiter.GetStateFilePath().c_str()); | 
 |  | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   EXPECT_CALL(limiter, SaveState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kInvalidState); | 
 |  | 
 |   gen::PerfettoCmdState output{}; | 
 |   ASSERT_TRUE(limiter.LoadStateConcrete(&output)); | 
 |   EXPECT_EQ(output.total_bytes_uploaded(), 0u); | 
 |   EXPECT_EQ(output.first_trace_timestamp(), 0u); | 
 |   EXPECT_EQ(output.last_trace_timestamp(), 0u); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_NoTimeTravel) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   gen::PerfettoCmdState input{}; | 
 |   input.set_first_trace_timestamp(100); | 
 |   input.set_last_trace_timestamp(100); | 
 |   ASSERT_TRUE(limiter.SaveStateConcrete(input)); | 
 |  | 
 |   args.allow_user_build_tracing = true; | 
 |   args.is_uploading = true; | 
 |   args.current_time = base::TimeSeconds(99); | 
 |  | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   EXPECT_CALL(limiter, SaveState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kInvalidState); | 
 |  | 
 |   gen::PerfettoCmdState output{}; | 
 |   ASSERT_TRUE(limiter.LoadStateConcrete(&output)); | 
 |   EXPECT_EQ(output.total_bytes_uploaded(), 0u); | 
 |   EXPECT_EQ(output.first_trace_timestamp(), 0u); | 
 |   EXPECT_EQ(output.last_trace_timestamp(), 0u); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_TooMuch_OtherSession) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   gen::PerfettoCmdState input{}; | 
 |   auto* session = input.add_session_state(); | 
 |   session->set_session_name("foo"); | 
 |   session->set_total_bytes_uploaded(100 * 1024 * 1024); | 
 |  | 
 |   ASSERT_TRUE(limiter.SaveStateConcrete(input)); | 
 |  | 
 |   args.is_user_build = true; | 
 |   args.allow_user_build_tracing = true; | 
 |   args.is_uploading = true; | 
 |   args.unique_session_name = "bar"; | 
 |   args.current_time = base::TimeSeconds(60 * 60); | 
 |  | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kOkToTrace); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_TooMuch_Session) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   gen::PerfettoCmdState input{}; | 
 |   auto* session = input.add_session_state(); | 
 |   session->set_session_name("foo"); | 
 |   session->set_total_bytes_uploaded(100 * 1024 * 1024); | 
 |  | 
 |   ASSERT_TRUE(limiter.SaveStateConcrete(input)); | 
 |  | 
 |   args.is_user_build = true; | 
 |   args.allow_user_build_tracing = true; | 
 |   args.is_uploading = true; | 
 |   args.unique_session_name = "foo"; | 
 |   args.current_time = base::TimeSeconds(60 * 60); | 
 |  | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kHitUploadLimit); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_TooMuch_User) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   gen::PerfettoCmdState input{}; | 
 |   input.set_total_bytes_uploaded(10 * 1024 * 1024 + 1); | 
 |   ASSERT_TRUE(limiter.SaveStateConcrete(input)); | 
 |  | 
 |   args.is_user_build = true; | 
 |   args.allow_user_build_tracing = true; | 
 |   args.is_uploading = true; | 
 |   args.current_time = base::TimeSeconds(60 * 60); | 
 |  | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kHitUploadLimit); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_TooMuch_Override) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   gen::PerfettoCmdState input{}; | 
 |   auto* session = input.add_session_state(); | 
 |   session->set_session_name("foo"); | 
 |   session->set_total_bytes_uploaded(10 * 1024 * 1024 + 1); | 
 |   ASSERT_TRUE(limiter.SaveStateConcrete(input)); | 
 |  | 
 |   args.allow_user_build_tracing = true; | 
 |   args.is_uploading = true; | 
 |   args.current_time = base::TimeSeconds(60 * 60); | 
 |   args.max_upload_bytes_override = 10 * 1024 * 1024 + 2; | 
 |   args.unique_session_name = "foo"; | 
 |  | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kOkToTrace); | 
 | } | 
 |  | 
 | // Override doesn't apply to traces without session name. | 
 | TEST(RateLimiterTest, DropBox_OverrideOnEmptySesssionName) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   gen::PerfettoCmdState input{}; | 
 |   input.set_total_bytes_uploaded(10 * 1024 * 1024 + 1); | 
 |   ASSERT_TRUE(limiter.SaveStateConcrete(input)); | 
 |  | 
 |   args.allow_user_build_tracing = true; | 
 |   args.is_uploading = true; | 
 |   args.current_time = base::TimeSeconds(60 * 60); | 
 |   args.max_upload_bytes_override = 10 * 1024 * 1024 + 2; | 
 |  | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kHitUploadLimit); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_TooMuchWasUploaded) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   gen::PerfettoCmdState input{}; | 
 |   input.set_first_trace_timestamp(1); | 
 |   input.set_last_trace_timestamp(1); | 
 |   input.set_total_bytes_uploaded(10 * 1024 * 1024 + 1); | 
 |   ASSERT_TRUE(limiter.SaveStateConcrete(input)); | 
 |  | 
 |   args.is_uploading = true; | 
 |   args.current_time = base::TimeSeconds(60 * 60 * 24 + 2); | 
 |  | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kOkToTrace); | 
 |  | 
 |   EXPECT_CALL(limiter, SaveState(_)); | 
 |   ASSERT_TRUE(limiter.OnTraceDone(args, true, 1024 * 1024)); | 
 |  | 
 |   gen::PerfettoCmdState output{}; | 
 |   ASSERT_TRUE(limiter.LoadStateConcrete(&output)); | 
 |   EXPECT_EQ(output.total_bytes_uploaded(), 1024u * 1024u); | 
 |   EXPECT_EQ(output.first_trace_timestamp(), | 
 |             static_cast<uint64_t>(args.current_time.count())); | 
 |   EXPECT_EQ(output.last_trace_timestamp(), | 
 |             static_cast<uint64_t>(args.current_time.count())); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_FailedToUpload) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   args.is_uploading = true; | 
 |   args.current_time = base::TimeSeconds(10000); | 
 |  | 
 |   EXPECT_CALL(limiter, SaveState(_)); | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kOkToTrace); | 
 |   ASSERT_FALSE(limiter.OnTraceDone(args, false, 1024 * 1024)); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_FailedToSave) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   args.is_uploading = true; | 
 |   args.current_time = base::TimeSeconds(10000); | 
 |  | 
 |   EXPECT_CALL(limiter, SaveState(_)); | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kOkToTrace); | 
 |  | 
 |   EXPECT_CALL(limiter, SaveState(_)).WillOnce(Return(false)); | 
 |   ASSERT_FALSE(limiter.OnTraceDone(args, true, 1024 * 1024)); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_CantTraceOnUser) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   args.is_user_build = true; | 
 |   args.allow_user_build_tracing = false; | 
 |   args.is_uploading = true; | 
 |   args.current_time = base::TimeSeconds(10000); | 
 |  | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kNotAllowedOnUserBuild); | 
 | } | 
 |  | 
 | TEST(RateLimiterTest, DropBox_CanTraceOnUser) { | 
 |   StrictMock<MockRateLimiter> limiter; | 
 |   RateLimiter::Args args; | 
 |  | 
 |   args.is_user_build = false; | 
 |   args.allow_user_build_tracing = false; | 
 |   args.is_uploading = true; | 
 |   args.current_time = base::TimeSeconds(10000); | 
 |  | 
 |   EXPECT_CALL(limiter, SaveState(_)); | 
 |   EXPECT_CALL(limiter, LoadState(_)); | 
 |   ASSERT_EQ(limiter.ShouldTrace(args), RateLimiter::kOkToTrace); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | }  // namespace perfetto |