@Test
public void whenTheRuleFails_thenOnFailureShouldBeExecuted() throws Exception {
    final Exception exception = new Exception("fatal error!");
    doThrow(exception).when(rule).execute();
    rulesEngine.registerRule(rule);
    rulesEngine.fireRules();
    InOrder inOrder = inOrder(ruleListener1, ruleListener2);
    inOrder.verify(ruleListener1).beforeExecute(rule);
    inOrder.verify(ruleListener2).beforeExecute(rule);
    inOrder.verify(ruleListener1).onFailure(rule, exception);
    inOrder.verify(ruleListener2).onFailure(rule, exception);
}

@Test
public void whenTheRuleFails_thenOnFailureShouldBeExecuted() throws Exception {
    final Exception exception = new Exception("fatal error!");
    doThrow(exception).when(rule1).execute(facts);
    when(rule1.evaluate(facts)).thenReturn(true);
    rules.clear();
    rules.register(rule1);
    rulesEngine.fire(rules, facts);
    InOrder inOrder = inOrder(rule1, fact1, fact2, ruleListener1, ruleListener2);
    inOrder.verify(ruleListener1).beforeExecute(rule1, facts);
    inOrder.verify(ruleListener2).beforeExecute(rule1, facts);
    inOrder.verify(ruleListener1).onFailure(rule1, exception, facts);
    inOrder.verify(ruleListener2).onFailure(rule1, exception, facts);
}

@Test
public void whenTheRuleEvaluatesToTrue_thenTheListenerShouldBeInvoked() throws Exception {
    when(rule.evaluate()).thenReturn(true);
    rulesEngine = aNewRulesEngine().withRuleListener(ruleListener1).build();
    rulesEngine.registerRule(rule);
    rulesEngine.fireRules();
    verify(ruleListener1).afterEvaluate(rule, true);
}

@Test
public void whenTheRuleEvaluatesToTrue_thenTheListenerShouldBeInvoked() throws Exception {
    when(rule1.evaluate(facts)).thenReturn(true);
    rulesEngine = aNewRulesEngine().withRuleListener(ruleListener1).build();
    rules.clear();
    rules.register(rule1);
    rulesEngine.fire(rules, facts);
    verify(ruleListener1).afterEvaluate(rule1, true);
}

@Override
public void registerRule(final Object rule) {
    rules.add(asRule(rule));
}

@Test
public void whenTheRuleEvaluatesToFalse_thenTheListenerShouldBeInvoked() throws Exception {
    when(rule.evaluate()).thenReturn(false);
    rulesEngine = aNewRulesEngine().withRuleListener(ruleListener1).build();
    rulesEngine.registerRule(rule);
    rulesEngine.fireRules();
    verify(ruleListener1).afterEvaluate(rule, false);
}

@Test
public void whenTheRuleEvaluatesToFalse_thenTheListenerShouldBeInvoked() throws Exception {
    when(rule1.evaluate(facts)).thenReturn(false);
    rulesEngine = aNewRulesEngine().withRuleListener(ruleListener1).build();
    rules.clear();
    rules.register(rule1);
    rulesEngine.fire(rules, facts);
    verify(ruleListener1).afterEvaluate(rule1, false);
}

@Test
public void whenTheRuleExecutesSuccessfully_thenOnSuccessShouldBeExecuted() throws Exception {
    rulesEngine.registerRule(rule);
    rulesEngine.fireRules();
    InOrder inOrder = inOrder(ruleListener1, ruleListener2);
    inOrder.verify(ruleListener1).beforeExecute(rule);
    inOrder.verify(ruleListener2).beforeExecute(rule);
    inOrder.verify(ruleListener1).onSuccess(rule);
    inOrder.verify(ruleListener2).onSuccess(rule);
}

@Test
public void whenTheRuleExecutesSuccessfully_thenOnSuccessShouldBeExecuted() throws Exception {
    when(rule1.evaluate(facts)).thenReturn(true);
    rules.clear();
    rules.register(rule1);
    rulesEngine.fire(rules, facts);
    InOrder inOrder = inOrder(rule1, fact1, fact2, ruleListener1, ruleListener2);
    inOrder.verify(ruleListener1).beforeExecute(rule1, facts);
    inOrder.verify(ruleListener2).beforeExecute(rule1, facts);
    inOrder.verify(ruleListener1).onSuccess(rule1, facts);
    inOrder.verify(ruleListener2).onSuccess(rule1, facts);
}

@Test
public void whenListenerReturnsFalse_thenTheRuleShouldBeSkippedBeforeBeingEvaluated() throws Exception {
    when(ruleListener1.beforeEvaluate(rule)).thenReturn(false);
    rulesEngine = aNewRulesEngine().withRuleListener(ruleListener1).build();
    rulesEngine.registerRule(rule);
    rulesEngine.fireRules();
    verify(rule, never()).evaluate();
}

@Test
public void whenListenerReturnsFalse_thenTheRuleShouldBeSkippedBeforeBeingEvaluated() throws Exception {
    when(ruleListener1.beforeEvaluate(rule1, facts)).thenReturn(false);
    rulesEngine = aNewRulesEngine().withRuleListener(ruleListener1).build();
    rules.register(rule1);
    rulesEngine.fire(rules, facts);
    verify(rule1, never()).evaluate(facts);
}

@Test
public void whenListenerReturnsTrue_thenTheRuleShouldBeEvaluated() throws Exception {
    when(ruleListener1.beforeEvaluate(rule)).thenReturn(true);
    rulesEngine = aNewRulesEngine().withRuleListener(ruleListener1).build();
    rulesEngine.registerRule(rule);
    rulesEngine.fireRules();
    verify(rule).evaluate();
}

@Test
public void whenListenerReturnsTrue_thenTheRuleShouldBeEvaluated() throws Exception {
    when(ruleListener1.beforeEvaluate(rule1, facts)).thenReturn(true);
    rulesEngine = aNewRulesEngine().withRuleListener(ruleListener1).build();
    rules.register(rule1);
    rulesEngine.fire(rules, facts);
    verify(rule1).evaluate(facts);
}


@Test
public void rulesThatExceedPriorityThresholdMustNotBeExecuted() throws Exception {
    rulesEngine.registerRule(rule1);
    rulesEngine.registerRule(rule2);
    rulesEngine.fireRules();
    verify(rule1).execute();
    verify(rule2, never()).execute();
}

@Test
public void rulesThatExceedPriorityThresholdMustNotBeExecuted() throws Exception {
    rules.register(rule1);
    rules.register(rule2);
    rulesEngine.fire(rules, facts);
    verify(rule1).execute(facts);
    verify(rule2, never()).execute(facts);
}

@Override
public void fire(Rules rules, Facts facts) {
    if (rules.isEmpty()) {
        LOGGER.warning("No rules registered! Nothing to apply");
        return;
    }
    sort(rules);
    logEngineParameters();
    log(rules, facts);
    apply(rules, facts);
}


@Test
public void testSkipOnFirstAppliedRule() throws Exception {
    rulesEngine.registerRule(rule1);
    rulesEngine.registerRule(rule2);
    rulesEngine.fireRules();
    verify(rule1).execute();
    verify(rule2, never()).execute();
}

@Test
public void testSkipOnFirstAppliedRule() throws Exception {
    rulesEngine.fire(rules, facts);
    verify(rule1).execute(facts);
    verify(rule2, never()).execute(facts);
}

@Test
public void testSkipOnFirstAppliedRuleWithException() throws Exception {
    rulesEngine.registerRule(rule0);
    rulesEngine.registerRule(rule1);
    rulesEngine.registerRule(rule2);
    rulesEngine.fireRules();
    verify(rule1).execute();
    verify(rule2, never()).execute();
}

@Test
public void testSkipOnFirstAppliedRuleWithException() throws Exception {
    rulesEngine.fire(rules, facts);
    verify(rule1).execute(facts);
    verify(rule2, never()).execute(facts);
}


@Test
public void testSkipOnFirstFailedRule() throws Exception {
    rulesEngine.registerRule(rule1);
    rulesEngine.registerRule(rule2);
    rulesEngine.fireRules();
    verify(rule1).execute();
    verify(rule2, never()).execute();
}

@Test
public void testSkipOnFirstFailedRule() throws Exception {
    rulesEngine.fire(rules, facts);
    verify(rule1).execute(facts);
    verify(rule2, never()).execute(facts);
}


