From 9e4f385813bd384e957ac811bef3b16cec48efdb Mon Sep 17 00:00:00 2001
From: ralfulrich <ralf.ulrich@kit.edu>
Date: Tue, 6 Apr 2021 14:10:17 +0200
Subject: [PATCH] attempt to improve code coverage

---
 .../framework/process/ProcessSequence.inl     |   2 +-
 .../process/SwitchProcessSequence.inl         |   2 +-
 corsika/framework/process/NullModel.hpp       |  25 +++-
 tests/framework/testNullModel.cpp             |   7 +-
 tests/framework/testProcessSequence.cpp       | 117 +++++++++++++++++-
 5 files changed, 143 insertions(+), 10 deletions(-)

diff --git a/corsika/detail/framework/process/ProcessSequence.inl b/corsika/detail/framework/process/ProcessSequence.inl
index 67adef1ce..47b1805a6 100644
--- a/corsika/detail/framework/process/ProcessSequence.inl
+++ b/corsika/detail/framework/process/ProcessSequence.inl
@@ -63,7 +63,7 @@ namespace corsika {
                                       process2_type>) {
         // interface checking on TProcess2
         static_assert(
-            has_method_doBoundaryCrossing_v<TProcess2, ProcessReturn, TParticle&>,
+            has_method_doBoundaryCrossing_v<TProcess2, ProcessReturn, TParticle>,
             "TDerived has no method with correct signature \"ProcessReturn "
             "doBoundaryCrossing(TParticle&, VolumeNode const&, VolumeNode const&)\" "
             "required for "
diff --git a/corsika/detail/framework/process/SwitchProcessSequence.inl b/corsika/detail/framework/process/SwitchProcessSequence.inl
index cc04512ee..eaa33839a 100644
--- a/corsika/detail/framework/process/SwitchProcessSequence.inl
+++ b/corsika/detail/framework/process/SwitchProcessSequence.inl
@@ -67,7 +67,7 @@ namespace corsika {
                                           process2_type>) {
 
             static_assert(
-                has_method_doBoundaryCrossing_v<TProcess2, ProcessReturn, TParticle&>,
+                has_method_doBoundaryCrossing_v<TProcess2, ProcessReturn, TParticle>,
                 "TDerived has no method with correct signature \"ProcessReturn "
                 "doBoundaryCrossing(TParticle&, VolumeNode const&, VolumeNode const&)\" "
                 "required for "
diff --git a/corsika/framework/process/NullModel.hpp b/corsika/framework/process/NullModel.hpp
index c7fb1f344..a28d9668a 100644
--- a/corsika/framework/process/NullModel.hpp
+++ b/corsika/framework/process/NullModel.hpp
@@ -8,7 +8,8 @@
 
 #pragma once
 
-#include <corsika/framework/process/BaseProcess.hpp>
+//#include <corsika/framework/process/BaseProcess.hpp>
+#include <corsika/framework/process/ProcessTraits.hpp>
 
 namespace corsika {
 
@@ -16,14 +17,32 @@ namespace corsika {
      @ingroup Processes
      @{
 
-     Process that does nothing
+     Process that does nothing. It is not even derived from
+     BaseProcess
    */
 
-  class NullModel : public BaseProcess<NullModel> {
+  class NullModel { // : public BaseProcess<NullModel> {
 
   public:
     NullModel() = default;
     ~NullModel() = default;
+
+    //! Default number of processes is just one, obviously
+    static unsigned int constexpr getNumberOfProcesses() { return 0; }
+  };
+
+  /**
+     is_process traits specialization to indicate compatibility ProcessSequence
+  */
+  template <>
+  struct is_process<NullModel, void> : std::true_type {};
+
+  /**
+     count_processes traits specialization to increase process count by one.
+   */
+  template <int N>
+  struct count_processes<NullModel, N, void> {
+    static unsigned int constexpr count = N;
   };
 
   //! @}
diff --git a/tests/framework/testNullModel.cpp b/tests/framework/testNullModel.cpp
index 0589b1782..5b1f351e1 100644
--- a/tests/framework/testNullModel.cpp
+++ b/tests/framework/testNullModel.cpp
@@ -23,5 +23,10 @@ TEST_CASE("NullModel", "[processes]") {
   logging::set_level(logging::level::info);
   corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
 
-  SECTION("interface") { [[maybe_unused]] NullModel model; }
+  SECTION("interface") {
+    [[maybe_unused]] NullModel nm;
+
+    CHECK(is_process_v<decltype(nm)>);
+    CHECK(count_processes<decltype(nm)>::count == 0);
+  }
 }
diff --git a/tests/framework/testProcessSequence.cpp b/tests/framework/testProcessSequence.cpp
index 78af81d43..58be1f358 100644
--- a/tests/framework/testProcessSequence.cpp
+++ b/tests/framework/testProcessSequence.cpp
@@ -21,19 +21,35 @@
 
 #include <boost/type_index.hpp>
 
+
+/*
+  Unit test for testing all Process types and their arrangement in
+  containers ProcessSequence and SwitchProcessSequence
+ */
+
 using namespace corsika;
 using namespace std;
 
 static int const nData = 10;
 
+// DummyNode is only needed for BoundaryCrossingProcess
+struct DummyNode {
+  DummyNode(int v) : data_(v) {}
+  int data_ = 0;
+};
+
 // The stack is non-existent for this example
 struct DummyStack {};
+
 // our data object (particle) is a simple arrary of doubles
 struct DummyData {
   double data_[nData] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  typedef DummyNode node_type; // for BoundaryCrossingProcess
 };
+
 // there is no real trajectory/track
 struct DummyTrajectory {};
+
 // since there is no stack, there is also no view. This is a simplistic dummy object
 // sufficient here.
 struct DummyView {
@@ -43,6 +59,7 @@ struct DummyView {
   DummyData& parent() { return p_; }
 };
 
+
 int globalCount = 0; // simple counter
 
 int checkDecay = 0;    // use this as a bit field
@@ -315,6 +332,22 @@ private:
   int count_ = 0;
 };
 
+class Boundary1 : public BoundaryCrossingProcess<Boundary1> {
+public:
+  Boundary1(double const v=1.0) : v_(v) {}
+  
+  template <typename Particle>
+  ProcessReturn doBoundaryCrossing(Particle& p, typename Particle::node_type const& from,
+                                   typename Particle::node_type const& to) {
+
+    for (int i = 0; i < nData; ++i) { p.data_[i] += v_*(from.data_ - to.data_); }
+    return ProcessReturn::Ok;
+  }
+private:
+  double v_ = 0.0;
+};
+
+
 TEST_CASE("ProcessSequence General", "ProcessSequence") {
 
   logging::set_level(logging::level::info);
@@ -493,6 +526,31 @@ TEST_CASE("ProcessSequence General", "ProcessSequence") {
     CHECK(contains_stack_process_v<decltype(sequence2)> == false);
     CHECK(contains_stack_process_v<decltype(sequence3)> == true);
   }
+
+  SECTION("BoundaryCrossingProcess") {
+
+    globalCount = 0;
+    Boundary1 b1;
+
+    auto sequence1 = make_sequence(b1);
+
+    DummyData particle;
+    DummyNode node_from(5);
+    DummyNode node_to(4);
+    
+    int const nLoop = 20;
+    for (int i = 0; i < nLoop; ++i) { sequence1.doBoundaryCrossing(particle, node_from, node_to); }
+
+    for (int i = 0; i < nData; i++) {
+      CORSIKA_LOG_DEBUG("data_[{}]={}", i, particle.data_[i]);
+      CHECK(particle.data_[i] == Approx(nLoop).margin(1e-9));
+    }
+
+    CHECK(is_process_sequence_v<decltype(sequence1)> == true);
+    CHECK(contains_stack_process_v<decltype(sequence1)> == false);
+    CHECK(count_processes<decltype(sequence1)>::count == 1);
+  }
+  
 }
 
 TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
@@ -517,18 +575,21 @@ TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
   auto cp2 = ContinuousProcess2(0, 2_m);
   auto cp3 = ContinuousProcess3(0, 3_m);
 
-  auto sequence1 = make_sequence(Process1(0), cp2, Decay1(0));
-  auto sequence2 = make_sequence(cp3, Process2(0), Decay2(0));
+  auto sequence1 = make_sequence(Process1(0), cp2, Decay1(0), Boundary1(1.0));
+  auto sequence2 = make_sequence(cp3, Process2(0), Boundary1(-1.0), Decay2(0));
 
   auto sequence3 = make_sequence(cp1, Process3(0),
                                  SwitchProcessSequence(sequence1, sequence2, select1));
 
+  auto sequence4 = make_sequence(cp1, Boundary1(2.0), Process3(0),
+                                 SwitchProcessSequence(sequence1, Boundary1(-1.0), select1));
+
   SECTION("Check construction") {
 
     auto sequence_alt =
         make_sequence(cp1, Process3(0),
-                      make_select(make_sequence(Process1(0), cp2, Decay1(0)),
-                                  make_sequence(cp3, Process2(0), Decay2(0)), select1));
+                      make_select(make_sequence(Process1(0), cp2, Decay1(0), Boundary1(1.0)),
+                                  make_sequence(cp3, Process2(0), Boundary1(-1.0), Decay2(0)), select1));
 
     auto switch_seq = SwitchProcessSequence(sequence1, sequence2, select1);
     CHECK(is_process_sequence_v<decltype(switch_seq)>);
@@ -624,6 +685,33 @@ TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
     CHECK(checkDecay == 0);
     CHECK(checkCont == 0);
     CHECK(checkSec == 0);
+
+    // check the SwitchProcessSequence where no process is selected in
+    // selected branch (fallthrough)
+
+    checkDecay = 0;
+    checkInteract = 0;
+    checkSec = 0;
+    checkCont = 0;
+    particle.data_[0] = -100; // data negative
+    sequence4.selectInteraction(view, lambda_select);
+    sequence4.doSecondaries(view);
+    sequence4.selectDecay(view, time_select);
+    sequence4.doSecondaries(view);
+    CHECK(checkInteract == 0);
+    CHECK(checkDecay == 0);   
+    CHECK(checkCont == 0);
+    CHECK(checkSec == 0);
+    
+    // check that large "select" value will correctly ignore the call
+    lambda_select = 1e5 * square(1_cm) / 1_g;
+    time_select = 1e5 / second;
+    checkDecay = 0;
+    checkInteract = 0;
+    sequence3.selectInteraction(view, lambda_select);
+    sequence3.selectDecay(view, time_select);
+    CHECK(checkInteract == 0);
+    CHECK(checkDecay == 0);
   }
 
   SECTION("Check ContinuousProcesses in SwitchProcessSequence") {
@@ -714,6 +802,27 @@ TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
                      ContinuousProcessIndex(step4).getIndex(),
                      boost::typeindex::type_id<decltype(sequence3)>().pretty_name());
   }
+
+  SECTION("Check BoundaryCrossingProcess in SwitchProcessSequence") {
+
+    DummyData particle;
+    DummyNode node_from(1);
+    DummyNode node_to(2);
+
+    particle.data_[0] =
+        100; // data positive, selects particular branch on SwitchProcessSequence
+
+    sequence4.doBoundaryCrossing(particle, node_from, node_to);
+
+    CHECK(particle.data_[0] == 97); // 100 - 2*1 - 1*1
+    
+    particle.data_[0] =
+        -100; // data positive, selects particular branch on SwitchProcessSequence
+
+    sequence4.doBoundaryCrossing(particle, node_from, node_to);
+    CHECK(particle.data_[0] == -101); // -100 - 2*1 + 1*1
+  }
+
 }
 
 TEST_CASE("ProcessSequence Indexing", "ProcessSequence") {
-- 
GitLab