From b2238b473ae496a9627069433a92cc3dfba361e8 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Wed, 4 Dec 2013 18:02:21 +0100 Subject: [PATCH 01/22] Added the google font locally, no connection to google required any more --- .../aardvark/frontend/css/openSansFont.css | 36 ++ .../frontend/fonts/opensans/OpenSans.woff | Bin 0 -> 15936 bytes .../frontend/fonts/opensans/OpenSansBold.woff | Bin 0 -> 15836 bytes .../fonts/opensans/OpenSansBoldItalic.woff | Bin 0 -> 16328 bytes .../fonts/opensans/OpenSansItalic.woff | Bin 0 -> 16592 bytes .../fonts/opensans/OpenSansLight.woff | Bin 0 -> 15868 bytes .../fonts/opensans/OpenSansLightItalic.woff | Bin 0 -> 16792 bytes .../js/graphViewer/ui/graphViewerUI.js | 309 +++++++----------- js/apps/system/aardvark/index.html | 2 +- js/apps/system/aardvark/manifest.json | 3 +- 10 files changed, 163 insertions(+), 187 deletions(-) create mode 100644 js/apps/system/aardvark/frontend/css/openSansFont.css create mode 100644 js/apps/system/aardvark/frontend/fonts/opensans/OpenSans.woff create mode 100644 js/apps/system/aardvark/frontend/fonts/opensans/OpenSansBold.woff create mode 100644 js/apps/system/aardvark/frontend/fonts/opensans/OpenSansBoldItalic.woff create mode 100644 js/apps/system/aardvark/frontend/fonts/opensans/OpenSansItalic.woff create mode 100644 js/apps/system/aardvark/frontend/fonts/opensans/OpenSansLight.woff create mode 100644 js/apps/system/aardvark/frontend/fonts/opensans/OpenSansLightItalic.woff diff --git a/js/apps/system/aardvark/frontend/css/openSansFont.css b/js/apps/system/aardvark/frontend/css/openSansFont.css new file mode 100644 index 0000000000..b5d5cbf9ca --- /dev/null +++ b/js/apps/system/aardvark/frontend/css/openSansFont.css @@ -0,0 +1,36 @@ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + src: local('Open Sans Light'), local('OpenSans-Light'), url(../fonts/opensans/OpenSansLight.woff) format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans'), local('OpenSans'), url(../fonts/opensans/OpenSans.woff) format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Bold'), local('OpenSans-Bold'), url(../fonts/opensans/OpenSansBold.woff) format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + src: local('Open Sans Light Italic'), local('OpenSansLight-Italic'), url(../fonts/opensans/OpenSansLightItalic.woff) format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + src: local('Open Sans Italic'), local('OpenSans-Italic'), url(../fonts/opensans/OpenSansItalic.woff) format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 700; + src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(../fonts/opensans/OpenSansBoldItalic.woff) format('woff'); +} diff --git a/js/apps/system/aardvark/frontend/fonts/opensans/OpenSans.woff b/js/apps/system/aardvark/frontend/fonts/opensans/OpenSans.woff new file mode 100644 index 0000000000000000000000000000000000000000..58e6cb38180fc8389b73569724935e791021ab94 GIT binary patch literal 15936 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 DA1mbM literal 0 HcmV?d00001 diff --git a/js/apps/system/aardvark/frontend/fonts/opensans/OpenSansBold.woff b/js/apps/system/aardvark/frontend/fonts/opensans/OpenSansBold.woff new file mode 100644 index 0000000000000000000000000000000000000000..a8b5bc261e2d20d18c8fcda6f65bb7c52cae3804 GIT binary patch literal 15836 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