@Test
public void WhenConditionIsFalse_ThenActionsShouldNotBeExecuted() throws Exception {
    when(condition.evaluate(facts)).thenReturn(false);
    Rule rule = RuleBuilder.aNewRule().when(condition).then(action1).then(action2).build();
    rules.register(rule);
    rulesEngine.fire(rules, facts);
    verify(action1, never()).execute(facts);
    verify(action2, never()).execute(facts);
}

@Test
public void WhenConditionIsFalse_ThenActionsShouldNotBeExecuted() throws Exception {
    when(condition.evaluate(facts)).thenReturn(false);
    Rule rule = new RuleBuilder().when(condition).then(action1).then(action2).build();
    rules.register(rule);
    rulesEngine.fire(rules, facts);
    verify(action1, never()).execute(facts);
    verify(action2, never()).execute(facts);
}

public static RuleBuilder aNewRule() {
    return new RuleBuilder();
}

@Test
public void WhenConditionIsTrue_ThenActionsShouldBeExecutedInOrder() throws Exception {
    when(condition.evaluate(facts)).thenReturn(true);
    Rule rule = RuleBuilder.aNewRule().when(condition).then(action1).then(action2).build();
    rules.register(rule);
    rulesEngine.fire(rules, facts);
    InOrder inOrder = Mockito.inOrder(action1, action2);
    inOrder.verify(action1).execute(facts);
    inOrder.verify(action2).execute(facts);
}

@Test
public void WhenConditionIsTrue_ThenActionsShouldBeExecutedInOrder() throws Exception {
    when(condition.evaluate(facts)).thenReturn(true);
    Rule rule = new RuleBuilder().when(condition).then(action1).then(action2).build();
    rules.register(rule);
    rulesEngine.fire(rules, facts);
    InOrder inOrder = Mockito.inOrder(action1, action2);
    inOrder.verify(action1).execute(facts);
    inOrder.verify(action2).execute(facts);
}


@Test
public void testDefaultRuleCreationWithDefaultValues() throws Exception {
    Rule rule = RuleBuilder.aNewRule().build();
    assertThat(rule.getName()).isEqualTo(Rule.DEFAULT_NAME);
    assertThat(rule.getDescription()).isEqualTo(Rule.DEFAULT_DESCRIPTION);
    assertThat(rule.getPriority()).isEqualTo(Rule.DEFAULT_PRIORITY);
    assertThat(rule).isInstanceOf(DefaultRule.class);
}

@Test
public void testDefaultRuleCreationWithDefaultValues() throws Exception {
    Rule rule = new RuleBuilder().build();
    assertThat(rule.getName()).isEqualTo(Rule.DEFAULT_NAME);
    assertThat(rule.getDescription()).isEqualTo(Rule.DEFAULT_DESCRIPTION);
    assertThat(rule.getPriority()).isEqualTo(Rule.DEFAULT_PRIORITY);
    assertThat(rule).isInstanceOf(DefaultRule.class);
}

@Test
public void testDefaultRuleCreationWithCustomValues() throws Exception {
    Rule rule = RuleBuilder.aNewRule().named("myRule").withDescription("myRuleDescription").withPriority(3).when(condition).then(action1).then(action2).build();
    assertThat(rule.getName()).isEqualTo("myRule");
    assertThat(rule.getDescription()).isEqualTo("myRuleDescription");
    assertThat(rule.getPriority()).isEqualTo(3);
    assertThat(rule).isInstanceOf(DefaultRule.class);
    assertThat(rule).extracting("condition").containsExactly(condition);
    assertThat(rule).extracting("actions").containsExactly(asList(action1, action2));
}

@Test
public void testDefaultRuleCreationWithCustomValues() throws Exception {
    Rule rule = new RuleBuilder().name("myRule").description("myRuleDescription").priority(3).when(condition).then(action1).then(action2).build();
    assertThat(rule.getName()).isEqualTo("myRule");
    assertThat(rule.getDescription()).isEqualTo("myRuleDescription");
    assertThat(rule.getPriority()).isEqualTo(3);
    assertThat(rule).isInstanceOf(DefaultRule.class);
    assertThat(rule).extracting("condition").containsExactly(condition);
    assertThat(rule).extracting("actions").containsExactly(asList(action1, action2));
}


@Test
public void testNewFileCreation() {
    List<String> original = new ArrayList<>();
    List<String> revised = new ArrayList<>();
    revised.add("line1");
    revised.add("line2");
    Patch<String> patch = DiffUtils.diff(original, revised);
    List<String> udiff = UnifiedDiffUtils.generateUnifiedDiff(null, "revised", original, patch, 10);
    assertEquals("--- ", udiff.get(0));
    assertEquals("+++ revised", udiff.get(1));
    assertEquals("@@ -0,0 +1,2 @@", udiff.get(2));
    UnifiedDiffUtils.parseUnifiedDiff(udiff);
}

@Test
public void testNewFileCreation() {
    List<String> original = new ArrayList<>();
    List<String> revised = new ArrayList<>();
    revised.add("line1");
    revised.add("line2");
    Patch<String> patch = DiffUtils.diff(original, revised);
    List<String> udiff = UnifiedDiffUtils.generateUnifiedDiff(null, "revised", original, patch, 10);
    assertEquals("--- /dev/null", udiff.get(0));
    assertEquals("+++ revised", udiff.get(1));
    assertEquals("@@ -0,0 +1,2 @@", udiff.get(2));
    UnifiedDiffUtils.parseUnifiedDiff(udiff);
}

public static List<String> generateUnifiedDiff(String originalFileName, String revisedFileName, List<String> originalLines, Patch<String> patch, int contextSize) {
    if (!patch.getDeltas().isEmpty()) {
        List<String> ret = new ArrayList<>();
        ret.add("--- " + Optional.ofNullable(originalFileName).orElse(""));
        ret.add("+++ " + Optional.ofNullable(revisedFileName).orElse(""));
        List<AbstractDelta<String>> patchDeltas = new ArrayList<>(patch.getDeltas());
        List<AbstractDelta<String>> deltas = new ArrayList<>();
        AbstractDelta<String> delta = patchDeltas.get(0);
        deltas.add(delta);
        if (patchDeltas.size() > 1) {
            for (int i = 1; i < patchDeltas.size(); i++) {
                int position = delta.getSource().getPosition();
                AbstractDelta<String> nextDelta = patchDeltas.get(i);
                if ((position + delta.getSource().size() + contextSize) >= (nextDelta.getSource().getPosition() - contextSize)) {
                    deltas.add(nextDelta);
                } else {
                    List<String> curBlock = processDeltas(originalLines, deltas, contextSize, false);
                    ret.addAll(curBlock);
                    deltas.clear();
                    deltas.add(nextDelta);
                }
                delta = nextDelta;
            }
        }
        List<String> curBlock = processDeltas(originalLines, deltas, contextSize, patchDeltas.size() == 1 && originalFileName == null);
        ret.addAll(curBlock);
        return ret;
    }
    return new ArrayList<>();
}

public static List<String> generateUnifiedDiff(String originalFileName, String revisedFileName, List<String> originalLines, Patch<String> patch, int contextSize) {
    if (!patch.getDeltas().isEmpty()) {
        List<String> ret = new ArrayList<>();
        ret.add("--- " + Optional.ofNullable(originalFileName).orElse(NULL_FILE_INDICATOR));
        ret.add("+++ " + Optional.ofNullable(revisedFileName).orElse(NULL_FILE_INDICATOR));
        List<AbstractDelta<String>> patchDeltas = new ArrayList<>(patch.getDeltas());
        List<AbstractDelta<String>> deltas = new ArrayList<>();
        AbstractDelta<String> delta = patchDeltas.get(0);
        deltas.add(delta);
        if (patchDeltas.size() > 1) {
            for (int i = 1; i < patchDeltas.size(); i++) {
                int position = delta.getSource().getPosition();
                AbstractDelta<String> nextDelta = patchDeltas.get(i);
                if ((position + delta.getSource().size() + contextSize) >= (nextDelta.getSource().getPosition() - contextSize)) {
                    deltas.add(nextDelta);
                } else {
                    List<String> curBlock = processDeltas(originalLines, deltas, contextSize, false);
                    ret.addAll(curBlock);
                    deltas.clear();
                    deltas.add(nextDelta);
                }
                delta = nextDelta;
            }
        }
        List<String> curBlock = processDeltas(originalLines, deltas, contextSize, patchDeltas.size() == 1 && originalFileName == null);
        ret.addAll(curBlock);
        return ret;
    }
    return new ArrayList<>();
}


