text:
// Copyright 2017 The Chromium 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 "chrome/browser/chromeos/tether/tether_service.h"

#include <memory>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/timer/mock_timer.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/ui/ash/network/tether_notification_presenter.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/components/multidevice/remote_device_test_util.h"
#include "chromeos/components/multidevice/software_feature.h"
#include "chromeos/components/tether/fake_notification_presenter.h"
#include "chromeos/components/tether/fake_tether_component.h"
#include "chromeos/components/tether/fake_tether_host_fetcher.h"
#include "chromeos/components/tether/tether_component_impl.h"
#include "chromeos/components/tether/tether_host_fetcher_impl.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/constants/chromeos_switches.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/dbus/power_manager/suspend.pb.h"
#include "chromeos/dbus/shill/shill_device_client.h"
#include "chromeos/dbus/shill/shill_manager_client.h"
#include "chromeos/dbus/shill/shill_service_client.h"
#include "chromeos/network/network_connect.h"
#include "chromeos/network/network_handler.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/network_type_pattern.h"
#include "chromeos/services/device_sync/cryptauth_device_manager.h"
#include "chromeos/services/device_sync/cryptauth_enroller.h"
#include "chromeos/services/device_sync/cryptauth_enrollment_manager.h"
#include "chromeos/services/device_sync/fake_cryptauth_enrollment_manager.h"
#include "chromeos/services/device_sync/fake_remote_device_provider.h"
#include "chromeos/services/device_sync/public/cpp/device_sync_client_impl.h"
#include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
#include "chromeos/services/device_sync/remote_device_provider_impl.h"
#include "chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
#include "chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h"
#include "chromeos/services/multidevice_setup/public/cpp/prefs.h"
#include "chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
#include "chromeos/services/secure_channel/public/cpp/client/secure_channel_client_impl.h"
#include "components/prefs/testing_pref_service.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/user_manager/scoped_user_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"

using testing::Invoke;
using testing::NiceMock;
using testing::Return;

namespace {

const char kTestUserPrivateKey[] = "kTestUserPrivateKey";
const size_t kNumTestDevices = 5;

chromeos::multidevice::RemoteDeviceRefList CreateTestDevices() {
  chromeos::multidevice::RemoteDeviceRefList list;
  for (size_t i = 0; i < kNumTestDevices; ++i) {
    list.push_back(
        chromeos::multidevice::RemoteDeviceRefBuilder()
            .SetSupportsMobileHotspot(true)
            .SetSoftwareFeatureState(
                chromeos::multidevice::SoftwareFeature::kBetterTogetherHost,
                chromeos::multidevice::SoftwareFeatureState::kSupported)
            .Build());
  }
  return list;
}

class MockExtendedBluetoothAdapter : public device::MockBluetoothAdapter {
 public:
  void SetAdvertisingInterval(
      const base::TimeDelta& min,
      const base::TimeDelta& max,
      const base::Closure& callback,
      const AdvertisementErrorCallback& error_callback) override {
    if (is_ble_advertising_supported_) {
      callback.Run();
    } else {
      error_callback.Run(device::BluetoothAdvertisement::ErrorCode::
                             ERROR_INVALID_ADVERTISEMENT_INTERVAL);
    }
  }

  void set_is_ble_advertising_supported(bool is_ble_advertising_supported) {
    is_ble_advertising_supported_ = is_ble_advertising_supported;
  }

 protected:
  ~MockExtendedBluetoothAdapter() override {}

 private:
  bool is_ble_advertising_supported_ = true;
};

class TestTetherService : public TetherService {
 public:
  TestTetherService(
      Profile* profile,
      chromeos::PowerManagerClient* power_manager_client,
      chromeos::device_sync::DeviceSyncClient* device_sync_client,
      chromeos::secure_channel::SecureChannelClient* secure_channel_client,
      chromeos::multidevice_setup::MultiDeviceSetupClient*
          multidevice_setup_client,
      chromeos::NetworkStateHandler* network_state_handler,
      session_manager::SessionManager* session_manager)
      : TetherService(profile,
                      power_manager_client,
                      device_sync_client,
                      secure_channel_client,
                      multidevice_setup_client,
                      network_state_handler,
                      session_manager) {}
  ~TestTetherService() override {}

  int updated_technology_state_count() {
    return updated_technology_state_count_;
  }

 protected:
  void UpdateTetherTechnologyState() override {
    updated_technology_state_count_++;
    TetherService::UpdateTetherTechnologyState();
  }

