chromium/chrome/browser/ash/guest_os/guest_os_mime_types_service_unittest.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/guest_os/guest_os_mime_types_service.h"

#include <stddef.h>
#include <vector>

#include "base/files/file_path.h"
#include "chrome/browser/ash/crostini/crostini_test_helper.h"
#include "chrome/browser/ash/crostini/crostini_util.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/dbus/vm_applications/apps.pb.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

using vm_tools::apps::MimeTypes;

namespace guest_os {

namespace {

constexpr char kTestVmName[] = "test_vm";
constexpr char kTestContainerName[] = "test_container";

}  // namespace

class GuestOsMimeTypesServiceTest : public testing::Test {
 public:
  GuestOsMimeTypesServiceTest()
      : crostini_test_helper_(&profile_),
        service_(std::make_unique<GuestOsMimeTypesService>(&profile_)) {}
  GuestOsMimeTypesServiceTest(const GuestOsMimeTypesServiceTest&) = delete;
  GuestOsMimeTypesServiceTest& operator=(const GuestOsMimeTypesServiceTest&) =
      delete;

 protected:
  GuestOsMimeTypesService* service() { return service_.get(); }

  Profile* profile() { return &profile_; }

  void UpdateMimeTypes(const std::vector<std::string>& file_extensions,
                       const std::vector<std::string>& mime_types,
                       const std::string& vm_name,
                       const std::string& container_name) {
    CHECK_EQ(file_extensions.size(), mime_types.size());
    MimeTypes mime_types_list;
    mime_types_list.set_vm_name(vm_name);
    mime_types_list.set_container_name(container_name);
    for (size_t i = 0; i < file_extensions.size(); ++i) {
      (*mime_types_list.mutable_mime_type_mappings())[file_extensions[i]] =
          mime_types[i];
    }
    service_->UpdateMimeTypes(mime_types_list);
    task_environment_.RunUntilIdle();
  }

  std::string GetMimeType(const std::string& filename) {
    return service_->GetMimeType(base::FilePath(filename), kTestVmName,
                                 kTestContainerName);
  }

  std::vector<std::string> GetExtensionTypesFromMimeTypes(
      std::set<std::string> supported_mime_types) {
    return service_->GetExtensionTypesFromMimeTypes(
        supported_mime_types, kTestVmName, kTestContainerName);
  }

 private:
  content::BrowserTaskEnvironment task_environment_;
  TestingProfile profile_;
  crostini::CrostiniTestHelper crostini_test_helper_;

