test-utils.h 10.2 KB
Newer Older
1 2 3 4
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
#ifndef V8_UNITTESTS_TEST_UTILS_H_
#define V8_UNITTESTS_TEST_UTILS_H_
7

8
#include <memory>
9 10
#include <vector>

11
#include "include/libplatform/libplatform.h"
12 13 14 15
#include "include/v8-array-buffer.h"
#include "include/v8-context.h"
#include "include/v8-local-handle.h"
#include "include/v8-primitive.h"
16
#include "src/api/api-inl.h"
17
#include "src/base/macros.h"
18
#include "src/base/utils/random-number-generator.h"
19
#include "src/handles/handles.h"
20 21
#include "src/objects/objects-inl.h"
#include "src/objects/objects.h"
22 23
#include "src/zone/accounting-allocator.h"
#include "src/zone/zone.h"
24 25 26 27
#include "testing/gtest-support.h"

namespace v8 {

28 29
class ArrayBufferAllocator;

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
template <typename TMixin>
class WithDefaultPlatformMixin : public TMixin {
 public:
  WithDefaultPlatformMixin() {
    platform_ = v8::platform::NewDefaultPlatform(
        0, v8::platform::IdleTaskSupport::kEnabled);
    CHECK_NOT_NULL(platform_.get());
    v8::V8::InitializePlatform(platform_.get());
#ifdef V8_SANDBOX
    CHECK(v8::V8::InitializeSandbox());
#endif  // V8_SANDBOX
    v8::V8::Initialize();
  }

  ~WithDefaultPlatformMixin() {
    CHECK_NOT_NULL(platform_.get());
    v8::V8::Dispose();
    v8::V8::DisposePlatform();
  }

  v8::Platform* platform() const { return platform_.get(); }

 private:
  std::unique_ptr<v8::Platform> platform_;
};

56
using CounterMap = std::map<std::string, int>;
57

58 59
enum CountersMode { kNoCounters, kEnableCounters };

60 61
// RAII-like Isolate instance wrapper.
class IsolateWrapper final {
62
 public:
63
  explicit IsolateWrapper(CountersMode counters_mode);
64
  ~IsolateWrapper();
65 66
  IsolateWrapper(const IsolateWrapper&) = delete;
  IsolateWrapper& operator=(const IsolateWrapper&) = delete;
67

68
  v8::Isolate* isolate() const { return isolate_; }
69

70
 private:
71 72
  std::unique_ptr<v8::ArrayBuffer::Allocator> array_buffer_allocator_;
  std::unique_ptr<CounterMap> counter_map_;
73 74 75 76 77 78
  v8::Isolate* isolate_;
};

//
// A set of mixins from which the test fixtures will be constructed.
//
79
template <typename TMixin, CountersMode kCountersMode = kNoCounters>
80
class WithIsolateMixin : public TMixin {
81
 public:
82
  WithIsolateMixin() : isolate_wrapper_(kCountersMode) {}
83 84 85 86 87 88 89

  v8::Isolate* v8_isolate() const { return isolate_wrapper_.isolate(); }

 private:
  v8::IsolateWrapper isolate_wrapper_;
};

90 91 92 93
template <typename TMixin>
class WithIsolateScopeMixin : public TMixin {
 public:
  WithIsolateScopeMixin()
94
      : isolate_scope_(this->v8_isolate()), handle_scope_(this->v8_isolate()) {}
95 96
  WithIsolateScopeMixin(const WithIsolateScopeMixin&) = delete;
  WithIsolateScopeMixin& operator=(const WithIsolateScopeMixin&) = delete;
97

98
  v8::Isolate* isolate() const { return this->v8_isolate(); }
99 100

  v8::internal::Isolate* i_isolate() const {
101
    return reinterpret_cast<v8::internal::Isolate*>(this->v8_isolate());
102
  }
103

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
  Local<Value> RunJS(const char* source) {
    return RunJS(
        v8::String::NewFromUtf8(this->v8_isolate(), source).ToLocalChecked());
  }

  Local<Value> RunJS(v8::String::ExternalOneByteStringResource* source) {
    return RunJS(v8::String::NewExternalOneByte(this->v8_isolate(), source)
                     .ToLocalChecked());
  }

  void CollectGarbage(i::AllocationSpace space) {
    i_isolate()->heap()->CollectGarbage(space,
                                        i::GarbageCollectionReason::kTesting);
  }

  void CollectAllGarbage() {
    i_isolate()->heap()->CollectAllGarbage(
        i::Heap::kNoGCFlags, i::GarbageCollectionReason::kTesting);
  }

  void CollectAllAvailableGarbage() {
    i_isolate()->heap()->CollectAllAvailableGarbage(
        i::GarbageCollectionReason::kTesting);
  }

  void PreciseCollectAllGarbage() {
    i_isolate()->heap()->PreciseCollectAllGarbage(
        i::Heap::kNoGCFlags, i::GarbageCollectionReason::kTesting);
  }

  v8::Local<v8::String> NewString(const char* string) {
    return v8::String::NewFromUtf8(this->v8_isolate(), string).ToLocalChecked();
  }

138
 private:
139 140 141 142 143 144 145
  Local<Value> RunJS(Local<String> source) {
    auto context = this->v8_isolate()->GetCurrentContext();
    Local<Script> script =
        v8::Script::Compile(context, source).ToLocalChecked();
    return script->Run(context).ToLocalChecked();
  }

146 147
  v8::Isolate::Scope isolate_scope_;
  v8::HandleScope handle_scope_;
148 149
};

150 151
template <typename TMixin>
class WithContextMixin : public TMixin {
152
 public:
153
  WithContextMixin()
154
      : context_(Context::New(this->v8_isolate())), context_scope_(context_) {}
155 156
  WithContextMixin(const WithContextMixin&) = delete;
  WithContextMixin& operator=(const WithContextMixin&) = delete;
157

158 159 160
  const Local<Context>& context() const { return v8_context(); }
  const Local<Context>& v8_context() const { return context_; }

161 162 163
  void SetGlobalProperty(const char* name, v8::Local<v8::Value> value) {
    CHECK(v8_context()
              ->Global()
164
              ->Set(v8_context(), TMixin::NewString(name), value)
165 166 167
              .FromJust());
  }

168
 private:
169
  v8::Local<v8::Context> context_;
170
  v8::Context::Scope context_scope_;
171 172
};

173 174 175 176
using TestWithPlatform =       //
    WithDefaultPlatformMixin<  //
        ::testing::Test>;

177 178
// Use v8::internal::TestWithIsolate if you are testing internals,
// aka. directly work with Handles.
179 180 181 182 183
using TestWithIsolate =                //
    WithIsolateScopeMixin<             //
        WithIsolateMixin<              //
            WithDefaultPlatformMixin<  //
                ::testing::Test>>>;
184 185 186

// Use v8::internal::TestWithNativeContext if you are testing internals,
// aka. directly work with Handles.
187 188 189 190 191 192
using TestWithContext =                    //
    WithContextMixin<                      //
        WithIsolateScopeMixin<             //
            WithIsolateMixin<              //
                WithDefaultPlatformMixin<  //
                    ::testing::Test>>>>;
193

194 195 196 197 198
namespace internal {

// Forward declarations.
class Factory;

199 200
template <typename TMixin>
class WithInternalIsolateMixin : public TMixin {
201
 public:
202
  WithInternalIsolateMixin() = default;
203 204
  WithInternalIsolateMixin(const WithInternalIsolateMixin&) = delete;
  WithInternalIsolateMixin& operator=(const WithInternalIsolateMixin&) = delete;
205 206 207 208

  Factory* factory() const { return isolate()->factory(); }
  Isolate* isolate() const { return TMixin::i_isolate(); }

209 210 211
  Handle<NativeContext> native_context() const {
    return isolate()->native_context();
  }
212

213 214
  template <typename T = Object>
  Handle<T> RunJS(const char* source) {
Marja Hölttä's avatar
Marja Hölttä committed
215
    return Handle<T>::cast(RunJSInternal(source));
216
  }
217 218 219 220 221

  Handle<Object> RunJSInternal(const char* source) {
    return Utils::OpenHandle(*TMixin::RunJS(source));
  }

222 223 224 225
  template <typename T = Object>
  Handle<T> RunJS(::v8::String::ExternalOneByteStringResource* source) {
    return Handle<T>::cast(RunJSInternal(source));
  }
226

227 228 229 230
  Handle<Object> RunJSInternal(
      ::v8::String::ExternalOneByteStringResource* source) {
    return Utils::OpenHandle(*TMixin::RunJS(source));
  }
231

232 233 234
  base::RandomNumberGenerator* random_number_generator() const {
    return isolate()->random_number_generator();
  }
235 236
};

237 238
template <typename TMixin>
class WithZoneMixin : public TMixin {
239
 public:
240 241
  explicit WithZoneMixin(bool support_zone_compression = false)
      : zone_(&allocator_, ZONE_NAME, support_zone_compression) {}
242 243
  WithZoneMixin(const WithZoneMixin&) = delete;
  WithZoneMixin& operator=(const WithZoneMixin&) = delete;
244 245 246 247

  Zone* zone() { return &zone_; }

 private:
248
  v8::internal::AccountingAllocator allocator_;
249 250 251
  Zone zone_;
};

252 253 254 255 256
using TestWithIsolate =                    //
    WithInternalIsolateMixin<              //
        WithIsolateScopeMixin<             //
            WithIsolateMixin<              //
                WithDefaultPlatformMixin<  //
257 258
                    ::testing::Test>>>>;

259 260
using TestWithZone = WithZoneMixin<WithDefaultPlatformMixin<  //
    ::testing::Test>>;
261

262 263 264 265 266 267
using TestWithIsolateAndZone =                 //
    WithZoneMixin<                             //
        WithInternalIsolateMixin<              //
            WithIsolateScopeMixin<             //
                WithIsolateMixin<              //
                    WithDefaultPlatformMixin<  //
268
                        ::testing::Test>>>>>;
269

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
using TestWithNativeContext =                  //
    WithInternalIsolateMixin<                  //
        WithContextMixin<                      //
            WithIsolateScopeMixin<             //
                WithIsolateMixin<              //
                    WithDefaultPlatformMixin<  //
                        ::testing::Test>>>>>;

using TestWithNativeContextAndCounters =       //
    WithInternalIsolateMixin<                  //
        WithContextMixin<                      //
            WithIsolateScopeMixin<             //
                WithIsolateMixin<              //
                    WithDefaultPlatformMixin<  //
                        ::testing::Test>,
                    kEnableCounters>>>>;

using TestWithNativeContextAndZone =               //
    WithZoneMixin<                                 //
        WithInternalIsolateMixin<                  //
            WithContextMixin<                      //
                WithIsolateScopeMixin<             //
                    WithIsolateMixin<              //
                        WithDefaultPlatformMixin<  //
                            ::testing::Test>>>>>>;

296
class V8_NODISCARD SaveFlags {
297 298 299
 public:
  SaveFlags();
  ~SaveFlags();
300 301
  SaveFlags(const SaveFlags&) = delete;
  SaveFlags& operator=(const SaveFlags&) = delete;
302 303

 private:
304
#define FLAG_MODE_APPLY(ftype, ctype, nam, def, cmt) ctype SAVED_##nam;
305
#include "src/flags/flag-definitions.h"
306
#undef FLAG_MODE_APPLY
307 308
};

309
// For GTest.
310
inline void PrintTo(Object o, ::std::ostream* os) {
311 312 313 314 315 316
  *os << reinterpret_cast<void*>(o.ptr());
}
inline void PrintTo(Smi o, ::std::ostream* os) {
  *os << reinterpret_cast<void*>(o.ptr());
}

317 318 319 320 321 322 323 324 325 326
// ManualGCScope allows for disabling GC heuristics. This is useful for tests
// that want to check specific corner cases around GC.
//
// The scope will finalize any ongoing GC on the provided Isolate.
class V8_NODISCARD ManualGCScope final : private SaveFlags {
 public:
  explicit ManualGCScope(i::Isolate* isolate);
  ~ManualGCScope() = default;
};

327 328 329
}  // namespace internal
}  // namespace v8

330
#endif  // V8_UNITTESTS_TEST_UTILS_H_