@Test
public void testIntegerOpen() {
    Schema schema = schema().mapper("name", integerMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42, null, true, false);
    Query query = rangeCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", null, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testIntegerOpen() {
    Schema schema = schema().mapper("name", integerMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42, null, true, false);
    Query query = rangeCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", null, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
}

@Test
public void testInetV6() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    RangeCondition rangeCondition = range("name").boost(0.5f).lower("2001:DB8:2de::e13").upper("2001:DB8:02de::e23").includeLower(true).includeUpper(true).build();
    Query query = rangeCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermRangeQuery.class, query.getClass());
    TermRangeQuery termRangeQuery = (TermRangeQuery) query;
    assertEquals("Query field is wrong", "name", termRangeQuery.getField());
    assertEquals("Query lower is wrong", "2001:db8:2de:0:0:0:0:e13", termRangeQuery.getLowerTerm().utf8ToString());
    assertEquals("Query upper is wrong", "2001:db8:2de:0:0:0:0:e23", termRangeQuery.getUpperTerm().utf8ToString());
    assertEquals("Query include lower is wrong", true, termRangeQuery.includesLower());
    assertEquals("Query include upper is wrong", true, termRangeQuery.includesUpper());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testInetV6() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    RangeCondition rangeCondition = range("name").boost(0.5f).lower("2001:DB8:2de::e13").upper("2001:DB8:02de::e23").includeLower(true).includeUpper(true).build();
    Query query = rangeCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermRangeQuery.class, query.getClass());
    TermRangeQuery termRangeQuery = (TermRangeQuery) query;
    assertEquals("Query field is wrong", "name", termRangeQuery.getField());
    assertEquals("Query lower is wrong", "2001:db8:2de:0:0:0:0:e13", termRangeQuery.getLowerTerm().utf8ToString());
    assertEquals("Query upper is wrong", "2001:db8:2de:0:0:0:0:e23", termRangeQuery.getUpperTerm().utf8ToString());
    assertEquals("Query include lower is wrong", true, termRangeQuery.includesLower());
    assertEquals("Query include upper is wrong", true, termRangeQuery.includesUpper());
}

@Test
public void testFloatOpen() {
    Schema schema = schema().mapper("name", floatMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42.42f, null, true, false);
    Query query = rangeCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42.42f, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", null, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testFloatOpen() {
    Schema schema = schema().mapper("name", floatMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42.42f, null, true, false);
    Query query = rangeCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42.42f, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", null, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
}

@Test
public void testLongOpen() {
    Schema schema = schema().mapper("name", longMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42f, null, true, false);
    Query query = rangeCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42L, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", null, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testLongOpen() {
    Schema schema = schema().mapper("name", longMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42f, null, true, false);
    Query query = rangeCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42L, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", null, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
}

@Test
public void testStringClose() {
    Schema schema = schema().mapper("name", stringMapper()).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", "alpha", "beta", true, true);
    Query query = rangeCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermRangeQuery.class, query.getClass());
    TermRangeQuery termRangeQuery = (TermRangeQuery) query;
    assertEquals("Query field is wrong", "name", termRangeQuery.getField());
    assertEquals("Query lower is wrong", "alpha", termRangeQuery.getLowerTerm().utf8ToString());
    assertEquals("Query upper is wrong", "beta", termRangeQuery.getUpperTerm().utf8ToString());
    assertEquals("Query include lower is wrong", true, termRangeQuery.includesLower());
    assertEquals("Query include upper is wrong", true, termRangeQuery.includesUpper());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testStringClose() {
    Schema schema = schema().mapper("name", stringMapper()).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", "alpha", "beta", true, true);
    Query query = rangeCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermRangeQuery.class, query.getClass());
    TermRangeQuery termRangeQuery = (TermRangeQuery) query;
    assertEquals("Query field is wrong", "name", termRangeQuery.getField());
    assertEquals("Query lower is wrong", "alpha", termRangeQuery.getLowerTerm().utf8ToString());
    assertEquals("Query upper is wrong", "beta", termRangeQuery.getUpperTerm().utf8ToString());
    assertEquals("Query include lower is wrong", true, termRangeQuery.includesLower());
    assertEquals("Query include upper is wrong", true, termRangeQuery.includesUpper());
}

@Test
public void testIntegerClose() {
    Schema schema = schema().mapper("name", integerMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42, 43, false, false);
    Query query = rangeCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", 43, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", false, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testIntegerClose() {
    Schema schema = schema().mapper("name", integerMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42, 43, false, false);
    Query query = rangeCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", 43, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", false, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
}

@Test
public void testDoubleClose() {
    Schema schema = schema().mapper("name", doubleMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42.42D, 43.42D, false, false);
    Query query = rangeCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42.42D, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", 43.42D, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", false, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testDoubleClose() {
    Schema schema = schema().mapper("name", doubleMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42.42D, 43.42D, false, false);
    Query query = rangeCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42.42D, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", 43.42D, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", false, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
}

@Test
public void testDoubleOpen() {
    Schema schema = schema().mapper("name", doubleMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42.42D, null, true, false);
    Query query = rangeCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42.42D, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", null, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testDoubleOpen() {
    Schema schema = schema().mapper("name", doubleMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42.42D, null, true, false);
    Query query = rangeCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42.42D, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", null, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
}

@Test
public void testFloatClose() {
    Schema schema = schema().mapper("name", floatMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42.42D, 43.42F, false, false);
    Query query = rangeCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42.42F, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", 43.42f, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", false, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testFloatClose() {
    Schema schema = schema().mapper("name", floatMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42.42D, 43.42F, false, false);
    Query query = rangeCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42.42F, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", 43.42f, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", false, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
}

@Test
public void testInetV4() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", "192.168.0.01", "192.168.0.045", true, true);
    Query query = rangeCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermRangeQuery.class, query.getClass());
    TermRangeQuery termRangeQuery = (TermRangeQuery) query;
    assertEquals("Query field is wrong", "name", termRangeQuery.getField());
    assertEquals("Query lower is wrong", "192.168.0.1", termRangeQuery.getLowerTerm().utf8ToString());
    assertEquals("Query upper is wrong", "192.168.0.45", termRangeQuery.getUpperTerm().utf8ToString());
    assertEquals("Query include lower is wrong", true, termRangeQuery.includesLower());
    assertEquals("Query include upper is wrong", true, termRangeQuery.includesUpper());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testInetV4() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", "192.168.0.01", "192.168.0.045", true, true);
    Query query = rangeCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermRangeQuery.class, query.getClass());
    TermRangeQuery termRangeQuery = (TermRangeQuery) query;
    assertEquals("Query field is wrong", "name", termRangeQuery.getField());
    assertEquals("Query lower is wrong", "192.168.0.1", termRangeQuery.getLowerTerm().utf8ToString());
    assertEquals("Query upper is wrong", "192.168.0.45", termRangeQuery.getUpperTerm().utf8ToString());
    assertEquals("Query include lower is wrong", true, termRangeQuery.includesLower());
    assertEquals("Query include upper is wrong", true, termRangeQuery.includesUpper());
}

@Test
public void testStringOpen() {
    Schema schema = schema().mapper("name", stringMapper()).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", "alpha", null, true, false);
    Query query = rangeCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermRangeQuery.class, query.getClass());
    TermRangeQuery termRangeQuery = (TermRangeQuery) query;
    assertEquals("Query field is wrong", "name", termRangeQuery.getField());
    assertEquals("Query lower is wrong", "alpha", termRangeQuery.getLowerTerm().utf8ToString());
    assertEquals("Query upper is wrong", null, termRangeQuery.getUpperTerm());
    assertEquals("Query include lower is wrong", true, termRangeQuery.includesLower());
    assertEquals("Query include upper is wrong", false, termRangeQuery.includesUpper());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testStringOpen() {
    Schema schema = schema().mapper("name", stringMapper()).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", "alpha", null, true, false);
    Query query = rangeCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermRangeQuery.class, query.getClass());
    TermRangeQuery termRangeQuery = (TermRangeQuery) query;
    assertEquals("Query field is wrong", "name", termRangeQuery.getField());
    assertEquals("Query lower is wrong", "alpha", termRangeQuery.getLowerTerm().utf8ToString());
    assertEquals("Query upper is wrong", null, termRangeQuery.getUpperTerm());
    assertEquals("Query include lower is wrong", true, termRangeQuery.includesLower());
    assertEquals("Query include upper is wrong", false, termRangeQuery.includesUpper());
}

@Test
public void testLongClose() {
    Schema schema = schema().mapper("name", longMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42L, 43, false, false);
    Query query = rangeCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42L, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", 43L, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", false, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testLongClose() {
    Schema schema = schema().mapper("name", longMapper().boost(1f)).build();
    RangeCondition rangeCondition = new RangeCondition(0.5f, "name", 42L, 43, false, false);
    Query query = rangeCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query field is wrong", "name", numericRangeQuery.getField());
    assertEquals("Query lower is wrong", 42L, numericRangeQuery.getMin());
    assertEquals("Query upper is wrong", 43L, numericRangeQuery.getMax());
    assertEquals("Query include lower is wrong", false, numericRangeQuery.includesMin());
    assertEquals("Query include upper is wrong", false, numericRangeQuery.includesMax());
}


@Test
public void testString() {
    Schema schema = schema().mapper("name", stringMapper().indexed(true).sorted(true)).build();
    RegexpCondition condition = new RegexpCondition(0.5f, "name", "tr*");
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", RegexpQuery.class, query.getClass());
    RegexpQuery regexQuery = (RegexpQuery) query;
    assertEquals("Query field is wrong", "name", regexQuery.getField());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testString() {
    Schema schema = schema().mapper("name", stringMapper().indexed(true).sorted(true)).build();
    RegexpCondition condition = new RegexpCondition(0.5f, "name", "tr*");
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", RegexpQuery.class, query.getClass());
    RegexpQuery regexQuery = (RegexpQuery) query;
    assertEquals("Query field is wrong", "name", regexQuery.getField());
}

@Override
@SuppressWarnings("unchecked")
public final Query query(Schema schema) {
    Mapper mapper = schema.getMapper(field);
    if (mapper == null) {
        throw new IndexException("No mapper found for field '%s'", field);
    } else if (!SingleColumnMapper.class.isAssignableFrom(mapper.getClass())) {
        throw new IndexException("Field '%s' requires a mapper of type '%s' but found '%s'", field, SingleColumnMapper.class.getSimpleName(), mapper);
    }
    return query((SingleColumnMapper<?>) mapper, schema.getAnalyzer());
}

@Test
public void testBlankValue() {
    Schema schema = schema().mapper("name", stringMapper().indexed(true).sorted(true)).build();
    RegexpCondition condition = new RegexpCondition(0.5f, "name", " ");
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", RegexpQuery.class, query.getClass());
    RegexpQuery regexQuery = (RegexpQuery) query;
    assertEquals("Query field is wrong", "name", regexQuery.getField());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testBlankValue() {
    Schema schema = schema().mapper("name", stringMapper().indexed(true).sorted(true)).build();
    RegexpCondition condition = new RegexpCondition(0.5f, "name", " ");
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", RegexpQuery.class, query.getClass());
    RegexpQuery regexQuery = (RegexpQuery) query;
    assertEquals("Query field is wrong", "name", regexQuery.getField());
}

@Test
public void testInetV6() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    RegexpCondition regexpCondition = new RegexpCondition(0.5f, "name", "2001:db8:2de:0:0:0:0:e*");
    Query query = regexpCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", RegexpQuery.class, query.getClass());
    RegexpQuery regexQuery = (RegexpQuery) query;
    assertEquals("Query field is wrong", "name", regexQuery.getField());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testInetV6() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    RegexpCondition regexpCondition = new RegexpCondition(0.5f, "name", "2001:db8:2de:0:0:0:0:e*");
    Query query = regexpCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", RegexpQuery.class, query.getClass());
    RegexpQuery regexQuery = (RegexpQuery) query;
    assertEquals("Query field is wrong", "name", regexQuery.getField());
}

@Test
public void testInetV4() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    RegexpCondition condition = new RegexpCondition(0.5f, "name", "192.168.*");
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", RegexpQuery.class, query.getClass());
    RegexpQuery regexQuery = (RegexpQuery) query;
    assertEquals("Query field is wrong", "name", regexQuery.getField());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testInetV4() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    RegexpCondition condition = new RegexpCondition(0.5f, "name", "192.168.*");
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", RegexpQuery.class, query.getClass());
    RegexpQuery regexQuery = (RegexpQuery) query;
    assertEquals("Query field is wrong", "name", regexQuery.getField());
}


@Test
public void testGetMapper() {
    Schema schema = schema().mapper("field", stringMapper()).build();
    SingleColumnCondition condition = new SingleColumnCondition(null, "field") {

        @Override
        public Query query(SingleColumnMapper<?> mapper, Analyzer analyzer) {
            return new MatchAllDocsQuery();
        }
    };
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", MatchAllDocsQuery.class, query.getClass());
}

@Test
public void testGetMapper() {
    Schema schema = schema().mapper("field", stringMapper()).build();
    SingleColumnCondition condition = new MockCondition(null, "field");
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", MatchAllDocsQuery.class, query.getClass());
}

@Test(expected = IndexException.class)
public void testGetMapperNotFound() {
    Schema schema = schema().build();
    SingleColumnCondition condition = new SingleColumnCondition(null, "field") {

        @Override
        public Query query(SingleColumnMapper<?> mapper, Analyzer analyzer) {
            return new MatchAllDocsQuery();
        }
    };
    condition.query(schema);
}

@Test(expected = IndexException.class)
public void testGetMapperNotFound() {
    Schema schema = schema().build();
    SingleColumnCondition condition = new MockCondition(null, "field");
    condition.query(schema);
}


@Test
public void testInetV4Value() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    WildcardCondition wildcardCondition = new WildcardCondition(0.5f, "name", "192.168.*");
    Query query = wildcardCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Expected wildcard query", WildcardQuery.class, query.getClass());
    WildcardQuery wildcardQuery = (WildcardQuery) query;
    assertEquals("Field name is not properly set", "name", wildcardQuery.getField());
    assertEquals("Term text is not properly set", "192.168.*", wildcardQuery.getTerm().text());
    assertEquals("Boost is not properly set", 0.5f, query.getBoost(), 0);
}

@Test
public void testInetV4Value() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    WildcardCondition wildcardCondition = new WildcardCondition(0.5f, "name", "192.168.*");
    Query query = wildcardCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Expected wildcard query", WildcardQuery.class, query.getClass());
    WildcardQuery wildcardQuery = (WildcardQuery) query;
    assertEquals("Field name is not properly set", "name", wildcardQuery.getField());
    assertEquals("Term text is not properly set", "192.168.*", wildcardQuery.getTerm().text());
}

@Test
public void testBlankValue() {
    Schema schema = schema().mapper("name", stringMapper().indexed(true).sorted(true)).build();
    WildcardCondition wildcardCondition = new WildcardCondition(0.5f, "name", " ");
    Query query = wildcardCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Expected wildcard query", WildcardQuery.class, query.getClass());
    WildcardQuery wildcardQuery = (WildcardQuery) query;
    assertEquals("Field name is not properly set", "name", wildcardQuery.getField());
    assertEquals("Term text is not properly set", " ", wildcardQuery.getTerm().text());
    assertEquals("Boost is not properly set", 0.5f, query.getBoost(), 0);
}

@Test
public void testBlankValue() {
    Schema schema = schema().mapper("name", stringMapper().indexed(true).sorted(true)).build();
    WildcardCondition wildcardCondition = new WildcardCondition(0.5f, "name", " ");
    Query query = wildcardCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Expected wildcard query", WildcardQuery.class, query.getClass());
    WildcardQuery wildcardQuery = (WildcardQuery) query;
    assertEquals("Field name is not properly set", "name", wildcardQuery.getField());
    assertEquals("Term text is not properly set", " ", wildcardQuery.getTerm().text());
}

@Test
public void testStringValue() {
    Schema schema = schema().mapper("name", stringMapper().indexed(true).sorted(true)).build();
    WildcardCondition wildcardCondition = new WildcardCondition(0.5f, "name", "tr*");
    Query query = wildcardCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Expected wildcard query", WildcardQuery.class, query.getClass());
    WildcardQuery wildcardQuery = (WildcardQuery) query;
    assertEquals("Field name is not properly set", "name", wildcardQuery.getField());
    assertEquals("Term text is not properly set", "tr*", wildcardQuery.getTerm().text());
    assertEquals("Boost is not properly set", 0.5f, query.getBoost(), 0);
}

@Test
public void testStringValue() {
    Schema schema = schema().mapper("name", stringMapper().indexed(true).sorted(true)).build();
    WildcardCondition wildcardCondition = new WildcardCondition(0.5f, "name", "tr*");
    Query query = wildcardCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Expected wildcard query", WildcardQuery.class, query.getClass());
    WildcardQuery wildcardQuery = (WildcardQuery) query;
    assertEquals("Field name is not properly set", "name", wildcardQuery.getField());
    assertEquals("Term text is not properly set", "tr*", wildcardQuery.getTerm().text());
}

@Test
public void testInetV6Value() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    WildcardCondition condition = new WildcardCondition(0.5f, "name", "2001:db8:2de:0:0:0:0:e*");
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Expected wildcard query", WildcardQuery.class, query.getClass());
    WildcardQuery wildcardQuery = (WildcardQuery) query;
    assertEquals("Field name is not properly set", "name", wildcardQuery.getField());
    assertEquals("Term text is not properly set", "2001:db8:2de:0:0:0:0:e*", wildcardQuery.getTerm().text());
    assertEquals("Boost is not properly set", 0.5f, query.getBoost(), 0);
}

@Test
public void testInetV6Value() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    WildcardCondition condition = new WildcardCondition(0.5f, "name", "2001:db8:2de:0:0:0:0:e*");
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Expected wildcard query", WildcardQuery.class, query.getClass());
    WildcardQuery wildcardQuery = (WildcardQuery) query;
    assertEquals("Field name is not properly set", "name", wildcardQuery.getField());
    assertEquals("Term text is not properly set", "2001:db8:2de:0:0:0:0:e*", wildcardQuery.getTerm().text());
}


@Test
public void testIndexFull() {
    String actual = index("ks", "table", "idx").keyspace("keyspace").directoryPath("path").refreshSeconds(10D).maxCachedMb(32).maxMergeMb(16).ramBufferMb(64).indexingThreads(4).indexingQueuesSize(100).excludedDataCenters("DC1,DC2").tokenRangeCacheSize(20).searchCacheSize(30).defaultAnalyzer("my_analyzer").analyzer("my_analyzer", classpathAnalyzer("my_class")).analyzer("snow", snowballAnalyzer("tartar").stopwords("a,b,c")).mapper("uuid", uuidMapper().validated(true)).mapper("string", stringMapper()).build();
    String expected = "CREATE CUSTOM INDEX idx ON keyspace.table() " + "USING 'com.stratio.cassandra.lucene.Index' " + "WITH OPTIONS = {" + "'refresh_seconds':'10.0'," + "'directory_path':'path'," + "'ram_buffer_mb':'64'," + "'max_merge_mb':'16'," + "'max_cached_mb':'32'," + "'indexing_threads':'4'," + "'indexing_queues_size':'100'," + "'excluded_data_centers':'DC1,DC2'," + "'token_range_cache_size':'20'," + "'search_cache_size':'30'," + "'schema':'{" + "\"analyzers\":{" + "\"my_analyzer\":{\"type\":\"classpath\",\"class\":\"my_class\"}," + "\"snow\":{\"type\":\"snowball\",\"language\":\"tartar\",\"stopwords\":\"a,b,c\"}}," + "\"default_analyzer\":\"my_analyzer\"," + "\"fields\":{" + "\"uuid\":{\"type\":\"uuid\",\"validated\":true}," + "\"string\":{\"type\":\"string\"}}}'}";
    assertEquals("index serialization is wrong", expected, actual);
}

@Test
public void testIndexFull() {
    String actual = index("ks", "table", "idx").keyspace("keyspace").directoryPath("path").refreshSeconds(10D).maxCachedMb(32).maxMergeMb(16).ramBufferMb(64).indexingThreads(4).indexingQueuesSize(100).excludedDataCenters("DC1,DC2").defaultAnalyzer("my_analyzer").analyzer("my_analyzer", classpathAnalyzer("my_class")).analyzer("snow", snowballAnalyzer("tartar").stopwords("a,b,c")).mapper("uuid", uuidMapper().validated(true)).mapper("string", stringMapper()).build();
    String expected = "CREATE CUSTOM INDEX idx ON keyspace.table() " + "USING 'com.stratio.cassandra.lucene.Index' " + "WITH OPTIONS = {" + "'refresh_seconds':'10.0'," + "'directory_path':'path'," + "'ram_buffer_mb':'64'," + "'max_merge_mb':'16'," + "'max_cached_mb':'32'," + "'indexing_threads':'4'," + "'indexing_queues_size':'100'," + "'excluded_data_centers':'DC1,DC2'," + "'schema':'{" + "\"analyzers\":{" + "\"my_analyzer\":{\"type\":\"classpath\",\"class\":\"my_class\"}," + "\"snow\":{\"type\":\"snowball\",\"language\":\"tartar\",\"stopwords\":\"a,b,c\"}}," + "\"default_analyzer\":\"my_analyzer\"," + "\"fields\":{" + "\"uuid\":{\"type\":\"uuid\",\"validated\":true}," + "\"string\":{\"type\":\"string\"}}}'}";
    assertEquals("index serialization is wrong", expected, actual);
}

