| // Copyright 2022 The Fuchsia 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 <memory> |
| |
| #include <gtest/gtest.h> |
| |
| #include "fuchsia/examples/diagnostics/cpp/fidl.h" |
| #include "lib/async-loop/cpp/loop.h" |
| #include "lib/async-loop/loop.h" |
| #include "lib/async/dispatcher.h" |
| #include "lib/fidl/cpp/interface_ptr.h" |
| #include "profile.h" |
| #include "src/lib/files/path.h" |
| #include "src/lib/testing/loop_fixture/real_loop_fixture.h" |
| |
| class ProfileTests : public gtest::RealLoopFixture { |
| public: |
| ProfileTests() : loop_(&kAsyncLoopConfigNeverAttachToThread) { loop_.StartThread(); } |
| |
| ~ProfileTests() override { loop_.Shutdown(); } |
| |
| std::shared_ptr<Profile> CreateNewProfile(std::string key) { |
| auto p = std::make_shared<Profile>(loop_.dispatcher(), files::JoinPath("/data", key)); |
| // profile will run on its own thread, so save it so that it doesn't die before loop variable. |
| profiles_.push_back(p); |
| return p; |
| } |
| |
| void StopServingProfiles() { |
| loop_.RunUntilIdle(); |
| loop_.Quit(); |
| profiles_.clear(); |
| loop_.JoinThreads(); |
| loop_.ResetQuit(); |
| loop_.StartThread(); |
| } |
| |
| fidl::InterfacePtr<fuchsia::examples::diagnostics::Profile> GetHandler( |
| const std::shared_ptr<Profile>& profile) { |
| fidl::InterfacePtr<fuchsia::examples::diagnostics::Profile> ptr; |
| profile->AddBinding(ptr.NewRequest(dispatcher())); |
| return ptr; |
| } |
| |
| fidl::InterfacePtr<fuchsia::examples::diagnostics::ProfileReader> GetReaderHandler( |
| const std::shared_ptr<Profile>& profile) { |
| fidl::InterfacePtr<fuchsia::examples::diagnostics::ProfileReader> ptr; |
| profile->AddReaderBinding(ptr.NewRequest(dispatcher())); |
| return ptr; |
| } |
| |
| private: |
| std::vector<std::shared_ptr<Profile>> profiles_; |
| async::Loop loop_; |
| }; |
| |
| TEST_F(ProfileTests, Name) { |
| auto profile = CreateNewProfile("Name"); |
| auto client = GetHandler(profile); |
| std::string name = "placeholder"; |
| client->GetName([&](std::string n) { name = std::move(n); }); |
| RunLoopUntil([&]() { return name != "placeholder"; }); |
| EXPECT_EQ(name, ""); |
| |
| std::string set_name = "my_name"; |
| client->SetName(set_name); |
| client->GetName([&](std::string n) { name = std::move(n); }); |
| RunLoopUntil([&]() { return !name.empty(); }); |
| EXPECT_EQ(name, set_name); |
| } |
| |
| TEST_F(ProfileTests, Balance) { |
| auto profile = CreateNewProfile("Balance"); |
| auto client = GetHandler(profile); |
| |
| int64_t balance = -1; |
| int64_t old_balance = balance; |
| client->GetBalance([&](int64_t b) { balance = b; }); |
| RunLoopUntil([&]() { return balance != old_balance; }); |
| EXPECT_EQ(balance, 0); |
| old_balance = balance; |
| |
| // Add balance |
| client->AddBalance(4); |
| client->WithdrawBalance(2, [](bool status) { ASSERT_TRUE(status); }); |
| client->AddBalance(10); |
| client->WithdrawBalance(13, [](bool status) { |
| // balance cannot go into negative so we should get false. |
| ASSERT_FALSE(status); |
| }); |
| client->GetBalance([&](int64_t b) { balance = b; }); |
| RunLoopUntil([&]() { return balance != old_balance; }); |
| EXPECT_EQ(balance, 12); |
| |
| // make sure we can withdraw all balance |
| old_balance = balance; |
| client->WithdrawBalance(12, [](bool status) { ASSERT_TRUE(status); }); |
| client->GetBalance([&](int64_t b) { balance = b; }); |
| RunLoopUntil([&]() { return balance != old_balance; }); |
| EXPECT_EQ(balance, 0); |
| } |
| |
| TEST_F(ProfileTests, Persists) { |
| std::string name = ""; |
| int64_t balance = -1; |
| std::string set_name = "my_name"; |
| |
| auto profile = CreateNewProfile("Persists"); |
| auto client = GetHandler(profile); |
| client->SetName(set_name); |
| client->AddBalance(10); |
| client->GetName([&](std::string n) { name = std::move(n); }); |
| client->GetBalance([&](int64_t b) { balance = b; }); |
| RunLoopUntil([&]() { return !name.empty() && balance != -1; }); |
| EXPECT_EQ(name, set_name); |
| EXPECT_EQ(balance, 10); |
| client.Unbind(); |
| StopServingProfiles(); |
| |
| // A profile created with the same key should contain the same information. |
| auto profile2 = CreateNewProfile("Persists"); |
| auto client2 = GetHandler(profile2); |
| name = ""; |
| balance = -1; |
| client2->GetName([&](std::string n) { name = std::move(n); }); |
| client2->GetBalance([&](int64_t b) { balance = b; }); |
| RunLoopUntil([&]() { return !name.empty() && balance != -1; }); |
| EXPECT_EQ(name, set_name); |
| EXPECT_EQ(balance, 10); |
| } |
| |
| // Test that reader can read latest changes to profile. |
| // Disabled because it is flaky. |
| TEST_F(ProfileTests, DISABLED_NameWithReader) { |
| auto profile = CreateNewProfile("NameWithReader"); |
| auto client = GetHandler(profile); |
| auto reader = GetReaderHandler(profile); |
| |
| std::string name = "placeholder"; |
| |
| std::string set_name = "my_name"; |
| |
| client->SetName(set_name); |
| reader->GetName([&](std::string n) { name = std::move(n); }); |
| |
| RunLoopUntil([&]() { return name != "placeholder"; }); |
| // Flakes sometimes, this will flake much more in an integration tests. |
| EXPECT_EQ(name, set_name); |
| } |
| |
| // Test that reader can read latest changes to profile. |
| // Disabled because it is flaky. |
| TEST_F(ProfileTests, DISABLED_BalanceWithReader) { |
| auto profile = CreateNewProfile("BalanceWithReader"); |
| auto client = GetHandler(profile); |
| auto reader = GetReaderHandler(profile); |
| int64_t balance = -1; |
| int64_t old_balance = balance; |
| |
| // Add balance |
| client->AddBalance(4); |
| client->WithdrawBalance(2, [](bool status) { ASSERT_TRUE(status); }); |
| reader->GetBalance([&](int64_t b) { balance = b; }); |
| RunLoopUntil([&]() { return balance != old_balance; }); |
| |
| // flaky test and fails more often. Server has a race condition even though |
| // it is single threaded. |
| EXPECT_EQ(balance, 2); |
| } |