//////////////////////////////////////////////////////////////////////////////// /// 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 Tobias Gödderz //////////////////////////////////////////////////////////////////////////////// #include "DependencyProxyMock.h" #include "gtest/gtest.h" namespace arangodb { namespace tests { namespace aql { using namespace arangodb::aql; /* * * * * * Mocks * * * * */ // Note that _itemBlockManager gets passed first to the parent constructor, // and only then gets instantiated. That is okay, however, because the // constructor will not access it. template DependencyProxyMock::DependencyProxyMock(arangodb::aql::ResourceMonitor& monitor, ::arangodb::aql::RegisterId nrRegisters) : DependencyProxy({}, _itemBlockManager, std::shared_ptr>(), nrRegisters), _itemsToReturn(), _numFetchBlockCalls(0), _monitor(monitor), _itemBlockManager(&_monitor, SerializationFormat::SHADOWROWS) {} template std::pair // NOLINTNEXTLINE google-default-arguments DependencyProxyMock::fetchBlock(size_t) { _numFetchBlockCalls++; if (_itemsToReturn.empty()) { return {ExecutionState::DONE, nullptr}; } std::pair returnValue = std::move(_itemsToReturn.front()); _itemsToReturn.pop(); return returnValue; } /* * * * * * * * * * * * * * Test helper functions * * * * * * * * * * * * */ template DependencyProxyMock& DependencyProxyMock::shouldReturn( ExecutionState state, SharedAqlItemBlockPtr const& block) { // Should only be called once on each instance TRI_ASSERT(_itemsToReturn.empty()); return andThenReturn(state, block); } template DependencyProxyMock& DependencyProxyMock::shouldReturn( std::pair firstReturnValue) { // Should only be called once on each instance TRI_ASSERT(_itemsToReturn.empty()); return andThenReturn(std::move(firstReturnValue)); } template DependencyProxyMock& DependencyProxyMock::shouldReturn( std::vector> firstReturnValues) { // Should only be called once on each instance TRI_ASSERT(_itemsToReturn.empty()); return andThenReturn(std::move(firstReturnValues)); } template DependencyProxyMock& DependencyProxyMock::andThenReturn( ExecutionState state, SharedAqlItemBlockPtr const& block) { auto inputRegisters = std::make_shared>(); // add all registers as input for (RegisterId i = 0; i < this->getNrInputRegisters(); i++) { inputRegisters->emplace(i); } return andThenReturn({state, block}); } template DependencyProxyMock& DependencyProxyMock::andThenReturn( std::pair additionalReturnValue) { _itemsToReturn.push(std::move(additionalReturnValue)); return *this; } template DependencyProxyMock& DependencyProxyMock::andThenReturn( std::vector> additionalReturnValues) { for (auto& it : additionalReturnValues) { andThenReturn(std::move(it)); } return *this; } template bool DependencyProxyMock::allBlocksFetched() const { return _itemsToReturn.empty(); } template size_t DependencyProxyMock::numFetchBlockCalls() const { return _numFetchBlockCalls; } template std::pair DependencyProxyMock::skipSome(size_t atMost) { ExecutionState state; SharedAqlItemBlockPtr block; std::tie(state, block) = _itemsToReturn.front(); if (block == nullptr) { return {ExecutionState::DONE, 0}; } size_t const firstShadowRow = [&]() { size_t row; for (row = 0; row < block->size(); ++row) { if (block->isShadowRow(row)) break; } return row; }(); atMost = (std::min)(firstShadowRow, atMost); if (block->size() <= atMost) { // Return the whole block std::tie(state, block) = fetchBlock(atMost); return {state, block->size()}; } TRI_ASSERT(block != nullptr); TRI_ASSERT(block->size() > atMost); SharedAqlItemBlockPtr rest = block->slice(atMost, block->size()); _itemsToReturn.front().second = rest; return {ExecutionState::HASMORE, atMost}; } template MultiDependencyProxyMock::MultiDependencyProxyMock( arangodb::aql::ResourceMonitor& monitor, ::arangodb::aql::RegisterId nrRegisters, size_t nrDeps) : DependencyProxy({}, _itemBlockManager, std::shared_ptr>(), nrRegisters), _itemBlockManager(&monitor, SerializationFormat::SHADOWROWS) { _dependencyMocks.reserve(nrDeps); for (size_t i = 0; i < nrDeps; ++i) { _dependencyMocks.emplace_back(DependencyProxyMock{monitor, nrRegisters}); } } template std::pair MultiDependencyProxyMock::fetchBlockForDependency(size_t dependency, size_t atMost) { return getDependencyMock(dependency).fetchBlock(atMost); } template std::pair MultiDependencyProxyMock::skipSomeForDependency( size_t dependency, size_t atMost) { return getDependencyMock(dependency).skipSome(atMost); } template bool MultiDependencyProxyMock::allBlocksFetched() const { for (auto& dep : _dependencyMocks) { if (!dep.allBlocksFetched()) { return false; } } return true; } template size_t MultiDependencyProxyMock::numFetchBlockCalls() const { size_t res = 0; for (auto& dep : _dependencyMocks) { res += dep.numFetchBlockCalls(); } return res; } } // namespace aql } // namespace tests } // namespace arangodb template class ::arangodb::tests::aql::DependencyProxyMock<::arangodb::aql::BlockPassthrough::Enable>; template class ::arangodb::tests::aql::DependencyProxyMock<::arangodb::aql::BlockPassthrough::Disable>; // Multiblock does not pass through template class ::arangodb::tests::aql::MultiDependencyProxyMock<::arangodb::aql::BlockPassthrough::Disable>;