/**
 * Copyright © CENRA (2016)
 *
 * remi.clement@espaces-naturels.fr
 *
 * This software is a multi-platform and portable application based on
 * the scientifical toolbox to monitor wetlands. With this toolbox it’s
 * possible to calculate indicators «RhoMéO» to monitor quality,
 * fonctionalities and urban and agricultural pressures of wetlands.
 * Indicators are calculated using naturalist data entered and/or imported,
 * and geographic data downloaded from a geographic server.
 *
 * This software is governed by the CeCILL license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 *
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.
 *
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.protocol.p06;

import fr.cenra.rhomeo.RhomeoTestCase;
import fr.cenra.rhomeo.api.Version;
import fr.cenra.rhomeo.api.data.DataContext;
import fr.cenra.rhomeo.api.data.Protocol;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.api.process.Indicator;
import fr.cenra.rhomeo.api.process.ProcessContext;
import fr.cenra.rhomeo.api.result.Index;
import fr.cenra.rhomeo.core.Session;
import fr.cenra.rhomeo.core.data.ReferenceManager;
import fr.cenra.rhomeo.core.data.reference.OdonataDepartmentReference;
import fr.cenra.rhomeo.core.data.reference.OdonataDepartmentReferenceDescription;
import fr.cenra.rhomeo.core.data.reference.OdonataHabitatReference;
import fr.cenra.rhomeo.core.data.reference.OdonataHabitatReferenceDescription;
import fr.cenra.rhomeo.core.data.reference.Taxref;
import fr.cenra.rhomeo.core.data.reference.TaxrefDescription;
import fr.cenra.rhomeo.core.list.HumidZoneType;
import fr.cenra.rhomeo.core.list.OdonateZoneBio;
import static fr.cenra.rhomeo.core.list.OdonateZoneBio.MED_OCCIDENTAL;
import fr.cenra.rhomeo.core.data.site.SiteImpl;
import fr.cenra.rhomeo.protocol.TaxonOperator;
import fr.cenra.rhomeo.protocol.TestUtilities;
import static fr.cenra.rhomeo.protocol.TestUtilities.createRefDataset;
import fr.cenra.rhomeo.protocol.YearResult;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.aeshna_affinis;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.aeshna_cyanea;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.anax_imperator;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.brachytron_pratense;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.ceriagrion_tenellum;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.coenagrion_puella;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.coenagrion_pulchellum;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.crocothemis_erythraea;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.erythromma_lindeni;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.erythromma_lindenii;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.erythromma_viridulum;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.gomphus_pulchellus;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.hab_01a;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.hab_05f;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.hab_06;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.hab_10b;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.ischnura_pumilio;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.lestes_viridis;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.libellula_depressa;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.libellula_fulva;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.orthetrum_albistylum;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.orthetrum_brunneum;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.orthetrum_coerulescens;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.pyrrhosoma_nymphula;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.sympetrum_depressiusculum;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.sympetrum_fonscolombii;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.sympetrum_pedemontanum;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.sympetrum_sanguineum;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.sympetrum_striolatum;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.ischnura_elegans;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.libellula_quadrimaculata;
import java.beans.IntrospectionException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import javafx.concurrent.Task;
import javafx.embed.swing.JFXPanel;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.orthetrum_cancellatum;
import static fr.cenra.rhomeo.protocol.p06.OdonataHabitatReferenceTestHelper.sympecma_fusca;
import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import javafx.application.Platform;
import org.junit.Ignore;

/**
 * TODO : most of the tests depends on the content of reference lists, which are
 * destined to change over time. This make this test suite unstable. We should
 * clean the tests to only test what is testable, or force a version of reference
 * which would be embedded in the tests.
 *
 * @author Samuel Andrés (Geomatys) <samuel.andres at geomatys.com>
 */
@Ignore
public class I10Test extends RhomeoTestCase {

    @Autowired
    private Session session;

    @Autowired
    @Qualifier(P06.NAME)
    private Protocol p06;

    @Autowired
    @Qualifier(I10.NAME)
    private Indicator indicator;

    @Autowired
    private OdonataHabitatReferenceDescription odonataHabitatReferenceDescription;

    @Autowired
    private OdonataDepartmentReferenceDescription odonataDepartmentReferenceDescription;

    @Autowired
    private TaxrefDescription taxRefDescription;

    @Autowired
    private AnnualRatePopulationIntegrity indexSpi;

    @Autowired
    private AnnualNbOfExpectedSpecies nbSpeciesIndexSpi;

    @Autowired
    private AnnualRateRichness richnessIndexSpi;

    private static final LocalDate SOME_DATE = LocalDate.now();

    private TaxonOperator operator;

    @Before
    public void before() throws IntrospectionException, InterruptedException, ExecutionException {
        try {
            new JFXPanel().setVisible(false);// necessary to init fx toolkit

            final ReferenceManager<OdonataHabitatReference> manager1 = ReferenceManager.getOrCreate(odonataHabitatReferenceDescription);
            final Task<ReferenceManager> t1 = manager1.refresh();
            t1.run();
            t1.get();

            Iterator<Version> it = manager1.getInstalledVersions().iterator();
            Assume.assumeTrue(it.hasNext());
            final Version habitatVersion = it.next();

            final ReferenceManager<OdonataDepartmentReference> manager2 = ReferenceManager.getOrCreate(odonataDepartmentReferenceDescription);
            final Task<ReferenceManager> t2 = manager2.refresh();
            t2.run();
            t2.get();

            it = manager2.getInstalledVersions().iterator();
            Assume.assumeTrue(it.hasNext());
            final Version depVersion = it.next();

            final ReferenceManager<Taxref> manager3 = ReferenceManager.getOrCreate(taxRefDescription);
            final Task<ReferenceManager> t3 = manager3.refresh();
            t3.run();
            t3.get();

            it = manager3.getInstalledVersions().iterator();
            Assume.assumeTrue(it.hasNext());
            final Version taxVersion = it.next();

            final Site site = new SiteImpl(null, "countyCode", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), OdonateZoneBio.MED_OCCIDENTAL.getCode(), null, "name", "remarks");

            session.startEdition(site, p06);
            session.getDataContext().getReferences().put((Class)OdonataHabitatReference.class, habitatVersion);
            session.getDataContext().getReferences().put((Class)OdonataDepartmentReference.class, depVersion);
            session.getDataContext().getReferences().put((Class)Taxref.class, taxVersion);
            operator = TaxonOperator.getOrCreate(new ProcessContext(session.getDataset(), session.getDataContext()));
        } catch (Exception ex) {
            Assume.assumeNoException(ex);
        }
    }

    @Test
    public void testIndicatorType(){
        Assert.assertTrue(indicator instanceof I10);
    }

    @Test
    public void testGetDefaultIndex(){
        Assert.assertEquals(AnnualRatePopulationIntegrity.NAME, indicator.getDefaultIndex());
    }

    @Test
    public void testT1_1() throws IntrospectionException{

        final OdonateZoneBio ODONATE_TYPE = MED_OCCIDENTAL;
        final String ID_DEPT = "01";
        final String HABITAT = hab_05f;

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                HABITAT);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);


        final Site site = new SiteImpl(null, ID_DEPT, "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), ODONATE_TYPE.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> result = ((I10) indicator).t1(statements, site);

        // Check the result contains only one "year" key, and this key maps the statement date.
        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));

        // Check the expected result list
        final Set<OdonataHabitatReference> resultList = result.get(SOME_DATE.getYear());

        // Check the content of the result list : all references must have the same habitat and odonate type as the given statement.
        for(final OdonataHabitatReference ref : resultList){
            Assert.assertEquals(ODONATE_TYPE.getZbio(), ref.getZbio());
            Assert.assertEquals(HABITAT, ref.getHabitat());
        }

        /*
        rhomeo=# select distinct h.cd_ref, h.espece from odo_dependance_habitat h join odo_especes_par_dept d on h.cd_ref=d.cd_ref and id_dept='01' and h.zbio='mediterraneen occidental' and h.habitat='05f' where h.valeur::float<3 and d.valeur>2;
        cd_ref |          espece
       --------+---------------------------
         65335 | Sympetrum fonscolombii
         65473 | Anax imperator
         65344 | Sympetrum striolatum
         65169 | Ceriagrion tenellum
         65265 | Libellula fulva
         65456 | Aeshna affinis
         65440 | Aeshna cyanea
         65109 | Ischnura elegans
         65115 | Ischnura pumilio
         65278 | Orthetrum cancellatum
         65300 | Crocothemis erythraea
         65322 | Sympetrum sanguineum
         65220 | Lestes viridis
         65415 | Brachytron pratense
         65141 | Coenagrion puella
         65290 | Orthetrum brunneum
        645873 | Erythromma lindenii
         65165 | Erythromma viridulum
         65282 | Orthetrum albistylum
         65308 | Sympetrum pedemontanum
         65101 | Pyrrhosoma nymphula
         65227 | Gomphus pulchellus
         65318 | Sympetrum depressiusculum
         65284 | Orthetrum coerulescens
         65262 | Libellula depressa
       (25 lignes)
       */

        final Map<Integer, Set<OdonataHabitatReference>> expected = new HashMap<>();
        final Set<OdonataHabitatReference> exp = new HashSet<>();
        exp.addAll(getHabitatReferences_2(sympetrum_fonscolombii, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(anax_imperator, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(sympetrum_striolatum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(ceriagrion_tenellum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(libellula_fulva, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(aeshna_affinis, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(aeshna_cyanea, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(ischnura_elegans, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(ischnura_pumilio, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(orthetrum_cancellatum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(crocothemis_erythraea, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(sympetrum_sanguineum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(lestes_viridis, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(brachytron_pratense, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(coenagrion_puella, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(orthetrum_brunneum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(erythromma_lindenii, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(erythromma_viridulum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(orthetrum_albistylum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(sympetrum_pedemontanum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(pyrrhosoma_nymphula, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(gomphus_pulchellus, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(sympetrum_depressiusculum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(orthetrum_coerulescens, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(libellula_depressa, ODONATE_TYPE.getZbio(), HABITAT));
        expected.put(SOME_DATE.getYear(), exp);


        Assert.assertEquals(25, expected.get(SOME_DATE.getYear()).size());
        Assert.assertEquals(expected, result);
    }

    @Test
    public void testT1_2() throws IntrospectionException{

        final OdonateZoneBio ODONATE_TYPE = MED_OCCIDENTAL;
        final String ID_DEPT = "04";
        final String HABITAT = hab_10b;

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                HABITAT);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);

        final Site site = new SiteImpl(null, ID_DEPT, "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), ODONATE_TYPE.getCode(), null, "name", "remarks");


        final Map<Integer, Set<OdonataHabitatReference>> result = ((I10) indicator).t1(statements, site);

        // Check the result contains only one "year" key, and this key maps the statement date.
        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));

        // Check the expected result list
        final Set<OdonataHabitatReference> resultList = result.get(SOME_DATE.getYear());

        // Check the content of the result list : all references must have the same habitat and odonate type as the given statement.
        for(final OdonataHabitatReference ref : resultList){
            Assert.assertEquals(ODONATE_TYPE.getZbio(), ref.getZbio());
            Assert.assertEquals(HABITAT, ref.getHabitat());
        }

        /*
        rhomeo=# select distinct h.cd_ref, h.espece from odo_dependance_habitat h join odo_especes_par_dept d on h.cd_ref=d.cd_ref and id_dept='04' and h.zbio='mediterraneen occidental' and h.habitat='10b' where h.valeur::float<3 and d.valeur>2;
        cd_ref |          espece
       --------+--------------------------
         65165 | Erythromma viridulum
         65101 | Pyrrhosoma nymphula
         65473 | Anax imperator
         65192 | Sympecma fusca
         65344 | Sympetrum striolatum
         65265 | Libellula fulva
         65262 | Libellula depressa
         65456 | Aeshna affinis
         65109 | Ischnura elegans
         65300 | Crocothemis erythraea
         65271 | Libellula quadrimaculata
         65322 | Sympetrum sanguineum
         65220 | Lestes viridis
         65145 | Coenagrion pulchellum
         65141 | Coenagrion puella
       (15 lignes)
       */

        final Map<Integer, Set<OdonataHabitatReference>> expected = new HashMap<>();
        final Set<OdonataHabitatReference> exp = new HashSet<>();
        exp.addAll(getHabitatReferences_2(erythromma_viridulum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(pyrrhosoma_nymphula, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(anax_imperator, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(sympecma_fusca, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(sympetrum_striolatum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(libellula_fulva, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(libellula_depressa, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(aeshna_affinis, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(ischnura_elegans, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(crocothemis_erythraea, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(libellula_quadrimaculata, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(sympetrum_sanguineum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(lestes_viridis, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(coenagrion_pulchellum, ODONATE_TYPE.getZbio(), HABITAT));
        exp.addAll(getHabitatReferences_2(coenagrion_puella, ODONATE_TYPE.getZbio(), HABITAT));
        expected.put(SOME_DATE.getYear(), exp);

        Assert.assertEquals(15, expected.get(SOME_DATE.getYear()).size());
        Assert.assertEquals(expected, result);
    }

    /**
     * @see I10Test#testT1_1()
     * @throws IntrospectionException
     */
    @Test
    public void testGetCdRefs_1() throws IntrospectionException{

        final OdonateZoneBio ODONATE_TYPE = MED_OCCIDENTAL;
        final String ID_DEPT = "01";
        final String HABITAT = hab_05f;

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                HABITAT);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);


        final Site site = new SiteImpl(null, ID_DEPT, "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), ODONATE_TYPE.getCode(), null, "name", "remarks");

        final Set<Integer> result = I10.getCdRefs(((I10) indicator).t1(statements, site));

        // Empiric content test.
        final Set<Integer> exp = new HashSet<>();

        exp.add(sympetrum_fonscolombii);
        exp.add(anax_imperator);
        exp.add(sympetrum_striolatum);
        exp.add(ceriagrion_tenellum);
        exp.add(libellula_fulva);
        exp.add(aeshna_affinis);
        exp.add(aeshna_cyanea);
        exp.add(ischnura_elegans);
        exp.add(ischnura_pumilio);
        exp.add(orthetrum_cancellatum);
        exp.add(crocothemis_erythraea);
        exp.add(sympetrum_sanguineum);
        exp.add(lestes_viridis);
        exp.add(brachytron_pratense);
        exp.add(coenagrion_puella);
        exp.add(orthetrum_brunneum);
        exp.add(645873); // erythromma_lindenii cd_ref is different from cd_nom
        exp.add(erythromma_viridulum);
        exp.add(orthetrum_albistylum);
        exp.add(sympetrum_pedemontanum);
        exp.add(pyrrhosoma_nymphula);
        exp.add(gomphus_pulchellus);
        exp.add(sympetrum_depressiusculum);
        exp.add(orthetrum_coerulescens);
        exp.add(libellula_depressa);
        Assert.assertEquals(exp, result);
    }

    /**
     * @see I10Test#testT1_2()
     * @throws IntrospectionException
     */
    @Test
    public void testGetCdRefs_2() throws IntrospectionException{

        final OdonateZoneBio ODONATE_TYPE = MED_OCCIDENTAL;
        final String ID_DEPT = "04";
        final String HABITAT = hab_10b;

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                HABITAT);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);

        final Site site = new SiteImpl(null, ID_DEPT, "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), ODONATE_TYPE.getCode(), null, "name", "remarks");


        final Set<Integer> result = I10.getCdRefs(((I10) indicator).t1(statements, site));

        // Empiric content test.
        final Set<Integer> exp = new HashSet<>();
        exp.add(erythromma_viridulum);
        exp.add(pyrrhosoma_nymphula);
        exp.add(anax_imperator);
        exp.add(sympecma_fusca);
        exp.add(sympetrum_striolatum);
        exp.add(libellula_fulva);
        exp.add(libellula_depressa);
        exp.add(aeshna_affinis);
        exp.add(ischnura_elegans);
        exp.add(crocothemis_erythraea);
        exp.add(libellula_quadrimaculata);
        exp.add(sympetrum_sanguineum);
        exp.add(lestes_viridis);
        exp.add(coenagrion_pulchellum);
        exp.add(coenagrion_puella);
        Assert.assertEquals(exp, result);
    }

    @Test
    public void testT2_1() throws IntrospectionException{

        final OdonateZoneBio ODONATE_TYPE = MED_OCCIDENTAL;
        final String ID_DEPT = "01";
        final String HABITAT = hab_05f;

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                HABITAT);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);


        final Site site = new SiteImpl(null, ID_DEPT, "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), ODONATE_TYPE.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Integer> result = I10.t2(t1);

        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));
        Assert.assertEquals(t1.get(SOME_DATE.getYear()).size(), (long) result.get(SOME_DATE.getYear()));
    }

    @Test
    public void testT2_2() throws IntrospectionException{

        final OdonateZoneBio ODONATE_TYPE = MED_OCCIDENTAL;
        final String ID_DEPT = "04";
        final String HABITAT = hab_10b;

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                HABITAT);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);

        final Site site = new SiteImpl(null, ID_DEPT, "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), ODONATE_TYPE.getCode(), null, "name", "remarks");


        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Integer> result = I10.t2(t1);

        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));
        Assert.assertEquals(t1.get(SOME_DATE.getYear()).size(), (long) result.get(SOME_DATE.getYear()));
    }

    @Test
    public void testT4_1() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);


        final Map<Integer, Set<Integer>> result = ((I10) indicator).t4(statements, operator);

        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));
        Assert.assertEquals(1, result.get(SOME_DATE.getYear()).size());

        Assert.assertEquals(aeshna_affinis, (int) result.get(SOME_DATE.getYear()).iterator().next());
    }

    @Test
    public void testT4_2() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);

        final Map<Integer, Set<Integer>> result = ((I10) indicator).t4(statements, operator);

        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));
        Assert.assertEquals(1, result.get(SOME_DATE.getYear()).size());

        Assert.assertEquals(brachytron_pratense, (int) result.get(SOME_DATE.getYear()).iterator().next());
    }

    @Test
    public void testT4_3() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);
        final OdonataStatement statement2 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);
        statements.add(statement2);

        final Map<Integer, Set<Integer>> result = ((I10) indicator).t4(statements, operator);

        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));
        Assert.assertEquals(2, result.get(SOME_DATE.getYear()).size());

        final Set<Integer> expectedSpecies = new HashSet<>();
        final Iterator<OdonataStatement> it1 = statements.iterator();
        while(it1.hasNext()){
            expectedSpecies.add(it1.next().getCd_nom());
        }
        Assert.assertEquals(2, expectedSpecies.size());

        final Iterator<Integer> it2 = result.get(SOME_DATE.getYear()).iterator();
        while(it2.hasNext()){
            Assert.assertTrue(expectedSpecies.contains(it2.next()));
        }
    }

    @Test
    public void testT4_4() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);
        final OdonataStatement statement2 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement3 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);
        statements.add(statement2);
        statements.add(statement3);

        final Map<Integer, Set<Integer>> result = ((I10) indicator).t4(statements, operator);

        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));
        Assert.assertEquals(2, result.get(SOME_DATE.getYear()).size());

        final Set<Integer> expectedSpecies = new HashSet<>();
        final Iterator<OdonataStatement> it1 = statements.iterator();
        while(it1.hasNext()){
            expectedSpecies.add(it1.next().getCd_nom());
        }
        Assert.assertEquals(2, expectedSpecies.size());

        final Iterator<Integer> it2 = result.get(SOME_DATE.getYear()).iterator();
        while(it2.hasNext()){
            Assert.assertTrue(expectedSpecies.contains(it2.next()));
        }
    }

    @Test // Empiric
    public void testT3_1() throws IntrospectionException, ReflectiveOperationException{


        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);


        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);

        Assert.assertEquals(aeshna_affinis, (int) t4.get(SOME_DATE.getYear()).iterator().next());

        final Map<Integer, Integer> result = I10.t3(t1, t4);

        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));
        Assert.assertEquals(1, (long) result.get(SOME_DATE.getYear()));

    }

    @Test // Empiric
    public void testT3_2() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);

        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);

        Assert.assertEquals(brachytron_pratense, (int) t4.get(SOME_DATE.getYear()).iterator().next());

        final Map<Integer, Integer> result = I10.t3(t1, t4);

        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));
        Assert.assertEquals(1, (long) result.get(SOME_DATE.getYear()));
    }

    @Test // Empiric
    public void testT3_3() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);
        final OdonataStatement statement2 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);
        statements.add(statement2);

        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);

        final Map<Integer, Integer> result = I10.t3(t1, t4);

        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));
        Assert.assertEquals(2, (long) result.get(SOME_DATE.getYear()));
    }

    @Test // Empiric
    public void testT3_4() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);
        final OdonataStatement statement2 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement3 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);
        statements.add(statement2);
        statements.add(statement3);

        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);

        final Map<Integer, Integer> result = I10.t3(t1, t4);

        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));
        Assert.assertEquals(2, (long) result.get(SOME_DATE.getYear()));
    }

    @Test // Empiric
    public void testT3_5() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);
        final OdonataStatement statement2 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement3 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement4 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                erythromma_lindeni,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);
        statements.add(statement2);
        statements.add(statement3);
        statements.add(statement4);

        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);

        final Map<Integer, Integer> result = I10.t3(t1, t4);

        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));

        /*
        - brachytron_pratense count only once.
        */
        Assert.assertEquals(3, (long) result.get(SOME_DATE.getYear()));
    }

    @Test // Empiric
    public void testT3_6() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);
        final OdonataStatement statement2 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement3 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement4 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                erythromma_lindeni,
                hab_10b);
        final OdonataStatement statement5 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                erythromma_lindenii,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);
        statements.add(statement2);
        statements.add(statement3);
        statements.add(statement4);
        statements.add(statement5);

        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);

        final Map<Integer, Integer> result = I10.t3(t1, t4);

        Assert.assertEquals(1, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));

        /*
        - brachytron_pratense count only once.
        - erythromma_lindeni is the reference taxon (cd_ref) of  erythromma_lindenii. Both taxon have the same 'nom_valide', but have different 'nom_complet'. So, both count.
        */
        Assert.assertEquals(4, (long) result.get(SOME_DATE.getYear()));
    }

    @Test // Empiric
    public void testT3_7() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);
        final OdonataStatement statement2 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement3 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE.plusYears(1),
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement4 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                erythromma_lindeni,
                hab_10b);
        final OdonataStatement statement5 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                erythromma_lindenii,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);
        statements.add(statement2);
        statements.add(statement3);
        statements.add(statement4);
        statements.add(statement5);

        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);

        final Map<Integer, Integer> result = I10.t3(t1, t4);

        Assert.assertEquals(2, result.keySet().size());
        Assert.assertNotNull(result.get(SOME_DATE.getYear()));
        Assert.assertNotNull(result.get(SOME_DATE.getYear()+1));

        Assert.assertEquals(4, (long) result.get(SOME_DATE.getYear()));
        Assert.assertEquals(1, (long) result.get(SOME_DATE.getYear()+1));
    }

    @Test
    public void testRate_1() throws IntrospectionException, ReflectiveOperationException{


        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);


        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);
        final Map<Integer, Integer> t3 = I10.t3(t1, t4);
        final Map<Integer, Integer> t2 = I10.t2(t1);


        Assert.assertEquals(1, t2.size());
        Assert.assertEquals(1, t3.size());

        /*
        rhomeo=# select distinct h.cd_ref, h.espece from odo_dependance_habitat h join odo_especes_par_dept d on h.cd_ref=d.cd_ref and id_dept='01' and h.zbio='mediterraneen occidental' and h.habitat='05f' where h.valeur::float<3 and d.valeur>2;
        cd_ref |          espece
       --------+---------------------------
         65335 | Sympetrum fonscolombii
         65473 | Anax imperator
         65344 | Sympetrum striolatum
         65169 | Ceriagrion tenellum
         65265 | Libellula fulva
         65456 | Aeshna affinis
         65440 | Aeshna cyanea
         65109 | Ischnura elegans
         65115 | Ischnura pumilio
         65278 | Orthetrum cancellatum
         65300 | Crocothemis erythraea
         65322 | Sympetrum sanguineum
         65220 | Lestes viridis
         65415 | Brachytron pratense
         65141 | Coenagrion puella
         65290 | Orthetrum brunneum
        645873 | Erythromma lindenii
         65165 | Erythromma viridulum
         65282 | Orthetrum albistylum
         65308 | Sympetrum pedemontanum
         65101 | Pyrrhosoma nymphula
         65227 | Gomphus pulchellus
         65318 | Sympetrum depressiusculum
         65284 | Orthetrum coerulescens
         65262 | Libellula depressa
       (25 lignes)
        */

        Assert.assertEquals(25, t2.values().iterator().next().intValue()); // 25 expected
        Assert.assertEquals(1, t3.values().iterator().next().intValue()); // 1 observed

        final Set<Index> rate = ((I10) indicator).rate(t2, t3);
        Assert.assertEquals(1, rate.size());
        Assert.assertEquals(4.0, rate.iterator().next().getValue().doubleValue(), 0.); // 1/25 = 4%

    }

    @Test
    public void testRate_2() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);

        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);
        final Map<Integer, Integer> t3 = I10.t3(t1, t4);
        final Map<Integer, Integer> t2 = I10.t2(t1);


        Assert.assertEquals(1, t2.size());
        Assert.assertEquals(1, t3.size());

        /*
        rhomeo=# select distinct h.cd_ref, h.espece from odo_dependance_habitat h join odo_especes_par_dept d on h.cd_ref=d.cd_ref and id_dept='01' and h.zbio='mediterraneen occidental' and h.habitat='10b' where h.valeur::float<3 and d.valeur>2;
        cd_ref |          espece
       --------+--------------------------
         65101 | Pyrrhosoma nymphula
        199909 | Aeshna isoceles
         65473 | Anax imperator
         65192 | Sympecma fusca
         65344 | Sympetrum striolatum
         65265 | Libellula fulva
         65262 | Libellula depressa
         65456 | Aeshna affinis
         65109 | Ischnura elegans
         65300 | Crocothemis erythraea
         65271 | Libellula quadrimaculata
         65322 | Sympetrum sanguineum
         65220 | Lestes viridis
         65145 | Coenagrion pulchellum
         65131 | Coenagrion scitulum
         65415 | Brachytron pratense
         65141 | Coenagrion puella
         65165 | Erythromma viridulum
       (18 lignes)
        */
        Assert.assertEquals(18, t2.values().iterator().next().intValue()); // 18 expected
        Assert.assertEquals(1, t3.values().iterator().next().intValue()); // 1 observed

        final Set<Index> rate = ((I10) indicator).rate(t2, t3);
        Assert.assertEquals(1, rate.size());
        Assert.assertEquals(100.*1./18., rate.iterator().next().getValue().doubleValue(), 0.);
    }

    @Test
    public void testRate_3() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);
        final OdonataStatement statement2 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);
        statements.add(statement2);

        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);
        final Map<Integer, Integer> t3 = I10.t3(t1, t4);
        final Map<Integer, Integer> t2 = I10.t2(t1);


        Assert.assertEquals(1, t2.size());
        Assert.assertEquals(1, t3.size());

        /*
        rhomeo=# select distinct h.cd_ref, h.espece
        from odo_dependance_habitat h join odo_especes_par_dept d on h.cd_ref=d.cd_ref and id_dept='01' and h.zbio='mediterraneen occidental' and (h.habitat='10b' or h.habitat='05f')
        where h.valeur::float<3 and d.valeur>2;
        cd_ref |          espece
       --------+---------------------------
         65335 | Sympetrum fonscolombii
         65473 | Anax imperator
         65344 | Sympetrum striolatum
         65169 | Ceriagrion tenellum
         65192 | Sympecma fusca
         65265 | Libellula fulva
         65440 | Aeshna cyanea
         65278 | Orthetrum cancellatum
         65322 | Sympetrum sanguineum
         65220 | Lestes viridis
         65131 | Coenagrion scitulum
         65290 | Orthetrum brunneum
        645873 | Erythromma lindenii
         65165 | Erythromma viridulum
         65282 | Orthetrum albistylum
         65308 | Sympetrum pedemontanum
         65101 | Pyrrhosoma nymphula
         65227 | Gomphus pulchellus
         65318 | Sympetrum depressiusculum
         65284 | Orthetrum coerulescens
        199909 | Aeshna isoceles
         65262 | Libellula depressa
         65456 | Aeshna affinis
         65109 | Ischnura elegans
         65115 | Ischnura pumilio
         65300 | Crocothemis erythraea
         65271 | Libellula quadrimaculata
         65145 | Coenagrion pulchellum
         65415 | Brachytron pratense
         65141 | Coenagrion puella
        (30 lignes)
         */
        Assert.assertEquals(30, t2.values().iterator().next().intValue()); // 43 expected
        Assert.assertEquals(2, t3.values().iterator().next().intValue()); // 1 observed

        final Set<Index> rate = ((I10) indicator).rate(t2, t3);
        Assert.assertEquals(1, rate.size());
        Assert.assertEquals(100.*2./30., rate.iterator().next().getValue().doubleValue(), 0.);
    }

    @Test
    public void testRate_4() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);
        final OdonataStatement statement2 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement3 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);
        statements.add(statement2);
        statements.add(statement3);

        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);
        final Map<Integer, Integer> t3 = I10.t3(t1, t4);
        final Map<Integer, Integer> t2 = I10.t2(t1);


        Assert.assertEquals(1, t2.size());
        Assert.assertEquals(1, t3.size());

        /*
        rhomeo=# select distinct h.cd_ref, h.espece
        from odo_dependance_habitat h join odo_especes_par_dept d on h.cd_ref=d.cd_ref and id_dept='01' and h.zbio='mediterraneen occidental' and (h.habitat='10b' or h.habitat='05f')
        where h.valeur::float<3 and d.valeur>2;
         cd_ref |          espece
        --------+---------------------------
          65335 | Sympetrum fonscolombii
          65473 | Anax imperator
          65344 | Sympetrum striolatum
          65169 | Ceriagrion tenellum
          65192 | Sympecma fusca
          65265 | Libellula fulva
          65440 | Aeshna cyanea
          65278 | Orthetrum cancellatum
          65322 | Sympetrum sanguineum
          65220 | Lestes viridis
          65131 | Coenagrion scitulum
          65290 | Orthetrum brunneum
         645873 | Erythromma lindenii
          65165 | Erythromma viridulum
          65282 | Orthetrum albistylum
          65308 | Sympetrum pedemontanum
          65101 | Pyrrhosoma nymphula
          65227 | Gomphus pulchellus
          65318 | Sympetrum depressiusculum
          65284 | Orthetrum coerulescens
         199909 | Aeshna isoceles
          65262 | Libellula depressa
          65456 | Aeshna affinis
          65109 | Ischnura elegans
          65115 | Ischnura pumilio
          65300 | Crocothemis erythraea
          65271 | Libellula quadrimaculata
          65145 | Coenagrion pulchellum
          65415 | Brachytron pratense
          65141 | Coenagrion puella
        (30 lignes)
         */
        Assert.assertEquals(30, t2.values().iterator().next().intValue()); // 43 expected
        Assert.assertEquals(2, t3.values().iterator().next().intValue()); // 2 observed

        final Set<Index> rate = ((I10) indicator).rate(t2, t3);
        Assert.assertEquals(1, rate.size());
        Assert.assertEquals(100.*2./30., rate.iterator().next().getValue().doubleValue(), 0.);
    }

    @Test
    public void testRate_5() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);
        final OdonataStatement statement2 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement3 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement4 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                erythromma_lindeni,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);
        statements.add(statement2);
        statements.add(statement3);
        statements.add(statement4);

        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);
        final Map<Integer, Integer> t3 = I10.t3(t1, t4);
        final Map<Integer, Integer> t2 = I10.t2(t1);


        Assert.assertEquals(1, t2.size());
        Assert.assertEquals(1, t3.size());

        /*
        - brachytron_pratense count only once.
        */
        Assert.assertEquals(3, (long) t3.get(SOME_DATE.getYear()));

        /*
        rhomeo=# select distinct h.cd_ref, h.espece
        from odo_dependance_habitat h join odo_especes_par_dept d on h.cd_ref=d.cd_ref and id_dept='01' and h.zbio='mediterraneen occidental' and (h.habitat='10b' or h.habitat='05f')
        where h.valeur::float<3 and d.valeur>2;
        cd_ref |          espece
       --------+---------------------------
         65335 | Sympetrum fonscolombii
         65473 | Anax imperator
         65344 | Sympetrum striolatum
         65169 | Ceriagrion tenellum
         65192 | Sympecma fusca
         65265 | Libellula fulva
         65440 | Aeshna cyanea
         65278 | Orthetrum cancellatum
         65322 | Sympetrum sanguineum
         65220 | Lestes viridis
         65131 | Coenagrion scitulum
         65290 | Orthetrum brunneum
        645873 | Erythromma lindenii
         65165 | Erythromma viridulum
         65282 | Orthetrum albistylum
         65308 | Sympetrum pedemontanum
         65101 | Pyrrhosoma nymphula
         65227 | Gomphus pulchellus
         65318 | Sympetrum depressiusculum
         65284 | Orthetrum coerulescens
        199909 | Aeshna isoceles
         65262 | Libellula depressa
         65456 | Aeshna affinis
         65109 | Ischnura elegans
         65115 | Ischnura pumilio
         65300 | Crocothemis erythraea
         65271 | Libellula quadrimaculata
         65145 | Coenagrion pulchellum
         65415 | Brachytron pratense
         65141 | Coenagrion puella
       (30 lignes)
         */
        Assert.assertEquals(30, t2.values().iterator().next().intValue()); // 43 expected
        Assert.assertEquals(3, t3.values().iterator().next().intValue()); // 3 observed

        final Set<Index> rate = ((I10) indicator).rate(t2, t3);
        Assert.assertEquals(1, rate.size());
        Assert.assertEquals(100.*3./30., rate.iterator().next().getValue().doubleValue(), 0.);
    }

    @Test
    public void testRate_6() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);
        final OdonataStatement statement2 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement3 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement4 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                erythromma_lindeni,
                hab_10b);
        final OdonataStatement statement5 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                erythromma_lindenii,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);
        statements.add(statement2);
        statements.add(statement3);
        statements.add(statement4);
        statements.add(statement5);

        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);
        final Map<Integer, Integer> t3 = I10.t3(t1, t4);
        final Map<Integer, Integer> t2 = I10.t2(t1);


        Assert.assertEquals(1, t2.size());
        Assert.assertEquals(1, t3.size());

        Assert.assertEquals(1, t3.keySet().size());
        Assert.assertNotNull(t3.get(SOME_DATE.getYear()));

        /*
        - brachytron_pratense count only once.
        - erythromma_lindeni is the reference taxon (cd_ref) of  erythromma_lindenii. Both taxon have the same 'nom_valide', but have different 'nom_complet'. So, both count.
        */
        Assert.assertEquals(4, (long) t3.get(SOME_DATE.getYear()));

        /*
        rhomeo=# select distinct h.cd_ref, h.espece
        from odo_dependance_habitat h join odo_especes_par_dept d on h.cd_ref=d.cd_ref and id_dept='01' and h.zbio='mediterraneen occidental' and (h.habitat='10b' or h.habitat='05f')
        where h.valeur::float<3 and d.valeur>2;
        cd_ref |          espece
       --------+---------------------------
         65335 | Sympetrum fonscolombii
         65473 | Anax imperator
         65344 | Sympetrum striolatum
         65169 | Ceriagrion tenellum
         65192 | Sympecma fusca
         65265 | Libellula fulva
         65440 | Aeshna cyanea
         65278 | Orthetrum cancellatum
         65322 | Sympetrum sanguineum
         65220 | Lestes viridis
         65131 | Coenagrion scitulum
         65290 | Orthetrum brunneum
        645873 | Erythromma lindenii
         65165 | Erythromma viridulum
         65282 | Orthetrum albistylum
         65308 | Sympetrum pedemontanum
         65101 | Pyrrhosoma nymphula
         65227 | Gomphus pulchellus
         65318 | Sympetrum depressiusculum
         65284 | Orthetrum coerulescens
        199909 | Aeshna isoceles
         65262 | Libellula depressa
         65456 | Aeshna affinis
         65109 | Ischnura elegans
         65115 | Ischnura pumilio
         65300 | Crocothemis erythraea
         65271 | Libellula quadrimaculata
         65145 | Coenagrion pulchellum
         65415 | Brachytron pratense
         65141 | Coenagrion puella
       (30 lignes)
         */
        Assert.assertEquals(30, t2.values().iterator().next().intValue()); // 43 expected
        Assert.assertEquals(4, t3.values().iterator().next().intValue()); // 4 observed

        final Set<Index> rate = ((I10) indicator).rate(t2, t3);
        Assert.assertEquals(1, rate.size());
        Assert.assertEquals(100.*4./30., rate.iterator().next().getValue().doubleValue(), 0.);
    }

    @Test
    public void testRate_7() throws IntrospectionException, ReflectiveOperationException{

        final OdonataStatement statement1 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                aeshna_affinis,
                hab_05f);
        final OdonataStatement statement2 = new OdonataStatement(
                "trackingPointId 1",
                SOME_DATE,
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement3 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE.plusYears(1),
                "Some remarks",
                brachytron_pratense,
                hab_10b);
        final OdonataStatement statement4 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                erythromma_lindeni,
                hab_10b);
        final OdonataStatement statement5 = new OdonataStatement(
                "trackingPointId 2",
                SOME_DATE,
                "Some remarks",
                erythromma_lindenii,
                hab_10b);

        final Collection<OdonataStatement> statements = new ArrayList<>();
        statements.add(statement1);
        statements.add(statement2);
        statements.add(statement3);
        statements.add(statement4);
        statements.add(statement5);

        final Site site = new SiteImpl(null, "01", "referent", "organization", HumidZoneType.BORDURES_COURS_EAU.getCode(), MED_OCCIDENTAL.getCode(), null, "name", "remarks");

        final Map<Integer, Set<OdonataHabitatReference>> t1 = ((I10) indicator).t1(statements, site);
        final Map<Integer, Set<Integer>> t4 = ((I10) indicator).t4(statements, operator);
        final Map<Integer, Integer> t3 = I10.t3(t1, t4);
        final Map<Integer, Integer> t2 = I10.t2(t1);


        Assert.assertEquals(2, t2.size());
        Assert.assertEquals(2, t3.size());


        Assert.assertEquals(2, t3.keySet().size());
        Assert.assertNotNull(t3.get(SOME_DATE.getYear()));
        Assert.assertNotNull(t3.get(SOME_DATE.getYear()+1));

        Assert.assertEquals(4, (long) t3.get(SOME_DATE.getYear()));
        Assert.assertEquals(1, (long) t3.get(SOME_DATE.getYear()+1));

        final Map<Integer, Integer> expectedT2 = new HashMap<>();
        final Map<Integer, Integer> expectedT3 = new HashMap<>();

        /*
        => SOME_DATE.getYear()

        rhomeo=# select distinct h.cd_ref, h.espece,
        from odo_dependance_habitat h join odo_especes_par_dept d on h.cd_ref=d.cd_ref and id_dept='01' and h.zbio='mediterraneen occidental' and (h.habitat='10b' or h.habitat='05f')
        where h.valeur::float<3 and d.valeur>2;
        cd_ref |          espece
       --------+---------------------------
         65335 | Sympetrum fonscolombii
         65473 | Anax imperator
         65344 | Sympetrum striolatum
         65169 | Ceriagrion tenellum
         65192 | Sympecma fusca
         65265 | Libellula fulva
         65440 | Aeshna cyanea
         65278 | Orthetrum cancellatum
         65322 | Sympetrum sanguineum
         65220 | Lestes viridis
         65131 | Coenagrion scitulum
         65290 | Orthetrum brunneum
        645873 | Erythromma lindenii
         65165 | Erythromma viridulum
         65282 | Orthetrum albistylum
         65308 | Sympetrum pedemontanum
         65101 | Pyrrhosoma nymphula
         65227 | Gomphus pulchellus
         65318 | Sympetrum depressiusculum
         65284 | Orthetrum coerulescens
        199909 | Aeshna isoceles
         65262 | Libellula depressa
         65456 | Aeshna affinis
         65109 | Ischnura elegans
         65115 | Ischnura pumilio
         65300 | Crocothemis erythraea
         65271 | Libellula quadrimaculata
         65145 | Coenagrion pulchellum
         65415 | Brachytron pratense
         65141 | Coenagrion puella
       (30 lignes)
         */

        expectedT2.put(SOME_DATE.getYear(), 30); // 43 expected
        expectedT3.put(SOME_DATE.getYear(), 4); // 4 observed

        /*
        => SOME_DATE.getYear()+1

        rhomeo=# select distinct h.cd_ref, h.espece
        from odo_dependance_habitat h join odo_especes_par_dept d on h.cd_ref=d.cd_ref and id_dept='01' and h.zbio='mediterraneen occidental' and h.habitat='10b'
        where h.valeur::float<3 and d.valeur>2;
        cd_ref |          espece
       --------+--------------------------
         65101 | Pyrrhosoma nymphula
        199909 | Aeshna isoceles
         65473 | Anax imperator
         65192 | Sympecma fusca
         65344 | Sympetrum striolatum
         65265 | Libellula fulva
         65262 | Libellula depressa
         65456 | Aeshna affinis
         65109 | Ischnura elegans
         65300 | Crocothemis erythraea
         65271 | Libellula quadrimaculata
         65322 | Sympetrum sanguineum
         65220 | Lestes viridis
         65145 | Coenagrion pulchellum
         65131 | Coenagrion scitulum
         65415 | Brachytron pratense
         65141 | Coenagrion puella
         65165 | Erythromma viridulum
       (18 lignes)
         */

        expectedT2.put(SOME_DATE.getYear()+1, 18); // 18 expected
        expectedT3.put(SOME_DATE.getYear()+1, 1); // 1 observed

        Assert.assertEquals(expectedT2, t2);
        Assert.assertEquals(expectedT3, t3);

        final Set<Index> rate = ((I10) indicator).rate(t2, t3);
        Assert.assertEquals(2, rate.size());

        final Set<Index> expectedRates = new HashSet<>();
        expectedRates.add(indexSpi.createIndex(100.*4./30., SOME_DATE.getYear()));
        expectedRates.add(indexSpi.createIndex(100.*1./18., SOME_DATE.getYear()+1));
        Assert.assertEquals(expectedRates, rate);
    }

    @Test
    public void testGetConsistentReferences() throws IntrospectionException{

        final String dept_id = "04";
        final OdonateZoneBio zoneBioOdonates = MED_OCCIDENTAL;

        final Map<String, Set<Map.Entry<OdonataHabitatReference, OdonataDepartmentReference>>> consistentRefs = ((I10) indicator).getConsistentReferences(zoneBioOdonates, dept_id);

        // Check for consistency
        for(final Map.Entry<String, Set<Map.Entry<OdonataHabitatReference, OdonataDepartmentReference>>> entry : consistentRefs.entrySet()){

            for(final Map.Entry<OdonataHabitatReference, OdonataDepartmentReference> ref : entry.getValue()){
                Assert.assertEquals(zoneBioOdonates.getZbio(), ref.getKey().getZbio());
                Assert.assertEquals(dept_id, ref.getValue().getId_dept());
                Assert.assertEquals(ref.getKey().getCd_ref(), ref.getValue().getCd_ref());
            }
        }

        /*
        rhomeo=# select distinct habitat from odo_dependance_habitat where habitat
        not in (
        select h.habitat from odo_dependance_habitat h join odo_especes_par_dept d on h.cd_nom=d.cd_nom and id_dept='04' and h.zbio='mediterraneen occidental');
         habitat
        ---------
         06
        (1 ligne)
        */
        Assert.assertNull(consistentRefs.get(hab_06));

        /*
        rhomeo=# select count(distinct habitat) from odo_dependance_habitat where habitat
        in (
        select h.habitat from odo_dependance_habitat h join odo_especes_par_dept d on h.cd_nom=d.cd_nom and id_dept='04' and h.zbio='mediterraneen occidental');
         count
        -------
            44
        (1 ligne)
        */
        Assert.assertEquals(44, consistentRefs.entrySet().size());

        /*
        rhomeo=# select count(distinct h.cd_ref) from odo_dependance_habitat h
        join odo_especes_par_dept d on h.cd_nom=d.cd_nom and id_dept='04' and h.zbio='mediterraneen occidental' and h.habitat='01a';
         count
        -------
            20
        (1 ligne)
        */
        Assert.assertEquals(20, consistentRefs.get(hab_01a).size());

        /*
        rhomeo=# select count(distinct h.cd_ref) from odo_dependance_habitat h
        join odo_especes_par_dept d on h.cd_nom=d.cd_nom and id_dept='04' and h.zbio='mediterraneen occidental' and h.habitat='05f';
         count
        -------
            52
        (1 ligne)
        */
        Assert.assertEquals(52, consistentRefs.get(hab_05f).size());

        /*
        rhomeo=# select count(distinct h.cd_ref) from odo_dependance_habitat h
        join odo_especes_par_dept d on h.cd_nom=d.cd_nom and id_dept='04' and h.zbio='mediterraneen occidental' and h.habitat='10b';
         count
        -------
            46
        (1 ligne)
        */
        Assert.assertEquals(46, consistentRefs.get(hab_10b).size());
    }

    @Test
    public void testGetDepartmentReferences() throws IntrospectionException{

        final String dept_id = "04";

        final Map<Integer, Set<OdonataDepartmentReference>> departmentReferences = ((I10) indicator).getDepartmentReferences(dept_id);

        Assert.assertEquals(87, departmentReferences.entrySet().size());

        for(final Map.Entry<Integer, Set<OdonataDepartmentReference>> departmentReference : departmentReferences.entrySet()){
            Assert.assertEquals(getDepartmentReferences_1(departmentReference.getKey(), dept_id), departmentReference.getValue());
        }

    }

    @Test
    public void testGetHabitatReferences() throws IntrospectionException{

        final OdonateZoneBio zoneBioOdonates = MED_OCCIDENTAL;

        final Map<String, Set<OdonataHabitatReference>> habitatReferences = ((I10) indicator).getHabitatReferences(zoneBioOdonates);

        Assert.assertEquals(44, habitatReferences.entrySet().size());

        for(final Map.Entry<String, Set<OdonataHabitatReference>> habitatReference : habitatReferences.entrySet()){
            Assert.assertEquals(getHabitatReferences_1(habitatReference.getKey(), MED_OCCIDENTAL), habitatReference.getValue());
        }

    }

    /**
     * Utility method to retrieve {@link OdonataHabitatReference}s.
     *
     * @param habitat
     * @param zonebio
     * @return
     * @throws IntrospectionException
     */
    private Set<OdonataHabitatReference> getHabitatReferences_1(final String habitat, final OdonateZoneBio zonebio) throws IntrospectionException {

        // Get habitat references.
        final List<OdonataHabitatReference> references = I10.getReferences(odonataHabitatReferenceDescription);

        // Prepare the result structure.
        final Set<OdonataHabitatReference> result = new HashSet<>();

        // Loop over habitat references.
        for (final OdonataHabitatReference reference : references) {
            if (zonebio.getZbio().equals(reference.getZbio()) && habitat.equals(reference.getHabitat())){
                    result.add(reference);
            }
        }
        return result;
    }

    /**
     * Utility method to retrieve {@link OdonataHabitatReference}s.
     *
     * @param cd_nom
     * @param zbio
     * @param habitat
     * @return
     * @throws IntrospectionException
     */
    private Set<OdonataHabitatReference> getHabitatReferences_2(final int cdNom, final String zbio, final String habitat) throws IntrospectionException{

        final List<OdonataHabitatReference> references = I10.getReferences(odonataHabitatReferenceDescription);

        final Set<OdonataHabitatReference> result = new HashSet<>();
        for(final OdonataHabitatReference ref : references){
            if(ref.getCd_nom()==cdNom && ref.getZbio().equals(zbio) && ref.getHabitat().equals(habitat)){
                result.add(ref);
            }
        }
        return result;
    }

    /**
     * Utility method to retrieve {@link OdonataDepartmentReference}s.
     *
     * @param cd_ref
     * @param id_dept
     * @return
     * @throws IntrospectionException
     */
    private Set<OdonataDepartmentReference> getDepartmentReferences_1(final int cdRef, final String id_dept) throws IntrospectionException {

        final List<OdonataDepartmentReference> references = I10.getReferences(odonataDepartmentReferenceDescription);

        final Set<OdonataDepartmentReference> result = new HashSet<>();
        for(final OdonataDepartmentReference ref : references){
            if(ref.getCd_ref()==cdRef && ref.getId_dept().equals(id_dept)){
                result.add(ref);
            }
        }
        return result;
    }

    /**
     * Utility method to retrieve {@link OdonataDepartmentReference}s.
     *
     * @param cd_nom
     * @param id_dept
     * @return
     * @throws IntrospectionException
     */
    private Set<OdonataDepartmentReference> getDepartmentReferences_2(final String cd_nom, final String id_dept) throws IntrospectionException {

        final int cdNom = Integer.parseInt(cd_nom);
        final List<OdonataDepartmentReference> references = I10.getReferences(odonataDepartmentReferenceDescription);

        final Set<OdonataDepartmentReference> result = new HashSet<>();
        for(final OdonataDepartmentReference ref : references){
            if(ref.getCd_nom()==cdNom && ref.getId_dept().equals(id_dept)){
                result.add(ref);
            }
        }
        return result;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    //   EMPIRIC TEST CASES
    //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * Compute indices by point and by year from reference data, and check that
     * result is compliant with client's provided ones.
     *
     * @throws java.lang.Exception If something goes wrong.
     */
    @Test
    public void testComputing7() throws Exception {

        final String testId = "P06-7";

        final Site site = new SiteImpl(null, "69", "referent", "organization", HumidZoneType.MARAIS_LAGUNES.getCode(), OdonateZoneBio.CONTINENTAL.getCode(), null, "name", "remarks");

        final ProcessContext pctx = new ProcessContext(createRefDataset(p06, "p06/test"+testId+".csv"), new DataContext(site, p06));

        // Compute indices. Separate them by type.
        final Set<Index<Double>> integrityIndices  = new HashSet<>();
        final Set<Index<Integer>> nbSpeciesIndices  = new HashSet<>();
        final Set<Index<Double>> richnessIndices  = new HashSet<>();

        final RunnableFuture f = new FutureTask((Callable) () -> {

            indicator.createProcesses(pctx).forEach(process -> {
                try {
                    process.run();
                    process.get().forEach(index -> {

                        if (index.getSpi()==indexSpi) {
                            integrityIndices.add(index);
                        }
                        else if(index.getSpi()==nbSpeciesIndexSpi){
                            nbSpeciesIndices.add(index);
                        }
                        else if(index.getSpi()==richnessIndexSpi){
                            richnessIndices.add(index);
                        }
                    });
                } catch (Exception ex) {
                    Assert.fail("Proccess failed !");
                }
            });
            return "";
        });

        Platform.runLater(f);
        Assert.assertNotNull(f.get());


        // Check results.
        Assert.assertEquals("Number of integrity indices is wrong !", 4, integrityIndices.size());
        Assert.assertEquals("Number of species number indices is wrong !", 4, nbSpeciesIndices.size());
        Assert.assertEquals("Number of richness indices is wrong !", 4, richnessIndices.size());


        // Load expected results
        final Map<Integer, Double> integrityResults =  TestUtilities.results("p06/result"+testId+"_"+indexSpi.getClass().getSimpleName()+".csv", YearResult.DoubleYearResult.class);
        final Map<Integer, Integer> nbSpeciesResults =  TestUtilities.results("p06/result"+testId+"_"+nbSpeciesIndexSpi.getClass().getSimpleName()+".csv", YearResult.IntegerYearResult.class);
        final Map<Integer, Double> richnessResults =  TestUtilities.results("p06/result"+testId+"_"+richnessIndexSpi.getClass().getSimpleName()+".csv", YearResult.DoubleYearResult.class);

        for (final Index<Double> index : integrityIndices) {
            Assert.assertEquals("Integrity result differs for year : ".concat(String.valueOf(index.getYear())),
                    integrityResults.get(index.getYear()),
                    index.getValue(),
                    0.01
            );
        }
        for (final Index<Integer> index : nbSpeciesIndices) {
            Assert.assertEquals("Species nb result differs for year : ".concat(String.valueOf(index.getYear())),
                    nbSpeciesResults.get(index.getYear()),
                    index.getValue()
            );
        }
        for (final Index<Double> index : richnessIndices) {
            Assert.assertEquals("Richness result differs for year : ".concat(String.valueOf(index.getYear())),
                    richnessResults.get(index.getYear()),
                    index.getValue(),
                    0.01
            );
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    //   DATA-DRIVEN TEST CASES
    //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * Data of this test needed to be pre-processed (right join, replace empty cd_odo by null values).
     *
     * copy (
     * select o.cd_nom, o.cd_ref, o.cd_odo as "speciesName", null as habitat,  null as behavior, null as remarks, r.date, p.id_point_suivi as "trackingPoint"
     * from obs_especes o right join releves r on o.id_releve=r.id_releve join points_de_suivis p on r.id_point_suivi=p.id_point_suivi
     * where p.id_site='82000007' and (o.code_thematique='odonates' or r.code_thematique='odonates')
     * )
     * to 'testP06-8.csv' delimiter ';' csv header;
     *
     * result => COPY 181
     *
     * Compute indices by point and by year from reference data, and check that
     * result is compliant with client's provided ones.
     *
     * POUR UN TEST PLUS FIABLE, VOIR LE TEST {@link I10Test#testComputing9() }
     *
     * @throws java.lang.Exception If something goes wrong.
     */
    @Test
    public void testComputing8() throws Exception {

        final String testId = "P06-8";

        final Site site = new SiteImpl(null, "74", "referent", "organization", HumidZoneType.MARAIS_LANDES_TOURB.getCode(), OdonateZoneBio.ALPIN.getCode(), null, "name", "remarks");

        // Données issues du site 82000007 (Bossenot), sans habitat et autres champs incomplets.
        final ProcessContext pctx = new ProcessContext(createRefDataset(p06, "p06/test"+testId+".csv"), new DataContext(site, p06));

        // Compute indices. Separate them by type.
        final Set<Index<Double>> richnessIndices  = new HashSet<>();

        final RunnableFuture f = new FutureTask((Callable) () -> {

            indicator.createProcesses(pctx).forEach(process -> {
                try {
                    process.run();
                    process.get().forEach(index -> {
                        if(index.getSpi()==richnessIndexSpi){
                            richnessIndices.add(index);
                        }
                    });
                } catch (Exception ex) {
                    Assert.fail("Proccess failed !");
                }
            });
            return "";
        });

        Platform.runLater(f);
        Assert.assertNotNull(f.get());


        // Check results.
        Assert.assertEquals("Number of richness indices is wrong !", 3, richnessIndices.size());


        // Load expected results
        final Map<Integer, Double> richnessResults =  TestUtilities.results("p06/result"+testId+"_"+richnessIndexSpi.getClass().getSimpleName()+".csv", YearResult.DoubleYearResult.class);

        for (final Index<Double> index : richnessIndices) {
            Assert.assertEquals("Richness result differs for year : ".concat(String.valueOf(index.getYear())),
                    richnessResults.get(index.getYear()),
                    index.getValue(),
                    0.01
            );
        }
    }

    /**
     * The following lines are the database query to generate dataset the dataset.
     *
     * copy (
     * select o.cd_nom, o.cd_ref, o.cd_odo as "speciesName", v.valeur as habitat, comportement as behavior, null as remarks, r.date, p.id_point_suivi as "trackingPoint"
     * from obs_especes o right join releves r on o.id_releve=r.id_releve join points_de_suivis p on r.id_point_suivi=p.id_point_suivi left join releves_valeurs v on v.id_releve=r.id_releve
     * where (v.id_attribut='habitats_odo' or v.id_attribut is null) and p.id_site='82000007' and (o.code_thematique='odonates' or r.code_thematique='odonates')
     * )
     * to '/home/samuel/Bureau/testP06-.csv' delimiter ';' csv header;
     *
     * The csv file need the behavior to be mapped.
     *
     * @throws Exception
     */
    @Test
    public void testComputing9() throws Exception {

        final String testId = "P06-9";

        final Site site = new SiteImpl(null, "74", "referent", "organization", HumidZoneType.MARAIS_LAGUNES.getCode(), OdonateZoneBio.ALPIN.getCode(), null, "name", "remarks");

        // Données issues du site 82000007 (Bossenot)
        final ProcessContext pctx = new ProcessContext(createRefDataset(p06, "p06/test"+testId+".csv"), new DataContext(site, p06));

        // Compute indices. Separate them by type.
        final Set<Index<Double>> integrityIndices  = new HashSet<>();
        final Set<Index<Integer>> nbSpeciesIndices  = new HashSet<>();
        final Set<Index<Double>> richnessIndices  = new HashSet<>();

        final RunnableFuture f = new FutureTask((Callable) () -> {

            indicator.createProcesses(pctx).forEach(process -> {
                try {
                    process.run();
                    process.get().forEach(index -> {

                        if (index.getSpi()==indexSpi) {
                            integrityIndices.add(index);
                        }
                        else if(index.getSpi()==nbSpeciesIndexSpi){
                            nbSpeciesIndices.add(index);
                        }
                        else if(index.getSpi()==richnessIndexSpi){
                            richnessIndices.add(index);
                        }
                    });
                } catch (Exception ex) {
                    Assert.fail("Proccess failed !");
                }
            });
            return "";
        });

        Platform.runLater(f);
        Assert.assertNotNull(f.get());


        // Check results.
        Assert.assertEquals("Number of integrity indices is wrong !", 3, integrityIndices.size());
        Assert.assertEquals("Number of species number indices is wrong !", 3, nbSpeciesIndices.size());
        Assert.assertEquals("Number of richness indices is wrong !", 3, richnessIndices.size());


        // Load expected results
        final Map<Integer, Double> integrityResults =  TestUtilities.results("p06/result"+testId+"_"+indexSpi.getClass().getSimpleName()+".csv", YearResult.DoubleYearResult.class);
        final Map<Integer, Integer> nbSpeciesResults =  TestUtilities.results("p06/result"+testId+"_"+nbSpeciesIndexSpi.getClass().getSimpleName()+".csv", YearResult.IntegerYearResult.class);
        final Map<Integer, Double> richnessResults =  TestUtilities.results("p06/result"+testId+"_"+richnessIndexSpi.getClass().getSimpleName()+".csv", YearResult.DoubleYearResult.class);

        for (final Index<Double> index : integrityIndices) {
            Assert.assertEquals("Integrity result differs for year : ".concat(String.valueOf(index.getYear())),
                    integrityResults.get(index.getYear()),
                    index.getValue(),
                    0.01
            );
        }
        for (final Index<Integer> index : nbSpeciesIndices) {
            Assert.assertEquals("Species nb result differs for year : ".concat(String.valueOf(index.getYear())),
                    nbSpeciesResults.get(index.getYear()),
                    index.getValue()
            );
        }
        for (final Index<Double> index : richnessIndices) {
            Assert.assertEquals("Richness result differs for year : ".concat(String.valueOf(index.getYear())),
                    richnessResults.get(index.getYear()),
                    index.getValue(),
                    0.01
            );
        }
    }
}
