PothosUtilSelfTests.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // Copyright (c) 2013-2020 Josh Blum
  2. // 2020 Nicholas Corgan
  3. // SPDX-License-Identifier: BSL-1.0
  4. #include "PothosUtil.hpp"
  5. #include <Pothos/Testing.hpp>
  6. #include <Pothos/Plugin/Loader.hpp>
  7. #include <Pothos/System/Paths.hpp>
  8. #include <Poco/Process.h>
  9. #include <Poco/Pipe.h>
  10. #include <Poco/PipeStream.h>
  11. #include <Poco/String.h>
  12. #include <Poco/Glob.h>
  13. #include <iostream>
  14. #include <sstream>
  15. #include <vector>
  16. #include <future>
  17. #include <cctype>
  18. #include <algorithm> //max
  19. struct SelfTestResults
  20. {
  21. std::vector<std::string> testsPassed;
  22. std::vector<std::string> testsFailed;
  23. };
  24. static std::string collectVerbose(const Poco::Pipe &pipe)
  25. {
  26. size_t maxLen = 0;
  27. Poco::PipeInputStream is(pipe);
  28. std::vector<std::string> lines;
  29. try
  30. {
  31. std::string line;
  32. while (is.good())
  33. {
  34. std::getline(is, line);
  35. while (not line.empty() and std::isspace(line.back()))
  36. {
  37. line.pop_back();
  38. }
  39. if (line.empty()) continue;
  40. maxLen = std::max(maxLen, line.length());
  41. lines.push_back(line);
  42. }
  43. }
  44. catch (...){}
  45. std::ostringstream ss;
  46. ss << " +-" << std::string(maxLen, '-') << "-+" << std::endl;
  47. for (const auto &line : lines)
  48. {
  49. const size_t padLen = maxLen-line.length();
  50. ss << " | " << line << std::string(padLen, ' ') << " |" << std::endl;
  51. }
  52. ss << " +-" << std::string(maxLen, '-') << "-+" << std::endl;
  53. return ss.str();
  54. }
  55. static bool spawnSelfTestOneProcess(const std::string &path, size_t numTrials)
  56. {
  57. const bool multipleTrials = (numTrials > 1);
  58. const int success = 200;
  59. std::cout << "Testing " << path << "... ";
  60. if(multipleTrials) std::cout << std::endl;
  61. else std::cout << std::flush;
  62. //create args
  63. Poco::Process::Args args;
  64. args.push_back("--self-test1");
  65. args.push_back(path);
  66. args.push_back("--success-code");
  67. args.push_back(std::to_string(success));
  68. args.push_back("--num-trials");
  69. args.push_back(std::to_string(numTrials));
  70. size_t numOk = 0;
  71. for(size_t trialNum = 0; trialNum < numTrials; ++trialNum)
  72. {
  73. //launch
  74. Poco::Process::Env env;
  75. Poco::Pipe outPipe; //no fwd stdio
  76. Poco::ProcessHandle ph(Poco::Process::launch(
  77. Pothos::System::getPothosUtilExecutablePath(),
  78. args, nullptr, &outPipe, &outPipe, env));
  79. std::future<std::string> verboseFuture(std::async(std::launch::async, &collectVerbose, outPipe));
  80. const bool ok = (ph.wait() == success);
  81. if(multipleTrials)
  82. {
  83. std::cout << " * Trial " << (trialNum+1) << ": " << std::flush;
  84. }
  85. std::cout << ((ok)? "success!" : "FAIL!") << std::endl;
  86. outPipe.close();
  87. verboseFuture.wait();
  88. if (not ok) std::cout << verboseFuture.get();
  89. else numOk++;
  90. }
  91. return numOk != 0;
  92. }
  93. static void runPluginSelfTestsR(const Pothos::PluginPath &path, SelfTestResults &results, Poco::Glob &glob, size_t numTrials)
  94. {
  95. //run the test found at path
  96. if (not Pothos::PluginRegistry::empty(path) and glob.match(path.toString()))
  97. {
  98. auto plugin = Pothos::PluginRegistry::get(path);
  99. if (plugin.getObject().type() == typeid(std::shared_ptr<Pothos::TestingBase>))
  100. {
  101. if (spawnSelfTestOneProcess(path.toString(), numTrials))
  102. {
  103. results.testsPassed.push_back(path.toString());
  104. }
  105. else
  106. {
  107. results.testsFailed.push_back(path.toString());
  108. }
  109. }
  110. }
  111. //iterate on the subtree stuff
  112. auto nodes = Pothos::PluginRegistry::list(path);
  113. for (auto it = nodes.begin(); it != nodes.end(); it++)
  114. {
  115. runPluginSelfTestsR(path.join(*it), results, glob, numTrials);
  116. }
  117. }
  118. void PothosUtilBase::selfTestOne(const std::string &, const std::string &path)
  119. {
  120. Pothos::ScopedInit init;
  121. const auto numTrials = this->config().getUInt("numTrials", 1);
  122. const bool multipleTrials = (numTrials > 1);
  123. auto plugin = Pothos::PluginRegistry::get(path);
  124. auto test = plugin.getObject().extract<std::shared_ptr<Pothos::TestingBase>>();
  125. if(!multipleTrials)
  126. {
  127. std::cout << "Testing " << plugin.getPath().toString() << "..." << std::endl;
  128. }
  129. try
  130. {
  131. for(size_t trialNum = 0; trialNum < numTrials; ++trialNum)
  132. {
  133. if(multipleTrials)
  134. {
  135. std::cout << "--------------------------------------------" << std::endl
  136. << "Testing " << plugin.getPath().toString() << " (trial "
  137. << (trialNum+1) << ")..." << std::endl << std::endl;
  138. }
  139. test->runTests();
  140. std::cout << "success!" << std::endl;
  141. }
  142. }
  143. catch(...)
  144. {
  145. throw;
  146. }
  147. }
  148. void PothosUtilBase::selfTests(const std::string &, const std::string &path)
  149. {
  150. Pothos::ScopedInit init;
  151. const auto numTrials = this->config().getUInt("numTrials", 1);
  152. SelfTestResults results;
  153. if (path.find('*') == std::string::npos)
  154. {
  155. Poco::Glob glob("*"); //not globing, match all
  156. runPluginSelfTestsR(path.empty()? "/" : path, results, glob, numTrials);
  157. }
  158. else
  159. {
  160. Poco::Glob glob(path); //path is a glob rule
  161. runPluginSelfTestsR("/", results, glob, numTrials);
  162. }
  163. std::cout << std::endl;
  164. const size_t totalTests = results.testsPassed.size() + results.testsFailed.size();
  165. if (results.testsFailed.empty())
  166. {
  167. std::cout << "All " << totalTests << " tests passed!" << std::endl;
  168. }
  169. else
  170. {
  171. std::cout << "Failed " << results.testsFailed.size() << " out of " << totalTests << " tests" << std::endl;
  172. for (auto it = results.testsFailed.begin(); it != results.testsFailed.end(); it++)
  173. {
  174. std::cout << " FAIL: " << *it << std::endl;
  175. }
  176. std::cout << std::endl;
  177. throw Pothos::Exception("Failures occurred in self test suite.");
  178. }
  179. std::cout << std::endl;
  180. }