// Copyright 2019 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/web_applications/web_app_sync_bridge.h"

#include <memory>
#include <vector>

#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/containers/contains.h"
#include "base/containers/cxx20_erase.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/bind.h"
#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
#include "chrome/browser/web_applications/test/fake_web_app_database_factory.h"
#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
#include "chrome/browser/web_applications/test/web_app_sync_test_utils.h"
#include "chrome/browser/web_applications/test/web_app_test.h"
#include "chrome/browser/web_applications/test/web_app_test_observers.h"
#include "chrome/browser/web_applications/test/web_app_test_utils.h"
#include "chrome/browser/web_applications/user_display_mode.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_command_manager.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_install_manager.h"
#include "chrome/browser/web_applications/web_app_registry_update.h"
#include "chrome/browser/web_applications/web_app_utils.h"
#include "components/sync/model/data_batch.h"
#include "components/sync/model/entity_change.h"
#include "components/sync/protocol/web_app_specifics.pb.h"
#include "components/sync/test/mock_model_type_change_processor.h"
#include "components/webapps/browser/install_result_code.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkColor.h"

namespace web_app {

namespace {

using testing::_;

using AppsList = std::vector<std::unique_ptr<WebApp>>;

void RemoveWebAppFromAppsList(AppsList* apps_list, const AppId& app_id) {
  base::EraseIf(*apps_list, [app_id](const std::unique_ptr<WebApp>& app) {
    return app->app_id() == app_id;
  });
}

bool IsSyncDataEqualIfApplied(const WebApp& expected_app,
                              std::unique_ptr<WebApp> app_to_apply_sync_data,
                              const syncer::EntityData& entity_data) {
  if (!entity_data.specifics.has_web_app())
    return false;

  const GURL sync_start_url(entity_data.specifics.web_app().start_url());
  if (expected_app.app_id() !=
      GenerateAppId(/*manifest_id=*/absl::nullopt, sync_start_url))
    return false;

  // ApplySyncDataToApp enforces kSync source on |app_to_apply_sync_data|.
  ApplySyncDataToApp(entity_data.specifics.web_app(),
                     app_to_apply_sync_data.get());
  app_to_apply_sync_data->SetName(entity_data.name);
  return expected_app == *app_to_apply_sync_data;
}

bool IsSyncDataEqual(const WebApp& expected_app,
                     const syncer::EntityData& entity_data) {
  auto app_to_apply_sync_data = std::make_unique<WebApp>(expected_app.app_id());
  return IsSyncDataEqualIfApplied(
      expected_app, std::move(app_to_apply_sync_data), entity_data);
}

bool RegistryContainsSyncDataBatchChanges(
    const Registry& registry,
    std::unique_ptr<syncer::DataBatch> data_batch) {
  if (!data_batch || !data_batch->HasNext())
    return registry.empty();

  while (data_batch->HasNext()) {
    syncer::KeyAndData key_and_data = data_batch->Next();
    auto web_app_iter = registry.find(key_and_data.first);
    if (web_app_iter == registry.end()) {
      LOG(ERROR) << "App not found in registry: " << key_and_data.first;
      return false;
    }

    if (!IsSyncDataEqual(*web_app_iter->second, *key_and_data.second))
      return false;
  }
  return true;
}

std::unique_ptr<WebApp> CreateWebAppWithSyncOnlyFields(const std::string& url) {
  const GURL start_url(url);
  const AppId app_id = GenerateAppId(/*manifest_id=*/absl::nullopt, start_url);

  auto web_app = std::make_unique<WebApp>(app_id);
  web_app->AddSource(WebAppManagement::kSync);
  web_app->SetStartUrl(start_url);
  web_app->SetName("Name");
  web_app->SetUserDisplayMode(UserDisplayMode::kStandalone);
  return web_app;
}

AppsList CreateAppsList(const std::string& base_url, int num_apps) {
  AppsList apps_list;

  for (int i = 0; i < num_apps; ++i) {
    apps_list.push_back(
        CreateWebAppWithSyncOnlyFields(base_url + base::NumberToString(i)));
  }

  return apps_list;
}

void InsertAppIntoRegistry(Registry* registry, std::unique_ptr<WebApp> app) {
  AppId app_id = app->app_id();
  ASSERT_FALSE(base::Contains(*registry, app_id));
  registry->emplace(std::move(app_id), std::move(app));
}

void InsertAppsListIntoRegistry(Registry* registry, const AppsList& apps_list) {
  for (const std::unique_ptr<WebApp>& app : apps_list)
    registry->emplace(app->app_id(), std::make_unique<WebApp>(*app));
}

void ConvertAppToEntityChange(const WebApp& app,
                              syncer::EntityChange::ChangeType change_type,
                              syncer::EntityChangeList* sync_data_list) {
  std::unique_ptr<syncer::EntityChange> entity_change;

  switch (change_type) {
    case syncer::EntityChange::ACTION_ADD:
      entity_change = syncer::EntityChange::CreateAdd(
          app.app_id(), std::move(*CreateSyncEntityData(app)));
      break;
    case syncer::EntityChange::ACTION_UPDATE:
      entity_change = syncer::EntityChange::CreateUpdate(
          app.app_id(), std::move(*CreateSyncEntityData(app)));
      break;
    case syncer::EntityChange::ACTION_DELETE:
      entity_change = syncer::EntityChange::CreateDelete(app.app_id());
      break;
  }

  sync_data_list->push_back(std::move(entity_change));
}

void ConvertAppsListToEntityChangeList(
    const AppsList& apps_list,
    syncer::EntityChangeList* sync_data_list) {
  for (auto& app : apps_list) {
    ConvertAppToEntityChange(*app, syncer::EntityChange::ACTION_ADD,
                             sync_data_list);
  }
}

// Returns true if the app converted from entity_data exists in the apps_list.
bool RemoveEntityDataAppFromAppsList(const std::string& storage_key,
                                     const syncer::EntityData& entity_data,
                                     AppsList* apps_list) {
  for (auto& app : *apps_list) {
    if (app->app_id() == storage_key) {
      if (!IsSyncDataEqual(*app, entity_data))
        return false;

      RemoveWebAppFromAppsList(apps_list, storage_key);
      return true;
    }
  }

  return false;
}

void RunCallbacksOnInstall(
    const std::vector<WebApp*>& apps,
    const WebAppInstallManager::RepeatingInstallCallback& callback,
    webapps::InstallResultCode code) {
  for (WebApp* app : apps)
    callback.Run(app->app_id(), code);
}

}  // namespace

class WebAppSyncBridgeTest : public WebAppTest {
 public:
  void SetUp() override {
    WebAppTest::SetUp();

    command_manager_ = std::make_unique<WebAppCommandManager>(
        profile(), FakeWebAppProvider::Get(profile()));
    install_manager_ = std::make_unique<WebAppInstallManager>(profile());
    registrar_mutable_ = std::make_unique<WebAppRegistrarMutable>(profile());
    sync_bridge_ = std::make_unique<WebAppSyncBridge>(
        registrar_mutable_.get(), mock_processor_.CreateForwardingProcessor());
    database_factory_ = std::make_unique<FakeWebAppDatabaseFactory>();

    sync_bridge_->SetSubsystems(database_factory_.get(), install_manager_.get(),
                                command_manager_.get());

    install_manager_->Start();

    ON_CALL(mock_processor_, IsTrackingMetadata())
        .WillByDefault(testing::Return(true));
  }

  void TearDown() override {
    DestroyManagers();
    WebAppTest::TearDown();
  }

  void InitSyncBridge() {
    base::RunLoop loop;
    sync_bridge_->Init(loop.QuitClosure());
    loop.Run();
  }

  void InitSyncBridgeFromAppList(const AppsList& apps_list) {
    Registry registry;
    InsertAppsListIntoRegistry(&registry, apps_list);
    database_factory().WriteRegistry(registry);
    InitSyncBridge();
  }

  void MergeSyncData(const AppsList& merged_apps) {
    syncer::EntityChangeList entity_data_list;
    ConvertAppsListToEntityChangeList(merged_apps, &entity_data_list);
    EXPECT_CALL(processor(), Put(_, _, _)).Times(0);
    EXPECT_CALL(processor(), Delete(_, _)).Times(0);
    sync_bridge().MergeSyncData(sync_bridge().CreateMetadataChangeList(),
                                std::move(entity_data_list));
  }

  bool IsDatabaseRegistryEqualToRegistrar() {
    Registry registry = database_factory().ReadRegistry();
    return IsRegistryEqual(registrar_registry(), registry);
  }

  void SetSyncInstallDelegateFailureIfCalled() {
    install_manager().SetInstallWebAppsAfterSyncDelegateForTesting(
        base::BindLambdaForTesting(
            [&](std::vector<WebApp*> apps_to_install,
                WebAppInstallManager::RepeatingInstallCallback callback) {
              ADD_FAILURE();
            }));

    install_manager().SetUninstallFromSyncDelegateForTesting(
        base::BindLambdaForTesting(
            [&](const std::vector<AppId>& apps_to_uninstall,
                WebAppInstallManager::RepeatingUninstallCallback callback) {
              ADD_FAILURE();
            }));
  }

  void CommitUpdate(std::unique_ptr<WebAppRegistryUpdate> update) {
    base::RunLoop run_loop;
    sync_bridge().CommitUpdate(
        std::move(update),
        base::BindLambdaForTesting([&run_loop](bool success) {
          ASSERT_TRUE(success);
          run_loop.Quit();
        }));
    run_loop.Run();
  }

 protected:
  void DestroyManagers() {
    if (command_manager_) {
      command_manager_->Shutdown();
      command_manager_.reset();
    }
    if (install_manager_) {
      install_manager_.reset();
    }
    if (registrar_mutable_) {
      registrar_mutable_.reset();
    }
    if (sync_bridge_) {
      sync_bridge_.reset();
    }
    if (database_factory_) {
      database_factory_.reset();
    }
  }

  syncer::MockModelTypeChangeProcessor& processor() { return mock_processor_; }
  FakeWebAppDatabaseFactory& database_factory() { return *database_factory_; }

  WebAppRegistrar& registrar() { return *registrar_mutable_; }

  WebAppSyncBridge& sync_bridge() { return *sync_bridge_; }

  WebAppInstallManager& install_manager() { return *install_manager_; }

  Registry& registrar_registry() { return registrar_mutable_->registry(); }

 private:
  std::unique_ptr<WebAppCommandManager> command_manager_;
  std::unique_ptr<WebAppInstallManager> install_manager_;
  std::unique_ptr<WebAppRegistrarMutable> registrar_mutable_;
  std::unique_ptr<WebAppSyncBridge> sync_bridge_;
  std::unique_ptr<FakeWebAppDatabaseFactory> database_factory_;

  testing::NiceMock<syncer::MockModelTypeChangeProcessor> mock_processor_;
};

// Tests that the WebAppSyncBridge correctly reports data from the
// WebAppDatabase.
TEST_F(WebAppSyncBridgeTest, GetData) {
  Registry registry;

  std::unique_ptr<WebApp> synced_app1 =
      CreateWebAppWithSyncOnlyFields("https://example.com/app1/");
  {
    WebApp::SyncFallbackData sync_fallback_data;
    sync_fallback_data.name = "Sync Name";
    sync_fallback_data.theme_color = SK_ColorCYAN;
    synced_app1->SetSyncFallbackData(std::move(sync_fallback_data));
  }
  InsertAppIntoRegistry(&registry, std::move(synced_app1));

  std::unique_ptr<WebApp> synced_app2 =
      CreateWebAppWithSyncOnlyFields("https://example.com/app2/");
  // sync_fallback_data is empty for this app.
  InsertAppIntoRegistry(&registry, std::move(synced_app2));

  std::unique_ptr<WebApp> policy_app = test::CreateWebApp(
      GURL("https://example.org/"), WebAppManagement::kPolicy);
  InsertAppIntoRegistry(&registry, std::move(policy_app));

  database_factory().WriteRegistry(registry);

  EXPECT_CALL(processor(), ModelReadyToSync(_)).Times(1);
  InitSyncBridge();

  {
    WebAppSyncBridge::StorageKeyList storage_keys;
    // Add an unknown key to test this is handled gracefully.
    storage_keys.push_back("unknown");
    for (const Registry::value_type& id_and_web_app : registry)
      storage_keys.push_back(id_and_web_app.first);

    base::RunLoop run_loop;
    sync_bridge().GetData(
        std::move(storage_keys),
        base::BindLambdaForTesting(
            [&](std::unique_ptr<syncer::DataBatch> data_batch) {
              EXPECT_TRUE(RegistryContainsSyncDataBatchChanges(
                  registry, std::move(data_batch)));
              run_loop.Quit();
            }));
    run_loop.Run();
  }

  {
    base::RunLoop run_loop;
    sync_bridge().GetAllDataForDebugging(base::BindLambdaForTesting(
        [&](std::unique_ptr<syncer::DataBatch> data_batch) {
          EXPECT_TRUE(RegistryContainsSyncDataBatchChanges(
              registry, std::move(data_batch)));
          run_loop.Quit();
        }));
    run_loop.Run();
  }
}

// Tests that the client & storage tags are correct for entity data.
TEST_F(WebAppSyncBridgeTest, Identities) {
  std::unique_ptr<WebApp> app =
      CreateWebAppWithSyncOnlyFields("https://example.com/");
  std::unique_ptr<syncer::EntityData> entity_data = CreateSyncEntityData(*app);

  EXPECT_EQ(app->app_id(), sync_bridge().GetClientTag(*entity_data));
  EXPECT_EQ(app->app_id(), sync_bridge().GetStorageKey(*entity_data));
}

// Test that a empty local data results in no changes sent to the sync system.
TEST_F(WebAppSyncBridgeTest, MergeSyncData_LocalSetAndServerSetAreEmpty) {
  InitSyncBridge();

  syncer::EntityChangeList sync_data_list;

  EXPECT_CALL(processor(), Put(_, _, _)).Times(0);

  sync_bridge().MergeSyncData(sync_bridge().CreateMetadataChangeList(),
                              std::move(sync_data_list));
}

TEST_F(WebAppSyncBridgeTest, MergeSyncData_LocalSetEqualsServerSet) {
  AppsList apps = CreateAppsList("https://example.com/", 10);

  Registry registry;
  InsertAppsListIntoRegistry(&registry, apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();

  // The incoming list of apps from the sync server.
  syncer::EntityChangeList sync_data_list;
  ConvertAppsListToEntityChangeList(apps, &sync_data_list);

  // The local app state is the same as the server state, so no changes should
  // be sent.
  EXPECT_CALL(processor(), Put(_, _, _)).Times(0);

  sync_bridge().MergeSyncData(sync_bridge().CreateMetadataChangeList(),
                              std::move(sync_data_list));

  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

TEST_F(WebAppSyncBridgeTest, MergeSyncData_LocalSetGreaterThanServerSet) {
  AppsList local_and_server_apps = CreateAppsList("https://example.com/", 10);
  AppsList expected_local_apps_to_upload =
      CreateAppsList("https://example.org/", 10);

  Registry registry;
  InsertAppsListIntoRegistry(&registry, local_and_server_apps);
  InsertAppsListIntoRegistry(&registry, expected_local_apps_to_upload);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();

  auto metadata_change_list = sync_bridge().CreateMetadataChangeList();
  syncer::MetadataChangeList* metadata_ptr = metadata_change_list.get();

  syncer::EntityChangeList sync_data_list;
  ConvertAppsListToEntityChangeList(local_and_server_apps, &sync_data_list);

  // MergeSyncData below should send |expected_local_apps_to_upload| to the
  // processor() to upload to USS.
  base::RunLoop run_loop;
  ON_CALL(processor(), Put(_, _, _))
      .WillByDefault([&](const std::string& storage_key,
                         std::unique_ptr<syncer::EntityData> entity_data,
                         syncer::MetadataChangeList* metadata) {
        EXPECT_EQ(metadata_ptr, metadata);
        EXPECT_TRUE(RemoveEntityDataAppFromAppsList(
            storage_key, *entity_data, &expected_local_apps_to_upload));
        if (expected_local_apps_to_upload.empty())
          run_loop.Quit();
      });

  sync_bridge().MergeSyncData(std::move(metadata_change_list),
                              std::move(sync_data_list));
  run_loop.Run();

  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

TEST_F(WebAppSyncBridgeTest, MergeSyncData_LocalSetLessThanServerSet) {
  AppsList local_and_server_apps = CreateAppsList("https://example.com/", 10);
  AppsList expected_apps_to_install =
      CreateAppsList("https://example.org/", 10);
  // These fields are not synced, these are just expected values.
  for (std::unique_ptr<WebApp>& expected_app_to_install :
       expected_apps_to_install) {
    expected_app_to_install->SetIsLocallyInstalled(
        AreAppsLocallyInstalledBySync());
    expected_app_to_install->SetIsFromSyncAndPendingInstallation(true);
  }

  Registry registry;
  InsertAppsListIntoRegistry(&registry, local_and_server_apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();

  syncer::EntityChangeList sync_data_list;
  ConvertAppsListToEntityChangeList(expected_apps_to_install, &sync_data_list);
  ConvertAppsListToEntityChangeList(local_and_server_apps, &sync_data_list);

  EXPECT_CALL(processor(), Put(_, _, _)).Times(0);

  base::RunLoop run_loop;
  // This is called after apps are installed from sync in MergeSyncData() below.
  install_manager().SetInstallWebAppsAfterSyncDelegateForTesting(
      base::BindLambdaForTesting(
          [&](std::vector<WebApp*> apps_to_install,
              WebAppInstallManager::RepeatingInstallCallback callback) {
            for (WebApp* app_to_install : apps_to_install) {
              // The app must be registered.
              EXPECT_TRUE(registrar().GetAppById(app_to_install->app_id()));
              // Add the app copy to the expected registry.
              registry.emplace(app_to_install->app_id(),
                               std::make_unique<WebApp>(*app_to_install));

              // Find the app in expected_apps_to_install set and remove the
              // entry.
              bool found = false;
              for (const std::unique_ptr<WebApp>& expected_app_to_install :
                   expected_apps_to_install) {
                if (expected_app_to_install->app_id() ==
                    app_to_install->app_id()) {
                  EXPECT_EQ(*expected_app_to_install, *app_to_install);
                  RemoveWebAppFromAppsList(&expected_apps_to_install,
                                           expected_app_to_install->app_id());
                  found = true;
                  break;
                }
              }
              EXPECT_TRUE(found);
            }

            EXPECT_TRUE(expected_apps_to_install.empty());

            RunCallbacksOnInstall(
                apps_to_install, callback,
                webapps::InstallResultCode::kSuccessNewInstall);
            run_loop.Quit();
          }));

  sync_bridge().MergeSyncData(sync_bridge().CreateMetadataChangeList(),
                              std::move(sync_data_list));
  run_loop.Run();

  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

TEST_F(WebAppSyncBridgeTest, ApplySyncChanges_EmptyEntityChanges) {
  AppsList merged_apps = CreateAppsList("https://example.com/", 10);

  Registry registry;
  InsertAppsListIntoRegistry(&registry, merged_apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();
  MergeSyncData(merged_apps);

  syncer::EntityChangeList entity_changes;
  EXPECT_CALL(processor(), Put(_, _, _)).Times(0);
  EXPECT_CALL(processor(), Delete(_, _)).Times(0);
  SetSyncInstallDelegateFailureIfCalled();

  sync_bridge().ApplySyncChanges(sync_bridge().CreateMetadataChangeList(),
                                 std::move(entity_changes));

  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

TEST_F(WebAppSyncBridgeTest, ApplySyncChanges_AddUpdateDelete) {
  // 20 initial apps with DisplayMode::kStandalone user display mode.
  AppsList merged_apps = CreateAppsList("https://example.com/", 20);

  Registry registry;
  InsertAppsListIntoRegistry(&registry, merged_apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();
  MergeSyncData(merged_apps);

  syncer::EntityChangeList entity_changes;

  for (std::unique_ptr<WebApp>& app_to_add :
       CreateAppsList("https://example.org/", 10)) {
    app_to_add->SetIsLocallyInstalled(AreAppsLocallyInstalledBySync());
    app_to_add->SetIsFromSyncAndPendingInstallation(true);

    ConvertAppToEntityChange(*app_to_add, syncer::EntityChange::ACTION_ADD,
                             &entity_changes);
  }

  // Update first 5 initial apps.
  for (int i = 0; i < 5; ++i) {
    auto app_to_update = std::make_unique<WebApp>(*merged_apps[i]);
    // Update user display mode field.
    app_to_update->SetUserDisplayMode(UserDisplayMode::kBrowser);
    ConvertAppToEntityChange(
        *app_to_update, syncer::EntityChange::ACTION_UPDATE, &entity_changes);
    // Override the app in the expected registry.
    registry[app_to_update->app_id()] = std::move(app_to_update);
  }

  // Delete next 5 initial apps. Leave the rest unchanged.
  for (int i = 5; i < 10; ++i) {
    const WebApp& app_to_delete = *merged_apps[i];
    ConvertAppToEntityChange(app_to_delete, syncer::EntityChange::ACTION_DELETE,
                             &entity_changes);
  }

  // There should be no changes sent to USS in the next ApplySyncChanges()
  // operation.
  EXPECT_CALL(processor(), Put(_, _, _)).Times(0);
  EXPECT_CALL(processor(), Delete(_, _)).Times(0);

  base::RunLoop run_loop;
  base::RepeatingClosure barrier_closure =
      base::BarrierClosure(2, run_loop.QuitClosure());

  install_manager().SetInstallWebAppsAfterSyncDelegateForTesting(
      base::BindLambdaForTesting(
          [&](std::vector<WebApp*> apps_to_install,
              WebAppInstallManager::RepeatingInstallCallback callback) {
            for (WebApp* app_to_install : apps_to_install) {
              // The app must be registered.
              EXPECT_TRUE(registrar().GetAppById(app_to_install->app_id()));
              // Add the app copy to the expected registry.
              registry.emplace(app_to_install->app_id(),
                               std::make_unique<WebApp>(*app_to_install));
            }

            RunCallbacksOnInstall(
                apps_to_install, callback,
                webapps::InstallResultCode::kSuccessNewInstall);
            barrier_closure.Run();
          }));

  install_manager().SetUninstallFromSyncDelegateForTesting(
      base::BindLambdaForTesting(
          [&](const std::vector<AppId>& apps_to_uninstall,
              WebAppInstallManager::RepeatingUninstallCallback callback) {
            EXPECT_EQ(5ul, apps_to_uninstall.size());

            for (const AppId& app_to_uninstall : apps_to_uninstall) {
              // The app must be registered.
              const WebApp* app = registrar().GetAppById(app_to_uninstall);
              // Sync expects that the apps are deleted by the delegate.
              EXPECT_TRUE(app);
              EXPECT_TRUE(app->is_uninstalling());
              EXPECT_TRUE(app->GetSources().none());
              registry.erase(app_to_uninstall);
              {
                ScopedRegistryUpdate update(&sync_bridge());
                update->DeleteApp(app_to_uninstall);
              }
              callback.Run(app_to_uninstall,
                           webapps::UninstallResultCode::kSuccess);
            }

            barrier_closure.Run();
          }));

  sync_bridge().ApplySyncChanges(sync_bridge().CreateMetadataChangeList(),
                                 std::move(entity_changes));
  run_loop.Run();

  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

TEST_F(WebAppSyncBridgeTest, ApplySyncChanges_DeleteHappensExternally) {
  // 5 initial apps.
  AppsList merged_apps = CreateAppsList("https://example.com/", 5);

  Registry registry;
  InsertAppsListIntoRegistry(&registry, merged_apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();
  MergeSyncData(merged_apps);

  syncer::EntityChangeList entity_changes;

  // Delete next 5 initial apps. Leave the rest unchanged.
  for (int i = 0; i < 5; ++i) {
    const WebApp& app_to_delete = *merged_apps[i];
    ConvertAppToEntityChange(app_to_delete, syncer::EntityChange::ACTION_DELETE,
                             &entity_changes);
  }

  // There should be no changes sent to USS in the next ApplySyncChanges()
  // operation.
  EXPECT_CALL(processor(), Put(_, _, _)).Times(0);
  EXPECT_CALL(processor(), Delete(_, _)).Times(0);

  base::RunLoop run_loop;
  std::vector<AppId> to_uninstall;
  WebAppInstallManager::RepeatingUninstallCallback uninstall_complete_callback;

  install_manager().SetUninstallFromSyncDelegateForTesting(
      base::BindLambdaForTesting(
          [&](const std::vector<AppId>& apps_to_uninstall,
              WebAppInstallManager::RepeatingUninstallCallback callback) {
            to_uninstall = apps_to_uninstall;
            uninstall_complete_callback = callback;
            run_loop.Quit();
          }));

  sync_bridge().ApplySyncChanges(sync_bridge().CreateMetadataChangeList(),
                                 std::move(entity_changes));
  run_loop.Run();

  EXPECT_EQ(5ul, to_uninstall.size());

  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
  for (const AppId& app_to_uninstall : to_uninstall) {
    const WebApp* app = registrar().GetAppById(app_to_uninstall);
    EXPECT_TRUE(app);
    EXPECT_TRUE(app->is_uninstalling());
    EXPECT_TRUE(app->GetSources().none());
  }
}

TEST_F(WebAppSyncBridgeTest, ApplySyncChanges_UpdateOnly) {
  AppsList merged_apps = CreateAppsList("https://example.com/", 10);

  Registry registry;
  InsertAppsListIntoRegistry(&registry, merged_apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();
  MergeSyncData(merged_apps);

  syncer::EntityChangeList entity_changes;

  // Update last 5 initial apps.
  for (int i = 5; i < 10; ++i) {
    auto app_to_update = std::make_unique<WebApp>(*merged_apps[i]);
    app_to_update->SetUserDisplayMode(UserDisplayMode::kStandalone);

    WebApp::SyncFallbackData sync_fallback_data;
    sync_fallback_data.name = "Sync Name";
    sync_fallback_data.theme_color = SK_ColorYELLOW;
    app_to_update->SetSyncFallbackData(std::move(sync_fallback_data));

    ConvertAppToEntityChange(
        *app_to_update, syncer::EntityChange::ACTION_UPDATE, &entity_changes);
    // Override the app in the expected registry.
    registry[app_to_update->app_id()] = std::move(app_to_update);
  }

  EXPECT_CALL(processor(), Put(_, _, _)).Times(0);
  EXPECT_CALL(processor(), Delete(_, _)).Times(0);

  // No installs or uninstalls are made here, only app updates.
  SetSyncInstallDelegateFailureIfCalled();

  sync_bridge().ApplySyncChanges(sync_bridge().CreateMetadataChangeList(),
                                 std::move(entity_changes));

  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

TEST_F(WebAppSyncBridgeTest,
       ApplySyncChanges_AddSyncAppsWithOverlappingPolicyApps) {
  AppsList policy_apps;
  for (int i = 0; i < 10; ++i) {
    std::unique_ptr<WebApp> policy_app = test::CreateWebApp(
        GURL("https://example.com/" + base::NumberToString(i)),
        WebAppManagement::kPolicy);
    policy_apps.push_back(std::move(policy_app));
  }

  Registry registry;
  InsertAppsListIntoRegistry(&registry, policy_apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();

  syncer::EntityChangeList entity_changes;

  // Install 5 kSync apps over existing kPolicy apps. Leave the rest unchanged.
  for (int i = 0; i < 5; ++i) {
    const WebApp& app_to_install = *policy_apps[i];
    ConvertAppToEntityChange(app_to_install, syncer::EntityChange::ACTION_ADD,
                             &entity_changes);
  }

  EXPECT_CALL(processor(), Put(_, _, _)).Times(0);
  EXPECT_CALL(processor(), Delete(_, _)).Times(0);
  SetSyncInstallDelegateFailureIfCalled();

  sync_bridge().ApplySyncChanges(sync_bridge().CreateMetadataChangeList(),
                                 std::move(entity_changes));

  // Modify the registry with the results that we expect.
  for (int i = 0; i < 5; ++i) {
    std::unique_ptr<WebApp>& expected_sync_and_policy_app =
        registry[policy_apps[i]->app_id()];
    expected_sync_and_policy_app->AddSource(WebAppManagement::kSync);
  }

  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

TEST_F(WebAppSyncBridgeTest,
       ApplySyncChanges_UpdateSyncAppsWithOverlappingPolicyApps) {
  AppsList policy_and_sync_apps;
  for (int i = 0; i < 10; ++i) {
    std::unique_ptr<WebApp> policy_and_sync_app = test::CreateWebApp(
        GURL("https://example.com/" + base::NumberToString(i)));
    policy_and_sync_app->AddSource(WebAppManagement::kPolicy);
    policy_and_sync_app->AddSource(WebAppManagement::kSync);
    policy_and_sync_apps.push_back(std::move(policy_and_sync_app));
  }

  Registry registry;
  InsertAppsListIntoRegistry(&registry, policy_and_sync_apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();
  MergeSyncData(policy_and_sync_apps);

  syncer::EntityChangeList entity_changes;

  // Update first 5 kSync apps which are shared with kPolicy. Leave the rest
  // unchanged.
  AppsList apps_to_update;
  for (int i = 0; i < 5; ++i) {
    auto app_to_update = std::make_unique<WebApp>(*policy_and_sync_apps[i]);
    app_to_update->SetUserDisplayMode(UserDisplayMode::kBrowser);

    WebApp::SyncFallbackData sync_fallback_data;
    sync_fallback_data.name = "Updated Sync Name";
    sync_fallback_data.theme_color = SK_ColorWHITE;
    app_to_update->SetSyncFallbackData(std::move(sync_fallback_data));

    ConvertAppToEntityChange(
        *app_to_update, syncer::EntityChange::ACTION_UPDATE, &entity_changes);
    apps_to_update.push_back(std::move(app_to_update));
  }

  EXPECT_CALL(processor(), Put(_, _, _)).Times(0);
  EXPECT_CALL(processor(), Delete(_, _)).Times(0);
  SetSyncInstallDelegateFailureIfCalled();

  sync_bridge().ApplySyncChanges(sync_bridge().CreateMetadataChangeList(),
                                 std::move(entity_changes));

  // Modify the registry with the results that we expect.
  for (int i = 0; i < 5; ++i)
    registry[policy_and_sync_apps[i]->app_id()] = std::move(apps_to_update[i]);

  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

// Tests that if a policy app is installed, and that app is also in 'sync' and
// is uninstalled through sync, then it should remain on the system as a policy
// app.
TEST_F(WebAppSyncBridgeTest,
       ApplySyncChanges_DeleteSyncAppsWithOverlappingPolicyApps) {
  AppsList policy_and_sync_apps;
  for (int i = 0; i < 10; ++i) {
    std::unique_ptr<WebApp> policy_and_sync_app = test::CreateWebApp(
        GURL("https://example.com/" + base::NumberToString(i)));
    policy_and_sync_app->AddSource(WebAppManagement::kPolicy);
    policy_and_sync_app->AddSource(WebAppManagement::kSync);
    policy_and_sync_apps.push_back(std::move(policy_and_sync_app));
  }

  Registry registry;
  InsertAppsListIntoRegistry(&registry, policy_and_sync_apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();
  MergeSyncData(policy_and_sync_apps);

  syncer::EntityChangeList entity_changes;

  // Uninstall 5 kSync apps which are shared with kPolicy. Leave the rest
  // unchanged.
  for (int i = 0; i < 5; ++i) {
    const WebApp& app_to_uninstall = *policy_and_sync_apps[i];
    ConvertAppToEntityChange(
        app_to_uninstall, syncer::EntityChange::ACTION_DELETE, &entity_changes);
  }

  EXPECT_CALL(processor(), Put(_, _, _)).Times(0);
  EXPECT_CALL(processor(), Delete(_, _)).Times(0);
  SetSyncInstallDelegateFailureIfCalled();

  sync_bridge().ApplySyncChanges(sync_bridge().CreateMetadataChangeList(),
                                 std::move(entity_changes));

  // Modify the registry with the results that we expect.
  for (int i = 0; i < 5; ++i) {
    std::unique_ptr<WebApp>& expected_policy_app =
        registry[policy_and_sync_apps[i]->app_id()];
    expected_policy_app->RemoveSource(WebAppManagement::kSync);
  }

  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

// Commits local data (e.g. installed web apps) before sync is hooked up. This
// tests that the web apps are correctly sent to USS after MergeSyncData is
// called.
TEST_F(WebAppSyncBridgeTest, CommitUpdate_CommitWhileNotTrackingMetadata) {
  EXPECT_CALL(processor(), ModelReadyToSync(_)).Times(1);
  InitSyncBridge();

  AppsList sync_apps = CreateAppsList("https://example.com/", 10);
  Registry expected_registry;
  InsertAppsListIntoRegistry(&expected_registry, sync_apps);

  EXPECT_CALL(processor(), Put(_, _, _)).Times(0);
  EXPECT_CALL(processor(), Delete(_, _)).Times(0);
  EXPECT_CALL(processor(), IsTrackingMetadata())
      .WillOnce(testing::Return(false));

  std::unique_ptr<WebAppRegistryUpdate> update = sync_bridge().BeginUpdate();

  for (const std::unique_ptr<WebApp>& app : sync_apps)
    update->CreateApp(std::make_unique<WebApp>(*app));

  CommitUpdate(std::move(update));
  testing::Mock::VerifyAndClear(&processor());

  // Do MergeSyncData next.
  base::RunLoop run_loop;
  ON_CALL(processor(), Put(_, _, _))
      .WillByDefault([&](const std::string& storage_key,
                         std::unique_ptr<syncer::EntityData> entity_data,
                         syncer::MetadataChangeList* metadata) {
        EXPECT_TRUE(RemoveEntityDataAppFromAppsList(storage_key, *entity_data,
                                                    &sync_apps));
        if (sync_apps.empty())
          run_loop.Quit();
      });

  EXPECT_CALL(processor(), Delete(_, _)).Times(0);
  EXPECT_CALL(processor(), IsTrackingMetadata())
      .WillOnce(testing::Return(true));

  sync_bridge().MergeSyncData(sync_bridge().CreateMetadataChangeList(),
                              syncer::EntityChangeList{});
  run_loop.Run();

  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), expected_registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

TEST_F(WebAppSyncBridgeTest, CommitUpdate_CreateSyncApp) {
  InitSyncBridge();

  AppsList sync_apps = CreateAppsList("https://example.com/", 10);
  Registry expected_registry;
  InsertAppsListIntoRegistry(&expected_registry, sync_apps);

  ON_CALL(processor(), Put(_, _, _))
      .WillByDefault([&](const std::string& storage_key,
                         std::unique_ptr<syncer::EntityData> entity_data,
                         syncer::MetadataChangeList* metadata) {
        ASSERT_TRUE(base::Contains(expected_registry, storage_key));
        const std::unique_ptr<WebApp>& expected_app =
            expected_registry.at(storage_key);
        EXPECT_TRUE(IsSyncDataEqual(*expected_app, *entity_data));
        RemoveWebAppFromAppsList(&sync_apps, storage_key);
      });
  EXPECT_CALL(processor(), Delete(_, _)).Times(0);
  EXPECT_CALL(processor(), IsTrackingMetadata())
      .WillOnce(testing::Return(true));

  std::unique_ptr<WebAppRegistryUpdate> update = sync_bridge().BeginUpdate();

  for (const std::unique_ptr<WebApp>& app : sync_apps)
    update->CreateApp(std::make_unique<WebApp>(*app));

  CommitUpdate(std::move(update));

  EXPECT_TRUE(sync_apps.empty());
  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), expected_registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

TEST_F(WebAppSyncBridgeTest, CommitUpdate_UpdateSyncApp) {
  AppsList sync_apps = CreateAppsList("https://example.com/", 10);
  Registry registry;
  InsertAppsListIntoRegistry(&registry, sync_apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();

  ON_CALL(processor(), Put(_, _, _))
      .WillByDefault([&](const std::string& storage_key,
                         std::unique_ptr<syncer::EntityData> entity_data,
                         syncer::MetadataChangeList* metadata) {
        ASSERT_TRUE(base::Contains(registry, storage_key));
        const std::unique_ptr<WebApp>& expected_app = registry.at(storage_key);
        EXPECT_TRUE(IsSyncDataEqual(*expected_app, *entity_data));
        RemoveWebAppFromAppsList(&sync_apps, storage_key);
      });
  EXPECT_CALL(processor(), Delete(_, _)).Times(0);

  std::unique_ptr<WebAppRegistryUpdate> update = sync_bridge().BeginUpdate();

  for (const std::unique_ptr<WebApp>& app : sync_apps) {
    // Obtain a writeable handle.
    WebApp* sync_app = update->UpdateApp(app->app_id());

    WebApp::SyncFallbackData sync_fallback_data;
    sync_fallback_data.name = "Updated Sync Name";
    sync_fallback_data.theme_color = SK_ColorBLACK;
    sync_app->SetSyncFallbackData(std::move(sync_fallback_data));
    sync_app->SetUserDisplayMode(UserDisplayMode::kBrowser);

    // Override the app in the expected registry.
    registry[sync_app->app_id()] = std::make_unique<WebApp>(*sync_app);
  }

  CommitUpdate(std::move(update));

  EXPECT_TRUE(sync_apps.empty());
  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

TEST_F(WebAppSyncBridgeTest, CommitUpdate_DeleteSyncApp) {
  AppsList sync_apps = CreateAppsList("https://example.com/", 10);
  Registry registry;
  InsertAppsListIntoRegistry(&registry, sync_apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();

  EXPECT_CALL(processor(), Put(_, _, _)).Times(0);
  ON_CALL(processor(), Delete(_, _))
      .WillByDefault([&](const std::string& storage_key,
                         syncer::MetadataChangeList* metadata) {
        EXPECT_TRUE(base::Contains(registry, storage_key));
        RemoveWebAppFromAppsList(&sync_apps, storage_key);
        // Delete the app in the expected registry.
        registry.erase(storage_key);
      });

  std::unique_ptr<WebAppRegistryUpdate> update = sync_bridge().BeginUpdate();

  for (const std::unique_ptr<WebApp>& app : sync_apps)
    update->DeleteApp(app->app_id());

  CommitUpdate(std::move(update));

  EXPECT_TRUE(sync_apps.empty());
  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

TEST_F(WebAppSyncBridgeTest,
       CommitUpdate_CreateSyncAppWithOverlappingPolicyApp) {
  AppsList policy_apps;
  for (int i = 0; i < 10; ++i) {
    std::unique_ptr<WebApp> policy_app = test::CreateWebApp(
        GURL("https://example.com/" + base::NumberToString(i)),
        WebAppManagement::kPolicy);
    policy_apps.push_back(std::move(policy_app));
  }

  Registry registry;
  InsertAppsListIntoRegistry(&registry, policy_apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();

  ON_CALL(processor(), Put(_, _, _))
      .WillByDefault([&](const std::string& storage_key,
                         std::unique_ptr<syncer::EntityData> entity_data,
                         syncer::MetadataChangeList* metadata) {
        ASSERT_TRUE(base::Contains(registry, storage_key));
        const WebApp& expected_app = *registry.at(storage_key);

        // kPolicy and Name is the difference for the sync "view". Add them to
        // make operator== work.
        std::unique_ptr<WebApp> entity_data_app =
            std::make_unique<WebApp>(expected_app.app_id());
        entity_data_app->AddSource(WebAppManagement::kPolicy);
        entity_data_app->SetName("Name");

        EXPECT_TRUE(IsSyncDataEqualIfApplied(
            expected_app, std::move(entity_data_app), *entity_data));

        RemoveWebAppFromAppsList(&policy_apps, storage_key);
      });
  EXPECT_CALL(processor(), Delete(_, _)).Times(0);

  std::unique_ptr<WebAppRegistryUpdate> update = sync_bridge().BeginUpdate();

  for (int i = 0; i < 10; ++i) {
    WebApp* app_to_update = update->UpdateApp(policy_apps[i]->app_id());

    // Add kSync source to first 5 apps. Modify the rest 5 apps locally.
    if (i < 5)
      app_to_update->AddSource(WebAppManagement::kSync);
    else
      app_to_update->SetDescription("Local policy app");

    // Override the app in the expected registry.
    registry[app_to_update->app_id()] =
        std::make_unique<WebApp>(*app_to_update);
  }

  CommitUpdate(std::move(update));

  EXPECT_EQ(5u, policy_apps.size());
  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

TEST_F(WebAppSyncBridgeTest,
       CommitUpdate_DeleteSyncAppWithOverlappingPolicyApp) {
  AppsList policy_and_sync_apps;
  for (int i = 0; i < 10; ++i) {
    std::unique_ptr<WebApp> policy_and_sync_app = test::CreateWebApp(
        GURL("https://example.com/" + base::NumberToString(i)));
    policy_and_sync_app->AddSource(WebAppManagement::kPolicy);
    policy_and_sync_app->AddSource(WebAppManagement::kSync);
    policy_and_sync_apps.push_back(std::move(policy_and_sync_app));
  }

  Registry registry;
  InsertAppsListIntoRegistry(&registry, policy_and_sync_apps);
  database_factory().WriteRegistry(registry);
  InitSyncBridge();

  ON_CALL(processor(), Put(_, _, _))
      .WillByDefault([&](const std::string& storage_key,
                         std::unique_ptr<syncer::EntityData> entity_data,
                         syncer::MetadataChangeList* metadata) {
        // Local changes to synced apps cause excessive |Put|.
        // See TODO in WebAppSyncBridge::UpdateSync.
        RemoveWebAppFromAppsList(&policy_and_sync_apps, storage_key);
      });
  ON_CALL(processor(), Delete(_, _))
      .WillByDefault([&](const std::string& storage_key,
                         syncer::MetadataChangeList* metadata) {
        ASSERT_TRUE(base::Contains(registry, storage_key));
        RemoveWebAppFromAppsList(&policy_and_sync_apps, storage_key);
      });

  std::unique_ptr<WebAppRegistryUpdate> update = sync_bridge().BeginUpdate();

  for (int i = 0; i < 10; ++i) {
    WebApp* app_to_update =
        update->UpdateApp(policy_and_sync_apps[i]->app_id());

    // Remove kSync source from first 5 apps. Modify the rest 5 apps locally.
    if (i < 5)
      app_to_update->RemoveSource(WebAppManagement::kSync);
    else
      app_to_update->SetDescription("Local policy app");

    // Override the app in the expected registry.
    registry[app_to_update->app_id()] =
        std::make_unique<WebApp>(*app_to_update);
  }

  CommitUpdate(std::move(update));

  EXPECT_TRUE(policy_and_sync_apps.empty());
  EXPECT_TRUE(IsRegistryEqual(registrar_registry(), registry));
  EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}

// Test that any apps that are still pending install from sync (or,
// |is_from_sync_and_pending_installation|) are continued to be installed when
// the bridge initializes.
TEST_F(WebAppSyncBridgeTest, InstallAppsFromSyncAndPendingInstallation) {
  AppsList apps_in_sync_install = CreateAppsList("https://example.com/", 10);
  for (std::unique_ptr<WebApp>& app : apps_in_sync_install) {
    app->SetIsLocallyInstalled(AreAppsLocallyInstalledBySync());
    app->SetIsFromSyncAndPendingInstallation(true);
  }

  Registry registry;
  InsertAppsListIntoRegistry(&registry, apps_in_sync_install);
  database_factory().WriteRegistry(registry);

  base::RunLoop run_loop;
  install_manager().SetInstallWebAppsAfterSyncDelegateForTesting(
      base::BindLambdaForTesting(
          [&](std::vector<WebApp*> apps_to_install,
              WebAppInstallManager::RepeatingInstallCallback callback) {
            for (WebApp* app_to_install : apps_to_install) {
              // The app must be registered.
              EXPECT_TRUE(registrar().GetAppById(app_to_install->app_id()));
              RemoveWebAppFromAppsList(&apps_in_sync_install,
                                       app_to_install->app_id());
            }

            EXPECT_TRUE(apps_in_sync_install.empty());
            RunCallbacksOnInstall(
                apps_to_install, callback,
                webapps::InstallResultCode::kSuccessNewInstall);
            run_loop.Quit();
          }));

  InitSyncBridge();

  run_loop.Run();
}

// Tests that OnWebAppsWillBeUpdatedFromSync observer notification is called
// properly.
TEST_F(WebAppSyncBridgeTest, ApplySyncChanges_OnWebAppsWillBeUpdatedFromSync) {
  AppsList initial_registry_apps = CreateAppsList("https://example.com/", 10);
  for (std::unique_ptr<WebApp>& app : initial_registry_apps)
    app->SetUserDisplayMode(UserDisplayMode::kBrowser);
  InitSyncBridgeFromAppList(initial_registry_apps);

  WebAppTestRegistryObserverAdapter observer{&registrar()};
  base::RunLoop run_loop;

  observer.SetWebAppWillBeUpdatedFromSyncDelegate(base::BindLambdaForTesting(
      [&](const std::vector<const WebApp*>& new_apps_state) {
        EXPECT_EQ(5u, new_apps_state.size());

        for (const WebApp* new_app_state : new_apps_state) {
          const WebApp* old_app_state =
              registrar().GetAppById(new_app_state->app_id());
          EXPECT_NE(*old_app_state, *new_app_state);

          EXPECT_EQ(old_app_state->user_display_mode(),
                    UserDisplayMode::kBrowser);
          EXPECT_EQ(new_app_state->user_display_mode(),
                    UserDisplayMode::kStandalone);

          // new and old states must be equal if diff fixed:
          auto old_app_state_no_diff = std::make_unique<WebApp>(*old_app_state);
          old_app_state_no_diff->SetUserDisplayMode(
              UserDisplayMode::kStandalone);
          EXPECT_EQ(*old_app_state_no_diff, *new_app_state);

          RemoveWebAppFromAppsList(&initial_registry_apps,
                                   new_app_state->app_id());
        }

        run_loop.Quit();
      }));

  AppsList apps_server_state;

  // Update first 5 apps: change user_display_mode field only.
  for (int i = 0; i < 5; ++i) {
    auto app_server_state = std::make_unique<WebApp>(*initial_registry_apps[i]);
    app_server_state->SetUserDisplayMode(UserDisplayMode::kStandalone);
    apps_server_state.push_back(std::move(app_server_state));
  }

  sync_bridge_test_utils::UpdateApps(sync_bridge(), apps_server_state);

  run_loop.Run();

  // 5 other apps left unchanged:
  EXPECT_EQ(5u, initial_registry_apps.size());
  for (int i = 0; i < 5; ++i) {
    EXPECT_EQ(UserDisplayMode::kBrowser,
              initial_registry_apps[i]->user_display_mode());
  }
}

TEST_F(WebAppSyncBridgeTest, RetryIncompleteUninstalls) {
  AppsList initial_registry_apps = CreateAppsList("https://example.com/", 5);
  std::vector<AppId> initial_app_ids;
  for (std::unique_ptr<WebApp>& app : initial_registry_apps) {
    app->SetUserDisplayMode(UserDisplayMode::kBrowser);
    app->SetIsUninstalling(true);
    initial_app_ids.push_back(app->app_id());
  }

  SetSyncInstallDelegateFailureIfCalled();

  base::RunLoop run_loop;
  install_manager().SetRetryIncompleteUninstallsDelegateForTesting(
      base::BindLambdaForTesting(
          [&](const base::flat_set<AppId>& apps_to_uninstall) {
            EXPECT_EQ(apps_to_uninstall.size(), 5ul);
            EXPECT_THAT(apps_to_uninstall, ::testing::UnorderedElementsAreArray(
                                               apps_to_uninstall));
            run_loop.Quit();
          }));

  InitSyncBridgeFromAppList(initial_registry_apps);

  run_loop.Run();
}

}  // namespace web_app


import re
import numpy
from datetime import datetime

from processing_components.visibility.msv2supp import cmp_to_total, STOKES_CODES, NUMERIC_STOKES, merge_baseline, \
    geo_to_ecef, get_eci_transform

from data_models.memory_data_models import Visibility, BlockVisibility, Configuration

import logging

log = logging.getLogger(__name__)

__version__ = '0.1'
__revision__ = '$Rev$'
__all__ = ['STOKES_CODES', 'NUMERIC_STOKES', 'Stand', 'Observatory', 'Antenna', 'BaseData']

try:
    from casacore.tables import table, tableutil

    @cmp_to_total
    class Stand(object):
        """
        Object to store the information (location and ID) about a stand.
        Stores stand:
         * ID number (id)
         * Position relative to the center stake in meters (x,y,z)

        The x, y, and z positions can also be accessed through subscripts:
         Stand[0] = x
         Stand[1] = y
         Stand[2] = z
        """

        def __init__(self, id, x, y, z):
            self.id = id
            self.x = float(x)
            self.y = float(y)
            self.z = float(z)

        def __lt__(self, y):
            if self.id < y.id:
                return True
            else:
                return False

        def __le__(self, y):
            if self.id <= y.id:
                return True
            else:
                return False

        def __eq__(self, y):
            if self.id == y.id:
                return True
            else:
                return False

        def __gt__(self, y):
            if self.id > y.id:
                return True
            else:
                return False

        def __ge__(self, y):
            if self.id >= y.id:
                return True
            else:
                return False

        def __cmp__(self, y):
            if self.id > y.id:
                return 1
            elif self.id < y.id:
                return -1
            else:
                return 0

        def __str__(self):
            return "Stand %i:  x=%+.2f m, y=%+.2f m, z=%+.2f m" % (self.id, self.x, self.y, self.z)

        def __reduce__(self):
            return (Stand, (self.id, self.x, self.y, self.z))

        def __getitem__(self, key):
            if key == 0:
                return self.x
            elif key == 1:
                return self.y
            elif key == 2:
                return self.z
            else:
                raise ValueError("Subscript %i out of range" % key)

        def __setitem__(self, key, value):
            if key == 0:
                self.x = float(value)
            elif key == 1:
                self.y = float(value)
            elif key == 2:
                self.z = float(value)
            else:
                raise ValueError("Subscript %i out of range" % key)

        def __add__(self, std):
            try:
                # If its a Stand instance, do this
                out = (self.x + std.x, self.y + std.y, self.z + std.z)
            except AttributeError:
                try:
                    # Maybe it is a list/tuple, so do this
                    out = (self.x + std[0], self.y + std[1], self.z + std[2])
                except TypeError:
                    out = (self.x + std, self.y + std, self.z + std)

            return out

        def __sub__(self, std):
            try:
                # If its a Stand instance, do this
                out = (self.x - std.x, self.y - std.y, self.z - std.z)
            except AttributeError:
                try:
                    # Maybe it is a list/tuple, so do this
                    out = (self.x - std[0], self.y - std[1], self.z - std[2])
                except TypeError:
                    out = (self.x - std, self.y - std, self.z - std)

            return out

    class Observatory(object):
        def __init__(self, name, lon, lat, alt):
            self.name = name
            self.lon = lon
            self.lat = lat
            self.alt = alt

    class Antenna(object):
        """
        Object to store the information about an antenna.  Stores antenna:
         * ID number (id)
         * Stand instance the antenna is part of (stand)
         * Polarization (0 == N-S; pol)
         * Antenna vertical mis-alignment in degrees (theta)
         * Antenna rotation mis-alignment in degrees (phi)
         * Fee instance the antenna is attached to (fee)
         * Port of the FEE used for the antenna (feePort)
         * Cable instance used to connect the antenna (cable)

         Some arguments are designed for future extension

        """

        def __init__(self, id, stand=None, pol=0, theta=0.0, phi=0.0,
                     fee=None, feePort=1, cable=None):
            self.id = int(id)

            if stand is None:
                self.stand = Stand(0, 0, 0, 0)
            else:
                self.stand = stand

            self.pol = int(pol)
            self.theta = float(theta)
            self.phi = float(phi)

            self.FEE = fee
            self.feePort = feePort

            self.cable = cable

        def __str__(self):
            return "Antenna %i: stand=%i, polarization=%i; " % (self.id, self.stand.id, self.pol)

        def __reduce__(self):
            return (Antenna, (
                self.id, self.stand, self.pol, self.theta, self.phi, self.fee, self.feePort, self.cable))

        def __lt__(self, y):
            if self.id < y.id:
                return True
            else:
                return False

        def __le__(self, y):
            if self.id <= y.id:
                return True
            else:
                return False

        def __eq__(self, y):
            if self.id == y.id:
                return True
            else:
                return False

        def __gt__(self, y):
            if self.id > y.id:
                return True
            else:
                return False

        def __ge__(self, y):
            if self.id >= y.id:
                return True
            else:
                return False

        def __cmp__(self, y):
            if self.id > y.id:
                return 1
            elif self.id < y.id:
                return -1
            else:
                return 0

    class Frequency:
        """
        Information about the frequency setup used in the file.
        """

        def __init__(self, bandFreq, channelWidth, bandwidth):
            self.id = 1
            self.bandFreq = bandFreq
            self.chWidth = channelWidth
            self.totalBW = bandwidth
            self.sideBand = 1
            self.baseBand = 0

    @cmp_to_total
    class MS_UVData(object):
        """
        UV visibility data set for a given observation time.
        """

        def __init__(self, obstime, inttime, baselines, visibilities, weights=None, pol=STOKES_CODES['XX'], source=None,
                     phasecentre=None, uvw=None):
            self.obstime = obstime
            self.inttime = inttime
            self.baselines = baselines
            self.visibilities = visibilities
            self.weights = weights
            self.pol = pol
            self.source = source
            self.phasecentre = phasecentre
            self.uvw = uvw

        def __lt__(self, y):
            sID = (self.obstime, abs(self.pol))
            yID = (y.obstime, abs(y.pol))
            if sID > yID:
                return False
            else:
                return True

        def __le__(self, y):
            sID = (self.obstime, abs(self.pol))
            yID = (y.obstime, abs(y.pol))
            if sID <= yID:
                return True
            else:
                return False

        def __ge__(self, y):
            sID = (self.obstime, abs(self.pol))
            yID = (y.obstime, abs(y.pol))
            if sID >= yID:
                return True
            else:
                return False

        def __gt__(self, y):
            sID = (self.obstime, abs(self.pol))
            yID = (y.obstime, abs(y.pol))
            if sID < yID:
                return False
            else:
                return True

        def __eq__(self, y):
            sID = (self.obstime, abs(self.pol))
            yID = (y.obstime, abs(y.pol))
            if sID == yID:
                return True
            else:
                return False

        def __cmp__(self, y):
            """
            Function to sort the self.data list in order of time and then
            polarization code.
            """

            sID = (self.obstime, abs(self.pol))
            yID = (y.obstime, abs(y.pol))

            if sID > yID:
                return 1
            elif sID < yID:
                return -1
            else:
                return 0

        def time(self):
            return self.obstime

        def get_uvw(self, HA, dec, obs):

            Nbase = len(self.baselines)
            uvw = numpy.zeros((Nbase, 3), dtype=numpy.float32)

            # Phase center coordinates
            # Convert numbers to radians and, for HA, hours to degrees
            HA2 = HA * 15.0 * numpy.pi / 180
            dec2 = dec * numpy.pi / 180
            lat2 = obs.location.geodetic[1].to('rad').value

            # Coordinate transformation matrices
            trans1 = numpy.array([[0, -numpy.sin(lat2), numpy.cos(lat2)],
                                  [1, 0, 0],
                                  [0, numpy.cos(lat2), numpy.sin(lat2)]])
            trans2 = numpy.array([[numpy.sin(HA2), numpy.cos(HA2), 0],
                                  [-numpy.sin(dec2) * numpy.cos(HA2), numpy.sin(dec2) * numpy.sin(HA2),
                                   numpy.cos(dec2)],
                                  [numpy.cos(dec2) * numpy.cos(HA2), -numpy.cos(dec2) * numpy.sin(HA2),
                                   numpy.sin(dec2)]])

            for i, (a1, a2) in enumerate(self.baselines):
                # Go from a east, north, up coordinate system to a celestial equation,
                # east, north celestial pole system
                xyzPrime = a1.stand - a2.stand
                xyz = trans1 @ numpy.array([[xyzPrime[0]], [xyzPrime[1]], [xyzPrime[2]]])

                # Go from CE, east, NCP to u, v, w
                temp = trans2 @ xyz
                uvw[i, :] = numpy.squeeze(temp)  # / speed_of_light

            return uvw

        def argsort(self, mapper=None, shift=16):
            packed = []
            for a1, a2 in self.baselines:
                if mapper is None:
                    s1, s2 = a1.stand.id, a2.stand.id
                else:
                    s1, s2 = mapper.index(a1.stand.id), mapper.index(a2.stand.id)
                packed.append(merge_baseline(s1, s2, shift=shift))
            packed = numpy.array(packed, dtype=numpy.int32)

            return numpy.argsort(packed)

    class BaseData(object):
        """
        Base Data class: For an observation of interferometer, we should have:
        Antenna, Frequency, Visibility Funcation, UVW
        """

        _MAX_ANTS = 255
        _PACKING_BIT_SHIFT = 8
        _STOKES_CODES = STOKES_CODES

        class _Antenna(object):
            """
            Holds information describing the location and properties of an antenna.
            """

            def __init__(self, id, x, y, z, bits=8, name=None):
                self.id = id
                self.x = x
                self.y = y
                self.z = z
                self.levels = bits
                self.name = name

            def getName(self):
                if self.name is None:
                    if isinstance(self.id, str):
                        return self.id
                    else:
                        return "AT%03i" % self.id
                else:
                    return self.name

        def parse_time(self, ref_time):
            """
            Given a time as either a integer, float, string, or datetime object,
            convert it to a string in the formation 'YYYY-MM-DDTHH:MM:SS'.
            """

            # Valid time string (modulo the 'T')
            timeRE = re.compile(r'\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(\.\d+)?')

            if type(ref_time) in (int, float):
                refDateTime = datetime.utcfromtimestamp(ref_time)
                ref_time = refDateTime.strftime("%Y-%m-%dT%H:%M:%S")
            elif type(ref_time) == datetime:
                ref_time = ref_time.strftime("%Y-%m-%dT%H:%M:%S")
            elif type(ref_time) == str:
                # Make sure that the string times are of the correct format
                if re.match(timeRE, ref_time) is None:
                    raise RuntimeError("Malformed date/time provided: %s" % ref_time)
                else:
                    ref_time = ref_time.replace(' ', 'T', 1)
            else:
                raise RuntimeError("Unknown time format provided.")

            return ref_time

        def __init__(self, filename, ref_time=0.0, source_name = None, frame='ITRF', verbose=False):
            # File-specific information
            self.filename = filename
            self.verbose = verbose
            self.site_config = None
            # Observatory-specific information
            self.siteName = 'Unknown'
            self.frame = frame
            self.source_name = source_name

            # Observation-specific information
            self.ref_time = self.parse_time(ref_time)
            self.nant = 0
            self.nchan = 0
            self.nstokes = 0
            self.refVal = 0
            self.refPix = 0
            self.channel_width = 0

            # Parameters that store the meta-data and data
            self.array = []
            self.freq = []
            self.stokes = []
            self.data = []
            self.uvw_arl = None

        def __enter__(self):
            return self

        def __exit__(self, type, value, tb):
            self.write()
            self.close()

        def set_stokes(self, polList):
            """
            Given a list of Stokes parameters, update the object's parameters.
            """

            for pol in polList:
                if type(pol) == str:
                    numericPol = self._STOKES_CODES[pol.upper()]
                else:
                    numericPol = pol

                if numericPol not in self.stokes:
                    self.stokes.append(numericPol)

            # Sort into order of 'XX', 'YY', 'XY', and 'YX' or 'I', 'Q', 'U', and 'V'
            self.stokes.sort()
            if self.stokes[0] < 0:
                self.stokes.reverse()

            self.nStokes = len(self.stokes)

        def set_frequency(self, freq, channel_width):
            """
            Given a numpy array of frequencies, set the relevant common observation
            parameters and add an entry to the self.freq list.
            """
            if self.nchan == 0:
                self.nchan = len(freq)
                self.refVal = freq[0]
                self.refPix = 1
                self.channelWidth = channel_width[0]
                offset = 0.0
            else:
                assert (len(freq) == self.nchan)
                offset = freq[0] - self.refVal
                self.channelWidth = channel_width[0]

            if self.nchan == 1:
                totalWidth = self.channel_width
            else:
                totalWidth = numpy.abs(freq[-1] - freq[0])

            freqSetup = Frequency(offset, self.channelWidth, totalWidth)
            self.freq.append(freqSetup)

        def set_geometry(self, *args, **kwds):
            """
            Given a station and an array of stands, set the relevant common observation
            parameters and add entries to the self.array list.
            """

            raise NotImplementedError

        def add_data_set(self, obstime, inttime, baselines, visibilities, weights=None, pol='XX', source=None):
            """
            Create a UVData object to store a collection of visibilities.

            """

            if type(pol) == str:
                numericPol = self._STOKES_CODES[pol.upper()]
            else:
                numericPol = pol

            self.data.append(
                MS_UVData(obstime, inttime, baselines, visibilities, weights=weights, pol=numericPol, source=source))

        def write(self):
            """
            Fill in the file will all of the required supporting metadata.
            """

            raise NotImplementedError

        def close(self):
            """
            Close out the file.
            """

            raise NotImplementedError

except ImportError:
    import warnings

    warnings.warn('Cannot import casacore.tables, MS support disabled', ImportWarning)

    raise RuntimeError("Cannot import casacore.tables, MS support disabled")


<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<android.support.v7.internal.widget.FitWindowsLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/action_bar_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:fitsSystemWindows="true">

    <android.support.v7.internal.widget.ViewStubCompat
        android:id="@+id/action_mode_bar_stub"
        android:inflatedId="@+id/action_mode_bar"
        android:layout="@layout/abc_action_mode_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <include layout="@layout/abc_screen_content_include" />

</android.support.v7.internal.widget.FitWindowsLinearLayout>
<!-- From: file:/usr/local/google/buildbot/repo_clients/https___googleplex-android.googlesource.com_a_platform_manifest.git/mnc-sdk-release/frameworks/support/v7/appcompat/res/layout/abc_screen_simple.xml --><!-- From: file:/Users/bkon4208/Documents/workspace/AdMobBannerProject/app/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.0.1/res/layout/abc_screen_simple.xml -->


DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

####################
# GLOBAL VARIABLES #
####################

TEST_EXIT_CODE=0
CAT_FILE_LIST=""

#######################
# AUXILIARY FUNCTIONS #
#######################

function cookbook_check
{
    TEST_RESULT=$?
    TEST_CASE=$1
    TEST_CASE_DETAILS=$2
    CAT_FILE=$3

    if [ "$TEST_RESULT" -ne "0" ]
    then
        echo "ERROR: $TEST_CASE $TEST_CASE_DETAILS"
        TEST_EXIT_CODE=1
        if [ "$CAT_FILE" != "" ]
        then
            CAT_FILE_LIST="$CAT_FILE_LIST $CAT_FILE"
        fi
    else
        echo "PASS: $TEST_CASE $TEST_CASE_DETAILS"
    fi
}

function simple_testcase
{
    TESTCASE=$1
    LOGFILE=${TESTCASE,,}.log
    ./bin/${TESTCASE,,} \
        >& ./log/$LOGFILE
    cookbook_check "$TESTCASE" "(run check)"

    diff ./ref/$LOGFILE ./log/$LOGFILE >& ./log/diff_$LOGFILE
    cookbook_check "$TESTCASE" "(logfile check)" "./log/diff_$LOGFILE"
}

function check_wavinfo
{
    TESTCASE=$1
    WAVFILE=$2

    LOGFILE=${TESTCASE,,}_wavinfo.log
    wavinfo ./out/$WAVFILE >& ./log/$LOGFILE
    cookbook_check "$TESTCASE" "(wavinfo)"

    diff ./ref/$LOGFILE ./log/$LOGFILE >& ./log/diff_$LOGFILE
    cookbook_check "$TESTCASE" "(wavinfo logfile check)" "./log/diff_$LOGFILE"
}

function check_wavefile
{
    TESTCASE=$1
    WAVFILE=$2

    LOGFILE=${TESTCASE,,}.log

    diff ./ref/$WAVFILE ./out/$WAVFILE >& ./log/diff_$LOGFILE
    cookbook_check "$TESTCASE" "(wavfile check)" ""
}

function cleanup_data
{
    rm log/*
    rm out/*
    rm src/*
    rm cookbook.ada
}

################
# PREPARATIONS #
################

# Change to cookbook directory
cd $DIR/../cookbook

# Create .ada file based on Markdown file
sed -n '/^~~~~~~~~~~/,/^~~~~~~~~~~/ p' < cookbook.md | sed '/^~~~~~~~~~~/ d' > cookbook.ada
cookbook_check "PREPARATIONS" "(sed)" ""

# Create source-code files based on cookbook file
gnatchop -wr cookbook.ada ./src >& ./log/gnatchop.log
cookbook_check "PREPARATIONS" "(gnatchop)" "./log/gnatchop.log"

# Set GPR environment variable
AUDIO_WAVEFILES_PATH=$(cd .. && pwd)
if [ -z "$GPR_PROJECT_PATH" ]
then
    export GPR_PROJECT_PATH=${AUDIO_WAVEFILES_PATH}
else
    export GPR_PROJECT_PATH="${GPR_PROJECT_PATH}:${AUDIO_WAVEFILES_PATH}"
fi
echo "GPR_PROJECT_PATH = $GPR_PROJECT_PATH" >& ./log/gprbuild_env.log

# Build application for each source-code file
gprbuild ./cookbook.gpr  >& ./log/gprbuild.log
cookbook_check "PREPARATIONS" "(gprbuild)" "./log/gprbuild.log"

##############
# RUN & TEST #
##############

simple_testcase Open_Close_Wavefile_For_Reading

simple_testcase Open_Close_Wavefile_For_Writing
check_wavinfo   Open_Close_Wavefile_For_Writing     "test.wav"

simple_testcase Display_Errors_For_Wavefiles

simple_testcase List_Errors_For_Wavefiles

simple_testcase Display_RIFF_Chunks

simple_testcase Read_Display_Wavefile_Data

simple_testcase Write_Mono_Silence_Wavefile
check_wavinfo   Write_Mono_Silence_Wavefile         "1ch_silence.wav"
check_wavefile  Write_Mono_Silence_Wavefile         "1ch_silence.wav"

simple_testcase Write_Stereo_Sine_Wavefile
check_wavinfo   Write_Stereo_Sine_Wavefile          "2ch_sine.wav"
check_wavefile  Write_Stereo_Sine_Wavefile          "2ch_sine.wav"

simple_testcase Write_5_1_Channel_Sine_Wavefile
check_wavinfo   Write_5_1_Channel_Sine_Wavefile     "5_1ch_sine.wav"
check_wavefile  Write_5_1_Channel_Sine_Wavefile     "5_1ch_sine.wav"

simple_testcase Write_7_1_4_Channel_Sine_Wavefile
check_wavinfo   Write_7_1_4_Channel_Sine_Wavefile   "7_1_4ch_sine.wav"
check_wavefile  Write_7_1_4_Channel_Sine_Wavefile   "7_1_4ch_sine.wav"

simple_testcase Display_Channel_Config

simple_testcase Append_Wavefile
check_wavinfo   Append_Wavefile                         "2ch_sine_append.wav"
check_wavefile  Append_Wavefile                         "2ch_sine_append.wav"

simple_testcase Copy_Wavefile
check_wavinfo   Copy_Wavefile                           "2ch_sine.wav"
check_wavefile  Copy_Wavefile                           "2ch_sine.wav"

simple_testcase Copy_Wavefile_Using_Fixed_Point_Buffer
check_wavinfo   Copy_Wavefile_Using_Fixed_Point_Buffer  "2ch_sine.wav"
check_wavefile  Copy_Wavefile_Using_Fixed_Point_Buffer  "2ch_sine.wav"

simple_testcase Copy_Parts_Of_Wavefile
check_wavinfo   Copy_Parts_Of_Wavefile                  "looped_clip.wav"
check_wavefile  Copy_Parts_Of_Wavefile                  "looped_clip.wav"

simple_testcase Convert_Fixed_To_Float_Wavefile
check_wavinfo   Convert_Fixed_To_Float_Wavefile         "2ch_float_sine.wav"
check_wavefile  Convert_Fixed_To_Float_Wavefile         "2ch_float_sine.wav"

simple_testcase Downmix_Stereo_To_Mono_Wavefile
check_wavinfo   Downmix_Stereo_To_Mono_Wavefile         "1ch_dmx_sine.wav"
check_wavefile  Downmix_Stereo_To_Mono_Wavefile         "1ch_dmx_sine.wav"

simple_testcase Downmix_5_1_To_2_0_Wavefile
check_wavinfo   Downmix_5_1_To_2_0_Wavefile             "2_0ch_dmx_sine.wav"
check_wavefile  Downmix_5_1_To_2_0_Wavefile             "2_0ch_dmx_sine.wav"

simple_testcase Downmix_7_1_4_To_5_1_Wavefile
check_wavinfo   Downmix_7_1_4_To_5_1_Wavefile           "5_1ch_dmx_sine.wav"
check_wavefile  Downmix_7_1_4_To_5_1_Wavefile           "5_1ch_dmx_sine.wav"

simple_testcase Direct_Copy_Wavefile
check_wavinfo   Direct_Copy_Wavefile                    "2ch_sine.wav"
check_wavefile  Direct_Copy_Wavefile                    "2ch_sine.wav"

simple_testcase Direct_Copy_Float_Wavefile
check_wavinfo   Direct_Copy_Float_Wavefile              "2ch_float_sine.wav"
check_wavefile  Direct_Copy_Float_Wavefile              "2ch_float_sine.wav"

simple_testcase Convert_8_Bit_To_16_Bit_Wavefile

simple_testcase Read_To_Memory_Channel_Interleaved

simple_testcase Read_To_Memory_Per_Channel

simple_testcase Extract_XML_Chunk
check_wavefile  Extract_XML_Chunk                       "2020-08-09.xml"

################
# FINALIZATION #
################

if [ "$CAT_FILE_LIST" != "" ]
then
    for cat_file in $CAT_FILE_LIST
    do
        echo "------------------------------------------"
        echo $cat_file
        echo "------------------------------------------"
        cat $cat_file
    done
fi

# cleanup_data

exit $TEST_EXIT_CODE


namespace TestRoom
{
    partial class Mainform
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.bTestVirtualDesktop = new System.Windows.Forms.Button();
            this.bTestLoaderService = new System.Windows.Forms.Button();
            this.bWcf = new System.Windows.Forms.Button();
            this.bTestCfg = new System.Windows.Forms.Button();
            this.bShowTaskbar = new System.Windows.Forms.Button();
            this.SuspendLayout();
            //
            // bTestVirtualDesktop
            //
            this.bTestVirtualDesktop.Location = new System.Drawing.Point(12, 12);
            this.bTestVirtualDesktop.Name = "bTestVirtualDesktop";
            this.bTestVirtualDesktop.Size = new System.Drawing.Size(151, 23);
            this.bTestVirtualDesktop.TabIndex = 0;
            this.bTestVirtualDesktop.Text = "Test Virtual Desktop";
            this.bTestVirtualDesktop.UseVisualStyleBackColor = true;
            this.bTestVirtualDesktop.Click += new System.EventHandler(this.bTestVirtualDesktop_Click);
            //
            // bTestLoaderService
            //
            this.bTestLoaderService.Location = new System.Drawing.Point(12, 41);
            this.bTestLoaderService.Name = "bTestLoaderService";
            this.bTestLoaderService.Size = new System.Drawing.Size(151, 23);
            this.bTestLoaderService.TabIndex = 1;
            this.bTestLoaderService.Text = "Test Loader Service";
            this.bTestLoaderService.UseVisualStyleBackColor = true;
            this.bTestLoaderService.Click += new System.EventHandler(this.bTestLoaderService_Click);
            //
            // bWcf
            //
            this.bWcf.Location = new System.Drawing.Point(12, 70);
            this.bWcf.Name = "bWcf";
            this.bWcf.Size = new System.Drawing.Size(151, 23);
            this.bWcf.TabIndex = 1;
            this.bWcf.Text = "Test Wcf";
            this.bWcf.UseVisualStyleBackColor = true;
            this.bWcf.Click += new System.EventHandler(this.bWcf_Click);
            //
            // bTestCfg
            //
            this.bTestCfg.Location = new System.Drawing.Point(12, 99);
            this.bTestCfg.Name = "bTestCfg";
            this.bTestCfg.Size = new System.Drawing.Size(151, 23);
            this.bTestCfg.TabIndex = 2;
            this.bTestCfg.Text = "Test Cfg";
            this.bTestCfg.UseVisualStyleBackColor = true;
            this.bTestCfg.Click += new System.EventHandler(this.bTestCfg_Click);
            //
            // bShowTaskbar
            //
            this.bShowTaskbar.Location = new System.Drawing.Point(12, 128);
            this.bShowTaskbar.Name = "bShowTaskbar";
            this.bShowTaskbar.Size = new System.Drawing.Size(151, 23);
            this.bShowTaskbar.TabIndex = 3;
            this.bShowTaskbar.Text = "Show Taskbar";
            this.bShowTaskbar.UseVisualStyleBackColor = true;
            this.bShowTaskbar.Click += new System.EventHandler(this.bShowTaskbar_Click);
            //
            // Mainform
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(863, 629);
            this.Controls.Add(this.bShowTaskbar);
            this.Controls.Add(this.bTestCfg);
            this.Controls.Add(this.bWcf);
            this.Controls.Add(this.bTestLoaderService);
            this.Controls.Add(this.bTestVirtualDesktop);
            this.Name = "Mainform";
            this.Text = "Test Room";
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Button bTestVirtualDesktop;
        private System.Windows.Forms.Button bTestLoaderService;
        private System.Windows.Forms.Button bWcf;
        private System.Windows.Forms.Button bTestCfg;
        private System.Windows.Forms.Button bShowTaskbar;
    }
}


DIRECTORY=$(dirname $0)
PROGRAM=$(basename $0)
NEWLINE=$'\n'

# define the error function
function die {
    echo "$1" 1>&2
    exit 1
}

# make sure that the right number of command line arguments were passed in
test $# -eq 4 || die "Usage: ${PROGRAM} <environment name> <client name> <ca directory path> <subject> ${NEWLINE}Example: ${PROGRAM} Sandbox CoffeeBucks /some/secret/path/ 'CN=http://acmecoffee.com,O=Acme Coffee,C=US'"

# generate a new client certificate keystore for the environment
java -classpath "${DIRECTORY}/../lib/*" craterdog.security.ClientCertificateGenerator "$@"


package com.englishtown.promises.impl;

import javax.inject.Inject;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Asynchronous executor that uses a fixed thread pool
 */
public class AsyncExecutor implements Executor {

    private ExecutorService executorService = Executors.newFixedThreadPool(20);

    @Inject
    public AsyncExecutor() {

    }

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws java.util.concurrent.RejectedExecutionException if this task cannot be
     *                                                         accepted for execution
     * @throws NullPointerException                            if command is null
     */
    @Override
    public void execute(Runnable command) {
        executorService.execute(command);
    }
}


package demo.java.v2c08javabean.ChartBean.com.horstmann.corejava;

import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.beans.*;

/**
 * A custom editor for an array of floating-point numbers.
 * @version 1.21 2007-10-03
 * @author Cay Horstmann
 */
public class DoubleArrayEditor extends PropertyEditorSupport
{
   @Override
   public void setValue(Object value)
   {
      super.setValue(value);
   }

   public Component getCustomEditor()
   {
      return new DoubleArrayEditorPanel(this);
   }

   public boolean supportsCustomEditor()
   {
      return true;
   }

   public boolean isPaintable()
   {
      return true;
   }

   public String getAsText()
   {
      return null;
   }

   public void paintValue(Graphics g, Rectangle box)
   {
      Graphics2D g2 = (Graphics2D) g;
      g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
            RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
      double[] values = (double[]) getValue();
      StringBuilder s = new StringBuilder();
      for (int i = 0; i < 3; i++)
      {
         if (values.length > i) s.append(values[i]);
         if (values.length > i + 1) s.append(", ");
      }
      if (values.length > 3) s.append("...");

      g2.setPaint(Color.white);
      g2.fill(box);
      g2.setPaint(Color.black);
      FontRenderContext context = g2.getFontRenderContext();
      Rectangle2D stringBounds = g2.getFont().getStringBounds(s.toString(), context);
      double w = stringBounds.getWidth();
      double x = box.x;
      if (w < box.width) x += (box.width - w) / 2;
      double ascent = -stringBounds.getY();
      double y = box.y + (box.height - stringBounds.getHeight()) / 2 + ascent;
      g2.drawString(s.toString(), (float) x, (float) y);
   }

   public String getJavaInitializationString()
   {
      double[] values = (double[]) getValue();
      StringBuilder s = new StringBuilder();
      s.append("new double[] {");
      for (int i = 0; i < values.length; i++)
      {
         if (i > 0) s.append(", ");
         s.append(values[i]);
      }
      s.append("}");
      return s.toString();
   }
}


CREATE TABLE [tbl_Task_List] (
  [Location_ID] VARCHAR (50),
  [Request_date] DATETIME ,
  [Task_desc] VARCHAR (100),
  [Requested_by] VARCHAR (50),
  [Task_status] VARCHAR (50),
  [Date_completed] DATETIME ,
  [Followup_by] VARCHAR (50),
  [Task_notes] LONGTEXT ,
  [Followup_notes] LONGTEXT ,
   CONSTRAINT [pk_tbl_Task_List] PRIMARY KEY ([Location_ID], [Request_date], [Task_desc])
)


require 'rails_helper'

describe GamesHelper, :type => :helper do
end


package com.example.android.tvleanback2.ui;

import android.app.Activity;
import android.app.FragmentManager;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v17.leanback.app.GuidedStepFragment;
import android.support.v17.leanback.widget.GuidanceStylist;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;

import com.example.android.tvleanback2.R;

import java.util.List;

/**
 * Activity that showcases different aspects of GuidedStepFragments.
 */
public class GuidedStepActivity extends Activity {

    private static final int CONTINUE = 0;
    private static final int BACK = 1;
    private static final int OPTION_CHECK_SET_ID = 10;
    private static final String[] OPTION_NAMES = {"Option A", "Option B", "Option C"};
    private static final String[] OPTION_DESCRIPTIONS = {"Here's one thing you can do",
            "Here's another thing you can do", "Here's one more thing you can do"};
    private static final int[] OPTION_DRAWABLES = {R.drawable.ic_guidedstep_option_a,
            R.drawable.ic_guidedstep_option_b, R.drawable.ic_guidedstep_option_c};
    private static final boolean[] OPTION_CHECKED = {true, false, false};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (null == savedInstanceState) {
            GuidedStepFragment.addAsRoot(this, new FirstStepFragment(), android.R.id.content);
        }
    }

    private static void addAction(List<GuidedAction> actions, long id, String title, String desc) {
        actions.add(new GuidedAction.Builder()
                .id(id)
                .title(title)
                .description(desc)
                .build());
    }

    private static void addCheckedAction(List<GuidedAction> actions, int iconResId, Context context,
                                         String title, String desc, boolean checked) {
        GuidedAction guidedAction = new GuidedAction.Builder()
                .title(title)
                .description(desc)
                .checkSetId(OPTION_CHECK_SET_ID)
                .iconResourceId(iconResId, context)
                .build();
        guidedAction.setChecked(checked);
        actions.add(guidedAction);
    }

    public static class FirstStepFragment extends GuidedStepFragment {
        @Override
        public int onProvideTheme() {
            return R.style.Theme_Example_Leanback_GuidedStep_First;
        }

        @Override
        @NonNull
        public Guidance onCreateGuidance(@NonNull Bundle savedInstanceState) {
            String title = getString(R.string.guidedstep_first_title);
            String breadcrumb = getString(R.string.guidedstep_first_breadcrumb);
            String description = getString(R.string.guidedstep_first_description);
            Drawable icon = getActivity().getDrawable(R.drawable.ic_main_icon);
            return new Guidance(title, description, breadcrumb, icon);
        }

        @Override
        public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
            addAction(actions, CONTINUE,
                    getResources().getString(R.string.guidedstep_continue),
                    getResources().getString(R.string.guidedstep_letsdoit));
            addAction(actions, BACK,
                    getResources().getString(R.string.guidedstep_cancel),
                    getResources().getString(R.string.guidedstep_nevermind));
        }

        @Override
        public void onGuidedActionClicked(GuidedAction action) {
            FragmentManager fm = getFragmentManager();
            if (action.getId() == CONTINUE) {
                GuidedStepFragment.add(fm, new SecondStepFragment());
            } else {
                getActivity().finishAfterTransition();
            }
        }
    }

    public static class SecondStepFragment extends GuidedStepFragment {

        @Override
        @NonNull
        public Guidance onCreateGuidance(Bundle savedInstanceState) {
            String title = getString(R.string.guidedstep_second_title);
            String breadcrumb = getString(R.string.guidedstep_second_breadcrumb);
            String description = getString(R.string.guidedstep_second_description);
            Drawable icon = getActivity().getDrawable(R.drawable.ic_main_icon);
            return new Guidance(title, description, breadcrumb, icon);
        }

        @Override
        public GuidanceStylist onCreateGuidanceStylist() {
            return new GuidanceStylist() {
                @Override
                public int onProvideLayoutId() {
                    return R.layout.guidedstep_second_guidance;
                }
            };
        }

        @Override
        public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
            String desc = getResources().getString(R.string.guidedstep_action_description);
            actions.add(new GuidedAction.Builder()
                    .title(getResources().getString(R.string.guidedstep_action_title))
                    .description(desc)
                    .multilineDescription(true)
                    .infoOnly(true)
                    .enabled(false)
                    .build());
            for (int i = 0; i < OPTION_NAMES.length; i++) {
                addCheckedAction(actions,
                        OPTION_DRAWABLES[i],
                        getActivity(),
                        OPTION_NAMES[i],
                        OPTION_DESCRIPTIONS[i],
                        OPTION_CHECKED[i]);
            }
        }

        @Override
        public void onGuidedActionClicked(GuidedAction action) {
            FragmentManager fm = getFragmentManager();
            ThirdStepFragment next = ThirdStepFragment.newInstance(getSelectedActionPosition() - 1);
            GuidedStepFragment.add(fm, next);
        }

    }

    public static class ThirdStepFragment extends GuidedStepFragment {
        private final static String ARG_OPTION_IDX = "arg.option.idx";

        public static ThirdStepFragment newInstance(final int option) {
            final ThirdStepFragment f = new ThirdStepFragment();
            final Bundle args = new Bundle();
            args.putInt(ARG_OPTION_IDX, option);
            f.setArguments(args);
            return f;
        }

        @Override
        @NonNull
        public Guidance onCreateGuidance(Bundle savedInstanceState) {
            String title = getString(R.string.guidedstep_third_title);
            String breadcrumb = getString(R.string.guidedstep_third_breadcrumb);
            String description = getString(R.string.guidedstep_third_command)
                    + OPTION_NAMES[getArguments().getInt(ARG_OPTION_IDX)];
            Drawable icon = getActivity().getDrawable(R.drawable.ic_main_icon);
            return new Guidance(title, description, breadcrumb, icon);
        }

        @Override
        public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
            addAction(actions, CONTINUE, "Done", "All finished");
            addAction(actions, BACK, "Back", "Forgot something...");
        }

        @Override
        public void onGuidedActionClicked(GuidedAction action) {
            if (action.getId() == CONTINUE) {
                getActivity().finishAfterTransition();
            } else {
                getFragmentManager().popBackStack();
            }
        }

    }

}


If your function returns a type that implements `MyTrait`, you can write its
return type as `-> impl MyTrait`. This can help simplify your type signatures quite a lot!

```rust,editable
use std::iter;
use std::vec::IntoIter;

// This function combines two `Vec<i32>` and returns an iterator over it.
// Look how complicated its return type is!
fn combine_vecs_explicit_return_type(
    v: Vec<i32>,
    u: Vec<i32>,
) -> iter::Cycle<iter::Chain<IntoIter<i32>, IntoIter<i32>>> {
    v.into_iter().chain(u.into_iter()).cycle()
}

// This is the exact same function, but its return type uses `impl Trait`.
// Look how much simpler it is!
fn combine_vecs(
    v: Vec<i32>,
    u: Vec<i32>,
) -> impl Iterator<Item=i32> {
    v.into_iter().chain(u.into_iter()).cycle()
}

fn main() {
    let v1 = vec![1, 2, 3];
    let v2 = vec![4, 5];
    let mut v3 = combine_vecs(v1, v2);
    assert_eq!(Some(1), v3.next());
    assert_eq!(Some(2), v3.next());
    assert_eq!(Some(3), v3.next());
    assert_eq!(Some(4), v3.next());
    assert_eq!(Some(5), v3.next());
    println!("all done");
}
```

More importantly, some Rust types can't be written out. For example, every
closure has its own unnamed concrete type. Before `impl Trait` syntax, you had
to allocate on the heap in order to return a closure. But now you can do it all
statically, like this:

```rust,editable
// Returns a function that adds `y` to its input
fn make_adder_function(y: i32) -> impl Fn(i32) -> i32 {
    let closure = move |x: i32| { x + y };
    closure
}

fn main() {
    let plus_one = make_adder_function(1);
    assert_eq!(plus_one(2), 3);
}
```

You can also use `impl Trait` to return an iterator that uses `map` or `filter`
closures! This makes using `map` and `filter` easier. Because closure types don't
have names, you can't write out an explicit return type if your function returns
iterators with closures. But with `impl Trait` you can do this easily:

```rust,editable
fn double_positives<'a>(numbers: &'a Vec<i32>) -> impl Iterator<Item = i32> + 'a {
    numbers
        .iter()
        .filter(|x| x > &&0)
        .map(|x| x * 2)
}
```


 * Write a program that prints the entire ASCII table of characters on the console (characters from 0 to 255).

 * Note: Some characters have a special purpose and will not be displayed as expected. You may skip them or display them differently.


import os
import sys
from common import call_pandoc, regex_process, listfiles

def convert_to_wordpress(filename):
    """Convert from Markdown to Wordpress."""
    basename, ext = os.path.splitext(filename)
    print("Converting '{0:s}' to Wordpress HTML...".format(filename))
    filename1 = basename + ".wordpress_tmp.txt"
    call_pandoc(filename, filename1, "markdown", "html", ["--gladtex"])
    # postprocessing for wordpress and MathJax-Latex
    patterns = [("<EQ ENV=\"math\">(.*?)</EQ>", "[latex]\\1[/latex]"),
                ("<EQ ENV=\"displaymath\">(.*?)</EQ>", "
[latex]\\1[/latex]
"),
                ("
","
"),
                ("
","
"),
                (""", "\""),
                ("'", "'"),
                ("
(.*?)
\n", ""),
                ]
    filename2 = basename + ".wordpress.txt"
    regex_process(filename1, filename2, patterns)
    os.remove(filename1)
    print("Done!")

def main():
    for file in listfiles():
        convert_to_wordpress(file)

if __name__ == '__main__':
    main()


"""
Dijkstra's algorithm implementation
https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
"""

from __future__ import print_function

def dijkstra(edges, source, target):
    """ Dijkstra's algorithm

    Parameters
    ----------
    edges : list of tuples
            list of dependencies between nodes in the graph
            [(source, target, weight), ...]
    source : str
            name of source node of the graph
    target : str
            name of target node of the graph

    Returns
    -------
    out : tuple
          length and path between source and target node
          tuple (length, path)

    """
    # determine the graph as dict where:
    # key - source node and
    # item - list of [(destination, weight), ...]
    graph = {}
    for src, dest, weight in edges:
        if src not in graph:
            graph[src] = []
        graph[src].append((dest, weight))
    # set a queue
    # weight, source node, path from source node
    queue = [(0, source, [source])]
    # initialize visited nodes
    # visited nodes - nodes with a known shortest path
    # key - visited node
    # item - (cost, path)
    visited = dict()

    while queue:
        # get item from queue
        cost, src, path = queue.pop()
        if src not in visited:
            # this is a new visited node
            visited[src] = (cost, path)

        if src == target:
            # target node was reached
            return cost, path

        # set initial weight, destination node and path
        min_weight, min_dest, min_path = float('Inf'), None, None
        # loop for all visited nodes
        for src_, (cost_, path_) in visited.items():
            # find a shortest path
            # from src node to the nearest node
            for dest, weight in graph.get(src_, ()):
                if dest in visited:
                    # skip nodes with a known shortest path
                    continue
                # calculate current cost
                current_cost = cost_ + weight
                if current_cost < min_weight:
                    min_weight, min_dest, min_path = current_cost, dest, path_

        if min_dest is not None:
            # we find shortest path to new node
            # shortest path was updated
            path = min_path + [min_dest]
            # add to the queue
            queue.insert(0, (min_weight, min_dest, path))

    # Return infinity if the target node is not reached
    return float('Inf'), []

if __name__ == "__main__":
    EDGES = [
        ('s', 't', 6),
        ('s', 'y', 7),
        ('t', 'x', 5),
        ('x', 't', -2),
        ('t', 'y', 8),
        ('y', 'z', 9),
        ('y', 'x', -3),
        ('t', 'z', -4),
        ('z', 's', 2),
        ('z', 'x', 7)
    ]
    for SRC, DEST, MINLEN in [
        ('s', 'z', 2), ('a', 'z', float('Inf')), ('s', 's', 0)]:

        LEN, PATH = dijkstra(EDGES, SRC, DEST)
        print("Length of the path from '{}' to '{}' = {}, ({})"
            .format(SRC, DEST, LEN, LEN == MINLEN))
        print("Path from '{}' to '{}': {}".format(SRC, DEST, PATH))


.class final Lo/BZ;
.super Lo/BY;
.source ""

# annotations
.annotation system Ldalvik/annotation/InnerClass;
    accessFlags = 0x8
    name = null
.end annotation

.annotation system Ldalvik/annotation/Signature;
    value = {
        "Lo/BY<TK;TV;>;"
    }
.end annotation

# instance fields
.field final synthetic ˊ:Lo/BY;

.field final synthetic ˋ:Ljava/util/concurrent/Executor;

# direct methods
.method constructor <init>(Lo/BY;Ljava/util/concurrent/Executor;)V
    .locals 0

    .line 186
    iput-object p1, p0, Lo/BZ;->ˊ:Lo/BY;

    iput-object p2, p0, Lo/BZ;->ˋ:Ljava/util/concurrent/Executor;

    invoke-direct {p0}, Lo/BY;-><init>()V

    return-void
.end method

# virtual methods
.method public ˊ(Ljava/lang/Object;)Ljava/lang/Object;
    .locals 1
    .annotation system Ldalvik/annotation/Signature;
        value = {
            "(TK;)TV;"
        }
    .end annotation

    .line 189
    iget-object v0, p0, Lo/BZ;->ˊ:Lo/BY;

    invoke-virtual {v0, p1}, Lo/BY;->ˊ(Ljava/lang/Object;)Ljava/lang/Object;

    move-result-object v0

    return-object v0
.end method

.method public ˊ(Ljava/lang/Iterable;)Ljava/util/Map;
    .locals 1
    .annotation system Ldalvik/annotation/Signature;
        value = {
            "(Ljava/lang/Iterable<+TK;>;)Ljava/util/Map<TK;TV;>;"
        }
    .end annotation

    .line 206
    iget-object v0, p0, Lo/BZ;->ˊ:Lo/BY;

    invoke-virtual {v0, p1}, Lo/BY;->ˊ(Ljava/lang/Iterable;)Ljava/util/Map;

    move-result-object v0

    return-object v0
.end method

.method public ˊ(Ljava/lang/Object;Ljava/lang/Object;)Lo/Rc;
    .locals 2
    .annotation system Ldalvik/annotation/Signature;
        value = {
            "(TK;TV;)Lo/Rc<TV;>;"
        }
    .end annotation

    .line 194
    new-instance v0, Lo/Ca;

    invoke-direct {v0, p0, p1, p2}, Lo/Ca;-><init>(Lo/BZ;Ljava/lang/Object;Ljava/lang/Object;)V

    invoke-static {v0}, Lo/Rd;->ˊ(Ljava/util/concurrent/Callable;)Lo/Rd;

    move-result-object v1

    .line 200
    iget-object v0, p0, Lo/BZ;->ˋ:Ljava/util/concurrent/Executor;

    invoke-interface {v0, v1}, Ljava/util/concurrent/Executor;->execute(Ljava/lang/Runnable;)V

    .line 201
    return-object v1
.end method


var util    = require('util');
var plugins = require('../../services/plugins');
var Parent  = plugins.require('models/fields/select');

function Checkboxlist() {
    Parent.call(this);

    this.on('getting.options', function(event) {
        if (typeof event.value === 'function') {
            event.value = event.value.call(this);
        }
    });
}

util.inherits(Checkboxlist, Parent);

module.exports = Checkboxlist;