/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
#include "cmCTestTestCommand.h"

#include <chrono>
#include <cstdlib>
#include <sstream>

#include <cmext/string_view>

#include "cmCTest.h"
#include "cmCTestGenericHandler.h"
#include "cmCTestTestHandler.h"
#include "cmDuration.h"
#include "cmMakefile.h"
#include "cmProperty.h"
#include "cmStringAlgorithms.h"

void cmCTestTestCommand::BindArguments()
{
  cmCTestHandlerCommand::BindArguments();
  Bind("START"_s, Start);
  Bind("END"_s, End);
  Bind("STRIDE"_s, Stride);
  Bind("EXCLUDE"_s, Exclude);
  Bind("INCLUDE"_s, Include);
  Bind("EXCLUDE_LABEL"_s, ExcludeLabel);
  Bind("INCLUDE_LABEL"_s, IncludeLabel);
  Bind("EXCLUDE_FIXTURE"_s, ExcludeFixture);
  Bind("EXCLUDE_FIXTURE_SETUP"_s, ExcludeFixtureSetup);
  Bind("EXCLUDE_FIXTURE_CLEANUP"_s, ExcludeFixtureCleanup);
  Bind("PARALLEL_LEVEL"_s, ParallelLevel);
  Bind("REPEAT"_s, Repeat);
  Bind("SCHEDULE_RANDOM"_s, ScheduleRandom);
  Bind("STOP_TIME"_s, StopTime);
  Bind("TEST_LOAD"_s, TestLoad);
  Bind("RESOURCE_SPEC_FILE"_s, ResourceSpecFile);
  Bind("STOP_ON_FAILURE"_s, StopOnFailure);
}

cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler()
{
  cmProp ctestTimeout = Makefile->GetDefinition("CTEST_TEST_TIMEOUT");

  cmDuration timeout;
  if (ctestTimeout) {
    timeout = cmDuration(atof(ctestTimeout->c_str()));
  } else {
    timeout = CTest->GetTimeOut();
    if (timeout <= cmDuration::zero()) {
      // By default use timeout of 10 minutes
      timeout = std::chrono::minutes(10);
    }
  }
  CTest->SetTimeOut(timeout);

  cmProp resourceSpecFile =
    Makefile->GetDefinition("CTEST_RESOURCE_SPEC_FILE");
  if (ResourceSpecFile.empty() && resourceSpecFile) {
    ResourceSpecFile = *resourceSpecFile;
  }

  cmCTestGenericHandler* handler = InitializeActualHandler();
  if (!Start.empty() || !End.empty() || !Stride.empty()) {
    handler->SetOption("TestsToRunInformation",
                       cmStrCat(Start, ',', End, ',', Stride).c_str());
  }
  if (!Exclude.empty()) {
    handler->SetOption("ExcludeRegularExpression", Exclude.c_str());
  }
  if (!Include.empty()) {
    handler->SetOption("IncludeRegularExpression", Include.c_str());
  }
  if (!ExcludeLabel.empty()) {
    handler->SetOption("ExcludeLabelRegularExpression", ExcludeLabel.c_str());
  }
  if (!IncludeLabel.empty()) {
    handler->SetOption("LabelRegularExpression", IncludeLabel.c_str());
  }
  if (!ExcludeFixture.empty()) {
    handler->SetOption("ExcludeFixtureRegularExpression",
                       ExcludeFixture.c_str());
  }
  if (!ExcludeFixtureSetup.empty()) {
    handler->SetOption("ExcludeFixtureSetupRegularExpression",
                       ExcludeFixtureSetup.c_str());
  }
  if (!ExcludeFixtureCleanup.empty()) {
    handler->SetOption("ExcludeFixtureCleanupRegularExpression",
                       ExcludeFixtureCleanup.c_str());
  }
  if (StopOnFailure) {
    handler->SetOption("StopOnFailure", "ON");
  }
  if (!ParallelLevel.empty()) {
    handler->SetOption("ParallelLevel", ParallelLevel.c_str());
  }
  if (!Repeat.empty()) {
    handler->SetOption("Repeat", Repeat.c_str());
  }
  if (!ScheduleRandom.empty()) {
    handler->SetOption("ScheduleRandom", ScheduleRandom.c_str());
  }
  if (!ResourceSpecFile.empty()) {
    handler->SetOption("ResourceSpecFile", ResourceSpecFile.c_str());
  }
  if (!StopTime.empty()) {
    CTest->SetStopTime(StopTime);
  }

  // Test load is determined by: TEST_LOAD argument,
  // or CTEST_TEST_LOAD script variable, or ctest --test-load
  // command line argument... in that order.
  unsigned long testLoad;
  cmProp ctestTestLoad = Makefile->GetDefinition("CTEST_TEST_LOAD");
  if (!TestLoad.empty()) {
    if (!cmStrToULong(TestLoad, &testLoad)) {
      testLoad = 0;
      cmCTestLog(CTest, WARNING,
                 "Invalid value for 'TEST_LOAD' : " << TestLoad << std::endl);
    }
  } else if (cmNonempty(ctestTestLoad)) {
    if (!cmStrToULong(*ctestTestLoad, &testLoad)) {
      testLoad = 0;
      cmCTestLog(CTest, WARNING,
                 "Invalid value for 'CTEST_TEST_LOAD' : " << *ctestTestLoad
                                                          << std::endl);
    }
  } else {
    testLoad = CTest->GetTestLoad();
  }
  handler->SetTestLoad(testLoad);

  if (cmProp labelsForSubprojects =
        Makefile->GetDefinition("CTEST_LABELS_FOR_SUBPROJECTS")) {
    CTest->SetCTestConfiguration("LabelsForSubprojects", *labelsForSubprojects,
                                 Quiet);
  }

  handler->SetQuiet(Quiet);
  return handler;
}

cmCTestGenericHandler* cmCTestTestCommand::InitializeActualHandler()
{
  cmCTestTestHandler* handler = CTest->GetTestHandler();
  handler->Initialize();
  return handler;
}
