Skip to content

Commit

Permalink
Merge pull request #374 from greboid/dev4
Browse files Browse the repository at this point in the history
Some more work on the channel who plugin.
  • Loading branch information
csmith committed Jan 18, 2015
2 parents 460e0d5 + 1758011 commit 08e47d3
Show file tree
Hide file tree
Showing 4 changed files with 308 additions and 3 deletions.
12 changes: 12 additions & 0 deletions channelwho/src/com/dmdirc/addons/channelwho/ChannelWhoModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@
package com.dmdirc.addons.channelwho;

import com.dmdirc.ClientModule;
import com.dmdirc.DMDircMBassador;
import com.dmdirc.plugins.PluginDomain;
import com.dmdirc.util.LoggingScheduledExecutorService;

import java.util.concurrent.ScheduledExecutorService;

import javax.inject.Named;

import dagger.Module;
import dagger.Provides;
Expand All @@ -44,4 +50,10 @@ public ChannelWhoModule(final String domain) {
public String getSettingsDomain() {
return domain;
}

@Provides
@Named("channelwho")
public ScheduledExecutorService getExecutorService(final DMDircMBassador eventBus) {
return new LoggingScheduledExecutorService(1, eventBus, "channelwho");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,113 @@

package com.dmdirc.addons.channelwho;

import com.dmdirc.config.ConfigBinder;
import com.dmdirc.config.ConfigBinding;
import com.dmdirc.events.ChannelUserAwayEvent;
import com.dmdirc.events.DisplayProperty;
import com.dmdirc.events.ServerNumericEvent;
import com.dmdirc.interfaces.Connection;
import com.dmdirc.interfaces.ConnectionManager;
import com.dmdirc.interfaces.GroupChat;
import com.dmdirc.interfaces.GroupChatUser;
import com.dmdirc.interfaces.config.AggregateConfigProvider;

import com.google.common.annotations.VisibleForTesting;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import net.engio.mbassy.listener.Handler;

/**
* Responsible for managing timers and settings required to who any {@link GroupChat}s on a
* {@link Connection} as specified by the user.
*/
public class ConnectionHandler {

private final Map<String, GroupChatUser> users;
private final Connection connection;
private final String domain;
private final ScheduledExecutorService executorService;
private final ConnectionManager connectionManager;
private final ConfigBinder configBinder;
private int whoInterval;
private ScheduledFuture<?> future;

public ConnectionHandler(final Connection connection, final String domain) {
public ConnectionHandler(
final AggregateConfigProvider config,
final ScheduledExecutorService executorService,
final ConnectionManager connectionManager, final String domain,
final Connection connection) {
this.connection = connection;
this.domain = domain;
this.executorService = executorService;
this.connectionManager = connectionManager;
configBinder = config.getBinder().withDefaultDomain(domain);
users = new HashMap<>();
}

public void load() {
configBinder.bind(this, ConnectionHandler.class);
connection.getWindowModel().getEventBus().subscribe(this);
}

public void unload() {
configBinder.unbind(this);
executorService.shutdown();
connection.getWindowModel().getEventBus().unsubscribe(this);
if (future != null) {
future.cancel(true);
}
}

@VisibleForTesting
void checkWho() {
connectionManager.getConnections().forEach(connection ->
connection.getGroupChatManager().getChannels().forEach(channel -> {
if (channel.getWindowModel().getConfigManager().getOptionBool(domain, "sendWho")) {
channel.requestUsersInfo();
}
}));
}

@VisibleForTesting
@ConfigBinding(key="whoInterval")
void handleWhoInterval(final int value) {
whoInterval = value;
if (future != null) {
future.cancel(true);
}
future = executorService.schedule(this::checkWho, whoInterval, TimeUnit.MILLISECONDS);
}

@VisibleForTesting
@Handler
void handleAwayEvent(final ChannelUserAwayEvent event) {
if (!event.getReason().isPresent()) {
event.setDisplayProperty(DisplayProperty.DO_NOT_DISPLAY, true);
users.put(event.getUser().getNickname(), event.getUser());
event.getChannel().getConnection()
.ifPresent(c -> c.requestUserInfo(event.getUser().getUser()));
}
}

@VisibleForTesting
@Handler
void handleServerNumericEvent(final ServerNumericEvent event) {
if (event.getNumeric() == 301) {
final String nickname = event.getArgs()[4];
final String reason = event.getArgs()[5];
final GroupChatUser user = users.remove(nickname);
if (user != null) {
connection.getWindowModel().getEventBus().publishAsync(
new ChannelUserAwayEvent(user.getGroupChat(), user,
Optional.ofNullable(reason)));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,41 @@

package com.dmdirc.addons.channelwho;

import com.dmdirc.ClientModule.GlobalConfig;
import com.dmdirc.interfaces.Connection;
import com.dmdirc.interfaces.ConnectionManager;
import com.dmdirc.interfaces.config.AggregateConfigProvider;
import com.dmdirc.plugins.PluginDomain;

import java.util.concurrent.ScheduledExecutorService;

import javax.inject.Inject;
import javax.inject.Named;

/**
* Factory for creating {@link ConnectionHandler}s.
*/
public class ConnectionHandlerFactory {

private final AggregateConfigProvider config;
private final ScheduledExecutorService executorService;
private final ConnectionManager connectionManager;
private final String domain;

@Inject
public ConnectionHandlerFactory(@PluginDomain(ChannelWhoPlugin.class) final String domain) {
public ConnectionHandlerFactory(@GlobalConfig final AggregateConfigProvider config,
@Named("channelwho") final ScheduledExecutorService executorService,
final ConnectionManager connectionManager,
@PluginDomain(ChannelWhoPlugin.class) final String domain) {
this.config = config;
this.executorService = executorService;
this.connectionManager = connectionManager;
this.domain = domain;
}

public ConnectionHandler get(final Connection connection) {
final ConnectionHandler handler = new ConnectionHandler(connection, domain);
final ConnectionHandler handler = new ConnectionHandler(config, executorService,
connectionManager, domain, connection);
handler.load();
return handler;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright (c) 2006-2015 DMDirc Developers
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package com.dmdirc.addons.channelwho;

import com.dmdirc.DMDircMBassador;
import com.dmdirc.config.ConfigBinder;
import com.dmdirc.events.ChannelUserAwayEvent;
import com.dmdirc.events.DisplayProperty;
import com.dmdirc.events.ServerNumericEvent;
import com.dmdirc.interfaces.Connection;
import com.dmdirc.interfaces.ConnectionManager;
import com.dmdirc.interfaces.GroupChat;
import com.dmdirc.interfaces.GroupChatManager;
import com.dmdirc.interfaces.GroupChatUser;
import com.dmdirc.interfaces.User;
import com.dmdirc.interfaces.WindowModel;
import com.dmdirc.interfaces.config.AggregateConfigProvider;

import com.google.common.collect.Lists;

import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class ConnectionHandlerTest {

@Mock private AggregateConfigProvider config;
@Mock private ConfigBinder configBinder;
@Mock private WindowModel windowModel;
@Mock private DMDircMBassador eventBus;
@Mock private ScheduledExecutorService scheduledExecutorService;
@Mock private ScheduledFuture scheduledFuture;
@Mock private ConnectionManager connectionManager;
@Mock private Connection connection;
@Mock private GroupChat groupChat;
@Mock private GroupChatUser groupChatUser;
@Mock private User user;
@Mock private GroupChatManager groupChatManager;
@Mock private ServerNumericEvent serverNumericEvent;
@Mock private ChannelUserAwayEvent channelUserAwayEvent;
@Captor private ArgumentCaptor<ChannelUserAwayEvent> eventArgumentCaptor;
private ConnectionHandler instance;

@Before
public void setUp() throws Exception {
when(scheduledExecutorService.schedule(any(Runnable.class), anyLong(), any()))
.thenReturn(scheduledFuture);
when(windowModel.getEventBus()).thenReturn(eventBus);
when(connection.getWindowModel()).thenReturn(windowModel);
when(config.getBinder()).thenReturn(configBinder);
when(connectionManager.getConnections()).thenReturn(Lists.newArrayList(connection));
when(connection.getGroupChatManager()).thenReturn(groupChatManager);
when(groupChatManager.getChannels()).thenReturn(Lists.newArrayList(groupChat));
when(groupChat.getWindowModel()).thenReturn(windowModel);
when(groupChat.getConnection()).thenReturn(Optional.of(connection));
when(configBinder.withDefaultDomain("domain")).thenReturn(configBinder);
when(windowModel.getConfigManager()).thenReturn(config);
when(groupChatUser.getNickname()).thenReturn("nickname");
when(channelUserAwayEvent.getUser()).thenReturn(groupChatUser);
when(channelUserAwayEvent.getChannel()).thenReturn(groupChat);
when(groupChatUser.getUser()).thenReturn(user);
instance = new ConnectionHandler(config, scheduledExecutorService, connectionManager,
"domain", connection);
instance.handleWhoInterval(5);
}

@Test
public void testLoad() throws Exception {
instance.load();
verify(configBinder).bind(instance, ConnectionHandler.class);
verify(eventBus).subscribe(instance);
verify(scheduledExecutorService).schedule(any(Runnable.class), eq(5l),
eq(TimeUnit.MILLISECONDS));
}

@Test
public void testUnload() throws Exception {
instance.unload();
verify(configBinder).unbind(instance);
verify(scheduledExecutorService).shutdown();
verify(eventBus).unsubscribe(instance);
}

@Test
public void testHandleWhoInterval() throws Exception {
instance.handleWhoInterval(10);
verify(scheduledFuture).cancel(true);
verify(scheduledExecutorService).schedule(any(Runnable.class), eq(5l),
eq(TimeUnit.MILLISECONDS));
verify(scheduledExecutorService).schedule(any(Runnable.class), eq(10l),
eq(TimeUnit.MILLISECONDS));
}

@Test
public void testCheckWho_True() throws Exception {
when(config.getOptionBool("domain", "sendWho")).thenReturn(true);
instance.checkWho();
verify(config).getOptionBool("domain", "sendWho");
verify(groupChat).requestUsersInfo();
}

@Test
public void testCheckWho_False() throws Exception {
when(config.getOptionBool("domain", "sendWho")).thenReturn(false);
instance.checkWho();
verify(config).getOptionBool("domain", "sendWho");
verify(groupChat, never()).requestUsersInfo();
}

@Test
public void testHandleAwayEvent_WithReason() throws Exception {
when(channelUserAwayEvent.getReason()).thenReturn(Optional.ofNullable("reason"));
instance.load();
instance.handleAwayEvent(channelUserAwayEvent);
verify(channelUserAwayEvent, never())
.setDisplayProperty(DisplayProperty.DO_NOT_DISPLAY, true);
verify(connection, never()).requestUserInfo(any());
}

@Test
public void testHandleAwayEvent_WithoutReason() throws Exception {
when(channelUserAwayEvent.getReason()).thenReturn(Optional.empty());
instance.load();
instance.handleAwayEvent(channelUserAwayEvent);
verify(channelUserAwayEvent).setDisplayProperty(DisplayProperty.DO_NOT_DISPLAY, true);
verify(connection).requestUserInfo(any());
}

@Test
public void testHandleServerNumericEvent_301() throws Exception {
when(serverNumericEvent.getNumeric()).thenReturn(301);
when(serverNumericEvent.getArgs()).thenReturn(
new String[]{"", "", "", "", "nickname", "reason"});
instance.load();
when(channelUserAwayEvent.getReason()).thenReturn(Optional.empty());
when(groupChatUser.getGroupChat()).thenReturn(groupChat);
instance.handleAwayEvent(channelUserAwayEvent);
instance.handleServerNumericEvent(serverNumericEvent);
verify(eventBus).publishAsync(eventArgumentCaptor.capture());
assertEquals("nickname", eventArgumentCaptor.getValue().getUser().getNickname());
assertEquals("reason", eventArgumentCaptor.getValue().getReason().get());
}

@Test
public void testHandleServerNumericEvent_101() throws Exception {
when(serverNumericEvent.getNumeric()).thenReturn(101);
when(channelUserAwayEvent.getReason()).thenReturn(Optional.empty());
instance.load();
instance.handleAwayEvent(channelUserAwayEvent);
instance.handleServerNumericEvent(serverNumericEvent);
verify(serverNumericEvent, never()).getArgs();
verify(eventBus, never()).publishAsync(any());
}
}

0 comments on commit 08e47d3

Please sign in to comment.