text:
<?php

if ( ! function_exists( 'ttfmake_create_config_select' ) ) :
/**
 * Generate a select input for the configuration overlay.
 *
 * @since  1.4.0.
 *
 * @param  string    $section_name    The section prefix for the input name.
 * @param  array     $args            Arguments for creating the input.
 * @param  array     $section_data    The data for the current section.
 * @return string                     The full input string.
 */
function ttfmake_create_config_select( $section_name, $args, $section_data ) {
	$return        = '';
	$current_value = ttfmake_get_current_configuration_value( $section_data, $args );

	if ( isset( $args['default'] ) && isset( $args['options'] ) ) {
		$id     = $section_name . '[' . $args['name'] . ']';
		$label  = ( isset( $args['label'] ) ) ? '' . esc_html( $args['label'] ) . '' : '';
		$class  = ( isset( $args['class'] ) ) ? ' class="' . esc_attr( $args['class'] ) . '"' : '';
		$description = ( isset( $args['description'] ) ) ? '
' . esc_html( $args['description'] ) . '
': '';
		$select = '<select id="' . $id . '"' . $class .' name="' . $id . '">%s</select>';

		$options = '';

		foreach ( $args['options'] as $key => $value ) {
			$options .= '<option value="' . esc_attr( $key ) . '"' . selected( $key, $current_value, false ) . '>' . esc_html( $value ) . '</option>';
		}

		$return = $label . sprintf( $select, $options ) . $description;
	}

	return $return;
}
endif;

if ( ! function_exists( 'ttfmake_create_config_checkbox' ) ) :
/**
 * Generate a checkbox input for the configuration overlay.
 *
 * @since  1.4.0.
 *
 * @param  string    $section_name    The section prefix for the input name.
 * @param  array     $args            Arguments for creating the input.
 * @param  array     $section_data    The data for the current section.
 * @return string                     The full input string.
 */
function ttfmake_create_config_checkbox( $section_name, $args, $section_data ) {
	$current_value = ttfmake_get_current_configuration_value( $section_data, $args );
	$id          = $section_name . '[' . esc_attr( $args['name'] ) . ']';
	$label       = ( isset( $args['label'] ) ) ? '' . esc_html( $args['label'] ) . '' : '';
	$description = ( isset( $args['description'] ) ) ? '
' . esc_html( $args['description'] ) . '
': '';
	$args        = '<input id="' . $id . '" type="checkbox" name="' . $id . '" value="1"' . checked( 1, $current_value, false ) . '>' . $description;

	return  $label . $args;
}
endif;

if ( ! function_exists( 'ttfmake_create_config_text' ) ) :
/**
 * Generate a text input for the configuration overlay.
 *
 * @since  1.4.0.
 *
 * @param  string    $section_name    The section prefix for the input name.
 * @param  array     $args            Arguments for creating the input.
 * @param  array     $section_data    The data for the current section.
 * @return string                     The full input string.
 */
function ttfmake_create_config_text( $section_name, $args, $section_data ) {
	$current_value = ttfmake_get_current_configuration_value( $section_data, $args );
	$id          = $section_name . '[' . esc_attr( $args['name'] ) . ']';
	$label       = ( isset( $args['label'] ) ) ? '' . esc_html( $args['label'] ) . '' : '';

	return  $label . '<input type="text" id="' . $id . '" name="' . $id . '" value="' . esc_attr( $current_value ) . '" />';
}
endif;

if ( ! function_exists( 'ttfmake_create_config_image' ) ) :
/**
 * Generate a image uploader input for the configuration overlay.
 *
 * @since  1.4.0.
 *
 * @param  string    $section_name    The section prefix for the input name.
 * @param  array     $args            Arguments for creating the input.
 * @param  array     $section_data    The data for the current section.
 * @return string                     The full input string.
 */
function ttfmake_create_config_image( $section_name, $args, $section_data ) {
	$current_value = ttfmake_get_current_configuration_value( $section_data, $args );
	$name        = $section_name . '[' . esc_attr( $args['name'] ) . ']';
	$label       = ( isset( $args['label'] ) ) ? '' . esc_html( $args['label'] ) . '' : '';

	return $label . ttfmake_get_builder_base()->add_uploader( $name, $current_value, __( 'Set image', 'make' ) );
}
endif;

if ( ! function_exists( 'ttfmake_create_config_color' ) ) :
/**
 * Generate a color picker input for the configuration overlay.
 *
 * @since  1.4.0.
 *
 * @param  string    $section_name    The section prefix for the input name.
 * @param  array     $args            Arguments for creating the input.
 * @param  array     $section_data    The data for the current section.
 * @return string                     The full input string.
 */
function ttfmake_create_config_color( $section_name, $args, $section_data ) {
	$current_value = ttfmake_get_current_configuration_value( $section_data, $args );
	$name        = $section_name . '[' . esc_attr( $args['name'] ) . ']';
	$label       = ( isset( $args['label'] ) ) ? '' . esc_html( $args['label'] ) . '' : '';
	$class       = ( isset( $args['class'] ) ) ? ' class="' . esc_attr( $args['class'] ) . '"' : '';

	return  $label . '<input id="' . $name . '" type="text" name="' . $name . '" ' . $class . ' value="' . esc_attr( $current_value ) . '" />';
}
endif;

if ( ! function_exists( 'ttfmake_create_config_section_title' ) ) :
/**
 * Generate a section title input for the configuration overlay.
 *
 * @since  1.4.0.
 *
 * @param  string    $section_name    The section prefix for the input name.
 * @param  array     $args            Arguments for creating the input.
 * @param  array     $section_data    The data for the current section.
 * @return string                     The full input string.
 */
function ttfmake_create_config_section_title( $section_name, $args, $section_data ) {
	$current_value = ttfmake_get_current_configuration_value( $section_data, $args );
	$placeholder = ( isset( $args['label'] ) ) ? ' placeholder="' . esc_attr( $args['label'] ) . '"' : '';
	$name        = 'name="' . $section_name . '[' . esc_attr( $args['name'] ) . ']"';
	$class       = ( isset( $args['class'] ) ) ? ' ' . esc_attr( $args['class'] ) : '';

	return  '<input' . $placeholder . ' type="text" ' . $name . ' value="' . esc_attr( $current_value ) . '" class="ttfmake-title' . $class . '" autocomplete="off">';
}
endif;

if ( ! function_exists( 'ttfmake_get_current_configuration_value' ) ) :
/**
 * Get the current or default value for an input.
 *
 * @since  1.4.0.
 *
 * @param  array     $section_data    The data for the current section.
 * @param  array     $args            Arguments for creating the input.
 * @return string                     The current value for the input.
 */
function ttfmake_get_current_configuration_value( $section_data, $args ) {
	$default_value = ( isset( $args['default'] ) ) ? $args['default'] : '';
	$current_value = ( isset( $section_data[ $args['name'] ] ) ) ? $section_data[ $args['name'] ] : $default_value;
	return $current_value;
}
endif;

if ( ! function_exists( 'ttfmake_create_input' ) ) :
/**
 * Create an input with header and footer wrapper.
 *
 * @since  1.4.0.
 *
 * @param  string    $section_name    The section prefix for the input name.
 * @param  array     $args            Arguments for creating the input.
 * @param  array     $section_data    The data for the current section.
 * @return string                     The full input string.
 */