@Override
public String build() {
    StringBuilder sb = new StringBuilder();
    sb.append("CREATE CUSTOM INDEX ");
    sb.append(name).append(" ");
    String fullTable = keyspace == null ? table : keyspace + "." + table;
    sb.append(String.format("ON %s() ", fullTable));
    sb.append("USING 'com.stratio.cassandra.lucene.Index' WITH OPTIONS = {");
    option(sb, "refresh_seconds", refreshSeconds);
    option(sb, "directory_path", directoryPath);
    option(sb, "ram_buffer_mb", ramBufferMb);
    option(sb, "max_merge_mb", maxMergeMb);
    option(sb, "max_cached_mb", maxCachedMb);
    option(sb, "indexing_threads", indexingThreads);
    option(sb, "indexing_queues_size", indexingQueuesSize);
    option(sb, "excluded_data_centers", excludedDataCenters);
    option(sb, "token_range_cache_size", tokenRangeCacheSize);
    option(sb, "search_cache_size", searchCacheSize);
    sb.append(String.format("'schema':'%s'}", schema));
    return sb.toString();
}

@Override
public String build() {
    StringBuilder sb = new StringBuilder();
    sb.append("CREATE CUSTOM INDEX ");
    sb.append(name).append(" ");
    String fullTable = keyspace == null ? table : keyspace + "." + table;
    sb.append(String.format("ON %s() ", fullTable));
    sb.append("USING 'com.stratio.cassandra.lucene.Index' WITH OPTIONS = {");
    option(sb, "refresh_seconds", refreshSeconds);
    option(sb, "directory_path", directoryPath);
    option(sb, "ram_buffer_mb", ramBufferMb);
    option(sb, "max_merge_mb", maxMergeMb);
    option(sb, "max_cached_mb", maxCachedMb);
    option(sb, "indexing_threads", indexingThreads);
    option(sb, "indexing_queues_size", indexingQueuesSize);
    option(sb, "excluded_data_centers", excludedDataCenters);
    sb.append(String.format("'schema':'%s'}", schema));
    return sb.toString();
}


@Test
public void testCRUD() throws IOException, InterruptedException {
    FSIndex index = new FSIndex("test_index", "com.stratio.cassandra.lucene:type=LuceneIndexes", Paths.get(folder.newFolder("directory" + UUID.randomUUID()).getPath()), new StandardAnalyzer(), REFRESH_SECONDS, IndexOptions.DEFAULT_RAM_BUFFER_MB, IndexOptions.DEFAULT_MAX_MERGE_MB, IndexOptions.DEFAULT_MAX_CACHED_MB, null);
    Sort sort = new Sort(new SortField("field", SortField.Type.STRING));
    Set<String> fields = Sets.newHashSet("field");
    index.init(sort, fields);
    assertEquals("Index must be empty", 0, index.getNumDocs());
    Term term1 = new Term("field", "value1");
    Document document1 = new Document();
    document1.add(new StringField("field", "value1", Field.Store.NO));
    document1.add(new SortedDocValuesField("field", new BytesRef("value1")));
    index.upsert(term1, document1);
    Term term2 = new Term("field", "value2");
    Document document2 = new Document();
    document2.add(new StringField("field", "value2", Field.Store.NO));
    document2.add(new SortedDocValuesField("field", new BytesRef("value2")));
    index.upsert(term2, document2);
    index.commit();
    Thread.sleep(REFRESH_MILLISECONDS);
    assertEquals("Expected 2 documents", 2, index.getNumDocs());
    Query query = new WildcardQuery(new Term("field", "value*"));
    DocumentIterator documentIterator = index.search(query, sort, null, 1);
    List<Document> documents = new ArrayList<>();
    while (documentIterator.hasNext()) {
        documents.add(documentIterator.next().left);
    }
    assertEquals("Expected 2 documents", 2, documents.size());
    index.delete(term1);
    index.commit();
    Thread.sleep(WAIT_MILLISECONDS);
    assertEquals("Expected 1 document", 1, index.getNumDocs());
    index.upsert(term1, document1);
    index.commit();
    Thread.sleep(WAIT_MILLISECONDS);
    assertEquals("Expected 2 documents", 2, index.getNumDocs());
    index.delete(new TermQuery(term1));
    Thread.sleep(WAIT_MILLISECONDS);
    assertEquals("Expected 1 document", 1, index.getNumDocs());
    index.upsert(term1, document1);
    index.upsert(term2, document2);
    index.upsert(term2, document2);
    index.commit();
    Thread.sleep(WAIT_MILLISECONDS);
    assertEquals("Expected 2 documents", 2, index.getNumDocs());
    index.truncate();
    index.commit();
    Thread.sleep(WAIT_MILLISECONDS);
    assertEquals("Expected 0 documents", 0, index.getNumDocs());
    index.delete();
    folder.delete();
}

@Test
public void testCRUD() throws IOException, InterruptedException {
    FSIndex index = new FSIndex("test_index", "com.stratio.cassandra.lucene:type=LuceneIndexes", Paths.get(folder.newFolder("directory" + UUID.randomUUID()).getPath()), new StandardAnalyzer(), REFRESH_SECONDS, IndexOptions.DEFAULT_RAM_BUFFER_MB, IndexOptions.DEFAULT_MAX_MERGE_MB, IndexOptions.DEFAULT_MAX_CACHED_MB);
    Sort sort = new Sort(new SortField("field", SortField.Type.STRING));
    Set<String> fields = Sets.newHashSet("field");
    index.init(sort, fields);
    assertEquals("Index must be empty", 0, index.getNumDocs());
    Term term1 = new Term("field", "value1");
    Document document1 = new Document();
    document1.add(new StringField("field", "value1", Field.Store.NO));
    document1.add(new SortedDocValuesField("field", new BytesRef("value1")));
    index.upsert(term1, document1);
    Term term2 = new Term("field", "value2");
    Document document2 = new Document();
    document2.add(new StringField("field", "value2", Field.Store.NO));
    document2.add(new SortedDocValuesField("field", new BytesRef("value2")));
    index.upsert(term2, document2);
    index.commit();
    Thread.sleep(REFRESH_MILLISECONDS);
    assertEquals("Expected 2 documents", 2, index.getNumDocs());
    Query query = new WildcardQuery(new Term("field", "value*"));
    DocumentIterator documentIterator = index.search(query, sort, null, 1);
    List<Document> documents = new ArrayList<>();
    while (documentIterator.hasNext()) {
        documents.add(documentIterator.next().left);
    }
    assertEquals("Expected 2 documents", 2, documents.size());
    index.delete(term1);
    index.commit();
    Thread.sleep(WAIT_MILLISECONDS);
    assertEquals("Expected 1 document", 1, index.getNumDocs());
    index.upsert(term1, document1);
    index.commit();
    Thread.sleep(WAIT_MILLISECONDS);
    assertEquals("Expected 2 documents", 2, index.getNumDocs());
    index.delete(new TermQuery(term1));
    Thread.sleep(WAIT_MILLISECONDS);
    assertEquals("Expected 1 document", 1, index.getNumDocs());
    index.upsert(term1, document1);
    index.upsert(term2, document2);
    index.upsert(term2, document2);
    index.commit();
    Thread.sleep(WAIT_MILLISECONDS);
    assertEquals("Expected 2 documents", 2, index.getNumDocs());
    index.truncate();
    index.commit();
    Thread.sleep(WAIT_MILLISECONDS);
    assertEquals("Expected 0 documents", 0, index.getNumDocs());
    index.delete();
    folder.delete();
}

@Override
public IndexSearcher newSearcher(IndexReader reader, IndexReader previousReader) {
    if (refreshTask != null) {
        refreshTask.run();
    }
    IndexSearcher searcher = new IndexSearcher(reader);
    searcher.setSimilarity(new NoIDFSimilarity());
    return searcher;
}

@Override
public IndexSearcher newSearcher(IndexReader reader, IndexReader previousReader) {
    IndexSearcher searcher = new IndexSearcher(reader);
    searcher.setSimilarity(new NoIDFSimilarity());
    return searcher;
}


@Test
public void testJson() throws IOException {
    String json1 = "{type:\"geo_distance\",field:\"geo_place\",latitude:0.0,longitude:0.0,reverse:false}";
    String json2 = JsonSerializer.toString(JsonSerializer.fromString(json1, GeoDistanceSortFieldBuilder.class));
    assertEquals("JSON serialization is wrong", json1, json2);
}

@Test
public void testJson() throws IOException {
    String json1 = "{type:\"geo_distance\",field:\"geo_place\",latitude:0.0,longitude:0.0,reverse:false}";
    String json2 = JsonSerializer.toString(JsonSerializer.fromString(json1, SortFieldBuilder.class));
    assertEquals("JSON serialization is wrong", json1, json2);
}

public static String toString(Object value) throws IOException {
    return INSTANCE.mapper.writeValueAsString(value);
}

@Test
public void testJsonReverse() throws IOException {
    String json1 = "{type:\"geo_distance\",field:\"geo_place\",latitude:0.0,longitude:0.0,reverse:false}";
    String json2 = JsonSerializer.toString(JsonSerializer.fromString(json1, GeoDistanceSortFieldBuilder.class));
    assertEquals("JSON serialization is wrong", json1, json2);
}

@Test
public void testJsonReverse() throws IOException {
    String json1 = "{type:\"geo_distance\",field:\"geo_place\",latitude:0.0,longitude:0.0,reverse:false}";
    String json2 = JsonSerializer.toString(JsonSerializer.fromString(json1, SortFieldBuilder.class));
    assertEquals("JSON serialization is wrong", json1, json2);
}

@Test
public void testJsonDefault() throws IOException {
    String json1 = "{type:\"geo_distance\",field:\"geo_place\",latitude:0.0,longitude:0.0,reverse:false}";
    GeoDistanceSortFieldBuilder builder = JsonSerializer.fromString(json1, GeoDistanceSortFieldBuilder.class);
    String json2 = JsonSerializer.toString(builder);
    assertEquals("JSON serialization is wrong", json1, json2);
}

