Consolidated ExtensionTest objects and minor refactoring

This commit is contained in:
Abbas Abou Daya 2019-01-03 05:46:09 +02:00
parent ee78c70164
commit 2f05976d0a
4 changed files with 89 additions and 93 deletions

View File

@ -16,17 +16,17 @@ import org.telegram.abilitybots.api.util.AbilityExtension;
import org.telegram.abilitybots.api.util.AbilityUtils; import org.telegram.abilitybots.api.util.AbilityUtils;
import org.telegram.abilitybots.api.util.Pair; import org.telegram.abilitybots.api.util.Pair;
import org.telegram.abilitybots.api.util.Trio; import org.telegram.abilitybots.api.util.Trio;
import org.telegram.telegrambots.bots.DefaultAbsSender;
import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.methods.GetFile; import org.telegram.telegrambots.meta.api.methods.GetFile;
import org.telegram.telegrambots.meta.api.methods.groupadministration.GetChatAdministrators; import org.telegram.telegrambots.meta.api.methods.groupadministration.GetChatAdministrators;
import org.telegram.telegrambots.meta.api.methods.send.SendDocument; import org.telegram.telegrambots.meta.api.methods.send.SendDocument;
import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User; import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.bots.DefaultAbsSender;
import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import org.telegram.telegrambots.meta.logging.BotLogger; import org.telegram.telegrambots.meta.logging.BotLogger;
import org.telegram.telegrambots.meta.api.objects.Update;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -614,30 +614,37 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
*/ */
private void registerAbilities() { private void registerAbilities() {
try { try {
List<AbilityExtension>extensions = stream(this.getClass().getMethods()) // Collect all classes that implement AbilityExtension declared in the bot
List<AbilityExtension> extensions = stream(getClass().getMethods())
.filter(checkReturnType(AbilityExtension.class)) .filter(checkReturnType(AbilityExtension.class))
.map(this.returnExtension(this)) .map(returnExtension(this))
.collect(Collectors.toList()); .collect(Collectors.toList());
// Add the bot itself as it is an AbilityExtension
extensions.add(this); extensions.add(this);
// Extract all abilities from every single extension instance
abilities = extensions.stream() abilities = extensions.stream()
.flatMap(ext -> stream(ext.getClass().getMethods()) .flatMap(ext -> stream(ext.getClass().getMethods())
.filter(checkReturnType(Ability.class)) .filter(checkReturnType(Ability.class))
.map(this.returnAbility(ext))) .map(returnAbility(ext)))
// Abilities are immutable, build it respectively
.collect(ImmutableMap::<String, Ability>builder, .collect(ImmutableMap::<String, Ability>builder,
(b, a) -> b.put(a.name(), a), (b, a) -> b.put(a.name(), a),
(b1, b2) -> b1.putAll(b2.build())) (b1, b2) -> b1.putAll(b2.build()))
.build(); .build();
// Extract all replies from every single extension instance
Stream<Reply> extensionReplies = extensions.stream() Stream<Reply> extensionReplies = extensions.stream()
.flatMap(ext -> stream(ext.getClass().getMethods()) .flatMap(ext -> stream(ext.getClass().getMethods())
.filter(checkReturnType(Reply.class)) .filter(checkReturnType(Reply.class))
.map(this.returnReply(ext))); .map(returnReply(ext)));
// Replies can be standalone or attached to abilities, fetch those too
Stream<Reply> abilityReplies = abilities.values().stream() Stream<Reply> abilityReplies = abilities.values().stream()
.flatMap(ability -> ability.replies().stream()); .flatMap(ability -> ability.replies().stream());
// Now create the replies registry (list)
replies = Stream.concat(abilityReplies, extensionReplies).collect( replies = Stream.concat(abilityReplies, extensionReplies).collect(
ImmutableList::<Reply>builder, ImmutableList::<Reply>builder,
Builder::add, Builder::add,
@ -647,16 +654,18 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
BotLogger.error(TAG, "Duplicate names found while registering abilities. Make sure that the abilities declared don't clash with the reserved ones.", e); BotLogger.error(TAG, "Duplicate names found while registering abilities. Make sure that the abilities declared don't clash with the reserved ones.", e);
throw propagate(e); throw propagate(e);
} }
} }
/**
* @param clazz the type to be tested
* @return a predicate testing the return type of the method corresponding to the class parameter
*/
private Predicate<Method> checkReturnType(Class<?> clazz) { private Predicate<Method> checkReturnType(Class<?> clazz) {
return method -> clazz.isAssignableFrom(method.getReturnType()); return method -> clazz.isAssignableFrom(method.getReturnType());
} }
/** /**
* Invokes the method curried and retrieves its return {@link Reply}. * Invokes the method and retrieves its return {@link Reply}.
* *
* @param obj an bot or extension that this method is invoked with * @param obj an bot or extension that this method is invoked with
* @return a {@link Function} which returns the {@link Reply} returned by the given method * @return a {@link Function} which returns the {@link Reply} returned by the given method
@ -666,13 +675,14 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
try { try {
return (AbilityExtension) method.invoke(obj); return (AbilityExtension) method.invoke(obj);
} catch (IllegalAccessException | InvocationTargetException e) { } catch (IllegalAccessException | InvocationTargetException e) {
BotLogger.error("Could not add ability", TAG, e); BotLogger.error("Could not add ability extension", TAG, e);
throw propagate(e); throw propagate(e);
} }
}; };
} }
/** /**
* Invokes the method curried and retrieves its return {@link Ability}. * Invokes the method and retrieves its return {@link Ability}.
* *
* @param obj an bot or extension that this method is invoked with * @param obj an bot or extension that this method is invoked with
* @return a {@link Function} which returns the {@link Ability} returned by the given method * @return a {@link Function} which returns the {@link Ability} returned by the given method
@ -689,7 +699,7 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
} }
/** /**
* Invokes the method curried and retrieves its return {@link Reply}. * Invokes the method and retrieves its return {@link Reply}.
* *
* @param obj an bot or extension that this method is invoked with * @param obj an bot or extension that this method is invoked with
* @return a {@link Function} which returns the {@link Reply} returned by the given method * @return a {@link Function} which returns the {@link Reply} returned by the given method
@ -699,7 +709,7 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
try { try {
return (Reply) method.invoke(obj); return (Reply) method.invoke(obj);
} catch (IllegalAccessException | InvocationTargetException e) { } catch (IllegalAccessException | InvocationTargetException e) {
BotLogger.error("Could not add ability", TAG, e); BotLogger.error("Could not add reply", TAG, e);
throw propagate(e); throw propagate(e);
} }
}; };

View File

@ -1,26 +0,0 @@
package org.telegram.abilitybots.api.bot;
import org.telegram.abilitybots.api.objects.Ability;
import org.telegram.abilitybots.api.util.AbilityExtension;
import static org.telegram.abilitybots.api.objects.Locality.ALL;
import static org.telegram.abilitybots.api.objects.Privacy.PUBLIC;
public class AbilityBotExtension implements AbilityExtension {
private String name;
public AbilityBotExtension(String name) {
this.name = name;
}
public Ability abc() {
return Ability.builder()
.name(this.name + "0abc")
.info("Test ability")
.locality(ALL)
.privacy(PUBLIC)
.action(messageContext -> {
})
.build();
}
}

View File

@ -4,8 +4,14 @@ import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.telegram.abilitybots.api.objects.Ability; import org.telegram.abilitybots.api.objects.Ability;
import org.telegram.abilitybots.api.util.AbilityExtension;
import java.io.IOException;
import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.assertTrue;
import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance;
import static org.telegram.abilitybots.api.objects.Locality.ALL;
import static org.telegram.abilitybots.api.objects.Privacy.PUBLIC;
public class ExtensionTest { public class ExtensionTest {
private ExtensionUsingBot bot; private ExtensionUsingBot bot;
@ -15,12 +21,6 @@ public class ExtensionTest {
bot = new ExtensionUsingBot(); bot = new ExtensionUsingBot();
} }
@After
public void teardown() {
bot = null;
}
@Test @Test
public void methodReturningAbilities() { public void methodReturningAbilities() {
assertTrue("Failed to find Ability in directly declared in root extension/bot", hasAbilityNamed("direct")); assertTrue("Failed to find Ability in directly declared in root extension/bot", hasAbilityNamed("direct"));
@ -28,9 +28,62 @@ public class ExtensionTest {
assertTrue("Failed to find Ability in directly declared in extension returned by method returning the AbilityExtension subclass", hasAbilityNamed("returningSubClass0abc")); assertTrue("Failed to find Ability in directly declared in extension returned by method returning the AbilityExtension subclass", hasAbilityNamed("returningSubClass0abc"));
} }
@After
public void tearDown() throws IOException {
bot.db.clear();
bot.db.close();
}
private boolean hasAbilityNamed(String name) { private boolean hasAbilityNamed(String name) {
return bot.abilities().values().stream().map(Ability::name).anyMatch(name::equals); return bot.abilities().values().stream().map(Ability::name).anyMatch(name::equals);
} }
public static class ExtensionUsingBot extends AbilityBot {
public ExtensionUsingBot() {
super("", "", offlineInstance("testing"));
}
@Override
public int creatorId() {
return 0;
}
public AbilityBotExtension methodReturningExtensionSubClass() {
return new AbilityBotExtension("returningSubClass");
}
public AbilityExtension methodReturningExtensionSuperClass() {
return new AbilityBotExtension("returningSuperClass");
}
public Ability methodReturningAbility() {
return Ability.builder()
.name("direct")
.info("Test ability")
.locality(ALL)
.privacy(PUBLIC)
.action(messageContext -> {
})
.build();
}
}
public static class AbilityBotExtension implements AbilityExtension {
private String name;
public AbilityBotExtension(String name) {
this.name = name;
}
public Ability abc() {
return Ability.builder()
.name(name + "0abc")
.info("Test ability")
.locality(ALL)
.privacy(PUBLIC)
.action(ctx -> {
})
.build();
}
}
} }

View File

@ -1,41 +0,0 @@
package org.telegram.abilitybots.api.bot;
import org.telegram.abilitybots.api.objects.Ability;
import org.telegram.abilitybots.api.util.AbilityExtension;
import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance;
import static org.telegram.abilitybots.api.objects.Locality.ALL;
import static org.telegram.abilitybots.api.objects.Privacy.PUBLIC;
public class ExtensionUsingBot extends AbilityBot {
public ExtensionUsingBot() {
super("", "", offlineInstance("testing"));
}
@Override
public int creatorId() {
return 0;
}
public AbilityBotExtension methodReturningExtensionSubClass() {
return new AbilityBotExtension("returningSubClass");
}
public AbilityExtension methodReturningExtensionSuperClass() {
return new AbilityBotExtension("returningSuperClass");
}
public Ability methodReturningAbility() {
return Ability.builder()
.name("direct")
.info("Test ability")
.locality(ALL)
.privacy(PUBLIC)
.action(messageContext -> {
})
.build();
}
}