Commit 9862f383 authored by Brad King's avatar Brad King

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.
parent 606e6ff9
......@@ -25,6 +25,11 @@
bool cmAddTestCommand
::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
// the executable to run (a target or external program) Remaining arguments
// 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
// seen. This preserves behavior from before test generators.
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->SetOldStyle(true);
this->Makefile->AddTestGenerator(new cmTestGenerator(test));
}
test->SetCommand(command);
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 "
"system (like tclsh). The test will be run with the current working "
"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);
private:
bool HandleNameMode(std::vector<std::string> const& args);
};
......
......@@ -23,6 +23,7 @@
cmTest::cmTest()
{
this->Makefile = 0;
this->OldStyle = true;
}
cmTest::~cmTest()
......
......@@ -63,11 +63,17 @@ public:
void SetMakefile(cmMakefile *mf);
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:
cmPropertyMap Properties;
cmStdString Name;
std::vector<std::string> Command;
bool OldStyle;
// The cmMakefile instance that owns this target. This should
// always be set.
cmMakefile* Makefile;
......
......@@ -16,7 +16,10 @@
=========================================================================*/
#include "cmTestGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmTest.h"
//----------------------------------------------------------------------------
......@@ -26,7 +29,7 @@ cmTestGenerator
cmScriptGenerator("CTEST_CONFIGURATION_TYPE", configurations),
Test(test)
{
this->ActionsPerConfig = false;
this->ActionsPerConfig = !test->GetOldStyle();
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)
{
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;
......
......@@ -36,6 +36,10 @@ public:
protected:
virtual void GenerateScriptConfigs(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;
bool TestGenerated;
......
#
# Testing
#
cmake_minimum_required (VERSION 2.6)
cmake_minimum_required (VERSION 2.7)
PROJECT (Testing)
#
......@@ -52,3 +52,9 @@ ADD_TEST(testing.1 ${Testing_BINARY_DIR}/bin/testing)
# skip level test
#
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)
int main()
{
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment