ENH: Add NAME mode to ADD_TEST command
This creates command mode add_test(NAME ...). This signature is extensible with more keyword arguments later. The main purpose is to enable automatic replacement of target names with built target file locations. A side effect of this feature is support for tests that only run under specific configurations.
This commit is contained in:
parent
606e6ff9cd
commit
9862f383d0
|
@ -25,6 +25,11 @@
|
||||||
bool cmAddTestCommand
|
bool cmAddTestCommand
|
||||||
::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
|
::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
|
||||||
{
|
{
|
||||||
|
if(!args.empty() && args[0] == "NAME")
|
||||||
|
{
|
||||||
|
return this->HandleNameMode(args);
|
||||||
|
}
|
||||||
|
|
||||||
// First argument is the name of the test Second argument is the name of
|
// First argument is the name of the test Second argument is the name of
|
||||||
// the executable to run (a target or external program) Remaining arguments
|
// the executable to run (a target or external program) Remaining arguments
|
||||||
// are the arguments to pass to the executable
|
// are the arguments to pass to the executable
|
||||||
|
@ -45,12 +50,116 @@ bool cmAddTestCommand
|
||||||
// Create the test but add a generator only the first time it is
|
// Create the test but add a generator only the first time it is
|
||||||
// seen. This preserves behavior from before test generators.
|
// seen. This preserves behavior from before test generators.
|
||||||
cmTest* test = this->Makefile->GetTest(args[0].c_str());
|
cmTest* test = this->Makefile->GetTest(args[0].c_str());
|
||||||
if(!test)
|
if(test)
|
||||||
|
{
|
||||||
|
// If the test was already added by a new-style signature do not
|
||||||
|
// allow it to be duplicated.
|
||||||
|
if(!test->GetOldStyle())
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << " given test name \"" << args[0]
|
||||||
|
<< "\" which already exists in this directory.";
|
||||||
|
this->SetError(e.str().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
test = this->Makefile->CreateTest(args[0].c_str());
|
test = this->Makefile->CreateTest(args[0].c_str());
|
||||||
|
test->SetOldStyle(true);
|
||||||
this->Makefile->AddTestGenerator(new cmTestGenerator(test));
|
this->Makefile->AddTestGenerator(new cmTestGenerator(test));
|
||||||
}
|
}
|
||||||
test->SetCommand(command);
|
test->SetCommand(command);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool cmAddTestCommand::HandleNameMode(std::vector<std::string> const& args)
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::vector<std::string> configurations;
|
||||||
|
std::vector<std::string> command;
|
||||||
|
|
||||||
|
// Read the arguments.
|
||||||
|
enum Doing {
|
||||||
|
DoingName,
|
||||||
|
DoingCommand,
|
||||||
|
DoingConfigs,
|
||||||
|
DoingNone
|
||||||
|
};
|
||||||
|
Doing doing = DoingName;
|
||||||
|
for(unsigned int i=1; i < args.size(); ++i)
|
||||||
|
{
|
||||||
|
if(args[i] == "COMMAND")
|
||||||
|
{
|
||||||
|
if(!command.empty())
|
||||||
|
{
|
||||||
|
this->SetError(" may be given at most one COMMAND.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
doing = DoingCommand;
|
||||||
|
}
|
||||||
|
else if(args[i] == "CONFIGURATIONS")
|
||||||
|
{
|
||||||
|
if(!configurations.empty())
|
||||||
|
{
|
||||||
|
this->SetError(" may be given at most one set of CONFIGURATIONS.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
doing = DoingConfigs;
|
||||||
|
}
|
||||||
|
else if(doing == DoingName)
|
||||||
|
{
|
||||||
|
name = args[i];
|
||||||
|
doing = DoingNone;
|
||||||
|
}
|
||||||
|
else if(doing == DoingCommand)
|
||||||
|
{
|
||||||
|
command.push_back(args[i]);
|
||||||
|
}
|
||||||
|
else if(doing == DoingConfigs)
|
||||||
|
{
|
||||||
|
configurations.push_back(args[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << " given unknown argument:\n " << args[i] << "\n";
|
||||||
|
this->SetError(e.str().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Require a test name.
|
||||||
|
if(name.empty())
|
||||||
|
{
|
||||||
|
this->SetError(" must be given non-empty NAME.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Require a command.
|
||||||
|
if(command.empty())
|
||||||
|
{
|
||||||
|
this->SetError(" must be given non-empty COMMAND.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Require a unique test name within the directory.
|
||||||
|
if(this->Makefile->GetTest(name.c_str()))
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << " given test NAME \"" << name
|
||||||
|
<< "\" which already exists in this directory.";
|
||||||
|
this->SetError(e.str().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the test.
|
||||||
|
cmTest* test = this->Makefile->CreateTest(name.c_str());
|
||||||
|
test->SetOldStyle(false);
|
||||||
|
test->SetCommand(command);
|
||||||
|
this->Makefile->AddTestGenerator(new cmTestGenerator(test, configurations));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -70,11 +70,21 @@ public:
|
||||||
"built by this project or an arbitrary executable on the "
|
"built by this project or an arbitrary executable on the "
|
||||||
"system (like tclsh). The test will be run with the current working "
|
"system (like tclsh). The test will be run with the current working "
|
||||||
"directory set to the CMakeList.txt files corresponding directory "
|
"directory set to the CMakeList.txt files corresponding directory "
|
||||||
"in the binary tree.";
|
"in the binary tree."
|
||||||
|
"\n"
|
||||||
|
" add_test(NAME <name> [CONFIGURATIONS [Debug|Release|...]]\n"
|
||||||
|
" COMMAND <command> [arg1 [arg2 ...]])\n"
|
||||||
|
"If COMMAND specifies an executable target (created by "
|
||||||
|
"add_executable) it will automatically be replaced by the location "
|
||||||
|
"of the executable created at build time. "
|
||||||
|
"If a CONFIGURATIONS option is given then the test will be executed "
|
||||||
|
"only when testing under one of the named configurations."
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmTypeMacro(cmAddTestCommand, cmCommand);
|
cmTypeMacro(cmAddTestCommand, cmCommand);
|
||||||
|
private:
|
||||||
|
bool HandleNameMode(std::vector<std::string> const& args);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
cmTest::cmTest()
|
cmTest::cmTest()
|
||||||
{
|
{
|
||||||
this->Makefile = 0;
|
this->Makefile = 0;
|
||||||
|
this->OldStyle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmTest::~cmTest()
|
cmTest::~cmTest()
|
||||||
|
|
|
@ -63,11 +63,17 @@ public:
|
||||||
void SetMakefile(cmMakefile *mf);
|
void SetMakefile(cmMakefile *mf);
|
||||||
cmMakefile *GetMakefile() { return this->Makefile;};
|
cmMakefile *GetMakefile() { return this->Makefile;};
|
||||||
|
|
||||||
|
/** Get/Set whether this is an old-style test. */
|
||||||
|
bool GetOldStyle() const { return this->OldStyle; }
|
||||||
|
void SetOldStyle(bool b) { this->OldStyle = b; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cmPropertyMap Properties;
|
cmPropertyMap Properties;
|
||||||
cmStdString Name;
|
cmStdString Name;
|
||||||
std::vector<std::string> Command;
|
std::vector<std::string> Command;
|
||||||
|
|
||||||
|
bool OldStyle;
|
||||||
|
|
||||||
// The cmMakefile instance that owns this target. This should
|
// The cmMakefile instance that owns this target. This should
|
||||||
// always be set.
|
// always be set.
|
||||||
cmMakefile* Makefile;
|
cmMakefile* Makefile;
|
||||||
|
|
|
@ -16,7 +16,10 @@
|
||||||
=========================================================================*/
|
=========================================================================*/
|
||||||
#include "cmTestGenerator.h"
|
#include "cmTestGenerator.h"
|
||||||
|
|
||||||
|
#include "cmLocalGenerator.h"
|
||||||
|
#include "cmMakefile.h"
|
||||||
#include "cmSystemTools.h"
|
#include "cmSystemTools.h"
|
||||||
|
#include "cmTarget.h"
|
||||||
#include "cmTest.h"
|
#include "cmTest.h"
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
@ -26,7 +29,7 @@ cmTestGenerator
|
||||||
cmScriptGenerator("CTEST_CONFIGURATION_TYPE", configurations),
|
cmScriptGenerator("CTEST_CONFIGURATION_TYPE", configurations),
|
||||||
Test(test)
|
Test(test)
|
||||||
{
|
{
|
||||||
this->ActionsPerConfig = false;
|
this->ActionsPerConfig = !test->GetOldStyle();
|
||||||
this->TestGenerated = false;
|
this->TestGenerated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,8 +95,69 @@ void cmTestGenerator::GenerateScriptConfigs(std::ostream& os,
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void cmTestGenerator::GenerateScriptActions(std::ostream& fout,
|
void cmTestGenerator::GenerateScriptActions(std::ostream& os,
|
||||||
Indent const& indent)
|
Indent const& indent)
|
||||||
|
{
|
||||||
|
if(this->ActionsPerConfig)
|
||||||
|
{
|
||||||
|
// This is the per-config generation in a single-configuration
|
||||||
|
// build generator case. The superclass will call our per-config
|
||||||
|
// method.
|
||||||
|
this->cmScriptGenerator::GenerateScriptActions(os, indent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This is an old-style test, so there is only one config.
|
||||||
|
//assert(this->Test->GetOldStyle());
|
||||||
|
this->GenerateOldStyle(os, indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
|
||||||
|
const char* config,
|
||||||
|
Indent const& indent)
|
||||||
|
{
|
||||||
|
this->TestGenerated = true;
|
||||||
|
|
||||||
|
// Start the test command.
|
||||||
|
os << indent << "ADD_TEST(" << this->Test->GetName() << " ";
|
||||||
|
|
||||||
|
// Get the test command line to be executed.
|
||||||
|
std::vector<std::string> const& command = this->Test->GetCommand();
|
||||||
|
|
||||||
|
// Check whether the command executable is a target whose name is to
|
||||||
|
// be translated.
|
||||||
|
std::string exe = command[0];
|
||||||
|
cmMakefile* mf = this->Test->GetMakefile();
|
||||||
|
cmTarget* target = mf->FindTargetToUse(exe.c_str());
|
||||||
|
if(target && target->GetType() == cmTarget::EXECUTABLE)
|
||||||
|
{
|
||||||
|
// Use the target file on disk.
|
||||||
|
exe = target->GetFullPath(config);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use the command name given.
|
||||||
|
cmSystemTools::ConvertToUnixSlashes(exe);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the command line with full escapes.
|
||||||
|
cmLocalGenerator* lg = mf->GetLocalGenerator();
|
||||||
|
os << lg->EscapeForCMake(exe.c_str());
|
||||||
|
for(std::vector<std::string>::const_iterator ci = command.begin()+1;
|
||||||
|
ci != command.end(); ++ci)
|
||||||
|
{
|
||||||
|
os << " " << lg->EscapeForCMake(ci->c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish the test command.
|
||||||
|
os << ")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void cmTestGenerator::GenerateOldStyle(std::ostream& fout,
|
||||||
|
Indent const& indent)
|
||||||
{
|
{
|
||||||
this->TestGenerated = true;
|
this->TestGenerated = true;
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,10 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual void GenerateScriptConfigs(std::ostream& os, Indent const& indent);
|
virtual void GenerateScriptConfigs(std::ostream& os, Indent const& indent);
|
||||||
virtual void GenerateScriptActions(std::ostream& os, Indent const& indent);
|
virtual void GenerateScriptActions(std::ostream& os, Indent const& indent);
|
||||||
|
virtual void GenerateScriptForConfig(std::ostream& os,
|
||||||
|
const char* config,
|
||||||
|
Indent const& indent);
|
||||||
|
void GenerateOldStyle(std::ostream& os, Indent const& indent);
|
||||||
|
|
||||||
cmTest* Test;
|
cmTest* Test;
|
||||||
bool TestGenerated;
|
bool TestGenerated;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
# Testing
|
# Testing
|
||||||
#
|
#
|
||||||
cmake_minimum_required (VERSION 2.6)
|
cmake_minimum_required (VERSION 2.7)
|
||||||
PROJECT (Testing)
|
PROJECT (Testing)
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -52,3 +52,9 @@ ADD_TEST(testing.1 ${Testing_BINARY_DIR}/bin/testing)
|
||||||
# skip level test
|
# skip level test
|
||||||
#
|
#
|
||||||
ADD_SUBDIRECTORY(Sub/Sub2)
|
ADD_SUBDIRECTORY(Sub/Sub2)
|
||||||
|
|
||||||
|
# Per-config target name test.
|
||||||
|
ADD_EXECUTABLE(perconfig perconfig.c)
|
||||||
|
SET_PROPERTY(TARGET perconfig PROPERTY RELEASE_POSTFIX -opt)
|
||||||
|
SET_PROPERTY(TARGET perconfig PROPERTY DEBUG_POSTFIX -dbg)
|
||||||
|
ADD_TEST(NAME testing.perconfig COMMAND perconfig)
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue