1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/test/base/v8_unit_test.h"
7 #include "base/files/file_util.h"
8 #include "base/logging.h"
9 #include "base/path_service.h"
10 #include "base/strings/string_piece.h"
11 #include "base/strings/stringprintf.h"
12 #include "chrome/common/chrome_paths.h"
13 #include "third_party/WebKit/public/web/WebKit.h"
17 // |args| are passed through the various JavaScript logging functions such as
18 // console.log. Returns a string appropriate for logging with LOG(severity).
19 std::string
LogArgs2String(const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
22 for (int i
= 0; i
< args
.Length(); i
++) {
23 v8::HandleScope
handle_scope(v8::Isolate::GetCurrent());
29 v8::String::Utf8Value
str(args
[i
]);
35 // Whether errors were seen.
36 bool had_errors
= false;
39 bool testResult_ok
= false;
41 // Location of test data (currently test/data/webui).
42 base::FilePath test_data_directory
;
44 // Location of generated test data (<(PROGRAM_DIR)/test_data).
45 base::FilePath gen_test_data_directory
;
49 V8UnitTest::V8UnitTest() : handle_scope_(blink::mainThreadIsolate()) {
50 InitPathsAndLibraries();
53 V8UnitTest::~V8UnitTest() {
56 void V8UnitTest::AddLibrary(const base::FilePath
& library_path
) {
57 user_libraries_
.push_back(library_path
);
60 bool V8UnitTest::ExecuteJavascriptLibraries() {
61 std::string utf8_content
;
62 for (std::vector
<base::FilePath
>::iterator user_libraries_iterator
=
63 user_libraries_
.begin();
64 user_libraries_iterator
!= user_libraries_
.end();
65 ++user_libraries_iterator
) {
66 std::string library_content
;
67 base::FilePath
library_file(*user_libraries_iterator
);
68 if (!user_libraries_iterator
->IsAbsolute()) {
69 base::FilePath gen_file
= gen_test_data_directory
.Append(library_file
);
70 library_file
= base::PathExists(gen_file
)
72 : test_data_directory
.Append(*user_libraries_iterator
);
74 library_file
= base::MakeAbsoluteFilePath(library_file
);
75 if (!base::ReadFileToString(library_file
, &library_content
)) {
76 ADD_FAILURE() << library_file
.value();
79 ExecuteScriptInContext(library_content
, library_file
.MaybeAsASCII());
80 if (::testing::Test::HasFatalFailure())
86 bool V8UnitTest::RunJavascriptTestF(const std::string
& testFixture
,
87 const std::string
& testName
) {
89 testResult_ok
= false;
91 if (!ExecuteJavascriptLibraries())
94 v8::Isolate
* isolate
= blink::mainThreadIsolate();
95 v8::HandleScope
handle_scope(isolate
);
96 v8::Local
<v8::Context
> context
=
97 v8::Local
<v8::Context
>::New(isolate
, context_
);
98 v8::Context::Scope
context_scope(context
);
100 v8::Handle
<v8::Value
> functionProperty
=
101 context
->Global()->Get(v8::String::NewFromUtf8(isolate
, "runTest"));
102 EXPECT_FALSE(functionProperty
.IsEmpty());
103 if (::testing::Test::HasNonfatalFailure())
105 EXPECT_TRUE(functionProperty
->IsFunction());
106 if (::testing::Test::HasNonfatalFailure())
108 v8::Handle
<v8::Function
> function
=
109 v8::Handle
<v8::Function
>::Cast(functionProperty
);
111 v8::Local
<v8::Array
> params
= v8::Array::New(isolate
);
113 v8::String::NewFromUtf8(isolate
,
115 v8::String::kNormalString
,
116 testFixture
.size()));
118 v8::String::NewFromUtf8(isolate
,
120 v8::String::kNormalString
,
122 v8::Handle
<v8::Value
> args
[] = {
123 v8::Boolean::New(isolate
, false),
124 v8::String::NewFromUtf8(isolate
, "RUN_TEST_F"), params
};
126 v8::TryCatch try_catch
;
127 v8::Handle
<v8::Value
> result
= function
->Call(context
->Global(), 3, args
);
128 // The test fails if an exception was thrown.
129 EXPECT_FALSE(result
.IsEmpty());
130 if (::testing::Test::HasNonfatalFailure())
133 // Ok if ran successfully, passed tests, and didn't have console errors.
134 return result
->BooleanValue() && testResult_ok
&& !had_errors
;
137 void V8UnitTest::InitPathsAndLibraries() {
138 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA
, &test_data_directory
));
139 test_data_directory
= test_data_directory
.AppendASCII("webui");
141 PathService::Get(chrome::DIR_GEN_TEST_DATA
, &gen_test_data_directory
));
143 base::FilePath mockPath
;
144 ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT
, &mockPath
));
145 mockPath
= mockPath
.AppendASCII("chrome");
146 mockPath
= mockPath
.AppendASCII("third_party");
147 mockPath
= mockPath
.AppendASCII("mock4js");
148 mockPath
= mockPath
.AppendASCII("mock4js.js");
149 AddLibrary(mockPath
);
151 base::FilePath accessibilityAuditPath
;
152 ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT
, &accessibilityAuditPath
));
153 accessibilityAuditPath
= accessibilityAuditPath
.AppendASCII("third_party");
154 accessibilityAuditPath
=
155 accessibilityAuditPath
.AppendASCII("accessibility-audit");
156 accessibilityAuditPath
= accessibilityAuditPath
.AppendASCII("axs_testing.js");
157 AddLibrary(accessibilityAuditPath
);
159 base::FilePath testApiPath
;
160 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA
, &testApiPath
));
161 testApiPath
= testApiPath
.AppendASCII("webui");
162 testApiPath
= testApiPath
.AppendASCII("test_api.js");
163 AddLibrary(testApiPath
);
166 void V8UnitTest::SetUp() {
167 v8::Isolate
* isolate
= blink::mainThreadIsolate();
168 v8::Handle
<v8::ObjectTemplate
> global
= v8::ObjectTemplate::New(isolate
);
169 v8::Handle
<v8::String
> logString
= v8::String::NewFromUtf8(isolate
, "log");
170 v8::Handle
<v8::FunctionTemplate
> logFunction
=
171 v8::FunctionTemplate::New(isolate
, &V8UnitTest::Log
);
172 global
->Set(logString
, logFunction
);
174 // Set up chrome object for chrome.send().
175 v8::Handle
<v8::ObjectTemplate
> chrome
= v8::ObjectTemplate::New(isolate
);
176 global
->Set(v8::String::NewFromUtf8(isolate
, "chrome"), chrome
);
177 chrome
->Set(v8::String::NewFromUtf8(isolate
, "send"),
178 v8::FunctionTemplate::New(isolate
, &V8UnitTest::ChromeSend
));
180 // Set up console object for console.log(), etc.
181 v8::Handle
<v8::ObjectTemplate
> console
= v8::ObjectTemplate::New(isolate
);
182 global
->Set(v8::String::NewFromUtf8(isolate
, "console"), console
);
183 console
->Set(logString
, logFunction
);
184 console
->Set(v8::String::NewFromUtf8(isolate
, "info"), logFunction
);
185 console
->Set(v8::String::NewFromUtf8(isolate
, "warn"), logFunction
);
186 console
->Set(v8::String::NewFromUtf8(isolate
, "error"),
187 v8::FunctionTemplate::New(isolate
, &V8UnitTest::Error
));
189 context_
.Reset(isolate
, v8::Context::New(isolate
, NULL
, global
));
192 void V8UnitTest::SetGlobalStringVar(const std::string
& var_name
,
193 const std::string
& value
) {
194 v8::Isolate
* isolate
= blink::mainThreadIsolate();
195 v8::Local
<v8::Context
> context
=
196 v8::Local
<v8::Context
>::New(isolate
, context_
);
197 v8::Context::Scope
context_scope(context
);
198 context
->Global()->Set(
199 v8::String::NewFromUtf8(isolate
,
201 v8::String::kNormalString
,
203 v8::String::NewFromUtf8(
204 isolate
, value
.c_str(), v8::String::kNormalString
, value
.length()));
207 void V8UnitTest::ExecuteScriptInContext(const base::StringPiece
& script_source
,
208 const base::StringPiece
& script_name
) {
209 v8::Isolate
* isolate
= blink::mainThreadIsolate();
210 v8::HandleScope
handle_scope(isolate
);
211 v8::Local
<v8::Context
> context
=
212 v8::Local
<v8::Context
>::New(isolate
, context_
);
213 v8::Context::Scope
context_scope(context
);
214 v8::Handle
<v8::String
> source
=
215 v8::String::NewFromUtf8(isolate
,
216 script_source
.data(),
217 v8::String::kNormalString
,
218 script_source
.size());
219 v8::Handle
<v8::String
> name
=
220 v8::String::NewFromUtf8(isolate
,
222 v8::String::kNormalString
,
225 v8::TryCatch try_catch
;
226 v8::Handle
<v8::Script
> script
= v8::Script::Compile(source
, name
);
227 // Ensure the script compiled without errors.
228 if (script
.IsEmpty())
229 FAIL() << ExceptionToString(try_catch
);
231 v8::Handle
<v8::Value
> result
= script
->Run();
232 // Ensure the script ran without errors.
233 if (result
.IsEmpty())
234 FAIL() << ExceptionToString(try_catch
);
237 std::string
V8UnitTest::ExceptionToString(const v8::TryCatch
& try_catch
) {
239 v8::HandleScope
handle_scope(v8::Isolate::GetCurrent());
240 v8::String::Utf8Value
exception(try_catch
.Exception());
241 v8::Local
<v8::Message
> message(try_catch
.Message());
242 if (message
.IsEmpty()) {
243 str
.append(base::StringPrintf("%s\n", *exception
));
245 v8::String::Utf8Value
filename(message
->GetScriptOrigin().ResourceName());
246 int linenum
= message
->GetLineNumber();
247 int colnum
= message
->GetStartColumn();
248 str
.append(base::StringPrintf(
249 "%s:%i:%i %s\n", *filename
, linenum
, colnum
, *exception
));
250 v8::String::Utf8Value
sourceline(message
->GetSourceLine());
251 str
.append(base::StringPrintf("%s\n", *sourceline
));
256 void V8UnitTest::TestFunction(const std::string
& function_name
) {
257 v8::Isolate
* isolate
= blink::mainThreadIsolate();
258 v8::HandleScope
handle_scope(isolate
);
259 v8::Local
<v8::Context
> context
=
260 v8::Local
<v8::Context
>::New(isolate
, context_
);
261 v8::Context::Scope
context_scope(context
);
263 v8::Handle
<v8::Value
> functionProperty
= context
->Global()->Get(
264 v8::String::NewFromUtf8(isolate
, function_name
.c_str()));
265 ASSERT_FALSE(functionProperty
.IsEmpty());
266 ASSERT_TRUE(functionProperty
->IsFunction());
267 v8::Handle
<v8::Function
> function
=
268 v8::Handle
<v8::Function
>::Cast(functionProperty
);
270 v8::TryCatch try_catch
;
271 v8::Handle
<v8::Value
> result
= function
->Call(context
->Global(), 0, NULL
);
272 // The test fails if an exception was thrown.
273 if (result
.IsEmpty())
274 FAIL() << ExceptionToString(try_catch
);
278 void V8UnitTest::Log(const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
279 LOG(INFO
) << LogArgs2String(args
);
282 void V8UnitTest::Error(const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
284 LOG(ERROR
) << LogArgs2String(args
);
287 void V8UnitTest::ChromeSend(const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
288 v8::HandleScope
handle_scope(v8::Isolate::GetCurrent());
289 // We expect to receive 2 args: ("testResult", [ok, message]). However,
290 // chrome.send may pass only one. Therefore we need to ensure we have at least
291 // 1, then ensure that the first is "testResult" before checking again for 2.
292 EXPECT_LE(1, args
.Length());
293 if (::testing::Test::HasNonfatalFailure())
295 v8::String::Utf8Value
message(args
[0]);
296 EXPECT_EQ("testResult", std::string(*message
, message
.length()));
297 if (::testing::Test::HasNonfatalFailure())
299 EXPECT_EQ(2, args
.Length());
300 if (::testing::Test::HasNonfatalFailure())
302 v8::Handle
<v8::Array
> testResult(args
[1].As
<v8::Array
>());
303 EXPECT_EQ(2U, testResult
->Length());
304 if (::testing::Test::HasNonfatalFailure())
306 testResult_ok
= testResult
->Get(0)->BooleanValue();
307 if (!testResult_ok
) {
308 v8::String::Utf8Value
message(testResult
->Get(1));
309 LOG(ERROR
) << *message
;