function ttfmake_create_input( $section_name, $args, $section_data ) {
	$final_output = '';

	if ( isset( $args['type'] ) ) {
		// Get the input HTML
		$function_name = 'ttfmake_create_config_' . $args['type'];

		if ( is_callable( $function_name ) ) {
			$input_html = call_user_func( $function_name, $section_name, $args, $section_data );

			/**
			 * Filter the wrapped used for the inputs.
			 *
			 * @since 1.4.0.
			 *
			 * @param string    $wrapper         The HTML to wrap around the input.
			 * @param string    $args            The input data that is wrapped.
			 * @param string    $section_data    The data for the section.
			 */
			$wrap = apply_filters( 'make_configuration_overlay_input_wrap', '
%2$s
', $args, $section_data );

			/**
			 * Filter the HTML for the input.
			 *
			 * @since 1.4.0.
			 *
			 * @param string    $this_output     The HTML for the input.
			 * @param string    $args            The input data.
			 * @param string    $section_data    The data for the section.
			 */
			$input_html = apply_filters( 'make_configuration_overlay_input', $input_html, $args, $section_data );

			if ( ! empty( $input_html ) ) {
				// Add "-wrap" to each class
				$class        = ( isset( $args['class'] ) ) ? esc_attr( str_replace( ' ', '-wrap ', $args['class'] ) . '-wrap' ) : '';
				$final_output = sprintf( $wrap, $class, $input_html );
			}
		}
	}

	return $final_output;
}
endif;


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

#include "chrome/browser/ash/login/easy_unlock/easy_unlock_service.h"

#include <stddef.h>

#include <map>
#include <memory>
#include <string>
#include <utility>

#include "ash/constants/ash_features.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller.h"
#include "chrome/browser/ash/login/easy_unlock/easy_unlock_service_factory.h"
#include "chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h"
#include "chrome/browser/ash/login/easy_unlock/smartlock_feature_usage_metrics.h"
#include "chrome/browser/ash/login/session/chrome_session_manager.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/login/users/mock_user_manager.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/dbus/dbus_thread_manager.h"
#include "chromeos/ash/components/multidevice/beacon_seed.h"
#include "chromeos/ash/components/multidevice/remote_device_test_util.h"
#include "chromeos/ash/components/proximity_auth/fake_lock_handler.h"
#include "chromeos/ash/components/proximity_auth/screenlock_bridge.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_api.pb.h"
#include "chromeos/ash/services/device_sync/public/cpp/fake_device_sync_client.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
#include "chromeos/ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "components/account_id/account_id.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/user_manager/scoped_user_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/display/display.h"
#include "ui/display/test/test_screen.h"
#include "ui/display/util/display_util.h"
#include "ui/views/test/test_views_delegate.h"

namespace ash {
namespace {

using ::device::MockBluetoothAdapter;
using ::testing::Return;

struct SmartLockStateTestCase {
  SmartLockState smart_lock_state;
  EasyUnlockAuthEvent easy_unlock_auth_event;
  SmartLockMetricsRecorder::SmartLockAuthEventPasswordState
      smart_lock_auth_event_password_state;
  bool should_be_valid_on_remote_auth_failure;
};

constexpr SmartLockStateTestCase kSmartLockStateTestCases[] = {
    {SmartLockState::kInactive,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_SERVICE_NOT_ACTIVE,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
         kServiceNotActive,
     true},
    {SmartLockState::kDisabled,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_SERVICE_NOT_ACTIVE,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
         kServiceNotActive,
     true},
    {SmartLockState::kBluetoothDisabled,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_NO_BLUETOOTH,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::kNoBluetooth,
     true},
    {SmartLockState::kConnectingToPhone,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_BLUETOOTH_CONNECTING,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
         kBluetoothConnecting,
     false},
    {SmartLockState::kPhoneNotFound,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_NO_PHONE,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
         kCouldNotConnectToPhone,
     false},
    {SmartLockState::kPhoneNotAuthenticated,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_PHONE_NOT_AUTHENTICATED,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
         kNotAuthenticated,
     false},
    {SmartLockState::kPhoneFoundLockedAndProximate,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_PHONE_LOCKED,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::kPhoneLocked,
     true},
    {SmartLockState::kPhoneFoundUnlockedAndDistant,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_RSSI_TOO_LOW,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::kRssiTooLow,
     false},
    {SmartLockState::kPhoneFoundLockedAndDistant,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_PHONE_LOCKED_AND_RSSI_TOO_LOW,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
         kPhoneLockedAndRssiTooLow,
     false},
    {SmartLockState::kPhoneAuthenticated,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_WITH_AUTHENTICATED_PHONE,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
         kAuthenticatedPhone,
     false},
    {SmartLockState::kPasswordReentryRequired,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_FORCED_REAUTH,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::kForcedReauth,
     false},
    {SmartLockState::kPhoneNotLockable,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_PHONE_NOT_LOCKABLE,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
         kPhoneNotLockable,
     false},
    {SmartLockState::kPrimaryUserAbsent,
     EasyUnlockAuthEvent::PASSWORD_ENTRY_PRIMARY_USER_ABSENT,
     SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
         kPrimaryUserAbsent,
     false}};

class MockEasyUnlockNotificationController
    : public EasyUnlockNotificationController {
 public:
  MockEasyUnlockNotificationController()
      : EasyUnlockNotificationController(nullptr) {}

  MockEasyUnlockNotificationController(
      const MockEasyUnlockNotificationController&) = delete;
  MockEasyUnlockNotificationController& operator=(
      const MockEasyUnlockNotificationController&) = delete;

  ~MockEasyUnlockNotificationController() override {}

  // EasyUnlockNotificationController:
  MOCK_METHOD0(ShowChromebookAddedNotification, void());
  MOCK_METHOD0(ShowPairingChangeNotification, void());
  MOCK_METHOD1(ShowPairingChangeAppliedNotification, void(const std::string&));
};

// Define a stub MultiDeviceSetupDialog because the base class has a protected
// constructor and destructor (which prevents usage of smart pointers).
class FakeMultiDeviceSetupDialog
    : public multidevice_setup::MultiDeviceSetupDialog {
 public:
  FakeMultiDeviceSetupDialog() = default;
};

}  // namespace

class EasyUnlockServiceRegularTest : public testing::Test {
 public:
  EasyUnlockServiceRegularTest(const EasyUnlockServiceRegularTest&) = delete;
  EasyUnlockServiceRegularTest& operator=(const EasyUnlockServiceRegularTest&) =
      delete;

 protected:
  EasyUnlockServiceRegularTest()
      : test_local_device_(
            multidevice::RemoteDeviceRefBuilder()
                .SetPublicKey("local device")
                .SetSoftwareFeatureState(
                    multidevice::SoftwareFeature::kSmartLockClient,
                    multidevice::SoftwareFeatureState::kEnabled)
                .SetBeaconSeeds(multidevice::FromCryptAuthSeedList(
                    std::vector<cryptauth::BeaconSeed>(4)))
                .Build()),
        test_remote_device_smart_lock_host_(
            multidevice::RemoteDeviceRefBuilder()
                .SetPublicKey("local device")
                .SetSoftwareFeatureState(
                    multidevice::SoftwareFeature::kSmartLockHost,
                    multidevice::SoftwareFeatureState::kEnabled)
                .SetBeaconSeeds(multidevice::FromCryptAuthSeedList(
                    std::vector<cryptauth::BeaconSeed>(4)))
                .Build()) {}
  ~EasyUnlockServiceRegularTest() override = default;

  void SetUp() override {
    display::Screen::SetScreenInstance(&test_screen_);
    display::SetInternalDisplayIds({test_screen_.GetPrimaryDisplay().id()});

    chromeos::PowerManagerClient::InitializeFake();

    // Note: this is necessary because objects owned by EasyUnlockService
    // depend on the BluetoothAdapter -- fetching the real one causes tests
    // to fail.
    mock_adapter_ = new testing::NiceMock<MockBluetoothAdapter>();
    device::BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);

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

    auto test_other_remote_device =
        multidevice::RemoteDeviceRefBuilder()
            .SetPublicKey("potential, but disabled, host device")
            .SetSoftwareFeatureState(
                multidevice::SoftwareFeature::kSmartLockHost,
                multidevice::SoftwareFeatureState::kSupported)
            .Build();
    test_remote_devices_.push_back(test_remote_device_smart_lock_host_);
    test_remote_devices_.push_back(test_other_remote_device);

    fake_secure_channel_client_ =
        std::make_unique<secure_channel::FakeSecureChannelClient>();
    fake_device_sync_client_ =
        std::make_unique<device_sync::FakeDeviceSyncClient>();
    fake_multidevice_setup_client_ =
        std::make_unique<multidevice_setup::FakeMultiDeviceSetupClient>();

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

    account_id_ = AccountId::FromUserEmail(profile_->GetProfileUserName());

    auto fake_chrome_user_manager = std::make_unique<FakeChromeUserManager>();
    fake_chrome_user_manager_ = fake_chrome_user_manager.get();
    scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
        std::move(fake_chrome_user_manager));
    SetPrimaryUserLoggedIn();

    fake_lock_handler_ = std::make_unique<proximity_auth::FakeLockHandler>();
    SetScreenLockState(false /* is_locked */);
  }

  void TearDown() override {
    SetScreenLockState(false /* is_locked */);
    easy_unlock_service_regular_->Shutdown();
    chromeos::PowerManagerClient::Shutdown();
    TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
    display::Screen::SetScreenInstance(nullptr);
  }

  // Most tests will want to pass `should_initialize_all_dependencies` == true,
  // but may pass false if they wish to tweak the dependencies' state themselves
  // before initializing the service.
  void InitializeService(bool should_initialize_all_dependencies) {
    if (should_initialize_all_dependencies) {
      fake_device_sync_client_->NotifyReady();
      SetLocalDevice(test_local_device_);
      SetSyncedDevices(test_remote_devices_);
      SetIsEnabled(true /* is_enabled */);
    }

    auto mock_notification_controller = std::make_unique<
        testing::StrictMock<MockEasyUnlockNotificationController>>();
    mock_notification_controller_ = mock_notification_controller.get();

    easy_unlock_service_regular_ = std::make_unique<EasyUnlockServiceRegular>(
        profile_.get(), fake_secure_channel_client_.get(),
        std::move(mock_notification_controller), fake_device_sync_client_.get(),
        fake_multidevice_setup_client_.get());
    easy_unlock_service_regular_->Initialize();
  }

  void SetLocalDevice(
      const absl::optional<multidevice::RemoteDeviceRef>& local_device) {
    fake_device_sync_client_->set_local_device_metadata(test_local_device_);
    fake_device_sync_client_->NotifyEnrollmentFinished();
  }

  void SetSyncedDevices(multidevice::RemoteDeviceRefList synced_devices) {
    fake_device_sync_client_->set_synced_devices(synced_devices);
    fake_device_sync_client_->NotifyNewDevicesSynced();
  }

  void SetIsEnabled(bool is_enabled) {
    fake_multidevice_setup_client_->SetFeatureState(
        multidevice_setup::mojom::Feature::kSmartLock,
        is_enabled ? multidevice_setup::mojom::FeatureState::kEnabledByUser
                   : multidevice_setup::mojom::FeatureState::kDisabledByUser);
  }

  void SetEasyUnlockAllowedPolicy(bool allowed) {
    profile_->GetTestingPrefService()->SetManagedPref(
        prefs::kEasyUnlockAllowed, std::make_unique<base::Value>(allowed));
  }

  void SetScreenLockState(bool is_locked) {
    if (is_locked)
      proximity_auth::ScreenlockBridge::Get()->SetFocusedUser(account_id_);

    fake_lock_handler_->ClearSmartLockState();
    fake_lock_handler_->ClearSmartLockAuthResult();

    proximity_auth::ScreenlockBridge::Get()->SetLockHandler(
        is_locked ? fake_lock_handler_.get() : nullptr);
  }

  void VerifyGetRemoteDevices(bool are_local_and_remote_devices_expected) {
    const base::Value::List* remote_devices =
        static_cast<EasyUnlockService*>(easy_unlock_service_regular_.get())
            ->GetRemoteDevices();
    if (are_local_and_remote_devices_expected)
      // 2 devices are expected: the local device and the remote device.
      EXPECT_EQ(2u, remote_devices->size());
    else
      EXPECT_FALSE(remote_devices);
  }

  void SetDisplaySize(const gfx::Size& size) {
    display::Display display = test_screen_.GetPrimaryDisplay();
    display.SetSize(size);
    test_screen_.display_list().RemoveDisplay(display.id());
    test_screen_.display_list().AddDisplay(display,
                                           display::DisplayList::Type::PRIMARY);
  }

  EasyUnlockAuthEvent GetPasswordAuthEvent() {
    return easy_unlock_service_regular_->GetPasswordAuthEvent();
  }

  SmartLockMetricsRecorder::SmartLockAuthEventPasswordState
  GetSmartUnlockPasswordAuthEvent() {
    return easy_unlock_service_regular_->GetSmartUnlockPasswordAuthEvent();
  }

  void ResetSmartLockState() {
    easy_unlock_service_regular_->ResetSmartLockState();
  }

  // Must outlive TestingProfiles.
  content::BrowserTaskEnvironment task_environment_;

  // PrefService which contains the browser process' local storage. It should be
  // destructed after TestingProfile.
  TestingPrefServiceSimple local_pref_service_;

  std::unique_ptr<TestingProfile> profile_;
  AccountId account_id_;
  FakeChromeUserManager* fake_chrome_user_manager_;
  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;

  const multidevice::RemoteDeviceRef test_local_device_;
  const multidevice::RemoteDeviceRef test_remote_device_smart_lock_host_;
  multidevice::RemoteDeviceRefList test_remote_devices_;

  std::unique_ptr<secure_channel::FakeSecureChannelClient>
      fake_secure_channel_client_;
  std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
  std::unique_ptr<multidevice_setup::FakeMultiDeviceSetupClient>
      fake_multidevice_setup_client_;
  std::unique_ptr<proximity_auth::FakeLockHandler> fake_lock_handler_;
  std::unique_ptr<EasyUnlockServiceRegular> easy_unlock_service_regular_;

  std::string profile_gaia_id_;

  scoped_refptr<testing::NiceMock<MockBluetoothAdapter>> mock_adapter_;

  testing::StrictMock<MockEasyUnlockNotificationController>*
      mock_notification_controller_;

  views::TestViewsDelegate view_delegate_;
  base::HistogramTester histogram_tester_;

  display::test::TestScreen test_screen_;

 private:
  void SetPrimaryUserLoggedIn() {
    const user_manager::User* user =
        fake_chrome_user_manager_->AddPublicAccountUser(account_id_);
    fake_chrome_user_manager_->UserLoggedIn(account_id_, user->username_hash(),
                                            false /* browser_restart */,
                                            false /* is_child */);
  }
};

TEST_F(EasyUnlockServiceRegularTest, NotAllowedWhenProhibited) {
  InitializeService(true /* should_initialize_all_dependencies */);
  fake_multidevice_setup_client_->SetFeatureState(
      multidevice_setup::mojom::Feature::kSmartLock,
      multidevice_setup::mojom::FeatureState::kProhibitedByPolicy);

  EXPECT_FALSE(easy_unlock_service_regular_->IsAllowed());
}

TEST_F(EasyUnlockServiceRegularTest, NotAllowedForEphemeralAccounts) {
  InitializeService(true /* should_initialize_all_dependencies */);

  // Only MockUserManager allows for stubbing
  // IsCurrentUserNonCryptohomeDataEphemeral() to return false so we use one
  // here in place of `fake_chrome_user_manager_`. Injecting it into a local
  // ScopedUserManager sets it up as the global UserManager instance.
  auto mock_user_manager =
      std::make_unique<testing::NiceMock<MockUserManager>>();
  ON_CALL(*mock_user_manager, IsCurrentUserNonCryptohomeDataEphemeral())
      .WillByDefault(Return(false));
  auto scoped_user_manager = std::make_unique<user_manager::ScopedUserManager>(
      std::move(mock_user_manager));

  EXPECT_FALSE(easy_unlock_service_regular_->IsAllowed());
}

TEST_F(EasyUnlockServiceRegularTest, GetProximityAuthPrefManager) {
  InitializeService(true /* should_initialize_all_dependencies */);

  EXPECT_TRUE(
      static_cast<EasyUnlockService*>(easy_unlock_service_regular_.get())
          ->GetProximityAuthPrefManager());
}

TEST_F(EasyUnlockServiceRegularTest, GetRemoteDevices) {
  InitializeService(true /* should_initialize_all_dependencies */);
  VerifyGetRemoteDevices(true /* are_local_and_remote_devices_expected */);
}

TEST_F(EasyUnlockServiceRegularTest, GetRemoteDevices_InitiallyNotReady) {
  SetIsEnabled(true /* is_enabled */);
  InitializeService(false /* should_initialize_all_dependencies */);
  VerifyGetRemoteDevices(false /* are_local_and_remote_devices_expected */);

  EXPECT_CALL(*mock_notification_controller_,
              ShowChromebookAddedNotification());

  fake_device_sync_client_->NotifyReady();
  SetLocalDevice(test_local_device_);
  SetSyncedDevices(test_remote_devices_);
  VerifyGetRemoteDevices(true /* are_local_and_remote_devices_expected */);
}

TEST_F(EasyUnlockServiceRegularTest,
       GetRemoteDevices_InitiallyNoSyncedDevices) {
  fake_device_sync_client_->NotifyReady();
  SetLocalDevice(test_local_device_);
  SetSyncedDevices(multidevice::RemoteDeviceRefList() /* synced_devices */);
  SetIsEnabled(true /* is_enabled */);
  InitializeService(false /* should_initialize_all_dependencies */);
  VerifyGetRemoteDevices(false /* are_local_and_remote_devices_expected */);

  EXPECT_CALL(*mock_notification_controller_,
              ShowChromebookAddedNotification());

  SetSyncedDevices(test_remote_devices_);
  VerifyGetRemoteDevices(true /* are_local_and_remote_devices_expected */);
}

// Test that the "Chromebook added" notification does not show while the
// MultiDeviceSetupDialog is active, and only shows once it is closed.
TEST_F(
    EasyUnlockServiceRegularTest,
    GetRemoteDevices_InitiallyNoSyncedDevices_MultiDeviceSetupDialogVisible) {
  SetDisplaySize(gfx::Size(1920, 1200));

  ChromeSessionManager manager;

  auto dialog = std::make_unique<FakeMultiDeviceSetupDialog>();
  multidevice_setup::MultiDeviceSetupDialog::SetInstanceForTesting(
      dialog.get());

  fake_device_sync_client_->NotifyReady();
  SetLocalDevice(test_local_device_);
  SetSyncedDevices(multidevice::RemoteDeviceRefList() /* synced_devices */);
  SetIsEnabled(true /* is_enabled */);
  InitializeService(false /* should_initialize_all_dependencies */);
  VerifyGetRemoteDevices(false /* are_local_and_remote_devices_expected */);

  // Calling SetSyncedDevices() below would usually cause the "Chromebook added"
  // notification to appear, but it shouldn't because MultiDeviceSetupDialog is
  // active.
  EXPECT_CALL(*mock_notification_controller_, ShowChromebookAddedNotification())
      .Times(0);
  SetSyncedDevices(test_remote_devices_);
  VerifyGetRemoteDevices(true /* are_local_and_remote_devices_expected */);

  // Now expect the "Chromebook added" notification to appear, and close the
  // dialog by deleting it (this indirectly calls the dialog close callbacks).
  // Using the real dialog Close() method invokes Ash and Widget code that is
  // too cumbersome to stub out.
  testing::Mock::VerifyAndClearExpectations(mock_notification_controller_);
  EXPECT_CALL(*mock_notification_controller_,
              ShowChromebookAddedNotification());
  dialog.reset();
  multidevice_setup::MultiDeviceSetupDialog::SetInstanceForTesting(nullptr);
}

TEST_F(EasyUnlockServiceRegularTest, GetRemoteDevices_InitiallyNotEnabled) {
  fake_device_sync_client_->NotifyReady();
  SetLocalDevice(test_local_device_);
  SetSyncedDevices(test_remote_devices_);
  SetIsEnabled(false /* is_enabled */);
  InitializeService(false /* should_initialize_all_dependencies */);
  VerifyGetRemoteDevices(false /* are_local_and_remote_devices_expected */);

  SetIsEnabled(true /* is_enabled */);
  VerifyGetRemoteDevices(true /* are_local_and_remote_devices_expected */);
}

TEST_F(EasyUnlockServiceRegularTest,
       GetRemoteDevices_DeferDeviceLoadUntilScreenIsUnlocked) {
  SetScreenLockState(true /* is_locked */);
  InitializeService(true /* should_initialize_all_dependencies */);
  VerifyGetRemoteDevices(false /* are_local_and_remote_devices_expected */);

  SetScreenLockState(false /* is_locked */);
  VerifyGetRemoteDevices(true /* are_local_and_remote_devices_expected */);
}

TEST_F(EasyUnlockServiceRegularTest, GetRemoteDevices_SmartLockHostChanged) {
  InitializeService(true /* should_initialize_all_dependencies */);
  SetScreenLockState(true /* is_locked */);
  VerifyGetRemoteDevices(true /* are_local_and_remote_devices_expected */);

  auto new_remote_device =
      multidevice::RemoteDeviceRefBuilder()
          .SetPublicKey("new smartlock host")
          .SetSoftwareFeatureState(multidevice::SoftwareFeature::kSmartLockHost,
                                   multidevice::SoftwareFeatureState::kEnabled)
          .Build();
  multidevice::RemoteDeviceRefList new_remote_devices;
  new_remote_devices.push_back(new_remote_device);

  EXPECT_CALL(*mock_notification_controller_, ShowPairingChangeNotification());
  SetSyncedDevices(new_remote_devices /* synced_devices */);
  VerifyGetRemoteDevices(true /* are_local_and_remote_devices_expected */);

  EXPECT_CALL(*mock_notification_controller_,
              ShowPairingChangeAppliedNotification(testing::_));
  SetScreenLockState(false /* is_locked */);
}

// Test through the core flow of unlocking the screen with Smart Lock.
// Unfortunately, the only observable side effect we have available is verifying
// that the success metric is emitted.
// The "SmartLock.AuthResult" Failure bucket is incorrectly emitted to during
// this test. See crbug.com/1255964 for more info.
TEST_F(EasyUnlockServiceRegularTest, AuthenticateWithEasyUnlock) {
  InitializeService(true /* should_initialize_all_dependencies */);
  SetScreenLockState(true /* is_locked */);

  auto* service =
      static_cast<EasyUnlockService*>(easy_unlock_service_regular_.get());

  // service->AttemptAuth() will fail if the SmartLockState is not
  // kPhoneAuthenticated.
  service->UpdateSmartLockState(SmartLockState::kPhoneAuthenticated);

  EXPECT_TRUE(service->AttemptAuth(account_id_));
  service->FinalizeUnlock(true);

  histogram_tester_.ExpectBucketCount("SmartLock.AuthResult.Unlock", 1, 0);
  histogram_tester_.ExpectBucketCount(
      "ChromeOS.FeatureUsage.SmartLock",
      feature_usage::FeatureUsageMetrics::Event::kUsedWithSuccess, 0);

  SetScreenLockState(false /* is_locked */);

  histogram_tester_.ExpectBucketCount("SmartLock.AuthResult.Unlock", 1, 1);
  histogram_tester_.ExpectBucketCount(
      "ChromeOS.FeatureUsage.SmartLock",
      feature_usage::FeatureUsageMetrics::Event::kUsedWithSuccess, 1);
}

// Regression test for crbug.com/974410.
// The "SmartLock.AuthResult" Failure bucket is incorrectly emitted to during
// this test. See crbug.com/1255964 for more info.
TEST_F(EasyUnlockServiceRegularTest, AuthenticateWithEasyUnlockMultipleTimes) {
  InitializeService(true /* should_initialize_all_dependencies */);
  SetScreenLockState(true /* is_locked */);

  auto* service =
      static_cast<EasyUnlockService*>(easy_unlock_service_regular_.get());

  // service->AttemptAuth() will fail if the SmartLockState is not
  // kPhoneAuthenticated.
  service->UpdateSmartLockState(SmartLockState::kPhoneAuthenticated);

  EXPECT_TRUE(service->AttemptAuth(account_id_));
  service->FinalizeUnlock(true);

  // The first auth attempt is still ongoing. A second auth attempt request
  // should be rejected.
  EXPECT_FALSE(service->AttemptAuth(account_id_));

  histogram_tester_.ExpectBucketCount("SmartLock.AuthResult.Unlock", 1, 0);
  histogram_tester_.ExpectBucketCount(
      "ChromeOS.FeatureUsage.SmartLock",
      feature_usage::FeatureUsageMetrics::Event::kUsedWithSuccess, 0);

  SetScreenLockState(false /* is_locked */);

  histogram_tester_.ExpectBucketCount("SmartLock.AuthResult.Unlock", 1, 1);
  histogram_tester_.ExpectBucketCount(
      "ChromeOS.FeatureUsage.SmartLock",
      feature_usage::FeatureUsageMetrics::Event::kUsedWithSuccess, 1);
}

// Regression test for b/213941298.
// Authenticate with Easy Unlock twice, calling UpdateSmartLockState in between.
// Ordinarily, this call to UpdateSmartLockState would be made by
// ProximityAuthClient during the OnScreenDidUnlock event. Check that the second
// call to AttemptAuth succeeds since UpdateSmartLockState clears out the last
// auth attempt, allowing the next auth attempt to proceed.
TEST_F(EasyUnlockServiceRegularTest,
       UpdateSmartLockStateClearsLastAuthAttempt) {
  InitializeService(true /* should_initialize_all_dependencies */);
  SetScreenLockState(true /* is_locked */);

  auto* service =
      static_cast<EasyUnlockService*>(easy_unlock_service_regular_.get());

  service->UpdateSmartLockState(SmartLockState::kPhoneAuthenticated);

  EXPECT_TRUE(service->AttemptAuth(account_id_));
  service->FinalizeUnlock(true);

  ASSERT_TRUE(fake_lock_handler_->smart_lock_auth_result().has_value());
  EXPECT_TRUE(fake_lock_handler_->smart_lock_auth_result().value());

  SetScreenLockState(false /* is_locked */);
  service->UpdateSmartLockState(SmartLockState::kInactive);

  EXPECT_FALSE(fake_lock_handler_->smart_lock_auth_result().has_value());
  EXPECT_FALSE(fake_lock_handler_->smart_lock_state().has_value());

  SetScreenLockState(true /* is_locked */);
  service->UpdateSmartLockState(SmartLockState::kPhoneAuthenticated);
  EXPECT_TRUE(service->AttemptAuth(account_id_));
  service->FinalizeUnlock(true);

  ASSERT_TRUE(fake_lock_handler_->smart_lock_auth_result().has_value());
  EXPECT_TRUE(fake_lock_handler_->smart_lock_auth_result().value());

  SetScreenLockState(false /* is_locked */);
}

TEST_F(EasyUnlockServiceRegularTest, GetInitialSmartLockState_FeatureEnabled) {
  InitializeService(true /* should_initialize_all_dependencies */);
  SetScreenLockState(true /* is_locked */);

  auto* service =
      static_cast<EasyUnlockService*>(easy_unlock_service_regular_.get());
  EXPECT_EQ(SmartLockState::kConnectingToPhone,
            service->GetInitialSmartLockState());
}

TEST_F(EasyUnlockServiceRegularTest, GetInitialSmartLockState_FeatureDisabled) {
  InitializeService(true /* should_initialize_all_dependencies */);
  SetIsEnabled(false);
  SetScreenLockState(true /* is_locked */);

  auto* service =
      static_cast<EasyUnlockService*>(easy_unlock_service_regular_.get());
  EXPECT_EQ(SmartLockState::kDisabled, service->GetInitialSmartLockState());
}

TEST_F(EasyUnlockServiceRegularTest, ShowInitialSmartLockState_FeatureEnabled) {
  InitializeService(true /* should_initialize_all_dependencies */);

  EXPECT_FALSE(fake_lock_handler_->smart_lock_state());

  SetScreenLockState(true /* is_locked */);

  // Before EasyUnlockService::UpdateSmartLockState() is called externally,
  // ensure that internal state is updated to an "initial" state to prevent
  // UI jank.
  EXPECT_EQ(SmartLockState::kConnectingToPhone,
            fake_lock_handler_->smart_lock_state().value());

  auto* service =
      static_cast<EasyUnlockService*>(easy_unlock_service_regular_.get());
  service->UpdateSmartLockState(SmartLockState::kConnectingToPhone);
  EXPECT_EQ(SmartLockState::kConnectingToPhone,
            fake_lock_handler_->smart_lock_state().value());
}

TEST_F(EasyUnlockServiceRegularTest,
       ShowInitialSmartLockState_FeatureDisabled) {
  InitializeService(true /* should_initialize_all_dependencies */);
  SetIsEnabled(false);

  EXPECT_FALSE(fake_lock_handler_->smart_lock_state());

  SetScreenLockState(true /* is_locked */);

  // Before EasyUnlockService::UpdateSmartLockState() is called externally,
  // ensure that internal state is updated to an "initial" state to prevent
  // UI jank.
  EXPECT_EQ(SmartLockState::kDisabled,
            fake_lock_handler_->smart_lock_state().value());
}

TEST_F(EasyUnlockServiceRegularTest, PrepareForSuspend) {
  InitializeService(/*should_initialize_all_dependencies=*/true);
  SetScreenLockState(/*is_locked=*/true);
  EasyUnlockService* service = easy_unlock_service_regular_.get();
  service->UpdateSmartLockState(SmartLockState::kPhoneAuthenticated);
  EXPECT_EQ(SmartLockState::kPhoneAuthenticated,
            fake_lock_handler_->smart_lock_state().value());
  chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
      power_manager::SuspendImminent::LID_CLOSED);
  EXPECT_EQ(SmartLockState::kConnectingToPhone,
            fake_lock_handler_->smart_lock_state().value());
}

TEST_F(EasyUnlockServiceRegularTest, HandleAuthFailureInUpdateSmartLockState) {
  InitializeService(/*should_initialize_all_dependencies=*/true);
  SetScreenLockState(/*is_locked=*/true);
  EasyUnlockService* service = easy_unlock_service_regular_.get();
  service->UpdateSmartLockState(SmartLockState::kPhoneAuthenticated);
  EXPECT_EQ(proximity_auth::mojom::AuthType::USER_CLICK,
            fake_lock_handler_->GetAuthType(account_id_));
  service->AttemptAuth(account_id_);
  service->FinalizeUnlock(true);
  EXPECT_TRUE(fake_lock_handler_->smart_lock_auth_result().has_value());
  EXPECT_TRUE(fake_lock_handler_->smart_lock_auth_result().value());
  service->UpdateSmartLockState(SmartLockState::kPhoneNotAuthenticated);
  EXPECT_EQ(proximity_auth::mojom::AuthType::OFFLINE_PASSWORD,
            fake_lock_handler_->GetAuthType(account_id_));
  EXPECT_TRUE(fake_lock_handler_->smart_lock_auth_result().has_value());
  EXPECT_FALSE(fake_lock_handler_->smart_lock_auth_result().value());
}

TEST_F(EasyUnlockServiceRegularTest, IsSmartLockStateValidOnRemoteAuthFailure) {
  InitializeService(/*should_initialize_all_dependencies=*/true);
  SetScreenLockState(/*is_locked=*/true);
  EasyUnlockService* service = easy_unlock_service_regular_.get();
  for (const SmartLockStateTestCase& testcase : kSmartLockStateTestCases) {
    fake_lock_handler_->ClearSmartLockState();
    fake_lock_handler_->ClearSmartLockAuthResult();
    service->UpdateSmartLockState(SmartLockState::kPhoneAuthenticated);
    service->AttemptAuth(account_id_);
    service->UpdateSmartLockState(testcase.smart_lock_state);
    if (testcase.smart_lock_state != SmartLockState::kPhoneAuthenticated) {
      EXPECT_EQ(proximity_auth::mojom::AuthType::OFFLINE_PASSWORD,
                fake_lock_handler_->GetAuthType(account_id_));
      if (testcase.should_be_valid_on_remote_auth_failure) {
        EXPECT_FALSE(fake_lock_handler_->smart_lock_auth_result().has_value());
      } else {
        EXPECT_TRUE(fake_lock_handler_->smart_lock_auth_result().has_value());
        EXPECT_FALSE(fake_lock_handler_->smart_lock_auth_result().value());
      }
    }
  }
}

TEST_F(EasyUnlockServiceRegularTest, FinalizeUnlock) {
  InitializeService(/*should_initialize_all_dependencies=*/true);
  SetScreenLockState(/*is_locked=*/true);
  EasyUnlockService* service = easy_unlock_service_regular_.get();
  service->FinalizeUnlock(true);
  EXPECT_EQ(0, fake_lock_handler_->unlock_called());
  service->UpdateSmartLockState(SmartLockState::kPhoneAuthenticated);
  service->AttemptAuth(account_id_);
  service->FinalizeUnlock(true);
  EXPECT_EQ(1, fake_lock_handler_->unlock_called());
  service->FinalizeUnlock(false);
  EXPECT_TRUE(fake_lock_handler_->smart_lock_auth_result().has_value());
  EXPECT_FALSE(fake_lock_handler_->smart_lock_auth_result().value());
}

TEST_F(EasyUnlockServiceRegularTest, GetPasswordAuthEvent) {
  InitializeService(/*should_initialize_all_dependencies=*/true);
  SetScreenLockState(/*is_locked=*/true);
  EasyUnlockService* service = easy_unlock_service_regular_.get();
  ResetSmartLockState();
  EXPECT_EQ(EasyUnlockAuthEvent::PASSWORD_ENTRY_NO_SMARTLOCK_STATE_HANDLER,
            GetPasswordAuthEvent());
  for (const SmartLockStateTestCase& testcase : kSmartLockStateTestCases) {
    fake_lock_handler_->ClearSmartLockState();
    fake_lock_handler_->ClearSmartLockAuthResult();
    service->UpdateSmartLockState(testcase.smart_lock_state);
    EXPECT_EQ(testcase.easy_unlock_auth_event, GetPasswordAuthEvent());
  }
}

TEST_F(EasyUnlockServiceRegularTest, GetSmartUnlockPasswordAuthEvent) {
  InitializeService(/*should_initialize_all_dependencies=*/true);
  SetScreenLockState(/*is_locked=*/true);
  EasyUnlockService* service = easy_unlock_service_regular_.get();
  ResetSmartLockState();
  EXPECT_EQ(
      SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::kUnknownState,
      GetSmartUnlockPasswordAuthEvent());
  for (const SmartLockStateTestCase& testcase : kSmartLockStateTestCases) {
    fake_lock_handler_->ClearSmartLockState();
    fake_lock_handler_->ClearSmartLockAuthResult();
    service->UpdateSmartLockState(testcase.smart_lock_state);
    EXPECT_EQ(testcase.smart_lock_auth_event_password_state,
              GetSmartUnlockPasswordAuthEvent());
  }
}

}  // namespace ash

redpajama_set_name: RedPajamaGithub


text:
using System;
using System.Linq;
using System.Reflection;
using System.Text;
using Xavalon.XamlStyler.Core.Extensions;
using Xavalon.XamlStyler.Core.Model;
using Xavalon.XamlStyler.Core.Services;

namespace Xavalon.XamlStyler.Core.MarkupExtensions.Formatter
{
    public class AttributeInfoFormatter
    {
        private readonly MarkupExtensionFormatter formatter;
        private readonly IndentService indentService;

        public AttributeInfoFormatter(MarkupExtensionFormatter formatter, IndentService indentService)
        {
            this.formatter = formatter;
            this.indentService = indentService;
        }

        /// <summary>
        /// Handles markup extension value in style as:
        /// XyzAttribute="{XyzMarkup value1,
        ///                          value2,
        ///                          key1=value1,
        ///                          key2=value2}"
        /// </summary>
        /// <param name="attrInfo"></param>
        /// <param name="baseIndentationString"></param>
        /// <returns></returns>
        public string ToMultiLineString(AttributeInfo attrInfo, string baseIndentationString)
        {
            if (!attrInfo.IsMarkupExtension)
            {
                throw new ArgumentException(
                    "AttributeInfo shall have a markup extension value.",
                    MethodBase.GetCurrentMethod().GetParameters()[0].Name);
            }

            if (attrInfo.IsMarkupExtension)
            {
                string currentIndentationString = $"{baseIndentationString}{String.Empty.PadLeft(attrInfo.Name.Length + 2, ' ')}";
                var lines = this.formatter.Format(attrInfo.MarkupExtension);

                var buffer = new StringBuilder();
                buffer.AppendFormat("{0}=\"{1}", attrInfo.Name, lines.First());
                foreach (var line in lines.Skip(1))
                {
                    buffer.AppendLine();
                    buffer.Append(this.indentService.Normalize(currentIndentationString + line));
                }

                buffer.Append('"');
                return buffer.ToString();
            }

            return $"{attrInfo.Name}=\"{attrInfo.Value}\"";
        }

        /// <summary>
        /// Single line value line in style as:
        /// attribute_name="attribute_value"
        /// </summary>
        /// <param name="attrInfo"></param>
        /// <returns></returns>
        public string ToSingleLineString(AttributeInfo attrInfo)
        {
            var valuePart = attrInfo.IsMarkupExtension
                ? this.formatter.FormatSingleLine(attrInfo.MarkupExtension)
                : attrInfo.Value.ToXmlEncodedString();

            return $"{attrInfo.Name}=\"{valuePart}\"";
        }
    }
}


text:
{% extends "curated_resources/base.html" %}
{% block curated_resources %}
    {% if latest_resources_list %}

        {% for poll in latest_resources_list %}

{{ poll.title }}

        {% endfor %}

    {% else %}

No polls are available.

    {% endif %}
{% endblock curated_resources %}


text:
'use strict';
/* eslint-disable prefer-spread, prefer-rest-params, consistent-return, no-confusing-arrow */

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var jsonPath = require('./json-path');
var ResubscribeNotifier = require('../utils/resubscribe-notifier');
var EventEmitter = require('component-emitter2');
var C = require('../constants/constants');
var messageBuilder = require('../message/message-builder');
var messageParser = require('../message/message-parser');
var utils = require('../utils/utils');

/**
 * This class represents a single record - an observable
 * dataset returned by client.record.getRecord()
 *
 * @extends {EventEmitter}
 *
 * @param {String} name              The unique name of the record
 * @param {Object} recordOptions     A map of options, e.g. { persist: true }
 * @param {Connection} Connection    The instance of the server connection
 * @param {Object} options        Deepstream options
 * @param {Client} client        deepstream.io client
 *
 * @constructor
 */
var Record = function Record(name, recordOptions, connection, options, client) {
  if (typeof name !== 'string' || name.length === 0) {
    throw new Error('invalid argument name');
  }

  this.name = name;
  this.usages = 0;
  this._recordOptions = recordOptions;
  this._connection = connection;
  this._client = client;
  this._options = options;
  this.isReady = false;
  this.isDestroyed = false;
  this.hasProvider = false;
  this._$data = Object.create(null);
  this.version = null;
  this._eventEmitter = new EventEmitter();
  this._queuedMethodCalls = [];
  this._writeCallbacks = {};

  this._mergeStrategy = null;
  if (options.mergeStrategy) {
    this.setMergeStrategy(options.mergeStrategy);
  }

  this._ackTimeoutRegistry = client._$getAckTimeoutRegistry();
  this._resubscribeNotifier = new ResubscribeNotifier(this._client, this._sendRead.bind(this));

  this._readAckTimeout = this._ackTimeoutRegistry.add({
    topic: C.TOPIC.RECORD,
    name: name,
    action: C.ACTIONS.SUBSCRIBE,
    timeout: this._options.recordReadAckTimeout
  });
  this._responseTimeout = this._ackTimeoutRegistry.add({
    topic: C.TOPIC.RECORD,
    name: name,
    action: C.ACTIONS.READ,
    event: C.EVENT.RESPONSE_TIMEOUT,
    timeout: this._options.recordReadTimeout
  });
  this._sendRead();
};

EventEmitter(Record.prototype); // eslint-disable-line

/**
 * Set a merge strategy to resolve any merge conflicts that may occur due
 * to offline work or write conflicts. The function will be called with the
 * local record, the remote version/data and a callback to call once the merge has
 * completed or if an error occurs ( which leaves it in an inconsistent state until
 * the next update merge attempt ).
 *
 * @param   {Function} mergeStrategy A Function that can resolve merge issues.
 *
 * @public
 * @returns {void}
 */
Record.prototype.setMergeStrategy = function (mergeStrategy) {
  if (typeof mergeStrategy === 'function') {
    this._mergeStrategy = mergeStrategy;
  } else {
    throw new Error('Invalid merge strategy: Must be a Function');
  }
};

/**
 * Returns a copy of either the entire dataset of the record
 * or - if called with a path - the value of that path within
 * the record's dataset.
 *
 * Returning a copy rather than the actual value helps to prevent
 * the record getting out of sync due to unintentional changes to
 * its data
 *
 * @param   {[String]} path A JSON path, e.g. users[ 2 ].firstname
 *
 * @public
 * @returns {Mixed} value
 */
Record.prototype.get = function (path) {
  return jsonPath.get(this._$data, path, this._options.recordDeepCopy);
};

/**
 * Wrapper function around the record.set that returns a promise
 * if no callback is supplied.
 *
 * @param {String|Object}   pathOrData     the path or data to write to the record
 * @param {Object|Function}   dataOrCallback the data to write to the record or the write
 *                                  acknowledgement callback
 * @param {Function} callback       the callback with the result of the write
 * @returns {Promise} if a callback is omitted a Promise is returned with the result of the write
 */
Record.prototype.setWithAck = function (pathOrData, dataOrCallback, callback) {
  var _this = this;

  if (pathOrData && dataOrCallback && callback) {
    this.set(pathOrData, dataOrCallback, callback);
  } else if (pathOrData && typeof dataOrCallback === 'function') {
    this.set(pathOrData, dataOrCallback);
  } else if (pathOrData && (typeof dataOrCallback === 'undefined' ? 'undefined' : _typeof(dataOrCallback)) === 'object') {
    return new Promise(function (resolve, reject) {
      _this.set(pathOrData, dataOrCallback, function (error) {
        return error === null ? resolve() : reject(error);
      });
    });
  } else {
    return new Promise(function (resolve, reject) {
      _this.set(pathOrData, function (error) {
        return error === null ? resolve() : reject(error);
      });
    });
  }
};

/**
 * Sets the value of either the entire dataset
 * or of a specific path within the record
 * and submits the changes to the server
 *
 * If the new data is equal to the current data, nothing will happen
 *
 * @param {[String|Object]} pathOrData Either a JSON path when called with
 *                                     two arguments or the data itself
 * @param {Object} data     The data that should be stored in the record
 *
 * @public
 * @returns {void}
 */
Record.prototype.set = function (pathOrData, dataOrCallback, callback) {
  var path = void 0;
  var data = void 0;
  if (arguments.length === 1) {
    // set( object )
    if ((typeof pathOrData === 'undefined' ? 'undefined' : _typeof(pathOrData)) !== 'object') {
      throw new Error('invalid argument data');
    }
    data = pathOrData;
  } else if (arguments.length === 2) {
    if (typeof pathOrData === 'string' && pathOrData.length !== 0 && typeof dataOrCallback !== 'function') {
      // set( path, data )
      path = pathOrData;
      data = dataOrCallback;
    } else if ((typeof pathOrData === 'undefined' ? 'undefined' : _typeof(pathOrData)) === 'object' && typeof dataOrCallback === 'function') {
      // set( data, callback )
      data = pathOrData;
      callback = dataOrCallback; // eslint-disable-line
    } else {
      throw new Error('invalid argument path');
    }
  } else if (arguments.length === 3) {
    // set( path, data, callback )
    if (typeof pathOrData !== 'string' || pathOrData.length === 0 || typeof callback !== 'function') {
      throw new Error('invalid arguments, must pass in a string, a value and a function');
    }
    path = pathOrData;
    data = dataOrCallback;
  }

  if (!path && (data === null || (typeof data === 'undefined' ? 'undefined' : _typeof(data)) !== 'object')) {
    throw new Error('invalid arguments, scalar values cannot be set without path');
  }

  if (this._checkDestroyed('set')) {
    return this;
  }

  if (!this.isReady) {
    this._queuedMethodCalls.push({ method: 'set', args: arguments });
    return this;
  }

  var oldValue = this._$data;
  var newValue = jsonPath.set(oldValue, path, data, this._options.recordDeepCopy);

  if (oldValue === newValue) {
    if (typeof callback === 'function') {
      var errorMessage = null;
      if (!utils.isConnected(this._client)) {
        errorMessage = 'Connection error: error updating record as connection was closed';
      }
      utils.requestIdleCallback(function () {
        return callback(errorMessage);
      });
    }
    return this;
  }

  var config = void 0;
  if (typeof callback === 'function') {
    config = {};
    config.writeSuccess = true;
    if (!utils.isConnected(this._client)) {
      utils.requestIdleCallback(function () {
        return callback('Connection error: error updating record as connection was closed');
      });
    } else {
      this._setUpCallback(this.version, callback);
    }
  }
  this._sendUpdate(path, data, config);
  this._applyChange(newValue);
  return this;
};

/**
 * Subscribes to changes to the records dataset.
 *
 * Callback is the only mandatory argument.
 *
 * When called with a path, it will only subscribe to updates
 * to that path, rather than the entire record
 *
 * If called with true for triggerNow, the callback will
 * be called immediatly with the current value
 *
 * @param   {[String]}    path      A JSON path within the record to subscribe to
 * @param   {Function}    callback         Callback function to notify on changes
 * @param   {[Boolean]}   triggerNow      A flag to specify whether the callback should
 *                                         be invoked immediatly with the current value
 *
 * @public
 * @returns {void}
 */
// eslint-disable-next-line
Record.prototype.subscribe = function (path, callback, triggerNow) {
  var _this2 = this;

  var args = this._normalizeArguments(arguments);

  if (args.path !== undefined && (typeof args.path !== 'string' || args.path.length === 0)) {
    throw new Error('invalid argument path');
  }
  if (typeof args.callback !== 'function') {
    throw new Error('invalid argument callback');
  }

  if (this._checkDestroyed('subscribe')) {
    return;
  }

  if (args.triggerNow) {
    this.whenReady(function () {
      _this2._eventEmitter.on(args.path, args.callback);
      args.callback(_this2.get(args.path));
    });
  } else {
    this._eventEmitter.on(args.path, args.callback);
  }
};

/**
 * Removes a subscription that was previously made using record.subscribe()
 *
 * Can be called with a path to remove the callback for this specific
 * path or only with a callback which removes it from the generic subscriptions
 *
 * Please Note: unsubscribe is a purely client side operation. If the app is no longer
 * interested in receiving updates for this record from the server it needs to call
 * discard instead
 *
 * @param   {[String|Function]}   pathOrCallback A JSON path
 * @param   {Function}         callback     The callback method. Please note, if a bound
 *                                          method was passed to subscribe, the same method
 *                                          must be passed to unsubscribe as well.
 *
 * @public
 * @returns {void}
 */
// eslint-disable-next-line
Record.prototype.unsubscribe = function (pathOrCallback, callback) {
  var args = this._normalizeArguments(arguments);

  if (args.path !== undefined && (typeof args.path !== 'string' || args.path.length === 0)) {
    throw new Error('invalid argument path');
  }
  if (args.callback !== undefined && typeof args.callback !== 'function') {
    throw new Error('invalid argument callback');
  }

  if (this._checkDestroyed('unsubscribe')) {
    return;
  }
  this._eventEmitter.off(args.path, args.callback);
};

/**
 * Removes all change listeners and notifies the server that the client is
 * no longer interested in updates for this record
 *
 * @public
 * @returns {void}
 */
Record.prototype.discard = function () {
  var _this3 = this;

  if (this._checkDestroyed('discard')) {
    return;
  }
  this.whenReady(function () {
    _this3.usages--;
    if (_this3.usages <= 0) {
      _this3.emit('destroyPending');
      _this3._discardTimeout = _this3._ackTimeoutRegistry.add({
        topic: C.TOPIC.RECORD,
        name: _this3.name,
        action: C.ACTIONS.UNSUBSCRIBE
      });
      _this3._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UNSUBSCRIBE, [_this3.name]);
    }
  });
};

/**
 * Deletes the record on the server.
 *
 * @public
 * @returns {void}
 */
Record.prototype.delete = function () {
  var _this4 = this;

  if (this._checkDestroyed('delete')) {
    return;
  }
  this.whenReady(function () {
    _this4.emit('destroyPending');
    _this4._deleteAckTimeout = _this4._ackTimeoutRegistry.add({
      topic: C.TOPIC.RECORD,
      name: _this4.name,
      action: C.ACTIONS.DELETE,
      event: C.EVENT.DELETE_TIMEOUT,
      timeout: _this4._options.recordDeleteTimeout
    });
    _this4._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.DELETE, [_this4.name]);
  });
};

/**
 * Convenience method, similar to promises. Executes callback
 * whenever the record is ready, either immediatly or once the ready
 * event is fired
 *
 * @param   {Function} callback Will be called when the record is ready
 *
 * @returns {void}
 */
Record.prototype.whenReady = function (callback) {
  var _this5 = this;

  if (this.isReady === true) {
    if (callback) {
      callback(this);
      return;
    }
    return Promise.resolve(this);
  }
  if (callback) {
    this.once('ready', callback.bind(this, this));
  } else {
    return new Promise(function (resolve) {
      return _this5.once('ready', resolve.bind(_this5, _this5));
    });
  }
};

/**
 * Callback for incoming messages from the message handler
 *
 * @param   {Object} message parsed and validated deepstream message
 *
 * @package private
 * @returns {void}
 */
Record.prototype._$onMessage = function (message) {
  if (message.action === C.ACTIONS.READ) {
    if (this.version === null) {
      this._ackTimeoutRegistry.clear(message);
      this._onRead(message);
    } else {
      this._applyUpdate(message, this._client);
    }
  } else if (message.action === C.ACTIONS.ACK) {
    this._processAckMessage(message);
  } else if (message.action === C.ACTIONS.UPDATE || message.action === C.ACTIONS.PATCH) {
    this._applyUpdate(message, this._client);
  } else if (message.action === C.ACTIONS.WRITE_ACKNOWLEDGEMENT) {
    Record._handleWriteAcknowledgements(message, this._writeCallbacks, this._client);
  } else if (message.data[0] === C.EVENT.VERSION_EXISTS) {
    // Otherwise it should be an error, and dealt with accordingly
    this._recoverRecord(message.data[2], JSON.parse(message.data[3]), message);
  } else if (message.data[0] === C.EVENT.MESSAGE_DENIED) {
    this._clearTimeouts();
  } else if (message.action === C.ACTIONS.SUBSCRIPTION_HAS_PROVIDER) {
    var hasProvider = messageParser.convertTyped(message.data[1], this._client);
    this.hasProvider = hasProvider;
    this.emit('hasProviderChanged', hasProvider);
  }
};

Record._handleWriteAcknowledgements = function (message, callbacks, client) {
  var versions = JSON.parse(message.data[1]);
  for (var i = 0; i < versions.length; i++) {
    var callback = callbacks[versions[i]];
    if (callback !== undefined) {
      callback(messageParser.convertTyped(message.data[2], client));
      delete callbacks[versions[i]];
    }
  }
};

/**
 * Called when a merge conflict is detected by a VERSION_EXISTS error or if an update recieved
 * is directly after the clients. If no merge strategy is configure it will emit a VERSION_EXISTS
 * error and the record will remain in an inconsistent state.
 *
 * @param   {Number} remoteVersion The remote version number
 * @param   {Object} remoteData The remote object data
 * @param   {Object} message parsed and validated deepstream message
 *
 * @private
 * @returns {void}
 */
Record.prototype._recoverRecord = function (remoteVersion, remoteData, message) {
  message.processedError = true;
  if (this._mergeStrategy) {
    this._mergeStrategy(this, remoteData, remoteVersion, this._onRecordRecovered.bind(this, remoteVersion, remoteData, message));
  } else {
    this.emit('error', C.EVENT.VERSION_EXISTS, 'received update for ' + remoteVersion + ' but version is ' + this.version);
  }
};

Record.prototype._sendUpdate = function (path, data, config) {
  this.version++;
  var msgData = void 0;
  if (!path) {
    msgData = config === undefined ? [this.name, this.version, data] : [this.name, this.version, data, config];
    this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UPDATE, msgData);
  } else {
    msgData = config === undefined ? [this.name, this.version, path, messageBuilder.typed(data)] : [this.name, this.version, path, messageBuilder.typed(data), config];
    this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.PATCH, msgData);
  }
};

/**
 * Callback once the record merge has completed. If successful it will set the
 * record state, else emit and error and the record will remain in an
 * inconsistent state until the next update.
 *
 * @param   {Number} remoteVersion The remote version number
 * @param   {Object} remoteData The remote object data
 * @param   {Object} message parsed and validated deepstream message
 *
 * @private
 * @returns {void}
 */
Record.prototype._onRecordRecovered = function (remoteVersion, remoteData, message, error, data) {
  if (!error) {
    var oldVersion = this.version;
    this.version = remoteVersion;

    var oldValue = this._$data;

    if (utils.deepEquals(oldValue, remoteData)) {
      return;
    }

    var newValue = jsonPath.set(oldValue, undefined, data, false);

    if (utils.deepEquals(data, remoteData)) {
      this._applyChange(data);

      var callback = this._writeCallbacks[remoteVersion];
      if (callback !== undefined) {
        callback(null);
        delete this._writeCallbacks[remoteVersion];
      }
      return;
    }

    var config = message.data[4];
    if (config && JSON.parse(config).writeSuccess) {
      var _callback = this._writeCallbacks[oldVersion];
      delete this._writeCallbacks[oldVersion];
      this._setUpCallback(this.version, _callback);
    }
    this._sendUpdate(undefined, data, config);
    this._applyChange(newValue);
  } else {
    this.emit('error', C.EVENT.VERSION_EXISTS, 'received update for ' + remoteVersion + ' but version is ' + this.version);
  }
};

/**
 * Callback for ack-messages. Acks can be received for
 * subscriptions, discards and deletes
 *
 * @param   {Object} message parsed and validated deepstream message
 *
 * @private
 * @returns {void}
 */
Record.prototype._processAckMessage = function (message) {
  var acknowledgedAction = message.data[0];

  if (acknowledgedAction === C.ACTIONS.SUBSCRIBE) {
    this._ackTimeoutRegistry.clear(message);
  } else if (acknowledgedAction === C.ACTIONS.DELETE) {
    this.emit('delete');
    this._destroy();
  } else if (acknowledgedAction === C.ACTIONS.UNSUBSCRIBE) {
    this.emit('discard');
    this._destroy();
  }
};

/**
 * Applies incoming updates and patches to the record's dataset
 *
 * @param   {Object} message parsed and validated deepstream message
 *
 * @private
 * @returns {void}
 */
Record.prototype._applyUpdate = function (message) {
  var version = parseInt(message.data[1], 10);
  var data = void 0;
  if (message.action === C.ACTIONS.PATCH) {
    data = messageParser.convertTyped(message.data[3], this._client);
  } else {
    data = JSON.parse(message.data[2]);
  }

  if (this.version === null) {
    this.version = version;
  } else if (this.version + 1 !== version) {
    if (message.action === C.ACTIONS.PATCH) {
      /**
      * Request a snapshot so that a merge can be done with the read reply which contains
      * the full state of the record
      **/
      this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SNAPSHOT, [this.name]);
    } else {
      this._recoverRecord(version, data, message);
    }
    return;
  }

  this.version = version;
  this._applyChange(jsonPath.set(this._$data, message.action === C.ACTIONS.PATCH ? message.data[2] : undefined, data));
};

/**
 * Callback for incoming read messages
 *
 * @param   {Object} message parsed and validated deepstream message
 *
 * @private
 * @returns {void}
 */
Record.prototype._onRead = function (message) {
  this.version = parseInt(message.data[1], 10);
  this._applyChange(jsonPath.set(this._$data, undefined, JSON.parse(message.data[2])));
  this._setReady();
};

/**
 * Invokes method calls that where queued while the record wasn't ready
 * and emits the ready event
 *
 * @private
 * @returns {void}
 */
Record.prototype._setReady = function () {
  this.isReady = true;
  for (var i = 0; i < this._queuedMethodCalls.length; i++) {
    this[this._queuedMethodCalls[i].method].apply(this, this._queuedMethodCalls[i].args);
  }
  this._queuedMethodCalls = [];
  this.emit('ready');
};

Record.prototype._setUpCallback = function (currentVersion, callback) {
  var newVersion = Number(this.version) + 1;
  this._writeCallbacks[newVersion] = callback;
};

/**
 * Sends the read message, either initially at record
 * creation or after a lost connection has been re-established
 *
 * @private
 * @returns {void}
 */
Record.prototype._sendRead = function () {
  this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.CREATEORREAD, [this.name]);
};

/**
 * Compares the new values for every path with the previously stored ones and
 * updates the subscribers if the value has changed
 *
 * @private
 * @returns {void}
 */
Record.prototype._applyChange = function (newData) {
  if (this.isDestroyed) {
    return;
  }

  var oldData = this._$data;
  this._$data = newData;

  var paths = this._eventEmitter.eventNames();
  for (var i = 0; i < paths.length; i++) {
    var newValue = jsonPath.get(newData, paths[i], false);
    var oldValue = jsonPath.get(oldData, paths[i], false);

    if (newValue !== oldValue) {
      this._eventEmitter.emit(paths[i], this.get(paths[i]));
    }
  }
};

/**
 * Creates a map based on the types of the provided arguments
 *
 * @param {Arguments} args
 *
 * @private
 * @returns {Object} arguments map
 */
Record.prototype._normalizeArguments = function (args) {
  // If arguments is already a map of normalized parameters
  // (e.g. when called by AnonymousRecord), just return it.
  if (args.length === 1 && _typeof(args[0]) === 'object') {
    return args[0];
  }

  var result = Object.create(null);

  for (var i = 0; i < args.length; i++) {
    if (typeof args[i] === 'string') {
      result.path = args[i];
    } else if (typeof args[i] === 'function') {
      result.callback = args[i];
    } else if (typeof args[i] === 'boolean') {
      result.triggerNow = args[i];
    }
  }

  return result;
};

/**
 * Clears all timeouts that are set when the record is created
 *
 * @private
 * @returns {void}
 */
Record.prototype._clearTimeouts = function () {
  this._ackTimeoutRegistry.remove({ ackId: this._readAckTimeout, silent: true });
  this._ackTimeoutRegistry.remove({ ackId: this._responseTimeout, silent: true });
  this._ackTimeoutRegistry.remove({ ackId: this._deleteAckTimeout, silent: true });
  this._ackTimeoutRegistry.remove({ ackId: this._discardTimeout, silent: true });
};

/**
 * A quick check that's carried out by most methods that interact with the record
 * to make sure it hasn't been destroyed yet - and to handle it gracefully if it has.
 *
 * @param   {String} methodName The name of the method that invoked this check
 *
 * @private
 * @returns {Boolean} is destroyed
 */
Record.prototype._checkDestroyed = function (methodName) {
  if (this.isDestroyed) {
    this.emit('error', 'Can\'t invoke \'' + methodName + '\'. Record \'' + this.name + '\' is already destroyed');
    return true;
  }

  return false;
};

/**
 * Destroys the record and nulls all
 * its dependencies
 *
 * @private
 * @returns {void}
 */
Record.prototype._destroy = function () {
  this._clearTimeouts();
  this._eventEmitter.off();
  this._resubscribeNotifier.destroy();
  this.isDestroyed = true;
  this.isReady = false;
  this._client = null;
  this._eventEmitter = null;
  this._connection = null;
};

module.exports = Record;


text:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

    <title>mbsp-tags</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link type="text/css" rel="stylesheet" href="../clips.css" />

    <link href="../js/shCore.css" rel="stylesheet" type="text/css" />
    <link href="../js/shThemeDefault.css" rel="stylesheet" type="text/css" />

    <div class="node node-type-page"

View online at: http://www.clips.ua.ac.be/pages/mbsp-tags

Penn Treebank II tag set

Pattern and MBSP assign meaningful tags to words and groups of words in a sentence. Each tag is a short code (such as "DT" for "determiner").

The tag set is based on the Penn Treebank Tagging Guidelines [pdf].

Part-of-speech tags

Part-of-speech tags are assigned to a single word according to its role in the sentence. Traditional grammar classifies words based on eight parts of speech: the verb (VB), the noun (NN), the pronoun (PR+DT), the adjective (JJ), the adverb (RB), the preposition (IN), the conjunction (CC), and the interjection (UH).

Tag
Description
Example

CC
conjunction, coordinating
and, or, but

CD
cardinal number
five, three, 13%

DT
determiner
the, a, these

EX
existential there
there were six boys

FW
foreign word
mais

IN
conjunction, subordinating or preposition
of, on, before, unless

JJ
adjective
nice, easy

JJR
adjective, comparative
nicer, easier

JJS
adjective, superlative
nicest, easiest

LS
list item marker

MD
verb, modal auxillary
may, should

NN
noun, singular or mass
tiger, chair, laughter

NNS
noun, plural
tigers, chairs, insects

NNP
noun, proper singular
Germany, God, Alice

NNPS
noun, proper plural
we met two Christmases ago

PDT
predeterminer
both his children

POS
possessive ending
's

PRP
pronoun, personal
me, you, it

PRP$
pronoun, possessive
my, your, our

RB
adverb
extremely, loudly, hard

RBR
adverb, comparative
better

RBS
adverb, superlative
best

RP
adverb, particle
about, off, up

SYM
symbol
%

TO
infinitival to
what to do?

UH
interjection
oh, oops, gosh

VB
verb, base form
think

VBZ
verb, 3rd person singular present
she thinks

VBP
verb, non-3rd person singular present
I think

VBD
verb, past tense
they thought

VBN
verb, past participle
a sunken ship

VBG
verb, gerund or present participle
thinking is fun

WDT
wh-determiner
which, whatever, whichever

WP
wh-pronoun, personal
what, who, whom

WP$
wh-pronoun, possessive
whose, whosever

WRB
wh-adverb
where, when

.
punctuation mark, sentence closer
.;?*

,
punctuation mark, comma
,

:
punctuation mark, colon
:

(
contextual separator, left paren
(

)
contextual separator, right paren
)

Chunk tags

Chunk tags are assigned to groups of words that belong together (i.e. phrases). The most common phrases are the noun phrase (NP, for example the black cat) and the verb phrase (VP, for example is purring).

Tag
Description
Words
Example
%

NP
noun phrase
DT+RB+JJ+NN + PR
the strange bird
 51

PP
prepositional phrase
TO+IN
in between
 19

VP
verb phrase
RB+MD+VB
was looking

9

ADVP
adverb phrase
RB
also

 6

ADJP
adjective phrase
CC+RB+JJ
warm and cosy
 3

SBAR
subordinating conjunction
IN
whether or not

3

PRT
particle
RP
up the stairs
 1

INTJ
interjection
UH
hello

 0

The IOB prefix marks whether a word is inside or outside of a chunk.

Tag
Description

I-
inside the chunk

B-
inside the chunk, preceding word is part of a different chunk

O
not part of a chunk

A prepositional noun phrase (PNP) is a group of chunks starting with a preposition (PP) followed by noun phrases (NP), for example: under the table.

Tag
Description
Chunks
Example

PNP
prepositional noun phrase
PP+NP
as of today

Relation tags

Relations tags describe the relation between different chunks, and clarify the role of a chunk in that relation. The most common roles in a sentence are SBJ (subject noun phrase) and OBJ (object noun phrase). They link NP to VP chunks. The subject of a sentence is the person, thing, place or idea that is doing or being something. The object of a sentence is the person/thing affected by the action.

Tag
Description
Chunks
Example
%

-SBJ
sentence subject
NP
the cat sat on the mat

35

-OBJ
sentence object
NP+SBAR
the cat grabs the fish

27

-PRD
predicate
PP+NP+ADJP
the cat feels warm and fuzzy

7

-TMP
temporal
PP+NP+ADVP
arrive at noon

7

-CLR
closely related
PP+NP+ADVP
work as a researcher

6

-LOC
location
PP
live in Belgium

4

-DIR
direction
PP
walk towards the door

3

-EXT
extent
PP+NP
drop 10 %

1

-PRP
purpose
PP+SBAR
die as a result of

1

Anchor tags

Anchor tags describe how prepositional noun phrases (PNP) are attached to other chunks in the sentence. For example, in the sentence, I eat pizza with a fork, the anchor of with a fork is eat because it answers the question: "In what way do I eat?"

Tag
Description
Example

A1
anchor chunks that corresponds to P1
eat with a fork

P1
PNP that corresponds to A1
eat with a fork

Occurence estimate

The given percentages for chunk and relations tags are based on tenfold cross validation on sections 10 to 19 of the WSJ Corpus of the Penn Treebank II by Sabine Buchholz, from which we derived a rough indication. The estimate means that if a 100 chunk tags are found, about 50 would be NP tags and 35 would have a SBJ relation tag. About 30 of the chunks would be tagged as NP-SBJ, and 15 as NP-OBJ.

Reference: Buchholz, S. (2002). Memory-Based Grammatical Relation Finding. ILK, Tilburg University.


text:
package com.raspi.chatapp.util;

import android.content.Context;

/**
 * These are the main constants used all over the application.
 */
public class Constants{
  /**
   * The string referring the the {@link android.content.SharedPreferences
   * SharedPreferences} I am using for everything that is not related to
   * settings. The settings are managed in the default {@link android.content
   * .SharedPreferences SharedPreferences}.
   */
  public static final String PREFERENCES = "com.raspi.chatapp.util.Constants.PREFERENCES";
  /**
   * This string references to everything related to the user name which I
   * use to log into the XMPP Server.
   */
  public static final String USERNAME = "com.raspi.chatapp.util.Constants.USERNAME";
  /**
   * This string references to everything related to my password which I use
   * to log into the XMPP Server.
   */
  public static final String PASSWORD = "com.raspi.chatapp.util.Constants.PASSWORD";
  /**
   * This string references to everything related to the buddyId of the
   * chat partner. This is not the {@link Constants#CHAT_NAME chatName}.
   */
  public static final String BUDDY_ID = "com.raspi.chatapp.util.Constants.BUDDY_ID";
  /**
   * This string references to everything related to the name of the
   * chat partner. This is not the {@link Constants#BUDDY_ID buddId}.
   */
  public static final String CHAT_NAME = "com.raspi.chatapp.util.Constants.CHAT_NAME";
  /**
   * This string references to everything related to the body of a sent or
   * received message. That means the actual content and not the metadata.
   */
  public static final String MESSAGE_BODY = "com.raspi.chatapp.util.Constants.MESSAGE_BODY";
  /**
   * This string references to everything related to the "presenceChanged"
   * event that is fired every time the presence of anyone in my roster has
   * changed. The presence signals the last time the buddy was online.
   */
  public static final String PRESENCE_CHANGED = "com.raspi.chatapp.util.Constants.PRESENCE_CHANGED";
  /**
   * This string references to everything related to the presence status.
   * That means the actual content of the new presence that has changed.
   */
  public static final String PRESENCE_STATUS = "com.raspi.chatapp.util.Constants.PRESENCE_STATUS";
  /**
   * This string references to everything related to the "messageReceived"
   * event which is fired as soon as someone sends me a message.
   */
  public static final String MESSAGE_RECEIVED = "com.raspi.chatapp.util.Constants.MESSAGE_RECEIVED";
  /**
   * This string references to everything related to the "reconnectionEvent"
   * which is fired every time I reconnected to the XMPP server.
   */
  public static final String RECONNECTED = "com.raspi.chatapp.util.Constants.RECONNECTED";
  /**
   * This string references to everything related to the type of a message
   * which, at the moment, are the following.
   *

   *
{@link com.raspi.chatapp.util.storage.MessageHistory#TYPE_TEXT
   * TEXT}

   *
{@link com.raspi.chatapp.util.storage.MessageHistory#TYPE_IMAGE IMAGE
   * }

   *

   */
  public static final String MESSAGE_TYPE = "com.raspi.chatapp.util.Constants.MESSAGE_TYPE";
  /**
   * This string references to everything related to the uri of the image to
   * be sent.
   */
  public static final String IMAGE_URI = "com.raspi.chatapp.util.Constants.IMAGE_URI";
  /**
   * This string references to everything related to the id of a message
   */
  public static final String MESSAGE_ID = "com.raspi.chatapp.util.Constants.MESSAGE_ID";
  /**
   * This string references to everything related to the id that the corresponding chat partner
   * has of this message. That means every message has an messageId, which is mine and an
   * othersId which is the message id of the buddy.
   */
  public static final String MESSAGE_OTHERS_ID = "com.raspi.chatapp.util.Constants.MESSAGE_OTHERS_ID";
  /**
   * This string references to everything related to the event
   * "messageStatusChanged" which is fired when the buddy sent a acknowledge
   * message. Currently are the following
   * {@link com.raspi.chatapp.util.internet.XmppManager#sendAcknowledgement(String, long, String) acknowledgements}
   * supported:
   *

   *
{@link com.raspi.chatapp.util.storage.MessageHistory#STATUS_RECEIVED RECEIVED}

   *
{@link com.raspi.chatapp.util.storage.MessageHistory#STATUS_READ READ
   * }

   *

   */
  public static final String MESSAGE_STATUS_CHANGED = "com.raspi.chatapp.util.Constants.MESSAGE_STATUS_CHANGED";
  /**
   * This string references to the {@link #PREFERENCES preference} whether a
   * password request should be made or not.
   */
  public static final String PWD_REQUEST = "com.raspi.chatapp.util.Constants.PWD_REQUEST";
  /**
   * This string references to everything related to the last presence I
   * sent. This needs to be saved because every time I reconnect I want to
   * send my presence to "initiate" the connection. I don't really get why
   * this should be necessary but it is...
   */
  public static final String LAST_PRESENCE_SENT = "com.raspi.chatapp.util.Constants.LAST_PRESENCE_SENT";
  /**
   * references to something to do with pressing back
   */
  public static final String PRESSED_BACK = "com.raspi.chatapp.util.Constants.PRESSED_BACK";
  /**
   * This string references to the file name of the wallpaper image.
   */
  public static final String WALLPAPER_NAME = "wallpaper.jpg";
  /**
   * This string references to the directory name the images I save are
   * stored. (Not the full path, only the directory!)
   */
  public static final String IMAGE_DIR = "ChatApp Images";

  public static int dipToPixel(Context context, int dip){
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int) (dip * scale + 0.5f);
  }
}


text:
namespace favicon {
namespace {

#if defined(OS_ANDROID) || defined(OS_IOS)
const bool kEnableTouchIcon = true;
#else
const bool kEnableTouchIcon = false;
#endif

}  // namespace

FaviconDriverImpl::FaviconDriverImpl(CoreFaviconService* favicon_service)
    : favicon_service_(favicon_service) {
  if (kEnableTouchIcon) {
    handlers_.push_back(std::make_unique<FaviconHandler>(
        favicon_service_, this, FaviconDriverObserver::NON_TOUCH_LARGEST));
    handlers_.push_back(std::make_unique<FaviconHandler>(
        favicon_service_, this, FaviconDriverObserver::TOUCH_LARGEST));
  } else {
    handlers_.push_back(std::make_unique<FaviconHandler>(
        favicon_service_, this, FaviconDriverObserver::NON_TOUCH_16_DIP));
  }
}

FaviconDriverImpl::~FaviconDriverImpl() = default;

void FaviconDriverImpl::FetchFavicon(const GURL& page_url,
                                     bool is_same_document) {
  for (const std::unique_ptr<FaviconHandler>& handler : handlers_)
    handler->FetchFavicon(page_url, is_same_document);
}

bool FaviconDriverImpl::HasPendingTasksForTest() {
  for (const std::unique_ptr<FaviconHandler>& handler : handlers_) {
    if (handler->HasPendingTasksForTest())
      return true;
  }
  return false;
}

void FaviconDriverImpl::SetFaviconOutOfDateForPage(const GURL& url,
                                                   bool force_reload) {
  if (favicon_service_) {
    favicon_service_->SetFaviconOutOfDateForPage(url);
    if (force_reload)
      favicon_service_->ClearUnableToDownloadFavicons();
  }
}

void FaviconDriverImpl::OnUpdateCandidates(
    const GURL& page_url,
    const std::vector<FaviconURL>& candidates,
    const GURL& manifest_url) {
  for (const std::unique_ptr<FaviconHandler>& handler : handlers_) {
    // We feed in the Web Manifest URL (if any) to the instance handling type
    // kWebManifestIcon, because those compete which each other (i.e. manifest
    // icons override inline touch icons).
    handler->OnUpdateCandidates(
        page_url, candidates,
        (handler->icon_types().count(
             favicon_base::IconType::kWebManifestIcon) != 0)
            ? manifest_url
            : GURL::EmptyGURL());
  }
}

}  // namespace favicon


text:
package net.bither.utils;

import net.bither.bitherj.utils.CharSequenceUtil;

import java.awt.*;

public class PasswordStrengthUtil {
    public static final PasswordStrength PassingPasswordStrength = PasswordStrength.Normal;
    public static final PasswordStrength WarningPasswordStrength = PasswordStrength.Medium;

    public enum PasswordStrength {
        Weak(0), Normal(1), Medium(2), Strong(3), VeryStrong(4);

        private int value;

        PasswordStrength(int value) {
            this.value = value;
        }

        public String getName() {
            switch (this) {
                case Normal:
                    return LocaliserUtils.getString("password_strength_normal");
                case Medium:
                    return LocaliserUtils.getString("password_strength_medium");
                case Strong:
                    return LocaliserUtils.getString("password_strength_strong");
                case VeryStrong:
                    return LocaliserUtils.getString("password_strength_very_strong");
                default:
                    return LocaliserUtils.getString("password_strength_weak");
            }
        }

        public Color getColor() {
            switch (this) {
                case Normal:
                    return new Color(238, 95, 91);
                case Medium:
                    return new Color(255, 163, 33);
                case Strong:
                    return new Color(98, 196, 98);
                case VeryStrong:
                    return new Color(98, 196, 98);
                default:
                    return new Color(238, 95, 91);
            }
        }

        public int getValue() {
            return value;
        }

        public int getProgress() {
            return value + 1;
        }

        public boolean passed() {
            return getValue() >= PassingPasswordStrength.getValue();
        }

        public boolean warning() {
            return passed() && getValue() <= WarningPasswordStrength.getValue();
        }
    }

    public static PasswordStrength checkPassword(CharSequence password) {
        switch (CharSequenceUtil.getRating(password)) {
            case 0:
                return PasswordStrength.Weak;
            case 1:
                return PasswordStrength.Normal;
            case 2:
                return PasswordStrength.Medium;
            case 3:
                return PasswordStrength.Strong;
            default:
                return PasswordStrength.VeryStrong;
        }
    }

}


text:
package org.apache.activemq.artemis.tests.integration.aerogear;

import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.api.core.client.ClientConsumer;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.api.core.client.ClientProducer;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.SendAcknowledgementHandler;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.config.ConnectorServiceConfiguration;
import org.apache.activemq.artemis.core.config.CoreQueueConfiguration;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.integration.aerogear.AeroGearConnectorServiceFactory;
import org.apache.activemq.artemis.integration.aerogear.AeroGearConstants;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.apache.activemq.artemis.utils.json.JSONArray;
import org.apache.activemq.artemis.utils.json.JSONException;
import org.apache.activemq.artemis.utils.json.JSONObject;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.AbstractHandler;
import org.mortbay.jetty.nio.SelectChannelConnector;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class AeroGearBasicServerTest extends ActiveMQTestBase
{

   private ActiveMQServer server;
   private ServerLocator locator;
   private Server jetty;

   @Override
   @Before
   public void setUp() throws Exception
   {
      super.setUp();
      /*
      * there will be a thread kept alive by the http connection, we could disable the thread check but this means that the tests
      * interfere with one another, we just have to wait for it to be killed
      * */
      jetty = new Server();
      SelectChannelConnector connector0 = new SelectChannelConnector();
      connector0.setPort(8080);
      connector0.setMaxIdleTime(30000);
      connector0.setHost("localhost");
      jetty.addConnector(connector0);
      jetty.start();
      HashMap<String, Object> params = new HashMap();
      params.put(AeroGearConstants.QUEUE_NAME, "testQueue");
      params.put(AeroGearConstants.ENDPOINT_NAME, "http://localhost:8080");
      params.put(AeroGearConstants.APPLICATION_ID_NAME, "9d646a12-e601-4452-9e05-efb0fccdfd08");
      params.put(AeroGearConstants.APPLICATION_MASTER_SECRET_NAME, "ed75f17e-cf3c-4c9b-a503-865d91d60d40");
      params.put(AeroGearConstants.RETRY_ATTEMPTS_NAME, 2);
      params.put(AeroGearConstants.RETRY_INTERVAL_NAME, 1);
      params.put(AeroGearConstants.BADGE_NAME, "99");
      params.put(AeroGearConstants.ALIASES_NAME, "me,him,them");
      params.put(AeroGearConstants.DEVICE_TYPE_NAME, "android,ipad");
      params.put(AeroGearConstants.SOUND_NAME, "sound1");
      params.put(AeroGearConstants.VARIANTS_NAME, "variant1,variant2");

      Configuration configuration = createDefaultInVMConfig()
         .addConnectorServiceConfiguration(
            new ConnectorServiceConfiguration()
               .setFactoryClassName(AeroGearConnectorServiceFactory.class.getName())
               .setParams(params)
               .setName("TestAeroGearService"))
         .addQueueConfiguration(new CoreQueueConfiguration()
                                   .setAddress("testQueue")
                                   .setName("testQueue"));

      server = addServer(createServer(configuration));
      server.start();

   }

   @Override
   @After
   public void tearDown() throws Exception
   {
      if (jetty != null)
      {
         jetty.stop();
      }
      super.tearDown();
   }

   @Test
   public void aerogearSimpleReceiveTest() throws Exception
   {
      CountDownLatch latch = new CountDownLatch(1);
      AeroGearHandler aeroGearHandler = new AeroGearHandler(latch);
      jetty.addHandler(aeroGearHandler);
      locator = createInVMNonHALocator();
      ClientSessionFactory sf = createSessionFactory(locator);
      ClientSession session = sf.createSession(false, true, true);
      ClientProducer producer = session.createProducer("testQueue");
      ClientMessage m = session.createMessage(true);
      m.putStringProperty(AeroGearConstants.AEROGEAR_ALERT.toString(), "hello from ActiveMQ!");
      m.putStringProperty("AEROGEAR_PROP1", "prop1");
      m.putBooleanProperty("AEROGEAR_PROP2", true);

      producer.send(m);

      assertTrue(latch.await(5, TimeUnit.SECONDS));
      assertNotNull(aeroGearHandler.jsonObject);
      JSONObject body = (JSONObject) aeroGearHandler.jsonObject.get("message");
      assertNotNull(body);
      String prop1 = body.getString("AEROGEAR_PROP1");
      assertNotNull(prop1);
      assertEquals(prop1, "prop1");
      prop1 = body.getString("AEROGEAR_PROP2");
      assertNotNull(prop1);
      assertEquals(prop1, "true");
      String alert = body.getString("alert");
      assertNotNull(alert);
      assertEquals(alert, "hello from ActiveMQ!");
      String sound = body.getString("sound");
      assertNotNull(sound);
      assertEquals(sound, "sound1");
      String badge = body.getString("badge");
      assertNotNull(badge);
      assertEquals(badge, "99");
      JSONArray jsonArray = (JSONArray) aeroGearHandler.jsonObject.get("variants");
      assertNotNull(jsonArray);
      assertEquals(jsonArray.getString(0), "variant1");
      assertEquals(jsonArray.getString(1), "variant2");
      jsonArray = (JSONArray) aeroGearHandler.jsonObject.get("alias");
      assertNotNull(jsonArray);
      assertEquals(jsonArray.getString(0), "me");
      assertEquals(jsonArray.getString(1), "him");
      assertEquals(jsonArray.getString(2), "them");
      jsonArray = (JSONArray) aeroGearHandler.jsonObject.get("deviceType");
      assertNotNull(jsonArray);
      assertEquals(jsonArray.getString(0), "android");
      assertEquals(jsonArray.getString(1), "ipad");
      Integer ttl = (Integer) aeroGearHandler.jsonObject.get("ttl");
      assertNotNull(ttl);
      assertEquals(ttl.intValue(), 3600);
      latch = new CountDownLatch(1);
      aeroGearHandler.resetLatch(latch);

      //now override the properties
      m = session.createMessage(true);
      m.putStringProperty(AeroGearConstants.AEROGEAR_ALERT.toString(), "another hello from ActiveMQ!");
      m.putStringProperty(AeroGearConstants.AEROGEAR_BADGE.toString(), "111");
      m.putStringProperty(AeroGearConstants.AEROGEAR_SOUND.toString(), "s1");
      m.putIntProperty(AeroGearConstants.AEROGEAR_TTL.toString(), 10000);
      m.putStringProperty(AeroGearConstants.AEROGEAR_ALIASES.toString(), "alias1,alias2");
      m.putStringProperty(AeroGearConstants.AEROGEAR_DEVICE_TYPES.toString(), "dev1,dev2");
      m.putStringProperty(AeroGearConstants.AEROGEAR_VARIANTS.toString(), "v1,v2");

      producer.send(m);
      assertTrue(latch.await(5, TimeUnit.SECONDS));
      assertNotNull(aeroGearHandler.jsonObject);
      body = (JSONObject) aeroGearHandler.jsonObject.get("message");
      assertNotNull(body);
      alert = body.getString("alert");
      assertNotNull(alert);
      assertEquals(alert, "another hello from ActiveMQ!");
      sound = body.getString("sound");
      assertNotNull(sound);
      assertEquals(sound, "s1");
      badge = body.getString("badge");
      assertNotNull(badge);
      assertEquals(badge, "111");
      jsonArray = (JSONArray) aeroGearHandler.jsonObject.get("variants");
      assertNotNull(jsonArray);
      assertEquals(jsonArray.getString(0), "v1");
      assertEquals(jsonArray.getString(1), "v2");
      jsonArray = (JSONArray) aeroGearHandler.jsonObject.get("alias");
      assertNotNull(jsonArray);
      assertEquals(jsonArray.getString(0), "alias1");
      assertEquals(jsonArray.getString(1), "alias2");
      jsonArray = (JSONArray) aeroGearHandler.jsonObject.get("deviceType");
      assertNotNull(jsonArray);
      assertEquals(jsonArray.getString(0), "dev1");
      assertEquals(jsonArray.getString(1), "dev2");
      ttl = (Integer) aeroGearHandler.jsonObject.get("ttl");
      assertNotNull(ttl);
      assertEquals(ttl.intValue(), 10000);
      session.start();
      ClientMessage message = session.createConsumer("testQueue").receiveImmediate();
      assertNull(message);
   }

   class AeroGearHandler extends AbstractHandler
   {
      JSONObject jsonObject;
      private CountDownLatch latch;

      public AeroGearHandler(CountDownLatch latch)
      {
         this.latch = latch;
      }

      @Override
      public void handle(String target, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, int i) throws IOException, ServletException
      {
         Request request = (Request) httpServletRequest;
         httpServletResponse.setContentType("text/html");
         httpServletResponse.setStatus(HttpServletResponse.SC_OK);
         request.setHandled(true);
         byte[] bytes = new byte[httpServletRequest.getContentLength()];
         httpServletRequest.getInputStream().read(bytes);
         String json = new String(bytes);
         try
         {
            jsonObject = new JSONObject(json);
         }
         catch (JSONException e)
         {
            jsonObject = null;
         }
         latch.countDown();
      }

      public void resetLatch(CountDownLatch latch)
      {
         this.latch = latch;
      }
   }

   @Test
   public void aerogearReconnectTest() throws Exception
   {
      jetty.stop();
      final CountDownLatch reconnectLatch = new CountDownLatch(1);
      jetty.addHandler(new AbstractHandler()
      {
         @Override
         public void handle(String target, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, int i) throws IOException, ServletException
         {
            Request request = (Request) httpServletRequest;
            httpServletResponse.setContentType("text/html");
            httpServletResponse.setStatus(HttpServletResponse.SC_OK);
            request.setHandled(true);
            reconnectLatch.countDown();
         }

      });
      locator = createInVMNonHALocator();
      ClientSessionFactory sf = createSessionFactory(locator);
      ClientSession session = sf.createSession(false, true, true);
      ClientProducer producer = session.createProducer("testQueue");
      final CountDownLatch latch = new CountDownLatch(2);
      ClientMessage m = session.createMessage(true);
      m.putStringProperty(AeroGearConstants.AEROGEAR_ALERT.toString(), "hello from ActiveMQ!");

      producer.send(m, new SendAcknowledgementHandler()
      {
         @Override
         public void sendAcknowledged(Message message)
         {
            latch.countDown();
         }
      });
      m = session.createMessage(true);
      m.putStringProperty(AeroGearConstants.AEROGEAR_ALERT.toString(), "another hello from ActiveMQ!");

      producer.send(m, new SendAcknowledgementHandler()
      {
         @Override
         public void sendAcknowledged(Message message)
         {
            latch.countDown();
         }
      });
      latch.await(5, TimeUnit.SECONDS);
      Thread.sleep(1000);
      jetty.start();
      reconnectLatch.await(5, TimeUnit.SECONDS);
      session.start();
      ClientMessage message = session.createConsumer("testQueue").receiveImmediate();
      assertNull(message);
   }

   @Test
   public void aerogear401() throws Exception
   {
      final CountDownLatch latch = new CountDownLatch(1);
      jetty.addHandler(new AbstractHandler()
      {
         @Override
         public void handle(String target, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, int i) throws IOException, ServletException
         {
            Request request = (Request) httpServletRequest;
            httpServletResponse.setContentType("text/html");
            httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            request.setHandled(true);
            latch.countDown();
         }

      });
      locator = createInVMNonHALocator();
      ClientSessionFactory sf = createSessionFactory(locator);
      ClientSession session = sf.createSession(false, true, true);
      ClientProducer producer = session.createProducer("testQueue");
      ClientMessage m = session.createMessage(true);
      m.putStringProperty(AeroGearConstants.AEROGEAR_ALERT.toString(), "hello from ActiveMQ!");

      producer.send(m);
      m = session.createMessage(true);
      m.putStringProperty(AeroGearConstants.AEROGEAR_ALERT.toString(), "another hello from ActiveMQ!");

      producer.send(m);
      assertTrue(latch.await(5, TimeUnit.SECONDS));
      session.start();
      ClientConsumer consumer = session.createConsumer("testQueue");
      ClientMessage message = consumer.receive(5000);
      assertNotNull(message);
      message = consumer.receive(5000);
      assertNotNull(message);
   }

   @Test
   public void aerogear404() throws Exception
   {
      jetty.addHandler(new AbstractHandler()
      {
         @Override
         public void handle(String target, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, int i) throws IOException, ServletException
         {
            Request request = (Request) httpServletRequest;
            httpServletResponse.setContentType("text/html");
            httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
            request.setHandled(true);
         }

      });
      locator = createInVMNonHALocator();
      ClientSessionFactory sf = createSessionFactory(locator);
      ClientSession session = sf.createSession(false, true, true);
      ClientProducer producer = session.createProducer("testQueue");
      ClientMessage m = session.createMessage(true);
      m.putStringProperty(AeroGearConstants.AEROGEAR_ALERT.toString(), "hello from ActiveMQ!");

      producer.send(m);
      m = session.createMessage(true);
      m.putStringProperty(AeroGearConstants.AEROGEAR_ALERT.toString(), "another hello from ActiveMQ!");

      producer.send(m);
      session.start();
      ClientConsumer consumer = session.createConsumer("testQueue");
      ClientMessage message = consumer.receive(5000);
      assertNotNull(message);
      message = consumer.receive(5000);
      assertNotNull(message);
   }
}