1// Copyright 2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#ifndef CCTEST_H_
29#define CCTEST_H_
30
31#include "v8.h"
32
33#ifndef TEST
34#define TEST(Name)                                                       \
35  static void Test##Name();                                              \
36  CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, true);  \
37  static void Test##Name()
38#endif
39
40#ifndef DEPENDENT_TEST
41#define DEPENDENT_TEST(Name, Dep)                                        \
42  static void Test##Name();                                              \
43  CcTest register_test_##Name(Test##Name, __FILE__, #Name, #Dep, true);  \
44  static void Test##Name()
45#endif
46
47#ifndef DISABLED_TEST
48#define DISABLED_TEST(Name)                                              \
49  static void Test##Name();                                              \
50  CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, false); \
51  static void Test##Name()
52#endif
53
54#define EXTENSION_LIST(V)                                                \
55  V(GC_EXTENSION,    "v8/gc")                                            \
56  V(PRINT_EXTENSION, "v8/print")                                         \
57  V(TRACE_EXTENSION, "v8/trace")
58
59#define DEFINE_EXTENSION_ID(Name, Ident) Name##_ID,
60enum CcTestExtensionIds {
61  EXTENSION_LIST(DEFINE_EXTENSION_ID)
62  kMaxExtensions
63};
64#undef DEFINE_EXTENSION_ID
65
66typedef v8::internal::EnumSet<CcTestExtensionIds> CcTestExtensionFlags;
67#define DEFINE_EXTENSION_FLAG(Name, Ident)                               \
68  static const CcTestExtensionFlags Name(1 << Name##_ID);
69  static const CcTestExtensionFlags NO_EXTENSIONS(0);
70  static const CcTestExtensionFlags ALL_EXTENSIONS((1 << kMaxExtensions) - 1);
71  EXTENSION_LIST(DEFINE_EXTENSION_FLAG)
72#undef DEFINE_EXTENSION_FLAG
73
74class CcTest {
75 public:
76  typedef void (TestFunction)();
77  CcTest(TestFunction* callback, const char* file, const char* name,
78         const char* dependency, bool enabled);
79  void Run() { callback_(); }
80  static CcTest* last() { return last_; }
81  CcTest* prev() { return prev_; }
82  const char* file() { return file_; }
83  const char* name() { return name_; }
84  const char* dependency() { return dependency_; }
85  bool enabled() { return enabled_; }
86  static v8::Isolate* default_isolate() { return default_isolate_; }
87
88  static v8::Handle<v8::Context> env() {
89    return v8::Local<v8::Context>::New(default_isolate_, context_);
90  }
91
92  static v8::Isolate* isolate() { return default_isolate_; }
93
94  // Helper function to initialize the VM.
95  static void InitializeVM(CcTestExtensionFlags extensions = NO_EXTENSIONS);
96
97 private:
98  friend int main(int argc, char** argv);
99  static void set_default_isolate(v8::Isolate* default_isolate) {
100    default_isolate_ = default_isolate;
101  }
102  TestFunction* callback_;
103  const char* file_;
104  const char* name_;
105  const char* dependency_;
106  bool enabled_;
107  CcTest* prev_;
108  static CcTest* last_;
109  static v8::Isolate* default_isolate_;
110  static v8::Persistent<v8::Context> context_;
111};
112
113// Switches between all the Api tests using the threading support.
114// In order to get a surprising but repeatable pattern of thread
115// switching it has extra semaphores to control the order in which
116// the tests alternate, not relying solely on the big V8 lock.
117//
118// A test is augmented with calls to ApiTestFuzzer::Fuzz() in its
119// callbacks.  This will have no effect when we are not running the
120// thread fuzzing test.  In the thread fuzzing test it will
121// pseudorandomly select a successor thread and switch execution
122// to that thread, suspending the current test.
123class ApiTestFuzzer: public v8::internal::Thread {
124 public:
125  void CallTest();
126
127  // The ApiTestFuzzer is also a Thread, so it has a Run method.
128  virtual void Run();
129
130  enum PartOfTest { FIRST_PART,
131                    SECOND_PART,
132                    THIRD_PART,
133                    FOURTH_PART,
134                    LAST_PART = FOURTH_PART };
135
136  static void SetUp(PartOfTest part);
137  static void RunAllTests();
138  static void TearDown();
139  // This method switches threads if we are running the Threading test.
140  // Otherwise it does nothing.
141  static void Fuzz();
142
143 private:
144  explicit ApiTestFuzzer(int num)
145      : Thread("ApiTestFuzzer"),
146        test_number_(num),
147        gate_(v8::internal::OS::CreateSemaphore(0)),
148        active_(true) {
149  }
150  ~ApiTestFuzzer() { delete gate_; }
151
152  static bool fuzzing_;
153  static int tests_being_run_;
154  static int current_;
155  static int active_tests_;
156  static bool NextThread();
157  int test_number_;
158  v8::internal::Semaphore* gate_;
159  bool active_;
160  void ContextSwitch();
161  static int GetNextTestNumber();
162  static v8::internal::Semaphore* all_tests_done_;
163};
164
165
166#define THREADED_TEST(Name)                                          \
167  static void Test##Name();                                          \
168  RegisterThreadedTest register_##Name(Test##Name, #Name);           \
169  /* */ TEST(Name)
170
171
172class RegisterThreadedTest {
173 public:
174  explicit RegisterThreadedTest(CcTest::TestFunction* callback,
175                                const char* name)
176      : fuzzer_(NULL), callback_(callback), name_(name) {
177    prev_ = first_;
178    first_ = this;
179    count_++;
180  }
181  static int count() { return count_; }
182  static RegisterThreadedTest* nth(int i) {
183    CHECK(i < count());
184    RegisterThreadedTest* current = first_;
185    while (i > 0) {
186      i--;
187      current = current->prev_;
188    }
189    return current;
190  }
191  CcTest::TestFunction* callback() { return callback_; }
192  ApiTestFuzzer* fuzzer_;
193  const char* name() { return name_; }
194
195 private:
196  static RegisterThreadedTest* first_;
197  static int count_;
198  CcTest::TestFunction* callback_;
199  RegisterThreadedTest* prev_;
200  const char* name_;
201};
202
203// A LocalContext holds a reference to a v8::Context.
204class LocalContext {
205 public:
206  LocalContext(v8::ExtensionConfiguration* extensions = 0,
207               v8::Handle<v8::ObjectTemplate> global_template =
208                   v8::Handle<v8::ObjectTemplate>(),
209               v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>()) {
210    v8::Isolate* isolate = v8::Isolate::GetCurrent();
211    v8::HandleScope scope(isolate);
212    v8::Local<v8::Context> context = v8::Context::New(isolate,
213                                                      extensions,
214                                                      global_template,
215                                                      global_object);
216    context_.Reset(isolate, context);
217    context->Enter();
218    // We can't do this later perhaps because of a fatal error.
219    isolate_ = context->GetIsolate();
220  }
221
222  virtual ~LocalContext() {
223    v8::HandleScope scope(isolate_);
224    v8::Local<v8::Context>::New(isolate_, context_)->Exit();
225    context_.Dispose();
226  }
227
228  v8::Context* operator->() {
229    return *reinterpret_cast<v8::Context**>(&context_);
230  }
231  v8::Context* operator*() { return operator->(); }
232  bool IsReady() { return !context_.IsEmpty(); }
233
234  v8::Local<v8::Context> local() {
235    return v8::Local<v8::Context>::New(isolate_, context_);
236  }
237
238 private:
239  v8::Persistent<v8::Context> context_;
240  v8::Isolate* isolate_;
241};
242
243static inline v8::Local<v8::Value> v8_num(double x) {
244  return v8::Number::New(x);
245}
246
247
248static inline v8::Local<v8::String> v8_str(const char* x) {
249  return v8::String::New(x);
250}
251
252
253static inline v8::Local<v8::Script> v8_compile(const char* x) {
254  return v8::Script::Compile(v8_str(x));
255}
256
257
258// Helper function that compiles and runs the source.
259static inline v8::Local<v8::Value> CompileRun(const char* source) {
260  return v8::Script::Compile(v8::String::New(source))->Run();
261}
262
263
264// Helper function that compiles and runs the source with given origin.
265static inline v8::Local<v8::Value> CompileRunWithOrigin(const char* source,
266                                                        const char* origin_url,
267                                                        int line_number,
268                                                        int column_number) {
269  v8::ScriptOrigin origin(v8::String::New(origin_url),
270                          v8::Integer::New(line_number),
271                          v8::Integer::New(column_number));
272  return v8::Script::Compile(v8::String::New(source), &origin)->Run();
273}
274
275
276// Pick a slightly different port to allow tests to be run in parallel.
277static inline int FlagDependentPortOffset() {
278  return ::v8::internal::FLAG_crankshaft == false ? 100 :
279         ::v8::internal::FLAG_always_opt ? 200 : 0;
280}
281
282
283// Helper function that simulates a full new-space in the heap.
284static inline void SimulateFullSpace(v8::internal::NewSpace* space) {
285  int new_linear_size = static_cast<int>(
286      *space->allocation_limit_address() - *space->allocation_top_address());
287  v8::internal::MaybeObject* maybe = space->AllocateRaw(new_linear_size);
288  v8::internal::FreeListNode* node = v8::internal::FreeListNode::cast(maybe);
289  node->set_size(space->heap(), new_linear_size);
290}
291
292
293// Helper function that simulates a full old-space in the heap.
294static inline void SimulateFullSpace(v8::internal::PagedSpace* space) {
295  int old_linear_size = static_cast<int>(space->limit() - space->top());
296  space->Free(space->top(), old_linear_size);
297  space->SetTop(space->limit(), space->limit());
298  space->ResetFreeList();
299  space->ClearStats();
300}
301
302
303#endif  // ifndef CCTEST_H_
304