@Test
public void testNewFileCreation() throws DiffException {
    List<String> original = new ArrayList<>();
    List<String> revised = new ArrayList<>();
    revised.add("line1");
    revised.add("line2");
    Patch<String> patch = DiffUtils.diff(original, revised);
    List<String> udiff = UnifiedDiffUtils.generateUnifiedDiff(null, "revised", original, patch, 10);
    assertEquals("--- null", udiff.get(0));
    assertEquals("+++ revised", udiff.get(1));
    assertEquals("@@ -0,0 +1,2 @@", udiff.get(2));
    UnifiedDiffUtils.parseUnifiedDiff(udiff);
}

@Test
public void testNewFileCreation() throws DiffException {
    List<String> original = new ArrayList<>();
    List<String> revised = new ArrayList<>();
    revised.add("line1");
    revised.add("line2");
    Patch<String> patch = DiffUtils.diff(original, revised);
    List<String> udiff = UnifiedDiffUtils.generateUnifiedDiff(null, "revised", original, patch, 10);
    assertEquals("--- ", udiff.get(0));
    assertEquals("+++ revised", udiff.get(1));
    assertEquals("@@ -0,0 +1,2 @@", udiff.get(2));
    UnifiedDiffUtils.parseUnifiedDiff(udiff);
}

public static List<String> generateUnifiedDiff(String originalFileName, String revisedFileName, List<String> originalLines, Patch<String> patch, int contextSize) {
    if (!patch.getDeltas().isEmpty()) {
        List<String> ret = new ArrayList<>();
        ret.add("--- " + originalFileName);
        ret.add("+++ " + revisedFileName);
        List<AbstractDelta<String>> patchDeltas = new ArrayList<>(patch.getDeltas());
        List<AbstractDelta<String>> deltas = new ArrayList<>();
        AbstractDelta<String> delta = patchDeltas.get(0);
        deltas.add(delta);
        if (patchDeltas.size() > 1) {
            for (int i = 1; i < patchDeltas.size(); i++) {
                int position = delta.getSource().getPosition();
                AbstractDelta<String> nextDelta = patchDeltas.get(i);
                if ((position + delta.getSource().size() + contextSize) >= (nextDelta.getSource().getPosition() - contextSize)) {
                    deltas.add(nextDelta);
                } else {
                    List<String> curBlock = processDeltas(originalLines, deltas, contextSize, false);
                    ret.addAll(curBlock);
                    deltas.clear();
                    deltas.add(nextDelta);
                }
                delta = nextDelta;
            }
        }
        List<String> curBlock = processDeltas(originalLines, deltas, contextSize, patchDeltas.size() == 1 && originalFileName == null);
        ret.addAll(curBlock);
        return ret;
    }
    return new ArrayList<>();
}

public static List<String> generateUnifiedDiff(String originalFileName, String revisedFileName, List<String> originalLines, Patch<String> patch, int contextSize) {
    if (!patch.getDeltas().isEmpty()) {
        List<String> ret = new ArrayList<>();
        ret.add("--- " + Optional.ofNullable(originalFileName).orElse(""));
        ret.add("+++ " + Optional.ofNullable(revisedFileName).orElse(""));
        List<AbstractDelta<String>> patchDeltas = new ArrayList<>(patch.getDeltas());
        List<AbstractDelta<String>> deltas = new ArrayList<>();
        AbstractDelta<String> delta = patchDeltas.get(0);
        deltas.add(delta);
        if (patchDeltas.size() > 1) {
            for (int i = 1; i < patchDeltas.size(); i++) {
                int position = delta.getSource().getPosition();
                AbstractDelta<String> nextDelta = patchDeltas.get(i);
                if ((position + delta.getSource().size() + contextSize) >= (nextDelta.getSource().getPosition() - contextSize)) {
                    deltas.add(nextDelta);
                } else {
                    List<String> curBlock = processDeltas(originalLines, deltas, contextSize, false);
                    ret.addAll(curBlock);
                    deltas.clear();
                    deltas.add(nextDelta);
                }
                delta = nextDelta;
            }
        }
        List<String> curBlock = processDeltas(originalLines, deltas, contextSize, patchDeltas.size() == 1 && originalFileName == null);
        ret.addAll(curBlock);
        return ret;
    }
    return new ArrayList<>();
}


@Test
public void test2() {
    thrown.expect(IllegalStateException.class);
    thrown.expectMessage("each method expected one @SQL annotation but not found");
    Dao2 dao = mango.create(Dao2.class, new LocalCacheHandler());
    dao.add();
}

@Test
public void test2() {
    thrown.expect(DescriptionException.class);
    thrown.expectMessage("each method expected one @SQL annotation but not found");
    Dao2 dao = mango.create(Dao2.class, new LocalCacheHandler());
    dao.add();
}

public <T> T create(Class<T> daoClass, @Nullable CacheHandler cacheHandler) {
    return create(daoClass, cacheHandler, isDefaultLazyInit);
}


@Test
public void test2() {
    thrown.expect(IncorrectSqlException.class);
    thrown.expectMessage("sql is null or empty");
    Dao dao = mango.create(Dao.class);
    dao.add2();
}

@Test
public void test2() {
    thrown.expect(DescriptionException.class);
    thrown.expectMessage("sql is null or empty");
    Dao dao = mango.create(Dao.class);
    dao.add2();
}

public <T> T create(Class<T> daoClass) {
    return create(daoClass, defaultCacheHandler, isDefaultLazyInit);
}


@Test
public void build() throws Exception {
    List<String> properties = Lists.newArrayList("id", "userName", "userAge");
    List<String> columns = Lists.newArrayList("id", "user_name", "user_age");
    CommonUpdateBuilder b = new CommonUpdateBuilder("id", properties, columns);
    assertThat(b.buildSql(), equalTo("update #table set user_name = :userName, user_age = :userAge where id = :id"));
}

@Test
public void build() throws Exception {
    List<String> properties = Lists.newArrayList("id", "userName", "userAge");
    List<String> columns = Lists.newArrayList("id", "user_name", "user_age");
    CommonUpdateBuilder b = new CommonUpdateBuilder("id", properties, columns);
    assertThat(b.buildSql(), equalTo("update #table set #if (:userName != null) user_name = :userName,#end #if (:userAge != null) user_age = :userAge,#end #trim_comma where id = :id"));
}

@Override
public String buildSql() {
    List<String> exps = new ArrayList<String>();
    for (int i = 0; i < properties.size(); i++) {
        String exp = columns.get(i) + " = :" + properties.get(i);
        exps.add(exp);
    }
    String s1 = Joiner.on(", ").join(exps);
    String s2 = columnId + " = :" + propertyId;
    return String.format(SQL_TEMPLATE, s1, s2);
}

@Override
public String buildSql() {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < properties.size(); i++) {
        String prop = ":" + properties.get(i);
        String col = columns.get(i);
        sb.append(String.format("#if (%s != null) %s = %s,#end ", prop, col, prop));
    }
    sb.append("#trim_comma");
    String str = columnId + " = :" + propertyId;
    return String.format(SQL_TEMPLATE, sb.toString(), str);
}


@Test
public void test() throws Exception {
    CommonBatchUpdateBuilderFactory factory = new CommonBatchUpdateBuilderFactory();
    String name = "update";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = int[].class;
    List<Type> parameterTypes = Lists.newArrayList(DynamicTokens.collectionToken(TypeToken.of(entityClass)).getType());
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("update #table set userid = :userId, user_age = :userAge where id = :id"));
}

@Test
public void test() throws Exception {
    CommonBatchUpdateBuilderFactory factory = new CommonBatchUpdateBuilderFactory();
    String name = "update";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = int[].class;
    List<Type> parameterTypes = Lists.newArrayList(DynamicTokens.collectionToken(TypeToken.of(entityClass)).getType());
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("update #table set #if (:userId != null) userid = :userId,#end #if (:userAge != null) user_age = :userAge,#end #trim_comma where id = :id"));
}


@Test
public void test() throws Exception {
    CommonUpdateBuilderFactory factory = new CommonUpdateBuilderFactory();
    String name = "update";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = int.class;
    List<Type> parameterTypes = Lists.newArrayList((Type) entityClass);
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("update #table set userid = :userId, user_age = :userAge where id = :id"));
}

