@testset "Fitness" begin
    @testset "hat_compare() Float64" begin
        @test hat_compare(1.0, 2.0) == -1
        @test hat_compare(-1.0, 1.0) == -1

        @test hat_compare(2.0, 1.0) == 1
        @test hat_compare(-2.0, -3.0) == 1

        @test hat_compare(1.0, 1.0) == 0
        @test hat_compare(0.0, 0.0) == 0
        @test hat_compare(-1.0, -1.0) == 0

        @test hat_compare(0.0, NaN) == -1
        @test hat_compare(NaN, 1.0) ==  1
        @test hat_compare(NaN, NaN) ==  0
    end

    @testset "ScalarFitnessScheme" begin
        @testset "is_minimizing()" begin
            mins = MinimizingFitnessScheme
            @test numobjectives(mins) == 1
            @test fitness_type(mins) === Float64
            @test fitness_eltype(mins) === Float64
            @test is_minimizing(mins)

            maxs = MaximizingFitnessScheme
            @test numobjectives(maxs) == 1
            @test fitness_type(maxs) === Float64
            @test fitness_eltype(maxs) === Float64
            @test is_minimizing(maxs) == false
        end

        @testset "hat_compare(..., MinimizingFitnessScheme)" begin
            scheme = MinimizingFitnessScheme

            @test hat_compare(1.0, 2.0, scheme) == -1
            @test hat_compare(2.0, 1.0, scheme) == 1
            @test hat_compare(1.0, 1.0, scheme) == 0
        end

        @testset "hat_compare(..., MaximizingFitnessScheme)" begin
            scheme = MaximizingFitnessScheme

            @test hat_compare(1.0, 2.0, scheme) == 1
            @test hat_compare(2.0, 1.0, scheme) == -1
            @test hat_compare(1.0, 1.0, scheme) == 0
        end

        # FIXME enable tests once v0.5 issue #14919 is fixed
        @testset "fitness_scheme(x, y)" begin
            mins = MinimizingFitnessScheme
            #@test !mins(5.0, 3.0)
            #@test mins(1.0, 2.0)

            maxs = MaximizingFitnessScheme
            #@test !maxs(3.0, 5.0)
            #@test maxs(2.0, 1.0)
        end
    end

    @testset "TupleFitnessScheme" begin
        @testset "general interface" begin
            @testset "ParetoFitnessScheme{1}" begin
                scheme = ParetoFitnessScheme{1}()
                @test numobjectives(scheme) == 1
                @test fitness_type(scheme) === Tuple{Float64}
                @test fitness_eltype(scheme) === Float64
                @test is_minimizing(scheme)
                @test isnafitness(nafitness(scheme), scheme)
                @test isequal(nafitness(scheme), (NaN,))
                @test isnafitness((NaN,), scheme)
                @test isnafitness((1.0,), scheme) == false
            end

            @testset "ParetoFitnessScheme{1}(is_minimizing=false)" begin
                scheme = ParetoFitnessScheme{1}(is_minimizing=false)
                @test fitness_type(scheme) == NTuple{1, Float64}
                @test fitness_eltype(scheme) === Float64
                @test numobjectives(scheme) == 1
                @test is_minimizing(scheme) == false
                @test isnafitness(nafitness(scheme), scheme)
                @test isequal(nafitness(scheme), (NaN,))
                @test isnafitness((NaN,), scheme)
                @test isnafitness((1.0,), scheme) == false
            end

            @testset "ParetoFitnessScheme{2}()" begin
                scheme = ParetoFitnessScheme{2}()
                @test fitness_type(scheme) == NTuple{2, Float64}
                @test fitness_eltype(scheme) === Float64
                @test isequal(nafitness(scheme), (NaN,NaN))
                @test numobjectives(scheme) == 2
                @test is_minimizing(scheme)
                @test isnafitness(nafitness(scheme), scheme)
                @test isnafitness((NaN, NaN), scheme)
                @test isnafitness((1.0, NaN), scheme)
                @test !isnafitness((1.0, 2.0), scheme)
            end

            @testset "ParetoFitnessScheme{3}()" begin
                scheme = ParetoFitnessScheme{3}()
                @test fitness_type(scheme) == NTuple{3, Float64}
                @test fitness_eltype(scheme) === Float64
                @test isequal(nafitness(scheme), (NaN,NaN,NaN))
                @test numobjectives(scheme) == 3
                @test is_minimizing(scheme)
                @test isnafitness(nafitness(scheme), scheme)
                @test isnafitness((NaN,NaN,NaN), scheme)
                @test isnafitness((1.0,NaN,2.0), scheme)
                @test isnafitness((1.0,2.0,2.0), scheme) == false
            end
        end

        @testset "hat_compare(..., ParetoFitnessScheme{1}())" begin
            scheme = ParetoFitnessScheme{1}()

            @test_throws MethodError hat_compare((-1.0,), (1.0, 2.0), scheme)

            @test hat_compare((-1.0,), (1.0,), scheme) == -1
            @test hat_compare((0.0,), (0.3,), scheme) == -1
            @test hat_compare((11.3,), (354.65,), scheme) == -1

            @test hat_compare((-1.0,), (-1.0,), scheme) == 0
            @test hat_compare((0.0,), (0.0,), scheme) == 0
            @test hat_compare((0.2,), (0.2,), scheme) == 0

            @test hat_compare((1.0,), (0.0,), scheme) == 1
            @test hat_compare((0.0,), (-0.4,), scheme) == 1
            @test hat_compare((-0.65,), (-34.2,), scheme) == 1
        end

        @testset "hat_compare(..., ParetoFitnessScheme{1}(is_minimizing=false))" begin
            scheme = ParetoFitnessScheme{1}(is_minimizing=false)
            @test hat_compare((-1.0,), (1.0,), scheme) == 1
            @test hat_compare((0.0,), (0.3,), scheme) == 1
            @test hat_compare((11.3,), (354.65,), scheme) == 1
            @test hat_compare((-1.0,), (-1.0,), scheme) == 0
            @test hat_compare((0.0,), (0.0,), scheme) == 0
            @test hat_compare((0.2,), (0.2,), scheme) == 0
            @test hat_compare((1.0,), (0.0,), scheme) == -1
            @test hat_compare((0.0,), (-0.4,), scheme) == -1
            @test hat_compare((-0.65,), (-34.2,), scheme) == -1
        end

        @testset "hat_compare(..., ParetoFitnessScheme{2}(is_minimizing=true))" begin
            scheme = ParetoFitnessScheme{2}(is_minimizing=true)
            @test is_minimizing(scheme)
            @test_throws MethodError hat_compare((-1.0,), (1.0, 2.0), scheme)
            @test_throws MethodError hat_compare((2.0, -1.0, 3.0), (1.0, 2.0), scheme)
            @test hat_compare((-1.0, 0.0), (1.0, 0.0), scheme) == -1
            @test hat_compare((0.0, -1.0), (0.3, 1.0), scheme) == -1
            @test hat_compare((-1.0, 2.0), (-1.0, 2.0), scheme) == 0
            @test hat_compare((0.0, 0.0), (0.0, 0.0), scheme) == 0
            @test hat_compare((0.2, -0.4), (0.2, -0.4), scheme) == 0
            @test hat_compare((0.4, -0.4), (0.2, -0.2), scheme) == 0
            @test hat_compare((-0.65, 1.0), (1.0, -34.2), scheme) == 0
            @test hat_compare((11.3, 100.0), (11.3, 10.0), scheme) == 1
            @test hat_compare((1.0, 0.0), (0.0, -1.0), scheme) == 1
            @test hat_compare((0.0, 34.0), (-0.4, 20.0), scheme) == 1
        end

        @testset "hat_compare(..., ParetoFitnessScheme{2}(is_minimizing=false))" begin
            scheme = ParetoFitnessScheme{2}(is_minimizing=false)
            @test is_minimizing(scheme) == false
            @test hat_compare((-1.0, 0.0), (1.0, 0.0), scheme) == 1
            @test hat_compare((0.0, -1.0), (0.3, 1.0), scheme) == 1
            @test hat_compare((-1.0, 2.0), (-1.0, 2.0), scheme) == 0
            @test hat_compare((0.0, 0.0), (0.0, 0.0), scheme) == 0
            @test hat_compare((0.2, -0.4), (0.2, -0.4), scheme) == 0
            @test hat_compare((0.4, -0.4), (0.2, -0.2), scheme) == 0
            @test hat_compare((-0.65, 1.0), (1.0, -34.2), scheme) == 0
            @test hat_compare((11.3, 100.0), (11.3, 10.0), scheme) == -1
            @test hat_compare((1.0, 0.0), (0.0, -1.0), scheme) == -1
            @test hat_compare((0.0, 34.0), (-0.4, 20.0), scheme) == -1
        end

        @testset "is_better/is_worse/same_fitness(..., ParetoFitnessScheme{2}(is_minimizing=true))" begin
            scheme = ParetoFitnessScheme{2}()

            @test is_better((-1.0, 0.0), (1.0, 0.0), scheme)
            @test is_better((0.0, 0.0), (1.0, 0.0), scheme)
            @test_throws MethodError is_better((0.0,), (1.0,), scheme)

            @test is_better((1.0, 0.0), (-1.0, 0.0), scheme) == false
            @test is_better((1.0, 0.0), (0.0, 0.0), scheme) == false

            @test is_worse((-1.0, 0.0), (1.0, 0.0), scheme) == false
            @test is_worse((0.0, 0.0), (1.0, 0.0), scheme) == false
            @test_throws MethodError is_worse((0.0, 1.0), (1.0,), scheme)

            @test is_worse((1.0, 0.0), (-1.0, 0.0), scheme)
            @test is_worse((1.0, 0.0), (0.0, 0.0), scheme)

            @test same_fitness((-1.0, 0.0), (1.0, 0.0), scheme) == false
            @test same_fitness((0.0, 0.0), (1.0, 0.0), scheme) == false
            @test_throws MethodError same_fitness((0.0,), (1.0,), scheme)

            @test same_fitness((-1.0, 0.0), (-1.0, 0.0), scheme)
            @test same_fitness((0.0, 0.0), (0.0, 0.0), scheme)
            @test_throws MethodError same_fitness((0.0,), (0.0,), scheme)
        end

        @testset "is_better/is_worse/same_fitness(..., ParetoFitnessScheme{2}(is_minimizing=false))" begin
            scheme = ParetoFitnessScheme{2}(is_minimizing=false)

            @test is_better((-1.0, 0.0), (1.0, 0.0), scheme) == false
            @test is_better((0.0, 0.0), (1.0, 0.0), scheme) == false

            @test is_better((1.0, 0.0), (-1.0, 0.0), scheme)
            @test is_better((1.0, 0.0), (0.0, 0.0), scheme)

            @test is_worse((-1.0, 0.0), (1.0, 0.0), scheme)
            @test is_worse((0.0, 0.0), (1.0, 0.0), scheme)

            @test is_worse((1.0, 0.0), (-1.0, 0.0), scheme) == false
            @test is_worse((1.0, 0.0), (0.0, 0.0), scheme) == false

            @test same_fitness((-1.0, 0.0), (1.0, 0.0), scheme) == false
            @test same_fitness((0.0, 0.0), (1.0, 0.0), scheme) == false

            @test same_fitness((-1.0, 0.0), (-1.0, 0.0), scheme)
            @test same_fitness((0.0, 0.0), (0.0, 0.0), scheme)
        end
    end

    @testset "ϵ-box FitnessScheme" begin
        @testset "ϵ-index()" begin
            for (u, ϵ, is_minim, u_ix, delta) in [
                    (2.3, 1.0, true, 2, 0.3),
                    (2.3, 0.1, true, 23, 0.0),
                    (2.3, 1.0, false, 3, 0.7),
                    (1.0, 1.0, true, 1.0, 0.0),
                    (2.0, 2.0, false, 1.0, 0.0),
                    (-1.1, 1.0, true, -2, 0.9),
                    (-5.4, 1.0, false, -5, 0.4)
                ]
                res = BlackBoxOptim.ϵ_index(u, ϵ, Val{is_minim})
                @test res[1] == u_ix
                @test isapprox(res[2], delta; atol=1E-12)
            end
        end

        @testset "IndexedTupleFitness" begin
            fit = (0.55, 0.3, -0.21)
            ifit1 = IndexedTupleFitness(fit, sum(fit), 0.1, Val{true})
            @test ifit1.orig == fit
            @test ifit1.agg == sum(fit)
            @test ifit1.index == (5, 3, -3)
            @test isapprox(ifit1.dist, norm([0.05, 0.09])/0.1)

            ifit2 = IndexedTupleFitness(fit, sum(fit), 0.1, Val{false})
            @test ifit2.orig == fit
            @test ifit2.agg == sum(fit)
            @test ifit2.index == (6, 3, -2)
            @test isapprox(ifit2.dist, norm([0.05, 0.01])/0.1)

            ifit3 = convert(IndexedTupleFitness, fit, EpsBoxDominanceFitnessScheme{3}(0.1))
            @test ifit3 == ifit1
            ifit4 = convert(IndexedTupleFitness, fit, EpsBoxDominanceFitnessScheme{3}(0.1, is_minimizing=false))
            @test ifit4 == ifit2
        end

        @testset "hat_compare(..., EpsBoxDominanceFitnessScheme{2}(...))" begin
            minscheme = EpsBoxDominanceFitnessScheme{2}(0.1, is_minimizing=true)
            maxscheme = EpsBoxDominanceFitnessScheme{2}(0.1, is_minimizing=false)

            @test numobjectives(minscheme) == numobjectives(maxscheme) == 2
            @test fitness_type(minscheme) === fitness_type(maxscheme) === IndexedTupleFitness{2, Float64}
            @test fitness_eltype(minscheme) === fitness_eltype(maxscheme) === Float64

            @test isnafitness(nafitness(minscheme), minscheme)
            @test isnafitness(nafitness(maxscheme), minscheme)
            @test isnafitness(nafitness(minscheme), maxscheme)
            @test isnafitness(nafitness(maxscheme), maxscheme)

            @test hat_compare((-1.0, 0.0), (-1.0, 0.0), minscheme) == (0, true)
            @test hat_compare((-1.0, 0.0), (-1.0, 0.0), maxscheme) == (0, true)
            @test hat_compare((-1.0, 0.0), (1.0, 0.0), minscheme) == (-1, false)
            @test hat_compare((-1.0, 0.0), (1.0, 0.0), maxscheme) == (1, false)
            @test hat_compare((-1.0, 0.0), (-0.99, 0.0), minscheme) == (-1, true)
            @test hat_compare((-1.0, 0.0), (-0.99, 0.0), maxscheme) == (1, false)
            @test hat_compare((-1.0, 0.0), (-1.01, 0.0), minscheme) == (1, false)
            @test hat_compare((-1.0, 0.0), (-1.01, 0.0), maxscheme) == (-1, true)

            @test hat_compare((-0.97, 0.44), (-0.96, 0.43), minscheme) == (0, true)
            @test hat_compare((-0.97, 0.44), (-0.96, 0.43), maxscheme) == (0, true)
            @test hat_compare((-1.0, 0.5), (-1.0, 0.51), minscheme) == (-1, true)
            @test hat_compare((-1.0, 0.5), (-1.0, 0.51), maxscheme) == (1, false)

            @test hat_compare((0.9, 0.0), (1.0, 0.0), minscheme) == (-1, false)
            @test hat_compare((0.9, 0.0), (1.0, 0.0), maxscheme) == (1, false)
            @test hat_compare((0.95, 0.0), (0.98, 0.0), minscheme) == (-1, true)
            @test hat_compare((0.95, 0.0), (0.98, 0.0), maxscheme) == (1, true)

            @test hat_compare((-1.0, 0.05), (1.0, 0.0), minscheme) == (-1, false)
            @test hat_compare((-1.0, 0.05), (1.0, 0.0), maxscheme) == (0, false)
            @test hat_compare((0.9, 0.05), (1.0, 0.0), minscheme) == (-1, false)
            @test hat_compare((0.9, 0.05), (1.0, 0.0), maxscheme) == (0, false)
            @test hat_compare((0.95, 0.05), (0.98, 0.0), minscheme) == (-1, true)
            @test hat_compare((0.95, 0.05), (0.98, 0.0), maxscheme) == (-1, false)
            @test hat_compare((0.95, 0.05), (0.98, 0.01), maxscheme) == (-1, true)
            @test hat_compare((0.95, 0.05), (0.96, 0.0), minscheme) == (1, true)
            @test hat_compare((0.95, 0.05), (0.96, 0.0), maxscheme) == (-1, false)
            @test hat_compare((0.95, 0.05), (0.96, 0.04), minscheme) == (-1, true)
            @test hat_compare((0.95, 0.05), (0.96, 0.04), maxscheme) == (-1, true)

            @test hat_compare((6.9928266604943286,0.03386770153536918),
                                (7.007808609410211,0.032833634435236035), minscheme, 0) == (-1, false)
            @test hat_compare((6.9928266604943286,0.03386770153536918),
                                (7.007808609410211,0.032833634435236035), minscheme, 1) == (-1, false)
            @test hat_compare((6.9928266604943286,0.03386770153536918),
                                (7.007808609410211,0.032833634435236035), minscheme, -1) == (-1, false)
        end
    end
end
