summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-16 01:38:49 +0000
committeraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-16 01:38:49 +0000
commit7896d694115cb2e64971ed0df60f61e3df2f7375 (patch)
treec2778927f7bbef5ee5c1e7cae3a941b097e139ae
parent6fa342707a9979b60fa0321f27983d170d728c66 (diff)
downloadchromium_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.pbxproj10
-rwxr-xr-xchrome/renderer/extensions/greasemonkey_api_unittest.cc89
-rw-r--r--chrome/renderer/resources/greasemonkey_api.js56
-rw-r--r--chrome/test/DEPS1
-rw-r--r--chrome/test/data/extensions/greasemonkey_api_test.js408
-rw-r--r--chrome/test/unit/unit_tests.scons3
-rw-r--r--chrome/test/unit/unittests.vcproj12
-rw-r--r--chrome/test/v8_unit_test.cc93
-rw-r--r--chrome/test/v8_unit_test.h100
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_
+