@Test
public void testJsonDefault() throws IOException {
    String json1 = "{type:\"geo_distance\",field:\"geo_place\",latitude:0.0,longitude:0.0,reverse:false}";
    SortFieldBuilder builder = JsonSerializer.fromString(json1, SortFieldBuilder.class);
    String json2 = JsonSerializer.toString(builder);
    assertEquals("JSON serialization is wrong", json1, json2);
}

public static <T> T fromString(String value, Class<T> valueType) throws IOException {
    return INSTANCE.mapper.readValue(value, valueType);
}


@Test
public void testBuildDefaults() {
    SimpleSortField sortField = new SimpleSortField("field", null);
    assertEquals("SortField is not created", "field", sortField.getField());
    assertEquals("SortField reverse is not set to default", SortField.DEFAULT_REVERSE, sortField.isReverse());
}

@Test
public void testBuildDefaults() {
    SimpleSortField sortField = new SimpleSortField("field", null);
    assertEquals("SortField is not created", "field", sortField.getField());
    assertEquals("SortField reverse is not set to default", SortField.DEFAULT_REVERSE, sortField.reverse);
}

public boolean isReverse() {
    return reverse;
}

@Test
public void testBuild() {
    SimpleSortField sortField = new SimpleSortField("field", true);
    assertEquals("SortField is not created", "field", sortField.getField());
    assertTrue("SortField reverse is not set", sortField.isReverse());
}

@Test
public void testBuild() {
    SimpleSortField sortField = new SimpleSortField("field", true);
    assertEquals("SortField is not created", "field", sortField.getField());
    assertTrue("SortField reverse is not set", sortField.reverse);
}


@Test
public void testBuild() {
    String mapper = "geo_place";
    double latitude = 0.0;
    double longitude = 0.0;
    GeoDistanceSortFieldBuilder builder = new GeoDistanceSortFieldBuilder(mapper, longitude, latitude).reverse(true);
    GeoDistanceSortField sortField = builder.build();
    assertNotNull("GeoDistanceSortField is not built", sortField);
    assertEquals("GeoDistanceSortField mapper name is not set", mapper, sortField.getMapper());
    assertEquals("GeoDistanceSortField reverse is not set", true, sortField.isReverse());
    assertTrue("GeoDistanceSortField latitude is not set", latitude == sortField.getLatitude());
    assertTrue("GeoDistanceSortField longitude is not set", longitude == sortField.getLongitude());
}

@Test
public void testBuild() {
    String mapper = "geo_place";
    double latitude = 0.0;
    double longitude = 0.0;
    GeoDistanceSortField field = new GeoDistanceSortFieldBuilder(mapper, longitude, latitude).reverse(true).build();
    assertNotNull("GeoDistanceSortField is not built", field);
    assertEquals("GeoDistanceSortField mapper name is not set", mapper, field.mapper);
    assertEquals("GeoDistanceSortField reverse is not set", true, field.reverse);
    assertTrue("GeoDistanceSortField latitude is not set", latitude == field.latitude);
    assertTrue("GeoDistanceSortField longitude is not set", longitude == field.longitude);
}

public double getLatitude() {
    return latitude;
}

@Test
public void testBuildReverse() {
    String mapper = "geo_place";
    double latitude = 0.0;
    double longitude = 0.0;
    GeoDistanceSortFieldBuilder builder = new GeoDistanceSortFieldBuilder(mapper, longitude, latitude).reverse(false);
    GeoDistanceSortField sortField = builder.build();
    assertNotNull("GeoDistanceSortField is not built", sortField);
    assertEquals("GeoDistanceSortField mapper name is not set", mapper, sortField.getMapper());
    assertEquals("GeoDistanceSortField reverse is not set", false, sortField.isReverse());
}

@Test
public void testBuildReverse() {
    String mapper = "geo_place";
    GeoDistanceSortField field = new GeoDistanceSortFieldBuilder(mapper, 0.0, 0.0).reverse(false).build();
    assertNotNull("GeoDistanceSortField is not built", field);
    assertEquals("GeoDistanceSortField mapper name is not set", mapper, field.mapper);
    assertEquals("GeoDistanceSortField reverse is not set", false, field.reverse);
}

@Test
public void testBuildDefault() {
    String mapper = "geo_place";
    double latitude = 0.0;
    double longitude = 0.0;
    GeoDistanceSortFieldBuilder builder = new GeoDistanceSortFieldBuilder(mapper, longitude, latitude);
    GeoDistanceSortField sortField = builder.build();
    assertNotNull("GeoDistanceSortField is not built", sortField);
    assertEquals("GeoDistanceSortField mapper name is not set", mapper, sortField.getMapper());
    assertEquals("GeoDistanceSortField reverse is not set to default", SortField.DEFAULT_REVERSE, sortField.isReverse());
}

@Test
public void testBuildDefault() {
    String mapper = "geo_place";
    double latitude = 0.0;
    double longitude = 0.0;
    GeoDistanceSortField field = new GeoDistanceSortFieldBuilder(mapper, longitude, latitude).build();
    assertNotNull("GeoDistanceSortField is not built", field);
    assertEquals("GeoDistanceSortField mapper name is not set", mapper, field.mapper);
    assertEquals("GeoDistanceSortField reverse is not properly set", SortField.DEFAULT_REVERSE, field.reverse);
}


@Test
public void testBuildReverse() {
    String field = "field";
    SimpleSortFieldBuilder builder = new SimpleSortFieldBuilder(field).reverse(false);
    SimpleSortField sortField = builder.build();
    assertNotNull("SimpleSortField is not built", sortField);
    assertEquals("SimpleSortField field name is not set", field, sortField.getField());
    assertEquals("SimpleSortField reverse is not set", false, sortField.isReverse());
}

@Test
public void testBuildReverse() {
    String field = "field";
    SimpleSortFieldBuilder builder = new SimpleSortFieldBuilder(field).reverse(false);
    SimpleSortField sortField = builder.build();
    assertNotNull("SimpleSortField is not built", sortField);
    assertEquals("SimpleSortField field name is not set", field, sortField.getField());
    assertEquals("SimpleSortField reverse is not set", false, sortField.reverse);
}

@Test
public void testBuild() {
    String field = "field";
    SimpleSortFieldBuilder builder = new SimpleSortFieldBuilder(field).reverse(true);
    SimpleSortField sortField = builder.build();
    assertNotNull("SimpleSortField is not built", sortField);
    assertEquals("SimpleSortField field name is not set", field, sortField.getField());
    assertEquals("SimpleSortField reverse is not set", true, sortField.isReverse());
}

@Test
public void testBuild() {
    String field = "field";
    SimpleSortFieldBuilder builder = new SimpleSortFieldBuilder(field).reverse(true);
    SimpleSortField sortField = builder.build();
    assertNotNull("SimpleSortField is not built", sortField);
    assertEquals("SimpleSortField field name is not set", field, sortField.getField());
    assertEquals("SimpleSortField reverse is not set", true, sortField.reverse);
}

@Test
public void testBuildDefault() {
    String field = "field";
    SimpleSortFieldBuilder builder = new SimpleSortFieldBuilder(field);
    SimpleSortField sortField = builder.build();
    assertNotNull("SimpleSortField is not built", sortField);
    assertEquals("SimpleSortField field name is not set", field, sortField.getField());
    assertEquals("SimpleSortField reverse is not set to default", SortField.DEFAULT_REVERSE, sortField.isReverse());
}

@Test
public void testBuildDefault() {
    String field = "field";
    SimpleSortFieldBuilder builder = new SimpleSortFieldBuilder(field);
    SimpleSortField sortField = builder.build();
    assertNotNull("SimpleSortField is not built", sortField);
    assertEquals("SimpleSortField field name is not set", field, sortField.getField());
    assertEquals("SimpleSortField reverse is not set to default", SortField.DEFAULT_REVERSE, sortField.reverse);
}


@Override
public Query doQuery(Schema schema) {
    SpatialStrategy strategy;
    Mapper mapper = schema.getMapper(field);
    if (mapper == null) {
        throw new IndexException("No mapper found for field '%s'", field);
    } else if (mapper instanceof GeoShapeMapper) {
        strategy = ((GeoShapeMapper) mapper).strategy;
    } else if (mapper instanceof GeoPointMapper) {
        strategy = ((GeoPointMapper) mapper).distanceStrategy;
    } else {
        throw new IndexException("'geo_shape' search requires a mapper of type 'geo_point' or 'geo_shape' " + "but found %s:%s", field, mapper);
    }
    JtsGeometry transformedGeometry = geometry;
    if (transformations != null) {
        for (GeoTransformation transformation : transformations) {
            transformedGeometry = transformation.apply(transformedGeometry, CONTEXT);
        }
    }
    SpatialArgs args = new SpatialArgs(operation.getSpatialOperation(), transformedGeometry);
    args.setDistErr(0.0);
    return strategy.makeQuery(args);
}

@Override
public Query doQuery(Schema schema) {
    SpatialStrategy strategy;
    Mapper mapper = schema.getMapper(field);
    if (mapper == null) {
        throw new IndexException("No mapper found for field '%s'", field);
    } else if (mapper instanceof GeoShapeMapper) {
        strategy = ((GeoShapeMapper) mapper).strategy;
    } else if (mapper instanceof GeoPointMapper) {
        strategy = ((GeoPointMapper) mapper).distanceStrategy;
    } else {
        throw new IndexException("'geo_shape' search requires a mapper of type 'geo_point' or 'geo_shape' " + "but found %s:%s", field, mapper);
    }
    JtsGeometry transformedGeometry = geometry;
    if (transformations != null) {
        for (GeoTransformation transformation : transformations) {
            transformedGeometry = transformation.apply(transformedGeometry);
        }
    }
    SpatialArgs args = new SpatialArgs(operation.getSpatialOperation(), transformedGeometry);
    args.setDistErr(0.0);
    return strategy.makeQuery(args);
}

@Test
public void testConstructor() throws ParseException {
    List<GeoTransformation> transformationList = new ArrayList<>();
    transformationList.add(new GeoTransformation.Buffer().maxDistance(GeoDistance.parse("2m")).minDistance(GeoDistance.parse("1m")));
    GeoShapeCondition condition = new GeoShapeCondition(0.1f, "geo_point", WKT, GeoOperation.IS_WITHIN, transformationList);
    Shape shape = GeoShapeCondition.CONTEXT.getWktShapeParser().parse(WKT);
    JtsGeometry geo = GeoShapeCondition.CONTEXT.makeShape(GeoShapeCondition.CONTEXT.getGeometryFrom(shape));
    assertEquals("Boost is not set", 0.1f, condition.boost, 0);
    assertEquals("Field is not set", "geo_point", condition.field);
    assertEquals("Geometry is not set", geo, condition.geometry);
    assertEquals("Operation is not set", GeoOperation.IS_WITHIN, condition.operation);
    assertEquals("Transformations is not set", transformationList, condition.transformations);
}

