1
0
Fork 0
arangodb/tests/IResearch/IResearchAnalyzerFeature-te...

1371 lines
52 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2017 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 "common.h"
#include "StorageEngineMock.h"
#include "analysis/analyzers.hpp"
#include "analysis/token_attributes.hpp"
#include "Aql/AqlFunctionFeature.h"
#if USE_ENTERPRISE
#include "Enterprise/Ldap/LdapFeature.h"
#endif
#include "GeneralServer/AuthenticationFeature.h"
#include "IResearch/IResearchAnalyzerFeature.h"
#include "IResearch/IResearchCommon.h"
#include "IResearch/VelocyPackHelper.h"
#include "RestServer/DatabaseFeature.h"
#include "RestServer/QueryRegistryFeature.h"
#include "RestServer/SystemDatabaseFeature.h"
#include "Sharding/ShardingFeature.h"
#include "StorageEngine/EngineSelectorFeature.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/OperationOptions.h"
#include "Utils/SingleCollectionTransaction.h"
#include "VocBase/KeyGenerator.h"
#include "VocBase/LogicalCollection.h"
#include "VocBase/ManagedDocumentResult.h"
NS_LOCAL
struct TestAttribute: public irs::attribute {
DECLARE_ATTRIBUTE_TYPE();
};
DEFINE_ATTRIBUTE_TYPE(TestAttribute);
REGISTER_ATTRIBUTE(TestAttribute); // required to open reader on segments with analized fields
struct TestTermAttribute: public irs::term_attribute {
public:
void value(irs::bytes_ref const& value) {
value_ = value;
}
};
class TestAnalyzer: public irs::analysis::analyzer {
public:
DECLARE_ANALYZER_TYPE();
TestAnalyzer(): irs::analysis::analyzer(TestAnalyzer::type()) {
_attrs.emplace(_term);
_attrs.emplace(_attr);
_attrs.emplace(_increment); // required by field_data::invert(...)
}
virtual irs::attribute_view const& attributes() const NOEXCEPT override { return _attrs; }
static ptr make(irs::string_ref const& args) {
if (args.null()) throw std::exception();
if (args.empty()) return nullptr;
PTR_NAMED(TestAnalyzer, ptr);
return ptr;
}
virtual bool next() override {
if (_data.empty()) return false;
_term.value(irs::bytes_ref(_data.c_str(), 1));
_data = irs::bytes_ref(_data.c_str() + 1, _data.size() - 1);
return true;
}
virtual bool reset(irs::string_ref const& data) override {
_data = irs::ref_cast<irs::byte_type>(data);
return true;
}
private:
irs::attribute_view _attrs;
irs::bytes_ref _data;
irs::increment _increment;
TestTermAttribute _term;
TestAttribute _attr;
};
DEFINE_ANALYZER_TYPE_NAMED(TestAnalyzer, "TestAnalyzer");
REGISTER_ANALYZER_JSON(TestAnalyzer, TestAnalyzer::make);
struct Analyzer {
irs::string_ref type;
irs::string_ref properties;
irs::flags features;
Analyzer() = default;
Analyzer(irs::string_ref const& t, irs::string_ref const& p, irs::flags const& f = irs::flags::empty_instance()): type(t), properties(p), features(f) {}
};
std::map<irs::string_ref, Analyzer>const& staticAnalyzers() {
static const std::map<irs::string_ref, Analyzer> analyzers = {
{ "identity", { "identity", irs::string_ref::NIL, { irs::frequency::type(), irs::norm::type() } } },
{"text_de", { "text", "{ \"locale\": \"de.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_en", { "text", "{ \"locale\": \"en.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_es", { "text", "{ \"locale\": \"es.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_fi", { "text", "{ \"locale\": \"fi.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_fr", { "text", "{ \"locale\": \"fr.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_it", { "text", "{ \"locale\": \"it.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_nl", { "text", "{ \"locale\": \"nl.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_no", { "text", "{ \"locale\": \"no.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_pt", { "text", "{ \"locale\": \"pt.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_ru", { "text", "{ \"locale\": \"ru.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_sv", { "text", "{ \"locale\": \"sv.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
{"text_zh", { "text", "{ \"locale\": \"zh.UTF-8\", \"ignored_words\": [ ] }", { irs::frequency::type(), irs::norm::type(), irs::position::type() } } },
};
return analyzers;
}
// AqlValue entries must be explicitly deallocated
struct VPackFunctionParametersWrapper {
arangodb::SmallVector<arangodb::aql::AqlValue>::allocator_type::arena_type arena;
arangodb::aql::VPackFunctionParameters instance;
VPackFunctionParametersWrapper(): instance(arena) {}
~VPackFunctionParametersWrapper() {
for(auto& entry: instance) {
entry.destroy();
}
}
arangodb::aql::VPackFunctionParameters* operator->() { return &instance; }
arangodb::aql::VPackFunctionParameters& operator*() { return instance; }
};
// AqlValue entrys must be explicitly deallocated
struct AqlValueWrapper {
arangodb::aql::AqlValue instance;
AqlValueWrapper(arangodb::aql::AqlValue&& other): instance(std::move(other)) {}
~AqlValueWrapper() { instance.destroy(); }
arangodb::aql::AqlValue* operator->() { return &instance; }
arangodb::aql::AqlValue& operator*() { return instance; }
};
// a way to set EngineSelectorFeature::ENGINE and nullify it via destructor,
// i.e. only after all TRI_vocbase_t and ApplicationServer have been destroyed
struct StorageEngineWrapper {
StorageEngineMock instance;
StorageEngineWrapper(
arangodb::application_features::ApplicationServer& server
): instance(server) { arangodb::EngineSelectorFeature::ENGINE = &instance; }
~StorageEngineWrapper() { arangodb::EngineSelectorFeature::ENGINE = nullptr; }
StorageEngineMock* operator->() { return &instance; }
StorageEngineMock& operator*() { return instance; }
};
NS_END
// -----------------------------------------------------------------------------
// --SECTION-- setup / tear-down
// -----------------------------------------------------------------------------
struct IResearchAnalyzerFeatureSetup {
StorageEngineWrapper engine; // can only nullify 'ENGINE' after all TRI_vocbase_t and ApplicationServer have been destroyed
std::unique_ptr<Vocbase> system; // ensure destruction after 'server'
arangodb::application_features::ApplicationServer server;
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
IResearchAnalyzerFeatureSetup(): engine(server), server(nullptr, nullptr) {
arangodb::tests::init();
// suppress INFO {authentication} Authentication is turned on (system only), authentication for unix sockets is turned on
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::WARN);
// setup required application features
features.emplace_back(new arangodb::AuthenticationFeature(server), true);
features.emplace_back(new arangodb::DatabaseFeature(server), false);
features.emplace_back(new arangodb::ShardingFeature(server), false);
features.emplace_back(new arangodb::QueryRegistryFeature(server), false); // required for constructing TRI_vocbase_t
arangodb::application_features::ApplicationServer::server->addFeature(features.back().first); // need QueryRegistryFeature feature to be added now in order to create the system database
system = irs::memory::make_unique<Vocbase>(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 0, TRI_VOC_SYSTEM_DATABASE); // QueryRegistryFeature required for instantiation
features.emplace_back(new arangodb::SystemDatabaseFeature(server, system.get()), false); // required for IResearchAnalyzerFeature
features.emplace_back(new arangodb::aql::AqlFunctionFeature(server), true); // required for IResearchAnalyzerFeature
#if USE_ENTERPRISE
features.emplace_back(new arangodb::LdapFeature(server), false); // required for AuthenticationFeature with USE_ENTERPRISE
#endif
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();
}
}
// suppress log messages since tests check error conditions
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::FATAL);
irs::logger::output_le(iresearch::logger::IRL_FATAL, stderr);
}
~IResearchAnalyzerFeatureSetup() {
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::DEFAULT);
arangodb::application_features::ApplicationServer::server = nullptr;
// destroy application features
for (auto& f : features) {
if (f.second) {
f.first->stop();
}
}
for (auto& f : features) {
f.first->unprepare();
}
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::DEFAULT);
}
};
// -----------------------------------------------------------------------------
// --SECTION-- test suite
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief setup
////////////////////////////////////////////////////////////////////////////////
TEST_CASE("IResearchAnalyzerFeatureTest", "[iresearch][iresearch-feature]") {
IResearchAnalyzerFeatureSetup s;
UNUSED(s);
SECTION("test_emplace") {
// add valid
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer0", "TestAnalyzer", "abc").first));
auto pool = feature.get("test_analyzer0");
CHECK((false == !pool));
CHECK((irs::flags() == pool->features()));
}
// add duplicate valid (same name+type+properties)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer1", "TestAnalyzer", "abc", irs::flags{ TestAttribute::type() }).first));
auto pool = feature.get("test_analyzer1");
CHECK((false == !pool));
CHECK((irs::flags({ TestAttribute::type() }) == pool->features()));
CHECK((false == !feature.emplace("test_analyzer1", "TestAnalyzer", "abc").first));
CHECK((false == !feature.get("test_analyzer1")));
}
// add duplicate invalid (same name+type different properties)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer2", "TestAnalyzer", "abc").first));
auto pool = feature.get("test_analyzer2");
CHECK((false == !pool));
CHECK((irs::flags() == pool->features()));
CHECK((true == !feature.emplace("test_analyzer2", "TestAnalyzer", "abcd").first));
CHECK((false == !feature.get("test_analyzer2")));
}
// add duplicate invalid (same name+properties different type)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer3", "TestAnalyzer", "abc").first));
auto pool = feature.get("test_analyzer3");
CHECK((false == !pool));
CHECK((irs::flags() == pool->features()));
CHECK((true == !feature.emplace("test_analyzer3", "invalid", "abc").first));
CHECK((false == !feature.get("test_analyzer3")));
}
// add invalid (instance creation failure)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((true == !feature.emplace("test_analyzer4", "TestAnalyzer", "").first));
CHECK((true == !feature.get("test_analyzer4")));
}
// add invalid (instance creation exception)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((true == !feature.emplace("test_analyzer5", "TestAnalyzer", irs::string_ref::NIL).first));
CHECK((true == !feature.get("test_analyzer5")));
}
// add invalid (not registred)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((true == !feature.emplace("test_analyzer6", "invalid", irs::string_ref::NIL).first));
CHECK((true == !feature.get("test_analyzer6")));
}
// add invalid (feature not started)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((true == !feature.emplace("test_analyzer7", "TestAnalyzer", "abc").first));
auto pool = feature.ensure("test_analyzer");
REQUIRE((false == !pool));
CHECK((irs::flags::empty_instance() == pool->features()));
auto analyzer = pool->get();
CHECK((true == !analyzer));
CHECK((true == !feature.get("test_analyzer7")));
}
// add static analyzer
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("identity", "identity", irs::string_ref::NIL).first));
auto pool = feature.get("identity");
CHECK((false == !pool));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type() }) == pool->features()));
auto analyzer = pool->get();
CHECK((false == !analyzer));
}
// add static analyzer (feature not started)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((false == !feature.emplace("identity", "identity", irs::string_ref::NIL).first));
auto pool = feature.get("identity");
CHECK((false == !pool));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type() }) == pool->features()));
auto analyzer = pool->get();
CHECK((false == !analyzer));
}
}
SECTION("test_ensure") {
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
// ensure (not started)
{
auto pool = feature.ensure("test_analyzer");
REQUIRE((false == !pool));
CHECK((irs::flags::empty_instance() == pool->features()));
auto analyzer = pool->get();
CHECK((true == !analyzer));
}
// ensure static analyzer (not started)
{
auto pool = feature.ensure("identity");
REQUIRE((false == !pool));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == pool->features()));
auto analyzer = pool->get();
CHECK((false == !analyzer));
}
}
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
feature.emplace("test_analyzer", "TestAnalyzer", "abc");
// ensure valid (started)
{
auto pool = feature.get("test_analyzer");
REQUIRE((false == !pool));
CHECK((irs::flags() == pool->features()));
auto analyzer = pool.get();
CHECK((false == !analyzer));
}
// ensure invalid (started)
{
CHECK((true == !feature.get("invalid")));
}
// ensure static analyzer (started)
{
auto pool = feature.ensure("identity");
REQUIRE((false == !pool));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type() }) == pool->features()));
auto analyzer = pool->get();
CHECK((false == !analyzer));
}
}
}
SECTION("test_get") {
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.ensure("test_analyzer");
// get valid (not started)
{
auto pool = feature.get("test_analyzer");
REQUIRE((false == !pool));
CHECK((irs::flags::empty_instance() == pool->features()));
auto analyzer = pool->get();
CHECK((true == !analyzer));
}
// get invalid (not started)
{
CHECK((true == !feature.get("invalid")));
}
// get static analyzer (not started)
{
auto pool = feature.get("identity");
REQUIRE((false == !pool));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == pool->features()));
auto analyzer = pool->get();
CHECK((false == !analyzer));
}
}
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
feature.emplace("test_analyzer", "TestAnalyzer", "abc");
// get valid (started)
{
auto pool = feature.get("test_analyzer");
REQUIRE((false == !pool));
CHECK((irs::flags() == pool->features()));
auto analyzer = pool.get();
CHECK((false == !analyzer));
}
// get invalid (started)
{
CHECK((true == !feature.get("invalid")));
}
// get static analyzer (started)
{
auto pool = feature.get("identity");
REQUIRE((false == !pool));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == pool->features()));
auto analyzer = pool->get();
CHECK((false == !analyzer));
}
}
}
SECTION("test_identity") {
// test static 'identity'
{
auto pool = arangodb::iresearch::IResearchAnalyzerFeature::identity();
CHECK((false == !pool));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == pool->features()));
CHECK(("identity" == pool->name()));
auto analyzer = pool->get();
CHECK((false == !analyzer));
auto& term = analyzer->attributes().get<irs::term_attribute>();
CHECK((false == !term));
CHECK((!analyzer->next()));
CHECK((analyzer->reset("abc def ghi")));
CHECK((analyzer->next()));
CHECK((irs::ref_cast<irs::byte_type>(irs::string_ref("abc def ghi")) == term->value()));
CHECK((!analyzer->next()));
CHECK((analyzer->reset("123 456")));
CHECK((analyzer->next()));
CHECK((irs::ref_cast<irs::byte_type>(irs::string_ref("123 456")) == term->value()));
CHECK((!analyzer->next()));
}
// test registered 'identity'
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((false == !feature.get("identity")));
auto pool = feature.ensure("identity");
CHECK((false == !pool));
feature.start();
pool = feature.get("identity");
REQUIRE((false == !pool));
CHECK((irs::flags({irs::norm::type(), irs::frequency::type()}) == pool->features()));
CHECK(("identity" == pool->name()));
auto analyzer = pool->get();
CHECK((false == !analyzer));
auto& term = analyzer->attributes().get<irs::term_attribute>();
CHECK((false == !term));
CHECK((!analyzer->next()));
CHECK((analyzer->reset("abc def ghi")));
CHECK((analyzer->next()));
CHECK((irs::ref_cast<irs::byte_type>(irs::string_ref("abc def ghi")) == term->value()));
CHECK((!analyzer->next()));
CHECK((analyzer->reset("123 456")));
CHECK((analyzer->next()));
CHECK((irs::ref_cast<irs::byte_type>(irs::string_ref("123 456")) == term->value()));
CHECK((!analyzer->next()));
}
}
SECTION("test_text_features") {
// test registered 'identity'
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
for (auto& analyzerEntry : staticAnalyzers()) {
CHECK((false == !feature.get(analyzerEntry.first)));
auto pool = feature.ensure(analyzerEntry.first);
CHECK((false == !pool));
feature.start();
pool = feature.get(analyzerEntry.first);
REQUIRE((false == !pool));
CHECK(analyzerEntry.second.features == pool->features());
CHECK(analyzerEntry.first == pool->name());
auto analyzer = pool->get();
CHECK((false == !analyzer));
auto& term = analyzer->attributes().get<irs::term_attribute>();
CHECK((false == !term));
}
}
}
SECTION("test_persistence") {
static std::vector<std::string> const EMPTY;
auto* database = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::SystemDatabaseFeature
>();
auto vocbase = database->use();
// ensure there is an empty configuration collection
{
auto collection = vocbase->lookupCollection("_iresearch_analyzers");
if (collection) {
vocbase->dropCollection(collection->id(), true, -1);
}
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr != collection));
}
// read invalid configuration (missing attributes)
{
{
std::string collection("_iresearch_analyzers");
arangodb::OperationOptions options;
arangodb::SingleCollectionTransaction trx(
arangodb::transaction::StandaloneContext::Create(*vocbase),
collection,
arangodb::AccessMode::Type::WRITE
);
trx.begin();
trx.truncate(collection, options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{}")->slice(), options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{ \"type\": \"identity\", \"properties\": null}")->slice(), options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": 12345, \"type\": \"identity\", \"properties\": null}")->slice(), options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": \"invalid1\", \"properties\": null}")->slice(), options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": \"invalid2\", \"type\": 12345, \"properties\": null}")->slice(), options);
trx.commit();
}
std::map<irs::string_ref, std::pair<irs::string_ref, irs::string_ref>> expected = {};
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
feature.visit([&expected](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
return true; // skip static analyzers
}
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.first == type));
CHECK((itr->second.second == properties));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
}
// read invalid configuration (duplicate non-identical records)
{
{
std::string collection("_iresearch_analyzers");
arangodb::OperationOptions options;
arangodb::SingleCollectionTransaction trx(
arangodb::transaction::StandaloneContext::Create(*vocbase),
collection,
arangodb::AccessMode::Type::WRITE
);
trx.begin();
trx.truncate(collection, options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": \"valid\", \"type\": \"identity\", \"properties\": null}")->slice(), options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": \"valid\", \"type\": \"identity\", \"properties\": \"abc\"}")->slice(), options);
trx.commit();
}
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK_THROWS((feature.start()));
}
// read invalid configuration (static analyzers present)
{
{
std::string collection("_iresearch_analyzers");
arangodb::OperationOptions options;
arangodb::SingleCollectionTransaction trx(
arangodb::transaction::StandaloneContext::Create(*vocbase),
collection,
arangodb::AccessMode::Type::WRITE
);
trx.begin();
trx.truncate(collection, options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": \"identity\", \"type\": \"identity\", \"properties\": \"abc\"}")->slice(), options);
trx.commit();
}
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK_THROWS((feature.start()));
}
// read valid configuration (different parameter options)
{
{
std::string collection("_iresearch_analyzers");
arangodb::OperationOptions options;
arangodb::SingleCollectionTransaction trx(
arangodb::transaction::StandaloneContext::Create(*vocbase),
collection,
arangodb::AccessMode::Type::WRITE
);
trx.begin();
trx.truncate(collection, options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": \"valid0\", \"type\": \"identity\", \"properties\": null }")->slice(), options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": \"valid1\", \"type\": \"identity\", \"properties\": true }")->slice(), options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": \"valid2\", \"type\": \"identity\", \"properties\": \"abc\" }")->slice(), options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": \"valid3\", \"type\": \"identity\", \"properties\": 3.14 }")->slice(), options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": \"valid4\", \"type\": \"identity\", \"properties\": [ 1, \"abc\" ] }")->slice(), options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": \"valid5\", \"type\": \"identity\", \"properties\": { \"a\": 7, \"b\": \"c\" }}")->slice(), options);
trx.commit();
}
std::map<irs::string_ref, std::pair<irs::string_ref, irs::string_ref>> expected = {
{ "valid0", { "identity", irs::string_ref::NIL } },
{ "valid2", { "identity", "abc" } },
{ "valid4", { "identity", "[1,\"abc\"]" } },
{ "valid5", { "identity", "{\"a\":7,\"b\":\"c\"}" } },
};
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
feature.visit([&expected](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
return true; // skip static analyzers
}
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.first == type));
CHECK((itr->second.second == properties));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
}
// add new records
{
{
arangodb::OperationOptions options;
arangodb::ManagedDocumentResult result;
auto collection = vocbase->lookupCollection("_iresearch_analyzers");
arangodb::transaction::Methods trx(
arangodb::transaction::StandaloneContext::Create(*vocbase),
EMPTY,
EMPTY,
EMPTY,
arangodb::transaction::Options()
);
CHECK((collection->truncate(trx, options).ok()));
}
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
auto result = feature.emplace("valid", "identity", "abc");
CHECK((result.first));
CHECK((result.second));
}
{
std::map<irs::string_ref, std::pair<irs::string_ref, irs::string_ref>> expected = {
{ "valid", { "identity", "abc" } },
};
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
feature.visit([&expected](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
return true; // skip static analyzers
}
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.first == type));
CHECK((itr->second.second == properties));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
}
}
// remove existing records
{
{
std::string collection("_iresearch_analyzers");
arangodb::OperationOptions options;
arangodb::SingleCollectionTransaction trx(
arangodb::transaction::StandaloneContext::Create(*vocbase),
collection,
arangodb::AccessMode::Type::WRITE
);
trx.begin();
trx.truncate(collection, options);
trx.insert(collection, arangodb::velocypack::Parser::fromJson("{\"name\": \"valid\", \"type\": \"identity\", \"properties\": null}")->slice(), options);
trx.commit();
}
{
std::map<irs::string_ref, std::pair<irs::string_ref, irs::string_ref>> expected = {
{ "identity", { "identity", irs::string_ref::NIL } },
{ "valid", { "identity", irs::string_ref::NIL } },
};
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
feature.visit([&expected](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
if (name != "identity" &&
staticAnalyzers().find(name) != staticAnalyzers().end()) {
return true; // skip static analyzers
}
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.first == type));
CHECK((itr->second.second == properties));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
CHECK((1 == feature.erase("valid")));
CHECK((0 == feature.erase("identity")));
}
{
std::map<irs::string_ref, std::pair<irs::string_ref, irs::string_ref>> expected = {};
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
feature.visit([&expected](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
return true; // skip static analyzers
}
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.first == type));
CHECK((itr->second.second == properties));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
}
}
}
SECTION("test_remove") {
// remove (not started)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((0 == feature.erase("test_analyzer")));
CHECK((true == !feature.get("test_analyzer")));
CHECK((false == !feature.ensure("test_analyzer")));
CHECK((1 == feature.erase("test_analyzer")));
}
// remove static analyzer (not started)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((0 == feature.erase("identity")));
CHECK((false == !feature.get("identity")));
CHECK((0 == feature.erase("identity")));
}
// remove existing (started)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer", "TestAnalyzer", "abc").first));
CHECK((false == !feature.get("test_analyzer")));
CHECK((1 == feature.erase("test_analyzer")));
CHECK((true == !feature.get("test_analyzer")));
}
// remove invalid (started)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((true == !feature.get("test_analyzer")));
CHECK((0 == feature.erase("test_analyzer")));
}
// remove static analyzer (started)
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.get("identity")));
CHECK((0 == feature.erase("identity")));
}
}
SECTION("test_start") {
auto* database = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::SystemDatabaseFeature
>();
auto vocbase = database->use();
// test feature start load configuration (inRecovery, no configuration collection)
{
// ensure no configuration collection
{
auto collection = vocbase->lookupCollection("_iresearch_analyzers");
if (collection) {
vocbase->dropCollection(collection->id(), true, -1);
}
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
}
auto before = StorageEngineMock::inRecoveryResult;
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((nullptr == vocbase->lookupCollection("_iresearch_analyzers")));
auto expected = staticAnalyzers();
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.type == type));
CHECK((itr->second.properties == properties));
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
}
// test feature start load configuration (inRecovery, no configuration collection, uninitialized analyzers)
{
// ensure no configuration collection
{
auto collection = vocbase->lookupCollection("_iresearch_analyzers");
if (collection) {
vocbase->dropCollection(collection->id(), true, -1);
}
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
}
auto before = StorageEngineMock::inRecoveryResult;
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((true == !feature.get("test_analyzer")));
CHECK((false == !feature.ensure("test_analyzer")));
feature.start();
CHECK((nullptr == vocbase->lookupCollection("_iresearch_analyzers")));
auto expected = staticAnalyzers();
expected.emplace(std::piecewise_construct, std::forward_as_tuple("test_analyzer"), std::forward_as_tuple(irs::string_ref::NIL, irs::string_ref::NIL));
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.type == type));
CHECK((itr->second.properties == properties));
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
}
// test feature start load configuration (inRecovery, with configuration collection)
{
// ensure there is an empty configuration collection
{
auto collection = vocbase->lookupCollection("_iresearch_analyzers");
if (collection) {
vocbase->dropCollection(collection->id(), true, -1);
}
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer", "identity", "abc").first));
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr != collection));
}
auto before = StorageEngineMock::inRecoveryResult;
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((nullptr != vocbase->lookupCollection("_iresearch_analyzers")));
auto expected = staticAnalyzers();
expected.emplace(std::piecewise_construct, std::forward_as_tuple("test_analyzer"), std::forward_as_tuple("identity", "abc"));
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.type == type));
CHECK((itr->second.properties == properties));
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
}
// test feature start load configuration (inRecovery, with configuration collection, uninitialized analyzers)
{
// ensure there is an empty configuration collection
{
auto collection = vocbase->lookupCollection("_iresearch_analyzers");
if (collection) {
vocbase->dropCollection(collection->id(), true, -1);
}
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer", "identity", "abc").first));
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr != collection));
}
auto before = StorageEngineMock::inRecoveryResult;
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((true == !feature.get("test_analyzer")));
CHECK((false == !feature.ensure("test_analyzer")));
feature.start();
CHECK((nullptr != vocbase->lookupCollection("_iresearch_analyzers")));
auto expected = staticAnalyzers();
expected.emplace(std::piecewise_construct, std::forward_as_tuple("test_analyzer"), std::forward_as_tuple("identity", "abc"));
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.type == type));
CHECK((itr->second.properties == properties));
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
}
// test feature start load configuration (no configuration collection)
{
// ensure no configuration collection
{
auto collection = vocbase->lookupCollection("_iresearch_analyzers");
if (collection) {
vocbase->dropCollection(collection->id(), true, -1);
}
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
}
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((nullptr != vocbase->lookupCollection("_iresearch_analyzers")));
auto expected = staticAnalyzers();
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.type == type));
CHECK((itr->second.properties == properties));
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
}
// test feature start load configuration (no configuration collection, static analyzers)
{
// ensure no configuration collection
{
auto collection = vocbase->lookupCollection("_iresearch_analyzers");
if (collection) {
vocbase->dropCollection(collection->id(), true, -1);
}
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
}
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((false == !feature.get("identity")));
feature.start();
CHECK((nullptr != vocbase->lookupCollection("_iresearch_analyzers")));
auto expected = staticAnalyzers();
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.type == type));
CHECK((itr->second.properties == properties));
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
}
// test feature start load configuration (with configuration collection)
{
// ensure there is an empty configuration collection
{
auto collection = vocbase->lookupCollection("_iresearch_analyzers");
if (collection) {
vocbase->dropCollection(collection->id(), true, -1);
}
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer", "identity", "abc").first));
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr != collection));
}
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((nullptr != vocbase->lookupCollection("_iresearch_analyzers")));
auto expected = staticAnalyzers();
expected.emplace(std::piecewise_construct, std::forward_as_tuple("test_analyzer"), std::forward_as_tuple("identity", "abc"));
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.type == type));
CHECK((itr->second.properties == properties));
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
}
// test feature start load configuration (with configuration collection, uninitialized analyzers)
{
// ensure there is an empty configuration collection
{
auto collection = vocbase->lookupCollection("_iresearch_analyzers");
if (collection) {
vocbase->dropCollection(collection->id(), true, -1);
}
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer", "identity", "abc").first));
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr != collection));
}
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
CHECK((true == !feature.get("test_analyzer")));
CHECK((false == !feature.ensure("test_analyzer")));
feature.start();
CHECK((nullptr != vocbase->lookupCollection("_iresearch_analyzers")));
auto expected = staticAnalyzers();
expected.emplace(std::piecewise_construct, std::forward_as_tuple("test_analyzer"), std::forward_as_tuple("identity", "abc"));
feature.visit([&expected, &feature](irs::string_ref const& name, irs::string_ref const& type, irs::string_ref const& properties)->bool {
auto itr = expected.find(name);
CHECK((itr != expected.end()));
CHECK((itr->second.type == type));
CHECK((itr->second.properties == properties));
CHECK((itr->second.features.is_subset_of(feature.get(name)->features())));
expected.erase(itr);
return true;
});
CHECK((expected.empty()));
}
}
SECTION("test_tokens") {
auto* database = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::SystemDatabaseFeature
>();
auto vocbase = database->use();
// create a new instance of an ApplicationServer and fill it with the required features
// cannot use the existing server since its features already have some state
std::shared_ptr<arangodb::application_features::ApplicationServer> originalServer(
arangodb::application_features::ApplicationServer::server,
[](arangodb::application_features::ApplicationServer* ptr)->void {
arangodb::application_features::ApplicationServer::server = ptr;
}
);
arangodb::application_features::ApplicationServer::server = nullptr; // avoid "ApplicationServer initialized twice"
arangodb::application_features::ApplicationServer server(nullptr, nullptr);
auto* analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server);
auto* functions = new arangodb::aql::AqlFunctionFeature(server);
auto* sharding = new arangodb::ShardingFeature(server);
auto* systemdb = new arangodb::SystemDatabaseFeature(server, s.system.get());
arangodb::application_features::ApplicationServer::server->addFeature(analyzers);
arangodb::application_features::ApplicationServer::server->addFeature(functions);
arangodb::application_features::ApplicationServer::server->addFeature(sharding);
arangodb::application_features::ApplicationServer::server->addFeature(systemdb);
sharding->prepare();
// ensure there is no configuration collection
{
auto collection = vocbase->lookupCollection("_iresearch_analyzers");
if (collection) {
vocbase->dropCollection(collection->id(), true, -1);
}
collection = vocbase->lookupCollection("_iresearch_analyzers");
CHECK((nullptr == collection));
}
// test function registration
{
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
// AqlFunctionFeature::byName(..) throws exception instead of returning a nullptr
CHECK_THROWS((functions->byName("TOKENS")));
feature.start();
CHECK((nullptr != functions->byName("TOKENS")));
}
analyzers->start();
REQUIRE((false == !analyzers->emplace("test_analyzer", "TestAnalyzer", "abc").first));
// test tokenization
{
auto* function = functions->byName("TOKENS");
CHECK((nullptr != functions->byName("TOKENS")));
auto& impl = function->implementation;
CHECK((false == !impl));
irs::string_ref analyzer("test_analyzer");
irs::string_ref data("abcdefghijklmnopqrstuvwxyz");
VPackFunctionParametersWrapper args;
args->emplace_back(data.c_str(), data.size());
args->emplace_back(analyzer.c_str(), analyzer.size());
AqlValueWrapper result(impl(nullptr, nullptr, *args));
CHECK((result->isArray()));
CHECK((26 == result->length()));
for (int64_t i = 0; i < 26; ++i) {
bool mustDestroy;
auto entry = result->at(nullptr, i, mustDestroy, false);
CHECK((entry.isString()));
auto value = arangodb::iresearch::getStringRef(entry.slice());
CHECK((1 == value.size()));
CHECK(('a' + i == value.c_str()[0]));
}
}
// test invalid arg count
{
auto* function = functions->byName("TOKENS");
CHECK((nullptr != functions->byName("TOKENS")));
auto& impl = function->implementation;
CHECK((false == !impl));
arangodb::SmallVector<arangodb::aql::AqlValue>::allocator_type::arena_type arena;
arangodb::aql::VPackFunctionParameters args{arena};
AqlValueWrapper result(impl(nullptr, nullptr, args));
CHECK((result->isNone()));
}
// test invalid data type
{
auto* function = functions->byName("TOKENS");
CHECK((nullptr != functions->byName("TOKENS")));
auto& impl = function->implementation;
CHECK((false == !impl));
irs::string_ref data("abcdefghijklmnopqrstuvwxyz");
VPackFunctionParametersWrapper args;
args->emplace_back(data.c_str(), data.size());
args->emplace_back(arangodb::aql::AqlValueHintDouble(123.4));
AqlValueWrapper result(impl(nullptr, nullptr, *args));
CHECK((result->isNone()));
}
// test invalid analyzer type
{
auto* function = functions->byName("TOKENS");
CHECK((nullptr != functions->byName("TOKENS")));
auto& impl = function->implementation;
CHECK((false == !impl));
irs::string_ref analyzer("test_analyzer");
VPackFunctionParametersWrapper args;
args->emplace_back(arangodb::aql::AqlValueHintDouble(123.4));
args->emplace_back(analyzer.c_str(), analyzer.size());
AqlValueWrapper result(impl(nullptr, nullptr, *args));
CHECK((result->isNone()));
}
// test invalid analyzer
{
auto* function = functions->byName("TOKENS");
CHECK((nullptr != functions->byName("TOKENS")));
auto& impl = function->implementation;
CHECK((false == !impl));
irs::string_ref analyzer("invalid");
irs::string_ref data("abcdefghijklmnopqrstuvwxyz");
VPackFunctionParametersWrapper args;
args->emplace_back(data.c_str(), data.size());
args->emplace_back(analyzer.c_str(), analyzer.size());
AqlValueWrapper result(impl(nullptr, nullptr, *args));
CHECK((result->isNone()));
}
}
SECTION("test_visit") {
arangodb::iresearch::IResearchAnalyzerFeature feature(s.server);
feature.start();
CHECK((false == !feature.emplace("test_analyzer0", "TestAnalyzer", "abc0").first));
CHECK((false == !feature.emplace("test_analyzer1", "TestAnalyzer", "abc1").first));
CHECK((false == !feature.emplace("test_analyzer2", "TestAnalyzer", "abc2").first));
// full visitation
{
std::set<std::pair<irs::string_ref, irs::string_ref>> expected = {
std::make_pair<irs::string_ref, irs::string_ref>("test_analyzer0", "abc0"),
std::make_pair<irs::string_ref, irs::string_ref>("test_analyzer1", "abc1"),
std::make_pair<irs::string_ref, irs::string_ref>("test_analyzer2", "abc2"),
};
auto result = feature.visit([&expected](
irs::string_ref const& name,
irs::string_ref const& type,
irs::string_ref const& properties
)->bool {
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
return true; // skip static analyzers
}
CHECK((type == "TestAnalyzer"));
CHECK((1 == expected.erase(std::make_pair<irs::string_ref, irs::string_ref>(irs::string_ref(name), irs::string_ref(properties)))));
return true;
});
CHECK((true == result));
CHECK((expected.empty()));
}
// partial visitation
{
std::set<std::pair<irs::string_ref, irs::string_ref>> expected = {
std::make_pair<irs::string_ref, irs::string_ref>("test_analyzer0", "abc0"),
std::make_pair<irs::string_ref, irs::string_ref>("test_analyzer1", "abc1"),
std::make_pair<irs::string_ref, irs::string_ref>("test_analyzer2", "abc2"),
};
auto result = feature.visit([&expected](
irs::string_ref const& name,
irs::string_ref const& type,
irs::string_ref const& properties
)->bool {
if (staticAnalyzers().find(name) != staticAnalyzers().end()) {
return true; // skip static analyzers
}
CHECK((type == "TestAnalyzer"));
CHECK((1 == expected.erase(std::make_pair<irs::string_ref, irs::string_ref>(irs::string_ref(name), irs::string_ref(properties)))));
return false;
});
CHECK((false == result));
CHECK((2 == expected.size()));
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate tests
////////////////////////////////////////////////////////////////////////////////
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------