@Test
public void test() throws Exception {
    CommonUpdateBuilderFactory factory = new CommonUpdateBuilderFactory();
    String name = "update";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = int.class;
    List<Type> parameterTypes = Lists.newArrayList((Type) entityClass);
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("update #table set #if (:userId != null) userid = :userId,#end #if (:userAge != null) user_age = :userAge,#end #trim_comma where id = :id"));
}


@Test
public void testUpdateEmpty() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    boolean old = mango.isCompatibleWithEmptyList();
    mango.setCompatibleWithEmptyList(true);
    assertThat(dao.updateWithInStatement(new ArrayList<Integer>(), "ash"), equalTo(0));
    mango.setCompatibleWithEmptyList(old);
}

@Test
public void testUpdateEmpty() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCompatibleWithEmptyList(true);
    mango.setConfig(newConfig);
    assertThat(dao.updateWithInStatement(new ArrayList<Integer>(), "ash"), equalTo(0));
    mango.setConfig(oldConfig);
}

public Mango setCompatibleWithEmptyList(boolean isCompatibleWithEmptyList) {
    config.setCompatibleWithEmptyList(isCompatibleWithEmptyList);
    return this;
}

@Test
public void testQueryEmpty() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    boolean old = mango.isCompatibleWithEmptyList();
    mango.setCompatibleWithEmptyList(true);
    assertThat(dao.getUserArray(new ArrayList<Integer>()).length, equalTo(0));
    mango.setCompatibleWithEmptyList(old);
}

@Test
public void testQueryEmpty() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCompatibleWithEmptyList(true);
    mango.setConfig(newConfig);
    assertThat(dao.getUserArray(new ArrayList<Integer>()).length, equalTo(0));
    mango.setConfig(oldConfig);
}


@Test
public void testUpdateEmpty() throws Exception {
    LocalCacheHandler cacheHandler = new LocalCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    boolean old = mango.isCompatibleWithEmptyList();
    mango.setCompatibleWithEmptyList(true);
    assertThat(dao.updateWithInStatement(new ArrayList<Integer>(), "ash"), equalTo(0));
    mango.setCompatibleWithEmptyList(old);
}

@Test
public void testUpdateEmpty() throws Exception {
    LocalCacheHandler cacheHandler = new LocalCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCompatibleWithEmptyList(true);
    mango.setConfig(newConfig);
    assertThat(dao.updateWithInStatement(new ArrayList<Integer>(), "ash"), equalTo(0));
    mango.setConfig(oldConfig);
}

@Test
public void testQueryEmpty() throws Exception {
    LocalCacheHandler cacheHandler = new LocalCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    boolean old = mango.isCompatibleWithEmptyList();
    mango.setCompatibleWithEmptyList(true);
    assertThat(dao.getUserArray(new ArrayList<Integer>()).length, equalTo(0));
    mango.setCompatibleWithEmptyList(old);
}

@Test
public void testQueryEmpty() throws Exception {
    LocalCacheHandler cacheHandler = new LocalCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCompatibleWithEmptyList(true);
    mango.setConfig(newConfig);
    assertThat(dao.getUserArray(new ArrayList<Integer>()).length, equalTo(0));
    mango.setConfig(oldConfig);
}


@Test
public void testQueryEmpty2() throws Exception {
    thrown.expect(EmptyCollectionException.class);
    thrown.expectMessage("value of :1 can't be empty");
    boolean old = mango.isCompatibleWithEmptyList();
    mango.setCompatibleWithEmptyList(false);
    dao.getUsersInList(new ArrayList<Integer>());
    mango.setCompatibleWithEmptyList(old);
}

@Test
public void testQueryEmpty2() throws Exception {
    thrown.expect(EmptyCollectionException.class);
    thrown.expectMessage("value of :1 can't be empty");
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCompatibleWithEmptyList(false);
    mango.setConfig(newConfig);
    try {
        dao.getUsersInList(new ArrayList<Integer>());
    } finally {
        mango.setConfig(oldConfig);
    }
}

@Test
public void testUpdateEmpty() {
    boolean old = mango.isCompatibleWithEmptyList();
    mango.setCompatibleWithEmptyList(true);
    assertThat(dao.updateUsers(new ArrayList<Integer>(), "ash"), equalTo(0));
    mango.setCompatibleWithEmptyList(old);
}

@Test
public void testUpdateEmpty() {
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCompatibleWithEmptyList(true);
    mango.setConfig(newConfig);
    assertThat(dao.updateUsers(new ArrayList<Integer>(), "ash"), equalTo(0));
    mango.setConfig(oldConfig);
}

@Test
public void testUpdateEmpty2() {
    thrown.expect(EmptyCollectionException.class);
    thrown.expectMessage("value of :ids can't be empty");
    boolean old = mango.isCompatibleWithEmptyList();
    mango.setCompatibleWithEmptyList(false);
    dao.updateUsers(new ArrayList<Integer>(), "ash");
    mango.setCompatibleWithEmptyList(old);
}

@Test
public void testUpdateEmpty2() {
    thrown.expect(EmptyCollectionException.class);
    thrown.expectMessage("value of :ids can't be empty");
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCompatibleWithEmptyList(false);
    mango.setConfig(newConfig);
    try {
        dao.updateUsers(new ArrayList<Integer>(), "ash");
    } finally {
        mango.setConfig(oldConfig);
    }
}

@Test
public void testQueryEmpty() throws Exception {
    boolean old = mango.isCompatibleWithEmptyList();
    mango.setCompatibleWithEmptyList(true);
    assertThat(dao.getUsersInList(new ArrayList<Integer>()).size(), equalTo(0));
    assertThat(dao.getUsersInArray(new int[] {}).length, equalTo(0));
    assertThat(dao.getUsersInArray2(new int[] {}), nullValue());
    assertThat(dao.getUsersInSet(new HashSet<Integer>()).size(), equalTo(0));
    mango.setCompatibleWithEmptyList(old);
}

@Test
public void testQueryEmpty() throws Exception {
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCompatibleWithEmptyList(true);
    mango.setConfig(newConfig);
    assertThat(dao.getUsersInList(new ArrayList<Integer>()).size(), equalTo(0));
    assertThat(dao.getUsersInArray(new int[] {}).length, equalTo(0));
    assertThat(dao.getUsersInArray2(new int[] {}), nullValue());
    assertThat(dao.getUsersInSet(new HashSet<Integer>()).size(), equalTo(0));
    mango.setConfig(oldConfig);
}


@Test
public void testException() {
    thrown.expect(MappingException.class);
    thrown.expectMessage("Unable to map column 'ID' to any property of 'class org.jfaster.mango.jdbc.BeanPropertyRowMapperTest$MullMsg'");
    Msg2Dao dao = mango.create(Msg2Dao.class);
    MullMsg msg = MullMsg.createRandomMsg();
    int id = dao.insert(msg.getUid(), msg.getYyCon());
    assertThat(id, greaterThan(0));
    System.out.println(dao.getMsg(id));
}

@Test
public void testException() {
    thrown.expect(MappingException.class);
    thrown.expectMessage("Unable to map column 'ID' to any property of 'class org.jfaster.mango.jdbc.BeanPropertyRowMapperTest$MullMsg'");
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCheckColumn(true);
    mango.setConfig(newConfig);
    Msg2Dao dao = mango.create(Msg2Dao.class);
    MullMsg msg = MullMsg.createRandomMsg();
    int id = dao.insert(msg.getUid(), msg.getYyCon());
    assertThat(id, greaterThan(0));
    try {
        dao.getMsg(id);
    } finally {
        mango.setConfig(oldConfig);
    }
}

public void setConfig(Config config) {
    configHolder.set(config);
}


@Test
public void test() throws Exception {
    CommonBatchUpdateBuilderFactory factory = new CommonBatchUpdateBuilderFactory();
    String name = "update";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = int[].class;
    List<Type> parameterTypes = Lists.newArrayList(DynamicTokens.collectionToken(TypeToken.of(entityClass)).getType());
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("update #table set #if (:userId != null) userid = :userId,#end #if (:userAge != null) user_age = :userAge,#end #trim_comma where id = :id"));
}

@Test
public void test() throws Exception {
    CommonBatchUpdateBuilderFactory factory = new CommonBatchUpdateBuilderFactory();
    String name = "update";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = int[].class;
    List<Type> parameterTypes = Lists.newArrayList(DynamicTokens.iterableToken(TypeToken.of(entityClass)).getType());
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("update #table set #if (:userId != null) userid = :userId,#end #if (:userAge != null) user_age = :userAge,#end #trim_comma where id = :id"));
}

