public interface AdapterGovernor extends BluetoothGovernor {

    /**
     * Returns name of the adapter.
     * @return name of the adapter
     * @throws NotReadyException if the adapter is not ready
     */
    String getName() throws NotReadyException;

    /**
     * Returns alias of the adapter.
     * @return alias of the adapter
     */
    String getAlias() throws NotReadyException;

    /**
     * Sets alias for the adapter.
     * @param alias new alias
     */
    void setAlias(String alias) throws NotReadyException;

    /**
     * Returns display name of the adapter.
     * @return display name of the adapter
     * @throws NotReadyException if the adapter object is not ready
     */
    String getDisplayName() throws NotReadyException;

    /**
     * Returns adapter powered status.
     * @return powered status
     * @throws NotReadyException if the adapter object is not ready
     */
    boolean isPowered() throws NotReadyException;

    /**
     * Returns adapter powered control status.
     * @return powered control status
     */
    boolean getPoweredControl();

    /**
     * Sets adapter powered control status.
     * @param powered a new powered control status
     */
    void setPoweredControl(boolean powered);

    /**
     * Returns adapter discovering status.
     * @return adapter discovering status
     * @throws NotReadyException if the adapter object is not ready
     */
    boolean isDiscovering() throws NotReadyException;

    /**
     * Returns adapter discovering control status.
     * @return adapter discovering control status
     */
    boolean getDiscoveringControl();

    /**
     * Sets adapter discovering control status.
     * @param discovering a new adapter discovering control status
     */
    void setDiscoveringControl(boolean discovering);

    /**
     * Returns estimated (used defined) signal propagation exponent. It is mainly used in estimated distance
     * calculation between the adapter and its devices. This factor is specific to the environment
     * where the adapter is used, i.e. how efficient the signal passes through obstacles on its way.
     * Normally it ranges from 2.0 (outdoors, no obstacles) to 4.0 (indoors, walls and furniture).
     * @return signal propagation exponent
     */
    double getSignalPropagationExponent();

    /**
     * Sets estimated (used defined) signal propagation exponent. It is mainly used in estimated distance
     * calculation between the adapter and its devices. This factor is specific to the environment
     * where the adapter is used, i.e. how efficient the signal passes through obstacles on its way.
     * Normally it ranges from 2.0 (outdoors, no obstacles) to 4.0 (indoors, walls and furniture).
     * @param exponent signal propagation exponent
     */
    void setSignalPropagationExponent(double exponent);

    /**
     * Returns a list of discovered Bluetooth devices by the adapter.
     * @return a list of discovered Bluetooth devices by the adapter
     * @throws NotReadyException if the adapter object is not ready
     */
    List<URL> getDevices() throws NotReadyException;

    /**
     * Returns a list of discovered device governors by the adapter.
     * @return a list of discovered device governors by the adapter
     * @throws NotReadyException if the adapter object is not ready
     */
    List<DeviceGovernor> getDeviceGovernors() throws NotReadyException;

    void addAdapterListener(AdapterListener adapterListener);

