diff options
author | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-16 01:38:49 +0000 |
---|---|---|
committer | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-16 01:38:49 +0000 |
commit | 7896d694115cb2e64971ed0df60f61e3df2f7375 (patch) | |
tree | c2778927f7bbef5ee5c1e7cae3a941b097e139ae /chrome/test | |
parent | 6fa342707a9979b60fa0321f27983d170d728c66 (diff) | |
download | chromium_src-7896d694115cb2e64971ed0df60f61e3df2f7375.zip chromium_src-7896d694115cb2e64971ed0df60f61e3df2f7375.tar.gz chromium_src-7896d694115cb2e64971ed0df60f61e3df2f7375.tar.bz2 |
Commit issue 19737: Partial implementation of tests for
Greasemonkey API.
Review URL: http://codereview.chromium.org/21387
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@9847 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/test')
-rw-r--r-- | chrome/test/DEPS | 1 | ||||
-rw-r--r-- | chrome/test/data/extensions/greasemonkey_api_test.js | 408 | ||||
-rw-r--r-- | chrome/test/unit/unit_tests.scons | 3 | ||||
-rw-r--r-- | chrome/test/unit/unittests.vcproj | 12 | ||||
-rw-r--r-- | chrome/test/v8_unit_test.cc | 93 | ||||
-rw-r--r-- | chrome/test/v8_unit_test.h | 100 |
6 files changed, 617 insertions, 0 deletions
diff --git a/chrome/test/DEPS b/chrome/test/DEPS index f6a76e9..fdb76c3 100644 --- a/chrome/test/DEPS +++ b/chrome/test/DEPS @@ -4,4 +4,5 @@ include_rules = [ "+sandbox/src", "+sandbox/tests", "+webkit/glue", + "+v8/include", # We have unit tests which use v8 to exercise JavaScript. ] diff --git a/chrome/test/data/extensions/greasemonkey_api_test.js b/chrome/test/data/extensions/greasemonkey_api_test.js new file mode 100644 index 0000000..d43e2a0 --- /dev/null +++ b/chrome/test/data/extensions/greasemonkey_api_test.js @@ -0,0 +1,408 @@ +var localStorage; +var document; +var window; + +function testGetSetValue() { + localStorage = new LocalStorageFake(); + GM_setValue('string', 'value'); + assert('value', GM_getValue('string')); + GM_setValue('integer', 123); + assert(123, GM_getValue('integer')); + GM_setValue('boolean', true); + assert(true, GM_getValue('boolean')); + + assert(undefined, GM_getValue('notset')); + assert('default', GM_getValue('notset', 'default')); + + // illegal values + assertThrow(function() { + GM_setValue('illegal value', null); + }); + assertThrow(function() { + GM_setValue('illegal value', 1.5); + }); +} + +function testDeleteValue() { + localStorage = new LocalStorageFake(); + GM_setValue('delete', 'value'); + assert('value', GM_getValue('delete')); + GM_deleteValue('delete'); + assert(undefined, GM_getValue('delete')); + GM_deleteValue('notset'); +} + +function testListValues() { + localStorage = new LocalStorageFake(); + var a = GM_listValues(); + assert(0, a.length); + + GM_setValue('one', 'first'); + a = GM_listValues(); + assert(1, a.length); + assert('one', a[0]); + + GM_setValue('two', 'second'); + a = GM_listValues(); + + // Test that keys that are not namespaced can't be read. + localStorage.setItem('three', 'third'); + assert(2, a.length); + assert(true, a.indexOf('one') >= 0); + assert(true, a.indexOf('two') >= 0); +} + +function testGetResourceURL() { + assertThrow(function() { + var resource = GM_getResourceURL('urlResourceName'); + }); +} + +function testGetResourceText() { + assertThrow(function() { + var resource = GM_getResourceText('textResourceName'); + }); +} + +function testAddStyle() { + var head = new ElementFake('head'); + document = new DocumentFake(head); + var css = 'color: #decaff;'; + GM_addStyle(css); + assert(1, head.childNodes.length); + var style = head.childNodes[0]; + assert("style", style.tagName); + assert("text/css", style.type); + assert(css, style.innerHTML); +} + +function testXmlhttpRequest() { + var xhr = GM_xmlhttpRequest({}); +} + +function testRegisterMenuCommand() { + assertThrow(function() { + GM_registerMenuCommand('name', function() {}, 'a', '', 'a'); + }); +} + +function testOpenInTab() { + var mockWindow = { + open: function(url, name, opt_options) { + this.openedUrl = url; + this.openedName = name; + this.openedOptions = opt_options; + } + }; + window = mockWindow; + var url = 'http://example.com'; + GM_openInTab(url); + assert(mockWindow.openedUrl, url); +} + +function testLog() { + var mockWindow = { + console: { + message: null, + log: function(message) { + this.message = message; + } + } + }; + window = mockWindow; + var message = 'hello world'; + GM_log(message); + assert(message, mockWindow.console.message); +} + +function LocalStorageFake() { + this.map_ = {}; + this.keys_ = []; +} +LocalStorageFake.prototype = { + length: 0, + key: function(index) { + if (index >= this.length) { + throw new Error('INDEX_SIZE_ERR'); + } + return this.keys_[index]; + }, + getItem: function(key) { + if (key in this.map_) { + return this.map_[key]; + } + return null; + }, + setItem: function(key, data) { + this.map_[key] = data; + this.updateKeys_(); + }, + removeItem: function(key) { + delete this.map_[key]; + this.updateKeys_(); + }, + clear: function() { + this.map_ = {}; + this.updateKeys_(); + }, + updateKeys_: function() { + var keys = []; + for (var key in this.map_) { + keys.push(key); + } + this.keys_ = keys; + this.length = keys.length; + } +} + +function DocumentFake(head) { + this.head_ = head; +} +DocumentFake.prototype = { + getElementsByTagName: function(tagName) { + if (tagName == 'head' && this.head_) { + return [this.head_]; + } + return []; + }, + createElement: function(tagName) { + return new ElementFake(tagName); + } +} + +function ElementFake(tagName) { + this.tagName = tagName; +} +ElementFake.prototype = { + childNodes: [], + appendChild: function(e) { + this.childNodes.push(e); + return e; + }, +} + +function assert(expected, actual) { + if (expected !== actual) { + throw new Error('Assert failed: "' + expected + '" !== "' + actual + '"'); + } +} + +function fail() { + throw new Error('Fail'); +} + +function assertThrow(f) { + var threw = false; + try { + f(); + } catch(e) { + threw = true; + } + if (!threw) { + throw new Error('Assert failed, expression did not throw.'); + } +} +var localStorage; +var document; +var window; + +function testGetSetValue() { + localStorage = new LocalStorageFake(); + GM_setValue('string', 'value'); + assert('value', GM_getValue('string')); + GM_setValue('integer', 123); + assert(123, GM_getValue('integer')); + GM_setValue('boolean', true); + assert(true, GM_getValue('boolean')); + + assert(undefined, GM_getValue('notset')); + assert('default', GM_getValue('notset', 'default')); + + // illegal values + assertThrow(function() { + GM_setValue('illegal value', null); + }); + assertThrow(function() { + GM_setValue('illegal value', 1.5); + }); +} + +function testDeleteValue() { + localStorage = new LocalStorageFake(); + GM_setValue('delete', 'value'); + assert('value', GM_getValue('delete')); + GM_deleteValue('delete'); + assert(undefined, GM_getValue('delete')); + GM_deleteValue('notset'); +} + +function testListValues() { + localStorage = new LocalStorageFake(); + var a = GM_listValues(); + assert(0, a.length); + + GM_setValue('one', 'first'); + a = GM_listValues(); + assert(1, a.length); + assert('one', a[0]); + + GM_setValue('two', 'second'); + a = GM_listValues(); + + // Test that keys that are not namespaced can't be read. + localStorage.setItem('three', 'third'); + assert(2, a.length); + assert(true, a.indexOf('one') >= 0); + assert(true, a.indexOf('two') >= 0); +} + +function testGetResourceURL() { + assertThrow(function() { + var resource = GM_getResourceURL('urlResourceName'); + }); +} + +function testGetResourceText() { + assertThrow(function() { + var resource = GM_getResourceText('textResourceName'); + }); +} + +function testAddStyle() { + var head = new ElementFake('head'); + document = new DocumentFake(head); + var css = 'color: #decaff;'; + GM_addStyle(css); + assert(1, head.childNodes.length); + var style = head.childNodes[0]; + assert("style", style.tagName); + assert("text/css", style.type); + assert(css, style.innerHTML); +} + +function testXmlhttpRequest() { + var xhr = GM_xmlhttpRequest({}); +} + +function testRegisterMenuCommand() { + assertThrow(function() { + GM_registerMenuCommand('name', function() {}, 'a', '', 'a'); + }); +} + +function testOpenInTab() { + var mockWindow = { + open: function(url, name, opt_options) { + this.openedUrl = url; + this.openedName = name; + this.openedOptions = opt_options; + } + }; + window = mockWindow; + var url = 'http://example.com'; + GM_openInTab(url); + assert(mockWindow.openedUrl, url); +} + +function testLog() { + var mockWindow = { + console: { + message: null, + log: function(message) { + this.message = message; + } + } + }; + window = mockWindow; + var message = 'hello world'; + GM_log(message); + assert(message, mockWindow.console.message); +} + +function LocalStorageFake() { + this.map_ = {}; + this.keys_ = []; +} +LocalStorageFake.prototype = { + length: 0, + key: function(index) { + if (index >= this.length) { + throw new Error('INDEX_SIZE_ERR'); + } + return this.keys_[index]; + }, + getItem: function(key) { + if (key in this.map_) { + return this.map_[key]; + } + return null; + }, + setItem: function(key, data) { + this.map_[key] = data; + this.updateKeys_(); + }, + removeItem: function(key) { + delete this.map_[key]; + this.updateKeys_(); + }, + clear: function() { + this.map_ = {}; + this.updateKeys_(); + }, + updateKeys_: function() { + var keys = []; + for (var key in this.map_) { + keys.push(key); + } + this.keys_ = keys; + this.length = keys.length; + } +} + +function DocumentFake(head) { + this.head_ = head; +} +DocumentFake.prototype = { + getElementsByTagName: function(tagName) { + if (tagName == 'head' && this.head_) { + return [this.head_]; + } + return []; + }, + createElement: function(tagName) { + return new ElementFake(tagName); + } +} + +function ElementFake(tagName) { + this.tagName = tagName; +} +ElementFake.prototype = { + childNodes: [], + appendChild: function(e) { + this.childNodes.push(e); + return e; + }, +} + +function assert(expected, actual) { + if (expected !== actual) { + throw new Error('Assert failed: "' + expected + '" !== "' + actual + '"'); + } +} + +function fail() { + throw new Error('Fail'); +} + +function assertThrow(f) { + var threw = false; + try { + f(); + } catch(e) { + threw = true; + } + if (!threw) { + throw new Error('Assert failed, expression did not throw.'); + } +} diff --git a/chrome/test/unit/unit_tests.scons b/chrome/test/unit/unit_tests.scons index 2a37fa6..c03c439 100644 --- a/chrome/test/unit/unit_tests.scons +++ b/chrome/test/unit/unit_tests.scons @@ -250,6 +250,7 @@ input_files = ChromeFileList([ '$CHROME_DIR/third_party/hunspell/google/hunspell_tests.cc', ]), MSVSFilter('renderer', [ + '$CHROME_DIR/renderer/extensions/greasemonkey_api_unittest.cc', '$CHROME_DIR/renderer/net/render_dns_master_unittest.cc', '$CHROME_DIR/renderer/net/render_dns_queue_unittest.cc', '$CHROME_DIR/renderer/render_view_unittest.cc', @@ -262,6 +263,8 @@ input_files = ChromeFileList([ '$CHROME_DIR/renderer/mock_render_process.h', '$CHROME_DIR/renderer/mock_render_thread.cc', '$CHROME_DIR/renderer/mock_render_thread.h', + '$CHROME_DIR/test/v8_unit_test.h', + '$CHROME_DIR/test/v8_unit_test.cc', ]), MSVSFilter('resources', [ '$CHROME_DIR/browser/browser_resources.h', diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj index 7c2daa2..6c58075 100644 --- a/chrome/test/unit/unittests.vcproj +++ b/chrome/test/unit/unittests.vcproj @@ -230,6 +230,14 @@ RelativePath="..\..\..\net\url_request\url_request_test_job.h" > </File> + <File + RelativePath="..\v8_unit_test.cc" + > + </File> + <File + RelativePath="..\v8_unit_test.h" + > + </File> </Filter> <Filter Name="hunspell" @@ -795,6 +803,10 @@ Name="renderer" > <File + RelativePath="..\..\renderer\extensions\greasemonkey_api_unittest.cc" + > + </File> + <File RelativePath="..\..\renderer\net\render_dns_master_unittest.cc" > </File> diff --git a/chrome/test/v8_unit_test.cc b/chrome/test/v8_unit_test.cc new file mode 100644 index 0000000..43db5f1 --- /dev/null +++ b/chrome/test/v8_unit_test.cc @@ -0,0 +1,93 @@ +// Copyright (c) 2009 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 <iostream> + +#include "base/string_util.h" +#include "chrome/test/v8_unit_test.h" + +void V8UnitTest::SetUp() { + v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); + global->Set(v8::String::New("log"), + v8::FunctionTemplate::New(&V8UnitTest::Log)); + context_ = v8::Context::New(NULL, global); +} + +void V8UnitTest::ExecuteScriptInContext(const StringPiece& script_source, + const StringPiece& script_name) { + v8::Context::Scope context_scope(context_); + v8::HandleScope handle_scope; + v8::Handle<v8::String> source = v8::String::New(script_source.data(), + script_source.size()); + v8::Handle<v8::String> name = v8::String::New(script_name.data(), + script_source.size()); + + v8::TryCatch try_catch; + v8::Handle<v8::Script> script = v8::Script::Compile(source, name); + // Ensure the script compiled without errors. + if (script.IsEmpty()) { + FAIL() << ExceptionToString(&try_catch); + } + v8::Handle<v8::Value> result = script->Run(); + // Ensure the script ran without errors. + if (result.IsEmpty()) { + FAIL() << ExceptionToString(&try_catch); + } +} + +std::string V8UnitTest::ExceptionToString(v8::TryCatch* try_catch) { + std::string str; + v8::HandleScope handle_scope; + v8::String::Utf8Value exception(try_catch->Exception()); + v8::Handle<v8::Message> message = try_catch->Message(); + if (message.IsEmpty()) { + str.append(StringPrintf("%s\n", *exception)); + } else { + v8::String::Utf8Value filename(message->GetScriptResourceName()); + int linenum = message->GetLineNumber(); + int colnum = message->GetStartColumn(); + str.append(StringPrintf("%s:%i:%i %s\n", *filename, linenum, colnum, + *exception)); + v8::String::Utf8Value sourceline(message->GetSourceLine()); + str.append(StringPrintf("%s\n", *sourceline)); + } + return str; +} + +void V8UnitTest::TestFunction(const std::string& function_name) { + v8::Context::Scope context_scope(context_); + v8::HandleScope handle_scope; + + v8::Handle<v8::Value> functionProperty = + context_->Global()->Get(v8::String::New(function_name.c_str())); + ASSERT_FALSE(functionProperty.IsEmpty()); + ASSERT_TRUE(functionProperty->IsFunction()); + v8::Handle<v8::Function> function = + v8::Handle<v8::Function>::Cast(functionProperty); + + v8::TryCatch try_catch; + v8::Handle<v8::Value> result = function->Call(context_->Global(), 0, NULL); + // The test fails if an exception was thrown. + if (result.IsEmpty()) { + FAIL() << ExceptionToString(&try_catch); + } +} + +// static +v8::Handle<v8::Value> V8UnitTest::Log(const v8::Arguments& args) { + std::string message; + bool first = true; + for (int i = 0; i < args.Length(); i++) { + v8::HandleScope handle_scope; + if (first) { + first = false; + } else { + message += " "; + } + v8::String::Utf8Value str(args[i]); + message += *str; + } + std::cout << message << "\n"; + return v8::Undefined(); +} diff --git a/chrome/test/v8_unit_test.h b/chrome/test/v8_unit_test.h new file mode 100644 index 0000000..5b4c4ce --- /dev/null +++ b/chrome/test/v8_unit_test.h @@ -0,0 +1,100 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_TEST_V8_UNIT_TEST_H_ +#define CHROME_TEST_V8_UNIT_TEST_H_ + +#include <string> + +#include "base/string_piece.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "v8/include/v8.h" + +// A superclass for unit tests that involve running JavaeScript. This class +// sets up V8 context and has methods that make it easy to execute scripts in +// this context as well as call functions in the context. +class V8UnitTest : public testing::Test { + public: + V8UnitTest() {} + virtual void SetUp(); + + protected: + // Executes the given script source in the context. The specified script + // name is used when reporting errors. + virtual void ExecuteScriptInContext(const StringPiece& script_source, + const StringPiece& script_name); + + // Converts a v8::TryCatch into a human readable string. + virtual std::string ExceptionToString(v8::TryCatch* try_catch); + + // Calls the specified function that resides in the global scope of the + // context. If the function throws an exception, FAIL() is called to + // indicate a unit test failure. This is useful for executing unit test + // functions implemented in JavaScript. + virtual void TestFunction(const std::string& function_name); + + // This method is bound to a global function "log" in the context. + // Scripts running in the context can call this to print out logging + // information to the console. + static v8::Handle<v8::Value> Log(const v8::Arguments& args); + + // Handle scope that is used throughout the life of this class. + v8::HandleScope handle_scope_; + + // Context for the JavaScript in the test. + v8::Handle<v8::Context> context_; +}; + +#endif // CHROME_TEST_V8_UNIT_TEST_H_ + +// Copyright (c) 2009 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. + +#ifndef CHROME_TEST_V8_UNIT_TEST_H_ +#define CHROME_TEST_V8_UNIT_TEST_H_ + +#include <string> + +#include "base/string_piece.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "v8/include/v8.h" + +// A superclass for unit tests that involve running JavaeScript. This class +// sets up V8 context and has methods that make it easy to execute scripts in +// this context as well as call functions in the context. +class V8UnitTest : public testing::Test { + public: + V8UnitTest() {} + virtual void SetUp(); + + protected: + // Executes the given script source in the context. The specified script + // name is used when reporting errors. + virtual void ExecuteScriptInContext(const StringPiece& script_source, + const StringPiece& script_name); + + // Converts a v8::TryCatch into a human readable string. + virtual std::string ExceptionToString(v8::TryCatch* try_catch); + + // Calls the specified function that resides in the global scope of the + // context. If the function throws an exception, FAIL() is called to + // indicate a unit test failure. This is useful for executing unit test + // functions implemented in JavaScript. + virtual void TestFunction(const std::string& function_name); + + // This method is bound to a global function "log" in the context. + // Scripts running in the context can call this to print out logging + // information to the console. + static v8::Handle<v8::Value> Log(const v8::Arguments& args); + + // Handle scope that is used throughout the life of this class. + v8::HandleScope handle_scope_; + + // Context for the JavaScript in the test. + v8::Handle<v8::Context> context_; +}; + +#endif // CHROME_TEST_V8_UNIT_TEST_H_ + |