| Index: test/cctest/cctest.h | 
| diff --git a/test/cctest/cctest.h b/test/cctest/cctest.h | 
| index a95645e010eabb82f3483bf26063e3d105492950..404b692b27480b62c3e7313b4b63a10f52edb7ae 100644 | 
| --- a/test/cctest/cctest.h | 
| +++ b/test/cctest/cctest.h | 
| @@ -28,6 +28,8 @@ | 
| #ifndef CCTEST_H_ | 
| #define CCTEST_H_ | 
|  | 
| +#include "v8.h" | 
| + | 
| #ifndef TEST | 
| #define TEST(Name)                                                       \ | 
| static void Test##Name();                                              \ | 
| @@ -72,4 +74,138 @@ class CcTest { | 
| CcTest* prev_; | 
| }; | 
|  | 
| +// Switches between all the Api tests using the threading support. | 
| +// In order to get a surprising but repeatable pattern of thread | 
| +// switching it has extra semaphores to control the order in which | 
| +// the tests alternate, not relying solely on the big V8 lock. | 
| +// | 
| +// A test is augmented with calls to ApiTestFuzzer::Fuzz() in its | 
| +// callbacks.  This will have no effect when we are not running the | 
| +// thread fuzzing test.  In the thread fuzzing test it will | 
| +// pseudorandomly select a successor thread and switch execution | 
| +// to that thread, suspending the current test. | 
| +class ApiTestFuzzer: public v8::internal::Thread { | 
| + public: | 
| +  void CallTest(); | 
| +  explicit ApiTestFuzzer(int num) | 
| +      : test_number_(num), | 
| +        gate_(v8::internal::OS::CreateSemaphore(0)), | 
| +        active_(true) { | 
| +  } | 
| +  ~ApiTestFuzzer() { delete gate_; } | 
| + | 
| +  // The ApiTestFuzzer is also a Thread, so it has a Run method. | 
| +  virtual void Run(); | 
| + | 
| +  enum PartOfTest { FIRST_PART, SECOND_PART }; | 
| + | 
| +  static void Setup(PartOfTest part); | 
| +  static void RunAllTests(); | 
| +  static void TearDown(); | 
| +  // This method switches threads if we are running the Threading test. | 
| +  // Otherwise it does nothing. | 
| +  static void Fuzz(); | 
| + private: | 
| +  static bool fuzzing_; | 
| +  static int tests_being_run_; | 
| +  static int current_; | 
| +  static int active_tests_; | 
| +  static bool NextThread(); | 
| +  int test_number_; | 
| +  v8::internal::Semaphore* gate_; | 
| +  bool active_; | 
| +  void ContextSwitch(); | 
| +  static int GetNextTestNumber(); | 
| +  static v8::internal::Semaphore* all_tests_done_; | 
| +}; | 
| + | 
| + | 
| +#define THREADED_TEST(Name)                                          \ | 
| +  static void Test##Name();                                          \ | 
| +  RegisterThreadedTest register_##Name(Test##Name, #Name);           \ | 
| +  /* */ TEST(Name) | 
| + | 
| + | 
| +class RegisterThreadedTest { | 
| + public: | 
| +  explicit RegisterThreadedTest(CcTest::TestFunction* callback, | 
| +                                const char* name) | 
| +      : fuzzer_(NULL), callback_(callback), name_(name) { | 
| +    prev_ = first_; | 
| +    first_ = this; | 
| +    count_++; | 
| +  } | 
| +  static int count() { return count_; } | 
| +  static RegisterThreadedTest* nth(int i) { | 
| +    CHECK(i < count()); | 
| +    RegisterThreadedTest* current = first_; | 
| +    while (i > 0) { | 
| +      i--; | 
| +      current = current->prev_; | 
| +    } | 
| +    return current; | 
| +  } | 
| +  CcTest::TestFunction* callback() { return callback_; } | 
| +  ApiTestFuzzer* fuzzer_; | 
| +  const char* name() { return name_; } | 
| + | 
| + private: | 
| +  static RegisterThreadedTest* first_; | 
| +  static int count_; | 
| +  CcTest::TestFunction* callback_; | 
| +  RegisterThreadedTest* prev_; | 
| +  const char* name_; | 
| +}; | 
| + | 
| + | 
| +// A LocalContext holds a reference to a v8::Context. | 
| +class LocalContext { | 
| + public: | 
| +  LocalContext(v8::ExtensionConfiguration* extensions = 0, | 
| +               v8::Handle<v8::ObjectTemplate> global_template = | 
| +                   v8::Handle<v8::ObjectTemplate>(), | 
| +               v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>()) | 
| +    : context_(v8::Context::New(extensions, global_template, global_object)) { | 
| +    context_->Enter(); | 
| +  } | 
| + | 
| +  virtual ~LocalContext() { | 
| +    context_->Exit(); | 
| +    context_.Dispose(); | 
| +  } | 
| + | 
| +  v8::Context* operator->() { return *context_; } | 
| +  v8::Context* operator*() { return *context_; } | 
| +  bool IsReady() { return !context_.IsEmpty(); } | 
| + | 
| +  v8::Local<v8::Context> local() { | 
| +    return v8::Local<v8::Context>::New(context_); | 
| +  } | 
| + | 
| + private: | 
| +  v8::Persistent<v8::Context> context_; | 
| +}; | 
| + | 
| + | 
| +static inline v8::Local<v8::Value> v8_num(double x) { | 
| +  return v8::Number::New(x); | 
| +} | 
| + | 
| + | 
| +static inline v8::Local<v8::String> v8_str(const char* x) { | 
| +  return v8::String::New(x); | 
| +} | 
| + | 
| + | 
| +static inline v8::Local<v8::Script> v8_compile(const char* x) { | 
| +  return v8::Script::Compile(v8_str(x)); | 
| +} | 
| + | 
| + | 
| +// Helper function that compiles and runs the source. | 
| +static inline v8::Local<v8::Value> CompileRun(const char* source) { | 
| +  return v8::Script::Compile(v8::String::New(source))->Run(); | 
| +} | 
| + | 
| + | 
| #endif  // ifndef CCTEST_H_ | 
|  |