diff --git a/ContentGrabbingJi-core/src/test/java/net/lamgc/cgj/bot/framework/base/BasicBotCodeTest.java b/ContentGrabbingJi-core/src/test/java/net/lamgc/cgj/bot/framework/base/BasicBotCodeTest.java
new file mode 100644
index 0000000..f7dac45
--- /dev/null
+++ b/ContentGrabbingJi-core/src/test/java/net/lamgc/cgj/bot/framework/base/BasicBotCodeTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 LamGC
+ *
+ * ContentGrabbingJi is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * ContentGrabbingJi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package net.lamgc.cgj.bot.framework.base;
+
+import net.lamgc.cgj.bot.framework.Platform;
+import net.lamgc.cgj.bot.framework.message.AbstractBotCode;
+import net.lamgc.cgj.bot.framework.message.BotCode;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @see BasicBotCode
+ */
+public class BasicBotCodeTest {
+
+ @Test
+ public void getPlatform() {
+ BotCode botCode = new BasicBotCode("test");
+
+ Assert.assertEquals("ContentGrabbingJi", botCode.getPlatform().getPlatformName());
+ Assert.assertEquals("CGJ", botCode.getPlatform().getPlatformIdentify());
+ }
+
+ @Test
+ public void contentToStringWithoutParameter() {
+ BotCode botCode = new BasicBotCode("test");
+
+ Assert.assertEquals("[test]", botCode.contentToString());
+ }
+
+ @Test
+ public void contentToStringWithParameter() {
+ final Map argumentsMap = new HashMap<>();
+ argumentsMap.put("arg1", "value1");
+ argumentsMap.put("arg2", "Hello World.");
+ argumentsMap.put("arg3", "测试");
+
+ BotCode botCode = new BasicBotCode("test", argumentsMap);
+ Assert.assertEquals("[test:arg3=%E6%B5%8B%E8%AF%95&arg2=Hello+World.&arg1=value1]", botCode.contentToString());
+ }
+
+ @Test
+ public void createInstanceByBotCode() {
+ class TestBotCode extends AbstractBotCode {
+
+ public TestBotCode(String functionName, Map functionProperties) {
+ super(functionName, functionProperties);
+ }
+
+ @Override
+ public Platform getPlatform() {
+ return null;
+ }
+
+ @Override
+ public String contentToString() {
+ return null;
+ }
+
+ }
+ final Map argumentsMap = new HashMap<>();
+ argumentsMap.put("arg1", "value1");
+ argumentsMap.put("arg2", "Hello World.");
+ argumentsMap.put("arg3", "测试");
+
+ BotCode expectBotCode = new TestBotCode("function", argumentsMap);
+ BotCode botCode = new BasicBotCode(expectBotCode);
+
+ Assert.assertEquals(expectBotCode.getFunctionName(), botCode.getFunctionName());
+ Assert.assertTrue(expectBotCode.getPropertiesKeys().containsAll(botCode.getPropertiesKeys()));
+
+ for (String key : expectBotCode.getPropertiesKeys()) {
+ Assert.assertEquals(expectBotCode.getProperty(key), botCode.getProperty(key));
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/ContentGrabbingJi-framework-api/src/test/java/net/lamgc/cgj/bot/framework/message/AbstractBotCodeTest.java b/ContentGrabbingJi-framework-api/src/test/java/net/lamgc/cgj/bot/framework/message/AbstractBotCodeTest.java
new file mode 100644
index 0000000..ce827dc
--- /dev/null
+++ b/ContentGrabbingJi-framework-api/src/test/java/net/lamgc/cgj/bot/framework/message/AbstractBotCodeTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2020 LamGC
+ *
+ * ContentGrabbingJi is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * ContentGrabbingJi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package net.lamgc.cgj.bot.framework.message;
+
+import net.lamgc.cgj.bot.framework.Platform;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * @see AbstractBotCode
+ */
+public class AbstractBotCodeTest {
+
+ @Test
+ public void createBotCodeWithoutParameterTest() {
+ final String functionName = "function";
+ BotCode botCode = new TestBotCode(functionName);
+ Assert.assertEquals(functionName, botCode.getFunctionName());
+ Assert.assertNotNull(botCode.getPropertiesKeys());
+ Assert.assertEquals(0, botCode.getPropertiesKeys().size());
+
+ botCode = new TestBotCode(functionName, new HashMap<>());
+ Assert.assertEquals(functionName, botCode.getFunctionName());
+ Assert.assertNotNull(botCode.getPropertiesKeys());
+ Assert.assertEquals(0, botCode.getPropertiesKeys().size());
+ }
+
+ @Test
+ public void createBotCodeWithParameterTest() {
+ final String functionName = "function";
+ final Map argumentsMap = new HashMap<>();
+ argumentsMap.put("arg1", "value1");
+ argumentsMap.put("arg2", "value2");
+
+ BotCode botCode = new TestBotCode(functionName, argumentsMap);
+ Assert.assertEquals(functionName, botCode.getFunctionName());
+ Assert.assertTrue(argumentsMap.keySet().containsAll(botCode.getPropertiesKeys()));
+
+ for (String key : argumentsMap.keySet()) {
+ Assert.assertEquals(argumentsMap.get(key), botCode.getProperty(key));
+ }
+
+ }
+
+ @Test
+ public void emptyOrNullFunctionNameTest() {
+ Assert.assertThrows(IllegalArgumentException.class, () -> new TestBotCode((String) null));
+ Assert.assertThrows(IllegalArgumentException.class, () -> new TestBotCode(""));
+ }
+
+ @Test
+ public void createByBotCodeTest() {
+ final String functionName = "function";
+ final Map argumentsMap = new HashMap<>();
+ argumentsMap.put("arg1", "value1");
+ argumentsMap.put("arg2", "value2");
+
+ BotCode botCode = new TestBotCode(functionName, argumentsMap);
+
+ BotCode newBotCode = new TestBotCode(botCode);
+ Assert.assertEquals(botCode.getFunctionName(), newBotCode.getFunctionName());
+ Assert.assertEquals(botCode.getPropertiesKeys().size(), newBotCode.getPropertiesKeys().size());
+ Assert.assertTrue(botCode.getPropertiesKeys().containsAll(newBotCode.getPropertiesKeys()));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void propertiesKeysUnmodifiedTest() {
+ final String functionName = "function";
+ final Map argumentsMap = new HashMap<>();
+ argumentsMap.put("arg1", "value1");
+ argumentsMap.put("arg2", "value2");
+
+ BotCode botCode = new TestBotCode(functionName, argumentsMap);
+
+ botCode.getPropertiesKeys().clear();
+ }
+
+ @Test
+ public void functionNameChangeTest() {
+ final String newFunctionName = "functionB";
+ BotCode botCode = new TestBotCode("functionA");
+ botCode.setFunctionName(newFunctionName);
+ Assert.assertEquals(newFunctionName, botCode.getFunctionName());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void setPropertyTest() throws NoSuchFieldException, IllegalAccessException {
+ BotCode botCode = new TestBotCode("function");
+ final String key = "arg";
+ final String value = "value";
+ Assert.assertThrows(NullPointerException.class, () -> botCode.setProperty(null, "value"));
+ botCode.setProperty(key, value);
+ Assert.assertEquals(value, botCode.getProperty(key));
+
+ botCode.setProperty(key, null);
+ Assert.assertNull(botCode.getProperty(key));
+ Field propertiesField = AbstractBotCode.class.getDeclaredField("functionProperties");
+ propertiesField.setAccessible(true);
+ Hashtable properties = (Hashtable) propertiesField.get(botCode);
+ Assert.assertFalse(properties.containsKey(key));
+ }
+
+ @Test
+ public void toStringTest() {
+ BotCode botCode = new TestBotCode("function");
+ String objectStr = botCode.getClass().getSimpleName() + '@' + Integer.toHexString(botCode.hashCode());
+ Assert.assertEquals(objectStr + "{Platform=null, functionName='function', " +
+ "functionProperties={Hashtable}}", botCode.toString());
+
+ botCode.setProperty("arg1", "value1");
+ botCode.setProperty("arg2", "测试");
+ botCode.setProperty("arg3", "Hello World.");
+ Assert.assertEquals(objectStr + "{Platform=null, functionName='function', " +
+ "functionProperties={Hashtable{\"arg1\"='value1', \"arg2\"='测试', \"arg3\"='Hello World.'}}",
+ botCode.toString());
+ }
+
+ private final static class TestBotCode extends AbstractBotCode {
+
+ public TestBotCode(String functionName) {
+ super(functionName);
+ }
+
+ public TestBotCode(BotCode botCode) {
+ super(botCode);
+ }
+
+ public TestBotCode(String functionName, Map functionProperties) {
+ super(functionName, functionProperties);
+ }
+
+ @Override
+ public Platform getPlatform() {
+ return null;
+ }
+
+ @Override
+ public String contentToString() {
+ return null;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/ContentGrabbingJi-framework-api/src/test/java/net/lamgc/cgj/bot/framework/message/MessageChainTest.java b/ContentGrabbingJi-framework-api/src/test/java/net/lamgc/cgj/bot/framework/message/MessageChainTest.java
new file mode 100644
index 0000000..690a1ed
--- /dev/null
+++ b/ContentGrabbingJi-framework-api/src/test/java/net/lamgc/cgj/bot/framework/message/MessageChainTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 LamGC
+ *
+ * ContentGrabbingJi is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * ContentGrabbingJi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package net.lamgc.cgj.bot.framework.message;
+
+import net.lamgc.cgj.bot.framework.Platform;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+/**
+ * @see MessageChain
+ */
+public class MessageChainTest {
+
+ @Test
+ public void createEmptyChainTest() {
+ MessageChain chain = new MessageChain();
+
+ Assert.assertEquals(0, chain.size());
+ Assert.assertTrue(chain.isEmpty());
+ }
+
+ @Test
+ public void createNoEmptyChainTest() {
+ String[] expectContents = new String[] {"This", " is ", "a simple", " message"};
+ final String[] contents = Arrays.copyOf(expectContents, expectContents.length + 3);
+ contents[expectContents.length] = "";
+ contents[expectContents.length + 1] = null;
+ contents[expectContents.length + 2] = ".";
+
+ MessageChain chain = new MessageChain(contents);
+
+ expectContents = Arrays.copyOf(expectContents, expectContents.length + 1);
+ expectContents[expectContents.length - 1] = ".";
+
+ final StringBuilder expectContentsString = new StringBuilder();
+ for (String content : expectContents) {
+ expectContentsString.append(content);
+ }
+ Assert.assertEquals(expectContents.length, chain.size());
+ Assert.assertFalse(chain.isEmpty());
+ Assert.assertEquals(expectContentsString.toString(), chain.contentToString());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void plusSelfTest() {
+ MessageChain chain = new MessageChain("test");
+ chain.plus(chain);
+ }
+
+ @Test
+ public void clearTest() {
+ MessageChain chain = new MessageChain("Test", "123");
+ Assert.assertFalse(chain.isEmpty());
+ chain.clear();
+ Assert.assertTrue(chain.isEmpty());
+ }
+
+ @Test
+ public void plusBotCodeTest() {
+ MessageChain chain = new MessageChain("BotCode: ");
+ chain.plus(new AbstractBotCode("test") {
+ @Override
+ public Platform getPlatform() {
+ return null;
+ }
+
+ @Override
+ public String contentToString() {
+ return "[test:arg=value]";
+ }
+ });
+ chain.plus(".");
+ Assert.assertEquals("BotCode: [test:arg=value].", chain.contentToString());
+ }
+
+ @Test
+ public void deleteTest() {
+ final String[] contents = new String[] {"This", " is ", "a ", "simple", " message."};
+ MessageChain chain = new MessageChain(contents);
+
+ Assert.assertEquals("a ", chain.delete(2).contentToString());
+ Assert.assertEquals(contents.length - 1, chain.size());
+ Assert.assertEquals("This is simple message.", chain.contentToString());
+ }
+
+ @Test
+ public void insertTest() {
+ final String[] contents = new String[] {"This", " is ", "simple", " message"};
+ MessageChain chain = new MessageChain(contents);
+
+ chain.insert(2, "a ");
+ chain.insert(5, new CharSequenceMessage("."));
+ Assert.assertEquals(contents.length + 2, chain.size());
+ Assert.assertEquals("This is a simple message.", chain.contentToString());
+ }
+
+}
\ No newline at end of file
diff --git a/ContentGrabbingJi-framework-api/src/test/java/net/lamgc/cgj/bot/framework/util/CollectionUtilsTest.java b/ContentGrabbingJi-framework-api/src/test/java/net/lamgc/cgj/bot/framework/util/CollectionUtilsTest.java
new file mode 100644
index 0000000..d7e0a12
--- /dev/null
+++ b/ContentGrabbingJi-framework-api/src/test/java/net/lamgc/cgj/bot/framework/util/CollectionUtilsTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 LamGC
+ *
+ * ContentGrabbingJi is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * ContentGrabbingJi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package net.lamgc.cgj.bot.framework.util;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+public class CollectionUtilsTest {
+
+ @Test
+ public void toMapTest() {
+ Map expectMap = new HashMap<>();
+ expectMap.put("arg1", "value1");
+ expectMap.put("arg2", "value2");
+ expectMap.put("arg3", "value3");
+
+ Set modifySet = new CopyOnWriteArraySet<>(expectMap.keySet());
+ modifySet.add(null);
+
+ Map map = CollectionUtils.toMap(modifySet, expectMap::get);
+ Assert.assertEquals(expectMap.size(), map.size());
+ Assert.assertTrue(expectMap.keySet().containsAll(map.keySet()));
+
+ for (String key : expectMap.keySet()) {
+ Assert.assertEquals(expectMap.get(key), map.get(key));
+ }
+ }
+
+}
\ No newline at end of file