@Test
public void testConstructor() throws ParseException {
    List<GeoTransformation> transformationList = new ArrayList<>();
    transformationList.add(new GeoTransformation.Buffer(GeoDistance.parse("1m"), GeoDistance.parse("2m")));
    GeoShapeCondition condition = new GeoShapeCondition(0.1f, "geo_point", WKT, GeoOperation.IS_WITHIN, transformationList);
    JtsGeometry geo = geometry(WKT);
    assertEquals("Boost is not set", 0.1f, condition.boost, 0);
    assertEquals("Field is not set", "geo_point", condition.field);
    assertEquals("Geometry is not set", geo, condition.geometry);
    assertEquals("Operation is not set", GeoOperation.IS_WITHIN, condition.operation);
    assertEquals("Transformations is not set", transformationList, condition.transformations);
}

public Buffer minDistance(GeoDistance minDistance) {
    this.minDistance = minDistance;
    return this;
}

@Test
public void testConstructorWithDefaults() throws ParseException {
    GeoShapeCondition condition = new GeoShapeCondition(null, "geo_point", WKT, null, null);
    assertNull("Boost is not set to default", condition.boost);
    assertEquals("Field is not set", "geo_point", condition.field);
    Shape shape = GeoShapeCondition.CONTEXT.getWktShapeParser().parse(WKT);
    JtsGeometry geo = GeoShapeCondition.CONTEXT.makeShape(GeoShapeCondition.CONTEXT.getGeometryFrom(shape));
    assertEquals("Geometry is not set", geo, condition.geometry);
    assertEquals("Operation is not set", GeoShapeCondition.DEFAULT_OPERATION, condition.operation);
    assertEquals("Transformations is not set", new ArrayList(), condition.transformations);
}

@Test
public void testConstructorWithDefaults() throws ParseException {
    GeoShapeCondition condition = new GeoShapeCondition(null, "geo_point", WKT, null, null);
    assertNull("Boost is not set to default", condition.boost);
    assertEquals("Field is not set", "geo_point", condition.field);
    JtsGeometry geo = geometry(WKT);
    assertEquals("Geometry is not set", geo, condition.geometry);
    assertEquals("Operation is not set", GeoShapeCondition.DEFAULT_OPERATION, condition.operation);
    assertEquals("Transformations is not set", new ArrayList(), condition.transformations);
}

public static JtsGeometry geometry(String string) {
    if (StringUtils.isBlank(string)) {
        throw new IndexException("Shape shouldn't be blank");
    }
    try {
        GeometryFactory geometryFactory = CONTEXT.getGeometryFactory();
        WKTReader reader = new WKTReader(geometryFactory);
        Geometry geometry = reader.read(string);
        if (!geometry.isValid()) {
            geometry = geometry.buffer(0);
        }
        return CONTEXT.makeShape(geometry);
    } catch (ParseException | IllegalArgumentException e) {
        throw new IndexException(e, "Shape '%s' is not parseable", string);
    }
}


@Test
public void testBufferTransformationParsing() throws IOException {
    String json = "{type:\"buffer\",max_distance:\"1km\",min_distance:\"10m\"}";
    Buffer buffer = JsonSerializer.fromString(json, Buffer.class);
    assertNotNull("JSON serialization is wrong", buffer);
    assertEquals("JSON serialization is wrong", 1.0, buffer.maxDistance().getValue(GeoDistanceUnit.KILOMETRES));
    assertEquals("JSON serialization is wrong", 10.0, buffer.minDistance().getValue(GeoDistanceUnit.METRES));
}

@Test
public void testBufferTransformationParsing() throws IOException {
    String json = "{type:\"buffer\",max_distance:\"1km\",min_distance:\"10m\"}";
    Buffer buffer = JsonSerializer.fromString(json, Buffer.class);
    assertNotNull("JSON serialization is wrong", buffer);
    assertEquals("JSON min distance serialization is wrong", 10.0, buffer.minDistance.getValue(METRES), 0);
    assertEquals("JSON max distance serialization is wrong", 1.0, buffer.maxDistance.getValue(KILOMETRES), 0);
}

public GeoDistance minDistance() {
    return minDistance;
}

@Test
public void testBufferTransformationToString() {
    GeoDistance min = GeoDistance.parse("-1m");
    GeoDistance max = GeoDistance.parse("-2m");
    GeoTransformation transformation = new Buffer().maxDistance(max).minDistance(min);
    assertEquals("Failed GeoTransformation.Buffer.toString ", "Buffer{maxDistance=GeoDistance{value=-2.0, unit=METRES}, " + "minDistance=GeoDistance{value=-1.0, unit=METRES}}", transformation.toString());
}

@Test
public void testBufferTransformationToString() {
    GeoDistance min = GeoDistance.parse("-1m");
    GeoDistance max = GeoDistance.parse("-2m");
    GeoTransformation transformation = new Buffer(min, max);
    assertEquals("Failed GeoTransformation.Buffer.toString ", "Buffer{" + "minDistance=GeoDistance{value=-1.0, unit=METRES}, " + "maxDistance=GeoDistance{value=-2.0, unit=METRES}}", transformation.toString());
}

@Override
public String toString() {
    return MoreObjects.toStringHelper(this).add("maxDistance", maxDistance).add("minDistance", minDistance).toString();
}

@Override
public String toString() {
    return MoreObjects.toStringHelper(this).add("minDistance", minDistance).add("maxDistance", maxDistance).toString();
}


@Test
public void testParseWKTPolygon() throws Exception {
    String wkt = "LINESTRING (30 10, 10 30, 40 40)";
    JtsGeometry geometry = geometryFromWKT(CONTEXT, wkt);
    assertEquals("Unable to parse WKT polygon", wkt, geometry.toString());
}

@Test
public void testParseWKTPolygon() throws Exception {
    String wkt = "LINESTRING (30 10, 10 30, 40 40)";
    JtsGeometry geometry = geometry(wkt);
    assertEquals("Unable to parse WKT polygon", wkt, geometry.toString());
}

@Test
public void testParseWKTSelfIntersectingPolygon() throws Exception {
    String wkt = "POLYGON((5 0, 10 0, 10 10, 0 10, 0 0, 5 0, 3 3, 5 6, 7 3, 5 0))";
    JtsGeometry geometry = geometryFromWKT(CONTEXT, wkt);
    assertEquals("Unable to parse invalid WKT shape", "POLYGON ((5 0, 0 0, 0 10, 10 10, 10 0, 5 0), (5 0, 7 3, 5 6, 3 3, 5 0))", geometry.toString());
}

@Test
public void testParseWKTSelfIntersectingPolygon() throws Exception {
    String wkt = "POLYGON((5 0, 10 0, 10 10, 0 10, 0 0, 5 0, 3 3, 5 6, 7 3, 5 0))";
    JtsGeometry geometry = geometry(wkt);
    assertEquals("Unable to parse invalid WKT shape", "POLYGON ((5 0, 0 0, 0 10, 10 10, 10 0, 5 0), (5 0, 7 3, 5 6, 3 3, 5 0))", geometry.toString());
}

@Test
public void testParseWKTMultipolygon() throws Exception {
    String wkt = "MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))";
    JtsGeometry geometry = geometryFromWKT(CONTEXT, wkt);
    assertEquals("Unable to parse WKT multipolygon", wkt, geometry.toString());
}

@Test
public void testParseWKTMultipolygon() throws Exception {
    String wkt = "MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))";
    JtsGeometry geometry = geometry(wkt);
    assertEquals("Unable to parse WKT multipolygon", wkt, geometry.toString());
}

@Test
public void testParseWKTLine() throws Exception {
    String wkt = "LINESTRING (30 10, 10 30, 40 40)";
    JtsGeometry geometry = geometryFromWKT(CONTEXT, wkt);
    assertEquals("Unable to parse WKT line", wkt, geometry.toString());
}

@Test
public void testParseWKTLine() throws Exception {
    String wkt = "LINESTRING (30 10, 10 30, 40 40)";
    JtsGeometry geometry = geometry(wkt);
    assertEquals("Unable to parse WKT line", wkt, geometry.toString());
}

@Test
public void testParseWKTMultiline() throws Exception {
    String wkt = "MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))";
    JtsGeometry geometry = geometryFromWKT(CONTEXT, wkt);
    assertEquals("Unable to parse WKT multiline", wkt, geometry.toString());
}

@Test
public void testParseWKTMultiline() throws Exception {
    String wkt = "MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))";
    JtsGeometry geometry = geometry(wkt);
    assertEquals("Unable to parse WKT multiline", wkt, geometry.toString());
}

@Test
public void testParseWKTMultipoint() throws Exception {
    String wkt = "MULTIPOINT ((10 40), (40 30), (20 20), (30 10))";
    JtsGeometry geometry = geometryFromWKT(CONTEXT, wkt);
    assertEquals("Unable to parse WKT multipoint", wkt, geometry.toString());
}

@Test
public void testParseWKTMultipoint() throws Exception {
    String wkt = "MULTIPOINT ((10 40), (40 30), (20 20), (30 10))";
    JtsGeometry geometry = geometry(wkt);
    assertEquals("Unable to parse WKT multipoint", wkt, geometry.toString());
}


@Test
public void testBuilderEmpty() {
    Search search = new Search(null, null, null, null);
    assertFalse("Default refresh is not set", search.refresh());
}

@Test
public void testBuilderEmpty() {
    Search search = search().build();
    assertFalse("Default refresh is not set", search.refresh());
}

public Search build() {
    Condition query = queryBuilder == null ? null : queryBuilder.build();
    Condition filter = filterBuilder == null ? null : filterBuilder.build();
    Sort sort = sortBuilder == null ? null : sortBuilder.build();
    return new Search(query, filter, sort, refresh);
}

public Search build() {
    return new Search(queryBuilder == null ? null : queryBuilder.build(), filterBuilder == null ? null : filterBuilder.build(), sortBuilder == null ? null : sortBuilder.build(), paging == null ? null : IndexPagingState.build(ByteBufferUtils.byteBuffer(paging)), refresh);
}

@Test
public void testToString() {
    Search search = search().query(match("field", "value").boost(0.5)).filter(match("field", "value").docValues(true)).sort(field("field")).refresh(true).build();
    assertEquals("Method #toString is wrong", "Search{query=MatchCondition{boost=0.5, field=field, value=value, docValues=false}, " + "filter=MatchCondition{boost=null, field=field, value=value, docValues=true}, " + "sort=Sort{sortFields=[SimpleSortField{field=field, reverse=false}]}, " + "refresh=true}", search.toString());
}