public static <E> TypeToken<Iterable<E>> iterableToken(TypeToken<E> entityToken) {
    return new TypeToken<Iterable<E>>() {
    }.where(new TypeParameter<E>() {
    }, entityToken);
}


@Test
public void test() throws Exception {
    CommonGetAllBuilderFactory factory = new CommonGetAllBuilderFactory();
    String name = "getAll";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = DynamicTokens.listToken(TypeToken.of(entityClass)).getType();
    List<Type> parameterTypes = Lists.newArrayList();
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("select id, userid, user_age from #table"));
}

@Test
public void test() throws Exception {
    CommonGetAllBuilderFactory factory = new CommonGetAllBuilderFactory();
    String name = "findAll";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = DynamicTokens.iterableToken(TypeToken.of(entityClass)).getType();
    List<Type> parameterTypes = Lists.newArrayList();
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("select id, userid, user_age from #table"));
}


@Test
public void test() throws Exception {
    CommonGetMultiBuilderFactory factory = new CommonGetMultiBuilderFactory();
    String name = "getMulti";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = DynamicTokens.listToken(TypeToken.of(entityClass)).getType();
    List<Type> parameterTypes = Lists.newArrayList(DynamicTokens.listToken(TypeToken.of(idClass)).getType());
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("select id, userid, user_age from #table where id in (:1)"));
}

@Test
public void test() throws Exception {
    CommonGetMultiBuilderFactory factory = new CommonGetMultiBuilderFactory();
    String name = "findByIds";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = DynamicTokens.iterableToken(TypeToken.of(entityClass)).getType();
    List<Type> parameterTypes = Lists.newArrayList(DynamicTokens.iterableToken(TypeToken.of(idClass)).getType());
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("select id, userid, user_age from #table where id in (:1)"));
}

public static <E> TypeToken<List<E>> listToken(TypeToken<E> entityToken) {
    return new TypeToken<List<E>>() {
    }.where(new TypeParameter<E>() {
    }, entityToken);
}


@Test
public void test() throws Exception {
    CommonGetOneBuilderFactory factory = new CommonGetOneBuilderFactory();
    String name = "getOne";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    List<Type> types = Lists.newArrayList((Type) idClass);
    Builder b = factory.doTryGetBuilder(name, entityClass, types, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("select id, userid, user_age from #table where id = :1"));
}

@Test
public void test() throws Exception {
    CommonGetOneBuilderFactory factory = new CommonGetOneBuilderFactory();
    String name = "findById";
    Class<?> entityClass = Order.class;
    Type returnType = DynamicTokens.optionalToken(TypeToken.of(entityClass)).getType();
    Class<Integer> idClass = Integer.class;
    List<Type> types = Lists.newArrayList((Type) idClass);
    Builder b = factory.doTryGetBuilder(name, returnType, types, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("select id, userid, user_age from #table where id = :1"));
}

public static <E> TypeToken<Optional<E>> optionalToken(TypeToken<E> entityToken) {
    return new TypeToken<Optional<E>>() {
    }.where(new TypeParameter<E>() {
    }, entityToken);
}


@Test
public void test() throws Exception {
    CrudMeta cm = new CrudMeta(Order.class);
    assertThat(cm.getProperty4Id(), equalTo("id"));
    assertThat(cm.getColumn4Id(), equalTo("id"));
    assertThat(cm.getProperty4AutoGeneratedValue(), equalTo("id"));
    assertThat(cm.getColumn4AutoGeneratedValue(), equalTo("id"));
    List<String> props = Lists.newArrayList("id", "userId", "userAge");
    assertThat(cm.getProperties(), equalTo(props));
    List<String> cols = Lists.newArrayList("id", "userid", "user_age");
    assertThat(cm.getColumns(), equalTo(cols));
}

@Test
public void test() throws Exception {
    CrudMeta cm = new CrudMeta(Order.class);
    assertThat(cm.getProperty4Id(), equalTo("id"));
    assertThat(cm.getColumn4Id(), equalTo("id"));
    assertThat(cm.getProperty4AutoGenerated(), equalTo("id"));
    assertThat(cm.getColumn4AutoGenerated(), equalTo("id"));
    List<String> props = Lists.newArrayList("id", "userId", "userAge");
    assertThat(cm.getProperties(), equalTo(props));
    List<String> cols = Lists.newArrayList("id", "userid", "user_age");
    assertThat(cm.getColumns(), equalTo(cols));
}

public String getColumn4AutoGenerated() {
    return column4AutoGenerated;
}

@Test
public void test2() throws Exception {
    CrudMeta cm = new CrudMeta(Order2.class);
    assertThat(cm.getProperty4Id(), equalTo("orderId"));
    assertThat(cm.getColumn4Id(), equalTo("order_id"));
    assertThat(cm.getProperty4AutoGeneratedValue(), nullValue());
    assertThat(cm.getColumn4AutoGeneratedValue(), nullValue());
    List<String> props = Lists.newArrayList("orderId", "userId", "userAge");
    assertThat(cm.getProperties(), equalTo(props));
    List<String> cols = Lists.newArrayList("order_id", "userid", "user_age");
    assertThat(cm.getColumns(), equalTo(cols));
}

@Test
public void test2() throws Exception {
    CrudMeta cm = new CrudMeta(Order2.class);
    assertThat(cm.getProperty4Id(), equalTo("orderId"));
    assertThat(cm.getColumn4Id(), equalTo("order_id"));
    assertThat(cm.getProperty4AutoGenerated(), nullValue());
    assertThat(cm.getColumn4AutoGenerated(), nullValue());
    List<String> props = Lists.newArrayList("orderId", "userId", "userAge");
    assertThat(cm.getProperties(), equalTo(props));
    List<String> cols = Lists.newArrayList("order_id", "userid", "user_age");
    assertThat(cm.getColumns(), equalTo(cols));
}

public String getColumn4AutoGeneratedValue() {
    return column4AutoGeneratedValue;
}

@Test
public void test3() throws Exception {
    CrudMeta cm = new CrudMeta(Order3.class);
    assertThat(cm.getProperty4Id(), equalTo("orderId"));
    assertThat(cm.getColumn4Id(), equalTo("order_id"));
    assertThat(cm.getProperty4AutoGeneratedValue(), equalTo("id"));
    assertThat(cm.getColumn4AutoGeneratedValue(), equalTo("id"));
    List<String> props = Lists.newArrayList("id", "orderId", "userId", "userAge");
    assertThat(cm.getProperties(), equalTo(props));
    List<String> cols = Lists.newArrayList("id", "order_id", "userid", "user_age");
    assertThat(cm.getColumns(), equalTo(cols));
}

@Test
public void test3() throws Exception {
    CrudMeta cm = new CrudMeta(Order3.class);
    assertThat(cm.getProperty4Id(), equalTo("orderId"));
    assertThat(cm.getColumn4Id(), equalTo("order_id"));
    assertThat(cm.getProperty4AutoGenerated(), equalTo("id"));
    assertThat(cm.getColumn4AutoGenerated(), equalTo("id"));
    List<String> props = Lists.newArrayList("id", "orderId", "userId", "userAge");
    assertThat(cm.getProperties(), equalTo(props));
    List<String> cols = Lists.newArrayList("id", "order_id", "userid", "user_age");
    assertThat(cm.getColumns(), equalTo(cols));
}


@Test
public void test() throws Exception {
    CommonFindAllBuilderFactory factory = new CommonFindAllBuilderFactory();
    String name = "findAll";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = DynamicTokens.iterableToken(TypeToken.of(entityClass)).getType();
    List<Type> parameterTypes = Lists.newArrayList();
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("select id, userid, user_age from #table"));
}

@Test
public void test() throws Exception {
    CommonFindAllBuilderFactory factory = new CommonFindAllBuilderFactory();
    String name = "findAll";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = DynamicTokens.listToken(TypeToken.of(entityClass)).getType();
    List<Type> parameterTypes = Lists.newArrayList();
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("select id, userid, user_age from #table"));
}

public static <E> TypeToken<List<E>> listToken(TypeToken<E> entityToken) {
    return new TypeToken<List<E>>() {
    }.where(new TypeParameter<E>() {
    }, entityToken);
}


