/*
  PathArray(Test)  -  verify container abstraction for a sequence of path elements

  Copyright (C)         Lumiera.org
    2017,               Hermann Vosseler <Ichthyostega@web.de>

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License as
  published by the Free Software Foundation; either version 2 of
  the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

* *****************************************************/

/** @file path-array-test.cpp
 ** unit test \ref PathArray_test
 */


#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/path-array.hpp"
#include "lib/format-util.hpp"
#include "lib/util.hpp"

#include <string>


using std::string;
using lib::Symbol;
using util::isnil;
using util::join;



namespace lib  {
namespace test {
  
  using lumiera::error::LERR_(INDEX_BOUNDS);
  using lumiera::error::LERR_(INVALID);
  
  using ParrT = lib::PathArray<5>;
  
  
  /******************************************************************************//**
   * @test verify abstraction wrapper to handle some char pointers in the way of a
   *       standard sequence container with iteration and range checks.
   * 
   * @see path-array.hpp
   * @see UICoord_test
   * @see Navigator
   */
  class PathArray_test : public Test
    {
      
      virtual void
      run (Arg)
        {
          verify_basics();
          verify_iteration();
          verify_boundaries();
          verify_comparisons();
        }
      
      
      void
      verify_basics()
        {
          ParrT undef;
          CHECK (isnil (undef));
          
          ParrT parr{"Γ","Δ","Θ","Ξ","Σ","Ψ","Φ","Ω"};
          CHECK (not isnil (parr));
          CHECK (8 == parr.size());
          
          // indexed access
          CHECK ("Γ" == parr[0]);
          CHECK ("Δ" == parr[1]);
          CHECK ("Θ" == parr[2]);
          CHECK ("Ξ" == parr[3]);
          CHECK ("Σ" == parr[4]);
          CHECK ("Ψ" == parr[5]);
          CHECK ("Φ" == parr[6]);
          CHECK ("Ω" == parr[7]);
          
          VERIFY_ERROR (INDEX_BOUNDS, parr[8]);
          
          // string representation
          CHECK ("Γ/Δ/Θ/Ξ/Σ/Ψ/Φ/Ω" == string(parr));
          
          // new value can be assigned, but not altered in place
          parr = ParrT{nullptr,nullptr,"Θ",nullptr,nullptr,nullptr,"Φ"};
          CHECK (7 == parr.size());
          
          // representation is trimmed and filled
          CHECK ("Θ/*/*/*/Φ" == string(parr));
          CHECK (Symbol::EMPTY == parr[0]);
          CHECK (Symbol::EMPTY == parr[1]);
          CHECK ("Θ" ==  parr[2]);
          CHECK ("*" ==  parr[3]);
          CHECK ("*" ==  parr[4]);
          CHECK ("*" ==  parr[5]);
          CHECK ("Φ" ==  parr[6]);
          VERIFY_ERROR (INDEX_BOUNDS, parr[7]);
          VERIFY_ERROR (INDEX_BOUNDS, parr[8]);
        }
      
      
      void
      verify_iteration()
        {
          ParrT parr;
          CHECK (isnil (parr));
          // path is iterable
          CHECK ("" == join(parr));
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","Σ","Ψ","Φ","Ω"};
          CHECK (8 == parr.size());
          // path is iterable
          CHECK ("ΓΔΘΞΣΨΦΩ" == join(parr,""));
          
          // iteration matches index order
          uint i=0;
          for (ParrT::iterator ii = parr.begin(); ii; ++ii, ++i)
            CHECK (parr[i] == *ii);
          CHECK (8 == i);
          
          
          // path information is automatically trimmed and filled
          parr = ParrT{nullptr,nullptr,"Θ",nullptr,nullptr,nullptr,"Φ"};
          CHECK (7 == parr.size());
          CHECK ("Θ***Φ" == join(parr,""));
          
          parr = ParrT{nullptr,"Δ",nullptr,"Ξ",nullptr,nullptr,nullptr};
          CHECK (4 == parr.size());
          CHECK ("Δ*Ξ" == join(parr,""));
          
          parr = ParrT{nullptr,"Δ",nullptr,"Ξ",nullptr,nullptr,"Φ"};
          CHECK (7 == parr.size());
          CHECK ("Δ*Ξ**Φ" == join(parr,""));
          
          parr = ParrT{nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,"Ω",nullptr};
          CHECK (16 == parr.size());
          CHECK ("Ω" == join(parr,""));
          
          // index numbering starts at absolute root
          CHECK ("Ω" == *parr.begin());
          CHECK (Symbol::EMPTY == parr[0]);
          CHECK (Symbol::EMPTY == parr[1]);
          CHECK (Symbol::EMPTY == parr[2]);
          CHECK ("Ω"           == parr[15]);
          VERIFY_ERROR (INDEX_BOUNDS, parr[16]);
          
          // but iteration starts with actual content
          parr = ParrT{nullptr,nullptr,"Θ","Ξ","Σ","Ψ","Φ","Ω"};
          CHECK ("Θ" == *parr.begin());
          CHECK ( 2  == parr.indexOf (*parr.begin()));
          CHECK ("Θ" == parr[2]);
          
          parr = ParrT{nullptr,nullptr,nullptr,nullptr,"Σ","Ψ","Φ","Ω"};
          CHECK ("Σ" == *parr.begin());
          CHECK ( 4  == parr.indexOf (*parr.begin()));
          CHECK ( 5  == parr.indexOf (*++parr.begin()));
          CHECK ( 6  == parr.indexOf (*++++parr.begin()));
          
          Literal some{"muck"};
          VERIFY_ERROR (INVALID, parr.indexOf (some));
        }
      
      
      /** @test cover some tricky corner cases of
       *   the bound checks and normalisation routine.
       * @remark the container used for this test has an
       *   inline chunk size of 5, which means any further
       *   elements are in heap allocated extension storage.
       *   Normalisation seamlessly surpasses that boundary.
       */
      void
      verify_boundaries()
        {
          ParrT parr;
          CHECK ("" == join(parr));
          CHECK (0 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","Σ","Ψ","Φ","Ω"};
          CHECK ("ΓΔΘΞΣΨΦΩ" == join(parr,""));
          CHECK (8 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","Σ","Ψ","Φ",""};
          CHECK ("ΓΔΘΞΣΨΦ" == join(parr,""));
          CHECK (7 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","Σ","Ψ","Φ",nullptr};
          CHECK ("ΓΔΘΞΣΨΦ" == join(parr,""));
          CHECK (7 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","Σ","Ψ",nullptr,""};
          CHECK ("ΓΔΘΞΣΨ" == join(parr,""));
          CHECK (6 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","Σ","",nullptr,""};
          CHECK ("ΓΔΘΞΣ" == join(parr,""));
          CHECK (5 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","Σ",nullptr,"",nullptr};
          CHECK ("ΓΔΘΞΣ" == join(parr,""));
          CHECK (5 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","Σ",nullptr,nullptr,nullptr};
          CHECK ("ΓΔΘΞΣ" == join(parr,""));
          CHECK (5 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","Σ",nullptr,nullptr,nullptr,""};
          CHECK ("ΓΔΘΞΣ" == join(parr,""));
          CHECK (5 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","Σ","","",""};
          CHECK ("ΓΔΘΞΣ" == join(parr,""));
          CHECK (5 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","","","Φ",""};
          CHECK ("ΓΔΘΞ**Φ" == join(parr,""));
          CHECK (7 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ",nullptr,"","Φ",""};
          CHECK ("ΓΔΘΞ**Φ" == join(parr,""));
          CHECK (7 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","",nullptr,"Φ",""};
          CHECK ("ΓΔΘΞ**Φ" == join(parr,""));
          CHECK (7 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ",nullptr,nullptr,"Φ",""};
          CHECK ("ΓΔΘΞ**Φ" == join(parr,""));
          CHECK (7 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","","","Φ",nullptr};
          CHECK ("ΓΔΘΞ**Φ" == join(parr,""));
          CHECK (7 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","","","Φ",nullptr,""};
          CHECK ("ΓΔΘΞ**Φ" == join(parr,""));
          CHECK (7 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","","","Φ",nullptr,"",nullptr};
          CHECK ("ΓΔΘΞ**Φ" == join(parr,""));
          CHECK (7 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","","","Φ",nullptr,"",nullptr,""};
          CHECK ("ΓΔΘΞ**Φ" == join(parr,""));
          CHECK (7 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ","",nullptr,"",nullptr,"",nullptr,""};
          CHECK ("ΓΔΘΞ" == join(parr,""));
          CHECK (4 == parr.size());
          
          parr = ParrT{"Γ","Δ","Θ","Ξ",nullptr,"",nullptr,"",nullptr,"",nullptr};
          CHECK ("ΓΔΘΞ" == join(parr,""));
          CHECK (4 == parr.size());
          
          parr = ParrT{"","Δ","Θ","Ξ","","",""};
          CHECK ("ΔΘΞ" == join(parr,""));
          CHECK (4 == parr.size());
          CHECK ("" == parr[0]);
          
          parr = ParrT{nullptr,"Δ","Θ","Ξ","","",""};
          CHECK ("ΔΘΞ" == join(parr,""));
          CHECK (4 == parr.size());
          CHECK ("" == parr[0]);
          
          parr = ParrT{nullptr,"Δ",nullptr,"Ξ","","",""};
          CHECK ("Δ*Ξ" == join(parr,""));
          CHECK (4 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("Δ" == parr[1]);
          CHECK ("*" == parr[2]);
          CHECK ("Ξ" == parr[3]);
          
          parr = ParrT{nullptr,"",nullptr,"Ξ","","",""};
          CHECK ("Ξ" == join(parr,""));
          CHECK (4 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("Ξ" == parr[3]);
          
          parr = ParrT{nullptr,"",nullptr,"Ξ","","Ψ",""};
          CHECK ("Ξ*Ψ" == join(parr,""));
          CHECK (6 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("Ξ" == parr[3]);
          CHECK ("*" == parr[4]);
          CHECK ("Ψ" == parr[5]);
          
          parr = ParrT{nullptr,"",nullptr,"Ξ",nullptr,"Ψ",""};
          CHECK ("Ξ*Ψ" == join(parr,""));
          CHECK (6 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("Ξ" == parr[3]);
          CHECK ("*" == parr[4]);
          CHECK ("Ψ" == parr[5]);
          
          parr = ParrT{nullptr,"",nullptr,"Ξ",nullptr,nullptr,"Φ",""};
          CHECK ("Ξ**Φ" == join(parr,""));
          CHECK (7 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("Ξ" == parr[3]);
          CHECK ("*" == parr[4]);
          CHECK ("*" == parr[5]);
          CHECK ("Φ" == parr[6]);
          
          parr = ParrT{nullptr,"",nullptr,"Ξ","",nullptr,"Φ",""};
          CHECK ("Ξ**Φ" == join(parr,""));
          CHECK (7 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("Ξ" == parr[3]);
          CHECK ("*" == parr[4]);
          CHECK ("*" == parr[5]);
          CHECK ("Φ" == parr[6]);
          
          parr = ParrT{nullptr,"",nullptr,"Ξ",nullptr,"","Φ",""};
          CHECK ("Ξ**Φ" == join(parr,""));
          CHECK (7 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("Ξ" == parr[3]);
          CHECK ("*" == parr[4]);
          CHECK ("*" == parr[5]);
          CHECK ("Φ" == parr[6]);
          
          parr = ParrT{nullptr,"",nullptr,"Ξ","","","Φ",""};
          CHECK ("Ξ**Φ" == join(parr,""));
          CHECK (7 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("Ξ" == parr[3]);
          CHECK ("*" == parr[4]);
          CHECK ("*" == parr[5]);
          CHECK ("Φ" == parr[6]);
          
          parr = ParrT{"",nullptr,"",nullptr,"Σ","","Φ",""};
          CHECK ("Σ*Φ" == join(parr,""));
          CHECK (7 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("" == parr[3]);
          CHECK ("Σ" == parr[4]);
          CHECK ("*" == parr[5]);
          CHECK ("Φ" == parr[6]);
          
          parr = ParrT{"",nullptr,"",nullptr,"Σ",nullptr,"Φ",""};
          CHECK ("Σ*Φ" == join(parr,""));
          CHECK (7 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("" == parr[3]);
          CHECK ("Σ" == parr[4]);
          CHECK ("*" == parr[5]);
          CHECK ("Φ" == parr[6]);
          
          parr = ParrT{"",nullptr,"",nullptr,"","Ψ",nullptr,"Ω",""};
          CHECK ("Ψ*Ω" == join(parr,""));
          CHECK (8 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("" == parr[3]);
          CHECK ("" == parr[4]);
          CHECK ("Ψ" == parr[5]);
          CHECK ("*" == parr[6]);
          CHECK ("Ω" == parr[7]);
          
          parr = ParrT{nullptr,"",nullptr,"",nullptr,"Ψ",nullptr,"Ω",""};
          CHECK ("Ψ*Ω" == join(parr,""));
          CHECK (8 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("" == parr[3]);
          CHECK ("" == parr[4]);
          CHECK ("Ψ" == parr[5]);
          CHECK ("*" == parr[6]);
          CHECK ("Ω" == parr[7]);
          
          parr = ParrT{nullptr,"",nullptr,"",nullptr,"","Φ",nullptr,"ω",""};
          CHECK ("Φ*ω" == join(parr,""));
          CHECK (9 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("" == parr[3]);
          CHECK ("" == parr[4]);
          CHECK ("" == parr[5]);
          CHECK ("Φ" == parr[6]);
          CHECK ("*" == parr[7]);
          CHECK ("ω" == parr[8]);
          
          parr = ParrT{"",nullptr,"",nullptr,"",nullptr,"Φ",nullptr,"ω",""};
          CHECK ("Φ*ω" == join(parr,""));
          CHECK (9 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("" == parr[3]);
          CHECK ("" == parr[4]);
          CHECK ("" == parr[5]);
          CHECK ("Φ" == parr[6]);
          CHECK ("*" == parr[7]);
          CHECK ("ω" == parr[8]);
          
          parr = ParrT{"",nullptr,"",nullptr,"",nullptr,"Φ",nullptr,"ω","*"};
          CHECK ("Φ*ω" == join(parr,""));
          CHECK (9 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("" == parr[3]);
          CHECK ("" == parr[4]);
          CHECK ("" == parr[5]);
          CHECK ("Φ" == parr[6]);
          CHECK ("*" == parr[7]);
          CHECK ("ω" == parr[8]);
          
          parr = ParrT{"",nullptr,"",nullptr,"",nullptr,"Φ",nullptr,"*",""};
          CHECK ("Φ" == join(parr,""));
          CHECK (7 == parr.size());
          CHECK ("" == parr[0]);
          CHECK ("" == parr[1]);
          CHECK ("" == parr[2]);
          CHECK ("" == parr[3]);
          CHECK ("" == parr[4]);
          CHECK ("" == parr[5]);
          CHECK ("Φ" == parr[6]);
          
          parr = ParrT{"",nullptr,"",nullptr,"",nullptr,""};
          CHECK ("" == join(parr,""));
          CHECK (0 == parr.size());
        }
      
      
      /** @test verify equality comparison
       * Equality of PathArray is based on overall size, position and _normalised_ content.
       * @note especially how `"*"` might match "" or `nullptr` at corresponding positions.
       */
      void
      verify_comparisons()
        {
          CHECK (ParrT("Γ","Δ","Θ","Ξ","Σ","Ψ","Φ","Ω") == ParrT("Γ","Δ","Θ","Ξ","Σ","Ψ","Φ","Ω"));
          CHECK (ParrT("Γ","Δ","Θ","Ξ","Σ","Ψ","Φ","Ω") != ParrT("Γ","Δ","Θ","Σ","Ξ","Ψ","Φ","Ω"));
          CHECK (ParrT("Γ","Δ","Θ","Ξ","Σ","Ψ","Φ","Ω") != ParrT("Γ","Δ","Θ","Ξ","Ξ","Ψ","Φ","Ω"));
          CHECK (ParrT("Γ","Δ","Θ","Ξ","Σ","Ψ","Φ","Ω") != ParrT("" ,"Γ","Δ","Θ","Ξ","Σ","Ψ","Φ"));
          CHECK (ParrT("Γ","Δ","Θ","Ξ","Σ","Ψ","Φ","" ) != ParrT("" ,"Γ","Δ","Θ","Ξ","Σ","Ψ","Φ"));
          CHECK (ParrT("Γ","Δ","Θ","Ξ","Σ","Ψ","Φ","" ) != ParrT("" ,"Γ","Δ","Θ","Ξ","Σ","Ψ","" ));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","Σ","Ψ","Φ","" ) != ParrT("*","Δ","Θ","Ξ","Σ","Ψ","Φ","" ));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","Σ","Ψ","Φ","" ) != ParrT("" ,"Δ","Θ","Ξ","" ,"Σ","Ψ","Φ"));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","Σ","Ψ","Φ","" ) != ParrT("" ,"Δ","Θ","Ξ","Σ***Ψ","Φ","" ));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","Σ","" ,"Φ","" ) != ParrT("" ,"Δ","Θ","Ξ","*","Ψ","Φ","" ));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","Σ","" ,"Φ","" ) != ParrT("" ,"Δ","Θ","Ξ","Σ","* ","Φ",""));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","Σ","" ,"Φ","" ) != ParrT("" ,"Δ","Θ","Ξ","Σ","**","Φ",""));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","Σ","Ψ","Φ","" ) == ParrT("" ,"Δ","Θ","Ξ","Σ","Ψ","Φ","*"));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","Σ","", "Φ","" ) == ParrT("" ,"Δ","Θ","Ξ","Σ","*","Φ","" ));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","Σ","Ψ","Φ","" ) == ParrT(nullptr,"Δ","Θ","Ξ","Σ","Ψ","Φ"));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","Σ","Ψ","Φ","" ) == ParrT("" ,"Δ","Θ","Ξ","Σ","Ψ","Φ",nullptr));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","Σ","Ψ","Φ","" ) == ParrT("" ,"Δ","Θ","Ξ","Σ","Ψ","Φ"));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","" ,"Ψ","Φ","" ) == ParrT("" ,"Δ","Θ","Ξ",nullptr,"Ψ","Φ"));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","*","Ψ","Φ","" ) == ParrT("" ,"Δ","Θ","Ξ",nullptr,"Ψ","Φ"));
          CHECK (ParrT("" ,"Δ","Θ","Ξ","*","Ψ","Φ","" ) == ParrT("" ,"Δ","Θ","Ξ","", "Ψ","Φ"));
          
          ParrT src{"Γ","Δ","Θ","Ξ","Σ","Ψ","Φ","Ω"};
          ParrT copy{src};
          CHECK (not isnil(copy));
          CHECK (src == copy);
          
          ParrT target {std::move (copy)};
          CHECK (src == target);
          CHECK (copy != target);
          CHECK (copy != src);
        }
    };
  
  
  /** Register this test class... */
  LAUNCHER (PathArray_test, "unit stage");
  
  
}} // namespace lib::test
