From 6af775feaf764515146a737a1451523bff7389a0 Mon Sep 17 00:00:00 2001
From: Daniel Vedder <daniel.vedder@idiv.de>
Date: Fri, 6 Jan 2023 15:03:47 +0100
Subject: [PATCH] Wrote tests for `initpopulation()`

Still errors - looks like `@habitat` still needs fixing.
---
 src/nature/nature.jl      | 20 ++++++++++++++++++--
 src/nature/populations.jl | 16 +++++-----------
 test/nature_tests.jl      | 40 ++++++++++++++++++++++++++++++++++++++-
 3 files changed, 62 insertions(+), 14 deletions(-)

diff --git a/src/nature/nature.jl b/src/nature/nature.jl
index 186c476..f0d647a 100644
--- a/src/nature/nature.jl
+++ b/src/nature/nature.jl
@@ -89,7 +89,9 @@ There are two parameters that all species must define:
 - `initialise!` should specify a function that will be used to create
     the starting population for this species. This function must take
     two arguments, a species dict and an `AgentBasedModel` object.
-    The easiest way to create this function is by using `initpopultion()`.
+    The easiest way to create this function is by using `initpopulation()`.
+    (To save typing, `@initialise!()` can be called instead. It defines
+    this variable and calls `initpopulation()`, all in one go.)
 - `phase` should be a string specifying the name of the first phase
     that individuals of this species will be assigned to on birth.
 
@@ -111,6 +113,16 @@ macro species(name, body)
     end
 end
 
+"""
+    @initialise!(habitatdescriptor; kwargs...)
+
+This is a wrapper around `initpopulation()` that can be called within the body of a `@species`
+definition block. It saves the user having to define the required `initialise!` variable by hand.
+"""
+macro initialise!(habitatdescriptor, kwargs...)
+    :($(esc(:initialise!)) = initpopulation($habitatdescriptor, $(kwargs...)))
+end
+
 """
     @phase(name, body)
 
@@ -248,7 +260,11 @@ circumvented by directly creating an equivalent function.
 macro habitat(body)
     quote
         function($(esc(:pos)), $(esc(:model)))
-            ($(esc(body))) ? return true : return false
+            if $(esc(body))
+                return true
+            else
+                return false
+            end
         end
     end
 end
diff --git a/src/nature/populations.jl b/src/nature/populations.jl
index bd6577f..62ba4e1 100644
--- a/src/nature/populations.jl
+++ b/src/nature/populations.jl
@@ -68,18 +68,12 @@ A simplified version of `initpopulation()`. Creates a function that initialises
 `popsize` individuals, spread at random across the landscape. If `popsize` is less
 than 1, it is interpreted as a population density (i.e. 1 animal per `popsize` pixels).
 """
-function initrandompopulation(popsize::Union{Int64,Float64}, asexual::Bool=true)
-    function(species::Dict{String,Any}, model::AgentBasedModel)
-        if popsize < 1.0
-            x, y = size(model.landscape)
-            popsize = Int(round(x*y*popsize))
-        end
-        for i in 1:popsize
-            sex = asexual ? hermaphrodite : rand([male, female])
-            add_agent!(Animal, model, species, sex, 0)
-        end
-        @info "Initialised $(popsize) $(species["name"])s."
+function initrandompopulation(popsize::Union{Int64,Float64}; kwargs...)
+    if popsize < 1.0
+        x, y = size(model.landscape)
+        popsize = round(x*y*popsize)
     end
+    initpopulation(@habitat(true), popsize=Int(popsize), kwargs...)
 end
 
 #XXX initpopulation with dispersal from an original source?
diff --git a/test/nature_tests.jl b/test/nature_tests.jl
index fb15b97..0cabc1d 100644
--- a/test/nature_tests.jl
+++ b/test/nature_tests.jl
@@ -7,4 +7,42 @@
     #TODO
 end
 
-#TODO
+@testset "Habitat macros" begin
+    #TODO
+end
+
+@testset "Species initialisation" begin
+    model = smalltestlandscape()
+    specname = "test_animal"
+    species = Dict("name"=>specname)
+    # create a set of initialisation functions
+    initfun1 = Ps.initrandompopulation(10)
+    initfun2 = Ps.initrandompopulation(6*6*3, asexual=true)
+    initfun3 = Ps.initpopulation(@habitat(Ps.@landcover() == Ps.grass), pairs=true)
+    initfun4 = Ps.initpopulation(@habitat(Ps.@landcover() == Ps.water &&
+                                          Ps.@countanimals(specname, 0) <= 5),
+                                 popsize=10)
+    # apply and test the initialisation functions
+    @test_logs (:info, "Initialised 10 $(specname)s.") initfun1(species, model)
+    @test all(a -> a.sex in (Ps.male, Ps.female), allagents(model))
+    genocide!(model)
+    @test_logs (:info, "Initialised 108 $(specname)s.") initfun2(species, model)
+    @test countanimals((1,1), model, specname, 0) == countanimals((6,6), model, specname, 0) == 3
+    @test Vector(nearby_animals((1,1), model, 0))[1].sex == Ps.hermaphrodite
+    genocide!(model)
+    @test_logs (:info, "Initialised 16 $(specname)s.") initfun3(species, model)
+    @test countanimals((2,2), model, specname, 2) == countanimals((5,3), model, specname, 1) == 0
+    @test countanimals((5,5), model, specname, 0) == countanimals((6,6), model, specname, 0) == 2
+    a1, a2 = Vector(nearby_animals((6,6), model, 0))
+    @test a1.sex != a2.sex
+    genocide!(model)
+    @test_logs((:warn, "There are not enough suitable locations for $(specname) in the landscape."),
+               (:info, "Initialised 5 $(specname)s."),
+               initfun4(species, model))
+    @test countanimals((1,1), model, specname, 4) == 0
+    @test countanimals((6,4), model, specname, 0) == 5
+end
+
+@testset "Population functions" begin
+    #TODO
+end
-- 
GitLab