@Test
public void testToString() {
    Search search = search().query(match("field", "value").boost(0.5)).filter(match("field", "value").docValues(true)).sort(field("field")).refresh(true).build();
    assertEquals("Method #toString is wrong", "Search{" + "query=MatchCondition{boost=0.5, field=field, value=value, docValues=false}, " + "filter=MatchCondition{boost=null, field=field, value=value, docValues=true}, " + "sort=Sort{sortFields=[SimpleSortField{field=field, reverse=false}]}, " + "refresh=true, paging=null}", search.toString());
}

@Override
public String toString() {
    return MoreObjects.toStringHelper(this).add("query", query).add("filter", filter).add("sort", sort).add("refresh", refresh).toString();
}

@Override
public String toString() {
    return MoreObjects.toStringHelper(this).add("query", query).add("filter", filter).add("sort", sort).add("refresh", refresh).add("paging", paging).toString();
}


@Test
public void testToString() {
    GeoDistanceSortField sortField = new GeoDistanceSortField("field", true, 0.0, 0.0);
    assertEquals("Method #toString is wrong", "GeoDistanceSortField{field=field, reverse=true, longitude=0.0, latitude=0.0}", sortField.toString());
}

@Test
public void testToString() {
    GeoDistanceSortField sortField = new GeoDistanceSortField("field", true, 0.0, 0.0);
    assertEquals("Method #toString is wrong", "GeoDistanceSortField{field=field, reverse=true, latitude=0.0, longitude=0.0}", sortField.toString());
}

@Override
public String toString() {
    return MoreObjects.toStringHelper(this).add("field", field).add("reverse", reverse).add("longitude", longitude).add("latitude", latitude).toString();
}

@Override
public String toString() {
    return MoreObjects.toStringHelper(this).add("field", field).add("reverse", reverse).add("latitude", latitude).add("longitude", longitude).toString();
}


@Test
public void testBuildInvalidLong() {
    String field = "field";
    double latitude = 0.0;
    double longitude = 200.0;
    GeoDistanceSortFieldBuilder builder = new GeoDistanceSortFieldBuilder(field, longitude, latitude);
    try {
        builder.build();
    } catch (IndexException e) {
        assertEquals("Creating a GeoDistanceSortFieldBuilder with invalid longitude must throw an IndexException", "longitude must be in range [-180.0, 180.0], but found 200.0", e.getMessage());
    }
}

@Test
public void testBuildInvalidLong() {
    String field = "field";
    double latitude = 0.0;
    double longitude = 200.0;
    GeoDistanceSortFieldBuilder builder = new GeoDistanceSortFieldBuilder(field, latitude, longitude);
    try {
        builder.build();
    } catch (IndexException e) {
        assertEquals("Creating a GeoDistanceSortFieldBuilder with invalid longitude must throw an IndexException", "longitude must be in range [-180.0, 180.0], but found 200.0", e.getMessage());
    }
}

@Override
public GeoDistanceSortField build() {
    return new GeoDistanceSortField(field, reverse, longitude, latitude);
}

@Override
public GeoDistanceSortField build() {
    return new GeoDistanceSortField(field, reverse, latitude, longitude);
}


@Test
public void testQueryWithBoost() {
    Condition condition = new MockCondition(0.7f);
    Schema schema = schema().build();
    Query query = condition.query(schema);
    assertEquals("Query boost is wrong", BoostQuery.class, query.getClass());
    BoostQuery boostQuery = (BoostQuery) query;
    assertEquals("Query type is wrong", 0.7f, boostQuery.getBoost(), 0);
}

@Test
public void testQueryWithBoost() {
    Condition condition = new MockCondition(0.7f);
    Schema schema = schema().build();
    Query query = condition.query(schema);
    assertEquals("Query boost is wrong", 0.7f, query.getBoost(), 0);
}

public final Query query(Schema schema) {
    Query query = doQuery(schema);
    return boost == null ? query : new BoostQuery(query, boost);
}

public final Query query(Schema schema) {
    Query query = doQuery(schema);
    if (boost != null) {
        query.setBoost(boost);
    }
    return query;
}


@Test
public void testQuery() {
    Schema schema = schema().mapper("name", dateRangeMapper("to", "from").pattern(TIMESTAMP_PATTERN)).build();
    DateRangeCondition condition = new DateRangeCondition(null, "name", 1L, 2L, null);
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", IntersectsPrefixTreeQuery.class, query.getClass());
    assertEquals("Query is wrong", "IntersectsPrefixTreeQuery(fieldName=name,queryShape=" + "[1970-01-01T00:00:00.001 TO 1970-01-01T00:00:00.002],detailLevel=9,prefixGridScanLevel=7)", query.toString());
}

@Test
public void testQuery() {
    Schema schema = schema().mapper("name", dateRangeMapper("to", "from").pattern(TIMESTAMP_PATTERN)).build();
    DateRangeCondition condition = new DateRangeCondition(null, "name", 1L, 2L, null);
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", ConstantScoreQuery.class, query.getClass());
    query = ((ConstantScoreQuery) query).getQuery();
    assertEquals("Query type is wrong", IntersectsPrefixTreeFilter.class, query.getClass());
    assertEquals("Query is wrong", "IntersectsPrefixTreeFilter(fieldName=name,queryShape=" + "[1970-01-01T00:00:00.001 TO 1970-01-01T00:00:00.002],detailLevel=9,prefixGridScanLevel=7)", query.toString());
}


@Test
public void testDigitsVersionInfo2() throws Exception {
    GitFlowVersionInfo info = new GitFlowVersionInfo("0.9-RC3-feature-SNAPSHOT", null);
    Assert.assertNotNull(info);
    info = info.digitsVersionInfo();
    Assert.assertNotNull(info);
    Assert.assertEquals(new GitFlowVersionInfo("0.9", null), info);
}

@Test
public void testDigitsVersionInfo2() throws Exception {
    GitFlowVersionInfo info = new GitFlowVersionInfo("0.9-RC3-feature-SNAPSHOT");
    Assert.assertNotNull(info);
    info = info.digitsVersionInfo();
    Assert.assertNotNull(info);
    Assert.assertEquals(new GitFlowVersionInfo("0.9"), info);
}

public GitFlowVersionInfo digitsVersionInfo() throws VersionParseException {
    return new GitFlowVersionInfo(joinDigitString(getDigits()), versionPolicy);
}

public GitFlowVersionInfo digitsVersionInfo() throws VersionParseException {
    return new GitFlowVersionInfo(joinDigitString(getDigits()));
}

@Test
public void testDigitsVersionInfo() throws Exception {
    GitFlowVersionInfo info = new GitFlowVersionInfo("0.9", null);
    Assert.assertNotNull(info);
    info = info.digitsVersionInfo();
    Assert.assertNotNull(info);
    Assert.assertEquals(new GitFlowVersionInfo("0.9", null), info);
}

@Test
public void testDigitsVersionInfo() throws Exception {
    GitFlowVersionInfo info = new GitFlowVersionInfo("0.9");
    Assert.assertNotNull(info);
    info = info.digitsVersionInfo();
    Assert.assertNotNull(info);
    Assert.assertEquals(new GitFlowVersionInfo("0.9"), info);
}


@Test
public void testDigitsVersionInfo2() throws Exception {
    GitFlowVersionInfo info = new GitFlowVersionInfo("0.9-RC3-feature-SNAPSHOT");
    Assert.assertNotNull(info);
    info = info.digitsVersionInfo();
    Assert.assertNotNull(info);
    Assert.assertEquals(new GitFlowVersionInfo("0.9"), info);
}

@Test
public void testDigitsVersionInfo2() throws Exception {
    GitFlowVersionInfo info = new GitFlowVersionInfo("0.9-RC3-feature-SNAPSHOT", null);
    Assert.assertNotNull(info);
    info = info.digitsVersionInfo();
    Assert.assertNotNull(info);
    Assert.assertEquals(new GitFlowVersionInfo("0.9", null), info);
}

public GitFlowVersionInfo digitsVersionInfo() throws VersionParseException {
    return new GitFlowVersionInfo(joinDigitString(getDigits()));
}

public GitFlowVersionInfo digitsVersionInfo() throws VersionParseException {
    return new GitFlowVersionInfo(joinDigitString(getDigits()), versionPolicy);
}

@Test
public void testDigitsVersionInfo() throws Exception {
    GitFlowVersionInfo info = new GitFlowVersionInfo("0.9");
    Assert.assertNotNull(info);
    info = info.digitsVersionInfo();
    Assert.assertNotNull(info);
    Assert.assertEquals(new GitFlowVersionInfo("0.9"), info);
}

@Test
public void testDigitsVersionInfo() throws Exception {
    GitFlowVersionInfo info = new GitFlowVersionInfo("0.9", null);
    Assert.assertNotNull(info);
    info = info.digitsVersionInfo();
    Assert.assertNotNull(info);
    Assert.assertEquals(new GitFlowVersionInfo("0.9", null), info);
}


@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldBeSafeToUpdateTorrentSeedsStatsWhileRegisteringTorrents() throws ExecutionException, InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000_000L).when(speedProvider).getCurrentSpeed();
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(1, speedProvider);
    bandwidthDispatcher.start();
    final ExecutorService executorService = Executors.newFixedThreadPool(5);
    final Collection<Future<?>> futures = new ArrayList<>();
    for (int i = 0; i < 25; ++i) {
        final InfoHash infoHash = new InfoHash((String.valueOf(i)).getBytes());
        futures.add(executorService.submit(() -> {
            bandwidthDispatcher.registerTorrent(infoHash);
            bandwidthDispatcher.updateTorrentPeers(infoHash, 20, 50);
        }));
    }
    for (final Future<?> future : futures) {
        future.get();
    }
    bandwidthDispatcher.stop();
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldBeSafeToUpdateTorrentSeedsStatsWhileRegisteringTorrents() throws ExecutionException, InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000000L).when(speedProvider).getInBytesPerSeconds();
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(1, speedProvider);
    bandwidthDispatcher.start();
    final ExecutorService executorService = Executors.newFixedThreadPool(5);
    final Collection<Future<?>> futures = new ArrayList<>();
    for (int i = 0; i < 25; ++i) {
        final InfoHash infoHash = new InfoHash((i + "").getBytes());
        futures.add(executorService.submit(() -> {
            bandwidthDispatcher.registerTorrent(infoHash);
            bandwidthDispatcher.updateTorrentPeers(infoHash, 20, 50);
        }));
    }
    for (final Future<?> future : futures) {
        future.get();
    }
    bandwidthDispatcher.stop();
}

public void updateTorrentPeers(final InfoHash infoHash, final int seeders, final int leechers) {
    log.debug("Updating Peers stats for {}", infoHash.getHumanReadable());
    this.lock.writeLock().lock();
    try {
        this.weightHolder.addOrUpdate(infoHash, new Peers(seeders, leechers));
        this.recomputeSpeeds();
    } finally {
        this.lock.writeLock().unlock();
    }
}

public void updateTorrentPeers(final InfoHash infoHash, final int seeders, final int leechers) {
    if (logger.isDebugEnabled()) {
        logger.debug("Updating Peers stats for {}", infoHash.humanReadableValue());
    }
    this.lock.writeLock().lock();
    try {
        this.weightHolder.addOrUpdate(infoHash, new Peers(seeders, leechers));
        this.recomputeSpeeds();
    } finally {
        this.lock.writeLock().unlock();
    }
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldRefreshRandomSpeedProviderAndRecomputeSpeed() {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000_000L).when(speedProvider).getCurrentSpeed();
    final BandwidthDispatcher bandwidthDispatcher = spy(new BandwidthDispatcher(2, speedProvider));
    bandwidthDispatcher.refreshCurrentBandwidth();
    Mockito.verify(speedProvider, Mockito.times(1)).refresh();
    Mockito.verify(bandwidthDispatcher, times(1)).recomputeSpeeds();
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldRefreshRandomSpeedProviderAndRecomputeSpeed() {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000000L).when(speedProvider).getInBytesPerSeconds();
    final BandwidthDispatcher bandwidthDispatcher = spy(new BandwidthDispatcher(2, speedProvider));
    bandwidthDispatcher.refreshCurrentBandwidth();
    Mockito.verify(speedProvider, Mockito.times(1)).refresh();
    Mockito.verify(bandwidthDispatcher, times(1)).recomputeSpeeds();
}

@VisibleForTesting
void refreshCurrentBandwidth() {
    log.debug("Refreshing global bandwidth");
    this.lock.writeLock().lock();
    try {
        this.randomSpeedProvider.refresh();
        this.recomputeSpeeds();
        if (log.isDebugEnabled()) {
            log.debug("Global bandwidth refreshed, new value is {}", FileUtils.byteCountToDisplaySize(this.randomSpeedProvider.getCurrentSpeed()));
        }
    } finally {
        this.lock.writeLock().unlock();
    }
}

@VisibleForTesting
void refreshCurrentBandwidth() {
    if (logger.isDebugEnabled()) {
        logger.debug("Refreshing global bandwidth");
    }
    this.lock.writeLock().lock();
    try {
        this.randomSpeedProvider.refresh();
        this.recomputeSpeeds();
        if (logger.isDebugEnabled()) {
            logger.debug("Global bandwidth refreshed, new value is {}", FileUtils.byteCountToDisplaySize(this.randomSpeedProvider.getInBytesPerSeconds()));
        }
    } finally {
        this.lock.writeLock().unlock();
    }
}

@SuppressWarnings({ "ResultOfMethodCallIgnored", "unchecked" })
@Test
public void shouldNotifyThatSpeedHasChangedAfterRegisteringTorrent() throws InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(10_000L).when(speedProvider).getCurrentSpeed();
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(2, speedProvider);
    bandwidthDispatcher.start();
    final SpeedChangedListener speedListener = spy(new VoidSpeedChangedListener());
    bandwidthDispatcher.setSpeedListener(speedListener);
    final InfoHash infoHash = new InfoHash(new byte[] { 12 });
    bandwidthDispatcher.registerTorrent(infoHash);
    bandwidthDispatcher.updateTorrentPeers(infoHash, 10, 10);
    Thread.sleep(30);
    bandwidthDispatcher.stop();
    Mockito.verify(speedListener, Mockito.times(1)).speedsHasChanged(anyMap());
}

@SuppressWarnings({ "ResultOfMethodCallIgnored", "unchecked" })
@Test
public void shouldNotifyThatSpeedHasChangedAfterRegisteringTorrent() throws InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(10000L).when(speedProvider).getInBytesPerSeconds();
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(2, speedProvider);
    bandwidthDispatcher.start();
    final SpeedChangedListener speedListener = spy(new VoidSpeedChangedListener());
    bandwidthDispatcher.setSpeedListener(speedListener);
    final InfoHash infoHash = new InfoHash(new byte[] { 12 });
    bandwidthDispatcher.registerTorrent(infoHash);
    bandwidthDispatcher.updateTorrentPeers(infoHash, 10, 10);
    Thread.sleep(30);
    bandwidthDispatcher.stop();
    Mockito.verify(speedListener, Mockito.times(1)).speedsHasChanged(anyMap());
}

public void registerTorrent(final InfoHash infoHash) {
    log.debug("{} has been added to bandwidth dispatcher.", infoHash.getHumanReadable());
    this.lock.writeLock().lock();
    try {
        this.torrentsSeedStats.put(infoHash, new TorrentSeedStats());
        this.speedMap.put(infoHash, new Speed(0));
    } finally {
        this.lock.writeLock().unlock();
    }
}

public void registerTorrent(final InfoHash infoHash) {
    if (logger.isDebugEnabled()) {
        logger.debug("{} has been added to bandwidth dispatcher.", infoHash.humanReadableValue());
    }
    this.lock.writeLock().lock();
    try {
        this.torrentsSeedStats.put(infoHash, new TorrentSeedStats());
        this.speedMap.put(infoHash, new Speed(0));
    } finally {
        this.lock.writeLock().unlock();
    }
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldNotIncrementRegisteredTorrentsWithZeroLeechers() throws InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000_000L).when(speedProvider).getCurrentSpeed();
    final InfoHash infoHash = new InfoHash(new byte[] { 12 });
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(2, speedProvider);
    bandwidthDispatcher.registerTorrent(infoHash);
    bandwidthDispatcher.updateTorrentPeers(infoHash, 100, 0);
    bandwidthDispatcher.start();
    Thread.sleep(10);
    final TorrentSeedStats seedStats = bandwidthDispatcher.getSeedStatForTorrent(infoHash);
    bandwidthDispatcher.stop();
    assertThat(seedStats.getDownloaded()).isEqualTo(0);
    assertThat(seedStats.getUploaded()).isEqualTo(0);
    assertThat(seedStats.getLeft()).isEqualTo(0);
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldNotIncrementRegisteredTorrentsWithZeroLeechers() throws InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000000L).when(speedProvider).getInBytesPerSeconds();
    final InfoHash infoHash = new InfoHash(new byte[] { 12 });
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(2, speedProvider);
    bandwidthDispatcher.registerTorrent(infoHash);
    bandwidthDispatcher.updateTorrentPeers(infoHash, 100, 0);
    bandwidthDispatcher.start();
    Thread.sleep(10);
    final TorrentSeedStats seedStats = bandwidthDispatcher.getSeedStatForTorrent(infoHash);
    bandwidthDispatcher.stop();
    assertThat(seedStats.getDownloaded()).isEqualTo(0);
    assertThat(seedStats.getUploaded()).isEqualTo(0);
    assertThat(seedStats.getLeft()).isEqualTo(0);
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldNotIncrementRegisteredTorrentsWithZeroSeeders() throws InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000_000L).when(speedProvider).getCurrentSpeed();
    final InfoHash infoHash = new InfoHash(new byte[] { 12 });
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(2, speedProvider);
    bandwidthDispatcher.registerTorrent(infoHash);
    bandwidthDispatcher.updateTorrentPeers(infoHash, 0, 100);
    bandwidthDispatcher.start();
    Thread.sleep(10);
    final TorrentSeedStats seedStats = bandwidthDispatcher.getSeedStatForTorrent(infoHash);
    bandwidthDispatcher.stop();
    assertThat(seedStats.getDownloaded()).isEqualTo(0);
    assertThat(seedStats.getUploaded()).isEqualTo(0);
    assertThat(seedStats.getLeft()).isEqualTo(0);
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldNotIncrementRegisteredTorrentsWithZeroSeeders() throws InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000000L).when(speedProvider).getInBytesPerSeconds();
    final InfoHash infoHash = new InfoHash(new byte[] { 12 });
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(2, speedProvider);
    bandwidthDispatcher.registerTorrent(infoHash);
    bandwidthDispatcher.updateTorrentPeers(infoHash, 0, 100);
    bandwidthDispatcher.start();
    Thread.sleep(10);
    final TorrentSeedStats seedStats = bandwidthDispatcher.getSeedStatForTorrent(infoHash);
    bandwidthDispatcher.stop();
    assertThat(seedStats.getDownloaded()).isEqualTo(0);
    assertThat(seedStats.getUploaded()).isEqualTo(0);
    assertThat(seedStats.getLeft()).isEqualTo(0);
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldNotIncrementRegisteredTorrentsBeforePeersHaveBeenAdded() throws InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000_000L).when(speedProvider).getCurrentSpeed();
    final InfoHash infoHash = new InfoHash(new byte[] { 12 });
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(2, speedProvider);
    bandwidthDispatcher.registerTorrent(infoHash);
    bandwidthDispatcher.start();
    Thread.sleep(10);
    final TorrentSeedStats seedStats = bandwidthDispatcher.getSeedStatForTorrent(infoHash);
    bandwidthDispatcher.stop();
    assertThat(seedStats.getDownloaded()).isEqualTo(0);
    assertThat(seedStats.getUploaded()).isEqualTo(0);
    assertThat(seedStats.getLeft()).isEqualTo(0);
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldNotIncrementRegisteredTorrentsBeforePeersHaveBeenAdded() throws InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000000L).when(speedProvider).getInBytesPerSeconds();
    final InfoHash infoHash = new InfoHash(new byte[] { 12 });
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(2, speedProvider);
    bandwidthDispatcher.registerTorrent(infoHash);
    bandwidthDispatcher.start();
    Thread.sleep(10);
    final TorrentSeedStats seedStats = bandwidthDispatcher.getSeedStatForTorrent(infoHash);
    bandwidthDispatcher.stop();
    assertThat(seedStats.getDownloaded()).isEqualTo(0);
    assertThat(seedStats.getUploaded()).isEqualTo(0);
    assertThat(seedStats.getLeft()).isEqualTo(0);
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldReturnZeroIfInfoHashIsNotRegistered() throws InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000_000L).when(speedProvider).getCurrentSpeed();
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(2, speedProvider);
    bandwidthDispatcher.start();
    Thread.sleep(10);
    final TorrentSeedStats seedStats = bandwidthDispatcher.getSeedStatForTorrent(new InfoHash(new byte[] { 12 }));
    bandwidthDispatcher.stop();
    assertThat(seedStats.getDownloaded()).isEqualTo(0);
    assertThat(seedStats.getUploaded()).isEqualTo(0);
    assertThat(seedStats.getLeft()).isEqualTo(0);
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldReturnZeroIfInfoHashIsNotRegistered() throws InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000000L).when(speedProvider).getInBytesPerSeconds();
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(2, speedProvider);
    bandwidthDispatcher.start();
    Thread.sleep(10);
    final TorrentSeedStats seedStats = bandwidthDispatcher.getSeedStatForTorrent(new InfoHash(new byte[] { 12 }));
    bandwidthDispatcher.stop();
    assertThat(seedStats.getDownloaded()).isEqualTo(0);
    assertThat(seedStats.getUploaded()).isEqualTo(0);
    assertThat(seedStats.getLeft()).isEqualTo(0);
}

