#!/lib/initcpio/busybox ash

. ./init_functions

failed=0
tests=0

e_ok=0
e_parser_failure=2
e_assertion_failure=130

assert() {
  local expect_fail= key= expected_value= actual_value

  if [ "$1" = '--expect-fail' ]; then
    expect_fail=y
    shift
  fi

  key=$1 expected_value=$2
  eval actual_value=\$"$1"

  case $actual_value in
    $expected_value)
      if [ -n "$expect_fail" ]; then
        echo "EXPECTED FAIL: $key: expected='$expected_value', got='$actual_value'"
        return 1
      fi
      ;;
    *)
      if [ -z "$expect_fail" ]; then
        echo "FAIL: $key: expected='$expected_value', got='$actual_value'"
        return 1
      fi
      ;;
  esac

  return 0
}

test_parse() {
  local flag= cmdline= expect_fail= expect_parse_fail=

  tests=$(( tests + 1 ))

  for flag; do
    case $flag in
      --expect-fail)
        expect_fail='--expect-fail'
        shift
        ;;
      --expect-parse-fail)
        expect_parse_fail=y
        shift
        ;;
      *)
        break
        ;;
    esac
  done

  cmdline=$1; shift
  [ -n "$V" ] && echo "testing cmdline: $cmdline"

  echo "$cmdline" | {
    parse_cmdline

    result=0
    while [ "$#" -gt 0 ]; do
      key=$1 expected_value=$2
      shift 2

      assert $expect_fail "$key" "$expected_value" || result=$e_assertion_failure
    done

    exit "$result"
  } 2>/dev/null

  case $? in
    $e_parser_failure)
      # parser failure
      if [ -z "$expect_parse_fail" ]; then
        echo "FAIL: parse_cmdline failed"
        failed=$(( failed + 1 ))
      fi
      ;;
    $e_assertion_failure)
      # test assertion failure
      failed=$(( failed + 1 ))
      ;;
    $e_ok)
      if [ -n "$expect_parse_fail" ]; then
        echo "EXPECTED_FAIL: parse_cmdline succeeded"
        failed=$(( failed + 1 ))
      fi
      ;;
  esac
}

# bare words
test_parse 'foo' \
  'foo' 'y'
test_parse 'foo bar' \
  'foo' 'y' \
  'bar' 'y'

# overwriting
test_parse 'foo=bar bar=baz foo bar="no pe"' \
  'bar' 'no pe' \
  'foo' 'y'

# simple key=value assignment
test_parse 'foo=bar' \
  'foo' 'bar'
test_parse 'foo=bar bar=baz' \
  'foo' 'bar' \
  'bar' 'baz'
test_parse '_derpy=hooves' \
  '_derpy' 'hooves'
test_parse 'f5=abc f_5_=abc' \
  'f5' 'abc' \
  'f_5_' 'abc'
test_parse 'v="foo bar=baz"' \
  'v' 'foo bar=baz'

# double quoting
test_parse 'foo="bar"' \
  'foo' 'bar'
test_parse 'foo="bar baz"' \
  'foo' 'bar baz'

# single quoting
test_parse "foo='bar'" \
  'foo' 'bar'
test_parse "foo='bar baz'" \
  'foo' 'bar baz'

# dangling quotes
test_parse 'foo="bar' \
  'foo' '"bar'
test_parse 'foo=bar"' \
  'foo' 'bar"'

# nested quotes
test_parse "foo='\"bar baz\"' herp='\"de\"rp'" \
  'foo' '"bar baz"' \
  'herp' '"de"rp'

# escaped quotes
test_parse 'foo=bar"baz' \
  'foo' 'bar"baz'

# neighboring quoted regions
test_parse --expect-fail 'foo="bar""baz"' \
  'foo' 'barbaz'
test_parse --expect-fail "foo=\"bar\"'baz'" \
  'foo' "barbaz"
test_parse --expect-fail "foo='bar'\"baz\"" \
  'foo' "barbaz"

# comments
test_parse 'foo=bar # ignored content' \
  'foo' 'bar' \
  'ignored' '' \
  'content' ''
test_parse 'foo=bar #ignored content' \
  'foo' 'bar' \
  'ignored' '' \
  'content' ''
test_parse 'foo="bar #baz" parse=this' \
  'foo' 'bar #baz' \
  'parse' 'this'

# shell metachars
test_parse 'foo=*' \
  'foo' '\*'
test_parse 'Make*' \
  'Makefile' ''
test_parse '[Makefile]*' \
  'Makefile' '' \
  'init' '' \
  'functions' ''

# invalid names
test_parse 'in-valid=name'
test_parse '6foo=bar'
test_parse '"gar bage"' \
  'gar' '' \
  'bage' ''

# special handling
test_parse 'rw' \
  'ro' '' \
  'rw' '' \
  'rwopt' 'rw'
test_parse 'ro' \
  'ro' '' \
  'rw' '' \
  'rwopt' 'ro'
test_parse 'fstype=btrfs' \
  'rootfstype' 'btrfs'
test_parse 'fsck.mode=force' \
  'forcefsck' 'y' \
  'fastboot' ''
test_parse 'fsck.mode=skip' \
  'forcefsck' '' \
  'fastboot' 'y'
test_parse 'rd.debug' \
  'rd_debug' 'y'
test_parse 'rd.log' \
  'rd_logmask' '6'
test_parse 'rd.log=all' \
  'rd_logmask' '7'
test_parse 'rd.log=console' \
  'rd_logmask' '4'
test_parse 'rd.log=kmsg' \
  'rd_logmask' '2'
test_parse 'rd.log=file' \
  'rd_logmask' '1'

# a mix of stuff
test_parse 'foo=bar bareword bar="ba az"' \
  'foo' 'bar' \
  'bareword' 'y' \
  'bar' 'ba az'

if [ "$failed" -eq 0 ]; then
  echo "PASS: ${0##*/test_} ($tests tests)"
  exit 0
else
  echo "FAIL: $failed tests failed"
  exit 1
fi
