From 399c1ce2f05e392e0bd0437300caddb58eeacb2c Mon Sep 17 00:00:00 2001 From: Vasiliy Date: Tue, 20 Mar 2018 16:40:35 +0300 Subject: [PATCH 1/2] manually-merge: add more tests, backport some miscellaneous fixes --- 3rdParty/CMakeLists.txt | 1 + 3rdParty/iresearch/core/CMakeLists.txt | 4 +- .../core/analysis/text_token_stream.cpp | 76 ++-- 3rdParty/iresearch/core/utils/file_utils.cpp | 24 +- .../Rest/Views/get_api_view_properties.md | 2 +- tests/CMakeLists.txt | 2 + tests/IResearch/IResearchIndex-test.cpp | 32 +- tests/IResearch/IResearchLinkMeta-test.cpp | 8 +- tests/Utils/CollectionNameResolver-test.cpp | 263 +++++++++++++ tests/VocBase/vocbase-test.cpp | 368 ++++++++++++++++++ 10 files changed, 721 insertions(+), 59 deletions(-) create mode 100644 tests/Utils/CollectionNameResolver-test.cpp create mode 100644 tests/VocBase/vocbase-test.cpp diff --git a/3rdParty/CMakeLists.txt b/3rdParty/CMakeLists.txt index 3db6d1bacf..fa6fe2568a 100644 --- a/3rdParty/CMakeLists.txt +++ b/3rdParty/CMakeLists.txt @@ -153,6 +153,7 @@ if (USE_IRESEARCH) set(IRESEARCH_EXCLUDE_STATIC_THIRD_PARTY_LIBS TRUE) # disable linking in of 3rd party libraries automatically find_package(IResearch REQUIRED) # set IRESEARCH_BUILD_DIR + set(CMAKE_MACOSX_RPATH ON) # suppress cmake warning (use same value as cmake default) set(CMAKE_MODULE_PATH_ORIGINAL ${CMAKE_MODULE_PATH}) # remember CMAKE_MODULE_PATH list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" # cmake overrides (must be first) diff --git a/3rdParty/iresearch/core/CMakeLists.txt b/3rdParty/iresearch/core/CMakeLists.txt index 2a1f876727..bacb86c4ad 100644 --- a/3rdParty/iresearch/core/CMakeLists.txt +++ b/3rdParty/iresearch/core/CMakeLists.txt @@ -289,7 +289,7 @@ if(MSVC) MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/iql/parser.yy DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/iql/parser.yy COMMAND ${CMAKE_COMMAND} -E make_directory iql - COMMAND ${CMAKE_COMMAND} -E compare_files ${CMAKE_CURRENT_SOURCE_DIR}/iql/parser.yy iql/parser.yy || bison --graph --report=all -o iql/parser.cc ${CMAKE_CURRENT_SOURCE_DIR}/iql/parser.yy + COMMAND ${CMAKE_COMMAND} -E compare_files ${CMAKE_CURRENT_SOURCE_DIR}/iql/parser.yy iql/parser.yy || bison --graph --report=all -Wnone -o iql/parser.cc ${CMAKE_CURRENT_SOURCE_DIR}/iql/parser.yy COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/iql/parser.yy iql/parser.yy ) else() @@ -298,7 +298,7 @@ else() MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/iql/parser.yy DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/iql/parser.yy COMMAND ${CMAKE_COMMAND} -E make_directory iql - COMMAND bison --graph --report=all -o iql/parser.cc ${CMAKE_CURRENT_SOURCE_DIR}/iql/parser.yy + COMMAND bison --graph --report=all -Wnone -o iql/parser.cc ${CMAKE_CURRENT_SOURCE_DIR}/iql/parser.yy ) endif() diff --git a/3rdParty/iresearch/core/analysis/text_token_stream.cpp b/3rdParty/iresearch/core/analysis/text_token_stream.cpp index 2e34ae5b30..bef48e4843 100644 --- a/3rdParty/iresearch/core/analysis/text_token_stream.cpp +++ b/3rdParty/iresearch/core/analysis/text_token_stream.cpp @@ -256,18 +256,25 @@ irs::analysis::analyzer::ptr construct( } } - // interpret the cache_key as a locale name - std::string locale_name(cache_key.c_str(), cache_key.size()); - auto locale = irs::locale_utils::locale(locale_name); - ignored_words_t buf; + try { + // interpret the cache_key as a locale name + std::string locale_name(cache_key.c_str(), cache_key.size()); + auto locale = irs::locale_utils::locale(locale_name); + ignored_words_t buf; - if (!get_ignored_words(buf, locale)) { - IR_FRMT_WARN("Failed to retrieve 'ignored_words' while constructing text_token_stream with cache key: %s", cache_key.c_str()); + if (!get_ignored_words(buf, locale)) { + IR_FRMT_WARN("Failed to retrieve 'ignored_words' while constructing text_token_stream with cache key: %s", cache_key.c_str()); - return nullptr; + return nullptr; + } + + return construct(cache_key, locale, std::move(buf)); + } catch (...) { + IR_FRMT_ERROR("Caught error while constructing text_token_stream cache key: %s", cache_key.c_str()); + IR_LOG_EXCEPTION(); } - return construct(cache_key, locale, std::move(buf)); + return nullptr; } //////////////////////////////////////////////////////////////////////////////// @@ -409,34 +416,41 @@ irs::analysis::analyzer::ptr make_json(const irs::string_ref& args) { return nullptr; } - static const rapidjson::Value empty; - auto locale = irs::locale_utils::locale(json["locale"].GetString()); - auto& ignored_words = json.HasMember("ignored_words") ? json["ignored_words"] : empty; - auto& ignored_words_path = json.HasMember("ignored_words_path") ? json["ignored_words_path"] : empty; + try { + static const rapidjson::Value empty; + auto locale = irs::locale_utils::locale(json["locale"].GetString()); + auto& ignored_words = json.HasMember("ignored_words") ? json["ignored_words"] : empty; + auto& ignored_words_path = json.HasMember("ignored_words_path") ? json["ignored_words_path"] : empty; - if (!ignored_words.IsArray()) { - return ignored_words_path.IsString() - ? construct(args, locale, ignored_words_path.GetString()) - : construct(args, locale) - ; - } - - ignored_words_t buf; - - for (auto itr = ignored_words.Begin(), end = ignored_words.End(); itr != end; ++itr) { - if (!itr->IsString()) { - IR_FRMT_WARN("Non-string value in 'ignored_words' while constructing text_token_stream from jSON arguments: %s", args.c_str()); - - return nullptr; + if (!ignored_words.IsArray()) { + return ignored_words_path.IsString() + ? construct(args, locale, ignored_words_path.GetString()) + : construct(args, locale) + ; } - buf.emplace(itr->GetString()); + ignored_words_t buf; + + for (auto itr = ignored_words.Begin(), end = ignored_words.End(); itr != end; ++itr) { + if (!itr->IsString()) { + IR_FRMT_WARN("Non-string value in 'ignored_words' while constructing text_token_stream from jSON arguments: %s", args.c_str()); + + return nullptr; + } + + buf.emplace(itr->GetString()); + } + + return ignored_words_path.IsString() + ? construct(args, locale, ignored_words_path.GetString(), std::move(buf)) + : construct(args, locale, std::move(buf)) + ; + } catch (...) { + IR_FRMT_ERROR("Caught error while constructing text_token_stream from jSON arguments: %s", args.c_str()); + IR_LOG_EXCEPTION(); } - return ignored_words_path.IsString() - ? construct(args, locale, ignored_words_path.GetString(), std::move(buf)) - : construct(args, locale, std::move(buf)) - ; + return nullptr; } //////////////////////////////////////////////////////////////////////////////// diff --git a/3rdParty/iresearch/core/utils/file_utils.cpp b/3rdParty/iresearch/core/utils/file_utils.cpp index 548b67509e..c099bf4c5e 100644 --- a/3rdParty/iresearch/core/utils/file_utils.cpp +++ b/3rdParty/iresearch/core/utils/file_utils.cpp @@ -78,7 +78,7 @@ inline int path_stats(file_stat_t& info, const file_path_t path) { auto parts = irs::file_utils::path_parts(path); return file_stat( - parts.basename.null() ? std::wstring(parts.dirname).c_str() : path, + parts.basename.empty() ? std::wstring(parts.dirname).c_str() : path, &info ); #else @@ -517,7 +517,7 @@ bool exists(bool& result, const file_path_t file) NOEXCEPT { result = 0 == path_stats(info, file); - if (!result) { + if (!result && ENOENT != errno) { auto path = boost::locale::conv::utf_to_utf(file); IR_FRMT_ERROR("Failed to get stat, error %d path: %s", errno, path.c_str()); @@ -532,16 +532,16 @@ bool exists_directory(bool& result, const file_path_t name) NOEXCEPT { result = 0 == path_stats(info, name); - if (!result) { - auto path = boost::locale::conv::utf_to_utf(name); - - IR_FRMT_ERROR("Failed to get stat, error %d path: %s", errno, path.c_str()); - } else { + if (result) { #ifdef _WIN32 result = (info.st_mode & _S_IFDIR) > 0; #else result = (info.st_mode & S_IFDIR) > 0; #endif + } else if (ENOENT != errno) { + auto path = boost::locale::conv::utf_to_utf(name); + + IR_FRMT_ERROR("Failed to get stat, error %d path: %s", errno, path.c_str()); } return true; @@ -553,16 +553,16 @@ bool exists_file(bool& result, const file_path_t name) NOEXCEPT { result = 0 == path_stats(info, name); - if (!result) { - auto path = boost::locale::conv::utf_to_utf(name); - - IR_FRMT_ERROR("Failed to get stat, error %d path: %s", errno, path.c_str()); - } else { + if (result) { #ifdef _WIN32 result = (info.st_mode & _S_IFREG) > 0; #else result = (info.st_mode & S_IFREG) > 0; #endif + } else if (ENOENT != errno) { + auto path = boost::locale::conv::utf_to_utf(name); + + IR_FRMT_ERROR("Failed to get stat, error %d path: %s", errno, path.c_str()); } return true; diff --git a/Documentation/DocuBlocks/Rest/Views/get_api_view_properties.md b/Documentation/DocuBlocks/Rest/Views/get_api_view_properties.md index 798deb0cb2..2ac8e40a0d 100644 --- a/Documentation/DocuBlocks/Rest/Views/get_api_view_properties.md +++ b/Documentation/DocuBlocks/Rest/Views/get_api_view_properties.md @@ -7,7 +7,7 @@ @RESTURLPARAMETERS @RESTDESCRIPTION -TBD +Returns an object containing the definition of the view identified by *view-name*. @RESTURLPARAM{view-name,string,required} The name of the view. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 12bf1ae026..b4c1ec7b3b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -48,7 +48,9 @@ if (USE_IRESEARCH) IResearch/ExpressionContextMock.cpp IResearch/VelocyPackHelper-test.cpp IResearch/ExecutionBlockMock-test.cpp + Utils/CollectionNameResolver-test.cpp VocBase/LogicalDataSource-test.cpp + VocBase/vocbase-test.cpp ) endif () diff --git a/tests/IResearch/IResearchIndex-test.cpp b/tests/IResearch/IResearchIndex-test.cpp index af0c6883bb..20259edb2a 100644 --- a/tests/IResearch/IResearchIndex-test.cpp +++ b/tests/IResearch/IResearchIndex-test.cpp @@ -374,12 +374,19 @@ SECTION("test_async_index") { // populate collections asynchronously { std::thread thread0([collection0, &resThread0]()->void { - irs::utf8_path resource; - resource/=irs::string_ref(IResearch_test_resource_dir); - resource/=irs::string_ref("simple_sequential.json"); + arangodb::velocypack::Builder builder; + + try { + irs::utf8_path resource; + + resource/=irs::string_ref(IResearch_test_resource_dir); + resource/=irs::string_ref("simple_sequential.json"); + builder = arangodb::basics::VelocyPackHelper::velocyPackFromFile(resource.utf8()); + } catch (...) { + return; // velocyPackFromFile(...) may throw exception + } auto doc = arangodb::velocypack::Parser::fromJson("{ \"seq\": 40, \"same\": \"xyz\", \"duplicated\": \"abcd\" }"); - auto builder = arangodb::basics::VelocyPackHelper::velocyPackFromFile(resource.utf8()); auto slice = builder.slice(); resThread0 = slice.isArray(); if (!resThread0) return; @@ -405,12 +412,19 @@ SECTION("test_async_index") { }); std::thread thread1([collection1, &resThread1]()->void { - irs::utf8_path resource; - resource/=irs::string_ref(IResearch_test_resource_dir); - resource/=irs::string_ref("simple_sequential.json"); + arangodb::velocypack::Builder builder; + + try { + irs::utf8_path resource; + + resource/=irs::string_ref(IResearch_test_resource_dir); + resource/=irs::string_ref("simple_sequential.json"); + builder = arangodb::basics::VelocyPackHelper::velocyPackFromFile(resource.utf8()); + } catch (...) { + return; // velocyPackFromFile(...) may throw exception + } auto doc = arangodb::velocypack::Parser::fromJson("{ \"seq\": 50, \"same\": \"xyz\", \"duplicated\": \"abcd\" }"); - auto builder = arangodb::basics::VelocyPackHelper::velocyPackFromFile(resource.utf8()); auto slice = builder.slice(); resThread1 = slice.isArray(); if (!resThread1) return; @@ -627,4 +641,4 @@ SECTION("test_fields") { // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/tests/IResearch/IResearchLinkMeta-test.cpp b/tests/IResearch/IResearchLinkMeta-test.cpp index c8e3a5c25c..7d705739aa 100644 --- a/tests/IResearch/IResearchLinkMeta-test.cpp +++ b/tests/IResearch/IResearchLinkMeta-test.cpp @@ -188,12 +188,12 @@ SECTION("test_inheritDefaults") { analyzers.start(); - defaults._fields["abc"] = std::move(arangodb::iresearch::IResearchLinkMeta()); + defaults._fields["abc"] = arangodb::iresearch::IResearchLinkMeta(); defaults._includeAllFields = true; defaults._trackListPositions = true; defaults._analyzers.clear(); defaults._analyzers.emplace_back(analyzers.ensure("empty")); - defaults._fields["abc"]->_fields["xyz"] = std::move(arangodb::iresearch::IResearchLinkMeta()); + defaults._fields["abc"]->_fields["xyz"] = arangodb::iresearch::IResearchLinkMeta(); auto json = arangodb::velocypack::Parser::fromJson("{}"); CHECK(true == meta.init(json->slice(), tmpString, defaults)); @@ -410,8 +410,8 @@ SECTION("test_writeCustomizedValues") { auto& overrideNone = *(meta._fields["c"]->_fields["none"]); overrideAll._fields.clear(); // do not inherit fields to match jSon inheritance - overrideAll._fields["x"] = std::move(arangodb::iresearch::IResearchLinkMeta()); - overrideAll._fields["y"] = std::move(arangodb::iresearch::IResearchLinkMeta()); + overrideAll._fields["x"] = arangodb::iresearch::IResearchLinkMeta(); + overrideAll._fields["y"] = arangodb::iresearch::IResearchLinkMeta(); overrideAll._includeAllFields = false; overrideAll._trackListPositions = false; overrideAll._analyzers.clear(); diff --git a/tests/Utils/CollectionNameResolver-test.cpp b/tests/Utils/CollectionNameResolver-test.cpp new file mode 100644 index 0000000000..61e8b9145c --- /dev/null +++ b/tests/Utils/CollectionNameResolver-test.cpp @@ -0,0 +1,263 @@ +//////////////////////////////////////////////////////////////////////////////// +/// DISCLAIMER +/// +/// Copyright 2018 ArangoDB 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 ArangoDB GmbH, Cologne, Germany +/// +/// @author Andrey Abramov +/// @author Vasiliy Nabatchikov +//////////////////////////////////////////////////////////////////////////////// + +#include "catch.hpp" +#include "../IResearch/common.h" +#include "../IResearch/StorageEngineMock.h" +#include "IResearch/ApplicationServerHelper.h" +#include "RestServer/DatabaseFeature.h" +#include "RestServer/QueryRegistryFeature.h" +#include "RestServer/ViewTypesFeature.h" +#include "StorageEngine/EngineSelectorFeature.h" +#include "Utils/CollectionNameResolver.h" +#include "VocBase/LogicalCollection.h" +#include "VocBase/LogicalView.h" +#include "velocypack/Parser.h" + +namespace { + +std::unique_ptr makeTestView( + arangodb::LogicalView* view, + arangodb::velocypack::Slice const& info, + bool isNew + ) { + struct Impl: public arangodb::ViewImplementation { + Impl(): ViewImplementation(nullptr, arangodb::velocypack::Slice::emptyObjectSlice()) { + } + virtual void drop() override {} + virtual void getPropertiesVPack( + arangodb::velocypack::Builder&, bool + ) const override { + } + virtual void open() override {} + virtual arangodb::Result updateProperties( + arangodb::velocypack::Slice const&, bool, bool + ) override { + return arangodb::Result(); + } + virtual bool visitCollections( + std::function const& + ) const override { + return true; + } + }; + + return std::unique_ptr(new Impl()); +} + +} + +// ----------------------------------------------------------------------------- +// --SECTION-- setup / tear-down +// ----------------------------------------------------------------------------- + +struct CollectionNameResolverSetup { + StorageEngineMock engine; + arangodb::application_features::ApplicationServer server; + std::vector> features; + + CollectionNameResolverSetup(): server(nullptr, nullptr) { + arangodb::EngineSelectorFeature::ENGINE = &engine; + + // setup required application features + features.emplace_back(new arangodb::DatabaseFeature(&server), false); // required for TRI_vocbase_t::dropCollection(...) + features.emplace_back(new arangodb::QueryRegistryFeature(&server), false); // required for TRI_vocbase_t instantiation + features.emplace_back(new arangodb::ViewTypesFeature(&server), false); // required for TRI_vocbase_t::createView(...) + + for (auto& f: features) { + arangodb::application_features::ApplicationServer::server->addFeature(f.first); + } + + for (auto& f: features) { + f.first->prepare(); + } + + for (auto& f: features) { + if (f.second) { + f.first->start(); + } + } + + // register view factory + arangodb::iresearch::getFeature()->emplace( + arangodb::LogicalDataSource::Type::emplace( + arangodb::velocypack::StringRef("testViewType") + ), + makeTestView + ); + } + + ~CollectionNameResolverSetup() { + arangodb::application_features::ApplicationServer::server = nullptr; + arangodb::EngineSelectorFeature::ENGINE = nullptr; + + // destroy application features + for (auto& f: features) { + if (f.second) { + f.first->stop(); + } + } + + for (auto& f: features) { + f.first->unprepare(); + } + } +}; + +// ----------------------------------------------------------------------------- +// --SECTION-- test suite +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief setup +//////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("CollectionNameResolverTest", "[vocbase]") { + CollectionNameResolverSetup s; + (void)(s); + +SECTION("test_getDataSource") { + auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"globallyUniqueId\": \"testCollectionGUID\", \"id\": 100, \"name\": \"testCollection\" }"); + auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"id\": 200, \"name\": \"testView\", \"type\": \"testViewType\" }"); // any arbitrary view type + Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); + arangodb::CollectionNameResolver resolver(&vocbase); + + // not present collection (no datasource) + { + CHECK((true == !resolver.getDataSource(100))); + CHECK((true == !resolver.getDataSource("100"))); + CHECK((true == !resolver.getDataSource("testCollection"))); + CHECK((true == !resolver.getDataSource("testCollectionGUID"))); + CHECK((true == !resolver.getCollection(100))); + CHECK((true == !resolver.getCollection("100"))); + CHECK((true == !resolver.getCollection("testCollection"))); + CHECK((true == !resolver.getCollection("testCollectionGUID"))); + } + + // not present view (no datasource) + { + CHECK((true == !resolver.getDataSource(200))); + CHECK((true == !resolver.getDataSource("200"))); + CHECK((true == !resolver.getDataSource("testView"))); + CHECK((true == !resolver.getDataSource("testViewGUID"))); + CHECK((true == !resolver.getView(200))); + CHECK((true == !resolver.getView("200"))); + CHECK((true == !resolver.getView("testView"))); + CHECK((true == !resolver.getView("testViewGUID"))); + } + + auto* collection = vocbase.createCollection(collectionJson->slice()); + auto view = vocbase.createView(viewJson->slice(), 42); + + CHECK((false == collection->deleted())); + CHECK((false == view->deleted())); + + // not present collection (is view) + { + CHECK((false == !resolver.getDataSource(200))); + CHECK((false == !resolver.getDataSource("200"))); + CHECK((false == !resolver.getDataSource("testView"))); + CHECK((true == !resolver.getDataSource("testViewGUID"))); + CHECK((true == !resolver.getCollection(200))); + CHECK((true == !resolver.getCollection("200"))); + CHECK((true == !resolver.getCollection("testView"))); + CHECK((true == !resolver.getCollection("testViewGUID"))); + } + + // not preset view (is collection) + { + CHECK((false == !resolver.getDataSource(100))); + CHECK((false == !resolver.getDataSource("100"))); + CHECK((false == !resolver.getDataSource("testCollection"))); + CHECK((false == !resolver.getDataSource("testCollectionGUID"))); + CHECK((true == !resolver.getView(100))); + CHECK((true == !resolver.getView("100"))); + CHECK((true == !resolver.getView("testCollection"))); + CHECK((true == !resolver.getView("testCollectionGUID"))); + } + + // present collection + { + CHECK((false == !resolver.getDataSource(100))); + CHECK((false == !resolver.getDataSource("100"))); + CHECK((false == !resolver.getDataSource("testCollection"))); + CHECK((false == !resolver.getDataSource("testCollectionGUID"))); + CHECK((false == !resolver.getCollection(100))); + CHECK((false == !resolver.getCollection("100"))); + CHECK((false == !resolver.getCollection("testCollection"))); + CHECK((false == !resolver.getCollection("testCollectionGUID"))); + } + + // present view + { + CHECK((false == !resolver.getDataSource(200))); + CHECK((false == !resolver.getDataSource("200"))); + CHECK((false == !resolver.getDataSource("testView"))); + CHECK((true == !resolver.getDataSource("testViewGUID"))); + CHECK((false == !resolver.getView(200))); + CHECK((false == !resolver.getView("200"))); + CHECK((false == !resolver.getView("testView"))); + CHECK((true == !resolver.getView("testViewGUID"))); + } + + CHECK((TRI_ERROR_NO_ERROR == vocbase.dropCollection(collection, true, 0))); + CHECK((TRI_ERROR_NO_ERROR == vocbase.dropView(view))); + CHECK((true == collection->deleted())); + CHECK((true == view->deleted())); + + // present collection (deleted, cached) + { + CHECK((false == !resolver.getDataSource(100))); + CHECK((false == !resolver.getDataSource("100"))); + CHECK((false == !resolver.getDataSource("testCollection"))); + CHECK((false == !resolver.getDataSource("testCollectionGUID"))); + CHECK((false == !resolver.getCollection(100))); + CHECK((false == !resolver.getCollection("100"))); + CHECK((false == !resolver.getCollection("testCollection"))); + CHECK((false == !resolver.getCollection("testCollectionGUID"))); + CHECK((true == resolver.getCollection(100)->deleted())); + } + + // present view (deleted, cached) + { + CHECK((false == !resolver.getDataSource(200))); + CHECK((false == !resolver.getDataSource("200"))); + CHECK((false == !resolver.getDataSource("testView"))); + CHECK((true == !resolver.getDataSource("testViewGUID"))); + CHECK((false == !resolver.getView(200))); + CHECK((false == !resolver.getView("200"))); + CHECK((false == !resolver.getView("testView"))); + CHECK((true == !resolver.getView("testViewGUID"))); + CHECK((true == resolver.getView(200)->deleted())); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief generate tests +//////////////////////////////////////////////////////////////////////////////// + +} + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/tests/VocBase/vocbase-test.cpp b/tests/VocBase/vocbase-test.cpp new file mode 100644 index 0000000000..b7e26defac --- /dev/null +++ b/tests/VocBase/vocbase-test.cpp @@ -0,0 +1,368 @@ +//////////////////////////////////////////////////////////////////////////////// +/// DISCLAIMER +/// +/// Copyright 2018 ArangoDB 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 ArangoDB GmbH, Cologne, Germany +/// +/// @author Andrey Abramov +/// @author Vasiliy Nabatchikov +//////////////////////////////////////////////////////////////////////////////// + +#include "catch.hpp" +#include "../IResearch/common.h" +#include "../IResearch/StorageEngineMock.h" +#include "IResearch/ApplicationServerHelper.h" +#include "RestServer/DatabaseFeature.h" +#include "RestServer/QueryRegistryFeature.h" +#include "RestServer/ViewTypesFeature.h" +#include "StorageEngine/EngineSelectorFeature.h" +#include "VocBase/LogicalCollection.h" +#include "VocBase/LogicalView.h" +#include "velocypack/Parser.h" + +namespace { + +std::unique_ptr makeTestView( + arangodb::LogicalView* view, + arangodb::velocypack::Slice const& info, + bool isNew + ) { + struct Impl: public arangodb::ViewImplementation { + Impl(): ViewImplementation(nullptr, arangodb::velocypack::Slice::emptyObjectSlice()) { + } + virtual void drop() override {} + virtual void getPropertiesVPack( + arangodb::velocypack::Builder&, bool + ) const override { + } + virtual void open() override {} + virtual arangodb::Result updateProperties( + arangodb::velocypack::Slice const&, bool, bool + ) override { + return arangodb::Result(); + } + virtual bool visitCollections( + std::function const& + ) const override { + return true; + } + }; + + return std::unique_ptr(new Impl()); +} + +} + +// ----------------------------------------------------------------------------- +// --SECTION-- setup / tear-down +// ----------------------------------------------------------------------------- + +struct VocbaseSetup { + StorageEngineMock engine; + arangodb::application_features::ApplicationServer server; + std::vector> features; + + VocbaseSetup(): server(nullptr, nullptr) { + arangodb::EngineSelectorFeature::ENGINE = &engine; + + // setup required application features + features.emplace_back(new arangodb::DatabaseFeature(&server), false); // required for TRI_vocbase_t::dropCollection(...) + features.emplace_back(new arangodb::QueryRegistryFeature(&server), false); // required for TRI_vocbase_t instantiation + features.emplace_back(new arangodb::ViewTypesFeature(&server), false); // required for TRI_vocbase_t::createView(...) + + for (auto& f: features) { + arangodb::application_features::ApplicationServer::server->addFeature(f.first); + } + + for (auto& f: features) { + f.first->prepare(); + } + + for (auto& f: features) { + if (f.second) { + f.first->start(); + } + } + + // register view factory + arangodb::iresearch::getFeature()->emplace( + arangodb::LogicalDataSource::Type::emplace( + arangodb::velocypack::StringRef("testViewType") + ), + makeTestView + ); + } + + ~VocbaseSetup() { + arangodb::application_features::ApplicationServer::server = nullptr; + arangodb::EngineSelectorFeature::ENGINE = nullptr; + + // destroy application features + for (auto& f: features) { + if (f.second) { + f.first->stop(); + } + } + + for (auto& f: features) { + f.first->unprepare(); + } + } +}; + +// ----------------------------------------------------------------------------- +// --SECTION-- test suite +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief setup +//////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("VocbaseTest", "[vocbase]") { + VocbaseSetup s; + (void)(s); + +SECTION("test_isAllowedName") { + // direct (non-system) + { + CHECK((false == TRI_vocbase_t::IsAllowedName(false, arangodb::velocypack::StringRef(nullptr, 0)))); + CHECK((false == TRI_vocbase_t::IsAllowedName(false, arangodb::velocypack::StringRef("")))); + CHECK((true == TRI_vocbase_t::IsAllowedName(false, arangodb::velocypack::StringRef("abc123")))); + CHECK((false == TRI_vocbase_t::IsAllowedName(false, arangodb::velocypack::StringRef("123abc")))); + CHECK((false == TRI_vocbase_t::IsAllowedName(false, arangodb::velocypack::StringRef("123")))); + CHECK((false == TRI_vocbase_t::IsAllowedName(false, arangodb::velocypack::StringRef("_123")))); + CHECK((false == TRI_vocbase_t::IsAllowedName(false, arangodb::velocypack::StringRef("_abc")))); + CHECK((false == TRI_vocbase_t::IsAllowedName(false, arangodb::velocypack::StringRef("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")))); // longer than TRI_COL_NAME_LENGTH + } + + // direct (system) + { + CHECK((false == TRI_vocbase_t::IsAllowedName(true, arangodb::velocypack::StringRef(nullptr, 0)))); + CHECK((false == TRI_vocbase_t::IsAllowedName(true, arangodb::velocypack::StringRef("")))); + CHECK((true == TRI_vocbase_t::IsAllowedName(true, arangodb::velocypack::StringRef("abc123")))); + CHECK((false == TRI_vocbase_t::IsAllowedName(true, arangodb::velocypack::StringRef("123abc")))); + CHECK((false == TRI_vocbase_t::IsAllowedName(true, arangodb::velocypack::StringRef("123")))); + CHECK((true == TRI_vocbase_t::IsAllowedName(true, arangodb::velocypack::StringRef("_123")))); + CHECK((true == TRI_vocbase_t::IsAllowedName(true, arangodb::velocypack::StringRef("_abc")))); + CHECK((false == TRI_vocbase_t::IsAllowedName(true, arangodb::velocypack::StringRef("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")))); // longer than TRI_COL_NAME_LENGTH + } + + // slice (default) + { + auto json0 = arangodb::velocypack::Parser::fromJson("{ }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json0->slice()))); + auto json1 = arangodb::velocypack::Parser::fromJson("{ \"name\": \"\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json1->slice()))); + auto json2 = arangodb::velocypack::Parser::fromJson("{ \"name\": \"abc123\" }"); + CHECK((true == TRI_vocbase_t::IsAllowedName(json2->slice()))); + auto json3 = arangodb::velocypack::Parser::fromJson("{ \"name\": \"123abc\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json3->slice()))); + auto json4 = arangodb::velocypack::Parser::fromJson("{ \"name\": \"123\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json4->slice()))); + auto json5 = arangodb::velocypack::Parser::fromJson("{ \"name\": \"_123\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json5->slice()))); + auto json6 = arangodb::velocypack::Parser::fromJson("{ \"name\": \"_abc\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json6->slice()))); + auto json7 = arangodb::velocypack::Parser::fromJson("{ \"name\": \"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json7->slice()))); // longer than TRI_COL_NAME_LENGTH + } + + // slice (non-system) + { + auto json0 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": false }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json0->slice()))); + auto json1 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": false, \"name\": \"\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json1->slice()))); + auto json2 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": false, \"name\": \"abc123\" }"); + CHECK((true == TRI_vocbase_t::IsAllowedName(json2->slice()))); + auto json3 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": false, \"name\": \"123abc\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json3->slice()))); + auto json4 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": false, \"name\": \"123\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json4->slice()))); + auto json5 = arangodb::velocypack::Parser::fromJson("{\"isSystem\": false, \"name\": \"_123\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json5->slice()))); + auto json6 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": false, \"name\": \"_abc\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json6->slice()))); + auto json7 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": false, \"name\": 123 }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json7->slice()))); + auto json8 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": 123, \"name\": \"abc\" }"); + CHECK((true == TRI_vocbase_t::IsAllowedName(json8->slice()))); + auto json9 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": false, \"name\": \"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json9->slice()))); // longer than TRI_COL_NAME_LENGTH + } + + // slice (system) + { + auto json0 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": true }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json0->slice()))); + auto json1 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": true, \"name\": \"\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json1->slice()))); + auto json2 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": true, \"name\": \"abc123\" }"); + CHECK((true == TRI_vocbase_t::IsAllowedName(json2->slice()))); + auto json3 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": true, \"name\": \"123abc\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json3->slice()))); + auto json4 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": true, \"name\": \"123\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json4->slice()))); + auto json5 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": true, \"name\": \"_123\" }"); + CHECK((true == TRI_vocbase_t::IsAllowedName(json5->slice()))); + auto json6 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": true, \"name\": \"_abc\" }"); + CHECK((true == TRI_vocbase_t::IsAllowedName(json6->slice()))); + auto json7 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": true, \"name\": 123 }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json7->slice()))); + auto json8 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": 123, \"name\": \"_abc\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json8->slice()))); + auto json9 = arangodb::velocypack::Parser::fromJson("{ \"isSystem\": true, \"name\": \"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\" }"); + CHECK((false == TRI_vocbase_t::IsAllowedName(json9->slice()))); // longer than TRI_COL_NAME_LENGTH + } +} + +SECTION("test_isSystemName") { + CHECK((false == TRI_vocbase_t::IsSystemName(""))); + CHECK((true == TRI_vocbase_t::IsSystemName("_"))); + CHECK((true == TRI_vocbase_t::IsSystemName("_abc"))); + CHECK((false == TRI_vocbase_t::IsSystemName("abc"))); +} + +SECTION("test_lookupDataSource") { + auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"globallyUniqueId\": \"testCollectionGUID\", \"id\": 100, \"name\": \"testCollection\" }"); + auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"id\": 200, \"name\": \"testView\", \"type\": \"testViewType\" }"); // any arbitrary view type + Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); + + // not present collection (no datasource) + { + CHECK((true == !vocbase.lookupDataSource(100))); + CHECK((true == !vocbase.lookupDataSource("100"))); + CHECK((true == !vocbase.lookupDataSource("testCollection"))); + CHECK((true == !vocbase.lookupDataSource("testCollectionGUID"))); + CHECK((true == !vocbase.lookupCollection(100))); + CHECK((true == !vocbase.lookupCollection("100"))); + CHECK((true == !vocbase.lookupCollection("testCollection"))); + CHECK((true == !vocbase.lookupCollection("testCollectionGUID"))); + } + + // not present view (no datasource) + { + CHECK((true == !vocbase.lookupDataSource(200))); + CHECK((true == !vocbase.lookupDataSource("200"))); + CHECK((true == !vocbase.lookupDataSource("testView"))); + CHECK((true == !vocbase.lookupDataSource("testViewGUID"))); + CHECK((true == !vocbase.lookupView(200))); + CHECK((true == !vocbase.lookupView("200"))); + CHECK((true == !vocbase.lookupView("testView"))); + CHECK((true == !vocbase.lookupView("testViewGUID"))); + } + + auto* collection = vocbase.createCollection(collectionJson->slice()); + auto view = vocbase.createView(viewJson->slice(), 42); + + CHECK((false == collection->deleted())); + CHECK((false == view->deleted())); + + // not present collection (is view) + { + CHECK((false == !vocbase.lookupDataSource(200))); + CHECK((false == !vocbase.lookupDataSource("200"))); + CHECK((false == !vocbase.lookupDataSource("testView"))); + CHECK((true == !vocbase.lookupDataSource("testViewGUID"))); + CHECK((true == !vocbase.lookupCollection(200))); + CHECK((true == !vocbase.lookupCollection("200"))); + CHECK((true == !vocbase.lookupCollection("testView"))); + CHECK((true == !vocbase.lookupCollection("testViewGUID"))); + CHECK((true == !vocbase.lookupCollectionByUuid("testView"))); + CHECK((true == !vocbase.lookupCollectionByUuid("testViewGUID"))); + } + + // not preset view (is collection) + { + CHECK((false == !vocbase.lookupDataSource(100))); + CHECK((false == !vocbase.lookupDataSource("100"))); + CHECK((false == !vocbase.lookupDataSource("testCollection"))); + CHECK((false == !vocbase.lookupDataSource("testCollectionGUID"))); + CHECK((true == !vocbase.lookupView(100))); + CHECK((true == !vocbase.lookupView("100"))); + CHECK((true == !vocbase.lookupView("testCollection"))); + CHECK((true == !vocbase.lookupView("testCollectionGUID"))); + } + + // present collection + { + CHECK((false == !vocbase.lookupDataSource(100))); + CHECK((false == !vocbase.lookupDataSource("100"))); + CHECK((false == !vocbase.lookupDataSource("testCollection"))); + CHECK((false == !vocbase.lookupDataSource("testCollectionGUID"))); + CHECK((false == !vocbase.lookupCollection(100))); + CHECK((false == !vocbase.lookupCollection("100"))); + CHECK((false == !vocbase.lookupCollection("testCollection"))); + CHECK((false == !vocbase.lookupCollection("testCollectionGUID"))); + CHECK((true == !vocbase.lookupCollectionByUuid("testCollection"))); + CHECK((false == !vocbase.lookupCollectionByUuid("testCollectionGUID"))); + } + + // present view + { + CHECK((false == !vocbase.lookupDataSource(200))); + CHECK((false == !vocbase.lookupDataSource("200"))); + CHECK((false == !vocbase.lookupDataSource("testView"))); + CHECK((true == !vocbase.lookupDataSource("testViewGUID"))); + CHECK((false == !vocbase.lookupView(200))); + CHECK((false == !vocbase.lookupView("200"))); + CHECK((false == !vocbase.lookupView("testView"))); + CHECK((true == !vocbase.lookupView("testViewGUID"))); + } + + CHECK((TRI_ERROR_NO_ERROR == vocbase.dropCollection(collection, true, 0))); + CHECK((TRI_ERROR_NO_ERROR == vocbase.dropView(view))); + CHECK((true == collection->deleted())); + CHECK((true == view->deleted())); + + // not present collection (deleted) + { + CHECK((true == !vocbase.lookupDataSource(100))); + CHECK((true == !vocbase.lookupDataSource("100"))); + CHECK((true == !vocbase.lookupDataSource("testCollection"))); + CHECK((true == !vocbase.lookupDataSource("testCollectionGUID"))); + CHECK((true == !vocbase.lookupCollection(100))); + CHECK((true == !vocbase.lookupCollection("100"))); + CHECK((true == !vocbase.lookupCollection("testCollection"))); + CHECK((true == !vocbase.lookupCollection("testCollectionGUID"))); + CHECK((true == !vocbase.lookupCollectionByUuid("testCollection"))); + CHECK((true == !vocbase.lookupCollectionByUuid("testCollectionGUID"))); + } + + // not present view (deleted) + { + CHECK((true == !vocbase.lookupDataSource(200))); + CHECK((true == !vocbase.lookupDataSource("200"))); + CHECK((true == !vocbase.lookupDataSource("testView"))); + CHECK((true == !vocbase.lookupDataSource("testViewGUID"))); + CHECK((true == !vocbase.lookupView(200))); + CHECK((true == !vocbase.lookupView("200"))); + CHECK((true == !vocbase.lookupView("testView"))); + CHECK((true == !vocbase.lookupView("testViewGUID"))); + CHECK((true == !vocbase.lookupCollectionByUuid("testCollection"))); + CHECK((true == !vocbase.lookupCollectionByUuid("testCollectionGUID"))); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief generate tests +//////////////////////////////////////////////////////////////////////////////// + +} + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- \ No newline at end of file From a553410296b5f6da42c0a036a33b3515751bdfdd Mon Sep 17 00:00:00 2001 From: Vasiliy Date: Tue, 20 Mar 2018 17:54:49 +0300 Subject: [PATCH 2/2] backport: explicitly set the database path in test setup --- tests/IResearch/IResearchQueryExists-test.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/IResearch/IResearchQueryExists-test.cpp b/tests/IResearch/IResearchQueryExists-test.cpp index 1272b64e4d..4d186cb5bc 100644 --- a/tests/IResearch/IResearchQueryExists-test.cpp +++ b/tests/IResearch/IResearchQueryExists-test.cpp @@ -30,6 +30,7 @@ #include "Enterprise/Ldap/LdapFeature.h" #endif +#include "Basics/files.h" #include "V8/v8-globals.h" #include "VocBase/LogicalCollection.h" #include "VocBase/LogicalView.h" @@ -132,6 +133,13 @@ struct IResearchQuerySetup { analyzers->emplace("test_analyzer", "TestAnalyzer", "abc"); // cache analyzer analyzers->emplace("test_csv_analyzer", "TestDelimAnalyzer", ","); // cache analyzer + auto* dbPathFeature = arangodb::application_features::ApplicationServer::getFeature("DatabasePath"); + irs::utf8_path testFilesystemPath; + + testFilesystemPath /= TRI_GetTempPath(); + testFilesystemPath /= std::string("arangodb_tests.") + std::to_string(TRI_microtime()); + const_cast(dbPathFeature->directory()) = testFilesystemPath.utf8(); + // suppress log messages since tests check error conditions arangodb::LogTopic::setLogLevel(arangodb::Logger::FIXME.name(), arangodb::LogLevel::ERR); // suppress WARNING DefaultCustomTypeHandler called arangodb::LogTopic::setLogLevel(arangodb::iresearch::IResearchFeature::IRESEARCH.name(), arangodb::LogLevel::FATAL);