@Override
public void run() {
    try {
        while (!this.stop) {
            Thread.sleep(this.threadPauseIntervalMs);
            ++this.threadLoopCounter;
            if (this.threadLoopCounter == TWENTY_MINS_MS / this.threadPauseIntervalMs) {
                this.refreshCurrentBandwidth();
                this.threadLoopCounter = 0;
            }
            this.lock.readLock().lock();
            final Set<Map.Entry<InfoHash, TorrentSeedStats>> entrySet = new HashSet<>(this.torrentsSeedStats.entrySet());
            this.lock.readLock().unlock();
            for (final Map.Entry<InfoHash, TorrentSeedStats> entry : entrySet) {
                final long speedInBytesPerSecond = ofNullable(this.speedMap.get(entry.getKey())).map(Speed::getBytesPerSecond).orElse(0L);
                entry.getValue().addUploaded((speedInBytesPerSecond * this.threadPauseIntervalMs) / 1000);
            }
        }
    } catch (final InterruptedException ignore) {
    }
}

@Override
public void run() {
    try {
        while (!this.stop) {
            Thread.sleep(this.threadPauseInterval);
            ++this.threadLoopCounter;
            if (this.threadLoopCounter == 1200000 / this.threadPauseInterval) {
                this.refreshCurrentBandwidth();
                this.threadLoopCounter = 0;
            }
            this.lock.readLock().lock();
            final Set<Map.Entry<InfoHash, TorrentSeedStats>> entrySet = Sets.newHashSet(this.torrentsSeedStats.entrySet());
            this.lock.readLock().unlock();
            for (final Map.Entry<InfoHash, TorrentSeedStats> entry : entrySet) {
                final Speed speed = this.speedMap.get(entry.getKey());
                final long speedInBytesPerSecond = speed == null ? 0 : speed.getBytesPerSeconds();
                entry.getValue().addUploaded((speedInBytesPerSecond * this.threadPauseInterval) / 1000);
            }
        }
    } catch (final InterruptedException ignore) {
    }
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldIncrementRegisteredTorrent() throws InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000_000L).when(speedProvider).getCurrentSpeed();
    final InfoHash infoHash = new InfoHash(new byte[] { 12 });
    final InfoHash infoHash2 = new InfoHash(new byte[] { 100 });
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(2, speedProvider);
    bandwidthDispatcher.registerTorrent(infoHash);
    bandwidthDispatcher.updateTorrentPeers(infoHash, 10, 10);
    bandwidthDispatcher.registerTorrent(infoHash2);
    bandwidthDispatcher.updateTorrentPeers(infoHash2, 20, 30);
    bandwidthDispatcher.start();
    Thread.sleep(30);
    final TorrentSeedStats seedStats = bandwidthDispatcher.getSeedStatForTorrent(infoHash);
    final TorrentSeedStats seedStats2 = bandwidthDispatcher.getSeedStatForTorrent(infoHash2);
    bandwidthDispatcher.stop();
    assertThat(seedStats.getUploaded()).isGreaterThan(1);
    assertThat(seedStats2.getUploaded()).isGreaterThan(1);
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void shouldIncrementRegisteredTorrent() throws InterruptedException {
    final RandomSpeedProvider speedProvider = Mockito.mock(RandomSpeedProvider.class);
    Mockito.doReturn(1000000L).when(speedProvider).getInBytesPerSeconds();
    final InfoHash infoHash = new InfoHash(new byte[] { 12 });
    final InfoHash infoHash2 = new InfoHash(new byte[] { 100 });
    final BandwidthDispatcher bandwidthDispatcher = new BandwidthDispatcher(2, speedProvider);
    bandwidthDispatcher.registerTorrent(infoHash);
    bandwidthDispatcher.updateTorrentPeers(infoHash, 10, 10);
    bandwidthDispatcher.registerTorrent(infoHash2);
    bandwidthDispatcher.updateTorrentPeers(infoHash2, 20, 30);
    bandwidthDispatcher.start();
    Thread.sleep(30);
    final TorrentSeedStats seedStats = bandwidthDispatcher.getSeedStatForTorrent(infoHash);
    final TorrentSeedStats seedStats2 = bandwidthDispatcher.getSeedStatForTorrent(infoHash2);
    bandwidthDispatcher.stop();
    assertThat(seedStats.getUploaded()).isGreaterThan(1);
    assertThat(seedStats2.getUploaded()).isGreaterThan(1);
}


@Test
public void shouldReturnRandomSpeedAfterBuild() {
    final RandomSpeedProvider speedProvider = new RandomSpeedProvider(this.mockedConf());
    assertThat(speedProvider.getCurrentSpeed() / 1000).isBetween(MIN_UPLOAD_RATE, MAX_UPLOAD_RATE);
}

@Test
public void shouldReturnRandomSpeedAfterBuild() {
    final RandomSpeedProvider speedProvider = new RandomSpeedProvider(this.mockedConf());
    assertThat(speedProvider.getInBytesPerSeconds() / 1000).isBetween(MIN_UPLOAD_RATE, MAX_UPLOAD_RATE);
}

public long getInBytesPerSeconds() {
    return this.currentSpeed;
}

@Test
public void shouldProvideValueWithinRange() {
    final RandomSpeedProvider speedProvider = new RandomSpeedProvider(this.mockedConf());
    for (int i = 0; i < 50; i++) {
        speedProvider.refresh();
        assertThat(speedProvider.getCurrentSpeed() / 1000).isBetween(MIN_UPLOAD_RATE, MAX_UPLOAD_RATE);
    }
}

@Test
public void shouldProvideValueWithinRange() {
    final RandomSpeedProvider speedProvider = new RandomSpeedProvider(this.mockedConf());
    for (int i = 0; i < 50; i++) {
        speedProvider.refresh();
        assertThat(speedProvider.getInBytesPerSeconds() / 1000).isBetween(MIN_UPLOAD_RATE, MAX_UPLOAD_RATE);
    }
}

public void refresh() {
    final long minUploadRateInBytes = appConfiguration.getMinUploadRate() * 1000L;
    final long maxUploadRateInBytes = appConfiguration.getMaxUploadRate() * 1000L;
    this.currentSpeed = (minUploadRateInBytes == maxUploadRateInBytes) ? maxUploadRateInBytes : ThreadLocalRandom.current().nextLong(minUploadRateInBytes, maxUploadRateInBytes);
}

public void refresh() {
    final Long minUploadRateInBytes = appConfiguration.getMinUploadRate() * 1000L;
    final Long maxUploadRateInBytes = appConfiguration.getMaxUploadRate() * 1000L;
    this.currentSpeed = (minUploadRateInBytes.equals(maxUploadRateInBytes)) ? maxUploadRateInBytes : ThreadLocalRandom.current().nextLong(minUploadRateInBytes, maxUploadRateInBytes);
}


@Test
public void shouldNotFailToDeserializeIfKeyGeneratorIsNotDefined() throws IOException {
    final String jsonWithoutKeyGenerator = validJSON.replaceAll("\"keyGenerator\":[ ]*\\{.*?algorithm\":[ ]*\\{.*?}.*?},", "").replaceAll("&key=\\{key}", "");
    final BitTorrentClientConfig bitTorrentClientConfig = mapper.readValue(jsonWithoutKeyGenerator, BitTorrentClientConfig.class);
    assertThat(bitTorrentClientConfig).isNotNull();
    assertThat(provider.createClient(bitTorrentClientConfig).getKey(null, RequestEvent.STARTED)).isEmpty();
}

@Test
public void shouldNotFailToDeserializeIfKeyGeneratorIsNotDefined() throws IOException {
    final String jsonWithoutKeyGenerator = validJSON.replaceAll("\"keyGenerator\":[ ]*\\{.*?algorithm\":[ ]*\\{.*?}.*?},", "").replaceAll("&key=\\{key}", "");
    final BitTorrentClientConfig bitTorrentClientConfig = mapper.readValue(jsonWithoutKeyGenerator, BitTorrentClientConfig.class);
    assertThat(bitTorrentClientConfig).isNotNull();
    assertThat(bitTorrentClientConfig.createClient().getKey(null, RequestEvent.STARTED)).isEmpty();
}

@VisibleForTesting
Optional<String> getKey(final InfoHash infoHash, final RequestEvent event) {
    return ofNullable(keyGenerator).map(keyGen -> keyGen.getKey(infoHash, event));
}

@VisibleForTesting
Optional<String> getKey(final InfoHash infoHash, final RequestEvent event) {
    if (keyGenerator == null) {
        return Optional.empty();
    }
    return Optional.of(keyGenerator.getKey(infoHash, event));
}

@Test
public void shouldNotFailToDeserializeIfRequestHeadersIsEmpty() throws IOException {
    final String jsonWithEmptyHeaders = validJSON.replaceAll("\"requestHeaders\":[ ]*\\[.*?\\]", "\"requestHeaders\":[]");
    final BitTorrentClientConfig bitTorrentClientConfig = mapper.readValue(jsonWithEmptyHeaders, BitTorrentClientConfig.class);
    assertThat(bitTorrentClientConfig).isNotNull();
    assertThat(provider.createClient(bitTorrentClientConfig).getHeaders()).hasSize(0);
}

@Test
public void shouldNotFailToDeserializeIfRequestHeadersIsEmpty() throws IOException {
    final String jsonWithEmptyHeaders = validJSON.replaceAll("\"requestHeaders\":[ ]*\\[.*?\\]", "\"requestHeaders\":[]");
    final BitTorrentClientConfig bitTorrentClientConfig = mapper.readValue(jsonWithEmptyHeaders, BitTorrentClientConfig.class);
    assertThat(bitTorrentClientConfig).isNotNull();
    assertThat(bitTorrentClientConfig.createClient().getHeaders()).hasSize(0);
}

@VisibleForTesting
public BitTorrentClient createClient() {
    return new BitTorrentClient(this.peerIdGenerator, this.keyGenerator, this.urlEncoder, query, ImmutableList.copyOf(requestHeaders), new NumwantProvider(this.numwant, this.numwantOnStop));
}