testing_instance.cc 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ppapi/tests/testing_instance.h"
  5. #include <stddef.h>
  6. #include <algorithm>
  7. #include <cstring>
  8. #include <iomanip>
  9. #include <sstream>
  10. #include <vector>
  11. #include "ppapi/cpp/core.h"
  12. #include "ppapi/cpp/module.h"
  13. #include "ppapi/cpp/var.h"
  14. #include "ppapi/cpp/view.h"
  15. #include "ppapi/tests/test_case.h"
  16. TestCaseFactory* TestCaseFactory::head_ = NULL;
  17. // Cookie value we use to signal "we're still working." See the comment above
  18. // the class declaration for how this works.
  19. static const char kProgressSignal[] = "...";
  20. // Returns a new heap-allocated test case for the given test, or NULL on
  21. // failure.
  22. TestingInstance::TestingInstance(PP_Instance instance)
  23. #if (defined __native_client__)
  24. : pp::Instance(instance),
  25. #else
  26. : pp::InstancePrivate(instance),
  27. #endif
  28. current_case_(NULL),
  29. executed_tests_(false),
  30. number_tests_executed_(0),
  31. nacl_mode_(false),
  32. ssl_server_port_(-1),
  33. websocket_port_(-1),
  34. remove_plugin_(true) {
  35. callback_factory_.Initialize(this);
  36. }
  37. TestingInstance::~TestingInstance() {
  38. if (current_case_)
  39. delete current_case_;
  40. }
  41. bool TestingInstance::Init(uint32_t argc,
  42. const char* argn[],
  43. const char* argv[]) {
  44. for (uint32_t i = 0; i < argc; i++) {
  45. if (std::strcmp(argn[i], "mode") == 0) {
  46. if (std::strcmp(argv[i], "nacl") == 0)
  47. nacl_mode_ = true;
  48. } else if (std::strcmp(argn[i], "protocol") == 0) {
  49. protocol_ = argv[i];
  50. } else if (std::strcmp(argn[i], "websocket_host") == 0) {
  51. websocket_host_ = argv[i];
  52. } else if (std::strcmp(argn[i], "websocket_port") == 0) {
  53. websocket_port_ = atoi(argv[i]);
  54. } else if (std::strcmp(argn[i], "ssl_server_port") == 0) {
  55. ssl_server_port_ = atoi(argv[i]);
  56. }
  57. }
  58. // Create the proper test case from the argument.
  59. for (uint32_t i = 0; i < argc; i++) {
  60. if (std::strcmp(argn[i], "testcase") == 0) {
  61. if (argv[i][0] == '\0')
  62. break;
  63. current_case_ = CaseForTestName(argv[i]);
  64. test_filter_ = argv[i];
  65. if (!current_case_)
  66. errors_.append(std::string("Unknown test case ") + argv[i]);
  67. else if (!current_case_->Init())
  68. errors_.append(" Test case could not initialize.");
  69. return true;
  70. }
  71. }
  72. // In DidChangeView, we'll dump out a list of all available tests.
  73. return true;
  74. }
  75. #if !(defined __native_client__)
  76. pp::Var TestingInstance::GetInstanceObject() {
  77. if (current_case_)
  78. return current_case_->GetTestObject();
  79. return pp::VarPrivate();
  80. }
  81. #endif
  82. void TestingInstance::HandleMessage(const pp::Var& message_data) {
  83. if (current_case_)
  84. current_case_->HandleMessage(message_data);
  85. }
  86. void TestingInstance::DidChangeView(const pp::View& view) {
  87. if (!executed_tests_) {
  88. executed_tests_ = true;
  89. pp::Module::Get()->core()->CallOnMainThread(
  90. 0,
  91. callback_factory_.NewCallback(&TestingInstance::ExecuteTests));
  92. }
  93. if (current_case_)
  94. current_case_->DidChangeView(view);
  95. }
  96. bool TestingInstance::HandleInputEvent(const pp::InputEvent& event) {
  97. if (current_case_)
  98. return current_case_->HandleInputEvent(event);
  99. return false;
  100. }
  101. void TestingInstance::EvalScript(const std::string& script) {
  102. SendTestCommand("EvalScript", script);
  103. }
  104. void TestingInstance::SetCookie(const std::string& name,
  105. const std::string& value) {
  106. SendTestCommand("SetCookie", name + "=" + value);
  107. }
  108. void TestingInstance::LogTest(const std::string& test_name,
  109. const std::string& error_message,
  110. PP_TimeTicks start_time) {
  111. current_test_name_ = test_name;
  112. // Compute the time to run the test and save it in a string for logging:
  113. PP_TimeTicks end_time(pp::Module::Get()->core()->GetTimeTicks());
  114. std::ostringstream number_stream;
  115. PP_TimeTicks elapsed_time(end_time - start_time);
  116. number_stream << std::fixed << std::setprecision(3) << elapsed_time;
  117. std::string time_string(number_stream.str());
  118. // Tell the browser we're still working.
  119. ReportProgress(kProgressSignal);
  120. number_tests_executed_++;
  121. std::string html;
  122. html.append("<div class=\"test_line\"><span class=\"test_name\">");
  123. html.append(test_name);
  124. html.append("</span> ");
  125. if (error_message.empty()) {
  126. html.append("<span class=\"pass\">PASS</span>");
  127. } else {
  128. html.append("<span class=\"fail\">FAIL</span>: <span class=\"err_msg\">");
  129. html.append(error_message);
  130. html.append("</span>");
  131. if (!errors_.empty())
  132. errors_.append(", "); // Separator for different error messages.
  133. errors_.append(test_name + " FAIL: " + error_message);
  134. }
  135. html.append(" <span class=\"time\">(");
  136. html.append(time_string);
  137. html.append("s)</span>");
  138. html.append("</div>");
  139. LogHTML(html);
  140. std::string test_time;
  141. test_time.append(test_name);
  142. test_time.append(" finished in ");
  143. test_time.append(time_string);
  144. test_time.append(" seconds.");
  145. LogTestTime(test_time);
  146. current_test_name_.clear();
  147. }
  148. void TestingInstance::AppendError(const std::string& message) {
  149. if (!errors_.empty())
  150. errors_.append(", ");
  151. errors_.append(message);
  152. }
  153. void TestingInstance::ExecuteTests(int32_t unused) {
  154. ReportProgress(kProgressSignal);
  155. // Clear the console.
  156. SendTestCommand("ClearConsole");
  157. if (!errors_.empty()) {
  158. // Catch initialization errors and output the current error string to
  159. // the console.
  160. LogError("Plugin initialization failed: " + errors_);
  161. } else if (!current_case_) {
  162. LogAvailableTests();
  163. errors_.append("FAIL: Only listed tests");
  164. } else {
  165. current_case_->RunTests(test_filter_);
  166. if (number_tests_executed_ == 0) {
  167. errors_.append("No tests executed. The test filter might be too "
  168. "restrictive: '" + test_filter_ + "'.");
  169. LogError(errors_);
  170. }
  171. if (current_case_->skipped_tests().size()) {
  172. // TODO(dmichael): Convert all TestCases to run all tests in one fixture,
  173. // and enable this check. Currently, a lot of our tests
  174. // run 1 test per fixture, which is slow.
  175. /*
  176. errors_.append("Some tests were not listed and thus were not run. Make "
  177. "sure all tests are passed in the test_case URL (even if "
  178. "they are marked DISABLED_). Forgotten tests: ");
  179. std::set<std::string>::const_iterator iter =
  180. current_case_->skipped_tests().begin();
  181. for (; iter != current_case_->skipped_tests().end(); ++iter) {
  182. errors_.append(*iter);
  183. errors_.append(" ");
  184. }
  185. LogError(errors_);
  186. */
  187. }
  188. if (current_case_->remaining_tests().size()) {
  189. errors_.append("Some listed tests were not found in the TestCase. Check "
  190. "the test names that were passed to make sure they match "
  191. "tests in the TestCase. Unknown tests: ");
  192. std::map<std::string, bool>::const_iterator iter =
  193. current_case_->remaining_tests().begin();
  194. for (; iter != current_case_->remaining_tests().end(); ++iter) {
  195. errors_.append(iter->first);
  196. errors_.append(" ");
  197. }
  198. LogError(errors_);
  199. }
  200. }
  201. if (remove_plugin_)
  202. SendTestCommand("RemovePluginWhenFinished");
  203. std::string result(errors_);
  204. if (result.empty())
  205. result = "PASS";
  206. SendTestCommand("DidExecuteTests", result);
  207. // Note, DidExecuteTests may unload the plugin. We can't really do anything
  208. // after this point.
  209. }
  210. TestCase* TestingInstance::CaseForTestName(const std::string& name) {
  211. std::string case_name = name.substr(0, name.find_first_of('_'));
  212. TestCaseFactory* iter = TestCaseFactory::head_;
  213. while (iter != NULL) {
  214. if (case_name == iter->name_)
  215. return iter->method_(this);
  216. iter = iter->next_;
  217. }
  218. return NULL;
  219. }
  220. void TestingInstance::SendTestCommand(const std::string& command) {
  221. std::string msg("TESTING_MESSAGE:");
  222. msg += command;
  223. PostMessage(pp::Var(msg));
  224. }
  225. void TestingInstance::SendTestCommand(const std::string& command,
  226. const std::string& params) {
  227. SendTestCommand(command + ":" + params);
  228. }
  229. void TestingInstance::LogAvailableTests() {
  230. // Print out a listing of all tests.
  231. std::vector<std::string> test_cases;
  232. TestCaseFactory* iter = TestCaseFactory::head_;
  233. while (iter != NULL) {
  234. test_cases.push_back(iter->name_);
  235. iter = iter->next_;
  236. }
  237. std::sort(test_cases.begin(), test_cases.end());
  238. std::string html;
  239. html.append("Available test cases: <dl>");
  240. for (size_t i = 0; i < test_cases.size(); ++i) {
  241. html.append("<dd><a href='?testcase=");
  242. html.append(test_cases[i]);
  243. if (nacl_mode_)
  244. html.append("&mode=nacl");
  245. html.append("'>");
  246. html.append(test_cases[i]);
  247. html.append("</a></dd>");
  248. }
  249. html.append("</dl>");
  250. html.append("<button onclick='RunAll()'>Run All Tests</button>");
  251. LogHTML(html);
  252. }
  253. void TestingInstance::LogError(const std::string& text) {
  254. std::string html;
  255. html.append("<span class=\"fail\">FAIL</span>: <span class=\"err_msg\">");
  256. html.append(text);
  257. html.append("</span>");
  258. LogHTML(html);
  259. }
  260. void TestingInstance::LogHTML(const std::string& html) {
  261. SendTestCommand("LogHTML", html);
  262. }
  263. void TestingInstance::ReportProgress(const std::string& progress_value) {
  264. SendTestCommand("ReportProgress", progress_value);
  265. }
  266. void TestingInstance::AddPostCondition(const std::string& script) {
  267. SendTestCommand("AddPostCondition", script);
  268. }
  269. void TestingInstance::LogTestTime(const std::string& test_time) {
  270. SendTestCommand("LogTestTime", test_time);
  271. }
  272. class Module : public pp::Module {
  273. public:
  274. Module() : pp::Module() {}
  275. virtual ~Module() {}
  276. virtual pp::Instance* CreateInstance(PP_Instance instance) {
  277. return new TestingInstance(instance);
  278. }
  279. };
  280. namespace pp {
  281. #if defined(WIN32)
  282. __declspec(dllexport)
  283. #else
  284. __attribute__((visibility("default")))
  285. #endif
  286. Module* CreateModule() {
  287. return new ::Module();
  288. }
  289. } // namespace pp