diff options
Diffstat (limited to 'js/src/jsapi-tests/tests.cpp')
-rw-r--r-- | js/src/jsapi-tests/tests.cpp | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/js/src/jsapi-tests/tests.cpp b/js/src/jsapi-tests/tests.cpp new file mode 100644 index 0000000000..1dc36e7bc3 --- /dev/null +++ b/js/src/jsapi-tests/tests.cpp @@ -0,0 +1,298 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jsapi-tests/tests.h" + +#include "mozilla/Utf8.h" // mozilla::Utf8Unit + +#include <stdio.h> + +#include "js/ArrayBuffer.h" +#include "js/CompilationAndEvaluation.h" // JS::Evaluate +#include "js/GlobalObject.h" // JS_NewGlobalObject +#include "js/Initialization.h" +#include "js/Prefs.h" +#include "js/PropertyAndElement.h" // JS_DefineFunction +#include "js/RootingAPI.h" +#include "js/SourceText.h" // JS::Source{Ownership,Text} + +JSAPIRuntimeTest* JSAPIRuntimeTest::list; +JSAPIFrontendTest* JSAPIFrontendTest::list; + +bool JSAPIRuntimeTest::init(JSContext* maybeReusableContext) { + if (maybeReusableContext && reuseGlobal) { + cx = maybeReusableContext; + global.init(cx, JS::CurrentGlobalOrNull(cx)); + return init(); + } + + MaybeFreeContext(maybeReusableContext); + + cx = createContext(); + if (!cx) { + return false; + } + + js::UseInternalJobQueues(cx); + + if (!JS::InitSelfHostedCode(cx)) { + return false; + } + global.init(cx); + createGlobal(); + if (!global) { + return false; + } + JS::EnterRealm(cx, global); + return init(); +} + +JSContext* JSAPIRuntimeTest::maybeForgetContext() { + if (!reuseGlobal) { + return nullptr; + } + + JSContext* reusableCx = cx; + global.reset(); + cx = nullptr; + return reusableCx; +} + +/* static */ +void JSAPIRuntimeTest::MaybeFreeContext(JSContext* maybeCx) { + if (maybeCx) { + JS::LeaveRealm(maybeCx, nullptr); + JS_DestroyContext(maybeCx); + } +} + +void JSAPIRuntimeTest::uninit() { + global.reset(); + MaybeFreeContext(cx); + cx = nullptr; + msgs.clear(); +} + +bool JSAPIRuntimeTest::exec(const char* utf8, const char* filename, + int lineno) { + JS::CompileOptions opts(cx); + opts.setFileAndLine(filename, lineno); + + JS::SourceText<mozilla::Utf8Unit> srcBuf; + JS::RootedValue v(cx); + return (srcBuf.init(cx, utf8, strlen(utf8), JS::SourceOwnership::Borrowed) && + JS::Evaluate(cx, opts, srcBuf, &v)) || + fail(JSAPITestString(utf8), filename, lineno); +} + +bool JSAPIRuntimeTest::execDontReport(const char* utf8, const char* filename, + int lineno) { + JS::CompileOptions opts(cx); + opts.setFileAndLine(filename, lineno); + + JS::SourceText<mozilla::Utf8Unit> srcBuf; + JS::RootedValue v(cx); + return srcBuf.init(cx, utf8, strlen(utf8), JS::SourceOwnership::Borrowed) && + JS::Evaluate(cx, opts, srcBuf, &v); +} + +bool JSAPIRuntimeTest::evaluate(const char* utf8, const char* filename, + int lineno, JS::MutableHandleValue vp) { + JS::CompileOptions opts(cx); + opts.setFileAndLine(filename, lineno); + + JS::SourceText<mozilla::Utf8Unit> srcBuf; + return (srcBuf.init(cx, utf8, strlen(utf8), JS::SourceOwnership::Borrowed) && + JS::Evaluate(cx, opts, srcBuf, vp)) || + fail(JSAPITestString(utf8), filename, lineno); +} + +bool JSAPIRuntimeTest::definePrint() { + return JS_DefineFunction(cx, global, "print", (JSNative)print, 0, 0); +} + +JSObject* JSAPIRuntimeTest::createGlobal(JSPrincipals* principals) { + /* Create the global object. */ + JS::RootedObject newGlobal(cx); + JS::RealmOptions options; + options.creationOptions().setSharedMemoryAndAtomicsEnabled(true); + newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, + JS::FireOnNewGlobalHook, options); + if (!newGlobal) { + return nullptr; + } + + global = newGlobal; + return newGlobal; +} + +struct CommandOptions { + bool list = false; + bool frontendOnly = false; + bool help = false; + const char* filter = nullptr; +}; + +void parseArgs(int argc, char* argv[], CommandOptions& options) { + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + options.help = true; + continue; + } + + if (strcmp(argv[i], "--list") == 0) { + options.list = true; + continue; + } + + if (strcmp(argv[i], "--frontend-only") == 0) { + options.frontendOnly = true; + continue; + } + + if (!options.filter) { + options.filter = argv[i]; + continue; + } + + printf("error: Unrecognized option: %s\n", argv[i]); + options.help = true; + } +} + +template <typename TestT> +void PrintTests(TestT* list) { + for (TestT* test = list; test; test = test->next) { + printf("%s\n", test->name()); + } +} + +template <typename TestT, typename InitF, typename RunF, typename BeforeUninitF> +void RunTests(int& total, int& failures, CommandOptions& options, TestT* list, + InitF init, RunF run, BeforeUninitF beforeUninit) { + for (TestT* test = list; test; test = test->next) { + const char* name = test->name(); + if (options.filter && strstr(name, options.filter) == nullptr) { + continue; + } + + total += 1; + + printf("%s\n", name); + + // Make sure the test name is printed before we enter the test that can + // crash on failure. + fflush(stdout); + + if (!init(test)) { + printf("TEST-UNEXPECTED-FAIL | %s | Failed to initialize.\n", name); + failures++; + test->uninit(); + continue; + } + + if (run(test)) { + printf("TEST-PASS | %s | ok\n", name); + } else { + JSAPITestString messages = test->messages(); + printf("%s | %s | %.*s\n", + (test->knownFail ? "TEST-KNOWN-FAIL" : "TEST-UNEXPECTED-FAIL"), + name, (int)messages.length(), messages.begin()); + if (!test->knownFail) { + failures++; + } + } + + beforeUninit(test); + + test->uninit(); + } +} + +int main(int argc, char* argv[]) { + int total = 0; + int failures = 0; + CommandOptions options; + parseArgs(argc, argv, options); + + if (options.help) { + printf("Usage: jsapi-tests [OPTIONS] [FILTER]\n"); + printf("\n"); + printf("Options:\n"); + printf(" -h, --help Display this message\n"); + printf(" --list List all tests\n"); + printf( + " --frontend-only Run tests for frontend-only APIs, with " + "light-weight entry point\n"); + return 0; + } + + // Override prefs for jsapi-tests. + JS::Prefs::setAtStartup_weakrefs(true); + JS::Prefs::setAtStartup_experimental_weakrefs_expose_cleanupSome(true); +#ifdef NIGHTLY_BUILD + JS::Prefs::setAtStartup_experimental_symbols_as_weakmap_keys(true); +#endif + + if (!options.frontendOnly) { + if (!JS_Init()) { + printf("TEST-UNEXPECTED-FAIL | jsapi-tests | JS_Init() failed.\n"); + return 1; + } + } else { + if (!JS_FrontendOnlyInit()) { + printf("TEST-UNEXPECTED-FAIL | jsapi-tests | JS_Init() failed.\n"); + return 1; + } + } + + if (options.list) { + PrintTests(JSAPIRuntimeTest::list); + PrintTests(JSAPIFrontendTest::list); + return 0; + } + + // Reinitializing the global for every test is quite slow, due to having to + // recompile all self-hosted builtins. Allow tests to opt-in to reusing the + // global. + JSContext* maybeReusedContext = nullptr; + + if (!options.frontendOnly) { + RunTests( + total, failures, options, JSAPIRuntimeTest::list, + [&maybeReusedContext](JSAPIRuntimeTest* test) { + return test->init(maybeReusedContext); + }, + [](JSAPIRuntimeTest* test) { return test->run(test->global); }, + [&maybeReusedContext](JSAPIRuntimeTest* test) { + // Return a non-nullptr pointer if the context & global can safely be + // reused for the next test. + maybeReusedContext = test->maybeForgetContext(); + }); + } + RunTests( + total, failures, options, JSAPIFrontendTest::list, + [](JSAPIFrontendTest* test) { return test->init(); }, + [](JSAPIFrontendTest* test) { return test->run(); }, + [](JSAPIFrontendTest* test) {}); + + if (!options.frontendOnly) { + JSAPIRuntimeTest::MaybeFreeContext(maybeReusedContext); + + MOZ_RELEASE_ASSERT(!JSRuntime::hasLiveRuntimes()); + JS_ShutDown(); + } else { + JS_FrontendOnlyShutDown(); + } + + if (failures) { + printf("\n%d unexpected failure%s.\n", failures, + (failures == 1 ? "" : "s")); + return 1; + } + printf("\nPassed: ran %d tests.\n", total); + return 0; +} |