@Test
public void test() throws Exception {
    CommonFindByIdsBuilderFactory factory = new CommonFindByIdsBuilderFactory();
    String name = "findByIds";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = DynamicTokens.iterableToken(TypeToken.of(entityClass)).getType();
    List<Type> parameterTypes = Lists.newArrayList(DynamicTokens.iterableToken(TypeToken.of(idClass)).getType());
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("select id, userid, user_age from #table where id in (:1)"));
}

@Test
public void test() throws Exception {
    CommonFindByIdsBuilderFactory factory = new CommonFindByIdsBuilderFactory();
    String name = "findByIds";
    Class<?> entityClass = Order.class;
    Class<Integer> idClass = Integer.class;
    Type returnType = DynamicTokens.listToken(TypeToken.of(entityClass)).getType();
    List<Type> parameterTypes = Lists.newArrayList(DynamicTokens.iterableToken(TypeToken.of(idClass)).getType());
    Builder b = factory.doTryGetBuilder(name, returnType, parameterTypes, entityClass, idClass);
    assertThat(b, notNullValue());
    assertThat(b.buildSql(), equalTo("select id, userid, user_age from #table where id in (:1)"));
}


@Test
public void testMultiKeysReturnList() throws Exception {
    LocalCacheHandler cacheHandler = new LocalCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    int base = 10000;
    List<User> users = createRandomUsers(5);
    List<Integer> ids = new ArrayList<Integer>();
    for (User user : users) {
        int id = dao.insert(user);
        user.setId(id);
        ids.add(id);
    }
    for (int i = 0; i < users.size(); i++) {
        ids.add(base + i);
    }
    List<User> actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    for (int i = 0; i < users.size(); i++) {
        Integer id = ids.get(i);
        User user = users.get(i);
        assertThat((User) cacheHandler.get(getUserKey(id)), equalTo(user));
    }
    for (int i = users.size(); i < ids.size(); i++) {
        assertThat((NullObject) cacheHandler.get(getUserKey(ids.get(i))), equalTo(new NullObject()));
    }
    actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    cacheHandler.delete(getUserKey(users.get(0).getId()));
    actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
}

@Test
public void testMultiKeysReturnList() throws Exception {
    LocalCacheHandler cacheHandler = new LocalCacheHandler();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    UserDao dao = mango.create(UserDao.class);
    int base = 10000;
    List<User> users = createRandomUsers(5);
    List<Integer> ids = new ArrayList<Integer>();
    for (User user : users) {
        int id = dao.insert(user);
        user.setId(id);
        ids.add(id);
    }
    for (int i = 0; i < users.size(); i++) {
        ids.add(base + i);
    }
    List<User> actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    for (int i = 0; i < users.size(); i++) {
        Integer id = ids.get(i);
        User user = users.get(i);
        assertThat((User) cacheHandler.get(getUserKey(id)), equalTo(user));
    }
    for (int i = users.size(); i < ids.size(); i++) {
        assertThat((NullObject) cacheHandler.get(getUserKey(ids.get(i))), equalTo(new NullObject()));
    }
    actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    cacheHandler.delete(getUserKey(users.get(0).getId()));
    actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
}

public Mango setCacheHandler(CacheHandler cacheHandler) {
    if (cacheHandler == null) {
        throw new NullPointerException("cacheHandler can't be null");
    }
    this.cacheHandler = cacheHandler;
    return this;
}


@Test
public void testReturnArrayList() throws Exception {
    Type type = new TypeToken<ArrayList<Msg>>() {
    }.getType();
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    List<Msg> msgs = new ArrayList<Msg>();
    MsgDao dao = mango.create(MsgDao.class, cacheHandler);
    int uid = 100;
    String key = getMsgKey(uid);
    for (int i = 0; i < 3; i++) {
        Msg msg = createRandomMsg(uid);
        msgs.add(msg);
        msg.setId(dao.insert(msg));
        assertThat(cacheHandler.get(key, type), nullValue());
        ArrayList<Msg> actual = dao.getArrayMsgs(uid);
        assertThat(actual, hasSize(msgs.size()));
        assertThat(actual, contains(msgs.toArray()));
        @SuppressWarnings("unchecked")
        ArrayList<Msg> cacheActual = (ArrayList<Msg>) cacheHandler.get(key, type);
        assertThat(cacheActual, hasSize(msgs.size()));
        assertThat(cacheActual, contains(msgs.toArray()));
    }
    ArrayList<Msg> actual = dao.getArrayMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    Msg msg = msgs.get(0);
    msg.setContent("ash");
    dao.update(msg);
    assertThat(cacheHandler.get(key, type), nullValue());
    actual = dao.getArrayMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    msg = msgs.remove(0);
    dao.delete(msg.getUid(), msg.getId());
    actual = dao.getArrayMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
}

@Test
public void testReturnArrayList() throws Exception {
    Type type = new TypeToken<ArrayList<Msg>>() {
    }.getType();
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    List<Msg> msgs = new ArrayList<Msg>();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    MsgDao dao = mango.create(MsgDao.class);
    int uid = 100;
    String key = getMsgKey(uid);
    for (int i = 0; i < 3; i++) {
        Msg msg = createRandomMsg(uid);
        msgs.add(msg);
        msg.setId(dao.insert(msg));
        assertThat(cacheHandler.get(key, type), nullValue());
        ArrayList<Msg> actual = dao.getArrayMsgs(uid);
        assertThat(actual, hasSize(msgs.size()));
        assertThat(actual, contains(msgs.toArray()));
        @SuppressWarnings("unchecked")
        ArrayList<Msg> cacheActual = (ArrayList<Msg>) cacheHandler.get(key, type);
        assertThat(cacheActual, hasSize(msgs.size()));
        assertThat(cacheActual, contains(msgs.toArray()));
    }
    ArrayList<Msg> actual = dao.getArrayMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    Msg msg = msgs.get(0);
    msg.setContent("ash");
    dao.update(msg);
    assertThat(cacheHandler.get(key, type), nullValue());
    actual = dao.getArrayMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    msg = msgs.remove(0);
    dao.delete(msg.getUid(), msg.getId());
    actual = dao.getArrayMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
}

@Test
public void testSingleKeyReturnList() throws Exception {
    Type type = new TypeToken<List<Msg>>() {
    }.getType();
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    List<Msg> msgs = new ArrayList<Msg>();
    MsgDao dao = mango.create(MsgDao.class, cacheHandler);
    int uid = 100;
    String key = getMsgKey(uid);
    for (int i = 0; i < 3; i++) {
        Msg msg = createRandomMsg(uid);
        msgs.add(msg);
        msg.setId(dao.insert(msg));
        assertThat(cacheHandler.get(key, type), nullValue());
        List<Msg> actual = dao.getMsgs(uid);
        assertThat(actual, hasSize(msgs.size()));
        assertThat(actual, contains(msgs.toArray()));
        @SuppressWarnings("unchecked")
        List<Msg> cacheActual = (List<Msg>) cacheHandler.get(key, type);
        assertThat(cacheActual, hasSize(msgs.size()));
        assertThat(cacheActual, contains(msgs.toArray()));
    }
    List<Msg> actual = dao.getMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    Msg msg = msgs.get(0);
    msg.setContent("ash");
    dao.update(msg);
    assertThat(cacheHandler.get(key, type), nullValue());
    actual = dao.getMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    msg = msgs.remove(0);
    dao.delete(msg.getUid(), msg.getId());
    actual = dao.getMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
}

@Test
public void testSingleKeyReturnList() throws Exception {
    Type type = new TypeToken<List<Msg>>() {
    }.getType();
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    List<Msg> msgs = new ArrayList<Msg>();
    MsgDao dao = mango.create(MsgDao.class);
    int uid = 100;
    String key = getMsgKey(uid);
    for (int i = 0; i < 3; i++) {
        Msg msg = createRandomMsg(uid);
        msgs.add(msg);
        msg.setId(dao.insert(msg));
        assertThat(cacheHandler.get(key, type), nullValue());
        List<Msg> actual = dao.getMsgs(uid);
        assertThat(actual, hasSize(msgs.size()));
        assertThat(actual, contains(msgs.toArray()));
        @SuppressWarnings("unchecked")
        List<Msg> cacheActual = (List<Msg>) cacheHandler.get(key, type);
        assertThat(cacheActual, hasSize(msgs.size()));
        assertThat(cacheActual, contains(msgs.toArray()));
    }
    List<Msg> actual = dao.getMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    Msg msg = msgs.get(0);
    msg.setContent("ash");
    dao.update(msg);
    assertThat(cacheHandler.get(key, type), nullValue());
    actual = dao.getMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    msg = msgs.remove(0);
    dao.delete(msg.getUid(), msg.getId());
    actual = dao.getMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
}