    void removeAdapterListener(AdapterListener adapterListener);

} public interface AdapterListener {

    /**
     * Fires when powered status is changed.
     * @param powered a new powered status
     */
    void powered(boolean powered);

    /**
     * Fires when discovering status is changed.
     * @param discovering a new discovering status
     */
    void discovering(boolean discovering);

} public interface BluetoothGovernor {

    /**
     * Returns the URL of the corresponding Bluetooth object.
     * @return the URL of the corresponding Bluetooth object
     */
    URL getURL();

    /**
     * Checks whether the governor is in state when its corresponding bluetooth object is acquired
     * and ready for manipulations.
     * @return true if the corresponding bluetooth object is acquired and ready for manipulations
     */
    boolean isReady();

    /**
     * Returns type of the corresponding Bluetooth object.
     * @return type of the corresponding Bluetooth object
     */
    BluetoothObjectType getType();

    /**
     * Returns the date/time of last known successful interaction with the corresponding native object.
     * @return the date/time of last known successful interaction with the corresponding native object
     */
    Instant getLastInteracted();

    /**
     * An accept method of the visitor pattern to process different bluetooth governors at once.
     * @param visitor bluetooth governor visitor
     * @throws Exception in case of any error
     */
    void accept(BluetoothObjectVisitor visitor) throws Exception;

    /**
     * Register a new governor listener.
     * @param listener a new governor listener
     */
    void addGovernorListener(GovernorListener listener);

    /**
     * Unregister a governor listener.
     * @param listener a governor listener
     */
    void removeGovernorListener(GovernorListener listener);


    <G extends BluetoothGovernor, V> CompletableFuture<V> when(Predicate<G> condition, Function<G, V> function);

    default <G extends BluetoothGovernor> CompletableFuture<Void> doWhen(Predicate<G> predicate,
                                                                              Consumer<G> consumer) {
        return when(predicate, g -> {
            consumer.accept((G) this);
            return null;
        });
    }

    /**
     * Returns a completable future that gets completed when governor becomes ready.
     * @param function a function that is invoked when governor becomes ready, the completable future is
     *                 completed with the result of this function
     * @param <G> bluetooth governor
     * @param <V> returned value
     * @return a completable future
     */
    default <G extends BluetoothGovernor, V> CompletableFuture<V> whenReady(Function<G, V> function) {
        return when(BluetoothGovernor::isReady, function);
    }

} public enum BluetoothObjectType {

    ADAPTER,
    DEVICE,
    CHARACTERISTIC

} public interface BluetoothObjectVisitor {

    void visit(AdapterGovernor governor) throws Exception;

    void visit(DeviceGovernor governor) throws Exception;

    void visit(CharacteristicGovernor governor) throws Exception;

} public interface CharacteristicGovernor extends BluetoothGovernor {

    /**
     * Returns access types (flags) supported by the characteristic.
     *
     * @return flags supported by the characteristic
     * @throws NotReadyException if the bluetooth object is not ready
     */
    Set<CharacteristicAccessType> getFlags() throws NotReadyException;

    /**
     * Checks whether the characteristic can be read.
     *
     * @return true if the characteristic can be read, otherwise false
     * @throws NotReadyException if the bluetooth object is not ready
     */
    boolean isReadable() throws NotReadyException;

    /**
     * Checks whether the characteristic can be written.
     *
     * @return true if the characteristic can be written, otherwise false
     * @throws NotReadyException if the bluetooth object is not ready
     */
    boolean isWritable() throws NotReadyException;

    /**
     * Checks whether the characteristic can notify.
     *
     * @return true if the characteristic can notify, otherwise false
     * @throws NotReadyException if the bluetooth object is not ready
     */
    boolean isNotifiable() throws NotReadyException;


    /**
     * Return true if notification is enabled, false otherwise.
     * @return true if notification is enabled, false otherwise
     * @throws NotReadyException if the bluetooth object is not ready
     */
    boolean isNotifying() throws NotReadyException;

    /**
     * Reads state from the characteristic.
     *
     * @return characteristic state
     * @throws NotReadyException if the bluetooth object is not ready
     */
    byte[] read() throws NotReadyException;

    /**
     * Writes state to the characteristic.
     * @param data a new characteristic state
     * @return true if the new state is written
     * @throws NotReadyException if the bluetooth object is not ready
     */
    boolean write(byte[] data) throws NotReadyException;

    /**
     * Register a new characteristic listener.
     * @param valueListener new characteristic listener
     */
    void addValueListener(ValueListener valueListener);

    /**
     * Removes a previously registered characteristic listener.
     * @param valueListener a previously registered characteristic listener
     */
    void removeValueListener(ValueListener valueListener);

    /**
     * Returns the date/time of last known received notification.
     * @return the date/time of last known received notification
     */
    Instant getLastNotified();

    boolean isAuthenticated();


    default <G extends CharacteristicGovernor, V> CompletableFuture<V> whenAuthenticated(Function<G, V> function) {
        return when(CharacteristicGovernor::isAuthenticated, function);
    }

    default <G extends CharacteristicGovernor> CompletableFuture<Void> whenAuthenticatedThanDo(Consumer<G> consumer) {
        return whenAuthenticated(g -> {
            consumer.accept((G) this);
            return null;
        });
    }

} public interface DeviceGovernor extends BluetoothGovernor {

    /**
     * Returns bluetooth class of the device.
     * 
     * @return bluetooth class
     * @throws NotReadyException if the bluetooth device is not ready
     */
    int getBluetoothClass() throws NotReadyException;

    /**
     * Checks whether the device supports BLE protocol.
     * 
     * @return true if the deice is a BLE device
     * @throws NotReadyException if the bluetooth device is not ready 
     */
    boolean isBleEnabled() throws NotReadyException;

    /**
     * Returns name of the device.
     * @return name of the adapter
     * @throws NotReadyException if the device is not ready
     */
    String getName() throws NotReadyException;

    /**
     *  Returns alias of the device.
     * @return alias of the device
     * @throws NotReadyException if the device is not ready
     */
    String getAlias() throws NotReadyException;

    /**
     * Sets alias for the device.
     * @param alias new alias
     */
    void setAlias(String alias) throws NotReadyException;

    /**
     * Returns display name of the device.
     * @return display name of the device
     * @throws NotReadyException if the device object is not ready
     */
    String getDisplayName() throws NotReadyException;

    /**
     * Checks whether the device is connected.
     * @return true if the device is connected, false otherwise
     * @throws NotReadyException if the device object is not ready
     */
    boolean isConnected() throws NotReadyException;

    /**
     * Returns device connection control status.
     * @return device connection control status
     */
    boolean getConnectionControl();

    /**
     * Sets device connection control status.
     * @param connected device connection control status
     */
    void setConnectionControl(boolean connected);

    /**
     * Checks whether the device is blocked.
     * @return true if the device is blocked, false otherwise
     * @throws NotReadyException if the device object is not ready
     */
    boolean isBlocked() throws NotReadyException;

    /**
     * Returns device blocked control status.
     *
     * @return device blocked control status
     */
    boolean getBlockedControl();

    /**
     * Sets device blocked control status.
     *
     * @param blocked a new blocked control status
     */
    void setBlockedControl(boolean blocked);

    /**
     * Checks whether the device is online.
     * A device is "online" if the device has shown its activity (see {@link BluetoothGovernor#getLastInteracted()})
     * within configured "online timeout" setting (see {@link #getOnlineTimeout()}).
     * @return true if online, false otherwise
     */
    boolean isOnline();

    /**
     * Returns the device online timeout in seconds (see {@link #isOnline()}).
     * @return online timeout in seconds
     */
    int getOnlineTimeout();

    /**
     * Sets the device online timeout in seconds (see {@link #isOnline()}).
     * @param onlineTimeout a new value for the device online timeout
     */
    void setOnlineTimeout(int onlineTimeout);

    /**
     * Returns device RSSI.
     * @return device RSSI
     * @throws NotReadyException if the device object is not ready
     */
    short getRSSI() throws NotReadyException;

    /**
     * Sets RSSI filter class. A new instance is created by using reflection and a default constructor.
     * Default implementation is Kalman filter
     * @param filter RSSI filter class
     */
    void setRssiFilter(Class<? extends Filter<Short>> filter);

    /**
     * Returns RSSI filter.
     * @return RSSI filter
     */
    Filter<Short> getRssiFilter();

    /**
     * Checks whether RSSI filtering is enabled.
     * @return true if enabled, false otherwise
     */
    boolean isRssiFilteringEnabled();

    /**
     * Enables/disables RSSI filtering (a filter must be set before).
     * @param enabled if true, disabled otherwise
     */
    void setRssiFilteringEnabled(boolean enabled);

    /**
     * Sets RSSI reporting rate (in milliseconds). RSSI is not reported more often than this value.
     * If is set to 0, then RSSI is reported unconditionally.
     * @param rate RSSI reporting rate
     */
    void setRssiReportingRate(long rate);

    /**
     * Returns RSSI reporting rate (in milliseconds). If RSSI equals to 0, then RSSI is reported unconditionally.
     * @return RSSI reporting rate
     */
    long getRssiReportingRate();

    /**
     * Returns actual (manufacturer defined) TX power of the device. Some bluetooth devices do not advertise
     * its TX power, in this case the returning value is 0.
     * <br>TX power is used in distance calculation ({@link #getEstimatedDistance()}).
     * @return actual TX power
     */
    short getTxPower();

    /**
     * Returns measured/estimated (user defined) TX power of the device that is measured 1 meter away from the adapter.
     * <br>TX power is used in distance calculation ({@link #getEstimatedDistance()}).
     * @return measured TX power
     */
    short getMeasuredTxPower();

    /**
     * Sets measured/estimated (user defined) TX power of the device that is measured 1 meter away from the adapter.
     * TX power is used in distance calculation ({@link #getEstimatedDistance()}).
     * <br>To measure TX power, step 1 meter away from the adapter and take note of RSSI value (this will be TX Power).
     * @param txPower TX power
     */
    void setMeasuredTxPower(short txPower);

    /**
     * Returns currently set the estimated (used defined) signal propagation exponent. It is used in distance
     * calculation ({@link #getEstimatedDistance()}). This factor is specific to the environment, i.e. how good or bad
     * the signal can penetrate through obstacles. Normally it ranges from 2.0 (outdoors, no obstacles)
     * to 4.0 (indoors, walls and furniture).
     * @return the signal propagation exponent
     */
    double getSignalPropagationExponent();

    /**
     * Sets the estimated (used defined) signal propagation exponent. It is used in distance
     * calculation ({@link #getEstimatedDistance()}). This factor is specific to the environment, i.e. how efficient
     * the signal passes through obstacles. Normally it ranges from 2.0 (outdoors, no obstacles)
     * to 4.0 (indoors, walls and furniture).
     * @param exponent the signal propagation exponent
     */
    void setSignalPropagationExponent(double exponent);

    /**
     * Returns estimated distance between this device and the adapter.
     * Either measured ({@link #setMeasuredTxPower(short)}) or actual ({@link #getTxPower()}) TX power must be
     * available for the estimation, otherwise the resulting value equals 0.
     * The calculation is based on the logarithmic function: d = 10 ^ ((TxPower - RSSI) / 10n)
     * where n ({@link #getSignalPropagationExponent()}) is the signal propagation exponent
     * that ranges from 2 to 4 (environment specific factor, e.g. 2 outdoors to 4 indoors)
     * @return estimated distance
     */
    double getEstimatedDistance();

    /**
     * Returns location of the device (closest adapter URL). If the governor represents a group of devices,
     * then the result depends on distance to closest adapter ({@link #getEstimatedDistance()}).
     * @return location of the device
     */
    URL getLocation();

    /**
     * Register a new Bluetooth Smart device listener.
     * @param listener a new Bluetooth Smart device listener
     */
    void addBluetoothSmartDeviceListener(BluetoothSmartDeviceListener listener);

    /**
     * Unregister a Bluetooth Smart device listener.
     * @param listener a previously registered listener
     */
    void removeBluetoothSmartDeviceListener(BluetoothSmartDeviceListener listener);

    /**
     * Registers a new Generic Bluetooth device listener.
     * @param listener a new Generic Bluetooth device listener
     */
    void addGenericBluetoothDeviceListener(GenericBluetoothDeviceListener listener);

    /**
     * Unregisters a Generic Bluetooth device listener.
     * @param listener a previously registered listener
     */
    void removeGenericBluetoothDeviceListener(GenericBluetoothDeviceListener listener);

    /**
     * Checks whether services have been resolved.
     * @return true if services are resolved, false otherwise
     */
    boolean isServicesResolved();

    /**
     * Returns a list of resolved services. Null is returned if services are not resolved yet.
     * @return a list of resolved services
     */
    List<GattService> getResolvedServices() throws NotReadyException;

    /**
     * Returns a map of services to their characteristics.
     *
     * @return a map of services to their characteristics
     * @throws NotReadyException if the device object is not ready
     */
    Map<URL, List<CharacteristicGovernor>> getServicesToCharacteristicsMap() throws NotReadyException;

    /**
     * Returns a list of characteristic URLs of the device.
     * @return a list of characteristic URLs of the device
     * @throws NotReadyException if the device object is not ready
     */
    List<URL> getCharacteristics() throws NotReadyException;

    /**
     * Returns a list of characteristic governors associated to the device.
     * @return a list of characteristic governors associated to the device
     * @throws NotReadyException if the device object is not ready
     */
    List<CharacteristicGovernor> getCharacteristicGovernors() throws NotReadyException;

    /**
     * Returns advertised manufacturer data. The key is manufacturer ID, the value is manufacturer data.
     * @return advertised manufacturer data
     */
    Map<Short, byte[]> getManufacturerData();

    /**
     * Returns advertised service data. The key is service UUID (16, 32 or 128 bit UUID), the value is service data.
     * @return advertised service data
     */
    Map<URL, byte[]> getServiceData();

    /**
     * Returns the date/time of last known advertised packet.
     * @return the date/time of last known advertised packet
     */
    Instant getLastAdvertised();

    void setAuthenticationProvider(AuthenticationProvider authenticationProvider);

    boolean isAuthenticated();

    /**
     * Returns a completable future that gets completed when services become resolved.
     * @param function a function that is invoked when services become resolved, the completable future is
     *                 completed with the result of this function
     * @param <G> bluetooth governor
     * @param <V> returned value
     * @return a completable future
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    default <G extends DeviceGovernor, V> CompletableFuture<V> whenServicesResolved(Function<G, V> function) {
        return when(DeviceGovernor::isServicesResolved, function);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    default <G extends DeviceGovernor, V> CompletableFuture<V> whenAuthenticated(Function<G, V> function) {
        return when(DeviceGovernor::isAuthenticated, function);
    }

    default <G extends DeviceGovernor> CompletableFuture<Void> doWhenAuthenticated(Consumer<G> consumer) {
        return whenAuthenticated(g -> {
            consumer.accept((G) this);
            return null;
        });
    }

} public interface Adapter extends BluetoothObject {

    String getName();

    String getAlias();
    void setAlias(String s);

    boolean isDiscovering();
    void enableDiscoveringNotifications(Notification<Boolean> notification);
    void disableDiscoveringNotifications();
    boolean startDiscovery();
    boolean stopDiscovery();

    boolean isPowered();
    void setPowered(boolean b);
    void enablePoweredNotifications(Notification<Boolean> notification);
    void disablePoweredNotifications();

    List<Device> getDevices();

} public interface BluetoothObjectFactory {


    /**
     * Returns an adapter by its URl. URL may not contain 'protocol' part.
     * @param url adapter URL
     * @return an adapter
     */
    Adapter getAdapter(URL url);

    /**
     * Returns a device by its URl. URL may not contain 'protocol' part.
     * @param url device URL
     * @return a device
     */
    Device getDevice(URL url);

    /**
     * Returns a characteristic by its URl. URL may not contain 'protocol' part.
     * @param url characteristic URL
     * @return a characteristic
     */
    Characteristic getCharacteristic(URL url);

    /**
     * Returns all discovered adapters by all registered transports.
     * @return all discovered adapters
     */
    Set<DiscoveredAdapter> getDiscoveredAdapters();

    /**
     * Returns all discovered devices by all registered transports.
     * @return all discovered devices
     */
    Set<DiscoveredDevice> getDiscoveredDevices();

    /**
     * Returns transport protocol name.
     * @return transport protocol name
     */
    String getProtocolName();

    /**
     * The bluetooth manager might call this method in order to pass some configuration variables.
     * @param config configuration
     */
    void configure(Map<String, Object> config);

    /**
     * Disposes and removes registered object from the transport.
     * @param url device to remove
     */
    void dispose(URL url);

} public interface Device extends BluetoothObject {

    int getBluetoothClass();

    boolean disconnect();

    boolean connect();

    String getName();

    String getAlias();

    void setAlias(String alias);

    boolean isBlocked();

    boolean isBleEnabled();

    void enableBlockedNotifications(Notification<Boolean> notification);

    void disableBlockedNotifications();

    void setBlocked(boolean blocked);

    short getRSSI();

    short getTxPower();

    void enableRSSINotifications(Notification<Short> notification);

    void disableRSSINotifications();

    boolean isConnected();

    void enableConnectedNotifications(Notification<Boolean> notification);

    void disableConnectedNotifications();

    boolean isServicesResolved();

    void enableServicesResolvedNotifications(Notification<Boolean> notification);

    void disableServicesResolvedNotifications();

    List<Service> getServices();

    Map<String, byte[]> getServiceData();

    Map<Short, byte[]> getManufacturerData();

    BluetoothAddressType getAddressType();

    void enableServiceDataNotifications(Notification<Map<String, byte[]>> notification);

    void disableServiceDataNotifications();

    void enableManufacturerDataNotifications(Notification<Map<Short, byte[]>> notification);

    void disableManufacturerDataNotifications();
} public interface Notification <T> {

    void notify(T value);

}
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.internal.util.reflection.Whitebox;
import org.mockito.runners.MockitoJUnitRunner;
import org.sputnikdev.bluetooth.URL;
import org.sputnikdev.bluetooth.manager.AdapterGovernor;
import org.sputnikdev.bluetooth.manager.AdapterListener;
import org.sputnikdev.bluetooth.manager.BluetoothGovernor;
import org.sputnikdev.bluetooth.manager.BluetoothObjectType;
import org.sputnikdev.bluetooth.manager.BluetoothObjectVisitor;
import org.sputnikdev.bluetooth.manager.CharacteristicGovernor;
import org.sputnikdev.bluetooth.manager.DeviceGovernor;
import org.sputnikdev.bluetooth.manager.transport.Adapter;
import org.sputnikdev.bluetooth.manager.transport.BluetoothObjectFactory;
import org.sputnikdev.bluetooth.manager.transport.Device;
import org.sputnikdev.bluetooth.manager.transport.Notification;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

        verifyNoMoreInteractions(adapter);
    }

    @Test
    public void testGetAlias() {
        assertEquals(ALIAS, governor.getAlias());

        verify(adapter, times(1)).getAlias();
        verify(governor).interact(eq("getAlias"), any(Function.class));

        verifyNoMoreInteractions(adapter);
    }

    @Test
    public void testSetAlias() {
        String newAlias = "new alias";

        governor.setAlias(newAlias);

        verify(adapter, times(1)).setAlias(newAlias);
        verify(governor).interact(eq("setAlias"), any(BiConsumer.class), eq(newAlias));

        verifyNoMoreInteractions(adapter);
    }

    @Test
    public void testGetName() {
        assertEquals(NAME, governor.getName());

        verify(adapter, times(1)).getName();
        verify(governor).interact(eq("getName"), any(Function.class));

        verifyNoMoreInteractions(adapter);
    }

    @Test
    public void testGetDisplayName() {
        when(adapter.getAlias()).thenReturn(ALIAS).thenReturn(null);
        when(adapter.getName()).thenReturn(NAME);

        assertEquals(ALIAS, governor.getDisplayName());
        verify(adapter, times(1)).getAlias();
        verify(governor, times(1)).interact(eq("getAlias"), any(Function.class));

        assertEquals(NAME, governor.getDisplayName());
        verify(adapter, times(2)).getAlias();
        verify(adapter, times(1)).getName();
        verify(governor, times(1)).interact(eq("getName"), any(Function.class));
        verify(governor, times(2)).interact(eq("getAlias"), any(Function.class));

        verifyNoMoreInteractions(adapter);
    }

    @Test
    public void testGetDevices() {
        List<URL> deviceURLs = new ArrayList<>(governor.getDevices());
        assertEquals(2, deviceURLs.size());
        Collections.sort(deviceURLs);
        assertEquals(DEVICE_1_URL, deviceURLs.get(0));
        assertEquals(DEVICE_2_URL, deviceURLs.get(1));

        verify(adapter, times(1)).getDevices();
        verifyNoMoreInteractions(adapter);
    }

    @Test
    public void testGetDeviceGovernors() {
        List<BluetoothGovernor> deviceGovernors = mock(List.class);
        when(bluetoothManager.getGovernors(any())).thenReturn(deviceGovernors);

        assertEquals(deviceGovernors, governor.getDeviceGovernors());

        verify(adapter).getDevices();
        verifyNoMoreInteractions(adapter);
    }

    @Test
    public void testToString() {
        when(adapter.getAlias()).thenReturn(ALIAS).thenReturn(null);
        assertEquals("[Adapter] " + URL + " [" + ALIAS + "]", governor.toString());
        assertEquals("[Adapter] " + URL + " [" + NAME + "]", governor.toString());
    }

    @Test
    public void testEquals() {
        URL url1 = new URL("/11:22:33:44:55:67");
        URL url2 = new URL("/11:22:33:44:55:66");

        assertFalse(url1.equals(url2));

        AdapterGovernorImpl gov1 = new AdapterGovernorImpl(bluetoothManager, url1);
        AdapterGovernorImpl gov2 = new AdapterGovernorImpl(bluetoothManager, url2);

        assertEquals(gov1, gov1);
        assertFalse(gov1.equals(new Object()));

        assertFalse(gov1.equals(gov2));

        gov2 = new AdapterGovernorImpl(bluetoothManager, url1);
        assertTrue(gov1.equals(gov2));
    }

    @Test
    public void testHashCode() {
        URL url1 = new URL("/11:22:33:44:55:67");
        URL url2 = new URL("/11:22:33:44:55:66");

        assertFalse(url1.hashCode() == url2.hashCode());

        AdapterGovernorImpl gov1 = new AdapterGovernorImpl(bluetoothManager, url1);
        AdapterGovernorImpl gov2 = new AdapterGovernorImpl(bluetoothManager, url2);
        assertFalse(gov1.hashCode() == gov2.hashCode());

        gov2 = new AdapterGovernorImpl(bluetoothManager, url1);
        assertTrue(gov1.hashCode() == gov2.hashCode());
    }

    @Test
    public void testGetType() {
        assertEquals(BluetoothObjectType.ADAPTER, governor.getType());