@Test
public void testRemoveExpired() throws Exception {

    VariableOrConstant<String, String>[] pattern1, pattern2, pattern3;
    List<VariableOrConstant<String, String>[]> patterns;
    Query<String, String> query1, query2, query3;
    String[] tuple;
    HashMap<String, String>
            map = new HashMap<>();
    long now = 42;
    pattern1 = new VariableOrConstant[]{
            newTerm(null, "x"), newTerm("knows", null), newTerm(null, "y")};
    pattern2 = new VariableOrConstant[]{
            newTerm(null, "y"), newTerm("knows", null), newTerm(null, "z")};
    pattern3 = new VariableOrConstant[]{
            newTerm(null, "z"), newTerm("knows", null), newTerm(null, "a")};
    patterns = new LinkedList<>();
    patterns.add(pattern1);
    patterns.add(pattern2);
    patterns.add(pattern3);
    query1 = new Query<>(patterns, 0);
    queryIndex.add(query1);
    pattern1 = new VariableOrConstant[]{
            newTerm(null, "a"), newTerm("name", null), newTerm("\"Arthur Dent\"", null)};
    pattern2 = new VariableOrConstant[]{
            newTerm(null, "a"), newTerm("race", null), newTerm("human", null)};
    patterns = new LinkedList<>();
    patterns.add(pattern1);
    patterns.add(pattern2);
    query2 = new Query<>(patterns, now + 1000L * 60);
    queryIndex.add(query2);
    pattern1 = new VariableOrConstant[]{
            newTerm("Paris", null), newTerm("capitalCityOf", null), newTerm(null, "country")};
    pattern2 = new VariableOrConstant[]{
            newTerm(null, "country"), newTerm("name", null), newTerm(null, "name")};
    pattern3 = new VariableOrConstant[]{
            newTerm(null, "country"), newTerm("population", null), newTerm(null, "pop")};
    patterns = new LinkedList<>();
    patterns.add(pattern1);
    patterns.add(pattern2);
    patterns.add(pattern3);
    query3 = new Query<>(patterns, now + 1000L * 120);
    queryIndex.add(query3);
    queryIndex.removeExpired(now);
    tuple = new String[]{"arthur", "knows", "ford"};
    assertSolutions(now, query1.getVariables(), tuple);
    tuple = new String[]{"ford", "knows", "zaphod"};
    assertSolutions(now, query1.getVariables(), tuple);
    tuple = new String[]{"zaphod", "knows", "trillian"};
    map.clear();
    map.put("x", "arthur");
    map.put("y", "ford");
    map.put("z", "zaphod");
    map.put("a", "trillian");
    assertSolutions(now, query1.getVariables(), tuple, map);
    tuple = new String[]{"arthur", "name", "\"Arthur Dent\""};
    assertSolutions(now, query2.getVariables(), tuple);
    tuple = new String[]{"arthur", "race", "human"};
    map.clear();
    map.put("a", "arthur");
    assertSolutions(now, query2.getVariables(), tuple, map);
    tuple = new String[]{"Paris", "capitalCityOf", "France"};
    assertSolutions(now, query3.getVariables(), tuple);
    tuple = new String[]{"France", "name", "\"France\""};
    assertSolutions(now, query3.getVariables(), tuple);
    tuple = new String[]{"France", "population", "dozens of millions"};
    map.clear();
    map.put("country", "France");
    map.put("name", "\"France\"");
    map.put("pop", "dozens of millions");
    assertSolutions(now, query3.getVariables(), tuple, map);
    now += 1000L * 61;
    queryIndex.removeExpired(now);
    // insert a new, unique tuple to trigger a solution
    tuple = new String[]{"zaphod", "knows", "marvin"};
    map.clear();
    map.put("x", "arthur");
    map.put("y", "ford");
    map.put("z", "zaphod");
    map.put("a", "marvin");
    assertSolutions(now, query1.getVariables(), tuple, map);
    // this pattern has gone
    tuple = new String[]{"arthur", "name", "\"Arthur\""};
    assertSolutions(now, query2.getVariables(), tuple);
    tuple = new String[]{"arthur", "race", "ape-descendant"};
    assertSolutions(now, query2.getVariables(), tuple);
    tuple = new String[]{"France", "population", "tens of millions"};
    map.clear();
    map.put("country", "France");
    map.put("name", "\"France\"");
    map.put("pop", "tens of millions");
    assertSolutions(now, query3.getVariables(), tuple, map);
    now += 1000L * 61;
    queryIndex.removeExpired(now);
    tuple = new String[]{"zaphod", "knows", "eddie"};
    map.clear();
    map.put("x", "arthur");
    map.put("y", "ford");
    map.put("z", "zaphod");
    map.put("a", "eddie");
    assertSolutions(now, query1.getVariables(), tuple, map);
    // now this pattern is gone, as well
    tuple = new String[]{"France", "population", "zillions of millions"};
    assertSolutions(now, query3.getVariables(), tuple);
}