cmake_minimum_required(VERSION 3.17)

set(test_filenames)
set(test_descriptions)
set(test_results)

macro(assert)
  if(NOT (${ARGN}))
    set(succeeded OFF)
  endif()
endmacro()

macro(skip_invalid_test)
  message(NOTICE "skipped invalid test: \"${filename}\"")
  return()
endmacro()

macro(push_result)
  set(test_filenames    ${test_filenames};${filename} PARENT_SCOPE)
  set(test_descriptions ${test_descriptions};${description} PARENT_SCOPE)
  set(test_results      ${test_results};${succeeded} PARENT_SCOPE)
endmacro()

function(execute_test filename)
  execute_process(
    COMMAND "${CMAKE_COMMAND}" -P "test/${filename}"
    RESULT_VARIABLE exitcode
    OUTPUT_VARIABLE output
    ERROR_QUIET
  )

  string(REPLACE "\n" ";" lines "${output}") # split output lines

  set(description "no description")
  set(succeeded ON)

  # read commands
  foreach(line ${lines})
    if("${line}" MATCHES "^-- (.*)$")
      separate_arguments(argv UNIX_COMMAND "${CMAKE_MATCH_1}")
      set(i 0)
      foreach(arg ${argv})
        set(arg_${i} "${arg}")
        math(EXPR i "${i}+1") # i += 1
      endforeach()
      unset(i)
    else()
      continue()
    endif()

    if("${arg_0}" STREQUAL "expect")
      if("${arg_1}" STREQUAL "exit")
        # expect exit code (zero or nonzero or any number)
        if("${arg_2}" STREQUAL "zero")
          # expect exit code to be zero
          assert(${exitcode} EQUAL 0)
        elseif("${arg_2}" STREQUAL "nonzero")
          # expect exit code to be nonzero
          assert(NOT (${exitcode} EQUAL 0))
        else()
          skip_invalid_test()
        endif()
      else()
        skip_invalid_test()
      endif()
    elseif("${arg_0}" STREQUAL "description")
      if("${arg_1}" STREQUAL "")
        skip_invalid_test()
      endif()
      set(description "${arg_1}")
    endif()
  endforeach()

  push_result()

endfunction()

function(show_result)
  set(succeeded_count 0)
  set(failed_count 0)

  foreach(result IN ZIP_LISTS test_filenames test_descriptions test_results)
    set(filename "${result_0}")
    set(description "${result_1}")
    set(succeeded "${result_2}")

    if(${succeeded})
      # succeeded
      math(EXPR succeeded_count "${succeeded_count}+1")
      message(STATUS "succeeded: ${result_0}: ${result_1}")
    else()
      # failed
      math(EXPR failed_count "${failed_count}+1")
      message(STATUS "failed: ${result_0}: ${result_1}")
    endif()
  endforeach()

  message(STATUS "succeeded ${succeeded_count}, failed ${failed_count}")

  if(${failed_count})
    message(FATAL_ERROR "test failed")
  endif()
endfunction()

set(tests
  test-config-arg-value.cmake
  test-config.cmake
  test-empty-condition.cmake
  test-uncompleted-args.cmake
  test-user-defined-invalid.cmake
  test-user-defined.cmake
)

foreach(test ${tests})
  execute_test(${test})
endforeach()

show_result()
