// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gin/modules/timer.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "gin/handle.h"
#include "gin/object_template_builder.h"
#include "gin/public/isolate_holder.h"
#include "gin/shell_runner.h"
#include "gin/test/v8_test.h"
#include "gin/try_catch.h"
#include "gin/wrappable.h"
#include "v8/include/v8.h"
namespace gin {
namespace {
class Result : public Wrappable<Result> {
public:
static WrapperInfo kWrapperInfo;
static Handle<Result> Create(v8::Isolate* isolate) {
return CreateHandle(isolate, new Result());
}
int count() const { return count_; }
void set_count(int count) { count_ = count; }
void Quit() {
base::MessageLoop::current()->QuitNow();
}
private:
Result() : count_(0) {
}
virtual ~Result() {
}
virtual ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) OVERRIDE {
return Wrappable<Result>::GetObjectTemplateBuilder(isolate)
.SetProperty("count", &Result::count, &Result::set_count)
.SetMethod("quit", &Result::Quit);
}
int count_;
};
WrapperInfo Result::kWrapperInfo = { gin::kEmbedderNativeGin };
struct TestHelper {
TestHelper(v8::Isolate* isolate)
: runner(new ShellRunner(&delegate, isolate)),
scope(runner.get()),
timer_module(TimerModule::Create(isolate)),
result(Result::Create(isolate)) {
EXPECT_FALSE(runner->global().IsEmpty());
runner->global()->Set(StringToV8(isolate, "timer"),
timer_module->GetWrapper(isolate));
runner->global()->Set(StringToV8(isolate, "result"),
result->GetWrapper(isolate));
}
void QuitSoon() {
loop.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(),
base::TimeDelta::FromMilliseconds(0));
}
ShellRunnerDelegate delegate;
scoped_ptr<ShellRunner> runner;
Runner::Scope scope;
Handle<TimerModule> timer_module;
Handle<Result> result;
base::MessageLoop loop;
};
} // namespace
typedef V8Test TimerUnittest;
TEST_F(TimerUnittest, OneShot) {
TestHelper helper(instance_->isolate());
std::string source =
"timer.createOneShot(0, function() {"
" result.count++;"
"});";
helper.runner->Run(source, "script");
EXPECT_EQ(0, helper.result->count());
helper.QuitSoon();
helper.loop.Run();
EXPECT_EQ(1, helper.result->count());
}
TEST_F(TimerUnittest, OneShotCancel) {
TestHelper helper(instance_->isolate());
std::string source =
"var t = timer.createOneShot(0, function() {"
" result.count++;"
"});"
"t.cancel()";
helper.runner->Run(source, "script");
EXPECT_EQ(0, helper.result->count());
helper.QuitSoon();
helper.loop.Run();
EXPECT_EQ(0, helper.result->count());
}
TEST_F(TimerUnittest, Repeating) {
TestHelper helper(instance_->isolate());
// TODO(aa): Cannot do: if (++result.count == 3) because of v8 bug. Create
// test case and report.
std::string source =
"timer.createRepeating(0, function() {"
" result.count++;"
" if (result.count == 3) {"
" result.quit();"
" }"
"});";
helper.runner->Run(source, "script");
EXPECT_EQ(0, helper.result->count());
helper.loop.Run();
EXPECT_EQ(3, helper.result->count());
}
TEST_F(TimerUnittest, TimerCallbackToDestroyedRunner) {
TestHelper helper(instance_->isolate());
std::string source =
"timer.createOneShot(0, function() {"
" result.count++;"
"});";
helper.runner->Run(source, "script");
EXPECT_EQ(0, helper.result->count());
// Destroy runner, which should destroy the timer object we created.
helper.QuitSoon();
helper.runner.reset(NULL);
helper.loop.Run();
// Timer should not have run because it was deleted.
EXPECT_EQ(0, helper.result->count());
}
} // namespace gin