tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }