tests.cpp (8341B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "jsapi-tests/tests.h" 8 9 #include "mozilla/Utf8.h" // mozilla::Utf8Unit 10 11 #include <stdio.h> 12 13 #include "js/ArrayBuffer.h" 14 #include "js/CompilationAndEvaluation.h" // JS::Evaluate 15 #include "js/GlobalObject.h" // JS_NewGlobalObject 16 #include "js/Initialization.h" 17 #include "js/Prefs.h" 18 #include "js/PropertyAndElement.h" // JS_DefineFunction 19 #include "js/RootingAPI.h" 20 #include "js/SourceText.h" // JS::Source{Ownership,Text} 21 22 JSAPITestList<JSAPIRuntimeTest> JSAPIRuntimeTest::list; 23 JSAPITestList<JSAPIFrontendTest> JSAPIFrontendTest::list; 24 25 bool JSAPIRuntimeTest::init(JSContext* maybeReusableContext) { 26 if (maybeReusableContext && reuseGlobal) { 27 cx = maybeReusableContext; 28 global.init(cx, JS::CurrentGlobalOrNull(cx)); 29 return init(); 30 } 31 32 MaybeFreeContext(maybeReusableContext); 33 34 cx = createContext(); 35 if (!cx) { 36 return false; 37 } 38 39 js::UseInternalJobQueues(cx); 40 41 if (!JS::InitSelfHostedCode(cx)) { 42 return false; 43 } 44 global.init(cx); 45 createGlobal(); 46 if (!global) { 47 return false; 48 } 49 JS::EnterRealm(cx, global); 50 return init(); 51 } 52 53 JSContext* JSAPIRuntimeTest::maybeForgetContext() { 54 if (!reuseGlobal) { 55 return nullptr; 56 } 57 58 JSContext* reusableCx = cx; 59 global.reset(); 60 cx = nullptr; 61 return reusableCx; 62 } 63 64 /* static */ 65 void JSAPIRuntimeTest::MaybeFreeContext(JSContext* maybeCx) { 66 if (maybeCx) { 67 JS::LeaveRealm(maybeCx, nullptr); 68 JS_DestroyContext(maybeCx); 69 } 70 } 71 72 void JSAPIRuntimeTest::uninit() { 73 global.reset(); 74 MaybeFreeContext(cx); 75 cx = nullptr; 76 msgs.clear(); 77 } 78 79 bool JSAPIRuntimeTest::exec(const char* utf8, const char* filename, 80 int lineno) { 81 JS::CompileOptions opts(cx); 82 opts.setFileAndLine(filename, lineno); 83 84 JS::SourceText<mozilla::Utf8Unit> srcBuf; 85 JS::RootedValue v(cx); 86 return (srcBuf.init(cx, utf8, strlen(utf8), JS::SourceOwnership::Borrowed) && 87 JS::Evaluate(cx, opts, srcBuf, &v)) || 88 fail(JSAPITestString(utf8), filename, lineno); 89 } 90 91 bool JSAPIRuntimeTest::execDontReport(const char* utf8, const char* filename, 92 int lineno) { 93 JS::CompileOptions opts(cx); 94 opts.setFileAndLine(filename, lineno); 95 96 JS::SourceText<mozilla::Utf8Unit> srcBuf; 97 JS::RootedValue v(cx); 98 return srcBuf.init(cx, utf8, strlen(utf8), JS::SourceOwnership::Borrowed) && 99 JS::Evaluate(cx, opts, srcBuf, &v); 100 } 101 102 bool JSAPIRuntimeTest::evaluate(const char* utf8, const char* filename, 103 int lineno, JS::MutableHandleValue vp) { 104 JS::CompileOptions opts(cx); 105 opts.setFileAndLine(filename, lineno); 106 107 JS::SourceText<mozilla::Utf8Unit> srcBuf; 108 return (srcBuf.init(cx, utf8, strlen(utf8), JS::SourceOwnership::Borrowed) && 109 JS::Evaluate(cx, opts, srcBuf, vp)) || 110 fail(JSAPITestString(utf8), filename, lineno); 111 } 112 113 bool JSAPIRuntimeTest::definePrint() { 114 return JS_DefineFunction(cx, global, "print", (JSNative)print, 0, 0); 115 } 116 117 JSObject* JSAPIRuntimeTest::createGlobal(JSPrincipals* principals) { 118 /* Create the global object. */ 119 JS::RootedObject newGlobal(cx); 120 JS::RealmOptions options; 121 options.creationOptions().setSharedMemoryAndAtomicsEnabled(true); 122 newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, 123 JS::FireOnNewGlobalHook, options); 124 if (!newGlobal) { 125 return nullptr; 126 } 127 128 global = newGlobal; 129 return newGlobal; 130 } 131 132 struct CommandOptions { 133 bool list = false; 134 bool frontendOnly = false; 135 bool help = false; 136 const char* filter = nullptr; 137 }; 138 139 void parseArgs(int argc, char* argv[], CommandOptions& options) { 140 for (int i = 1; i < argc; i++) { 141 if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { 142 options.help = true; 143 continue; 144 } 145 146 if (strcmp(argv[i], "--list") == 0) { 147 options.list = true; 148 continue; 149 } 150 151 if (strcmp(argv[i], "--frontend-only") == 0) { 152 options.frontendOnly = true; 153 continue; 154 } 155 156 if (!options.filter) { 157 options.filter = argv[i]; 158 continue; 159 } 160 161 printf("error: Unrecognized option: %s\n", argv[i]); 162 options.help = true; 163 } 164 } 165 166 template <typename TestT> 167 void PrintTests(JSAPITestList<TestT> list) { 168 for (TestT* test = list.getFirst(); test; test = test->next) { 169 printf("%s\n", test->name()); 170 } 171 } 172 173 template <typename TestT, typename InitF, typename RunF, typename BeforeUninitF> 174 void RunTests(int& total, int& failures, CommandOptions& options, 175 JSAPITestList<TestT> list, InitF init, RunF run, 176 BeforeUninitF beforeUninit) { 177 for (TestT* test = list.getFirst(); test; test = test->next) { 178 const char* name = test->name(); 179 if (options.filter && strstr(name, options.filter) == nullptr) { 180 continue; 181 } 182 183 total += 1; 184 185 printf("%s\n", name); 186 187 // Make sure the test name is printed before we enter the test that can 188 // crash on failure. 189 fflush(stdout); 190 191 if (!init(test)) { 192 printf("TEST-UNEXPECTED-FAIL | %s | Failed to initialize.\n", name); 193 failures++; 194 test->uninit(); 195 continue; 196 } 197 198 if (run(test)) { 199 printf("TEST-PASS | %s | ok\n", name); 200 } else { 201 JSAPITestString messages = test->messages(); 202 printf("%s | %s | %.*s\n", 203 (test->knownFail ? "TEST-KNOWN-FAIL" : "TEST-UNEXPECTED-FAIL"), 204 name, (int)messages.length(), messages.begin()); 205 if (!test->knownFail) { 206 failures++; 207 } 208 } 209 210 beforeUninit(test); 211 212 test->uninit(); 213 } 214 } 215 216 int main(int argc, char* argv[]) { 217 int total = 0; 218 int failures = 0; 219 CommandOptions options; 220 parseArgs(argc, argv, options); 221 222 if (options.help) { 223 printf("Usage: jsapi-tests [OPTIONS] [FILTER]\n"); 224 printf("\n"); 225 printf("Options:\n"); 226 printf(" -h, --help Display this message\n"); 227 printf(" --list List all tests\n"); 228 printf( 229 " --frontend-only Run tests for frontend-only APIs, with " 230 "light-weight entry point\n"); 231 return 0; 232 } 233 234 // Override prefs for jsapi-tests. 235 JS::Prefs::setAtStartup_experimental_weakrefs_expose_cleanupSome(true); 236 JS::Prefs::setAtStartup_experimental_symbols_as_weakmap_keys(true); 237 238 if (!options.frontendOnly) { 239 if (!JS_Init()) { 240 printf("TEST-UNEXPECTED-FAIL | jsapi-tests | JS_Init() failed.\n"); 241 return 1; 242 } 243 } else { 244 if (!JS_FrontendOnlyInit()) { 245 printf("TEST-UNEXPECTED-FAIL | jsapi-tests | JS_Init() failed.\n"); 246 return 1; 247 } 248 } 249 250 if (options.list) { 251 PrintTests(JSAPIRuntimeTest::list); 252 PrintTests(JSAPIFrontendTest::list); 253 return 0; 254 } 255 256 // Reinitializing the global for every test is quite slow, due to having to 257 // recompile all self-hosted builtins. Allow tests to opt-in to reusing the 258 // global. 259 JSContext* maybeReusedContext = nullptr; 260 261 if (!options.frontendOnly) { 262 RunTests( 263 total, failures, options, JSAPIRuntimeTest::list, 264 [&maybeReusedContext](JSAPIRuntimeTest* test) { 265 return test->init(maybeReusedContext); 266 }, 267 [](JSAPIRuntimeTest* test) { return test->run(test->global); }, 268 [&maybeReusedContext](JSAPIRuntimeTest* test) { 269 // Return a non-nullptr pointer if the context & global can safely be 270 // reused for the next test. 271 maybeReusedContext = test->maybeForgetContext(); 272 }); 273 } 274 RunTests( 275 total, failures, options, JSAPIFrontendTest::list, 276 [](JSAPIFrontendTest* test) { return test->init(); }, 277 [](JSAPIFrontendTest* test) { return test->run(); }, 278 [](JSAPIFrontendTest* test) {}); 279 280 if (!options.frontendOnly) { 281 JSAPIRuntimeTest::MaybeFreeContext(maybeReusedContext); 282 283 MOZ_RELEASE_ASSERT(!JSRuntime::hasLiveRuntimes()); 284 JS_ShutDown(); 285 } else { 286 JS_FrontendOnlyShutDown(); 287 } 288 289 if (failures) { 290 printf("\n%d unexpected failure%s.\n", failures, 291 (failures == 1 ? "" : "s")); 292 return 1; 293 } 294 printf("\nPassed: ran %d tests.\n", total); 295 return 0; 296 }