mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-25 16:09:42 -06:00
CTest: add safe conversion from cmDuration to integer types
A problem area by recent refactoring of time to std::chrono has been the unsafe conversion from duration<double> to std::chrono::seconds, which is of an unspecified integer type. This commit adds a template function that for a given type provides a safe conversion, effectively clamping a duration<double> into what fits safely in that type. A specialisation for int and unsigned int are provided. It changes the protential problem areas to use this safe function.
This commit is contained in:
committed by
Brad King
parent
695951bc46
commit
ff62b00522
@@ -602,6 +602,9 @@ set(SRCS
|
|||||||
cm_codecvt.hxx
|
cm_codecvt.hxx
|
||||||
cm_codecvt.cxx
|
cm_codecvt.cxx
|
||||||
cm_thread.hxx
|
cm_thread.hxx
|
||||||
|
|
||||||
|
cmDuration.h
|
||||||
|
cmDuration.cxx
|
||||||
)
|
)
|
||||||
|
|
||||||
SET_PROPERTY(SOURCE cmProcessOutput.cxx APPEND PROPERTY COMPILE_DEFINITIONS
|
SET_PROPERTY(SOURCE cmProcessOutput.cxx APPEND PROPERTY COMPILE_DEFINITIONS
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "cmCTestMemCheckHandler.h"
|
#include "cmCTestMemCheckHandler.h"
|
||||||
|
|
||||||
#include "cmCTest.h"
|
#include "cmCTest.h"
|
||||||
|
#include "cmDuration.h"
|
||||||
#include "cmSystemTools.h"
|
#include "cmSystemTools.h"
|
||||||
#include "cmXMLParser.h"
|
#include "cmXMLParser.h"
|
||||||
#include "cmXMLWriter.h"
|
#include "cmXMLWriter.h"
|
||||||
@@ -920,12 +921,11 @@ bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput(
|
|||||||
break; // stop the copy of output if we are full
|
break; // stop the copy of output if we are full
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmCTestOptionalLog(this->CTest, DEBUG, "End test (elapsed: "
|
cmCTestOptionalLog(
|
||||||
<< std::chrono::duration_cast<std::chrono::seconds>(
|
this->CTest, DEBUG, "End test (elapsed: "
|
||||||
std::chrono::steady_clock::now() - sttime)
|
<< cmDurationTo<unsigned int>(std::chrono::steady_clock::now() - sttime)
|
||||||
.count()
|
<< "s)" << std::endl,
|
||||||
<< "s)" << std::endl,
|
this->Quiet);
|
||||||
this->Quiet);
|
|
||||||
log = ostr.str();
|
log = ostr.str();
|
||||||
this->DefectCount += defects;
|
this->DefectCount += defects;
|
||||||
return defects == 0;
|
return defects == 0;
|
||||||
@@ -966,12 +966,11 @@ bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput(
|
|||||||
results[err]++;
|
results[err]++;
|
||||||
defects++;
|
defects++;
|
||||||
}
|
}
|
||||||
cmCTestOptionalLog(this->CTest, DEBUG, "End test (elapsed: "
|
cmCTestOptionalLog(
|
||||||
<< std::chrono::duration_cast<std::chrono::seconds>(
|
this->CTest, DEBUG, "End test (elapsed: "
|
||||||
std::chrono::steady_clock::now() - sttime)
|
<< cmDurationTo<unsigned int>(std::chrono::steady_clock::now() - sttime)
|
||||||
.count()
|
<< "s)" << std::endl,
|
||||||
<< "s)" << std::endl,
|
this->Quiet);
|
||||||
this->Quiet);
|
|
||||||
if (defects) {
|
if (defects) {
|
||||||
// only put the output of Bounds Checker if there were
|
// only put the output of Bounds Checker if there were
|
||||||
// errors or leaks detected
|
// errors or leaks detected
|
||||||
|
|||||||
@@ -621,17 +621,11 @@ bool cmCTestRunTest::ForkProcess(cmDuration testTimeOut, bool explicitTimeout,
|
|||||||
if (testTimeOut == cmDuration::zero() && explicitTimeout) {
|
if (testTimeOut == cmDuration::zero() && explicitTimeout) {
|
||||||
timeout = cmDuration::zero();
|
timeout = cmDuration::zero();
|
||||||
}
|
}
|
||||||
cmCTestOptionalLog(
|
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, this->Index
|
||||||
this->CTest, HANDLER_VERBOSE_OUTPUT, this->Index
|
<< ": "
|
||||||
<< ": "
|
<< "Test timeout computed to be: "
|
||||||
<< "Test timeout computed to be: "
|
<< cmDurationTo<unsigned int>(timeout) << "\n",
|
||||||
<< (timeout == cmCTest::MaxDuration()
|
this->TestHandler->GetQuiet());
|
||||||
? std::string("infinite")
|
|
||||||
: std::to_string(
|
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(timeout)
|
|
||||||
.count()))
|
|
||||||
<< "\n",
|
|
||||||
this->TestHandler->GetQuiet());
|
|
||||||
|
|
||||||
this->TestProcess->SetTimeout(timeout);
|
this->TestProcess->SetTimeout(timeout);
|
||||||
|
|
||||||
|
|||||||
@@ -159,9 +159,9 @@ void cmCTestScriptHandler::UpdateElapsedTime()
|
|||||||
{
|
{
|
||||||
if (this->Makefile) {
|
if (this->Makefile) {
|
||||||
// set the current elapsed time
|
// set the current elapsed time
|
||||||
auto itime = std::chrono::duration_cast<std::chrono::seconds>(
|
auto itime = cmDurationTo<unsigned int>(std::chrono::steady_clock::now() -
|
||||||
std::chrono::steady_clock::now() - this->ScriptStartTime);
|
this->ScriptStartTime);
|
||||||
auto timeString = std::to_string(itime.count());
|
auto timeString = std::to_string(itime);
|
||||||
this->Makefile->AddDefinition("CTEST_ELAPSED_TIME", timeString.c_str());
|
this->Makefile->AddDefinition("CTEST_ELAPSED_TIME", timeString.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -569,10 +569,9 @@ int cmCTestScriptHandler::RunCurrentScript()
|
|||||||
auto interval = std::chrono::steady_clock::now() - startOfInterval;
|
auto interval = std::chrono::steady_clock::now() - startOfInterval;
|
||||||
auto minimumInterval = cmDuration(this->MinimumInterval);
|
auto minimumInterval = cmDuration(this->MinimumInterval);
|
||||||
if (interval < minimumInterval) {
|
if (interval < minimumInterval) {
|
||||||
auto sleepTime = std::chrono::duration_cast<std::chrono::seconds>(
|
auto sleepTime =
|
||||||
minimumInterval - interval)
|
cmDurationTo<unsigned int>(minimumInterval - interval);
|
||||||
.count();
|
this->SleepInSeconds(sleepTime);
|
||||||
this->SleepInSeconds(static_cast<unsigned int>(sleepTime));
|
|
||||||
}
|
}
|
||||||
if (this->EmptyBinDirOnce) {
|
if (this->EmptyBinDirOnce) {
|
||||||
this->EmptyBinDir = false;
|
this->EmptyBinDir = false;
|
||||||
|
|||||||
@@ -1092,14 +1092,11 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
|
|||||||
if (timeout <= cmDuration::zero()) {
|
if (timeout <= cmDuration::zero()) {
|
||||||
timeout = std::chrono::seconds(1);
|
timeout = std::chrono::seconds(1);
|
||||||
}
|
}
|
||||||
cmCTestLog(
|
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Test timeout computed to be: "
|
||||||
this, HANDLER_VERBOSE_OUTPUT, "Test timeout computed to be: "
|
<< (timeout == cmCTest::MaxDuration()
|
||||||
<< (timeout == cmCTest::MaxDuration()
|
? std::string("infinite")
|
||||||
? std::string("infinite")
|
: std::to_string(cmDurationTo<unsigned int>(timeout)))
|
||||||
: std::to_string(
|
<< "\n");
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(timeout)
|
|
||||||
.count()))
|
|
||||||
<< "\n");
|
|
||||||
if (cmSystemTools::SameFile(argv[0], cmSystemTools::GetCTestCommand()) &&
|
if (cmSystemTools::SameFile(argv[0], cmSystemTools::GetCTestCommand()) &&
|
||||||
!this->ForceNewCTestProcess) {
|
!this->ForceNewCTestProcess) {
|
||||||
cmCTest inst;
|
cmCTest inst;
|
||||||
@@ -1121,8 +1118,7 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
|
|||||||
timeout > cmDuration::zero()) {
|
timeout > cmDuration::zero()) {
|
||||||
args.push_back("--test-timeout");
|
args.push_back("--test-timeout");
|
||||||
std::ostringstream msg;
|
std::ostringstream msg;
|
||||||
msg << std::chrono::duration_cast<std::chrono::seconds>(timeout)
|
msg << cmDurationTo<unsigned int>(timeout);
|
||||||
.count();
|
|
||||||
args.push_back(msg.str());
|
args.push_back(msg.str());
|
||||||
}
|
}
|
||||||
args.push_back(i);
|
args.push_back(i);
|
||||||
|
|||||||
27
Source/cmDuration.cxx
Normal file
27
Source/cmDuration.cxx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||||
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||||
|
#define CMDURATION_CPP
|
||||||
|
#include "cmDuration.h"
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T cmDurationTo(const cmDuration& duration)
|
||||||
|
{
|
||||||
|
/* This works because the comparison operators for duration rely on
|
||||||
|
* std::common_type.
|
||||||
|
* So for example duration<int>::max() gets promoted to a duration<double>,
|
||||||
|
* which can then be safely compared.
|
||||||
|
*/
|
||||||
|
if (duration >= std::chrono::duration<T>::max()) {
|
||||||
|
return std::chrono::duration<T>::max().count();
|
||||||
|
}
|
||||||
|
if (duration <= std::chrono::duration<T>::min()) {
|
||||||
|
return std::chrono::duration<T>::min().count();
|
||||||
|
}
|
||||||
|
// Ensure number of seconds by defining ratio<1>
|
||||||
|
return std::chrono::duration_cast<std::chrono::duration<T, std::ratio<1>>>(
|
||||||
|
duration)
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
template int cmDurationTo<int>(const cmDuration&);
|
||||||
|
template unsigned int cmDurationTo<unsigned int>(const cmDuration&);
|
||||||
@@ -6,3 +6,19 @@
|
|||||||
#include <ratio>
|
#include <ratio>
|
||||||
|
|
||||||
typedef std::chrono::duration<double, std::ratio<1>> cmDuration;
|
typedef std::chrono::duration<double, std::ratio<1>> cmDuration;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function will return number of seconds in the requested type T.
|
||||||
|
*
|
||||||
|
* A duration_cast from duration<double> to duration<T> will not yield what
|
||||||
|
* one might expect if the double representation does not fit into type T.
|
||||||
|
* This function aims to safely convert, by clamping the double value between
|
||||||
|
* the permissible valid values for T.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
T cmDurationTo(const cmDuration& duration);
|
||||||
|
|
||||||
|
#ifndef CMDURATION_CPP
|
||||||
|
extern template int cmDurationTo<int>(const cmDuration&);
|
||||||
|
extern template unsigned int cmDurationTo<unsigned int>(const cmDuration&);
|
||||||
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user