@Test
public void testMultiKeysReturnList() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    List<User> users = createRandomUsers(5);
    List<Integer> ids = new ArrayList<Integer>();
    for (User user : users) {
        int id = dao.insert(user);
        user.setId(id);
        ids.add(id);
    }
    List<User> actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    for (int i = 0; i < ids.size(); i++) {
        Integer id = ids.get(i);
        User user = users.get(i);
        assertThat((User) cacheHandler.get(getUserKey(id), TypeToken.of(User.class).getType()), equalTo(user));
    }
    actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    cacheHandler.delete(getUserKey(users.get(0).getId()));
    actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
}

@Test
public void testMultiKeysReturnList() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    UserDao dao = mango.create(UserDao.class);
    List<User> users = createRandomUsers(5);
    List<Integer> ids = new ArrayList<Integer>();
    for (User user : users) {
        int id = dao.insert(user);
        user.setId(id);
        ids.add(id);
    }
    List<User> actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    for (int i = 0; i < ids.size(); i++) {
        Integer id = ids.get(i);
        User user = users.get(i);
        assertThat((User) cacheHandler.get(getUserKey(id), TypeToken.of(User.class).getType()), equalTo(user));
    }
    actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    cacheHandler.delete(getUserKey(users.get(0).getId()));
    actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
}

@SuppressWarnings("unchecked")
@Test
public void testSingleKeyReturnList2() throws Exception {
    Type type = new TypeToken<List<Msg>>() {
    }.getType();
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    List<Msg> msgs = new ArrayList<Msg>();
    MsgDao dao = mango.create(MsgDao.class, cacheHandler);
    int uid = 100;
    String key = getMsgKey(uid);
    List<Msg> actual = dao.getMsgs(uid);
    assertThat(actual, hasSize(0));
    List<Msg> cacheActual = (List<Msg>) cacheHandler.get(key, type);
    assertThat(cacheActual, hasSize(0));
    Msg msg = createRandomMsg(uid);
    msgs.add(msg);
    msg.setId(dao.insert(msg));
    actual = dao.getMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    cacheActual = (List<Msg>) cacheHandler.get(key, type);
    assertThat(cacheActual, hasSize(msgs.size()));
    assertThat(cacheActual, contains(msgs.toArray()));
    msg = msgs.remove(0);
    dao.delete(msg.getUid(), msg.getId());
    actual = dao.getMsgs(uid);
    assertThat(actual, hasSize(0));
    cacheActual = (List<Msg>) cacheHandler.get(key, type);
    assertThat(cacheActual, hasSize(0));
}

@SuppressWarnings("unchecked")
@Test
public void testSingleKeyReturnList2() throws Exception {
    Type type = new TypeToken<List<Msg>>() {
    }.getType();
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    List<Msg> msgs = new ArrayList<Msg>();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    MsgDao dao = mango.create(MsgDao.class);
    int uid = 100;
    String key = getMsgKey(uid);
    List<Msg> actual = dao.getMsgs(uid);
    assertThat(actual, hasSize(0));
    List<Msg> cacheActual = (List<Msg>) cacheHandler.get(key, type);
    assertThat(cacheActual, hasSize(0));
    Msg msg = createRandomMsg(uid);
    msgs.add(msg);
    msg.setId(dao.insert(msg));
    actual = dao.getMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    cacheActual = (List<Msg>) cacheHandler.get(key, type);
    assertThat(cacheActual, hasSize(msgs.size()));
    assertThat(cacheActual, contains(msgs.toArray()));
    msg = msgs.remove(0);
    dao.delete(msg.getUid(), msg.getId());
    actual = dao.getMsgs(uid);
    assertThat(actual, hasSize(0));
    cacheActual = (List<Msg>) cacheHandler.get(key, type);
    assertThat(cacheActual, hasSize(0));
}

