Home | History | Annotate | Download | only in cctest
      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, true);  \
     37   static void Test##Name()
     38 #endif
     39 
     40 #ifndef UNINITIALIZED_TEST
     41 #define UNINITIALIZED_TEST(Name)                                               \
     42   static void Test##Name();                                                    \
     43   CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, true, false); \
     44   static void Test##Name()
     45 #endif
     46 
     47 #ifndef DEPENDENT_TEST
     48 #define DEPENDENT_TEST(Name, Dep)                                              \
     49   static void Test##Name();                                                    \
     50   CcTest register_test_##Name(Test##Name, __FILE__, #Name, #Dep, true, true);  \
     51   static void Test##Name()
     52 #endif
     53 
     54 #ifndef DISABLED_TEST
     55 #define DISABLED_TEST(Name)                                                    \
     56   static void Test##Name();                                                    \
     57   CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, false, true); \
     58   static void Test##Name()
     59 #endif
     60 
     61 #define EXTENSION_LIST(V)                                                \
     62   V(GC_EXTENSION,    "v8/gc")                                            \
     63   V(PRINT_EXTENSION, "v8/print")                                         \
     64   V(TRACE_EXTENSION, "v8/trace")
     65 
     66 #define DEFINE_EXTENSION_ID(Name, Ident) Name##_ID,
     67 enum CcTestExtensionIds {
     68   EXTENSION_LIST(DEFINE_EXTENSION_ID)
     69   kMaxExtensions
     70 };
     71 #undef DEFINE_EXTENSION_ID
     72 
     73 typedef v8::internal::EnumSet<CcTestExtensionIds> CcTestExtensionFlags;
     74 #define DEFINE_EXTENSION_FLAG(Name, Ident)                               \
     75   static const CcTestExtensionFlags Name(1 << Name##_ID);
     76   static const CcTestExtensionFlags NO_EXTENSIONS(0);
     77   static const CcTestExtensionFlags ALL_EXTENSIONS((1 << kMaxExtensions) - 1);
     78   EXTENSION_LIST(DEFINE_EXTENSION_FLAG)
     79 #undef DEFINE_EXTENSION_FLAG
     80 
     81 
     82 class CcTest {
     83  public:
     84   typedef void (TestFunction)();
     85   CcTest(TestFunction* callback, const char* file, const char* name,
     86          const char* dependency, bool enabled, bool initialize);
     87   void Run();
     88   static CcTest* last() { return last_; }
     89   CcTest* prev() { return prev_; }
     90   const char* file() { return file_; }
     91   const char* name() { return name_; }
     92   const char* dependency() { return dependency_; }
     93   bool enabled() { return enabled_; }
     94 
     95   static v8::Isolate* isolate() {
     96     CHECK(isolate_ != NULL);
     97     isolate_used_ = true;
     98     return isolate_;
     99   }
    100 
    101   static i::Isolate* i_isolate() {
    102     return reinterpret_cast<i::Isolate*>(isolate());
    103   }
    104 
    105   static i::Heap* heap() {
    106     return i_isolate()->heap();
    107   }
    108 
    109   static v8::Local<v8::Object> global() {
    110     return isolate()->GetCurrentContext()->Global();
    111   }
    112 
    113   // TODO(dcarney): Remove.
    114   // This must be called first in a test.
    115   static void InitializeVM() {
    116     CHECK(!isolate_used_);
    117     CHECK(!initialize_called_);
    118     initialize_called_ = true;
    119     v8::HandleScope handle_scope(CcTest::isolate());
    120     v8::Context::New(CcTest::isolate())->Enter();
    121   }
    122 
    123   // Only for UNINITIALIZED_TESTs
    124   static void DisableAutomaticDispose();
    125 
    126   // Helper function to configure a context.
    127   // Must be in a HandleScope.
    128   static v8::Local<v8::Context> NewContext(
    129       CcTestExtensionFlags extensions,
    130       v8::Isolate* isolate = CcTest::isolate());
    131 
    132  private:
    133   friend int main(int argc, char** argv);
    134   TestFunction* callback_;
    135   const char* file_;
    136   const char* name_;
    137   const char* dependency_;
    138   bool enabled_;
    139   bool initialize_;
    140   CcTest* prev_;
    141   static CcTest* last_;
    142   static v8::Isolate* isolate_;
    143   static bool initialize_called_;
    144   static bool isolate_used_;
    145 };
    146 
    147 // Switches between all the Api tests using the threading support.
    148 // In order to get a surprising but repeatable pattern of thread
    149 // switching it has extra semaphores to control the order in which
    150 // the tests alternate, not relying solely on the big V8 lock.
    151 //
    152 // A test is augmented with calls to ApiTestFuzzer::Fuzz() in its
    153 // callbacks.  This will have no effect when we are not running the
    154 // thread fuzzing test.  In the thread fuzzing test it will
    155 // pseudorandomly select a successor thread and switch execution
    156 // to that thread, suspending the current test.
    157 class ApiTestFuzzer: public v8::internal::Thread {
    158  public:
    159   void CallTest();
    160 
    161   // The ApiTestFuzzer is also a Thread, so it has a Run method.
    162   virtual void Run();
    163 
    164   enum PartOfTest { FIRST_PART,
    165                     SECOND_PART,
    166                     THIRD_PART,
    167                     FOURTH_PART,
    168                     LAST_PART = FOURTH_PART };
    169 
    170   static void SetUp(PartOfTest part);
    171   static void RunAllTests();
    172   static void TearDown();
    173   // This method switches threads if we are running the Threading test.
    174   // Otherwise it does nothing.
    175   static void Fuzz();
    176 
    177  private:
    178   explicit ApiTestFuzzer(int num)
    179       : Thread("ApiTestFuzzer"),
    180         test_number_(num),
    181         gate_(0),
    182         active_(true) {
    183   }
    184   ~ApiTestFuzzer() {}
    185 
    186   static bool fuzzing_;
    187   static int tests_being_run_;
    188   static int current_;
    189   static int active_tests_;
    190   static bool NextThread();
    191   int test_number_;
    192   v8::internal::Semaphore gate_;
    193   bool active_;
    194   void ContextSwitch();
    195   static int GetNextTestNumber();
    196   static v8::internal::Semaphore all_tests_done_;
    197 };
    198 
    199 
    200 #define THREADED_TEST(Name)                                          \
    201   static void Test##Name();                                          \
    202   RegisterThreadedTest register_##Name(Test##Name, #Name);           \
    203   /* */ TEST(Name)
    204 
    205 
    206 class RegisterThreadedTest {
    207  public:
    208   explicit RegisterThreadedTest(CcTest::TestFunction* callback,
    209                                 const char* name)
    210       : fuzzer_(NULL), callback_(callback), name_(name) {
    211     prev_ = first_;
    212     first_ = this;
    213     count_++;
    214   }
    215   static int count() { return count_; }
    216   static RegisterThreadedTest* nth(int i) {
    217     CHECK(i < count());
    218     RegisterThreadedTest* current = first_;
    219     while (i > 0) {
    220       i--;
    221       current = current->prev_;
    222     }
    223     return current;
    224   }
    225   CcTest::TestFunction* callback() { return callback_; }
    226   ApiTestFuzzer* fuzzer_;
    227   const char* name() { return name_; }
    228 
    229  private:
    230   static RegisterThreadedTest* first_;
    231   static int count_;
    232   CcTest::TestFunction* callback_;
    233   RegisterThreadedTest* prev_;
    234   const char* name_;
    235 };
    236 
    237 // A LocalContext holds a reference to a v8::Context.
    238 class LocalContext {
    239  public:
    240   LocalContext(v8::Isolate* isolate,
    241                v8::ExtensionConfiguration* extensions = 0,
    242                v8::Handle<v8::ObjectTemplate> global_template =
    243                    v8::Handle<v8::ObjectTemplate>(),
    244                v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>()) {
    245     Initialize(isolate, extensions, global_template, global_object);
    246   }
    247 
    248   LocalContext(v8::ExtensionConfiguration* extensions = 0,
    249                v8::Handle<v8::ObjectTemplate> global_template =
    250                    v8::Handle<v8::ObjectTemplate>(),
    251                v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>()) {
    252     Initialize(CcTest::isolate(), extensions, global_template, global_object);
    253   }
    254 
    255   virtual ~LocalContext() {
    256     v8::HandleScope scope(isolate_);
    257     v8::Local<v8::Context>::New(isolate_, context_)->Exit();
    258     context_.Reset();
    259   }
    260 
    261   v8::Context* operator->() {
    262     return *reinterpret_cast<v8::Context**>(&context_);
    263   }
    264   v8::Context* operator*() { return operator->(); }
    265   bool IsReady() { return !context_.IsEmpty(); }
    266 
    267   v8::Local<v8::Context> local() {
    268     return v8::Local<v8::Context>::New(isolate_, context_);
    269   }
    270 
    271  private:
    272   void Initialize(v8::Isolate* isolate,
    273                   v8::ExtensionConfiguration* extensions,
    274                   v8::Handle<v8::ObjectTemplate> global_template,
    275                   v8::Handle<v8::Value> global_object) {
    276      v8::HandleScope scope(isolate);
    277      v8::Local<v8::Context> context = v8::Context::New(isolate,
    278                                                        extensions,
    279                                                        global_template,
    280                                                        global_object);
    281      context_.Reset(isolate, context);
    282      context->Enter();
    283      // We can't do this later perhaps because of a fatal error.
    284      isolate_ = isolate;
    285   }
    286 
    287   v8::Persistent<v8::Context> context_;
    288   v8::Isolate* isolate_;
    289 };
    290 
    291 static inline v8::Local<v8::Value> v8_num(double x) {
    292   return v8::Number::New(x);
    293 }
    294 
    295 
    296 static inline v8::Local<v8::String> v8_str(const char* x) {
    297   return v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), x);
    298 }
    299 
    300 
    301 static inline v8::Local<v8::Script> v8_compile(const char* x) {
    302   return v8::Script::Compile(v8_str(x));
    303 }
    304 
    305 
    306 // Helper function that compiles and runs the source.
    307 static inline v8::Local<v8::Value> CompileRun(const char* source) {
    308   return v8::Script::Compile(
    309       v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), source))->Run();
    310 }
    311 
    312 
    313 // Helper function that compiles and runs the source with given origin.
    314 static inline v8::Local<v8::Value> CompileRunWithOrigin(const char* source,
    315                                                         const char* origin_url,
    316                                                         int line_number,
    317                                                         int column_number) {
    318   v8::Isolate* isolate = v8::Isolate::GetCurrent();
    319   v8::ScriptOrigin origin(v8::String::NewFromUtf8(isolate, origin_url),
    320                           v8::Integer::New(line_number),
    321                           v8::Integer::New(column_number));
    322   return v8::Script::Compile(v8::String::NewFromUtf8(isolate, source), &origin)
    323       ->Run();
    324 }
    325 
    326 
    327 // Pick a slightly different port to allow tests to be run in parallel.
    328 static inline int FlagDependentPortOffset() {
    329   return ::v8::internal::FLAG_crankshaft == false ? 100 :
    330          ::v8::internal::FLAG_always_opt ? 200 : 0;
    331 }
    332 
    333 
    334 // Helper function that simulates a full new-space in the heap.
    335 static inline void SimulateFullSpace(v8::internal::NewSpace* space) {
    336   int new_linear_size = static_cast<int>(
    337       *space->allocation_limit_address() - *space->allocation_top_address());
    338   if (new_linear_size == 0) return;
    339   v8::internal::MaybeObject* maybe = space->AllocateRaw(new_linear_size);
    340   v8::internal::FreeListNode* node = v8::internal::FreeListNode::cast(maybe);
    341   node->set_size(space->heap(), new_linear_size);
    342 }
    343 
    344 
    345 // Helper function that simulates a full old-space in the heap.
    346 static inline void SimulateFullSpace(v8::internal::PagedSpace* space) {
    347   space->EmptyAllocationInfo();
    348   space->ResetFreeList();
    349   space->ClearStats();
    350 }
    351 
    352 
    353 // Helper class for new allocations tracking and checking.
    354 // To use checking of JS allocations tracking in a test,
    355 // just create an instance of this class.
    356 class HeapObjectsTracker {
    357  public:
    358   HeapObjectsTracker() {
    359     heap_profiler_ = i::Isolate::Current()->heap_profiler();
    360     CHECK_NE(NULL, heap_profiler_);
    361     heap_profiler_->StartHeapObjectsTracking(true);
    362   }
    363 
    364   ~HeapObjectsTracker() {
    365     i::Isolate::Current()->heap()->CollectAllAvailableGarbage();
    366     CHECK_EQ(0, heap_profiler_->heap_object_map()->FindUntrackedObjects());
    367     heap_profiler_->StopHeapObjectsTracking();
    368   }
    369 
    370  private:
    371   i::HeapProfiler* heap_profiler_;
    372 };
    373 
    374 
    375 #endif  // ifndef CCTEST_H_
    376