YqG3j~z}n`l>VV$L_nIi)>HP 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 literal 0 HcmV?d00001 diff --git a/js/apps/system/aardvark/frontend/fonts/opensans/OpenSansItalic.woff b/js/apps/system/aardvark/frontend/fonts/opensans/OpenSansItalic.woff new file mode 100644 index 0000000000000000000000000000000000000000..039a294953ae18f154a91ca7407cbe9d12f37cb3 GIT binary patch literal 16592 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 literal 0 HcmV?d00001 diff --git a/js/apps/system/aardvark/frontend/fonts/opensans/OpenSansLight.woff b/js/apps/system/aardvark/frontend/fonts/opensans/OpenSansLight.woff new file mode 100644 index 0000000000000000000000000000000000000000..8025ba6cb5cbd7c1b444b27b4331efa4eec454f5 GIT binary patch literal 15868 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 literal 0 HcmV?d00001 diff --git a/js/apps/system/aardvark/frontend/fonts/opensans/OpenSansLightItalic.woff b/js/apps/system/aardvark/frontend/fonts/opensans/OpenSansLightItalic.woff new file mode 100644 index 0000000000000000000000000000000000000000..c1e29dc534e750ffaa6c641c0f9ab369765daf63 GIT binary patch literal 16792 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 - + diff --git a/js/apps/system/aardvark/manifest.json b/js/apps/system/aardvark/manifest.json index 39d91675de..d62b11b581 100644 --- a/js/apps/system/aardvark/manifest.json +++ b/js/apps/system/aardvark/manifest.json @@ -75,7 +75,7 @@ "frontend/js/lib/handlebars-1.0.rc.1.js", "frontend/js/lib/underscore.js", "frontend/js/lib/backbone.js", - "frontend/js/lib/d3.v3.js", + "frontend/js/lib/d3.v3.min.js", "frontend/js/lib/nv.d3.js", "frontend/js/lib/d3.fisheye.js", "frontend/js/lib/ColVis.js", @@ -93,6 +93,7 @@ "css/style.css": { "files": [ + "frontend/css/openSansFont.css", "frontend/css/swagger/hightlight.default.css", "frontend/css/bootstrap.css", "frontend/css/arangodbIcons.css", From 87e056ea2547d90144a32b57be40efb9d91d2039 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Wed, 4 Dec 2013 18:19:02 +0100 Subject: [PATCH 02/22] Fixed karma config, it now requires d3.v3.min instead of d3.v3. There is inconsistent behaviour between these two --- js/apps/system/aardvark/test/karma/karma.conf.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/js/apps/system/aardvark/test/karma/karma.conf.js b/js/apps/system/aardvark/test/karma/karma.conf.js index 15abb81f54..81ed4441fc 100644 --- a/js/apps/system/aardvark/test/karma/karma.conf.js +++ b/js/apps/system/aardvark/test/karma/karma.conf.js @@ -32,7 +32,7 @@ module.exports = function(karma) { 'frontend/js/lib/handlebars-1.0.rc.1.js', 'frontend/js/lib/underscore.js', 'frontend/js/lib/backbone.js', - 'frontend/js/lib/d3.v3.js', + 'frontend/js/lib/d3.v3.min.js', 'frontend/js/lib/nv.d3.js', 'frontend/js/lib/d3.fisheye.js', 'frontend/js/lib/ejs_0.9_alpha_1_production.js', @@ -173,7 +173,6 @@ module.exports = function(karma) { {pattern: 'frontend/js/templates/*.ejs', served:true, included:false, watched: true}, // Specs - // GraphViewer 'test/specs/graphViewer/specColourMapper/colourMapperSpec.js', 'test/specs/graphViewer/specWindowObjects/domObserverFactorySpec.js', @@ -195,7 +194,7 @@ module.exports = function(karma) { 'test/specs/graphViewer/specEvents/eventDispatcherSpec.js', // 'test/specs/graphViewer/specEvents/eventDispatcherUISpec.js', // 'test/specs/graphViewer/specZoomManager/zoomManagerSpec.js', - // 'test/specs/graphViewer/specGraphViewer/graphViewerSpec.js', + 'test/specs/graphViewer/specGraphViewer/graphViewerSpec.js', 'test/specs/graphViewer/specGraphViewer/graphViewerUISpec.js', 'test/specs/graphViewer/specGraphViewer/graphViewerWidgetSpec.js', 'test/specs/graphViewer/specGraphViewer/graphViewerPreviewSpec.js', @@ -221,7 +220,6 @@ module.exports = function(karma) { // Router 'test/specs/router/routerSpec.js', - // JSLint 'test/specJSLint/jsLintSpec.js' ], From 0c4b20d4cbc0352d4bd01446bd80c1b5a6b344fd Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Wed, 4 Dec 2013 18:26:45 +0100 Subject: [PATCH 03/22] Internal modification of the graph viewer ui --- .../js/graphViewer/ui/graphViewerUI.js | 321 +++++++++++------- .../specAdapter/arangoAdapterUISpec.js | 2 + 2 files changed, 193 insertions(+), 130 deletions(-) diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerUI.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerUI.js index 00372f88b8..06edd75a2f 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerUI.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerUI.js @@ -43,9 +43,8 @@ function GraphViewerUI(container, adapterConfig, optWidth, optHeight, viewerConf throw "An adapter configuration has to be given"; } - var self = this, - graphViewer, - width = (optWidth || container.offsetWidth) - 60, + var graphViewer, + width = (optWidth || container.offsetWidth) - 81, height = optHeight || container.offsetHeight, menubar = document.createElement("ul"), background = document.createElement("div"), @@ -76,34 +75,182 @@ function GraphViewerUI(container, adapterConfig, optWidth, optHeight, viewerConf return list; }, - makeConfigure = function (div, id) { - var ul, li, a, span, list; - div.className = "pagination pagination-small pagination-right btn-group"; - ul = document.createElement("ul"); - li = document.createElement("li"); - li.className = "enabled"; - a = document.createElement("a"); - a.id = id; - a.className = "arangoHeaderA"; - span = document.createElement("span"); - span.className = "glyphicon glyphicon-cog"; - span.title = "Configure"; + makeFilterDiv = function() { + var + div = document.createElement("div"), + innerDiv = document.createElement("div"), + queryLine = document.createElement("div"), + searchAttrDiv = document.createElement("div"), + searchAttrExampleToggle = document.createElement("button"), + searchAttrExampleCaret = document.createElement("span"), + searchValueField = document.createElement("input"), + searchStart = document.createElement("img"), + equalsField = document.createElement("span"), + + showSpinner = function() { + $(background).css("cursor", "progress"); + }, + + hideSpinner = function() { + $(background).css("cursor", ""); + }, + + alertError = function(msg) { + window.alert(msg); + }, + + resultCB = function(res) { + hideSpinner(); + if (res && res.errorCode && res.errorCode === 404) { + alertError("Sorry could not find a matching node."); + return; + } + return; + }, + + searchFunction = function() { + showSpinner(); + if (searchAttrField.value === "" + || searchAttrField.value === undefined) { + graphViewer.loadGraph( + searchValueField.value, + resultCB + ); + } else { + graphViewer.loadGraphWithAttributeValue( + searchAttrField.value, + searchValueField.value, + resultCB + ); + } + }; + + div.id = "filterDropdown"; + div.className = "headerDropdown"; + innerDiv.className = "dropdownInner"; + queryLine.className = "queryline"; + + searchAttrField = document.createElement("input"); + searchAttrExampleList = document.createElement("ul"); + + searchAttrDiv.className = "pull-left input-append searchByAttribute"; + searchAttrField.id = "attribute"; + //searchAttrField.className = "input"; + searchAttrField.type = "text"; + searchAttrField.placeholder = "Attribute name"; + searchAttrExampleToggle.id = "attribute_example_toggle"; + searchAttrExampleToggle.className = "btn gv_example_toggle"; + searchAttrExampleCaret.className = "caret gv_caret"; + searchAttrExampleList.className = "dropdown-menu"; + searchValueField.id = "value"; + searchValueField.className = "searchInput"; + //searchValueField.className = "filterValue"; + searchValueField.type = "text"; + searchValueField.placeholder = "Attribute value"; + searchStart.id = "loadnode"; + searchStart.className = "searchSubmit"; + equalsField.className = "searchEqualsLabel"; + equalsField.appendChild(document.createTextNode("==")); + + innerDiv.appendChild(queryLine); + queryLine.appendChild(searchAttrDiv); + + searchAttrDiv.appendChild(searchAttrField); + searchAttrDiv.appendChild(searchAttrExampleToggle); + searchAttrDiv.appendChild(searchAttrExampleList); + searchAttrExampleToggle.appendChild(searchAttrExampleCaret); + queryLine.appendChild(equalsField); + queryLine.appendChild(searchValueField); + queryLine.appendChild(searchStart); + + searchStart.onclick = searchFunction; + $(searchValueField).keypress(function(e) { + if (e.keyCode === 13 || e.which === 13) { + searchFunction(); + return false; + } + }); - div.appendChild(ul); - ul.appendChild(li); - li.appendChild(a); - a.appendChild(span); - - list = document.createElement("ul"); - list.className = "dropdown-menu gv_configure_menu"; - list.style.display = "none"; - div.appendChild(list); - - a.onclick = function () { - $(list).slideToggle(200); + searchAttrExampleToggle.onclick = function() { + $(searchAttrExampleList).slideToggle(200); }; - return list; + div.appendChild(innerDiv); + return div; + }, + + makeConfigureDiv = function () { + var div, innerDiv, nodeList, nodeHeader, colList, colHeader; + div = document.createElement("div"); + div.id = "configureDropdown"; + div.className = "headerDropdown"; + innerDiv = document.createElement("div"); + innerDiv.className = "dropdownInner"; + nodeList = document.createElement("ul"); + nodeHeader = document.createElement("li"); + nodeHeader.className = "nav-header"; + nodeHeader.appendChild(document.createTextNode("Vertices")); + colList = document.createElement("ul"); + colHeader = document.createElement("li"); + colHeader.className = "nav-header"; + colHeader.appendChild(document.createTextNode("Connection")); + nodeList.appendChild(nodeHeader); + colList.appendChild(colHeader); + innerDiv.appendChild(nodeList); + innerDiv.appendChild(colList); + div.appendChild(innerDiv); + return { + configure: div, + nodes: nodeList, + col: colList + }; + }, + + makeConfigure = function (div, idConf, idFilter) { + var ul, liConf, aConf, spanConf, liFilter, aFilter, spanFilter, lists; + div.className = "pagination pagination-small pagination-right btn-group"; + ul = document.createElement("ul"); + liConf = document.createElement("li"); + liConf.className = "enabled"; + aConf = document.createElement("a"); + aConf.id = idConf; + aConf.className = "arangoHeaderA"; + spanConf = document.createElement("span"); + spanConf.className = "glyphicon glyphicon-cog"; + $(spanConf).attr("data-original-title", "Configure"); + + liFilter = document.createElement("li"); + liFilter.className = "enabled"; + aFilter = document.createElement("a"); + aFilter.id = idFilter; + aFilter.className = "arangoHeaderA"; + spanFilter = document.createElement("span"); + spanFilter.className = "glyphicon glyphicon-filter"; + $(spanFilter).attr("data-original-title", "Filter"); + + div.appendChild(ul); + + ul.appendChild(liConf); + liConf.appendChild(aConf); + aConf.appendChild(spanConf); + + ul.appendChild(liFilter); + liFilter.appendChild(aFilter); + aFilter.appendChild(spanFilter); + + lists = makeConfigureDiv(); + lists.filter = makeFilterDiv(); + aConf.onclick = function () { + $(lists.configure).slideToggle(200); + $(lists.filter).hide(); + }; + + aFilter.onclick = function () { + $(lists.filter).slideToggle(200); + $(lists.configure).hide(); + }; + + return lists; }, createSVG = function () { @@ -112,7 +259,7 @@ function GraphViewerUI(container, adapterConfig, optWidth, optHeight, viewerConf .attr("id", "graphViewerSVG") .attr("width",width) .attr("height",height) - .attr("class", "graphViewer pull-right") + .attr("class", "graphViewer") .style("width", width + "px") .style("height", height + "px"); }, @@ -152,9 +299,9 @@ function GraphViewerUI(container, adapterConfig, optWidth, optHeight, viewerConf slider = document.createElement("div"); slider.id = "gv_zoom_slider"; slider.className = "gv_zoom_slider"; - + background.appendChild(zoomUI); - zoomUI.appendChild(zoomButtons); + background.insertBefore(zoomUI, svg[0][0]); zoomUI.appendChild(slider); $( "#gv_zoom_slider" ).slider({ @@ -179,8 +326,8 @@ function GraphViewerUI(container, adapterConfig, optWidth, optHeight, viewerConf graphViewer.dispatcherConfig ); toolbox.id = "toolbox"; - toolbox.className = "btn-group btn-group-vertical pull-left toolbox"; - background.appendChild(toolbox); + toolbox.className = "btn-group btn-group-vertical toolbox"; + background.insertBefore(toolbox, svg[0][0]); dispatcherUI.addAll(); // Default selection $("#control_event_expand").click(); @@ -206,6 +353,7 @@ function GraphViewerUI(container, adapterConfig, optWidth, optHeight, viewerConf searchAttrExampleList.appendChild(entry); entry.onclick = function() { searchAttrField.value = r; + $(searchAttrExampleList).slideToggle(200); }; }); }); @@ -213,19 +361,13 @@ function GraphViewerUI(container, adapterConfig, optWidth, optHeight, viewerConf createMenu = function() { var transparentHeader = document.createElement("div"), - searchDiv = document.createElement("div"), - searchAttrDiv = document.createElement("div"), - searchAttrExampleToggle = document.createElement("button"), - searchAttrExampleCaret = document.createElement("span"), - searchValueField = document.createElement("input"), - searchStart = document.createElement("img"), buttons = document.createElement("div"), - equalsField = document.createElement("span"), configureDropDown = document.createElement("div"), - configureList = makeConfigure( + configureLists = makeConfigure( configureDropDown, - "configuredropdown" - ), + "configuredropdown", + "filterdropdown" + ); /* nodeShaperDropDown = document.createElement("div"), @@ -270,56 +412,17 @@ function GraphViewerUI(container, adapterConfig, optWidth, optHeight, viewerConf ), */ - showSpinner = function() { - $(background).css("cursor", "progress"); - }, - hideSpinner = function() { - $(background).css("cursor", ""); - }, - - alertError = function(msg) { - window.alert(msg); - }, - - resultCB = function(res) { - hideSpinner(); - if (res && res.errorCode && res.errorCode === 404) { - alertError("Sorry could not find a matching node."); - return; - } - return; - }, - - searchFunction = function() { - showSpinner(); - if (searchAttrField.value === "" - || searchAttrField.value === undefined) { - graphViewer.loadGraph( - searchValueField.value, - resultCB - ); - } else { - graphViewer.loadGraphWithAttributeValue( - searchAttrField.value, - searchValueField.value, - resultCB - ); - } - }; nodeShaperUI = new NodeShaperControls( - configureList, + configureLists.nodes, graphViewer.nodeShaper ); adapterUI = new ArangoAdapterControls( - configureList, + configureLists.col, graphViewer.adapter ); - searchAttrField = document.createElement("input"); - searchAttrExampleList = document.createElement("ul"); - menubar.id = "menubar"; menubar.className = "thumbnails2"; @@ -328,27 +431,6 @@ function GraphViewerUI(container, adapterConfig, optWidth, optHeight, viewerConf buttons.id = "modifiers"; buttons.className = "pull-right"; - searchDiv.id = "transparentPlaceholder"; - searchDiv.className = "pull-left"; - - searchAttrDiv.className = "pull-left input-append searchByAttribute"; - searchAttrField.id = "attribute"; - searchAttrField.className = "input"; - searchAttrField.type = "text"; - searchAttrField.placeholder = "Attribute"; - searchAttrExampleToggle.id = "attribute_example_toggle"; - searchAttrExampleToggle.className = "btn gv_example_toggle"; - searchAttrExampleToggle.setAttribute("data-toggle", "dropdown"); - searchAttrExampleCaret.className = "caret gv_caret"; - searchAttrExampleList.className = "dropdown-menu"; - searchValueField.id = "value"; - searchValueField.className = "input-xlarge searchInput"; - searchValueField.type = "text"; - searchValueField.placeholder = "Value"; - searchStart.id = "loadnode"; - searchStart.className = "searchSubmit"; - equalsField.className = "searchEqualsLabel"; - equalsField.appendChild(document.createTextNode("==")); /* nodeShaperDropDown.id = "nodeshapermenu"; @@ -359,25 +441,9 @@ function GraphViewerUI(container, adapterConfig, optWidth, optHeight, viewerConf configureDropDown.id = "configuremenu"; - searchStart.onclick = searchFunction; - $(searchValueField).keypress(function(e) { - if (e.keyCode === 13 || e.which === 13) { - searchFunction(); - return false; - } - - }); - menubar.appendChild(transparentHeader); - transparentHeader.appendChild(searchDiv); - searchDiv.appendChild(searchAttrDiv); - searchAttrDiv.appendChild(searchAttrField); - searchAttrDiv.appendChild(searchAttrExampleToggle); - searchAttrDiv.appendChild(searchAttrExampleList); - searchAttrExampleToggle.appendChild(searchAttrExampleCaret); - searchDiv.appendChild(equalsField); - searchDiv.appendChild(searchValueField); - searchDiv.appendChild(searchStart); + menubar.appendChild(configureLists.configure); + menubar.appendChild(configureLists.filter); transparentHeader.appendChild(buttons); buttons.appendChild(configureDropDown); @@ -405,13 +471,8 @@ function GraphViewerUI(container, adapterConfig, optWidth, optHeight, viewerConf createColourList = function() { colourList = nodeShaperUI.createColourMappingList(); - colourList.style.position = "absolute"; - var intSVG = $("#graphViewerSVG"); - colourList.style.top = intSVG.position().top.toFixed(1) + "px"; - colourList.style.left = (intSVG.position().left + intSVG.width()).toFixed(1) + "px"; - colourList.style.height = intSVG.height() + "px"; - colourList.style.overflow = "auto"; - container.appendChild(colourList); + colourList.className = "gv_colour_list"; + background.insertBefore(colourList, svg[0][0]); }; container.appendChild(menubar); container.appendChild(background); diff --git a/js/apps/system/aardvark/test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js b/js/apps/system/aardvark/test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js index 46eee15b42..094aaa6225 100644 --- a/js/apps/system/aardvark/test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js +++ b/js/apps/system/aardvark/test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js @@ -103,6 +103,7 @@ adapterUI.addControlChangeCollections(); expect($("#control_adapter_list " + idPrefix).length).toEqual(1); + expect(true).toBeFalsy(); expect($("#control_adapter_list " + idPrefix)[0]).toConformToListCSS(); helper.simulateMouseEvent("click", "control_adapter_collections"); expect($(idPrefix + "_modal").length).toEqual(1); @@ -116,6 +117,7 @@ it('should be added to the list', function() { runs(function() { + expect(42).toEqual(23); $(idPrefix + "_node_collection").prop("selectedIndex", 0); $(idPrefix + "_edge_collection").prop("selectedIndex", 1); helper.simulateMouseEvent("click", idPrefix.substr(1) + "_submit"); From 44b3237882dd060400ed60411ace091b9855ab6d Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 4 Dec 2013 21:52:15 +0100 Subject: [PATCH 04/22] moved server-only tests to js/server/tests this hopefully removes some future confusion about the tests also moved some graph stuff into js/common as client-side graphs did not fully work make logfile path accessible --- UnitTests/Makefile.unittests | 24 +++++++-------- arangod/RestServer/ArangoServer.cpp | 4 +-- arangod/RestServer/ArangoServer.h | 30 +++++++++---------- arangod/V8Server/ApplicationV8.cpp | 6 ++-- js/actions/api-foxx.js | 3 +- .../frontend/js/bootstrap/module-internal.js | 11 ++++++- .../js/modules/org/arangodb/graph-common.js | 24 +++++++++++++++ .../frontend/js/modules/org/arangodb/graph.js | 6 ++++ js/client/modules/org/arangodb/graph.js | 6 ++++ js/common/bootstrap/module-internal.js | 11 ++++++- .../modules/org/arangodb/graph-common.js | 24 +++++++++++++++ js/common/tests/shell-graph-algorithms.js | 4 +-- js/server/modules/org/arangodb/graph.js | 26 +--------------- .../tests/shell-bitarray-index.js | 0 .../tests/shell-foxx-base-middleware.js | 0 .../tests/shell-foxx-format-middleware.js | 0 .../tests/shell-foxx-model.js | 0 .../tests/shell-foxx-preprocessor.js | 0 .../tests/shell-foxx-repository.js | 0 .../tests/shell-foxx-template-middleware.js | 0 js/{common => server}/tests/shell-foxx.js | 0 .../tests/shell-skiplist-index.js | 0 lib/BasicsC/logging.c | 24 +++++++++++++++ lib/BasicsC/logging.h | 6 ++++ 24 files changed, 146 insertions(+), 63 deletions(-) rename js/{common => server}/tests/shell-bitarray-index.js (100%) rename js/{common => server}/tests/shell-foxx-base-middleware.js (100%) rename js/{common => server}/tests/shell-foxx-format-middleware.js (100%) rename js/{common => server}/tests/shell-foxx-model.js (100%) rename js/{common => server}/tests/shell-foxx-preprocessor.js (100%) rename js/{common => server}/tests/shell-foxx-repository.js (100%) rename js/{common => server}/tests/shell-foxx-template-middleware.js (100%) rename js/{common => server}/tests/shell-foxx.js (100%) rename js/{common => server}/tests/shell-skiplist-index.js (100%) diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index 897ec4c2cc..54c796f7bc 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -365,6 +365,9 @@ SHELL_COMMON = \ @top_srcdir@/js/common/tests/shell-download.js \ @top_srcdir@/js/common/tests/shell-edge.js \ @top_srcdir@/js/common/tests/shell-fs.js \ + @top_srcdir@/js/common/tests/shell-graph-traversal.js \ + @top_srcdir@/js/common/tests/shell-graph-algorithms.js \ + @top_srcdir@/js/common/tests/shell-graph-measurement.js \ @top_srcdir@/js/common/tests/shell-keygen.js \ @top_srcdir@/js/common/tests/shell-simple-query.js \ @top_srcdir@/js/common/tests/shell-statement.js \ @@ -388,19 +391,16 @@ SHELL_SERVER_ONLY = \ @top_srcdir@/js/server/tests/transactions.js \ @top_srcdir@/js/server/tests/routing.js \ @top_srcdir@/js/server/tests/shell-any.js \ - @top_srcdir@/js/common/tests/shell-bitarray-index.js \ + @top_srcdir@/js/server/tests/shell-bitarray-index.js \ @top_srcdir@/js/server/tests/shell-database.js \ - @top_srcdir@/js/common/tests/shell-foxx.js \ - @top_srcdir@/js/common/tests/shell-foxx-repository.js \ - @top_srcdir@/js/common/tests/shell-foxx-model.js \ - @top_srcdir@/js/common/tests/shell-foxx-base-middleware.js \ - @top_srcdir@/js/common/tests/shell-foxx-template-middleware.js \ - @top_srcdir@/js/common/tests/shell-foxx-format-middleware.js \ - @top_srcdir@/js/common/tests/shell-foxx-preprocessor.js \ - @top_srcdir@/js/common/tests/shell-graph-traversal.js \ - @top_srcdir@/js/common/tests/shell-graph-algorithms.js \ - @top_srcdir@/js/common/tests/shell-graph-measurement.js \ - @top_srcdir@/js/common/tests/shell-skiplist-index.js \ + @top_srcdir@/js/server/tests/shell-foxx.js \ + @top_srcdir@/js/server/tests/shell-foxx-repository.js \ + @top_srcdir@/js/server/tests/shell-foxx-model.js \ + @top_srcdir@/js/server/tests/shell-foxx-base-middleware.js \ + @top_srcdir@/js/server/tests/shell-foxx-template-middleware.js \ + @top_srcdir@/js/server/tests/shell-foxx-format-middleware.js \ + @top_srcdir@/js/server/tests/shell-foxx-preprocessor.js \ + @top_srcdir@/js/server/tests/shell-skiplist-index.js \ @top_srcdir@/js/server/tests/shell-skiplist-rm-performance.js \ @top_srcdir@/js/server/tests/shell-skiplist-correctness.js diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 56d855b4e8..92783a1611 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -270,7 +270,6 @@ ArangoServer::ArangoServer (int argc, char** argv) _databasePath(), _defaultMaximalSize(TRI_JOURNAL_DEFAULT_MAXIMAL_SIZE), _defaultWaitForSync(false), - _developmentMode(false), _forceSyncProperties(true), _unusedForceSyncShapes(false), _disableReplicationLogger(false), @@ -554,10 +553,9 @@ void ArangoServer::buildApplicationServer () { // configure v8 if (_applicationServer->programOptions().has("development-mode")) { - _developmentMode = true; _applicationV8->enableDevelopmentMode(); } - + // ............................................................................. // set language of default collator // ............................................................................. diff --git a/arangod/RestServer/ArangoServer.h b/arangod/RestServer/ArangoServer.h index 0fb1d7f877..598dfaa716 100644 --- a/arangod/RestServer/ArangoServer.h +++ b/arangod/RestServer/ArangoServer.h @@ -340,21 +340,6 @@ namespace triagens { bool _defaultWaitForSync; -//////////////////////////////////////////////////////////////////////////////// -/// @brief development mode -/// -/// @CMDOPT{\--development-mode} -/// -/// Specifying this option will start the server in development mode. The -/// development mode forces reloading of all actions and Foxx applications on -/// every HTTP request. This is very resource-intensive and slow, but makes -/// developing server-side actions and Foxx applications much easier. -/// -/// Never use this option in production. -//////////////////////////////////////////////////////////////////////////////// - - bool _developmentMode; - //////////////////////////////////////////////////////////////////////////////// /// @brief force syncing of collection properties /// @@ -509,6 +494,21 @@ namespace triagens { string _defaultLanguage; +//////////////////////////////////////////////////////////////////////////////// +/// @brief development mode +/// +/// @CMDOPT{\--development-mode} +/// +/// Specifying this option will start the server in development mode. The +/// development mode forces reloading of all actions and Foxx applications on +/// every HTTP request. This is very resource-intensive and slow, but makes +/// developing server-side actions and Foxx applications much easier. +/// +/// Never use this option in production. +//////////////////////////////////////////////////////////////////////////////// + + bool _developmentMode; /* variable is only used for documentation generation */ + //////////////////////////////////////////////////////////////////////////////// /// @brief the server //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/V8Server/ApplicationV8.cpp b/arangod/V8Server/ApplicationV8.cpp index 7c8b1568b2..5c8903a088 100644 --- a/arangod/V8Server/ApplicationV8.cpp +++ b/arangod/V8Server/ApplicationV8.cpp @@ -764,8 +764,10 @@ bool ApplicationV8::prepareV8Instance (const size_t i) { { v8::HandleScope scope; - TRI_AddGlobalVariableVocbase(context->_context, "APP_PATH", v8::String::New(_appPath.c_str())); - TRI_AddGlobalVariableVocbase(context->_context, "DEV_APP_PATH", v8::String::New(_devAppPath.c_str())); + char const* logfile = TRI_GetFilenameLogging(); + TRI_AddGlobalVariableVocbase(context->_context, "LOGFILE_PATH", logfile != 0 ? v8::String::New(logfile) : v8::Null()); + TRI_AddGlobalVariableVocbase(context->_context, "APP_PATH", v8::String::New(_appPath.c_str(), _appPath.size())); + TRI_AddGlobalVariableVocbase(context->_context, "DEV_APP_PATH", v8::String::New(_devAppPath.c_str(), _devAppPath.size())); TRI_AddGlobalVariableVocbase(context->_context, "DEVELOPMENT_MODE", v8::Boolean::New(_developmentMode)); } diff --git a/js/actions/api-foxx.js b/js/actions/api-foxx.js index 91c94b40cb..bcc1e8491e 100644 --- a/js/actions/api-foxx.js +++ b/js/actions/api-foxx.js @@ -272,7 +272,8 @@ actions.defineHttp({ callback : function (req, res) { var result = { appPath: module.appPath(), - devAppPath: internal.developmentMode ? module.devAppPath() : null + devAppPath: internal.developmentMode ? module.devAppPath() : null, + logFilePath: internal.logfilePath }; actions.resultOk(req, res, actions.HTTP_OK, { result: result }); diff --git a/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js b/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js index 08c19b2346..9da5250c43 100644 --- a/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js +++ b/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js @@ -18,7 +18,7 @@ COLOR_BOLD_WHITE, COLOR_YELLOW, COLOR_BOLD_YELLOW, COLOR_CYAN, COLOR_BOLD_CYAN, COLOR_MAGENTA, COLOR_BOLD_MAGENTA, PRETTY_PRINT, VALGRIND, VERSION, UPGRADE, BYTES_SENT_DISTRIBUTION, BYTES_RECEIVED_DISTRIBUTION, CONNECTION_TIME_DISTRIBUTION, - REQUEST_TIME_DISTRIBUTION, DEVELOPMENT_MODE, THREAD_NUMBER, + REQUEST_TIME_DISTRIBUTION, DEVELOPMENT_MODE, THREAD_NUMBER, LOGFILE_PATH, SYS_PLATFORM */ //////////////////////////////////////////////////////////////////////////////// @@ -128,6 +128,15 @@ SYS_LOG("warning", "################################################################################"); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief logfilePath +//////////////////////////////////////////////////////////////////////////////// + + if (typeof LOGFILE_PATH !== "undefined") { + exports.logfilePath = LOGFILE_PATH; + delete LOGFILE_PATH; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief quiet //////////////////////////////////////////////////////////////////////////////// diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph-common.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph-common.js index 9d7fe9811e..62db68571a 100644 --- a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph-common.js +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph-common.js @@ -523,6 +523,30 @@ Vertex.prototype.addOutEdge = function (ine, id, label, data) { return this._graph.addEdge(this, ine, id, label, data); }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the number of edges +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.degree = function () { + return this.getEdges().length; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the number of in-edges +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.inDegree = function () { + return this.getInEdges().length; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the number of out-edges +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.outDegree = function () { + return this.getOutEdges().length; +}; + //////////////////////////////////////////////////////////////////////////////// /// @brief returns the identifier of a vertex /// diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph.js index 2ae475c90b..82b7a7df06 100644 --- a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph.js +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph.js @@ -60,6 +60,7 @@ Edge.prototype.setProperty = function (name, value) { update = this._properties; update[name] = value; + this._graph.emptyCachedPredecessors(); results = GraphAPI.putEdge(this._graph._properties._key, this._properties._key, update); @@ -279,6 +280,8 @@ Graph.prototype.drop = function () { Graph.prototype._saveEdge = function(id, out_vertex_id, in_vertex_id, params) { var results; + + this.emptyCachedPredecessors(); params._key = id; params._from = out_vertex_id; @@ -379,6 +382,7 @@ Graph.prototype.getEdges = function () { //////////////////////////////////////////////////////////////////////////////// Graph.prototype.removeVertex = function (vertex) { + this.emptyCachedPredecessors(); GraphAPI.deleteVertex(this._properties._key, vertex._properties._key); vertex._properties = undefined; }; @@ -388,7 +392,9 @@ Graph.prototype.removeVertex = function (vertex) { //////////////////////////////////////////////////////////////////////////////// Graph.prototype.removeEdge = function (edge) { + this.emptyCachedPredecessors(); GraphAPI.deleteEdge(this._properties._key, edge._properties._key); + this._edgesCache[edge._properties._id] = undefined; edge._properties = undefined; }; diff --git a/js/client/modules/org/arangodb/graph.js b/js/client/modules/org/arangodb/graph.js index ff63a94733..4cbcd2b297 100644 --- a/js/client/modules/org/arangodb/graph.js +++ b/js/client/modules/org/arangodb/graph.js @@ -59,6 +59,7 @@ Edge.prototype.setProperty = function (name, value) { update = this._properties; update[name] = value; + this._graph.emptyCachedPredecessors(); results = GraphAPI.putEdge(this._graph._properties._key, this._properties._key, update); @@ -278,6 +279,8 @@ Graph.prototype.drop = function () { Graph.prototype._saveEdge = function(id, out_vertex_id, in_vertex_id, params) { var results; + + this.emptyCachedPredecessors(); params._key = id; params._from = out_vertex_id; @@ -378,6 +381,7 @@ Graph.prototype.getEdges = function () { //////////////////////////////////////////////////////////////////////////////// Graph.prototype.removeVertex = function (vertex) { + this.emptyCachedPredecessors(); GraphAPI.deleteVertex(this._properties._key, vertex._properties._key); vertex._properties = undefined; }; @@ -387,7 +391,9 @@ Graph.prototype.removeVertex = function (vertex) { //////////////////////////////////////////////////////////////////////////////// Graph.prototype.removeEdge = function (edge) { + this.emptyCachedPredecessors(); GraphAPI.deleteEdge(this._properties._key, edge._properties._key); + this._edgesCache[edge._properties._id] = undefined; edge._properties = undefined; }; diff --git a/js/common/bootstrap/module-internal.js b/js/common/bootstrap/module-internal.js index 08c19b2346..9da5250c43 100644 --- a/js/common/bootstrap/module-internal.js +++ b/js/common/bootstrap/module-internal.js @@ -18,7 +18,7 @@ COLOR_BOLD_WHITE, COLOR_YELLOW, COLOR_BOLD_YELLOW, COLOR_CYAN, COLOR_BOLD_CYAN, COLOR_MAGENTA, COLOR_BOLD_MAGENTA, PRETTY_PRINT, VALGRIND, VERSION, UPGRADE, BYTES_SENT_DISTRIBUTION, BYTES_RECEIVED_DISTRIBUTION, CONNECTION_TIME_DISTRIBUTION, - REQUEST_TIME_DISTRIBUTION, DEVELOPMENT_MODE, THREAD_NUMBER, + REQUEST_TIME_DISTRIBUTION, DEVELOPMENT_MODE, THREAD_NUMBER, LOGFILE_PATH, SYS_PLATFORM */ //////////////////////////////////////////////////////////////////////////////// @@ -128,6 +128,15 @@ SYS_LOG("warning", "################################################################################"); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief logfilePath +//////////////////////////////////////////////////////////////////////////////// + + if (typeof LOGFILE_PATH !== "undefined") { + exports.logfilePath = LOGFILE_PATH; + delete LOGFILE_PATH; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief quiet //////////////////////////////////////////////////////////////////////////////// diff --git a/js/common/modules/org/arangodb/graph-common.js b/js/common/modules/org/arangodb/graph-common.js index 6f865cb686..393bad85c5 100644 --- a/js/common/modules/org/arangodb/graph-common.js +++ b/js/common/modules/org/arangodb/graph-common.js @@ -522,6 +522,30 @@ Vertex.prototype.addOutEdge = function (ine, id, label, data) { return this._graph.addEdge(this, ine, id, label, data); }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the number of edges +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.degree = function () { + return this.getEdges().length; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the number of in-edges +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.inDegree = function () { + return this.getInEdges().length; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the number of out-edges +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.outDegree = function () { + return this.getOutEdges().length; +}; + //////////////////////////////////////////////////////////////////////////////// /// @brief returns the identifier of a vertex /// diff --git a/js/common/tests/shell-graph-algorithms.js b/js/common/tests/shell-graph-algorithms.js index 5ffc024727..086c75ae5a 100644 --- a/js/common/tests/shell-graph-algorithms.js +++ b/js/common/tests/shell-graph-algorithms.js @@ -175,8 +175,6 @@ function dijkstraSuite() { try { // Drop the graph if it exsits graph = new Graph(graph_name); - print("FOUND: "); - PRINT_OBJECT(graph); graph.drop(); } catch (err1) { } @@ -293,7 +291,7 @@ function dijkstraSuite() { e2 = graph.addEdge(v2, v3), e3, pathes = v1.pathTo(v3, {cached: true}); - + e3 = graph.addEdge(v1, v3); pathes = v1.pathTo(v3, {cached: true}); diff --git a/js/server/modules/org/arangodb/graph.js b/js/server/modules/org/arangodb/graph.js index e351d9dd01..9cd647f332 100644 --- a/js/server/modules/org/arangodb/graph.js +++ b/js/server/modules/org/arangodb/graph.js @@ -325,30 +325,6 @@ Vertex.prototype.setProperty = function (name, value) { return value; }; -//////////////////////////////////////////////////////////////////////////////// -/// @brief returns the number of edges -//////////////////////////////////////////////////////////////////////////////// - -Vertex.prototype.degree = function () { - return this.getEdges().length; -}; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief returns the number of in-edges -//////////////////////////////////////////////////////////////////////////////// - -Vertex.prototype.inDegree = function () { - return this.getInEdges().length; -}; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief returns the number of out-edges -//////////////////////////////////////////////////////////////////////////////// - -Vertex.prototype.outDegree = function () { - return this.getOutEdges().length; -}; - //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// @@ -500,7 +476,7 @@ Graph.prototype.initialize = function (name, vertices, edges, waitForSync) { this._verticesCache = {}; this._edgesCache = {}; - // and store the cashes + // and store the caches this.predecessors = {}; this.distances = {}; }; diff --git a/js/common/tests/shell-bitarray-index.js b/js/server/tests/shell-bitarray-index.js similarity index 100% rename from js/common/tests/shell-bitarray-index.js rename to js/server/tests/shell-bitarray-index.js diff --git a/js/common/tests/shell-foxx-base-middleware.js b/js/server/tests/shell-foxx-base-middleware.js similarity index 100% rename from js/common/tests/shell-foxx-base-middleware.js rename to js/server/tests/shell-foxx-base-middleware.js diff --git a/js/common/tests/shell-foxx-format-middleware.js b/js/server/tests/shell-foxx-format-middleware.js similarity index 100% rename from js/common/tests/shell-foxx-format-middleware.js rename to js/server/tests/shell-foxx-format-middleware.js diff --git a/js/common/tests/shell-foxx-model.js b/js/server/tests/shell-foxx-model.js similarity index 100% rename from js/common/tests/shell-foxx-model.js rename to js/server/tests/shell-foxx-model.js diff --git a/js/common/tests/shell-foxx-preprocessor.js b/js/server/tests/shell-foxx-preprocessor.js similarity index 100% rename from js/common/tests/shell-foxx-preprocessor.js rename to js/server/tests/shell-foxx-preprocessor.js diff --git a/js/common/tests/shell-foxx-repository.js b/js/server/tests/shell-foxx-repository.js similarity index 100% rename from js/common/tests/shell-foxx-repository.js rename to js/server/tests/shell-foxx-repository.js diff --git a/js/common/tests/shell-foxx-template-middleware.js b/js/server/tests/shell-foxx-template-middleware.js similarity index 100% rename from js/common/tests/shell-foxx-template-middleware.js rename to js/server/tests/shell-foxx-template-middleware.js diff --git a/js/common/tests/shell-foxx.js b/js/server/tests/shell-foxx.js similarity index 100% rename from js/common/tests/shell-foxx.js rename to js/server/tests/shell-foxx.js diff --git a/js/common/tests/shell-skiplist-index.js b/js/server/tests/shell-skiplist-index.js similarity index 100% rename from js/common/tests/shell-skiplist-index.js rename to js/server/tests/shell-skiplist-index.js diff --git a/lib/BasicsC/logging.c b/lib/BasicsC/logging.c index 4d670ffdae..342e79fb0c 100644 --- a/lib/BasicsC/logging.c +++ b/lib/BasicsC/logging.c @@ -107,6 +107,12 @@ static bool Initialised = false; static bool ShutdownInitalised = false; +//////////////////////////////////////////////////////////////////////////////// +/// @brief name of first log file +//////////////////////////////////////////////////////////////////////////////// + +static char* LogfileName = 0; + //////////////////////////////////////////////////////////////////////////////// /// @brief log appenders //////////////////////////////////////////////////////////////////////////////// @@ -1706,6 +1712,11 @@ TRI_log_appender_t* TRI_CreateLogAppenderFile (char const* filename, TRI_PushBackVectorPointer(&Appenders, &appender->base); TRI_UnlockSpin(&AppendersLock); + // register the name of the first logfile + if (LogfileName == NULL) { + LogfileName = TRI_DuplicateStringZ(TRI_CORE_MEM_ZONE, filename); + } + // and return base structure return &appender->base; } @@ -1962,6 +1973,14 @@ TRI_log_appender_t* TRI_CreateLogAppenderSyslog (char const* name, /// @{ //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @brief return global log file name +//////////////////////////////////////////////////////////////////////////////// + +char const* TRI_GetFilenameLogging () { + return LogfileName; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief initialises the logging components //////////////////////////////////////////////////////////////////////////////// @@ -2023,6 +2042,11 @@ bool TRI_ShutdownLogging (bool clearBuffers) { // logging is now inactive (this will terminate the logging thread) LoggingActive = 0; + if (LogfileName != NULL) { + TRI_Free(TRI_CORE_MEM_ZONE, LogfileName); + LogfileName = 0; + } + // join with the logging thread if (ThreadedLogging) { TRI_LockCondition(&LogCondition); diff --git a/lib/BasicsC/logging.h b/lib/BasicsC/logging.h index a67426c8c1..5d05695440 100644 --- a/lib/BasicsC/logging.h +++ b/lib/BasicsC/logging.h @@ -544,6 +544,12 @@ struct TRI_log_appender_s* TRI_CreateLogAppenderSyslog (char const*, /// @{ //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @brief return global log file name +//////////////////////////////////////////////////////////////////////////////// + +char const* TRI_GetFilenameLogging (void); + //////////////////////////////////////////////////////////////////////////////// /// @brief initialises the logging components /// From 26fc85f88ce64fa00b6c96ae1389f45b1037d145 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 4 Dec 2013 20:37:30 +0100 Subject: [PATCH 05/22] allow references as function parameters --- arangod/Ahuacatl/ahuacatl-functions.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arangod/Ahuacatl/ahuacatl-functions.c b/arangod/Ahuacatl/ahuacatl-functions.c index 3f639a4ce4..973a1b48e5 100644 --- a/arangod/Ahuacatl/ahuacatl-functions.c +++ b/arangod/Ahuacatl/ahuacatl-functions.c @@ -150,6 +150,10 @@ static bool CheckArgumentType (TRI_aql_node_t const* parameter, const param_t* const allowed) { param_t found = InitParam(); + if (parameter->_type == TRI_AQL_NODE_REFERENCE) { + return true; + } + if (parameter->_type == TRI_AQL_NODE_PARAMETER) { // node is a bind parameter char* name = TRI_AQL_NODE_STRING(parameter); From 8ac6d9728e064f2b4fb3ba4a0167ec8d154d41d2 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 4 Dec 2013 20:39:51 +0100 Subject: [PATCH 06/22] better clone some objects returned by AQL user-defined functions --- js/server/modules/org/arangodb/ahuacatl.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/js/server/modules/org/arangodb/ahuacatl.js b/js/server/modules/org/arangodb/ahuacatl.js index 2fc8a2f45b..a2aca06cc2 100644 --- a/js/server/modules/org/arangodb/ahuacatl.js +++ b/js/server/modules/org/arangodb/ahuacatl.js @@ -261,13 +261,17 @@ function FIX_VALUE (value) { } if (type === 'object') { + var result = { }; for (i in value) { if (value.hasOwnProperty(i)) { - value[i] = FIX_VALUE(value[i]); + if (typeof value[i] === 'function') { + continue; + } + result[i] = FIX_VALUE(value[i]); } } - return value; + return result; } return null; From 705efbfb07f1151df070603d55f7595a84f83e9f Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 4 Dec 2013 22:42:05 +0100 Subject: [PATCH 07/22] improved error and help messages --- js/client/modules/org/arangodb/foxx/manager.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/js/client/modules/org/arangodb/foxx/manager.js b/js/client/modules/org/arangodb/foxx/manager.js index 1850c4a7e1..0de641bc8a 100644 --- a/js/client/modules/org/arangodb/foxx/manager.js +++ b/js/client/modules/org/arangodb/foxx/manager.js @@ -367,7 +367,8 @@ function processSource (src) { processGithubRepository(src); } else { - throwBadParameter("Unknown application type '" + src.type + "'"); + throwBadParameter("Unknown application type '" + src.type + "'. " + + "expected type: 'github', 'zip', or 'directory'."); } // upload file to the server @@ -1444,7 +1445,9 @@ exports.help = function () { } arangodb.print(); - arangodb.print("use foxx-manager --help to show a list of global options"); + arangodb.print("Use foxx-manager --help to show a list of global options\n"); + arangodb.print("There is also an online manual available at:"); + arangodb.print("https://www.arangodb.org/manuals/current/UserManualFoxxManager.html"); // additional newline arangodb.print(); From 1306e0e65113be1094e85339b2ba89423e7f80ff Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 4 Dec 2013 23:10:22 +0100 Subject: [PATCH 08/22] committed generated file --- lib/BasicsC/voc-mimetypes.h | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/lib/BasicsC/voc-mimetypes.h b/lib/BasicsC/voc-mimetypes.h index 10f9fb2f56..e69de29bb2 100644 --- a/lib/BasicsC/voc-mimetypes.h +++ b/lib/BasicsC/voc-mimetypes.h @@ -1,29 +0,0 @@ - -#ifndef TRIAGENS_BASICS_C_VOC_MIMETYPES_H -#define TRIAGENS_BASICS_C_VOC_MIMETYPES_H 1 - -#ifdef __cplusplus -extern "C" { -#endif - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Mimetypes -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief initialise mimetypes -//////////////////////////////////////////////////////////////////////////////// - -void TRI_InitialiseEntriesMimetypes (void); - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -#ifdef __cplusplus -} -#endif - -#endif - From 9593af0334ff9eb96883e3a1c774edb78cccd21b Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 4 Dec 2013 23:20:38 +0100 Subject: [PATCH 09/22] some more text for the user manual overview page --- Documentation/UserManual/UserManual.md | 62 +++++++++++++++++--------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/Documentation/UserManual/UserManual.md b/Documentation/UserManual/UserManual.md index 5655837cdc..016b17c585 100644 --- a/Documentation/UserManual/UserManual.md +++ b/Documentation/UserManual/UserManual.md @@ -3,24 +3,44 @@ ArangoDB's User Manual (@VERSION) {#UserManual} @NAVIGATE_UserManual -@CHAPTER_REF{Installing} -@CHAPTER_REF{FirstStepsArangoDB} -@CHAPTER_REF{UserManualArangosh} -@CHAPTER_REF{UserManualWebInterface} -@CHAPTER_REF{HandlingDatabases} -@CHAPTER_REF{HandlingCollections} -@CHAPTER_REF{HandlingDocuments} -@CHAPTER_REF{HandlingEdges} -@CHAPTER_REF{SimpleQueries} -@CHAPTER_REF{Aql} -@CHAPTER_REF{ExtendingAql} -@CHAPTER_REF{AqlExamples} -@CHAPTER_REF{Graphs} -@CHAPTER_REF{Traversals} -@CHAPTER_REF{Transactions} -@CHAPTER_REF{UserManualReplication} -@CHAPTER_REF{UserManualFoxx} -@CHAPTER_REF{UserManualFoxxManager} -@CHAPTER_REF{HandlingEndpoints} -@CHAPTER_REF{CommandLine} -@CHAPTER_REF{Glossary} +@CHAPTER_REF{Installing}: Downloading and compiling ArangoDB + +@CHAPTER_REF{FirstStepsArangoDB}: An Introduction to ArangoDB + +@CHAPTER_REF{UserManualArangosh}: Using the ArangoDB shell + +@CHAPTER_REF{UserManualWebInterface}: Administering ArangoDB via the web interface + +@CHAPTER_REF{HandlingDatabases}: Organising data in databases + +@CHAPTER_REF{HandlingCollections}: Managing collections + +@CHAPTER_REF{HandlingDocuments}: Saving, updating, and deleting documents + +@CHAPTER_REF{HandlingEdges}: Storing and querying edges + +@CHAPTER_REF{SimpleQueries}: Running more specialised queries + +@CHAPTER_REF{Aql}: The ArangoDB query language for complex querying + +@CHAPTER_REF{ExtendingAql}: How to embed user-defined functions into AQL + +@CHAPTER_REF{AqlExamples}: Example queries for AQL + +@CHAPTER_REF{Graphs}: Managing graphs in ArangoDB + +@CHAPTER_REF{Traversals}: Running custom graph traversals + +@CHAPTER_REF{Transactions}: Wrapping multiple operations in an ACID transaction + +@CHAPTER_REF{UserManualReplication}: Configuring, starting, stopping the replication + +@CHAPTER_REF{UserManualFoxx}: A JavaScript framework to build simple applications and APIs with ArangoDB + +@CHAPTER_REF{UserManualFoxxManager}: Installing and uninstalling Foxx applications + +@CHAPTER_REF{HandlingEndpoints}: Making the server listen on multiple ports + +@CHAPTER_REF{CommandLine}: All the command-line options + +@CHAPTER_REF{Glossary}: The obvious definitions From dc88db354524de06b908ee6b3861db09fe470f2f Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 4 Dec 2013 23:49:14 +0100 Subject: [PATCH 10/22] added extra notes about Foxx deployment --- Documentation/UserManual/Foxx.md | 45 ++++++++++++++++++++++++++++- Documentation/UserManual/FoxxTOC.md | 1 + 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/Documentation/UserManual/Foxx.md b/Documentation/UserManual/Foxx.md index d47a1c7eab..0e9ba726d6 100644 --- a/Documentation/UserManual/Foxx.md +++ b/Documentation/UserManual/Foxx.md @@ -14,7 +14,7 @@ They provide all the HTTP goodness. If you just want to install an existing application, please use the @ref UserManualFoxxManager. If you want to create your own application, -please continue. +please continue reading. Overview ======== @@ -965,3 +965,46 @@ provides a few mechanisms for this: Of course you can also use `console.log` or any other means of logging output. +Deploying a Foxx application{#UserManualFoxxDeployment} +======================================================= + +When a Foxx application is ready to be used in production, it is time to leave the +development mode and deploy the app in a production environment. + +The first step is to copy the application's script directory to the target ArangoDB +server. If your development and production environment are the same, there is +nothing to do. If production runs on a different server, you should copy the +development application directory to some temporary place on the production server. + +When the application code is present on the production server, you can use the +`fetch` and `mount` commands from the @ref UserManualFoxxManager to register the +application in the production ArangoDB instance and make it available. + +Here are the individual steps to carry out: + +- development: + - cd into the directory that application code is in. Then create a tar.gz file with + the application code (replace `app` with the actual name): + + cd /path/to/development/apps/directory + tar cvfz app.tar.gz app + + - copy the tar.gz file to the production server: + + scp app.tar.gz production:/tmp/ + +- production: + - create a temporary directory, e.g. `/tmp/apps` and extract the tar archive into + this directory: + + mkdir /tmp/apps + cd /tmp/apps + tar xvfz /tmp/app.tar.gz + + - start the ArangoDB shell and run the following commands in it: + + fm.fetch("directory", "/tmp/apps/app"); + fm.mount("app", "/app"); + +More information on how to deploy applications from different sources can be +found in the @ref UserManualFoxxManager "Foxx Manager manual". diff --git a/Documentation/UserManual/FoxxTOC.md b/Documentation/UserManual/FoxxTOC.md index 47f921b340..054cf9bfcb 100644 --- a/Documentation/UserManual/FoxxTOC.md +++ b/Documentation/UserManual/FoxxTOC.md @@ -8,3 +8,4 @@ TOC {#UserManualFoxxTOC} - @ref UserManualFoxxDetailsModel - @ref UserManualFoxxDetailsRepository - @ref UserManualFoxxDevelopment + - @ref UserManualFoxxDeployment From d3acf530da80be3d9572b07feb58c32d4394faa8 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Thu, 5 Dec 2013 10:26:21 +0100 Subject: [PATCH 11/22] re-added stuff that got lost yesterday --- lib/BasicsC/voc-mimetypes.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/BasicsC/voc-mimetypes.h b/lib/BasicsC/voc-mimetypes.h index e69de29bb2..10f9fb2f56 100644 --- a/lib/BasicsC/voc-mimetypes.h +++ b/lib/BasicsC/voc-mimetypes.h @@ -0,0 +1,29 @@ + +#ifndef TRIAGENS_BASICS_C_VOC_MIMETYPES_H +#define TRIAGENS_BASICS_C_VOC_MIMETYPES_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Mimetypes +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief initialise mimetypes +//////////////////////////////////////////////////////////////////////////////// + +void TRI_InitialiseEntriesMimetypes (void); + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +} +#endif + +#endif + From aca868dba87ecbff90327a5657b7b396e1cd5900 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Fri, 6 Dec 2013 14:44:51 +0100 Subject: [PATCH 12/22] Added information line about the bug in Safari --- js/apps/system/aardvark/frontend/js/templates/queryView.ejs | 1 + 1 file changed, 1 insertion(+) diff --git a/js/apps/system/aardvark/frontend/js/templates/queryView.ejs b/js/apps/system/aardvark/frontend/js/templates/queryView.ejs index 26c26e0986..3c59150a76 100644 --- a/js/apps/system/aardvark/frontend/js/templates/queryView.ejs +++ b/js/apps/system/aardvark/frontend/js/templates/queryView.ejs @@ -61,6 +61,7 @@

Submit: CTRL/CMD + Return ; Undo: CTRL/CMD + Z ; Redo: CTRL/CMD + SHIFT + Z ; Toggle Comments: CTRL/CMD + SHIFT + C +
Warning: Right now there is a bug in Safari, the cursor is misplaced. Please use a different browser for now. Fix in progress.
From 1ebac6f02273dcc09cef0121c74636ea2f9951c5 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Fri, 6 Dec 2013 18:22:05 +0100 Subject: [PATCH 13/22] Removed old location of graphviewer tests, they have been moved to the central frontend test repo --- .../jasmine_test/jasmineTestLinks.html | 20 --- .../graphViewer/jasmine_test/runnerAll.html | 106 ------------- .../jasmine_test/runnerArangoAdapter.html | 73 --------- .../jasmine_test/runnerColourMapper.html | 66 -------- .../jasmine_test/runnerEdgeShaper.html | 74 --------- .../jasmine_test/runnerEventDispatcher.html | 78 --------- .../jasmine_test/runnerEventLibrary.html | 66 -------- .../jasmine_test/runnerForceLayouter.html | 71 --------- .../jasmine_test/runnerGraphViewer.html | 87 ---------- .../jasmine_test/runnerJSLint.html | 56 ------- .../jasmine_test/runnerJSONAdapter.html | 66 -------- .../jasmine_test/runnerNodeReducer.html | 71 --------- .../jasmine_test/runnerNodeShaper.html | 73 --------- .../jasmine_test/runnerZoomManager.html | 71 --------- .../jasmine_test/specJSLint/jsLintSpec.js | 78 --------- .../js/graphViewer/karma/karma.conf.js | 149 ------------------ 16 files changed, 1205 deletions(-) delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/jasmineTestLinks.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerAll.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerArangoAdapter.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerColourMapper.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerEdgeShaper.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerEventDispatcher.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerEventLibrary.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerForceLayouter.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerGraphViewer.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerJSLint.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerJSONAdapter.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerNodeReducer.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerNodeShaper.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerZoomManager.html delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/specJSLint/jsLintSpec.js delete mode 100644 js/apps/system/aardvark/frontend/js/graphViewer/karma/karma.conf.js diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/jasmineTestLinks.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/jasmineTestLinks.html deleted file mode 100644 index 15bea4a365..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/jasmineTestLinks.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerAll.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerAll.html deleted file mode 100644 index b01e81a832..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerAll.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - Jasmine Test Graph Interface - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerArangoAdapter.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerArangoAdapter.html deleted file mode 100644 index 087559bd42..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerArangoAdapter.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - Jasmine Test Arango Adapter - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerColourMapper.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerColourMapper.html deleted file mode 100644 index 31c88503fe..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerColourMapper.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - Jasmine Test Edge Shaper - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerEdgeShaper.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerEdgeShaper.html deleted file mode 100644 index 00dd0e8615..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerEdgeShaper.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - Jasmine Test Edge Shaper - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerEventDispatcher.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerEventDispatcher.html deleted file mode 100644 index e712b76ba0..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerEventDispatcher.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - Jasmine Test Event Dispatcher - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerEventLibrary.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerEventLibrary.html deleted file mode 100644 index 2f01364e18..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerEventLibrary.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - Jasmine Test Event Library - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerForceLayouter.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerForceLayouter.html deleted file mode 100644 index d11f56dff0..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerForceLayouter.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - Jasmine Test Force Layouter - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerGraphViewer.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerGraphViewer.html deleted file mode 100644 index 7f2a9a5be6..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerGraphViewer.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - Jasmine Test Graph Viewer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerJSLint.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerJSLint.html deleted file mode 100644 index 1b4928c060..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerJSLint.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - Jasmine Test JSLint - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerJSONAdapter.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerJSONAdapter.html deleted file mode 100644 index fce1d07333..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerJSONAdapter.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - Jasmine Test JSON Adapter - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerNodeReducer.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerNodeReducer.html deleted file mode 100644 index 25bf9a697a..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerNodeReducer.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - Jasmine Test Zoom Manager - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerNodeShaper.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerNodeShaper.html deleted file mode 100644 index 45089e852b..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerNodeShaper.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - Jasmine Test Node Shaper - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerZoomManager.html b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerZoomManager.html deleted file mode 100644 index 64776f4f75..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/runnerZoomManager.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - Jasmine Test Zoom Manager - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Back to List -

- - diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/specJSLint/jsLintSpec.js b/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/specJSLint/jsLintSpec.js deleted file mode 100644 index fb48a51e0c..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/jasmine_test/specJSLint/jsLintSpec.js +++ /dev/null @@ -1,78 +0,0 @@ -/*jslint indent: 2, nomen: true, maxlen: 100, white: true plusplus: true */ -/*global beforeEach, afterEach */ -/*global describe, it, expect, jasmine, spyOn*/ -/*global waitsFor, runs, waits */ -/*global _, JSLINT*/ -/*global document*/ - -(function () { - "use strict"; - describe('JSLint', function () { - var options = {}, - lint = /^\/specJSLint|[\w\W]jsLintSpec\.js$/; - - function get(path) { - path = path + "?" + new Date().getTime(); - - var xhr; - try { - xhr = new jasmine.XmlHttpRequest(); - xhr.open("GET", path, false); - xhr.send(null); - } catch (e) { - throw new Error("couldn't fetch " + path + ": " + e); - } - if (xhr.status < 200 || xhr.status > 299) { - throw new Error("Could not load '" + path + "'."); - } - - return xhr.responseText; - } - - describe('checking codeFiles', function() { - var files = /^([\w\W]*lib\/[\w\W]*)|([\w\W]*Spec\.js)$/; - - _.each(document.getElementsByTagName('script'), function (element) { - var script = element.getAttribute('src'); - if (script === null || files.test(script)) { - return; - } - it(script, function () { - var self = this, - source = get(script), - result = JSLINT(source, options); - _.each(JSLINT.errors, function (error) { - self.addMatcherResult(new jasmine.ExpectationResult({ - passed: false, - message: "line " + error.line + ' - ' + error.reason + ' - ' + error.evidence - })); - }); - expect(true).toBe(true); // force spec to show up if there are no errors - }); - }); - }); - - describe('checking specFiles', function() { - var files = /^\/spec*|[\w\W]*Spec\.js$/; - - _.each(document.getElementsByTagName('script'), function (element) { - var script = element.getAttribute('src'); - if (!files.test(script) || lint.test(script)) { - return; - } - it(script, function () { - var self = this, - source = get(script), - result = JSLINT(source, options); - _.each(JSLINT.errors, function (error) { - self.addMatcherResult(new jasmine.ExpectationResult({ - passed: false, - message: "line " + error.line + ' - ' + error.reason + ' - ' + error.evidence - })); - }); - expect(true).toBe(true); // force spec to show up if there are no errors - }); - }); - }); - }); -}()); \ No newline at end of file diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/karma/karma.conf.js b/js/apps/system/aardvark/frontend/js/graphViewer/karma/karma.conf.js deleted file mode 100644 index 9350abdda2..0000000000 --- a/js/apps/system/aardvark/frontend/js/graphViewer/karma/karma.conf.js +++ /dev/null @@ -1,149 +0,0 @@ -// Karma configuration -// Generated on Thu Jul 04 2013 11:39:34 GMT+0200 (CEST) - -module.exports = function(karma) { - karma.set({ - - // base path, that will be used to resolve files and exclude - basePath: '../jasmine_test/', - - - // frameworks to use - frameworks: ['jasmine'], - - - // list of files / patterns to load in the browser - files: [ - 'lib/jasmine-1.3.1/jasmine-html.js', - 'lib/jslint.js', - '../../lib/d3.v3.min.js', - '../../lib/d3.fisheye.js', - '../../lib/underscore.js', - '../../lib/jquery-1.8.3.js', - '../../lib/bootstrap.js', - '../../lib/jquery.livequery.js', - '../../lib/jquery-ui-1.9.2.custom.js', - - // Mocks - 'helper/eventHelper.js', - 'helper/objectsHelper.js', - 'helper/mocks.js', - 'helper/commMock.js', - 'helper/uiMatchers.js', - - // Core Modules - '../graphViewer.js', - '../graph/domObserverFactory.js', - '../graph/colourMapper.js', - '../graph/communityNode.js', - '../graph/webWorkerWrapper.js', - '../graph/nodeShaper.js', - '../graph/abstractAdapter.js', - '../graph/jsonAdapter.js', - '../graph/arangoAdapter.js', - '../graph/foxxAdapter.js', - '../graph/previewAdapter.js', - '../graph/edgeShaper.js', - '../graph/forceLayouter.js', - '../graph/eventDispatcher.js', - '../graph/eventLibrary.js', - '../graph/zoomManager.js', - '../graph/nodeReducer.js', - '../graph/modularityJoiner.js', - // UI Modules - '../ui/modalDialogHelper.js', - '../ui/nodeShaperControls.js', - '../ui/edgeShaperControls.js', - '../ui/arangoAdapterControls.js', - '../ui/layouterControls.js', - '../ui/uiComponentsHelper.js', - '../ui/eventDispatcherControls.js', - '../ui/graphViewerUI.js', - '../ui/graphViewerWidget.js', - '../ui/graphViewerPreview.js', - - // Specs - - 'specColourMapper/colourMapperSpec.js', - 'specWindowObjects/domObserverFactorySpec.js', - 'specCommunityNode/communityNodeSpec.js', - 'specAdapter/interfaceSpec.js', - 'specAdapter/abstractAdapterSpec.js', - 'specAdapter/jsonAdapterSpec.js', - 'specAdapter/arangoAdapterSpec.js', - 'specAdapter/foxxAdapterSpec.js', - 'specAdapter/previewAdapterSpec.js', - 'specAdapter/arangoAdapterUISpec.js', - 'specNodeShaper/nodeShaperSpec.js', - 'specNodeShaper/nodeShaperUISpec.js', - 'specEdgeShaper/edgeShaperSpec.js', - 'specEdgeShaper/edgeShaperUISpec.js', - 'specForceLayouter/forceLayouterSpec.js', - 'specForceLayouter/forceLayouterUISpec.js', - 'specEvents/eventLibrarySpec.js', - 'specEvents/eventDispatcherSpec.js', - 'specEvents/eventDispatcherUISpec.js', - 'specZoomManager/zoomManagerSpec.js', - 'specGraphViewer/graphViewerSpec.js', - 'specGraphViewer/graphViewerUISpec.js', - 'specGraphViewer/graphViewerWidgetSpec.js', - 'specGraphViewer/graphViewerPreviewSpec.js', - 'specNodeReducer/nodeReducerSpec.js', - 'specNodeReducer/modularityJoinerSpec.js', - 'specWindowObjects/workerWrapperSpec.js', - 'specJSLint/jsLintSpec.js' - ], - - - // list of files to exclude - exclude: [ - - ], - - - // test results reporter to use - // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage' - reporters: ['progress'], - - - // web server port - port: 9876, - - - // cli runner port - runnerPort: 9100, - - - // enable / disable colors in the output (reporters and logs) - colors: true, - - - // level of logging - // possible values: karma.LOG_DISABLE || karma.LOG_ERROR || karma.LOG_WARN || karma.LOG_INFO || karma.LOG_DEBUG - logLevel: karma.LOG_INFO, - - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: true, - - - // Start these browsers, currently available: - // - Chrome - // - ChromeCanary - // - Firefox - // - Opera - // - Safari (only Mac) - // - PhantomJS - // - IE (only Windows) - browsers: [], - - - // If browser does not capture in given timeout [ms], kill it - captureTimeout: 60000, - - - // Continuous Integration mode - // if true, it capture browsers, run tests and exit - singleRun: false - }); -}; From a200e542c0ef1e8edda585e08b25249e61e986e1 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Fri, 6 Dec 2013 04:19:43 +0100 Subject: [PATCH 14/22] Update README.md Interesting and useful for researchers. --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 22ed3dd22d..6c82e3f98f 100644 --- a/README.md +++ b/README.md @@ -92,3 +92,14 @@ You can use the Google group for improvements, feature requests, comments http://www.arangodb.org/connect + +Citing ArangoDB +--------------- +Please kindly cite ArangoDB in your publications if it helps your research: + +@misc{Jan13ArangoDB, + Author = {Jan Steeman, Michael Hackstein}, + Title = { {ArangoDB}: An Open Source multi-purpose database supporting flexible data models for documents, graphs, and key-values.}, + Year = {2013}, + Howpublished = {\url{http://arangodb.org/} +} From 118016a87599a15cf63e1d2c0cefc3f8e75ac397 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Fri, 6 Dec 2013 18:01:31 +0100 Subject: [PATCH 15/22] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6c82e3f98f..31c9e6d9b4 100644 --- a/README.md +++ b/README.md @@ -97,9 +97,11 @@ Citing ArangoDB --------------- Please kindly cite ArangoDB in your publications if it helps your research: -@misc{Jan13ArangoDB, - Author = {Jan Steeman, Michael Hackstein}, +```bibtex +@misc{ArangoDB2013, + Author = {ArangoDB}, Title = { {ArangoDB}: An Open Source multi-purpose database supporting flexible data models for documents, graphs, and key-values.}, Year = {2013}, Howpublished = {\url{http://arangodb.org/} } +``` From cb854e82507d36321529e1a419b3ea4d606e6e2e Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Sun, 8 Dec 2013 13:23:34 +0100 Subject: [PATCH 16/22] Fixed tests for modularityJoiner to run with phantomJS (different result because of different sorting order of object keys) --- .../system/aardvark/test/karma/karma.conf.js | 2 +- .../specNodeReducer/modularityJoinerSpec.js | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/js/apps/system/aardvark/test/karma/karma.conf.js b/js/apps/system/aardvark/test/karma/karma.conf.js index 81ed4441fc..4e73e536c7 100644 --- a/js/apps/system/aardvark/test/karma/karma.conf.js +++ b/js/apps/system/aardvark/test/karma/karma.conf.js @@ -199,7 +199,7 @@ module.exports = function(karma) { 'test/specs/graphViewer/specGraphViewer/graphViewerWidgetSpec.js', 'test/specs/graphViewer/specGraphViewer/graphViewerPreviewSpec.js', 'test/specs/graphViewer/specNodeReducer/nodeReducerSpec.js', - // 'test/specs/graphViewer/specNodeReducer/modularityJoinerSpec.js', + 'test/specs/graphViewer/specNodeReducer/modularityJoinerSpec.js', 'test/specs/graphViewer/specWindowObjects/workerWrapperSpec.js', // Arango diff --git a/js/apps/system/aardvark/test/specs/graphViewer/specNodeReducer/modularityJoinerSpec.js b/js/apps/system/aardvark/test/specs/graphViewer/specNodeReducer/modularityJoinerSpec.js index 45043b703f..96f4decc54 100644 --- a/js/apps/system/aardvark/test/specs/graphViewer/specNodeReducer/modularityJoinerSpec.js +++ b/js/apps/system/aardvark/test/specs/graphViewer/specNodeReducer/modularityJoinerSpec.js @@ -936,17 +936,26 @@ this.addMatchers({ toContainKarateClubCommunities: function() { var c1 = [ - "10", "15", "16", "19", "21", "23", "27", - "30", "31", "33", "34", "9" + "10", "15", "16", "19", "21", "23", + "30", "33", "34" ].sort().join(), c2 = ["1", "12", "13", "14", "18", "2", "20", "22", "3", "4", "8"].sort().join(), c3 = ["11", "17", "5", "6", "7"].sort().join(), c4 = ["24", "25", "26", "28", "29", "32"].sort().join(), + swap = ["9", "27", "31"], comms = this.actual, failed = false, msg = "Found incorrect: "; _.each(comms, function(o) { - var check = o.nodes.sort().join(); + var check = o.nodes; + _.each(swap, function (s) { + var index = _.indexOf(check, s); + if (index > -1) { + check = _.without(check, s); + swap = _.without(swap, s); + } + }); + check = check.sort().join(); switch (check) { case c1: c1 = ""; @@ -980,6 +989,9 @@ if (c4 !== "") { notFound += "[" + c4 + "] "; } + if (swap.length > 0) { + notFound += "[" + swap.join() + "] "; + } return msg + " and did not find: " + notFound; }; return !failed; @@ -2058,4 +2070,4 @@ }); }); -}()); \ No newline at end of file +}()); From 62e9a9d3d694eb70b7229b2f9f340edc06fb74a9 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Sun, 8 Dec 2013 14:42:00 +0100 Subject: [PATCH 17/22] Fixed tests for arango ui component in graphviewer. It is now also added to PhantomJS test suite --- .../graphViewer/ui/arangoAdapterControls.js | 8 +-- .../js/graphViewer/ui/edgeShaperControls.js | 17 +++--- .../graphViewer/ui/eventDispatcherControls.js | 2 - .../js/graphViewer/ui/layouterControls.js | 11 ++-- .../js/graphViewer/ui/nodeShaperControls.js | 17 +++--- .../js/graphViewer/ui/uiComponentsHelper.js | 9 +-- .../system/aardvark/test/karma/karma.conf.js | 3 +- .../specs/graphViewer/helper/uiMatchers.js | 1 - .../specAdapter/arangoAdapterUISpec.js | 55 ++++++++++--------- 9 files changed, 60 insertions(+), 63 deletions(-) diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/arangoAdapterControls.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/arangoAdapterControls.js index c2d62d24fb..860cb4064d 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/arangoAdapterControls.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/arangoAdapterControls.js @@ -37,15 +37,13 @@ function ArangoAdapterControls(list, adapter) { if (adapter === undefined) { throw "The ArangoAdapter has to be given."; } - var baseClass = "adapter"; - this.addControlChangeCollections = function(callback) { var prefix = "control_adapter_collections", idprefix = prefix + "_"; adapter.getCollections(function(nodeCols, edgeCols) { adapter.getGraphs(function(graphs) { - uiComponentsHelper.createButton(baseClass, list, "Collections", prefix, function() { + uiComponentsHelper.createButton(list, "Collections", prefix, function() { modalDialogHelper.createModalDialog("Switch Collections", idprefix, [{ type: "decission", @@ -132,7 +130,7 @@ function ArangoAdapterControls(list, adapter) { prioList = adapter.getPrioList(), label = "Group vertices"; - uiComponentsHelper.createButton(baseClass, list, label, prefix, function() { + uiComponentsHelper.createButton(list, label, prefix, function() { modalDialogHelper.createModalChangeDialog(label, idprefix, [{ type: "extendable", @@ -155,7 +153,7 @@ function ArangoAdapterControls(list, adapter) { }); /* adapter.getCollections(function(nodeCols, edgeCols) { - uiComponentsHelper.createButton(baseClass, list, "Collections", prefix, function() { + uiComponentsHelper.createButton(list, "Collections", prefix, function() { modalDialogHelper.createModalDialog("Switch Collections", idprefix, [{ type: "list", diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/edgeShaperControls.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/edgeShaperControls.js index 6df9be8843..ea8de4b218 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/edgeShaperControls.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/edgeShaperControls.js @@ -37,13 +37,12 @@ function EdgeShaperControls(list, shaper) { if (shaper === undefined) { throw "The EdgeShaper has to be given."; } - var self = this, - baseClass = "graph"; + var self = this; this.addControlOpticShapeNone = function() { var prefix = "control_edge_none", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "None", prefix, function() { + uiComponentsHelper.createButton(list, "None", prefix, function() { shaper.changeTo({ shape: { type: EdgeShaper.shapes.NONE @@ -55,7 +54,7 @@ function EdgeShaperControls(list, shaper) { this.addControlOpticShapeArrow = function() { var prefix = "control_edge_arrow", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Arrow", prefix, function() { + uiComponentsHelper.createButton(list, "Arrow", prefix, function() { shaper.changeTo({ shape: { type: EdgeShaper.shapes.ARROW @@ -69,7 +68,7 @@ function EdgeShaperControls(list, shaper) { this.addControlOpticLabel = function() { var prefix = "control_edge_label", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Label", prefix, function() { + uiComponentsHelper.createButton(list, "Label", prefix, function() { modalDialogHelper.createModalDialog("Switch Label Attribute", idprefix, [{ type: "text", @@ -90,7 +89,7 @@ function EdgeShaperControls(list, shaper) { this.addControlOpticSingleColour = function() { var prefix = "control_edge_singlecolour", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Single Colour", prefix, function() { + uiComponentsHelper.createButton(list, "Single Colour", prefix, function() { modalDialogHelper.createModalDialog("Switch to Colour", idprefix, [{ type: "text", @@ -111,7 +110,7 @@ function EdgeShaperControls(list, shaper) { this.addControlOpticAttributeColour = function() { var prefix = "control_edge_attributecolour", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Colour by Attribute", prefix, function() { + uiComponentsHelper.createButton(list, "Colour by Attribute", prefix, function() { modalDialogHelper.createModalDialog("Display colour by attribute", idprefix, [{ type: "text", @@ -132,7 +131,7 @@ function EdgeShaperControls(list, shaper) { this.addControlOpticGradientColour = function() { var prefix = "control_edge_gradientcolour", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Gradient Colour", prefix, function() { + uiComponentsHelper.createButton(list, "Gradient Colour", prefix, function() { modalDialogHelper.createModalDialog("Change colours for gradient", idprefix, [{ type: "text", @@ -173,4 +172,4 @@ function EdgeShaperControls(list, shaper) { self.addAllActions(); }; -} \ No newline at end of file +} diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/eventDispatcherControls.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/eventDispatcherControls.js index e53e353349..538c44c96a 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/eventDispatcherControls.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/eventDispatcherControls.js @@ -76,7 +76,6 @@ function EventDispatcherControls(list, nodeShaper, edgeShaper, dispatcherConfig) edit: "edit", view: "view" }, - baseClass = "event", dispatcher = new EventDispatcher(nodeShaper, edgeShaper, dispatcherConfig), setCursorIcon = function(icon) { @@ -102,7 +101,6 @@ function EventDispatcherControls(list, nodeShaper, edgeShaper, dispatcherConfig) }, createButton = function(title, callback) { uiComponentsHelper.createButton( - baseClass, list, title, "control_event_" + title, diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/layouterControls.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/layouterControls.js index 88e4272af8..e5f90dd141 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/layouterControls.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/layouterControls.js @@ -37,13 +37,12 @@ function LayouterControls(list, layouter) { if (layouter === undefined) { throw "The Layouter has to be given."; } - var self = this, - baseClass = "layout"; + var self = this; this.addControlGravity = function() { var prefix = "control_layout_gravity", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Gravity", prefix, function() { + uiComponentsHelper.createButton(list, "Gravity", prefix, function() { modalDialogHelper.createModalDialog("Switch Gravity Strength", idprefix, [{ type: "text", @@ -61,7 +60,7 @@ function LayouterControls(list, layouter) { this.addControlCharge = function() { var prefix = "control_layout_charge", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Charge", prefix, function() { + uiComponentsHelper.createButton(list, "Charge", prefix, function() { modalDialogHelper.createModalDialog("Switch Charge Strength", idprefix, [{ type: "text", @@ -79,7 +78,7 @@ function LayouterControls(list, layouter) { this.addControlDistance = function() { var prefix = "control_layout_distance", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Distance", prefix, function() { + uiComponentsHelper.createButton(list, "Distance", prefix, function() { modalDialogHelper.createModalDialog("Switch Distance Strength", idprefix, [{ type: "text", @@ -101,4 +100,4 @@ function LayouterControls(list, layouter) { self.addControlCharge(); }; -} \ No newline at end of file +} diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/nodeShaperControls.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/nodeShaperControls.js index 1f058b7d4f..ea0e7edfda 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/nodeShaperControls.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/nodeShaperControls.js @@ -38,7 +38,6 @@ function NodeShaperControls(list, shaper) { throw "The NodeShaper has to be given."; } var self = this, - baseClass = "graph", colourDiv, fillColourDiv = function(mapping) { @@ -63,7 +62,7 @@ function NodeShaperControls(list, shaper) { }; this.addControlOpticShapeNone = function() { - uiComponentsHelper.createButton(baseClass, list, "None", "control_node_none", function() { + uiComponentsHelper.createButton(list, "None", "control_node_none", function() { shaper.changeTo({ shape: { type: NodeShaper.shapes.NONE @@ -75,7 +74,7 @@ function NodeShaperControls(list, shaper) { this.addControlOpticShapeCircle = function() { var prefix = "control_node_circle", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Circle", prefix, function() { + uiComponentsHelper.createButton(list, "Circle", prefix, function() { modalDialogHelper.createModalDialog("Switch to Circle", idprefix, [{ type: "text", @@ -96,7 +95,7 @@ function NodeShaperControls(list, shaper) { this.addControlOpticShapeRect = function() { var prefix = "control_node_rect", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Rectangle", prefix, function() { + uiComponentsHelper.createButton(list, "Rectangle", prefix, function() { modalDialogHelper.createModalDialog("Switch to Rectangle", "control_node_rect_", [{ type: "text", @@ -122,7 +121,7 @@ function NodeShaperControls(list, shaper) { this.addControlOpticLabel = function() { var prefix = "control_node_label", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Label", prefix, function() { + uiComponentsHelper.createButton(list, "Label", prefix, function() { modalDialogHelper.createModalChangeDialog("Change label attribute", idprefix, [{ type: "text", @@ -144,7 +143,7 @@ function NodeShaperControls(list, shaper) { this.addControlOpticSingleColour = function() { var prefix = "control_node_singlecolour", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Single Colour", prefix, function() { + uiComponentsHelper.createButton(list, "Single Colour", prefix, function() { modalDialogHelper.createModalDialog("Switch to Colour", idprefix, [{ type: "text", @@ -170,7 +169,7 @@ function NodeShaperControls(list, shaper) { this.addControlOpticAttributeColour = function() { var prefix = "control_node_attributecolour", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Colour by Attribute", prefix, function() { + uiComponentsHelper.createButton(list, "Colour by Attribute", prefix, function() { modalDialogHelper.createModalDialog("Display colour by attribute", idprefix, [{ type: "text", @@ -191,7 +190,7 @@ function NodeShaperControls(list, shaper) { this.addControlOpticExpandColour = function() { var prefix = "control_node_expandcolour", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Expansion Colour", prefix, function() { + uiComponentsHelper.createButton(list, "Expansion Colour", prefix, function() { modalDialogHelper.createModalDialog("Display colours for expansion", idprefix, [{ type: "text", @@ -221,7 +220,7 @@ function NodeShaperControls(list, shaper) { this.addControlOpticLabelAndColour = function(adapter) { var prefix = "control_node_labelandcolour", idprefix = prefix + "_"; - uiComponentsHelper.createButton(baseClass, list, "Label", prefix, function() { + uiComponentsHelper.createButton(list, "Label", prefix, function() { modalDialogHelper.createModalChangeDialog("Change label attribute", idprefix, [{ type: "text", diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/uiComponentsHelper.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/uiComponentsHelper.js index 323d998190..8ded61658e 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/uiComponentsHelper.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/uiComponentsHelper.js @@ -33,23 +33,24 @@ var uiComponentsHelper = uiComponentsHelper || {}; (function componentsHelper() { "use strict"; - uiComponentsHelper.createButton = function(baseclass, list, title, prefix, callback) { + uiComponentsHelper.createButton = function(list, title, prefix, callback) { var li = document.createElement("li"), button = document.createElement("button"); - li.className = baseclass + "_control " + prefix; + li.className = "graph_control " + prefix; li.id = prefix; li.appendChild(button); button.className = "btn btn-primary gv_dropdown_entry"; button.appendChild(document.createTextNode(title)); list.appendChild(li); + button.id = prefix + "_button"; button.onclick = callback; }; - uiComponentsHelper.createListEntry = function(baseclass, list, title, prefix, callback) { + uiComponentsHelper.createListEntry = function(list, title, prefix, callback) { var button = document.createElement("li"), a = document.createElement("a"), label = document.createElement("label"); - button.className = baseclass + "_control " + prefix; + button.className = "graph_control " + prefix; button.id = prefix; button.appendChild(a); a.className = "gv_dropdown_entry"; diff --git a/js/apps/system/aardvark/test/karma/karma.conf.js b/js/apps/system/aardvark/test/karma/karma.conf.js index 4e73e536c7..71879c559f 100644 --- a/js/apps/system/aardvark/test/karma/karma.conf.js +++ b/js/apps/system/aardvark/test/karma/karma.conf.js @@ -171,7 +171,6 @@ module.exports = function(karma) { //Templates {pattern: 'frontend/js/templates/*.ejs', served:true, included:false, watched: true}, - // Specs // GraphViewer 'test/specs/graphViewer/specColourMapper/colourMapperSpec.js', @@ -183,7 +182,7 @@ module.exports = function(karma) { 'test/specs/graphViewer/specAdapter/arangoAdapterSpec.js', 'test/specs/graphViewer/specAdapter/foxxAdapterSpec.js', 'test/specs/graphViewer/specAdapter/previewAdapterSpec.js', - // 'test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js', + 'test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js', 'test/specs/graphViewer/specNodeShaper/nodeShaperSpec.js', // 'test/specs/graphViewer/specNodeShaper/nodeShaperUISpec.js', 'test/specs/graphViewer/specEdgeShaper/edgeShaperSpec.js', diff --git a/js/apps/system/aardvark/test/specs/graphViewer/helper/uiMatchers.js b/js/apps/system/aardvark/test/specs/graphViewer/helper/uiMatchers.js index 53f9584ceb..c9a6166292 100644 --- a/js/apps/system/aardvark/test/specs/graphViewer/helper/uiMatchers.js +++ b/js/apps/system/aardvark/test/specs/graphViewer/helper/uiMatchers.js @@ -95,7 +95,6 @@ var uiMatchers = uiMatchers || {}; expect(btn).toBeTag("button"); expect(btn).toBeOfClass("btn"); expect(btn).toBeOfClass("btn-icon"); - // Correctness of buttons is checked in eventDispatcherUISpec. }); return true; }, diff --git a/js/apps/system/aardvark/test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js b/js/apps/system/aardvark/test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js index 094aaa6225..52b365409d 100644 --- a/js/apps/system/aardvark/test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js +++ b/js/apps/system/aardvark/test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js @@ -100,13 +100,13 @@ var idPrefix = "#control_adapter_collections"; beforeEach(function() { - adapterUI.addControlChangeCollections(); - - expect($("#control_adapter_list " + idPrefix).length).toEqual(1); - expect(true).toBeFalsy(); - expect($("#control_adapter_list " + idPrefix)[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_adapter_collections"); - expect($(idPrefix + "_modal").length).toEqual(1); + runs(function() { + adapterUI.addControlChangeCollections(); + expect($("#control_adapter_list " + idPrefix).length).toEqual(1); + expect($("#control_adapter_list " + idPrefix)[0]).toConformToListCSS(); + helper.simulateMouseEvent("click", idPrefix.substr(1) + "_button"); + expect($(idPrefix + "_modal").length).toEqual(1); + }); }); afterEach(function() { @@ -117,7 +117,6 @@ it('should be added to the list', function() { runs(function() { - expect(42).toEqual(23); $(idPrefix + "_node_collection").prop("selectedIndex", 0); $(idPrefix + "_edge_collection").prop("selectedIndex", 1); helper.simulateMouseEvent("click", idPrefix.substr(1) + "_submit"); @@ -193,25 +192,29 @@ }); describe('change priority list control', function() { + + var idPrefix; + beforeEach(function() { + idPrefix = "#control_adapter_priority"; adapterUI.addControlChangePriority(); - expect($("#control_adapter_list #control_adapter_priority").length).toEqual(1); - expect($("#control_adapter_list #control_adapter_priority")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_adapter_priority"); - expect($("#control_adapter_priority_modal").length).toEqual(1); + expect($("#control_adapter_list " + idPrefix).length).toEqual(1); + expect($("#control_adapter_list " + idPrefix)[0]).toConformToListCSS(); + helper.simulateMouseEvent("click", idPrefix.substr(1) + "_button"); + expect($(idPrefix + "_modal").length).toEqual(1); }); afterEach(function() { waitsFor(function() { - return $("#control_adapter_priority_modal").length === 0; + return $(idPrefix + "_modal").length === 0; }, 2000, "The modal dialog should disappear."); }); it('should be added to the list', function() { runs(function() { - $("#control_adapter_priority_attribute_1").attr("value", "foo"); - helper.simulateMouseEvent("click", "control_adapter_priority_submit"); + $(idPrefix + "_attribute_1").attr("value", "foo"); + helper.simulateMouseEvent("click", idPrefix.substr(1) + "_submit"); expect(adapter.changeTo).toHaveBeenCalledWith({ prioList: ["foo"] }); @@ -220,8 +223,8 @@ it('should not add empty attributes to priority', function() { runs(function() { - $("#control_adapter_priority_attribute_1").attr("value", ""); - helper.simulateMouseEvent("click", "control_adapter_priority_submit"); + $(idPrefix + "_attribute_1").attr("value", ""); + helper.simulateMouseEvent("click", idPrefix.substr(1) + "_submit"); expect(adapter.changeTo).toHaveBeenCalledWith({ prioList: [] }); @@ -230,13 +233,13 @@ it('should add a new line to priority on demand', function() { runs(function() { - helper.simulateMouseEvent("click", "control_adapter_priority_attribute_addLine"); - expect($("#control_adapter_priority_attribute_1").length).toEqual(1); - expect($("#control_adapter_priority_attribute_2").length).toEqual(1); - expect($("#control_adapter_priority_attribute_addLine").length).toEqual(1); - $("#control_adapter_priority_attribute_1").attr("value", "foo"); - $("#control_adapter_priority_attribute_2").attr("value", "bar"); - helper.simulateMouseEvent("click", "control_adapter_priority_submit"); + helper.simulateMouseEvent("click", idPrefix.substr(1) + "_attribute_addLine"); + expect($(idPrefix + "_attribute_1").length).toEqual(1); + expect($(idPrefix + "_attribute_2").length).toEqual(1); + expect($(idPrefix + "_attribute_addLine").length).toEqual(1); + $(idPrefix + "_attribute_1").attr("value", "foo"); + $(idPrefix + "_attribute_2").attr("value", "bar"); + helper.simulateMouseEvent("click", idPrefix.substr(1) + "_submit"); expect(adapter.changeTo).toHaveBeenCalledWith({ prioList: ["foo", "bar"] }); @@ -293,6 +296,8 @@ }); }); }); + + /* TO_DO it('should load the current prioList from the adapter', function() { @@ -328,7 +333,7 @@ helper.simulateMouseEvent("click", "control_adapter_priority_cancel"); }); }); - + */ }); it('should be able to add all controls to the list', function() { From f04c25e0519df4411bb373c0c23d1593758bfa88 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Tue, 10 Dec 2013 10:51:41 +0100 Subject: [PATCH 18/22] Finally all GraphViewer Tests added and running under phantomjs and added to automatic test --- .../system/aardvark/test/karma/karma.conf.js | 10 ++++----- .../specEdgeShaper/edgeShaperUISpec.js | 12 +++++----- .../specEvents/eventDispatcherUISpec.js | 4 +++- .../specForceLayouter/forceLayouterUISpec.js | 6 ++--- .../specNodeShaper/nodeShaperUISpec.js | 22 +++++++++---------- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/js/apps/system/aardvark/test/karma/karma.conf.js b/js/apps/system/aardvark/test/karma/karma.conf.js index 71879c559f..bf1789595d 100644 --- a/js/apps/system/aardvark/test/karma/karma.conf.js +++ b/js/apps/system/aardvark/test/karma/karma.conf.js @@ -184,15 +184,15 @@ module.exports = function(karma) { 'test/specs/graphViewer/specAdapter/previewAdapterSpec.js', 'test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js', 'test/specs/graphViewer/specNodeShaper/nodeShaperSpec.js', - // 'test/specs/graphViewer/specNodeShaper/nodeShaperUISpec.js', + 'test/specs/graphViewer/specNodeShaper/nodeShaperUISpec.js', 'test/specs/graphViewer/specEdgeShaper/edgeShaperSpec.js', - // 'test/specs/graphViewer/specEdgeShaper/edgeShaperUISpec.js', + 'test/specs/graphViewer/specEdgeShaper/edgeShaperUISpec.js', 'test/specs/graphViewer/specForceLayouter/forceLayouterSpec.js', - // 'test/specs/graphViewer/specForceLayouter/forceLayouterUISpec.js', + 'test/specs/graphViewer/specForceLayouter/forceLayouterUISpec.js', 'test/specs/graphViewer/specEvents/eventLibrarySpec.js', 'test/specs/graphViewer/specEvents/eventDispatcherSpec.js', - // 'test/specs/graphViewer/specEvents/eventDispatcherUISpec.js', - // 'test/specs/graphViewer/specZoomManager/zoomManagerSpec.js', + 'test/specs/graphViewer/specEvents/eventDispatcherUISpec.js', + 'test/specs/graphViewer/specZoomManager/zoomManagerSpec.js', 'test/specs/graphViewer/specGraphViewer/graphViewerSpec.js', 'test/specs/graphViewer/specGraphViewer/graphViewerUISpec.js', 'test/specs/graphViewer/specGraphViewer/graphViewerWidgetSpec.js', diff --git a/js/apps/system/aardvark/test/specs/graphViewer/specEdgeShaper/edgeShaperUISpec.js b/js/apps/system/aardvark/test/specs/graphViewer/specEdgeShaper/edgeShaperUISpec.js index 3a077736b2..7a09038815 100644 --- a/js/apps/system/aardvark/test/specs/graphViewer/specEdgeShaper/edgeShaperUISpec.js +++ b/js/apps/system/aardvark/test/specs/graphViewer/specEdgeShaper/edgeShaperUISpec.js @@ -74,7 +74,7 @@ expect($("#control_edge_list #control_edge_none").length).toEqual(1); expect($("#control_edge_list #control_edge_none")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_edge_none"); + helper.simulateMouseEvent("click", "control_edge_none_button"); expect(shaper.changeTo).toHaveBeenCalledWith({ shape: { @@ -91,7 +91,7 @@ expect($("#control_edge_list #control_edge_arrow").length).toEqual(1); expect($("#control_edge_list #control_edge_arrow")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_edge_arrow"); + helper.simulateMouseEvent("click", "control_edge_arrow_button"); expect(shaper.changeTo).toHaveBeenCalledWith({ shape: { @@ -109,7 +109,7 @@ expect($("#control_edge_list #control_edge_label").length).toEqual(1); expect($("#control_edge_list #control_edge_label")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_edge_label"); + helper.simulateMouseEvent("click", "control_edge_label_button"); $("#control_edge_label_key").attr("value", "theAnswer"); helper.simulateMouseEvent("click", "control_edge_label_submit"); @@ -131,7 +131,7 @@ expect($("#control_edge_list #control_edge_singlecolour").length).toEqual(1); expect($("#control_edge_list #control_edge_singlecolour")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_edge_singlecolour"); + helper.simulateMouseEvent("click", "control_edge_singlecolour_button"); $("#control_edge_singlecolour_stroke").attr("value", "#123456"); helper.simulateMouseEvent("click", "control_edge_singlecolour_submit"); @@ -156,7 +156,7 @@ expect($("#control_edge_list #control_edge_attributecolour").length).toEqual(1); expect($("#control_edge_list #control_edge_attributecolour")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_edge_attributecolour"); + helper.simulateMouseEvent("click", "control_edge_attributecolour_button"); $("#control_edge_attributecolour_key").attr("value", "label"); helper.simulateMouseEvent("click", "control_edge_attributecolour_submit"); @@ -181,7 +181,7 @@ expect($("#control_edge_list #control_edge_gradientcolour").length).toEqual(1); expect($("#control_edge_list #control_edge_gradientcolour")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_edge_gradientcolour"); + helper.simulateMouseEvent("click", "control_edge_gradientcolour_button"); $("#control_edge_gradientcolour_source").attr("value", "#123456"); $("#control_edge_gradientcolour_target").attr("value", "#654321"); helper.simulateMouseEvent("click", "control_edge_gradientcolour_submit"); diff --git a/js/apps/system/aardvark/test/specs/graphViewer/specEvents/eventDispatcherUISpec.js b/js/apps/system/aardvark/test/specs/graphViewer/specEvents/eventDispatcherUISpec.js index 2eaec3642d..baa91c2777 100644 --- a/js/apps/system/aardvark/test/specs/graphViewer/specEvents/eventDispatcherUISpec.js +++ b/js/apps/system/aardvark/test/specs/graphViewer/specEvents/eventDispatcherUISpec.js @@ -252,7 +252,9 @@ expect(adapter.createNode).toHaveBeenCalledWith( {}, - jasmine.any(Function) + jasmine.any(Function), + jasmine.any(Number), // Number not yet correctly tested + jasmine.any(Number) ); /* expect(adapter.createNode).toHaveBeenCalledWith( diff --git a/js/apps/system/aardvark/test/specs/graphViewer/specForceLayouter/forceLayouterUISpec.js b/js/apps/system/aardvark/test/specs/graphViewer/specForceLayouter/forceLayouterUISpec.js index 90206ba8ae..5c62d69879 100644 --- a/js/apps/system/aardvark/test/specs/graphViewer/specForceLayouter/forceLayouterUISpec.js +++ b/js/apps/system/aardvark/test/specs/graphViewer/specForceLayouter/forceLayouterUISpec.js @@ -75,7 +75,7 @@ expect($("#control_layout_list #control_layout_gravity").length).toEqual(1); expect($("#control_layout_list #control_layout_gravity")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_layout_gravity"); + helper.simulateMouseEvent("click", "control_layout_gravity_button"); expect($("#control_layout_gravity_modal").length).toEqual(1); @@ -100,7 +100,7 @@ expect($("#control_layout_list #control_layout_distance").length).toEqual(1); expect($("#control_layout_list #control_layout_distance")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_layout_distance"); + helper.simulateMouseEvent("click", "control_layout_distance_button"); expect($("#control_layout_distance_modal").length).toEqual(1); @@ -125,7 +125,7 @@ expect($("#control_layout_list #control_layout_charge").length).toEqual(1); expect($("#control_layout_list #control_layout_charge")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_layout_charge"); + helper.simulateMouseEvent("click", "control_layout_charge_button"); expect($("#control_layout_charge_modal").length).toEqual(1); diff --git a/js/apps/system/aardvark/test/specs/graphViewer/specNodeShaper/nodeShaperUISpec.js b/js/apps/system/aardvark/test/specs/graphViewer/specNodeShaper/nodeShaperUISpec.js index 9c83bb48db..e8a648234c 100644 --- a/js/apps/system/aardvark/test/specs/graphViewer/specNodeShaper/nodeShaperUISpec.js +++ b/js/apps/system/aardvark/test/specs/graphViewer/specNodeShaper/nodeShaperUISpec.js @@ -87,7 +87,7 @@ expect($("#control_node_list #control_node_none").length).toEqual(1); expect($("#control_node_list #control_node_none")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_node_none"); + helper.simulateMouseEvent("click", "control_node_none_button"); expect(shaper.changeTo).toHaveBeenCalledWith({ shape: { @@ -103,13 +103,11 @@ expect($("#control_node_list #control_node_circle").length).toEqual(1); expect($("#control_node_list #control_node_circle")[0]).toConformToListCSS(); - - helper.simulateMouseEvent("click", "control_node_circle"); + helper.simulateMouseEvent("click", "control_node_circle_button"); expect($("#control_node_circle_modal").length).toEqual(1); - $("#control_node_circle_radius").attr("value", 42); helper.simulateMouseEvent("click", "control_node_circle_submit"); - + expect(shaper.changeTo).toHaveBeenCalledWith({ shape: { type: NodeShaper.shapes.CIRCLE, @@ -131,7 +129,7 @@ expect($("#control_node_list #control_node_rect").length).toEqual(1); expect($("#control_node_list #control_node_rect")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_node_rect"); + helper.simulateMouseEvent("click", "control_node_rect_button"); $("#control_node_rect_width").attr("value", 42); $("#control_node_rect_height").attr("value", 12); helper.simulateMouseEvent("click", "control_node_rect_submit"); @@ -158,7 +156,7 @@ expect($("#control_node_list #control_node_label").length).toEqual(1); expect($("#control_node_list #control_node_label")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_node_label"); + helper.simulateMouseEvent("click", "control_node_label_button"); $("#control_node_label_key").attr("value", "theAnswer"); helper.simulateMouseEvent("click", "control_node_label_submit"); @@ -180,7 +178,7 @@ expect($("#control_node_list #control_node_singlecolour").length).toEqual(1); expect($("#control_node_list #control_node_singlecolour")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_node_singlecolour"); + helper.simulateMouseEvent("click", "control_node_singlecolour_button"); $("#control_node_singlecolour_fill").attr("value", "#123456"); $("#control_node_singlecolour_stroke").attr("value", "#654321"); helper.simulateMouseEvent("click", "control_node_singlecolour_submit"); @@ -207,7 +205,7 @@ expect($("#control_node_list #control_node_attributecolour").length).toEqual(1); expect($("#control_node_list #control_node_attributecolour")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_node_attributecolour"); + helper.simulateMouseEvent("click", "control_node_attributecolour_button"); $("#control_node_attributecolour_key").attr("value", "label"); helper.simulateMouseEvent("click", "control_node_attributecolour_submit"); @@ -232,7 +230,7 @@ expect($("#control_node_list #control_node_expandcolour").length).toEqual(1); expect($("#control_node_list #control_node_expandcolour")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_node_expandcolour"); + helper.simulateMouseEvent("click", "control_node_expandcolour_button"); $("#control_node_expandcolour_expanded").attr("value", "#123456"); $("#control_node_expandcolour_collapsed").attr("value", "#654321"); helper.simulateMouseEvent("click", "control_node_expandcolour_submit"); @@ -324,7 +322,7 @@ expect($("#control_node_list #control_node_expandcolour").length).toEqual(1); expect($("#control_node_list #control_node_expandcolour")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_node_expandcolour"); + helper.simulateMouseEvent("click", "control_node_expandcolour_button"); $("#control_node_expandcolour_expanded").attr("value", "#123456"); $("#control_node_expandcolour_collapsed").attr("value", "#654321"); @@ -355,7 +353,7 @@ expect($("#control_node_list #control_node_labelandcolour").length).toEqual(1); expect($("#control_node_list #control_node_labelandcolour")[0]).toConformToListCSS(); - helper.simulateMouseEvent("click", "control_node_labelandcolour"); + helper.simulateMouseEvent("click", "control_node_labelandcolour_button"); $("#control_node_labelandcolour_label-attribute").attr("value", "label"); helper.simulateMouseEvent("click", "control_node_labelandcolour_submit"); From 5e5a5fb32b3b5371c907c5949800531d8f3962b1 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 11 Dec 2013 09:56:54 +0100 Subject: [PATCH 19/22] added JS bindings for agency --- arangod/Cluster/AgencyComm.cpp | 132 +++++++++-- arangod/Cluster/AgencyComm.h | 51 ++-- arangod/Cluster/ApplicationCluster.cpp | 13 +- arangod/Cluster/ApplicationCluster.h | 4 + arangod/RestServer/ArangoServer.cpp | 12 + arangod/V8Server/v8-vocbase.cpp | 316 +++++++++++++++++++++++++ lib/V8/v8-globals.cpp | 2 +- lib/V8/v8-globals.h | 14 +- 8 files changed, 500 insertions(+), 44 deletions(-) diff --git a/arangod/Cluster/AgencyComm.cpp b/arangod/Cluster/AgencyComm.cpp index a2ce046310..a0e0ace67d 100644 --- a/arangod/Cluster/AgencyComm.cpp +++ b/arangod/Cluster/AgencyComm.cpp @@ -132,6 +132,21 @@ std::string AgencyCommResult::errorMessage () const { return result; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief extract the error details from the result +/// if there is no error, an empty string will be returned +//////////////////////////////////////////////////////////////////////////////// + +std::string AgencyCommResult::errorDetails () const { + const std::string errorMessage = this->errorMessage(); + + if (errorMessage.empty()) { + return _message; + } + + return _message + " (" + errorMessage + ")"; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief recursively flatten the JSON response into a map //////////////////////////////////////////////////////////////////////////////// @@ -211,6 +226,7 @@ bool AgencyCommResult::processJsonNode (TRI_json_t const* node, // otherwise return value out[prefix] = std::string(value->_value._string.data, value->_value._string.length - 1); } + } } } @@ -297,7 +313,8 @@ AgencyConnectionOptions AgencyComm::_globalConnectionOptions = { /// @brief constructs an agency communication object //////////////////////////////////////////////////////////////////////////////// -AgencyComm::AgencyComm () { +AgencyComm::AgencyComm (bool addNewEndpoints) + : _addNewEndpoints(addNewEndpoints) { } //////////////////////////////////////////////////////////////////////////////// @@ -401,7 +418,8 @@ void AgencyComm::disconnect () { /// @brief adds an endpoint to the endpoints list //////////////////////////////////////////////////////////////////////////////// -bool AgencyComm::addEndpoint (std::string const& endpointSpecification) { +bool AgencyComm::addEndpoint (std::string const& endpointSpecification, + bool toFront) { LOG_TRACE("adding global endpoint '%s'", endpointSpecification.c_str()); { @@ -430,7 +448,12 @@ bool AgencyComm::addEndpoint (std::string const& endpointSpecification) { return false; } - AgencyComm::_globalEndpoints.push_back(agencyEndpoint); + if (toFront) { + AgencyComm::_globalEndpoints.push_front(agencyEndpoint); + } + else { + AgencyComm::_globalEndpoints.push_back(agencyEndpoint); + } } return true; @@ -501,6 +524,12 @@ bool AgencyComm::hasEndpoint (std::string const& endpointSpecification) { //////////////////////////////////////////////////////////////////////////////// void AgencyComm::setPrefix (std::string const& prefix) { + // agency prefix must not be changed + if (! _globalPrefix.empty() && prefix != _globalPrefix) { + LOG_ERROR("agency-prefix cannot be changed at runtime"); + return; + } + _globalPrefix = prefix; // make sure prefix starts with a forward slash @@ -518,6 +547,14 @@ void AgencyComm::setPrefix (std::string const& prefix) { LOG_TRACE("setting agency-prefix to '%s'", prefix.c_str()); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief gets the global prefix for all operations +//////////////////////////////////////////////////////////////////////////////// + +std::string AgencyComm::prefix () { + return _globalPrefix; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief generate a timestamp //////////////////////////////////////////////////////////////////////////////// @@ -539,6 +576,32 @@ std::string AgencyComm::generateStamp () { /// @brief get a stringified version of the endpoints //////////////////////////////////////////////////////////////////////////////// +const std::vector AgencyComm::getEndpoints () { + std::vector result; + + { + // iterate over the list of endpoints + READ_LOCKER(AgencyComm::_globalLock); + + std::list::const_iterator it = AgencyComm::_globalEndpoints.begin(); + + while (it != AgencyComm::_globalEndpoints.end()) { + AgencyEndpoint const* agencyEndpoint = (*it); + + assert(agencyEndpoint != 0); + + result.push_back(agencyEndpoint->_endpoint->getSpecification()); + ++it; + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get a stringified version of the endpoints +//////////////////////////////////////////////////////////////////////////////// + const std::string AgencyComm::getEndpointsString () { std::string result; @@ -600,6 +663,27 @@ AgencyEndpoint* AgencyComm::createAgencyEndpoint (std::string const& endpointSpe // --SECTION-- public methods // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief gets the backend version +//////////////////////////////////////////////////////////////////////////////// + +std::string AgencyComm::getVersion () { + AgencyCommResult result; + + sendWithFailover(triagens::rest::HttpRequest::HTTP_REQUEST_GET, + _globalConnectionOptions._requestTimeout, + result, + "version", + "", + false); + + if (result.successful()) { + return result._body; + } + + return ""; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief creates a directory in the backend //////////////////////////////////////////////////////////////////////////////// @@ -803,7 +887,7 @@ AgencyEndpoint* AgencyComm::popEndpoint () { /// @brief reinsert an endpoint into the queue //////////////////////////////////////////////////////////////////////////////// -bool AgencyComm::requeueEndpoint (AgencyEndpoint* agencyEndpoint, +void AgencyComm::requeueEndpoint (AgencyEndpoint* agencyEndpoint, bool wasWorking) { WRITE_LOCKER(AgencyComm::_globalLock); const size_t numEndpoints = _globalEndpoints.size(); @@ -825,8 +909,6 @@ bool AgencyComm::requeueEndpoint (AgencyEndpoint* agencyEndpoint, } assert(_globalEndpoints.size() == numEndpoints); - - return wasWorking; } //////////////////////////////////////////////////////////////////////////////// @@ -908,8 +990,21 @@ bool AgencyComm::sendWithFailover (triagens::rest::HttpRequest::HttpRequestType if (! AgencyComm::hasEndpoint(endpoint)) { // redirection to an unknown endpoint + + if (_addNewEndpoints) { + AgencyComm::addEndpoint(endpoint, true); + + // re-check the new endpoint + if (AgencyComm::hasEndpoint(endpoint)) { + ++numEndpoints; + continue; + } + } + LOG_ERROR("found redirection to unknown endpoint '%s'. Will not follow!", endpoint.c_str()); + + // this is an error return false; } @@ -917,10 +1012,16 @@ bool AgencyComm::sendWithFailover (triagens::rest::HttpRequest::HttpRequestType continue; } - // watches might time out, this still counts as a success - const bool wasSuccessful = result.successful() || (isWatch && result._statusCode == 0); + // we can stop iterating over endpoints if the operation succeeded, + // if a watch timed out or + // if the reason for failure was a client-side error + const bool canAbort = result.successful() || + (isWatch && result._statusCode == 0) || + (result._statusCode >= 400 && result._statusCode <= 499); - if (requeueEndpoint(agencyEndpoint, wasSuccessful)) { + requeueEndpoint(agencyEndpoint, canAbort); + + if (canAbort) { // we're done return true; } @@ -954,13 +1055,14 @@ bool AgencyComm::send (triagens::httpclient::GeneralClientConnection* connection assert(! url.empty()); result._statusCode = 0; - - LOG_TRACE("sending %s request to agency at endpoint '%s', url '%s': %s", + +/* + LOG_INFO("sending %s request to agency at endpoint '%s', url '%s': %s", triagens::rest::HttpRequest::translateMethod(method).c_str(), connection->getEndpoint()->getSpecification().c_str(), url.c_str(), body.c_str()); - + */ triagens::httpclient::SimpleHttpClient client(connection, timeout, false); @@ -1014,12 +1116,12 @@ bool AgencyComm::send (triagens::httpclient::GeneralClientConnection* connection if (found) { result._index = triagens::basics::StringUtils::uint64(lastIndex); } - - LOG_TRACE("request to agency returned status code %d, message: '%s', body: '%s'", +/* + LOG_INFO("request to agency returned status code %d, message: '%s', body: '%s'", result._statusCode, result._message.c_str(), result._body.c_str()); - +*/ delete response; return result.successful(); diff --git a/arangod/Cluster/AgencyComm.h b/arangod/Cluster/AgencyComm.h index a2798b8807..43391cc57e 100644 --- a/arangod/Cluster/AgencyComm.h +++ b/arangod/Cluster/AgencyComm.h @@ -147,6 +147,13 @@ namespace triagens { std::string errorMessage () const; +//////////////////////////////////////////////////////////////////////////////// +/// @brief extract the error details from the result +/// if there is no error, an empty string will be returned +//////////////////////////////////////////////////////////////////////////////// + + std::string errorDetails () const; + //////////////////////////////////////////////////////////////////////////////// /// @brief return the location header (might be empty) //////////////////////////////////////////////////////////////////////////////// @@ -202,7 +209,7 @@ namespace triagens { /// @brief creates a communication channel //////////////////////////////////////////////////////////////////////////////// - AgencyComm (); + AgencyComm (bool = true); //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a communication channel @@ -236,31 +243,45 @@ namespace triagens { /// @brief adds an endpoint to the agents list //////////////////////////////////////////////////////////////////////////////// - static bool addEndpoint (std::string const&); + static bool addEndpoint (std::string const&, + bool = false); //////////////////////////////////////////////////////////////////////////////// /// @brief removes an endpoint from the agents list //////////////////////////////////////////////////////////////////////////////// static bool removeEndpoint (std::string const&); + //////////////////////////////////////////////////////////////////////////////// /// @brief checks if an endpoint is present //////////////////////////////////////////////////////////////////////////////// static bool hasEndpoint (std::string const&); +//////////////////////////////////////////////////////////////////////////////// +/// @brief get a stringified version of the endpoints +//////////////////////////////////////////////////////////////////////////////// + + static const std::vector getEndpoints (); + //////////////////////////////////////////////////////////////////////////////// /// @brief get a stringified version of the endpoints //////////////////////////////////////////////////////////////////////////////// static const std::string getEndpointsString (); - + //////////////////////////////////////////////////////////////////////////////// /// @brief sets the global prefix for all operations //////////////////////////////////////////////////////////////////////////////// static void setPrefix (std::string const&); +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the global prefix for all operations +//////////////////////////////////////////////////////////////////////////////// + + static std::string prefix (); + //////////////////////////////////////////////////////////////////////////////// /// @brief generate a timestamp //////////////////////////////////////////////////////////////////////////////// @@ -281,6 +302,12 @@ namespace triagens { // --SECTION-- public methods // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief gets the backend version +//////////////////////////////////////////////////////////////////////////////// + + std::string getVersion (); + //////////////////////////////////////////////////////////////////////////////// /// @brief creates a directory in the backend //////////////////////////////////////////////////////////////////////////////// @@ -351,7 +378,7 @@ namespace triagens { /// @brief reinsert an endpoint into the queue //////////////////////////////////////////////////////////////////////////////// - bool requeueEndpoint (AgencyEndpoint*, + void requeueEndpoint (AgencyEndpoint*, bool); //////////////////////////////////////////////////////////////////////////////// @@ -389,22 +416,10 @@ namespace triagens { private: //////////////////////////////////////////////////////////////////////////////// -/// @brief connect timeout +/// @brief automatically add unknown endpoints if redirected to by agency? //////////////////////////////////////////////////////////////////////////////// - double _connectTimeout; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief request timeout -//////////////////////////////////////////////////////////////////////////////// - - double _requestTimeout; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief connect retries -//////////////////////////////////////////////////////////////////////////////// - - size_t _connectRetries; + bool _addNewEndpoints; // ----------------------------------------------------------------------------- // --SECTION-- private static variables diff --git a/arangod/Cluster/ApplicationCluster.cpp b/arangod/Cluster/ApplicationCluster.cpp index 7441d485ca..70d55df658 100644 --- a/arangod/Cluster/ApplicationCluster.cpp +++ b/arangod/Cluster/ApplicationCluster.cpp @@ -205,15 +205,20 @@ bool ApplicationCluster::start () { _myAddress.c_str()); } - ServerState::instance()->setRole(role); ServerState::instance()->setState(ServerState::STATE_STARTUP); + + AgencyComm comm; + const std::string version = comm.getVersion(); - LOG_INFO("Cluster feature is turned on. Server id: '%s', internal address: %s, role: %s, agency endpoints: %s", + LOG_INFO("Cluster feature is turned on. " + "Agency version: %s, Agency endpoints: %s, " + "server id: '%s', internal address: %s, role: %s", + version.c_str(), + endpoints.c_str(), _myId.c_str(), _myAddress.c_str(), - ServerState::roleToString(role).c_str(), - endpoints.c_str()); + ServerState::roleToString(role).c_str()); // start heartbeat thread _heartbeat = new HeartbeatThread(_myId, _heartbeatInterval * 1000, 5); diff --git a/arangod/Cluster/ApplicationCluster.h b/arangod/Cluster/ApplicationCluster.h index ad04cb3107..dace1d3c49 100644 --- a/arangod/Cluster/ApplicationCluster.h +++ b/arangod/Cluster/ApplicationCluster.h @@ -70,6 +70,10 @@ namespace triagens { public: +//////////////////////////////////////////////////////////////////////////////// +/// @brief whether or not the cluster is enabled +//////////////////////////////////////////////////////////////////////////////// + inline bool enabled () const { return _enableCluster; } diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 77275fcc89..526e6a47f2 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -501,6 +501,7 @@ void ArangoServer::buildApplicationServer () { #ifdef TRI_ENABLE_CLUSTER _applicationCluster = new ApplicationCluster(); + if (_applicationCluster == 0) { LOG_FATAL_AND_EXIT("out of memory"); } @@ -656,6 +657,12 @@ void ArangoServer::buildApplicationServer () { mode == OperationMode::MODE_UNITTESTS || mode == OperationMode::MODE_JSLINT || mode == OperationMode::MODE_SCRIPT) { + +#ifdef TRI_ENABLE_CLUSTER + // we need to prepare the cluster even in console mode + _applicationCluster->prepare(); +#endif + int res = executeConsole(mode); TRI_EXIT_FUNCTION(res, NULL); @@ -663,6 +670,11 @@ void ArangoServer::buildApplicationServer () { #ifdef TRI_ENABLE_MRUBY else if (mode == OperationMode::MODE_RUBY_CONSOLE) { + +#ifdef TRI_ENABLE_CLUSTER + // we need to prepare the cluster even in console mode + _applicationCluster->prepare(); +#endif int res = executeRubyConsole(); TRI_EXIT_FUNCTION(res, NULL); diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 59f64c682d..c46b724643 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -76,6 +76,10 @@ #include "v8.h" #include "V8/JSLoader.h" +#ifdef TRI_ENABLE_CLUSTER +#include "Cluster/AgencyComm.h" +#endif + #include "unicode/timezone.h" #include "unicode/utypes.h" #include "unicode/datefmt.h" @@ -2205,6 +2209,293 @@ static TRI_general_cursor_t* UnwrapGeneralCursor (v8::Handle cursorO /// @} //////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- agency functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief compares and swaps a value in the agency +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER + +static v8::Handle JS_CasAgency (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 3) { + TRI_V8_EXCEPTION_USAGE(scope, "set(, , )"); + } + + const std::string key = TRI_ObjectToString(argv[0]); + const std::string oldValue = TRI_ObjectToString(argv[1]); + const std::string newValue = TRI_ObjectToString(argv[2]); + + AgencyComm comm; + AgencyCommResult result = comm.casValue(key, oldValue, newValue); +// TODO + return scope.Close(v8::Undefined()); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates a directory in the agency +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER + +static v8::Handle JS_CreateDirectoryAgency (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() != 1) { + TRI_V8_EXCEPTION_USAGE(scope, "createDirectory()"); + } + + const std::string key = TRI_ObjectToString(argv[0]); + + AgencyComm comm; + AgencyCommResult result = comm.createDirectory(key); + + if (! result.successful()) { + const std::string errorDetails = result.errorDetails(); + v8::Handle err = v8::String::New(errorDetails.c_str(), errorDetails.size()); + return scope.Close(v8::ThrowException(err)); + } + + return scope.Close(v8::True()); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @brief gets a value from the agency +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER + +static v8::Handle JS_GetAgency (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 1) { + TRI_V8_EXCEPTION_USAGE(scope, "get(, )"); + } + + const std::string key = TRI_ObjectToString(argv[0]); + bool recursive = false; + + if (argv.Length() > 1) { + recursive = TRI_ObjectToBoolean(argv[1]); + } + + AgencyComm comm; + AgencyCommResult result = comm.getValues(key, recursive); + + if (! result.successful()) { + const std::string errorDetails = result.errorDetails(); + v8::Handle err = v8::String::New(errorDetails.c_str(), errorDetails.size()); + return scope.Close(v8::ThrowException(err)); + } + + std::map out; + + result.flattenJson(out, "", false); + std::map::const_iterator it = out.begin(); + + v8::Handle l = v8::Object::New(); + + while (it != out.end()) { + const std::string key = (*it).first; + const std::string value = (*it).second; + + l->Set(v8::String::New(key.c_str(), key.size()), v8::String::New(value.c_str(), value.size())); + ++it; + } + + return scope.Close(l); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes a value from the agency +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER + +static v8::Handle JS_RemoveAgency (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 1) { + TRI_V8_EXCEPTION_USAGE(scope, "remove(, )"); + } + + const std::string key = TRI_ObjectToString(argv[0]); + bool recursive = false; + + if (argv.Length() > 1) { + recursive = TRI_ObjectToBoolean(argv[1]); + } + + AgencyComm comm; + AgencyCommResult result = comm.removeValues(key, recursive); + + if (! result.successful()) { + const std::string errorDetails = result.errorDetails(); + v8::Handle err = v8::String::New(errorDetails.c_str(), errorDetails.size()); + return scope.Close(v8::ThrowException(err)); + } + + return scope.Close(v8::True()); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @brief sets a value in the agency +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER + +static v8::Handle JS_SetAgency (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 2) { + TRI_V8_EXCEPTION_USAGE(scope, "set(, )"); + } + + const std::string key = TRI_ObjectToString(argv[0]); + const std::string value = TRI_ObjectToString(argv[1]); + + AgencyComm comm; + AgencyCommResult result = comm.setValue(key, value); + + if (! result.successful()) { + const std::string errorDetails = result.errorDetails(); + v8::Handle err = v8::String::New(errorDetails.c_str(), errorDetails.size()); + return scope.Close(v8::ThrowException(err)); + } + + return scope.Close(v8::True()); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @brief watches a value in the agency +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER + +static v8::Handle JS_WatchAgency (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 1) { + TRI_V8_EXCEPTION_USAGE(scope, "watch(, , 1) { + waitIndex = TRI_ObjectToUInt64(argv[1], true); + } + if (argv.Length() > 2) { + timeout = TRI_ObjectToDouble(argv[2]); + } + + AgencyComm comm; + AgencyCommResult result = comm.watchValue(key, waitIndex, timeout); + + if (! result.successful()) { + const std::string errorDetails = result.errorDetails(); + v8::Handle err = v8::String::New(errorDetails.c_str(), errorDetails.size()); + return scope.Close(v8::ThrowException(err)); + } + + if (result._statusCode > 0) { + std::map out; + result.flattenJson(out, "", false); + std::map::const_iterator it = out.begin(); + + v8::Handle l = v8::Array::New(); + + while (it != out.end()) { + const std::string key = (*it).first; + const std::string value = (*it).second; + + l->Set(v8::String::New(key.c_str(), key.size()), v8::String::New(value.c_str(), value.size())); + ++it; + } + + return scope.Close(l); + } + + return scope.Close(v8::False()); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the agency endpoints +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER + +static v8::Handle JS_EndpointsAgency (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() != 0) { + TRI_V8_EXCEPTION_USAGE(scope, "endpoints()"); + } + + const std::vector endpoints = AgencyComm::getEndpoints(); + + v8::Handle l = v8::Array::New(); + + for (size_t i = 0; i < endpoints.size(); ++i) { + const std::string endpoint = endpoints[i]; + + l->Set((uint32_t) i, v8::String::New(endpoint.c_str(), endpoint.size())); + } + + return scope.Close(l); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the agency prefix +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER + +static v8::Handle JS_PrefixAgency (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() != 0) { + TRI_V8_EXCEPTION_USAGE(scope, "prefix()"); + } + + const std::string prefix = AgencyComm::prefix(); + + return scope.Close(v8::String::New(prefix.c_str(), prefix.size())); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the agency version +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER + +static v8::Handle JS_VersionAgency (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() != 0) { + TRI_V8_EXCEPTION_USAGE(scope, "version()"); + } + + AgencyComm comm; + const std::string version = comm.getVersion(); + + return scope.Close(v8::String::New(version.c_str(), version.size())); +} +#endif + // ----------------------------------------------------------------------------- // --SECTION-- javascript functions // ----------------------------------------------------------------------------- @@ -9027,6 +9318,31 @@ void TRI_InitV8VocBridge (v8::Handle context, TRI_AddGlobalFunctionVocbase(context, "LIST_ENDPOINTS", JS_ListEndpoints, true); TRI_AddGlobalFunctionVocbase(context, "RELOAD_AUTH", JS_ReloadAuth, true); TRI_AddGlobalFunctionVocbase(context, "TRANSACTION", JS_Transaction, true); + +#ifdef TRI_ENABLE_CLUSTER + // ............................................................................. + // generate the agency template + // ............................................................................. + + ft = v8::FunctionTemplate::New(); + ft->SetClassName(TRI_V8_SYMBOL("ArangoAgency")); + + rt = ft->InstanceTemplate(); + rt->SetInternalFieldCount(2); + + TRI_AddMethodVocbase(rt, "cas", JS_CasAgency); + TRI_AddMethodVocbase(rt, "createDirectory", JS_CreateDirectoryAgency); + TRI_AddMethodVocbase(rt, "get", JS_GetAgency); + TRI_AddMethodVocbase(rt, "remove", JS_RemoveAgency); + TRI_AddMethodVocbase(rt, "set", JS_SetAgency); + TRI_AddMethodVocbase(rt, "watch", JS_WatchAgency); + TRI_AddMethodVocbase(rt, "endpoints", JS_EndpointsAgency); + TRI_AddMethodVocbase(rt, "prefix", JS_PrefixAgency); + TRI_AddMethodVocbase(rt, "version", JS_VersionAgency); + + v8g->AgencyTempl = v8::Persistent::New(isolate, rt); + TRI_AddGlobalFunctionVocbase(context, "ArangoAgency", ft->GetFunction()); +#endif // ............................................................................. // create global variables diff --git a/lib/V8/v8-globals.cpp b/lib/V8/v8-globals.cpp index 219de27ce9..b8c6c541aa 100644 --- a/lib/V8/v8-globals.cpp +++ b/lib/V8/v8-globals.cpp @@ -39,10 +39,10 @@ TRI_v8_global_s::TRI_v8_global_s (v8::Isolate* isolate) : JSBarriers(), JSCollections(), + AgencyTempl(), ErrorTempl(), GeneralCursorTempl(), ShapedJsonTempl(), - TransactionTempl(), VocbaseColTempl(), VocbaseTempl(), diff --git a/lib/V8/v8-globals.h b/lib/V8/v8-globals.h index b50d319549..3645f5b05a 100644 --- a/lib/V8/v8-globals.h +++ b/lib/V8/v8-globals.h @@ -209,6 +209,14 @@ typedef struct TRI_v8_global_s { // --SECTION-- JAVASCRIPT OBJECT TEMPLATES // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief agency template +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER + v8::Persistent AgencyTempl; +#endif + //////////////////////////////////////////////////////////////////////////////// /// @brief error template //////////////////////////////////////////////////////////////////////////////// @@ -227,12 +235,6 @@ typedef struct TRI_v8_global_s { v8::Persistent ShapedJsonTempl; -//////////////////////////////////////////////////////////////////////////////// -/// @brief transaction template -//////////////////////////////////////////////////////////////////////////////// - - v8::Persistent TransactionTempl; - //////////////////////////////////////////////////////////////////////////////// /// @brief TRI_vocbase_col_t template //////////////////////////////////////////////////////////////////////////////// From 620f4b1c47a497f12cea3f1453568b84ee2f3ec5 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 11 Dec 2013 18:37:15 +0100 Subject: [PATCH 20/22] added agency tests --- arangod/V8Server/v8-vocbase.cpp | 54 ++-- js/server/tests/agency.js | 451 ++++++++++++++++++++++++++++++++ 2 files changed, 486 insertions(+), 19 deletions(-) create mode 100644 js/server/tests/agency.js diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index c46b724643..1baf69aa4d 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -2223,17 +2223,32 @@ static v8::Handle JS_CasAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 3) { - TRI_V8_EXCEPTION_USAGE(scope, "set(, , )"); + TRI_V8_EXCEPTION_USAGE(scope, "set(, , , )"); } const std::string key = TRI_ObjectToString(argv[0]); const std::string oldValue = TRI_ObjectToString(argv[1]); const std::string newValue = TRI_ObjectToString(argv[2]); + + bool shouldThrow = false; + if (argv.Length() > 3) { + shouldThrow = TRI_ObjectToBoolean(argv[3]); + } AgencyComm comm; AgencyCommResult result = comm.casValue(key, oldValue, newValue); -// TODO - return scope.Close(v8::Undefined()); + + if (! result.successful()) { + if (! shouldThrow) { + return scope.Close(v8::False()); + } + + const std::string errorDetails = result.errorDetails(); + v8::Handle err = v8::String::New(errorDetails.c_str(), errorDetails.size()); + return scope.Close(v8::ThrowException(err)); + } + + return scope.Close(v8::True()); } #endif @@ -2401,32 +2416,33 @@ static v8::Handle JS_WatchAgency (v8::Arguments const& argv) { AgencyComm comm; AgencyCommResult result = comm.watchValue(key, waitIndex, timeout); - + + if (result._statusCode == 0) { + // watch timed out + return scope.Close(v8::False()); + } + if (! result.successful()) { const std::string errorDetails = result.errorDetails(); v8::Handle err = v8::String::New(errorDetails.c_str(), errorDetails.size()); return scope.Close(v8::ThrowException(err)); } - if (result._statusCode > 0) { - std::map out; - result.flattenJson(out, "", false); - std::map::const_iterator it = out.begin(); + std::map out; + result.flattenJson(out, "", false); + std::map::const_iterator it = out.begin(); - v8::Handle l = v8::Array::New(); + v8::Handle l = v8::Object::New(); - while (it != out.end()) { - const std::string key = (*it).first; - const std::string value = (*it).second; + while (it != out.end()) { + const std::string key = (*it).first; + const std::string value = (*it).second; - l->Set(v8::String::New(key.c_str(), key.size()), v8::String::New(value.c_str(), value.size())); - ++it; - } - - return scope.Close(l); + l->Set(v8::String::New(key.c_str(), key.size()), v8::String::New(value.c_str(), value.size())); + ++it; } - - return scope.Close(v8::False()); + + return scope.Close(l); } #endif diff --git a/js/server/tests/agency.js b/js/server/tests/agency.js new file mode 100644 index 0000000000..346b163232 --- /dev/null +++ b/js/server/tests/agency.js @@ -0,0 +1,451 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief test the agency communication layer +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2010-2012 triagens GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Jan Steemann +/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +var jsunity = require("jsunity"); +var internal = require("internal"); + +// ----------------------------------------------------------------------------- +// --SECTION-- agency +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite: agency +//////////////////////////////////////////////////////////////////////////////// + +function AgencySuite () { + var agency; + + return { + + setUp : function () { + agency = new ArangoAgency(); + + try { + agency.remove("UnitTestsAgency", true); + } + catch (err) { + // dir may not exist. this is not a problem + } + + agency.createDirectory("UnitTestsAgency"); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test version +//////////////////////////////////////////////////////////////////////////////// + + testVersion : function () { + assertMatch(/^etcd/, agency.version()); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test watch +//////////////////////////////////////////////////////////////////////////////// + + testWatchTimeout : function () { + assertTrue(agency.set("UnitTestsAgency/foo", "bar")); + + var wait = 3; + var start = require("internal").time(); + assertFalse(agency.watch("UnitTestsAgency/foo", 0, wait)); + var end = require("internal").time(); + assertEqual(wait, Math.round(end - start)); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test watch +//////////////////////////////////////////////////////////////////////////////// + + testWatchChange : function () { + assertTrue(agency.set("UnitTestsAgency/foo", "bar")); + + var wait = 1; + var start = require("internal").time(); + assertFalse(agency.watch("UnitTestsAgency/foo", 0, wait)); + var end = require("internal").time(); + assertEqual(wait, Math.round(end - start)); + + assertTrue(agency.set("UnitTestsAgency/foo", "baz")); + assertTrue(agency.set("UnitTestsAgency/foo", "bart")); + start = require("internal").time(); + var result = agency.watch("UnitTestsAgency/foo", 1, wait); + end = require("internal").time(); + + assertEqual(0, Math.round(end - start)); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test cas +//////////////////////////////////////////////////////////////////////////////// + + testCas : function () { + assertTrue(agency.set("UnitTestsAgency/foo", "bar")); + + assertTrue(agency.cas("UnitTestsAgency/foo", "bar", "baz")); + assertTrue(agency.cas("UnitTestsAgency/foo", "baz", "bart")); + assertFalse(agency.cas("UnitTestsAgency/foo", "foo", "bar")); + + try { + agency.cas("UnitTestsAgency/foo", "foo", "bar", true); + fail(); + } + catch (err) { + } + + assertTrue(agency.cas("UnitTestsAgency/foo", "bart", "baz", true)); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test createDir +//////////////////////////////////////////////////////////////////////////////// + + testCreateDir : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + + try { + // re-create an existing dir + agency.createDir("UnitTestsAgency/someDir"); + fail(); + } + catch (err) { + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test createDir / remove +//////////////////////////////////////////////////////////////////////////////// + + testCreateRemoveDir : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + + assertTrue(agency.remove("UnitTestsAgency/someDir", true)); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test removeDir +//////////////////////////////////////////////////////////////////////////////// + + testRemoveDirNonRecursive : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + + try { + assertTrue(agency.remove("UnitTestsAgency/someDir", false)); + fail(); + } + catch (err) { + // not a file + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test removeDir +//////////////////////////////////////////////////////////////////////////////// + + testRemoveDirRecursive1 : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/1")); + assertTrue(agency.createDirectory("UnitTestsAgency/1/1")); + assertTrue(agency.createDirectory("UnitTestsAgency/1/2")); + assertTrue(agency.createDirectory("UnitTestsAgency/1/2/1")); + assertTrue(agency.createDirectory("UnitTestsAgency/1/2/2")); + assertTrue(agency.createDirectory("UnitTestsAgency/2")); + assertTrue(agency.createDirectory("UnitTestsAgency/2/1")); + agency.set("UnitTestsAgency/1/1/foo", "bar"); + agency.set("UnitTestsAgency/2/1/foo", "baz"); + + assertTrue(agency.remove("UnitTestsAgency/1/1", true)); + assertTrue(agency.remove("UnitTestsAgency/1/2", true)); + assertTrue(agency.remove("UnitTestsAgency/1", true)); + assertTrue(agency.remove("UnitTestsAgency/2", true)); + + var values = agency.get("UnitTestsAgency", true); + assertEqual({ }, values); // empty + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test removeDir +//////////////////////////////////////////////////////////////////////////////// + + testRemoveDirRecursive2 : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/1")); + assertTrue(agency.createDirectory("UnitTestsAgency/1/1")); + assertTrue(agency.createDirectory("UnitTestsAgency/1/2")); + assertTrue(agency.createDirectory("UnitTestsAgency/1/2/1")); + assertTrue(agency.createDirectory("UnitTestsAgency/1/2/2")); + assertTrue(agency.createDirectory("UnitTestsAgency/2")); + assertTrue(agency.createDirectory("UnitTestsAgency/2/1")); + agency.set("UnitTestsAgency/1/1/foo", "bar"); + agency.set("UnitTestsAgency/2/1/foo", "baz"); + + assertTrue(agency.remove("UnitTestsAgency/1", true)); + + try { + agency.get("UnitTestsAgency/1", true); + fail(); + } + catch (err) { + // key not found + } + + var values = agency.get("UnitTestsAgency/2", true); + assertEqual({ "UnitTestsAgency/2/1/foo" : "baz" }, values); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test removeDir +//////////////////////////////////////////////////////////////////////////////// + + testRemoveDirRecursive3 : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/1")); + assertTrue(agency.createDirectory("UnitTestsAgency/1/1")); + assertTrue(agency.createDirectory("UnitTestsAgency/1/2")); + assertTrue(agency.createDirectory("UnitTestsAgency/1/2/1")); + assertTrue(agency.createDirectory("UnitTestsAgency/1/2/2")); + assertTrue(agency.createDirectory("UnitTestsAgency/2")); + assertTrue(agency.createDirectory("UnitTestsAgency/2/1")); + agency.set("UnitTestsAgency/1/1/foo", "bar"); + agency.set("UnitTestsAgency/2/1/foo", "baz"); + + assertTrue(agency.remove("UnitTestsAgency", true)); + + try { + agency.get("UnitTestsAgency", true); + fail(); + } + catch (err) { + // key not found + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test removeDir +//////////////////////////////////////////////////////////////////////////////// + + testRemoveDirNonExisting : function () { + try { + assertTrue(agency.remove("UnitTestsAgency/someDir", true)); + fail(); + } + catch (err) { + // key not found + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test remove +//////////////////////////////////////////////////////////////////////////////// + + testRemoveKeys : function () { + var i; + + for (i = 0; i < 100; ++i) { + assertTrue(agency.set("UnitTestsAgency/" + i, "value" + i)); + } + + for (i = 10; i < 90; ++i) { + assertTrue(agency.remove("UnitTestsAgency/" + i)); + } + + var values; + for (i = 0; i < 100; ++i) { + if (i >= 10 && i < 90) { + try { + values = agency.get("UnitTestsAgency/" + i); + fail(); + } + catch (err) { + } + } + else { + values = agency.get("UnitTestsAgency/" + i); + assertTrue(values.hasOwnProperty("UnitTestsAgency/" + i)); + assertEqual(values["UnitTestsAgency/" + i], "value" + i); + } + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test set / get +//////////////////////////////////////////////////////////////////////////////// + + testGet : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + + agency.set("UnitTestsAgency/someDir/foo", "bar"); + + var values = agency.get("UnitTestsAgency/someDir/foo"); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/foo")); + assertEqual(values["UnitTestsAgency/someDir/foo"], "bar"); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test set / get directory +//////////////////////////////////////////////////////////////////////////////// + + testGetDirectory : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + assertTrue(agency.set("UnitTestsAgency/someDir/foo", "bar")); + + var values = agency.get("UnitTestsAgency", false); + assertEqual({ }, values); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test set / get multi +//////////////////////////////////////////////////////////////////////////////// + + testGetMulti : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + assertTrue(agency.set("UnitTestsAgency/someDir/foo", "bar")); + assertTrue(agency.set("UnitTestsAgency/someDir/baz", "bart")); + + var values = agency.get("UnitTestsAgency", true); + assertEqual({ "UnitTestsAgency/someDir/baz" : "bart", "UnitTestsAgency/someDir/foo" : "bar" }, values); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test set / get +//////////////////////////////////////////////////////////////////////////////// + + testGetUpdated : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + + agency.set("UnitTestsAgency/someDir/foo", "bar"); + + var values = agency.get("UnitTestsAgency/someDir/foo"); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/foo")); + assertEqual(values["UnitTestsAgency/someDir/foo"], "bar"); + + agency.set("UnitTestsAgency/someDir/foo", "baz"); + values = agency.get("UnitTestsAgency/someDir/foo"); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/foo")); + assertEqual(values["UnitTestsAgency/someDir/foo"], "baz"); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test set / get +//////////////////////////////////////////////////////////////////////////////// + + testGetDeleted : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + + agency.set("UnitTestsAgency/someDir/foo", "bar"); + + var values = agency.get("UnitTestsAgency/someDir/foo"); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/foo")); + assertEqual(values["UnitTestsAgency/someDir/foo"], "bar"); + + agency.remove("UnitTestsAgency/someDir/foo"); + + try { + values = agency.get("UnitTestsAgency/someDir/foo"); + fail(); + } + catch (err) { + // key not found + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test get +//////////////////////////////////////////////////////////////////////////////// + + testGetNonExisting1 : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + + try { + agency.get("UnitTestsAgency/someDir/foo"); + fail(); + } + catch (err) { + // key not found + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test get +//////////////////////////////////////////////////////////////////////////////// + + testGetNonExisting2 : function () { + try { + agency.get("UnitTestsAgency/someOtherDir"); + fail(); + } + catch (err) { + // key not found + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test set / get +//////////////////////////////////////////////////////////////////////////////// + + testGetUrlEncodedValue : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + + var value = "bar=BAT;foo=%47abc;degf=2343%20hihi aha\nabc"; + agency.set("UnitTestsAgency/someDir/foobar", value); + + var values = agency.get("UnitTestsAgency/someDir/foobar"); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/foobar")); + assertEqual(values["UnitTestsAgency/someDir/foobar"], value); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test set / get +//////////////////////////////////////////////////////////////////////////////// + + testGetUrlEncodedKey : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + + var key = "foo bar baz / hihi"; + agency.set("UnitTestsAgency/someDir/" + encodeURIComponent(key), "something"); + + var values = agency.get("UnitTestsAgency/someDir/" + encodeURIComponent(key)); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/" + key)); + assertEqual(values["UnitTestsAgency/someDir/" + key], "something"); + } + + }; +} + +// ----------------------------------------------------------------------------- +// --SECTION-- main +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the test suites +//////////////////////////////////////////////////////////////////////////////// + +jsunity.run(AgencySuite); + +return jsunity.done(); + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// End: + From 025a11dbeee0df1aee45dd5698cbe2b98b930c87 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 11 Dec 2013 18:37:26 +0100 Subject: [PATCH 21/22] fixed comments --- js/server/tests/compaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/server/tests/compaction.js b/js/server/tests/compaction.js index f2fd4accac..ebe0f50703 100644 --- a/js/server/tests/compaction.js +++ b/js/server/tests/compaction.js @@ -29,7 +29,7 @@ var jsunity = require("jsunity"); var internal = require("internal"); // ----------------------------------------------------------------------------- -// --SECTION-- collection methods +// --SECTION-- compaction // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// From 32b26f539249624ad8c5489f6a65c2b1fcdea365 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 11 Dec 2013 18:51:50 +0100 Subject: [PATCH 22/22] added agency test --- UnitTests/Makefile.unittests | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index 54c796f7bc..bdfdee4396 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -119,6 +119,10 @@ SERVER_OPT := \ --server.threads 4 \ $(SERVER_START) +if ENABLE_CLUSTER +SERVER_OPT += --cluster.agency-endpoint tcp://127.0.0.1:4001 --cluster.agency-prefix UnitTests --cluster.my-id arangod --cluster.my-address tcp://127.0.0.1:8529 +endif + CLIENT_OPT := \ --configuration none \ --javascript.startup-directory @top_srcdir@/js \ @@ -404,6 +408,11 @@ SHELL_SERVER_ONLY = \ @top_srcdir@/js/server/tests/shell-skiplist-rm-performance.js \ @top_srcdir@/js/server/tests/shell-skiplist-correctness.js +if ENABLE_CLUSTER +SHELL_SERVER_ONLY += \ + @top_srcdir@/js/server/tests/agency.js +endif + SHELL_SERVER = $(SHELL_COMMON) $(SHELL_SERVER_ONLY) .PHONY: unittests-shell-server