 private:
  int updated_technology_state_count_ = 0;
};

class FakeTetherComponentWithDestructorCallback
    : public chromeos::tether::FakeTetherComponent {
 public:
  FakeTetherComponentWithDestructorCallback(
      const base::Closure& destructor_callback)
      : FakeTetherComponent(false /* has_asynchronous_shutdown */),
        destructor_callback_(destructor_callback) {}

  ~FakeTetherComponentWithDestructorCallback() override {
    destructor_callback_.Run();
  }

 private:
  base::Closure destructor_callback_;
};

class TestTetherComponentFactory final
    : public chromeos::tether::TetherComponentImpl::Factory {
 public:
  TestTetherComponentFactory() {}

  // Returns nullptr if no TetherComponent has been created or if the last one
  // that was created has already been deleted.
  FakeTetherComponentWithDestructorCallback* active_tether_component() {
    return active_tether_component_;
  }

  // chromeos::tether::TetherComponentImpl::Factory:
  std::unique_ptr<chromeos::tether::TetherComponent> CreateInstance(
      chromeos::device_sync::DeviceSyncClient* device_sync_client,
      chromeos::secure_channel::SecureChannelClient* secure_channel_client,
      chromeos::tether::TetherHostFetcher* tether_host_fetcher,
      chromeos::tether::NotificationPresenter* notification_presenter,
      chromeos::tether::GmsCoreNotificationsStateTrackerImpl*
          gms_core_notifications_state_tracker,
      PrefService* pref_service,
      chromeos::NetworkStateHandler* network_state_handler,
      chromeos::ManagedNetworkConfigurationHandler*
          managed_network_configuration_handler,
      chromeos::NetworkConnect* network_connect,
      chromeos::NetworkConnectionHandler* network_connection_handler,
      scoped_refptr<device::BluetoothAdapter> adapter,
      session_manager::SessionManager* session_manager) override {
    active_tether_component_ = new FakeTetherComponentWithDestructorCallback(
        base::Bind(&TestTetherComponentFactory::OnActiveTetherComponentDeleted,
                   base::Unretained(this)));
    was_tether_component_active_ = true;
    return base::WrapUnique(active_tether_component_);
  }

  bool was_tether_component_active() { return was_tether_component_active_; }

  const chromeos::tether::TetherComponent::ShutdownReason&
  last_shutdown_reason() {
    return last_shutdown_reason_;
  }

 private:
  void OnActiveTetherComponentDeleted() {
    last_shutdown_reason_ = *active_tether_component_->last_shutdown_reason();
    active_tether_component_ = nullptr;
  }

  FakeTetherComponentWithDestructorCallback* active_tether_component_ = nullptr;
  bool was_tether_component_active_ = false;
  chromeos::tether::TetherComponent::ShutdownReason last_shutdown_reason_;
};

class FakeRemoteDeviceProviderFactory
    : public chromeos::device_sync::RemoteDeviceProviderImpl::Factory {
 public:
  FakeRemoteDeviceProviderFactory() = default;
  ~FakeRemoteDeviceProviderFactory() override = default;

  // chromeos::device_sync::RemoteDeviceProviderImpl::Factory:
  std::unique_ptr<chromeos::device_sync::RemoteDeviceProvider> CreateInstance(
      chromeos::device_sync::CryptAuthDeviceManager* device_manager,
      chromeos::device_sync::CryptAuthV2DeviceManager* v2_device_manager,
      const std::string& user_email,
      const std::string& user_private_key) override {
    return std::make_unique<chromeos::device_sync::FakeRemoteDeviceProvider>();
  }
};

class FakeTetherHostFetcherFactory
    : public chromeos::tether::TetherHostFetcherImpl::Factory {
 public:
  FakeTetherHostFetcherFactory(
      const chromeos::multidevice::RemoteDeviceRefList& initial_devices)
      : initial_devices_(initial_devices) {}
  virtual ~FakeTetherHostFetcherFactory() = default;

  chromeos::tether::FakeTetherHostFetcher* last_created() {
    return last_created_;
  }

  void SetNoInitialDevices() { initial_devices_.clear(); }

  // chromeos::tether::TetherHostFetcherImpl::Factory :
  std::unique_ptr<chromeos::tether::TetherHostFetcher> CreateInstance(
      chromeos::device_sync::DeviceSyncClient* device_sync_client,
      chromeos::multidevice_setup::MultiDeviceSetupClient*
          multidevice_setup_client) override {
    last_created_ =
        new chromeos::tether::FakeTetherHostFetcher(initial_devices_);
    return base::WrapUnique(last_created_);
  }

 private:
  chromeos::multidevice::RemoteDeviceRefList initial_devices_;
  chromeos::tether::FakeTetherHostFetcher* last_created_ = nullptr;
};

class FakeDeviceSyncClientImplFactory
    : public chromeos::device_sync::DeviceSyncClientImpl::Factory {
 public:
  FakeDeviceSyncClientImplFactory() = default;

  ~FakeDeviceSyncClientImplFactory() override = default;

  // chromeos::device_sync::DeviceSyncClientImpl::Factory:
  std::unique_ptr<chromeos::device_sync::DeviceSyncClient> CreateInstance()
      override {
    auto fake_device_sync_client =
        std::make_unique<chromeos::device_sync::FakeDeviceSyncClient>();
    fake_device_sync_client->NotifyReady();
    return fake_device_sync_client;
  }
};

class FakeSecureChannelClientImplFactory
    : public chromeos::secure_channel::SecureChannelClientImpl::Factory {
 public:
  FakeSecureChannelClientImplFactory() = default;

  ~FakeSecureChannelClientImplFactory() override = default;

  // chromeos::secure_channel::SecureChannelClientImpl::Factory:
  std::unique_ptr<chromeos::secure_channel::SecureChannelClient> CreateInstance(
      mojo::PendingRemote<chromeos::secure_channel::mojom::SecureChannel>
          channel,
      scoped_refptr<base::TaskRunner> task_runner) override {
    return std::make_unique<
        chromeos::secure_channel::FakeSecureChannelClient>();
  }
};

class FakeMultiDeviceSetupClientImplFactory
    : public chromeos::multidevice_setup::MultiDeviceSetupClientImpl::Factory {
 public:
  FakeMultiDeviceSetupClientImplFactory() = default;

  ~FakeMultiDeviceSetupClientImplFactory() override = default;

  // chromeos::multidevice_setup::MultiDeviceSetupClientImpl::Factory:
  std::unique_ptr<chromeos::multidevice_setup::MultiDeviceSetupClient>
  CreateInstance(
      mojo::PendingRemote<chromeos::multidevice_setup::mojom::MultiDeviceSetup>)
      override {
    auto fake_multidevice_setup_client = std::make_unique<
        chromeos::multidevice_setup::FakeMultiDeviceSetupClient>();
    fake_multidevice_setup_client_ = fake_multidevice_setup_client.get();
    return fake_multidevice_setup_client;
  }

  chromeos::multidevice_setup::FakeMultiDeviceSetupClient*
  fake_multidevice_setup_client() {
    return fake_multidevice_setup_client_;
  }

 private:
  chromeos::multidevice_setup::FakeMultiDeviceSetupClient*
      fake_multidevice_setup_client_;
};

}  // namespace

class TetherServiceTest : public testing::Test {
 protected:
  TetherServiceTest() : test_devices_(CreateTestDevices()) {}
  ~TetherServiceTest() override {}

  void SetUp() override {
    fake_notification_presenter_ = nullptr;
    mock_timer_ = nullptr;

    chromeos::DBusThreadManager::Initialize();
    manager_test_ = chromeos::DBusThreadManager::Get()
                        ->GetShillManagerClient()
                        ->GetTestInterface();

    chromeos::NetworkHandler::Initialize();
    chromeos::NetworkConnect::Initialize(nullptr);

    TestingProfile::Builder builder;
    profile_ = builder.Build();

    fake_chrome_user_manager_ = new chromeos::FakeChromeUserManager();
    scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
        base::WrapUnique(fake_chrome_user_manager_));

    chromeos::PowerManagerClient::InitializeFake();

    fake_device_sync_client_ =
        std::make_unique<chromeos::device_sync::FakeDeviceSyncClient>();
    fake_device_sync_client_->NotifyReady();

    fake_device_sync_client_impl_factory_ =
        std::make_unique<FakeDeviceSyncClientImplFactory>();
    chromeos::device_sync::DeviceSyncClientImpl::Factory::SetFactoryForTesting(
        fake_device_sync_client_impl_factory_.get());

    fake_secure_channel_client_ =
        std::make_unique<chromeos::secure_channel::FakeSecureChannelClient>();
    fake_secure_channel_client_impl_factory_ =
        std::make_unique<FakeSecureChannelClientImplFactory>();
    chromeos::secure_channel::SecureChannelClientImpl::Factory::
        SetFactoryForTesting(fake_secure_channel_client_impl_factory_.get());

    fake_multidevice_setup_client_ = std::make_unique<
        chromeos::multidevice_setup::FakeMultiDeviceSetupClient>();
    fake_multidevice_setup_client_impl_factory_ =
        std::make_unique<FakeMultiDeviceSetupClientImplFactory>();
    chromeos::multidevice_setup::MultiDeviceSetupClientImpl::Factory::
        SetFactoryForTesting(fake_multidevice_setup_client_impl_factory_.get());
    initial_feature_state_ =
        chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser;

    fake_enrollment_manager_ = std::make_unique<
        chromeos::device_sync::FakeCryptAuthEnrollmentManager>();
    fake_enrollment_manager_->set_user_private_key(kTestUserPrivateKey);

    mock_adapter_ =
        base::MakeRefCounted<NiceMock<MockExtendedBluetoothAdapter>>();
    SetIsBluetoothPowered(true);
    is_adapter_present_ = true;
    ON_CALL(*mock_adapter_, IsPresent())
        .WillByDefault(Invoke(this, &TetherServiceTest::IsBluetoothPresent));
    ON_CALL(*mock_adapter_, IsPowered())
        .WillByDefault(Invoke(this, &TetherServiceTest::IsBluetoothPowered));
    device::BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);

    test_tether_component_factory_ =
        base::WrapUnique(new TestTetherComponentFactory());
    chromeos::tether::TetherComponentImpl::Factory::SetFactoryForTesting(
        test_tether_component_factory_.get());
    shutdown_reason_verified_ = false;

    fake_remote_device_provider_factory_ =
        base::WrapUnique(new FakeRemoteDeviceProviderFactory());
    chromeos::device_sync::RemoteDeviceProviderImpl::Factory::
        SetFactoryForTesting(fake_remote_device_provider_factory_.get());

    fake_tether_host_fetcher_factory_ =
        base::WrapUnique(new FakeTetherHostFetcherFactory(test_devices_));
    chromeos::tether::TetherHostFetcherImpl::Factory::SetFactoryForTesting(
        fake_tether_host_fetcher_factory_.get());

    TestingBrowserProcess::GetGlobal()->SetLocalState(&local_pref_service_);
    RegisterLocalState(local_pref_service_.registry());
  }

  void TearDown() override {
    TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);

    chromeos::device_sync::DeviceSyncClientImpl::Factory::SetFactoryForTesting(
        nullptr);
    chromeos::secure_channel::SecureChannelClientImpl::Factory::
        SetFactoryForTesting(nullptr);
    chromeos::multidevice_setup::MultiDeviceSetupClientImpl::Factory::
        SetFactoryForTesting(nullptr);

    ShutdownTetherService();

    if (tether_service_) {
      // As of crbug.com/798605, SHUT_DOWN should not be logged since it does
      // not contribute meaningful data.
      histogram_tester_.ExpectBucketCount(
          "InstantTethering.FeatureState",
          TetherService::TetherFeatureState::SHUT_DOWN, 0 /* count */);
      tether_service_.reset();
    }

    EXPECT_EQ(test_tether_component_factory_->was_tether_component_active(),
              shutdown_reason_verified_);

    chromeos::PowerManagerClient::Shutdown();
    chromeos::NetworkConnect::Shutdown();
    chromeos::NetworkHandler::Shutdown();
    chromeos::DBusThreadManager::Shutdown();
  }

  void SetPrimaryUserLoggedIn() {
    const AccountId account_id(
        AccountId::FromUserEmail(profile_->GetProfileUserName()));
    const user_manager::User* user =
        fake_chrome_user_manager_->AddPublicAccountUser(account_id);
    fake_chrome_user_manager_->UserLoggedIn(account_id, user->username_hash(),
                                            false /* browser_restart */,
                                            false /* is_child */);
  }

  void CreateTetherService() {
    fake_multidevice_setup_client_->SetFeatureState(
        chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
        initial_feature_state_);

    tether_service_ = base::WrapUnique(new TestTetherService(
        profile_.get(), chromeos::FakePowerManagerClient::Get(),
        fake_device_sync_client_.get(), fake_secure_channel_client_.get(),
        fake_multidevice_setup_client_.get(), network_state_handler(),
        nullptr /* session_manager */));

    fake_notification_presenter_ =
        new chromeos::tether::FakeNotificationPresenter();
    mock_timer_ = new base::MockOneShotTimer();
    tether_service_->SetTestDoubles(
        base::WrapUnique(fake_notification_presenter_),
        base::WrapUnique(mock_timer_));

    SetPrimaryUserLoggedIn();

    // Ensure that TetherService does not prematurely update its
    // TechnologyState before it fetches the BluetoothAdapter.
    EXPECT_EQ(
        chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
        network_state_handler()->GetTechnologyState(
            chromeos::NetworkTypePattern::Tether()));
    VerifyTetherActiveStatus(false /* expected_active */);

    if (!fake_device_sync_client_->is_ready()) {
      return;
    }

    // Allow the posted task to fetch the BluetoothAdapter to finish.
    base::RunLoop().RunUntilIdle();
  }

  void ShutdownTetherService() {
    if (tether_service_)
      tether_service_->Shutdown();
  }

  void SetTetherTechnologyStateEnabled(bool enabled) {
    network_state_handler()->SetTetherTechnologyState(
        enabled
            ? chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED
            : chromeos::NetworkStateHandler::TechnologyState::
                  TECHNOLOGY_AVAILABLE);
  }

  void SetCellularTechnologyStateEnabled(bool enabled) {
    network_state_handler()->SetTechnologyEnabled(
        chromeos::NetworkTypePattern::Cellular(), enabled,
        chromeos::network_handler::ErrorCallback());
    base::RunLoop().RunUntilIdle();
  }

  void SetIsBluetoothPowered(bool powered) {
    is_adapter_powered_ = powered;
    for (auto& observer : mock_adapter_->GetObservers())
      observer.AdapterPoweredChanged(mock_adapter_.get(), powered);
  }

  void set_is_adapter_present(bool present) { is_adapter_present_ = present; }

  bool IsBluetoothPresent() { return is_adapter_present_; }
  bool IsBluetoothPowered() { return is_adapter_powered_; }

  void RemoveWifiFromSystem() {
    manager_test_->RemoveTechnology(shill::kTypeWifi);
    base::RunLoop().RunUntilIdle();
  }

  void DisconnectDefaultShillNetworks() {
    const chromeos::NetworkState* default_state;
    while ((default_state = network_state_handler()->DefaultNetwork())) {
      chromeos::DBusThreadManager::Get()
          ->GetShillServiceClient()
          ->GetTestInterface()
          ->SetServiceProperty(default_state->path(), shill::kStateProperty,
                               base::Value(shill::kStateIdle));
      base::RunLoop().RunUntilIdle();
    }
  }

  void VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState expected_technology_state_and_reason,
      int expected_count) {
    histogram_tester_.ExpectBucketCount("InstantTethering.FeatureState",
                                        expected_technology_state_and_reason,
                                        expected_count);
  }

  void VerifyTetherActiveStatus(bool expected_active) {
    EXPECT_EQ(
        expected_active,
        test_tether_component_factory_->active_tether_component() != nullptr);
  }

  void VerifyLastShutdownReason(
      const chromeos::tether::TetherComponent::ShutdownReason&
          expected_shutdown_reason) {
    EXPECT_EQ(expected_shutdown_reason,
              test_tether_component_factory_->last_shutdown_reason());
    shutdown_reason_verified_ = true;
  }

  chromeos::ShillManagerClient::TestInterface* manager_test() {
    return manager_test_;
  }

  chromeos::NetworkStateHandler* network_state_handler() {
    return chromeos::NetworkHandler::Get()->network_state_handler();
  }

  const chromeos::multidevice::RemoteDeviceRefList test_devices_;
  const content::BrowserTaskEnvironment task_environment_;

  std::unique_ptr<TestingProfile> profile_;
  chromeos::FakeChromeUserManager* fake_chrome_user_manager_;
  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
  std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
      test_pref_service_;
  std::unique_ptr<TestTetherComponentFactory> test_tether_component_factory_;
  std::unique_ptr<FakeRemoteDeviceProviderFactory>
      fake_remote_device_provider_factory_;
  std::unique_ptr<FakeTetherHostFetcherFactory>
      fake_tether_host_fetcher_factory_;
  chromeos::tether::FakeNotificationPresenter* fake_notification_presenter_;
  base::MockOneShotTimer* mock_timer_;
  std::unique_ptr<chromeos::device_sync::FakeDeviceSyncClient>
      fake_device_sync_client_;
  std::unique_ptr<FakeDeviceSyncClientImplFactory>
      fake_device_sync_client_impl_factory_;
  std::unique_ptr<chromeos::secure_channel::FakeSecureChannelClient>
      fake_secure_channel_client_;
  std::unique_ptr<FakeSecureChannelClientImplFactory>
      fake_secure_channel_client_impl_factory_;
  std::unique_ptr<chromeos::multidevice_setup::FakeMultiDeviceSetupClient>
      fake_multidevice_setup_client_;
  std::unique_ptr<FakeMultiDeviceSetupClientImplFactory>
      fake_multidevice_setup_client_impl_factory_;
  std::unique_ptr<chromeos::device_sync::FakeCryptAuthEnrollmentManager>
      fake_enrollment_manager_;

  chromeos::multidevice_setup::mojom::FeatureState initial_feature_state_;

  scoped_refptr<MockExtendedBluetoothAdapter> mock_adapter_;
  bool is_adapter_present_;
  bool is_adapter_powered_;
  bool shutdown_reason_verified_;

  // PrefService which contains the browser process' local storage.
  TestingPrefServiceSimple local_pref_service_;

  std::unique_ptr<TestTetherService> tether_service_;

  base::HistogramTester histogram_tester_;

  chromeos::ShillManagerClient::TestInterface* manager_test_ = nullptr;

 private:
  DISALLOW_COPY_AND_ASSIGN(TetherServiceTest);
};

TEST_F(TetherServiceTest, TestShutdown) {
  CreateTetherService();
  VerifyTetherActiveStatus(true /* expected_active */);

  ShutdownTetherService();

  // The TechnologyState should not have changed due to Shutdown() being called.
  // If it had changed, any settings UI that was previously open would have
  // shown visual jank.
  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::USER_LOGGED_OUT);
}

// TODO(https://crbug.com/893878): Fix disabled test.
TEST_F(TetherServiceTest, DISABLED_TestAsyncTetherShutdown) {
  CreateTetherService();

  // Tether should be ENABLED, and there should be no AsyncShutdownTask.
  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  // Use an asynchronous shutdown.
  test_tether_component_factory_->active_tether_component()
      ->set_has_asynchronous_shutdown(true);

  // Disable the Tether preference. This should trigger the asynchrnous
  // shutdown.
  SetTetherTechnologyStateEnabled(false);

  // Tether should be active, but shutting down.
  VerifyTetherActiveStatus(true /* expected_active */);
  EXPECT_EQ(
      chromeos::tether::TetherComponent::Status::SHUTTING_DOWN,
      test_tether_component_factory_->active_tether_component()->status());

  // Tether should be AVAILABLE.
  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));

  // Complete the shutdown process; TetherService should delete its
  // TetherComponent instance.
  test_tether_component_factory_->active_tether_component()
      ->FinishAsynchronousShutdown();
  VerifyTetherActiveStatus(false /* expected_active */);
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::PREF_DISABLED);
}

TEST_F(TetherServiceTest, TestSuspend) {
  CreateTetherService();
  VerifyTetherActiveStatus(true /* expected_active */);

  chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
      power_manager::SuspendImminent_Reason_OTHER);

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  chromeos::FakePowerManagerClient::Get()->SendSuspendDone();

  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
      power_manager::SuspendImminent_Reason_OTHER);

  VerifyTetherFeatureStateRecorded(TetherService::TetherFeatureState::SUSPENDED,
                                   2 /* expected_count */);
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::USER_CLOSED_LID);
}

TEST_F(TetherServiceTest, TestDeviceSyncClientNotReady) {
  fake_device_sync_client_ =
      std::make_unique<chromeos::device_sync::FakeDeviceSyncClient>();

  CreateTetherService();

  fake_device_sync_client_->NotifyReady();
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  ShutdownTetherService();
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::USER_LOGGED_OUT);
}

TEST_F(TetherServiceTest,
       TestMultiDeviceSetupClientInitiallyHasNoVerifiedHost) {
  fake_tether_host_fetcher_factory_->SetNoInitialDevices();
  initial_feature_state_ = chromeos::multidevice_setup::mojom::FeatureState::
      kUnavailableNoVerifiedHost;

  CreateTetherService();

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  fake_tether_host_fetcher_factory_->last_created()->set_tether_hosts(
      test_devices_);
  fake_multidevice_setup_client_->SetFeatureState(
      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);

  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  ShutdownTetherService();
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::USER_LOGGED_OUT);
}

TEST_F(TetherServiceTest, TestMultiDeviceSetupClientLosesVerifiedHost) {
  CreateTetherService();

  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  fake_tether_host_fetcher_factory_->last_created()->set_tether_hosts({});
  fake_multidevice_setup_client_->SetFeatureState(
      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
      chromeos::multidevice_setup::mojom::FeatureState::
          kUnavailableNoVerifiedHost);

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  mock_timer_->Fire();
  ShutdownTetherService();
  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::NO_AVAILABLE_HOSTS,
      1 /* expected_count */);
  VerifyLastShutdownReason(chromeos::tether::TetherComponent::ShutdownReason::
                               MULTIDEVICE_HOST_UNVERIFIED);
}

TEST_F(TetherServiceTest, TestBetterTogetherSuiteInitiallyDisabled) {
  initial_feature_state_ = chromeos::multidevice_setup::mojom::FeatureState::
      kUnavailableSuiteDisabled;

  CreateTetherService();

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  fake_multidevice_setup_client_->SetFeatureState(
      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);

  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  ShutdownTetherService();
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::USER_LOGGED_OUT);
}

TEST_F(TetherServiceTest, TestBetterTogetherSuiteBecomesDisabled) {
  CreateTetherService();

  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  fake_multidevice_setup_client_->SetFeatureState(
      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
      chromeos::multidevice_setup::mojom::FeatureState::
          kUnavailableSuiteDisabled);

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  ShutdownTetherService();
  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::BETTER_TOGETHER_SUITE_DISABLED,
      1 /* expected_count */);
  VerifyLastShutdownReason(chromeos::tether::TetherComponent::ShutdownReason::
                               BETTER_TOGETHER_SUITE_DISABLED);
}

TEST_F(TetherServiceTest, TestBleAdvertisingNotSupported) {
  mock_adapter_->set_is_ble_advertising_supported(false);

  CreateTetherService();

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::BLE_ADVERTISING_NOT_SUPPORTED,
      1 /* expected_count */);
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::OTHER);
}

TEST_F(TetherServiceTest,
       TestBleAdvertisingNotSupported_BluetoothIsInitiallyNotPowered) {
  SetIsBluetoothPowered(false);

  mock_adapter_->set_is_ble_advertising_supported(false);

  CreateTetherService();

  // TetherService has not yet been able to find out that BLE advertising is not
  // supported.
  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNINITIALIZED,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);
  EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(
      prefs::kInstantTetheringBleAdvertisingSupported));

  SetIsBluetoothPowered(true);

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);
  EXPECT_FALSE(profile_->GetPrefs()->GetBoolean(
      prefs::kInstantTetheringBleAdvertisingSupported));

  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::BLE_ADVERTISING_NOT_SUPPORTED,
      1 /* expected_count */);
}

TEST_F(
    TetherServiceTest,
    TestBleAdvertisingNotSupportedAndRecorded_BluetoothIsInitiallyNotPowered) {
  SetIsBluetoothPowered(false);

  mock_adapter_->set_is_ble_advertising_supported(false);

  // Simulate a login after we determined that BLE advertising is not supported.
  profile_->GetPrefs()->SetBoolean(
      prefs::kInstantTetheringBleAdvertisingSupported, false);

  CreateTetherService();

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  SetIsBluetoothPowered(true);

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::BLE_ADVERTISING_NOT_SUPPORTED,
      1 /* expected_count */);
}

TEST_F(TetherServiceTest, TestBleAdvertisingSupportedButIncorrectlyRecorded) {
  // Simulate a login after we incorrectly determined that BLE advertising is
  // not supported (this is not an expected case, but may have happened if
  // BluetoothAdapter::SetAdvertisingInterval() failed for a weird, one-off
  // reason).
  profile_->GetPrefs()->SetBoolean(
      prefs::kInstantTetheringBleAdvertisingSupported, false);

  CreateTetherService();

  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);
  EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(
      prefs::kInstantTetheringBleAdvertisingSupported));

  VerifyTetherFeatureStateRecorded(TetherService::TetherFeatureState::ENABLED,
                                   1 /* expected_count */);

  ShutdownTetherService();
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::USER_LOGGED_OUT);
}

TEST_F(TetherServiceTest, TestGet_NotPrimaryUser_FeatureFlagDisabled) {
  EXPECT_FALSE(TetherService::Get(profile_.get()));
}

TEST_F(TetherServiceTest, TestGet_PrimaryUser_FeatureFlagDisabled) {
  SetPrimaryUserLoggedIn();
  EXPECT_FALSE(TetherService::Get(profile_.get()));
}

TEST_F(TetherServiceTest, TestGet_NotPrimaryUser_FeatureFlagEnabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(chromeos::features::kInstantTethering);

  EXPECT_FALSE(TetherService::Get(profile_.get()));
}

// TODO(https://crbug.com/893878): Fix disabled test.
TEST_F(TetherServiceTest, DISABLED_TestGet_PrimaryUser_FeatureFlagEnabled) {
  SetPrimaryUserLoggedIn();

  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      {chromeos::features::kInstantTethering} /* enabled_features */,
      {} /* disabled_features */);

  TetherService* tether_service = TetherService::Get(profile_.get());
  ASSERT_TRUE(tether_service);

  base::RunLoop().RunUntilIdle();
  tether_service->Shutdown();

  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::USER_LOGGED_OUT);
}

// TODO(https://crbug.com/893878): Fix disabled test.
TEST_F(
    TetherServiceTest,
    DISABLED_TestGet_PrimaryUser_FeatureFlagEnabled_MultiDeviceApiFlagEnabled) {
  SetPrimaryUserLoggedIn();

  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      {chromeos::features::kInstantTethering} /* enabled_features */,
      {} /* disabled_features */);

  TetherService* tether_service = TetherService::Get(profile_.get());
  ASSERT_TRUE(tether_service);

  base::RunLoop().RunUntilIdle();
  tether_service->Shutdown();

  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::USER_LOGGED_OUT);
}

// TODO(https://crbug.com/893878): Fix disabled test.
TEST_F(
    TetherServiceTest,
    DISABLED_TestGet_PrimaryUser_FeatureFlagEnabled_MultiDeviceApiAndMultiDeviceSetupFlagsEnabled) {
  SetPrimaryUserLoggedIn();

  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      {chromeos::features::kInstantTethering} /* enabled_features */,
      {} /* disabled_features */);

  TetherService* tether_service = TetherService::Get(profile_.get());
  ASSERT_TRUE(tether_service);

  fake_multidevice_setup_client_impl_factory_->fake_multidevice_setup_client()
      ->SetFeatureState(
          chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
          chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);

  base::RunLoop().RunUntilIdle();
  tether_service->Shutdown();

  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::USER_LOGGED_OUT);
}

TEST_F(TetherServiceTest, TestNoTetherHosts) {
  fake_tether_host_fetcher_factory_->SetNoInitialDevices();
  CreateTetherService();

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  // Simulate this being the final state of Tether by passing time.
  mock_timer_->Fire();

  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::NO_AVAILABLE_HOSTS,
      1 /* expected_count */);
}

// TODO(https://crbug.com/893878): Fix disabled test.
TEST_F(TetherServiceTest, DISABLED_TestProhibitedByPolicy) {
  profile_->GetPrefs()->SetBoolean(
      chromeos::multidevice_setup::kInstantTetheringAllowedPrefName, false);

  CreateTetherService();

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_PROHIBITED,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::PROHIBITED, 1 /* expected_count */);
}

TEST_F(TetherServiceTest, TestBluetoothNotPresent) {
  set_is_adapter_present(false);

  CreateTetherService();

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  // Simulate this being the final state of Tether by passing time.
  mock_timer_->Fire();

  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::BLE_NOT_PRESENT,
      1 /* expected_count */);
}

TEST_F(TetherServiceTest, TestMetricsFalsePositives) {
  set_is_adapter_present(false);
  fake_tether_host_fetcher_factory_->SetNoInitialDevices();
  CreateTetherService();

  set_is_adapter_present(true);
  SetIsBluetoothPowered(true);

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  fake_tether_host_fetcher_factory_->last_created()->set_tether_hosts(
      test_devices_);
  fake_tether_host_fetcher_factory_->last_created()->NotifyTetherHostsUpdated();

  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  // No metric should have been recorded for BLE_NOT_PRESENT and
  // NO_AVAILABLE_HOSTS, but ENABLED should have been recorded.
  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::BLE_NOT_PRESENT,
      0 /* expected_count */);
  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::NO_AVAILABLE_HOSTS,
      0 /* expected_count */);
  VerifyTetherFeatureStateRecorded(TetherService::TetherFeatureState::ENABLED,
                                   1 /* expected_count */);

  // Ensure that the pending state recording has been canceled.
  ASSERT_FALSE(mock_timer_->IsRunning());

  ShutdownTetherService();
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::USER_LOGGED_OUT);
}

TEST_F(TetherServiceTest, TestWifiNotPresent) {
  RemoveWifiFromSystem();

  CreateTetherService();

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));

  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::WIFI_NOT_PRESENT,
      1 /* expected_count */);
}

TEST_F(TetherServiceTest, TestIsBluetoothPowered) {
  SetIsBluetoothPowered(false);

  CreateTetherService();

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNINITIALIZED,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  SetIsBluetoothPowered(true);

  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  SetIsBluetoothPowered(false);

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNINITIALIZED,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::BLUETOOTH_DISABLED,
      2 /* expected_count */);
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::BLUETOOTH_DISABLED);
}

// TODO(https://crbug.com/893878): Fix disabled test.
TEST_F(TetherServiceTest, DISABLED_TestCellularIsUnavailable) {
  manager_test()->RemoveTechnology(shill::kTypeCellular);
  ASSERT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Cellular()));

  CreateTetherService();

  SetTetherTechnologyStateEnabled(false);
  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::PREF_DISABLED);

  SetTetherTechnologyStateEnabled(true);
  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  VerifyTetherFeatureStateRecorded(TetherService::TetherFeatureState::ENABLED,
                                   2 /* expected_count */);
}

// TODO(https://crbug.com/893878): Fix disabled test.
TEST_F(TetherServiceTest, DISABLED_TestCellularIsAvailable) {
  // TODO (lesliewatkins): Investigate why cellular needs to be removed and
  // re-added for NetworkStateHandler to return the correct TechnologyState.
  manager_test()->RemoveTechnology(shill::kTypeCellular);
  manager_test()->AddTechnology(shill::kTypeCellular, false);

  CreateTetherService();

  // Cellular disabled
  SetCellularTechnologyStateEnabled(false);
  ASSERT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Cellular()));
  VerifyTetherActiveStatus(false /* expected_active */);

  SetTetherTechnologyStateEnabled(false);
  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  SetTetherTechnologyStateEnabled(true);
  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  // Cellular enabled
  SetCellularTechnologyStateEnabled(true);
  ASSERT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Cellular()));
  VerifyTetherActiveStatus(true /* expected_active */);

  SetTetherTechnologyStateEnabled(false);
  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);

  SetTetherTechnologyStateEnabled(true);
  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  SetCellularTechnologyStateEnabled(false);

  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::CELLULAR_DISABLED,
      2 /* expected_count */);
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::CELLULAR_DISABLED);
}

// TODO(https://crbug.com/893878): Fix disabled test.
TEST_F(TetherServiceTest, DISABLED_TestDisabled) {
  profile_->GetPrefs()->SetBoolean(
      chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, false);

  CreateTetherService();

  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  EXPECT_FALSE(profile_->GetPrefs()->GetBoolean(
      chromeos::multidevice_setup::kInstantTetheringEnabledPrefName));
  VerifyTetherActiveStatus(false /* expected_active */);

  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::USER_PREFERENCE_DISABLED,
      1 /* expected_count */);
}

// TODO(https://crbug.com/893878): Fix disabled test.
TEST_F(TetherServiceTest, DISABLED_TestEnabled) {
  CreateTetherService();

  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  SetTetherTechnologyStateEnabled(false);
  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  EXPECT_FALSE(profile_->GetPrefs()->GetBoolean(
      chromeos::multidevice_setup::kInstantTetheringEnabledPrefName));
  VerifyTetherActiveStatus(false /* expected_active */);
  histogram_tester_.ExpectBucketCount(
      "InstantTethering.UserPreference.OnToggle", false,
      1u /* expected_count */);

  SetTetherTechnologyStateEnabled(true);
  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(
      chromeos::multidevice_setup::kInstantTetheringEnabledPrefName));
  VerifyTetherActiveStatus(true /* expected_active */);
  histogram_tester_.ExpectBucketCount(
      "InstantTethering.UserPreference.OnToggle", true,
      1u /* expected_count */);

  VerifyTetherFeatureStateRecorded(TetherService::TetherFeatureState::ENABLED,
                                   2 /* expected_count */);
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::PREF_DISABLED);
}

