| // Copyright 2019 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <dbus/message.h> |
| #include <gmock/gmock.h> |
| #include <grpcpp/impl/call.h> |
| #include <grpcpp/grpcpp.h> |
| #include <gtest/gtest.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <vm_applications/apps.pb.h> |
| #include <vm_protos/proto_bindings/container_host.grpc.pb.h> |
| #include <vm_protos/proto_bindings/container_host.pb.h> |
| |
| #include "vm_tools/cicerone/container_listener_impl.h" |
| #include "vm_tools/cicerone/dbus_message_testing_helper.h" |
| #include "vm_tools/cicerone/service.h" |
| #include "vm_tools/cicerone/service_testing_helper.h" |
| |
| namespace vm_tools { |
| namespace cicerone { |
| |
| namespace { |
| |
| using ::testing::_; |
| using ::testing::AllOf; |
| using ::testing::Invoke; |
| using ::testing::Matches; |
| using ::testing::UnorderedElementsAreArray; |
| using ::testing::Unused; |
| |
| constexpr char kDefaultPluginVmContainerName[] = "penguin"; |
| |
| class SysinfoProviderMock : public GuestMetrics::SysinfoProvider { |
| public: |
| MOCK_METHOD(int64_t, AmountOfTotalDiskSpace, (base::FilePath), ()); |
| MOCK_METHOD(int64_t, AmountOfFreeDiskSpace, (base::FilePath), ()); |
| }; |
| |
| // Helper for testing proto-based MethodCalls sent to the dbus. Extracts the |
| // proto in from the MethodCall to |protobuf| so that it can be tested further. |
| // Returns an empty (but not NULL) dbus::Response. |
| // |
| // Does not confirm method name (a.k.a. GetMethod) or interface name; use |
| // HasMethodName and HasInterfaceName for that. |
| base::expected<std::unique_ptr<dbus::Response>, dbus::Error> |
| ProtoMethodCallHelper(dbus::MethodCall* method_call, |
| google::protobuf::MessageLite* protobuf) { |
| dbus::MessageReader reader(method_call); |
| EXPECT_TRUE(reader.PopArrayOfBytesAsProto(protobuf)); |
| EXPECT_FALSE(reader.HasMoreData()); |
| |
| // MockObjectProxy will take ownership of the created Response object. See |
| // comments in MockObjectProxy. |
| return base::ok(dbus::Response::CreateEmpty()); |
| } |
| |
| // Same, but for method calls that just have a string. |
| base::expected<std::unique_ptr<dbus::Response>, dbus::Error> |
| StringMethodCallHelper(dbus::MethodCall* method_call, std::string* s) { |
| dbus::MessageReader reader(method_call); |
| EXPECT_TRUE(reader.PopString(s)); |
| EXPECT_FALSE(reader.HasMoreData()); |
| return base::ok(dbus::Response::CreateEmpty()); |
| } |
| |
| // Same but for signals |
| void ProtoSignalHelper(dbus::Signal* signal, |
| google::protobuf::MessageLite* protobuf) { |
| dbus::MessageReader reader(signal); |
| EXPECT_TRUE(reader.PopArrayOfBytesAsProto(protobuf)); |
| EXPECT_FALSE(reader.HasMoreData()); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| ValidContainerShutdownCallShouldProduceDBusMessage) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::ContainerShutdownInfo request; |
| vm_tools::EmptyMessage response; |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| |
| ContainerShutdownSignal shutdown_result; |
| EXPECT_CALL(test_framework.get_mock_exported_object(), |
| SendSignal(AllOf(HasInterfaceName(kVmCiceroneInterface), |
| HasMethodName(kContainerShutdownSignal)))) |
| .WillOnce(Invoke([&shutdown_result](dbus::Signal* signal) { |
| ProtoSignalHelper(signal, &shutdown_result); |
| })); |
| std::string unregister_hostname; |
| EXPECT_CALL(test_framework.get_mock_crosdns_service_proxy(), |
| CallMethodAndBlock( |
| AllOf(HasInterfaceName(crosdns::kCrosDnsInterfaceName), |
| HasMethodName(crosdns::kRemoveHostnameIpMappingMethod)), |
| _)) |
| .WillOnce( |
| Invoke([&unregister_hostname](dbus::MethodCall* method_call, Unused) { |
| return StringMethodCallHelper(method_call, &unregister_hostname); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = test_framework.get_service() |
| .GetContainerListenerImpl() |
| ->ContainerShutdown(&ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(shutdown_result.vm_name(), ServiceTestingHelper::kDefaultVmName); |
| EXPECT_EQ(shutdown_result.container_name(), |
| ServiceTestingHelper::kDefaultContainerName); |
| EXPECT_EQ(shutdown_result.owner_id(), ServiceTestingHelper::kDefaultOwnerId); |
| |
| EXPECT_EQ(unregister_hostname, |
| ServiceTestingHelper::kDefaultContainerHostname); |
| } |
| |
| void ValidUpdateApplicationListCallShouldProduceDBusMessageGeneric( |
| bool plugin_vm, const char* container_name) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| if (plugin_vm) { |
| test_framework.SetUpPluginVm(); |
| } else { |
| test_framework.SetUpDefaultVmAndContainer(); |
| } |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::UpdateApplicationListRequest request; |
| vm_tools::EmptyMessage response; |
| |
| const std::string kDesktopFileId = "nethack-x11"; |
| const std::string kName = "Nethack"; |
| const std::string kComment = "Get the amulet!"; |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| vm_tools::container::Application* application = request.add_application(); |
| application->set_desktop_file_id(kDesktopFileId); |
| application->mutable_name()->add_values()->set_value(kName); |
| application->mutable_comment()->add_values()->set_value(kComment); |
| application->set_no_display(false); |
| |
| vm_tools::apps::ApplicationList dbus_result; |
| EXPECT_CALL( |
| test_framework.get_mock_vm_applications_service_proxy(), |
| CallMethodAndBlock( |
| AllOf( |
| HasInterfaceName(vm_tools::apps::kVmApplicationsServiceInterface), |
| HasMethodName( |
| vm_tools::apps:: |
| kVmApplicationsServiceUpdateApplicationListMethod)), |
| _)) |
| .WillOnce(Invoke([&dbus_result](dbus::MethodCall* method_call, Unused) { |
| return ProtoMethodCallHelper(method_call, &dbus_result); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = test_framework.get_service() |
| .GetContainerListenerImpl() |
| ->UpdateApplicationList(&ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(dbus_result.vm_name(), ServiceTestingHelper::kDefaultVmName); |
| EXPECT_EQ(dbus_result.container_name(), container_name); |
| EXPECT_EQ(dbus_result.owner_id(), ServiceTestingHelper::kDefaultOwnerId); |
| ASSERT_EQ(dbus_result.apps_size(), 1); |
| const vm_tools::apps::App& dbus_app = dbus_result.apps(0); |
| EXPECT_EQ(dbus_app.desktop_file_id(), kDesktopFileId); |
| const vm_tools::apps::App::LocaleString& dbus_name = dbus_app.name(); |
| ASSERT_EQ(dbus_name.values_size(), 1); |
| EXPECT_EQ(dbus_name.values(0).value(), kName); |
| EXPECT_EQ(dbus_name.values(0).locale(), ""); |
| const vm_tools::apps::App::LocaleString& dbus_comment = dbus_app.comment(); |
| ASSERT_EQ(dbus_comment.values_size(), 1); |
| EXPECT_EQ(dbus_comment.values(0).value(), kComment); |
| EXPECT_EQ(dbus_comment.values(0).locale(), ""); |
| EXPECT_FALSE(dbus_app.no_display()); |
| EXPECT_EQ(dbus_app.mime_types_size(), 0); |
| EXPECT_EQ(dbus_app.startup_wm_class(), ""); |
| EXPECT_FALSE(dbus_app.startup_notify()); |
| EXPECT_EQ(dbus_app.keywords().values_size(), 0); |
| EXPECT_EQ(dbus_app.package_id(), ""); |
| EXPECT_EQ(dbus_app.extensions_size(), 0); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| ValidUpdateApplicationListCallShouldProduceDBusMessageForDefaultVm) { |
| ValidUpdateApplicationListCallShouldProduceDBusMessageGeneric( |
| false /* plugin_vm */, ServiceTestingHelper::kDefaultContainerName); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| ValidUpdateApplicationListCallShouldProduceDBusMessageForPluginVm) { |
| ValidUpdateApplicationListCallShouldProduceDBusMessageGeneric( |
| true /* plugin_vm */, kDefaultPluginVmContainerName); |
| } |
| |
| vm_tools::container::Application::LocalizedString MakeLocalizedString( |
| const std::string& field_name, int seed) { |
| vm_tools::container::Application::LocalizedString result; |
| for (int value = 0; value < seed; ++value) { |
| vm_tools::container::Application::LocalizedString::StringWithLocale* |
| locale = result.add_values(); |
| |
| if ((value % 5) != 0) { |
| locale->set_value(field_name + |
| ":value:" + base::NumberToString(seed * 1000 + value)); |
| } |
| if ((value % 4) != 0) { |
| locale->set_locale( |
| field_name + ":locale:" + base::NumberToString(seed * 1000 + value)); |
| } |
| } |
| |
| return result; |
| } |
| |
| vm_tools::container::Application::LocaleStrings MakeLocaleStrings( |
| const std::string& field_name, int seed) { |
| vm_tools::container::Application::LocaleStrings result; |
| for (int value = 0; value < seed; ++value) { |
| vm_tools::container::Application::LocaleStrings::StringsWithLocale* locale = |
| result.add_values(); |
| |
| for (int sub_value = 0; sub_value < value; ++sub_value) { |
| locale->add_value( |
| field_name + ":value:" + |
| base::NumberToString(seed * 1000 + value * 10 + sub_value)); |
| } |
| if ((value % 4) != 0) { |
| locale->set_locale( |
| field_name + ":locale:" + base::NumberToString(seed * 1000 + value)); |
| } |
| } |
| |
| return result; |
| } |
| |
| // Make an UpdateApplicationListRequest with every field filled in and multiple |
| // entries in every repeated field. |
| vm_tools::container::UpdateApplicationListRequest |
| MakeComplexUpdateApplicationListRequest() { |
| vm_tools::container::UpdateApplicationListRequest request; |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| |
| for (int app_num = 0; app_num < 10; ++app_num) { |
| vm_tools::container::Application* application = request.add_application(); |
| |
| // Non-repeated fields: |
| application->set_desktop_file_id("desktop:" + |
| base::NumberToString(app_num)); |
| application->set_no_display((app_num % 2) == 0); |
| application->set_startup_wm_class("startup_wm_class:" + |
| base::NumberToString(app_num)); |
| application->set_startup_notify((app_num % 3) == 0); |
| application->set_package_id("package_id:" + base::NumberToString(app_num)); |
| |
| // Repeated fields: |
| *application->mutable_name() = MakeLocalizedString("name", app_num); |
| *application->mutable_comment() = |
| MakeLocalizedString("comment", 10 - app_num); |
| for (int mime_type = 0; mime_type < 5; ++mime_type) { |
| application->add_mime_types( |
| "mime:" + base::NumberToString(app_num * 1000 + mime_type)); |
| } |
| *application->mutable_keywords() = MakeLocaleStrings("keyword", app_num); |
| for (int extension = 0; extension < 5; ++extension) { |
| application->add_extensions( |
| "extension:" + base::NumberToString(app_num * 1000 + extension)); |
| } |
| } |
| return request; |
| } |
| |
| void CompareLocaleString( |
| const vm_tools::container::Application::LocalizedString& expected, |
| const vm_tools::apps::App::LocaleString& actual) { |
| // Order is not important, only existence. |
| EXPECT_EQ(expected.values_size(), actual.values_size()); |
| std::set<int> expected_indexes_seen; |
| for (const vm_tools::apps::App::LocaleString::Entry& actual_entry : |
| actual.values()) { |
| bool found = false; |
| int expected_index = 0; |
| while (!found && expected_index < expected.values_size()) { |
| if (expected.values(expected_index).locale() == actual_entry.locale() && |
| expected.values(expected_index).value() == actual_entry.value() && |
| expected_indexes_seen.find(expected_index) == |
| expected_indexes_seen.end()) { |
| found = true; |
| } else { |
| ++expected_index; |
| } |
| } |
| |
| EXPECT_TRUE(found) << "Could not find expected.values() with locale " |
| << actual_entry.locale() << " and value " |
| << actual_entry.value(); |
| expected_indexes_seen.insert(expected_index); |
| } |
| EXPECT_EQ(expected_indexes_seen.size(), actual.values_size()); |
| } |
| |
| void CompareLocaleStrings( |
| const vm_tools::container::Application::LocaleStrings& expected, |
| const vm_tools::apps::App::LocaleStrings& actual) { |
| // Order is not important, only existence. |
| EXPECT_EQ(expected.values_size(), actual.values_size()); |
| std::set<int> expected_indexes_seen; |
| for (const vm_tools::apps::App::LocaleStrings::StringsWithLocale& |
| actual_entry : actual.values()) { |
| bool found = false; |
| int expected_index = 0; |
| while (!found && expected_index < expected.values_size()) { |
| if (expected.values(expected_index).locale() == actual_entry.locale() && |
| Matches(UnorderedElementsAreArray( |
| expected.values(expected_index).value()))(actual_entry.value()) && |
| expected_indexes_seen.find(expected_index) == |
| expected_indexes_seen.end()) { |
| found = true; |
| } else { |
| ++expected_index; |
| } |
| } |
| |
| EXPECT_TRUE(found) << "Could not find expected.values() with locale " |
| << actual_entry.locale() << " and " |
| << actual_entry.value_size() << " values"; |
| expected_indexes_seen.insert(expected_index); |
| } |
| EXPECT_EQ(expected_indexes_seen.size(), actual.values_size()); |
| } |
| |
| void CompareInputApplicationToOutputApp( |
| const vm_tools::container::Application& expected, |
| const vm_tools::apps::App& actual) { |
| // Compare non-repeated fields. |
| EXPECT_EQ(expected.desktop_file_id(), actual.desktop_file_id()); |
| EXPECT_EQ(expected.no_display(), actual.no_display()); |
| EXPECT_EQ(expected.startup_wm_class(), actual.startup_wm_class()); |
| EXPECT_EQ(expected.startup_notify(), actual.startup_notify()); |
| EXPECT_EQ(expected.package_id(), actual.package_id()); |
| |
| EXPECT_THAT(actual.mime_types(), |
| UnorderedElementsAreArray(expected.mime_types())); |
| |
| CompareLocaleString(expected.name(), actual.name()); |
| CompareLocaleString(expected.comment(), actual.comment()); |
| CompareLocaleStrings(expected.keywords(), actual.keywords()); |
| |
| EXPECT_THAT(actual.extensions(), |
| UnorderedElementsAreArray(expected.extensions())); |
| } |
| |
| // Ensures that all the entries in expected match the entries in actual. Allows |
| // different order of the applications and sub-fields. EXPECT fails if |
| // differences are found. |
| void CompareUpdateApplicationListRequestToApplicationList( |
| const vm_tools::container::UpdateApplicationListRequest& expected, |
| const vm_tools::apps::ApplicationList& actual) { |
| EXPECT_EQ(actual.apps_size(), expected.application_size()); |
| std::set<int> expected_indexes_seen; |
| for (const vm_tools::apps::App& actual_app : actual.apps()) { |
| // Don't assume input & output order are the same, because that's not part |
| // of the API contract. Assume desktop_file_id is unique, though, since |
| // MakeComplexUpdateApplicationListRequest will do that. |
| bool found = false; |
| int expected_index = 0; |
| while (!found && expected_index < expected.application_size()) { |
| if (expected.application(expected_index).desktop_file_id() == |
| actual_app.desktop_file_id()) { |
| found = true; |
| } else { |
| ++expected_index; |
| } |
| } |
| |
| ASSERT_TRUE(found) |
| << "Could not find expected.application() with desktop_file_id " |
| << actual_app.desktop_file_id(); |
| EXPECT_TRUE(expected_indexes_seen.find(expected_index) == |
| expected_indexes_seen.end()) |
| << "desktop_file_id " << actual_app.desktop_file_id() |
| << " appears twice in actual"; |
| expected_indexes_seen.insert(expected_index); |
| |
| CompareInputApplicationToOutputApp(expected.application(expected_index), |
| actual_app); |
| } |
| EXPECT_EQ(expected_indexes_seen.size(), actual.apps_size()); |
| } |
| |
| void LongerUpdateApplicationListCallShouldProduceDBusMessageGeneric( |
| bool plugin_vm, const char* container_name) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| if (plugin_vm) { |
| test_framework.SetUpPluginVm(); |
| } else { |
| test_framework.SetUpDefaultVmAndContainer(); |
| } |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::UpdateApplicationListRequest request( |
| MakeComplexUpdateApplicationListRequest()); |
| vm_tools::EmptyMessage response; |
| |
| vm_tools::apps::ApplicationList dbus_result; |
| EXPECT_CALL( |
| test_framework.get_mock_vm_applications_service_proxy(), |
| CallMethodAndBlock( |
| AllOf( |
| HasInterfaceName(vm_tools::apps::kVmApplicationsServiceInterface), |
| HasMethodName( |
| vm_tools::apps:: |
| kVmApplicationsServiceUpdateApplicationListMethod)), |
| _)) |
| .WillOnce(Invoke([&dbus_result](dbus::MethodCall* method_call, Unused) { |
| return ProtoMethodCallHelper(method_call, &dbus_result); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = test_framework.get_service() |
| .GetContainerListenerImpl() |
| ->UpdateApplicationList(&ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(dbus_result.vm_name(), ServiceTestingHelper::kDefaultVmName); |
| EXPECT_EQ(dbus_result.container_name(), container_name); |
| EXPECT_EQ(dbus_result.owner_id(), ServiceTestingHelper::kDefaultOwnerId); |
| |
| CompareUpdateApplicationListRequestToApplicationList(request, dbus_result); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| LongerUpdateApplicationListCallShouldProduceDBusMessageForDefaultVm) { |
| LongerUpdateApplicationListCallShouldProduceDBusMessageGeneric( |
| false /* plugin_vm */, ServiceTestingHelper::kDefaultContainerName); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| LongerUpdateApplicationListCallShouldProduceDBusMessageForPluginVm) { |
| LongerUpdateApplicationListCallShouldProduceDBusMessageGeneric( |
| true /* plugin_vm */, kDefaultPluginVmContainerName); |
| } |
| |
| void ValidOpenUrlCallShouldProduceDBusMessageGeneric(bool plugin_vm) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| if (plugin_vm) { |
| test_framework.SetUpPluginVm(); |
| } else { |
| test_framework.SetUpDefaultVmAndContainer(); |
| } |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::OpenUrlRequest request; |
| vm_tools::EmptyMessage response; |
| |
| const std::string kUrl = "whycrostiniisgreat.com"; |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| request.set_url(kUrl); |
| |
| std::string resulting_url; |
| EXPECT_CALL( |
| test_framework.get_mock_url_handler_service_proxy(), |
| CallMethodAndBlock( |
| AllOf(HasInterfaceName(chromeos::kUrlHandlerServiceInterface), |
| HasMethodName(chromeos::kUrlHandlerServiceOpenUrlMethod)), |
| _)) |
| .WillOnce(Invoke([&resulting_url](dbus::MethodCall* method_call, Unused) { |
| return StringMethodCallHelper(method_call, &resulting_url); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service().GetContainerListenerImpl()->OpenUrl( |
| &ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(resulting_url, kUrl); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| ValidOpenUrlCallShouldProduceDBusMessageForDefaultVm) { |
| ValidOpenUrlCallShouldProduceDBusMessageGeneric(false); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| ValidOpenUrlCallShouldProduceDBusMessageForPluginVm) { |
| ValidOpenUrlCallShouldProduceDBusMessageGeneric(true); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| ValidInstallLinuxPackageProgressCallShouldProduceDBusMessage) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::InstallLinuxPackageProgressInfo request; |
| vm_tools::EmptyMessage response; |
| |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| request.set_status( |
| vm_tools::container::InstallLinuxPackageProgressInfo::DOWNLOADING); |
| request.set_progress_percent(52); |
| |
| InstallLinuxPackageProgressSignal dbus_result; |
| EXPECT_CALL( |
| test_framework.get_mock_exported_object(), |
| SendSignal(AllOf(HasInterfaceName(kVmCiceroneInterface), |
| HasMethodName(kInstallLinuxPackageProgressSignal)))) |
| .WillOnce(Invoke([&dbus_result](dbus::Signal* signal) { |
| ProtoSignalHelper(signal, &dbus_result); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service() |
| .GetContainerListenerImpl() |
| ->InstallLinuxPackageProgress(&ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(dbus_result.vm_name(), ServiceTestingHelper::kDefaultVmName); |
| EXPECT_EQ(dbus_result.container_name(), |
| ServiceTestingHelper::kDefaultContainerName); |
| EXPECT_EQ(dbus_result.owner_id(), ServiceTestingHelper::kDefaultOwnerId); |
| EXPECT_EQ(dbus_result.status(), |
| InstallLinuxPackageProgressSignal::DOWNLOADING); |
| EXPECT_EQ(dbus_result.progress_percent(), 52); |
| EXPECT_EQ(dbus_result.failure_details(), ""); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| FailureInstallLinuxPackageProgressCallShouldProduceDBusMessage) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::InstallLinuxPackageProgressInfo request; |
| vm_tools::EmptyMessage response; |
| |
| const std::string kFailureDetails = "I prefer not to."; |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| request.set_status( |
| vm_tools::container::InstallLinuxPackageProgressInfo::FAILED); |
| request.set_failure_details(kFailureDetails); |
| |
| InstallLinuxPackageProgressSignal dbus_result; |
| EXPECT_CALL( |
| test_framework.get_mock_exported_object(), |
| SendSignal(AllOf(HasInterfaceName(kVmCiceroneInterface), |
| HasMethodName(kInstallLinuxPackageProgressSignal)))) |
| .WillOnce(Invoke([&dbus_result](dbus::Signal* signal) { |
| ProtoSignalHelper(signal, &dbus_result); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service() |
| .GetContainerListenerImpl() |
| ->InstallLinuxPackageProgress(&ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(dbus_result.vm_name(), ServiceTestingHelper::kDefaultVmName); |
| EXPECT_EQ(dbus_result.container_name(), |
| ServiceTestingHelper::kDefaultContainerName); |
| EXPECT_EQ(dbus_result.owner_id(), ServiceTestingHelper::kDefaultOwnerId); |
| EXPECT_EQ(dbus_result.status(), InstallLinuxPackageProgressSignal::FAILED); |
| EXPECT_EQ(dbus_result.progress_percent(), 0); |
| EXPECT_EQ(dbus_result.failure_details(), kFailureDetails); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| ValidUninstallPackageProgressCallShouldProduceDBusMessage) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::UninstallPackageProgressInfo request; |
| vm_tools::EmptyMessage response; |
| |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| request.set_status( |
| vm_tools::container::UninstallPackageProgressInfo::UNINSTALLING); |
| request.set_progress_percent(72); |
| |
| UninstallPackageProgressSignal dbus_result; |
| EXPECT_CALL(test_framework.get_mock_exported_object(), |
| SendSignal(AllOf(HasInterfaceName(kVmCiceroneInterface), |
| HasMethodName(kUninstallPackageProgressSignal)))) |
| .WillOnce(Invoke([&dbus_result](dbus::Signal* signal) { |
| ProtoSignalHelper(signal, &dbus_result); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service() |
| .GetContainerListenerImpl() |
| ->UninstallPackageProgress(&ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(dbus_result.vm_name(), ServiceTestingHelper::kDefaultVmName); |
| EXPECT_EQ(dbus_result.container_name(), |
| ServiceTestingHelper::kDefaultContainerName); |
| EXPECT_EQ(dbus_result.owner_id(), ServiceTestingHelper::kDefaultOwnerId); |
| EXPECT_EQ(dbus_result.status(), UninstallPackageProgressSignal::UNINSTALLING); |
| EXPECT_EQ(dbus_result.progress_percent(), 72); |
| EXPECT_EQ(dbus_result.failure_details(), ""); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| FailedUninstallPackageProgressCallShouldProduceDBusMessage) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::UninstallPackageProgressInfo request; |
| vm_tools::EmptyMessage response; |
| |
| const std::string kFailureDetails = "Hahaha NO"; |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| request.set_status(vm_tools::container::UninstallPackageProgressInfo::FAILED); |
| request.set_failure_details(kFailureDetails); |
| |
| UninstallPackageProgressSignal dbus_result; |
| EXPECT_CALL(test_framework.get_mock_exported_object(), |
| SendSignal(AllOf(HasInterfaceName(kVmCiceroneInterface), |
| HasMethodName(kUninstallPackageProgressSignal)))) |
| .WillOnce(Invoke([&dbus_result](dbus::Signal* signal) { |
| ProtoSignalHelper(signal, &dbus_result); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service() |
| .GetContainerListenerImpl() |
| ->UninstallPackageProgress(&ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(dbus_result.vm_name(), ServiceTestingHelper::kDefaultVmName); |
| EXPECT_EQ(dbus_result.container_name(), |
| ServiceTestingHelper::kDefaultContainerName); |
| EXPECT_EQ(dbus_result.owner_id(), ServiceTestingHelper::kDefaultOwnerId); |
| EXPECT_EQ(dbus_result.status(), UninstallPackageProgressSignal::FAILED); |
| EXPECT_EQ(dbus_result.progress_percent(), 0); |
| EXPECT_EQ(dbus_result.failure_details(), kFailureDetails); |
| } |
| |
| TEST(ContainerListenerImplTest, ValidOpenTerminalCallShouldProduceDBusMessage) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::OpenTerminalRequest request; |
| vm_tools::EmptyMessage response; |
| |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| *request.add_params() = "-c"; |
| *request.add_params() = "/bin/ls"; |
| *request.add_params() = "/tmp"; |
| |
| vm_tools::apps::TerminalParams dbus_result; |
| EXPECT_CALL( |
| test_framework.get_mock_vm_applications_service_proxy(), |
| CallMethodAndBlock( |
| AllOf( |
| HasInterfaceName(vm_tools::apps::kVmApplicationsServiceInterface), |
| HasMethodName( |
| vm_tools::apps::kVmApplicationsServiceLaunchTerminalMethod)), |
| _)) |
| .WillOnce(Invoke([&dbus_result](dbus::MethodCall* method_call, Unused) { |
| return ProtoMethodCallHelper(method_call, &dbus_result); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service().GetContainerListenerImpl()->OpenTerminal( |
| &ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(dbus_result.vm_name(), ServiceTestingHelper::kDefaultVmName); |
| EXPECT_EQ(dbus_result.container_name(), |
| ServiceTestingHelper::kDefaultContainerName); |
| EXPECT_EQ(dbus_result.owner_id(), ServiceTestingHelper::kDefaultOwnerId); |
| ASSERT_EQ(dbus_result.params_size(), 3); |
| EXPECT_EQ(dbus_result.params(0), "-c"); |
| EXPECT_EQ(dbus_result.params(1), "/bin/ls"); |
| EXPECT_EQ(dbus_result.params(2), "/tmp"); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| ValidUpdateMimeTypesCallShouldProduceDBusMessage) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::UpdateMimeTypesRequest request; |
| vm_tools::EmptyMessage response; |
| |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| (*request.mutable_mime_type_mappings())["aiff"] = "audio/x-aiff"; |
| (*request.mutable_mime_type_mappings())["rar"] = "application/rar"; |
| (*request.mutable_mime_type_mappings())["png"] = "image/png"; |
| |
| vm_tools::apps::MimeTypes dbus_result; |
| EXPECT_CALL( |
| test_framework.get_mock_vm_applications_service_proxy(), |
| CallMethodAndBlock( |
| AllOf( |
| HasInterfaceName(vm_tools::apps::kVmApplicationsServiceInterface), |
| HasMethodName( |
| vm_tools::apps::kVmApplicationsServiceUpdateMimeTypesMethod)), |
| _)) |
| .WillOnce(Invoke([&dbus_result](dbus::MethodCall* method_call, Unused) { |
| return ProtoMethodCallHelper(method_call, &dbus_result); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service().GetContainerListenerImpl()->UpdateMimeTypes( |
| &ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(dbus_result.vm_name(), ServiceTestingHelper::kDefaultVmName); |
| EXPECT_EQ(dbus_result.container_name(), |
| ServiceTestingHelper::kDefaultContainerName); |
| EXPECT_EQ(dbus_result.owner_id(), ServiceTestingHelper::kDefaultOwnerId); |
| const auto& mime_type_mappings = dbus_result.mime_type_mappings(); |
| EXPECT_EQ(mime_type_mappings.size(), 3); |
| auto aiff_iter = mime_type_mappings.find("aiff"); |
| ASSERT_TRUE(aiff_iter != mime_type_mappings.end()); |
| EXPECT_EQ(aiff_iter->second, "audio/x-aiff"); |
| auto rar_iter = mime_type_mappings.find("rar"); |
| ASSERT_TRUE(rar_iter != mime_type_mappings.end()); |
| EXPECT_EQ(rar_iter->second, "application/rar"); |
| auto png_iter = mime_type_mappings.find("png"); |
| ASSERT_TRUE(png_iter != mime_type_mappings.end()); |
| EXPECT_EQ(png_iter->second, "image/png"); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| ValidApplyAnsiblePlaybookProgressCallShouldProduceDBusMessage) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::ApplyAnsiblePlaybookProgressInfo request; |
| vm_tools::EmptyMessage response; |
| |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| request.set_status( |
| vm_tools::container::ApplyAnsiblePlaybookProgressInfo::SUCCEEDED); |
| |
| ApplyAnsiblePlaybookProgressSignal dbus_result; |
| EXPECT_CALL( |
| test_framework.get_mock_exported_object(), |
| SendSignal(AllOf(HasInterfaceName(kVmCiceroneInterface), |
| HasMethodName(kApplyAnsiblePlaybookProgressSignal)))) |
| .WillOnce(Invoke([&dbus_result](dbus::Signal* signal) { |
| ProtoSignalHelper(signal, &dbus_result); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service() |
| .GetContainerListenerImpl() |
| ->ApplyAnsiblePlaybookProgress(&ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(dbus_result.vm_name(), ServiceTestingHelper::kDefaultVmName); |
| EXPECT_EQ(dbus_result.container_name(), |
| ServiceTestingHelper::kDefaultContainerName); |
| EXPECT_EQ(dbus_result.owner_id(), ServiceTestingHelper::kDefaultOwnerId); |
| EXPECT_EQ(dbus_result.status(), |
| ApplyAnsiblePlaybookProgressSignal::SUCCEEDED); |
| EXPECT_EQ(dbus_result.failure_details(), ""); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| FailedApplyAnsiblePlaybookProgressCallShouldProduceDBusMessage) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::ApplyAnsiblePlaybookProgressInfo request; |
| vm_tools::EmptyMessage response; |
| |
| const std::string kFailureDetails = "Hahaha NO"; |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| request.set_status( |
| vm_tools::container::ApplyAnsiblePlaybookProgressInfo::FAILED); |
| request.set_failure_details(kFailureDetails); |
| |
| ApplyAnsiblePlaybookProgressSignal dbus_result; |
| EXPECT_CALL( |
| test_framework.get_mock_exported_object(), |
| SendSignal(AllOf(HasInterfaceName(kVmCiceroneInterface), |
| HasMethodName(kApplyAnsiblePlaybookProgressSignal)))) |
| .WillOnce(Invoke([&dbus_result](dbus::Signal* signal) { |
| ProtoSignalHelper(signal, &dbus_result); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service() |
| .GetContainerListenerImpl() |
| ->ApplyAnsiblePlaybookProgress(&ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(dbus_result.vm_name(), ServiceTestingHelper::kDefaultVmName); |
| EXPECT_EQ(dbus_result.container_name(), |
| ServiceTestingHelper::kDefaultContainerName); |
| EXPECT_EQ(dbus_result.owner_id(), ServiceTestingHelper::kDefaultOwnerId); |
| EXPECT_EQ(dbus_result.status(), ApplyAnsiblePlaybookProgressSignal::FAILED); |
| EXPECT_EQ(dbus_result.failure_details(), kFailureDetails); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| InProgressApplyAnsiblePlaybookProgressCallShouldProduceDBusMessage) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::ApplyAnsiblePlaybookProgressInfo request; |
| vm_tools::EmptyMessage response; |
| |
| const std::string kStatusString = "Yesh milord. More work?"; |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| request.set_status( |
| vm_tools::container::ApplyAnsiblePlaybookProgressInfo::IN_PROGRESS); |
| request.add_status_string(kStatusString); |
| |
| ApplyAnsiblePlaybookProgressSignal dbus_result; |
| EXPECT_CALL( |
| test_framework.get_mock_exported_object(), |
| SendSignal(AllOf(HasInterfaceName(kVmCiceroneInterface), |
| HasMethodName(kApplyAnsiblePlaybookProgressSignal)))) |
| .WillOnce(Invoke([&dbus_result](dbus::Signal* signal) { |
| ProtoSignalHelper(signal, &dbus_result); |
| })); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service() |
| .GetContainerListenerImpl() |
| ->ApplyAnsiblePlaybookProgress(&ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| EXPECT_EQ(dbus_result.vm_name(), ServiceTestingHelper::kDefaultVmName); |
| EXPECT_EQ(dbus_result.container_name(), |
| ServiceTestingHelper::kDefaultContainerName); |
| EXPECT_EQ(dbus_result.owner_id(), ServiceTestingHelper::kDefaultOwnerId); |
| EXPECT_EQ(dbus_result.status(), |
| ApplyAnsiblePlaybookProgressSignal::IN_PROGRESS); |
| EXPECT_EQ(dbus_result.status_string(0), kStatusString); |
| } |
| |
| TEST(ContainerListenerImplTest, ValidReportMetricsCallShouldAccumulateMetrics) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| // Fake an RPC from the guest containing two metrics. |
| vm_tools::container::ReportMetricsRequest request; |
| vm_tools::container::ReportMetricsResponse response; |
| |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| vm_tools::container::Metric* m = request.add_metric(); |
| m->set_name("borealis-swap-kb-written"); |
| m->set_value(123456); |
| m = request.add_metric(); |
| m->set_name("borealis-disk-kb-read"); |
| m->set_value(654321); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service().GetContainerListenerImpl()->ReportMetrics( |
| &ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| // Force a call to GuestMetrics::ReportDailyMetrics, which should report all |
| // eight disk metrics to UMA. The two four which received data from the fake |
| // RPC above should contain the reported data, and the other six should be |
| // zero. |
| EXPECT_CALL(*test_framework.GetMetricsLibraryMock(), SendToUMA(_, _, _, _, _)) |
| .Times(4) |
| .WillRepeatedly([](const std::string& name, int sample, int min, int max, |
| int nbuckets) { |
| if (name == "Borealis.Disk.SwapWritesDaily") { |
| EXPECT_EQ(sample, 123456); |
| } else if (name == "Borealis.Disk.StatefulReadsDaily") { |
| EXPECT_EQ(sample, 654321); |
| } else { |
| // Borealis.Disk.{SwapReads,StatefulWrites}Daily |
| EXPECT_EQ(sample, 0); |
| EXPECT_EQ(name.find("Crostini"), std::string::npos); |
| } |
| return true; |
| }); |
| test_framework.GetGuestMetrics()->ReportMetricsImmediatelyForTesting(); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| ValidReportMetricsCallShouldAccumulateMetricsForCrostini) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS, |
| "termina"); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| // Fake an RPC from the guest containing two metrics. |
| vm_tools::container::ReportMetricsRequest request; |
| vm_tools::container::ReportMetricsResponse response; |
| |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| vm_tools::container::Metric* m = request.add_metric(); |
| m->set_name("borealis-swap-kb-written"); |
| m->set_value(123456); |
| m = request.add_metric(); |
| m->set_name("borealis-disk-kb-read"); |
| m->set_value(654321); |
| m = request.add_metric(); |
| m->set_name("crostini-disk-kb-read"); |
| m->set_value(123321); |
| m = request.add_metric(); |
| m->set_name("crostini-disk-kb-written"); |
| m->set_value(321123); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service().GetContainerListenerImpl()->ReportMetrics( |
| &ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| |
| // Force a call to GuestMetrics::ReportDailyMetrics, which should report all |
| // eight disk metrics to UMA. The two four which received data from the fake |
| // RPC above should contain the reported data, and the other six should be |
| // zero. |
| EXPECT_CALL(*test_framework.GetMetricsLibraryMock(), SendToUMA(_, _, _, _, _)) |
| .Times(4) |
| .WillRepeatedly([](const std::string& name, int sample, int min, int max, |
| int nbuckets) { |
| if (name == "Crostini.Disk.StatefulWritesDaily") { |
| EXPECT_EQ(sample, 321123); |
| } else if (name == "Crostini.Disk.StatefulReadsDaily") { |
| EXPECT_EQ(sample, 123321); |
| } else { |
| // Crostini.Disk.Swap{Reads,Writes}Daily |
| EXPECT_EQ(sample, 0); |
| } |
| return true; |
| }); |
| test_framework.GetGuestMetrics()->ReportMetricsImmediatelyForTesting(); |
| } |
| |
| TEST(ContainerListenerImplTest, |
| ReportingInodeCountShouldGenerateAndEmitSpaceMetrics) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| |
| // Setup a mock provider for sysinfo. |
| auto sysinfo_provider = std::make_unique<SysinfoProviderMock>(); |
| SysinfoProviderMock* sysinfo_provider_reference = sysinfo_provider.get(); |
| test_framework.GetGuestMetrics()->SetSysinfoProviderForTesting( |
| std::move(sysinfo_provider)); |
| |
| // Fake an RPC from the guest containing two metrics. |
| vm_tools::container::ReportMetricsRequest request; |
| vm_tools::container::ReportMetricsResponse response; |
| |
| // Setup metrics request. |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| vm_tools::container::Metric* m = request.add_metric(); |
| m->set_name("borealis-inode-count"); |
| m->set_value(100); |
| |
| // Replace the concierge object proxy with our mock. |
| EXPECT_CALL(test_framework.get_mock_bus(), |
| GetObjectProxy(vm_tools::concierge::kVmConciergeServiceName, _)) |
| .WillOnce( |
| testing::Return(&test_framework.get_mock_concierge_service_proxy())); |
| |
| // Replace the ListVmDisks request to concierge. |
| EXPECT_CALL( |
| test_framework.get_mock_concierge_service_proxy(), |
| DoCallMethod( |
| AllOf(HasInterfaceName(vm_tools::concierge::kVmConciergeInterface), |
| HasMethodName(vm_tools::concierge::kListVmDisksMethod)), |
| _, _)) |
| .WillOnce(Invoke([](dbus::MethodCall* method_call, int timeout, |
| base::OnceCallback<void(dbus::Response*)>* callback) { |
| method_call->SetSerial(123); |
| std::unique_ptr<dbus::Response> dbus_response( |
| dbus::Response::FromMethodCall(method_call)); |
| dbus::MessageWriter writer(dbus_response.get()); |
| vm_tools::concierge::ListVmDisksResponse response; |
| |
| // Set the response. |
| vm_tools::concierge::VmDiskInfo* image = response.add_images(); |
| image->set_name("borealis"); |
| image->set_size(1000000); |
| image->set_path("/mnt/stateful/borealis.img"); |
| |
| writer.AppendProtoAsArrayOfBytes(response); |
| std::move(*callback).Run(std::move(dbus_response.get())); |
| })); |
| |
| // Set the response to AmountOfTotalDiskSpace. |
| EXPECT_CALL(*sysinfo_provider_reference, |
| AmountOfTotalDiskSpace(base::FilePath("/mnt/stateful"))) |
| .WillOnce(testing::Return(2000000)); |
| // Set the response to AmountOfFreeDiskSpace. |
| EXPECT_CALL(*sysinfo_provider_reference, |
| AmountOfFreeDiskSpace(base::FilePath("/mnt/stateful"))) |
| .WillOnce(testing::Return(500000)); |
| |
| // InodeRatioAtStartup [KiB] = |
| // (1000000[image_size]/100[inode_count])/1024 = 9 |
| EXPECT_CALL(*test_framework.GetMetricsLibraryMock(), |
| SendToUMA("Borealis.Disk.InodeRatioAtStartup", 9, 0, 10240, 50)); |
| // VMUsageToTotalSpacePercentageAtStartup [%] = |
| // (1000000[image_size]/2000000[total_size])/100 = 50% |
| EXPECT_CALL(*test_framework.GetMetricsLibraryMock(), |
| SendPercentageToUMA( |
| "Borealis.Disk.VMUsageToTotalSpacePercentageAtStartup", 50)); |
| // VMUsageToTotalUsagePercentageAtStartup [%] = |
| // (1000000[image_size]/(2000000[total_size] - 500000[free_space]))/100 = 66% |
| EXPECT_CALL(*test_framework.GetMetricsLibraryMock(), |
| SendPercentageToUMA( |
| "Borealis.Disk.VMUsageToTotalUsagePercentageAtStartup", 66)); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service().GetContainerListenerImpl()->ReportMetrics( |
| &ctx, &request, &response); |
| ASSERT_TRUE(status.ok()) << status.error_message(); |
| } |
| |
| TEST(ContainerListenerImplTest, ReportMetricsCallWithTooManyMetricsShouldFail) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::ReportMetricsRequest request; |
| vm_tools::container::ReportMetricsResponse response; |
| |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| for (int i = 0; i < 40; ++i) { |
| vm_tools::container::Metric* m = request.add_metric(); |
| m->set_name("a-fake-metric-whose-name-will-be-ignored"); |
| m->set_value(123456); |
| } |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service().GetContainerListenerImpl()->ReportMetrics( |
| &ctx, &request, &response); |
| ASSERT_FALSE(status.ok()) << status.error_message(); |
| } |
| |
| TEST(ContainerListenerImplTest, ReportMetricsCallWithBadMetricNameShouldFail) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::ReportMetricsRequest request; |
| vm_tools::container::ReportMetricsResponse response; |
| |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| vm_tools::container::Metric* m = request.add_metric(); |
| m->set_name("This name contains forbidden characters!"); |
| m->set_value(123456); |
| |
| grpc::ServerContext ctx; |
| grpc::Status status = |
| test_framework.get_service().GetContainerListenerImpl()->ReportMetrics( |
| &ctx, &request, &response); |
| ASSERT_FALSE(status.ok()) << status.error_message(); |
| } |
| |
| TEST(ContainerListenerImplTest, ReportMetricsCallsShouldBeRateLimited) { |
| ServiceTestingHelper test_framework(ServiceTestingHelper::NORMAL_MOCKS); |
| test_framework.SetUpDefaultVmAndContainer(); |
| test_framework.ExpectNoDBusMessages(); |
| |
| vm_tools::container::ReportMetricsRequest request; |
| vm_tools::container::ReportMetricsResponse response; |
| |
| request.set_token(ServiceTestingHelper::kDefaultContainerToken); |
| vm_tools::container::Metric* m = request.add_metric(); |
| m->set_name("borealis-swap-kb-written"); |
| m->set_value(123456); |
| |
| grpc::ServerContext ctx; |
| // The first 40 calls should succeed. |
| for (int i = 0; i < 40; i++) { |
| grpc::Status status = |
| test_framework.get_service().GetContainerListenerImpl()->ReportMetrics( |
| &ctx, &request, &response); |
| EXPECT_TRUE(status.ok()) << status.error_message(); |
| EXPECT_EQ(response.error(), 0); |
| } |
| // 41st call should hit the rate limit and fail. |
| grpc::Status status = |
| test_framework.get_service().GetContainerListenerImpl()->ReportMetrics( |
| &ctx, &request, &response); |
| EXPECT_TRUE(status.ok()) << status.error_message(); |
| EXPECT_NE(response.error(), 0); |
| } |
| |
| } // namespace |
| |
| } // namespace cicerone |
| } // namespace vm_tools |