  std::unique_ptr<GuestOsMimeTypesService> service_;
};

TEST_F(GuestOsMimeTypesServiceTest, SetAndGetMimeTypes) {
  // 'text/plain' is already mapped by system, so we will not store it.
  std::vector<std::string> file_extensions = {
      "foo", "bar", "gz", "xz", "tar.gz", "c", "C", "z", "🦈x", "txt"};
  std::vector<std::string> mime_types = {
      "x/foo", "x/bar", "x/gz", "x/xz",    "x/tar.gz",
      "x/c",   "x/C",   "x/z",  "x/shark", "text/plain"};

  // Mime types not registered yet.
  EXPECT_EQ("", GetMimeType("test.foo"));

  UpdateMimeTypes(file_extensions, mime_types, kTestVmName, kTestContainerName);

  EXPECT_EQ("x/foo", GetMimeType("test.foo"));
  EXPECT_EQ("x/bar", GetMimeType("test.bar"));
  // Use double extension if possible.
  EXPECT_EQ("x/tar.gz", GetMimeType("test.tar.gz"));
  // Fall back to final extension.
  EXPECT_EQ("x/xz", GetMimeType("test.tar.xz"));
  // Case insensitive match on extension.
  EXPECT_EQ("x/c", GetMimeType("test.c"));
  EXPECT_EQ("x/C", GetMimeType("test.C"));
  EXPECT_EQ("x/z", GetMimeType("test.z"));
  EXPECT_EQ("x/z", GetMimeType("test.Z"));
  EXPECT_EQ("x/tar.gz", GetMimeType("test.tar.GZ"));
  // Support unicode.
  EXPECT_EQ("x/shark", GetMimeType("test.🦈X"));
  // We only store items different to platform.
  EXPECT_EQ("", GetMimeType("test.txt"));
}

// Test that UpdateMimeTypes doesn't clobber MIME types from different VMs or
// containers.
TEST_F(GuestOsMimeTypesServiceTest, MultipleContainers) {
  UpdateMimeTypes({"foo"}, {"foo/mime"}, "vm 1", "container 1");
  UpdateMimeTypes({"bar"}, {"bar/mime"}, "vm 1", "container 2");
  UpdateMimeTypes({"foobar"}, {"foobar/mime"}, "vm 2", "container 1");

  EXPECT_EQ("foo/mime", service()->GetMimeType(base::FilePath("test.foo"),
                                               "vm 1", "container 1"));
  EXPECT_EQ("bar/mime", service()->GetMimeType(base::FilePath("test.bar"),
                                               "vm 1", "container 2"));
  EXPECT_EQ("foobar/mime", service()->GetMimeType(base::FilePath("test.foobar"),
                                                  "vm 2", "container 1"));

  EXPECT_EQ("", service()->GetMimeType(base::FilePath("test.bar"), "vm 1",
                                       "container 1"));
  EXPECT_EQ("", service()->GetMimeType(base::FilePath("test.foobar"), "vm 1",
                                       "container 2"));
  EXPECT_EQ("", service()->GetMimeType(base::FilePath("test.foo"), "vm 2",
                                       "container 1"));

  // Clobber bar with bar2 and ensure the old association is gone and new one is
  // there.
  UpdateMimeTypes({"bar2"}, {"bar2/mime"}, "vm 1", "container 2");
  EXPECT_EQ("bar2/mime", service()->GetMimeType(base::FilePath("test.bar2"),
                                                "vm 1", "container 2"));
  EXPECT_EQ("", service()->GetMimeType(base::FilePath("test.bar"), "vm 1",
                                       "container 2"));
}

// Test that ClearMimeTypes works, and only removes apps from the
// specified VM.
TEST_F(GuestOsMimeTypesServiceTest, ClearMimeTypes) {
  UpdateMimeTypes({"foo"}, {"foo/mime"}, "vm 1", "container 1");
  UpdateMimeTypes({"bar"}, {"bar/mime"}, "vm 1", "container 2");
  UpdateMimeTypes({"foobar"}, {"foobar/mime"}, "vm 2", "container 1");

  EXPECT_EQ("foo/mime", service()->GetMimeType(base::FilePath("test.foo"),
                                               "vm 1", "container 1"));
  EXPECT_EQ("bar/mime", service()->GetMimeType(base::FilePath("test.bar"),
                                               "vm 1", "container 2"));
  EXPECT_EQ("foobar/mime", service()->GetMimeType(base::FilePath("test.foobar"),
                                                  "vm 2", "container 1"));

  service()->ClearMimeTypes("vm 2", "");

  EXPECT_EQ("foo/mime", service()->GetMimeType(base::FilePath("test.foo"),
                                               "vm 1", "container 1"));
  EXPECT_EQ("bar/mime", service()->GetMimeType(base::FilePath("test.bar"),
                                               "vm 1", "container 2"));
  EXPECT_EQ("", service()->GetMimeType(base::FilePath("test.foobar"), "vm 2",
                                       "container 1"));
}

TEST_F(GuestOsMimeTypesServiceTest, SetMimeTypesAndGetExtensionTypes) {
  std::vector<std::string> mime_types = {"x/foo", "x/bar", "x/abcdef",
                                         "x/abcdef"};
  std::vector<std::string> file_extensions = {"foo", "bar", "abc", "def"};

  // Mime/ extension types not registered yet.
  std::vector<std::string> result = GetExtensionTypesFromMimeTypes({"x/foo"});
  EXPECT_EQ(0u, result.size());

  UpdateMimeTypes(file_extensions, mime_types, kTestVmName, kTestContainerName);
  result = GetExtensionTypesFromMimeTypes({"x/foo"});
  EXPECT_EQ(1u, result.size());
  EXPECT_EQ("foo", result[0]);

  result = GetExtensionTypesFromMimeTypes({"x/bar"});
  EXPECT_EQ(1u, result.size());
  EXPECT_EQ("bar", result[0]);

  // We should have 2 possible extensions for this mime type case.
  result = GetExtensionTypesFromMimeTypes({"x/abcdef"});
  EXPECT_EQ(2u, result.size());
  EXPECT_TRUE(base::Contains(result, "abc"));
  EXPECT_TRUE(base::Contains(result, "def"));
}

}  // namespace guest_os