#!/bin/bash
# Test csvm2csv
#
#   Copyright (C) 2014-2023 Ryan Specialty, LLC.
#
#   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 3 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, see <http://www.gnu.org/licenses/>.
##

cd "$( dirname "$0" )"


# just to ensure that we run all the tests
declare -i testsum=0


# Run test case with input and expected values
run-test()
{
  local -r input="${1?Missing input}"
  local -r expected="${2?Missing expected}"

  ((testsum++))

  # SUT invocation
  declare -r given=$( ../csvm2csv < <( cat <<< "$input" ) )

  test $? -eq 0 || return 1

  # expected output
  diff <( cat <<< "$expected" ) <( cat <<< "$given" ) || {
    echo "test $testsum failure" >&2
    return 1
  }
}



test-comment()
{
  local -r input='# comment before header should be removed
header, line
# this is also a comment
1, 2
# which should be ignored
3, 4'

  local -r expected='header,line
1,2
3,4'

  run-test "$input" "$expected"
}


test-range()
{
  declare -r input='header, line
1--3, 2
3--5, 4--6'

  declare -r expected='header,line
1,2
2,2
3,2
3,4
3,5
3,6
4,4
4,5
4,6
5,4
5,5
5,6'

  run-test "$input" "$expected"
}


test-delim()
{
  declare -r input='header, line
1;4, 2
4;3, 6;9'

  declare -r expected='header,line
1,2
3,6
3,9
4,2
4,6
4,9'

  run-test "$input" "$expected"
}


test-var()
{
  declare -r input='header, line
:foo=1
:bar_baz-quux=2
$foo,1
$bar_baz-quux,$foo'

  declare -r expected='header,line
1,1
2,1'

  run-test "$input" "$expected"
}

# same as above but with whitespace
test-whitespace-including-tabs-ok()
{
  declare -r input='header, line
:foo=1
:bar_baz-quux=2
$foo, 1
$bar_baz-quux,	$foo'

  declare -r expected='header,line
1,1
2,1'

  run-test "$input" "$expected"
}


test-range-delim()
{
  declare -r input='header, line
1--3;5--6, 2'

  declare -r expected='header,line
1,2
2,2
3,2
5,2
6,2'

  run-test "$input" "$expected"
}


test-var-in-range-delim()
{
  declare -r input='header, line
:foo=1
:bar=3
$foo--$bar, $foo;$bar'

  declare -r expected='header,line
1,1
1,3
2,1
2,3
3,1
3,3'

  run-test "$input" "$expected"
}


test-var-with-range-delim()
{
  declare -r input='header, line
:foo=1--2;4
:bar=5
$foo;$bar, 1'

  declare -r expected='header,line
1,1
2,1
4,1
5,1'

  run-test "$input" "$expected"
}


test-var-with-var()
{
  declare -r input='header, line
:foo=2
:bar=4
:range=$foo--$bar
:baz=$range;$foo
$baz, 5'

  # note that the output is sorted
  declare -r expected='header,line
2,5
2,5
3,5
4,5'

  run-test "$input" "$expected"
}


# :foo=0 should be considered to be defined
test-var-zero-ref()
{
  declare -r input='header, line
:foo=0
$foo'

  declare -r expected='header,line
0'

  run-test "$input" "$expected"
}


test-directive-stripped()
{
  declare -r input='!DIRECTIVE
header, line'

  declare -r expected='header,line'

  run-test "$input" "$expected"
}


test-no-sort()
{
  declare -r input='!NOSORT
header, line
1,1
0,0'

  declare -r expected='header,line
1,1
0,0'

  run-test "$input" "$expected"
}


# all directives should be put on a single line
test-fail-multi-directive()
{
  declare -r input='!DIRECTIVE1
!DIRECTIVE2
header, line'

  ((testsum++))

  local result
  ! result=$( ../csvm2csv 2>&1 <<< "$input" ) || return 1

  [[ "$result" =~ !DIRECTIVE2 ]]
}


test-fail-unknown-var-ref()
{
  ((testsum++))

  local result
  ! result=$( ../csvm2csv 2>&1 <<< '$undefined' ) || return 1

  [[ "$result" =~ unknown.*\$undefined ]]
}


test-fail-non-numeric-range()
{
  ((testsum++))

  local result
  ! result=$( ../csvm2csv 2>&1 <<< 'A--Z' ) || return 1

  [[ "$result" =~ invalid\ range.*A--Z ]]
}


test-fail-invalid-var-dfn()
{
  ((testsum++))

  local result
  ! result=$( ../csvm2csv 2>&1 <<< ':BAD@#=var' ) || return 1

  [[ "$result" =~ invalid\ variable\ definition.*:BAD@#=var ]]
}


test-comment \
  && test-range \
  && test-delim \
  && test-var \
  && test-range-delim \
  && test-var-in-range-delim \
  && test-var-with-range-delim \
  && test-var-with-var \
  && test-var-zero-ref \
  && test-directive-stripped \
  && test-no-sort \
  && test-whitespace-including-tabs-ok \
  && test-fail-multi-directive \
  && test-fail-unknown-var-ref \
  && test-fail-non-numeric-range \
  && test-fail-invalid-var-dfn \
  || {
    echo 'csvm2csv test failed' >&2
    exit 1
  }

# safety check
test "$testsum" -eq 16 || {
  echo 'error: did not run all csvm2csv tests!' >&2
  exit 1
}
