diff --git a/Documentation/Examples/api-index-create-new-fulltext b/Documentation/Examples/api-index-create-new-fulltext new file mode 100644 index 0000000000..1774bb913c --- /dev/null +++ b/Documentation/Examples/api-index-create-new-fulltext @@ -0,0 +1,17 @@ +> curl --data @- -X POST --dump - http://localhost:8529/_api/index?collection=109061392 +{ "type" : "fulltext", "indexSubstrings" : false, "fields" : [ "text" ] } + +HTTP/1.1 201 Created +content-type: application/json + +{ + "code": 201, + "fields": [ + "text" + ], + "id": "109061392/1748352392", + "type": "fulltext", + "isNewlyCreated": true, + "indexSubstrings": false, + "error": false +} diff --git a/UnitTests/Makefile.files b/UnitTests/Makefile.files index 0bb84d192b..22b6a781ab 100755 --- a/UnitTests/Makefile.files +++ b/UnitTests/Makefile.files @@ -212,7 +212,8 @@ SHELL_COMMON = @top_srcdir@/js/common/tests/shell-document.js \ @top_srcdir@/js/common/tests/shell-index-geo.js \ @top_srcdir@/js/common/tests/shell-cap-constraint.js \ @top_srcdir@/js/common/tests/shell-unique-constraint.js \ - @top_srcdir@/js/common/tests/shell-hash-index.js + @top_srcdir@/js/common/tests/shell-hash-index.js \ + @top_srcdir@/js/common/tests/shell-fulltext.js SHELL_SERVER = $(SHELL_COMMON) \ @top_srcdir@/js/common/tests/shell-graph.js \ diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 74464430e6..31315d53d5 100755 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -4079,12 +4079,16 @@ static v8::Handle JS_LookupSkiplistVocbaseCol (v8::Arguments const& a //////////////////////////////////////////////////////////////////////////////// /// @brief ensures that a fulltext index exists /// -/// @FUN{ensureFulltextIndex(@FA{field}} +/// @FUN{ensureFulltextIndex(@FA{field}, @FA{indexSubstrings}} /// /// Creates a fulltext index on all documents on attribute @FA{field}. /// All documents, which do not have the attribute or with a non-textual value /// inside the attribute are ignored. /// +/// IF @FA{indexSubstrings} is set to @LIT{true}, then substrings are also +/// indexed. This allows search substring search queries, but will make the +/// index consume more memory. +/// /// In case that the index was successfully created, the index identifier /// is returned. /// diff --git a/js/actions/system/api-index.js b/js/actions/system/api-index.js index db0c21a76d..98ba92c2f7 100644 --- a/js/actions/system/api-index.js +++ b/js/actions/system/api-index.js @@ -55,7 +55,7 @@ var API = "_api/index"; /// /// @RESTHEADER{GET /_api/index,reads all indexes of a collection} /// -/// @REST{GET /_api/index?collection=@FA{collection-identifier}} +/// @REST{GET /_api/index?collection=@FA{collection-name}} /// /// Returns an object with an attribute @LIT{indexes} containing a list of all /// index descriptions for the given collection. The same information is also @@ -104,7 +104,7 @@ function GET_api_indexes (req, res) { /// The result is an objects describing the index. It has at least the following /// attributes: /// -/// - @LIT{id}: The identifier of the collection. +/// - @LIT{id}: The identifier of the index. /// /// - @LIT{type}: The type of the collection. /// @@ -118,7 +118,7 @@ function GET_api_indexes (req, res) { function GET_api_index (req, res) { // ............................................................................. - // /_api/index?collection= + // /_api/index?collection= // ............................................................................. if (req.suffix.length === 0) { @@ -126,7 +126,7 @@ function GET_api_index (req, res) { } // ............................................................................. - // /_api/index// + // /_api/index// // ............................................................................. else if (req.suffix.length === 2) { @@ -159,21 +159,20 @@ function GET_api_index (req, res) { /// /// @RESTHEADER{POST /_api/index,creates a cap constraint} /// -/// @REST{POST /_api/index?collection=@FA{collection-identifier}} +/// @REST{POST /_api/index?collection=@FA{collection-name}} /// -/// Creates a cap constraint for the collection @FA{collection-identifier}, if +/// Creates a cap constraint for the collection @FA{collection-name}, if /// it does not already exist. Expects an object containing the index details. /// /// - @LIT{type}: must be equal to @LIT{"cap"}. /// /// - @LIT{size}: The maximal size of documents. /// -/// If the index does not already exists and could be created, then a @LIT{HTTP +/// If the index does not already exist and could be created, then a @LIT{HTTP /// 201} is returned. If the index already exists, then a @LIT{HTTP 200} is /// returned. /// -/// If the @FA{collection-identifier} is unknown, then a @LIT{HTTP 404} is -/// returned. It is possible to specify a name instead of an identifier. +/// If the @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned. /// /// @EXAMPLES /// @@ -204,9 +203,9 @@ function POST_api_index_cap (req, res, collection, body) { /// /// @RESTHEADER{GET /_api/index,creates a geo-spatial index} /// -/// @REST{POST /_api/index?collection=@FA{collection-identifier}} +/// @REST{POST /_api/index?collection=@FA{collection-name}} /// -/// Creates a geo-spatial index in the collection @FA{collection-identifier}, if +/// Creates a geo-spatial index in the collection @FA{collection-name}, if /// it does not already exist. Expects an object containing the index details. /// /// - @LIT{type}: must be equal to @LIT{"geo"}. @@ -240,12 +239,11 @@ function POST_api_index_cap (req, res, collection, body) { /// @FA{ignoreNull} is true, then documents with a null in @FA{location} or at /// least one null in @FA{latitude} or @FA{longitude} are ignored. /// -/// If the index does not already exists and could be created, then a @LIT{HTTP +/// If the index does not already exist and could be created, then a @LIT{HTTP /// 201} is returned. If the index already exists, then a @LIT{HTTP 200} is /// returned. /// -/// If the @FA{collection-identifier} is unknown, then a @LIT{HTTP 404} is -/// returned. It is possible to specify a name instead of an identifier. +/// If the @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned. /// /// @EXAMPLES /// @@ -336,9 +334,9 @@ function POST_api_index_geo (req, res, collection, body) { /// /// @RESTHEADER{POST /_api/index,creates a hash index} /// -/// @REST{POST /_api/index?collection=@FA{collection-identifier}} +/// @REST{POST /_api/index?collection=@FA{collection-name}} /// -/// Creates a hash index for the collection @FA{collection-identifier}, if it +/// Creates a hash index for the collection @FA{collection-name}, if it /// does not already exist. The call expects an object containing the index /// details. /// @@ -348,12 +346,11 @@ function POST_api_index_geo (req, res, collection, body) { /// /// - @LIT{unique}: If @LIT{true}, then create a unique index. /// -/// If the index does not already exists and could be created, then a @LIT{HTTP +/// If the index does not already exist and could be created, then a @LIT{HTTP /// 201} is returned. If the index already exists, then a @LIT{HTTP 200} is /// returned. /// -/// If the @FA{collection-identifier} is unknown, then a @LIT{HTTP 404} is -/// returned. It is possible to specify a name instead of an identifier. +/// If the @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned. /// /// If the collection already contains documents and you try to create a unique /// hash index in such a way that there are documents violating the uniqueness, @@ -400,9 +397,9 @@ function POST_api_index_hash (req, res, collection, body) { /// /// @RESTHEADER{POST /_api/index,creates a hash index} /// -/// @REST{POST /_api/index?collection=@FA{collection-identifier}} +/// @REST{POST /_api/index?collection=@FA{collection-name}} /// -/// Creates a skip-list index for the collection @FA{collection-identifier}, if +/// Creates a skip-list index for the collection @FA{collection-name}, if /// it does not already exist. The call expects an object containing the index /// details. /// @@ -412,12 +409,11 @@ function POST_api_index_hash (req, res, collection, body) { /// /// - @LIT{unique}: If @LIT{true}, then create a unique index. /// -/// If the index does not already exists and could be created, then a @LIT{HTTP +/// If the index does not already exist and could be created, then a @LIT{HTTP /// 201} is returned. If the index already exists, then a @LIT{HTTP 200} is /// returned. /// -/// If the @FA{collection-identifier} is unknown, then a @LIT{HTTP 404} is -/// returned. It is possible to specify a name instead of an identifier. +/// If the @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned. /// /// If the collection already contains documents and you try to create a unique /// skip-list index in such a way that there are documents violating the @@ -455,14 +451,70 @@ function POST_api_index_skiplist (req, res, collection, body) { } } +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates a fulltext index +/// +/// @RESTHEADER{POST /_api/index,creates a fulltext index} +/// +/// @REST{POST /_api/index?collection=@FA{collection-name}} +/// +/// Creates a fulltext index for the collection @FA{collection-name}, if +/// it does not already exist. The call expects an object containing the index +/// details. +/// +/// - @LIT{type}: must be equal to @LIT{"fulltext"}. +/// +/// - @LIT{fields}: A list of attribute names. Currently, the list is limited +/// to exactly one attribute. +/// +/// - @LIT{indexSubstrings}: If @LIT{true}, then substrings are also indexes. +/// This will enable substring searching via the index but will make the index +/// consume more memory. +/// +/// If the index does not already exist and could be created, then a @LIT{HTTP +/// 201} is returned. If the index already exists, then a @LIT{HTTP 200} is +/// returned. +/// +/// If the @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned. +/// +/// @EXAMPLES +/// +/// Creating a fulltext index: +/// +/// @verbinclude api-index-create-new-fulltext +//////////////////////////////////////////////////////////////////////////////// + +function POST_api_index_fulltext (req, res, collection, body) { + var fields = body.fields; + + if (! (fields instanceof Array)) { + actions.resultBad(req, res, actions.ERROR_HTTP_BAD_PARAMETER, + "fields must be a list of attribute names: " + fields); + } + + if (fields.length != 1 || typeof fields[0] !== 'string') { + actions.resultBad(req, res, actions.ERROR_HTTP_BAD_PARAMETER, + "fields must contain exactly one attribute name"); + } + + var index = collection.ensureFulltextIndex.call(collection, fields, body.indexSubstrings || false); + + if (index.isNewlyCreated) { + actions.resultOk(req, res, actions.HTTP_CREATED, index); + } + else { + actions.resultOk(req, res, actions.HTTP_OK, index); + } +} + //////////////////////////////////////////////////////////////////////////////// /// @brief creates a bitarray /// /// @RESTHEADER{POST /_api/index,creates a bitarray index} /// -/// @REST{POST /_api/index?collection=@FA{collection-identifier}} +/// @REST{POST /_api/index?collection=@FA{collection-name}} /// -/// Creates a bitarray index for the collection @FA{collection-identifier}, if +/// Creates a bitarray index for the collection @FA{collection-name}, if /// it does not already exist. The call expects an object containing the index /// details. /// @@ -472,12 +524,11 @@ function POST_api_index_skiplist (req, res, collection, body) { /// /// - @LIT{unique}: Must always be set to @LIT{false}. /// -/// If the index does not already exists and could be created, then a @LIT{HTTP +/// If the index does not already exist and could be created, then a @LIT{HTTP /// 201} is returned. If the index already exists, then a @LIT{HTTP 200} is /// returned. /// -/// If the @FA{collection-identifier} is unknown, then a @LIT{HTTP 404} is -/// returned. It is possible to specify a name instead of an identifier. +/// If the @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned. /// /// @EXAMPLES /// @@ -516,9 +567,9 @@ function POST_api_index_bitarray (req, res, collection, body) { /// /// @RESTHEADER{POST /_api/index,creates an index} /// -/// @REST{POST /_api/index?collection=@FA{collection-identifier}} +/// @REST{POST /_api/index?collection=@FA{collection-name}} /// -/// Creates a new index in the collection @FA{collection-identifier}. Expects +/// Creates a new index in the collection @FA{collection-name}. Expects /// an object containing the index details. /// /// See @ref IndexCapHttp, @ref IndexGeoHttp, @ref IndexHashHttp, and @@ -526,16 +577,15 @@ function POST_api_index_bitarray (req, res, collection, body) { /// be created. To change this, use the @LIT{unique} attribute in the index details /// and set its value to @LIT{true}. /// -/// If the index does not already exists and could be created, then a @LIT{HTTP +/// If the index does not already exist and could be created, then a @LIT{HTTP /// 201} is returned. If the index already exists, then a @LIT{HTTP 200} is /// returned. /// -/// If the @FA{collection-identifier} is unknown, then a @LIT{HTTP 404} is -/// returned. It is possible to specify a name instead of an identifier. +/// If the @FA{collection-name} is unknown, then a @LIT{HTTP 404} is returned. /// /// @EXAMPLES /// -/// Creating an unique constraint: +/// Creating a unique constraint: /// /// @verbinclude api-index-create-new-unique-constraint /// @@ -550,13 +600,17 @@ function POST_api_index_bitarray (req, res, collection, body) { /// Creating a unique skip-list: /// /// @verbinclude api-index-create-new-unique-skiplist +/// +/// Creating a fulltext index: +/// +/// @verbinclude api-index-create-new-fulltext //////////////////////////////////////////////////////////////////////////////// function POST_api_index (req, res) { if (req.suffix.length !== 0) { actions.resultBad(req, res, actions.ERROR_HTTP_BAD_PARAMETER, - "expect POST /" + API + "?collection="); + "expect POST /" + API + "?collection="); return; } @@ -586,6 +640,9 @@ function POST_api_index (req, res) { else if (body.type === "skiplist") { POST_api_index_skiplist(req, res, collection, body); } + else if (body.type === "fulltext") { + POST_api_index_fulltext(req, res, collection, body); + } else if (body.type === "bitarray") { POST_api_index_bitarray(req, res, collection, body); } diff --git a/js/client/client.js b/js/client/client.js index 57baecef2a..2e66ef0e2e 100755 --- a/js/client/client.js +++ b/js/client/client.js @@ -1265,8 +1265,7 @@ function ArangoCollection (database, data) { body = { type : "bitarray", unique : false, fields : fields }; - var requestResult = this._database._connection.POST(this._indexurl(), - JSON.stringify(body)); + var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body)); client.checkRequestResult(requestResult); @@ -1283,8 +1282,7 @@ function ArangoCollection (database, data) { body = { type : "cap", size : size }; - var requestResult = this._database._connection.POST(this._indexurl(), - JSON.stringify(body)); + var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body)); client.checkRequestResult(requestResult); @@ -1306,8 +1304,7 @@ function ArangoCollection (database, data) { body = { type : "skiplist", unique : true, fields : fields }; - var requestResult = this._database._connection.POST(this._indexurl(), - JSON.stringify(body)); + var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body)); client.checkRequestResult(requestResult); @@ -1329,8 +1326,24 @@ function ArangoCollection (database, data) { body = { type : "skiplist", unique : false, fields : fields }; - var requestResult = this._database._connection.POST(this._indexurl(), - JSON.stringify(body)); + var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body)); + + client.checkRequestResult(requestResult); + + return requestResult; + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds a fulltext index +//////////////////////////////////////////////////////////////////////////////// + + ArangoCollection.prototype.ensureFulltextIndex = function (attribute, indexSubstrings) { + var doIndexSubstrings = indexSubstrings || false; + var body; + + body = { type : "fulltext", indexSubstrings : doIndexSubstrings, fields : [ attribute ] }; + + var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body)); client.checkRequestResult(requestResult); @@ -1352,8 +1365,7 @@ function ArangoCollection (database, data) { body = { type : "hash", unique : true, fields : fields }; - var requestResult = this._database._connection.POST(this._indexurl(), - JSON.stringify(body)); + var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body)); client.checkRequestResult(requestResult); @@ -1375,8 +1387,7 @@ function ArangoCollection (database, data) { body = { type : "hash", unique : false, fields : fields }; - var requestResult = this._database._connection.POST(this._indexurl(), - JSON.stringify(body)); + var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body)); client.checkRequestResult(requestResult); @@ -1418,8 +1429,7 @@ function ArangoCollection (database, data) { }; } - var requestResult = this._database._connection.POST(this._indexurl(), - JSON.stringify(body)); + var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body)); client.checkRequestResult(requestResult); @@ -1472,8 +1482,7 @@ function ArangoCollection (database, data) { } } - var requestResult = this._database._connection.POST(this._indexurl(), - JSON.stringify(body)); + var requestResult = this._database._connection.POST(this._indexurl(), JSON.stringify(body)); client.checkRequestResult(requestResult); @@ -1560,8 +1569,7 @@ function ArangoCollection (database, data) { ArangoCollection.prototype.rename = function (name) { var body = { name : name }; - var requestResult = this._database._connection.PUT(this._baseurl("rename"), - JSON.stringify(body)); + var requestResult = this._database._connection.PUT(this._baseurl("rename"), JSON.stringify(body)); client.checkRequestResult(requestResult); @@ -1577,8 +1585,7 @@ function ArangoCollection (database, data) { //////////////////////////////////////////////////////////////////////////////// ArangoCollection.prototype.refresh = function () { - var requestResult = this._database._connection.GET( - this._database._collectionurl(this._id) + "?useId=true"); + var requestResult = this._database._connection.GET( this._database._collectionurl(this._id) + "?useId=true"); client.checkRequestResult(requestResult); diff --git a/js/common/tests/shell-fulltext.js b/js/common/tests/shell-fulltext.js new file mode 100644 index 0000000000..565f5fb609 --- /dev/null +++ b/js/common/tests/shell-fulltext.js @@ -0,0 +1,223 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief tests for fulltext indexes +/// +/// @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 2012, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +var jsunity = require("jsunity"); +var internal = require("internal"); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite +//////////////////////////////////////////////////////////////////////////////// + +function fulltextTestSuite () { + var cn = "UnitTestsFulltext"; + var c = null; + + return { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set up +//////////////////////////////////////////////////////////////////////////////// + + setUp : function () { + internal.db._drop(cn); + c = internal.db._create(cn); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief tear down +//////////////////////////////////////////////////////////////////////////////// + + tearDown : function () { + internal.db._drop(cn); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks the index creation +//////////////////////////////////////////////////////////////////////////////// + + testCreateIndexSimple : function () { + var idx = c.ensureFulltextIndex("text"); + + var indexes = c.getIndexes(); + for (var i = 0; i < indexes.length; ++i) { + var index = indexes[i]; + if (index.type != "fulltext") { + continue; + } + + assertEqual(idx.id, index.id); + assertEqual("fulltext", index.type); + assertEqual([ "text" ], index.fields); + assertEqual(false, index.indexSubstrings); + return; + } + + fail(); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks the index creation +//////////////////////////////////////////////////////////////////////////////// + + testCreateIndexExisting : function () { + var idx = c.ensureFulltextIndex("textattr", false); + + var indexes = c.getIndexes(); + for (var i = 0; i < indexes.length; ++i) { + var index = indexes[i]; + if (index.type != "fulltext") { + continue; + } + + assertEqual(idx.id, index.id); + assertEqual("fulltext", index.type); + assertEqual([ "textattr" ], index.fields); + assertEqual(false, index.indexSubstrings); + + assertEqual(idx.id, c.ensureFulltextIndex("textattr", false).id); + return; + } + + fail(); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks the index creation +//////////////////////////////////////////////////////////////////////////////// + + testCreateIndexSubstrings : function () { + var idx = c.ensureFulltextIndex("iam-an-indexed-ATTRIBUTE", true); + + var indexes = c.getIndexes(); + for (var i = 0; i < indexes.length; ++i) { + var index = indexes[i]; + if (index.type != "fulltext") { + continue; + } + + assertEqual(idx.id, index.id); + assertEqual("fulltext", index.type); + assertEqual([ "iam-an-indexed-ATTRIBUTE" ], index.fields); + assertEqual(true, index.indexSubstrings); + return; + } + + fail(); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks the index creation +//////////////////////////////////////////////////////////////////////////////// + + testCreateIndexSubstringsExsiting : function () { + var idx = c.ensureFulltextIndex("iam-an-indexed-ATTRIBUTE", true); + + var indexes = c.getIndexes(); + for (var i = 0; i < indexes.length; ++i) { + var index = indexes[i]; + if (index.type != "fulltext") { + continue; + } + + assertEqual(idx.id, index.id); + assertEqual("fulltext", index.type); + assertEqual([ "iam-an-indexed-ATTRIBUTE" ], index.fields); + assertEqual(true, index.indexSubstrings); + + assertEqual(idx.id, c.ensureFulltextIndex("iam-an-indexed-ATTRIBUTE", true).id); + return; + } + + fail(); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks the index creation +//////////////////////////////////////////////////////////////////////////////// + + testCreateMultipleIndexes : function () { + var idx1 = c.ensureFulltextIndex("attr1", false); + var idx2 = c.ensureFulltextIndex("attr1", true); + var idx3 = c.ensureFulltextIndex("attr2", true); + + var indexes = c.getIndexes(); + for (var i = 0; i < indexes.length; ++i) { + var index = indexes[i]; + if (index.type != "fulltext") { + continue; + } + + if (index.id == idx1.id) { + assertEqual("fulltext", index.type); + assertEqual([ "attr1" ], index.fields); + assertEqual(false, index.indexSubstrings); + } + else if (index.id == idx2.id) { + assertEqual("fulltext", index.type); + assertEqual([ "attr1" ], index.fields); + assertEqual(true, index.indexSubstrings); + } + else if (index.id == idx3.id) { + assertEqual("fulltext", index.type); + assertEqual([ "attr2" ], index.fields); + assertEqual(true, index.indexSubstrings); + } + else { + fail(); + } + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks dropping indexes +//////////////////////////////////////////////////////////////////////////////// + + testDropIndexes : function () { + var idx1 = c.ensureFulltextIndex("attr1", false); + var idx2 = c.ensureFulltextIndex("attr1", true); + var idx3 = c.ensureFulltextIndex("attr2", true); + + assertTrue(c.dropIndex(idx1)); + assertTrue(c.dropIndex(idx2)); + assertTrue(c.dropIndex(idx3)); + }, + + }; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the test suite +//////////////////////////////////////////////////////////////////////////////// + +jsunity.run(fulltextTestSuite); + +return jsunity.done(); + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// End: