tor-browser

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

DevTools.h (6107B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #ifndef mozilla_devtools_gtest_DevTools__
      7 #define mozilla_devtools_gtest_DevTools__
      8 
      9 #include <utility>
     10 
     11 #include "CoreDump.pb.h"
     12 #include "gmock/gmock.h"
     13 #include "gtest/gtest.h"
     14 #include "js/Principals.h"
     15 #include "js/UbiNode.h"
     16 #include "js/UniquePtr.h"
     17 #include "jsapi.h"
     18 #include "jspubtd.h"
     19 #include "mozilla/CycleCollectedJSContext.h"
     20 #include "mozilla/devtools/HeapSnapshot.h"
     21 #include "mozilla/dom/ChromeUtils.h"
     22 #include "nsCRTGlue.h"
     23 
     24 using namespace mozilla;
     25 using namespace mozilla::devtools;
     26 using namespace mozilla::dom;
     27 using namespace testing;
     28 
     29 // GTest fixture class that all of our tests derive from.
     30 struct DevTools : public ::testing::Test {
     31  bool _initialized;
     32  JSContext* cx;
     33  JS::Compartment* compartment;
     34  JS::Zone* zone;
     35  JS::PersistentRooted<JSObject*> global;
     36 
     37  DevTools() : _initialized(false), cx(nullptr) {}
     38 
     39  virtual void SetUp() {
     40    MOZ_ASSERT(!_initialized);
     41 
     42    cx = getContext();
     43    if (!cx) return;
     44 
     45    global.init(cx, createGlobal());
     46    if (!global) return;
     47    JS::EnterRealm(cx, global);
     48 
     49    compartment = js::GetContextCompartment(cx);
     50    zone = js::GetContextZone(cx);
     51 
     52    _initialized = true;
     53  }
     54 
     55  JSContext* getContext() { return CycleCollectedJSContext::Get()->Context(); }
     56 
     57  static void reportError(JSContext* cx, const char* message,
     58                          JSErrorReport* report) {
     59    fprintf(stderr, "%s:%u:%s\n",
     60            report->filename ? report->filename.c_str() : "<no filename>",
     61            (unsigned int)report->lineno, message);
     62  }
     63 
     64  static const JSClass* getGlobalClass() {
     65    static const JSClass globalClass = {"global", JSCLASS_GLOBAL_FLAGS,
     66                                        &JS::DefaultGlobalClassOps};
     67    return &globalClass;
     68  }
     69 
     70  JSObject* createGlobal() {
     71    /* Create the global object. */
     72    JS::RealmOptions options;
     73    // dummy
     74    options.behaviors().setReduceTimerPrecisionCallerType(
     75        JS::RTPCallerTypeToken{0});
     76    return JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
     77                              JS::FireOnNewGlobalHook, options);
     78  }
     79 
     80  virtual void TearDown() {
     81    _initialized = false;
     82 
     83    if (global) {
     84      JS::LeaveRealm(cx, nullptr);
     85      global = nullptr;
     86    }
     87  }
     88 };
     89 
     90 // Helper to define a test and ensure that the fixture is initialized properly.
     91 #define DEF_TEST(name, body)   \
     92  TEST_F(DevTools, name) {     \
     93    ASSERT_TRUE(_initialized); \
     94    body                       \
     95  }
     96 
     97 // Fake JS::ubi::Node implementation
     98 class MOZ_STACK_CLASS FakeNode {
     99 public:
    100  JS::ubi::EdgeVector edges;
    101  JS::Compartment* compartment;
    102  JS::Zone* zone;
    103  size_t size;
    104 
    105  explicit FakeNode() : compartment(nullptr), zone(nullptr), size(1) {}
    106 };
    107 
    108 namespace JS {
    109 namespace ubi {
    110 
    111 template <>
    112 class Concrete<FakeNode> : public Base {
    113  const char16_t* typeName() const override { return concreteTypeName; }
    114 
    115  js::UniquePtr<EdgeRange> edges(JSContext*, bool) const override {
    116    return js::UniquePtr<EdgeRange>(js_new<PreComputedEdgeRange>(get().edges));
    117  }
    118 
    119  Size size(mozilla::MallocSizeOf) const override { return get().size; }
    120 
    121  JS::Zone* zone() const override { return get().zone; }
    122 
    123  JS::Compartment* compartment() const override { return get().compartment; }
    124 
    125 protected:
    126  explicit Concrete(FakeNode* ptr) : Base(ptr) {}
    127  FakeNode& get() const { return *static_cast<FakeNode*>(ptr); }
    128 
    129 public:
    130  static const char16_t concreteTypeName[];
    131  static void construct(void* storage, FakeNode* ptr) {
    132    new (storage) Concrete(ptr);
    133  }
    134 };
    135 
    136 }  // namespace ubi
    137 }  // namespace JS
    138 
    139 inline void AddEdge(FakeNode& node, FakeNode& referent,
    140                    const char16_t* edgeName = nullptr) {
    141  char16_t* ownedEdgeName = nullptr;
    142  if (edgeName) {
    143    ownedEdgeName = NS_xstrdup(edgeName);
    144  }
    145 
    146  JS::ubi::Edge edge(ownedEdgeName, &referent);
    147  ASSERT_TRUE(node.edges.append(std::move(edge)));
    148 }
    149 
    150 // Custom GMock Matchers
    151 
    152 // Use the testing namespace to avoid static analysis failures in the gmock
    153 // matcher classes that get generated from MATCHER_P macros.
    154 namespace testing {
    155 
    156 // Ensure that given node has the expected number of edges.
    157 MATCHER_P2(EdgesLength, cx, expectedLength, "") {
    158  auto edges = arg.edges(cx);
    159  if (!edges) return false;
    160 
    161  int actualLength = 0;
    162  for (; !edges->empty(); edges->popFront()) actualLength++;
    163 
    164  return Matcher<int>(Eq(expectedLength))
    165      .MatchAndExplain(actualLength, result_listener);
    166 }
    167 
    168 // Get the nth edge and match it with the given matcher.
    169 MATCHER_P3(Edge, cx, n, matcher, "") {
    170  auto edges = arg.edges(cx);
    171  if (!edges) return false;
    172 
    173  int i = 0;
    174  for (; !edges->empty(); edges->popFront()) {
    175    if (i == n) {
    176      return Matcher<const JS::ubi::Edge&>(matcher).MatchAndExplain(
    177          edges->front(), result_listener);
    178    }
    179 
    180    i++;
    181  }
    182 
    183  return false;
    184 }
    185 
    186 // Ensures that two char16_t* strings are equal.
    187 MATCHER_P(UTF16StrEq, str, "") { return NS_strcmp(arg, str) == 0; }
    188 
    189 MATCHER_P(UniqueUTF16StrEq, str, "") { return NS_strcmp(arg.get(), str) == 0; }
    190 
    191 MATCHER(UniqueIsNull, "") { return arg.get() == nullptr; }
    192 
    193 // Matches an edge whose referent is the node with the given id.
    194 MATCHER_P(EdgeTo, id, "") {
    195  return Matcher<const DeserializedEdge&>(
    196             Field(&DeserializedEdge::referent, id))
    197      .MatchAndExplain(arg, result_listener);
    198 }
    199 
    200 }  // namespace testing
    201 
    202 // A mock `Writer` class to be used with testing `WriteHeapGraph`.
    203 class MockWriter : public CoreDumpWriter {
    204 public:
    205  virtual ~MockWriter() override = default;
    206  MOCK_METHOD2(writeNode,
    207               bool(const JS::ubi::Node&, CoreDumpWriter::EdgePolicy));
    208  MOCK_METHOD1(writeMetadata, bool(uint64_t));
    209 };
    210 
    211 inline void ExpectWriteNode(MockWriter& writer, FakeNode& node) {
    212  EXPECT_CALL(writer, writeNode(Eq(JS::ubi::Node(&node)), _))
    213      .Times(1)
    214      .WillOnce(Return(true));
    215 }
    216 
    217 #endif  // mozilla_devtools_gtest_DevTools__