/*============================================================================ CMake - Cross Platform Makefile Generator Copyright 2000-2009 Kitware, Inc., Insight Software Consortium Distributed under the OSI-approved BSD License (the "License"); see accompanying file Copyright.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more information. ============================================================================*/ #ifndef cmCTest_h #define cmCTest_h #include "cmStandardIncludes.h" #include "cmListFileCache.h" #include class cmake; class cmMakefile; class cmCTestGenericHandler; class cmGeneratedFileStream; class cmCTestCommand; class cmCTestScriptHandler; class cmCTestStartCommand; #define cmCTestLog(ctSelf, logType, msg) \ do { \ cmOStringStream cmCTestLog_msg; \ cmCTestLog_msg << msg; \ (ctSelf)->Log(cmCTest::logType, __FILE__, __LINE__,\ cmCTestLog_msg.str().c_str());\ } while ( 0 ) #ifdef cerr # undef cerr #endif #define cerr no_cerr_use_cmCTestLog #ifdef cout # undef cout #endif #define cout no_cout_use_cmCTestLog class cmCTest { friend class cmCTestRunTest; friend class cmCTestMultiProcessHandler; public: /** Enumerate parts of the testing and submission process. */ enum Part { PartStart, PartUpdate, PartConfigure, PartBuild, PartTest, PartCoverage, PartMemCheck, PartSubmit, PartNotes, PartExtraFiles, PartCount // Update names in constructor when adding a part }; /** Representation of one part. */ struct PartInfo { PartInfo(): Enabled(false) {} void SetName(const char* name) { this->Name = name; } const char* GetName() const { return this->Name.c_str(); } void Enable() { this->Enabled = true; } operator bool() const { return this->Enabled; } std::vector SubmitFiles; private: bool Enabled; std::string Name; }; #ifdef CMAKE_BUILD_WITH_CMAKE enum HTTPMethod { HTTP_GET, HTTP_POST }; /** * Perform an HTTP request. */ static int HTTPRequest(std::string url, HTTPMethod method, std::string& response, std::string fields = "", int timeout = 10); #endif /** Get a testing part id from its string name. Returns PartCount if the string does not name a valid part. */ Part GetPartFromName(const char* name); typedef std::vector VectorOfStrings; typedef std::set SetOfStrings; ///! Process Command line arguments int Run(std::vector &, std::string* output = 0); /** * Initialize and finalize testing */ bool InitializeFromCommand(cmCTestStartCommand* command); void Finalize(); /** * Process the tests. This is the main routine. The execution of the * tests should look like this: * * ctest foo; * foo.Initialize(); * // Set some things on foo * foo.ProcessTests(); * foo.Finalize(); */ int ProcessTests(); /* * A utility function that returns the nightly time */ struct tm* GetNightlyTime(std::string str, bool tomorrowtag); /* * Is the tomorrow tag set? */ bool GetTomorrowTag() { return this->TomorrowTag; }; /** * Try to run tests of the project */ int TestDirectory(bool memcheck); ///! what is the configuraiton type, e.g. Debug, Release etc. std::string const& GetConfigType(); double GetTimeOut() { return this->TimeOut; } void SetTimeOut(double t) { this->TimeOut = t; } double GetGlobalTimeout() { return this->GlobalTimeout; } // how many test to run at the same time int GetParallelLevel() { return this->ParallelLevel; } void SetParallelLevel(int); /** * Check if CTest file exists */ bool CTestFileExists(const std::string& filename); bool AddIfExists(Part part, const char* file); /** * Set the cmake test */ bool SetTest(const char*, bool report = true); /** * Set the cmake test mode (experimental, nightly, continuous). */ void SetTestModel(int mode); int GetTestModel() { return this->TestModel; }; std::string GetTestModelString(); static int GetTestModelFromString(const char* str); static std::string CleanString(const std::string& str); std::string GetCTestConfiguration(const char *name); void SetCTestConfiguration(const char *name, const char* value); void EmptyCTestConfiguration(); /** * constructor and destructor */ cmCTest(); ~cmCTest(); //! Set the notes files to be created. void SetNotesFiles(const char* notes); void PopulateCustomVector(cmMakefile* mf, const char* definition, VectorOfStrings& vec); void PopulateCustomInteger(cmMakefile* mf, const char* def, int& val); ///! Get the current time as string std::string CurrentTime(); /** * Return the time remaianing that the script is allowed to run in * seconds if the user has set the variable CTEST_TIME_LIMIT. If that has * not been set it returns 1e7 seconds */ double GetRemainingTimeAllowed(); ///! Open file in the output directory and set the stream bool OpenOutputFile(const std::string& path, const std::string& name, cmGeneratedFileStream& stream, bool compress = false); ///! Should we only show what we would do? bool GetShowOnly(); bool ShouldUseHTTP10() { return this->UseHTTP10; } bool ShouldCompressTestOutput(); std::string GetCDashVersion(); //Used for parallel ctest job scheduling std::string GetScheduleType() { return this->ScheduleType; } void SetScheduleType(std::string type) { this->ScheduleType = type; } ///! The max output width int GetMaxTestNameWidth() const; void SetMaxTestNameWidth(int w) { this->MaxTestNameWidth = w;} /** * Run a single executable command and put the stdout and stderr * in output. * * If verbose is false, no user-viewable output from the program * being run will be generated. * * If timeout is specified, the command will be terminated after * timeout expires. Timeout is specified in seconds. * * Argument retVal should be a pointer to the location where the * exit code will be stored. If the retVal is not specified and * the program exits with a code other than 0, then the this * function will return false. * * If the command has spaces in the path the caller MUST call * cmSystemTools::ConvertToRunCommandPath on the command before passing * it into this function or it will not work. The command must be correctly * escaped for this to with spaces. */ bool RunCommand(const char* command, std::string* stdOut, std::string* stdErr, int* retVal = 0, const char* dir = 0, double timeout = 0.0); //! Start CTest XML output file void StartXML(std::ostream& ostr, bool append); //! End CTest XML output file void EndXML(std::ostream& ostr); //! Run command specialized for make and configure. Returns process status // and retVal is return value or exception. int RunMakeCommand(const char* command, std::string* output, int* retVal, const char* dir, int timeout, std::ofstream& ofs); /* * return the current tag */ std::string GetCurrentTag(); //! Get the path to the build tree std::string GetBinaryDir(); //! Get the short path to the file. This means if the file is in binary or //source directory, it will become /.../relative/path/to/file std::string GetShortPathToFile(const char* fname); //! Get the path to CTest const char* GetCTestExecutable() { return this->CTestSelf.c_str(); } const char* GetCMakeExecutable() { return this->CMakeSelf.c_str(); } enum { EXPERIMENTAL, NIGHTLY, CONTINUOUS }; // provide some more detailed info on the return code for ctest enum { UPDATE_ERRORS = 0x01, CONFIGURE_ERRORS = 0x02, BUILD_ERRORS = 0x04, TEST_ERRORS = 0x08, MEMORY_ERRORS = 0x10, COVERAGE_ERRORS = 0x20, SUBMIT_ERRORS = 0x40 }; ///! Are we producing XML bool GetProduceXML(); void SetProduceXML(bool v); //! Run command specialized for tests. Returns process status and retVal is // return value or exception. If environment is non-null, it is used to set // environment variables prior to running the test. After running the test, // environment variables are restored to their previous values. int RunTest(std::vector args, std::string* output, int *retVal, std::ostream* logfile, double testTimeOut, std::vector* environment); /** * Execute handler and return its result. If the handler fails, it returns * negative value. */ int ExecuteHandler(const char* handler); /* * Get the handler object */ cmCTestGenericHandler* GetHandler(const char* handler); cmCTestGenericHandler* GetInitializedHandler(const char* handler); /* * Set the CTest variable from CMake variable */ bool SetCTestConfigurationFromCMakeVariable(cmMakefile* mf, const char* dconfig, const char* cmake_var); //! Make string safe to be send as an URL static std::string MakeURLSafe(const std::string&); /** Decode a URL to the original string. */ static std::string DecodeURL(const std::string&); //! Should ctect configuration be updated. When using new style ctest // script, this should be true. void SetSuppressUpdatingCTestConfiguration(bool val) { this->SuppressUpdatingCTestConfiguration = val; } //! Add overwrite to ctest configuration. // The format is key=value void AddCTestConfigurationOverwrite(const char* encstr); //! Create XML file that contains all the notes specified int GenerateNotesFile(const std::vector &files); //! Submit extra files to the server bool SubmitExtraFiles(const char* files); bool SubmitExtraFiles(const std::vector &files); //! Set the output log file name void SetOutputLogFileName(const char* name); //! Set the visual studio or Xcode config type void SetConfigType(const char* ct); //! Various log types enum { DEBUG = 0, OUTPUT, HANDLER_OUTPUT, HANDLER_VERBOSE_OUTPUT, WARNING, ERROR_MESSAGE, OTHER }; //! Add log to the output void Log(int logType, const char* file, int line, const char* msg); //! Get the version of dart server int GetDartVersion() { return this->DartVersion; } //! Add file to be submitted void AddSubmitFile(Part part, const char* name); std::vector const& GetSubmitFiles(Part part) { return this->Parts[part].SubmitFiles; } void ClearSubmitFiles(Part part) { this->Parts[part].SubmitFiles.clear(); } //! Read the custom configuration files and apply them to the current ctest int ReadCustomConfigurationFileTree(const char* dir, cmMakefile* mf); std::vector &GetInitialCommandLineArguments() { return this->InitialCommandLineArguments; }; //! Set the track to submit to void SetSpecificTrack(const char* track); const char* GetSpecificTrack(); void SetFailover(bool failover) { this->Failover = failover; } bool GetFailover() { return this->Failover; } void SetBatchJobs(bool batch = true) { this->BatchJobs = batch; } bool GetBatchJobs() { return this->BatchJobs; } bool GetVerbose() { return this->Verbose;} bool GetExtraVerbose() { return this->ExtraVerbose;} /** Direct process output to given streams. */ void SetStreams(std::ostream* out, std::ostream* err) { this->StreamOut = out; this->StreamErr = err; } void AddSiteProperties(std::ostream& ); bool GetLabelSummary() { return this->LabelSummary;} private: std::string ConfigType; std::string ScheduleType; bool Verbose; bool ExtraVerbose; bool ProduceXML; bool LabelSummary; bool UseHTTP10; bool Failover; bool BatchJobs; bool ForceNewCTestProcess; bool RunConfigurationScript; //flag for lazy getter (optimization) bool ComputedCompressOutput; int GenerateNotesFile(const char* files); // these are helper classes typedef std::map t_TestingHandlers; t_TestingHandlers TestingHandlers; bool ShowOnly; //! Map of configuration properties typedef std::map CTestConfigurationMap; std::string CTestConfigFile; // TODO: The ctest configuration should be a hierarchy of // configuration option sources: command-line, script, ini file. // Then the ini file can get re-loaded whenever it changes without // affecting any higher-precedence settings. CTestConfigurationMap CTestConfiguration; CTestConfigurationMap CTestConfigurationOverwrites; PartInfo Parts[PartCount]; typedef std::map PartMapType; PartMapType PartMap; std::string CurrentTag; bool TomorrowTag; int TestModel; std::string SpecificTrack; double TimeOut; double GlobalTimeout; int MaxTestNameWidth; int ParallelLevel; int CompatibilityMode; // information for the --build-and-test options std::string CMakeSelf; std::string CTestSelf; std::string BinaryDir; std::string NotesFiles; bool InteractiveDebugMode; bool ShortDateFormat; bool CompressXMLFiles; bool CompressTestOutput; void InitStreams(); std::ostream* StreamOut; std::ostream* StreamErr; void BlockTestErrorDiagnostics(); /** * Initialize a dashboard run in the given build tree. The "script" * argument is true when running from a command-driven (ctest_start) * dashboard script, and false when running from the CTest command * line. Note that a declarative dashboard script does not actually * call this method because it sets CTEST_COMMAND to drive a build * through the ctest command line. */ int Initialize(const char* binary_dir, bool script); //! parse the option after -D and convert it into the appropriate steps bool AddTestsForDashboardType(std::string &targ); //! parse and process most common command line arguments void HandleCommandLineArguments(size_t &i, std::vector &args); //! hande the -S -SP and -SR arguments void HandleScriptArguments(size_t &i, std::vector &args, bool &SRArgumentSpecified); //! Reread the configuration file bool UpdateCTestConfiguration(); //! Create not from files. int GenerateCTestNotesOutput(std::ostream& os, const VectorOfStrings& files); ///! Find the running cmake void FindRunningCMake(); //! Check if the argument is the one specified bool CheckArgument(const std::string& arg, const char* varg1, const char* varg2 = 0); //! Output errors from a test void OutputTestErrors(std::vector const &process_output); bool SuppressUpdatingCTestConfiguration; bool Debug; bool ShowLineNumbers; bool Quiet; int DartVersion; std::vector InitialCommandLineArguments; int SubmitIndex; cmGeneratedFileStream* OutputLogFile; int OutputLogFileLastTag; bool OutputTestOutputOnTestFailure; }; class cmCTestLogWrite { public: cmCTestLogWrite(const char* data, size_t length) : Data(data), Length(length) {} const char* Data; size_t Length; }; inline std::ostream& operator<< (std::ostream& os, const cmCTestLogWrite& c) { if (!c.Length) { return os; } os.write(c.Data, c.Length); os.flush(); return os; } #endif