@Test
public void testMultiKeysReturnSet() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    List<User> users = createRandomUsers(5);
    List<Integer> ids = new ArrayList<Integer>();
    for (User user : users) {
        int id = dao.insert(user);
        user.setId(id);
        ids.add(id);
    }
    Set<User> actual = dao.getUserSet(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    for (int i = 0; i < ids.size(); i++) {
        Integer id = ids.get(i);
        User user = users.get(i);
        assertThat((User) cacheHandler.get(getUserKey(id), TypeToken.of(User.class).getType()), equalTo(user));
    }
    actual = dao.getUserSet(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    cacheHandler.delete(getUserKey(users.get(0).getId()));
    actual = dao.getUserSet(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
}

@Test
public void testMultiKeysReturnSet() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    UserDao dao = mango.create(UserDao.class);
    List<User> users = createRandomUsers(5);
    List<Integer> ids = new ArrayList<Integer>();
    for (User user : users) {
        int id = dao.insert(user);
        user.setId(id);
        ids.add(id);
    }
    Set<User> actual = dao.getUserSet(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    for (int i = 0; i < ids.size(); i++) {
        Integer id = ids.get(i);
        User user = users.get(i);
        assertThat((User) cacheHandler.get(getUserKey(id), TypeToken.of(User.class).getType()), equalTo(user));
    }
    actual = dao.getUserSet(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    cacheHandler.delete(getUserKey(users.get(0).getId()));
    actual = dao.getUserSet(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
}

@Test
public void testBatchUpdate() throws Exception {
    Type type = TypeToken.of(User.class).getType();
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    List<User> users = createRandomUsers(5);
    List<Integer> ids = new ArrayList<Integer>();
    for (User user : users) {
        int id = dao.insert(user);
        user.setId(id);
        ids.add(id);
    }
    for (Integer id : ids) {
        assertThat(cacheHandler.get(getUserKey(id), type), nullValue());
    }
    dao.getUserList(ids);
    for (int i = 0; i < ids.size(); i++) {
        int id = ids.get(i);
        User user = users.get(i);
        assertThat((User) cacheHandler.get(getUserKey(id), type), equalTo(user));
    }
    List<User> newUsers = new ArrayList<User>();
    String name = "ash";
    for (int i = 0; i < ids.size(); i++) {
        int id = ids.get(i);
        User newUser = new User();
        newUser.setId(id);
        newUser.setName(name);
        newUsers.add(newUser);
        users.get(i).setName(name);
    }
    int[] r = dao.batchUpdate(newUsers);
    assertThat(r.length, greaterThan(0));
    for (int i = 0; i < ids.size(); i++) {
        int id = ids.get(i);
        assertThat(cacheHandler.get(getUserKey(id), type), nullValue());
    }
    List<User> actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
}

@Test
public void testBatchUpdate() throws Exception {
    Type type = TypeToken.of(User.class).getType();
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    UserDao dao = mango.create(UserDao.class);
    List<User> users = createRandomUsers(5);
    List<Integer> ids = new ArrayList<Integer>();
    for (User user : users) {
        int id = dao.insert(user);
        user.setId(id);
        ids.add(id);
    }
    for (Integer id : ids) {
        assertThat(cacheHandler.get(getUserKey(id), type), nullValue());
    }
    dao.getUserList(ids);
    for (int i = 0; i < ids.size(); i++) {
        int id = ids.get(i);
        User user = users.get(i);
        assertThat((User) cacheHandler.get(getUserKey(id), type), equalTo(user));
    }
    List<User> newUsers = new ArrayList<User>();
    String name = "ash";
    for (int i = 0; i < ids.size(); i++) {
        int id = ids.get(i);
        User newUser = new User();
        newUser.setId(id);
        newUser.setName(name);
        newUsers.add(newUser);
        users.get(i).setName(name);
    }
    int[] r = dao.batchUpdate(newUsers);
    assertThat(r.length, greaterThan(0));
    for (int i = 0; i < ids.size(); i++) {
        int id = ids.get(i);
        assertThat(cacheHandler.get(getUserKey(id), type), nullValue());
    }
    List<User> actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
}

@Test
public void testUpdateWithInStatement() throws Exception {
    Type type = TypeToken.of(User.class).getType();
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    List<User> users = createRandomUsers(5);
    List<Integer> ids = new ArrayList<Integer>();
    for (User user : users) {
        int id = dao.insert(user);
        user.setId(id);
        ids.add(id);
    }
    for (Integer id : ids) {
        assertThat(cacheHandler.get(getUserKey(id), type), nullValue());
    }
    dao.getUserList(ids);
    for (int i = 0; i < ids.size(); i++) {
        int id = ids.get(i);
        User user = users.get(i);
        assertThat((User) cacheHandler.get(getUserKey(id), type), equalTo(user));
    }
    String name = "ash";
    int r = dao.updateWithInStatement(ids, name);
    assertThat(r, greaterThan(0));
    for (int i = 0; i < ids.size(); i++) {
        int id = ids.get(i);
        assertThat(cacheHandler.get(getUserKey(id), type), nullValue());
        users.get(i).setName(name);
    }
    List<User> actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
}

@Test
public void testUpdateWithInStatement() throws Exception {
    Type type = TypeToken.of(User.class).getType();
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    UserDao dao = mango.create(UserDao.class);
    List<User> users = createRandomUsers(5);
    List<Integer> ids = new ArrayList<Integer>();
    for (User user : users) {
        int id = dao.insert(user);
        user.setId(id);
        ids.add(id);
    }
    for (Integer id : ids) {
        assertThat(cacheHandler.get(getUserKey(id), type), nullValue());
    }
    dao.getUserList(ids);
    for (int i = 0; i < ids.size(); i++) {
        int id = ids.get(i);
        User user = users.get(i);
        assertThat((User) cacheHandler.get(getUserKey(id), type), equalTo(user));
    }
    String name = "ash";
    int r = dao.updateWithInStatement(ids, name);
    assertThat(r, greaterThan(0));
    for (int i = 0; i < ids.size(); i++) {
        int id = ids.get(i);
        assertThat(cacheHandler.get(getUserKey(id), type), nullValue());
        users.get(i).setName(name);
    }
    List<User> actual = dao.getUserList(ids);
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
}

@Test
public void testBatchUpdateEmpetyList() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    List<User> users = Lists.newArrayList();
    assertThat(dao.batchUpdate(users).length, equalTo(0));
}

@Test
public void testBatchUpdateEmpetyList() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    UserDao dao = mango.create(UserDao.class);
    List<User> users = Lists.newArrayList();
    assertThat(dao.batchUpdate(users).length, equalTo(0));
}

@Test
public void testReturnLinkedList() throws Exception {
    Type type = new TypeToken<LinkedList<Msg>>() {
    }.getType();
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    List<Msg> msgs = new ArrayList<Msg>();
    MsgDao dao = mango.create(MsgDao.class, cacheHandler);
    int uid = 100;
    String key = getMsgKey(uid);
    for (int i = 0; i < 3; i++) {
        Msg msg = createRandomMsg(uid);
        msgs.add(msg);
        msg.setId(dao.insert(msg));
        assertThat(cacheHandler.get(key, type), nullValue());
        LinkedList<Msg> actual = dao.getLinkedMsgs(uid);
        assertThat(actual, hasSize(msgs.size()));
        assertThat(actual, contains(msgs.toArray()));
        @SuppressWarnings("unchecked")
        LinkedList<Msg> cacheActual = (LinkedList<Msg>) cacheHandler.get(key, type);
        assertThat(cacheActual, hasSize(msgs.size()));
        assertThat(cacheActual, contains(msgs.toArray()));
    }
    LinkedList<Msg> actual = dao.getLinkedMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    Msg msg = msgs.get(0);
    msg.setContent("ash");
    dao.update(msg);
    assertThat(cacheHandler.get(key, type), nullValue());
    actual = dao.getLinkedMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    msg = msgs.remove(0);
    dao.delete(msg.getUid(), msg.getId());
    actual = dao.getLinkedMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
}

@Test
public void testReturnLinkedList() throws Exception {
    Type type = new TypeToken<LinkedList<Msg>>() {
    }.getType();
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    List<Msg> msgs = new ArrayList<Msg>();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    MsgDao dao = mango.create(MsgDao.class);
    int uid = 100;
    String key = getMsgKey(uid);
    for (int i = 0; i < 3; i++) {
        Msg msg = createRandomMsg(uid);
        msgs.add(msg);
        msg.setId(dao.insert(msg));
        assertThat(cacheHandler.get(key, type), nullValue());
        LinkedList<Msg> actual = dao.getLinkedMsgs(uid);
        assertThat(actual, hasSize(msgs.size()));
        assertThat(actual, contains(msgs.toArray()));
        @SuppressWarnings("unchecked")
        LinkedList<Msg> cacheActual = (LinkedList<Msg>) cacheHandler.get(key, type);
        assertThat(cacheActual, hasSize(msgs.size()));
        assertThat(cacheActual, contains(msgs.toArray()));
    }
    LinkedList<Msg> actual = dao.getLinkedMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    Msg msg = msgs.get(0);
    msg.setContent("ash");
    dao.update(msg);
    assertThat(cacheHandler.get(key, type), nullValue());
    actual = dao.getLinkedMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
    msg = msgs.remove(0);
    dao.delete(msg.getUid(), msg.getId());
    actual = dao.getLinkedMsgs(uid);
    assertThat(actual, hasSize(msgs.size()));
    assertThat(actual, contains(msgs.toArray()));
}

@Test
public void testMultiKeysReturnArray() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    List<User> users = createRandomUsers(5);
    List<Integer> ids = new ArrayList<Integer>();
    for (User user : users) {
        int id = dao.insert(user);
        user.setId(id);
        ids.add(id);
    }
    List<User> actual = Arrays.asList(dao.getUserArray(ids));
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    for (int i = 0; i < ids.size(); i++) {
        Integer id = ids.get(i);
        User user = users.get(i);
        assertThat((User) cacheHandler.get(getUserKey(id), TypeToken.of(User.class).getType()), equalTo(user));
    }
    actual = Arrays.asList(dao.getUserArray(ids));
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    cacheHandler.delete(getUserKey(users.get(0).getId()));
    actual = Arrays.asList(dao.getUserArray(ids));
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
}

@Test
public void testMultiKeysReturnArray() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    UserDao dao = mango.create(UserDao.class);
    List<User> users = createRandomUsers(5);
    List<Integer> ids = new ArrayList<Integer>();
    for (User user : users) {
        int id = dao.insert(user);
        user.setId(id);
        ids.add(id);
    }
    List<User> actual = Arrays.asList(dao.getUserArray(ids));
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    for (int i = 0; i < ids.size(); i++) {
        Integer id = ids.get(i);
        User user = users.get(i);
        assertThat((User) cacheHandler.get(getUserKey(id), TypeToken.of(User.class).getType()), equalTo(user));
    }
    actual = Arrays.asList(dao.getUserArray(ids));
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
    cacheHandler.delete(getUserKey(users.get(0).getId()));
    actual = Arrays.asList(dao.getUserArray(ids));
    assertThat(actual, hasSize(users.size()));
    assertThat(actual, containsInAnyOrder(users.toArray()));
}

@Test
public void testUpdateEmpty() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCompatibleWithEmptyList(true);
    mango.setConfig(newConfig);
    assertThat(dao.updateWithInStatement(new ArrayList<Integer>(), "ash"), equalTo(0));
    mango.setConfig(oldConfig);
}

@Test
public void testUpdateEmpty() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    UserDao dao = mango.create(UserDao.class);
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCompatibleWithEmptyList(true);
    mango.setConfig(newConfig);
    assertThat(dao.updateWithInStatement(new ArrayList<Integer>(), "ash"), equalTo(0));
    mango.setConfig(oldConfig);
}

@Test
public void testQueryEmpty() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    UserDao dao = mango.create(UserDao.class, cacheHandler);
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCompatibleWithEmptyList(true);
    mango.setConfig(newConfig);
    assertThat(dao.getUserArray(new ArrayList<Integer>()).length, equalTo(0));
    mango.setConfig(oldConfig);
}

@Test
public void testQueryEmpty() throws Exception {
    MockRedisCacheHandler cacheHandler = new MockRedisCacheHandler();
    Mango mango = Mango.newInstance(ds);
    mango.setCacheHandler(cacheHandler);
    UserDao dao = mango.create(UserDao.class);
    Config oldConfig = mango.getConfig();
    Config newConfig = new Config();
    newConfig.setCompatibleWithEmptyList(true);
    mango.setConfig(newConfig);
    assertThat(dao.getUserArray(new ArrayList<Integer>()).length, equalTo(0));
    mango.setConfig(oldConfig);
}