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 | |
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
-rw-r--r-- | chrome/chrome.xcodeproj/project.pbxproj | 10 | ||||
-rwxr-xr-x | chrome/renderer/extensions/greasemonkey_api_unittest.cc | 89 | ||||
-rw-r--r-- | chrome/renderer/resources/greasemonkey_api.js | 56 | ||||
-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 |
9 files changed, 766 insertions, 6 deletions
diff --git a/chrome/chrome.xcodeproj/project.pbxproj b/chrome/chrome.xcodeproj/project.pbxproj index 2ee4f1a..e4e3046 100644 --- a/chrome/chrome.xcodeproj/project.pbxproj +++ b/chrome/chrome.xcodeproj/project.pbxproj @@ -250,6 +250,7 @@ 71784BA931A610A94A7FEDC9 /* history_querying_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BF9F50E9D48F7009A6919 /* history_querying_unittest.cc */; }; 7442556F908264C52BEBFE4D /* starred_url_database_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFA090E9D48F7009A6919 /* starred_url_database_unittest.cc */; }; 7F84A3FF0F6102F46E0E5155 /* history_publisher.cc in Sources */ = {isa = PBXBuildFile; fileRef = 269003C4E493789D82B6B0F9 /* history_publisher.cc */; }; + 81B6908E490BFDF37C4BA7CE /* v8_unit_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 759B3A1B81E261598122B03B /* v8_unit_test.cc */; }; 81E4783DE6F497B9BCC5B9F6 /* bookmark_model.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3D00CDB6C665E7ED1A1090D7 /* bookmark_model.cc */; }; 8268477E0F2F69C8009F6555 /* profile_manager_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BF8E60E9D4839009A6919 /* profile_manager_unittest.cc */; }; 8268477F0F2F69D1009F6555 /* profile_manager.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BF8E40E9D4839009A6919 /* profile_manager.cc */; }; @@ -357,6 +358,7 @@ BAC2B8AD0F436C7F0063A33E /* cross_site_resource_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = A7C613DC0F30D886008CEE5D /* cross_site_resource_handler.cc */; }; BADB8B710F3A35AC00989B26 /* resource_dispatcher_host.cc in Sources */ = {isa = PBXBuildFile; fileRef = BADB8B6D0F3A356000989B26 /* resource_dispatcher_host.cc */; }; C53EBF7E1E204C3780FD4A7D /* ssl_policy.cc in Sources */ = {isa = PBXBuildFile; fileRef = B5D16ED10F21451600861FAC /* ssl_policy.cc */; }; + C6C256C56BA114D53849523F /* greasemonkey_api_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1F43A7C85092C58AAF011F78 /* greasemonkey_api_unittest.cc */; }; C8F5EB819EA38CE9D50AF5B5 /* history.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BF9EC0E9D48F7009A6919 /* history.cc */; }; CA81330E0F423C4D0096C1DC /* query_parser_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFA020E9D48F7009A6919 /* query_parser_unittest.cc */; }; CA8133120F423C710096C1DC /* url_database_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFA150E9D48F7009A6919 /* url_database_unittest.cc */; }; @@ -1820,6 +1822,7 @@ 0082A7520F16987A000AA0EF /* user_script_master.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = user_script_master.h; sourceTree = "<group>"; }; 05C9D404FC8116984CCCEDED /* base_session_service.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = base_session_service.cc; path = browser/sessions/base_session_service.cc; sourceTree = SOURCE_ROOT; }; 0B7CC9C105E90E0665852528 /* url_fetcher_protect.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = url_fetcher_protect.cc; sourceTree = "<group>"; }; + 1F43A7C85092C58AAF011F78 /* greasemonkey_api_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = greasemonkey_api_unittest.cc; path = renderer/extensions/greasemonkey_api_unittest.cc; sourceTree = SOURCE_ROOT; }; 269003C4E493789D82B6B0F9 /* history_publisher.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = history_publisher.cc; sourceTree = "<group>"; }; 26D97CE692D919FEB1521E43 /* ssl_error_info.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ssl_error_info.cc; path = ssl/ssl_error_info.cc; sourceTree = "<group>"; }; 28AA584AB2ECFB33C7C7FD8A /* template_url_parser.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = template_url_parser.cc; sourceTree = "<group>"; }; @@ -1835,6 +1838,7 @@ 3D00CDB6C665E7ED1A1090D7 /* bookmark_model.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bookmark_model.cc; path = bookmarks/bookmark_model.cc; sourceTree = "<group>"; }; 433B6EFB7A1D931A13C9556F /* url_fetcher_protect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = url_fetcher_protect.h; sourceTree = "<group>"; }; 4590B61B84978823B2BADA68 /* file_descriptor_set_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_descriptor_set_posix.cc; sourceTree = "<group>"; }; + 4AF0AB80CD8E1D34AC0D5C51 /* v8_unit_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = v8_unit_test.h; sourceTree = "<group>"; }; 4D1F59EA0F2A6B590040C1E3 /* image_diff */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = image_diff; sourceTree = BUILT_PRODUCTS_DIR; }; 4D1F59FD0F2A6BBB0040C1E3 /* image_diff.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = image_diff.cc; sourceTree = "<group>"; }; 4D1F5AB10F2A6EE90040C1E3 /* libpng.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libpng.xcodeproj; path = third_party/libpng/libpng.xcodeproj; sourceTree = "<group>"; }; @@ -2396,6 +2400,7 @@ 629BF493DEA096E2DD844F2B /* autofill_manager.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = autofill_manager.cc; sourceTree = "<group>"; }; 6447F24FADC63E58A44DB762 /* url_pattern.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = url_pattern.cc; path = extensions/url_pattern.cc; sourceTree = "<group>"; }; 699499C4FBA07FB2D7B298A2 /* user_script.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = user_script.cc; path = extensions/user_script.cc; sourceTree = "<group>"; }; + 759B3A1B81E261598122B03B /* v8_unit_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = v8_unit_test.cc; sourceTree = "<group>"; }; 778D7927798B7E3FAA498D3D /* url_fetcher.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = url_fetcher.cc; sourceTree = "<group>"; }; 7849CCC221723C1BC14D6384 /* history_publisher_none.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = history_publisher_none.cc; sourceTree = "<group>"; }; 8104B4AFD95DCA06B2F37551 /* chrome_paths_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = chrome_paths_internal.h; sourceTree = "<group>"; }; @@ -3100,6 +3105,7 @@ 35AC9D9A03545594C102C5C1 /* session_backend.cc */, C3F9577C67188A5A4E3A6E41 /* session_restore.cc */, 05C9D404FC8116984CCCEDED /* base_session_service.cc */, + 1F43A7C85092C58AAF011F78 /* greasemonkey_api_unittest.cc */, ); sourceTree = "<group>"; }; @@ -3815,6 +3821,8 @@ 4D7BFCED0E9D4DFE009A6919 /* unit */, 826858F80F326FA3009F6555 /* testing_profile.cc */, 826858F90F326FA3009F6555 /* testing_profile.h */, + 759B3A1B81E261598122B03B /* v8_unit_test.cc */, + 4AF0AB80CD8E1D34AC0D5C51 /* v8_unit_test.h */, ); path = test; sourceTree = "<group>"; @@ -5500,6 +5508,7 @@ A54612DC0EE9958600A8EE5D /* extensions_service_unittest.cc in Sources */, 331218300F3C010A006CB2B0 /* external_js_object.cc in Sources */, 97DD178B77011735FE4399E9 /* file_descriptor_set_unittest.cc in Sources */, + C6C256C56BA114D53849523F /* greasemonkey_api_unittest.cc in Sources */, 0EE123B79B750A2FCEFB4569 /* history_backend_unittest.cc in Sources */, 71784BA931A610A94A7FEDC9 /* history_querying_unittest.cc in Sources */, 4D7BFB3C0E9D4C25009A6919 /* history_types_unittest.cc in Sources */, @@ -5547,6 +5556,7 @@ CA8133120F423C710096C1DC /* url_database_unittest.cc in Sources */, 8570EB3F140C07ABF1957F12 /* url_pattern_unittest.cc in Sources */, A0EB956531B9DB1E40DAE980 /* user_script_unittest.cc in Sources */, + 81B6908E490BFDF37C4BA7CE /* v8_unit_test.cc in Sources */, B502DA280F098056005BE90C /* visit_database_unittest.cc in Sources */, 4D7BFB420E9D4C35009A6919 /* visit_tracker_unittest.cc in Sources */, E46C4B4C0F21098F00B393B8 /* worker_thread_ticker_unittest.cc in Sources */, diff --git a/chrome/renderer/extensions/greasemonkey_api_unittest.cc b/chrome/renderer/extensions/greasemonkey_api_unittest.cc new file mode 100755 index 0000000..fef69a8 --- /dev/null +++ b/chrome/renderer/extensions/greasemonkey_api_unittest.cc @@ -0,0 +1,89 @@ +// 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 "base/file_util.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/test/v8_unit_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "grit/renderer_resources.h" + +// TODO(port) +#if defined(OS_WIN) + +static const char kGreasemonkeyApi[] = "greasemonkey_api.js"; +static const char kGreasemonkeyApiTest[] = "greasemonkey_api_test.js"; + +class GreasemonkeyApiTest : public V8UnitTest { + public: + GreasemonkeyApiTest() {} + + virtual void SetUp() { + V8UnitTest::SetUp(); + + // Add the greasemonkey api to the context. + StringPiece api_js = + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_GREASEMONKEY_API_JS); + ExecuteScriptInContext(api_js, kGreasemonkeyApi); + + // Add the test functions to the context. + std::wstring test_js_file_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_js_file_path)); + file_util::AppendToPath(&test_js_file_path, L"extensions"); + file_util::AppendToPath(&test_js_file_path, + UTF8ToWide(kGreasemonkeyApiTest)); + std::string test_js; + ASSERT_TRUE(file_util::ReadFileToString(test_js_file_path, &test_js)); + ExecuteScriptInContext(test_js, kGreasemonkeyApiTest); + } +}; + +TEST_F(GreasemonkeyApiTest, GetSetValue) { + TestFunction("testGetSetValue"); +} + +TEST_F(GreasemonkeyApiTest, DeleteValue) { + TestFunction("testDeleteValue"); +} + +TEST_F(GreasemonkeyApiTest, ListValues) { + TestFunction("testListValues"); +} + +TEST_F(GreasemonkeyApiTest, GetResourceURL) { + TestFunction("testGetResourceURL"); +} + +TEST_F(GreasemonkeyApiTest, GetResourceText) { + TestFunction("testGetResourceText"); +} + +TEST_F(GreasemonkeyApiTest, AddStyle) { + TestFunction("testAddStyle"); +} + +/* +TEST_F(GreasemonkeyApiTest, XmlhttpRequest) { + TestFunction("testXmlhttpRequest"); +} +*/ + +TEST_F(GreasemonkeyApiTest, RegisterMenuCommand) { + TestFunction("testRegisterMenuCommand"); +} + +TEST_F(GreasemonkeyApiTest, OpenInTab) { + TestFunction("testOpenInTab"); +} + +TEST_F(GreasemonkeyApiTest, Log) { + TestFunction("testLog"); +} + +#endif // #if defined(OSWIN) + diff --git a/chrome/renderer/resources/greasemonkey_api.js b/chrome/renderer/resources/greasemonkey_api.js index 2d791dd..57fa252 100644 --- a/chrome/renderer/resources/greasemonkey_api.js +++ b/chrome/renderer/resources/greasemonkey_api.js @@ -1,20 +1,57 @@ // Implementation of the Greasemonkey API, see: // http://wiki.greasespot.net/Greasemonkey_Manual:APIs +const MIN_INT_32 = -0x80000000; +const MAX_INT_32 = 0x7FFFFFFF; + +// Prefix for user script values that are stored in localStorage. +const STORAGE_NS = "__userscript__."; + function GM_getValue(name, defaultValue) { - throw new Error("not implemented."); + var value = localStorage.getItem(STORAGE_NS + name); + return value ? value : defaultValue; } function GM_setValue(name, value) { - throw new Error("not implemented."); + // The values for GM_getValue() and GM_setValue() can only be boolean, + // strings, or 32 bit integers. See the setPrefs function in: + // http://greasemonkey.devjavu.com/browser/trunk/src/chrome/chromeFiles/content/prefmanager.js + var goodType = false; + switch (typeof(value)) { + case "string": + case "boolean": + goodType = true; + break; + case "number": + // Note that "value % 1 == 0" checks that the number is not a float. + if (value % 1 == 0 && value >= MIN_INT_32 && value <= MAX_INT_32) { + goodType = true; + } + break; + } + + if (!goodType) { + throw new Error("Unsupported type for GM_setValue. Supported types " + + "are: string, bool, and 32 bit integers."); + } + + localStorage.setItem(STORAGE_NS + name, value); } function GM_deleteValue(name) { - throw new Error("not implemented."); + localStorage.removeItem(STORAGE_NS + name); } function GM_listValues() { - throw new Error("not implemented."); + var values = []; + for (var i = 0; i < localStorage.length; i++) { + var key = localStorage.key(i); + if (key.indexOf(STORAGE_NS) == 0) { + key = key.substring(STORAGE_NS.length); + values.push(key); + } + } + return values; } function GM_getResourceURL(resourceName) { @@ -26,7 +63,14 @@ function GM_getResourceText(resourceName) { } function GM_addStyle(css) { - throw new Error("not implemented."); + var head = document.getElementsByTagName("head")[0]; + if (!head) { + return; + } + var style = document.createElement("style"); + style.type = "text/css"; + style.innerHTML = css; + head.appendChild(style); } function GM_xmlhttpRequest(details) { @@ -39,7 +83,7 @@ function GM_registerMenuCommand(commandName, commandFunc, accelKey, } function GM_openInTab(url) { - throw new Error("not implemented."); + window.open(url, ""); } function GM_log(message) { 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_ + |