text: // Copyright 2017 The Chromium OS Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  // How to build and run the tests: see arc_setup_util_unittest.cc
  
  #include "arc/setup/arc_setup.h"
  
  #include <set>
  
  #include <base/command_line.h>
  #include <base/environment.h>
  #include <base/files/file_path.h>
  #include <base/files/scoped_temp_dir.h>
  #include <base/macros.h>
  #include <base/stl_util.h>
  #include <gtest/gtest.h>
  
  #include "arc/setup/arc_setup_util.h"
  
  namespace arc {
  
  namespace {
  
  class MockArcMounter : public ArcMounter {
   public:
    MockArcMounter() = default;
    MockArcMounter(const MockArcMounter&) = delete;
    MockArcMounter& operator=(const MockArcMounter&) = delete;
  
    ~MockArcMounter() override = default;
  
    bool Mount(const std::string& source,
               const base::FilePath& target,
               const char* filesystem_type,
               unsigned long mount_flags,  // NOLINT(runtime/int)
               const char* data) override {
      mount_points_.insert(target.value());
      return true;
    }
  
    bool Remount(const base::FilePath& target_directory,
                 unsigned long mount_flags,  // NOLINT(runtime/int)
                 const char* data) override {
      return true;
    }
  
    bool LoopMount(const std::string& source,
                   const base::FilePath& target,
                   unsigned long mount_flags) override {  // NOLINT(runtime/int)
      loop_mount_points_.insert(target.value());
      return true;
    }
  
    bool BindMount(const base::FilePath& old_path,
                   const base::FilePath& new_path) override {
      mount_points_.insert(new_path.value());
      return true;
    }
  
    bool SharedMount(const base::FilePath& path) override { return true; }
  
    bool Umount(const base::FilePath& path) override {
      auto it = mount_points_.find(path.value());
      if (it == mount_points_.end())
        return false;
      mount_points_.erase(it);
      return true;
    }
  
    bool UmountIfExists(const base::FilePath& path) override {
      Umount(path);
      // If mount does not exist, succeed anyway.
      return true;
    }
  
    bool LoopUmount(const base::FilePath& path) override {
      auto it = loop_mount_points_.find(path.value());
      if (it == loop_mount_points_.end())
        return false;
      loop_mount_points_.erase(it);
      return true;
    }
  
    bool LoopUmountIfExists(const base::FilePath& path) override {
      LoopUmount(path);
      // If loop mount does not exist, succeed anyway.
      return true;
    }
  
    std::multiset<std::string> mount_points_;
    std::multiset<std::string> loop_mount_points_;
  };
  
  }  // namespace
  
  // Tests MockArcMounter itself.
  TEST(ArcSetup, TestMockArcMounter) {
    MockArcMounter mounter;
    EXPECT_TRUE(mounter.BindMount(base::FilePath("/a"), base::FilePath("/b")));
    EXPECT_TRUE(mounter.BindMount(base::FilePath("/c"), base::FilePath("/b")));
    EXPECT_EQ(2U, mounter.mount_points_.size());
    EXPECT_FALSE(mounter.Umount(base::FilePath("/x")));  // unknown path
    EXPECT_TRUE(mounter.Umount(base::FilePath("/b")));
    EXPECT_EQ(1U, mounter.mount_points_.size());
    EXPECT_TRUE(mounter.Umount(base::FilePath("/b")));
    EXPECT_TRUE(mounter.mount_points_.empty());
    EXPECT_FALSE(mounter.Umount(base::FilePath("/b")));  // now /b is unknown
  
    // Do the same for loop.
    EXPECT_TRUE(mounter.LoopMount("/a.img", base::FilePath("/d"), 0U));
    EXPECT_TRUE(mounter.LoopMount("/c.img", base::FilePath("/d"), 0U));
    EXPECT_EQ(2U, mounter.loop_mount_points_.size());
    EXPECT_FALSE(mounter.LoopUmount(base::FilePath("/x")));  // unknown path
    EXPECT_TRUE(mounter.LoopUmount(base::FilePath("/d")));
    EXPECT_EQ(1U, mounter.loop_mount_points_.size());
    EXPECT_TRUE(mounter.LoopUmount(base::FilePath("/d")));
    EXPECT_TRUE(mounter.loop_mount_points_.empty());
    EXPECT_FALSE(mounter.LoopUmount(base::FilePath("/d")));  // now /d is unknown
  }
  
  // Tests --mode=onetime-setup and --mode=onetime-stop.
  TEST(ArcSetup, TestOnetimeSetupStop) {
    const char* argv[] = {"test", "--mode=onetime-setup"};
    base::CommandLine::ForCurrentProcess()->InitFromArgv(base::size(argv), argv);
    std::unique_ptr<base::Environment> env(base::Environment::Create());
  
    // ArcSetup needs some config variables.
    base::ScopedTempDir temp_directory;
    ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
    const base::FilePath config_json(temp_directory.GetPath().Append("json"));
    ASSERT_TRUE(WriteToFile(config_json, 0700, "{\"WRITABLE_MOUNT\": false}"));
  
    ArcSetup setup(Mode::ONETIME_SETUP, config_json);
    setup.set_arc_mounter_for_testing(std::make_unique<MockArcMounter>());
  
    // Do the one-time setup and confirm both loop and non-loop mount points are
    // not empty.
    setup.MountOnOnetimeSetupForTesting();
    // Check that |loop_mount_points_| has system and vendor images etc.
    EXPECT_FALSE(
        static_cast<const MockArcMounter*>(setup.arc_mounter_for_testing())
            ->loop_mount_points_.empty());
  
    // Do the one-time stop and confirm all mount points are cleaned up.
    setup.UnmountOnOnetimeStopForTesting();
    EXPECT_TRUE(
        static_cast<const MockArcMounter*>(setup.arc_mounter_for_testing())
            ->mount_points_.empty());
    EXPECT_TRUE(
        static_cast<const MockArcMounter*>(setup.arc_mounter_for_testing())
            ->loop_mount_points_.empty());
  }
  
  }  // namespace arc
  