cmCTestSubmitHandler.cxx 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133
  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
  8. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "cmCTestSubmitHandler.h"
  14. #include "cmSystemTools.h"
  15. #include "cmVersion.h"
  16. #include "cmGeneratedFileStream.h"
  17. #include "cmCTest.h"
  18. #include <cmsys/Process.h>
  19. #include <cmsys/Base64.h>
  20. // For XML-RPC submission
  21. #include "cm_xmlrpc.h"
  22. // For curl submission
  23. #include "cm_curl.h"
  24. #include <sys/stat.h>
  25. typedef std::vector<char> cmCTestSubmitHandlerVectorOfChar;
  26. static size_t
  27. cmCTestSubmitHandlerWriteMemoryCallback(void *ptr, size_t size, size_t nmemb,
  28. void *data)
  29. {
  30. register int realsize = (int)(size * nmemb);
  31. cmCTestSubmitHandlerVectorOfChar *vec
  32. = static_cast<cmCTestSubmitHandlerVectorOfChar*>(data);
  33. const char* chPtr = static_cast<char*>(ptr);
  34. vec->insert(vec->end(), chPtr, chPtr + realsize);
  35. return realsize;
  36. }
  37. static size_t
  38. cmCTestSubmitHandlerCurlDebugCallback(CURL *, curl_infotype, char *chPtr,
  39. size_t size, void *data)
  40. {
  41. cmCTestSubmitHandlerVectorOfChar *vec
  42. = static_cast<cmCTestSubmitHandlerVectorOfChar*>(data);
  43. vec->insert(vec->end(), chPtr, chPtr + size);
  44. return size;
  45. }
  46. //----------------------------------------------------------------------------
  47. cmCTestSubmitHandler::cmCTestSubmitHandler() : HTTPProxy(), FTPProxy()
  48. {
  49. this->HTTPProxy = "";
  50. this->HTTPProxyType = 0;
  51. this->HTTPProxyAuth = "";
  52. this->FTPProxy = "";
  53. this->FTPProxyType = 0;
  54. this->CDash = false;
  55. // We submit all available parts by default.
  56. for(cmCTest::Part p = cmCTest::PartStart;
  57. p != cmCTest::PartCount; p = cmCTest::Part(p+1))
  58. {
  59. this->SubmitPart[p] = true;
  60. }
  61. }
  62. //----------------------------------------------------------------------------
  63. void cmCTestSubmitHandler::Initialize()
  64. {
  65. this->CDash = false;
  66. this->Superclass::Initialize();
  67. this->HTTPProxy = "";
  68. this->HTTPProxyType = 0;
  69. this->HTTPProxyAuth = "";
  70. this->FTPProxy = "";
  71. this->FTPProxyType = 0;
  72. this->LogFile = 0;
  73. }
  74. //----------------------------------------------------------------------------
  75. bool cmCTestSubmitHandler::SubmitUsingFTP(const cmStdString& localprefix,
  76. const std::set<cmStdString>& files,
  77. const cmStdString& remoteprefix,
  78. const cmStdString& url)
  79. {
  80. CURL *curl;
  81. CURLcode res;
  82. FILE* ftpfile;
  83. char error_buffer[1024];
  84. /* In windows, this will init the winsock stuff */
  85. ::curl_global_init(CURL_GLOBAL_ALL);
  86. cmCTest::SetOfStrings::const_iterator file;
  87. for ( file = files.begin(); file != files.end(); ++file )
  88. {
  89. /* get a curl handle */
  90. curl = curl_easy_init();
  91. if(curl)
  92. {
  93. // Using proxy
  94. if ( this->FTPProxyType > 0 )
  95. {
  96. curl_easy_setopt(curl, CURLOPT_PROXY, this->FTPProxy.c_str());
  97. switch (this->FTPProxyType)
  98. {
  99. case 2:
  100. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
  101. break;
  102. case 3:
  103. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
  104. break;
  105. default:
  106. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  107. }
  108. }
  109. // enable uploading
  110. ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1) ;
  111. cmStdString local_file = *file;
  112. if ( !cmSystemTools::FileExists(local_file.c_str()) )
  113. {
  114. local_file = localprefix + "/" + *file;
  115. }
  116. cmStdString upload_as
  117. = url + "/" + remoteprefix + cmSystemTools::GetFilenameName(*file);
  118. struct stat st;
  119. if ( ::stat(local_file.c_str(), &st) )
  120. {
  121. cmCTestLog(this->CTest, ERROR_MESSAGE, " Cannot find file: "
  122. << local_file.c_str() << std::endl);
  123. ::curl_easy_cleanup(curl);
  124. ::curl_global_cleanup();
  125. return false;
  126. }
  127. ftpfile = ::fopen(local_file.c_str(), "rb");
  128. *this->LogFile << "\tUpload file: " << local_file.c_str() << " to "
  129. << upload_as.c_str() << std::endl;
  130. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Upload file: "
  131. << local_file.c_str() << " to "
  132. << upload_as.c_str() << std::endl);
  133. ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  134. // specify target
  135. ::curl_easy_setopt(curl,CURLOPT_URL, upload_as.c_str());
  136. // now specify which file to upload
  137. ::curl_easy_setopt(curl, CURLOPT_INFILE, ftpfile);
  138. // and give the size of the upload (optional)
  139. ::curl_easy_setopt(curl, CURLOPT_INFILESIZE,
  140. static_cast<long>(st.st_size));
  141. // and give curl the buffer for errors
  142. ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer);
  143. // specify handler for output
  144. ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
  145. cmCTestSubmitHandlerWriteMemoryCallback);
  146. ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
  147. cmCTestSubmitHandlerCurlDebugCallback);
  148. /* we pass our 'chunk' struct to the callback function */
  149. cmCTestSubmitHandlerVectorOfChar chunk;
  150. cmCTestSubmitHandlerVectorOfChar chunkDebug;
  151. ::curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk);
  152. ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void *)&chunkDebug);
  153. // Now run off and do what you've been told!
  154. res = ::curl_easy_perform(curl);
  155. if ( chunk.size() > 0 )
  156. {
  157. cmCTestLog(this->CTest, DEBUG, "CURL output: ["
  158. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  159. << std::endl);
  160. }
  161. if ( chunkDebug.size() > 0 )
  162. {
  163. cmCTestLog(this->CTest, DEBUG, "CURL debug output: ["
  164. << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]"
  165. << std::endl);
  166. }
  167. fclose(ftpfile);
  168. if ( res )
  169. {
  170. cmCTestLog(this->CTest, ERROR_MESSAGE,
  171. " Error when uploading file: "
  172. << local_file.c_str() << std::endl);
  173. cmCTestLog(this->CTest, ERROR_MESSAGE, " Error message was: "
  174. << error_buffer << std::endl);
  175. *this->LogFile << " Error when uploading file: "
  176. << local_file.c_str()
  177. << std::endl
  178. << " Error message was: "
  179. << error_buffer << std::endl
  180. << " Curl output was: ";
  181. // avoid dereference of empty vector
  182. if(chunk.size())
  183. {
  184. *this->LogFile << cmCTestLogWrite(&*chunk.begin(), chunk.size());
  185. cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: ["
  186. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  187. << std::endl);
  188. }
  189. *this->LogFile << std::endl;
  190. ::curl_easy_cleanup(curl);
  191. ::curl_global_cleanup();
  192. return false;
  193. }
  194. // always cleanup
  195. ::curl_easy_cleanup(curl);
  196. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Uploaded: " + local_file
  197. << std::endl);
  198. }
  199. }
  200. ::curl_global_cleanup();
  201. return true;
  202. }
  203. //----------------------------------------------------------------------------
  204. // Uploading files is simpler
  205. bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
  206. const std::set<cmStdString>& files,
  207. const cmStdString& remoteprefix,
  208. const cmStdString& url)
  209. {
  210. CURL *curl;
  211. CURLcode res;
  212. FILE* ftpfile;
  213. char error_buffer[1024];
  214. /* In windows, this will init the winsock stuff */
  215. ::curl_global_init(CURL_GLOBAL_ALL);
  216. cmStdString::size_type kk;
  217. cmCTest::SetOfStrings::const_iterator file;
  218. for ( file = files.begin(); file != files.end(); ++file )
  219. {
  220. /* get a curl handle */
  221. curl = curl_easy_init();
  222. if(curl)
  223. {
  224. // Using proxy
  225. if ( this->HTTPProxyType > 0 )
  226. {
  227. curl_easy_setopt(curl, CURLOPT_PROXY, this->HTTPProxy.c_str());
  228. switch (this->HTTPProxyType)
  229. {
  230. case 2:
  231. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
  232. break;
  233. case 3:
  234. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
  235. break;
  236. default:
  237. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  238. if (this->HTTPProxyAuth.size() > 0)
  239. {
  240. curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD,
  241. this->HTTPProxyAuth.c_str());
  242. }
  243. }
  244. }
  245. /* enable uploading */
  246. curl_easy_setopt(curl, CURLOPT_UPLOAD, 1) ;
  247. /* HTTP PUT please */
  248. ::curl_easy_setopt(curl, CURLOPT_PUT, 1);
  249. ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  250. cmStdString local_file = *file;
  251. if ( !cmSystemTools::FileExists(local_file.c_str()) )
  252. {
  253. local_file = localprefix + "/" + *file;
  254. }
  255. cmStdString remote_file
  256. = remoteprefix + cmSystemTools::GetFilenameName(*file);
  257. *this->LogFile << "\tUpload file: " << local_file.c_str() << " to "
  258. << remote_file.c_str() << std::endl;
  259. cmStdString ofile = "";
  260. for ( kk = 0; kk < remote_file.size(); kk ++ )
  261. {
  262. char c = remote_file[kk];
  263. char hexCh[4] = { 0, 0, 0, 0 };
  264. hexCh[0] = c;
  265. switch ( c )
  266. {
  267. case '+':
  268. case '?':
  269. case '/':
  270. case '\\':
  271. case '&':
  272. case ' ':
  273. case '=':
  274. case '%':
  275. sprintf(hexCh, "%%%02X", (int)c);
  276. ofile.append(hexCh);
  277. break;
  278. default:
  279. ofile.append(hexCh);
  280. }
  281. }
  282. cmStdString upload_as
  283. = url + ((url.find("?",0) == cmStdString::npos) ? "?" : "&")
  284. + "FileName=" + ofile;
  285. struct stat st;
  286. if ( ::stat(local_file.c_str(), &st) )
  287. {
  288. cmCTestLog(this->CTest, ERROR_MESSAGE, " Cannot find file: "
  289. << local_file.c_str() << std::endl);
  290. ::curl_easy_cleanup(curl);
  291. ::curl_global_cleanup();
  292. return false;
  293. }
  294. ftpfile = ::fopen(local_file.c_str(), "rb");
  295. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Upload file: "
  296. << local_file.c_str() << " to "
  297. << upload_as.c_str() << " Size: " << st.st_size << std::endl);
  298. // specify target
  299. ::curl_easy_setopt(curl,CURLOPT_URL, upload_as.c_str());
  300. // now specify which file to upload
  301. ::curl_easy_setopt(curl, CURLOPT_INFILE, ftpfile);
  302. // and give the size of the upload (optional)
  303. ::curl_easy_setopt(curl, CURLOPT_INFILESIZE,
  304. static_cast<long>(st.st_size));
  305. // and give curl the buffer for errors
  306. ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer);
  307. // specify handler for output
  308. ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
  309. cmCTestSubmitHandlerWriteMemoryCallback);
  310. ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
  311. cmCTestSubmitHandlerCurlDebugCallback);
  312. /* we pass our 'chunk' struct to the callback function */
  313. cmCTestSubmitHandlerVectorOfChar chunk;
  314. cmCTestSubmitHandlerVectorOfChar chunkDebug;
  315. ::curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk);
  316. ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void *)&chunkDebug);
  317. // Now run off and do what you've been told!
  318. res = ::curl_easy_perform(curl);
  319. if ( chunk.size() > 0 )
  320. {
  321. cmCTestLog(this->CTest, DEBUG, "CURL output: ["
  322. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  323. << std::endl);
  324. }
  325. if ( chunkDebug.size() > 0 )
  326. {
  327. cmCTestLog(this->CTest, DEBUG, "CURL debug output: ["
  328. << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]"
  329. << std::endl);
  330. }
  331. fclose(ftpfile);
  332. if ( res )
  333. {
  334. cmCTestLog(this->CTest, ERROR_MESSAGE,
  335. " Error when uploading file: "
  336. << local_file.c_str() << std::endl);
  337. cmCTestLog(this->CTest, ERROR_MESSAGE, " Error message was: "
  338. << error_buffer << std::endl);
  339. *this->LogFile << " Error when uploading file: "
  340. << local_file.c_str()
  341. << std::endl
  342. << " Error message was: " << error_buffer
  343. << std::endl;
  344. // avoid deref of begin for zero size array
  345. if(chunk.size())
  346. {
  347. *this->LogFile << " Curl output was: "
  348. << cmCTestLogWrite(&*chunk.begin(), chunk.size())
  349. << std::endl;
  350. cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: ["
  351. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  352. << std::endl);
  353. }
  354. ::curl_easy_cleanup(curl);
  355. ::curl_global_cleanup();
  356. return false;
  357. }
  358. // always cleanup
  359. ::curl_easy_cleanup(curl);
  360. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Uploaded: " + local_file
  361. << std::endl);
  362. }
  363. }
  364. ::curl_global_cleanup();
  365. return true;
  366. }
  367. //----------------------------------------------------------------------------
  368. bool cmCTestSubmitHandler::TriggerUsingHTTP(
  369. const std::set<cmStdString>& files,
  370. const cmStdString& remoteprefix,
  371. const cmStdString& url)
  372. {
  373. CURL *curl;
  374. char error_buffer[1024];
  375. /* In windows, this will init the winsock stuff */
  376. ::curl_global_init(CURL_GLOBAL_ALL);
  377. cmCTest::SetOfStrings::const_iterator file;
  378. for ( file = files.begin(); file != files.end(); ++file )
  379. {
  380. /* get a curl handle */
  381. curl = curl_easy_init();
  382. if(curl)
  383. {
  384. // Using proxy
  385. if ( this->HTTPProxyType > 0 )
  386. {
  387. curl_easy_setopt(curl, CURLOPT_PROXY, this->HTTPProxy.c_str());
  388. switch (this->HTTPProxyType)
  389. {
  390. case 2:
  391. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
  392. break;
  393. case 3:
  394. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
  395. break;
  396. default:
  397. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  398. if (this->HTTPProxyAuth.size() > 0)
  399. {
  400. curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD,
  401. this->HTTPProxyAuth.c_str());
  402. }
  403. }
  404. }
  405. ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  406. // and give curl the buffer for errors
  407. ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer);
  408. // specify handler for output
  409. ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
  410. cmCTestSubmitHandlerWriteMemoryCallback);
  411. ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
  412. cmCTestSubmitHandlerCurlDebugCallback);
  413. /* we pass our 'chunk' struct to the callback function */
  414. cmCTestSubmitHandlerVectorOfChar chunk;
  415. cmCTestSubmitHandlerVectorOfChar chunkDebug;
  416. ::curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk);
  417. ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void *)&chunkDebug);
  418. cmStdString rfile
  419. = remoteprefix + cmSystemTools::GetFilenameName(*file);
  420. cmStdString ofile = "";
  421. cmStdString::iterator kk;
  422. for ( kk = rfile.begin(); kk < rfile.end(); ++ kk)
  423. {
  424. char c = *kk;
  425. char hexCh[4] = { 0, 0, 0, 0 };
  426. hexCh[0] = c;
  427. switch ( c )
  428. {
  429. case '+':
  430. case '?':
  431. case '/':
  432. case '\\':
  433. case '&':
  434. case ' ':
  435. case '=':
  436. case '%':
  437. sprintf(hexCh, "%%%02X", (int)c);
  438. ofile.append(hexCh);
  439. break;
  440. default:
  441. ofile.append(hexCh);
  442. }
  443. }
  444. cmStdString turl
  445. = url + ((url.find("?",0) == cmStdString::npos) ? "?" : "&")
  446. + "xmlfile=" + ofile;
  447. *this->LogFile << "Trigger url: " << turl.c_str() << std::endl;
  448. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Trigger url: "
  449. << turl.c_str() << std::endl);
  450. curl_easy_setopt(curl, CURLOPT_URL, turl.c_str());
  451. if ( curl_easy_perform(curl) )
  452. {
  453. cmCTestLog(this->CTest, ERROR_MESSAGE, " Error when triggering: "
  454. << turl.c_str() << std::endl);
  455. cmCTestLog(this->CTest, ERROR_MESSAGE, " Error message was: "
  456. << error_buffer << std::endl);
  457. *this->LogFile << "\tTrigerring failed with error: " << error_buffer
  458. << std::endl
  459. << " Error message was: " << error_buffer
  460. << std::endl;
  461. if(chunk.size())
  462. {
  463. *this->LogFile
  464. << " Curl output was: "
  465. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << std::endl;
  466. cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: ["
  467. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  468. << std::endl);
  469. }
  470. ::curl_easy_cleanup(curl);
  471. ::curl_global_cleanup();
  472. return false;
  473. }
  474. if ( chunk.size() > 0 )
  475. {
  476. cmCTestLog(this->CTest, DEBUG, "CURL output: ["
  477. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  478. << std::endl);
  479. }
  480. if ( chunkDebug.size() > 0 )
  481. {
  482. cmCTestLog(this->CTest, DEBUG, "CURL debug output: ["
  483. << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size())
  484. << "]" << std::endl);
  485. }
  486. // always cleanup
  487. ::curl_easy_cleanup(curl);
  488. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl);
  489. }
  490. }
  491. ::curl_global_cleanup();
  492. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Dart server triggered..."
  493. << std::endl);
  494. return true;
  495. }
  496. //----------------------------------------------------------------------------
  497. bool cmCTestSubmitHandler::SubmitUsingSCP(
  498. const cmStdString& scp_command,
  499. const cmStdString& localprefix,
  500. const std::set<cmStdString>& files,
  501. const cmStdString& remoteprefix,
  502. const cmStdString& url)
  503. {
  504. if ( !scp_command.size() || !localprefix.size() ||
  505. !files.size() || !remoteprefix.size() || !url.size() )
  506. {
  507. return 0;
  508. }
  509. std::vector<const char*> argv;
  510. argv.push_back(scp_command.c_str()); // Scp command
  511. argv.push_back(scp_command.c_str()); // Dummy string for file
  512. argv.push_back(scp_command.c_str()); // Dummy string for remote url
  513. argv.push_back(0);
  514. cmsysProcess* cp = cmsysProcess_New();
  515. cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
  516. //cmsysProcess_SetTimeout(cp, timeout);
  517. int problems = 0;
  518. cmCTest::SetOfStrings::const_iterator file;
  519. for ( file = files.begin(); file != files.end(); ++file )
  520. {
  521. int retVal;
  522. std::string lfname = localprefix;
  523. cmSystemTools::ConvertToUnixSlashes(lfname);
  524. lfname += "/" + *file;
  525. lfname = cmSystemTools::ConvertToOutputPath(lfname.c_str());
  526. argv[1] = lfname.c_str();
  527. std::string rfname = url + "/" + remoteprefix + *file;
  528. argv[2] = rfname.c_str();
  529. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Execute \"" << argv[0]
  530. << "\" \"" << argv[1] << "\" \""
  531. << argv[2] << "\"" << std::endl);
  532. *this->LogFile << "Execute \"" << argv[0] << "\" \"" << argv[1] << "\" \""
  533. << argv[2] << "\"" << std::endl;
  534. cmsysProcess_SetCommand(cp, &*argv.begin());
  535. cmsysProcess_Execute(cp);
  536. char* data;
  537. int length;
  538. while(cmsysProcess_WaitForData(cp, &data, &length, 0))
  539. {
  540. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  541. cmCTestLogWrite(data, length));
  542. }
  543. cmsysProcess_WaitForExit(cp, 0);
  544. int result = cmsysProcess_GetState(cp);
  545. if(result == cmsysProcess_State_Exited)
  546. {
  547. retVal = cmsysProcess_GetExitValue(cp);
  548. if ( retVal != 0 )
  549. {
  550. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "\tSCP returned: "
  551. << retVal << std::endl);
  552. *this->LogFile << "\tSCP returned: " << retVal << std::endl;
  553. problems ++;
  554. }
  555. }
  556. else if(result == cmsysProcess_State_Exception)
  557. {
  558. retVal = cmsysProcess_GetExitException(cp);
  559. cmCTestLog(this->CTest, ERROR_MESSAGE, "\tThere was an exception: "
  560. << retVal << std::endl);
  561. *this->LogFile << "\tThere was an exception: " << retVal << std::endl;
  562. problems ++;
  563. }
  564. else if(result == cmsysProcess_State_Expired)
  565. {
  566. cmCTestLog(this->CTest, ERROR_MESSAGE, "\tThere was a timeout"
  567. << std::endl);
  568. *this->LogFile << "\tThere was a timeout" << std::endl;
  569. problems ++;
  570. }
  571. else if(result == cmsysProcess_State_Error)
  572. {
  573. cmCTestLog(this->CTest, ERROR_MESSAGE, "\tError executing SCP: "
  574. << cmsysProcess_GetErrorString(cp) << std::endl);
  575. *this->LogFile << "\tError executing SCP: "
  576. << cmsysProcess_GetErrorString(cp) << std::endl;
  577. problems ++;
  578. }
  579. }
  580. cmsysProcess_Delete(cp);
  581. if ( problems )
  582. {
  583. return false;
  584. }
  585. return true;
  586. }
  587. //----------------------------------------------------------------------------
  588. bool cmCTestSubmitHandler::SubmitUsingXMLRPC(const cmStdString& localprefix,
  589. const std::set<cmStdString>& files,
  590. const cmStdString& remoteprefix,
  591. const cmStdString& url)
  592. {
  593. xmlrpc_env env;
  594. char ctestString[] = "CTest";
  595. std::string ctestVersionString = cmVersion::GetCMakeVersion();
  596. char* ctestVersion = const_cast<char*>(ctestVersionString.c_str());
  597. cmStdString realURL = url + "/" + remoteprefix + "/Command/";
  598. /* Start up our XML-RPC client library. */
  599. xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS, ctestString, ctestVersion);
  600. /* Initialize our error-handling environment. */
  601. xmlrpc_env_init(&env);
  602. /* Call the famous server at UserLand. */
  603. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Submitting to: "
  604. << realURL.c_str() << " (" << remoteprefix.c_str() << ")" << std::endl);
  605. cmCTest::SetOfStrings::const_iterator file;
  606. for ( file = files.begin(); file != files.end(); ++file )
  607. {
  608. xmlrpc_value *result;
  609. cmStdString local_file = *file;
  610. if ( !cmSystemTools::FileExists(local_file.c_str()) )
  611. {
  612. local_file = localprefix + "/" + *file;
  613. }
  614. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Submit file: "
  615. << local_file.c_str() << std::endl);
  616. struct stat st;
  617. if ( ::stat(local_file.c_str(), &st) )
  618. {
  619. cmCTestLog(this->CTest, ERROR_MESSAGE, " Cannot find file: "
  620. << local_file.c_str() << std::endl);
  621. return false;
  622. }
  623. // off_t can be bigger than size_t. fread takes size_t.
  624. // make sure the file is not too big.
  625. if(static_cast<off_t>(static_cast<size_t>(st.st_size)) !=
  626. static_cast<off_t>(st.st_size))
  627. {
  628. cmCTestLog(this->CTest, ERROR_MESSAGE, " File too big: "
  629. << local_file.c_str() << std::endl);
  630. return false;
  631. }
  632. size_t fileSize = static_cast<size_t>(st.st_size);
  633. FILE* fp = fopen(local_file.c_str(), "rb");
  634. if ( !fp )
  635. {
  636. cmCTestLog(this->CTest, ERROR_MESSAGE, " Cannot open file: "
  637. << local_file.c_str() << std::endl);
  638. return false;
  639. }
  640. unsigned char *fileBuffer = new unsigned char[fileSize];
  641. if ( fread(fileBuffer, 1, fileSize, fp) != fileSize )
  642. {
  643. delete [] fileBuffer;
  644. fclose(fp);
  645. cmCTestLog(this->CTest, ERROR_MESSAGE, " Cannot read file: "
  646. << local_file.c_str() << std::endl);
  647. return false;
  648. }
  649. fclose(fp);
  650. char remoteCommand[] = "Submit.put";
  651. char* pRealURL = const_cast<char*>(realURL.c_str());
  652. result = xmlrpc_client_call(&env, pRealURL, remoteCommand,
  653. "(6)", fileBuffer, (xmlrpc_int32)fileSize );
  654. delete [] fileBuffer;
  655. if ( env.fault_occurred )
  656. {
  657. cmCTestLog(this->CTest, ERROR_MESSAGE, " Submission problem: "
  658. << env.fault_string << " (" << env.fault_code << ")" << std::endl);
  659. xmlrpc_env_clean(&env);
  660. xmlrpc_client_cleanup();
  661. return false;
  662. }
  663. /* Dispose of our result value. */
  664. xmlrpc_DECREF(result);
  665. }
  666. /* Clean up our error-handling environment. */
  667. xmlrpc_env_clean(&env);
  668. /* Shutdown our XML-RPC client library. */
  669. xmlrpc_client_cleanup();
  670. return true;
  671. }
  672. //----------------------------------------------------------------------------
  673. int cmCTestSubmitHandler::ProcessHandler()
  674. {
  675. std::string iscdash = this->CTest->GetCTestConfiguration("IsCDash");
  676. // cdash does not need to trigger so just return true
  677. if(iscdash.size())
  678. {
  679. this->CDash = true;
  680. }
  681. const std::string &buildDirectory
  682. = this->CTest->GetCTestConfiguration("BuildDirectory");
  683. if ( buildDirectory.size() == 0 )
  684. {
  685. cmCTestLog(this->CTest, ERROR_MESSAGE,
  686. "Cannot find BuildDirectory key in the DartConfiguration.tcl"
  687. << std::endl);
  688. return -1;
  689. }
  690. if ( getenv("HTTP_PROXY") )
  691. {
  692. this->HTTPProxyType = 1;
  693. this->HTTPProxy = getenv("HTTP_PROXY");
  694. if ( getenv("HTTP_PROXY_PORT") )
  695. {
  696. this->HTTPProxy += ":";
  697. this->HTTPProxy += getenv("HTTP_PROXY_PORT");
  698. }
  699. if ( getenv("HTTP_PROXY_TYPE") )
  700. {
  701. cmStdString type = getenv("HTTP_PROXY_TYPE");
  702. // HTTP/SOCKS4/SOCKS5
  703. if ( type == "HTTP" )
  704. {
  705. this->HTTPProxyType = 1;
  706. }
  707. else if ( type == "SOCKS4" )
  708. {
  709. this->HTTPProxyType = 2;
  710. }
  711. else if ( type == "SOCKS5" )
  712. {
  713. this->HTTPProxyType = 3;
  714. }
  715. }
  716. if ( getenv("HTTP_PROXY_USER") )
  717. {
  718. this->HTTPProxyAuth = getenv("HTTP_PROXY_USER");
  719. }
  720. if ( getenv("HTTP_PROXY_PASSWD") )
  721. {
  722. this->HTTPProxyAuth += ":";
  723. this->HTTPProxyAuth += getenv("HTTP_PROXY_PASSWD");
  724. }
  725. }
  726. if ( getenv("FTP_PROXY") )
  727. {
  728. this->FTPProxyType = 1;
  729. this->FTPProxy = getenv("FTP_PROXY");
  730. if ( getenv("FTP_PROXY_PORT") )
  731. {
  732. this->FTPProxy += ":";
  733. this->FTPProxy += getenv("FTP_PROXY_PORT");
  734. }
  735. if ( getenv("FTP_PROXY_TYPE") )
  736. {
  737. cmStdString type = getenv("FTP_PROXY_TYPE");
  738. // HTTP/SOCKS4/SOCKS5
  739. if ( type == "HTTP" )
  740. {
  741. this->FTPProxyType = 1;
  742. }
  743. else if ( type == "SOCKS4" )
  744. {
  745. this->FTPProxyType = 2;
  746. }
  747. else if ( type == "SOCKS5" )
  748. {
  749. this->FTPProxyType = 3;
  750. }
  751. }
  752. }
  753. if ( this->HTTPProxy.size() > 0 )
  754. {
  755. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Use HTTP Proxy: "
  756. << this->HTTPProxy << std::endl);
  757. }
  758. if ( this->FTPProxy.size() > 0 )
  759. {
  760. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Use FTP Proxy: "
  761. << this->FTPProxy << std::endl);
  762. }
  763. cmGeneratedFileStream ofs;
  764. this->StartLogFile("Submit", ofs);
  765. std::string prefix = this->GetSubmitResultsPrefix();
  766. // TODO:
  767. // Check if test is enabled
  768. this->CTest->AddIfExists(cmCTest::PartUpdate, "Update.xml");
  769. this->CTest->AddIfExists(cmCTest::PartConfigure, "Configure.xml");
  770. this->CTest->AddIfExists(cmCTest::PartBuild, "Build.xml");
  771. this->CTest->AddIfExists(cmCTest::PartTest, "Test.xml");
  772. if(this->CTest->AddIfExists(cmCTest::PartCoverage, "Coverage.xml"))
  773. {
  774. cmCTest::VectorOfStrings gfiles;
  775. std::string gpath
  776. = buildDirectory + "/Testing/" + this->CTest->GetCurrentTag();
  777. std::string::size_type glen = gpath.size() + 1;
  778. gpath = gpath + "/CoverageLog*";
  779. cmCTestLog(this->CTest, DEBUG, "Globbing for: " << gpath.c_str()
  780. << std::endl);
  781. if ( cmSystemTools::SimpleGlob(gpath, gfiles, 1) )
  782. {
  783. size_t cc;
  784. for ( cc = 0; cc < gfiles.size(); cc ++ )
  785. {
  786. gfiles[cc] = gfiles[cc].substr(glen);
  787. cmCTestLog(this->CTest, DEBUG, "Glob file: " << gfiles[cc].c_str()
  788. << std::endl);
  789. this->CTest->AddSubmitFile(cmCTest::PartCoverage, gfiles[cc].c_str());
  790. }
  791. }
  792. else
  793. {
  794. cmCTestLog(this->CTest, ERROR_MESSAGE, "Problem globbing" << std::endl);
  795. }
  796. }
  797. this->CTest->AddIfExists(cmCTest::PartMemCheck, "DynamicAnalysis.xml");
  798. this->CTest->AddIfExists(cmCTest::PartMemCheck, "Purify.xml");
  799. this->CTest->AddIfExists(cmCTest::PartNotes, "Notes.xml");
  800. // Query parts for files to submit.
  801. cmCTest::SetOfStrings files;
  802. for(cmCTest::Part p = cmCTest::PartStart;
  803. p != cmCTest::PartCount; p = cmCTest::Part(p+1))
  804. {
  805. // Skip parts we are not submitting.
  806. if(!this->SubmitPart[p])
  807. {
  808. continue;
  809. }
  810. // Submit files from this part.
  811. std::vector<std::string> const& pfiles = this->CTest->GetSubmitFiles(p);
  812. for(std::vector<std::string>::const_iterator pi = pfiles.begin();
  813. pi != pfiles.end(); ++pi)
  814. {
  815. files.insert(*pi);
  816. }
  817. }
  818. if ( ofs )
  819. {
  820. ofs << "Upload files:" << std::endl;
  821. int cnt = 0;
  822. cmCTest::SetOfStrings::iterator it;
  823. for ( it = files.begin(); it != files.end(); ++ it )
  824. {
  825. ofs << cnt << "\t" << it->c_str() << std::endl;
  826. cnt ++;
  827. }
  828. }
  829. cmCTestLog(this->CTest, HANDLER_OUTPUT, "Submit files (using "
  830. << this->CTest->GetCTestConfiguration("DropMethod") << ")"
  831. << std::endl);
  832. const char* specificTrack = this->CTest->GetSpecificTrack();
  833. if ( specificTrack )
  834. {
  835. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Send to track: "
  836. << specificTrack << std::endl);
  837. }
  838. this->SetLogFile(&ofs);
  839. if ( this->CTest->GetCTestConfiguration("DropMethod") == "" ||
  840. this->CTest->GetCTestConfiguration("DropMethod") == "ftp" )
  841. {
  842. ofs << "Using drop method: FTP" << std::endl;
  843. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Using FTP submit method"
  844. << std::endl
  845. << " Drop site: ftp://");
  846. std::string url = "ftp://";
  847. url += cmCTest::MakeURLSafe(
  848. this->CTest->GetCTestConfiguration("DropSiteUser")) + ":" +
  849. cmCTest::MakeURLSafe(this->CTest->GetCTestConfiguration(
  850. "DropSitePassword")) + "@" +
  851. this->CTest->GetCTestConfiguration("DropSite") +
  852. cmCTest::MakeURLSafe(
  853. this->CTest->GetCTestConfiguration("DropLocation"));
  854. if ( this->CTest->GetCTestConfiguration("DropSiteUser").size() > 0 )
  855. {
  856. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  857. this->CTest->GetCTestConfiguration(
  858. "DropSiteUser").c_str());
  859. if ( this->CTest->GetCTestConfiguration("DropSitePassword").size() > 0 )
  860. {
  861. cmCTestLog(this->CTest, HANDLER_OUTPUT, ":******");
  862. }
  863. cmCTestLog(this->CTest, HANDLER_OUTPUT, "@");
  864. }
  865. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  866. this->CTest->GetCTestConfiguration("DropSite")
  867. << this->CTest->GetCTestConfiguration("DropLocation") << std::endl);
  868. if ( !this->SubmitUsingFTP(buildDirectory + "/Testing/"
  869. + this->CTest->GetCurrentTag(),
  870. files, prefix, url) )
  871. {
  872. cmCTestLog(this->CTest, ERROR_MESSAGE,
  873. " Problems when submitting via FTP"
  874. << std::endl);
  875. ofs << " Problems when submitting via FTP" << std::endl;
  876. return -1;
  877. }
  878. if(!this->CDash)
  879. {
  880. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Using HTTP trigger method"
  881. << std::endl
  882. << " Trigger site: "
  883. << this->CTest->GetCTestConfiguration("TriggerSite")
  884. << std::endl);
  885. if ( !this->
  886. TriggerUsingHTTP(files, prefix,
  887. this->CTest->GetCTestConfiguration("TriggerSite")))
  888. {
  889. cmCTestLog(this->CTest, ERROR_MESSAGE,
  890. " Problems when triggering via HTTP" << std::endl);
  891. ofs << " Problems when triggering via HTTP" << std::endl;
  892. return -1;
  893. }
  894. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Submission successful"
  895. << std::endl);
  896. ofs << " Submission successful" << std::endl;
  897. return 0;
  898. }
  899. }
  900. else if ( this->CTest->GetCTestConfiguration("DropMethod") == "http" )
  901. {
  902. ofs << "Using drop method: HTTP" << std::endl;
  903. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Using HTTP submit method"
  904. << std::endl
  905. << " Drop site: http://");
  906. std::string url = "http://";
  907. if ( this->CTest->GetCTestConfiguration("DropSiteUser").size() > 0 )
  908. {
  909. url += this->CTest->GetCTestConfiguration("DropSiteUser");
  910. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  911. this->CTest->GetCTestConfiguration("DropSiteUser").c_str());
  912. if ( this->CTest->GetCTestConfiguration("DropSitePassword").size() > 0 )
  913. {
  914. url += ":" + this->CTest->GetCTestConfiguration("DropSitePassword");
  915. cmCTestLog(this->CTest, HANDLER_OUTPUT, ":******");
  916. }
  917. url += "@";
  918. cmCTestLog(this->CTest, HANDLER_OUTPUT, "@");
  919. }
  920. url += this->CTest->GetCTestConfiguration("DropSite") +
  921. this->CTest->GetCTestConfiguration("DropLocation");
  922. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  923. this->CTest->GetCTestConfiguration("DropSite")
  924. << this->CTest->GetCTestConfiguration("DropLocation") << std::endl);
  925. if ( !this->SubmitUsingHTTP(buildDirectory + "/Testing/" +
  926. this->CTest->GetCurrentTag(), files, prefix, url) )
  927. {
  928. cmCTestLog(this->CTest, ERROR_MESSAGE,
  929. " Problems when submitting via HTTP" << std::endl);
  930. ofs << " Problems when submitting via HTTP" << std::endl;
  931. return -1;
  932. }
  933. if(!this->CDash)
  934. {
  935. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Using HTTP trigger method"
  936. << std::endl
  937. << " Trigger site: "
  938. << this->CTest->GetCTestConfiguration("TriggerSite")
  939. << std::endl);
  940. if ( !this->
  941. TriggerUsingHTTP(files, prefix,
  942. this->CTest->GetCTestConfiguration("TriggerSite")))
  943. {
  944. cmCTestLog(this->CTest, ERROR_MESSAGE,
  945. " Problems when triggering via HTTP" << std::endl);
  946. ofs << " Problems when triggering via HTTP" << std::endl;
  947. return -1;
  948. }
  949. }
  950. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Submission successful"
  951. << std::endl);
  952. ofs << " Submission successful" << std::endl;
  953. return 0;
  954. }
  955. else if ( this->CTest->GetCTestConfiguration("DropMethod") == "xmlrpc" )
  956. {
  957. ofs << "Using drop method: XML-RPC" << std::endl;
  958. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Using XML-RPC submit method"
  959. << std::endl);
  960. std::string url = this->CTest->GetCTestConfiguration("DropSite");
  961. prefix = this->CTest->GetCTestConfiguration("DropLocation");
  962. if ( !this->SubmitUsingXMLRPC(buildDirectory + "/Testing/" +
  963. this->CTest->GetCurrentTag(), files, prefix, url) )
  964. {
  965. cmCTestLog(this->CTest, ERROR_MESSAGE,
  966. " Problems when submitting via XML-RPC" << std::endl);
  967. ofs << " Problems when submitting via XML-RPC" << std::endl;
  968. return -1;
  969. }
  970. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Submission successful"
  971. << std::endl);
  972. ofs << " Submission successful" << std::endl;
  973. return 0;
  974. }
  975. else if ( this->CTest->GetCTestConfiguration("DropMethod") == "scp" )
  976. {
  977. std::string url;
  978. std::string oldWorkingDirectory;
  979. if ( this->CTest->GetCTestConfiguration("DropSiteUser").size() > 0 )
  980. {
  981. url += this->CTest->GetCTestConfiguration("DropSiteUser") + "@";
  982. }
  983. url += this->CTest->GetCTestConfiguration("DropSite") + ":" +
  984. this->CTest->GetCTestConfiguration("DropLocation");
  985. // change to the build directory so that we can uses a relative path
  986. // on windows since scp dosn't support "c:" a drive in the path
  987. oldWorkingDirectory = cmSystemTools::GetCurrentWorkingDirectory();
  988. cmSystemTools::ChangeDirectory(buildDirectory.c_str());
  989. if ( !this->SubmitUsingSCP(
  990. this->CTest->GetCTestConfiguration("ScpCommand"),
  991. "Testing/"+this->CTest->GetCurrentTag(), files, prefix, url) )
  992. {
  993. cmSystemTools::ChangeDirectory(oldWorkingDirectory.c_str());
  994. cmCTestLog(this->CTest, ERROR_MESSAGE,
  995. " Problems when submitting via SCP"
  996. << std::endl);
  997. ofs << " Problems when submitting via SCP" << std::endl;
  998. return -1;
  999. }
  1000. cmSystemTools::ChangeDirectory(oldWorkingDirectory.c_str());
  1001. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Submission successful"
  1002. << std::endl);
  1003. ofs << " Submission successful" << std::endl;
  1004. return 0;
  1005. }
  1006. cmCTestLog(this->CTest, ERROR_MESSAGE, " Unknown submission method: \""
  1007. << this->CTest->GetCTestConfiguration("DropMethod") << "\"" << std::endl);
  1008. return -1;
  1009. }
  1010. //----------------------------------------------------------------------------
  1011. std::string cmCTestSubmitHandler::GetSubmitResultsPrefix()
  1012. {
  1013. std::string name = this->CTest->GetCTestConfiguration("Site") +
  1014. "___" + this->CTest->GetCTestConfiguration("BuildName") +
  1015. "___" + this->CTest->GetCurrentTag() + "-" +
  1016. this->CTest->GetTestModelString() + "___XML___";
  1017. return name;
  1018. }
  1019. //----------------------------------------------------------------------------
  1020. void cmCTestSubmitHandler::SelectParts(std::set<cmCTest::Part> const& parts)
  1021. {
  1022. // Check whether each part is selected.
  1023. for(cmCTest::Part p = cmCTest::PartStart;
  1024. p != cmCTest::PartCount; p = cmCTest::Part(p+1))
  1025. {
  1026. this->SubmitPart[p] = (parts.find(p) != parts.end());
  1027. }
  1028. }