summaryrefslogtreecommitdiffstats
path: root/js/src/jsapi-tests/tests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jsapi-tests/tests.cpp')
-rw-r--r--js/src/jsapi-tests/tests.cpp292
1 files changed, 292 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..d2babb1c8e
--- /dev/null
+++ b/js/src/jsapi-tests/tests.cpp
@@ -0,0 +1,292 @@
+/* -*- 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/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()
+ .setWeakRefsEnabled(JS::WeakRefSpecifier::EnabledWithCleanupSome)
+ .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;
+ }
+
+ 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;
+}