TEST_F(TetherServiceTest, TestUserPrefChangesViaFeatureStateChange) {
  // Start with the feature disabled.
  initial_feature_state_ =
      chromeos::multidevice_setup::mojom::FeatureState::kDisabledByUser;
  profile_->GetPrefs()->SetBoolean(
      chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, false);
  CreateTetherService();

  // Enable the feature.
  profile_->GetPrefs()->SetBoolean(
      chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, true);
  fake_multidevice_setup_client_->SetFeatureState(
      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);
  VerifyTetherFeatureStateRecorded(TetherService::TetherFeatureState::ENABLED,
                                   1 /* expected_count */);
  histogram_tester_.ExpectBucketCount(
      "InstantTethering.UserPreference.OnToggle", true,
      1u /* expected_count */);

  // Disable the feature.
  profile_->GetPrefs()->SetBoolean(
      chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, false);
  fake_multidevice_setup_client_->SetFeatureState(
      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
      chromeos::multidevice_setup::mojom::FeatureState::kDisabledByUser);
  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);
  VerifyTetherFeatureStateRecorded(
      TetherService::TetherFeatureState::USER_PREFERENCE_DISABLED,
      2 /* expected_count */);
  histogram_tester_.ExpectBucketCount(
      "InstantTethering.UserPreference.OnToggle", false,
      1u /* expected_count */);

  // Enable the feature.
  profile_->GetPrefs()->SetBoolean(
      chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, true);
  fake_multidevice_setup_client_->SetFeatureState(
      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);
  VerifyTetherFeatureStateRecorded(TetherService::TetherFeatureState::ENABLED,
                                   2 /* expected_count */);
  histogram_tester_.ExpectBucketCount(
      "InstantTethering.UserPreference.OnToggle", true,
      2u /* expected_count */);

  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::PREF_DISABLED);
}

TEST_F(TetherServiceTest, TestUserPrefChangesViaTechnologyStateChange) {
  CreateTetherService();

  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);

  SetTetherTechnologyStateEnabled(false);
  fake_multidevice_setup_client_->InvokePendingSetFeatureEnabledStateCallback(
      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
      false /* expected_enabled */, base::nullopt /* expected_auth_token */,
      true /* success */);
  profile_->GetPrefs()->SetBoolean(
      chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, false);
  fake_multidevice_setup_client_->SetFeatureState(
      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
      chromeos::multidevice_setup::mojom::FeatureState::kDisabledByUser);
  EXPECT_EQ(
      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
      network_state_handler()->GetTechnologyState(
          chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(false /* expected_active */);
  histogram_tester_.ExpectBucketCount(
      "InstantTethering.UserPreference.OnToggle", false,
      1u /* expected_count */);

  SetTetherTechnologyStateEnabled(true);
  fake_multidevice_setup_client_->InvokePendingSetFeatureEnabledStateCallback(
      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
      true /* expected_enabled */, base::nullopt /* expected_auth_token */,
      false /* success */);
  profile_->GetPrefs()->SetBoolean(
      chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, true);
  fake_multidevice_setup_client_->SetFeatureState(
      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
            network_state_handler()->GetTechnologyState(
                chromeos::NetworkTypePattern::Tether()));
  VerifyTetherActiveStatus(true /* expected_active */);
  histogram_tester_.ExpectBucketCount(
      "InstantTethering.UserPreference.OnToggle", true,
      1u /* expected_count */);

  VerifyTetherFeatureStateRecorded(TetherService::TetherFeatureState::ENABLED,
                                   2 /* expected_count */);
  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::PREF_DISABLED);
}

// TODO(https://crbug.com/893878): Fix disabled test.
// Test against a past defect that made TetherService and NetworkStateHandler
// repeatly update technology state after the other did so. TetherService should
// only update technology state if NetworkStateHandler has provided a different
// state than the user preference.
TEST_F(TetherServiceTest, DISABLED_TestEnabledMultipleChanges) {
  CreateTetherService();

  // CreateTetherService calls RunUntilIdle() so UpdateTetherTechnologyState()
  // may be called multiple times in the initialization process.
  int updated_technology_state_count =
      tether_service_->updated_technology_state_count();

  SetTetherTechnologyStateEnabled(false);
  SetTetherTechnologyStateEnabled(false);
  SetTetherTechnologyStateEnabled(false);

  updated_technology_state_count++;
  EXPECT_EQ(updated_technology_state_count,
            tether_service_->updated_technology_state_count());

  SetTetherTechnologyStateEnabled(true);
  SetTetherTechnologyStateEnabled(true);
  SetTetherTechnologyStateEnabled(true);

  updated_technology_state_count++;
  EXPECT_EQ(updated_technology_state_count,
            tether_service_->updated_technology_state_count());

  VerifyLastShutdownReason(
      chromeos::tether::TetherComponent::ShutdownReason::PREF_DISABLED);
}


text:
<computer_group>
	<name>%group_name%</name>
	<is_smart>true</is_smart>
	<criteria>
		<criterion>
			<name>Application Title</name>
			<priority>0</priority>
			<and_or>and</and_or>
			<search_type>is</search_type>
			<value>%JSS_INVENTORY_NAME%</value>
		</criterion>
		<criterion>
			<name>AxureRP9-TrialVersion</name>
			<priority>1</priority>
			<and_or>and</and_or>
			<search_type>is not</search_type>
			<value>%VERSION%</value>
		</criterion>
		<criterion>
			<name>Computer Group</name>
			<priority>2</priority>
			<and_or>and</and_or>
			<search_type>member of</search_type>
			<value>Testing</value>
		</criterion>
	</criteria>
</computer_group>

redpajama_set_name: RedPajamaGithub


text:
<?php
namespace Bits4breakfast\Zephyros;

use Bits4breakfast\Zephyros\Exception\Http\BadRequestException;

class Router {
	private $route = null;
	private $config = null;

	public function __construct(Route $route, Config $config) {
		$this->route = $route;
		$this->config = $config;
	}

	public function route() {
		if (isset($_POST["_method"])) {
			$this->route->method = $_POST["_method"];
		} else {
			$this->route->method = $_SERVER['REQUEST_METHOD'];
		}

		if ($this->route->method != "GET" && $this->route->method != "POST" && $this->route->method != "PUT" && $this->route->method != "DELETE") {
			throw new BadRequestException();
		}

		if (isset($_GET['controller'])) {
			$this->route->controller = $_GET['controller'];
			$this->route->action = isset($_GET['action']) ? $_GET['action'] : '';
			$this->route->id = isset($_GET['id']) ? $_GET['id'] : 0;
		} else {
			$uri = substr($_SERVER["REQUEST_URI"], 1);
			if (strpos($uri, "?") !== false) {
				$uri = explode("?", $uri);
				$uri = $uri[0];
			}

			list($this->route->controller, $this->route->action, $this->route->id) = (array) array_pad(explode("/", $uri), 3, "");
		}

		foreach (['json', 'csv'] as $format) {
			if (strpos($this->route->id, ".".$format) !== false) {
				$this->route->format = $format;
				$this->route->id = str_replace(".".$format, "", $this->route->id);
				break;
			} else if (strpos($this->route->controller, ".".$format) !== false) {
				$this->route->format = $format;
				$this->route->controller = str_replace(".".$format, "", $this->route->controller);
				break;
			} else if (strpos($this->route->action, ".".$format) !== false) {
				$this->route->format = $format;
				$this->route->action = str_replace(".".$format, "", $this->route->action);
				break;
			}
		}

		if (is_numeric($this->route->action)) {
			$this->route->id = (int) $this->route->action;
			$this->route->action = "";
		}

		if ($this->route->id != null) {
			if (is_numeric($this->route->id)) {
				$this->route->id = (int) $this->route->id;
			} else {
				$this->route->id = @mysql_escape_string($this->route->id);
			}
		} else {
			$this->route->id = 0;
		}

		if ($this->route->controller == "") {
			$this->route->controller = "home";
		}

		return [
			$this->config->get('kernel_namespace'),
			'Controller',
			ucfirst(strtolower($this->route->subdomain)),
			Inflector::camelize($this->route->controller)
		];
	}
}


text:
#include "xenia/kernel/util/xdbf_utils.h"

namespace xe {
namespace kernel {
namespace util {

constexpr fourcc_t kXdbfSignatureXdbf = make_fourcc("XDBF");
constexpr fourcc_t kXdbfSignatureXstc = make_fourcc("XSTC");
constexpr fourcc_t kXdbfSignatureXstr = make_fourcc("XSTR");

XdbfWrapper::XdbfWrapper(const uint8_t* data, size_t data_size)
    : data_(data), data_size_(data_size) {
  if (!data || data_size <= sizeof(XbdfHeader)) {
    data_ = nullptr;
    return;
  }

  const uint8_t* ptr = data_;

  header_ = reinterpret_cast<const XbdfHeader*>(ptr);
  ptr += sizeof(XbdfHeader);
  if (header_->magic != kXdbfSignatureXdbf) {
    data_ = nullptr;
    return;
  }

  entries_ = reinterpret_cast<const XbdfEntry*>(ptr);
  ptr += sizeof(XbdfEntry) * header_->entry_count;

  files_ = reinterpret_cast<const XbdfFileLoc*>(ptr);
  ptr += sizeof(XbdfFileLoc) * header_->free_count;

  content_offset_ = ptr;
}

XdbfBlock XdbfWrapper::GetEntry(XdbfSection section, uint64_t id) const {
  for (uint32_t i = 0; i < header_->entry_used; ++i) {
    auto& entry = entries_[i];
    if (entry.section == static_cast<uint16_t>(section) && entry.id == id) {
      XdbfBlock block;
      block.buffer = content_offset_ + entry.offset;
      block.size = entry.size;
      return block;
    }
  }
  return {0};
}

std::string XdbfWrapper::GetStringTableEntry(XLanguage language,
                                             uint16_t string_id) const {
  auto language_block =
      GetEntry(XdbfSection::kStringTable, static_cast<uint64_t>(language));
  if (!language_block) {
    return "";
  }

  auto xstr_head =
      reinterpret_cast<const XdbfXstrHeader*>(language_block.buffer);
  assert_true(xstr_head->magic == kXdbfSignatureXstr);
  assert_true(xstr_head->version == 1);

  const uint8_t* ptr = language_block.buffer + sizeof(XdbfXstrHeader);
  for (uint16_t i = 0; i < xstr_head->string_count; ++i) {
    auto entry = reinterpret_cast<const XdbfStringTableEntry*>(ptr);
    ptr += sizeof(XdbfStringTableEntry);
    if (entry->id == string_id) {
      return std::string(reinterpret_cast<const char*>(ptr),
                         entry->string_length);
    }
    ptr += entry->string_length;
  }
  return "";
}

constexpr uint64_t kXdbfIdTitle = 0x8000;
constexpr uint64_t kXdbfIdXstc = 0x58535443;

XdbfBlock XdbfGameData::icon() const {
  return GetEntry(XdbfSection::kImage, kXdbfIdTitle);
}

XLanguage XdbfGameData::default_language() const {
  auto block = GetEntry(XdbfSection::kMetadata, kXdbfIdXstc);
  if (!block.buffer) {
    return XLanguage::kEnglish;
  }
  auto xstc = reinterpret_cast<const XdbfXstc*>(block.buffer);
  assert_true(xstc->magic == kXdbfSignatureXstc);
  return static_cast<XLanguage>(static_cast<uint32_t>(xstc->default_language));
}

std::string XdbfGameData::title() const {
  return GetStringTableEntry(default_language(), kXdbfIdTitle);
}

std::string XdbfGameData::title(XLanguage language) const {
  return GetStringTableEntry(language, kXdbfIdTitle);
}

}  // namespace util
}  // namespace kernel
}  // namespace xe


text:
/**
 *
 */
package org.github.etcd.service.rest;

import java.io.Serializable;
import java.util.List;

public class EtcdNode implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long createdIndex;
    private Long modifiedIndex;
    private boolean dir;
    private String key;
    private String value;
    private Long ttl;
    private String expiration;

    private List<EtcdNode> nodes;

    public EtcdNode() {
    }
    public EtcdNode(String key, boolean dir, String value, Long ttl) {
        this.key = key;
        this.dir = dir;
        this.value = value;
        this.ttl = ttl;
    }
    public EtcdNode(String key, String value) {
        this(key, false, value, null);
    }
    public EtcdNode(String key, String value, Long ttl) {
        this(key, false, value, ttl);
    }
    public EtcdNode(String key) {
        this(key, true, null, null);
    }
    public EtcdNode(String key, Long ttl) {
        this(key, true, null, ttl);
    }

    public Long getCreatedIndex() {
        return createdIndex;
    }
    public void setCreatedIndex(Long createdIndex) {
        this.createdIndex = createdIndex;
    }
    public Long getModifiedIndex() {
        return modifiedIndex;
    }
    public void setModifiedIndex(Long modifiedIndex) {
        this.modifiedIndex = modifiedIndex;
    }
    public boolean isDir() {
        return dir;
    }
    public void setDir(boolean dir) {
        this.dir = dir;
    }
    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }
    public Long getTtl() {
        return ttl;
    }
    public void setTtl(Long ttl) {
        this.ttl = ttl;
    }
    public String getExpiration() {
        return expiration;
    }
    public void setExpiration(String expiration) {
        this.expiration = expiration;
    }
    public List<EtcdNode> getNodes() {
        return nodes;
    }
    public void setNodes(List<EtcdNode> nodes) {
        this.nodes = nodes;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((createdIndex == null) ? 0 : createdIndex.hashCode());
        result = prime * result + (dir ? 1231 : 1237);
        result = prime * result
                + ((expiration == null) ? 0 : expiration.hashCode());
        result = prime * result + ((key == null) ? 0 : key.hashCode());
        result = prime * result
                + ((modifiedIndex == null) ? 0 : modifiedIndex.hashCode());
        result = prime * result + ((value == null) ? 0 : value.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        EtcdNode other = (EtcdNode) obj;
        if (createdIndex == null) {
            if (other.createdIndex != null)
                return false;
        } else if (!createdIndex.equals(other.createdIndex))
            return false;
        if (dir != other.dir)
            return false;
        if (expiration == null) {
            if (other.expiration != null)
                return false;
        } else if (!expiration.equals(other.expiration))
            return false;
        if (key == null) {
            if (other.key != null)
                return false;
        } else if (!key.equals(other.key))
            return false;
        if (modifiedIndex == null) {
            if (other.modifiedIndex != null)
                return false;
        } else if (!modifiedIndex.equals(other.modifiedIndex))
            return false;
        if (value == null) {
            if (other.value != null)
                return false;
        } else if (!value.equals(other.value))
            return false;
        return true;
    }
    @Override
    public String toString() {
        return "EtcdNode [key=" + key + ", dir=" + dir + ", value=" + value
                + ", ttl=" + ttl + ", nodes=" + nodes + "]";
    }

}


text:
[Minio](https://minio.io) is an object storage server released under Apache License v2.0. It is compatible with Amazon S3 cloud storage service. It is best suited for storing unstructured data such as photos, videos, log files, backups and container / VM images. Size of an object can range from a few KBs to a maximum of 5TB.

Minio server is light enough to be bundled with the application stack, similar to NodeJS, Redis and MySQL.

This installation uses the single node version of Minio (Minio FS), checkout the [Minio docs](https://docs.minio.io) for more details.

The instructions below use a pinned hostname constraint to ensure the application is always restarted on the same host by Marathon. This allows it to get back to its data but means that you could lose data if that agent goes down.

- Estimated time for completion: less than 5 minutes.
- Target audience:
    - Operators
    - Application admins
    - Developers
    - Devops Engineers
- Scope: Learn to install Minio on DC/OS and learn to use it using Minio command line tool `mc`.

**Table of Contents**:

- [Prerequisites](#prerequisites)
- [Minio Installation using GUI](#minio-installation-using-gui)
- [Access Minio Browser](#access-minio-browser)
- [Install Minio Client](#install-minio-client)
- [Configure Minio Client](#configure-minio-client)
- [Uninstalling Minio](#uninstalling-minio)
- [Explore Further](#explore-further)

## Prerequisites

- DC/OS 1.9 or later
- [Marathon-LB](https://dcos.io/docs/1.10/usage/service-discovery/marathon-lb/usage/) must be installed and running
- Identify [IP of the public agent](https://dcos.io/docs/1.10/administration/locate-public-agent/) where Marathon-LB or an available hostname configured to point to the public agent(s) where Marathon-LB is running.

## Minio installation using GUI

- Visit the DC/OS admin page, and click on Catalog on the left menu bar, then search for Minio, click on the ```Deploy``` button on the right hand side.

![Search & Install Minio](img/minio-package.png)

- Click on the `Install Package` button for the single-click default installation. You can reach your Minio server at `host:9000` where `host` is IP address or hostname of public-agent where Marathon-LB is installed.

![Single-click Install](img/minio-package-install-oneclick.png)

- This installs Minio server instance with factory defaults

- To customize the storage and network configuration, click on `Services` and select the minio service, then select `CONFIGURE`.

- Service Name - Unique name for this Minio server instance.
![Resources](img/config-service.png)

- CPU & Memory
![Resources](img/config-resource.png)

- Volume size
![Storage](img/config-volume-size.png)

- Credentials - Access Key and Secret Key for this Minio server.
![Credentials](img/config-creds.png)

- Networking - Choose the port on which Minio server is available and whether it is available externally.
![Networking](img/config-network.png)

- We're ready to install! Verify your settings are correct and then click ```Review and Deploy``` green button and `Deploy`.

![Review & Install](img/config-review-1.png)

- This completes the install process. Once Minio has been deployed, navigate to the IP/hostname of Public Agent where Marathon-LB is installed and the port you specified. You should see the following login page.

## Access Minio Browser

![Minio Browser](img/minio-browser.png)

- The default access key and secret key are `minio` and `minio123` respectively. This can be used to log in on Minio browser page.

![Minio Browser](img/minio-access.png)

- Once you have successfully loggedin you should see the following screen.

![Minio browser](img/minio-home.png)

## Install Minio Client

Minio Client (mc) provides a modern alternative to UNIX commands like ls, cat, cp, mirror, diff etc. It supports filesystems and Amazon S3 compatible cloud storage service (AWS Signature v2 and v4).

[Click here](https://docs.minio.io/docs/minio-client-quickstart-guide) for instructions on installing mc.

## Configure Minio Client

- There are different ways to configure Minio Client for different S3 cloud storage providers.
- Refer to [Guide](https://docs.minio.io/docs/minio-client-complete-guide) for further configuration.

### Example - Minio Cloud Storage

```sh
mc config host add <ALIAS> <YOUR-MINIO-BROWSER-ADDRESS> <YOUR-ACCESS-KEY> <YOUR-SECRET-KEY> <API-SIGNATURE>(optional)
```

*Example: Create a new bucket named "mybucket" on http://54.255.206.228*

```sh
mc config host add minio-dcos http://54.255.206.228 ELVUN4CWOAIRWOT2AO9D 4e6CIFtWW1/YI91Pd6FONRJgssKiFO2Ea1d5fDwp S3v4
mc mb minio-dcos/mybucket
Bucket created successfully 'minio-dcos/mybucket'.
```
Now switch to Minio Browser, you can see mybacket in left-side panel where all the backets are listed.

Last but not least I want you to see what Minio's resource footprint looks like:

![DCOS Dashboard](img/dcos-dashboard.png)

## Uninstalling Minio

```sh
dcos package uninstall minio
```

## Explore Further
- [Minio Erasure Code QuickStart Guide](https://docs.minio.io/docs/minio-erasure-code-quickstart-guide)
- [Use `mc` with Minio Server](https://docs.minio.io/docs/minio-client-quickstart-guide)
- [Minio Client SDK](https://docs.minio.io/categories/17)
- [Use `aws-cli` with Minio Server](https://docs.minio.io/docs/aws-cli-with-minio)
- [Use `s3cmd` with Minio Server](https://docs.minio.io/docs/s3cmd-with-minio)
- [Use `minio-go` SDK with Minio Server](https://docs.minio.io/docs/golang-client-quickstart-guide)
- [The Minio documentation website](https://docs.minio.io)


text:
import os
from nose.tools import eq_, raises
from pyexcel._compact import StringIO
from pyexcel_text.jsonp import JsonParser

class TestStructure:

    def setUp(self):
        self.parser = JsonParser("json")

    @raises(Exception)
    def test_wrong_format(self):
        sheet_name = 'test'
        self.content = self.parser.parse_file(get_file("unknown.json"),
                                              sheet_name=sheet_name)

    @raises(Exception)
    def test_bad_dict(self):
        sheet_name = 'test'
        self.content = self.parser.parse_file(get_file("bad_dict.json"),
                                              sheet_name=sheet_name)

    def test_dict(self):
        sheet_name = 'test'
        self.content = self.parser.parse_file(get_file("dict.json"),
                                              sheet_name=sheet_name)
        expected = {
            sheet_name: [
                ['a', 'b', 'c'],
                [1, 2, 4],
                [2, 3, 5]
            ]
        }
        self._verify(expected)

    def test_records(self):
        sheet_name = 'test'
        self.content = self.parser.parse_file(get_file("records.json"),
                                              sheet_name=sheet_name)
        expected = {
            sheet_name: [
                ['a', 'b', 'c'],
                [1, 2, 3],
                [1, 2, 4]
            ]
        }
        self._verify(expected)

    def test_book_dict(self):
        sheet_name = 'test'
        self.content = self.parser.parse_file(get_file("bookdict.json"),
                                              sheet_name=sheet_name)
        expected = {
            'sheet 1': [[1, 2], [2, 3]],
            'sheet 2': [[1, 2], [3, 4]]
        }

        self._verify(expected)

    def _verify(self, expected):
        for key in self.content:
            self.content[key] = list(self.content[key])
        eq_(self.content, expected)

class TestMedia:

    def setUp(self):
        self.parser = JsonParser("json")

    def test_file(self):
        self.content = self.parser.parse_file(get_file("array.json"),
                                              sheet_name='test')
        self._verify()

    def test_stream(self):
        with open(get_file("array.json"), 'r') as f:
            file_stream = StringIO(f.read())
        self.content = self.parser.parse_file_stream(file_stream,
                                                     sheet_name='test')
        self._verify()

    def test_content(self):
        with open(get_file("array.json"), 'r') as f:
            file_content = f.read()
        self.content = self.parser.parse_file_content(file_content,
                                                      sheet_name='test')
        self._verify()

    def _verify(self):
        for key in self.content:
            self.content[key] = list(self.content[key])
        eq_(self.content, {'test': [[1, 2, 3]]})

def get_file(file_name):
    return os.path.join("tests", "fixtures", file_name)


text:
package ske.eventsourcing.domain;

import com.google.common.base.Objects;
import ske.eventsourcing.event.DomainEvent;
import ske.eventsourcing.eventstore.EventSourceIdentifier;

public class DummyChangeAddressEvent extends DomainEvent {

    private final String address;

    public DummyChangeAddressEvent(EventSourceIdentifier id, String address) {
        super(id);
        this.address = address;
    }

    public String getAddress() {
        return address;
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this).add("address", address).toString();
    }

}


text:
class UserWelcomeMailerWorker
  include Sidekiq::Worker

  attr_reader :user, :project

  def perform(user_id, project_id=nil)
    @user = User.find(user_id)
    if @user && !@user.email.blank?
      @project_name = if project_id
        Project.find(project_id).try(:display_name)
      end
      UserWelcomeMailer.welcome_user(user, @project_name).deliver
    end
  rescue ActiveRecord::RecordNotFound
    nil
  rescue Net::SMTPSyntaxError => e
    @user.update_attribute(:valid_email, false)
  end
end